diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..106f6eee4 --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +[antflake8] +max-line-length = 120 +select = C,E,F,W,B,B950 +extend-ignore = F403, F405, E203, E501, W503 +exclude = *_pb2.py, lark_token.py diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..360c0032a --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,31 @@ +- With issues: + - Use the search tool before opening a new issue. + - Please provide source code and commit sha if you found a bug. + - Review existing issues and provide feedback or react to them. + +## Description + + + +## How to reproduce + + + +TODO + +## Expectations + + + +TODO + +## Actual result + + + +TODO + +## Environment + +- KCLVM version: +- Operating system: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..8717809e0 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,6 @@ +- With pull requests: + - Open your pull request against `main` + - Your pull request should have no more than two commits, if not you should squash them. + - It should pass all tests in the available continuous integration systems such as GitHub Actions. + - You should add/modify tests to cover your proposed code changes. + - If your pull request contains a new feature, please document it on the README. diff --git a/.github/workflows/github-actions.yaml b/.github/workflows/github-actions.yaml new file mode 100644 index 000000000..b701967e1 --- /dev/null +++ b/.github/workflows/github-actions.yaml @@ -0,0 +1,34 @@ +name: KCLVM-actions +on: [push] +jobs: + test-unit: + name: Test + runs-on: ubuntu-latest + container: + image: kusionstack/kclvm-builder + steps: + - name: Check out code + uses: actions/checkout@v3 + # with: + # submodules: 'true' + - name: Build KCLVM + run: make build + shell: bash + - name: Grammar test + run: | + chmod +x ./internal/kclvm_py/scripts/test_grammar.sh + topdir=$(realpath $(dirname $0)) ./internal/kclvm_py/scripts/test_grammar.sh + shell: bash + - name: Internal python unit test + run: | + chmod +x ./internal/kclvm_py/scripts/test_unit.sh + ./internal/kclvm_py/scripts/test_unit.sh + shell: bash + # - name: Rust unit test + # uses: actions-rs/toolchain@v1 + # with: + # toolchain: stable + # override: true + # components: rustfmt, clippy + # run: cd kclvm && make test + # shell: bash diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..497d9329c --- /dev/null +++ b/.gitignore @@ -0,0 +1,81 @@ +/build/ +.vs/ +.vscode/ +.idea/ + +# kcl files +.pytest_cache +__pycache__ +__kclcache__/ +build-result/ +dist/ +kclvm.egg-info/ +.eggs/ + +# Mac OS X files +.DS_Store + +_python37_home_ +_site-packages + +__kcl_test_main.k + +# kclvm bundles +/kclvm-*.zip +/kcl-go* +/_build_dist +.kusion + +/venv/ + +# Coverage +.coverage +.coverage.* +/cover +/scripts/cover + +/result + +coverage.xml +TEST-kclvm.xml + +/go-test-coverprofile.* + +# Benchmark +.benchmarks + +# Code check +.code_check + +/_output + +# Lark parser cache +lark_parser.pickle + +/vendor +/_build* +/__build* +/_3rdparty + + +# Rust +*.o +*.a +*.so +*.orig +/target +/*/target +/*/*/target +/**/target + +/kclvm/vendor +/scripts/docker/kclvm-builder/vendor* +/scripts/docker/kclvm-builder/crates.io-index +/scripts/docker/kclvm-builder-centos7/crates.io-index +/scripts/docker/kclvm-builder-ubuntu/crates.io-index +*.tar.gz +_a.out.* +_a.out_*.* +# KCLVM cache +.kclvm +__main__.* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..56f9403ef --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "plugins"] + path = plugins + url = https://github.com/KusionStack/kcl-plugin diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..c0de0584f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,134 @@ +# KCLVM Developing Guide + +KCLVM follows a very standard Github development process, using Github tracker for issues and merging pull requests into master. If you would like to contribute something, or simply want to hack on the code this document should help you get started. + +Before we accept a non-trivial patch or pull request we will need you to sign the Contributor License Agreement. Signing the contributor’s agreement does not grant anyone commits rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do. Active contributors might be asked to join the core team and given the ability to merge pull requests. + +## Install Dependencies + +### macOS and OS X + ++ `Python3.7+` ++ `Go 1.16+` ++ `Rust 2021 edition` ++ `openssl@1.1` + +``` +brew install openssl@1.1 +``` + +### Linux + ++ `Go 1.16+` ++ `Rust 2021 edition` ++ `Python3 Building Dependencies` + +For UNIX based systems, you can run: + +``` +yum groupinstall -y "Development Tools" +yum install -y gcc patch libffi-devel python-devel zlib-devel bzip2-devel ncurses-devel sqlite-devel +yum install -y libpcap-devel xz-devel readline-devel tk-devel gdbm-devel db4-deve +yum -y install yum-utils +yum-builddep -y python3 +yum install -y zlib* +yum install -y openssl-devel +yum install -y glibc-static +``` + +On Debian, Ubuntu, and other apt based systems, you can run: + +``` +apt-get update + +apt-get install -y git wget curl +apt-get install -y make gcc patch +apt-get install -y python-dev libffi-dev +apt-get install -y zlib1g-dev ncurses-dev build-essential libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev +``` + +### Docker + +Use the image `kusionstack/kclvm-builder`, run: + +``` +make sh-in-docker +``` + +## Building and Testing + +### Scripting + +We provide a simple `run.sh` script to build and package with. + +To build everything, run: + +``` +./run.sh -a build +``` + +Building includes two steps, which are `build-cpython` and `build-kclvm`. Alternatively, these steps can be invoked separately: + +``` +./run.sh -a build-cpython +./run.sh -a build-kclvm +``` + +Building KCL requires local ssl module. Use -s $yourLocalSSL to specify custom ssl path: + +``` +./run.sh -a build -s $your-local-ssl +./run.sh -a build-cpython -s $your-local-ssl +``` + +If -s option unset, default ssl path will be used: +Darwin: `$(brew --prefix openssl@1.1)` +Linux: `/usr/lib64` + +To use KCL, add the path `_build/dist/{os}/kclvm/bin` to the `PATH` environment variable. Here, `{os}` stands for the operating system, which can be `Darwin`, `centos`, `ubuntu` or other types. + +Then, you can run: + +``` +kcl hello.k +``` + +To perform testing, run: + +``` +./run.sh -a test +``` + +Next, we can refer to [KCLVM README](./kclvm/README.md) for the next step of environment configuration and build the KCLVM rust code. + +If we have changed any codes in the program, we can update kclvm binaries, run: + +``` +./run.sh -a update-kclvm +``` + +To build a tar file, run: + +``` +./run.sh -a release +``` + +## Using KCL + +The specific user manual is as follows: [KCLVM User Manual](docs/cmd/README_KCLVM_USE.md) + +## Code Structure + +KCL has added the following files and directories: + ++ `kclvm` - The KCL compiler code. ++ `test` - All KCL test cases include regression tests and unit tests. ++ `scripts` - The directory where additional scripts to build and test KCL resist. ++ `run.sh` - The script to perform operations such as building and testing. ++ `internal/kclvm_py` - KCLVM Python implementation, will be abandoned soon, please do not submit any code to it, it will be reorganized in the way of KCLVM Python SDK in the future. ++ `docs` - KCL command line Documentation. ++ `spec` - KCL model and API Specification. + +During building and testing, the following directories can be generated: + ++ `_build` - The directory to save building results including distributions. diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..989e2c59e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..6962930d7 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +PROJECT_NAME = KCLVM + +PWD:=$(shell pwd) + +KCLVM_VERSION := $(shell cat VERSION) +BUILD_IMAGE:=kusionstack/kclvm-builder + +# export DOCKER_DEFAULT_PLATFORM=linux/amd64 +# or +# --platform linux/amd64 + +RUN_IN_DOCKER:=docker run -it --rm +RUN_IN_DOCKER+=-v ~/.ssh:/root/.ssh +RUN_IN_DOCKER+=-v ~/.gitconfig:/root/.gitconfig +RUN_IN_DOCKER+=-v ~/go/pkg/mod:/go/pkg/mod +RUN_IN_DOCKER+=-v ${PWD}:/root/kclvm +RUN_IN_DOCKER+=-w /root/kclvm ${BUILD_IMAGE} + +# ---------------- +# KCLVM build +# ---------------- + +build: + ${PWD}/run.sh -a build + +# ---------------- +# Docker +# ---------------- + +sh-in-docker: + ${RUN_IN_DOCKER} bash diff --git a/README.md b/README.md index 5d81dab1f..bccca797e 100644 --- a/README.md +++ b/README.md @@ -1 +1,91 @@ -# KCLVM \ No newline at end of file +# KCL + +![license](https://img.shields.io/badge/license-Apache--2.0-green.svg) +[![Continuous Integration](https://github.com/KusionStack/KCLVM/actions/workflows/github-actions.yaml/badge.svg)](https://github.com/KusionStack/KCLVM/actions?query=branch%3Amain) + +[中文](./README_ZH.md) + +Kusion Configuration Language (KCL) is an open source configuration language mainly used in [Kusion Stack](https://kusionstack.io). KCL is a statically typed language for configuration and policy scenarios, based on concepts such as declarative and Object-Oriented Programming (OOP) paradigms. + +## Core Features + ++ **Simple** + + Originated from Python and Golang, incorporating functional language features. + + Absorbs integrated language elements such as statements, expressions, conditions, loops, etc. + + Type and data separation, schema declaration configuration definition. ++ **Stable** + + Strong immutable constraint. + + Compile-time type deduction, type checking. + + Rule policy definition: attribute-centric constraint expressions, query results based on constraints. + + Testable: assert, print, and test tools. ++ **Scalable** + + Configuration unification: compile-time configuration dependency graph substitution. + + Configuration attribute operators: meet the needs of configuration override, merge, add and delete, etc. + + Configuration reuse: rich built-in data structures and syntax semantics, easily to expand one configuration of different scenarios. ++ **Engineering** + + Schema single inheritance and declarative model reuse and assembly. + + Tool & API granular configuration automation. + + Rich built-in functions and system libraries. + + Top-level dynamic data input. + + Code organization: modules and packages. + + [Plug-in system](https://github.com/KusionStack/kcl-plugin): reuse common programming language ecology. + + [OpenAPI model support](https://github.com/KusionStack/kcl-openapi): Swagger and KCL schema bidirectional conversion, Kubernetes CRD conversion to KCL schema. ++ **High Performance** + + Works with the LLVM optimizer, supports compilation to native code and formats like WASM and executes efficiently. + +## Installing & Documentation + +### How to install + +[Download](https://github.com/KusionStack/KCLVM/releases) the latest release from GitHub and add `{install-location}/kclvm/bin` to environment PATH. + +### Quick Showcase + +`./samples/fib.k` is an example of calculating the Fibonacci sequence. + +```kcl +schema Fib: + n1: int = n - 1 + n2: int = n1 - 1 + n: int + value: int + + if n <= 1: + value = 1 + elif n == 2: + value = 1 + else: + value = Fib {n: n1}.value + Fib {n: n2}.value + +fib8 = Fib {n: 8}.value +``` + +We can execute the following command to get a YAML output. + +``` +kcl ./samples/fib.k +``` + +YAML output + +```yaml +fib8: 21 +``` + +### Documentation + +Detailed documentation is available at https://kusionstack.io + +## Developing & Contributing + +### Developing + +See [Developing Guide](./CONTRIBUTING.md). + +### Roadmap + +See [KCLVM Roadmap](https://kusionstack.io/docs/governance/intro/roadmap#kclvm-%E8%B7%AF%E7%BA%BF%E8%A7%84%E5%88%92) + +## License + +Apache License Version 2.0 diff --git a/README_ZH.md b/README_ZH.md new file mode 100644 index 000000000..92f929b24 --- /dev/null +++ b/README_ZH.md @@ -0,0 +1,91 @@ +# KCL + +![license](https://img.shields.io/badge/license-Apache--2.0-green.svg) +[![Continuous Integration](https://github.com/KusionStack/KCLVM/actions/workflows/github-actions.yaml/badge.svg)](https://github.com/KusionStack/KCLVM/actions?query=branch%3Amain) + +[English](./README.md) + +Kusion 配置语言(KCL)是一种开源配置语言,主要用于 [Kusion Stack](https://kusionstack.io) 开放协同技术栈。并且 KCL 是一种基于声明性和面向对象编程 (OOP) 范式等概念,用于配置和策略场景的静态类型语言。 + +## 核心特性 + ++ **简单** + + 源于 Python、Golang,融入函数语言特性 + + 吸收语句、表达式、条件、循环等语言元素 + + 类型和数据分离,Schema 声明配置定义 ++ **稳定** + + 强不可变约束 + + 编译时类型推导、类型检查 + + Rule 策略定义:以属性为中心的约束表达式、根据约束查询结果 + + 可测试:语言内置 assert 断言、print 打印和测试工具 ++ **可扩展** + + 配置合并:编译时配置依赖图代换 + + 配置属性运算符:满足配置覆盖、合并、添加和删除等需求 + + 配置复用:丰富的内置数据结构和语法语义,轻松扩展同一份配置到不同场景 ++ **工程化** + + Schema 单一继承和声明性模型复用和组装 + + 工具和API 粒度的配置自动化“增删改查” + + 丰富的内置函数和系统库 + + 顶层数据动态导入 + + 代码组织:模块和包 + + [插件系统](https://github.com/KusionStack/kcl-plugin):复用通用编程语言生态。 + + [OpenAPI 模型支持](https://github.com/KusionStack/kcl-openapi):Swagger 与 Schema 双向转换,Kubernetes CRD 转换为 Schema ++ **高性能** + + 配合 LLVM 优化器、支持编译到本地代码和 WASM 等格式并高效执行 + +## 安装 & 文档 + +### 如何安装 + +从 Github releases 页面[下载](https://github.com/KusionStack/KCLVM/releases),并且将 `{install-location}/kclvm/bin` 添加到您的环境变量中 + +### 快速开始 + +`./samples/fib.k` 是一个计算斐波那契数列的例子 + +```kcl +schema Fib: + n1: int = n - 1 + n2: int = n1 - 1 + n: int + value: int + + if n <= 1: + value = 1 + elif n == 2: + value = 1 + else: + value = Fib {n: n1}.value + Fib {n: n2}.value + +fib8 = Fib {n: 8}.value +``` + +我们可以通过执行如下命令得到 YAML 输出 + +``` +kcl ./samples/fib.k +``` + +YAML 输出 + +```yaml +fib8: 21 +``` + +### 文档 + +更多文档请访问 https://kusionstack.io + +## 开发 & 贡献 + +### 开发 + +参考[开发手册](./CONTRIBUTING.md). + +### 路线规划 + +参考[KCLVM 路线规划](https://kusionstack.io/docs/governance/intro/roadmap#kclvm-%E8%B7%AF%E7%BA%BF%E8%A7%84%E5%88%92) + +## 许可 + +Apache License Version 2.0 diff --git a/VERSION b/VERSION new file mode 100644 index 000000000..44bb5d1f7 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.4.1 \ No newline at end of file diff --git a/docs/cmd/README_KCLVM_USE.md b/docs/cmd/README_KCLVM_USE.md new file mode 100644 index 000000000..652f595c2 --- /dev/null +++ b/docs/cmd/README_KCLVM_USE.md @@ -0,0 +1,129 @@ +# Kusion Configuration Language Virtual Machine (KCLVM) Command-Line Interface (CLI) Tool + +KCLVM mainly uses a CLI tool to help us to build our cloud native configuration application, and the CLI tool is a cross-platform tool chain for compiling the KCL file. We can easily use it to generate our wanted configuration YAML file. + +## Abstract + +```cli +kcl [-h] [-D ARGUMENT] [-S PATH_SELECTOR] [-O OVERRIDES] + [-Y [SETTING [SETTING ...]]] [-o OUTPUT] [-n] [-r] [-c] [-s] [-v] + [-d] [-p] [-L] [-l] [-V] [--target {native,wasm}] + [file [file ...]] +``` + +## Parameters + +* `ARGUMENT`: The top-level argument. +* `OUTPUT`: The output file. +* `SETTING`: The top-level YAML setting file. +* `PATH_SELECTOR`: The configuration selector path. +* `OVERRIDES`: The configuration override path and value. + +## Arguments + +### Positional Arguments + +* `file`: The input KCL files to be compiled. + +### Optional Arguments + +* `-h|--help`: Show the help message and exit. +* `-D|--argument`: Specify the top-level argument. +* `-Y|--setting`: Specify the top-level setting file. +* `-o|--output`: Specify the output file. +* `-n|--disable-none`: Disable dumping None values. +* `-r|--strict-range-check`: Do perform strict numeric range check. +* `-c|--compile-only`: Compile only and do not generate the YAML file. +* `-s|--save-temps`: Save intermediate files. +* `-v|--verbose`: Run in verbose mode. +* `-d|--debug`: Run in debug mode (for developers only). +* `-p|--profile`: Perform profiling. +* `-L|--list-attributes`: Show schema attributes list. +* `-l|--list-options`: Show kcl options list. +* `-V|--version`: Show the kcl version. +* `-S|--path-selector`: Specify the path selector. +* `-O|--overrides`: Specify the configuration override path and value. +* `--target {native,wasm}`: Specify the target type + +## Examples + +* If we have written our KCL files, KCL can be invoked as: + +``` +kcl your_config.k +``` + +* In addition, we can specify the location of the output: + +``` +kcl your_config.k -o your_yaml.yaml +``` + +* We can use `-D ARGUMENT` or `--argument ARGUMENT` to specify the top-level arguments: + +``` +kcl your_config.k -D your_arguments + +Examples: +kcl your_config.k -D argsName=argsValue +``` + +* We can use `-Y SETTING` or `--setting SETTING` to specify the top-level arguments through the YAML file: + +``` +kcl your_config.k -Y your_setting_file.yaml +``` + +* If we don’t want to display `none` values in the generated file, we can use the parameter `-n`: + +``` +kcl your_config.k -n +``` + +* If we want to perform the strict numeric range check, we can use the parameter `-r`: + +``` +kcl your_config.k -r +``` + +* If we want to save intermediate files, we can use the parameter `-s`: + +``` +kcl your_config.k -s +``` + +* If we want to show schema attributes list and kcl options list, we can use the parameter `-L` and `-l`: + +``` +kcl your_config.k -L -l +``` + +* If we want to get part of KCL configuration, we can use `-S` or `--path-selector` to specify the configuration override path and value: + +``` +kcl your_config.k -S pkg:variable_path +``` + +* If we want to override part of the KCL configuration content, we can use `-O` or `--overrides` to specify the configuration override path and value: + +``` +kcl your_config.k -O pkg:variable_path=value +``` + +* If we want to compile KCL code into a native dynamic link library, we can use `--target` to specify the `native` target. + +``` +kcl your_config.k --target native +``` + +* If we want to compile KCL code into a WASM module, we can use `--target` to specify the `wasm` target. + +``` +kcl your_config.k --target wasm +``` + +* For more information, we can use the following command to show the help message: + +``` +kcl --help +``` diff --git a/docs/tools/docgen/docgen.md b/docs/tools/docgen/docgen.md new file mode 100644 index 000000000..3ba4e2645 --- /dev/null +++ b/docs/tools/docgen/docgen.md @@ -0,0 +1,317 @@ +# KCL 文档生成工具 + +Kusion 命令行工具支持从 KCL 源码中一键提取模型文档,并支持丰富的输出格式:JSON,YAML 和 Markdown 等。本文介绍 KCL 语言的文档规范,举例说明如何使用 KCL 文档生成工具提取文档,并展示新增本地化语言文档的流程。 + +## KCL 语言的文档规范 + +KCL文件的文档主要包含如下两个部分: + +* 当前 KCL Moudle 的文档:对当前 KCL 文件的说明 +* KCL 文件内包含的所有 Schema 的文档:对当前 Schema 的说明,其中包含 Schema 描述、Schema 各属性的描述、Examples 三部分,具体格式如下: + +1. Schema 描述 + +```python +"""这是Schema一个简短的描述信息 +""" +``` + +2. Schema 各属性的描述:包含属性描述、属性类型、默认值、是否可选 + +```python +""" +Attributes +---------- +x : type, default is a, optional. + Description of parameter `x`. +y : type, default is b, required. + Description of parameter `y`. +""" +``` + +其中,使用 `----------` 表示 `Attributes` 为一个标题(`-` 符号长度与标题长度保持一致),属性名称与属性类型用冒号 `:` 分隔,属性的说明另起一行并增加缩进进行书写。属性的默认值说明跟在属性类型之后使用逗号 `,` 分隔,书写为 `default is {默认值}` 形式,此外需要说明属性是否为可选/必选,对于可选属性在默认值之后书写 `optional`,对于必选属性在默认值之后书写 `required`。 + + +3. Examples + +```python +""" +Examples +-------- +val = Schema { + name = "Alice" + age = 18 +} +""" +``` + +此外,KCL 文档字符串语法应采用 [re-structured text (reST)](https://docutils.sourceforge.io/rst.html) 语法子集,并使用 [Sphinx](https://www.sphinx-doc.org/en/master/) 渲染呈现。 + +## 从 KCL 源码生成文档 + +使用 kcl-doc generate 命令,从用户指定的文件或目录中提取文档,并输出到指定目录。 + +* 参数说明 +``` +usage: kcl-doc generate [-h] [--format YAML] [-o OUTPUT] [--r] + [--i18n-locale LOCALE] [--repo-url REPO_URL] + [files [files ...]] + +positional arguments: + files KCL file paths. If there's more than one files to + generate, separate them by space + +optional arguments: + -h, --help show this help message and exit + --format YAML Doc file format, support YAML, JSON and MARKDOWN. + Defaults to MARKDOWN + -o OUTPUT, --output-path OUTPUT + Specify the output directory. Defaults to ./kcl_doc + --r, -R, --recursive Search directory recursively + --i18n-locale LOCALE I18n locale, e.g.: zh, zh_cn, en, en_AS. Defaults to + en + --repo-url REPO_URL The source code repository url. It will displayed in + the generated doc to link to the source code. + --i18n-path I18N_PATH + The i18n input file path. It can be a path to an i18n + file when generating doc for a single kcl file, or a + path to a directory that contains i18n files when + generating docs for multipule kcl files. The program + will search for the i18n input file according to the + locale when generating docs. If i18n file exists, use + it instead of source file to generate the doc +``` + +* 从指定的一个或多个文件中提取文档,并输出到指定目录 + +```text +kcl-doc generate your_config.k your_another_config.k -o your_docs_output_dir +``` + +* 从指定目录内,递归地查找 KCL 源码文件,并提取文档 + +```text +kcl-doc generate your_config_dir -r -o your_docs_output_dir +``` + +* 在生成文档时,指定源码仓库地址。一经指定,生成的文档中将包含指向源码文件的链接 + +```text +kcl-doc generate your_config.k -o your_docs_output_dir --repo-url https://url/to/source_code +``` + +## 新增本地化语言的文档 + +如前所示,默认情况下,文档生成工具提取的文档以源码 docstring 的内容为准,因而文档的语言随 docstring 编写语言而定。如果需要为源文件新增本地化语言的文档,则可以遵循按如下步骤: + +1. 初始化 i18n 配置文件。该步骤基于指定的 KCL 源码文件,生成相应的 i18n 配置文件,文件格式可选 JSON/YAML,默认为 YAML. 输出的配置文件名称将以指定的目标本地化方言结尾 + +```text +kcl-doc init-i18n your_config.k --format JSON --i18n-locale your_target_locale +``` + +2. 手动修改上述生成的 i18n 配置文件,使用目标语言修改配置中的 doc 字段 + +3. 基于修改后的 i18n 配置,生成本地化语言的文档。工具将查找指定目标语言的 i18n 配置文件,并转化为最终的文档 + +```text +kcl-doc generate your_config_dir --i18n-locale your_target_locale --format Markdown +``` + +接下来,通过一个小例子演示新增本地化语言文档的过程。 + +1. 准备 KCL 源码文件,例如 server.k: + +```python +schema Server: + """Server is the common user interface for long-running + services adopting the best practice of Kubernetes. + + Attributes + ---------- + workloadType : str, default is "Deployment", required + Use this attribute to specify which kind of long-running service you want. + Valid values: Deployment, CafeDeployment. + See also: kusion_models/core/v1/workload_metadata.k. + name : str, required + A Server-level attribute. + The name of the long-running service. + See also: kusion_models/core/v1/metadata.k. + labels : {str:str}, optional + A Server-level attribute. + The labels of the long-running service. + See also: kusion_models/core/v1/metadata.k. + + Examples + ---------------------- + myCustomApp = AppConfiguration { + name = "componentName" + } + """ + + workloadType: str = "Deployment" + name: str + labels?: {str: str} +``` + +2. 从 server.k 得到初始化的 i18n 配置文件,例如希望为其增加中文文档,指定生成的配置文件格式为 YAML + +```text +kcl init-i18n server.k --format YAML --i18n-locale zh_cn +``` + +该命令将在当前目录下创建 kcl_doc 目录,并生成 i18n 配置文件 kcl_doc/i18n_server_zh_cn.yaml,其内容如下: + +```yaml +name: server +relative_path: ./server.k +schemas: +- name: Server + doc: | + Server is the common user interface for long-running + services adopting the best practice of Kubernetes. + attributes: + - name: workloadType + doc: | + Use this attribute to specify which kind of long-running service you want. + Valid values: Deployment, CafeDeployment. + See also: kusion_models/core/v1/workload_metadata.k. + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + default_value: '"Deployment"' + is_optional: false + - name: name + doc: | + A Server-level attribute. + The name of the long-running service. + See also: kusion_models/core/v1/metadata.k. + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + is_optional: false + default_value: '' + - name: labels + doc: | + A Server-level attribute. + The labels of the long-running service. + See also: kusion_models/core/v1/metadata.k. + type: + type_str: '{str: str}' + type_category: DICT + dict_type: + key_type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + value_type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + is_optional: true + default_value: '' + examples: | + myCustomApp = AppConfiguration { + name = "componentName" + } +doc: '' +source_code_url: '' +``` + +3. 修改初始化得到的 i18n 配置,将其中的 doc 字段修改为中文的描述,修改后的配置如下: + +```yaml +name: server +relative_path: ./server.k +schemas: +- name: Server + doc: | + Server 模型定义了采用 Kubernetes 最佳实践的持续运行的服务的通用配置接口 + attributes: + - name: workloadType + doc: | + workloadType 属性定义了服务的类型,是服务级别的属性。合法的取值有:Deployment, CafeDeployment. + 另请查看:kusion_models/core/v1/workload_metadata.k. + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + default_value: '"Deployment"' + is_optional: false + - name: name + doc: | + name 为服务的名称,是服务级别的属性。 + 另请查看:kusion_models/core/v1/metadata.k. + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + is_optional: false + default_value: '' + - name: labels + doc: | + labels 为服务的标签,是服务级别的属性。 + 另请查看:kusion_models/core/v1/metadata.k. + type: + type_str: '{str: str}' + type_category: DICT + dict_type: + key_type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + value_type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + is_optional: true + default_value: '' + examples: | + myCustomApp = AppConfiguration { + name = "componentName" + } +doc: '' +source_code_url: '' +``` + +4. 基于修改后的 i18n 配置,生成本地化语言的文档,执行如下命令,将输出中文的文档 kcl_doc/doc_server_zh_cn.md,命令及生成的文档内容如下: + +```text +kcl-doc generate server.k --i18n-locale zh_cn --format Markdown +``` + +~~~markdown +# server +## Schema Server +Server 模型定义了采用 Kubernetes 最佳实践的持续运行的服务的通用配置接口 + +### Attributes +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**workloadType**
workloadType 属性定义了服务的类型,是服务级别的属性。合法的取值有:Deployment, CafeDeployment.
另请查看:kusion_models/core/v1/workload_metadata.k.|str|"Deployment"|**required**| +|**name**
name 为服务的名称,是服务级别的属性。
另请查看:kusion_models/core/v1/metadata.k.|str|Undefined|**required**| +|**labels**
labels 为服务的标签,是服务级别的属性。
另请查看:kusion_models/core/v1/metadata.k.|{str: str}|Undefined|optional| +### Examples +``` +myCustomApp = AppConfiguration { + name = "componentName" +} +``` + + + +~~~ + +## 附录 + +### 常见的 reST 概念 + +对于 reST 格式的文档,段落和缩进很重要,新段落用空白行标记,缩进即为表示输出中的缩进。可以使用如下方式表示字体样式: + +* \*斜体\* +* \*\*粗体\*\* +* \`\`等宽字体\`\` + +参考 [reST 文档](https://docutils.sourceforge.io/rst.html)获得更多帮助。 diff --git a/docs/tools/format/format.md b/docs/tools/format/format.md new file mode 100644 index 000000000..da4c4224f --- /dev/null +++ b/docs/tools/format/format.md @@ -0,0 +1,64 @@ +# KCL 格式化工具 + +## 简介 + +KCL 支持通过内置的命令行工具一键格式化多个 KCL 文件文档。本文展示 KCL 编码风格和 KCL 格式化工具的使用方式。 + +## 使用方式 + +* 单文件格式化 + +```text +kcl-fmt your_config.k +``` + +* 文件夹内多文件格式化 + +```text +kcl-fmt your_config_path -R +``` + +* 命令行参数 + * `-R|--recursive` 设置是否递归遍历子文件夹 + * `-w|--fmt-output` 设置是否输出到标准输出流,不加 `-w` 表示原地格式化 KCL 文件 + +## 格式化文件效果展示 + +* 格式化前 + +```py +import math +mixin DeploymentMixin: + service:str ="my-service" +schema DeploymentBase: + name: str + image : str +schema Deployment[replicas] ( DeploymentBase ) : + mixin[DeploymentMixin] + replicas : int = replicas + command: [str ] + labels: {str: str} +deploy = Deployment(replicas = 3){} +``` + +* 格式化后 + +```py +import math + +mixin DeploymentMixin: + service: str = "my-service" + +schema DeploymentBase: + name: str + image: str + +schema Deployment[replicas](DeploymentBase): + mixin [DeploymentMixin] + replicas: int = replicas + command: [str] + labels: {str:str} + +deploy = Deployment(replicas=3) {} + +``` diff --git a/docs/tools/kcl-lint/lint.md b/docs/tools/kcl-lint/lint.md new file mode 100644 index 000000000..3899dd99e --- /dev/null +++ b/docs/tools/kcl-lint/lint.md @@ -0,0 +1,155 @@ +# KCL Lint 工具 + +## 简介 + +KCL 支持通过内置的命令行工具对 KCL 代码进行检查,并支持多种输出格式。本文档展示 KCL Lint 工具的使用方式。 + +## 示例 + +### 工程结构 + +``` +. +└── Test + └── kcl.mod + └── .kcllint + └── a.k + └── b.k + └── dir + └── c.k + └── test.k +``` + +其中,`.kcllint` 文件为配置参数文件,非必需项,`a.k`,`b.k`,`c.k`,`test.k` 为测试的 kcl 文件。 + +命令: + +```bash +kcl-lint your_config.k +``` + +或 + +``` +kcl-lint your_config_path +``` + +lint 配置文件 + +``` +kcl-lint --config abspath/.kcllint your_config.k +``` + +输出结果示例: + +``` +/Users/../test.k:12:1: E0501 line too long (122 > 120 characters) +# line too long, line too long, line too long, line too long, line too long, line too long, line too long, line too long, +^ + +/Users/../test.k:14:1: E0413 Import b should be placed at the top of the module +import b +^ + + +Check total 1 files: +1 E0413: ImportStmt is not at the top of the file +1 E0501: Line too long +KCL Lint: 2 problems + +``` + +## KCL Lint 工具使用方式 + +### CLI 参数 + +``` +usage: kcl-lint [-h] [--config file] [file] + +positional arguments: + file KCL file path + +optional arguments: + -h, --help show this help message and exit + --config file KCL lint config path + +``` + ++ --config : lint 配置文件 `.kcllint` 的路径 ++ file : 需要检查的单个 `.k` 文件路径或路径目录下的所有 `.k` 文件,支持绝对路径或当前目录的相对路径 + +### Lint 配置参数 + +#### 优先级 + +Lint 的配置参数的优先级如下: + +1. CLI 参数中的 `--config file` 路径的 `.kcllint` 文件 +2. 被检查 `.k` 文件所在目录或被检查目录下的 `.kcllint` 文件 +3. 默认配置 + +#### .kcllint + +`.kcllint` 文件以 yaml 格式书写。其内容包括: + +- check_list 用于选择检查的 checker,可选项为 `"import"`、`"misc"`、`"basic"` +- ignore 用于选择忽略的检查项,可选项见错误代码 +- max_line_length 为检查的参数,即单行代码最大长度 +- output 用于选择输出流和输出格式,可选项为 `"stdout"`、`"file"`、`"sarif"` +- output_path 为可选项,当 output 选择了"file"或"sarif",则必须设置输出文件的路径 +- [schema|mixin|argument|variable|schema_attribute]_naming_style 使用内置命名规范检查 +- [schema|mixin|argument|variable|schema_attribute]_RGX 使用自定义正则表达式进行命名规范检查 +- bad_names 禁用的命名 + +示例: + +```yaml +check_list: ["import","misc"] +ignore: ["E0501"] +max_line_length: 120 +output: ["stdout"] +``` + +#### 默认配置 + +```yaml +check_list: [import, misc, basic] +ignore: [] +max_line_length: 200 +output: [stdout] +output_path: null +module_naming_style: ANY +package_naming_style: ANY +schema_naming_style: PascalCase +mixin_naming_style: PascalCase +argument_naming_style: camelCase +variable_naming_style: ANY +schema_attribute_naming_style: ANY +module_rgx: null +package_rgx: null +schema_rgx: null +mixin_rgx: null +argument_rgx: null +variable_rgx: null +schema_attribute_rgx: null +bad_names: [foo, bar, baz, toto, tata, tutu, I, l, O] +``` + +### 检查项及错误代码 + +目前提供 import,misc,和 basic + +- import_checker + + - E0401: Unable to import. + - W0401: Reimport. + - E0406: Module import itself. + - W0411: Import but unused. + - E0413: ImportStmt is not at the top of the file. +- misc_checker + + - E0501: Line too long. +- basic_checker + + - C0103: Invalid-name + - C0104: Disallowed-name. diff --git a/docs/tools/kcl-test/kcl-test.md b/docs/tools/kcl-test/kcl-test.md new file mode 100644 index 000000000..55e0f87f4 --- /dev/null +++ b/docs/tools/kcl-test/kcl-test.md @@ -0,0 +1,173 @@ +# KCL 单元测试工具 + +## 简介 + +KCL 支持通过内置的 `kcl-test` 命令行工具和 `testing` 包提供了简易的测试框架。每个目录下的全部 KCL 文件是一个测试整体,每个 `_test.k` 中 `Test` 开头的 schema 是一个测试案例。 + +## 使用方式 + +假设有 hello.k 文件,代码如下: + +```python +schema Person: + name: str = "kcl" + age: int = 1 + +hello = Person { + name = "hello kcl" + age = 102 +} +``` + +构造 hello_test.k 测试文件,内容如下: + +```python +schema TestPerson: + a = Person{} + assert a.name == 'kcl' + +schema TestPerson_age: + a = Person{} + assert a.age == 1 + +schema TestPerson_ok: + a = Person{} + assert a.name == "kcl" + assert a.age == 1 +``` + +然后再目录下执行 `kcl-test` 命令: + +``` +$ kcl-test +ok /pkg/to/app [365.154142ms] +$ +``` + +## 失败的测试 + +将 hello_test.k 测试代码修改如下,构造失败的测试: + +```python +# Copyright 2021 The KCL Authors. All rights reserved. + +import testing + +schema TestPerson: + a = Person{} + assert a.name == 'kcl2' + +schema TestPerson_age: + a = Person{} + assert a.age == 123 + +schema TestPerson_ok: + a = Person{} + assert a.name == "kcl2" + assert a.age == 1 +``` + +测试输出的错误如下: + +``` +$ kcl-test +FAIL /pkg/to/app [354.153775ms] +---- failed [48.817552ms] + KCL Runtime Error: File /pkg/to/app/hello_test.k:7: + assert a.name == 'kcl2' + Assertion failure +---- failed [47.515009ms] + KCL Runtime Error: File /pkg/to/app/hello_test.k:11: + assert a.age == 123 + Assertion failure +---- failed [47.26677ms] + KCL Runtime Error: File /pkg/to/app/hello_test.k:15: + assert a.name == "kcl2" + Assertion failure +$ +``` + +## 配置 option 参数 + +可以通过 testing 包指定面值类型的命令行参数: + +```python +schema TestOptions: + testing.arguments("name", "ktest") + testing.arguments("age", "123") + testing.arguments("int0", 0) + testing.arguments("float0", 0.0) + testing.arguments("bool-true", True) + testing.arguments("bool-false", False) + + name = option("name") + assert name == "ktest" + + age = option("age") + assert age == 123 + + assert option("int0") == 0 + assert option("float0") == 0.0 + assert option("bool-true") == True + assert option("bool-false") == False +``` + +其中 `testing.arguments` 定义一组 key-value 参数,只有在当前的测试中有效。 + +option 参数也可以从 settings.yaml 文件读取。假设有 `./settings.yaml` 文件如下: + +```yaml + - key: app-name + value: app + - key: env-type + value: prod + - key: image + value: reg.docker.inc.com/test-image +``` + +然后可以通过 `testing.setting_file("./settings.yaml")` 方式配置参数。同时依然支持 `testing.arguments()` 覆盖配置文件中的参数: + +```py +schema TestOptions_setting: + testing.setting_file("./settings.yaml") + testing.arguments("file", "settings.yaml") + + assert option("app-name") == "app" + assert option("file") == "settings.yaml" +``` + +testing.setting_file("settings.yaml") 则是从 yaml 文件加载对应的 key-value 参数。 + +## 测试插件 + +如果要测试的目录含有 `plugin.py` 和测试文件,自动切换到插件模式。那么将测试前设置 `KCL_PLUGINS_ROOT` 环境变量(不能再访问其他目录的插件)用于测试当前插件,测试完成之后恢复之前的 `KCL_PLUGINS_ROOT` 环境变量。 + +## 集成测试 + +目录含有 `*.k` 时自动执行集成测试,如果有 `stdout.golden` 则验证输出的结果,如果有 `stderr.golden` 则验证错误。支持 `settings.yaml` 文件定义命令行参数。 + +如果有 k 文件含有 `# kcl-test: ignore` 标注注释将忽略测试。 + +## 批量测试 + +- `kcl-test path` 执行指定目录的测试, 当前目录可以省略该参数 +- `kcl-test -run=regexp` 可以执行匹配模式的测试 +- `kcl-test ./...` 递归执行子目录的单元测试 + +## 命令行参数 + +``` +$ kcl-test -h +NAME: + kcl-go test - test packages + +USAGE: + kcl-go test [command options] [packages] + +OPTIONS: + --run value Run only those tests matching the regular expression. + --quiet, -q Set quiet mode (default: false) + --verbose, -v Log all tests as they are run (default: false) + --debug, -d Run in debug mode (for developers only) (default: false) + --help, -h show help (default: false) +``` diff --git a/docs/tools/kcl-vet/kcl-vet.md b/docs/tools/kcl-vet/kcl-vet.md new file mode 100644 index 000000000..d10e2ace5 --- /dev/null +++ b/docs/tools/kcl-vet/kcl-vet.md @@ -0,0 +1,77 @@ +# KCL Validation 工具 + +## 简介 + +KCL 支持通过内置的 `kcl-vet` 命令行工具提供了基本的配置数据校验能力,可以编写 KCL schema 对输入的 JSON/YAML 格式文件进行类型以及数值的校验。 + +## 使用方式 + +假设有 data.json 文件,代码如下: + +```json +{ + "name": "Alice", + "age": "18", + "message": "This is Alice", + "data": { + "id": "1", + "value": "value1" + }, + "labels": { + "key": "value" + }, + "hc": [1, 2, 3] +} +``` + +构造 schema.k 校验文件,内容如下: + +```py +schema User: + name: str + age: int + message?: str + data: Data + labels: {str:} + hc: [int] + + check: + age > 10 + +schema Data: + id: int + value: str +``` + +在目录下执行如下命令 + +``` +$ kcl-vet data.json schema.k +Validate succuss! +``` + +## 指定校验的 schema + +当教研的 KCL 文件中存在多个 schema 定义时,kcl-vet 工具会默认取第一个 schema 定义进行校验,如果需要指定校验的 schema,可以使用 `-d|--schema` 参数 + +``` +$kcl-vet data.json schema.k -d User +``` + +## 命令行参数 + +``` +$ kcl-vet -h +usage: kcl-vet [-h] [-d schema] [--format format] [-n attribute_name] + data_file kcl_file + +positional arguments: + data_file Validation data file + kcl_file KCL file + +optional arguments: + -h, --help show this help message and exit + -d schema, --schema schema + --format format Validation data file format, support YAML and JSON + -n attribute_name, --attribute-name attribute_name +``` diff --git a/icons/KCL128x128.png b/icons/KCL128x128.png new file mode 100644 index 000000000..2e1660d7f Binary files /dev/null and b/icons/KCL128x128.png differ diff --git a/icons/KCL128x128.svg b/icons/KCL128x128.svg new file mode 100644 index 000000000..ded49cd57 --- /dev/null +++ b/icons/KCL128x128.svg @@ -0,0 +1,12 @@ + + + + + background + + + + Layer 1 + K + + \ No newline at end of file diff --git a/icons/KCL16x16.png b/icons/KCL16x16.png new file mode 100644 index 000000000..e84bab49d Binary files /dev/null and b/icons/KCL16x16.png differ diff --git a/icons/KCL16x16.svg b/icons/KCL16x16.svg new file mode 100644 index 000000000..761f6496f --- /dev/null +++ b/icons/KCL16x16.svg @@ -0,0 +1,15 @@ + + + + background + + + + + + + Layer 1 + K + + \ No newline at end of file diff --git a/icons/KCL40x40.png b/icons/KCL40x40.png new file mode 100644 index 000000000..7fa6866c3 Binary files /dev/null and b/icons/KCL40x40.png differ diff --git a/icons/KCL40x40.svg b/icons/KCL40x40.svg new file mode 100644 index 000000000..390bb86ae --- /dev/null +++ b/icons/KCL40x40.svg @@ -0,0 +1,12 @@ + + + + + background + + + + Layer 1 + K + + \ No newline at end of file diff --git a/internal/kclvm_py/Makefile b/internal/kclvm_py/Makefile new file mode 100644 index 000000000..5a19ae925 --- /dev/null +++ b/internal/kclvm_py/Makefile @@ -0,0 +1,8 @@ +default: + +gen: + go run 0_gen.go > z_embed.go + -go fmt + go test + +clean: diff --git a/internal/kclvm_py/README.md b/internal/kclvm_py/README.md new file mode 100644 index 000000000..f0ee47045 --- /dev/null +++ b/internal/kclvm_py/README.md @@ -0,0 +1,3 @@ +# KCLVM-Python + +KCLVM Python implementation, will be abandoned soon, please do not submit any code to it, it will be reorganized in the way of KCLVM Python SDK in the future. diff --git a/internal/kclvm_py/__init__.py b/internal/kclvm_py/__init__.py new file mode 100644 index 000000000..d74aaf8ad --- /dev/null +++ b/internal/kclvm_py/__init__.py @@ -0,0 +1 @@ +# Copyright 2021 The KCL Authors. All rights reserved. diff --git a/internal/kclvm_py/api/object/__init__.py b/internal/kclvm_py/api/object/__init__.py new file mode 100644 index 000000000..b6d5b552a --- /dev/null +++ b/internal/kclvm_py/api/object/__init__.py @@ -0,0 +1,53 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .object import * +from .function import * +from .schema import * +from .decorator import * +from .bytecode import * + +__all__ = [ + "KCLObject", + "KCLBaseTypeObject", + "KCLLiteralObject", + "KCLIntObject", + "KCLFloatObject", + "KCLStringObject", + "KCLNameConstantObject", + "KCLTrueObject", + "KCLFalseObject", + "KCLNoneObject", + "KCLBuiltinTypeObject", + "KCLListTypeObject", + "KCLDictTypeObject", + "KCLUnionTypeObject", + "KCLSchemaTypeObject", + "KCLNumberMultiplierTypeObject", + "KCLSchemaObject", + "KCLSchemaIndexSignatureObject", + "KCLDictObject", + "KCLListObject", + "KCLClosureObject", + "KCLFunctionObject", + "KCLBuiltinFunctionObject", + "KCLMemberFunctionObject", + "KCLCompiledFunctionObject", + "KCLUnpackObject", + "KCLSliceObject", + "KCLObjectType", + "KCLIterObject", + "KCLModuleObject", + "KCLDecoratorObject", + "KWArg", + "SCHEMA_SELF_VALUE_KEY", + "SCHEMA_CONFIG_VALUE_KEY", + "Parameter", + "KCLFunctionTypeObject", + "to_kcl_obj", + "to_python_obj", + "KCLBytecode", + "KCLProgram", + "KCLResult", + "KCLTypeKind", + "SchemaTypeRefGraph", +] diff --git a/internal/kclvm_py/api/object/bytecode.py b/internal/kclvm_py/api/object/bytecode.py new file mode 100644 index 000000000..77d174c92 --- /dev/null +++ b/internal/kclvm_py/api/object/bytecode.py @@ -0,0 +1,63 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing + +from .object import ( + KCLObject, + to_python_obj, + to_kcl_obj, +) +import kclvm.api.object.internal as internal + + +class KCLResult: + def __init__( + self, m: typing.Dict[str, KCLObject], filename: typing.Optional[str] = None + ): + self.m: typing.Dict[str, KCLObject] = m + self.filename: str = filename + + def __str__(self) -> str: + return f"{self.m}" + + def filter_by_path_selector( + self, to_kcl: bool = True + ) -> typing.Dict[str, KCLObject]: + if not internal.is_selector_mode(): + return self.m + selector_index = internal.build_selector_index() + filtered_result = {} + for k, v in self.m.items(): + if k in selector_index: + select_data = internal.select_instance_attributes( + to_python_obj(self.m[k]), selector_index[k] + ) + filtered_result[k] = to_kcl_obj(select_data) if to_kcl else select_data + self.m = filtered_result or self.m + return self.m + + +class KCLBytecode: + def __init__( + self, + *, + names: typing.List[str] = None, + constants: typing.List[KCLObject] = None, + instructions: typing.List[int] = None, + ): + self.names: typing.List[str] = names if names else [] + self.constants: typing.List[KCLObject] = constants if constants else [] + self.instructions: typing.List[int] = instructions if instructions else [] + + +class KCLProgram: + def __init__( + self, + *, + root: str = "", + main: str = "", + pkgs: typing.Dict[str, KCLBytecode] = None, + ): + self.root: str = root if root else "" + self.main: str = main if main else "" + self.pkgs: typing.Dict[str, KCLBytecode] = pkgs if pkgs else {} diff --git a/internal/kclvm_py/api/object/decorator.py b/internal/kclvm_py/api/object/decorator.py new file mode 100644 index 000000000..43202e476 --- /dev/null +++ b/internal/kclvm_py/api/object/decorator.py @@ -0,0 +1,58 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import List, Any +from dataclasses import dataclass + +from kclvm.api.object.internal import decorator_factory, Decorator, DecoratorTargetType + +from .object import ( + KCLObject, + KCLObjectType, + to_python_obj, + to_kcl_obj, +) +from .function import ( + KCLFunctionObject, + KWArg, +) + + +@dataclass +class KCLDecoratorObject(KCLFunctionObject): + target: DecoratorTargetType + name: str + key: str = "" + value: Any = None + decorator: Decorator = None + + def resolve(self, args: List[KCLObject], kwargs: List[KWArg]): + """Build a internal decorator object""" + args = to_python_obj(args) + kwargs = to_python_obj({kw.name.value: kw.value for kw in kwargs}) + self.decorator = decorator_factory.get(self.name, self.target, *args, **kwargs) + return self + + def call( + self, args: List[KCLObject], kwargs: List[KWArg], vm=None, key=None, value=None + ) -> KCLObject: + """Decorator run""" + self.key = to_python_obj(key) + self.value = to_python_obj(value) + return to_kcl_obj( + self.decorator.run( + self.key, + self.value if self.target == DecoratorTargetType.ATTRIBUTE else None, + ) + ) + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.DECORATOR + + def type_str(self) -> str: + """ + Get the object type + """ + return "decorator" diff --git a/internal/kclvm_py/api/object/function.py b/internal/kclvm_py/api/object/function.py new file mode 100644 index 000000000..62d45cb6b --- /dev/null +++ b/internal/kclvm_py/api/object/function.py @@ -0,0 +1,181 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from abc import abstractmethod +from typing import Callable, List, Optional +from dataclasses import dataclass + +from .object import ( + ANT_TYPE_STR, + KCLObject, + KCLListObject, + KCLObjectType, + KCLBaseTypeObject, + KCLTypeKind, + to_python_obj, + to_kcl_obj, +) + +BUILTIN_KCL_OBJ_FUNCTIONS = [ + "typeof", +] + + +@dataclass +class KWArg: + """KCL function call arguments + + Parameters + ---------- + - name: str + - value: KCLObject + """ + + name: KCLObject + value: KCLObject + + +@dataclass +class Parameter: + """KCL function definition parameter + + Parameters + ---------- + - name: str + The argument name + - value: KCLObject + Value is the default value to use if the argument is not provided. + If no default is specified then value is None. + - type_annotation: str + The argument type annotation. + """ + + name: str = None + value: Optional[KCLObject] = None + type_annotation: Optional[str] = None + type: Optional[KCLBaseTypeObject] = None + + def param_doc(self) -> str: + _type_str = self.type.type_str() if self.type else "any" + _value_doc = "" + if self.value: + _value_str = ( + f'"{self.value.value}"' + if isinstance(self.value.value, str) + else f"{self.value.value}" + ) + _value_doc = f"={_value_str}" + return f"{self.name}: {_type_str}{_value_doc}" + + +@dataclass +class KCLFunctionObject(KCLObject): + name: str + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.FUNCTION + + def type_str(self) -> str: + """ + Get the object type + """ + return "function" + + @abstractmethod + def call(self, args: List[KCLObject], kwargs: List[KWArg], vm=None) -> KCLObject: + pass + + +@dataclass +class KCLBuiltinFunctionObject(KCLFunctionObject): + function: Callable + + def call(self, args: List[KCLObject], kwargs: List[KWArg], vm=None) -> KCLObject: + if not self.function: + raise Exception("invalid kcl function object") + + if self._is_kcl_obj_builtin(): + return to_kcl_obj( + self.function(*args, **{kw.name.value: kw.value for kw in kwargs}) + ) + + return to_kcl_obj( + self.function( + *to_python_obj(args), + **to_python_obj({kw.name.value: kw.value for kw in kwargs}), + ) + ) + + def _is_kcl_obj_builtin(self) -> bool: + return self.name in BUILTIN_KCL_OBJ_FUNCTIONS + + +@dataclass +class KCLMemberFunctionObject(KCLFunctionObject): + obj: KCLObject + + def call(self, args: List[KCLObject], kwargs: List[KWArg], vm=None) -> KCLObject: + return to_kcl_obj( + self.obj.call_member_method( + self.name, + *to_python_obj(args), + **to_python_obj({kw.name.value: kw.value for kw in kwargs}), + ) + ) + + +@dataclass +class KCLCompiledFunctionObject(KCLFunctionObject): + instructions: list = None + names: list = None + constants: list = None + num_parameters: int = 0 + num_locals: int = 0 + params: List[Parameter] = None + pkgpath: str = None + closure: KCLListObject = None + + +@dataclass +class KCLClosureObject(KCLObject): + name: str = None + parameters: List[str] = None + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.CLOSURE + + def type_str(self) -> str: + """ + Get the object type + """ + return "closure" + + +@dataclass +class KCLFunctionTypeObject(KCLBaseTypeObject): + name: str + params: List[Parameter] + self_type: Optional[KCLBaseTypeObject] + return_type: KCLBaseTypeObject + doc: Optional[str] = None + kwonlyargs_index: int = None + is_variadic: bool = False + + def type_str(self) -> str: + return "function(({}) -> {})".format( + ", ".join( + [ + p.type.type_str() if p.type else ANT_TYPE_STR + for p in self.params or [] + ] + ), + self.return_type.type_str() if self.return_type else ANT_TYPE_STR, + ) + + def type_kind(self) -> str: + return KCLTypeKind.FuncKind diff --git a/internal/kclvm_py/api/object/internal/__init__.py b/internal/kclvm_py/api/object/internal/__init__.py new file mode 100644 index 000000000..3c83e9176 --- /dev/null +++ b/internal/kclvm_py/api/object/internal/__init__.py @@ -0,0 +1,44 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .option import ( + kcl_option, + kcl_option_exec, + kcl_option_init_all, + kcl_option_init, + kcl_option_reset, + kcl_option_check, +) +from .decorators import ( + decorator_factory, + Decorator, + DecoratorTargetType, + Deprecated, + Info, +) +from .selector import select +from .undefined import Undefined, UndefinedType +from .path_selector import ( + is_selector_mode, + build_selector_index, + select_instance_attributes, +) + +__all__ = [ + "kcl_option", + "kcl_option_exec", + "kcl_option_init_all", + "kcl_option_init", + "kcl_option_reset", + "kcl_option_check", + "select", + "is_selector_mode", + "build_selector_index", + "select_instance_attributes", + "decorator_factory", + "Decorator", + "DecoratorTargetType", + "Deprecated", + "Info", + "Undefined", + "UndefinedType", +] diff --git a/internal/kclvm_py/api/object/internal/common.py b/internal/kclvm_py/api/object/internal/common.py new file mode 100644 index 000000000..78c2216c7 --- /dev/null +++ b/internal/kclvm_py/api/object/internal/common.py @@ -0,0 +1,228 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import re + +import kclvm.kcl.error as kcl_error + +DICT_TYPE_NAME = "dict" +LIST_TYPE_NAME = "list" +SCHEMA_TYPE_NAME = "Schema" +CLASS_TYPE_TMPL = "" +builtin_types = ["str", "int", "float", "bool"] + + +def isdicttype(expectedtype): + if isinstance(expectedtype, str) and len(expectedtype) >= 2: + return expectedtype[0] == "{" and expectedtype[-1] == "}" + else: + return False + + +def islisttype(expectedtype): + if isinstance(expectedtype, str) and len(expectedtype) >= 2: + return expectedtype[0] == "[" and expectedtype[-1] == "]" + else: + return False + + +def separate_kv(expectedtype): + stack = "" + n = 0 + try: + for c in expectedtype: + if c == "[" or c == "{": + stack += c + elif c == "]": + if stack[-1] != "[": + raise + stack = stack[:-1] + elif c == "}": + if stack[-1] != "{": + raise + stack = stack[:-1] + elif c == ":": + if len(stack) != 0: + raise + return expectedtype[:n], expectedtype[n + 1 :] + n += 1 + except Exception: + return None, None + return "", "" + + +def dereferencetype(expectedtype): + if ( + len(expectedtype) > 1 + and (expectedtype[0] == "[" and expectedtype[-1] == "]") + or (expectedtype[0] == "{" and expectedtype[-1] == "}") + ): + return expectedtype[1:-1] + return expectedtype + + +def split_type_union(type_union: str): + """ + Split the union type e.g. 'A|B|C' -> ['A', 'B', 'C'], do not split '|' in dict and list + """ + i = 0 + s_index = 0 + stack = [] + types = [] + while i < len(type_union): + c = type_union[i] + if c == "|" and len(stack) == 0: + types.append(type_union[s_index:i]) + s_index = i + 1 + # List/Dict type + if c == "[" or c == "{": + stack.append(c) + # List/Dict type + if c == "]" or c == "}": + stack.pop() + # String literal type + if c == '"': + matched = re.match(r'"(?!"").*?(? bool: + """Whether a type string is a union type string, e.g. A|B|C, + and detect '|' in type string except '|' in dict or list. + """ + stack = [] + i = 0 + while i < len(tpe or ""): + c = tpe[i] + if c == "|" and not stack: + return True + if c in "[{": + stack.append(c) + if c in "]}": + stack.pop() + if c == '"': + matched = re.match(r'"(?!"").*?(?", tpe_str).group(1) + else: + return tpe_str + + +def get_builtin_type(tpe_str): + if "kclvm_runtime.builtins" in tpe_str: + return re.match(r"kclvm_runtime.builtins.(.*)", tpe_str).group(1) + else: + return tpe_str + + +def demangle_type(tpe: str, var=None): + if not tpe: + return tpe + if isdicttype(tpe): + key_tpe, value_tpe = separate_kv(dereferencetype(tpe)) + return "{{{}:{}}}".format(demangle_type(key_tpe), demangle_type(value_tpe)) + elif islisttype(tpe): + return "[{}]".format(demangle_type(dereferencetype(tpe))) + else: + return get_builtin_type(tpe) + + +def do_union(obj, delta): + """ + Union delta to obj recursively + """ + obj_tpe = get_class_name(str(type(obj))) + delta_tpe = get_class_name(str(type(delta))) + if isinstance(obj, list): + if not isinstance(delta, list): + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + arg_msg="union failure, expect list, got {}".format( + demangle_type(delta_tpe, delta) + ), + ) + length = len(obj) if len(obj) > len(delta) else len(delta) + result_list = obj + for idx in range(length): + if idx >= len(obj): + result_list.append(delta[idx]) + elif idx < len(delta): + result_list[idx] = union(result_list[idx], delta[idx]) + return result_list + if isinstance(obj, dict): + if not isinstance(delta, dict): + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + arg_msg="union failure, expect dict, got {}".format( + demangle_type(delta_tpe, delta) + ), + ) + result_dict = obj + for k in delta: + if k not in obj: + result_dict[k] = delta[k] + else: + result_dict[k] = union(obj[k], delta[k]) + return result_dict + if type(obj) != type(delta): + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + arg_msg="union failure, expect {}, got {}".format( + demangle_type(obj_tpe), demangle_type(delta_tpe, delta) + ), + ) + return delta + + +def union(obj, delta, or_mode=False): + if obj is None: + return delta + if delta is None: + return obj + if isinstance(obj, (list, dict)) or isinstance(delta, (list, dict)): + return do_union(obj, delta) + if or_mode: + return obj | delta + else: + return obj if delta is None else delta diff --git a/internal/kclvm_py/api/object/internal/decorators.py b/internal/kclvm_py/api/object/internal/decorators.py new file mode 100644 index 000000000..a48811aeb --- /dev/null +++ b/internal/kclvm_py/api/object/internal/decorators.py @@ -0,0 +1,255 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import sys +import enum +from abc import ABCMeta, abstractmethod +from typing import Dict, Type + +import kclvm.kcl.error as kcl_error + +DECORATOR_TARGET_ERR_NAME_MSG = ": Decorator target name cannot be empty" + + +class DecoratorTargetType(enum.Enum): + """ + Marked annotated position by the decorator + """ + + SCHEMA_TYPE = 0 + ATTRIBUTE = 1 + + +class Decorator(metaclass=ABCMeta): + """ + An abstract decorator. + + This abc is used to run actions as a wrapper of key-value pair in kcl schema. + + A concrete decorator should inherit this class and impel the run method to handle key-value. + + :class:`~Deprecated` class is a sample of decorator which is used to check deprecation of key on config. + + you can use your ``Deprecated`` class like this in your source code as follows: + + .. code-block:: python + + schema Person: + @deprecated(version="1.16", reason="use firstName and lastName instead", strict=True) + name : str + + .. code-block:: python + + @deprecated(version="1.16", reason="use firstName and lastName instead", strict=True) + schema Person: + name : str + + """ + + def __init__(self, name: str, target: DecoratorTargetType, *args, **kwargs): + self.name = name + try: + self.target = ( + target + if isinstance(target, DecoratorTargetType) + else DecoratorTargetType(target) + ) + except ValueError: + msg = ( + kcl_error.INVALID_DECORATOR_TARGET_MSG.format(target) + if target + else kcl_error.INVALID_DECORATOR_TARGET_MSG.format("") + ) + kcl_error.report_exception( + err_type=kcl_error.ErrType.InvalidDecoratorTarget_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=kwargs.get("filename"), + line_no=kwargs.get("lineno"), + col_no=kwargs.get("columnno"), + ) + ], + arg_msg=msg, + ) + self.filename = kwargs.get("filename") + self.lineno = kwargs.get("lineno") + self.columnno = kwargs.get("columnno") + + @abstractmethod + def run(self, key: str, value, *args, **kwargs): + """ + Decorate on a key-value pair in runtime. + + :param key: Key to Decorated + :param value: Value to Decorated + :param args: Decorator run positional args + :param kwargs: Decorator run keyword args + :return: + """ + pass + + +class DecoratorFactory: + """ + A decorator factory used to get decorator object. + """ + + def __init__(self): + self._decorators: Dict[str, Type[Decorator]] = {} + + def register(self, name: str, decorator: Type[Decorator]): + """ + Register a decorator with a unique name. + + :param name: Name of the decorator + :param decorator: The decorator to be registered + :return: None + """ + self._decorators[name] = decorator + + def get(self, name: str, target: DecoratorTargetType, *args, **kwargs): + """ + Get and return a decorator object. + An UnKnownDecorator will be thrown if no decorator found. + + :param name: Name of the decorator + :param target: Target of the decorator e.g., schema and attribute + :param args: Decorator meta info positional args + :param kwargs: Decorator meta info keyword args + :return: A decorator object + """ + decorator = self._decorators.get(name) + if not decorator: + filename = kwargs.get("filename") + lineno = kwargs.get("lineno") + columnno = kwargs.get("columnno") + kcl_error.report_exception( + err_type=kcl_error.ErrType.UnKnownDecorator_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=lineno, col_no=columnno + ) + ], + arg_msg=kcl_error.UNKNOWN_DECORATOR_MSG.format(name) + if name + else kcl_error.UNKNOWN_DECORATOR_MSG.format(""), + ) + return decorator(name, target, *args, **kwargs) + + +class Deprecated(Decorator): + """This decorator is used to get the deprecation message according to the wrapped key-value pair. + + Examples + -------- + @deprecated(version="v1.16", reason="The age attribute was deprecated", strict=True) + schema Person: + name: str + age: int + """ + + NAME = "deprecated" + + def __init__(self, name, target, *args, **kwargs): + """ + Construct a deprecated decorator + + :param args: Deprecated decorator build positional args + :param kwargs: Deprecated decorator build keyword args + """ + super().__init__(self.NAME, target, *args, **kwargs) + self.version = kwargs.get("version", "") + self.reason = kwargs.get("reason", "") + self.strict = kwargs.get("strict", True) + + def run(self, key: str, value, *args, **kwargs): + """ + Build and report deprecation message. + + A KCL runtime error will be thrown if self.strict is True, otherwise it just print warning message. + + :param key: Key to Deprecated decorated + :param value: Value to Deprecated decorated + :param args: Deprecated decorator run positional args + :param kwargs: Deprecated decorator run positional args + :return should_change_value: Mark the value of the deprecated schema attribute should be modified + """ + filename = self.filename + lineno = self.lineno + columnno = self.columnno + if not key: + kcl_error.report_exception( + err_type=kcl_error.ErrType.NameError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=lineno, col_no=columnno + ) + ], + arg_msg=kcl_error.NAME_ERROR_MSG.format(DECORATOR_TARGET_ERR_NAME_MSG), + ) + # Mark the value of the deprecated schema attribute should be modified + should_change_value = False + # Error or warning message + msg = "" + # Append a version info into message + if self.version: + msg += "since version {}".format(self.version) + # Append a reason info into message + if self.reason: + msg += ", " + self.reason + if self.strict: + if self.target == DecoratorTargetType.SCHEMA_TYPE or ( + self.target == DecoratorTargetType.ATTRIBUTE and value + ): + kcl_error.report_exception( + err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=lineno, col_no=columnno + ) + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format(key, msg), + ) + should_change_value = True + else: + # If it is a modified schema attribute, ignore the assignment without reporting an error. + kcl_error.print_kcl_warning_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.Deprecated_Warning_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=lineno, col_no=columnno + ) + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format(key, msg), + ), + file=sys.stderr, + ) + should_change_value = False + return should_change_value + + +class Info(Decorator): + """Info decorator is used to mark some compile-time information for external API queries + + Examples + -------- + @info(message="User message") + schema Person: + name: str + age: int + """ + + NAME = "info" + + def __init__(self, name, target, *args, **kwargs): + """Construct a Info decorator""" + super().__init__(self.NAME, target, *args, **kwargs) + + def run(self, key: str, value, *args, **kwargs): + """Nothing to do on Info decorator""" + pass + + +decorator_factory = DecoratorFactory() +decorator_factory.register(Deprecated.NAME, Deprecated) +decorator_factory.register(Info.NAME, Info) diff --git a/internal/kclvm_py/api/object/internal/option.py b/internal/kclvm_py/api/object/internal/option.py new file mode 100644 index 000000000..561db4a56 --- /dev/null +++ b/internal/kclvm_py/api/object/internal/option.py @@ -0,0 +1,292 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing +import ast +import os + +import kclvm.kcl.error as kcl_error +import kclvm.internal.util as util +import kclvm.config + + +class KclOptionElem: + def __init__(self, name: str): + self.name = name + self.defined = False + self.value_type = "" # bool/int/float/str/list/dict + self.required = False + self.default = None + self.help = "" + self.file = "" + self.line = 0 + + self.inited = False + self.value = None + + def get_help(self, verbose_mode=0) -> str: + name = self.name + default = self.default if self.default else "?" + + type_and_required = "" + if self.value_type != "" and self.required: + type_and_required = f" ({self.value_type},required)" + elif self.value_type != "": + type_and_required = f" ({self.value_type})" + elif self.required: + type_and_required = " (required)" + + if verbose_mode > 1 and self.file and self.line > 0: + filename = os.path.relpath(self.file, os.getcwd()) + return f"{name}={default}{type_and_required} {self.help} ({filename}:{self.line})".strip() + else: + return f"{name}={default}{type_and_required} {self.help}".strip() + + def __str__(self) -> str: + return self.get_help() + + +class _KclOptionDict: + def __init__(self): + self.m: typing.Dict[str, KclOptionElem] = {} # map[name]KclOptionElem + self.reset() + + def reset(self): + self.m = {} # map[name]KclOptionElem + + def len(self) -> int: + return len(self.m) + + def get_dict(self): + return self.m # map[name]KclOptionElem + + def keys(self) -> list: + return list(self.m.keys()) + + def has_key(self, name: str) -> bool: + return name in self.m + + @classmethod + def _check_value_type(cls, value_type: str, value: typing.Any) -> bool: + if not value_type or value is None: + return True + return cls._get_typed_value(value_type, value) is not None + + # noinspection PyBroadException + @classmethod + def _get_typed_value(cls, value_type: str, value) -> typing.Any: + if not value_type: + return value + if value is None: + return None + + if value_type == "bool": + return True if str(value).lower() in ("yes", "true", "1") else bool(value) + if value_type == "int": + try: + return int(value) + except Exception: + return None + if value_type == "float": + try: + return float(value) + except Exception: + return None + if value_type == "str": + return str(value) + if value_type == "list": + if isinstance(value, list): + return value + try: + result = ast.literal_eval(value) + return result if type(result) is list else None + except Exception: + return None + if value_type == "dict": + if isinstance(value, dict): + return value + try: + result = ast.literal_eval(value) + return result if type(result) is dict else None + except Exception: + return None + + # unknown type + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"unknown type: {value_type}", + ) + + def option( + self, + name: str, + *, + value_type="", + required=False, + default=None, + help="", + file="", + line=0, + ) -> typing.Any: + + if value_type and value_type not in [ + "bool", + "int", + "float", + "str", + "list", + "dict", + ]: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"'{value_type}' must one of bool/int/float/str/list/dict or empty string", + ) + + opt = self.get(name) + opt.defined = True + + if value_type: + opt.value_type = value_type + if required: + opt.required = required + if default is not None: + opt.default = default + if help: + opt.help = help + if file: + opt.file = file + if line > 0: + opt.line = line + + if opt.value is None: + opt.value = opt.default + + if value_type and opt.value is not None: + raw_value = opt.value + opt.value = self._get_typed_value(value_type, opt.value) + if opt.value is None: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"cannot use '{raw_value}' as type '{value_type}'", + ) + + self.m[name] = opt + kcl_option_exec() # FIXME: filename, lineno, colno info + return opt.value # Do type conversion? + + def init_value(self, *d_list, name: str = "", value=None, **attrs): + def _init_kv(name_: str, value_: str): + opt = self.get(name_) + opt.value = value_ + opt.inited = True + self.m[name_] = opt + + for d in d_list: + kv = d.split("=") + if len(kv) != 2 or not kv[0]: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"invalid args: {d}", + ) + _init_kv(kv[0], kv[1]) + + for name, value in attrs.items(): + _init_kv(name, value) + + if name: + _init_kv(name, value) + + def get(self, name: str) -> KclOptionElem: + if name in self.m: + return self.m[name] + else: + return KclOptionElem(name) + + def check(self, *, check_required=True, check_none=False, verbose_mode=0): + for name, v in self.m.items(): + if verbose_mode > 1 and not v.defined: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[kcl_error.ErrFileMsg(line_no=1)], + arg_msg=f"invalid '-D {name}={v.value}', option('{name}') undefined", + ) + if check_required and v.required and (not v.inited): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[kcl_error.ErrFileMsg(filename=v.file, line_no=v.line)], + arg_msg=f"option('{name}') must be initialized, try '-D {name}=?' argument", + ) + if check_none and not v.value: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[kcl_error.ErrFileMsg(filename=v.file, line_no=v.line)], + arg_msg=f"option('{name}') is None", + ) + if not self._check_value_type(v.value_type, v.value): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[kcl_error.ErrFileMsg(line_no=1)], + arg_msg=f"cannot use '-D {name}={v.value}' as type {v.value_type}", + ) + + return None + + def help(self, *, prefix: str = "", verbose_mode=0) -> str: + if len(self.m) == 0: + return "" + + msg = "option list:\n" + for name in self.keys(): + msg += f"{prefix}{self.get(name).get_help(verbose_mode)}\n" + return msg[:-1] # remove \n + + +_kcl_option_dict = _KclOptionDict() + + +def kcl_option_dict(): + return _kcl_option_dict.get_dict() + + +def kcl_option_help(verbose_mode=0) -> str: + return _kcl_option_dict.help(prefix=" -D ", verbose_mode=verbose_mode) + + +def kcl_option_check(verbose_mode=0): + _kcl_option_dict.check(verbose_mode=verbose_mode) + + +def kcl_option_init(*args, name: str = "", value=None, **attrs): + _kcl_option_dict.init_value(*args, name=name, value=value, **attrs) + + +def kcl_option( + name: str, *, type="", required=False, default=None, help="", file="", line=0 +) -> typing.Any: + return _kcl_option_dict.option( + name, + value_type=type, + required=required, + default=default, + help=help, + file=file, + line=line, + ) + + +def kcl_option_reset(): + _kcl_option_dict.reset() + + +def kcl_option_exec(): + if kclvm.config.list_option_mode > 0: + kclvm.config.options_help_message = kcl_option_help( + verbose_mode=kclvm.config.list_option_mode + ) + else: + kcl_option_check() + + +def kcl_option_init_all(): + if kclvm.config.arguments: + for _k, _v in util.merge_option_same_keys(kclvm.config.arguments).items(): + kcl_option_init(name=_k, value=_v) diff --git a/internal/kclvm_py/api/object/internal/path_selector.py b/internal/kclvm_py/api/object/internal/path_selector.py new file mode 100644 index 000000000..2defaccbf --- /dev/null +++ b/internal/kclvm_py/api/object/internal/path_selector.py @@ -0,0 +1,164 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import Union, Tuple, Optional, List + +import kclvm.kcl.error as kcl_error +import kclvm.config +from kclvm.internal.util import dotdict + +from .common import union +from .selector import select + +SELECT_ALL_SYMBOL = "*" +SELECT_INDEX_LEFT_SYMBOL = "[" +SELECT_INDEX_RIGHT_SYMBOL = "]" +SELECT_KEYS_LEFT_SYMBOL = "{" +SELECT_KEYS_RIGHT_SYMBOL = "}" +SELECT_KEYS_SPLIT_SYMBOL = "," + + +def build_selector_index() -> dict: + """Build path selector index map""" + + return _build_selector_index(kclvm.config.path_selector) + + +def _build_selector_index(path_selector: List[List[str]]): + """ + Build selector index with given path selector: [pkg, name.attr1.attr2] + + - single element: name.name1.name2 + - elements: name.{name1,name2}.name, notice that there is no space between name1 and Name2 + - all elements: name.*.name + - list indices: name.[0].name + + :param filename: + :param path_selector: + :return: index with format: {name: {attr1: {attr2: ... attrn: {}}}} + """ + identifiers = list( + map( + lambda select_item: list(map(eval_py_data, select_item[1].split("."))), + path_selector, + ) + ) + select_index = dotdict() + for identifier in identifiers: + index = select_index + for name in identifier: + if not index.get(name): + index[name] = dotdict() + index = index[name] + return select_index + + +def eval_py_data(s): + if isinstance(s, str) and s.isnumeric(): + import ast + + return ast.literal_eval(s) + return s + + +def is_selector_mode() -> bool: + """Mark whether is path selector mode""" + return len(kclvm.config.path_selector) > 0 + + +def parse_selector( + select_value: Union[str, int, float] +) -> Tuple[bool, Optional[int], list]: + """Parse input selector string to selector conditions + + Returns: + is_all: bool + index: Optional[int] + keys: List[str] + """ + # If the selector is numeric, it is a key + if isinstance(select_value, (int, float)): + return False, None, [select_value] + # Case 1: all element selector a.* + is_all = len(select_value) == 1 and select_value[0] == SELECT_ALL_SYMBOL + index = None + keys = [] + # Case 2: index selector a.[0] + is_may_index = ( + len(select_value) > 2 + and select_value[0] == SELECT_INDEX_LEFT_SYMBOL + and select_value[-1] == SELECT_INDEX_RIGHT_SYMBOL + ) + if is_may_index: + try: + index = int(select_value[1:-1]) + except Exception: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg="invalid path selector value {}".format(select_value), + ) + is_may_keys = ( + len(select_value) > 2 + and select_value[0] == SELECT_KEYS_LEFT_SYMBOL + and select_value[-1] == SELECT_KEYS_RIGHT_SYMBOL + ) + # Case 3: keys selector a.{key1, key2} + if is_may_keys: + keys += select_value[1:-1].split(SELECT_KEYS_SPLIT_SYMBOL) + # Case 4: single selector a.b.c + if not is_all and index is None and not keys: + keys.append(select_value) + return is_all, index, keys + + +def select_instance_attributes(instance, attrs): + """ + Select attributes from instance with dot selector, like a.b.c + + - single element: name.name1.name2 + - elements: name.{name1, name2}.name + - all elements: name.*.name + - list indices: name.[0].name + + :param instance: instance to select attributes + :param attrs: attributes used to select, format: {attr1: {attr2: ... attrn: {}}} + :return: + """ + + def select_instance_attribute(instance, attrs): + selected = {} + for attr in attrs: + attr_name = attr + # 1. Parse path selector including [0], *, {key1, key2, ...} and key + is_all, index, keys = parse_selector(attr_name) + # 2. Select value according path selector value + select_result = select(instance, is_all, index, keys) + if not select_result: + return None + select_result = select_result[keys[0]] if len(keys) == 1 else select_result + # 3. Sub select result if more attr to select + sub_select_result = {} + # 4. Get the sub path select result + if attrs.get(attr): + sub_select_result = select_instance_attribute( + select_result, + attrs[attr], + ) + # Dict/Schema selector + final_result = sub_select_result if sub_select_result else select_result + if not isinstance(instance, list): + if len(keys) == 1: + selected[keys[0]] = final_result + else: + selected = union(selected, final_result) + # List selector + else: + selected = final_result + return selected + + # Select self + if not attrs: + return instance + + result = select_instance_attribute(instance, attrs) + # No select result, return None + return result or None diff --git a/internal/kclvm_py/api/object/internal/selector.py b/internal/kclvm_py/api/object/internal/selector.py new file mode 100644 index 000000000..8c126ba3d --- /dev/null +++ b/internal/kclvm_py/api/object/internal/selector.py @@ -0,0 +1,309 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import Optional, Union, List, Callable +from abc import ABCMeta, abstractmethod + +import kclvm.kcl.info as kcl_info +import kclvm.kcl.error as kcl_error + +from .common import ( + LIST_TYPE_NAME, + DICT_TYPE_NAME, + SCHEMA_TYPE_NAME, +) + +SCHEMA_SETTINGS_ATTR_NAME = "__settings__" +SELECTOR_ERROR = "SelectorError" +SELECTOR_EMPTY_VAR_ERROR_MSG = "selector expression variable can't be empty" +SELECTOR_INVALID_VAR_TYPE_ERROR_MSG = ( + "invalid selector expression variable type, expected list, dict and Schema" +) +SELECTOR_INVALID_EXPR_ERROR_MSG = "invalid selector expression" +SELECTOR_INVALID_USE_ERROR_MSG = "{} variable can't be used with {} selector expression" +SELECTOR_INVALID_CONDITION_ERROR_MSG = "invalid selector expression lambda condition" + + +SelectorVar = Union[list, dict] + + +class Selector(metaclass=ABCMeta): + """ + Selector expression interface + """ + + def __init__(self, data: SelectorVar) -> None: + self.data: SelectorVar = data + + @abstractmethod + def get_all(self) -> Optional[SelectorVar]: + """ + Get all child items form self.data + """ + pass + + @abstractmethod + def get_by_index(self, index: int) -> Optional[SelectorVar]: + """ + Get all child items by index + """ + pass + + @abstractmethod + def get_by_keys(self, keys: List[str]) -> Optional[SelectorVar]: + """ + Get all child items form self.data when child key is in keys + """ + pass + + @abstractmethod + def get_item_by_condition(self, condition: Callable) -> Optional[SelectorVar]: + """ + Get all child items form self.data when child meets the condition + """ + pass + + def get_var(self) -> SelectorVar: + """ + Get data variable + """ + return self.data + + def select( + self, + is_all: bool = False, + index: Optional[int] = None, + keys: List[str] = None, + condition: Callable = None, + ) -> Optional[SelectorVar]: + if is_all: + return self.get_all() + if index is not None: + return self.get_by_index(index) + if keys: + return self.get_by_keys(keys) + if condition and callable(condition): + return self.get_item_by_condition(condition) + kcl_error.report_exception( + err_type=kcl_error.ErrType.SelectorError_TYPE, + arg_msg=SELECTOR_INVALID_EXPR_ERROR_MSG, + ) + + @staticmethod + def _handle_collection( + result: Optional[Union[list, dict]] + ) -> Optional[Union[list, dict]]: + """ + If the result len is zero, return None, else return itself + """ + return result if result else None + + +class ListSelector(Selector): + """ + List selector expression inherited from Selector + """ + + def __init__(self, data: SelectorVar) -> None: + super().__init__(data) + + def get_all(self) -> SelectorVar: + """ + Get all child items form self.data + """ + return self.get_var() + + def get_by_index(self, index: int) -> Optional[SelectorVar]: + """ + Get all child items by index + """ + var = self.get_var() + return var[index] if -len(var) <= index < len(var) else None + + def get_by_keys(self, keys: List[str]) -> SelectorVar: + """ + Get all child items form self.data when child key is in keys + """ + kcl_error.report_exception( + err_type=kcl_error.ErrType.SelectorError_TYPE, + arg_msg=SELECTOR_INVALID_USE_ERROR_MSG.format( + LIST_TYPE_NAME, DICT_TYPE_NAME + ), + ) + + def get_item_by_condition(self, condition: Callable) -> Optional[SelectorVar]: + """ + Get all child items form self.data when child meets the condition + """ + var = self.get_var() + if condition.__code__.co_argcount == 1: + return self._handle_collection([v for v in var if condition(v)]) + elif condition.__code__.co_argcount == 2: + return self._handle_collection( + [v for i, v in enumerate(var) if condition(i, v)] + ) + kcl_error.report_exception( + err_type=kcl_error.ErrType.SelectorError_TYPE, + arg_msg=SELECTOR_INVALID_CONDITION_ERROR_MSG, + ) + + +class DictSelector(Selector): + """ + List selector expression inherited from Selector + """ + + def __init__(self, data: SelectorVar) -> None: + super().__init__(data) + + def get_all(self) -> SelectorVar: + """ + Get all child items form self.data + """ + return self.get_var() + + def get_by_index(self, index: int) -> SelectorVar: + """ + Get all child items by index + """ + kcl_error.report_exception( + err_type=kcl_error.ErrType.SelectorError_TYPE, + arg_msg=SELECTOR_INVALID_USE_ERROR_MSG.format( + DICT_TYPE_NAME, LIST_TYPE_NAME + ), + ) + + def get_by_keys(self, keys: List[str]) -> Optional[SelectorVar]: + """ + Get all child items form self.data when child key is in keys + """ + var = self.get_var() + return self._handle_collection({k: var[k] for k in var if k in keys}) + + def get_item_by_condition(self, condition: Callable) -> Optional[SelectorVar]: + """ + Get all child items form self.data when child meets the condition + """ + var = self.get_var() + if condition.__code__.co_argcount == 1: + return self._handle_collection({k: var[k] for k in var if condition(k)}) + elif condition.__code__.co_argcount == 2: + return self._handle_collection( + {k: v for k, v in var.items() if condition(k, v)} + ) + kcl_error.report_exception( + err_type=kcl_error.ErrType.SelectorError_TYPE, + arg_msg=SELECTOR_INVALID_CONDITION_ERROR_MSG, + ) + + +class SchemaSelector(Selector): + """ + Schema selector expression inherited from Selector + """ + + def __init__(self, data: SelectorVar) -> None: + super().__init__(data) + + def get_all(self) -> Optional[SelectorVar]: + """ + Get all child items form self.data + """ + var = self.get_var() + return self._handle_collection( + { + kcl_info.demangle(k): var[k] + for k in var + if kcl_info.ismangled(k) and SCHEMA_SETTINGS_ATTR_NAME not in k + } + ) + + def get_by_index(self, index: int) -> SelectorVar: + """ + Get all child items by index + """ + kcl_error.report_exception( + err_type=kcl_error.ErrType.SelectorError_TYPE, + arg_msg=SELECTOR_INVALID_USE_ERROR_MSG.format( + SCHEMA_TYPE_NAME, LIST_TYPE_NAME + ), + ) + + def get_by_keys(self, keys: List[str]) -> Optional[SelectorVar]: + """ + Get all child items form self.data when child key is in keys + """ + data = self.get_all() + return self._handle_collection({k: data[k] for k in data if k in keys}) + + def get_item_by_condition(self, condition: Callable) -> Optional[SelectorVar]: + """ + Get all child items form self.data when child meets the condition + """ + var = self.get_var() + if condition.__code__.co_argcount == 1: + return self._handle_collection( + { + kcl_info.demangle(k): var[k] + for k in var + if kcl_info.ismangled(k) and condition(kcl_info.demangle(k)) + } + ) + elif condition.__code__.co_argcount == 2: + return self._handle_collection( + { + kcl_info.demangle(k): v + for k, v in var.items() + if kcl_info.ismangled(k) and condition(kcl_info.demangle(k), v) + } + ) + kcl_error.report_exception( + err_type=kcl_error.ErrType.SelectorError_TYPE, + arg_msg=SELECTOR_INVALID_CONDITION_ERROR_MSG, + ) + + +class SelectorFactory: + """ + Factory class to build selector + """ + + @staticmethod + def get(var: SelectorVar) -> Selector: + """ + Get selector class using 'var' + """ + + def is_kcl_schema(obj): + from kclvm.api.object import KCLSchemaObject + + return isinstance(obj, KCLSchemaObject) + + if isinstance(var, list): + return ListSelector(var) + if isinstance(var, dict): + return DictSelector(var) + if is_kcl_schema(var): + return SchemaSelector(var) + kcl_error.report_exception( + err_type=kcl_error.ErrType.SelectorError_TYPE, + arg_msg=SELECTOR_INVALID_VAR_TYPE_ERROR_MSG, + ) + + +def select( + var: SelectorVar, + is_all: bool = False, + index: Optional[int] = None, + keys: List[str] = None, + condition: Callable = None, +): + """ + Use the selector expression to filter out the child elements of 'var' + and return their references + """ + if var is None: + kcl_error.report_exception( + err_type=kcl_error.ErrType.SelectorError_TYPE, + arg_msg=SELECTOR_EMPTY_VAR_ERROR_MSG, + ) + return SelectorFactory.get(var).select(is_all, index, keys or [], condition) diff --git a/internal/kclvm_py/api/object/internal/undefined.py b/internal/kclvm_py/api/object/internal/undefined.py new file mode 100644 index 000000000..496f2fb1c --- /dev/null +++ b/internal/kclvm_py/api/object/internal/undefined.py @@ -0,0 +1,29 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + + +class UndefinedType: + def __repr__(self): + return self.__str__() + + def __str__(self): + """Built-in str(Undefined) like str(None)""" + return "Undefined" + + def __bool__(self): + """Built-in bool(Undefined) like bool(None)""" + return False + + @staticmethod + def type_str(): + """Error message show type""" + return "UndefinedType" + + @property + def value(self): + return self._value + + def __init__(self): + self._value = None + + +Undefined = UndefinedType() diff --git a/internal/kclvm_py/api/object/object.py b/internal/kclvm_py/api/object/object.py new file mode 100644 index 000000000..906b2fab7 --- /dev/null +++ b/internal/kclvm_py/api/object/object.py @@ -0,0 +1,1475 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import Optional, Union, List, Dict, Any, Iterator, cast +from abc import abstractmethod +from copy import deepcopy +from dataclasses import dataclass, field +from enum import Enum, IntEnum + +import kclvm.kcl.error as kcl_error +import kclvm.kcl.info as kcl_info +import kclvm.kcl.ast as ast + +from kclvm.api.object.internal import Deprecated, Undefined, UndefinedType + +# -------------------------------- +# KCL Object definitions +# -------------------------------- + +AttrType = str + + +class KCLObjectType(Enum): + LITERAL = "LITERAL" + INTEGER = "INTEGER" + FLOAT = "FLOAT" + BOOLEAN = "BOOLEAN" + NONE = "NONE" + UNDEFINED = "UNDEFINED" + RETURN_VALUE = "RETURN_VALUE" + ERROR = "ERROR" + FUNCTION = "FUNCTION" + STRING = "STRING" + BUILTIN = "BUILTIN" + LIST = "LIST" + DICT = "DICT" + NUMBER_MULTIPLIER = "NUMBER_MULTIPLIER" + TUPLE = "TUPLE" + ITER = "ITER" + COMPILED_FUNCTION = "COMPILED_FUNCTION" + CLOSURE = "CLOSURE" + DECORATOR = "DECORATOR" + SLICE = "SLICE" + TYPE = "TYPE" + SCHEMA_TYPE = "SCHEMA_TYPE" + SCHEMA = "SCHEMA" + SCHEMA_INDEX_SIGNATURE = "SCHEMA_INDEX_SIGNATURE" + NAME_CONSTANT = "NAME_CONSTANT" + MODULE = "MODULE" + PACKAGE = "PACKAGE" + UNPACK = "UNPACK" + SCHEMA_CONFIG = "SCHEMA_CONFIG" + RUNTIME_CODE = "RUNTIME_CODE" + + +class KCLSchemaReverseFields: + SETTINGS = "__settings__" + NAME = "__schema_name__" + TYPE = "__schema_type__" + PKG_PATH = "__pkg_path__" + + +NUMBER_MULTIPLIER_TYPE_STR = "number_multiplier" +ANT_TYPE_STR = "any" + + +@dataclass +class KCLObject: + @abstractmethod + def type(self) -> KCLObjectType: + """ + Get the object type + """ + pass + + @abstractmethod + def type_str(self) -> str: + """ + Get the object type + """ + pass + + def is_truthy(self) -> bool: + tpe = self.type() + if tpe in [KCLObjectType.BOOLEAN, KCLObjectType.NONE]: + return self.value + elif tpe == KCLObjectType.UNDEFINED: + return False + elif tpe in [ + KCLObjectType.LIST, + KCLObjectType.DICT, + KCLObjectType.SCHEMA_TYPE, + KCLObjectType.STRING, + ]: + return bool(self.value) + elif tpe == KCLObjectType.INTEGER: + return self.value != 0 + elif tpe == KCLObjectType.FLOAT: + return self.value != 0.0 + return True + + +@dataclass +class KCLLiteralObject(KCLObject): + value: Any + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.LITERAL + + def type_str(self) -> str: + """ + Get the object type + """ + return "literal" + + +@dataclass +class KCLIntObject(KCLLiteralObject): + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.INTEGER + + def type_str(self) -> str: + """ + Get the object type + """ + return "int" + + +@dataclass +class KCLFloatObject(KCLLiteralObject): + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.FLOAT + + def type_str(self) -> str: + """ + Get the object type + """ + return "float" + + +@dataclass +class KCLNumberMultiplierObject(KCLFloatObject): + raw_value: int + binary_suffix: str + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.NUMBER_MULTIPLIER + + def type_str(self) -> str: + """ + Get the object type + """ + return ( + f"{NUMBER_MULTIPLIER_TYPE_STR}({self.raw_value}{self.binary_suffix})" + if self.raw_value and self.binary_suffix + else NUMBER_MULTIPLIER_TYPE_STR + ) + + def __str__(self) -> str: + return f"{self.raw_value}{self.binary_suffix}" + + def __repr__(self) -> str: + return self.__str__() + + def __int__(self) -> int: + return int(self.value) + + def __float__(self) -> float: + return float(self.value) + + def __bool__(self) -> bool: + return bool(self.value) + + +@dataclass +class KCLStringObject(KCLLiteralObject): + + MEMBER_FUNCTIONS = [ + "capitalize", + "count", + "endswith", + "find", + "format", + "index", + "isalnum", + "isalpha", + "isdigit", + "islower", + "isspace", + "istitle", + "isupper", + "join", + "lower", + "upper", + "lstrip", + "rstrip", + "replace", + "rfind", + "rindex", + "rsplit", + "split", + "splitlines", + "startswith", + "strip", + "title", + ] + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.STRING + + def type_str(self) -> str: + """ + Get the object type + """ + return "str" + + def check_attr(self, name: str): + if not name: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg="kcl string object member name can't be empty or None", + ) + if not hasattr(self.value, name) and name not in self.MEMBER_FUNCTIONS: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"attribute {name} not found", + ) + + def get_member_method(self, name: str): + from .function import KCLMemberFunctionObject + + self.check_attr(name) + return KCLMemberFunctionObject(obj=self, name=name) + + def call_member_method(self, name: str, *args, **kwargs): + + return getattr(self.value, name).__call__(*args, **kwargs) + + +@dataclass +class KCLNameConstantObject(KCLObject): + value: Any + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.NAME_CONSTANT + + def type_str(self) -> str: + """ + Get the object type + """ + return "name_constant" + + +@dataclass +class KCLTrueObject(KCLNameConstantObject): + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.BOOLEAN + + def type_str(self) -> str: + """ + Get the object type + """ + return "bool" + + @staticmethod + def instance() -> KCLNameConstantObject: + return TRUE_INSTANCE + + +@dataclass +class KCLFalseObject(KCLNameConstantObject): + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.BOOLEAN + + def type_str(self) -> str: + """ + Get the object type + """ + return "bool" + + @staticmethod + def instance() -> KCLNameConstantObject: + return FALSE_INSTANCE + + +@dataclass +class KCLNoneObject(KCLNameConstantObject): + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.NONE + + def type_str(self) -> str: + """ + Get the object type + """ + return "NoneType" + + @staticmethod + def instance() -> "KCLNoneObject": + return NONE_INSTANCE + + +@dataclass +class KCLUndefinedObject(KCLNameConstantObject): + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.UNDEFINED + + def type_str(self) -> str: + """ + Get the object type + """ + return Undefined.type_str() + + @staticmethod + def instance() -> "KCLUndefinedObject": + return UNDEFINED_INSTANCE + + +@dataclass +class KCLListObject(KCLObject): + items: List[KCLObject] = field(default_factory=list) + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.LIST + + def type_str(self) -> str: + """ + Get the object type + """ + return "list" + + def append(self, item: KCLObject): + self.items.append(item) + + def append_unpack(self, items: KCLObject): + if not isinstance( + items, + ( + KCLListObject, + KCLSchemaObject, + KCLDictObject, + KCLNoneObject, + KCLUndefinedObject, + ), + ): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"'{items.type_str()}' object is not iterable", + ) + if not isinstance(items, (KCLNoneObject, KCLUndefinedObject)): + self.items = [*self.items, *items.value] + + @property + def value(self): + return self.items + + def remove_at(self, index: KCLIntObject): + del self.items[to_python_obj(index)] + + def remove(self, val: KCLObject): + self.items.remove(val) + + +@dataclass +class KCLDictObject(KCLObject): + value: dict = field(default_factory=dict) + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.DICT + + def type_str(self) -> str: + """ + Get the object type + """ + return "dict" + + @property + def config_keys(self) -> set: + return set(self.value.keys()) + + def update_key_value(self, k: AttrType, v: KCLObject): + assert k is not None + self.value[to_python_obj(k)] = to_kcl_obj(v) + + def has_key(self, k: AttrType): + return to_python_obj(k) in self.value + + def __contains__(self, k: AttrType): + return to_python_obj(k) in self.value + + def get(self, k: AttrType) -> KCLObject: + if k is None or isinstance(k, UndefinedType): + return KCLUndefinedObject.instance() + if isinstance(k, (KCLNoneObject, KCLUndefinedObject)): + return KCLUndefinedObject.instance() + return self.value.get(to_python_obj(k), KCLUndefinedObject.instance()) + + def get_keys(self) -> KCLListObject: + return KCLListObject([to_kcl_obj(v) for v in self.value.keys()]) + + def get_values(self) -> KCLListObject: + return KCLListObject([to_kcl_obj(v) for v in self.value.values()]) + + def append_unpack(self, items: KCLObject): + if not isinstance( + items, + ( + KCLListObject, + KCLSchemaObject, + KCLDictObject, + KCLNoneObject, + KCLUndefinedObject, + ), + ): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"'{items.type_str()}' object is not iterable", + ) + value = ( + KCLDictObject(value={}) + if isinstance(items, (KCLNoneObject, KCLUndefinedObject)) + else items + ) + if isinstance(value, KCLSchemaObject): + config = KCLSchemaConfigObject(value=value.value) + config.update_attr_op_using_obj(value) + self.union_with(deepcopy(config)) + else: + self.union_with(deepcopy(value)) + + def update(self, data: Union[dict, "KCLDictObject"]): + if isinstance(data, KCLDictObject): + for k, v in data.value.items(): + self.value[k] = to_kcl_obj(v) + if isinstance(data, dict): + for k, v in data.items(): + self.value[k] = to_kcl_obj(v) + + def union_with(self, obj: KCLObject, should_idempotent_check: bool = True): + from kclvm.vm.runtime.evaluator import union + + union(self, to_kcl_obj(obj), should_idempotent_check=should_idempotent_check) + if isinstance(self, KCLConfigObjectMixin): + self.update_attr_op_using_obj(obj) + + def merge_with(self, obj: KCLObject): + from kclvm.vm.runtime.evaluator import merge + + union_obj = cast( + KCLDictObject, + merge([self, obj]), + ) + self.update(union_obj) + + def insert_with_key( + self, attr: Union[str, KCLStringObject], obj: KCLObject, index=-1 + ): + value = self.get(attr) + if ( + value is None + or value is Undefined + or isinstance(value, UndefinedType) + or isinstance(value, (KCLNoneObject, KCLUndefinedObject)) + ): + value = KCLListObject() + self.value[attr] = value + if not isinstance(value, KCLListObject) or not isinstance(obj, KCLListObject): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg="only list attribute can be inserted value", + ) + if index is None or index == -1: + value = self.value[attr].value + obj.value + self.update({attr: value}) + elif index >= 0: + value = ( + self.value[attr].value[:index] + + obj.value + + self.value[attr].value[index:] + ) + self.update({attr: value}) + + def insert_with( + self, data: Union[dict, "KCLDictObject"], index: Optional[int] = None + ): + obj = data.value if isinstance(data, KCLDictObject) else data + if not isinstance(obj, dict): + return + for k, v in obj.items(): + self.insert_with_key(k, v, index) + + def list_key_override(self, attr: str, v: KCLObject, index: int): + value = self.get(attr) + if not isinstance(value, KCLListObject): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg="only list attribute can be inserted value", + ) + if v is None or isinstance(v, (KCLNoneObject, KCLUndefinedObject)): + self.value[attr].value.pop(index) + else: + self.value[attr].value[index] = v + + def unique_merge_with(self, obj: KCLObject): + from kclvm.vm.runtime.evaluator import union + + union(self, to_kcl_obj(obj), should_idempotent_check=True) + + def delete(self, key: Union[str, KCLStringObject]): + del self.value[to_python_obj(key)] + + +@dataclass +class KCLConfigObjectMixin: + operation_map: Dict[str, int] = field(default_factory=dict) + insert_index_map: Dict[str, Union[str, int]] = field(default_factory=dict) + + def add_operation(self, key: str, operation: int, insert_index=-1): + if not self.operation_map: + self.operation_map = {} + if not self.insert_index_map: + self.insert_index_map = {} + if not key: + return + self.operation_map[key] = operation + self.insert_index_map[key] = insert_index + + def get_operation(self, key: str) -> int: + if not self.operation_map: + self.operation_map = {} + return self.operation_map.get(key, ast.ConfigEntryOperation.UNION) + + def get_insert_index(self, key: str) -> Optional[Union[str, int]]: + if not self.insert_index_map: + return None + return self.insert_index_map.get(key) + + def update_attr_op_using_obj(self, obj: KCLObject): + if isinstance(obj, (KCLSchemaConfigObject, KCLSchemaObject)): + self.operation_map = {**self.operation_map, **obj.operation_map} + self.insert_index_map = {**self.insert_index_map, **obj.insert_index_map} + + +@dataclass +class KCLSchemaConfigObject(KCLDictObject, KCLConfigObjectMixin): + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.DICT + + def type_str(self) -> str: + return "dict" # Please note it is actually a dict + + +@dataclass +class KCLSchemaObject(KCLObject, KCLConfigObjectMixin): + name: str = None + pkgpath: str = None + instance_pkgpath: str = None + attrs: dict = None + runtime_type: str = None + is_relaxed: bool = False + config_keys: set = field(default_factory=set) + __tags__: dict = field(default_factory=dict) + __decorators__: dict = field(default_factory=dict) + __stmt_buffer__: list = field(default_factory=dict) + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.SCHEMA + + def type_str(self) -> str: + """ + Get the object type + """ + return self.name + + def full_type_str(self) -> str: + """ + Get the object type + """ + return ( + f"{self.pkgpath}.{self.name}" + if self.pkgpath and self.pkgpath != ast.Program.MAIN_PKGPATH + else self.name + ) + + @property + def value(self): + return self.attrs + + def update_info(self, name: str, runtime_type: str, is_relaxed: bool): + self.name = name + self.runtime_type = runtime_type + self.is_relaxed = is_relaxed + + def construct( + self, config: Optional[KCLDictObject] = None, _args=None, _builder=None + ): + if config and isinstance(config, KCLDictObject): + self.attrs = config.value + return self + + def update(self, data: Union[dict, KCLObject]): + if isinstance(data, KCLDictObject): + for k, v in data.value.items(): + self.attrs[to_python_obj(k)] = to_kcl_obj(v) + if isinstance(data, dict): + for k, v in data.items(): + self.attrs[to_python_obj(k)] = to_kcl_obj(v) + + def get(self, k: Union[str, KCLStringObject], do_check: bool = True): + if not self.attrs or k not in self.attrs and do_check: + kcl_error.report_exception( + err_type=kcl_error.ErrType.AttributeError_TYPE, + arg_msg=f"schema '{self.full_type_str()}' attribute '{k}' not found", + ) + return self.attrs.get(to_python_obj(k)) + + def update_key_value(self, k: str, v: KCLObject): + assert k is not None + self.attrs[to_python_obj(k)] = to_kcl_obj(v) + + def append_unpack(self, items: KCLObject): + if not isinstance( + items, + ( + KCLListObject, + KCLSchemaObject, + KCLDictObject, + KCLNoneObject, + KCLUndefinedObject, + ), + ): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"'{items.type_str()}' object is not iterable", + ) + value = ( + KCLDictObject(value={}) + if isinstance(items, (KCLNoneObject, KCLUndefinedObject)) + else items + ) + if isinstance(value, KCLSchemaObject): + config = KCLSchemaConfigObject(value=value.value) + config.update_attr_op_using_obj(value) + self.union_with(config) + else: + self.union_with(value) + + def insert_with_key( + self, attr: Union[str, KCLStringObject], obj: KCLObject, index=-1 + ): + self.insert_with(attr, obj, index) + + def insert_with(self, attr: Union[str, KCLStringObject], obj: KCLObject, index=-1): + value = self.get(attr, do_check=False) + if ( + value is None + or value is Undefined + or isinstance(value, UndefinedType) + or isinstance(value, (KCLNoneObject, KCLUndefinedObject)) + ): + value = KCLListObject() + self.attrs[attr] = value + if not isinstance(value, KCLListObject) or not isinstance(obj, KCLListObject): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg="only list attribute can be inserted value", + ) + if index is None or index == -1: + value = self.attrs[attr].value + obj.value + self.update({attr: value}) + elif index >= 0: + value = ( + self.attrs[attr].value[:index] + + obj.value + + self.attrs[attr].value[index:] + ) + self.update({attr: value}) + + def list_key_override(self, attr: str, v: KCLObject, index: int): + value = self.get(attr) + if not isinstance(value, KCLListObject): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg="only list attribute can be inserted value", + ) + if v is None or isinstance(v, (KCLNoneObject, KCLUndefinedObject)): + self.attrs[attr].value.pop(index) + else: + self.attrs[attr].value[index] = v + + def union_with( + self, obj: Union[KCLObject, dict], should_idempotent_check: bool = True + ): + from kclvm.vm.runtime.evaluator import union + + union(self, to_kcl_obj(obj), should_idempotent_check=should_idempotent_check) + if isinstance(self, KCLConfigObjectMixin): + self.update_attr_op_using_obj(obj) + + def delete(self, key: Union[str, KCLStringObject]): + del self.value[to_python_obj(key)] + + def has_key(self, attr: Union[str, KCLStringObject]): + return to_python_obj(attr) in self.attrs + + def __contains__(self, attr: Union[str, KCLStringObject]): + return to_python_obj(attr) in self.attrs + + def should_add_attr(self, name: str) -> bool: + """Determine whether an attribute can be added to schema attributes, + such as non-exported variables that start with `_` or relaxed attributes. + + Three situations that should be added: + 1. The attribute was originally an attribute of this schema + 2. Variables starting with an underscore `_` + 3. The schema is relaxed + """ + return ( + name in self + and self.get_attr_type(name) + or (isinstance(name, str) and name.startswith("_")) + or self.is_relaxed + ) + + # Attribute type + + def set_attr_type(self, attr: str, types: List[str]): + if not attr: + return + tagged = kcl_info.tagging("attr_type", attr) + if not self.__tags__: + self.__tags__ = {} + self.__tags__[tagged] = types + + def get_attr_type(self, attr: str) -> List[str]: + + if not attr or not self.__tags__: + return [] + tagged = kcl_info.tagging("attr_type", attr) + if tagged in self.__tags__: + return self.__tags__[tagged] + else: + return [] + + def set_attr_runtime_type(self, attr: str, types: List[str]): + if not attr: + return + tagged = kcl_info.tagging("runtime_attr_type", attr) + if not self.__tags__: + self.__tags__ = {} + if tagged in self.__tags__ and self.__tags__[tagged] != types: + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + arg_msg=f"can't change schema field type of '{attr}'", + ) + self.__tags__[tagged] = types + + # Optional + + def set_attr_optional(self, attr: str, is_optional: bool): + if not attr: + return + tagged = kcl_info.tagging("is_optional", attr) + if not self.__tags__: + self.__tags__ = {} + if tagged in self.__tags__: + if self.__tags__[tagged] is False and is_optional is True: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"can't change the required schema attribute of '{attr}' to optional", + ) + self.__tags__[tagged] = is_optional + + def get_attr_optional(self, attr: str) -> bool: + if not attr: + return False + tagged = kcl_info.tagging("is_optional", attr) + if not self.__tags__: + self.__tags__ = {} + return self.__tags__.get(tagged, False) + + def check_optional_attrs(self): + """Check all schema attributes are optional. + If the schema attribute is not optional and its value is None, an error is reported + """ + if not self.__tags__: + return + for k, v in self.attrs.items(): + # Note k is a string, v is a KCLObject + is_optional = self.get_attr_optional(str(k)) + # Relaxed schema attribute has no types and do not check the None value + types = self.get_attr_type(k) + # Whether v is not a optional attribute + if ( + types + and not is_optional + and (v is None or isinstance(v, (KCLNoneObject, KCLUndefinedObject))) + ): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"attribute '{k}' of {self.name} is required and can't be None or Undefined", + ) + + # Attribute mutable + + def set_immutable_flag(self, attr: str, is_final: bool): + if not attr: + return + tagged = kcl_info.tagging("immutable", attr) + if not self.__tags__: + self.__tags__ = {} + self.__tags__[tagged] = is_final + + def get_immutable_flag(self, attr: str) -> bool: + if not attr: + return False + tagged = kcl_info.tagging("immutable", attr) + if not self.__tags__: + self.__tags__ = {} + return self.__tags__.get(tagged, False) + + # Decorators + + def add_decorator(self, field: str, decorator) -> None: + """ + Add a decorator to the schema + + Parameters + ---------- + - field: The schema attribute name or schema name + - decorator: A decorator class + + Return + ------ + None + """ + if not field: + return + if not self.__decorators__: + self.__decorators__ = {} + tagged = kcl_info.tagging("decorator", field) + if tagged not in self.__decorators__: + self.__decorators__[tagged] = [] + self.__decorators__[tagged].append(decorator) + + def run_all_decorators(self) -> None: + """ + Run schema all decorators and + parameters of per decorator is its key and value. + """ + if not self.__decorators__: + return + for k in self.__decorators__: + for decorator in self.__decorators__[k]: + name_member = kcl_info.detagging("decorator", k) + value = decorator.call( + None, + None, + key=name_member, + value=self.attrs.get(name_member), + ) + # If a schema attribute is deprecated, RESET it to be None + if ( + not value + or isinstance(value, (KCLNoneObject, KCLUndefinedObject)) + and decorator.name == Deprecated.NAME + ): + self.attrs[name_member] = None + + # Statement buffer + + def stmt_buffer_enqueue(self, content): + if not self.__stmt_buffer__: + self.__stmt_buffer__ = [] + if content and hasattr(self, "__stmt_buffer__"): + self.__stmt_buffer__.append(content) + + def stmt_buffer(self): + if hasattr(self, "__stmt_buffer__"): + return self.__stmt_buffer__ + return None + + +@dataclass +class KCLUnpackObject(KCLObject): + value: Union[ + KCLListObject, KCLDictObject, KCLSchemaObject, KCLNoneObject, KCLUndefinedObject + ] = None + is_double_star: bool = False + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.UNPACK + + def type_str(self) -> str: + """ + Get the object type + """ + return "unpack" + + def unpack(self): + if not isinstance( + self.value, + ( + KCLListObject, + KCLDictObject, + KCLSchemaObject, + KCLNoneObject, + KCLUndefinedObject, + ), + ): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"only list, dict and schema object can be used with unpack operators * and **, got {self.value}", + ) + return self.value + + +@dataclass +class KCLSliceObject(KCLObject): + start: KCLObject = None + stop: KCLObject = None + step: KCLObject = None + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.SLICE + + def type_str(self) -> str: + """ + Get the object type + """ + return "slice" + + @property + def value(self): + return slice(self.start.value, self.stop.value, self.step.value) + + +@dataclass +class KCLModuleObject(KCLObject): + name: str + asname: str = None + value: Dict[str, KCLObject] = None + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.MODULE + + def type_str(self) -> str: + """ + Get the object type + """ + return "module" + + def get(self, name: str): + name = to_python_obj(name) + if self.value and name and name in self.value: + return self.value[name] + kcl_error.report_exception( + err_type=kcl_error.ErrType.AttributeError_Runtime_TYPE, + arg_msg=f"module '{self.name}' has no attribute '{name}'", + ) + + +@dataclass +class KCLIterObject(KCLObject): + iter: Iterator + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.ITER + + def type_str(self) -> str: + """ + Get the object type + """ + return "iter" + + def next(self) -> KCLObject: + next_obj = next(self.iter) + return to_kcl_obj(next_obj if isinstance(next_obj, tuple) else (next_obj,)) + + @staticmethod + def build_iter(obj: KCLObject, iter_variable_count: int = 1): + if obj.type() not in [ + KCLObjectType.DICT, + KCLObjectType.LIST, + KCLObjectType.STRING, + KCLObjectType.SCHEMA, + ]: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"{obj.type_str()} object is not iterable", + ) + assert 0 < iter_variable_count <= 2 + if iter_variable_count == 1: + return KCLIterObject(iter=iter(obj.value)) + if obj.type() in [KCLObjectType.LIST, KCLObjectType.STRING]: + return KCLIterObject(iter=iter(enumerate(obj.value))) + if obj.type() in [KCLObjectType.DICT, KCLObjectType.SCHEMA]: + return KCLIterObject(iter=iter(obj.value.items())) + raise Exception(f"invalid iter object type {type(obj)}") + + @property + def value(self): + return self.iter + + +@dataclass +class KCLTupleObject(KCLObject): + value: List[KCLObject] + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.TUPLE + + def type_str(self) -> str: + """ + Get the object type + """ + return "tuple" + + +@dataclass +class KCLErrorObject(KCLObject, Exception): + file: Optional[str] = None + lineno: Optional[int] = None + colno: Optional[int] = None + msg: str = None + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.ERROR + + def type_str(self) -> str: + """ + Get the object type + """ + return "error" + + @property + def value(self): + return self.msg + + +# ---------------------- +# KCL type objects +# ---------------------- + + +class KCLTypeKind(IntEnum): + NoneKind = 0 + AnyKind = 1 + UnionKind = 2 + BoolKind = 3 + BoolLitKind = 4 + IntKind = 5 + IntLitKind = 6 + FloatKind = 7 + FloatLitKind = 8 + StrKind = 9 + StrLitKind = 10 + ListKind = 11 + DictKind = 12 + SchemaKind = 13 + SchemaDefKind = 14 + NumberMultiplierKind = 15 + FuncKind = 16 + VoidKind = 17 + ModuleKind = 18 + NamedKind = 19 + + +class TypeAliasMixin: + # Mark the type is a value or a type alias + is_type_alias: bool = False + + +@dataclass +class KCLBaseTypeObject(KCLObject, TypeAliasMixin): + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.TYPE + + def type_str(self) -> str: + """ + Get the object type + """ + return "base_type" + + def type_kind(self) -> int: + return -1 + + +@dataclass +class KCLAnyTypeObject(KCLBaseTypeObject): + value: str = None # Can only be any + + def type_str(self) -> str: + """ + Get the object type + """ + return "any" + + def type_kind(self) -> int: + return KCLTypeKind.AnyKind + + +@dataclass +class KCLBuiltinTypeObject(KCLBaseTypeObject): + def type_str(self) -> str: + """ + Get the object type + """ + return "builtin" + + +@dataclass +class KCLIntTypeObject(KCLBuiltinTypeObject): + def type_str(self): + return "int" + + def type_kind(self) -> int: + return KCLTypeKind.IntKind + + +@dataclass +class KCLFloatTypeObject(KCLBuiltinTypeObject): + def type_str(self): + return "float" + + def type_kind(self) -> int: + return KCLTypeKind.FloatKind + + +@dataclass +class KCLStringTypeObject(KCLBuiltinTypeObject): + def type_str(self): + return "str" + + def type_kind(self) -> int: + return KCLTypeKind.StrKind + + +@dataclass +class KCLBoolTypeObject(KCLBuiltinTypeObject): + def type_str(self) -> str: + """ + Get the object type + """ + return "bool" + + def type_kind(self) -> int: + return KCLTypeKind.BoolKind + + +@dataclass +class KCLNameConstantTypeObject(KCLBaseTypeObject): + value: Optional[bool] = None + + def type_str(self) -> str: + """ + Get the object type + """ + return "NoneType" if self.value is None else "bool" + + +@dataclass +class KCLStringLitTypeObject(KCLBaseTypeObject): + value: str = None + + def type_str(self) -> str: + """ + Get the object type + """ + return f"str({self.value})" + + def type_kind(self) -> int: + return KCLTypeKind.StrLitKind + + +@dataclass +class KCLNumberLitTypeObject(KCLBaseTypeObject): + value: Union[int, float] = None + + def type_str(self) -> str: + """ + Get the object type + """ + return ( + f"int({self.value})" + if isinstance(self.value, int) + else f"float({self.value})" + ) + + def type_kind(self) -> int: + return ( + KCLTypeKind.IntLitKind + if self.is_int_lit_type() + else KCLTypeKind.FloatLitKind + ) + + def is_int_lit_type(self) -> bool: + return isinstance(self.value, int) + + def is_float_lit_type(self) -> bool: + return isinstance(self.value, float) + + +@dataclass +class KCLIntLitTypeObject(KCLNumberLitTypeObject): + def type_kind(self) -> int: + return KCLTypeKind.IntLitKind + + +@dataclass +class KCLFloatLitTypeObject(KCLNumberLitTypeObject): + def type_kind(self) -> int: + return KCLTypeKind.FloatLitKind + + +@dataclass +class KCLBoolLitTypeObject(KCLBaseTypeObject): + value: bool = None + + def type_str(self) -> str: + return f"bool({self.value})" + + def type_kind(self) -> int: + return KCLTypeKind.BoolLitKind + + +@dataclass +class KCLListTypeObject(KCLBaseTypeObject): + item_type: KCLBaseTypeObject = None + + def type_str(self) -> str: + """ + Get the object type + """ + return "[{}]".format(self.item_type.type_str()) + + def type_kind(self) -> int: + return KCLTypeKind.ListKind + + +@dataclass +class KCLDictTypeObject(KCLBaseTypeObject): + key_type: KCLBaseTypeObject = None + value_type: KCLBaseTypeObject = None + + def type_str(self) -> str: + """ + Get the object type + """ + return "{{{}:{}}}".format(self.key_type.type_str(), self.value_type.type_str()) + + def type_kind(self) -> int: + return KCLTypeKind.DictKind + + +@dataclass +class KCLUnionTypeObject(KCLBaseTypeObject): + types: List[KCLBaseTypeObject] = None + + def type_str(self) -> str: + """ + Get the object type + """ + return "{}".format("|".join([t.type_str() for t in self.types])) + + def type_kind(self) -> int: + return KCLTypeKind.UnionKind + + +@dataclass +class KCLModuleTypeObject(KCLBaseTypeObject): + pkgpath: str = "" + imported_filenames: List[str] = field(default_factory=list) + is_user_module: bool = False + is_system_module: bool = False + is_plugin_module: bool = False + + def type_str(self) -> str: + return "module" + + def type_kind(self) -> int: + return KCLTypeKind.ModuleKind + + +@dataclass +class KCLNumberMultiplierTypeObject(KCLBaseTypeObject): + value: int = None + raw_value: int = None + binary_suffix: str = None + + def type_str(self) -> str: + return ( + f"{NUMBER_MULTIPLIER_TYPE_STR}({self.raw_value}{self.binary_suffix})" + if self.raw_value and self.binary_suffix + else NUMBER_MULTIPLIER_TYPE_STR + ) + + def type_kind(self) -> int: + return KCLTypeKind.NumberMultiplierKind + + def is_literal(self) -> bool: + return bool(self.binary_suffix) + + +@dataclass +class KCLNamedTypeObject(KCLBaseTypeObject): + name: str + + def type_str(self) -> str: + return "named" + + def type_kind(self) -> int: + return KCLTypeKind.NamedKind + + +@dataclass +class KCLNoneTypeObject(KCLNameConstantTypeObject): + def type_str(self) -> str: + """ + Get the object type + """ + return "NoneType" + + def type_kind(self) -> int: + return KCLTypeKind.NoneKind + + +@dataclass +class KCLVoidTypeObject(KCLBaseTypeObject): + def type_str(self) -> str: + """ + Get the object type + """ + return "void" + + def type_kind(self) -> int: + return KCLTypeKind.VoidKind + + +# -------------------- +# KCL Object instances +# -------------------- + + +TRUE_INSTANCE = KCLTrueObject(value=True) +FALSE_INSTANCE = KCLFalseObject(value=False) +NONE_INSTANCE = KCLNoneObject(value=None) +UNDEFINED_INSTANCE = KCLUndefinedObject(value=Undefined.value) + + +def to_python_obj(v: Union[KCLObject, int, float, str, bool, list, dict]) -> Any: + if isinstance(v, KCLObject): + if isinstance(v, KCLUndefinedObject): + return Undefined + elif isinstance(v, KCLNumberMultiplierObject): + return v + elif isinstance(v, (KCLLiteralObject, KCLNameConstantObject, KCLSliceObject)): + return v.value + elif isinstance(v, (KCLDictObject, KCLSchemaObject)): + return {_k: to_python_obj(_v) for _k, _v in v.value.items()} + elif isinstance(v, (KCLListObject, KCLTupleObject)): + return [to_python_obj(_v) for _v in v.value] + elif v is None or v is Undefined or isinstance(v, UndefinedType): + return v + elif isinstance(v, list): + return [to_python_obj(_v) for _v in v] + elif isinstance(v, dict): + return {_k: to_python_obj(_v) for _k, _v in v.items()} + elif isinstance(v, (int, float, str, bool, dict, list)): + return v + else: + raise Exception(f"invalid KCL object type {type(v)} to native object") + + +def to_kcl_obj( + value: Union[KCLObject, int, float, str, bool, list, dict, tuple] +) -> KCLObject: + if isinstance(value, KCLObject): + return value + if value is None: + return KCLNoneObject.instance() + if value is Undefined or isinstance(value, UndefinedType): + return KCLUndefinedObject.instance() + if isinstance(value, bool): + return KCLTrueObject.instance() if value else KCLFalseObject.instance() + elif isinstance(value, int): + return KCLIntObject(value=value) + elif isinstance(value, float): + return KCLFloatObject(value=value) + elif isinstance(value, str): + return KCLStringObject(value=value) + elif isinstance(value, tuple): + return KCLTupleObject(value=[to_kcl_obj(v) for v in value]) + elif isinstance(value, (list, set)): + return KCLListObject([to_kcl_obj(v) for v in value]) + elif isinstance(value, dict): + if KCLSchemaReverseFields.SETTINGS in value and isinstance( + value[KCLSchemaReverseFields.SETTINGS], (dict, KCLDictObject) + ): + return KCLSchemaObject( + attrs={k: to_kcl_obj(v) for k, v in value.items()}, + name=value[KCLSchemaReverseFields.SETTINGS].get( + KCLSchemaReverseFields.NAME + ), + runtime_type=value[KCLSchemaReverseFields.SETTINGS].get( + KCLSchemaReverseFields.TYPE + ), + pkgpath=value[KCLSchemaReverseFields.SETTINGS].get( + KCLSchemaReverseFields.PKG_PATH + ), + ) + return KCLDictObject({k: to_kcl_obj(v) for k, v in value.items()}) + else: + raise Exception(f"invalid native object type {type(value)} to KCL object") diff --git a/internal/kclvm_py/api/object/schema.py b/internal/kclvm_py/api/object/schema.py new file mode 100644 index 000000000..d7f179d0a --- /dev/null +++ b/internal/kclvm_py/api/object/schema.py @@ -0,0 +1,859 @@ +# Copyright 2021 The KCL Authors. All rights reserved. +from copy import deepcopy +from dataclasses import dataclass, field +from typing import Optional, Union, List, Dict +from .decorator import KCLDecoratorObject +from .object import ( + KCLObject, + KCLObjectType, + KCLTrueObject, + KCLFalseObject, + KCLIntObject, + KCLFloatObject, + KCLStringObject, + KCLBaseTypeObject, + KCLDictObject, + KCLSchemaObject, + KCLNoneObject, + KCLUndefinedObject, + KCLTypeKind, + KCLAnyTypeObject, + KCLStringTypeObject, + KCLSchemaReverseFields, + to_kcl_obj, +) +from .function import KWArg, KCLCompiledFunctionObject + +import kclvm.kcl.error as kcl_error +import kclvm.kcl.info as kcl_info +import kclvm.api.object.internal.common as common + +from kclvm.internal.util import hash +from kclvm.compiler.check.check_type import ( + type_pack_and_check, + check_type, + has_literal_type, +) +from kclvm.kcl.ast import ast + +SETTINGS_OUTPUT_KEY = "output_type" +SETTINGS_OUTPUT_STANDALONE = "STANDALONE" +SETTINGS_OUTPUT_INLINE = "INLINE" +SETTINGS_OUTPUT_IGNORE = "IGNORE" + +SCHEMA_SETTINGS_ATTR_NAME = "__settings__" +SCHEMA_TYPE_ATTR_NAME = "__schema_type__" +SCHEMA_RUNTIME_TYPE_ATTR_NAME = "__runtime_schema_type__" +MAIN_MODULE_NAME = "__main__" + +SCHEMA_SELF_VALUE_KEY = "$schema_self" +SCHEMA_CONFIG_VALUE_KEY = "$schema_config" +SCHEMA_CONFIG_META_KEY = "$schema_config_meta" + + +class RefGraph: + """ + Reference graph + """ + + def __init__(self): + self.adjs = {} + + def _find_node_index(self, node): + return self.nodeset.index(node) + + def add_node_judge_cycle(self, node, another_node): + """ + add edge into the schema inheritance graph and check if cyclic inheritance occurs in schema + """ + if node not in self.adjs: + self.adjs[node] = [] + if another_node not in self.adjs: + self.adjs[another_node] = [] + self.adjs[another_node].append(node) + return self._has_cycle() + + def _has_cycle(self): + """ + Determine whether the schema inheritance graph is a Directed Acyclic Graph (DAG). + The detection uses Depth First Search (DFS) algorithm for each node + in the ergodic graph, and the time complexity is O (V + E), + V: the total number of detected nodes, + E: the total number of edges connected by nodes + """ + visited = {name: 0 for name in self.adjs.keys()} + + def _dfs(name): + visited[name] = 1 + for adj in self.adjs[name]: + if visited[adj] == 1: + return True + if visited[adj] == 0: + if _dfs(adj): + return True + else: + continue + visited[name] = 2 + return False + + for name in visited.keys(): + if visited[name] == 0 and _dfs(name): + return True + return False + + +class SchemaTypeRefGraph(RefGraph): + def get_sub_schemas(self, name: str) -> list: + """Get all sub schemas by name using BFS""" + result = [] + if not name: + return result + sub_schemas = self.adjs.get(name, []) + result += sub_schemas + for sub_schema in sub_schemas: + result += self.get_sub_schemas(sub_schema) + return result + + +class SchemaTypeFactory: + """ + A schema_type factory used to get schema_type object. + """ + + def __init__(self): + self._schema_types = {} + + def register(self, name: str, schema_type: "KCLSchemaTypeObject"): + """ + Register a schema_type with a unique name. + + :param name: Name of the schema_type + :param schema_type: The schema_type to be registered + :return: None + """ + self._schema_types[name] = schema_type + + def get(self, name: str): + """ + Get and return a schema_type object. + + :param name: Name of the schema_type + :return: A schema_type object + """ + schema_type = self._schema_types.get(name) + if not schema_type: + raise Exception(f"unknown schema type '{name}'") + return schema_type + + +@dataclass +class KCLSchemaIndexSignatureObject(KCLObject): + key_name: Optional[str] = None + key_type: str = None + value_type: str = None + value: KCLObject = None + any_other: bool = None + key_kcl_type: KCLBaseTypeObject = None + value_kcl_type: KCLBaseTypeObject = None + node: ast.SchemaIndexSignature = None + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.SCHEMA_INDEX_SIGNATURE + + def type_str(self) -> str: + """ + Get the object type string + """ + return "IndexSignatureType" + + def def_str(self) -> str: + return ( + # key_name + f"[{self.key_name + ': ' if self.key_name else ''}" + # ... + + ("..." if self.any_other else "") + # key_type + + f"{self.key_type}]: " + # value_type + + f"{self.value_type}" + ) + + +@dataclass +class KCLSchemaAttrObject(KCLObject): + is_optional: bool = True + is_final: bool = False + has_default: bool = False + attr_type: KCLBaseTypeObject = None + attr_node: Optional[ast.AST] = None + + +@dataclass +class KCLSchemaTypeObject(KCLBaseTypeObject): + name: str = None + MEMBER_FUNCTIONS = ["instances"] # Member function list + name: Optional[str] = None # Schema name + __refs__: Optional[list] = field(default_factory=list) # Instance reference list + func: Optional[KCLCompiledFunctionObject] = None # Body functions + check_fn: Optional[KCLCompiledFunctionObject] = None # Check function + is_mixin: bool = False # Mark is a schema mixin + is_protocol: bool = False # Mark is a schema protocol + is_rule: bool = False # Mark is a rule block + is_relaxed: bool = False # Mark is a relaxed schema + pkgpath: str = "" # Schema definition package path + filename: str = "" # Definition path location + doc: str = "" # Schema definition document string + runtime_type: Optional[str] = None # Schema runtime type file_hash + schema_name + base: Optional["KCLSchemaTypeObject"] = None # Base schema + protocol: Optional["KCLSchemaTypeObject"] = None # Protocol schema + mixins_names: Optional[List[str]] = field(default_factory=list) + mixins: List["KCLSchemaTypeObject"] = field( + default_factory=list + ) # Schema mixin list + attrs: Optional[dict] = field(default_factory=dict) # Schema attributes + attr_list: Optional[list] = field(default_factory=list) # Schema attribute order + attr_obj_map: Dict[Union[str, int, float], Optional[KCLSchemaAttrObject]] = field( + default_factory=dict + ) # Schema attribute type map + settings: Optional[dict] = field(default_factory=dict) # Schema settings + decorators: Optional[List[KCLDecoratorObject]] = field( + default_factory=list + ) # Schema decorator list + index_signature: Optional[ + KCLSchemaIndexSignatureObject + ] = None # Schema Index signature + node_ref: Optional[ast.SchemaStmt] = None + + # ----------------- + # Schema eval cache + # ----------------- + + _eval_cache = {} + + def can_add_members(self) -> bool: + return ( + self.name.endswith("Mixin") + or self.index_signature is not None + or self.is_relaxed + ) + + def type(self) -> KCLObjectType: + """ + Get the object type + """ + return KCLObjectType.SCHEMA_TYPE + + def type_str(self) -> str: + """ + Get the object type string + """ + return ( + self.name + if (self.pkgpath == "__main__" or not self.pkgpath) + else f"{self.pkgpath}.{self.name}" + ) + + def type_str_with_pkgpath(self) -> str: + """ + Get the object type string with pkgpath + """ + return ( + self.name + if (self.pkgpath == "__main__" or not self.pkgpath) + else f"@{self.pkgpath}.{self.name}" + ) + + def type_kind(self): + return KCLTypeKind.SchemaKind + + @property + def value(self) -> str: + """Return the runtime type string""" + return self.runtime_type + + @property + def key_type(self) -> str: + if self.index_signature: + return self.index_signature.key_kcl_type + return KCLStringTypeObject() + + @property + def value_type(self) -> str: + if self.index_signature: + return self.index_signature.value_kcl_type + return KCLAnyTypeObject() + + @property + def should_add_additional_key(self) -> bool: + return self.is_relaxed or self.index_signature is not None + + @property + def file_and_type(self) -> str: + return self.filename + self.runtime_type + + @property + def file_and_name(self) -> str: + return self.filename + self.name + + def is_sub_schema_of(self, base: Union["KCLSchemaTypeObject", str]) -> bool: + base_type_obj = base + if not isinstance(base_type_obj, KCLSchemaTypeObject): + return False + if ( + self.runtime_type == base.runtime_type + or self.file_and_name == base.file_and_name + ): + return True + base_ref = self.base + while base_ref and base_ref.runtime_type != base_type_obj.runtime_type: + base_ref = base_ref.base + return True if base_ref else False + + def get_obj_of_attr(self, attr: Union[int, str]) -> Optional[KCLSchemaAttrObject]: + if attr in self.attr_obj_map: + return self.attr_obj_map[attr] + base_ref = self.base + while base_ref and attr not in base_ref.attr_obj_map: + base_ref = base_ref.base + if base_ref: + return base_ref.attr_obj_map[attr] + return ( + self.protocol.attr_obj_map[attr] + if self.protocol and attr in self.protocol.attr_obj_map + else None + ) + + def get_type_of_attr(self, attr: Union[int, str]) -> Optional[KCLBaseTypeObject]: + attr_obj = self.get_obj_of_attr(attr) + return attr_obj.attr_type if attr_obj else None + + def get_node_of_attr(self, attr: Union[int, str]) -> Optional[ast.AST]: + attr_obj = self.get_obj_of_attr(attr) + return attr_obj.attr_node if attr_obj else None + + def set_type_of_attr(self, attr: Union[int, str], tpe: KCLBaseTypeObject): + if attr in self.attr_obj_map: + self.attr_obj_map[attr].attr_type = tpe + else: + self.attr_obj_map[attr] = KCLSchemaAttrObject(attr_type=tpe) + + def set_node_of_attr(self, attr: Union[int, str], ast_node: ast.AST): + if attr in self.attr_obj_map: + self.attr_obj_map[attr].attr_node = ast_node + else: + self.attr_obj_map[attr] = KCLSchemaAttrObject(attr_node=ast_node) + + def add_decorators(self, decorators: List[KCLDecoratorObject]): + if not self.decorators: + self.decorators = [] + self.decorators += decorators + + def set_func(self, func: KCLCompiledFunctionObject): + assert isinstance(func, KCLCompiledFunctionObject) + self.func = func + + def set_check_func(self, check_fn: Optional[KCLCompiledFunctionObject]): + if check_fn: + assert isinstance(check_fn, KCLCompiledFunctionObject) + self.check_fn = check_fn + else: + self.check_fn = None + + @staticmethod + def schema_runtime_type(tpe: str, filename=""): + return f"runtime_type_{hash(filename)}_{tpe}" + + @staticmethod + def new( + name: str, + base: Optional["KCLSchemaTypeObject"] = None, + protocol: Optional["KCLSchemaTypeObject"] = None, + filename: str = "", + is_relaxed: bool = False, + is_mixin: bool = False, + pkgpath: str = "", + attr_list: list = None, + index_signature: Optional[KCLSchemaIndexSignatureObject] = None, + vm=None, + ): + if not name: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg="schema name can't be None", + ) + runtime_type = KCLSchemaTypeObject.schema_runtime_type(name, pkgpath) + settings = { + SETTINGS_OUTPUT_KEY: SETTINGS_OUTPUT_INLINE + if not kcl_info.isprivate_field(name) + else SETTINGS_OUTPUT_IGNORE, + KCLSchemaReverseFields.NAME: name, + KCLSchemaReverseFields.TYPE: f"{pkgpath}.{name}", + KCLSchemaReverseFields.PKG_PATH: pkgpath, + } + obj = KCLSchemaTypeObject( + pkgpath=pkgpath, + name=name, + base=base, + protocol=protocol, + settings=settings, + filename=filename, + is_relaxed=is_relaxed, + is_mixin=is_mixin, + runtime_type=runtime_type, + attrs={attr: KCLUndefinedObject.instance() for attr in attr_list}, + attr_list=attr_list, + index_signature=index_signature, + ) + return obj + + def update_mixins(self, vm=None): + """Get mixins by name""" + if not self.mixins and self.mixins_names: + self.mixins = [] + for mixin_name in self.mixins_names: + if "." in mixin_name: # pkg.Schema + schema_type_obj = vm.find_schema_type(mixin_name) + if schema_type_obj and isinstance( + schema_type_obj, KCLSchemaTypeObject + ): + self.mixins.append(schema_type_obj) + else: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg="name '{}' is not defined".format(mixin_name), + ) + else: + if mixin_name not in vm.frames[0].globals: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg="name '{}' is not defined".format(mixin_name), + ) + schema_type_obj = vm.frames[0].globals[mixin_name] + self.mixins.append(schema_type_obj) + + def has_base(self): + return self.base and isinstance(self.base, KCLSchemaTypeObject) + + def new_empty_instance(self) -> KCLSchemaObject: + return KCLSchemaObject( + attrs={SCHEMA_SETTINGS_ATTR_NAME: to_kcl_obj(self.settings)}, + pkgpath=self.pkgpath, + name=self.name, + runtime_type=self.runtime_type, + ) + + def new_instance( + self, + config: Union[dict, KCLDictObject], + config_meta: dict, + args: List[KCLObject], + kwargs: List[KWArg], + vm, + ): + from kclvm.vm.runtime.evaluator import SchemaEvalContext + + context = SchemaEvalContext.build_from_vm( + vm=vm, + type_obj=self, + schema_obj=self.new_empty_instance(), + config=config, + config_meta=config_meta, + args=args, + kwargs=kwargs, + ) + # Save origin eval context + org_eval_ctx = vm.lazy_eval_ctx + vm.lazy_eval_ctx = context + # Reset the eval status before the evaluation + context.eval_reset() + # Get all schema attribute value place holders using the schema type object and the cache + if self.file_and_type in self._eval_cache: + context.place_holder_map = self._eval_cache[self.file_and_type] + else: + context.get_all_place_holders() + self._eval_cache[self.file_and_type] = context.place_holder_map + # New a schema instance using the type object + context.schema_obj = self._new_instance( + config, + config_meta, + args, + kwargs, + vm, + # Put the schema instance reference + inst=context.schema_obj, + ) + # Reset the eval status after the evaluation + context.eval_reset() + # Reload origin eval context + vm.lazy_eval_ctx = org_eval_ctx + # Return the schema instance + context.schema_obj.config_keys = ( + set(config.value.keys()) + if isinstance(config, KCLDictObject) + else set(config.keys()) + ) + return context.schema_obj + + def _new_instance( + self, + config: Union[dict, KCLDictObject], + config_meta: dict, + args: List[KCLObject], + kwargs: List[KWArg], + vm, + inst: KCLSchemaObject = None, + is_sub_schema: bool = False, + ) -> KCLSchemaObject: + self.do_args_type_check(args, kwargs, config_meta, vm) + inst = inst or self.new_empty_instance() + inst.instance_pkgpath = vm.ctx.pkgpath + for decorator in self.decorators: + inst.add_decorator(self.name, decorator=decorator) + if self.base and isinstance(self.base, KCLSchemaTypeObject): + inst = self.base._new_instance( + config, config_meta, [], [], vm, inst, is_sub_schema=True + ) + # Record all schema attributes + inst.union_with(self.attrs, should_idempotent_check=False) + for mixin in self.mixins: + inst.union_with(mixin.attrs, should_idempotent_check=False) + # Record the schema name, runtime_type and relaxed + inst.update_info(self.name, self.runtime_type, self.is_relaxed) + vm.push_frame_using_callable( + self.pkgpath, + self.func, + (args if args else []) + [config_meta, config, inst], + kwargs, + args_len=len(args), + ) + # Run the schema compiled function body + vm.run(run_current=True, ignore_nop=True) + + if SCHEMA_SETTINGS_ATTR_NAME not in inst: + inst.update({SCHEMA_SETTINGS_ATTR_NAME: self.settings}) + if SCHEMA_SETTINGS_ATTR_NAME in config: + inst.update( + {SCHEMA_SETTINGS_ATTR_NAME: config.get(SCHEMA_SETTINGS_ATTR_NAME)} + ) + + # Add settings attr + if not self.attrs: + self.attrs = {} + self.attrs[SCHEMA_SETTINGS_ATTR_NAME] = ( + inst.get(SCHEMA_SETTINGS_ATTR_NAME) or self.settings + ) + + # Do relaxed schema check and config patch + relaxed_keys = self.do_relaxed_check( + inst, config_meta, config, is_sub_schema, vm + ) + + # Record the schema name, runtime_type and relaxed + inst.update_info(self.name, self.runtime_type, self.is_relaxed) + + self.update_mixins(vm=vm) + + # Do all mixins expand execution after schema context + if self.mixins: + for mixin in self.mixins: + inst = mixin._new_instance( + config, config_meta, [], [], vm, inst, is_sub_schema=True + ) + + # Record the schema name, runtime_type and relaxed + inst.update_info(self.name, self.runtime_type, self.is_relaxed) + + # Record schema instance + if not self.__refs__: + self.__refs__ = [] + self.__refs__.append(inst) + + # Deal schema stmt queue + if not is_sub_schema and inst.stmt_buffer(): + buffers = inst.stmt_buffer() + func = KCLCompiledFunctionObject( + name=self.func.name, + params=self.func.params, + names=self.func.names, + constants=self.func.constants, + ) + relaxed = inst.is_relaxed + for buffer in buffers: + is_relaxed, pkg_path, codes = buffer + func.instructions = codes + inst.is_relaxed = is_relaxed + vm.push_frame_using_callable( + pkg_path, + func, + (args if args else []) + [config_meta, config, inst], + kwargs, + args_len=len(args), + ) + # Run the schema compiled function body + vm.run(run_current=True, ignore_nop=True) + inst.is_relaxed = relaxed + # Run all decorators + inst.run_all_decorators() + # Do all checks + if not is_sub_schema: + inst.operation_map = { + KCLSchemaReverseFields.SETTINGS: ast.ConfigEntryOperation.OVERRIDE + } + inst.update_attr_op_using_obj(config) + inst.check_optional_attrs() + self.do_check(inst, config_meta, config, relaxed_keys, vm) + # Return the schema object + return inst + + def do_args_type_check( + self, + args: List[KCLObject], + kwargs: List[KWArg], + config_meta: dict, + vm=None, + ): + """Check args type""" + + def check_arg_type(arg_name: str, value: KCLObject, expected_type: str): + checked, value_tpe = check_type(value, expected_type, vm) + if not checked: + if has_literal_type([expected_type]): + if isinstance( + value, + ( + KCLNoneObject, + KCLTrueObject, + KCLFalseObject, + KCLIntObject, + KCLFloatObject, + ), + ): + value_tpe = f"{value_tpe}({value.value})" + elif isinstance(value, KCLStringObject): + value_tpe = f'{value_tpe}("{value.value}")' + + conf_filename, conf_line, conf_column = ( + config_meta.get("$filename"), + config_meta.get("$lineno"), + config_meta.get("$columnno"), + ) + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=conf_filename, + line_no=conf_line, + col_no=conf_column, + ) + ], + arg_msg='argument "{}" expect {}, got {}'.format( + arg_name, + common.get_tpes_str([expected_type]).replace("@", ""), + common.get_class_name(value_tpe), + ), + ) + + if self.func.params: + for i, value in enumerate(args or []): + arg_name = self.func.params[i].name + expected_type = self.func.params[i].type_annotation + check_arg_type(arg_name, value, expected_type) + + for kwarg in kwargs or []: + arg_name = kwarg.name.value + value = kwarg.value + if arg_name not in [p.name for p in self.func.params]: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"schema arguments got an unexpected keyword argument '{arg_name}'", + ) + expected_types = [ + p.type_annotation for p in self.func.params if arg_name == p.name + ] + expected_type = expected_types[0] if expected_types else None + check_arg_type(arg_name, value, expected_type) + + def do_relaxed_check( + self, + inst: KCLSchemaObject, + config_meta: dict, + config: Union[dict, KCLDictObject], + is_sub_schema: bool, + vm, + ) -> List[str]: + """Do relaxed schema check and config patch""" + relaxed_keys = [] + if not is_sub_schema: + config_native = ( + config.value if isinstance(config, KCLDictObject) else config + ) + config_meta_native = config_meta + relaxed_keys = [ + key for key in config_native if key not in inst.value.keys() + ] + if self.protocol: + relaxed_keys = [ + key for key in relaxed_keys if key not in self.protocol.attr_list + ] + if self.is_relaxed or self.index_signature: + filename = vm.get_filename() + if self.index_signature and not self.index_signature.any_other: + for key in inst.value.keys(): + if key != SCHEMA_SETTINGS_ATTR_NAME: + value = inst.get(key) + checked, _ = check_type( + value, + self.index_signature.value_type, + vm=vm, + ) + if not checked: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[kcl_error.ErrFileMsg(filename=filename)], + arg_msg=f"the type '{value.type_str()}' of schema attribute '{key}' " + f"does not meet the index signature definition {self.index_signature.def_str()}", + ) + for key in relaxed_keys: + lineno, columnno = None, None + if key in config_meta_native: + lineno, columnno = ( + config_meta_native[key].get("lineno"), + config_meta_native[key].get("columnno"), + ) + value = config.get(key) + if self.index_signature and self.index_signature.value_type: + types = [self.index_signature.value_type] + from kclvm.vm.runtime.evaluator import union + + value = type_pack_and_check( + union( + deepcopy(self.index_signature.value), + value, + should_idempotent_check=True, + vm=vm, + ), + types, + filename, + lineno, + columnno, + vm=vm, + config_meta=config_meta, + ) + inst.update({key: value}) + + elif relaxed_keys: + lineno, columnno = None, None + if relaxed_keys[0] in config_meta_native: + lineno, columnno = ( + config_meta_native[relaxed_keys[0]].get("lineno"), + config_meta_native[relaxed_keys[0]].get("columnno"), + ) + kcl_error.report_exception( + err_type=kcl_error.ErrType.CannotAddMembers_Runtime_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=vm.get_filename(), line_no=lineno, col_no=columnno + ) + ], + arg_msg=kcl_error.CANNOT_ADD_MEMBERS_MSG.format( + ",".join([str(k) for k in relaxed_keys]), self.name + ), + ) + return relaxed_keys + + def do_check( + self, + inst: KCLSchemaObject, + config_meta: dict, + config: Union[dict, KCLDictObject], + relaxed_keys: List[str], + vm, + ): + assert inst, f"{inst}" + assert vm + + def call_check_fn(local_name: str = None, local_value: KCLObject = None): + if self.check_fn: + vm.push_frame_using_callable( + self.pkgpath, self.check_fn, [config_meta, config, inst], [] + ) + if local_name and local_value: + vm.update_local(local_name, local_value) + vm.run(run_current=True, ignore_nop=True) + + # check base + if self.base and isinstance(self.base, KCLSchemaTypeObject): + self.base.do_check(inst, config_meta, config, relaxed_keys, vm) + + # check mixin + for mixin in self.mixins or []: + mixin.do_check(inst, config_meta, config, relaxed_keys, vm) + + # check self + if self.index_signature and self.index_signature.key_name and relaxed_keys: + # For loop index signature attributes + for key in relaxed_keys: + call_check_fn(self.index_signature.key_name, to_kcl_obj(key)) + else: + call_check_fn() + + # Member Functions + + def instances(self, main_pkg: bool = True): + """Get all schema instances of self type and sub types""" + if not self.__refs__: + self.__refs__ = [] + return deepcopy( + [ + inst + for inst in self.__refs__ + if inst.instance_pkgpath == MAIN_MODULE_NAME + ] + if main_pkg + else self.__refs__ + ) + + def get_member_method(self, name: str): + from .function import KCLMemberFunctionObject + + if not name: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg="kcl string object member name can't be empty or None", + ) + if name not in self.MEMBER_FUNCTIONS: + kcl_error.report_exception( + err_type=kcl_error.ErrType.AttributeError_TYPE, + arg_msg=f"attribute '{name}' not found", + ) + return KCLMemberFunctionObject(obj=self, name=name) + + def call_member_method(self, name: str, *args, **kwargs): + if not hasattr(self, name) and name not in self.MEMBER_FUNCTIONS: + kcl_error.report_exception( + err_type=kcl_error.ErrType.AttributeError_TYPE, + arg_msg=f"attribute '{name}' not found", + ) + return getattr(self, name).__call__(*args, **kwargs) + + +@dataclass +class KCLSchemaDefTypeObject(KCLBaseTypeObject): + """Schema definition type denotes the schema definition type used in normal expressions. + + - `Person` of `data = Person.instances()` is a schema def type. + - `person` of `person = Person {}` is a schema type. + """ + + schema_type: KCLSchemaTypeObject + + def type_str(self) -> str: + """Get the object type""" + return self.schema_type.type_str() if self.schema_type else super().type_str() + + def type_kind(self) -> int: + """Get the""" + return KCLTypeKind.SchemaDefKind diff --git a/internal/kclvm_py/api/readme.md b/internal/kclvm_py/api/readme.md new file mode 100644 index 000000000..1333ed77b --- /dev/null +++ b/internal/kclvm_py/api/readme.md @@ -0,0 +1 @@ +TODO diff --git a/internal/kclvm_py/api/version/__init__.py b/internal/kclvm_py/api/version/__init__.py new file mode 100644 index 000000000..30175578d --- /dev/null +++ b/internal/kclvm_py/api/version/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .version import VERSION, CHECKSUM + +__all__ = ["VERSION", "CHECKSUM"] diff --git a/internal/kclvm_py/api/version/checksum.txt b/internal/kclvm_py/api/version/checksum.txt new file mode 100644 index 000000000..2046c0aee --- /dev/null +++ b/internal/kclvm_py/api/version/checksum.txt @@ -0,0 +1 @@ +e07ed7af0d9bd1e86a3131714e4bd20c \ No newline at end of file diff --git a/internal/kclvm_py/api/version/version.py b/internal/kclvm_py/api/version/version.py new file mode 100644 index 000000000..0d76f7cba --- /dev/null +++ b/internal/kclvm_py/api/version/version.py @@ -0,0 +1,7 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import os +from pathlib import Path + +VERSION = "0.4.1" +CHECKSUM = Path(f"{os.path.dirname(__file__)}/checksum.txt").read_text().strip() diff --git a/internal/kclvm_py/compiler/__init__.py b/internal/kclvm_py/compiler/__init__.py new file mode 100644 index 000000000..d74aaf8ad --- /dev/null +++ b/internal/kclvm_py/compiler/__init__.py @@ -0,0 +1 @@ +# Copyright 2021 The KCL Authors. All rights reserved. diff --git a/internal/kclvm_py/compiler/astutil/__init__.py b/internal/kclvm_py/compiler/astutil/__init__.py new file mode 100644 index 000000000..dc57c7e76 --- /dev/null +++ b/internal/kclvm_py/compiler/astutil/__init__.py @@ -0,0 +1,23 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .builder import BuildLitNodeFromString, BuildLitNodeFromValue, BuildNodeFromString +from .filter import Declaration, filter_declarations, filter_stmt +from .fix import ( + fix_set_parent_info, + fix_qualified_identifier, + fix_and_get_module_import_list, + fix_test_schema_auto_relaxed, +) + +__all__ = [ + "BuildLitNodeFromString", + "BuildLitNodeFromValue", + "BuildNodeFromString", + "Declaration", + "filter_declarations", + "filter_stmt", + "fix_set_parent_info", + "fix_qualified_identifier", + "fix_and_get_module_import_list", + "fix_test_schema_auto_relaxed", +] diff --git a/internal/kclvm_py/compiler/astutil/builder.py b/internal/kclvm_py/compiler/astutil/builder.py new file mode 100644 index 000000000..20c46a7e0 --- /dev/null +++ b/internal/kclvm_py/compiler/astutil/builder.py @@ -0,0 +1,90 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import re +from ast import literal_eval +from typing import Union + +import kclvm.kcl.ast as ast +import kclvm.compiler.parser as parser +import kclvm.api.object.internal as internal + + +def BuildNodeFromString(value: str, line: int = None, column: int = None) -> ast.Expr: + lit_node = BuildLitNodeFromString(value, line, column) + if isinstance(lit_node, ast.StringLit): + try: + val = parser.ParseExpr(code=value) + # If `val` is a identifier, convert it to a string literal + return lit_node if isinstance(val, ast.Identifier) else val + except Exception: + return lit_node + return lit_node + + +def BuildLitNodeFromValue( + value: Union[int, float, str, bool], line: int = None, column: int = None +) -> ast.Literal: + if value is None: + val = ast.NameConstantLit() + val.value = None + elif value is internal.Undefined: + val = ast.NameConstantLit() + val.value = internal.Undefined + elif value is True: + val = ast.NameConstantLit() + val.value = True + elif value is False: + val = ast.NameConstantLit() + val.value = False + elif isinstance(value, (int, float)): + val = ast.NumberLit(value=value) + else: + val = ast.StringLit() + val.value = value if isinstance(value, str) else str(value) + val.line = line + val.column = column + return val + + +def BuildLitNodeFromString( + value: str, line: int = None, column: int = None +) -> ast.Literal: + if value in ["True", "true"]: + val = ast.NameConstantLit() + val.value = True + elif value in ["False", "false"]: + val = ast.NameConstantLit() + val.value = False + elif value in ["None", "null"]: + val = ast.NameConstantLit() + val.value = None + elif value in ["Undefined"]: + val = ast.NameConstantLit() + val.value = internal.Undefined + elif is_number(value): + val = ast.NumberLit(value=literal_eval(value)) + else: + val = ast.StringLit() + val.value = str(value) + val.raw_value = value + + if val.value and val.value[0] == "'" and val.value[-1] == "'": + val.value = val.value[1:-1] + elif val.value and val.value[0] == '"' and val.value[-1] == '"': + val.value = val.value[1:-1] + + if ( + val.raw_value + and val.raw_value[0] not in ["'", '"'] + and val.raw_value[-1] not in ["'", '"'] + ): + val.raw_value = '"' + val.raw_value.replace('"', '\\"') + '"' + val.line = line + val.column = column + return val + + +def is_number(value: str): + """Whether a string is a number string""" + pattern = re.compile(r"^[-+]?[-0-9]\d*\.\d*|[-+]?\.?[0-9]\d*$") + return bool(pattern.match(value)) diff --git a/internal/kclvm_py/compiler/astutil/filter.py b/internal/kclvm_py/compiler/astutil/filter.py new file mode 100644 index 000000000..d9c24730d --- /dev/null +++ b/internal/kclvm_py/compiler/astutil/filter.py @@ -0,0 +1,76 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import cast, List, Union, Optional, Type +from dataclasses import dataclass + +import kclvm.kcl.ast as ast + + +@dataclass +class Declaration: + filename: str + name: str + value: ast.Expr + is_union: bool + + +def filter_declarations( + module: ast.Module, + ast_type: Optional[Union[Type[ast.AST], str, tuple, list]] = None, +) -> List[Declaration]: + """Get all global AssignStmt key-value pair config according to the `ast_type`. + When the `ast_type` is None, select all declarations + """ + if not module or not isinstance(module, ast.Module): + return [] + declaration_list = [] + for stmt in module.body or []: + declaration = None + if isinstance(stmt, ast.AssignStmt): + stmt = cast(ast.AssignStmt, stmt) + for target in stmt.targets: + name = target.get_name() + if target.ctx == ast.ExprContext.STORE: + value = cast(ast.Expr, stmt.value) + declaration = Declaration( + filename=stmt.filename, + name=name, + value=value, + is_union=False, + ) + elif isinstance(stmt, ast.UnificationStmt): + stmt = cast(ast.UnificationStmt, stmt) + name = stmt.target.get_name() + value = cast(ast.Expr, stmt.value) + declaration = Declaration( + filename=stmt.filename, name=name, value=stmt.value, is_union=True + ) + if declaration: + if ast_type is None: + declaration_list.append(declaration) + elif isinstance(ast_type, (list, tuple)) and isinstance( + stmt.value, tuple(ast_type) + ): + declaration_list.append(declaration) + elif isinstance(ast_type, str) and value.type == ast_type: + declaration_list.append(declaration) + elif isinstance(ast_type, type(ast.AST)) and isinstance( + stmt.value, ast_type + ): + declaration_list.append(declaration) + return declaration_list + + +def filter_stmt( + module: ast.Module, stmt_type: Union[str, Type[ast.Stmt]] +) -> List[ast.Stmt]: + """Get all AugAssignStmt at the top level of the module""" + if not module or not isinstance(module, ast.Module): + return [] + if not stmt_type: + return [] + result = [] + for stmt in module.body or []: + if stmt.type == stmt_type or isinstance(stmt, stmt_type): + result.append(stmt) + return result diff --git a/internal/kclvm_py/compiler/astutil/fix.py b/internal/kclvm_py/compiler/astutil/fix.py new file mode 100644 index 000000000..a38ac7ce1 --- /dev/null +++ b/internal/kclvm_py/compiler/astutil/fix.py @@ -0,0 +1,486 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import re +import typing +import copy +from collections import OrderedDict + +import kclvm.kcl.error as kcl_error +import kclvm.kcl.info as kcl_info +import kclvm.kcl.ast as ast +import kclvm.compiler.parser.lark_parser as lark_parser +import kclvm.compiler.vfs as vfs + +PKGPATH_IDENTIFIER_DOT_REGEX = r"[\d\w_]+\." +PKGPATH_DOT_REGEX = r"@[\d\w_\.]+\." + + +def _get_global_names(m: ast.Module) -> typing.List[str]: + assert m + assert isinstance(m, ast.Module) + + global_name_dict: typing.Dict[str, ast.AST] = OrderedDict() + + def walkFn_global(t: ast.AST) -> typing.Optional[typing.Callable]: + nonlocal global_name_dict + + if isinstance(t, (ast.SchemaStmt, ast.RuleStmt)): + node = t + if kcl_info.isprivate_field(node.name) or node.name not in global_name_dict: + global_name_dict[node.name] = node + else: + kcl_error.report_exception( + err_type=kcl_error.ErrType.UniqueKeyError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=lark_parser.filename, + line_no=node.line, + col_no=node.column, + ) + ], + arg_msg=kcl_error.UNIQUE_KEY_MSG.format(node.name), + ) + return None + + if isinstance(t, (ast.SchemaExpr, ast.LambdaExpr)): + return None + + if isinstance(t, ast.ImportStmt): + return None + + if isinstance(t, (ast.ListComp, ast.DictComp)): + return None + + if isinstance(t, ast.AssignStmt): + node = typing.cast(ast.AssignStmt, t) + for expr in node.targets: + if not isinstance(expr, ast.Identifier) or isinstance( + node.value, ast.LambdaExpr + ): + continue + + ident = typing.cast(ast.Identifier, expr) + is_config = isinstance(node.value, ast.SchemaExpr) + if ( + kcl_info.isprivate_field(ident.names[0]) + or (ident.names[0] not in global_name_dict) + or is_config + ): + global_name_dict[ident.names[0]] = node + else: + kcl_error.report_exception( + err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=lark_parser.filename, + line_no=ident.line, + col_no=ident.column, + end_col_no=ident.end_column, + ) + ], + ) + + # continue walk + return walkFn_global + + # walk tree + ast.WalkTree(m, walkFn_global) + + # dict to list + return list(global_name_dict.keys()) + + +def _get_schema_local_names( + schema: typing.Union[ast.SchemaStmt, ast.RuleStmt] +) -> typing.List[str]: + assert schema + assert isinstance(schema, (ast.SchemaStmt, ast.RuleStmt)) + + local_name_dict: typing.Dict[str, ast.AST] = OrderedDict() + + # walk args + if schema.args: + for x in schema.args.args: + assert len(x.names) == 1, f"schema.args={schema.args}" + local_name_dict[x.names[0]] = x + + def walkFn_schema_local(t: ast.AST) -> typing.Optional[typing.Callable]: + if isinstance(t, ast.SchemaAttr): + node = typing.cast(ast.SchemaAttr, t) + local_name_dict[node.name] = node + return None + + if isinstance(t, ast.AssignStmt): + node = typing.cast(ast.AssignStmt, t) + + # a = b = c.d = value + for expr in node.targets: + if not isinstance(expr, ast.Identifier): + continue + + ident = typing.cast(ast.Identifier, expr) + + # skip: c.d = value + if len(ident.names) == 1: + local_name_dict[ident.names[0]] = ident + + # continue walk + return walkFn_schema_local + + # walk tree + ast.WalkTree(schema, walkFn_schema_local) + + return list(local_name_dict.keys()) + + +def fix_set_parent_info(m: ast.Module): + """ + set parent info on ast + :param m: target module ast + """ + + def _walk(t: ast.AST): + def _set_parent( + parent: ast.AST, inner: typing.Union[typing.List, typing.Dict, ast.AST] + ): + if isinstance(inner, list): + [_set_parent(parent, item) for item in inner] + return + if isinstance(inner, dict): + [_set_parent(parent, v) for _, v in inner] + return + if isinstance(inner, ast.AST): + inner.parent = parent + _walk(inner) + + for _, value in ast.iter_fields(t): + _set_parent(t, value) + + assert m and isinstance(m, ast.Module) + return _walk(m) + + +def fix_qualified_identifier( + m: ast.Module, *, import_names: typing.Optional[typing.Dict[str, str]] = None +): + """ + import path.to.pkg as pkgname + + x = pkgname.Name + """ + # 0. init import names + if import_names is None or not isinstance(import_names, dict): + import_names = {} + for import_spec in m.GetImportList(): + import_names[import_spec.name] = import_spec.path + + # 1. init global names + _global_names = _get_global_names(m) + for name in _global_names: + if name not in m.global_names: + m.global_names.append(name) + + # 2. init schema local name + _schema_local_names: typing.Dict[str, typing.List[str]] = {} + for schema in m.GetSchemaAndRuleList(): + _schema_local_names[schema.name] = _get_schema_local_names(schema) + if schema.name not in m.local_names: + m.local_names = _schema_local_names + + current_schema_name = "" + generator_local_vars = [] + + def walkFn_fix_global_ident(t: ast.AST) -> typing.Optional[typing.Callable]: + if isinstance(t, (ast.DictComp, ast.ListComp)): + for gen in t.generators or []: + for ident in gen.targets: + generator_local_vars.append(ident.get_first_name()) + ast.WalkTree(gen, walkFn_fix_global_ident) + if isinstance(t, ast.ListComp): + ast.WalkTree(t.elt, walkFn_fix_global_ident) + if isinstance(t, ast.DictComp): + ast.WalkTree(t.key, walkFn_fix_global_ident) + ast.WalkTree(t.value, walkFn_fix_global_ident) + generator_local_vars.clear() + return None + elif isinstance(t, ast.QuantExpr): + for ident in t.variables: + generator_local_vars.append(ident.get_first_name()) + ast.WalkTree(t.target, walkFn_fix_global_ident) + ast.WalkTree(t.test, walkFn_fix_global_ident) + ast.WalkTree(t.if_cond, walkFn_fix_global_ident) + generator_local_vars.clear() + if not isinstance(t, ast.Identifier): + return walkFn_fix_global_ident + + ident = typing.cast(ast.Identifier, t) + if len(ident.names) < 2: + return None + + # skip global name and generator local variables in list/dict comp and quant expression + if ident.names[0] in _global_names or ident.names[0] in generator_local_vars: + return None + + # fix qualified identifier + if ident.names[0] in import_names: + ident.pkgpath = import_names[ident.names[0]] + + return None + + def walkFn_fix_schema_ident(t: ast.AST) -> typing.Optional[typing.Callable]: + nonlocal current_schema_name + assert current_schema_name, f"current_schema_name={current_schema_name}" + + if not isinstance(t, ast.Identifier): + return walkFn_fix_global_ident + + ident = typing.cast(ast.Identifier, t) + if len(ident.names) < 2: + return None + + # skip local name + _local_names = _schema_local_names[current_schema_name] + if ident.names[0] in _local_names: + return None + + # skip global name + if ident.names[0] in _global_names: + return None + + # fix qualified identifier + if ident.names[0] in import_names: + ident.pkgpath = import_names[ident.names[0]] + + return None + + # ----------------------------------------------------- + + # 3. fix all ident + for stmt in m.body or []: + if isinstance(stmt, (ast.SchemaStmt, ast.RuleStmt)): + node = stmt + current_schema_name = node.name + ast.WalkTree(node, walkFn_fix_schema_ident) + current_schema_name = "" + continue + + ast.WalkTree(stmt, walkFn_fix_global_ident) + + # OK + return + + +def fix_and_get_module_import_list( + root: str, m: ast.Module, is_fix: bool = True, reversed: bool = False +) -> typing.List[ast.ImportStmt]: + assert m + assert isinstance(m, ast.Module) + assert m.pkg + + import_spec_list: typing.List[ast.ImportStmt] = [] + pkgpath_table = {} + + for stmt in m.body or []: + if not isinstance(stmt, ast.ImportStmt): + continue + + if is_fix: + assert stmt.path + assert stmt.pkg_name + + stmt.rawpath = stmt.path + + stmt.path = vfs.FixImportPath(root, m.filename, stmt.path) + stmt.name = stmt.pkg_name + if reversed: + pkgpath_table[stmt.path] = stmt.name + else: + pkgpath_table[stmt.name] = stmt.path + + import_spec = copy.deepcopy(stmt) + import_spec_list.append(import_spec) + + if not is_fix: + return import_spec_list + + # fix types name + # asname.Name => @abs.pkg.Name + # [asname.Name] => [@abs.pkg.Name] + # {str:asname.Name} => {str:@abs.pkg.Name} + # {str:[asname.Name]} => {str:[@abs.pkg.Name]} + # asname1.Name1 | asname2.Name2 => @abs.pkg1.Name1 | @abs.pkg2.Name2 + for stmt in m.body or []: + if isinstance(stmt, ast.AssignStmt): + assign_stmt = typing.cast(ast.AssignStmt, stmt) + if assign_stmt.type_annotation: + if reversed: + assign_stmt.type_annotation = re.sub( + PKGPATH_DOT_REGEX, + lambda x: f"{pkgpath_table[x.group()[1:-1]]}." + if x.group()[1:-1] in pkgpath_table + else x.group(), + assign_stmt.type_annotation, + ) + else: + assign_stmt.type_annotation = re.sub( + PKGPATH_IDENTIFIER_DOT_REGEX, + lambda x: f"@{pkgpath_table[x.group()[:-1]]}." + if x.group()[:-1] in pkgpath_table + else x.group(), + assign_stmt.type_annotation, + ) + elif isinstance(stmt, ast.SchemaStmt): + schema_stmt = typing.cast(ast.SchemaStmt, stmt) + # Fix schema arguments type + if schema_stmt.args and schema_stmt.args.type_annotation_list: + for i, _type in enumerate(schema_stmt.args.type_annotation_list): + # if the `_type` is None, the schema argument has no any type annotation + if not _type: + continue + if reversed: + schema_stmt.args.type_annotation_list[i] = re.sub( + PKGPATH_DOT_REGEX, + lambda x: f"{pkgpath_table[x.group()[1:-1]]}." + if x.group()[1:-1] in pkgpath_table + else x.group(), + _type, + ) + else: + schema_stmt.args.type_annotation_list[i] = re.sub( + PKGPATH_IDENTIFIER_DOT_REGEX, + lambda x: f"@{pkgpath_table[x.group()[:-1]]}." + if x.group()[:-1] in pkgpath_table + else x.group(), + _type, + ) + # Fix schame attr type + for attr in schema_stmt.body or []: + if not isinstance(attr, ast.SchemaAttr): + continue + schema_attr = typing.cast(ast.SchemaAttr, attr) + _type = schema_attr.type_str + if reversed: + schema_attr.type_str = re.sub( + PKGPATH_DOT_REGEX, + lambda x: f"{pkgpath_table[x.group()[1:-1]]}." + if x.group()[1:-1] in pkgpath_table + else x.group(), + _type, + ) + else: + schema_attr.type_str = re.sub( + PKGPATH_IDENTIFIER_DOT_REGEX, + lambda x: f"@{pkgpath_table[x.group()[:-1]]}." + if x.group()[:-1] in pkgpath_table + else x.group(), + _type, + ) + elif isinstance(stmt, ast.RuleStmt): + rule_stmt = typing.cast(ast.RuleStmt, stmt) + if rule_stmt.args and rule_stmt.args.type_annotation_list: + for i, _type in enumerate(rule_stmt.args.type_annotation_list): + # if the `_type` is None, the rule argument has no any type annotation + if not _type: + continue + if reversed: + rule_stmt.args.type_annotation_list[i] = re.sub( + PKGPATH_DOT_REGEX, + lambda x: f"{pkgpath_table[x.group()[1:-1]]}." + if x.group()[1:-1] in pkgpath_table + else x.group(), + _type, + ) + else: + rule_stmt.args.type_annotation_list[i] = re.sub( + PKGPATH_IDENTIFIER_DOT_REGEX, + lambda x: f"@{pkgpath_table[x.group()[:-1]]}." + if x.group()[:-1] in pkgpath_table + else x.group(), + _type, + ) + elif isinstance(stmt, ast.TypeAliasStmt): + # Fix rule arguments type + type_alias_stmt = typing.cast(ast.TypeAliasStmt, stmt) + if type_alias_stmt.type_value.plain_type_str: + if reversed: + type_alias_stmt.type_value.plain_type_str = re.sub( + PKGPATH_DOT_REGEX, + lambda x: f"{pkgpath_table[x.group()[1:-1]]}." + if x.group()[1:-1] in pkgpath_table + else x.group(), + type_alias_stmt.type_value.plain_type_str, + ) + else: + type_alias_stmt.type_value.plain_type_str = re.sub( + PKGPATH_IDENTIFIER_DOT_REGEX, + lambda x: f"@{pkgpath_table[x.group()[:-1]]}." + if x.group()[:-1] in pkgpath_table + else x.group(), + type_alias_stmt.type_value.plain_type_str, + ) + + class TypeNameTransformer(ast.TreeTransformer): + def walk_LambdaExpr(self, node: ast.LambdaExpr): + if node.args and node.args.type_annotation_list: + for i, _type in enumerate(node.args.type_annotation_list): + # if the `_type` is None, the schema argument has no any type annotation + if not _type: + continue + if reversed: + node.args.type_annotation_list[i] = re.sub( + PKGPATH_DOT_REGEX, + lambda x: f"{pkgpath_table[x.group()[1:-1]]}." + if x.group()[1:-1] in pkgpath_table + else x.group(), + _type, + ) + else: + node.args.type_annotation_list[i] = re.sub( + PKGPATH_IDENTIFIER_DOT_REGEX, + lambda x: f"@{pkgpath_table[x.group()[:-1]]}." + if x.group()[:-1] in pkgpath_table + else x.group(), + _type, + ) + if node.return_type_str: + if reversed: + node.return_type_str = re.sub( + PKGPATH_DOT_REGEX, + lambda x: f"{pkgpath_table[x.group()[1:-1]]}." + if x.group()[1:-1] in pkgpath_table + else x.group(), + node.return_type_str, + ) + else: + node.return_type_str = re.sub( + PKGPATH_IDENTIFIER_DOT_REGEX, + lambda x: f"@{pkgpath_table[x.group()[:-1]]}." + if x.group()[:-1] in pkgpath_table + else x.group(), + node.return_type_str, + ) + return node + + TypeNameTransformer().walk(m) + + return import_spec_list + + +def fix_test_schema_auto_relaxed(m: ast.Module): + if not m.filename.endswith("_test.k"): + return + + for stmt in m.body or []: + if not isinstance(stmt, ast.SchemaStmt): + continue + + schema = typing.cast(ast.SchemaStmt, stmt) + if schema.name.startswith("Test"): + for x in schema.body or []: + if not isinstance(x, ast.SchemaAttr): + continue + attr = typing.cast(ast.SchemaAttr, x) + attr.type_str = "" + attr.is_optional = True + + return diff --git a/internal/kclvm_py/compiler/build/__init__.py b/internal/kclvm_py/compiler/build/__init__.py new file mode 100644 index 000000000..d74aaf8ad --- /dev/null +++ b/internal/kclvm_py/compiler/build/__init__.py @@ -0,0 +1 @@ +# Copyright 2021 The KCL Authors. All rights reserved. diff --git a/internal/kclvm_py/compiler/build/compiler.py b/internal/kclvm_py/compiler/build/compiler.py new file mode 100644 index 000000000..cbbd67774 --- /dev/null +++ b/internal/kclvm_py/compiler/build/compiler.py @@ -0,0 +1,2219 @@ +"""The `compiler` file mainly contains the function `CompileProgram` +which is used to compile the AST obtained by the parser module into +KCL bytecode. + +The KCL compiler is mainly based on `ast.TreeWalker` to implement +traversal of all AST nodes, perform semantic checks and generate +corresponding bytecodes, and implement scope checks based on the +symbol table. + +The main compilation process is to use `ast.TreeTransformer` to +preprocess the AST, such as eliminating syntactic sugar, checking +import, VFS path mapping, and configuration merging, etc. Then +generate the corresponding KCL bytecode, which mainly includes opcode, +operand, name memory, object memory, etc. The KCL bytecode is input +into the KCL virtual machine for execution and the result is obtained. + +:note: When the definition of any AST node is modified or the AST node +is added/deleted, it is necessary to modify the corresponding processing +in the compiler walk_{AST Name} methods. +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" + +import typing + +from dataclasses import dataclass +from typing import Callable, Any, List, Dict, Optional, Union + +import kclvm.kcl.ast as ast +import kclvm.kcl.error as kcl_error +import kclvm.api.object as objpkg +import kclvm.api.object.internal as obj_internal +import kclvm.compiler.vfs as vfs +import kclvm.compiler.extension.builtin as builtin +import kclvm.compiler.astutil.fix as fix +import kclvm.vm.code as vm +import kclvm.unification as unification +import kclvm.tools.query as query + +from kclvm.api.object.internal import Undefined +from kclvm.kcl.types import ResolveProgram, ProgramScope, ANY_TYPE, parse_type_str +from kclvm.compiler.build.symtable import SymbolTable, SymbolScope +from kclvm.compiler.build.utils import units +from kclvm.internal.util import CheckRules + +from kclvm.compiler.build.data import ( + CMP_OP_MAPPING, + BIN_OP_MAPPING, + UNARY_OP_MAPPING, + ARG_OP_MAPPING, + EXPR_OP_MAPPING, + SUBSCR_OP_MAPPING, + SYMBOL_SCOPE_LOAD_OP_MAPPING, + SYMBOL_SCOPE_STORE_OP_MAPPING, + CompilerInternalErrorMeta, + SchemaConfigMeta, +) + + +_COMPILE_ERROR = kcl_error.ErrType.CompileError_TYPE +_BODY_ATTR = "body" +_EXPRS_ATTR = "exprs" + +LAMBDA_FUNC_NAME = "" +RESERVED_IDENTIFIERS = [ + "True", + "False", + "None", + "Undefined", +] +LITERAL_EXPRS = ( + ast.NumberLit, + ast.StringLit, + ast.NameConstantLit, + ast.QuantExpr, + ast.ListExpr, + ast.ListComp, + ast.DictComp, +) + + +@dataclass +class RuntimeCode(objpkg.KCLObject): + """ + Runtime code is a temporary structure for storing compilation results. + """ + + names: List[str] + constants: List[objpkg.KCLObject] + codes: List[int] + + def type(self) -> objpkg.KCLObjectType: + """ + Get the object type + """ + return objpkg.KCLObjectType.RUNTIME_CODE + + def type_str(self) -> str: + """ + Get the object type + """ + return "runtime_code" + + +# ----------------------------------------------------------------------------- +# _CompilerBase +# ----------------------------------------------------------------------------- + + +class _CompilerBase(ast.TreeWalker): + """_ComplierBase function""" + + def __init__(self, filename=""): + super().__init__() + + self.pkgpath: str = "" + + # File information + self.filename: str = filename + self.lineno: int = 0 + self.colno: int = 0 + # Compiler parameters + self.names: List[str] = [] + self.constants: List[objpkg.KCLObject] = builtin.get_builtin_func_objects() + # Symbol table + self.symtable: SymbolTable = SymbolTable.new_with_built_in() + # Compile scope level + self.scopes: list = [vm.CompilationScope(instructions=[])] + # In schema expression level + self._is_in_schema_exprs: List[bool] = [False] + # In schema statement level + self._is_in_schema_stmt: List[bool] = [False] + # In lambda expression level + self._is_in_lambda_expr: List[bool] = [False] + # In if statement + self._is_in_if_stmt: List[bool] = [False] + # Local vars + self._local_vars: List[str] = [] + # Schema func body and check cache + self._schema_build_cache: Dict[str, objpkg.RuntimeCode] = {} + # Lambda temp var index + self._lambda_temp_var_index = 0 + + # Base walker functions + + def generic_walk(self, t: ast.AST): + """Called if no explicit walker function exists for a node.""" + if not isinstance(t, ast.AST): + kcl_error.report_exception( + err_type=_COMPILE_ERROR, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=self.filename, + line_no=self.lineno, + col_no=self.colno, + ) + ], + arg_msg=CompilerInternalErrorMeta.INVALID_KCL_AST_MSG, + ) + if hasattr(t, _BODY_ATTR): + for n in t.body: + self.walk(n) + elif hasattr(t, _EXPRS_ATTR): + for n in t.exprs: + self.walk(n) + else: + self.walk(t) + + def update_line_column(self, t: ast.AST): + self.filename = t.filename or self.filename + self.lineno = t.get_line() if t.get_line() else self.lineno + self.colno = t.get_column() if t.get_column() else self.colno + + def expr_or_load_none(self, t: ast.Expr): + if t: + self.expr(t) + else: + self.load_constant(None) + + def stmt_or_load_none(self, t: ast.Stmt): + if t: + self.stmt(t) + else: + self.load_constant(None) + + def expr(self, t: ast.Expr): + if not t: + return + self.update_line_column(t) + self.walk(t) + + def stmt(self, t: ast.Stmt): + if not t: + return + self.update_line_column(t) + self.walk(t) + + def exprs(self, exprs: List[ast.Expr]): + if not exprs: + return + assert isinstance(exprs, list) + for expr in exprs: + self.expr(expr) + + def stmts(self, stmts: List[ast.Stmt]): + if not stmts: + return + assert isinstance(stmts, list) + for stmt in stmts: + self.stmt(stmt) + + # Util functions + + def get_node_name(self, t: ast.AST): + """Get the ast.AST node name""" + assert isinstance(t, ast.AST) + return t.type + + def raise_err(self, msg: str = ""): + """Raise a KCL compile error""" + msg = msg if msg else CompilerInternalErrorMeta.INVALID_KCL_AST_MSG + kcl_error.report_exception( + err_type=_COMPILE_ERROR, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=self.filename, + line_no=self.lineno, + col_no=self.colno, + ) + ], + arg_msg=msg, + ) + + # Emit functions + + def enter_scope(self): + """ + Enter scope such as internal of function and schema + """ + scope = vm.CompilationScope(instructions=[]) + self.scopes.append(scope) + self.symtable = SymbolTable.new(self.symtable, self.symtable.num_definitions) + + def leave_scope(self) -> List[int]: + """ + Leave scope + """ + if not self.scopes: + self.raise_err(CompilerInternalErrorMeta.INVALID_GLOBAL_IMPLICIT_SCOPE) + instructions = self.current_instruction() + self.scopes.pop() + self.symtable.outer.num_definitions = self.symtable.num_definitions + self.symtable = self.symtable.outer + return instructions # Return internal scope instructions + + def current_instruction(self) -> List[int]: + """Get the current instruction""" + return self.scopes[-1].instructions if self.scopes else [] + + def add_instruction(self, ins: List[int]) -> int: + """ + Add instructions into the current compile scope + """ + if not self.scopes: + self.raise_err(CompilerInternalErrorMeta.INVALID_GLOBAL_IMPLICIT_SCOPE) + pos = len(self.current_instruction()) + if not ins: + return pos + self.scopes[-1].instructions.extend( + ins + [(self.filename, self.lineno, self.colno)] + ) + return pos + + def add_constant(self, cst: objpkg.KCLObject) -> int: + """ + Add a KCLObject constant into the constant list + """ + self.constants.append(cst) + return len(self.constants) + + def add_name(self, name: str) -> int: + """ + Add a identifier string into the name list + """ + self.names.append(name) + return len(self.names) + + def change_operand(self, op: int, op_pos: int, operand: int): + """ + Change the operand in index 'op_pos' + """ + current_instruction = self.current_instruction() + if op_pos > len(current_instruction) + vm.InstructionWithArg.size() - 1: + self.raise_err(CompilerInternalErrorMeta.INVALID_OP_POS.format(op_pos)) + assert op == current_instruction[op_pos] + inst = vm.InstructionWithArg(op=vm.Opcode(op), lineno=self.lineno, arg=operand) + current_instruction[ + op_pos : op_pos + vm.InstructionWithArg.size() + ] = inst.output() + + def operand(self, operand1: int = 0, operand2: int = 0, operand3: int = 0): + """ + Build a total operand using operands + """ + assert 0 <= operand1 <= 255 and 0 <= operand2 <= 255 and 0 <= operand3 <= 255 + return operand1 + (operand2 << 8) + (operand3 << 16) + + def emit(self, op: vm.Opcode, operand: Optional[int] = None) -> int: + """ + Generate byte code and operand + + Parameters + --------- + op: operation code + operand: operand + """ + ins = ( + [op, (self.filename, self.lineno, self.colno)] + if operand is None + else [ + op, + (operand & 0xFF), + ((operand >> 8) & 0xFF), + ((operand >> 16) & 0xFF), + (self.filename, self.lineno, self.colno), + ] + ) + pos = len(self.scopes[-1].instructions) + self.scopes[-1].instructions.extend(ins) + return pos + + # Emit function object and call + + def make_func_with_content( + self, + content_func: Callable, + name: str, + args: ast.Arguments = None, + cached_name: str = None, + ): + if not content_func or not isinstance(content_func, Callable): + raise Exception(f"invalid function body {content_func}") + free_symbols = [] + argc = 0 + if args: + + def _check_defaults_legal(): + mark = False + for j, default in enumerate(reversed(args.defaults)): + if default is None: + mark = True + if default is not None and mark is True: + kcl_error.report_exception( + err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=args.filename, + line_no=default.line, + col_no=args.args[len(args.defaults) - j - 1].column, + end_col_no=default.end_column, + arg_msg="A default argument", + ) + ], + arg_msg="non-default argument follows default argument", + ) + + CheckRules.check_list_len_equal( + [args.args, args.defaults, args.type_annotation_list] + ) + _check_defaults_legal() + arg_defaults = len([default for default in args.defaults if default]) + argc = self.operand(len(args.args), arg_defaults, 0) + for i, _ in enumerate(args.args): + self.load_constant(args.GetArgName(i)) + self.load_constant(args.GetArgType(i)) + self.load_constant(args.GetArgDefault(i)) + if cached_name and cached_name in self._schema_build_cache: + num_locals = 0 + count = self.add_constant(self._schema_build_cache[cached_name]) + else: + self.enter_scope() + if args: + for arg in args.args: + self.symtable.define(arg.get_name(), scope=SymbolScope.LOCAL) + self.add_name(arg.get_name()) + # self.expr(arg) + content_func(args) + free_symbols = self.symtable.free_symbols + instructions = self.leave_scope() + runtime_code = RuntimeCode( + codes=instructions, + names=self.names, + constants=self.constants, + ) + count = self.add_constant(runtime_code) + if cached_name and cached_name not in self._schema_build_cache: + self._schema_build_cache[cached_name] = runtime_code + num_locals = len(free_symbols) + if num_locals > 0: + for symbol in free_symbols: + self.emit(vm.Opcode.LOAD_CLOSURE, symbol.index) + self.emit(vm.Opcode.BUILD_LIST, num_locals) + # Load code + self.emit(vm.Opcode.LOAD_CONST, count - 1) + # Load function/closure name + self.load_constant(name) + self.emit( + vm.Opcode.MAKE_FUNCTION if num_locals == 0 else vm.Opcode.MAKE_CLOSURE, argc + ) + + def emit_call( + self, + args: List[ast.Expr], + keywords: List[ast.Keyword], + ): + self.exprs(args) + check_table = set() + for kw in keywords: + if kw in check_table: + self.raise_err(CompilerInternalErrorMeta.DUPLICATED_KW.format(kw.arg)) + check_table.add(kw) + self.load_constant(kw.arg.names[0]) + self.expr(kw.value) + op = vm.Opcode.CALL_FUNCTION + self.emit(op, len(args) + (len(keywords) << 8)) + + # Jump and label Instructions + + def set_jmp(self, op: vm.Opcode, label: vm.Label) -> int: + inst = None + if op in [ + vm.Opcode.JUMP_IF_FALSE_OR_POP, + vm.Opcode.JUMP_IF_TRUE_OR_POP, + vm.Opcode.JUMP_ABSOLUTE, + vm.Opcode.POP_JUMP_IF_FALSE, + vm.Opcode.POP_JUMP_IF_TRUE, + ]: + inst = vm.JumpAbs( + op=op, + dest=label, + filename=self.filename, + lineno=self.lineno, + colno=self.colno, + ) + elif op in [ + vm.Opcode.JUMP_FORWARD, + vm.Opcode.FOR_ITER, + ]: + inst = vm.JumpRel( + op=op, + dest=label, + filename=self.filename, + lineno=self.lineno, + colno=self.colno, + ) + else: + self.raise_err(CompilerInternalErrorMeta.INVALID_ARGED_OP_CODE.format(op)) + pos = self.add_instruction(inst.output()) + return pos + + def op_jmp(self, op: vm.Opcode, label: vm.Label): + pos = self.set_jmp(op, label) + label.number = op + label.pos = pos + + def op_label(self, label: vm.Label) -> int: + assert isinstance(label, vm.Label) + if label.number is not None: + self.change_operand( + label.number, label.pos, len(self.current_instruction()) + ) + return self.add_instruction(label.output()) + + def set_label(self, label: vm.Label): + assert isinstance(label, vm.Label) + label.pos = len(self.current_instruction()) + label.number = None + return self.add_instruction(label.output()) + + # Decorator + + def op_decorator( + self, + name: str, + key: str, + args: ast.CallExpr, + target: obj_internal.DecoratorTargetType, + ): + if not name: + kcl_error.report_exception( + err_type=_COMPILE_ERROR, + arg_msg="decorator name can't be None", + ) + decorator = objpkg.KCLDecoratorObject(name=name, target=target, key=key) + if args: + self.exprs(args.args) + check_table = set() + for kw in args.keywords: + if kw in check_table: + self.raise_err( + CompilerInternalErrorMeta.DUPLICATED_KW.format(kw.arg) + ) + check_table.add(kw) + self.load_constant(kw.arg.names[0]) + self.expr(kw.value) + n = self.operand(len(args.args), len(args.keywords)) + self.load_constant(decorator) + self.emit(vm.Opcode.MAKE_DECORATOR, n) + else: + self.load_constant(decorator) + self.emit(vm.Opcode.MAKE_DECORATOR, 0) + + # Symbol operations + + def store_symbol( + self, + name: str, + *, + scope: SymbolScope = None, + do_check: bool = True, + init_global_name: bool = False, + ) -> int: + symbol, exist = self.symtable.define(name, scope) + symbol.define_count = 0 if init_global_name else (symbol.define_count + 1) + if exist and do_check: + if symbol.define_count > 1: + # Variable name 'a' must be unique in package context + kcl_error.report_exception( + err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=self.filename, + line_no=self.lineno, + col_no=self.colno, + ) + ], + ) + index = self.add_name(name) - 1 + if symbol.scope == SymbolScope.INTERNAL: + return index + op = SYMBOL_SCOPE_STORE_OP_MAPPING.get(symbol.scope) + if not op: + self.raise_err(CompilerInternalErrorMeta.INVALID_GLOBAL_IMPLICIT_SCOPE) + self.emit(op, index) + return index + + def load_symbol(self, name: str, emit: bool = True): + """ + Identifier symbol e.g., a, b, and c + """ + if not name: + self.raise_err(CompilerInternalErrorMeta.INVALID_NAME) + symbol = self.symtable.resolve(name) + if not symbol: + self.raise_err(CompilerInternalErrorMeta.SYMBOL_NOT_DEFINED.format(name)) + code = SYMBOL_SCOPE_LOAD_OP_MAPPING.get(symbol.scope) + if not code: + self.raise_err( + CompilerInternalErrorMeta.INVALID_SYMBOL_SCOPE.format(symbol.scope) + ) + if emit: + self.emit(code, symbol.index) + return symbol + + def op_name(self, op: vm.Opcode, name: str): + self.symtable.define(name, SymbolScope.INTERNAL) + index = self.add_name(name) - 1 + self.emit(op, index) + # Leave the inner attr scope, delete the variable from the symbol table. + self.symtable.delete(name, SymbolScope.INTERNAL) + + # Object constant operations + + def load_constant(self, value: Any): + """ + Runtime Literal constant e.g., 1, 1.1 and None + """ + obj = objpkg.to_kcl_obj(value) + count = self.add_constant(obj) + self.emit(vm.Opcode.LOAD_CONST, count - 1) + + def compile_program(self, prog: ast.Program) -> Optional[objpkg.KCLProgram]: + p = objpkg.KCLProgram( + root=prog.root, + main=prog.main, + ) + for pkgpath in prog.pkgs: + # Symbol table + self.symtable: SymbolTable = SymbolTable.new_with_built_in() + self.symtable.num_definitions = len(self.names) + # Compile scope level + self.scopes: list = [vm.CompilationScope(instructions=[])] + self.pkg_scope = self.program_scope.scope_map[pkgpath] + self.compile(pkgpath, prog.pkgs[pkgpath]) + p.pkgs[pkgpath] = objpkg.KCLBytecode( + names=self.names, + constants=self.constants, + instructions=self.current_instruction(), + ) + return p + + def compile(self, pkgpath: str, m_list: List[ast.Module]) -> Optional[RuntimeCode]: + assert pkgpath + assert m_list + self.pkgpath = pkgpath + + # Define global names + for m in m_list: + self.filename = m.filename + # Global schema and rule names + schema_rule_names = {n.name for n in m.GetSchemaAndRuleList()} + for name in m.global_names: + self.load_constant(Undefined) + self.store_symbol(name, init_global_name=True) + if name not in schema_rule_names: + self.symtable.delete(name, SymbolScope.GLOBAL) + + # Do import + for m in m_list: + self.filename = m.filename + for stmt in m.body: + if isinstance(stmt, ast.ImportStmt): + self.update_line_column(stmt) + import_spec = typing.cast(ast.ImportStmt, stmt) + self.load_constant(0) + self.load_constant(None) + + self.emit( + vm.Opcode.IMPORT_NAME, + self.store_symbol( + import_spec.path, + scope=SymbolScope.LOCAL, + init_global_name=True, + ), + ) + self.store_symbol( + f"@{import_spec.path}", + scope=SymbolScope.GLOBAL, + init_global_name=True, + ) + + # Define schema type + for m in m_list: + self.filename = m.filename + for stmt in m.body: + if isinstance(stmt, ast.SchemaStmt): + self.stmt(stmt) + elif isinstance(stmt, ast.RuleStmt): + self.stmt(stmt) + + # Define schema type twice + for m in m_list: + self.filename = m.filename + for stmt in m.body: + if isinstance(stmt, ast.SchemaStmt): + self.stmt(stmt) + elif isinstance(stmt, ast.RuleStmt): + self.stmt(stmt) + + # Exec stmt + for m in m_list: + self.filename = m.filename + for stmt in m.body: + self.stmt(stmt) + + +# ----------------------------------------------------------------------------- +# Compiler +# ----------------------------------------------------------------------------- + + +@dataclass +class Compiler(_CompilerBase): + """The Compiler class used to build code object, which will be + consumed by the virtual machine. + + It is mainly composed of code that traverses the tree, and + bytecode-related functions are defined in _ComplierBase. + """ + + def __init__(self, program_scope: ProgramScope, filename=""): + super().__init__(filename) + self.program_scope: ProgramScope = program_scope + self.pkg_scope = program_scope.scope_map[ast.Program.MAIN_PKGPATH] + + def get_type_from_identifier(self, t: ast.Identifier): + if not t or not isinstance(t, ast.Identifier): + return ANY_TYPE + tpe = parse_type_str(t.get_name()) + if not isinstance(tpe, objpkg.KCLNamedTypeObject): + return tpe + if len(t.names) == 1: + name = t.names[0] + if name in self.pkg_scope.elems: + return self.pkg_scope.elems[name].type + return ANY_TYPE + elif len(t.names) == 2: + pkgpath = t.pkgpath + name = t.names[1] + if pkgpath in self.pkg_scope.elems: + tpe = self.pkg_scope.elems[pkgpath].type + if ( + not tpe + or not isinstance(tpe, objpkg.KCLModuleTypeObject) + or tpe.pkgpath not in self.program_scope.scope_map + or name not in self.program_scope.scope_map[tpe.pkgpath].elems + ): + return ANY_TYPE + return self.program_scope.scope_map[tpe.pkgpath].elems[name].type + return ANY_TYPE + self.raise_err(msg="Invalid as keyword right identifier") + + # Walker functions + def walk_Module(self, t: ast.Module): + assert isinstance(t, ast.Module) + self.filename = t.filename + self.stmts(t.body) + + def walk_ExprStmt(self, t: ast.ExprStmt): + """ast.AST: ExprStmt""" + assert isinstance(t, ast.ExprStmt) + exprs = t.exprs + for expr in exprs: + # Ignore the doc string + if isinstance(expr, ast.StringLit): + continue + # Insert nop op before the expr statement + if self._is_in_schema_stmt[-1] and not self._is_in_if_stmt[-1]: + self.emit(vm.Opcode.SCHEMA_NOP) + self.expr(expr) + # Lambda expression temp variable + if self._is_in_lambda_expr[-1]: + self.store_symbol(f"@{self._lambda_temp_var_index}") + self._lambda_temp_var_index += 1 + # Store lambda expr variable and pop the stored value + self.emit(vm.Opcode.POP_TOP) + # If it is a literal and pop it from the stack except in the lambda expression + elif isinstance(expr, LITERAL_EXPRS) or isinstance(expr, ast.CallExpr): + self.emit(vm.Opcode.POP_TOP) + elif isinstance(expr, ast.SchemaExpr): + self.emit(vm.Opcode.EMIT_EXPR) + # Insert nop op after the expr statement + if self._is_in_schema_stmt[-1] and not self._is_in_if_stmt[-1]: + self.emit(vm.Opcode.SCHEMA_NOP) + + def walk_AssertStmt(self, t: ast.AssertStmt): + """ast.AST: AssertStmt + + Parameters + ---------- + test: Optional[Expr] + if_cond: Optional[Expr] + msg: Optional[Expr] + """ + assert isinstance(t, ast.AssertStmt) and t.test + + label_if_cond = vm.Label() + + if t.if_cond: + self.expr(t.if_cond) + self.op_jmp(vm.Opcode.POP_JUMP_IF_FALSE, label_if_cond) + + self.expr(t.test) + label = vm.Label() + self.op_jmp(vm.Opcode.POP_JUMP_IF_TRUE, label) + self.expr_or_load_none(t.msg) + self.emit(vm.Opcode.RAISE_VARARGS, 1) + self.op_label(label) + + if t.if_cond: + self.op_label(label_if_cond) + + def walk_IfStmt(self, t: ast.IfStmt): + """ast.AST: IfStmt + + Parameters + ---------- + - cond: Expr + - body: List[Stmt] + - elif_cond: List[Expr] + - elif_body: List[List[Stmt]] + - else_body: List[Stmt] + + Instructions: + ------------ + - vm.Opcode.POP_JUMP_IF_FALSE {body} + + """ + assert isinstance(t, ast.IfStmt) + assert t.cond + assert t.body + self.expr(t.cond) + self._is_in_if_stmt.append(True) + jump_if_false_label = vm.Label() + jump_last_labels = [vm.Label()] + # If condition + self.op_jmp(vm.Opcode.POP_JUMP_IF_FALSE, jump_if_false_label) + self.stmts(t.body) + self.op_jmp(vm.Opcode.JUMP_FORWARD, jump_last_labels[0]) + self.op_label(jump_if_false_label) + # Elif list + for elif_cond, elif_body in zip(t.elif_cond, t.elif_body): + self.expr(elif_cond) + jump_elif_false_label = vm.Label() + self.op_jmp(vm.Opcode.POP_JUMP_IF_FALSE, jump_elif_false_label) + self.stmts(elif_body) + jump_last_label = vm.Label() + self.op_jmp(vm.Opcode.JUMP_FORWARD, jump_last_label) + jump_last_labels.append(jump_last_label) + self.op_label(jump_elif_false_label) + self.stmts(t.else_body) + # After else + for label in jump_last_labels: + self.op_label(label) + self._is_in_if_stmt.pop() + if self._is_in_schema_stmt[-1]: + self.emit(vm.Opcode.SCHEMA_NOP) + + def walk_ImportStmt(self, t: ast.ImportStmt): + """ast.AST: ImportStmt + + Parameters + --------- + - path: str + - name: str + - asname: str + + Instructions + ------ + - vm.Opcode.IMPORT_NAME {symbol_index} 0 0 + + StackLayout + ----------- + TOS + - asname + - name + """ + assert isinstance(t, ast.ImportStmt) + assert t.pkg_name + + import_spec = typing.cast(ast.ImportStmt, t) + self.load_constant(0) + self.load_constant(None) + if self.pkgpath == "__main__": + self.emit( + vm.Opcode.IMPORT_NAME, + self.store_symbol( + import_spec.path, + scope=SymbolScope.LOCAL, + init_global_name=True, + ), + ) + self.store_symbol( + import_spec.pkg_name, + scope=SymbolScope.LOCAL, + init_global_name=True, + ) + else: + self.emit( + vm.Opcode.IMPORT_NAME, + self.store_symbol( + import_spec.path, + scope=SymbolScope.GLOBAL, + init_global_name=True, + ), + ) + self.store_symbol( + import_spec.pkg_name, + scope=SymbolScope.GLOBAL, + init_global_name=True, + ) + + def walk_RuleStmt(self, t: ast.RuleStmt): + """ast.AST: RuleStmt + + Parameters + ---------- + - doc: str = "" + - name: str = "" + - parent_rules: List[Identifier] = [] + - decorators: List[Decorator] = [] + - checks: List[CheckExpr] = [] + - name_node: Optional[Name] = None + - args: Optional[Arguments] = None + - for_host_name: Optional[Identifier] = None + + Stack Layout + ------------ + TOS + - 6. index signature + - 5. decorator list + - 4. check func + - 3. schema_body_func + - 2. mixin type object list + - 1. parent_type_obj + - 0. self type object + BOS + """ + assert isinstance(t, ast.RuleStmt) + assert self.pkgpath + # The schema type object + schema_type_obj = self.pkg_scope.elems.get(t.name).type.schema_type + + def schema_body_func(args: ast.Arguments): + # Store magic variables including config, config_meta and schema self pointer + magic_argument_list = [ + objpkg.SCHEMA_SELF_VALUE_KEY, + objpkg.SCHEMA_CONFIG_VALUE_KEY, + objpkg.SCHEMA_CONFIG_META_KEY, + ] + for key in magic_argument_list: + self.store_symbol(key) + self.emit(vm.Opcode.POP_TOP) + self.emit(vm.Opcode.SCHEMA_NOP) + # Pop frame and return the schema object + self.emit(vm.Opcode.RETURN_VALUE) + + def schema_check_func(_args: ast.Arguments): + # Store magic variables including config, config_meta and schema self pointer + magic_argument_list = [ + objpkg.SCHEMA_SELF_VALUE_KEY, + objpkg.SCHEMA_CONFIG_VALUE_KEY, + objpkg.SCHEMA_CONFIG_META_KEY, + ] + + for key in magic_argument_list: + self.store_symbol(key) + self.emit(vm.Opcode.POP_TOP) + + for check in t.checks or []: + self.expr(check) + + self.emit(vm.Opcode.RETURN_VALUE) + + schema_type_obj.attr_obj_map = {} + schema_type_obj.node_ref = None + self.load_constant(schema_type_obj) + # Rule statement has no schema parent name + self.load_constant(None) + # Parent rules + for rule in t.parent_rules or []: + rule_names = rule.names + if rule.pkgpath: + rule_names[0] = f"@{rule.pkgpath}" + self.load_constant(".".join(rule_names)) + rule_count = len(t.parent_rules) if t.parent_rules else 0 + # In schema level push + self._is_in_schema_stmt.append(True) + # Rule statement has not body func + # Schema body function including schema args, attribute context + self.make_func_with_content( + schema_body_func, + t.name, + t.args, + cached_name=schema_type_obj.runtime_type + "body", + ) + # Rule check expressions + if t.checks: + self.make_func_with_content( + schema_check_func, + t.name, + cached_name=schema_type_obj.runtime_type + "check", + ) + else: + self.load_constant(None) + + # Decorators + for decorator in t.decorators or []: + self.op_decorator( + decorator.name.get_name(), + t.name, + decorator.args, + obj_internal.DecoratorTargetType.SCHEMA_TYPE, + ) + decorator_count = len(t.decorators) if t.decorators else 0 + # Rule statement has no index signature + self.load_constant(None) + self.emit( + vm.Opcode.MAKE_SCHEMA, + self.operand(decorator_count, rule_count, 0), + ) + # Store the schema type object to the schema name symbol + self.store_symbol(t.name, init_global_name=True) + # In schema level pop + self._is_in_schema_stmt.pop() + + def walk_SchemaStmt(self, t: ast.SchemaStmt): + """ast.AST: SchemaStmt + + Parameters + ---------- + - doc: str + - name: str + - parent_name: Identifier + - is_mixin: bool + - args: Arguments + - settings: dict + - mixins: List[str] + - body: List[Union[SchemaAttr, Stmt]] + - decorators: List[Decorator] + - checks: List[CheckExpr] + + Stack Layout + ------------ + TOS + - 6. index signature + - 5. decorator list + - 4. check func + - 3. schema_body_func + - 2. mixin type object list + - 1. parent_type_obj + - 0. self type object + BOS + + vm.Opcode + ------ + {vm.Opcode.MAKE_SCHEMA} {decorator count} {mixin count} {attr count} -> SchemaTypeObject + """ + assert isinstance(t, ast.SchemaStmt) + assert self.pkgpath + # The schema type object + schema_type_obj = self.pkg_scope.elems.get(t.name).type.schema_type + + def schema_body_func(args: ast.Arguments): + # Store magic variables including config, config_meta and schema self pointer + magic_argument_list = [ + objpkg.SCHEMA_SELF_VALUE_KEY, + objpkg.SCHEMA_CONFIG_VALUE_KEY, + objpkg.SCHEMA_CONFIG_META_KEY, + ] + + for key in magic_argument_list: + self.store_symbol(key) + self.emit(vm.Opcode.POP_TOP) + + self.emit(vm.Opcode.SCHEMA_NOP) + # Emit schema context body including schema attribute declaration and expression + self.stmts(t.body) + self.emit(vm.Opcode.SCHEMA_NOP) + # Pop frame and return the schema object + self.emit(vm.Opcode.RETURN_VALUE) + + def schema_check_func(_args: ast.Arguments): + # Store magic variables including config, config_meta and schema self pointer + magic_argument_list = [ + objpkg.SCHEMA_SELF_VALUE_KEY, + objpkg.SCHEMA_CONFIG_VALUE_KEY, + objpkg.SCHEMA_CONFIG_META_KEY, + ] + + for key in magic_argument_list: + self.store_symbol(key) + self.emit(vm.Opcode.POP_TOP) + + for check in t.checks or []: + self.expr(check) + + self.emit(vm.Opcode.RETURN_VALUE) + + schema_type_obj.attr_obj_map = {} + schema_type_obj.node_ref = None + # Get the parent type obj of the schema if exist + self.load_constant(schema_type_obj) + self.expr_or_load_none(t.parent_name) + parent_name_str = t.parent_name.get_name() if t.parent_name else "" + if parent_name_str.endswith("Mixin"): + kcl_error.report_exception( + err_type=kcl_error.ErrType.IllegalInheritError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=self.filename, + line_no=t.line, + col_no=t.column, + end_col_no=t.end_column, + ) + ], + arg_msg=f"mixin inheritance {parent_name_str} is prohibited", + ) + + # Mixins + for mixin in t.mixins or []: + mixin_names = mixin.names + if mixin.pkgpath: + mixin_names[0] = f"@{mixin.pkgpath}" + + if not mixin_names[-1].endswith("Mixin"): + kcl_error.report_exception( + err_type=kcl_error.ErrType.MixinNamingError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=self.filename, + line_no=mixin.line, + col_no=mixin.column, + end_col_no=mixin.end_column, + ) + ], + arg_msg=f"a valid mixin name should end with 'Mixin', got '{mixin_names[-1]}'", + ) + + self.load_constant(".".join(mixin_names)) + mixin_count = len(t.mixins) if t.mixins else 0 + + # In schema level push + self._is_in_schema_stmt.append(True) + + # Schema body function including schema args, attribute context + self.make_func_with_content( + schema_body_func, + t.name, + t.args, + cached_name=schema_type_obj.runtime_type + "body", + ) + + # Schema check function + if t.checks: + self.make_func_with_content( + schema_check_func, + t.name, + cached_name=schema_type_obj.runtime_type + "check", + ) + else: + self.load_constant(None) + + # Decorators + for decorator in t.decorators or []: + self.op_decorator( + decorator.name.get_name(), + t.name, + decorator.args, + obj_internal.DecoratorTargetType.SCHEMA_TYPE, + ) + decorator_count = len(t.decorators) if t.decorators else 0 + # Index signature + self.stmt_or_load_none(t.index_signature) + self.emit( + vm.Opcode.MAKE_SCHEMA, + self.operand(decorator_count, mixin_count, 0), + ) + # Store the schema type object to the schema name symbol + self.store_symbol(t.name, init_global_name=True) + # In schema level pop + self._is_in_schema_stmt.pop() + + def walk_SchemaAttr(self, t: ast.SchemaAttr): + """ast.AST: SchemaAttr + + Parameters + ---------- + - doc: str + - name: str + - type_str: str + - is_optional: bool + - value: Expr + - decorators: List[Decorator] + - op: Union[AugOp, Assign] + + StackLayout + ----------- + TOS + - decorators + - types + - attr_name + - default + - is_optional + - op: vm.Opcode. + """ + self._local_vars = [] + self.load_constant(ARG_OP_MAPPING.get(t.op)) + # Optional + self.load_constant(bool(t.is_optional)) + # Final + self.load_constant(False) + # Has default + self.load_constant(bool(t.value)) + # Default value + self.expr_or_load_none(t.value) + # Attr name + self.load_constant(t.name) + # Attr type + self.load_constant(t.type_str) + # Decorators + for decorator in t.decorators or []: + self.op_decorator( + decorator.name.get_name(), + t.name, + decorator.args, + obj_internal.DecoratorTargetType.ATTRIBUTE, + ) + self.emit(vm.Opcode.SCHEMA_ATTR, len(t.decorators)) + self.emit(vm.Opcode.SCHEMA_NOP) + + def walk_SchemaIndexSignature(self, t: ast.SchemaIndexSignature): + """ast.AST: SchemaIndexSignature + + Parameters + ---------- + - key_name: Optional[str] = None + - key_type: Optional[str] = "str" + - value_type: Optional[str] = "" + - value: Optional[Expr] = None + - any_other: bool = False + """ + assert isinstance(t, ast.SchemaIndexSignature) + if not t.key_type or t.key_type not in ["str", "float", "int"]: + kcl_error.report_exception( + err_type=kcl_error.ErrType.IndexSignatureError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=self.filename, + line_no=t.get_line(), + col_no=t.get_column(), + end_col_no=t.get_end_column(), + ) + ], + arg_msg='the index signature parameter type must be "str", "int" or "float"', + ) + self.expr_or_load_none(t.value) + self.load_constant(t.any_other) + self.load_constant(t.key_name) + self.load_constant(t.value_type) + self.load_constant(t.key_type) + + def walk_IfExpr(self, t: ast.IfExpr): + """ast.AST: IfExpr + + Parameters + ---------- + - cond: Expr + - body: Expr + - orelse: Expr + """ + assert isinstance(t, ast.IfExpr) + self.expr(t.cond) + jump_if_false_label = vm.Label() + self.op_jmp(vm.Opcode.POP_JUMP_IF_FALSE, jump_if_false_label) + self.expr(t.body) + jump_last_label = vm.Label() + self.op_jmp(vm.Opcode.JUMP_FORWARD, jump_last_label) + self.op_label(jump_if_false_label) + self.expr(t.orelse) + self.op_label(jump_last_label) + + def walk_UnaryExpr(self, t: ast.UnaryExpr): + """ast.AST: UnaryExpr(Expr) + + Parameters + ---------- + - op: UnaryOp + - operand: Expr + """ + assert isinstance(t, ast.UnaryExpr) + opcode = UNARY_OP_MAPPING.get(t.op) + if not opcode: + self.raise_err(CompilerInternalErrorMeta.UNKNOWN_UNARYOP.format(t.op)) + self.expr(t.operand) + self.emit(opcode) + + def walk_BinaryExpr(self, t: ast.BinaryExpr): + """ast.AST: BinaryExpr + + Parameters + ---------- + - left: Expr + - right: Expr + - op: BinaryOperator + + StackLayout + ----------- + TOS + - right + - left + """ + assert isinstance(t, ast.BinaryExpr) and t.left and t.right and t.op + op = BIN_OP_MAPPING.get(t.op) + if not op: + self.raise_err(CompilerInternalErrorMeta.UNKNOWN_BINOP.format(t.op)) + if op == vm.Opcode.BINARY_LOGIC_AND or op == vm.Opcode.BINARY_LOGIC_OR: + # LogicExpr + op = ( + vm.Opcode.JUMP_IF_FALSE_OR_POP + if op == vm.Opcode.BINARY_LOGIC_AND + else vm.Opcode.JUMP_IF_TRUE_OR_POP + ) + values = [t.left, t.right] + label = vm.Label() + for i, e in enumerate(values): + self.expr(e) + if i != len(values) - 1: + self.op_jmp(op, label) + self.op_label(label) + else: + # BinaryExpr + self.expr(t.left) + if op == vm.Opcode.MEMBER_SHIP_AS: + type_object = self.get_type_from_identifier(t.right) + self.load_constant(type_object) + else: + self.expr(t.right) + # Update the op filename/line/column meta + self.update_line_column(t) + self.emit(op) + + def walk_SelectorExpr(self, t: ast.SelectorExpr): + """ast.AST: SelectorExpr + + Parameters + ---------- + - value: Expr + - attr: Identifier + - ctx: ExprContext + - has_question: bool + """ + assert isinstance(t, ast.SelectorExpr) + jump_if_false_label = vm.Label() + if t.has_question: + self.expr(t.value) # value is the condition + self.op_jmp(vm.Opcode.POP_JUMP_IF_FALSE, jump_if_false_label) + + if t.ctx != ast.ExprContext.AUGSTORE: + self.expr(t.value) + op = EXPR_OP_MAPPING.get(t.ctx) + if not op: + self.raise_err( + CompilerInternalErrorMeta.INVALID_PARAM_IN_ATTR.format(t.ctx) + ) + if t.ctx == ast.ExprContext.AUGLOAD: + self.emit(vm.Opcode.DUP_TOP) + elif t.ctx == ast.ExprContext.AUGSTORE: + self.emit(vm.Opcode.ROT_TWO) + + self.op_name(op, t.attr.get_name()) + + if t.has_question: + jump_last_label = vm.Label() + self.op_jmp(vm.Opcode.JUMP_FORWARD, jump_last_label) + self.op_label(jump_if_false_label) + self.load_constant(None) + self.op_label(jump_last_label) + + def walk_CallExpr(self, t: ast.CallExpr): + """ast.AST: CallExpr + + Parameters + ---------- + - func: Expr + - args: List[Expr] + - keywords: List[Keyword] + """ + assert isinstance(t, ast.CallExpr) + self.expr(t.func) + self.emit_call(t.args, t.keywords) + + def walk_Subscript(self, t: ast.Subscript): + """ast.AST: Subscript + + Parameters + ---------- + - value: Expr + - index: Expr + - lower: Expr + - upper: Expr + - step: Expr + - has_question: bool + """ + assert isinstance(t, ast.Subscript) + jump_if_false_label = vm.Label() + if t.has_question: + self.expr(t.value) # value is the condition + self.op_jmp(vm.Opcode.POP_JUMP_IF_FALSE, jump_if_false_label) + + if t.ctx != ast.ExprContext.AUGSTORE: + self.expr(t.value) + if t.index: + self.expr(t.index) + else: + n = 2 + for expr in [t.lower, t.upper]: + self.expr_or_load_none(expr) + if t.step: + n += 1 + self.expr(t.step) + self.emit(vm.Opcode.BUILD_SLICE, n) + opcodes = SUBSCR_OP_MAPPING.get(t.ctx) + if not opcodes: + self.raise_err( + CompilerInternalErrorMeta.INVALID_PARAM_IN_SUBSCR.format(t.ctx) + ) + for op in opcodes: + self.emit(op) + + if t.has_question: + jump_last_label = vm.Label() + self.op_jmp(vm.Opcode.JUMP_FORWARD, jump_last_label) + self.op_label(jump_if_false_label) + self.load_constant(None) + self.op_label(jump_last_label) + + def walk_ParenExpr(self, t: ast.ParenExpr): + """ast.AST: ParenExpr + + Parameters + ---------- + - expr: Expr + """ + assert isinstance(t, ast.ParenExpr) + self.expr(t.expr) + + def walk_QuantExpr(self, t: ast.QuantExpr): + """ast.AST: QuantExpr + + Parameters + ---------- + - target: Expr + - variables: List[Identifier] + - op: QuantOperation + - test: Optional[Expr] + - if_cond: Optional[Expr] + - ctx: ExprContext + + Notes + ----- + For different quantifier operations, results are different + any/all: bool + map: list + filter: list/dict/schema + """ + assert isinstance(t, ast.QuantExpr) + + # Quantifier expression initial result + if t.op in [ast.QuantOperation.ALL, ast.QuantOperation.ANY]: + self.load_constant(t.op == ast.QuantOperation.ALL) + elif t.op == ast.QuantOperation.MAP: + self.emit(vm.Opcode.BUILD_LIST, 0) + elif t.op == ast.QuantOperation.FILTER: + self.expr(t.target) + else: + self.raise_err(CompilerInternalErrorMeta.INVALID_QUANTIFIER_OP.format(t.op)) + + # Jump labels + start = vm.Label() + end_for = vm.Label() + all_any_end = vm.Label() + + # Copy collection value to be filtered + if t.op == ast.QuantOperation.FILTER: + self.emit(vm.Opcode.COPY_TOP) + self.emit(vm.Opcode.ROT_TWO) + self.emit(vm.Opcode.POP_TOP) + + # Iter the loop target + self.expr(t.target) + self.emit(vm.Opcode.GET_ITER, len(t.variables)) + + # Mark the beginning of for-loop + self.set_label(start) + # Declare iter and the mapping end of iter + self.op_jmp(vm.Opcode.FOR_ITER, end_for) + + # Push loop variables, such as filter k, v in data:' + key_name = None + val_name = None + for i, v in enumerate(t.variables): + name = v.get_name(False) + key_name = name if i == 0 else key_name + val_name = name if i == 1 else val_name + self.update_line_column(v) + self.store_symbol(name, scope=SymbolScope.LOCAL) + self._local_vars.append(name) + # POP the temp var_key variable + self.emit(vm.Opcode.POP_TOP) + + # QuantExpr inner or_test [IF or_test] + label_if_cond = vm.Label() + # Expression filter jump condition + if t.if_cond: + self.expr(t.if_cond) + self.op_jmp(vm.Opcode.POP_JUMP_IF_FALSE, label_if_cond) + + # Loop body if exist + if t.test: + self.expr(t.test) + if t.op in [ast.QuantOperation.ALL, ast.QuantOperation.ANY]: + self.op_jmp( + vm.Opcode.POP_JUMP_IF_FALSE + if t.op == ast.QuantOperation.ALL + else vm.Opcode.POP_JUMP_IF_TRUE, + all_any_end, + ) + elif t.op == ast.QuantOperation.MAP: + # Operand 2 denote the distance of the list to be mapped and TOS + self.emit(vm.Opcode.LIST_APPEND, 2) + elif t.op == ast.QuantOperation.FILTER: + filter_label = vm.Label() + self.op_jmp(vm.Opcode.POP_JUMP_IF_TRUE, filter_label) + # Copy the list/dict/schema loop variable + self.load_symbol(key_name) + if val_name: + self.load_symbol(val_name) + self.load_constant(True) + else: + self.load_constant(None) + self.load_constant(False) + # Operand 3 denote the distance of the list to be filtered and TOS + self.emit(vm.Opcode.DELETE_ITEM, 5) + self.op_label(filter_label) + + # Expression filter jump label + if t.if_cond: + self.op_label(label_if_cond) + # To next cycle + self.set_jmp(vm.Opcode.JUMP_ABSOLUTE, start) # Mark start + # Mark for-loop else constant + if t.op in [ast.QuantOperation.ALL, ast.QuantOperation.ANY]: + self.op_label(all_any_end) + self.emit(vm.Opcode.POP_TOP) + # Pop the initial value of the empty all/any value + self.emit(vm.Opcode.POP_TOP) + self.load_constant(t.op == ast.QuantOperation.ANY) + # Mark the end of for-loop + self.op_label(end_for) + + # Delete temp loop variables + for v in t.variables: + name = v.get_name(False) + self.symtable.delete(name, SymbolScope.LOCAL) + self._local_vars = [] + + def walk_ListExpr(self, t: ast.ListExpr): + """ast.AST: ListExpr + + Parameters + ---------- + - elts: List[Expr] + """ + assert isinstance(t, ast.ListExpr) + self.exprs(t.elts) + self.emit(vm.Opcode.BUILD_LIST, len(t.elts)) + + def walk_ListIfItemExpr(self, t: ast.ListIfItemExpr): + """ast.AST: ListIfItemExpr + + Parameters + ---------- + if_cond: Optional[Expr] = None + exprs: List[Expr] = [] + orelse: Optional[Expr] = None + + if condition item1 elif item2 else item3 + """ + assert isinstance(t, ast.ListIfItemExpr) + self.expr(t.if_cond) + jump_if_false_label = vm.Label() + jump_last_label = vm.Label() + self.op_jmp(vm.Opcode.POP_JUMP_IF_FALSE, jump_if_false_label) + self.exprs(t.exprs) + self.emit(vm.Opcode.BUILD_LIST, len(t.exprs)) + self.emit(vm.Opcode.UNPACK_SEQUENCE, 1) + self.op_jmp(vm.Opcode.JUMP_FORWARD, jump_last_label) + self.op_label(jump_if_false_label) + if t.orelse: + # Add the orelse item into the list + self.expr(t.orelse) + else: + # *None denotes do not add None into the list + self.load_constant(None) + self.emit(vm.Opcode.UNPACK_SEQUENCE, 1) + if t.orelse and not isinstance(t.orelse, ast.ListIfItemExpr): + self.emit(vm.Opcode.UNPACK_SEQUENCE, 1) + self.op_label(jump_last_label) + + def walk_ConfigExpr(self, t: ast.ConfigExpr): + """ast.AST: ConfigExpr + + Parameters + ---------- + - items: List[ConfigEntry] + """ + assert isinstance(t, ast.ConfigExpr) + self.op_config_data(t) + + def walk_ConfigIfEntryExpr(self, t: ast.ConfigIfEntryExpr): + """ast.AST: ConfigIfEntryExpr + + Parameters + ---------- + if_cond: Optional[Expr] = None + keys: List[Expr] = [] + values: List[Expr] = [] + operations: List[Expr] = [] + orelse: Optional[Expr] + + if condition: key: value -> **({key: value} if condition else self.expr(orelse)) + """ + assert isinstance(t, ast.ConfigIfEntryExpr) + self.expr(t.if_cond) + jump_if_false_label = vm.Label() + self.op_jmp(vm.Opcode.POP_JUMP_IF_FALSE, jump_if_false_label) + self.op_config_data_entries(t.keys, t.values, t.operations) + jump_last_label = vm.Label() + self.op_jmp(vm.Opcode.JUMP_FORWARD, jump_last_label) + self.op_label(jump_if_false_label) + self.expr_or_load_none(t.orelse) + self.op_label(jump_last_label) + + def walk_StarredExpr(self, t: ast.StarredExpr): + assert isinstance(t, ast.StarredExpr) and t.value + self.expr(t.value) + self.emit(vm.Opcode.UNPACK_SEQUENCE, 1) + + def comp_generator( + self, + generators: List[ast.CompClause], + gen_index: int, + elt: ast.Expr, + val: Optional[ast.Expr], + op: ast.ConfigEntryOperation, + node: Union[ast.ListComp, ast.DictComp], + ): + start = vm.Label() + end_for = vm.Label() + gen = generators[gen_index] + + variable_count = len(gen.targets) + assert 0 < variable_count <= 2 + + self.expr(gen.iter) + self.emit(vm.Opcode.GET_ITER, variable_count) + + self.set_label(start) + # Declare iter and the mapping end of iter + self.op_jmp(vm.Opcode.FOR_ITER, end_for) + # Push target, such as i in 'for i in [1,2]' + for target in gen.targets: + target_name = target.get_name(False) + self.update_line_column(target) + self.store_symbol( + target_name, scope=SymbolScope.LOCAL + ) # Target in for_comp is a local variable + self._local_vars.append(target_name) + self.emit(vm.Opcode.POP_TOP) # POP the temp target variable + + for e in gen.ifs: + self.expr(e) + self.set_jmp(vm.Opcode.POP_JUMP_IF_FALSE, start) + + gen_index += 1 + if gen_index >= len(generators): + if isinstance(node, ast.ListComp): + self.expr(elt) + self.emit(vm.Opcode.LIST_APPEND, int(gen_index + 1)) + elif isinstance(node, ast.DictComp): + self.expr(val) + self.expr(elt) + self.load_constant(op) + self.emit(vm.Opcode.MAP_ADD, int(gen_index + 1)) + else: + self.raise_err(CompilerInternalErrorMeta.UNKNOWN_COMP.format(node)) + else: + self.comp_generator(generators, gen_index, elt, val, op, node) + # To next cycle + self.set_jmp(vm.Opcode.JUMP_ABSOLUTE, start) # Mark start + # Mark the end of for-loop + self.op_label(end_for) + for target in gen.targets: + target_name = target.get_name(False) + self.symtable.delete(target_name, SymbolScope.LOCAL) + self._local_vars = [] + + def walk_ListComp(self, t: ast.ListComp): + """ast.AST: ListComp + + Parameters + ---------- + - elt: Expr + - generators: List[CompClause] + - targets: List[Expr] + - iter: Expr + - ifs: List[Expr] + """ + assert isinstance(t, ast.ListComp) + self.emit(vm.Opcode.BUILD_LIST, 0) + self.comp_generator(t.generators, 0, t.elt, None, None, t) + + def walk_DictComp(self, t: ast.DictComp): + """ast.AST: DictComp + + Parameters + ---------- + - key: Expr + - value: Expr + - generators: List[CompClause] + """ + assert isinstance(t, ast.DictComp) + self.emit(vm.Opcode.BUILD_MAP, 0) + self.comp_generator(t.generators, 0, t.key, t.value, t.operation, t) + + def get_schema_conf_meta( + self, + n: typing.Optional[ast.Identifier], + t: ast.ConfigExpr, + ): + """Print the schema conf meta""" + conf_meta = {} + if n: + conf_meta[SchemaConfigMeta.FILENAME] = self.filename + conf_meta[SchemaConfigMeta.LINE] = n.line + conf_meta[SchemaConfigMeta.COLUMN] = n.column + if isinstance(t, ast.ConfigExpr): + for k, v in zip(t.keys, t.values): + if not k: + # Double star unpack expression + continue + if isinstance(k, ast.Identifier): + name = k.get_first_name() + elif isinstance(k, ast.Literal): + name = str(k.value) + else: + name = str(k) + conf_meta[name] = { + "lineno": k.get_line(), + "columnno": k.get_column(), + "filename": k.filename or self.filename, + "$conf_meta": self.get_schema_conf_meta(None, v), + } + return conf_meta + + def op_config_data_entries( + self, keys: List[ast.Expr], values: List[ast.Expr], operations: List[int] + ): + self.emit(vm.Opcode.BUILD_SCHEMA_CONFIG) + for key, value, operation in zip(keys, values, operations): + insert_index_node = None + is_nest_key = False + if key is None: + self.load_constant(None) + self.expr(value) + self.emit(vm.Opcode.UNPACK_SEQUENCE, 2) + else: + if isinstance(key, ast.Subscript): + if isinstance(key.value, ast.Identifier) and isinstance( + key.index, ast.NumberLit + ): + insert_index_node = key.index + key = key.value + if isinstance(key, ast.Identifier): + if len(key.names) == 1: + name = key.get_name(False) + if name in self._local_vars: + self.expr(key) + else: + self.load_constant(name) + else: + is_nest_key = True + self.load_constant(key.get_name()) + else: + self.expr(key) + self.expr(value) + self.load_constant(is_nest_key) + self.load_constant(operation) + self.expr_or_load_none(insert_index_node) + self.emit(vm.Opcode.STORE_SCHEMA_CONFIG) + + def op_config_data(self, t: ast.ConfigExpr): + assert isinstance(t, ast.ConfigExpr) + self.op_config_data_entries(t.keys, t.values, t.operations) + + def walk_SchemaExpr(self, t: ast.SchemaExpr): + """ast.AST: SchemaExpr + + Parameters + ---------- + - name: Identifier + - config: ConfigExpr + - schema_args: Arguments + """ + assert isinstance(t, ast.SchemaExpr) + # Schema Config data features: 1. Omitted quotes; 2. Nest_key + config_meta = self.get_schema_conf_meta(t.name, t.config) + self.exprs(t.args) + check_table = set() + for kw in t.kwargs: + if kw in check_table: + self.raise_err(CompilerInternalErrorMeta.DUPLICATED_KW.format(kw.arg)) + check_table.add(kw) + self.load_constant(kw.arg.names[0]) + self.expr(kw.value) + self.load_constant(config_meta) + self._is_in_schema_exprs.append(True) + self.expr(t.config) + self.expr(t.name) + n = len(t.args) + (len(t.kwargs) << 8) + self.emit(vm.Opcode.BUILD_SCHEMA, n) + self._is_in_schema_exprs.pop() + + def walk_CheckExpr(self, t: ast.CheckExpr): + """ast.AST: CheckExpr + + Parameters + ---------- + - test: Expr + - if_cond: Expr + - msg: Expr + """ + assert isinstance(t, ast.CheckExpr) and t.test + + label_if_cond = vm.Label() + + if t.if_cond: + self.expr(t.if_cond) + self.op_jmp(vm.Opcode.POP_JUMP_IF_FALSE, label_if_cond) + + self.expr(t.test) + label = vm.Label() + self.op_jmp(vm.Opcode.POP_JUMP_IF_TRUE, label) + self.expr_or_load_none(t.msg) + self.emit(vm.Opcode.RAISE_CHECK, 1) + self.op_label(label) + + if t.if_cond: + self.op_label(label_if_cond) + + def walk_LambdaExpr(self, t: ast.LambdaExpr): + """ast.AST: LambdaExpr + + Parameters + ---------- + - args: Optional[Arguments] + - return_type_str: Optional[str] + - return_type_node: Optional[Type] + - body: List[Stmt] + """ + + def lambda_body_func(args: ast.Arguments): + # Emit lambda function body + self.stmts(t.body) + # Pop frame and return the schema object + self.emit(vm.Opcode.RETURN_LAST_VALUE) + + self._is_in_lambda_expr.append(True) + self.make_func_with_content( + lambda_body_func, + LAMBDA_FUNC_NAME, + t.args, + ) + self._is_in_lambda_expr.pop() + + def walk_Compare(self, t: ast.Compare): + assert isinstance(t, ast.Compare) + if len(t.ops) == 0: + self.raise_err(CompilerInternalErrorMeta.NO_OPS_OR_CMPS) + if len(t.ops) != len(t.comparators): + self.raise_err(CompilerInternalErrorMeta.UNEQUAL_OPS_AND_CMPS) + self.expr(t.left) + labels = [] + for i in range(len(t.ops)): + has_next = i < (len(t.ops) - 1) + self.expr(t.comparators[i]) + if has_next: + # Duplicates the reference on top of the stack. + self.emit(vm.Opcode.DUP_TOP) + # Lifts second and third stack item one position up, moves top down to position three. + self.emit(vm.Opcode.ROT_THREE) + else: + self.emit(vm.Opcode.ROT_TWO) + if CMP_OP_MAPPING.get(t.ops[i]): + # Performs a Boolean operation. The operation name can be found in cmp_op[opname]. + self.emit(vm.Opcode.COMPARE_OP, CMP_OP_MAPPING.get(t.ops[i])) + else: + self.raise_err(CompilerInternalErrorMeta.UNKNOWN_CMPOP.format(t.ops[i])) + if has_next: + # If TOS is false, sets the bytecode counter to target and leaves TOS on the stack. + # Otherwise (TOS is true), TOS is popped. + label = vm.Label() + labels.append(label) + self.op_jmp(vm.Opcode.JUMP_IF_FALSE_OR_POP, label) + if len(t.ops) > 1: + end_label = vm.Label() + # Increments bytecode counter by end label + self.op_jmp(vm.Opcode.JUMP_FORWARD, end_label) + for label in labels: + self.op_label(label) + # Swaps the two top-most stack items. + self.emit(vm.Opcode.ROT_TWO) + # Removes the TOS item. + self.emit(vm.Opcode.POP_TOP) + self.op_label(end_label) + + def walk_Identifier(self, t: ast.Identifier): + """ast.AST: Identifier + + Parameters + ---------- + - names: List[Name] + """ + assert isinstance(t, ast.Identifier) and t.ctx + names = t.names + if t.pkgpath: + names[0] = f"@{t.pkgpath}" + + if len(names) == 1: + name = names[0] + if name in RESERVED_IDENTIFIERS: + self.raise_err(CompilerInternalErrorMeta.INVALID_NAME) + + if t.ctx in [ast.ExprContext.LOAD, ast.ExprContext.AUGLOAD]: + # must be right value + if self._is_in_schema_stmt[-1] and name not in self._local_vars: + self.symtable.define(name, SymbolScope.INTERNAL) + index = self.add_name(name) - 1 + self.load_constant(name) + self.emit(vm.Opcode.SCHEMA_LOAD_ATTR, index) + # Leave the inner attr scope, delete the variable from the symbol table. + self.symtable.delete(name, SymbolScope.INTERNAL) + else: + self.load_symbol(name) + elif t.ctx in [ast.ast.ExprContext.AUGSTORE, ast.ast.ExprContext.STORE]: + if self._is_in_lambda_expr[-1]: + # Store lambda expr variable and pop the stored value + self.store_symbol(name) + self.emit(vm.Opcode.POP_TOP) + elif self._is_in_schema_stmt[-1]: + self.load_constant(name) + self.emit(vm.Opcode.SCHEMA_UPDATE_ATTR, 0) + if not self._is_in_if_stmt[-1]: + self.emit(vm.Opcode.SCHEMA_NOP) + else: + self.store_symbol(name) + elif t.ctx in [ast.ast.ExprContext.DEL]: + pass + else: + assert False + + elif len(names) > 1: + + if t.ctx != ast.ExprContext.AUGSTORE: + self.expr( + ast.Identifier( + names=[names[0]], line=self.lineno, column=self.colno + ) + ) + name_pairs = list(zip(names, names[1:])) + + for i, data in enumerate(name_pairs): + name, attr = data[0], data[1] + + ctx = t.ctx # TODO: Fix single name context in AST + if i == 0 and ( + ctx == ast.ExprContext.STORE or ctx == ast.ExprContext.AUGSTORE + ): + self.store_symbol(name) + if ( + t.ctx == ast.ExprContext.STORE + and i != (len(name_pairs) - 1) + and len(name_pairs) > 1 + ): + ctx = ast.ExprContext.LOAD + + op = EXPR_OP_MAPPING.get(ctx) + + if not op: + self.raise_err( + CompilerInternalErrorMeta.INVALID_PARAM_IN_ATTR.format(ctx) + ) + if ctx == ast.ExprContext.AUGLOAD: + self.emit(vm.Opcode.DUP_TOP) + elif ctx == ast.ExprContext.AUGSTORE: + self.emit(vm.Opcode.ROT_TWO) + + self.op_name(op, attr) + else: + self.raise_err(CompilerInternalErrorMeta.INVALID_NAME) + + def walk_NumberLit(self, t: ast.AST): + """ast.AST: NumberLit + + Parameters + ---------- + - value + """ + assert isinstance(t, ast.NumberLit) + + if t.binary_suffix: + value = units.cal_num(t.value, t.binary_suffix) + self.load_constant( + objpkg.KCLNumberMultiplierObject( + value=value, + raw_value=t.value, + binary_suffix=t.binary_suffix, + ) + ) + else: + self.load_constant(t.value) + + def walk_StringLit(self, t: ast.StringLit): + """ast.AST: StringLit + + Parameters + ---------- + - value + """ + assert isinstance(t, ast.StringLit) + self.load_constant(t.value) + + def walk_NameConstantLit(self, t: ast.NameConstantLit): + """ast.AST: NameConstantLit + + Parameters + ---------- + - value + """ + assert isinstance(t, ast.NameConstantLit) + self.load_constant(t.value) + + def walk_JoinedString(self, t: ast.JoinedString): + """ast.AST: JoinedString + Parameters + ---------- + - values: List[Union[Expr, StringLit]] + TOS + --- + - format_spec + - formatted expr list + Operand + ------- + Formatted expr list count + """ + assert isinstance(t, ast.JoinedString) + assert t.values + for value in t.values: + if isinstance(value, ast.FormattedValue): + self.expr(value.value) + self.load_constant(value.format_spec) + self.emit(vm.Opcode.FORMAT_VALUES, 1) + elif isinstance(value, ast.StringLit): + self.expr(value) + elif isinstance(value, ast.Expr): + self.expr(value) + self.load_constant(None) + self.emit(vm.Opcode.FORMAT_VALUES, 1) + else: + self.raise_err( + CompilerInternalErrorMeta.INVALID_STRING_INTERPOLATION_ITEM + ) + for i in range(len(t.values) - 1): + self.emit(vm.Opcode.BINARY_ADD) + + def walk_TypeAliasStmt(self, t: ast.TypeAliasStmt): + """ast.AST: TypeAliasStmt + + Parameters + ---------- + - type_name: Identifier + - type_value: Type + """ + # TypeAliasStmt has been replaced in the ResolveProgram function, + # there is no need to do any processing here + pass + + def walk_UnificationStmt(self, t: ast.UnificationStmt): + """ast.AST: UnificationStmt + + Parameters + ---------- + - target: Identifier + - value: Expr + """ + self._local_vars = [] + name = t.target.get_name() + if self._is_in_schema_stmt[-1]: + # Assign operator + self.load_constant(None) + # Optional + self.load_constant(False) + # Final + self.load_constant(False) + # Has default + self.load_constant(bool(t.value)) + # Default value + t.target.set_ctx(ast.ExprContext.LOAD) + self.expr(t.target) + self.expr_or_load_none(t.value) + self.emit(vm.Opcode.INPLACE_OR) + # Attr name + self.load_constant(name) + # Attr type + self.load_constant(t.value.name.get_name()) + # 0 denotes the decorator count + self.emit(vm.Opcode.SCHEMA_ATTR, 0) + self.emit(vm.Opcode.SCHEMA_NOP) + else: + if not self.symtable.resolve(name): + self.expr(t.value) + self.expr(t.target) + else: + t.target.set_ctx(ast.ExprContext.LOAD) + self.expr(t.target) + self.expr(t.value) + self.emit(vm.Opcode.INPLACE_OR) + t.target.set_ctx(ast.ExprContext.STORE) + self.expr(t.target) + + def walk_AssignStmt(self, t: ast.AssignStmt): + """ast.AST: AssignStmt + + Parameters + ---------- + - targets: List[Identifier] + - value: Expr + """ + self._local_vars = [] + # Infer to schema + if t.type_annotation and isinstance( + self.get_type_from_identifier(t.targets[0]), objpkg.KCLSchemaTypeObject + ): + # Config meta + self.load_constant({}) + # Config + self.expr(t.value) + # Load the schema type + names = t.type_annotation.split(".") + self.load_symbol(names[0]) + for name in names[1:]: + self.op_name(vm.Opcode.LOAD_ATTR, name) + # Build schema + self.emit(vm.Opcode.BUILD_SCHEMA, 0) + else: + self.expr(t.value) + for i, target in enumerate(t.targets): + self.update_line_column(target) + self.expr(target) + + def walk_AugAssignStmt(self, t: ast.AugAssignStmt): + """ast.AST: AugAssignStmt + + Parameters + ---------- + - target: Identifier + - value: Expr + - op: AugOp + """ + assert isinstance(t, ast.AugAssignStmt) and t.target and t.value and t.op + t.target.set_ctx(ast.ExprContext.LOAD) + self.expr(t.target) + self.expr(t.value) + opcode = ARG_OP_MAPPING.get(t.op) + if not opcode: + self.raise_err(CompilerInternalErrorMeta.UNKNOWN_AUG_BINOP.format(t.op)) + self.emit(opcode) + if t.target.pkgpath: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=self.filename, + line_no=t.target.get_line(), + col_no=t.target.get_column(), + end_col_no=t.target.get_end_column(), + ) + ], + arg_msg="module '{}' can't be assigned".format( + t.target.pkgpath.replace("@", "") + ), + ) + t.target.set_ctx(ast.ExprContext.STORE) + self.expr(t.target) + + +# ----------------------------------------------------------------------------- +# CompileProgram/ResolveProgram +# ----------------------------------------------------------------------------- + + +def FixAndResolveProgram(prog: ast.Program) -> ProgramScope: + """Fix AST program and resolve it.""" + # Preprocess + for pkgpath in prog.pkgs: + # Configuration merge with the same name + if pkgpath == ast.Program.MAIN_PKGPATH: + prog.pkgs[pkgpath] = unification.MergeASTList(prog.pkgs[pkgpath]) + + # Resolve program including the import check and the type check + return ResolveProgram(prog) + + +def CompileProgram( + prog: ast.Program, enable_cache: bool = True +) -> Optional[objpkg.KCLProgram]: + """Compile function""" + if not prog or not isinstance(prog, ast.Program): + return + modfile = vfs.LoadModFile(prog.root) + enable_cache = modfile.build.enable_pkg_cache and enable_cache + kcl_program = vfs.LoadBytecodeCache(prog.root, prog) if enable_cache else None + # Preprocess + for pkgpath in prog.pkgs: + # Configuration merge with the same name + if pkgpath == ast.Program.MAIN_PKGPATH: + # Config merge + prog.pkgs[pkgpath] = unification.MergeASTList(prog.pkgs[pkgpath]) + # Fix identifier pkgpath + import_names = {} + for m in prog.pkgs[pkgpath]: + fix.fix_qualified_identifier(m, import_names=import_names) + + # Apply command line arguments + query.ApplyOverrides(prog, prog.cmd_overrides) + + if not kcl_program: + # Resolve program and get the scope + scope = ResolveProgram(prog) + # Compile program using the scope + kcl_program = Compiler(scope).compile_program(prog) + if enable_cache: + vfs.SaveBytecodeCache(prog.root, prog, kcl_program) + return kcl_program + + +# ----------------------------------------------------------------------------- +# END +# ----------------------------------------------------------------------------- diff --git a/internal/kclvm_py/compiler/build/data.py b/internal/kclvm_py/compiler/build/data.py new file mode 100644 index 000000000..631e1c068 --- /dev/null +++ b/internal/kclvm_py/compiler/build/data.py @@ -0,0 +1,151 @@ +from kclvm.kcl.ast import BinOp, CmpOp, UnaryOp, AugOp, ExprContext + +from kclvm.vm.code import Opcode +from kclvm.compiler.build.symtable import SymbolScope + + +CMP_OP_MAPPING = { + CmpOp.Eq: Opcode.COMPARE_EQUAL_TO, + CmpOp.NotEq: Opcode.COMPARE_NOT_EQUAL_TO, + CmpOp.Lt: Opcode.COMPARE_LESS_THAN, + CmpOp.LtE: Opcode.COMPARE_LESS_THAN_OR_EQUAL_TO, + CmpOp.Gt: Opcode.COMPARE_GREATER_THAN, + CmpOp.GtE: Opcode.COMPARE_GREATER_THAN_OR_EQUAL_TO, + CmpOp.Is: Opcode.COMPARE_IS, + CmpOp.IsNot: Opcode.COMPARE_IS_NOT, + CmpOp.In: Opcode.COMPARE_IN, + CmpOp.NotIn: Opcode.COMPARE_NOT_IN, + CmpOp.Not: Opcode.COMPARE_IS_NOT, # 'not' => 'is not' +} + + +BIN_OP_MAPPING = { + **CMP_OP_MAPPING, + BinOp.Add: Opcode.BINARY_ADD, + BinOp.Sub: Opcode.BINARY_SUBTRACT, + BinOp.Mul: Opcode.BINARY_MULTIPLY, + BinOp.Div: Opcode.BINARY_TRUE_DIVIDE, + BinOp.Mod: Opcode.BINARY_MODULO, + BinOp.Pow: Opcode.BINARY_POWER, + BinOp.LShift: Opcode.BINARY_LSHIFT, + BinOp.RShift: Opcode.BINARY_RSHIFT, + BinOp.BitOr: Opcode.BINARY_OR, + BinOp.BitXor: Opcode.BINARY_XOR, + BinOp.BitAnd: Opcode.BINARY_AND, + BinOp.FloorDiv: Opcode.BINARY_FLOOR_DIVIDE, + BinOp.And: Opcode.BINARY_LOGIC_AND, + BinOp.Or: Opcode.BINARY_LOGIC_OR, + BinOp.As: Opcode.MEMBER_SHIP_AS, +} + +UNARY_OP_MAPPING = { + UnaryOp.Invert: Opcode.UNARY_INVERT, + UnaryOp.Not: Opcode.UNARY_NOT, + UnaryOp.UAdd: Opcode.UNARY_POSITIVE, + UnaryOp.USub: Opcode.UNARY_NEGATIVE, +} + +ARG_OP_MAPPING = { + AugOp.Add: Opcode.INPLACE_ADD, + AugOp.Sub: Opcode.INPLACE_SUBTRACT, + AugOp.Mul: Opcode.INPLACE_MULTIPLY, + AugOp.Div: Opcode.INPLACE_TRUE_DIVIDE, + AugOp.Mod: Opcode.INPLACE_MODULO, + AugOp.Pow: Opcode.INPLACE_POWER, + AugOp.LShift: Opcode.INPLACE_LSHIFT, + AugOp.RShift: Opcode.INPLACE_RSHIFT, + AugOp.BitOr: Opcode.INPLACE_OR, + AugOp.BitXor: Opcode.INPLACE_XOR, + AugOp.BitAnd: Opcode.INPLACE_AND, + AugOp.FloorDiv: Opcode.INPLACE_FLOOR_DIVIDE, +} + +EXPR_OP_MAPPING = { + ExprContext.AUGLOAD: Opcode.LOAD_ATTR, + ExprContext.LOAD: Opcode.LOAD_ATTR, + ExprContext.AUGSTORE: Opcode.STORE_ATTR, + ExprContext.STORE: Opcode.STORE_ATTR, + ExprContext.DEL: Opcode.DELETE_ATTR, +} + + +SUBSCR_OP_MAPPING = { + ExprContext.AUGLOAD: [Opcode.DUP_TOP_TWO, Opcode.BINARY_SUBSCR], + ExprContext.LOAD: [Opcode.BINARY_SUBSCR], + ExprContext.AUGSTORE: [Opcode.ROT_THREE, Opcode.STORE_SUBSCR], + ExprContext.STORE: [Opcode.STORE_SUBSCR], + ExprContext.DEL: [Opcode.DELETE_SUBSCR], +} + +SYMBOL_SCOPE_LOAD_OP_MAPPING = { + SymbolScope.BUILT_IN: Opcode.LOAD_BUILT_IN, + SymbolScope.LOCAL: Opcode.LOAD_LOCAL, + SymbolScope.GLOBAL: Opcode.LOAD_NAME, + SymbolScope.FREE: Opcode.LOAD_FREE, + SymbolScope.INTERNAL: Opcode.LOAD_NAME, +} + +SYMBOL_SCOPE_STORE_OP_MAPPING = { + SymbolScope.GLOBAL: Opcode.STORE_GLOBAL, + SymbolScope.LOCAL: Opcode.STORE_LOCAL, + SymbolScope.FREE: Opcode.STORE_FREE, +} + + +class CompilerInternalErrorMeta: + COMPILE_ERR = "compile error {}" + UNKNOWN_MOD = "unknown Module {}" + UNKNOWN_STMT = "unknown Stmt {}" + UNKNOWN_EXPR = "unknown Expr {}" + UNKNOWN_NUM = "unknown num {}" + UNKNOWN_NAME_CONST = "unknown NameContext const {}" + UNKNOWN_BINOP = "unknown BinOp {}" + UNKNOWN_AUG_BINOP = "unknown Aug BinOp {}" + UNKNOWN_UNARYOP = "unknown UnaryOp {}" + UNKNOWN_LOOPTYPE = "unknown loop type {}" + UNKNOWN_CMPOP = "unknown CompareOp {}" + UNKNOWN_COMP = "unknown comprehension {}" + INVALID_KCL_AST_MSG = "invalid KCL AST type" + INVALID_PARAM_IN_ATTR = "invalid param {} in attribute expression" + INVALID_PARAM_IN_SUBSCR = "invalid param {} in attribute subscript" + INVALID_SUBSCRIPT_KIND = "invalid subscript kind {}" + INVALID_TARGET_IN_LIST_TUPLE = ( + "invalid starred assignment target outside a list or tuple" + ) + INVALID_STARRED_EXPR = "invalid starred expression outside assignment target " + INVALID_TWO_STARRED_EXPR = "two starred expressions in assignment" + INVALID_JMP_CALL = "opJmp called with non jump instruction" + INVALID_NAME = "NameContext can't be None, True or False" + INVALID_SYMBOL = "symbol can't be None, True or False" + INVALID_SYMBOL_SCOPE = "invalid symbol scope {}" + INVALID_NONE_OP = "opcode can't be none" + INVALID_ARGED_OP_CODE = "opcode {} can't takes an argument" + INVALID_OP_CODE = "opcode {} can't be emitted without an argument" + INVALID_EXPR_CONTEXT = "invalid AST AssignStmt ExprContext value {}" + INVALID_OP_POS = "invalid opcode pos {}" + INVALID_MISSING_ARG = "missing arg in Arged opcode" + INVALID_GLOBAL_IMPLICIT_SCOPE = "not expecting scopeGlobalImplicit in set qualname" + INVALID_CLOSURE_FREE_SCOPE = ( + "invalid closure scope {} for free var {} in symbol table {}" + ) + INVALID_GENERATORS = "invalid generators" + INVALID_STRING_INTERPOLATION_ITEM = "invalid string interpolation item" + INVALID_QUANTIFIER_OP = "invalid quantifier expression operation {}" + SYMBOL_NOT_DEFINED = "name '{}' is not defined" + UNEQUAL_OPS_AND_CMPS = "unequal ops and comparators in compare" + UNEQUAL_DICT_KV_SIZE = "unequal dict keys len {} and values len {}" + TOO_MANY_EXPRS_IN_STAR_UNPACK = "too many expressions in star-unpacking assignment" + FAILED_SET_AUG_ASSIGN_CTX = "can't set context in AugAssign" + NO_OPS_OR_CMPS = "no ops or comparators in compare" + NO_SYMBOL_TABLE_FOR_AST = "no symbol table found for ast {}" + DUPLICATED_KW = "duplicated keyword argument" + + +class SchemaConfigMeta: + """ + SchemaConfigMeta defines the names of meta information + """ + + LINE = "$lineno" + COLUMN = "$columnno" + FILENAME = "$filename" diff --git a/internal/kclvm_py/compiler/build/preprocess.py b/internal/kclvm_py/compiler/build/preprocess.py new file mode 100644 index 000000000..5104e30d5 --- /dev/null +++ b/internal/kclvm_py/compiler/build/preprocess.py @@ -0,0 +1,139 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import Optional + +import kclvm.kcl.ast as ast + +_IDENTIFIER_PREFIX = "$" + + +class ASTIdentifierPrefixTransformer(ast.TreeTransformer): + @staticmethod + def _remove_prefix(name: str): + if not name: + return name + return name.replace(_IDENTIFIER_PREFIX, "") + + def walk_Name(self, node: ast.Name) -> None: + node.value = self._remove_prefix(node.value) + return node + + def walk_Identifier(self, node: ast.Identifier): + node.names = [self._remove_prefix(name) for name in node.names] + for name_node in node.name_nodes or []: + self.walk(name_node) + return node + + def walk_SchemaStmt(self, node: ast.SchemaStmt): + node.name = self._remove_prefix(node.name) + if node.parent_name: + node.parent_name = self.walk(node.parent_name) + for mixin in node.mixins or []: + self.walk(mixin) + for stmt in node.body or []: + self.walk(stmt) + for check in node.checks or []: + self.walk(check) + return node + + def walk_RuleStmt(self, node: ast.RuleStmt): + node.name = self._remove_prefix(node.name) + for rule in node.parent_rules or []: + self.walk(rule) + for check in node.checks or []: + self.walk(check) + return node + + def walk_SchemaAttr(self, node: ast.SchemaAttr): + node.name = self._remove_prefix(node.name) + node.type_str = self._remove_prefix(node.type_str) + return node + + def walk_ImportStmt(self, node: ast.ImportStmt): + if node.asname: + node.asname = self._remove_prefix(node.asname) + if node.as_name_node: + self.walk(node.as_name_node) + node.name = self._remove_prefix(node.name) + node.path = self._remove_prefix(node.path) + for path_node in node.path_nodes or []: + self.walk(path_node) + return node + + +class ConfigNestVarTransformer(ast.TreeTransformer): + def walk_ConfigEntry(self, t: ast.ConfigEntry): + # Unpack the nest var form `a.b.c = 1` to `a: {b: {c = 1}}` + is_nest_key = isinstance(t.key, ast.Identifier) and len(t.key.names) > 1 + if is_nest_key: + names = t.key.names + value = t.value + t.key.names = [t.key.names[0]] + for i, name in enumerate(names[1:][::-1]): + is_last_item = i == 0 + name_node = ast.Identifier( + names=[name], line=t.key.line, column=t.key.column + ) + name_node.filename = t.filename + entry_value = ast.ASTFactory.get_ast_configentry( + name_node, + value, + t.operation if is_last_item else ast.ConfigEntryOperation.UNION, + t.filename, + ) + value = ast.ConfigExpr(line=t.key.line, column=t.key.column) + value.filename = t.filename + value.items.append(entry_value) + t.value = value + t.operation = ast.ConfigEntryOperation.UNION + self.walk(t.value) + return t + + def walk_ConfigIfEntryExpr(self, t: ast.ConfigIfEntryExpr): + keys = [] + values = [] + operations = [] + for key, value, operation in zip(t.keys, t.values, t.operations): + is_nest_key = isinstance(key, ast.Identifier) and len(key.names) > 1 + if is_nest_key: + names = key.names + key.names = [key.names[0]] + for i, name in enumerate(names[1:][::-1]): + is_last_item = i == 0 + name_node = ast.Identifier( + names=[name], line=key.line, column=key.column + ) + name_node.filename = t.filename + entry_value = ast.ASTFactory.get_ast_configentry( + name_node, + value, + operation if is_last_item else ast.ConfigEntryOperation.UNION, + t.filename, + ) + value = ast.ConfigExpr(line=key.line, column=key.column) + value.filename = t.filename + value.items.append(entry_value) + operations.append(ast.ConfigEntryOperation.UNION) + else: + operations.append(operation) + keys.append(key) + values.append(value) + t.keys = keys + t.values = [self.walk(v) for v in values] + t.operations = operations + if t.orelse: + self.walk(t.orelse) + return t + + +def fix_identifier_prefix(node: Optional[ast.AST]) -> Optional[ast.AST]: + """Fix AST Identifier prefix and unpack the nest var form `a.b.c = 1` to `a: {b: {c = 1}}` + + Examples + -------- + $filter -> filter + """ + if not node or not isinstance(node, ast.AST): + return node + ConfigNestVarTransformer().walk(node) + return ASTIdentifierPrefixTransformer().walk(node) diff --git a/internal/kclvm_py/compiler/build/symtable.py b/internal/kclvm_py/compiler/build/symtable.py new file mode 100644 index 000000000..6f8773b90 --- /dev/null +++ b/internal/kclvm_py/compiler/build/symtable.py @@ -0,0 +1,190 @@ +from dataclasses import dataclass +from enum import Enum +from typing import Optional, List, Dict, Tuple + +import kclvm.kcl.info as kcl_info + +from kclvm.compiler.extension.builtin import BUILTIN_FUNCTIONS + + +class SymbolScope(Enum): + """Symbol Scope + + Parameters + ---------- + GLOBAL: str + Top level variables including variables in IfStmt + + LOCAL: str + Variables in schema context or in for comprehension + + BUILTIN: str + Built-in variables e.g., `print`, `sum`, + + FREE: str + Variables that need to be captured by closures + + INTERNAL: str + Internal variables used only for record names e.g., `b` and `c` in `a.b.c` + """ + + GLOBAL = "GLOBAL" + LOCAL = "LOCAL" + BUILT_IN = "BUILT_IN" + FREE = "FREE" + INTERNAL = "INTERNAL" + + +@dataclass +class Symbol: + """Symbol + + Parameters + ---------- + name: str + The symbol name + + index: int + The symbol index in the symbol table + + scope: + The symbol scope + + define_count: + The number of times the symbol is defined or declared + """ + + name: str + index: int + scope: SymbolScope = None + define_count: int = 0 + + +@dataclass +class SymbolTable: + """Symbol table + + Parameters + ---------- + outer: SymbolTable + The symbol table in the outer symbol scope + e.g., The schema context symbol table outer is the top level scope symbol table. + + store: Dict[str, Symbol] + The current scope symbol map to store all symbols + + free_symbols: List[Symbol] + The free symbol list in the current scope + + num_definitions: int + The total number of symbols + """ + + outer: "SymbolTable" = None + store: Dict[str, Symbol] = None + free_symbols: List[Symbol] = None + num_definitions: int = 0 + + def define(self, name: str, scope: SymbolScope = None) -> Tuple[Symbol, bool]: + """ + Define a symbol named 'name' with 'scope' and put it into symbol table + """ + assert isinstance(name, str) + + def default_scope(): + """The inner default symbol scope + + If outer is exist, return LOCAL scope, else return GLOBAL scope + """ + return SymbolScope.GLOBAL if self.outer is None else SymbolScope.LOCAL + + symbol = Symbol( + name=name, index=self.num_definitions, scope=scope or default_scope() + ) + is_exist = False + if ( + name in self.store + and not kcl_info.isprivate_field(name) + and self.store[name].scope == SymbolScope.GLOBAL + ): + is_exist = True + self.num_definitions += 1 + return self.store[name], is_exist + # Internal scope variable skip exist symbol + if symbol.scope == SymbolScope.INTERNAL and name in self.store: + pass + else: + self.store[name] = symbol + self.num_definitions += 1 + return symbol, is_exist + + def define_builtin(self, name: str, index: int) -> Symbol: + """Define a builtin function object""" + if self.store is None or not isinstance(self.store, dict): + raise Exception("Invalid symbol table store") + symbol = Symbol(name=name, index=index) + symbol.scope = SymbolScope.BUILT_IN + self.store[name] = symbol + return symbol + + def define_free(self, original: Symbol) -> Symbol: + """ + Define a symbol named 'name' with free scope and put it into symbol table + """ + self.free_symbols.append(original) + self.store[original.name] = Symbol( + name=original.name, index=original.index, scope=SymbolScope.FREE + ) + return self.store[original.name] + + def register_builtins(self): + """Register builtin functions into symbol table""" + builtins = BUILTIN_FUNCTIONS + for i, builtin in enumerate(builtins): + self.define_builtin(builtin, i) + return self + + def resolve(self, name: str) -> Optional[Symbol]: + """Resolve a symbol named 'name' + + Search from the current scope, if not found, search from the symbol table of its outer + """ + obj = self.store.get(name) + if not obj and self.outer: + obj = self.outer.resolve(name) + if not obj: + return obj + if obj.scope == SymbolScope.GLOBAL or obj.scope == SymbolScope.BUILT_IN: + return obj + elif obj.scope == SymbolScope.INTERNAL: + return None + return self.define_free(obj) + return obj + + def delete(self, name: str, scope: SymbolScope): + """Delete name from the symbol table""" + if ( + not name + or not scope + or name not in self.store + or self.store[name].scope != scope + ): + return + del self.store[name] + + # Static methods + + @staticmethod + def new(outer=None, num=0): + """New an empty symbol table""" + return SymbolTable( + outer=outer, + store={}, + free_symbols=[], + num_definitions=num, + ) + + @staticmethod + def new_with_built_in(outer=None, num=0): + """New a symbol table with all builtin functions""" + return SymbolTable.new(outer, num).register_builtins() diff --git a/internal/kclvm_py/compiler/build/utils/units.py b/internal/kclvm_py/compiler/build/utils/units.py new file mode 100644 index 000000000..a1c155609 --- /dev/null +++ b/internal/kclvm_py/compiler/build/utils/units.py @@ -0,0 +1,112 @@ +# Units based on SI and ECI +# Ref: https://en.wikipedia.org/wiki/Binary_prefix +# Kubernetes use case: https://pkg.go.dev/k8s.io/apimachinery/pkg/api/resource#Quantity + +from typing import Union + + +IEC_SUFFIX = "i" +EXPONENTS = {"n": -3, "u": -2, "m": -1, "K": 1, "k": 1, "M": 2, "G": 3, "T": 4, "P": 5} +NUMBER_MULTIPLIER_REGEX = r"^([1-9][0-9]{0,63})(E|P|T|G|M|K|k|m|u|n|Ei|Pi|Ti|Gi|Mi|Ki)$" + + +def cal_num(value: int, suffix: str) -> int: + """ + Calculate number based on value and binary suffix. + + Supported suffixes: + SI: n | u | m | k | K | M | G | T | P + IEC: Ki | Mi | Gi | Ti | Pi + + Input: + value: int. + suffix: str. + + Returns: + int + + Raises: + ValueError on invalid or unknown suffix + """ + + if not isinstance(value, int): + raise ValueError("Unsupported value type: {}".format(type(value))) + + if not suffix: + return value + + base = 1000 + unit = suffix + + validate_unit(unit) + + if unit[-1] == "i": + base = 1024 + unit = unit[:-1] + + exponent = EXPONENTS[unit] + return value * (base ** exponent) + + +def to_quantity(quantity: Union[str, int]) -> int: + """ + Parse and return number based on input quantity. + + Supported suffixes: + SI: n | u | m | k | K | M | G | T | P + IEC: Ki | Mi | Gi | Ti | Pi + + Input: + quantity: str. + + Returns: + int + + Raises: + ValueError on invalid or unknown input + """ + if not isinstance(quantity, (int, str)): + raise ValueError("Unsupported quantity type: {}".format(type(quantity))) + + if isinstance(quantity, int): + return quantity + + number = quantity + suffix = None + if len(quantity) >= 2 and quantity[-1] == IEC_SUFFIX: + if quantity[-2] in EXPONENTS: + number = quantity[:-2] + suffix = quantity[-2:] + elif len(quantity) >= 1 and quantity[-1] in EXPONENTS: + number = quantity[:-1] + suffix = quantity[-1:] + + if not number: + raise ValueError("Number can't be empty") + + number = int(number) + + if suffix is None: + return number + + validate_unit(suffix[0]) + + if suffix.endswith(IEC_SUFFIX): + base = 1024 + else: + base = 1000 + + exponent = EXPONENTS[suffix[0]] + return number * (base ** exponent) + + +def validate_unit(unit: str) -> None: + # IEC validate + if not unit or not isinstance(unit, str) or len(unit) > 2: + raise ValueError("Invalid suffix {}".format(unit)) + + if unit in ["ni", "ui", "mi", "ki"]: + raise ValueError("Invalid suffix {}".format(unit)) + + if unit[0] not in EXPONENTS: + raise ValueError("Invalid suffix {}".format(unit)) diff --git a/internal/kclvm_py/compiler/check/check_type/__init__.py b/internal/kclvm_py/compiler/check/check_type/__init__.py new file mode 100644 index 000000000..2857db22d --- /dev/null +++ b/internal/kclvm_py/compiler/check/check_type/__init__.py @@ -0,0 +1,21 @@ +from .check_type import ( + check, + check_type, + check_type_dict, + runtime_type, + runtime_types, + type_pack_and_check, + check_type_builtin, + has_literal_type, +) + +__all__ = [ + "check", + "check_type", + "check_type_dict", + "runtime_type", + "runtime_types", + "type_pack_and_check", + "check_type_builtin", + "has_literal_type", +] diff --git a/internal/kclvm_py/compiler/check/check_type/check_type.py b/internal/kclvm_py/compiler/check/check_type/check_type.py new file mode 100644 index 000000000..1876147a3 --- /dev/null +++ b/internal/kclvm_py/compiler/check/check_type/check_type.py @@ -0,0 +1,585 @@ +#! /usr/bin/env python3 + +from ast import literal_eval +from typing import Optional, List + +import kclvm.kcl.error as kcl_error +import kclvm.kcl.info as kcl_info +import kclvm.config +import kclvm.api.object as objpkg +import kclvm.api.object.internal.common as common +from kclvm.api.object.internal import Undefined, UndefinedType + +STR_TYPE = "str" +BOOL_TYPE = "bool" +INT_TYPE = "int" +FLOAT_TYPE = "float" +BUILTIN_TYPES = [STR_TYPE, BOOL_TYPE, INT_TYPE, FLOAT_TYPE] + +_KCL_TYPE_any = "any" +_KCL_TYPE_True = "True" +_KCL_TYPE_False = "False" +_KCL_TYPE_None = "None" + + +# ------------------------------ +# Numeric range check +# ------------------------------ + + +def check( + kcl_obj, + filename: Optional[str] = None, + lineno: Optional[int] = None, + columnno: Optional[int] = None, +): + """Check whether the KCL object meets the scope requirements""" + if not kclvm.config.debug: + return kcl_obj + strict_range_check = kclvm.config.strict_range_check + check_bit = 32 if strict_range_check else 64 + int_min = kcl_info.INT32_MIN if strict_range_check else kcl_info.INT64_MIN + int_max = kcl_info.INT32_MAX if strict_range_check else kcl_info.INT64_MAX + float_min = kcl_info.FLOAT32_MIN if strict_range_check else kcl_info.FLOAT64_MIN + float_max = kcl_info.FLOAT32_MAX if strict_range_check else kcl_info.FLOAT64_MAX + + def check_object(obj: objpkg.KCLObject): + if isinstance(obj, objpkg.KCLIntObject): + value = obj.value + if not (int_min <= value <= int_max): + kcl_error.report_exception( + err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=lineno, col_no=columnno + ) + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format( + str(obj.value), check_bit + ), + ) + elif isinstance(obj, objpkg.KCLFloatObject): + abs_var = abs(obj.value) + if 0 < abs_var < float_min: + kcl_error.report_warning( + err_type=kcl_error.ErrType.FloatUnderflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=lineno, col_no=columnno + ) + ], + arg_msg=kcl_error.FLOAT_UNDER_FLOW_MSG.format( + str(obj.value), check_bit + ), + ) + obj.value = 0.0 + elif abs_var > float_max: + kcl_error.report_exception( + err_type=kcl_error.ErrType.FloatOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=lineno, col_no=columnno + ) + ], + arg_msg=kcl_error.FLOAT_OVER_FLOW_MSG.format( + str(obj.value), check_bit + ), + ) + elif isinstance(obj, objpkg.KCLListObject): + for i in obj.value: + check_object(i) + elif isinstance(obj, (objpkg.KCLDictObject, objpkg.KCLSchemaObject)): + for k, v in obj.value.items(): + check_object(k) + check_object(v) + return obj + + obj = check_object(kcl_obj) + return obj + + +# ------------------------------ +# Type pack and check functions +# ------------------------------ + + +def runtime_types( + tpes: List[str], + vm=None, + filename: Optional[str] = None, + lineno: Optional[int] = None, + columnno: Optional[int] = None, +): + if not tpes: + return tpes + runtime_tpes = [] + for tpe in tpes: + runtime_tpe = runtime_type(tpe, vm, filename, lineno, columnno) + if runtime_tpe: + runtime_tpes.append(runtime_tpe) + return runtime_tpes + + +def runtime_type( + tpe: str, + vm=None, + filename: Optional[str] = None, + lineno: Optional[int] = None, + columnno: Optional[int] = None, +): + if not tpe: + return tpe + if common.isdicttype(tpe): + tpe = common.dereferencetype(tpe) + key_type, value_type = common.separate_kv(tpe) + if key_type is None or value_type is None: + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=lineno, col_no=columnno + ) + ], + arg_msg="error in dict type, key-value type can't be None", + ) + key_type = runtime_type( + key_type, + vm, + filename, + lineno, + columnno, + ) + value_type = runtime_type( + value_type, + vm, + filename, + lineno, + columnno, + ) + return "{{{}:{}}}".format(key_type, value_type) + elif common.islisttype(tpe): + tpe = common.dereferencetype(tpe) + ele_type = runtime_type(tpe, vm, filename, lineno, columnno) + return "[{}]".format(ele_type) + elif tpe in BUILTIN_TYPES: + return tpe + else: + if "." in tpe: + schema_tpe_obj = vm.find_schema_type(tpe) + if isinstance(schema_tpe_obj, objpkg.KCLSchemaTypeObject): + return schema_tpe_obj.runtime_type + else: + schema_name = tpe + if schema_name in vm.ctx.globals and isinstance( + vm.ctx.globals[schema_name], objpkg.KCLSchemaTypeObject + ): + return vm.ctx.globals[schema_name].runtime_type + return None + + +def convert_collection_value( + value, + expected_type: Optional[str], + filename: Optional[str] = None, + lineno: Optional[int] = None, + columnno: Optional[int] = None, + vm=None, + config_meta=None, +): + assert isinstance(value, objpkg.KCLObject) + + if expected_type == _KCL_TYPE_any: + return value + + is_collection = isinstance(value, (objpkg.KCLDictObject, objpkg.KCLListObject)) + invalid_match_dict = common.isdicttype(expected_type) and not isinstance( + value, objpkg.KCLDictObject + ) + invalid_match_list = common.islisttype(expected_type) and not isinstance( + value, objpkg.KCLListObject + ) + invalid_match = invalid_match_dict or invalid_match_list + if ( + not expected_type + or not is_collection + or invalid_match + or common.is_type_union(expected_type) + ): + return value + if common.isdicttype(expected_type): + # convert dict + key_tpe, value_tpe = common.separate_kv(common.dereferencetype(expected_type)) + expected_dict = {} + for k, v in value.value.items(): + expected_value = convert_collection_value( + v, value_tpe, filename, lineno, columnno, vm + ) + expected_dict[k] = expected_value + obj = objpkg.KCLSchemaConfigObject(value=expected_dict) + obj.update_attr_op_using_obj(value) + return obj + elif common.islisttype(expected_type): + # convert list + expected_type = common.dereferencetype(expected_type) + expected_list = [] + for i in value.value: + expected_schema = convert_collection_value( + i, + expected_type, + filename, + lineno, + columnno, + vm, + ) + expected_list.append(expected_schema) + return objpkg.KCLListObject(expected_list) + elif expected_type in BUILTIN_TYPES: + # Do nothing on built-in types + return value + elif isinstance(value, objpkg.KCLListObject): + # List value not match the schema type + return value + elif "." in expected_type: + # use cross pkg schema like 'pkg.schema' + # kcl support use cross pkg schema without import + schema_type = vm.find_schema_type(expected_type) + if schema_type and isinstance(schema_type, objpkg.KCLSchemaTypeObject): + config_meta_new = config_meta or vm.ctx.locals.get( + objpkg.SCHEMA_CONFIG_META_KEY + ) + return schema_type.new_instance(value, config_meta_new, [], [], vm) + elif ( + expected_type in vm.ctx.globals + and vm.ctx.globals[expected_type].type() == objpkg.KCLObjectType.SCHEMA_TYPE + ): + # Schema in current module context without import + config_meta_new = config_meta or vm.ctx.locals.get( + objpkg.SCHEMA_CONFIG_META_KEY + ) + schema_type = vm.ctx.globals[expected_type] + return schema_type.new_instance(value, config_meta_new, [], [], vm) + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg(filename=filename, line_no=lineno, col_no=columnno) + ], + arg_msg="name '{}' is not defined".format(expected_type), + ) + + +def type_pack_and_check( + value, + expected_types: List[str], + filename=None, + lineno=None, + columno=None, + vm=None, + config_meta=None, +): + """ + Type pack and check + """ + if value is None or value is Undefined or isinstance(value, UndefinedType): + return value + value = objpkg.to_kcl_obj(value) + if ( + isinstance(value, (objpkg.KCLNoneObject, objpkg.KCLUndefinedObject)) + or not expected_types + ): + return value + is_schema = isinstance(value, objpkg.KCLSchemaObject) + value_tpe = value.type_str() + checked = False + convertted_value = None + for expected_type in expected_types: + convertted_value = ( + convert_collection_value( + value, + expected_type, + filename, + lineno, + columno, + vm=vm, + config_meta=config_meta, + ) + if not is_schema + else value + ) + checked, value_tpe = check_type(convertted_value, expected_type, vm=vm) + if checked: + break + if not checked: + if has_literal_type(expected_types): + if isinstance( + value, + ( + objpkg.KCLNoneObject, + objpkg.KCLTrueObject, + objpkg.KCLFalseObject, + objpkg.KCLIntObject, + objpkg.KCLFloatObject, + ), + ): + value_tpe = f"{value_tpe}({value.value})" + elif isinstance(value, objpkg.KCLStringObject): + value_tpe = f'{value_tpe}("{value.value}")' + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg(filename=filename, line_no=lineno, col_no=columno) + ], + arg_msg="expect {}, got {}".format( + common.get_tpes_str(expected_types).replace("@", ""), + common.get_class_name(value_tpe), + ), + ) + return convertted_value + + +def has_literal_type(expected_types: List[str]) -> bool: + for expected_type in expected_types: + if is_literal_expected_type(expected_type): + return True + elif common.is_type_union(expected_type): + for typ in common.split_type_union(expected_type): + if is_literal_expected_type(typ): + return True + return False + + +def is_none_or_undefined(value) -> bool: + """Wether the value is None or Undefined""" + return value is None or isinstance( + value, (objpkg.UndefinedType, objpkg.KCLNoneObject, objpkg.KCLUndefinedObject) + ) + + +def check_type(value, expected_type: Optional[str], vm=None): + value_tpe = value.type_str() + # if expected type is a union type e.g. A|B|C int|str|[int] + if not expected_type or expected_type == _KCL_TYPE_any: + return True, value_tpe + if is_none_or_undefined(value): + return True, value_tpe + if common.is_type_union(expected_type): + return check_type_union( + value, + value_tpe, + expected_type, + vm=vm, + ) + elif check_literal_type(value, expected_type): + return True, expected_type + elif check_number_multiplier_type(value, expected_type): + return True, expected_type + # if value type is a dict type e.g. {"k": "v"} + elif isinstance(value, objpkg.KCLDictObject): + return check_type_dict( + value, + expected_type, + vm=vm, + ) + # if value type is a list type e.g. [1, 2, 3] + elif isinstance(value, objpkg.KCLListObject): + return check_type_list(value, expected_type, vm=vm) + elif value is not None and not isinstance( + value, (objpkg.KCLNoneObject, objpkg.KCLUndefinedObject) + ): + # if value type is a built-in type e.g. str, int, float, bool + if match_builtin_type(value_tpe, expected_type): + return True, value_tpe + # not list/dict, not built-in type, treat as user defined schema + if isinstance(value, objpkg.KCLSchemaObject): + return is_schema_expected_type(expected_type), value_tpe + return False, value_tpe + # Type Error + return False, value_tpe + + +def is_schema_expected_type(expected_type: Optional[str]) -> bool: + """Is scheam expected type""" + if not expected_type: + return True + return ( + not common.islisttype(expected_type) + and not common.isdicttype(expected_type) + and not common.is_builtin_type(expected_type) + and not is_literal_expected_type(expected_type) + ) + + +def is_literal_expected_type(expected_type: Optional[str]) -> bool: + if expected_type in [_KCL_TYPE_None, _KCL_TYPE_True, _KCL_TYPE_False]: + return True + + # str + if expected_type.startswith('"'): + return expected_type.endswith('"') + if expected_type.startswith("'"): + return expected_type.endswith("'") + + # int or float + if expected_type.isdigit(): + return True + if expected_type.replace(".", "", 1).isdigit() and expected_type.count(".") < 2: + return True + + # non literal type + return False + + +def check_literal_type(value, expected_type: Optional[str]): + if not is_literal_expected_type(expected_type): + return False + # none + if isinstance(value, objpkg.KCLNoneObject): + return expected_type == _KCL_TYPE_None + + # bool + if isinstance(value, objpkg.KCLFalseObject): + return expected_type == _KCL_TYPE_False + if isinstance(value, objpkg.KCLTrueObject): + return expected_type == _KCL_TYPE_True + + # number + if isinstance(value, (objpkg.KCLIntObject, objpkg.KCLFloatObject)): + return f"{value.value}" == expected_type + + # str + if isinstance(value, objpkg.KCLStringObject): + return f"{value.value}" == literal_eval(expected_type) + + return False + + +def check_number_multiplier_type(value, expected_type: Optional[str]): + """Check number multiplier""" + if isinstance(value, objpkg.KCLNumberMultiplierObject): + import kclvm.kcl.types.type_parser as type_parser + + if type_parser.is_number_multiplier_literal_type(expected_type): + return str(value) == expected_type + return expected_type == "units.NumberMultiplier" + return False + + +def check_type_dict( + value, + expected_type, + filename=None, + lineno=None, + columnno=None, + vm=None, +): + # Empty any type in [] or {:} + if expected_type == "": + return True, common.DICT_TYPE_NAME + if not common.isdicttype(expected_type): + return False, common.DICT_TYPE_NAME + + # validation None type on dict key and value + expected_type = common.dereferencetype(expected_type) + expected_key_type, expected_value_type = common.separate_kv(expected_type) + if expected_key_type is None or expected_value_type is None: + # either expected key type or value type can't be None + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg(filename=filename, line_no=lineno, col_no=columnno) + ], + arg_msg="error in dict type, key-value type can't be None", + ) + expected_type = common.dereferencetype(expected_type) + runtime_key_type, runtime_value_type = common.separate_kv(expected_type) + if runtime_key_type is None or runtime_value_type is None: + # either runtime key type or value type can't be None + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg(filename=filename, line_no=lineno, col_no=columnno) + ], + arg_msg="error in dict type, key-value type can't be None", + ) + # foreach k,v in dict, check expected and runtime type of key and value + for k, v in value.value.items(): + key_checked, value_checked = True, True + key_value_tpe, value_value_tpe = "", "" + # if no type is specified in dict, ignore drill-down type check + if expected_value_type: + value_checked, value_value_tpe = check_type(v, expected_value_type, vm=vm) + if not key_checked or not value_checked: + # shortcut on check failure + return False, "{{{}:{}}}".format(key_value_tpe, value_value_tpe) + return True, common.DICT_TYPE_NAME + + +def check_type_list( + value, + expected_type, + vm=None, +): + # Empty any type in [] or {:} + if expected_type == "": + return True, common.LIST_TYPE_NAME + if not common.islisttype(expected_type): + return False, common.LIST_TYPE_NAME + expected_type = common.dereferencetype(expected_type) + # foreach element in list, check expected and runtime type + for i in value.value: + checked, value_tpe = check_type(i, expected_type, vm=vm) + if not checked: + # shortcut on check failure + return False, "[{}]".format(value_tpe) + return True, common.LIST_TYPE_NAME + + +def check_type_builtin( + value, + expected_types, + should_raise_err=True, +): + if not expected_types: + return True + if any([tpe not in BUILTIN_TYPES for tpe in expected_types]): + return True + if any([match_builtin_type(value.type_str(), tpe) for tpe in expected_types]): + return True + if should_raise_err: + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + arg_msg="expect {}, got {}".format( + common.get_tpes_str(expected_types).replace("@", ""), + common.get_class_name(value.type_str()), + ), + ) + return False + + +def match_builtin_type(value_tpe, expected_type): + return ( + value_tpe == expected_type + or value_tpe == common.CLASS_TYPE_TMPL.format(expected_type) + or (value_tpe == INT_TYPE and expected_type == FLOAT_TYPE) + ) + + +def check_type_union(value, value_tpe, expected_type, vm=None): + """ + Match built-in union type or single built-in type + """ + expected_types = common.split_type_union(expected_type) + if len(expected_types) == 1: + return False + return ( + any( + [ + check_type( + value, + tpe, + vm=vm, + )[0] + for tpe in expected_types + ] + ), + value_tpe, + ) diff --git a/internal/kclvm_py/compiler/extension/builtin/__init__.py b/internal/kclvm_py/compiler/extension/builtin/__init__.py new file mode 100644 index 000000000..4bc55a25a --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/__init__.py @@ -0,0 +1,17 @@ +from .builtin import ( + kcl_builtin, + BUILTIN_FUNCTIONS, + STANDARD_SYSTEM_MODULES, + get_builtin_func_objects, + get_system_module_func_objects, + get_system_module_members, +) + +__all__ = [ + "kcl_builtin", + "BUILTIN_FUNCTIONS", + "STANDARD_SYSTEM_MODULES", + "get_builtin_func_objects", + "get_system_module_func_objects", + "get_system_module_members", +] diff --git a/internal/kclvm_py/compiler/extension/builtin/builtin.py b/internal/kclvm_py/compiler/extension/builtin/builtin.py new file mode 100644 index 000000000..26045214a --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/builtin.py @@ -0,0 +1,427 @@ +from typing import Callable, Optional, Dict, Set +from functools import wraps +import inspect + +from kclvm.api.object import ( + KCLObject, + KCLNoneObject, + KCLTrueObject, + KCLSchemaObject, + KCLBuiltinFunctionObject, + to_kcl_obj, +) +from kclvm.api.object.internal import kcl_option +from kclvm.compiler.build.utils import units + +import kclvm.kcl.info as kcl_info + +STANDARD_SYSTEM_MODULES = [ + "collection", + "net", + "math", + "datetime", + "regex", + "yaml", + "json", + "crypto", + "base64", + "testing", + "units", +] +STANDARD_SYSTEM_MODULE_LOCATION = "kclvm.compiler.extension.builtin.system_module" + + +def kcl_builtin(func): + """KCL builtin decorator""" + + @wraps(func) + def decorated(*args, **kwargs): + return func(*args, **kwargs) + + return decorated + + +def kcl_obj_builtin(func): + """KCL builtin decorator""" + + @wraps(func) + def decorated(*args, **kwargs): + return func(*args, **kwargs) + + return decorated + + +@kcl_builtin +def KMANGLED_option( + key: str, *, type="", required=False, default=None, help="", file="", line=0 +): + """Return the top level argument by the key""" + + return kcl_option( + key, + type=type, + required=required, + default=default, + help=help, + file=file, + line=line, + ) + + +@kcl_builtin +def KMANGLED_print(*args, **kwargs): + """Prints the values to stdout""" + import builtins + + builtins.print(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_multiplyof(a, b): + """Check if the modular result of a and b is 0""" + if isinstance(a, int) and isinstance(b, int): + return (a % b) == 0 + return False + + +@kcl_builtin +def KMANGLED_isunique(inval): + """Check if a list has duplicated elements""" + if isinstance(inval, list): + if len(inval) == len(set(inval)): + return True + return False + + +@kcl_builtin +def KMANGLED_len(inval): + """Return the length of a value""" + import builtins + + return builtins.len(inval) + + +@kcl_builtin +def KMANGLED_abs(*args, **kwargs): + """Return the absolute value of the argument.""" + import builtins + + return builtins.abs(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_all_true(*args, **kwargs): + """Return True if bool(x) is True for all values x in the iterable. + + If the iterable is empty, return True. + """ + import builtins + + return builtins.all(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_any_true(*args, **kwargs): + """Return True if bool(x) is True for any x in the iterable. + + If the iterable is empty, return False. + """ + import builtins + + return builtins.any(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_hex(*args, **kwargs): + """Return the hexadecimal representation of an integer.""" + import builtins + + return builtins.hex(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_sorted(*args, **kwargs): + """Return a new list containing all items from the iterable in ascending order. + + A custom key function can be supplied to customize the sort order, and the reverse + flag can be set to request the result in descending order. + """ + import builtins + + return [x for x in builtins.sorted(*args, **kwargs)] + + +@kcl_builtin +def KMANGLED_bin(*args, **kwargs): + """Return the binary representation of an integer.""" + import builtins + + return builtins.bin(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_oct(*args, **kwargs): + """Return the octal representation of an integer.""" + import builtins + + return builtins.oct(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_ord(*args, **kwargs): + """Return the Unicode code point for a one-character string.""" + import builtins + + return builtins.ord(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_range(*args, **kwargs): + """Return the range of a value""" + import builtins + + return [x for x in builtins.range(*args, **kwargs)] + + +@kcl_builtin +def KMANGLED_max(*args, **kwargs): + """With a single iterable argument, return its biggest item. + The default keyword-only argument specifies an object to return + if the provided iterable is empty. With two or more arguments, + return the largest argument. + """ + import builtins + + return builtins.max(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_min(*args, **kwargs): + """With a single iterable argument, return its smallest item. + The default keyword-only argument specifies an object to return + if the provided iterable is empty. With two or more arguments, + return the smallest argument. + """ + import builtins + + return builtins.min(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_sum(*args, **kwargs): + """When the iterable is empty, return the start value. This function is + intended specifically for use with numeric values and may reject + non-numeric types. + """ + import builtins + + return builtins.sum(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_pow(*args, **kwargs): + """Equivalent to x**y (with two arguments) or x**y % z (with three arguments) + + Some types, such as ints, are able to use a more efficient algorithm when + invoked using the three argument form. + """ + import builtins + + return builtins.pow(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_round(*args, **kwargs): + """Round a number to a given precision in decimal digits. + + The return value is an integer if ndigits is omitted or None. + Otherwise the return value has the same type as the number. + ndigits may be negative. + """ + import builtins + + return builtins.round(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_zip(*args, **kwargs): + """Return a zip object whose .__next__() method returns + a tuple where the i-th element comes from the i-th iterable + argument. The .__next__() method continues until the shortest + iterable in the argument sequence is exhausted and then + it raises StopIteration. + """ + import builtins + + return [list(r) for r in builtins.zip(*args, **kwargs)] + + +@kcl_builtin +def KMANGLED_int(*args, **kwargs) -> int: + """Convert a number or string to an integer, or return 0 if no arguments + are given. If x is a number, return x.__int__(). For floating point numbers, + this truncates towards zero. + """ + args = list(args) + if len(args) >= 1 and isinstance(args[0], str): + args[0] = str(units.to_quantity(args[0])) + return int(*tuple(args), **kwargs) + + +@kcl_builtin +def KMANGLED_float(x) -> float: + """Convert a string or number to a floating point number, if possible.""" + return float(x) + + +@kcl_builtin +def KMANGLED_str(x) -> str: + """Create a new string object from the given object. + If encoding or errors is specified, then the object must + expose a data buffer that will be decoded using the + given encoding and error handler. + """ + return str(x) + + +@kcl_builtin +def KMANGLED_list(*args, **kwargs) -> list: + """Built-in mutable sequence. + + If no argument is given, the constructor creates a new empty list. + The argument must be an iterable if specified. + """ + return list(*args, **kwargs) + + +@kcl_builtin +def KMANGLED_dict(x) -> dict: + """dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a + mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized + as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary + initialized with the name=value pairs in the keyword argument list. + For example: dict(one=1, two=2) + """ + return dict(x) + + +@kcl_builtin +def KMANGLED_bool(x) -> bool: + """Returns True when the argument x is true, False otherwise. + The builtins True and False are the only two instances of the class bool. + The class bool is a subclass of the class int, and cannot be subclassed. + """ + return bool(x) + + +@kcl_obj_builtin +def KMANGLED_typeof(x: any, *, full_name: bool = False) -> str: + """Return the type of the kcl object""" + if isinstance(full_name, KCLTrueObject): + full_name = True + + if x is None or isinstance(x, KCLNoneObject): + return "None" + + if isinstance(x, KCLSchemaObject): + if full_name: + return x.full_type_str() + else: + return x.type_str() + + if isinstance(x, KCLObject): + return x.type_str() + else: + return type(x).__name__ + + +BUILTIN_FUNCTIONS_MAP = { + "option": KMANGLED_option, + "print": KMANGLED_print, + "multiplyof": KMANGLED_multiplyof, + "isunique": KMANGLED_isunique, + "len": KMANGLED_len, + "abs": KMANGLED_abs, + "all_true": KMANGLED_all_true, + "any_true": KMANGLED_any_true, + "hex": KMANGLED_hex, + "sorted": KMANGLED_sorted, + "bin": KMANGLED_bin, + "oct": KMANGLED_oct, + "ord": KMANGLED_ord, + "range": KMANGLED_range, + "max": KMANGLED_max, + "min": KMANGLED_min, + "sum": KMANGLED_sum, + "pow": KMANGLED_pow, + "round": KMANGLED_round, + "zip": KMANGLED_zip, + "bool": KMANGLED_bool, + "int": KMANGLED_int, + "str": KMANGLED_str, + "float": KMANGLED_float, + "list": KMANGLED_list, + "dict": KMANGLED_dict, + "typeof": KMANGLED_typeof, +} + +BUILTIN_FUNCTIONS = list(BUILTIN_FUNCTIONS_MAP.keys()) + + +def get_builtin_func_objects(): + """Get all builtin function objects""" + return [ + KCLBuiltinFunctionObject(name=builtin, function=BUILTIN_FUNCTIONS_MAP[builtin]) + for builtin in BUILTIN_FUNCTIONS + ] + + +def new_builtin_function( + name: str, func: Callable +) -> Optional[KCLBuiltinFunctionObject]: + """New a KCL builtin function object using native python function""" + if not func or not name: + return None + return KCLBuiltinFunctionObject(name=name, function=func) + + +def get_system_module_func_objects( + module_name: str, +) -> Dict[str, KCLBuiltinFunctionObject]: + """Get all KCL builtin functions from the standard system module named 'module_name'""" + if not module_name or module_name not in STANDARD_SYSTEM_MODULES: + return {} + module = __import__( + f"{STANDARD_SYSTEM_MODULE_LOCATION}.{module_name}", + fromlist=STANDARD_SYSTEM_MODULE_LOCATION, + ) + members = inspect.getmembers(module) + result = { + kcl_info.demangle(member_name): new_builtin_function( + kcl_info.demangle(member_name), member + ) + if inspect.isfunction(member) + else to_kcl_obj(member) + for member_name, member in members + if kcl_info.ismangled(member_name) + } + return result + + +def get_system_module_members( + module_name: str, +) -> Dict[str, Set[str]]: + """Get all members from the standard system module named 'module_name'""" + if not module_name or module_name not in STANDARD_SYSTEM_MODULES: + return {} + module = __import__( + f"{STANDARD_SYSTEM_MODULE_LOCATION}.{module_name}", + fromlist=STANDARD_SYSTEM_MODULE_LOCATION, + ) + members = inspect.getmembers(module) + result = { + kcl_info.demangle(member_name) + for member_name, _ in members + if kcl_info.ismangled(member_name) + } + return result diff --git a/internal/kclvm_py/compiler/extension/builtin/system_module/base64.py b/internal/kclvm_py/compiler/extension/builtin/system_module/base64.py new file mode 100644 index 000000000..1bc516130 --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/system_module/base64.py @@ -0,0 +1,9 @@ +import base64 as _base64 + + +def KMANGLED_encode(value: str, encoding: str = "utf-8") -> str: + return _base64.b64encode(value.encode(encoding)).decode(encoding) + + +def KMANGLED_decode(value: str, encoding: str = "utf-8") -> str: + return _base64.b64decode(value).decode(encoding) diff --git a/internal/kclvm_py/compiler/extension/builtin/system_module/collection.py b/internal/kclvm_py/compiler/extension/builtin/system_module/collection.py new file mode 100644 index 000000000..32a7aa66c --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/system_module/collection.py @@ -0,0 +1,13 @@ +from copy import deepcopy + +import kclvm.api.object.internal.common as common + + +def KMANGLED_union_all(data: list) -> dict: + if not data: + return {} + data_copy = deepcopy(data) + value = data[0] + for d in data_copy[1:]: + common.union(value, d) + return value diff --git a/internal/kclvm_py/compiler/extension/builtin/system_module/crypto.py b/internal/kclvm_py/compiler/extension/builtin/system_module/crypto.py new file mode 100644 index 000000000..f60d85a6e --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/system_module/crypto.py @@ -0,0 +1,25 @@ +import hashlib as _hashlib + + +def KMANGLED_md5(value: str, encoding: str = "utf-8") -> str: + return _hashlib.md5(value.encode(encoding)).hexdigest() + + +def KMANGLED_sha1(value: str, encoding: str = "utf-8") -> str: + return _hashlib.sha1(value.encode(encoding)).hexdigest() + + +def KMANGLED_sha224(value: str, encoding: str = "utf-8") -> str: + return _hashlib.sha224(value.encode(encoding)).hexdigest() + + +def KMANGLED_sha256(value: str, encoding: str = "utf-8") -> str: + return _hashlib.sha256(value.encode(encoding)).hexdigest() + + +def KMANGLED_sha384(value: str, encoding: str = "utf-8") -> str: + return _hashlib.sha384(value.encode(encoding)).hexdigest() + + +def KMANGLED_sha512(value: str, encoding: str = "utf-8") -> str: + return _hashlib.sha512(value.encode(encoding)).hexdigest() diff --git a/internal/kclvm_py/compiler/extension/builtin/system_module/datetime.py b/internal/kclvm_py/compiler/extension/builtin/system_module/datetime.py new file mode 100644 index 000000000..b9b0c191b --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/system_module/datetime.py @@ -0,0 +1,32 @@ +#! /usr/bin/env python3 + +from datetime import datetime as dt +import time as _time + + +def KMANGLED_today(): + """ + Return the datetime today + """ + return str(dt.today()) + + +def KMANGLED_now(): + """ + Return the time at now + """ + return _time.asctime(_time.localtime(_time.time())) + + +def KMANGLED_ticks() -> float: + """ + Return the current time in seconds since the Epoch. + """ + return _time.time() + + +def KMANGLED_date() -> str: + """ + Return the datetime string + """ + return _time.strftime("%Y-%m-%d %H:%M:%S", _time.localtime()) diff --git a/internal/kclvm_py/compiler/extension/builtin/system_module/json.py b/internal/kclvm_py/compiler/extension/builtin/system_module/json.py new file mode 100644 index 000000000..c2364ae71 --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/system_module/json.py @@ -0,0 +1,30 @@ +from pathlib import Path +import json + +from .util import filter_fields + + +def KMANGLED_encode( + data, sort_keys=False, indent=None, ignore_private=False, ignore_none=False +): + return json.dumps( + filter_fields(data, ignore_private, ignore_none), + sort_keys=sort_keys, + indent=indent, + ) + + +def KMANGLED_decode(value: str): + return json.loads(value) + + +def KMANGLED_dump_to_file( + data, + filename: str, + sort_keys=False, + indent=None, + ignore_private=False, + ignore_none=False, +): + json_str = KMANGLED_encode(data, sort_keys, indent, ignore_private, ignore_none) + Path(filename).write_text(json_str) diff --git a/internal/kclvm_py/compiler/extension/builtin/system_module/math.py b/internal/kclvm_py/compiler/extension/builtin/system_module/math.py new file mode 100644 index 000000000..beea98f88 --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/system_module/math.py @@ -0,0 +1,67 @@ +#! /usr/bin/env python3 + +import math + + +def KMANGLED_ceil(*args, **kwargs): + return math.ceil(*args, **kwargs) + + +def KMANGLED_factorial(*args, **kwargs): + return math.factorial(*args, **kwargs) + + +def KMANGLED_floor(*args, **kwargs): + return math.floor(*args, **kwargs) + + +def KMANGLED_gcd(*args, **kwargs): + return math.gcd(*args, **kwargs) + + +def KMANGLED_isfinite(*args, **kwargs): + return math.isfinite(*args, **kwargs) + + +def KMANGLED_isinf(*args, **kwargs): + return math.isinf(*args, **kwargs) + + +def KMANGLED_isnan(*args, **kwargs): + return math.isnan(*args, **kwargs) + + +def KMANGLED_modf(x): + return list(math.modf(x)) + + +def KMANGLED_exp(*args, **kwargs): + return math.exp(*args, **kwargs) + + +def KMANGLED_expm1(*args, **kwargs): + return math.expm1(*args, **kwargs) + + +def KMANGLED_log(*args, **kwargs): + return math.log(*args, **kwargs) + + +def KMANGLED_log1p(*args, **kwargs): + return math.log1p(*args, **kwargs) + + +def KMANGLED_log2(*args, **kwargs): + return math.log2(*args, **kwargs) + + +def KMANGLED_log10(n): + return math.log10(n) + + +def KMANGLED_pow(*args, **kwargs): + return math.pow(*args, **kwargs) + + +def KMANGLED_sqrt(*args, **kwargs): + return math.sqrt(*args, **kwargs) diff --git a/internal/kclvm_py/compiler/extension/builtin/system_module/net.py b/internal/kclvm_py/compiler/extension/builtin/system_module/net.py new file mode 100644 index 000000000..32eb49e8c --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/system_module/net.py @@ -0,0 +1,153 @@ +#! /usr/bin/env python3 +import ipaddress as _ip +import socket as _socket + +import kclvm.kcl.error as kcl_error + + +def check_empty_str(ip): + if not isinstance(ip, str) or ip.strip() == "": + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg="ip must be non-empty string", + ) + + +def _get_ip(ip): + try: + return _ip.ip_address(ip) + except ValueError: + return None + + +def KMANGLED_split_host_port(ip_end_point: str): + """ + split the 'host' and 'port' from the ip end point + """ + check_empty_str(ip_end_point) + return ip_end_point.split(":") + + +def KMANGLED_join_host_port(host, port): + """ + merge the 'host' and 'port' + """ + return "{}:{}".format(host, port) + + +def KMANGLED_fqdn(name=""): + """ + get Fully Qualified Domain Name (FQDN) + """ + return _socket.getfqdn(str(name)) + + +def KMANGLED_parse_IP(ip): + """ + parse 'ip' to a real IP address + """ + return _get_ip(ip) + + +def KMANGLED_to_IP4(ip): + """ + get the IP4 form of 'ip' + """ + return str(_get_ip(ip)) + + +def KMANGLED_to_IP16(ip): + """ + get the IP16 form of 'ip' + """ + return int(_get_ip(ip)) + + +def KMANGLED_IP_string(ip: str): + """ + get the IP string + """ + return _get_ip(ip) + + +def KMANGLED_is_IPv4(ip: str): + """ + whether 'ip' is a IPv4 one + """ + ip = _get_ip(ip) + return isinstance(ip, _ip.IPv4Address) + + +def KMANGLED_is_IP(ip: str) -> bool: + """ + whether ip is a valid ip address + + Parameters + ---------- + - ip: input ip address + + Returns + ------- + - is_ip: a bool type return value + """ + ip = _get_ip(ip) + return ip is not None + + +def KMANGLED_is_loopback_IP(ip: str): + """ + whether 'ip' is a loopback one + """ + ip = _get_ip(ip) + return ip.is_loopback if ip else False + + +def KMANGLED_is_multicast_IP(ip: str): + """ + whether 'ip' is a multicast one + """ + ip = _get_ip(ip) + return ip.is_multicast if ip else False + + +def KMANGLED_is_interface_local_multicast_IP(ip: str): + """ + whether 'ip' is a interface, local and multicast one + """ + try: + ip = _ip.ip_interface(ip) + return (ip.is_site_local and ip.is_multicast) if ip else False + except ValueError: + return False + + +def KMANGLED_is_link_local_multicast_IP(ip: str): + """ + whether 'ip' is a link local and multicast one + """ + ip = _get_ip(ip) + return (ip.is_link_local and ip.is_multicast) if ip else False + + +def KMANGLED_is_link_local_unicast_IP(ip: str): + """ + whether 'ip' is a link local and unicast one + """ + ip = _get_ip(ip) + return (ip.is_link_local and not ip.is_multicast) if ip else False + + +def KMANGLED_is_global_unicast_IP(ip: str): + """ + whether 'ip' is a global and unicast one + """ + ip = _get_ip(ip) + return (ip.is_global and not ip.is_multicast) if ip else False + + +def KMANGLED_is_unspecified_IP(ip: str): + """ + whether 'ip' is a unspecified one + """ + ip = _get_ip(ip) + return ip.is_unspecified if ip else False diff --git a/internal/kclvm_py/compiler/extension/builtin/system_module/regex.py b/internal/kclvm_py/compiler/extension/builtin/system_module/regex.py new file mode 100644 index 000000000..ed20b1306 --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/system_module/regex.py @@ -0,0 +1,47 @@ +#! /usr/bin/env python3 +import re as _re + + +def KMANGLED_replace(string: str, pattern: str, replace: str, count: int = 0): + """ + Return the string obtained by replacing the leftmost non-overlapping occurrences + of the pattern in string by the replacement. + """ + return _re.sub(pattern, replace, string, count) + + +def KMANGLED_match(string: str, pattern: str): + """ + Try to apply the pattern at the start of the string, returning a Match object, or None if no match was found. + """ + return bool(_re.match(pattern, string)) + + +def KMANGLED_compile(pattern: str): + """ + Compile a regular expression pattern, returning a bool value denoting whether the pattern is valid + """ + return bool(_re.compile(pattern)) + + +def KMANGLED_findall(string: str, pattern: str): + """ + Return a list of all non-overlapping matches in the string. + """ + return _re.findall(pattern, string) + + +def KMANGLED_search(string: str, pattern: str): + """ + Scan through string looking for a match to the pattern, + returning a Match object, or None if no match was found. + """ + return bool(_re.search(pattern, string)) + + +def KMANGLED_split(string: str, pattern: str, maxsplit: int = 0): + """ + Scan through string looking for a match to the pattern, + returning a Match object, or None if no match was found. + """ + return _re.split(pattern, string, maxsplit) diff --git a/internal/kclvm_py/compiler/extension/builtin/system_module/testing.py b/internal/kclvm_py/compiler/extension/builtin/system_module/testing.py new file mode 100644 index 000000000..7fa5227f0 --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/system_module/testing.py @@ -0,0 +1,25 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import os +import typing + + +def KMANGLED_arguments(name: str, value: typing.Union[bool, int, float, str]) -> None: + """Set arguments for option function in test.""" + + assert isinstance(name, str) + assert isinstance(value, (bool, int, float, str)) + + # TODO: KMANGLED_arguments Support complex parameter types + + assert name, f"testing.arguments: '{name}' is invalid name" + return + + +def KMANGLED_setting_file(filename: str) -> None: + """Set setting file for option function in test.""" + assert isinstance(filename, str) + + assert os.path.exists(filename), f"testing.setting_file: '{filename}' not exists" + assert os.path.isfile(filename), f"testing.setting_file: '{filename}' is not file" + return diff --git a/internal/kclvm_py/compiler/extension/builtin/system_module/units.py b/internal/kclvm_py/compiler/extension/builtin/system_module/units.py new file mode 100644 index 000000000..d4c400c86 --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/system_module/units.py @@ -0,0 +1,140 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +from typing import Union + +import kclvm.api.object as objpkg + +# -------------------------------------- +# Numerical Constants +# Usage: +# import units +# memory = 1024 * units.Mi # 1024Mi +# -------------------------------------- + +KMANGLED_n = 1e-09 +KMANGLED_u = 1e-06 +KMANGLED_m = 0.001 +KMANGLED_k = 1_000 +KMANGLED_K = 1_000 +KMANGLED_M = 1_000_000 +KMANGLED_G = 1_000_000_000 +KMANGLED_T = 1_000_000_000_000 +KMANGLED_P = 1_000_000_000_000_000 +KMANGLED_Ki = 1024 +KMANGLED_Mi = 1024 ** 2 +KMANGLED_Gi = 1024 ** 3 +KMANGLED_Ti = 1024 ** 4 +KMANGLED_Pi = 1024 ** 5 + +UNIT_MAPPING = { + "n": KMANGLED_n, + "u": KMANGLED_u, + "m": KMANGLED_m, + "k": KMANGLED_k, + "K": KMANGLED_K, + "M": KMANGLED_M, + "G": KMANGLED_G, + "T": KMANGLED_T, + "P": KMANGLED_P, + "Ki": KMANGLED_Ki, + "Mi": KMANGLED_Mi, + "Gi": KMANGLED_Gi, + "Ti": KMANGLED_Ti, + "Pi": KMANGLED_Pi, +} + +# -------------------------------------- +# Numerical Multiplier Type +# Usage: +# import units +# memory: units.NumberMultiplier = 1M +# -------------------------------------- + +KMANGLED_NumberMultiplier = objpkg.KCLNumberMultiplierTypeObject() + +# ------------------------------------------ +# Unit ToString Methods +# Usage: +# import units +# disk = units.to_Ki(1024) # "1Ki" +# Input: +# num: int +# Returns: +# int +# Raises: +# ValueError on invalid or unknown input +# ------------------------------------------ + + +def KMANGLED_to_n(num: int) -> str: + """Int literal to string with `n` suffix""" + return to_unit(num, "n") + + +def KMANGLED_to_u(num: int) -> str: + """Int literal to string with `u` suffix""" + return to_unit(num, "u") + + +def KMANGLED_to_m(num: int) -> str: + """Int literal to string with `m` suffix""" + return to_unit(num, "m") + + +def KMANGLED_to_K(num: int) -> str: + """Int literal to string with `K` suffix""" + return to_unit(num, "K") + + +def KMANGLED_to_M(num: int) -> str: + """Int literal to string with `M` suffix""" + return to_unit(num, "M") + + +def KMANGLED_to_G(num: int) -> str: + """Int literal to string with `G` suffix""" + return to_unit(num, "G") + + +def KMANGLED_to_T(num: int) -> str: + """Int literal to string with `T` suffix""" + return to_unit(num, "T") + + +def KMANGLED_to_P(num: int) -> str: + """Int literal to string with `P` suffix""" + return to_unit(num, "P") + + +def KMANGLED_to_Ki(num: int) -> str: + """Int literal to string with `Ki` suffix""" + return to_unit(num, "Ki") + + +def KMANGLED_to_Mi(num: int) -> str: + """Int literal to string with `Mi` suffix""" + return to_unit(num, "Mi") + + +def KMANGLED_to_Gi(num: int) -> str: + """Int literal to string with `Gi` suffix""" + return to_unit(num, "Gi") + + +def KMANGLED_to_Ti(num: int) -> str: + """Int literal to string with `Ti` suffix""" + return to_unit(num, "Ti") + + +def KMANGLED_to_Pi(num: int) -> str: + """Int literal to string with `Pi` suffix""" + return to_unit(num, "Pi") + + +def to_unit(num: Union[int, float], suffix: str) -> str: + """Connect numbers and suffixes""" + if not isinstance(num, (int, float)): + raise ValueError("Unsupported number type: {}".format(type(num))) + if not suffix or not isinstance(suffix, str) or suffix not in list(UNIT_MAPPING): + raise ValueError("Unsupported unit suffix: {}".format(suffix)) + return str(int(num // UNIT_MAPPING[suffix])) + suffix diff --git a/internal/kclvm_py/compiler/extension/builtin/system_module/util.py b/internal/kclvm_py/compiler/extension/builtin/system_module/util.py new file mode 100644 index 000000000..57a269fd7 --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/system_module/util.py @@ -0,0 +1,39 @@ +from typing import Any + +from kclvm.api.object import KCLNumberMultiplierObject +from kclvm.api.object.internal import Undefined, UndefinedType + +import kclvm.kcl.info as kcl_info + + +def filter_fields( + value: Any, ignore_private: bool = False, ignore_none: bool = False +) -> Any: + """Remove private attributes start with '_' and None value in data""" + if not value: + return value + if value is Undefined or isinstance(value, UndefinedType): + return value + if isinstance(value, KCLNumberMultiplierObject): + return str(value) + if isinstance(value, list): + return [ + filter_fields(_v, ignore_private, ignore_none) + for _v in value + if ignore_none and _v is not None or not ignore_none + if not isinstance(_v, UndefinedType) + ] + elif isinstance(value, dict): + return { + filter_fields(_k, ignore_private, ignore_none): filter_fields( + _v, ignore_private, ignore_none + ) + for _k, _v in value.items() + if not kcl_info.isprivate_field(_k) and ignore_private or not ignore_private + if ignore_none and _v is not None or not ignore_none + if not isinstance(_v, UndefinedType) + } + elif isinstance(value, (int, float, str, bool)): + return value + else: + raise Exception("Invalid KCL Object") diff --git a/internal/kclvm_py/compiler/extension/builtin/system_module/yaml.py b/internal/kclvm_py/compiler/extension/builtin/system_module/yaml.py new file mode 100644 index 000000000..f6c043b09 --- /dev/null +++ b/internal/kclvm_py/compiler/extension/builtin/system_module/yaml.py @@ -0,0 +1,46 @@ +from io import StringIO +from pathlib import Path +import ruamel.yaml as yaml + +from .util import filter_fields + +_yaml = yaml.YAML() +_yaml.representer.add_representer( + str, + lambda dumper, data: dumper.represent_scalar( + u"tag:yaml.org,2002:str", data, style="|" + ) + if "\n" in data + else dumper.represent_str(data), +) +# Convert None to null +_yaml.representer.add_representer( + type(None), + lambda dumper, data: dumper.represent_scalar(u"tag:yaml.org,2002:null", u"null"), +) + + +def KMANGLED_encode(data, sort_keys=False, ignore_private=False, ignore_none=False): + buffer = StringIO() + data_filtered = filter_fields(data, ignore_private, ignore_none) + if sort_keys: + sorted_dict = yaml.comments.CommentedMap() + for k in sorted(data_filtered): + sorted_dict[k] = data_filtered[k] + _yaml.dump(sorted_dict, buffer) + else: + _yaml.dump(data_filtered, buffer) + return buffer.getvalue() + + +def KMANGLED_decode(value: str): + buffer = StringIO(value) + data = yaml.safe_load(buffer) + return data + + +def KMANGLED_dump_to_file( + data, filename: str, sort_keys=False, ignore_private=False, ignore_none=False +): + yaml_str = KMANGLED_encode(data, sort_keys, ignore_private, ignore_none) + Path(filename).write_text(yaml_str) diff --git a/internal/kclvm_py/compiler/extension/plugin/Makefile b/internal/kclvm_py/compiler/extension/plugin/Makefile new file mode 100644 index 000000000..93a02bd3c --- /dev/null +++ b/internal/kclvm_py/compiler/extension/plugin/Makefile @@ -0,0 +1,9 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +default: + python3 ./__main__.py + +test: + python3 -m pytest + +clean: diff --git a/internal/kclvm_py/compiler/extension/plugin/__init__.py b/internal/kclvm_py/compiler/extension/plugin/__init__.py new file mode 100644 index 000000000..6c5abc72f --- /dev/null +++ b/internal/kclvm_py/compiler/extension/plugin/__init__.py @@ -0,0 +1,25 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +from .plugin import ( + reset_plugin, + get_plugin_root, + get_info, + get_source_code, + get_plugin, + init_plugin, + gendoc, +) +from .plugin_model import PLUGIN_MODULE_NAME, get_plugin_func_objects, get_plugin_names + +__all__ = [ + "reset_plugin", + "get_plugin_root", + "get_info", + "get_source_code", + "get_plugin", + "init_plugin", + "gendoc", + "PLUGIN_MODULE_NAME", + "get_plugin_func_objects", + "get_plugin_names", +] diff --git a/internal/kclvm_py/compiler/extension/plugin/main.py b/internal/kclvm_py/compiler/extension/plugin/main.py new file mode 100644 index 000000000..433e57e77 --- /dev/null +++ b/internal/kclvm_py/compiler/extension/plugin/main.py @@ -0,0 +1,125 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import argparse +import sys +import json + +import kclvm.compiler.extension.plugin.plugin as plugin + + +def Main(): + parser = argparse.ArgumentParser(prog="kcl-plugin") + + subparsers = parser.add_subparsers( + dest="kcl_plugin_subcmd_name", help="kcl plugin sub commands" + ) + + parser_list = subparsers.add_parser("list", help="list all plugins") + + parser_init = subparsers.add_parser("init", help="init a new plugin") + + parser_info = subparsers.add_parser("info", help="show plugin document") + subparsers.add_parser("gendoc", help="gen all plugins document") + subparsers.add_parser("version", help="show plugin version") + parser_test = subparsers.add_parser("test", help="test plugin") + + parser_list.add_argument(dest="plugin_keyword", metavar="keyword", nargs="?") + + parser_init.add_argument(dest="plugin_name", metavar="name") + parser_info.add_argument(dest="plugin_name", metavar="name", nargs="?") + + parser_test.add_argument(dest="plugin_name", metavar="name") + + args = parser.parse_args() + + if args.kcl_plugin_subcmd_name == "list": + plugin_root = plugin.get_plugin_root() + if not plugin_root: + print("# plugin_root: ") + sys.exit(0) + + print(f"# plugin_root: {plugin.get_plugin_root()}") + names = plugin.get_plugin_names() + + count = 0 + for name in names: + if args.plugin_keyword and args.plugin_keyword not in name: + continue + info = plugin.get_info(name) + print(f"{info['name']}: {info['describe']} - {info['version']}") + count = count + 1 + + if count == 0: + print("no plugin") + sys.exit(0) + + sys.exit(0) + + if args.kcl_plugin_subcmd_name == "init": + plugin_root = plugin.get_plugin_root() + if not plugin_root: + print("# plugin_root: ") + sys.exit(0) + + names = plugin.get_plugin_names() + if args.plugin_name in names: + print(f"{args.plugin_name} exists") + sys.exit(1) + + plugin.init_plugin(args.plugin_name) + sys.exit(0) + + if args.kcl_plugin_subcmd_name == "info": + plugin_root = plugin.get_plugin_root() + if not plugin_root: + print("# plugin_root: ") + sys.exit(0) + + if not args.plugin_name: + print(f"plugin_root: {plugin.get_plugin_root()}") + sys.exit(0) + + names = plugin.get_plugin_names() + if args.plugin_name not in names: + print(f"{args.plugin_name} not found") + sys.exit(1) + + info = plugin.get_info(args.plugin_name) + + print(json.dumps(info, indent=4)) + sys.exit(0) + + if args.kcl_plugin_subcmd_name == "gendoc": + plugin_root = plugin.get_plugin_root() + if not plugin_root: + print("# plugin_root: ") + sys.exit(0) + + for name in plugin.get_plugin_names(): + plugin.gendoc(name) + + sys.exit(0) + + if args.kcl_plugin_subcmd_name == "version": + print(plugin.get_plugin_version()) + sys.exit(0) + + # TODO: Using kcl-test + if args.kcl_plugin_subcmd_name == "test": + names = plugin.get_plugin_names() + if args.plugin_name not in names: + print(f"{args.plugin_name} not found") + sys.exit(1) + + import pytest + + pytest.main(["-x", plugin.get_plugin_root(args.plugin_name)]) + sys.exit(0) + + parser.print_help() + plugin_root = plugin.get_plugin_root() + sys.exit(0) + + +if __name__ == "__main__": + Main() diff --git a/internal/kclvm_py/compiler/extension/plugin/plugin.py b/internal/kclvm_py/compiler/extension/plugin/plugin.py new file mode 100644 index 000000000..b82574928 --- /dev/null +++ b/internal/kclvm_py/compiler/extension/plugin/plugin.py @@ -0,0 +1,326 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import os +import glob +import re +import copy +import pathlib +import typing +import sys +import traceback +import inspect + +import kclvm.kcl.info as kcl_info +import kclvm.compiler.extension.plugin.template as plugin_template + + +UNKNOWN_VERSION = "unknown" + + +class Init: + def __init__(self, root: str = "", info_map=None): + if info_map is None: + info_map = {} + self.plugins_root = root or "" + self.plugins_info_map = info_map or {} + + +_re_plugin_name = re.compile("^[a-z][a-z0-9_-]*$") + + +def _is_valid_plugin_name(plugin_name: str) -> bool: + return _re_plugin_name.match(plugin_name) is not None + + +def _normalize_plugin_name(plugin_name: str) -> str: + if plugin_name.startswith("kcl_plugin.") or plugin_name.startswith("kcl_plugin/"): + plugin_name = plugin_name[len("kcl_plugin.") :] + return plugin_name + + +def _get_plugin(root: str, plugin_name: str) -> typing.Optional[any]: + if not os.path.exists(f"{root}/{plugin_name}/plugin.py"): + return None + + import importlib.util + + spec = importlib.util.spec_from_file_location( + f"kcl_plugin.{plugin_name}", f"{root}/{plugin_name}/plugin.py" + ) + pkg = importlib.util.module_from_spec(spec) + + try: + spec.loader.exec_module(pkg) + except Exception: + ex_type, ex_val, ex_stack = sys.exc_info() + print( + f"WARN: {root}/{plugin_name}/plugin.py:{traceback.extract_tb(ex_stack)[-1].lineno}: init_plugin failed: {ex_val}" + ) + return None + + func_map = {} + for func_name, func_body in inspect.getmembers(pkg, inspect.isfunction): + if func_name.startswith(kcl_info.MANGLE_PREFIX): + func_map[func_name[len(kcl_info.MANGLE_PREFIX) :]] = func_body + + for func_name, func_body in inspect.getmembers(pkg, inspect.isfunction): + if not func_name.startswith(kcl_info.MANGLE_PREFIX) and func_name[0] != "_": + if func_name not in func_map: + func_map[kcl_info.MANGLE_PREFIX + func_name] = func_body + + for func_name in func_map: + setattr(pkg, func_name, func_map[func_name]) + + return pkg + + +def _get_info(pkg) -> dict: + info = copy.deepcopy(getattr(pkg, "INFO")) + + info["method"] = {} + for s in dir(pkg): + if s.startswith(kcl_info.MANGLE_PREFIX): + func_name = s[len(kcl_info.MANGLE_PREFIX) :] + func_doc = getattr(pkg, s).__doc__ + info["method"][func_name] = func_doc if func_doc else "no doc" + + return info + + +def find_plugin_root() -> typing.Optional[str]: + return _find_plugin_root() + + +def _find_plugin_root() -> typing.Optional[str]: + # 1. try $KCL_PLUGINS_ROOT env + env_plugin_root = os.getenv("KCL_PLUGINS_ROOT", "") + if env_plugin_root != "": + return env_plugin_root + + # 2. try ${pwd}/.../plugins/hello/plugin.py + cwd_plugin_path = pathlib.Path(os.getcwd()).absolute() + root = cwd_plugin_path.root + while cwd_plugin_path: + if cwd_plugin_path == cwd_plugin_path.parent or str(cwd_plugin_path) == root: + break + plugin_list_file_path = cwd_plugin_path.joinpath("plugins/hello/plugin.py") + if plugin_list_file_path.exists() and plugin_list_file_path.is_file(): + return str(cwd_plugin_path.joinpath("plugins")) + if cwd_plugin_path.joinpath("kcl.mod").exists(): + break + cwd_plugin_path = cwd_plugin_path.parent + + # 3. try ${__file__}/.../plugins/hello/plugin.py + cwd_plugin_path = pathlib.Path(__file__).parent.absolute() + root = cwd_plugin_path.root + while cwd_plugin_path: + if cwd_plugin_path == cwd_plugin_path.parent or str(cwd_plugin_path) == root: + break + plugin_list_file_path = cwd_plugin_path.joinpath("plugins/hello/plugin.py") + if plugin_list_file_path.exists() and plugin_list_file_path.is_file(): + return str(cwd_plugin_path.joinpath("plugins")) + cwd_plugin_path = cwd_plugin_path.parent + + # 4. try $HOME/.kusion/kclvm/plugins + home_plugin_root = os.path.join(os.getenv("HOME"), ".kusion/kclvm/plugins") + if os.path.exists(f"{home_plugin_root}/hello/plugin.py"): + return home_plugin_root + + # 5. not found + return None + + +def _init_plugin_root() -> typing.Tuple[typing.Optional[str], dict]: + plugins_root = _find_plugin_root() + if plugins_root is None: + return None, {} + + plugins_info = {} + + # 'hello' is builtin plugin, and used in test code + if not os.path.exists(f"{plugins_root}/hello/plugin.py"): + os.makedirs(f"{plugins_root}/hello") + + with open(f"{plugins_root}/hello/plugin.py", "w") as file: + file.write(plugin_template.get_plugin_template_code("hello")) + with open(f"{plugins_root}/hello/plugin_test.py", "w") as file: + file.write(plugin_template.get_plugin_test_template_code("hello")) + + # scan all plugins + k_files = glob.glob(f"{plugins_root}/*/plugin.py", recursive=False) + for i in range(len(k_files)): + plugin_name = os.path.basename(k_files[i][: -len("/plugin.py")]) + if _is_valid_plugin_name(plugin_name): + pkg = _get_plugin(plugins_root, plugin_name) + if not pkg: + continue + info = _get_info(pkg) + + plugins_info[plugin_name] = info + return plugins_root, plugins_info + + +# init plugins +_plugin_root, _plugin_info_map = _init_plugin_root() +init_ = Init(_plugin_root, _plugin_info_map) + + +# ----------------------------------------------------------------------------- +# API +# ----------------------------------------------------------------------------- + + +def reset_plugin(plugin_root: str = ""): + global _plugin_root + global _plugin_info_map + global init_ + + os.environ["KCL_PLUGINS_ROOT"] = f"{plugin_root}" + _plugin_root, _plugin_info_map = _init_plugin_root() + init_ = Init(_plugin_root, _plugin_info_map) + + +def get_plugin_version() -> str: + if not init_.plugins_root: + return UNKNOWN_VERSION + version_path = pathlib.Path(f"{init_.plugins_root}/VERSION") + if version_path.exists(): + return version_path.read_text() + return UNKNOWN_VERSION + + +def get_plugin_root(plugin_name: str = "") -> typing.Optional[str]: + if init_.plugins_root is None: + return None + if plugin_name != "": + plugin_name = _normalize_plugin_name(plugin_name) + return f"{init_.plugins_root}/{plugin_name}" + + return init_.plugins_root + + +def get_plugin_names() -> typing.List[str]: + if init_.plugins_root is None: + return [] + plugin_names = [] + for s in init_.plugins_info_map: + plugin_names.append(s) + + plugin_names.sort() + return plugin_names + + +def get_info(plugin_name: str) -> typing.Optional[dict]: + if init_.plugins_root is None: + return None + + plugin_name = _normalize_plugin_name(plugin_name) + + if plugin_name not in init_.plugins_info_map: + return None + return init_.plugins_info_map[plugin_name] + + +def get_source_code(plugin_name: str) -> typing.Optional[str]: + if init_.plugins_root is None: + return None + + plugin_name = _normalize_plugin_name(plugin_name) + + if plugin_name not in init_.plugins_info_map: + return None + + code = "" + with open(f"{init_.plugins_root}/{plugin_name}/plugin.py") as f: + code += f.read() + return code + + +def get_plugin(plugin_name: str) -> typing.Optional[any]: + if init_.plugins_root is None: + return None + + plugin_name = _normalize_plugin_name(plugin_name) + return _get_plugin(init_.plugins_root, plugin_name) + + +# ----------------------------------------------------------------------------- +# UTILS +# ----------------------------------------------------------------------------- + + +def init_plugin(plugin_name: str): + if init_.plugins_root is None: + return None + + plugin_name = _normalize_plugin_name(plugin_name) + + if not _is_valid_plugin_name(plugin_name): + print(f'WARN: init_plugin("{plugin_name}") failed, invalid name') + return + + if os.path.exists(f"{init_.plugins_root}/{plugin_name}/plugin.py"): + print(f'WARN: init_plugin("{plugin_name}") failed, plugin exists') + return + + golden_plugin_skectch_code = plugin_template.get_plugin_template_code(plugin_name) + golden_plugin_skectch_code_test = plugin_template.get_plugin_test_template_code( + plugin_name + ) + + if not os.path.exists(f"{init_.plugins_root}/{plugin_name}"): + os.makedirs(f"{init_.plugins_root}/{plugin_name}") + + with open(f"{init_.plugins_root}/{plugin_name}/plugin.py", "w") as file: + file.write(golden_plugin_skectch_code) + + with open(f"{init_.plugins_root}/{plugin_name}/plugin_test.py", "w") as file: + file.write(golden_plugin_skectch_code_test) + + gendoc(plugin_name) + + +def gendoc(plugin_name: str): + if init_.plugins_root is None: + print("WARN: plugin root not found") + return + + if not _is_valid_plugin_name(plugin_name): + print(f'WARN: gendoc("{plugin_name}") failed, invalid name') + return + + pkg = _get_plugin(init_.plugins_root, plugin_name) + if not pkg: + print("WARN: plugin init failed") + return + info = _get_info(pkg) + if info is None: + print(f'WARN: gendoc("{plugin_name}") failed, not found plugin') + return + + with open(f"{init_.plugins_root}/{plugin_name}/api.md", "w") as file: + file.write(f"# plugin: `{info['name']}` - {info['describe']}\n\n") + file.write(f"{info['long_describe']}\n\n") + file.write(f"*version: {info['version']}*\n\n") + + for func_name in info["method"]: + func_doc = info["method"][func_name] + + func_doc_line = [] + for line in func_doc.splitlines(): + if line.startswith(" "): + line = line[4:] + func_doc_line.append(line) + + file.write(f"## `{func_name}`\n\n") + for line in func_doc_line: + file.write(f"{line}\n") + + file.write("\n") + + return + + +# ----------------------------------------------------------------------------- +# END +# ----------------------------------------------------------------------------- diff --git a/internal/kclvm_py/compiler/extension/plugin/plugin_model.py b/internal/kclvm_py/compiler/extension/plugin/plugin_model.py new file mode 100644 index 000000000..901d22db2 --- /dev/null +++ b/internal/kclvm_py/compiler/extension/plugin/plugin_model.py @@ -0,0 +1,48 @@ +from typing import Callable, Optional, Dict +from functools import wraps +import inspect + +import kclvm.kcl.info as kcl_info + +from .plugin import get_plugin_names, get_plugin +from kclvm.api.object import KCLBuiltinFunctionObject + + +PLUGIN_MODULE_NAME = "kcl_plugin." +STANDARD_SYSTEM_MODULE_LOCATION = "kclvm_plugin" + + +def kcl_plugin(func): + @wraps(func) + def decorated(*args, **kwargs): + return func(*args, **kwargs) + + return decorated + + +def new_plugin_function( + name: str, func: Callable +) -> Optional[KCLBuiltinFunctionObject]: + """New a plugin function object using a native plugin function""" + if not func or not name: + return None + return KCLBuiltinFunctionObject(name=name, function=func) + + +def get_plugin_func_objects(plugin_name: str) -> Dict[str, KCLBuiltinFunctionObject]: + """Get all plugin function objects from a plugin named 'plugin_name'""" + if ( + not plugin_name + or plugin_name.replace(PLUGIN_MODULE_NAME, "") not in get_plugin_names() + ): + return {} + module = get_plugin(plugin_name) + members = inspect.getmembers(module) + result = { + kcl_info.demangle(func_name): new_plugin_function( + kcl_info.demangle(func_name), func + ) + for func_name, func in members + if kcl_info.ismangled(func_name) + } + return result diff --git a/internal/kclvm_py/compiler/extension/plugin/template.py b/internal/kclvm_py/compiler/extension/plugin/template.py new file mode 100644 index 000000000..bb1653f5e --- /dev/null +++ b/internal/kclvm_py/compiler/extension/plugin/template.py @@ -0,0 +1,100 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + + +def get_plugin_template_code(plugin_name: str) -> str: + return f'''# Copyright 2020 The KCL Authors. All rights reserved. + +INFO = {{ + 'name': '{plugin_name}', + 'describe': '{plugin_name} doc', + 'long_describe': 'long describe', + 'version': '0.0.1', +}} + + +global_int: int = 0 + + +def set_global_int(v: int): + global global_int + global_int = v + + +def get_global_int() -> int: + return global_int + + +def say_hello(msg: str): + print('{plugin_name}.say_hello:', msg) + return None + + +def add(a: int, b: int) -> int: + """add two numbers, and return result""" + return a + b + + +def tolower(s: str) -> str: + return s.lower() + + +def update_dict(d: dict, key: str, value: str) -> dict: + d[key] = value + return d + + +def list_append(l: list, *values) -> list: + for v in values: + l.append(v) + return l + + +def foo(a, b, *, x, **values): + print(a, b, x, values) + return {{'a': a, 'b': b, 'x': x, **values}} +''' + + +def get_plugin_test_template_code(plugin_name: str) -> str: + return """# Copyright 2020 The KCL Authors. All rights reserved. + +# python3 -m pytest + +import plugin + + +def test_add(): + assert plugin.add(1, 2) == 3 + + +def test_tolower(): + assert plugin.tolower('KCL') == 'kcl' + + +def test_update_dict(): + assert plugin.update_dict({{'name': 123}}, 'name', 'kcl')['name'] == 'kcl' + + +def test_list_append(): + l = plugin.list_append(['abc'], 'name', 123) + assert len(l) == 3 + assert l[0] == 'abc' + assert l[1] == 'name' + assert l[2] == 123 + + +def test_foo(): + v = plugin.foo('aaa', 'bbb', x=123, y=234, abcd=1234) + assert len(v) == 5 + assert v['a'] == 'aaa' + assert v['b'] == 'bbb' + assert v['x'] == 123 + assert v['y'] == 234 + assert v['abcd'] == 1234 + + v = plugin.foo('aaa', 'bbb', x=123) + assert len(v) == 3 + assert v['a'] == 'aaa' + assert v['b'] == 'bbb' + assert v['x'] == 123 +""" diff --git a/internal/kclvm_py/compiler/parser/__init__.py b/internal/kclvm_py/compiler/parser/__init__.py new file mode 100644 index 000000000..a64023e0a --- /dev/null +++ b/internal/kclvm_py/compiler/parser/__init__.py @@ -0,0 +1,11 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +from .parser import ParseMode, ParseFile, ParseExpr, LoadProgram + + +__all__ = [ + "ParseMode", + "ParseFile", + "ParseExpr", + "LoadProgram", +] diff --git a/internal/kclvm_py/compiler/parser/lark.proto b/internal/kclvm_py/compiler/parser/lark.proto new file mode 100644 index 000000000..060a04ea3 --- /dev/null +++ b/internal/kclvm_py/compiler/parser/lark.proto @@ -0,0 +1,18 @@ +// Copyright 2020 The KCL Authors. All rights reserved. + +syntax = "proto3"; + +package kclvm.compiler.parser.lark; + +// https://github.com/lark-parser/lark/blob/master/docs/_static/lark_cheatsheet.pdf + +message Tree { + string type = 1; + string token_value = 2; + repeated Tree children = 3; + + int32 line = 101; + int32 column = 102; + int32 end_line = 103; + int32 end_column = 104; +} diff --git a/internal/kclvm_py/compiler/parser/lark_parser.py b/internal/kclvm_py/compiler/parser/lark_parser.py new file mode 100644 index 000000000..02a09f908 --- /dev/null +++ b/internal/kclvm_py/compiler/parser/lark_parser.py @@ -0,0 +1,240 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import typing +import pathlib + +from lark import Lark +from lark.indenter import Indenter +from lark.tree import Tree as LarkTree +from lark.lexer import Token as LarkToken + +import kclvm.kcl.error as kcl_error +import kclvm.compiler.parser.lark_pb2 as lark_pb + +_CACHE_FILE = pathlib.Path(__file__).parent.joinpath("lark_parser.pickle") +START_RULE = "start" + +filename = "" + + +class KCLIndenter(Indenter): + NL_type = "NEWLINE" + OPEN_PAREN_types = ["LPAR", "LSQB", "LBRACE"] + CLOSE_PAREN_types = ["RPAR", "RSQB", "RBRACE"] + INDENT_type = "_INDENT" + DEDENT_type = "_DEDENT" + tab_len = 4 + _indent_has_space = False # Mark whether there are spaces in an indent_level + _indent_has_tab = False # Mark whether there are tabs in an indent_level + + def __init__(self): + super().__init__() + self.reset_indent_space_tab() + + def reset_indent_space_tab(self): + self._indent_has_space = False + self._indent_has_tab = False + + def process(self, stream): + self.paren_level = 0 + self.indent_level = [0] + self.reset_indent_space_tab() + return self._process(stream) + + def check_tab_error(self, space_count, tab_count, line=None, column=None): + """Check TabError: Inconsistent use of tabs and spaces in indentation""" + self._indent_has_space = True if space_count else self._indent_has_space + self._indent_has_tab = True if tab_count else self._indent_has_tab + if self._indent_has_space and self._indent_has_tab: + import kclvm.compiler.parser.lark_parser as lark_parser + + kcl_error.report_exception( + err_type=kcl_error.ErrType.TabError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=lark_parser.filename, line_no=line, col_no=column + ) + ], + ) + + def handle_NL(self, token): + """Do not edit it, inherit from base class 'Indenter'""" + if self.paren_level > 0: + return + + yield token + + indent_str = token.rsplit("\n", 1)[1] # Tabs and spaces + space_count, tab_count = indent_str.count(" "), indent_str.count("\t") + indent = space_count + tab_count * self.tab_len + self.check_tab_error(space_count, tab_count, token.end_line, token.end_column) + + if indent > self.indent_level[-1]: + self.indent_level.append(indent) + yield LarkToken.new_borrow_pos(self.INDENT_type, indent_str, token) + else: + while indent < self.indent_level[-1]: + self.indent_level.pop() + self.reset_indent_space_tab() + lark_token = LarkToken.new_borrow_pos( + self.DEDENT_type, indent_str, token + ) + yield lark_token + + if indent != self.indent_level[-1]: + import kclvm.compiler.parser.lark_parser as lark_parser + + kcl_error.report_exception( + err_type=kcl_error.ErrType.IndentationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=lark_parser.filename, + line_no=lark_token.end_line, + col_no=lark_token.end_column, + ) + ], + arg_msg=kcl_error.INDENTATION_ERROR_MSG.format(str(indent)), + ) + + +_kcl_lark_parser: typing.Optional[Lark] = None + + +def GetKclLarkParser() -> Lark: + global _kcl_lark_parser + + if _kcl_lark_parser is None: + _kcl_lark_parser = Lark.open( + "../../kcl/grammar/kcl.lark", + parser="lalr", + propagate_positions=True, + postlex=KCLIndenter(), + rel_to=__file__, + cache=str(_CACHE_FILE), + ) + + return _kcl_lark_parser + + +def IsRuleType(node_type: str) -> bool: + return node_type.islower() + + +def IsTokenType(node_type: str) -> bool: + return node_type.isupper() + + +def GetNode( + node: lark_pb.Tree, node_type: str, *more_node_type: str +) -> typing.Optional[lark_pb.Tree]: + node_list = GetNodeList(node, node_type, *more_node_type, max_size=1) + return node_list[0] if node_list else None + + +def GetNodeList( + node: lark_pb.Tree, + target_node_type: str, + *more_target_node_type: str, + max_size=0, + recursively: bool = True +) -> typing.List[lark_pb.Tree]: + node_type_list = [target_node_type, *more_target_node_type] + + if not node: + return [] + + if node.type in node_type_list: + return [node] # OK + + # try sub node + node_list = [] + for n in node.children or []: + if n.type in node_type_list: + node_list.append(n) + if 0 < max_size <= len(node_list): + return node_list + continue + if recursively: + node_list.extend( + GetNodeList( + n, target_node_type, *more_target_node_type, max_size=max_size + ) + ) + if 0 < max_size <= len(node_list): + return node_list + + return node_list + + +def WalkTree(t: lark_pb.Tree, walk_fn): + walk_fn(t) + for n in t.children: + WalkTree(n, walk_fn) + + +def ParseFile(filename: str, code: str, ignore_file_line: bool = False) -> lark_pb.Tree: + if not code: + with open(filename) as f: + code = str(f.read()) + return ParseCode(code, ignore_file_line=ignore_file_line) + + +def ParseCode(src: str, ignore_file_line: bool = False) -> lark_pb.Tree: + def _pb_build_Tree(_t: LarkTree) -> lark_pb.Tree: + if isinstance(_t, LarkTree): + rule_type = _t.data + + assert rule_type.islower() + assert len(_t.children) >= 0 + + # Empty file and return a empty lark tree node + if rule_type == START_RULE and not _t.children: + t = lark_pb.Tree( + type=rule_type, + token_value="", + children=[], + ) + elif not ignore_file_line: + t = lark_pb.Tree( + type=rule_type, + token_value="", # rule, not token + children=[], + line=_t.meta.line, + column=_t.meta.column, + end_line=_t.meta.end_line, + end_column=_t.meta.end_column, + ) + else: + t = lark_pb.Tree( + type=rule_type, token_value="", children=[] # rule, not token + ) + + for v in _t.children: + t.children.append(_pb_build_Tree(v)) + + return t + + if isinstance(_t, LarkToken): + token_type = _t.type + + assert token_type.isupper() + + if not ignore_file_line: + return lark_pb.Tree( + type=token_type, + token_value=_t.value, + children=[], + line=_t.line, + column=_t.column, + end_line=_t.end_line, + end_column=_t.end_column, + ) + else: + return lark_pb.Tree(type=token_type, token_value=_t.value, children=[]) + + return lark_pb.Tree() + + # To prevent empty files and files that only contain line continuation symbols + src += "\n" + tree = GetKclLarkParser().parse(src) + return _pb_build_Tree(tree) diff --git a/internal/kclvm_py/compiler/parser/lark_pb2.py b/internal/kclvm_py/compiler/parser/lark_pb2.py new file mode 100644 index 000000000..5a9a1df8b --- /dev/null +++ b/internal/kclvm_py/compiler/parser/lark_pb2.py @@ -0,0 +1,31 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from __future__ import annotations # for python 3.7 + +from typing import List +from dataclasses import dataclass, field + +# protobuf: +# +# message Tree { +# string type = 1; +# string token_value = 2; +# repeated Tree children = 3; +# +# int32 line = 101; +# int32 column = 102; +# int32 end_line = 103; +# int32 end_column = 104; +# } + + +@dataclass +class Tree: + type: str = "" + token_value: str = "" + children: List[Tree] = field(default_factory=lambda: []) + + line: int = 0 + column: int = 0 + end_line: int = 0 + end_column: int = 0 diff --git a/internal/kclvm_py/compiler/parser/lark_tree.py b/internal/kclvm_py/compiler/parser/lark_tree.py new file mode 100644 index 000000000..0560ebb66 --- /dev/null +++ b/internal/kclvm_py/compiler/parser/lark_tree.py @@ -0,0 +1,413 @@ +#! /usr/bin/env python3 + +import os +import json +from typing import Union, List, Dict, Optional +from copy import deepcopy + +from lark.exceptions import UnexpectedCharacters, UnexpectedToken +from lark.tree import Tree as LarkTree +from lark.lexer import Token as LarkToken + +import kclvm.kcl.error as kcl_error +from kclvm.compiler.parser.lark_parser import GetKclLarkParser + +TREE_TYPE = "tree" +TOKEN_TYPE = "token" + + +class Token: + ASSIGN = "ASSIGN" # "=" + COLON = "COLON" # ":" + SEMI_COLON = "SEMI_COLON" # ";" + COMMA = "COMMA" # "," + LEFT_PARENTHESES = "LEFT_PARENTHESES" # "(" + RIGHT_PARENTHESES = "RIGHT_PARENTHESES" # ")" + LEFT_BRACKETS = "LEFT_BRACKETS" # "[" + RIGHT_BRACKETS = "RIGHT_BRACKETS" # "]" + LEFT_BRACE = "LEFT_BRACE" # "{" + RIGHT_BRACE = "RIGHT_BRACE" # "}" + PLUS = "PLUS" # "+" + MINUS = "MINUS" # "-" + MULTIPLY = "MULTIPLY" # "*" + DIVIDE = "DIVIDE" # "/" + MOD = "MOD" # "%" + DOT = "DOT" # "." + AND = "AND" # "&" + OR = "OR" # "|" + XOR = "XOR" # "^" + NOT = "NOT" # "~" + LESS_THAN = "LESS_THAN" # "<" + GREATER_THAN = "GREATER_THAN" # ">" + EQUAL_TO = "EQUAL_TO" # "==" + NOT_EQUAL_TO = "NOT_EQUAL_TO" # "!=" + GREATER_THAN_OR_EQUAL_TO = "GREATER_THAN_OR_EQUAL_TO" # ">=" + LESS_THAN_OR_EQUAL_TO = "LESS_THAN_OR_EQUAL_TO" # "<=" + DOUBLE_STAR = "DOUBLE_STAR" # "**" + DOUBLE_DIVIDE = "DOUBLE_DIVIDE" # "//" + SHIFT_LEFT = "SHIFT_LEFT" # "<<" + SHIFT_RIGHT = "SHIFT_RIGHT" # ">>" + + COMP_PLUS = "COMP_PLUS" # "+=" + COMP_MINUS = "COMP_MINUS" # "-=" + COMP_MULTIPLY = "COMP_MULTIPLY" # "*=" + COMP_DIVIDE = "COMP_DIVIDE" # "/=" + COMP_MOD = "COMP_MOD" # "%=" + COMP_AND = "COMP_AND" # "&=" + COMP_OR = "COMP_OR" # "|=" + COMP_XOR = "COMP_XOR" # "^=" + COMP_NOT = "COMP_NOT" # "~=" + COMP_DOUBLE_STAR = "COMP_DOUBLE_STAR" # "**=" + COMP_DOUBLE_DIVIDE = "COMP_DOUBLE_DIVIDE" # "//=" + COMP_SHIFT_LEFT = "COMP_SHIFT_LEFT" # "<<=" + COMP_SHIFT_RIGHT = "COMP_SHIFT_RIGHT" # ">>=" + + IMPORT = "IMPORT" # "import" + AS = "AS" # "as" + DEF = "DEF" # "def" + LAMBDA = "LAMBDA" # "lambda" + SCHEMA = "SCHEMA" # "schema" + MIXIN = "MIXIN" # "mixin" + PROTOCOL = "PROTOCOL" # "protocol" + RELAXED = "RELAXED" # "relaxed" + CHECK = "CHECK" # "check" + INIT = "INIT" # "init" + TYPE = "TYPE" # "type" + FOR = "FOR" # "for" + ASSERT = "ASSERT" # "assert" + IF = "IF" # "if" + ELIF = "ELIF" # "elif" + ELSE = "ELSE" # "else" + L_OR = "L_OR" # "or" + L_AND = "L_AND" # "and" + L_NOT = "NOT" # "not" + L_L_NOT = "L_NOT" + + IN = "IN" # "in" + IS = "IS" # "is" + FINAL = "FINAL" # "final" + + ALL = "ALL" + ANY = "ANY" + MAP = "MAP" + FILTER = "FILTER" + + TRUE = "TRUE" + FALSE = "FALSE" + NONE = "NONE" + + NAME = "NAME" + COMMENT = "COMMENT" + NEWLINE = "NEWLINE" + + STRING = "STRING" + LONG_STRING = "LONG_STRING" + + DEC_NUMBER = "DEC_NUMBER" + HEX_NUMBER = "HEX_NUMBER" + OCT_NUMBER = "OCT_NUMBER" + BIN_NUMBER = "BIN_NUMBER" + FLOAT_NUMBER = "FLOAT_NUMBER" + IMAG_NUMBER = "IMAG_NUMBER" + + RIGHT_ARROW = "RIGHT_ARROW" + + @staticmethod + def is_string(token: str): + return token in [Token.STRING, Token.LONG_STRING] + + +class Tree: + DICT_KEY = "dict_key" + DICT_COMP = "dict_comp" + LIST_COMP = "list_comp" + DICT_EXPR = "dict_expr" + LIST_EXPR = "list_expr" + MULTI_EXPR = "multi_expr" + DOUBLE_STAR_EXPR = "double_star_expr" + CONFIG_EXPR = "config_expr" + SUB_SCRIPT = "subscript" + IDENTIFIER = "identifier" + STR_CALL_EXPR = "str_call_expr" + CALL_EXPR = "call_expr" + CALL_SUFFIX = "call_suffix" + SLICE_SUFFIX = "slice_suffix" + COMP_ITER = "comp_iter" + COMP_FOR = "comp_for" + COMP_IF = "comp_if" + COMP_CLAUSE = "comp_clause" + TEST = "test" + OR_TEST = "or_test" + SIMPLE_EXPR = "simple_expr" + EXPR = "expr" + PRIMARY_EXPR = "primary_expr" + STMT = "stmt" + IF_STMT = "if_stmt" + SCHEMA_MEMBER_STMT = ("schema_member_stmt",) + SCHEMA_BODY = "schema_body" + SCHEMA_ARGUMENTS = "schema_arguments" + MIXINS = ("mixins",) + TYPE_ALIAS_STMT = "type_alias_stmt" + SCHEMA_STMT = "schema_stmt" + SIMPLE_STMT = "simple_stmt" + DOC_STMT = "doc_stmt" + COMPOUND_STMT = "compound_stmt" + SMALL_STMT = "small_stmt" + IMPORT_STMT = "import_stmt" + ASSIGN_STMT = "assign_stmt" + ASSERT_STMT = "assert_stmt" + EXPR_STMT = "expr_stmt" + MEMBER_STMT = "member_stmt" + TYPE = "type" + EXECUTION_BLOCK = "execution_block" + STRING_DOT_NAME = "string_dot_name" + + SELECTOR_EXPR = "selector_expr" + SELECTOR_SUFFIX = "selector_suffix" + LIST_SELECTOR_SUFFIX = "list_selector_suffix" + DICT_SELECTOR_SUFFIX = "dict_selector_suffix" + + +BRACKETS_TOKENS = [ + Token.LEFT_PARENTHESES, # "(" + Token.RIGHT_PARENTHESES, # ")" + Token.LEFT_BRACKETS, # "[" + Token.RIGHT_BRACKETS, # "]" + Token.LEFT_BRACE, # "{" + Token.RIGHT_BRACE, # "}" +] +OPERATOR_TOKENS = { + Token.ASSIGN, # "=" + Token.PLUS, # "+" + Token.MINUS, # "-" + Token.MULTIPLY, # "*" + Token.DIVIDE, # "/" + Token.MOD, # "%" + Token.AND, # "&" + Token.OR, # "|" + Token.XOR, # "^" + Token.NOT, # "~" + Token.LESS_THAN, # "<" + Token.GREATER_THAN, # ">" + Token.EQUAL_TO, # "==" + Token.NOT_EQUAL_TO, # "!=" + Token.GREATER_THAN_OR_EQUAL_TO, # ">=" + Token.LESS_THAN_OR_EQUAL_TO, # "<=" + Token.DOUBLE_STAR, # "**" + Token.DOUBLE_DIVIDE, # "//" + Token.SHIFT_LEFT, # "<<" + Token.SHIFT_RIGHT, # ">>" + Token.COMP_PLUS, # "+=" + Token.COMP_MINUS, # "-=" + Token.COMP_MULTIPLY, # "*=" + Token.COMP_DIVIDE, # "/=" + Token.COMP_MOD, # "%=" + Token.COMP_AND, # "&=" + Token.COMP_OR, # "|=" + Token.COMP_XOR, # "^=" + Token.COMP_NOT, # "~=" + Token.COMP_DOUBLE_STAR, # "**=" + Token.COMP_DOUBLE_DIVIDE, # "//=" + Token.COMP_SHIFT_LEFT, # "<<=" + Token.COMP_SHIFT_RIGHT, # ">>=" +} +SEPERATOR_TOKENS = { + Token.COLON, # ":" + Token.SEMI_COLON, # ";" + Token.COMMA, # "," +} +FUNCTION_EXPRS = { + Tree.CALL_EXPR, + Tree.STR_CALL_EXPR, +} +_WALK_FUNCTION_PREFIX = "walk_" + +AstType = Dict[str, Union[str, List]] + + +class TreeWalker: + """ + The TreeWalk class can be used as a superclass in order + to walk an AST or similar tree. + + This class is meant to be subclassed, with the subclass adding walker + methods. + + Per default the walker functions for the nodes are ``'walk_'`` + + class name of the node. So a `expr_stmt` node visit function would + be `walk_expr_stmt`. This behavior can be changed by overriding + the `walk` method. If no walker function exists for a node + (return value `None`) the `generic_walker` walker is used instead. + """ + + def __init__(self) -> None: + pass + + def walk(self, node: AstType) -> None: + """Visit a node.""" + name = node["name"] + method = _WALK_FUNCTION_PREFIX + name + walker = getattr(self, method, self.generic_walk) + walker(node) + + def generic_walk(self, node: AstType): + """Called if no explicit walker function exists for a node.""" + children_key = "children" + if children_key in node: + for n in node[children_key]: + self.walk(n) + else: + self.walk(node) + + def walk_nodes(self, *nodes: Union[AstType, str]) -> None: + """Write nodes""" + if not nodes: + return + for node in nodes: + self.walk_node(node) + + def walk_node(self, node: Union[AstType, str]) -> None: + """Write node""" + self.walk(node) + + def get(self, node: AstType, name: str) -> Optional[AstType]: + """ + Get children from 'node' named 'name' + """ + if not node or "children" not in node: + return None + for i, n in enumerate(node["children"]): + if n["name"] == name: + node["children"] = node["children"][i + 1 :] + return n + return None + + def has(self, node: AstType, name: str) -> bool: + """ + Whether 'node' has the children named 'name' + """ + return name in [n["name"] for n in node["children"]] + + def get_value(self, node: AstType, default: Optional[str] = None) -> Optional[str]: + """ + Get tree node value recursively + """ + if not node: + return default + if node["type"] == TOKEN_TYPE: + return node.get("value", default) + elif node["type"] == TREE_TYPE: + return "".join(self.get_value(n) for n in node["children"]) + return default + + def get_children_value( + self, node: AstType, name: Optional[str] = None, default: Optional[str] = None + ) -> Optional[str]: + """ + Get children value recursively from 'node' named 'name' + """ + return self.get_value(self.get(node, name), default) if name else default + + def get_internal( + self, node: AstType, begin_name: str, end_name: str + ) -> Optional[List[AstType]]: + """ + Get children from 'begin_name' to 'end_name' + """ + begin_index = -1 + end_index = -1 + for i, n in enumerate(node["children"]): + if n["name"] == begin_name and begin_index == -1: + begin_index = i + if n["name"] == end_name: + end_index = i + if end_index >= begin_index: + result = deepcopy(node["children"][begin_index : end_index + 1]) + node["children"] = node["children"][end_index + 1 :] + return result + return None + + +def build_children(t: LarkTree) -> List[AstType]: + """ + Build json ast children of tree node, children type can be token or tree + """ + return list(map(lambda c: walk_lark_tree(c), t.children)) + + +def build_token_content(t: LarkToken) -> AstType: + """ + Build json ast token node + """ + return { + "type": TOKEN_TYPE, + "name": t.type, + "value": t.value, + "line": t.line, + "column": t.column, + } + + +def build_tree_content(t: LarkTree) -> AstType: + """ + Build json ast tree node + """ + return { + "type": TREE_TYPE, + "name": t.data, + "children": build_children(t), + } + + +def walk_lark_tree(t: Union[LarkTree, LarkToken]) -> AstType: + """ + Traverse the lark tree and return python dict + """ + if isinstance(t, LarkTree): + return build_tree_content(t) + elif isinstance(t, LarkToken): + return build_token_content(t) + else: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg="Unknown node type: {}".format(type(t)), + ) + + +def get_lark_tree_from_expr( + source: str, to_json_str: bool = True +) -> Union[str, AstType]: + try: + # Get lark parse tree + parse_tree = GetKclLarkParser().parse(source + "\n") + # Convert python dict to standard json + ast = walk_lark_tree(parse_tree) + return (json.dumps(ast) + "\n") if to_json_str else ast + except ( + UnexpectedCharacters, + UnexpectedToken, + ) as err: + kcl_error.report_exception( + err_type=kcl_error.ErrType.InvalidSyntax_TYPE, + file_msgs=[kcl_error.ErrFileMsg(line_no=err.line, col_no=err.column)], + ) + except Exception as err: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, arg_msg=str(err) + ) + + +def get_lark_tree_from_file(filename: str, to_json_str: bool = True) -> str: + """ + Get kcl json ast from .k file + """ + with open(filename, "r", encoding="utf-8") as pyfile: + source = pyfile.read() + return get_lark_tree_from_expr(source, to_json_str) + + +def get_lark_tree_from_path(path: str) -> List[str]: + """ + Get kcl json ast from kmodule package + """ + return [get_lark_tree_from_file(file) for file in sorted(os.listdir(path))] diff --git a/internal/kclvm_py/compiler/parser/parser.py b/internal/kclvm_py/compiler/parser/parser.py new file mode 100644 index 000000000..ac002776a --- /dev/null +++ b/internal/kclvm_py/compiler/parser/parser.py @@ -0,0 +1,3036 @@ +"""The `parser` file mainly contains the `walker` which is used to +traversal the lark tree generated by lark-parser and generate the kcl +ast. +The KCL parser is mainly based on `ast.TreeWalker` to implement +traversal of all lark tree nodes, generate corresponding ast nodes. + +The `walk` method will automatically call the walk_{node name} method +corresponding lark tree nodes. + +The work flow of walk_{node name} method looks like: + +`lark tree node -> walk -> walk_{node name} -> ast tree node` + +The `generic_walk` method is the default implementation of `walk` method +If one lark tree node does not need the walk_{node name} method. + +The work flow of `generic_walk` method looks like: + +`lark tree node -> walk -> # no walk_{node name} # -> generic_walk -> ast tree node` + +There are also some util methods to simplify each walk_{node name} +method in class `ASTBuilderWalker`. + +:note: Some util methods are for special scenarios. When using these methods, +please carefully read the comments of the method +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" +import typing +import os +import re +import glob + +from ast import literal_eval +from pathlib import Path +from enum import IntEnum + +import lark + +import kclvm.kcl.error as kcl_error +import kclvm.config +import kclvm.api.object.internal as internal +import kclvm.compiler.vfs as vfs +import kclvm.compiler.parser.lark_pb2 as lark_pb +import kclvm.compiler.parser.lark_parser as lark_parser +import kclvm.compiler.parser.lark_tree as lark_tree +import kclvm.compiler.extension.builtin.builtin as builtin +import kclvm.compiler.astutil.fix as fix +import kclvm.compiler.build.preprocess as preprocess +import kclvm.kcl.ast as ast +import kclvm.internal.util.check_utils as check_utils + +from kclvm.internal.util.check_utils import PreCheck, PostCheck, CheckRules +from kclvm.kcl.ast.precedence import OpPrecedence, OP_PREC_MAP + +_INVALID_NEWLINE_STRING_MSG = "invalid NEWLINE token string value {}" + +ENDLINE_TOKEN = "\n" +COMMENT_START_TOKEN = "#" +STRING_INTERPOLATION_REGEX = re.compile(r"\$\{(.*?(:.*?)?)\}") +INLINE_COMMENT_WITH_MULTILINE_REGEX = re.compile("#[^\n]*[\n\t ]*") +KCL_MOD_PATH_ENV = "${KCL_MOD}" +DEFAULT_CACHE_OPTION = vfs.CacheOption() +AST_WITH_PARENT_CACHE_OPTION = vfs.CacheOption(cache_dir=vfs.FST_CACHE_DIR) + + +class ParseMode(IntEnum): + Null = 0 + ParseComments = 1 + + +class StrWithLocation(str): + def __init__(self) -> None: + super().__init__() + self.line: int = 0 + self.column: int = 0 + self.end_line: int = 0 + self.end_column: int = 0 + + def copy_line_column_from(self, node): + self.line = node.line + self.column = node.column + self.end_line = node.end_line + self.end_column = node.end_column + + return self + + +class Token(StrWithLocation): + def __init__(self, value: typing.Optional[str] = None) -> None: + super().__init__() + self.value = value + + +class Mixins(ast.AST): + def __init__(self) -> None: + super().__init__() + self.values: typing.List[ast.Identifier] = [] + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, ast.Identifier)), "value") + def append(self, value: ast.Identifier): + self.values.append(value) + + +def report_complier_err( + arg=None, + err_type=kcl_error.ErrType.CompileError_TYPE, + filename=None, + line_no=None, + col_no=None, + end_col_no=None, +): + kcl_error.report_exception( + err_type=err_type, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=line_no, col_no=col_no, end_col_no=end_col_no + ) + ], + arg_msg=arg, + ) + + +def report_syntax_err( + arg=None, + err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + filename=None, + line_no=None, + col_no=None, + end_col_no=None, + source=None, +): + kcl_error.report_exception( + err_type=err_type, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, + line_no=line_no, + col_no=col_no, + end_col_no=end_col_no, + src_code=source, + ) + ], + arg_msg=arg, + ) + + +def get_ast_cache_option(set_ast_parent: bool = False) -> vfs.CacheOption: + """Get AST cache option with the `set_ast_parent` parameter""" + return AST_WITH_PARENT_CACHE_OPTION if set_ast_parent else DEFAULT_CACHE_OPTION + + +class ASTBuilderWalker(ast.TreeWalker): + def __init__(self): + self.tokens_with_pos = [ + ast.LarkToken.L_COLON, + ast.LarkToken.L_RIGHT_BRACKETS, + ast.LarkToken.L_LEFT_BRACKETS, + ] + + def walk_non_token_children( + self, node: lark_pb.Tree + ) -> typing.List[typing.Union[None, ast.AST, list, str, tuple]]: + """ + Traverse all non-terminal child nodes of the current lark tree node, + and call the corresponding walk method. + Return the list of the walk method returns. + + Args: + node: The lark tree node + + Returns: the list of AST node generated from child nodes of the current lark tree node + + """ + return [ + self.walk(n) for n in node.children if not lark_parser.IsTokenType(n.type) + ] + + def walk_filtered_elements_ordered_by_types( + self, + node: lark_pb.Tree, + filter_types: typing.List[str], + with_token: bool = False, + ) -> typing.List[typing.Union[None, ast.AST, list, str, tuple]]: + """ + Traverse child nodes, not be selected by node types, of the current lark tree node, + and call the corresponding walk method. + Return the list of the walk method returns. + + Args: + node: The lark tree node + filter_types: The list of nodes that do not need to be traversed + with_token: A bool for traverses the terminal node or not + + Returns: the list of AST node generated from child nodes selected of the current lark tree node + + """ + if with_token: + return [self.walk(n) for n in node.children if n.type not in filter_types] + else: + return [ + self.walk(n) + for n in node.children + if n.type not in filter_types and not lark_parser.IsTokenType(n.type) + ] + + def walk_elements_ordered_by_types( + self, node: lark_pb.Tree, node_types: typing.List[str] + ) -> typing.Tuple: + """ + Traverse child nodes, selected by node types, of the current lark tree node, + and call the corresponding walk method. + Return the tuple of the walk method returns. + + Args: + node: The lark tree node + node_types: The list of nodes that need to be traversed + + Returns: the tuple of the walk method returns + + Notes: The order of the elements in node_types must be the same as the order of the lark tree node children. + The order of the elements in return tuple will be the same as the order of the node_types. + + """ + p: typing.List[typing.Union[None, ast.AST, list, str, tuple]] = [] + for n in node.children: + for i, nt in enumerate(node_types): + if n.type == nt: + if i < len(p): + continue + p.extend([None] * (i - len(p))) + p.append(self.walk(n)) + break + + p.extend([None] * (len(node_types) - len(p))) + + assert len(p) == len(node_types) + return tuple(p) + + def walk_all_by_type( + self, node: lark_pb.Tree, node_type: str + ) -> typing.List[typing.Union[None, ast.AST, list, str, tuple]]: + """ + Find all children nodes, selected by node type, of the current lark tree node, + and call the corresponding walk methods. + Return the list of the walk method returns. + + Args: + node: the lark tree node + node_type: type of selected node + + Returns: the list of the walk method returns. + + """ + return [self.walk(n) for n in node.children if n.type == node_type] + + def walk_one_by_type( + self, node: lark_pb.Tree, node_type: str + ) -> typing.Union[None, ast.AST, list, str, tuple]: + """ + Find the first child node, selected by node type, of the current lark tree node, + and call the corresponding walk method. + Return the walk method return. + + Args: + node: the lark tree node + node_type: type of selected node + + Returns: the walk method return. + + """ + for n in node.children: + if n.type == node_type: + return self.walk(n) + return None + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, lark_pb.Tree)), "node") + @PostCheck(lambda v: CheckRules.check_type_allow_none(v, Token)) + def get_token_with_pos(self, node: lark_pb.Tree) -> typing.Optional[Token]: + return ( + Token(node.token_value).copy_line_column_from(node) + if node.type in self.tokens_with_pos + else None + ) + + def walk(self, node: lark_pb.Tree) -> typing.Union[None, ast.AST, list, str, tuple]: + """ + The walk method will automatically call the walk_{node name} method + corresponding lark tree nodes. + Return the ast node, the list/tuple of ast nodes and the str of token value generated by lark tree nodes + + Args: + node: the lark tree node + + Returns: the ast node, the list/tuple of ast nodes and the str of token value generated by lark tree nodes + + """ + assert node + method = self._WALK_FUNCTION_PREFIX + self.get_node_name(node) + walker = getattr(self, method, self.generic_walk) + return walker(node) + + def generic_walk( + self, node: lark_pb.Tree + ) -> typing.Union[None, ast.AST, list, str, tuple]: + """ + The generic_walk method is the default implementation of `walk` method + If one lark tree node does not need the walk_{node name} method. + Up to now, only the node with one child, the node with one non-terminal + child and one "NEWLINE" child, the node with one terminal node is allowed + + Args: + node: the lark tree node in Allowed Cases, Allowed Cases : A -> B, A->B NEWLINE, A -> TOKEN + + Returns: For each case in allowed cases, return walk(B), walk(B), TOKEN.token_value + + """ + assert node + assert len(node.children) <= 2 + if len(node.children) == 1: + return self.walk(node.children[0]) + elif ( + len(node.children) == 2 and node.children[1].type == ast.LarkToken.L_NEWLINE + ): + return self.walk(node.children[0]) + elif len(node.children) == 0 and lark_parser.IsTokenType(node.type): + return ( + node.token_value + if node.type not in self.tokens_with_pos + else self.get_token_with_pos(node) + ) + + raise AttributeError(f"Some 'walk_{node.type}()' methods are missing") + + def get_node_name(self, node: lark_pb.Tree) -> str: + return node.type + + +class ASTBuilder(ASTBuilderWalker): + def __init__( + self, + root: lark_pb.Tree, + *, + pkg: str = "", + name: str = "", + filename: str = "", + mode: ParseMode = ParseMode.Null, + ): + super().__init__() + self.root: lark_pb.Tree = root + self.pkg: str = pkg + self.filename: str = filename + self.name = name + self.mode = mode + self.ast_prece_table: dict = {} + + def BuildAST(self) -> ast.Module: + return check_utils.check_allow_none(self.walk(self.root), ast.Module) + + def register_precedence( + self, + op: typing.Union[ast.BinOp, ast.AugOp, ast.UnaryOp, ast.CmpOp], + node: typing.Union[ast.BinOp, ast.AugOp, ast.UnaryOp, ast.CmpOp, ast.AST], + ): + if not op or not node: + raise ValueError("A operation symbol is needed for precedence registration") + if not isinstance(op, (ast.BinOp, ast.AugOp, ast.UnaryOp, ast.CmpOp)): + raise ValueError( + "{} can not be used to register precedence".format(type(op).__name__) + ) + if not isinstance( + node, (ast.BinOp, ast.AugOp, ast.UnaryOp, ast.CmpOp, ast.AST) + ): + raise ValueError("{} do not need precedence".format(type(node).__name__)) + + prece = OP_PREC_MAP[op] + self.ast_prece_table[hash(node)] = prece + + def get_precedence( + self, node: typing.Union[ast.BinOp, ast.AugOp, ast.UnaryOp, ast.CmpOp, ast.AST] + ) -> int: + return self.ast_prece_table.get(hash(node), OpPrecedence.HIGHEST) + + @property + def is_parse_comments(self) -> bool: + return bool(self.mode & ParseMode.ParseComments) + + def split_newline_value(self, value: str) -> typing.List[str]: + """Split a NEWLINE token value into newline parts and inline comment parts + Input: "\n \n # comment \n # comment \n\n # comment \n" + Output: ["\n \n ", "# comment ", "\n ", "# comment ", "\n\n ", "# comment ", "\n"] + """ + if not value: + return [] + value = value.replace("\r", ENDLINE_TOKEN).replace("\t", " ") + parts: typing.List[str] = [] + # Mark containing COMMENT token + index = value.find(COMMENT_START_TOKEN) + if index == -1: + return [value] # Single NEWLINE without COMMENT + elif index > 0: + parts.append(value[:index]) # Add first NEWLINE token + inline_comments = INLINE_COMMENT_WITH_MULTILINE_REGEX.findall(value) + for comment in inline_comments: + index = comment.find(ENDLINE_TOKEN) + if index == -1: + report_complier_err( + _INVALID_NEWLINE_STRING_MSG.format(comment), filename=self.filename + ) + parts.append(comment[:index]) # Add COMMENT token + parts.append(comment[index:]) # Add NEWLINE token + if len(parts) > 1 and ENDLINE_TOKEN not in parts[-1]: + # Last part is not NEWLINE, raise an error + report_complier_err( + _INVALID_NEWLINE_STRING_MSG.format(value), filename=self.filename + ) + return parts + + def lark_name_to_ast_name(self, lark_name: lark_pb.Tree) -> ast.Name: + assert lark_name.type == lark_tree.Token.NAME + return ast.Name(value=lark_name.token_value).set_ast_position( + lark_name, filename=self.filename + ) + + def walk_NEWLINE(self, node: lark_pb.Tree) -> typing.List[ast.Comment]: + """Syntax + COMMENT: /#[^\n]*/ + NEWLINE: ( /\r?\n[\t ]*/ | COMMENT )+ + """ + if not node or node.type != ast.LarkToken.L_NEWLINE: + return [] + if not self.is_parse_comments: + return [] + parts = self.split_newline_value(node.token_value) + newline_count = 0 + blank_count = 0 + comments: typing.List[ast.Comment] = [] + col_no = node.column + for part in parts: + if part and part.startswith(COMMENT_START_TOKEN): + comments.append( + ast.Comment( + filename=self.filename, + line=node.line + newline_count, + end_line=node.line + newline_count, + column=col_no + blank_count, + end_column=col_no + blank_count + len(part), + text=part, + ) + ) + else: + newline_count += part.count("\n") + blank_count += part[part.rfind("\n") :].count(" ") + col_no = 1 if newline_count > 0 else node.column + return comments + + def walk_start(self, node: lark_pb.Tree) -> ast.Module: + """Syntax + start: (NEWLINE | statement)* + statement: simple_stmt | compound_stmt + simple_stmt: (assign_stmt | expr_stmt | assert_stmt | import_stmt | type_alias_stmt) NEWLINE + compound_stmt: if_stmt | schema_stmt + """ + + def __build_doc() -> str: + if not p.doc and len(p.body) >= 1: + for i, stmt in enumerate(p.body): + p.body[i].filename = p.filename + if isinstance(stmt, ast.ExprStmt): + expr_stmt = typing.cast(ast.ExprStmt, stmt) + if len(expr_stmt.exprs) == 1 and isinstance( + expr_stmt.exprs[0], ast.StringLit + ): + str_lit = check_utils.check_not_none( + expr_stmt.exprs[0], ast.StringLit + ) + if str_lit.is_long_string: + return self.simplify_StringLitValue(str_lit) + break + return "" + + def __build_comments(): + if not self.is_parse_comments: + return [] + comments: typing.List[ast.Comment] = [] + newline_token_list = lark_parser.GetNodeList(node, ast.LarkToken.L_NEWLINE) + for newline in newline_token_list: + comments.extend(self.walk(newline)) + return comments + + p = check_utils.check_not_none( + ast.ASTFactory.get_ast_module(node, self.pkg, self.filename, self.name), + ast.Module, + ) + + p.body = check_utils.check_all_allow_none( + list, self.walk_non_token_children(node), ast.Stmt + ) + p.doc = check_utils.check_allow_none(__build_doc(), str) + p.comments = check_utils.check_all_allow_none( + list, __build_comments(), ast.Comment + ) + + if not self.is_parse_comments: + if p.body: + p.set_end_line_column(p.body[-1]) + else: + p.offset_end_line(-1) + else: + if p.body and p.comments: + if p.body[-1].end_line > p.comments[-1].end_line: + p.set_end_line_column(p.body[-1]) + else: + p.set_end_line_column(p.comments[-1]) + elif p.comments: + p.set_end_line_column(p.comments[-1]) + elif p.body: + p.set_end_line_column(p.body[-1]) + else: + p.offset_end_line(-1) + + return p + + def walk_unification_stmt(self, node: lark_pb.Tree) -> ast.UnificationStmt: + """Syntax + unification_stmt: NAME COLON schema_expr + """ + stmt = ast.ASTFactory.get_ast(ast.UnificationStmt, node, self.filename) + stmt.target, stmt.value = self.walk(node.children[0]), self.walk( + node.children[2] + ) + stmt.target.ctx = ast.ExprContext.STORE + return stmt + + # ----------------------------------------------------- + # type_alias_stmt + # ----------------------------------------------------- + + def walk_type_alias_stmt(self, node: lark_pb.Tree) -> ast.TypeAliasStmt: + """Syntax + type_alias_stmt: "type" NAME ASSIGN type + """ + stmt = ast.ASTFactory.get_ast(ast.TypeAliasStmt, node, self.filename) + types = [ast.LarkToken.L_NAME, ast.LarkToken.L_type] + name, type_node = self.walk_elements_ordered_by_types(node, types) + stmt.type_value = check_utils.check_allow_none(type_node, ast.Type) + stmt.type_name = check_utils.check_not_none(name, ast.Identifier) + return stmt + + # ----------------------------------------------------- + # assign_stmt + # ----------------------------------------------------- + + def walk_assign_stmt( + self, node: lark_pb.Tree + ) -> typing.Union[ast.AssignStmt, ast.AugAssignStmt]: + """Syntax + assign_stmt: identifier [COLON type] (ASSIGN identifier)* ASSIGN test + | identifier (COMP_PLUS | COMP_MINUS | COMP_MULTIPLY | COMP_DOUBLE_STAR | COMP_DIVIDE + | COMP_DOUBLE_DIVIDE | COMP_MOD | COMP_AND | COMP_OR | COMP_XOR | COMP_SHIFT_LEFT + | COMP_SHIFT_RIGHT | L_OR | L_AND) + test + """ + + def __build_assign_stmt() -> ast.AssignStmt: + assign_stmt: ast.AssignStmt = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.AssignStmt, node, self.filename), + ast.AssignStmt, + ) + + assign_stmt.targets = targets + assign_stmt.type_annotation = ( + type_annotation_node.plain_type_str if type_annotation_node else None + ) + assign_stmt.type_annotation_node = type_annotation_node + for target in assign_stmt.targets: + target.ctx = ast.ExprContext.STORE + assign_stmt.value = test + return assign_stmt + + def __build_aug_assign_stmt() -> ast.AugAssignStmt: + aug_assign_stmt = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.AugAssignStmt, node, self.filename), + ast.AugAssignStmt, + ) + + op_node = node.children[1] if len(node.children) == 3 else None + aug_assign_stmt.op = ast.AugOp(op_node.type) + aug_assign_stmt.target = targets[0] + aug_assign_stmt.target.ctx = ast.ExprContext.AUGSTORE + aug_assign_stmt.value = test + return aug_assign_stmt + + targets = check_utils.check_all_not_none( + list, + self.walk_all_by_type(node, ast.LarkToken.L_identifier), + ast.Identifier, + ) + + test = ( + check_utils.check_not_none(self.walk(node.children[-1]), ast.Expr) + if len(node.children) > 0 + else None + ) + + types = [ast.LarkToken.L_type, ast.LarkToken.L_ASSIGN] + type_annotation_node, has_assign = self.walk_elements_ordered_by_types( + node, types + ) + type_annotation_node: ast.Type = check_utils.check_allow_none( + type_annotation_node, ast.Type + ) + has_assign = check_utils.check_allow_none(has_assign, str) + + if has_assign: + return __build_assign_stmt() + elif len(targets) == 1: + return __build_aug_assign_stmt() + + def walk_simple_assign_stmt( + self, node: lark_pb.Tree + ) -> typing.Union[ast.AssignStmt, ast.AugAssignStmt]: + return self.walk_assign_stmt(node) + + # ----------------------------------------------------- + # assert_stmt + # ----------------------------------------------------- + + def walk_assert_stmt(self, node: lark_pb.Tree) -> ast.AssertStmt: + """Syntax + assert_stmt: ASSERT simple_expr (IF simple_expr)? (COMMA test)? + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.AssertStmt, node, self.filename), ast.AssertStmt + ) + + p.test, p.if_cond, p.msg = self.walk_elements_ordered_by_types( + node, + [ + ast.LarkToken.L_simple_expr, + ast.LarkToken.L_simple_expr, + ast.LarkToken.L_test, + ], + ) + p.test = check_utils.check_not_none(p.test, ast.Expr) + p.if_cond = check_utils.check_allow_none(p.if_cond, ast.Expr) + p.msg = check_utils.check_allow_none(p.msg, ast.Expr) + return p + + # ----------------------------------------------------- + # import_stmt + # ----------------------------------------------------- + + def walk_import_stmt(self, node: lark_pb.Tree) -> ast.ImportStmt: + """Syntax + import_stmt: IMPORT dot_name (AS NAME)? + """ + p: ast.ImportStmt = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.ImportStmt, node, self.filename), ast.ImportStmt + ) + + path, ident = self.walk_elements_ordered_by_types( + node, [ast.LarkToken.L_dot_name, ast.LarkToken.L_NAME] + ) + p.path = path[0] + p.path = check_utils.check_not_none(p.path, str) + ident = check_utils.check_allow_none(ident, ast.Identifier) + + p.name = p.path[p.path.rfind(".") + 1 :] + p.asname = "".join(ident.names) if ident else None + + p.path_nodes = path[1] + if ident: + assert ( + len(ident.name_nodes) == 1 + ), "as name in the import stmt must be a single NAME" + p.as_name_node = ident.name_nodes[0] + + return p + + def walk_dot_name(self, node: lark_pb.Tree) -> (str, typing.List[ast.Name]): + """Syntax + dot_name: (leading_dots identifier) | identifier + """ + types = [ast.LarkToken.L_leading_dots, ast.LarkToken.L_identifier] + dots, ident = self.walk_elements_ordered_by_types(node, types) + + dots = check_utils.check_allow_none(dots, str) + ident: ast.Identifier = check_utils.check_not_none(ident, ast.Identifier) + + dot_name = dots or "" + dot_name += ".".join([name for name in ident.names]) + + return dot_name, ident.name_nodes + + def walk_leading_dots(self, node: lark_pb.Tree) -> str: + """Syntax + leading_dots: DOT+ + """ + return "".join([child.token_value for child in node.children]) + + # ----------------------------------------------------- + # if_stmt + # ----------------------------------------------------- + + def walk_if_stmt(self, node: lark_pb.Tree) -> ast.IfStmt: + """Syntax + if_stmt: IF test COLON execution_block (ELIF test COLON execution_block)* (ELSE COLON execution_block)? + execution_block: if_simple_stmt | NEWLINE _INDENT schema_init_stmt+ _DEDENT + schema_init_stmt: if_simple_stmt | if_stmt + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.IfStmt, node, self.filename), ast.IfStmt + ) + test_list = check_utils.check_all_not_none( + list, self.walk_all_by_type(node, ast.LarkToken.L_test), ast.Expr + ) + execution_block_list = check_utils.check_all_not_none( + list, self.walk_all_by_type(node, ast.LarkToken.L_execution_block), list + ) + + for execution_block in execution_block_list: + check_utils.check_all_not_none(list, execution_block, ast.Stmt) + + has_else = check_utils.check_allow_none( + self.walk_one_by_type(node, ast.LarkToken.L_ELSE), str + ) + + p.cond = test_list.pop(0) + if not p.body: + p.body = execution_block_list.pop(0) + p.set_end_line_column(p.body[-1]) + if has_else and p.body: + p.else_body = execution_block_list.pop(-1) + p.set_end_line_column(p.else_body[-1]) + if p.body and len(execution_block_list) > 0: + p.elif_body = execution_block_list + assert len(p.elif_body[-1]) > 0 + p.set_end_line_column(p.elif_body[-1][-1]) + if len(test_list) > 0: + p.elif_cond = test_list + + if p.else_body: + p.set_end_line_column(p.else_body[-1]) + + return p + + def walk_execution_block(self, node: lark_pb.Tree) -> typing.List[ast.Stmt]: + """Syntax + execution_block: if_simple_stmt | NEWLINE _INDENT schema_init_stmt+ _DEDENT + schema_init_stmt: if_simple_stmt | if_stmt + """ + if len(node.children) == 1: + return [check_utils.check_allow_none(self.walk(node.children[0]), ast.Stmt)] + else: + stmt_list = self.walk_all_by_type(node, ast.LarkToken.L_schema_init_stmt) + return check_utils.check_all_not_none( + list, + stmt_list, + ast.IfStmt, + ast.ExprStmt, + ast.AssertStmt, + ast.AssignStmt, + ast.AugAssignStmt, + ) + + # ----------------------------------------------------- + # schema_stmt + # ----------------------------------------------------- + + def walk_schema_stmt(self, node: lark_pb.Tree) -> ast.SchemaStmt: + """Syntax + schema_stmt: [decorators] (SCHEMA|MIXIN|PROTOCOL) [RELAXED] NAME + [LEFT_BRACKETS [schema_arguments] RIGHT_BRACKETS] + [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] + [for_host] COLON NEWLINE [schema_body] + schema_arguments: schema_argument (COMMA schema_argument)* + schema_body: _INDENT (string NEWLINE)* [mixin_stmt] + (schema_attribute_stmt|schema_init_stmt|schema_index_signature)* + [check_block] _DEDENT + schema_attribute_stmt: attribute_stmt NEWLINE + attribute_stmt: [decorators] (FINAL)? NAME COLON type [(ASSIGN|COMP_OR) test] + schema_init_stmt: if_simple_stmt | if_stmt + """ + + def __build_schema_stmt_parent_name() -> typing.Optional[ast.Identifier]: + """Syntax + schema_stmt:[LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] + """ + idents = self.walk_all_by_type(node, ast.LarkToken.L_identifier) + if len(idents) > 1: + report_complier_err( + arg=kcl_error.MULTI_INHERIT_MSG.format(f"{p.name}"), + err_type=kcl_error.ErrType.MultiInheritError_TYPE, + filename=self.filename, + line_no=idents[0].line, + col_no=idents[0].column, + end_col_no=idents[-1].end_column, + ) + return ( + idents[0] if idents and isinstance(idents[0], ast.Identifier) else None + ) + + def __build_schema_stmt_doc(sch_body: lark_pb.Tree) -> str: + """Syntax + schema_body: (string NEWLINE)* + ?string: STRING | LONG_STRING + """ + for child in sch_body.children: + if child.type == ast.LarkToken.L_string: + if ( + len(child.children) > 0 + and child.children[0].type == ast.LarkToken.L_LONG_STRING + ): + return literal_eval(child.children[0].token_value) + return "" + + return "" + + def __build_schema_stmt_index_signature( + sch_body: lark_pb.Tree, + ) -> typing.Optional[ast.SchemaIndexSignature]: + """Syntax + schema_body: (schema_index_signature)* + schema_index_signature: LEFT_BRACKETS [NAME COLON] [ELLIPSIS] basic_type RIGHT_BRACKETS + COLON type [ASSIGN test] NEWLINE + """ + index_sig = check_utils.check_all_allow_none( + list, + self.walk_all_by_type(sch_body, ast.LarkToken.L_schema_index_signature), + ast.SchemaIndexSignature, + ) + if len(index_sig) > 1: + report_complier_err( + err_type=kcl_error.ErrType.IndexSignatureError_TYPE, + filename=self.filename, + line_no=index_sig[1].line, + col_no=index_sig[1].column, + end_col_no=index_sig[1].end_column, + arg="only one index signature is allowed in the schema", + ) + return index_sig[0] if index_sig else None + + p: ast.SchemaStmt = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.SchemaStmt, node, self.filename), ast.SchemaStmt + ) + + types = [ + ast.LarkToken.L_decorators, + ast.LarkToken.L_MIXIN, + ast.LarkToken.L_PROTOCOL, + ast.LarkToken.L_NAME, + ast.LarkToken.L_COLON, + ast.LarkToken.L_schema_body, + ] + ( + decorators, + is_mixin, + is_protocol, + ident, + colon, + body, + ) = self.walk_elements_ordered_by_types(node, types) + colon = check_utils.check_not_none(colon, Token) + ident = check_utils.check_not_none(ident, ast.Identifier) + p.decorators = ( + check_utils.check_all_allow_none(list, decorators, ast.Decorator) or [] + ) + p.body = check_utils.check_all_allow_none(list, body or [], ast.Stmt) + p.name = "".join(ident.names) + p.is_mixin = bool(is_mixin) or p.name.endswith("Mixin") + p.is_protocol = bool(is_protocol) or p.name.endswith("Protocol") + assert len(ident.name_nodes) == 1, "schema name must be a single NAME" + p.name_node = ident.name_nodes[0] + + mixins = None + for n in node.children: + if n.type == ast.LarkToken.L_schema_body: + p.doc = check_utils.check_allow_none(__build_schema_stmt_doc(n), str) + p.index_signature = check_utils.check_allow_none( + __build_schema_stmt_index_signature(n), ast.SchemaIndexSignature + ) + types = [ast.LarkToken.L_mixin_stmt, ast.LarkToken.L_check_block] + mixins, checks = self.walk_elements_ordered_by_types(n, types) + p.mixins = ( + check_utils.check_all_allow_none( + list, mixins.values, ast.Identifier + ) + if mixins + else [] + ) + p.checks = ( + check_utils.check_all_allow_none(list, checks, ast.CheckExpr) or [] + ) + elif n.type == ast.LarkToken.L_schema_arguments: + p.args = check_utils.check_allow_none(self.walk(n), ast.Arguments) + elif n.type == ast.LarkToken.L_for_host: + p.for_host_name = self.walk(n) + + p.parent_name = check_utils.check_allow_none( + __build_schema_stmt_parent_name(), ast.Identifier + ) + + if p.checks: + p.set_end_line_column(p.checks[-1]) + elif p.body: + p.set_end_line_column(p.body[-1]) + if ( + p.index_signature + and p.index_signature.get_end_line() > p.body[-1].get_end_line() + ): + p.set_end_line_column(p.index_signature) + elif p.index_signature: + p.set_end_line_column(p.index_signature) + elif p.mixins and mixins and isinstance(mixins, Mixins): + p.set_end_line_column(mixins) + else: + p.set_end_line(colon.end_line) + p.set_end_column(colon.end_column) + + return p + + def walk_for_host(self, node: lark_pb.Tree) -> typing.Optional[ast.Identifier]: + """Syntax + for_host: FOR identifier + """ + + for n in node.children or []: + if n.type == ast.LarkToken.L_identifier: + return typing.cast(ast.Identifier, self.walk(n)) + return None + + def walk_schema_arguments( + self, node: lark_pb.Tree + ) -> typing.Optional[ast.Arguments]: + """Syntax + schema_stmt: [decorators] SCHEMA [RELAXED] NAME [LEFT_BRACKETS [schema_arguments] RIGHT_BRACKETS] + [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] COLON NEWLINE [schema_body] + schema_arguments: schema_argument (COMMA schema_argument)* + schema_argument: NAME [COLON type] [ASSIGN test] + """ + + p: ast.Arguments = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.Arguments, node, self.filename), ast.Arguments + ) + + arguments: typing.List[ + (ast.Identifier, typing.Optional[ast.Type], typing.Optional[ast.Expr]) + ] = self.walk_non_token_children(node) + assert all(len(argument) == 3 for argument in arguments) + + p.args = [argument[0] for argument in arguments] + for arg in p.args: + arg.ctx = ast.ExprContext.STORE + p.type_annotation_node_list = [argument[1] for argument in arguments] + p.type_annotation_list = [ + t.plain_type_str if t else None for t in p.type_annotation_node_list + ] + p.defaults = [argument[2] for argument in arguments] + + return p + + def walk_schema_argument( + self, node: lark_pb.Tree + ) -> (ast.Identifier, typing.Optional[ast.Type], typing.Optional[ast.Expr]): + """Syntax + schema_argument: NAME [COLON type] [ASSIGN test] + """ + types = [ast.LarkToken.L_NAME, ast.LarkToken.L_type, ast.LarkToken.L_test] + arg_name, arg_type_node, arg_value = self.walk_elements_ordered_by_types( + node, types + ) + arg_type_node: ast.Type = check_utils.check_allow_none(arg_type_node, ast.Type) + arg_name = check_utils.check_not_none(arg_name, ast.Identifier) + return arg_name, arg_type_node, arg_value + + def walk_schema_body( + self, node: lark_pb.Tree + ) -> typing.List[typing.Union[ast.SchemaAttr, ast.Stmt]]: + """ + schema_body: _INDENT (string NEWLINE)* [mixin_stmt] + (schema_attribute_stmt|schema_init_stmt|schema_index_signature)* + [check_block] _DEDENT + schema_init_stmt: if_simple_stmt | if_stmt + """ + filters = [ + ast.LarkToken.L_string, + ast.LarkToken.L_mixin_stmt, + ast.LarkToken.L_schema_index_signature, + ast.LarkToken.L_check_block, + ] + return check_utils.check_all_allow_none( + list, + self.walk_filtered_elements_ordered_by_types(node, filters), + ast.SchemaAttr, + ast.Stmt, + ) + + def walk_attribute_stmt(self, node: lark_pb.Tree) -> ast.SchemaAttr(): + """Syntax + schema_attribute_stmt: attribute_stmt NEWLINE + attribute_stmt: [decorators] (FINAL)? NAME [QUESTION] COLON type [(ASSIGN|COMP_OR) test] + """ + p: ast.SchemaAttr = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.SchemaAttr, node, self.filename), ast.SchemaAttr + ) + + types = [ + ast.LarkToken.L_decorators, + ast.LarkToken.L_identifier, + ast.LarkToken.L_QUESTION, + ast.LarkToken.L_type, + ast.LarkToken.L_test, + ] + ( + decorators, + name, + is_optional, + type_annotation, + p.value, + ) = self.walk_elements_ordered_by_types(node, types) + name: ast.Identifier = check_utils.check_not_none(name, ast.Identifier) + type_annotation = check_utils.check_not_none(type_annotation, ast.Type) + + p.name = ".".join(name.names) + p.type_str = type_annotation.plain_type_str + p.decorators = decorators or [] + p.is_optional = bool(is_optional) + p.name_node = name.name_nodes[0] + p.type_node = type_annotation + + for n in node.children: + if n.type in [ast.LarkToken.L_ASSIGN, ast.LarkToken.L_COMP_OR]: + p.op = ast.AugOp(n.type) + continue + + return p + + def walk_schema_index_signature( + self, node: lark_pb.Tree + ) -> ast.SchemaIndexSignature: + """Syntax + schema_index_signature: LEFT_BRACKETS [NAME COLON] [ELLIPSIS] basic_type RIGHT_BRACKETS + COLON type [ASSIGN test] NEWLINE + """ + p: ast.SchemaIndexSignature = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.SchemaIndexSignature, node, self.filename), + ast.SchemaIndexSignature, + ) + + types = [ + ast.LarkToken.L_NAME, + ast.LarkToken.L_ELLIPSIS, + ast.LarkToken.L_basic_type, + ast.LarkToken.L_type, + ast.LarkToken.L_test, + ] + ( + ident, + any_other, + key_type_node, + p.value_type_node, + p.value, + ) = self.walk_elements_ordered_by_types(node, types) + + p.key_type = check_utils.check_not_none(key_type_node.type_name, str) + p.value_type = check_utils.check_not_none(p.value_type_node.plain_type_str, str) + p.value_type_node = check_utils.check_not_none(p.value_type_node, ast.Type) + ident = check_utils.check_allow_none(ident, ast.Identifier) + + p.any_other = bool(any_other) + p.key_name = "".join(ident.names) if ident else None + if ident: + assert ( + len(ident.name_nodes) == 1 + ), "name of the schema index signature must be a single NAME" + p.name_node = ident.name_nodes[0] + + p.set_end_line(p.value_type_node.end_line) + p.set_end_column(p.value_type_node.end_column) + + if p.value: + p.set_end_line_column(p.value) + + return p + + # ----------------------------------------------------- + # decorators + # ----------------------------------------------------- + + def walk_decorators(self, node: lark_pb.Tree) -> typing.List[ast.Decorator]: + """Syntax + decorators: (AT decorator_expr NEWLINE)+ + decorator_expr: identifier [call_suffix] + """ + return check_utils.check_all_not_none( + list, + self.walk_all_by_type(node, ast.LarkToken.L_decorator_expr), + ast.Decorator, + ) + + def walk_decorator_expr(self, node: lark_pb.Tree) -> ast.Decorator: + """Syntax + decorator_expr: identifier [call_suffix] + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.Decorator, node, self.filename), ast.Decorator + ) + + types = [ast.LarkToken.L_identifier, ast.LarkToken.L_call_suffix] + p.name, p.args = self.walk_elements_ordered_by_types(node, types) + p.name = check_utils.check_not_none(p.name, ast.Identifier) + p.args = check_utils.check_allow_none(p.args, ast.CallExpr) + return p + + # ----------------------------------------------------- + # rule_stmt + # ----------------------------------------------------- + + def walk_rule_stmt(self, node: lark_pb.Tree) -> ast.RuleStmt: + """Syntax + rule_stmt: [decorators] RULE NAME [LEFT_BRACKETS [schema_arguments] RIGHT_BRACKETS] [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] [for_host] COLON NEWLINE [rule_body] + rule_body: _INDENT (string NEWLINE)* check_expr+ _DEDENT + """ + + def __build_rule_stmt_parent_names() -> typing.List[ast.Identifier]: + """Syntax + rule_stmt: [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] + """ + idents = self.walk_all_by_type(node, ast.LarkToken.L_identifier) + return idents or [] + + def __build_rule_stmt_doc(rule_body: lark_pb.Tree) -> str: + """Syntax + rule_body: (string NEWLINE)* + ?string: STRING | LONG_STRING + """ + for child in rule_body.children: + if child.type == ast.LarkToken.L_string: + if ( + len(child.children) > 0 + and child.children[0].type == ast.LarkToken.L_LONG_STRING + ): + return literal_eval(child.children[0].token_value) + return "" + + return "" + + p: ast.RuleStmt = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.RuleStmt, node, self.filename), ast.RuleStmt + ) + + types = [ + ast.LarkToken.L_decorators, + ast.LarkToken.L_NAME, + ast.LarkToken.L_COLON, + ] + ( + decorators, + ident, + colon, + ) = self.walk_elements_ordered_by_types(node, types) + colon = check_utils.check_not_none(colon, Token) + ident = check_utils.check_not_none(ident, ast.Identifier) + p.decorators = ( + check_utils.check_all_allow_none(list, decorators, ast.Decorator) or [] + ) + p.name = "".join(ident.names) + assert len(ident.name_nodes) == 1, "rule name must be a single NAME" + p.name_node = ident.name_nodes[0] + + checks = None + for n in node.children: + if n.type == ast.LarkToken.L_rule_body: + p.doc = check_utils.check_allow_none(__build_rule_stmt_doc(n), str) + checks = self.walk_all_by_type(n, ast.LarkToken.L_check_expr) + p.checks = ( + check_utils.check_all_allow_none(list, checks, ast.CheckExpr) or [] + ) + elif n.type == ast.LarkToken.L_schema_arguments: + p.args = check_utils.check_allow_none(self.walk(n), ast.Arguments) + elif n.type == ast.LarkToken.L_for_host: + p.for_host_name = self.walk(n) + + p.parent_rules = check_utils.check_allow_none( + __build_rule_stmt_parent_names(), list + ) + + if p.checks: + p.set_end_line_column(p.checks[-1]) + else: + p.set_end_line(colon.end_line) + p.set_end_column(colon.end_column) + + return p + + # ----------------------------------------------------- + # type + # ----------------------------------------------------- + + def walk_type(self, node: lark_pb.Tree) -> ast.Type: + """Syntax + type: type_element (OR type_element)* + type_element: schema_type | basic_type | compound_type | literal_type + schema_type: identifier + basic_type: basic_type: STRING_TYPE | INT_TYPE | FLOAT_TYPE | BOOL_TYPE | ANY_TYPE + compound_type: list_type | dict_type + literal_type: string | number | TRUE | FALSE | NONE + list_type: LEFT_BRACKETS (type)? RIGHT_BRACKETS + dict_type: LEFT_BRACE (type)? COLON (type)? RIGHT_BRACE + """ + p: ast.Type = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.Type, node, filename=self.filename), ast.Type + ) + type_elements = check_utils.check_all_not_none( + list, + self.walk_non_token_children(node), + ast.Identifier, + ast.BasicType, + ast.ListType, + ast.DictType, + ast.LiteralType, + ) + p.type_elements = type_elements + + plain_types: typing.List[str] = [] + for type_element in type_elements: + if isinstance(type_element, ast.Identifier): + plain_types.append(".".join(type_element.names)) + continue + if isinstance(type_element, ast.BasicType): + plain_types.append(type_element.type_name) + continue + if isinstance(type_element, (ast.ListType, ast.DictType)): + plain_types.append(type_element.plain_type_str) + continue + if isinstance(type_element, ast.LiteralType): + plain_types.append(type_element.plain_value) + continue + + report_complier_err( + "Invalid type annotation. Valid type annotations: SchemaType, BasicType, ListType, LiteralType", + filename=self.filename, + line_no=type_element.line, + col_no=type_element.column, + end_col_no=type_element.end_column, + ) + p.plain_type_str = "|".join(plain_types) + return p + + def walk_basic_type(self, node: lark_pb.Tree) -> ast.BasicType: + assert len(node.children) == 1 + p: ast.BasicType = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.BasicType, node, self.filename), ast.BasicType + ) + p.type_name = node.children[0].token_value + return p + + def walk_literal_type(self, node: lark_pb.Tree) -> ast.LiteralType: + assert len(node.children) == 1 + p: ast.LiteralType = ast.LiteralType().set_ast_position( + node, filename=self.filename + ) + value_type = node.children[0].type + if value_type in ["string", "number"]: + lit = self.walk(node.children[0]) + if isinstance(lit, ast.StringLit): + p.value_type = lit.type + p.string_value = lit + p.plain_value = lit.raw_value + return p + if isinstance(lit, ast.NumberLit): + p.value_type = lit.type + p.number_value = lit + p.plain_value = ( + f"{str(lit.value)}{lit.binary_suffix}" + if lit.binary_suffix + else str(lit.value) + ) + return p + report_complier_err( + f"Invalid Literal Type {lit}. Valid Literal Types: StringLiteral, NumberLiteral", + filename=self.filename, + line_no=node.children[0].line, + col_no=node.children[0].column, + end_col_no=node.children[0].end_column, + ) + else: + assert lark_parser.IsTokenType(value_type) + value = node.children[0].token_value + assert value in ["True", "False", "None"] + p.plain_value = value + p.value_type = "bool" if value in ["True", "False"] else "None" + return p + + def walk_list_type(self, node: lark_pb.Tree) -> ast.ListType: + """ + list_type: LEFT_BRACKETS (type)? RIGHT_BRACKETS + """ + p: ast.ListType = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.ListType, node, filename=self.filename), + ast.ListType, + ) + inner_type: ast.Type = check_utils.check_allow_none( + self.walk_one_by_type(node, ast.LarkToken.L_type), ast.Type + ) + p.inner_type = inner_type + p.plain_type_str = f"[{inner_type.plain_type_str}]" if inner_type else "[]" + return p + + def walk_dict_type(self, node: lark_pb.Tree) -> ast.DictType: + """ + dict_type: LEFT_BRACE (type)? COLON (type)? RIGHT_BRACE + """ + p: ast.DictType = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.DictType, node, filename=self.filename), + ast.DictType, + ) + types = [ast.LarkToken.L_type, ast.LarkToken.L_COLON, ast.LarkToken.L_type] + key, colon, val = self.walk_elements_ordered_by_types(node, types) + + key: ast.Type = check_utils.check_allow_none(key, ast.Type) + val: ast.Type = check_utils.check_allow_none(val, ast.Type) + assert colon and colon == ":" + + p.key_type = key + p.value_type = val + + plain_key = key.plain_type_str if key else "" + plain_value = val.plain_type_str if val else "" + p.plain_type_str = "{" + f"{plain_key}:{plain_value}" + "}" + return p + + # ----------------------------------------------------- + # check_stmt + # ----------------------------------------------------- + + def walk_check_block(self, node: lark_pb.Tree) -> typing.List[ast.CheckExpr]: + """Syntax + check_block: CHECK COLON NEWLINE _INDENT check_expr+ _DEDENT + check_expr: simple_expr [IF simple_expr] [COMMA primary_expr] NEWLINE + """ + return check_utils.check_all_allow_none( + list, self.walk_non_token_children(node), ast.CheckExpr + ) + + def walk_check_expr(self, node: lark_pb.Tree) -> ast.CheckExpr: + """Syntax + check_expr: simple_expr [IF simple_expr] [COMMA primary_expr] NEWLINE + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.CheckExpr, node, self.filename), ast.CheckExpr + ) + + types = [ + ast.LarkToken.L_simple_expr, + ast.LarkToken.L_simple_expr, + ast.LarkToken.L_primary_expr, + ] + p.test, p.if_cond, p.msg = self.walk_elements_ordered_by_types(node, types) + + p.msg = check_utils.check_allow_none(p.msg, ast.Expr) + p.test = check_utils.check_not_none(p.test, ast.Expr) + + if p.msg: + p.set_end_line_column(p.msg) + elif p.if_cond: + p.set_end_line_column(p.if_cond) + else: + p.set_end_line_column(p.test) + + return p + + # ----------------------------------------------------- + # mixin_stmt + # ----------------------------------------------------- + + def walk_mixin_stmt(self, node: lark_pb.Tree) -> Mixins: + """Syntax + mixin_stmt: MIXIN LEFT_BRACKETS [mixins | multiline_mixins] RIGHT_BRACKETS NEWLINE + multiline_mixins: NEWLINE _INDENT mixins NEWLINE _DEDENT + mixins: identifier (COMMA (NEWLINE mixins | identifier))* + """ + mixins = Mixins() + left_bracket = check_utils.check_not_none( + self.walk_one_by_type(node, ast.LarkToken.L_LEFT_BRACKETS), Token + ) + right_bracket = check_utils.check_not_none( + self.walk_one_by_type(node, ast.LarkToken.L_RIGHT_BRACKETS), Token + ) + + mixins.set_line(left_bracket.line) + mixins.set_column(left_bracket.column) + mixins.set_end_line(right_bracket.end_line) + mixins.set_end_column(right_bracket.end_column) + + def walk_fn(n: lark_pb.Tree): + if n.type == ast.LarkToken.L_identifier: + mixins.append( + check_utils.check_allow_none(self.walk(n), ast.Identifier) + ) + + lark_parser.WalkTree(node, walk_fn) + return mixins + + # ----------------------------------------------------- + # expression_stmt + # ----------------------------------------------------- + + def walk_testlist_expr(self, node: lark_pb.Tree) -> ast.ExprStmt: + """ + expr_stmt: testlist_expr + testlist_expr: test (COMMA test)* + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.ExprStmt, node, self.filename), ast.ExprStmt + ) + p.exprs = check_utils.check_all_not_none( + list, self.walk_non_token_children(node), ast.Expr + ) + return p + + def walk_if_expr(self, node: lark_pb.Tree) -> ast.IfExpr: + """ + if_expr: simple_expr IF simple_expr ELSE test + """ + assert len(node.children) == 5 + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.IfExpr, node, self.filename), ast.IfExpr + ) + + types = [ + ast.LarkToken.L_simple_expr, + ast.LarkToken.L_simple_expr, + ast.LarkToken.L_test, + ] + p.body, p.cond, p.orelse = self.walk_elements_ordered_by_types(node, types) + + p.body = check_utils.check_not_none(p.body, ast.Expr) + p.cond = check_utils.check_not_none(p.cond, ast.Expr) + p.orelse = check_utils.check_not_none(p.orelse, ast.Expr) + return p + + def walk_simple_expr(self, node: lark_pb.Tree) -> ast.Expr: + """This function is equivalent to a filter, only the child nodes of the + current node need to adjust the priority, and all expressions passing + through this node will adjust the priority. + """ + assert len(node.children) == 1 + simple_expr = check_utils.check_not_none(self.walk(node.children[0]), ast.Expr) + return self.add_precedence(simple_expr) + + def add_precedence( + self, expr: ast.Expr + ) -> typing.Union[ast.UnaryExpr, ast.BinaryExpr, ast.Compare, ast.Expr]: + if not isinstance(expr, (ast.BinaryExpr, ast.UnaryExpr, ast.Compare)): + return expr + p = ( + self.add_compare_precedence(expr) + if isinstance(expr, ast.Compare) + else self.add_cal_expr_precedence(expr) + ) + p = check_utils.check_not_none(p, ast.UnaryExpr, ast.BinaryExpr, ast.Compare) + return p + + def add_cal_expr_precedence( + self, expr: typing.Union[ast.BinaryExpr, ast.UnaryExpr] + ) -> typing.Union[ast.UnaryExpr, ast.BinaryExpr, ast.Compare, ast.Expr]: + + if isinstance(expr, ast.UnaryExpr): + right_expr = expr.operand + elif isinstance(expr, ast.BinaryExpr): + right_expr = expr.right + else: + return expr + + start_local = right_expr + prece_op = self.get_precedence(expr.op) + prece_operand = self.get_precedence(right_expr) + + col_no_stack = ( + [] + ) # The stack is used to pass up the line and column number changes + if prece_op >= prece_operand and isinstance( + right_expr, (ast.BinaryExpr, ast.Compare) + ): + tmp = right_expr + while prece_op >= self.get_precedence(tmp.left) and isinstance( + tmp.left, (ast.BinaryExpr, ast.Compare) + ): + col_no_stack.append(tmp) + tmp = tmp.left + if isinstance(expr, ast.UnaryExpr): + expr.operand = tmp.left + elif isinstance(expr, ast.BinaryExpr): + expr.right = tmp.left + expr.set_end_column(tmp.left.get_end_column()) + tmp.left = expr + tmp.set_column(expr.get_column()) + # Calculate the change of the line and column number and pass it up + while col_no_stack: + tmp_col = col_no_stack.pop() + tmp_col.set_column(expr.get_column()) + expr = start_local + + expr = check_utils.check_not_none( + expr, ast.UnaryExpr, ast.BinaryExpr, ast.Compare + ) + return expr + + def add_compare_precedence( + self, expr: ast.Compare + ) -> typing.Union[ast.BinaryExpr, ast.Compare, ast.Expr]: + prece_op = self.get_precedence(expr) + comparators = expr.comparators + for i in range(len(comparators)): + prece_child = self.get_precedence(comparators[i]) + col_no_stack = [] + if isinstance(comparators[i], ast.BinaryExpr) and prece_op > prece_child: + tmp = comparators.pop(i) + start_local = tmp + while prece_op >= self.get_precedence(tmp.left) and isinstance( + tmp.left, ast.BinaryExpr + ): + col_no_stack.append(tmp) + tmp = tmp.left + comparators.insert(i, tmp.left) + expr.set_end_column(comparators[-1].get_end_column()) + tmp.left = expr + tmp.set_column(expr.get_column()) + # Calculate the change of the line and column number and pass it up + while col_no_stack: + tmp_col = col_no_stack.pop() + tmp_col.set_column(expr.get_column()) + expr = start_local + + expr = check_utils.check_not_none(expr, ast.BinaryExpr, ast.Compare) + return expr + + def walk_unary_expr(self, node: lark_pb.Tree) -> ast.UnaryExpr: + """Syntax + simple_expr : primary_expr | unary_expr | binary_expr + unary_expr: un_op simple_expr + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.UnaryExpr, node, self.filename), ast.UnaryExpr + ) + + types = [ast.LarkToken.L_un_op, ast.LarkToken.L_simple_expr] + p.op, p.operand = self.walk_elements_ordered_by_types(node, types) + + p.op = check_utils.check_not_none(p.op, ast.UnaryOp) + p.operand = check_utils.check_not_none(p.operand, ast.Expr) + + self.register_precedence(p.op, p) + return p + + def walk_binary_expr( + self, node: lark_pb.Tree + ) -> typing.Union[ast.BinaryExpr, ast.Compare]: + """Syntax + binary_expr: simple_expr bin_op simple_expr + simple_expr : primary_expr | unary_expr | binary_expr + """ + + def __build_compare() -> typing.Union[ast.BinaryExpr, ast.Compare]: + assert len(node.children) == 3 + cmp_expr = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.Compare, node, self.filename), ast.Compare + ) + cmp_expr.left = check_utils.check_not_none( + self.walk(node.children[0]), ast.Expr + ) + + for i in range(1, len(node.children)): + n = node.children[i] + if ( + n.type == ast.LarkToken.L_bin_op + and len(n.children) > 0 + and ast.judge_compare_op(n.children[0].type) + ): + cmp_op = check_utils.check_not_none(self.walk(n), ast.CmpOp) + cmp_expr.ops.append(cmp_op) + continue + elif n.type == ast.LarkToken.L_simple_expr: + comparator = check_utils.check_not_none(self.walk(n), ast.Expr) + if isinstance(comparator, ast.Compare): + cmp_expr.ops.extend(comparator.ops) + cmp_expr.comparators.append(comparator.left) + cmp_expr.comparators.extend(comparator.comparators) + elif isinstance(comparator, ast.Expr): + cmp_expr.comparators.append(comparator) + else: + report_complier_err( + f"Unsupported comparator type: {type(comparator)}", + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + continue + else: + report_complier_err( + f"Unsupported compare operation type: {n.type}", + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + + if len(cmp_expr.ops) > 0: + self.register_precedence(cmp_expr.ops[0], cmp_expr) + return cmp_expr + + assert len(node.children) > 2 + assert len(node.children[1].children) > 0 + if ast.judge_compare_op(node.children[1].children[0].type): + return __build_compare() + + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.BinaryExpr, node, self.filename), ast.BinaryExpr + ) + + types = [ + ast.LarkToken.L_simple_expr, + ast.LarkToken.L_bin_op, + ast.LarkToken.L_simple_expr, + ] + p.left, p.op, p.right = self.walk_elements_ordered_by_types(node, types) + + p.left = check_utils.check_not_none(p.left, ast.Expr) + p.op = check_utils.check_not_none(p.op, ast.BinOp) + p.right = check_utils.check_not_none(p.right, ast.Expr) + + self.register_precedence(p.op, p) + return p + + def walk_un_op(self, node: lark_pb.Tree) -> ast.UnaryOp: + """Syntax + un_op: L_NOT | PLUS | MINUS | NOT + """ + assert len(node.children) > 0 + p = check_utils.check_not_none( + ast.ASTFactory.get_op(ast.UnaryOp, node.children[0].type), ast.UnaryOp + ) + self.register_precedence(p, p) + return p + + def walk_bin_op(self, node: lark_pb.Tree) -> typing.Union[ast.BinOp, ast.CmpOp]: + """Syntax + bin_op: L_OR | L_AND + | OR | XOR | AND + | SHIFT_LEFT | SHIFT_RIGHT + | PLUS | MINUS | MULTIPLY | DIVIDE | MOD | DOUBLE_DIVIDE + | DOUBLE_STAR | EQUAL_TO | NOT_EQUAL_TO + | LESS_THAN | GREATER_THAN | LESS_THAN_OR_EQUAL_TO | GREATER_THAN_OR_EQUAL_TO + | IN | L_NOT IN | IS | IS L_NOT | L_NOT | AS + """ + + def __build_cmp_op() -> ast.CmpOp: + real_type: str = "" + for index in range(len(node.children)): + if index != len(node.children) - 1: + real_type += node.children[index].type + " " + else: + real_type += node.children[index].type + + cmp_op = check_utils.check_not_none( + ast.ASTFactory.get_op(ast.CmpOp, real_type), ast.CmpOp + ) + return cmp_op + + assert len(node.children) > 0 + if ast.judge_compare_op(node.children[0].type): + p = __build_cmp_op() + else: + p = check_utils.check_not_none( + ast.ASTFactory.get_op(ast.BinOp, node.children[0].type), ast.BinOp + ) + self.register_precedence(p, p) + return p + + def walk_primary_expr( + self, node: lark_pb.Tree + ) -> typing.Union[ast.Literal, ast.Expr]: + """Syntax + primary_expr: identifier call_suffix | operand | primary_expr select_suffix | primary_expr call_suffix + | primary_expr slice_suffix + select_suffix:[question] DOT NAME + call_suffix: LEFT_PARENTHESES [arguments [COMMA]] RIGHT_PARENTHESES + slice_suffix: LEFT_BRACKETS (test | [test] COLON [test] [COLON [test]]) RIGHT_BRACKETS + arguments: argument (COMMA argument)* + argument: test | NAME ASSIGN test | MULTIPLY test | DOUBLE_STAR test + number: DEC_NUMBER | HEX_NUMBER | BIN_NUMBER | OCT_NUMBER | FLOAT_NUMBER | IMAG_NUMBER + string: STRING | LONG_STRING + """ + assert len(node.children) > 0 + if len(node.children) == 1: + expr = check_utils.check_not_none( + self.walk(node.children[0]), ast.Literal, ast.Expr + ) + return expr + elif len(node.children) == 2: + n0 = node.children[0] + if n0.type == ast.LarkToken.L_identifier: + func_expr: ast.Identifier = check_utils.check_not_none( + self.walk(n0), ast.Identifier + ) + call_suffix: ast.CallExpr = check_utils.check_not_none( + self.walk(node.children[1]), ast.CallExpr + ) + call_suffix.func = func_expr + call_suffix.set_ast_position(node, self.filename) + return call_suffix + elif n0.type == ast.LarkToken.L_primary_expr: + func_expr = check_utils.check_not_none( + self.walk(n0), ast.Literal, ast.Expr + ) + p = check_utils.check_not_none( + self.walk(node.children[1]), + ast.SelectorExpr, + ast.Subscript, + ast.CallExpr, + ) + + if node.children[1].type in [ + ast.LarkToken.L_select_suffix, + ast.LarkToken.L_slice_suffix, + ]: + p.value = func_expr + elif node.children[1].type == ast.LarkToken.L_call_suffix: + p.func = func_expr + p.set_ast_position(node, self.filename) + return p + + report_complier_err( + "Unsupported expression", + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + + def walk_operand( + self, node: lark_pb.Tree + ) -> typing.Union[str, ast.Expr, ast.ParenExpr, ast.Identifier]: + """Syntax + operand: identifier | number | string | TRUE | FALSE | NONE | list_expr | list_comp | dict_expr | dict_comp + | schema_expr | LEFT_PARENTHESES expression RIGHT_PARENTHESES + """ + if len(node.children) == 1: + return self.walk(node.children[0]) + elif len(node.children) == 3: + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.ParenExpr, node, self.filename), + ast.ParenExpr, + ) + p.expr = self.walk(node.children[1]) + return p + else: + report_complier_err( + "Unsupported expression", + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + + def walk_call_suffix(self, node: lark_pb.Tree) -> ast.CallExpr: + """Syntax + call_suffix: LEFT_PARENTHESES [arguments [COMMA]] RIGHT_PARENTHESES + arguments: argument (COMMA argument)* + argument: test | NAME ASSIGN test | MULTIPLY test | DOUBLE_STAR test + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.CallExpr, node, self.filename), ast.CallExpr + ) + + args = self.walk_one_by_type(node, ast.LarkToken.L_arguments) + if args and isinstance(args, ast.CallExpr): + args.set_end_line_column(p) + args.set_ast_position(node, self.filename) + return args + return p + + def walk_select_suffix(self, node: lark_pb.Tree) -> ast.SelectorExpr: + """Syntax + select_suffix: [QUESTION] DOT NAME + """ + p: ast.SelectorExpr = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.SelectorExpr, node, self.filename), + ast.SelectorExpr, + ) + p.attr = check_utils.check_not_none( + self.walk_one_by_type(node, ast.LarkToken.L_NAME), ast.Identifier + ) + p.has_question = len(node.children) == 3 + return p + + def walk_slice_suffix(self, node: lark_pb.Tree) -> ast.Subscript: + """Syntax + slice_suffix: [QUESTION] LEFT_BRACKETS (test | [test] COLON [test] [COLON [test]]) RIGHT_BRACKETS + test: if_expr | primary_expr | unary_expr | binary_expr + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.Subscript, node, self.filename), ast.Subscript + ) + + types = [ + ast.LarkToken.L_QUESTION, + ast.LarkToken.L_test, + ast.LarkToken.L_COLON, + ast.LarkToken.L_test, + ast.LarkToken.L_COLON, + ast.LarkToken.L_test, + ] + ( + has_question, + p.lower, + colon1, + p.upper, + colon2, + p.step, + ) = self.walk_elements_ordered_by_types(node, types) + + has_question = check_utils.check_allow_none(has_question, str) + p.lower = check_utils.check_allow_none(p.lower, ast.Expr) + colon1 = check_utils.check_allow_none(colon1, str) + p.upper = check_utils.check_allow_none(p.upper, ast.Expr) + colon2 = check_utils.check_allow_none(colon2, str) + p.step = check_utils.check_allow_none(p.step, ast.Expr) + + p.has_question = bool(has_question) + if not colon1 and not colon2: + p.index = p.lower + p.lower = None + + return p + + def walk_arguments(self, node: lark_pb.Tree) -> ast.CallExpr: + """Syntax + arguments: argument (COMMA argument)* + argument: test | NAME ASSIGN test | MULTIPLY test | DOUBLE_STAR test + """ + p: ast.CallExpr = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.CallExpr, node, self.filename), ast.CallExpr + ) + has_keyword = False + + for arg in node.children: + if arg.type == ast.LarkToken.L_argument: + assert len(arg.children) > 0 + n0 = arg.children[0] + if n0.type == ast.LarkToken.L_test: + if has_keyword: + report_syntax_err( + arg="positional argument follows keyword argument", + filename=self.filename, + line_no=n0.line, + col_no=n0.column, + end_col_no=n0.end_column, + ) + p.args.append( + check_utils.check_allow_none(self.walk(arg), ast.Expr) + ) + continue + elif n0.type == ast.LarkToken.L_NAME: + p.keywords.append( + check_utils.check_allow_none(self.walk(arg), ast.Keyword) + ) + has_keyword = True + continue + else: + report_complier_err( + "Unsupported argument", + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + return p + + def walk_argument(self, node: lark_pb.Tree) -> ast.Expr: + """Syntax + argument: test | NAME ASSIGN test | MULTIPLY test | DOUBLE_STAR test + """ + assert len(node.children) > 0 + name, test = self.walk_elements_ordered_by_types( + node, [ast.LarkToken.L_NAME, ast.LarkToken.L_test] + ) + name = check_utils.check_allow_none(name, ast.Identifier) + test = check_utils.check_not_none(test, ast.Expr) + if name: + keyword: ast.Keyword = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.Keyword, node.children[0], self.filename), + ast.Keyword, + ) + keyword.arg = name + keyword.value = test + assert ( + len(name.name_nodes) == 1 + ), "name of the keyword argument must be a single NAME" + return keyword + else: + return test + + # ----------------------------------------------------- + # operand + # ----------------------------------------------------- + + def walk_identifier( + self, node: lark_pb.Tree, *, _ctx: ast.ExprContext = ast.ExprContext.LOAD + ) -> ast.Identifier: + """Syntax + identifier: NAME (DOT NAME)* + """ + p: ast.Identifier = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.Identifier, node, self.filename), ast.Identifier + ) + p.names = [ + n.token_value for n in node.children if n.type == ast.LarkToken.L_NAME + ] + p.name_nodes = [ + self.lark_name_to_ast_name(n) + for n in node.children + if n.type == ast.LarkToken.L_NAME + ] + return p + + def walk_quant_expr(self, node: lark_pb.Tree) -> ast.QuantExpr: + """Syntax + quant_expr: quant_op [ identifier COMMA ] identifier IN quant_target LEFT_BRACE (simple_expr [IF simple_expr] + | NEWLINE _INDENT simple_expr [IF simple_expr] NEWLINE _DEDENT)? RIGHT_BRACE + quant_target: string | identifier | list_expr | list_comp | dict_expr | dict_comp + quant_op: ALL | ANY | FILTER | MAP + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.QuantExpr, node, self.filename), ast.QuantExpr + ) + types = [ + ast.LarkToken.L_quant_op, + ast.LarkToken.L_quant_target, + ast.LarkToken.L_simple_expr, + ast.LarkToken.L_simple_expr, + ] + op, p.target, p.test, p.if_cond = self.walk_elements_ordered_by_types( + node, types + ) + op = check_utils.check_not_none(op, str) + p.target = check_utils.check_not_none(p.target, ast.Expr) + p.test = check_utils.check_not_none(p.test, ast.Expr) + p.if_cond = check_utils.check_allow_none(p.if_cond, ast.Expr) + p.variables = check_utils.check_all_not_none( + list, + self.walk_all_by_type(node, ast.LarkToken.L_identifier), + ast.Identifier, + ) + p.op = ast.QuantOperation.MAPPING[op] + + return p + + def walk_list_expr(self, node: lark_pb.Tree) -> ast.ListExpr: + """Syntax + list_expr: LEFT_BRACKETS [list_items | NEWLINE _INDENT list_items _DEDENT] RIGHT_BRACKETS + list_items: list_item ((COMMA [NEWLINE] | NEWLINE) list_item)* [COMMA] [NEWLINE] + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.ListExpr, node, self.filename), ast.ListExpr + ) + p.elts = ( + check_utils.check_all_allow_none( + list, self.walk_one_by_type(node, ast.LarkToken.L_list_items), ast.Expr + ) + or [] + ) + + return p + + def walk_list_items(self, node: lark_pb.Tree) -> typing.List[ast.Expr]: + """Syntax + list_items: list_item ((COMMA [NEWLINE] | NEWLINE) list_item)* [COMMA] [NEWLINE] + list_item: test | star_expr | if_item + """ + return check_utils.check_all_allow_none( + list, self.walk_all_by_type(node, ast.LarkToken.L_list_item), ast.Expr + ) + + def walk_list_comp(self, node: lark_pb.Tree) -> ast.ListComp: + """Syntax + list_comp: LEFT_BRACKETS (list_item comp_clause+ + | NEWLINE _INDENT list_item comp_clause+ _DEDENT) RIGHT_BRACKETS + list_item: test | star_expr + comp_clause: FOR loop_variables [COMMA] IN test [NEWLINE] [IF test [NEWLINE]] + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.ListComp, node, self.filename), ast.ListComp + ) + p.elt = check_utils.check_not_none( + self.walk_one_by_type(node, ast.LarkToken.L_list_item), ast.Expr + ) + p.generators = check_utils.check_all_not_none( + list, + self.walk_all_by_type(node, ast.LarkToken.L_comp_clause), + ast.CompClause, + ) + return p + + def walk_dict_comp(self, node: lark_pb.Tree) -> ast.DictComp: + """Syntax + dict_comp: LEFT_BRACE (entry comp_clause+ | NEWLINE _INDENT entry comp_clause+ _DEDENT) RIGHT_BRACE + entry: test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr + comp_clause: FOR loop_variables [COMMA] IN test [NEWLINE] [IF test [NEWLINE]] + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.DictComp, node, self.filename), ast.DictComp + ) + + key_value = self.walk_one_by_type(node, ast.LarkToken.L_entry) + assert len(key_value) == 3 + assert key_value[0] is None or isinstance(key_value[0], ast.Expr) + p.key = key_value[0] + p.operation = key_value[1] + p.value = check_utils.check_not_none(key_value[2], ast.Expr) + p.generators = check_utils.check_all_not_none( + list, + self.walk_all_by_type(node, ast.LarkToken.L_comp_clause), + ast.CompClause, + ) + return p + + def walk_entries( + self, node: lark_pb.Tree + ) -> typing.List[typing.Tuple[typing.Optional[ast.Expr], ast.Expr]]: + """Syntax + entries: entry ((COMMA [NEWLINE] | NEWLINE) entry)* [COMMA] [NEWLINE] + entry: test COLON test | double_star_expr + """ + return self.walk_non_token_children(node) + + def walk_entry( + self, node: lark_pb.Tree + ) -> typing.Tuple[typing.Optional[ast.Expr], ast.Expr]: + """Syntax + entry: test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry + double_star_expr: DOUBLE_STAR primary_expr + """ + if len(node.children) == 3: + key = check_utils.check_not_none(self.walk(node.children[0]), ast.Expr) + val = check_utils.check_not_none(self.walk(node.children[2]), ast.Expr) + op = ast.ConfigEntryOperation.MAPPING[node.children[1].type] + return key, op, val + elif len(node.children) == 1: + return self.walk(node.children[0]) + else: + report_complier_err( + "Unsupported entry", + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + + def walk_comp_clause(self, node: lark_pb.Tree) -> ast.CompClause: + """Syntax + comp_clause: FOR loop_variables [COMMA] IN simple_expr [NEWLINE] [IF test [NEWLINE]] + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.CompClause, node, self.filename), ast.CompClause + ) + + types = [ + ast.LarkToken.L_loop_variables, + ast.LarkToken.L_simple_expr, + ast.LarkToken.L_test, + ] + p.targets, p.iter, test = self.walk_elements_ordered_by_types(node, types) + p.targets = check_utils.check_all_not_none(list, p.targets, ast.Identifier) + for i, target in enumerate(p.targets): + p.targets[i].ctx = ast.ExprContext.STORE + p.iter = check_utils.check_not_none(p.iter, ast.Expr) + test = check_utils.check_allow_none(test, ast.Expr) + + p.ifs = [test] if test else [] + return p + + def walk_if_entry( + self, node: lark_pb.Tree + ) -> typing.Tuple[None, ast.ConfigIfEntryExpr]: + """Syntax + if_entry: IF test COLON if_entry_exec_block (ELIF test COLON if_entry_exec_block)* (ELSE COLON if_entry_exec_block)? + if_entry_exec_block: (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry) [NEWLINE] | NEWLINE _INDENT (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry) ((COMMA [NEWLINE] | [NEWLINE]) (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry))* [COMMA] [NEWLINE] _DEDENT + """ + if_entry_node = node + tests = lark_parser.GetNodeList( + if_entry_node, ast.LarkToken.L_test, recursively=False + ) + exec_blocks = lark_parser.GetNodeList( + if_entry_node, ast.LarkToken.L_if_entry_exec_block, recursively=False + ) + assert tests and exec_blocks + if_exec_blocks = exec_blocks[0] + key_value_tests = lark_parser.GetNodeList( + if_exec_blocks, + ast.LarkToken.L_test, + ast.LarkToken.L_double_star_expr, + ast.LarkToken.L_if_entry, + recursively=False, + ) + operation_nodes = lark_parser.GetNodeList( + if_exec_blocks, + ast.LarkToken.L_COLON, + ast.LarkToken.L_ASSIGN, + ast.LarkToken.L_COMP_PLUS, + recursively=False, + ) + val = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.ConfigIfEntryExpr, node, self.filename), + ast.ConfigIfEntryExpr, + ) + val.if_cond = self.walk(tests[0]) + i = 0 + j = 0 + while i < len(key_value_tests): + if ( + key_value_tests[i].type == ast.LarkToken.L_double_star_expr + or key_value_tests[i].type == ast.LarkToken.L_if_entry + ): + key, value = self.walk(key_value_tests[i]) + val.keys.append(key) + val.values.append(value) + val.operations.append(ast.ConfigEntryOperation.OVERRIDE) + i += 1 + else: + val.keys.append(self.walk(key_value_tests[i])) + val.values.append(self.walk(key_value_tests[i + 1])) + val.operations.append( + ast.ConfigEntryOperation.MAPPING.get(operation_nodes[j].type) + ) + j += 1 + i += 2 + if len(exec_blocks) > 1: + if len(tests) >= 1: + has_else = len(exec_blocks) > len(tests) + next_node = None + if has_else: + block = exec_blocks[-1] + config_expr = ast.ConfigExpr() + config_expr.set_ast_position(block, self.filename) + key_value_tests = lark_parser.GetNodeList( + block, + ast.LarkToken.L_test, + ast.LarkToken.L_double_star_expr, + ast.LarkToken.L_if_entry, + recursively=False, + ) + operation_nodes = lark_parser.GetNodeList( + block, + ast.LarkToken.L_COLON, + ast.LarkToken.L_ASSIGN, + ast.LarkToken.L_COMP_PLUS, + recursively=False, + ) + i = 0 + j = 0 + while i < len(key_value_tests): + if ( + key_value_tests[i].type == ast.LarkToken.L_double_star_expr + or key_value_tests[i].type == ast.LarkToken.L_if_entry + ): + key, value = self.walk(key_value_tests[i]) + operation = ast.ConfigEntryOperation.OVERRIDE + i += 1 + else: + key = self.walk(key_value_tests[i]) + value = self.walk(key_value_tests[i + 1]) + operation = ast.ConfigEntryOperation.MAPPING.get( + operation_nodes[j].type + ) + j += 1 + i += 2 + config_expr.items.append( + check_utils.check_not_none( + ast.ASTFactory.get_ast_configentry( + key, value, operation, self.filename + ), + ast.ConfigEntry, + ) + ) + next_node = config_expr + exec_blocks = exec_blocks[:-1] + for test, block in zip(tests[1:][::-1], exec_blocks[1:][::-1]): + config_if_expr = ast.ConfigIfEntryExpr() + config_if_expr.set_ast_position(test, self.filename) + key_value_tests = lark_parser.GetNodeList( + block, + ast.LarkToken.L_test, + ast.LarkToken.L_double_star_expr, + ast.LarkToken.L_if_entry, + recursively=False, + ) + operation_nodes = lark_parser.GetNodeList( + block, + ast.LarkToken.L_COLON, + ast.LarkToken.L_ASSIGN, + ast.LarkToken.L_COMP_PLUS, + recursively=False, + ) + config_if_expr.if_cond = self.walk(test) + i = 0 + j = 0 + while i < len(key_value_tests): + if ( + key_value_tests[i].type == ast.LarkToken.L_double_star_expr + or key_value_tests[i].type == ast.LarkToken.L_if_entry + ): + key, value = self.walk(key_value_tests[i]) + config_if_expr.keys.append(key) + config_if_expr.values.append(value) + config_if_expr.operations.append( + ast.ConfigEntryOperation.OVERRIDE + ) + i += 1 + else: + config_if_expr.keys.append(self.walk(key_value_tests[i])) + config_if_expr.values.append( + self.walk(key_value_tests[i + 1]) + ) + config_if_expr.operations.append( + ast.ConfigEntryOperation.MAPPING.get( + operation_nodes[j].type + ) + ) + j += 1 + i += 2 + config_if_expr.orelse = next_node + next_node = config_if_expr + val.orelse = next_node + return None, val + + def walk_if_item(self, node: lark_pb.Tree) -> ast.ListIfItemExpr: + """Syntax + if_item: IF test COLON if_item_exec_block (ELIF test COLON if_item_exec_block)* (ELSE COLON if_item_exec_block)? + if_item_exec_block: list_item [NEWLINE] | NEWLINE _INDENT list_item ((COMMA [NEWLINE] | NEWLINE) list_item)* [COMMA] [NEWLINE] _DEDENT + """ + x = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.ListIfItemExpr, node, self.filename), + ast.ListIfItemExpr, + ) + if_item_node = node + tests = lark_parser.GetNodeList( + if_item_node, ast.LarkToken.L_test, recursively=False + ) + exec_blocks = lark_parser.GetNodeList( + if_item_node, ast.LarkToken.L_if_item_exec_block, recursively=False + ) + x.if_cond = self.walk(tests[0]) + item_tests = lark_parser.GetNodeList( + exec_blocks[0], + ast.LarkToken.L_list_item, + recursively=False, + ) + x.exprs = [self.walk(test) for test in item_tests] + next_node = None + if len(exec_blocks) > 1: + if len(tests) >= 1: + has_else = len(exec_blocks) > len(tests) + if has_else: + block = exec_blocks[-1] + item_tests = lark_parser.GetNodeList( + block, + ast.LarkToken.L_list_item, + recursively=False, + ) + list_expr = ast.ListExpr() + list_expr.set_ast_position(block, self.filename) + list_expr.elts = [self.walk(test) for test in item_tests] + next_node = list_expr + exec_blocks = exec_blocks[:-1] + for test, block in zip(tests[1:][::-1], exec_blocks[1:][::-1]): + list_if_expr = ast.ListIfItemExpr() + list_if_expr.set_ast_position(test, self.filename) + item_tests = lark_parser.GetNodeList( + block, + ast.LarkToken.L_list_item, + recursively=False, + ) + list_if_expr.if_cond = self.walk(test) + list_if_expr.exprs = [self.walk(test) for test in item_tests] + list_if_expr.orelse = next_node + next_node = list_if_expr + x.orelse = next_node + return x + + def walk_star_expr(self, node: lark_pb.Tree) -> ast.StarredExpr: + """Syntax + star_expr: MULTIPLY primary_expr + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.StarredExpr, node, self.filename), + ast.StarredExpr, + ) + assert len(node.children) == 2 + p.value = check_utils.check_not_none(self.walk(node.children[1]), ast.Expr) + return p + + def walk_double_star_expr(self, node: lark_pb.Tree) -> typing.Tuple: + """Syntax + double_star_expr: DOUBLE_STAR primary_expr + """ + val = check_utils.check_not_none( + self.walk_one_by_type(node, ast.LarkToken.L_primary_expr), + ast.Literal, + ast.Expr, + ) + return None, val + + def walk_loop_variables(self, node: lark_pb.Tree) -> typing.List: + """Syntax + loop_variables: primary_expr (COMMA primary_expr)* + """ + loop_variables = self.walk_all_by_type(node, ast.LarkToken.L_primary_expr) + if len(loop_variables) == 0 or len(loop_variables) > 2: + report_complier_err( + arg=f"the number of loop variables is {len(loop_variables)}, which can only be 1 or 2", + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + + for n in loop_variables: + is_identifier = isinstance(n, ast.Identifier) + identifier = typing.cast(ast.Identifier, n) + if not is_identifier or (is_identifier and len(identifier.names) != 1): + report_complier_err( + "loop variables can only be ordinary identifiers", + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + return loop_variables + + def walk_schema_expr(self, node: lark_pb.Tree) -> ast.SchemaExpr: + """Syntax + schema_expr: identifier (LEFT_PARENTHESES [arguments] RIGHT_PARENTHESES)? config_expr + arguments: argument (COMMA argument)* + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.SchemaExpr, node, self.filename), ast.SchemaExpr + ) + + types = [ + ast.LarkToken.L_identifier, + ast.LarkToken.L_arguments, + ast.LarkToken.L_config_expr, + ] + p.name, call_expr, p.config = self.walk_elements_ordered_by_types(node, types) + + p.name = check_utils.check_not_none(p.name, ast.Identifier) + p.config = check_utils.check_not_none(p.config, ast.ConfigExpr) + call_expr = check_utils.check_allow_none(call_expr, ast.CallExpr) + + p.args = call_expr.args if call_expr else [] + p.kwargs = call_expr.keywords if call_expr else [] + return p + + def walk_config_expr(self, node: lark_pb.Tree) -> ast.ConfigExpr: + """Syntax + config_expr: LEFT_BRACE [config_entries | NEWLINE _INDENT config_entries _DEDENT] RIGHT_BRACE + config_entries: config_entry ((COMMA [NEWLINE] | NEWLINE) config_entry)* [COMMA] [NEWLINE] + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.ConfigExpr, node, self.filename), ast.ConfigExpr + ) + p.items = ( + check_utils.check_all_allow_none( + list, + self.walk_one_by_type(node, ast.LarkToken.L_config_entries), + ast.ConfigEntry, + ) + or [] + ) + return p + + def walk_config_entries(self, node: lark_pb.Tree) -> typing.List[ast.ConfigEntry]: + """Syntax + config_entries: config_entry ((COMMA [NEWLINE] | NEWLINE) config_entry)* [COMMA] [NEWLINE] + config_entry: (test config_op test) | DOUBLE_STAR primary_expr + config_op: COLON | ASSIGN | COMP_PLUS + """ + return check_utils.check_all_allow_none( + list, + self.walk_all_by_type(node, ast.LarkToken.L_config_entry), + ast.ConfigEntry, + ) + + def walk_config_entry(self, node: lark_pb.Tree) -> ast.ConfigEntry: + """Syntax + config_entry: test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr + double_star_expr: DOUBLE_STAR primary_expr + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.ConfigEntry, node, self.filename), + ast.ConfigEntry, + ) + + if len(node.children) == 3: + config_op = node.children[1] + p.key = check_utils.check_not_none(self.walk(node.children[0]), ast.Expr) + p.value = check_utils.check_not_none(self.walk(node.children[2]), ast.Expr) + p.operation = ast.ConfigEntryOperation.MAPPING.get(config_op.type) + assert p.operation in range( + ast.ConfigEntryOperation.get_min(), + ast.ConfigEntryOperation.get_max() + 1, + ) + elif len(node.children) == 1: + key_value = self.walk(node.children[0]) + assert len(key_value) == 2 + assert key_value[0] is None + p.value = check_utils.check_not_none(key_value[1], ast.Literal, ast.Expr) + p.key = key_value[0] + else: + report_complier_err( + "Unsupported schema config entry", + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + return p + + def walk_lambda_expr(self, node: lark_pb.Tree) -> ast.LambdaExpr: + """Syntax + lambda_expr: LAMBDA [schema_arguments] [RIGHT_ARROW type] LEFT_BRACE [expr_stmt | NEWLINE _INDENT schema_init_stmt+] _DEDENT RIGHT_BRACE + """ + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.LambdaExpr, node, self.filename), ast.LambdaExpr + ) + types = [ + ast.LarkToken.L_schema_arguments, + ast.LarkToken.L_type, + ] + p.args, p.return_type_node = self.walk_elements_ordered_by_types(node, types) + p.return_type_str = ( + p.return_type_node.plain_type_str if p.return_type_node else "" + ) + expr_stmt_list = lark_parser.GetNodeList( + node, ast.LarkToken.L_expr_stmt, recursively=False + ) + p.body = ( + [self.walk(expr_stmt_list[0])] + if expr_stmt_list + else self.walk_all_by_type(node, ast.LarkToken.L_schema_init_stmt) + ) + return p + + def walk_NAME(self, node: lark_pb) -> ast.Identifier: + def __build_identifier_by_name() -> ast.Identifier: + p: ast.Identifier = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.Identifier, node, self.filename), + ast.Identifier, + ) + p.names.append(node.token_value) + p.name_nodes.append(self.lark_name_to_ast_name(node)) + return p + + assert node + return __build_identifier_by_name() + + def walk_constant(self, node: lark_pb.Tree) -> ast.NameConstantLit: + """ + Grammar: + NONE | TRUE | FALSE + + Input: + constant node token. + + Output: + constant ast. + """ + + if len(node.children) == 1 and ast.LarkToken.is_name_constant( + node.children[0].type + ): + token_type = node.children[0].type + p = check_utils.check_not_none( + ast.ASTFactory.get_ast( + ast.NameConstantLit, node.children[0], self.filename + ), + ast.NameConstantLit, + ) + p.value = ( + literal_eval(node.children[0].token_value) + if token_type != ast.LarkToken.L_UNDEFINED + else internal.Undefined + ) + return p + else: + report_complier_err( + "Unsupported Constant", + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + + def walk_number(self, node: lark_pb.Tree) -> ast.NumberLit: + """ + Grammar: + number: DEC_NUMBER [multiplier] | HEX_NUMBER | BIN_NUMBER | OCT_NUMBER | FLOAT_NUMBER + Input: + number lit tree node. + Output: + number lit ast. + """ + p: ast.NumberLit = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.NumberLit, node, self.filename), ast.NumberLit + ) + + if len(node.children) >= 1 and ast.LarkToken.is_int_number( + node.children[0].type + ): + p.value = ( + literal_eval(node.children[0].token_value) + if node.children[0].type != ast.LarkToken.L_OCT_NUMBER + else int(node.children[0].token_value, base=8) + ) + if len(node.children) == 2: + p.binary_suffix = self.walk(node.children[1]) + return p + elif len(node.children) >= 1 and ast.LarkToken.is_float_number( + node.children[0].type + ): + p.value = literal_eval(node.children[0].token_value) + return p + else: + report_complier_err( + "Unsupported Number", + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + + def walk_multiplier(self, node: lark_pb.Tree) -> str: + """ + Grammar: + multiplier: ( SI_N_L | SI_U_L | SI_M_L | SI_K_L | SI_K | SI_M | SI_G | SI_T | SI_P ) [ IEC ] + Input: + multiplier tree node. + Output: + multiplier ast. + """ + assert len(node.children) >= 1 + bp = node.children[0].token_value + if len(node.children) == 2: + bp += "i" + + if not hasattr(ast.NumberBinarySuffix, bp): + report_complier_err( + f"unsupported quantity type: {bp}", + filename=self.filename, + line_no=node.children[0].line, + col_no=node.children[0].column, + end_col_no=node.children[0].end_column, + ) + + return getattr(ast.NumberBinarySuffix, bp) + + def walk_string( + self, node: lark_pb.Tree + ) -> typing.Union[ast.StringLit, ast.JoinedString]: + """ + According to pypy JoinedString + """ + assert len(node.children) >= 1 + node = node.children[0] + token_value = literal_eval(node.token_value) + string_value = ( + bytes.decode(token_value) + if isinstance(token_value, bytes) + else str(token_value) + ) + quotation = '"' if node.token_value.endswith('"') else "'" + quotation_index = node.token_value.find(quotation) + has_raw_prefix = ( + "r" in node.token_value[:quotation_index] + or "R" in node.token_value[:quotation_index] + ) + has_interpolation = bool(STRING_INTERPOLATION_REGEX.search(string_value)) + if has_raw_prefix or not has_interpolation: + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.StringLit, node, self.filename), + ast.StringLit, + ) + p.value = string_value + else: + p = check_utils.check_not_none( + ast.ASTFactory.get_ast(ast.JoinedString, node, self.filename), + ast.JoinedString, + ) + end = 0 + str_len = len(string_value) + if "$" in string_value.replace("${", "").replace("$$", ""): + # Check invalid single `$` + report_complier_err( + arg="invalid single '$', expecting '$' or '{'", + err_type=kcl_error.ErrType.InvalidFormatSpec_TYPE, + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + while string_value and STRING_INTERPOLATION_REGEX.search(string_value): + match = STRING_INTERPOLATION_REGEX.search(string_value) + expr_string = match.group(1) + format_spec = None + if ":" in expr_string: + parts = expr_string.split(":") + expr_string, format_spec = parts[0], parts[1].strip() + start, end = match.span() + string_lit = string_value[:start] + if string_lit is not None: + p.values.append( + ast.ASTFactory.get_ast_literal( + ast.StringLit, + line=node.line, + column=node.column + start, + value=string_lit.replace("$$", "$"), + filename=self.filename, + ) + ) + string_value = string_value[end:] + expr = ParseExpr( + expr_string, + filename=self.filename, + line_offset=node.line - 1, + column_offset=node.column + 2 + start, + ) + if expr: + expr.filename = self.filename + if not expr: + report_complier_err( + arg=f"invalid string interpolation expression '{expr_string}'", + err_type=kcl_error.ErrType.InvalidFormatSpec_TYPE, + filename=self.filename, + line_no=node.line, + col_no=node.column, + end_col_no=node.end_column, + ) + formatted_value = check_utils.check_not_none( + ast.ASTFactory.get_ast_formatted_value( + expr, format_spec, self.filename + ), + ast.FormattedValue, + ) + p.values.append(formatted_value) + if end < str_len and string_value is not None: + p.values.append( + check_utils.check_not_none( + ast.ASTFactory.get_ast_literal( + ast.StringLit, + line=node.line, + column=node.column + end, + value=string_value.replace("$$", "$"), + filename=self.filename, + ), + ast.StringLit, + ) + ) + p.raw_value = node.token_value + p.is_long_string = node.type == ast.LarkToken.L_LONG_STRING + return p + + # ------------------------------------------------------------------------- + # simplify_StringLitValue + # ------------------------------------------------------------------------- + + def simplify_StringLitValue(self, str_lit: ast.StringLit) -> str: + if not str_lit or not str_lit.value: + return "" + + s: str = str_lit.value + # """str_value""" -> str_value + if s.startswith('"""'): + return s[3:-3] + + # '''str_value''' -> str_value + if s.startswith("'''"): + return s[3:-3] + + # "str_value" -> str_value + if s.startswith('"'): + return s[1:-1] + + # 'str_value' -> str_value + if s.startswith("'"): + return s[1:-1] + + # str_value -> str_value + return s + + +# ----------------------------------------------------------------------------- +# ParseFile +# ----------------------------------------------------------------------------- + + +def ParseFile( + filename: typing.Optional[str], + code: typing.Optional[str] = None, + *, + pkg: str = "", + mode: ParseMode = ParseMode.Null, +) -> ast.Module: + try: + if code is None: + code = Path(filename).read_text(encoding="utf-8") + # Store the KCL error file cache map + kcl_error.FileCache.put(filename, code) + lark_parser.filename = filename + larktree = lark_parser.ParseCode(code) + return ASTBuilder(larktree, pkg=pkg, filename=filename, mode=mode).BuildAST() + except ( + lark.exceptions.UnexpectedCharacters, + lark.exceptions.UnexpectedToken, + ) as err: + if kclvm.config.debug > 0: + raise + token_msgs = None + allowed_tokens = ( + list(err.accepts) + if isinstance(err, lark.exceptions.UnexpectedToken) + else list(err.allowed) + ) + if allowed_tokens: + allowed_tokens.sort() + token_msgs = [ + ParseAcceptToken(allowed_token) for allowed_token in allowed_tokens + ] + arg_msg = f"Expected one of {token_msgs}" if token_msgs else None + syntax_err = kcl_error.get_exception( + err_type=kcl_error.ErrType.InvalidSyntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, + line_no=err.line, + col_no=err.column, + src_code=code, + arg_msg=arg_msg, + ) + ], + ) + syntax_err.accepts_lark = allowed_tokens + syntax_err.accepts_msg = token_msgs + raise syntax_err + + +@PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "key") +@PostCheck(lambda v: CheckRules.check_type_not_none(v, str)) +def ParseAcceptToken(key: str) -> str: + if key in ast.lark_token.LarkToken.LL_token_str_value_map.keys(): + token_value = ast.lark_token.LarkToken.LL_token_str_value_map[key] + elif key in ast.lark_token.LarkToken.LL_token_list: + token_value = key.lower() + else: + token_value = key + return token_value + + +# ----------------------------------------------------------------------------- +# ParseExpr +# ----------------------------------------------------------------------------- + + +def ParseExpr( + code: str, + filename: str = None, + line_offset: int = 0, + column_offset: int = 0, + *, + pkg: str = "", + mode: ParseMode = ParseMode.Null, +) -> ast.Expr: + ast.AST.set_offset(line_offset, column_offset) + module = ParseFile(filename, code, pkg=pkg, mode=mode) + ast.AST.reset_offset() + return module.GetFirstExprInExprStmt() + + +# ----------------------------------------------------------------------------- +# LoadProgram +# ----------------------------------------------------------------------------- + + +def _loadPackages( + modfile: kclvm.config.KclModFile, + import_spec: ast.ImportStmt, + pkgs: typing.Dict[str, typing.List[ast.Module]], + missing_pkgs: typing.List[str], + main_files: typing.List[str], + mode: ParseMode = ParseMode.Null, + set_ast_parent: bool = False, +): + root: str = modfile.root + pkgpath: str = import_spec.path + + assert root, f"root={root}" + assert pkgs + + if not pkgpath: + return + + # plugin pkgs + if pkgpath.startswith("kcl_plugin."): + return # ignore import error + + # builtin pkgs + if pkgpath in builtin.STANDARD_SYSTEM_MODULES: + return + + if pkgpath in pkgs: + return + if pkgpath in missing_pkgs: + return + + # try cache + if modfile.build.enable_pkg_cache: + if modfile.build.cached_pkg_prefix and pkgpath.startswith( + modfile.build.cached_pkg_prefix + ): + m_list = vfs.LoadPkgCache( + root, pkgpath, option=get_ast_cache_option(set_ast_parent) + ) + if m_list and isinstance(m_list, list): + pkgs[pkgpath] = m_list + for m in pkgs[pkgpath]: + import_spec_list = fix.fix_and_get_module_import_list( + root, m, False + ) + for import_spec in import_spec_list: + _loadPackages( + modfile, + import_spec, + pkgs, + missing_pkgs, + main_files, + mode=mode, + set_ast_parent=set_ast_parent, + ) + return + + # scan all kcl files + all_k_files = glob.glob(f"{root}/{pkgpath.replace('.', '/')}/*.k", recursive=False) + + # skip `_*.k` and `*_test.k` kcl files + k_files: typing.List[str] = [] + for s in all_k_files: + if os.path.basename(s).startswith("_"): + continue + if s.endswith("_test.k"): + continue + + k_files.append(s) + + if len(k_files) == 0 and os.path.isfile(f"{root}/{pkgpath.replace('.', '/')}.k"): + file = f"{root}/{pkgpath.replace('.', '/')}.k" + k_files.append(file) + + if len(k_files) == 0: + missing_pkgs.append(pkgpath) + return + + pkgs[pkgpath]: typing.List[ast.Module] = [] + + k_files.sort() + for filename in k_files: + with open(filename, "r", encoding="utf-8") as f: + code = str(f.read()) + + m = ParseFile(filename, code, pkg=pkgpath, mode=mode) + m.relative_filename = m.filename.replace(root, ".", 1) + preprocess.fix_identifier_prefix(m) + fix.fix_and_get_module_import_list(root, m) + fix.fix_qualified_identifier(m) + if set_ast_parent: + fix.fix_set_parent_info(m) + pkgs[pkgpath].append(m) + + # save cache + if modfile.build.enable_pkg_cache: + if modfile.build.cached_pkg_prefix and pkgpath.startswith( + modfile.build.cached_pkg_prefix + ): + vfs.SavePkgCache( + root, + pkgpath, + pkgs[pkgpath], + option=get_ast_cache_option(set_ast_parent), + ) + + for m in pkgs[pkgpath]: + import_spec_list = fix.fix_and_get_module_import_list(root, m, False) + for import_spec in import_spec_list: + _loadPackages( + modfile, + import_spec, + pkgs, + missing_pkgs, + main_files, + mode=mode, + set_ast_parent=set_ast_parent, + ) + + return + + +def LoadProgram( + *path: str, + work_dir: str = "", + k_code_list: typing.List[str] = None, + mode: ParseMode = ParseMode.Null, + set_ast_parent: bool = False, + load_packages: bool = True, +) -> ast.Program: + assert len(path) > 0 + # Clear the KCL error file cache map + kcl_error.FileCache.clear() + k_code_list = k_code_list or [] + if work_dir or kclvm.config.current_path: + path_list = [ + str(x).replace( + KCL_MOD_PATH_ENV, + vfs.GetPkgRoot(work_dir or kclvm.config.current_path) or "", + ) + for x in path + ] + else: + path_list = [str(x) for x in path] + k_files: typing.List[str] = [] + kclvm.config.input_file = path_list + for i, s in enumerate(path_list): + s = os.path.abspath(s) + if os.path.isdir(s): + for filename in glob.glob(f"{s}/*.k", recursive=False): + if os.path.basename(filename).startswith("_"): + continue + k_files.append(os.path.abspath(filename)) + elif i < len(k_code_list) or os.path.isfile(s): + k_files.append(os.path.abspath(s)) + else: + arg_msg = ( + f"Cannot find the kcl file, please check whether the file path {s}" + if s + else f"The file path {s} is None" + ) + kcl_error.report_exception( + err_type=kcl_error.ErrType.CannotFindModule_TYPE, arg_msg=arg_msg + ) + + if not k_files: + report_complier_err("No input KCL files") + + if not work_dir: + work_dir = os.path.dirname(k_files[0]) + + root: str = vfs.MustGetPkgRoot(k_files) + if not root: + root = work_dir + + modfile: kclvm.config.KclModFile = vfs.LoadModFile(root) + + pkgs: typing.Dict[str, typing.List[ast.Module]] = {} + missing_pkgs: typing.List[str] = [] + + main_pkg_modules: typing.List[ast.Module] = [] + __kcl_main__: str = "__main__" + import_names = {} + + for i, filename in enumerate(k_files): + code: typing.Optional[str] = None + if i < len(k_code_list): + code = k_code_list[i] + # Save main package cache + m = None + if modfile.build.enable_pkg_cache: + m = vfs.LoadMainPkgCache( + root, filename, option=get_ast_cache_option(set_ast_parent) + ) + if not m: + m = ParseFile( + filename, pkg=__kcl_main__, code=code, mode=ParseMode.ParseComments + ) + m.relative_filename = m.filename.replace(root, ".", 1) + preprocess.fix_identifier_prefix(m) + fix.fix_test_schema_auto_relaxed(m) + import_list = fix.fix_and_get_module_import_list(root, m) + for import_stmt in import_list: + import_names[import_stmt.name] = import_stmt.path + fix.fix_qualified_identifier(m, import_names=import_names) + if set_ast_parent: + fix.fix_set_parent_info(m) + else: + import_list = m.GetImportList() + for import_stmt in import_list: + import_names[import_stmt.name] = import_stmt.path + # Save main package cache + if modfile.build.enable_pkg_cache: + vfs.SaveMainPkgCache( + root, filename, m, option=get_ast_cache_option(set_ast_parent) + ) + m.name = __kcl_main__ + main_pkg_modules.append(m) + + pkgs[__kcl_main__] = main_pkg_modules + + for m in main_pkg_modules: + import_spec_list = fix.fix_and_get_module_import_list(root, m, False) + if load_packages: + for import_spec in import_spec_list: + _loadPackages( + modfile, + import_spec, + pkgs, + missing_pkgs, + k_files, + mode=mode, + set_ast_parent=set_ast_parent, + ) + + return ast.Program(root=root, main=__kcl_main__, pkgs=pkgs) + + +# ----------------------------------------------------------------------------- +# END +# ----------------------------------------------------------------------------- diff --git a/internal/kclvm_py/compiler/vfs/__init__.py b/internal/kclvm_py/compiler/vfs/__init__.py new file mode 100644 index 000000000..4bb8b268e --- /dev/null +++ b/internal/kclvm_py/compiler/vfs/__init__.py @@ -0,0 +1,35 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .vfs import ( + LoadPkgCache, + SavePkgCache, + LoadMainPkgCache, + SaveMainPkgCache, + LoadBytecodeCache, + SaveBytecodeCache, + IsAbsPkgPath, + IsRelPkgPath, + FixImportPath, + CacheOption, + DEFAULT_CACHE_DIR, + FST_CACHE_DIR, +) +from .kcl_mod import GetPkgRoot, MustGetPkgRoot, LoadModFile + +__all__ = [ + "LoadPkgCache", + "SavePkgCache", + "LoadMainPkgCache", + "SaveMainPkgCache", + "LoadBytecodeCache", + "SaveBytecodeCache", + "IsAbsPkgPath", + "IsRelPkgPath", + "FixImportPath", + "GetPkgRoot", + "MustGetPkgRoot", + "LoadModFile", + "CacheOption", + "DEFAULT_CACHE_DIR", + "FST_CACHE_DIR", +] diff --git a/internal/kclvm_py/compiler/vfs/kcl_mod.py b/internal/kclvm_py/compiler/vfs/kcl_mod.py new file mode 100644 index 000000000..3e7c99d56 --- /dev/null +++ b/internal/kclvm_py/compiler/vfs/kcl_mod.py @@ -0,0 +1,88 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing +import pathlib +import os +import sys + +import toml +import google.protobuf.json_format as json_format + +import kclvm.config +import kclvm.kcl.error as kcl_error + + +def GetPkgRoot( + k_file_path: str, should_current_file_work_dir: bool = True +) -> typing.Optional[str]: + """Search kcl.mod filepath with the KCL file path""" + if not k_file_path: + return None + + # search by kcl.mod file + module_path = pathlib.Path(os.path.abspath(k_file_path)) + root = module_path.root + while module_path: + if module_path == module_path.parent or str(module_path) == root: + break + + kcl_mod_path = module_path.joinpath("kcl.mod") + if kcl_mod_path.exists() and kcl_mod_path.is_file(): + return str(module_path) + + module_path = module_path.parent + + if should_current_file_work_dir and k_file_path.endswith(".k"): + return os.path.dirname(k_file_path) + + return None + + +def MustGetPkgRoot(file_paths: typing.List[str]) -> typing.Optional[str]: + """Search kcl.mod filepath with the KCL file paths, + when found multiple kcl.mod paths, raise a compile error. + """ + # Get kcl.mod paths from input file paths and remove empty path using the filter function. + paths = set( + filter( + None, + [ + GetPkgRoot(file_path, should_current_file_work_dir=False) + for file_path in file_paths or [] + ], + ) + ) + # Not found kcl.mod. + if not paths: + return None + # Find one kcl.mod. + if len(paths) == 1: + return list(paths)[0] + # Find multiple kcl.mod, raise an error. + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg=f"conflict kcl.mod file paths: {paths}", + ) + + +def LoadModFile(root: str) -> kclvm.config.KclModFile: + k_mod_file_path = f"{root}/kcl.mod" + + if not os.path.exists(k_mod_file_path): + mod_file = kclvm.config.KclModFile(root=root) + return mod_file + + d = toml.load(k_mod_file_path) + mod_file = kclvm.config.KclModFile(root=root) + json_format.ParseDict(d, mod_file, ignore_unknown_fields=True) + mod_file.root = root + return mod_file + + +if __name__ == "__main__": + if len(sys.argv) < 2 or (sys.argv[1] == "-h" or sys.argv[1] == "-help"): + print("usage: python3 ./this_py_file ") + sys.exit(0) + + f = LoadModFile(sys.argv[1]) + print(f) diff --git a/internal/kclvm_py/compiler/vfs/vfs.py b/internal/kclvm_py/compiler/vfs/vfs.py new file mode 100644 index 000000000..54f93f273 --- /dev/null +++ b/internal/kclvm_py/compiler/vfs/vfs.py @@ -0,0 +1,373 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import re +import pathlib +import pickle +import os +import hashlib +import time + +from dataclasses import dataclass +from typing import Dict + +import kclvm.api.version as ver_pkg +import kclvm.api.object as objpkg +import kclvm.kcl.ast as ast + +from filelock import FileLock + +# ------------- +# Type alias +# ------------- + +CacheInfo = str +Cache = Dict[str, CacheInfo] + +LOCK_SUFFIX = ".lock" +NORMAL_CACHE_SUFFIX = ".data" +BYTECODE_CACHE_PREFIX = "bytecode" +BYTECODE_CACHE_SUFFIX = ".kclc" +DEFAULT_CACHE_DIR = ".kclvm/cache" +FST_CACHE_DIR = ".kclvm/fst_cache" +CACHE_INFO_FILENAME = "info.pickle" + + +@dataclass +class CacheOption: + cache_dir: str = DEFAULT_CACHE_DIR + + +def _get_cache_dir(root: str, cache_dir: str = DEFAULT_CACHE_DIR) -> str: + return os.path.join(root, cache_dir, f"{ver_pkg.VERSION}-{ver_pkg.CHECKSUM}") + + +def _get_cache_filename( + root: str, pkgpath: str, cache_dir: str = DEFAULT_CACHE_DIR +) -> str: + return os.path.join( + root, + cache_dir, + f"{ver_pkg.VERSION}-{ver_pkg.CHECKSUM}", + f"{pkgpath}{NORMAL_CACHE_SUFFIX}", + ) + + +def _get_cache_info_filename(root: str, cache_dir: str = DEFAULT_CACHE_DIR): + return os.path.join( + root, cache_dir, f"{ver_pkg.VERSION}-{ver_pkg.CHECKSUM}", CACHE_INFO_FILENAME + ) + + +def _get_cache_bytecode_filename( + root: str, check_sum: str, cache_dir: str = DEFAULT_CACHE_DIR +): + return os.path.join( + root, + cache_dir, + f"{ver_pkg.VERSION}-{ver_pkg.CHECKSUM}", + f"{BYTECODE_CACHE_PREFIX}_{check_sum}{BYTECODE_CACHE_SUFFIX}", + ) + + +def read_info_cache(root: str, cache_dir: str = DEFAULT_CACHE_DIR) -> Cache: + """Read the cache if it exists and is well formed. + If it is not well formed, the call to write_info_cache later should resolve the issue. + """ + cache_file = pathlib.Path(_get_cache_info_filename(root, cache_dir=cache_dir)) + if not cache_file.exists(): + return {} + + with cache_file.open("rb") as fobj: + try: + cache: Cache = pickle.load(fobj) + except (pickle.UnpicklingError, ValueError): + return {} + + return cache + + +def write_info_cache( + cache: Cache, root: str, filepath: str, cache_dir: str = DEFAULT_CACHE_DIR +) -> None: + """Update the cache info file.""" + dst_filename = _get_cache_info_filename(root, cache_dir=cache_dir) + try: + cache_dir = _get_cache_dir(root, cache_dir=cache_dir) + pathlib.Path(cache_dir).mkdir(parents=True, exist_ok=True) + relative_path = filepath.replace(root, ".", 1) + new_cache = { + **cache, + **{relative_path: get_cache_info(filepath)}, + } + tmp_filename = f"{cache_dir}/.{os.getpid()}{time.time_ns()}.tmp" + # Write cache atomic + with FileLock(dst_filename + LOCK_SUFFIX): + f = open(tmp_filename, "wb") + pickle.dump(new_cache, f) + f.flush() + os.fsync(f.fileno()) + f.close() + os.rename(tmp_filename, dst_filename) + except (pickle.UnpicklingError, ValueError): + f.close() + os.remove(tmp_filename) + + +def get_cache_info(path: str) -> CacheInfo: + """Return the information used to check if a file or path is already changed or not.""" + path = pathlib.Path(path) + check_sum = hashlib.md5() + if os.path.isfile(path): + with open(path, "rb") as f: + check_sum.update(f.read()) + else: + for file in list(sorted(path.glob("*.k"))): + with open(file, "rb") as f: + check_sum.update(f.read()) + return check_sum.hexdigest() + + +def get_pkg_realpath_from_pkgpath(root: str, pkgpath: str) -> str: + """Get the pkgpath real path in the file system according to the root and pkgpath""" + filepath = f"{root}/{pkgpath.replace('.', '/')}" + if os.path.isfile(f"{filepath}.k"): + filepath = f"{filepath}.k" + return filepath + + +def load_data_from_file(filename) -> any: + f = open(filename, "rb") + # PyCharm + # noinspection PyBroadException + try: + x = pickle.load(f) + f.close() + return x + except Exception: + f.close() + os.remove(filename) + return None + + +def save_data_to_file(dst_filename: str, tmp_filename: str, x: any): + try: + with FileLock(dst_filename + LOCK_SUFFIX): + # write cache atomic + f = open(tmp_filename, "wb") + # PyCharm + # noinspection PyBroadException + pickle.dump(x, f) + f.flush() + os.fsync(f.fileno()) + f.close() + os.rename(tmp_filename, dst_filename) + return + except Exception: + f.close() + os.remove(tmp_filename) + return + + +def LoadPkgCache(root: str, pkgpath: str, option: CacheOption = CacheOption()) -> any: + if not root or not pkgpath: + return None + + filename = _get_cache_filename(root, pkgpath, cache_dir=option.cache_dir) + if not os.path.exists(filename): + return None + + # Compare the md5 using cache + realpath = get_pkg_realpath_from_pkgpath(root, pkgpath) + if os.path.exists(realpath): + cache_info = read_info_cache(root, cache_dir=option.cache_dir) + relative_path = realpath.replace(root, ".", 1) + path_info_in_cache = cache_info.get(relative_path) + path_info = get_cache_info(realpath) + if path_info_in_cache != path_info: + return None + + return load_data_from_file(filename) + + +def SavePkgCache(root: str, pkgpath: str, x: any, option: CacheOption = CacheOption()): + if not root or not pkgpath or not x: + return + + dst_filename = _get_cache_filename(root, pkgpath, cache_dir=option.cache_dir) + + # Save the pkgpath timesample and filesize into the cache + realpath = get_pkg_realpath_from_pkgpath(root, pkgpath) + if os.path.exists(realpath): + cache_info = read_info_cache(root, cache_dir=option.cache_dir) + write_info_cache(cache_info, root, realpath, cache_dir=option.cache_dir) + + cache_dir = _get_cache_dir(root, cache_dir=option.cache_dir) + pathlib.Path(cache_dir).mkdir(parents=True, exist_ok=True) + + tmp_filename = f"{cache_dir}/{pkgpath}.{os.getpid()}{time.time_ns()}.tmp" + + save_data_to_file(dst_filename, tmp_filename, x) + + +def LoadMainPkgCache( + root: str, filename: str, option: CacheOption = CacheOption() +) -> any: + if not root or not filename: + return None + + cache_name = filename.replace(root, "").replace("/", "_") + cache_filename = _get_cache_filename(root, cache_name, cache_dir=option.cache_dir) + + if not os.path.exists(cache_filename): + return None + + # Compare the md5 using cache + if os.path.exists(filename): + cache_info = read_info_cache(root, cache_dir=option.cache_dir) + relative_path = filename.replace(root, ".", 1) + path_info_in_cache = cache_info.get(relative_path) + path_info = get_cache_info(filename) + if path_info_in_cache != path_info: + return None + + return load_data_from_file(cache_filename) + + +def SaveMainPkgCache( + root: str, filename: str, x: any, option: CacheOption = CacheOption() +): + if not root or not filename: + return + + cache_name = filename.replace(root, "").replace("/", "_") + dst_filename = _get_cache_filename(root, cache_name, cache_dir=option.cache_dir) + + if os.path.exists(filename): + cache_info = read_info_cache(root, cache_dir=option.cache_dir) + write_info_cache(cache_info, root, filename, cache_dir=option.cache_dir) + + cache_dir = _get_cache_dir(root, cache_dir=option.cache_dir) + pathlib.Path(cache_dir).mkdir(parents=True, exist_ok=True) + + tmp_filename = f"{cache_dir}/{cache_name}.{os.getpid()}{time.time_ns()}.tmp" + + save_data_to_file(dst_filename, tmp_filename, x) + + +def LoadBytecodeCache( + root: str, ast_program: ast.Program, option: CacheOption = CacheOption() +) -> objpkg.KCLProgram: + if not root: + return None + if not ast_program or not isinstance(ast_program, ast.Program): + return None + if not ast_program.pkgs: + return None + check_sum = ast_program.get_check_sum(root) + cache_filename = _get_cache_bytecode_filename( + root, check_sum, cache_dir=option.cache_dir + ) + if not os.path.exists(cache_filename): + return None + return load_data_from_file(cache_filename) + + +def SaveBytecodeCache( + root: str, + ast_program: ast.Program, + program: objpkg.KCLProgram, + option: CacheOption = CacheOption(), +): + if not root: + return + if not ast_program or not isinstance(ast_program, ast.Program): + return + if not program or not isinstance(program, objpkg.KCLProgram): + return + pkgs = list(program.pkgs.keys()) if program.pkgs else None + if not pkgs: + return + check_sum = ast_program.get_check_sum(root) + dst_filename = _get_cache_bytecode_filename( + root, check_sum, cache_dir=option.cache_dir + ) + if os.path.exists(dst_filename): + return + cache_dir = _get_cache_dir(root, cache_dir=option.cache_dir) + pathlib.Path(cache_dir).mkdir(parents=True, exist_ok=True) + + tmp_filename = f"{cache_dir}/{os.getpid()}{time.time_ns()}.kclc.tmp" + + save_data_to_file(dst_filename, tmp_filename, program) + + +def IsAbsPkgPath(s: str) -> bool: + if not s or not isinstance(s, str): + return False + if s.startswith("."): + return False + if os.path.isabs(s): + return False + if ".." in s: + return False + if re.search(r"\s", s): + return False + + return True + + +def IsRelPkgPath(s: str) -> bool: + return s.strip().startswith(".") if s and isinstance(s, str) else False + + +def FixImportPath(root: str, filepath: str, import_path: str) -> str: + """ + relpath: import .sub + FixImportPath(root, "path/to/app/file.k", ".sub") => path.to.app.sub + FixImportPath(root, "path/to/app/file.k", "..sub") => path.to.sub + FixImportPath(root, "path/to/app/file.k", "...sub") => path.sub + FixImportPath(root, "path/to/app/file.k", "....sub") => sub + FixImportPath(root, "path/to/app/file.k", ".....sub") => "" + + abspath: import path.to.sub + FixImportPath(root, "path/to/app/file.k", "path.to.sub") => path.to.sub + """ + assert root + assert filepath + assert import_path + + if not import_path.startswith("."): + return import_path + + # Filepath to pkgpath + pkgpath: str = ( + os.path.relpath(os.path.dirname(filepath), root).replace("/", ".").rstrip(".") + ) + pkgpath = pkgpath.replace("\\", ".").rstrip(".") + + leading_dot_count = len(import_path) + for i in range(len(import_path)): + if import_path[i] != ".": + leading_dot_count = i + break + + # The pkgpath is the current root path + if not pkgpath: + return import_path.lstrip(".") if leading_dot_count <= 1 else "" + + if leading_dot_count == 1: + return pkgpath + import_path + + ss = pkgpath.split(".") + + if (leading_dot_count - 1) < len(ss): + return ( + ".".join(ss[: -(leading_dot_count - 1)]) + + "." + + import_path[leading_dot_count:] + ) + + if (leading_dot_count - 1) == len(ss): + return import_path[leading_dot_count:] + + return "" diff --git a/internal/kclvm_py/config/__init__.py b/internal/kclvm_py/config/__init__.py new file mode 100644 index 000000000..eee38774f --- /dev/null +++ b/internal/kclvm_py/config/__init__.py @@ -0,0 +1,53 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .config import ( + verbose, + debug, + save_temps, + output, + input_file, + current_path, + disable_none, + strict_range_check, + arguments, + list_option_mode, + disable_schema_check, + options_help_message, + path_selector, + overrides, + dump, + parse_config, + is_target_native, + KCLTopLevelArgumentAction, + KCLPathSelectorAction, + KCLOverrideAction, + KCLCLISettingAction, +) +from .modfile_pb2 import KclModFile +from .settings import load_settings_files + +__all__ = [ + "verbose", + "debug", + "save_temps", + "output", + "input_file", + "current_path", + "disable_none", + "strict_range_check", + "arguments", + "list_option_mode", + "disable_schema_check", + "options_help_message", + "path_selector", + "overrides", + "dump", + "parse_config", + "is_target_native", + "KCLTopLevelArgumentAction", + "KCLPathSelectorAction", + "KCLOverrideAction", + "KCLCLISettingAction", + "KclModFile", + "load_settings_files", +] diff --git a/internal/kclvm_py/config/config.py b/internal/kclvm_py/config/config.py new file mode 100644 index 000000000..bf9680aa5 --- /dev/null +++ b/internal/kclvm_py/config/config.py @@ -0,0 +1,371 @@ +from ast import literal_eval +from copy import deepcopy +from pathlib import Path +from typing import List +import argparse as _argparse +import ruamel.yaml as _yaml +from io import StringIO + +import kclvm.kcl.ast as ast +import kclvm.kcl.error as kcl +import kclvm.config + +verbose = 0 +""" +Print more information and intermediate representation. +""" + +debug = 0 +""" +Print debug information for developers. +""" + +save_temps = False +""" +Save the intermediate representation files. +""" + +output = None +""" +The name of the output file. +""" + +input_file = None +""" +The name of the input file. +""" + +current_path = None +""" +The current path where KCL is executed. +""" + +disable_none = False +""" +Disable dumping values that are None. +""" + +strict_range_check = False +""" +Whether perform strict numberic range check +""" + +arguments = [] +""" +Top level arguments. +""" + +list_option_mode = 0 +""" +List option mode (>= 0). +""" + +disable_schema_check = False +""" +Disabel schema check +""" + +options_help_message = "" +""" +Optopns help message +""" + +path_selector = [] +""" +The path selector +""" + +overrides = [] +""" +The configuration override path and value +""" + +is_target_native = False +"""Whether the target is 'native'""" + + +def dump(): + if verbose: + print("=== Configurations ===") + print("verbose = {}".format(verbose)) + print("save_temps = {}".format(save_temps)) + print("output = {}".format(output)) + print("disable_none = {}".format(disable_none)) + print("input_file = {}".format(input_file)) + print("current_path = {}".format(current_path)) + print("arguments = {}".format(arguments)) + print("strict_range_check = {}".format(strict_range_check)) + + print("list_option_mode = {}".format(list_option_mode)) + print("disable_schema_check = {}".format(disable_schema_check)) + + +def parse_config(argsdict: dict): + """ + Parse kclvm config with cli argument dict + """ + if not argsdict: + return + kclvm.config.verbose = ( + int(argsdict.get(KCLCLIFlag.VERBOSE, 0)) or kclvm.config.verbose + ) + kclvm.config.debug = int(argsdict.get(KCLCLIFlag.DEBUG, 0)) or kclvm.config.debug + kclvm.config.list_option_mode = ( + int(argsdict.get(KCLCLIFlag.LIST_OPTION_MODE, 0)) + or kclvm.config.list_option_mode + ) + kclvm.config.save_temps = ( + argsdict.get(KCLCLIFlag.SAVE_TEMPS, False) or kclvm.config.save_temps + ) + kclvm.config.strict_range_check = ( + argsdict.get(KCLCLIFlag.STRICT_RANGE_CHECK) or kclvm.config.strict_range_check + ) + kclvm.config.disable_none = ( + argsdict.get(KCLCLIFlag.DISABLE_NONE, False) or kclvm.config.disable_none + ) + kclvm.config.path_selector = argsdict.get( + KCLCLIFlag.PATH_SELECTOR, kclvm.config.path_selector + ) + kclvm.config.overrides = argsdict.get(KCLCLIFlag.OVERRIDES, kclvm.config.overrides) + kclvm.config.input_file = ( + argsdict.get(KCLCLIFlag.FILES) + or argsdict.get(KCLCLIFlag.FILE) + or kclvm.config.input_file + ) + kclvm.config.output = ( + argsdict.get(KCLCLIFlag.OUTPUT) + if argsdict.get(KCLCLIFlag.OUTPUT) + else kclvm.config.output + ) + kclvm.config.arguments += argsdict.get(KCLCLIFlag.ARGUMENT, []) + + +def _deal_key_value(values, keys, vals): + is_valid_value = False + _k, _v = None, None + try: + _k, _v = values + _v = literal_eval(_v) + except Exception: + pass + # _v is a string type, and avoid quotation ' and " mark escape error + _v = _v.replace("'", "\\'").replace('"', '\\"') if isinstance(_v, str) else _v + # the _v can only be KCL internal type + if _v is None or isinstance(_v, (bool, int, float, str, list, dict)): + is_valid_value = True + if _k is None or not isinstance(_k, str) or _k.strip() == "": + kcl.report_exception( + err_type=kcl.ErrType.IllegalArgumentError_TYPE, + arg_msg=f"Invalid option name: '{_k}'. should be a non-empty string", + ) + if not is_valid_value: + kcl.report_exception( + err_type=kcl.ErrType.IllegalArgumentError_TYPE, + arg_msg=f"Invalid option value: '{_object_to_yaml_str(_v)}' for option(\"{_k}\").", + ) + keys.append(_k) + vals.append(_v) + + +class KCLCLIFlag: + FILE = "file" + FILES = "files" + DISABLE_NONE = "disable_none" + DEBUG = "debug" + STRICT_RANGE_CHECK = "strict_range_check" + OUTPUT = "output" + VERBOSE = "verbose" + SAVE_TEMPS = "save_temps" + ARGUMENT = "argument" + PATH_SELECTOR = "path_selector" + OVERRIDES = "overrides" + LIST_OPTION_MODE = "list_option_mode" + + +class KCLTopLevelArgumentAction(_argparse.Action): + """KCL CLI top level argument argparse action""" + + def __call__(self, parser, namespace, values, option_string=None): + invalid_arg_msg = 'Invalid value for option "--argument(-D)"' + split_values = values.split("=") + _keys, _vals = [], [] + if len(split_values) == 2 and str(split_values[1]).strip() != "": + try: + _deal_key_value(split_values, _keys, _vals) + except Exception as err: + err_msg = err.arg_msg if isinstance(err, kcl.KCLException) else err + kcl.report_exception( + err_type=kcl.ErrType.IllegalArgumentError_TYPE, + arg_msg=f"{invalid_arg_msg}: {err_msg}", + ) + else: + kcl.report_exception( + err_type=kcl.ErrType.IllegalArgumentError_TYPE, + arg_msg=f"{invalid_arg_msg}: should be in = pattern, got: {values}", + ) + args = getattr(namespace, self.dest) + args = [] if args is None else deepcopy(args) + args += [(_k, _v) for _k, _v in zip(_keys, _vals)] + setattr(namespace, self.dest, args) + + +class KCLPathSelectorAction(_argparse.Action): + """KCL path selector action""" + + def __call__(self, parser, namespace, values, option_string=None): + def report_exception(): + kcl.report_exception( + err_type=kcl.ErrType.IllegalArgumentError_TYPE, + arg_msg=f"Invalid value for option \"--path-selector(-S)\": '{values}'. path selector should be in : pattern", + ) + + split_values = values.split(":", 1) + if len(split_values) > 2: + report_exception() + split_values = split_values if len(split_values) == 2 else ["", *split_values] + args = getattr(namespace, self.dest) + args = [] if args is None else deepcopy(args) + args += [split_values] + setattr(namespace, self.dest, args) + + +class KCLOverrideAction(_argparse.Action): + """KCL path override action""" + + def __call__(self, parser, namespace, values, option_string=None): + def report_exception(): + kcl.report_exception( + err_type=kcl.ErrType.IllegalArgumentError_TYPE, + arg_msg=f"Invalid value for option \"--override(-O)\": '{values}'. the override expr should be in = pattern", + ) + + args = getattr(namespace, self.dest) + if "=" in values: + split_values = values.split("=", 1) + paths = split_values[0].split(":", 1) + if len(split_values) < 2 or len(paths) > 2: + report_exception() + paths.append(split_values[1]) + args = [] if args is None else deepcopy(args) + paths = paths if len(paths) == 3 else ["", *paths] + paths += [ast.OverrideAction.CREATE_OR_UPDATE] + args += [paths] + elif values.endswith("-"): + paths = values[:-1].split(":", 1) + if len(paths) > 2: + report_exception() + paths = paths if len(paths) == 2 else ["", *paths] + paths += ["", ast.OverrideAction.DELETE] + args += [paths] + setattr(namespace, self.dest, args) + + +class KCLCLISettingAction: + """KCL CLI top level setting argparse action""" + + DEFAULT_SETTING_PATH = "kcl.yaml" + OPTION_FILE_TYPES = [".yaml", ".yml"] + KCL_OPTION_KEY = "kcl_options" + KCL_CLI_CONFIG_KEY = "kcl_cli_configs" + + ARGUMENTS_ITEM_KEY = "key" + ARGUMENTS_ITEM_VALUE = "value" + + KCL_OPTION_EXAMPLE = """ +kcl_options: + - key: myArg # the option key must be a string value + value: myArgValue""" + INVALID_OPTION_MSG = f"invalid kcl_options value, should be list of key/value mapping. \n=== A good example will be:==={KCL_OPTION_EXAMPLE}" + + def deal_config_obj(self, data: dict, keys: list, vals: list) -> bool: + if data is None or not isinstance(data, dict): + kcl.report_exception( + err_type=kcl.ErrType.IllegalArgumentError_TYPE, + arg_msg=f"setting file content should be a mapping, got: {_object_to_yaml_str(data)}", + ) + # Deal Arguments + kcl_options_mapping_list = data.get(self.KCL_OPTION_KEY) or [] + if not isinstance(kcl_options_mapping_list, list): + kcl.report_exception( + err_type=kcl.ErrType.IllegalArgumentError_TYPE, + arg_msg=f"{self.INVALID_OPTION_MSG}\n=== got: ===\n{_object_to_yaml_str({self.KCL_OPTION_KEY: kcl_options_mapping_list})}", + ) + for key_value in kcl_options_mapping_list: + if key_value is None or not isinstance(key_value, dict): + kcl.report_exception( + err_type=kcl.ErrType.IllegalArgumentError_TYPE, + arg_msg=f"{self.INVALID_OPTION_MSG}\n=== got: ===\n{_object_to_yaml_str({self.KCL_OPTION_KEY: [key_value]})}", + ) + _deal_key_value( + ( + key_value.get(self.ARGUMENTS_ITEM_KEY), + key_value.get(self.ARGUMENTS_ITEM_VALUE), + ), + keys, + vals, + ) + # Deal KCL CLI parameters + data = data.get(self.KCL_CLI_CONFIG_KEY, {}) + parse_config(data) + return True + + def deal_setting_file(self, filename, keys, vals): + data = None + if any(filename.endswith(t) for t in self.OPTION_FILE_TYPES): + try: + data = _yaml.safe_load(Path(filename).read_text(encoding="utf-8")) + except Exception as err: + err_msg = err.arg_msg if isinstance(err, kcl.KCLException) else err + kcl.report_exception( + err_type=kcl.ErrType.IllegalArgumentError_TYPE, + file_msgs=[kcl.ErrFileMsg(filename=filename)], + arg_msg=f"Invalid yaml content of setting file:\n{err_msg}", + ) + try: + self.deal_config_obj(data or {}, keys, vals) + except Exception as err: + err_msg = err.arg_msg if isinstance(err, kcl.KCLException) else err + kcl.report_exception( + err_type=kcl.ErrType.IllegalArgumentError_TYPE, + file_msgs=[kcl.ErrFileMsg(filename=filename)], + arg_msg=f"Invalid configuration in setting file:\n{err_msg}", + ) + else: + kcl.report_exception( + err_type=kcl.ErrType.IllegalArgumentError_TYPE, + file_msgs=[kcl.ErrFileMsg(filename=filename)], + arg_msg='Invalid value for option "--setting(-Y)": the setting file should be in yaml format', + ) + + def deal(self, filenames: List[str]) -> list: + if not filenames and not Path(self.DEFAULT_SETTING_PATH).exists(): + return [] + args = [] + for filename in filenames or [self.DEFAULT_SETTING_PATH]: + _keys, _vals = [], [] + self.deal_setting_file(filename, _keys, _vals) + args += [(_k, _v) for _k, _v in zip(_keys, _vals)] + return args + + +def _object_to_yaml_str(obj, options=None): + if not isinstance(obj, (list, dict)): + return obj + yaml_config = _yaml.YAML() + yaml_config.indent(mapping=3, sequence=2, offset=0) + yaml_config.allow_duplicate_keys = True + + # show null + def my_represent_none(self, data): + return self.represent_scalar("tag:yaml.org,2002:null", "null") + + yaml_config.representer.add_representer(type(None), my_represent_none) + + # yaml to string + if options is None: + options = {} + string_stream = StringIO() + yaml_config.dump(obj, string_stream, **options) + output_str = string_stream.getvalue() + string_stream.close() + return output_str diff --git a/internal/kclvm_py/config/modfile_pb2.py b/internal/kclvm_py/config/modfile_pb2.py new file mode 100644 index 000000000..8a04f775e --- /dev/null +++ b/internal/kclvm_py/config/modfile_pb2.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: modfile/modfile.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15modfile/modfile.proto\x12\rkclvm.modfile\"\xa2\x01\n\nKclModFile\x12\x0c\n\x04root\x18\x01 \x01(\t\x12\x10\n\x08root_pkg\x18\x02 \x01(\t\x12\x36\n\x05\x62uild\x18\x03 \x01(\x0b\x32\'.kclvm.modfile.KclModFile_build_section\x12<\n\x08\x65xpected\x18\x04 \x01(\x0b\x32*.kclvm.modfile.KclModFile_expected_section\"_\n\x18KclModFile_build_section\x12\x18\n\x10\x65nable_pkg_cache\x18\x01 \x01(\x08\x12\x19\n\x11\x63\x61\x63hed_pkg_prefix\x18\x02 \x01(\t\x12\x0e\n\x06target\x18\x03 \x01(\t\"\x98\x01\n\x1bKclModFile_expected_section\x12\x16\n\x0emin_build_time\x18\x01 \x01(\t\x12\x16\n\x0emax_build_time\x18\x02 \x01(\t\x12\x15\n\rkclvm_version\x18\x03 \x01(\t\x12\x1a\n\x12kcl_plugin_version\x18\x04 \x01(\t\x12\x16\n\x0eglobal_version\x18\x05 \x01(\tb\x06proto3') + + + +_KCLMODFILE = DESCRIPTOR.message_types_by_name['KclModFile'] +_KCLMODFILE_BUILD_SECTION = DESCRIPTOR.message_types_by_name['KclModFile_build_section'] +_KCLMODFILE_EXPECTED_SECTION = DESCRIPTOR.message_types_by_name['KclModFile_expected_section'] +KclModFile = _reflection.GeneratedProtocolMessageType('KclModFile', (_message.Message,), { + 'DESCRIPTOR' : _KCLMODFILE, + '__module__' : 'modfile.modfile_pb2' + # @@protoc_insertion_point(class_scope:kclvm.modfile.KclModFile) + }) +_sym_db.RegisterMessage(KclModFile) + +KclModFile_build_section = _reflection.GeneratedProtocolMessageType('KclModFile_build_section', (_message.Message,), { + 'DESCRIPTOR' : _KCLMODFILE_BUILD_SECTION, + '__module__' : 'modfile.modfile_pb2' + # @@protoc_insertion_point(class_scope:kclvm.modfile.KclModFile_build_section) + }) +_sym_db.RegisterMessage(KclModFile_build_section) + +KclModFile_expected_section = _reflection.GeneratedProtocolMessageType('KclModFile_expected_section', (_message.Message,), { + 'DESCRIPTOR' : _KCLMODFILE_EXPECTED_SECTION, + '__module__' : 'modfile.modfile_pb2' + # @@protoc_insertion_point(class_scope:kclvm.modfile.KclModFile_expected_section) + }) +_sym_db.RegisterMessage(KclModFile_expected_section) + +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _KCLMODFILE._serialized_start=41 + _KCLMODFILE._serialized_end=203 + _KCLMODFILE_BUILD_SECTION._serialized_start=205 + _KCLMODFILE_BUILD_SECTION._serialized_end=300 + _KCLMODFILE_EXPECTED_SECTION._serialized_start=303 + _KCLMODFILE_EXPECTED_SECTION._serialized_end=455 +# @@protoc_insertion_point(module_scope) diff --git a/internal/kclvm_py/config/settings.py b/internal/kclvm_py/config/settings.py new file mode 100644 index 000000000..904e2e342 --- /dev/null +++ b/internal/kclvm_py/config/settings.py @@ -0,0 +1,70 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import pathlib +from typing import List + +import kclvm.config +import kclvm.internal.util as util +import kclvm.internal.gpyrpc.gpyrpc_pb2 as pb2 + + +KCL_MOD_PATH_ENV = "${KCL_MOD}" + + +def load_settings_files( + work_dir: str, files: List[str] +) -> pb2.LoadSettingsFiles_Result: + """Load KCL CLI config from the setting files. + + Parameter + --------- + work_dir : str + The kcl run work directory. + files: + The setting YAML files. + + Returns + ------- + result: LoadSettingsFiles_Result + The merged kcl singleton config. + """ + from kclvm.compiler.vfs import GetPkgRoot + + if not files: + return pb2.LoadSettingsFiles_Result( + kcl_cli_configs=pb2.CliConfig(), kcl_options=[] + ) + key_value_pairs = [ + pb2.KeyValuePair(key=k, value=v) + for k, v in util.merge_option_same_keys( + kclvm.config.KCLCLISettingAction().deal(files) + ).items() + ] + if work_dir or kclvm.config.current_path: + files = [ + str( + pathlib.Path(work_dir) + .joinpath( + str(x).replace( + KCL_MOD_PATH_ENV, + GetPkgRoot(work_dir or kclvm.config.current_path or files[0]) + or "", + ) + ) + .resolve() + ) + for x in kclvm.config.input_file + ] + return pb2.LoadSettingsFiles_Result( + kcl_cli_configs=pb2.CliConfig( + files=files, + output=kclvm.config.output, + overrides=kclvm.config.overrides, + path_selector=kclvm.config.path_selector, + strict_range_check=kclvm.config.strict_range_check, + disable_none=kclvm.config.disable_none, + verbose=kclvm.config.verbose, + debug=kclvm.config.debug, + ), + kcl_options=key_value_pairs, + ) diff --git a/internal/kclvm_py/encoding/__init__.py b/internal/kclvm_py/encoding/__init__.py new file mode 100644 index 000000000..d74aaf8ad --- /dev/null +++ b/internal/kclvm_py/encoding/__init__.py @@ -0,0 +1 @@ +# Copyright 2021 The KCL Authors. All rights reserved. diff --git a/internal/kclvm_py/encoding/protobuf/__init__.py b/internal/kclvm_py/encoding/protobuf/__init__.py new file mode 100644 index 000000000..ead1b79e0 --- /dev/null +++ b/internal/kclvm_py/encoding/protobuf/__init__.py @@ -0,0 +1,96 @@ +"""The `protobuf` module defines functionality for parsing protocol buffer +definitions and instances. + +Proto definition mapping follows the guidelines of mapping Proto to JSON as +discussed in https://developers.google.com/protocol-buffers/docs/proto3, and +carries some of the mapping further when possible with KCL. + +The following type mappings of definitions apply: + + Proto type KCL type/def Comments + message schema Message fields become KCL attribute, whereby + names are mapped to lowerCamelCase. + enum e1 | e2 | ... Where ex are strings. A separate mapping is + generated to obtain the numeric values. + oneof e1 | e2 | ... KCL union type. + map {K:V} + repeated V [V] + bool bool + string str + bytes str A base64-encoded string when converted to JSON. + int32, fixed32 int An integer with bounds as defined by int. + uint32 int An integer with bounds as defined by int. + int64, fixed64 int An integer with bounds as defined by int. + uint64 int An integer with bounds as defined by int. + float float A number with bounds as defined by float. + double float A number with bounds as defined by float. + Struct schema + Value any + google.proto.Any any + ListValue [...] + NullValue any + BoolValue bool + StringValue str + NumberValue float + StringValue str + Empty any + Timestamp float + Duration int + +Protobuf definitions can be annotated with KCL constraints that are included +in the generated KCL: + (kcl.opt) FieldOptions + val str KCL attribute default value. + optional bool Whether the KCL attribute is optional + final bool Whether the KCL attribute is final + +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" + +from .parser import ( + Import, + Package, + Option, + Field, + OneOfField, + OneOf, + Map, + Reserved, + Range, + EnumField, + Enum, + Message, + Service, + Rpc, + Proto, + ImportOption, + Type, + KeyType, + parse_code, +) +from .protobuf import ( + protobuf_to_kcl, +) + +__all__ = [ + "Import", + "Package", + "Option", + "Field", + "OneOfField", + "OneOf", + "Map", + "Reserved", + "Range", + "EnumField", + "Enum", + "Message", + "Service", + "Rpc", + "Proto", + "ImportOption", + "Type", + "KeyType", + "parse_code", + "protobuf_to_kcl", +] diff --git a/internal/kclvm_py/encoding/protobuf/kcl.proto b/internal/kclvm_py/encoding/protobuf/kcl.proto new file mode 100644 index 000000000..10b4ece1a --- /dev/null +++ b/internal/kclvm_py/encoding/protobuf/kcl.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package kcl; + +import "google/protobuf/descriptor.proto"; + +message FieldOptions { + string val = 1; + bool optional = 2; + bool final = 3; +} + +extend google.protobuf.FieldOptions { + FieldOptions opt = 1; +} diff --git a/internal/kclvm_py/encoding/protobuf/parser.py b/internal/kclvm_py/encoding/protobuf/parser.py new file mode 100644 index 000000000..afc340687 --- /dev/null +++ b/internal/kclvm_py/encoding/protobuf/parser.py @@ -0,0 +1,477 @@ +# -*- coding: utf-8 -*- + +# Parser for protocol buffer .proto files +# Thanks to https://github.com/python-parsy/parsy/blob/master/examples/proto3.py +import enum as stdlib_enum +from string import ascii_letters, digits, hexdigits, octdigits + +import attr + +from parsy import char_from, from_enum, generate, regex, seq, string + +# This file follows the spec at +# https://developers.google.com/protocol-buffers/docs/reference/proto3-spec +# very closely. + +# However, because we are parsing into useful objects, we do transformations +# along the way e.g. turning into integers, strings etc. and custom objects. +# Some of the lowest level items have been implemented using 'regex' and converting +# the descriptions to regular expressions. Higher level constructs have been +# implemented using other parsy primitives and combinators. + +# Notes: + +# 1. Whitespace is very badly defined in the 'spec', so we guess what is meant. +# 2. The spec doesn't allow for comments, and neither does this parser. +# Other places mention that C++ style comments are allowed. To support that, +# this parser would need to be changed into split lexing/parsing stages +# (otherwise you hit issues with comments start markers within string literals). +# 3. Other notes inline. + + +# Our utilities +def optional_string(s): + return string(s).times(0, 1).concat() + + +def convert_octal(s): + return int(s, 8) + + +def convert_hex(s): + return int(s, 16) + + +def exclude_none(data): + return [i for i in data if i is not None] + + +convert_decimal = int + + +def lexeme(p): + """ + From a parser (or string), make a parser that consumes + whitespace on either side. + """ + if isinstance(p, str): + p = string(p) + return regex(r"\s*") >> p << regex(r"\s*") + + +def is_present(p): + """ + Given a parser or string, make a parser that returns + True if the parser matches, False otherwise + """ + return lexeme(p).optional().map(lambda v: False if v is None else True) + + +# Our data structures +@attr.s +class Import: + identifier = attr.ib() + option = attr.ib() + + +@attr.s +class Package: + identifier = attr.ib() + + +@attr.s +class Option: + name = attr.ib() + value = attr.ib() + + +@attr.s +class Field: + repeated = attr.ib() + type = attr.ib() + name = attr.ib() + number = attr.ib() + options = attr.ib() + + +@attr.s +class OneOfField: + type = attr.ib() + name = attr.ib() + number = attr.ib() + options = attr.ib() + + +@attr.s +class OneOf: + name = attr.ib() + fields = attr.ib() + + +@attr.s +class Map: + key_type = attr.ib() + type = attr.ib() + name = attr.ib() + number = attr.ib() + options = attr.ib() + + +@attr.s +class Reserved: + items = attr.ib() + + +@attr.s +class Range: + from_ = attr.ib() + to = attr.ib() + + +@attr.s +class EnumField: + name = attr.ib() + value = attr.ib() + options = attr.ib() + + +@attr.s +class Enum: + name = attr.ib() + body = attr.ib() + + +@attr.s +class Message: + name = attr.ib() + body = attr.ib() + + +@attr.s +class Service: + name = attr.ib() + body = attr.ib() + + +@attr.s +class Rpc: + name = attr.ib() + request_stream = attr.ib() + request_message_type = attr.ib() + response_stream = attr.ib() + response_message_type = attr.ib() + options = attr.ib() + + +@attr.s +class Proto: + syntax = attr.ib() + statements = attr.ib() + + +# Enums: +class ImportOption(stdlib_enum.Enum): + WEAK = "weak" + PUBLIC = "public" + + +class Type(stdlib_enum.Enum): + DOUBLE = "double" + FLOAT = "float" + INT32 = "int32" + INT64 = "int64" + UINT32 = "uint32" + UINT64 = "uint64" + SINT32 = "sint32" + SINT64 = "sint64" + FIXED32 = "fixed32" + FIXED64 = "fixed64" + SFIXED32 = "sfixed32" + SFIXED64 = "sfixed64" + BOOL = "bool" + STRING = "string" + BYTES = "bytes" + + +class KeyType(stdlib_enum.Enum): + INT32 = "int32" + INT64 = "int64" + UINT32 = "uint32" + UINT64 = "uint64" + SINT32 = "sint32" + SINT64 = "sint64" + FIXED32 = "fixed32" + FIXED64 = "fixed64" + SFIXED32 = "sfixed32" + SFIXED64 = "sfixed64" + BOOL = "bool" + STRING = "string" + + +# Some extra constants to avoid typing +SEMI = lexeme(";") +EQ = lexeme("=") +LPAREN = lexeme("(") +RPAREN = lexeme(")") +LBRACE = lexeme("{") +RBRACE = lexeme("}") + +# -- Beginning of following spec -- +# Letters and digits +letter = char_from(ascii_letters) +decimalDigit = char_from(digits) +octalDigit = char_from(octdigits) +hexDigit = char_from(hexdigits) + +# Identifiers + +# Compared to spec, we add some '_' prefixed items which are not wrapped in `lexeme`, +# on the assumption that spaces in the middle of identifiers are not accepted. +_ident = (letter + (letter | decimalDigit | string("_")).many().concat()).desc("ident") +ident = lexeme(_ident) +fullIdent = lexeme(ident + (string(".") + ident).many().concat()).desc("fullIdent") +_messageName = _ident +messageName = lexeme(ident).desc("messageName") +_enumName = ident +enumName = lexeme(_enumName).desc("enumName") +fieldName = ident.desc("fieldName") +oneofName = ident.desc("oneofName") +mapName = ident.desc("mapName") +serviceName = ident.desc("serviceName") +rpcName = ident.desc("rpcName") +messageType = ( + optional_string(".") + (_ident + string(".")).many().concat() + _messageName +) +enumType = optional_string(".") + (_ident + string(".")).many().concat() + _enumName + +# Integer literals +decimalLit = regex("[1-9][0-9]*").desc("decimalLit").map(convert_decimal) +octalLit = regex("0[0-7]*").desc("octalLit").map(convert_octal) +hexLit = regex("0[x|X][0-9a-fA-F]+").desc("octalLit").map(convert_hex) +intLit = decimalLit | octalLit | hexLit + + +# Floating-point literals +decimals = r"[0-9]+" +exponent = r"[e|E][+|-]?" + decimals +floatLit = ( + regex( + r"({decimals}\.({decimals})?({exponent})?)|{decimals}{exponent}|\.{decimals}({exponent})?".format( + decimals=decimals, exponent=exponent + ) + ) + .desc("floatLit") + .map(float) +) + + +# Boolean +boolLit = (string("true").result(True) | string("false").result(False)).desc("boolLit") + + +# String literals +hexEscape = regex(r"\\[x|X]") >> regex("[0-9a-fA-F]{2}").map(convert_hex).map(chr) +octEscape = regex(r"\\") >> regex("[0-7]{2}").map(convert_octal).map(chr) +charEscape = regex(r"\\") >> ( + string("a").result("\a") + | string("b").result("\b") + | string("f").result("\f") + | string("n").result("\n") + | string("r").result("\r") + | string("t").result("\t") + | string("v").result("\v") + | string("\\").result("\\") + | string("'").result("'") + | string('"').result('"') +) +escapes = hexEscape | octEscape | charEscape +# Correction to spec regarding " and ' inside quoted strings +strLit = ( + string("'") >> (escapes | regex(r"[^\0\n\'\\]")).many().concat() << string("'") + | string('"') >> (escapes | regex(r"[^\0\n\"\\]")).many().concat() << string('"') +).desc("strLit") +quote = string("'") | string('"') + +# EmptyStatement +emptyStatement = string(";").result(None) + + +# Signed numbers: +def signedNumberChange(s, num): + """(Extra compared to spec, to cope with need to produce signed numeric values)""" + return (-1) if s == "-" else (+1) + + +sign = regex("[-+]?") +signedIntLit = seq(sign, intLit).combine(signedNumberChange) +signedFloatLit = seq(sign, floatLit).combine(signedNumberChange) + + +# Constant +# put fullIdent at end to disabmiguate from boolLit +constant = signedIntLit | signedFloatLit | strLit | boolLit | fullIdent + +# Syntax +syntax = lexeme("syntax") >> EQ >> quote >> string("proto3") << quote + SEMI + +# Import Statement +import_option = from_enum(ImportOption) + +import_ = seq( + lexeme("import") >> import_option.optional().tag("option"), + lexeme(strLit).tag("identifier") << SEMI, +).combine_dict(Import) + +# Package +package = seq(lexeme("package") >> fullIdent << SEMI).map(Package) + +# Option +optionName = (ident | (LPAREN >> fullIdent << RPAREN)) + ( + string(".") + ident +).many().concat() +option = seq( + lexeme("option") >> optionName.tag("name"), + EQ >> constant.tag("value") << SEMI, +).combine_dict(Option) + +# Normal field +type_ = lexeme(from_enum(Type) | messageType | enumType) +fieldNumber = lexeme(intLit) + +fieldOption = seq(optionName.tag("name"), EQ >> constant.tag("value")).combine_dict( + Option +) +fieldOptions = fieldOption.sep_by(lexeme(","), min=1) +fieldOptionList = ( + (lexeme("[") >> fieldOptions << lexeme("]")) + .optional() + .map(lambda o: [] if o is None else o) +) + +field = seq( + is_present("repeated").tag("repeated"), + type_.tag("type"), + fieldName.tag("name") << EQ, + fieldNumber.tag("number"), + fieldOptionList.tag("options") << SEMI, +).combine_dict(Field) + + +# Oneof and oneof field +oneofField = seq( + type_.tag("type"), + fieldName.tag("name") << EQ, + fieldNumber.tag("number"), + fieldOptionList.tag("options") << SEMI, +).combine_dict(OneOfField) +oneof = seq( + lexeme("oneof") >> oneofName.tag("name"), + LBRACE + >> (oneofField | emptyStatement).many().map(exclude_none).tag("fields") + << RBRACE, +).combine_dict(OneOf) + +# Map field +keyType = lexeme(from_enum(KeyType)) +mapField = seq( + lexeme("map") >> lexeme("<") >> keyType.tag("key_type"), + lexeme(",") >> type_.tag("type"), + lexeme(">") >> mapName.tag("name"), + EQ >> fieldNumber.tag("number"), + fieldOptionList.tag("options") << SEMI, +).combine_dict(Map) + +# Reserved +range_ = seq( + lexeme(intLit).tag("from_"), + (lexeme("to") >> (intLit | lexeme("max"))).optional().tag("to"), +).combine_dict(Range) +ranges = range_.sep_by(lexeme(","), min=1) +# The spec for 'reserved' indicates 'fieldName' here, which is never a quoted string. +# But the example has a quoted string. We have changed it to 'strLit' +fieldNames = strLit.sep_by(lexeme(","), min=1) +reserved = seq(lexeme("reserved") >> (ranges | fieldNames) << SEMI).combine(Reserved) + +# Enum definition +enumValueOption = seq(optionName.tag("name") << EQ, constant.tag("value")).combine_dict( + Option +) +enumField = seq( + ident.tag("name") << EQ, + lexeme(intLit).tag("value"), + (lexeme("[") >> enumValueOption.sep_by(lexeme(","), min=1) << lexeme("]")) + .optional() + .map(lambda o: [] if o is None else o) + .tag("options") + << SEMI, +).combine_dict(EnumField) +enumBody = ( + LBRACE >> (option | enumField | emptyStatement).many().map(exclude_none) << RBRACE +) +enum = seq(lexeme("enum") >> enumName.tag("name"), enumBody.tag("body")).combine_dict( + Enum +) + + +# Message definition +@generate +def message(): + yield lexeme("message") + name = yield messageName + body = yield messageBody + return Message(name=name, body=body) + + +messageBody = ( + LBRACE + >> ( + field | enum | message | option | oneof | mapField | reserved | emptyStatement + ).many() + << RBRACE +) + + +# Service definition +rpc = seq( + lexeme("rpc") >> rpcName.tag("name"), + LPAREN >> (is_present("stream").tag("request_stream")), + messageType.tag("request_message_type") << RPAREN, + lexeme("returns") >> LPAREN >> (is_present("stream").tag("response_stream")), + messageType.tag("response_message_type") << RPAREN, + ((LBRACE >> (option | emptyStatement).many() << RBRACE) | SEMI.result([])) + .optional() + .map(exclude_none) + .tag("options"), +).combine_dict(Rpc) + +service = seq( + lexeme("service") >> serviceName.tag("name"), + LBRACE + >> (option | rpc | emptyStatement).many().map(exclude_none).tag("body") + << RBRACE, +).combine_dict(Service) + + +# Proto file +topLevelDef = message | enum | service +proto = seq( + syntax.tag("syntax"), + (import_ | package | option | topLevelDef | emptyStatement) + .many() + .map(exclude_none) + .tag("statements"), +).combine_dict(Proto) + + +def parse_code(code: str) -> Proto: + """Parse a proto code string + + Parameters + ---------- + code : str. The proto code string. + + Returns + ------- + result : Proto. The proto node structure. + """ + return proto.parse(code) diff --git a/internal/kclvm_py/encoding/protobuf/printer.py b/internal/kclvm_py/encoding/protobuf/printer.py new file mode 100644 index 000000000..ca911d22d --- /dev/null +++ b/internal/kclvm_py/encoding/protobuf/printer.py @@ -0,0 +1,393 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import io +from typing import List +from dataclasses import dataclass + +import kclvm.kcl.ast as ast + +from .token import TokenValue +from .parser import ( + Import, + Package, + Option, + Field, + OneOfField, + OneOf, + Map, + Reserved, + Range, + EnumField, + Enum, + Message, + Service, + Rpc, + Proto, + ImportOption, + Type, + KeyType, +) + + +PROTO_NODE_TUPLE = ( + Import, + Package, + Option, + Field, + OneOfField, + OneOf, + Map, + Reserved, + Range, + EnumField, + Enum, + Message, + Service, + Rpc, + Proto, + ImportOption, + Type, + KeyType, +) + +_INVALID_NODE_MSG = "Invalid proto node" +WHITESPACE = " " +TAB = "\t" +NEWLINE = "\n" + + +# --------------------------------------------------- +# Printer config +# --------------------------------------------------- + + +@dataclass +class Config: + tab_len: int = 4 + indent_len: int = 4 + use_spaces: bool = True + + +class BasePrinter(ast.TreeWalker): + def __init__(self, config: Config, out: io.TextIOBase): + self.output: str = "" + self.config: Config = config + self.out: io.TextIOBase = out + self.indent: int = 0 + + # Base walker functions + + def get_node_name(self, t): + """Get the ast.AST node name""" + return type(t).__name__ + + def enter(self): + self.indent += 1 + + def leave(self): + self.indent -= 1 + + # --------------- + # Write functions + # --------------- + + @staticmethod + def interleave(inter, f, seq): + """Call f on each item in seq, calling inter() in between.""" + if not seq: + return + seq = iter(seq) + f(next(seq)) + for x in seq: + inter() + f(x) + + def write(self, text: str = ""): + self.out.write(text) + + def fill(self): + self.write( + (self.indent * self.config.indent_len * WHITESPACE) + if self.config.use_spaces + else (TAB * self.indent) + ) + + def print(self, *values): + for value in values or []: + if isinstance(value, PROTO_NODE_TUPLE): + self.walk(value) + elif value is True: + self.write("true") + elif value is False: + self.write("false") + else: + self.write(str(value)) + + +class Printer(BasePrinter): + def __init__(self, config: Config, out: io.TextIOBase): + super().__init__(config, out) + + def walk_Proto(self, t: Proto): + self.print( + TokenValue.SYNTAX, + WHITESPACE, + TokenValue.EQ, + WHITESPACE, + f'"{t.syntax}"', + TokenValue.SEMI, + NEWLINE, + ) + for node in t.statements or []: + self.walk(node) + + def walk_Import(self, t: Import): + self.print( + TokenValue.IMPORT, + WHITESPACE, + t.option.value, + WHITESPACE, + f'"{t.identifier}"', + TokenValue.SEMI, + NEWLINE, + ) + + def walk_Package(self, t: Package): + self.print( + TokenValue.PACKAGE, + WHITESPACE, + ) + self.interleave( + lambda: self.print(TokenValue.COMMA), lambda n: self.print(n), t.identifier + ) + self.print( + TokenValue.SEMI, + NEWLINE, + ) + + def walk_Option(self, t: Option): + self.fill() + self.print( + TokenValue.OPTION, + WHITESPACE, + t.name, + WHITESPACE, + TokenValue.EQ, + WHITESPACE, + f'"{t.value}"' if isinstance(t.value, str) else t.value, + TokenValue.SEMI, + NEWLINE, + ) + + def walk_Enum(self, t: Enum): + self.write(NEWLINE) + self.fill() + self.print( + TokenValue.ENUM, + WHITESPACE, + t.name, + WHITESPACE, + TokenValue.LBRACE, + NEWLINE, + ) + self.enter() + for node in t.body: + self.walk(node) + self.leave() + self.fill() + self.print(TokenValue.RBRACE) + self.write(NEWLINE) + + def walk_EnumField(self, t: EnumField): + self.fill() + self.print( + t.name, + WHITESPACE, + TokenValue.EQ, + WHITESPACE, + t.value, + ) + self.write_field_options(t.options) + self.write(TokenValue.SEMI) + self.write(NEWLINE) + + def walk_Message(self, t: Message): + self.write(NEWLINE) + self.fill() + self.print( + TokenValue.MESSAGE, + WHITESPACE, + t.name, + WHITESPACE, + TokenValue.LBRACE, + NEWLINE, + ) + self.enter() + for node in t.body: + self.walk(node) + self.leave() + self.fill() + self.print(TokenValue.RBRACE) + self.write(NEWLINE) + + def walk_Map(self, t: Map): + self.fill() + self.print( + TokenValue.MAP, + TokenValue.LANGLE_BRACK, + t.key_type, + TokenValue.COMMA, + WHITESPACE, + t.type, + TokenValue.RANGLE_BRACK, + WHITESPACE, + t.name, + WHITESPACE, + TokenValue.EQ, + WHITESPACE, + t.number, + ) + self.write_field_options(t.options) + self.write(TokenValue.SEMI) + self.write(NEWLINE) + + def walk_Field(self, t: Field): + self.fill() + if t.repeated: + self.print( + TokenValue.REPEATED, + WHITESPACE, + ) + self.print( + t.type, WHITESPACE, t.name, WHITESPACE, TokenValue.EQ, WHITESPACE, t.number + ) + self.write_field_options(t.options) + self.write(TokenValue.SEMI) + self.write(NEWLINE) + + def walk_OneOf(self, t: OneOf): + self.fill() + self.print( + TokenValue.ONEOF, + WHITESPACE, + t.name, + WHITESPACE, + TokenValue.LBRACE, + NEWLINE, + ) + self.enter() + for node in t.fields: + self.walk(node) + self.leave() + self.fill() + self.print(TokenValue.RBRACE) + self.write(NEWLINE) + + def walk_OneOfField(self, t: OneOfField): + self.fill() + self.print( + t.type, WHITESPACE, t.name, WHITESPACE, TokenValue.EQ, WHITESPACE, t.number + ) + self.write_field_options(t.options) + self.write(TokenValue.SEMI) + self.write(NEWLINE) + + def walk_Service(self, t: Service): + self.write(NEWLINE) + self.fill() + self.print( + TokenValue.SERVICE, + WHITESPACE, + t.name, + WHITESPACE, + TokenValue.LBRACE, + NEWLINE, + ) + self.enter() + for node in t.body: + self.walk(node) + self.leave() + self.fill() + self.print(TokenValue.RBRACE) + self.write(NEWLINE) + + def walk_Rpc(self, t: Rpc): + self.fill() + self.print( + TokenValue.RPC, + WHITESPACE, + t.name, + TokenValue.LPAREN, + t.request_message_type, + TokenValue.RPAREN, + WHITESPACE, + TokenValue.RETURNS, + WHITESPACE, + TokenValue.LPAREN, + t.response_message_type, + TokenValue.RPAREN, + ) + self.write_field_options(t.options) + self.write(TokenValue.SEMI) + self.write(NEWLINE) + + def walk_Reserved(self, t: Reserved): + self.fill() + self.print( + TokenValue().RESERVED, + WHITESPACE, + ) + self.interleave( + lambda: self.write(TokenValue.COMMA + WHITESPACE), + lambda n: self.print(f'"{n}"' if isinstance(n, str) else n), + t.items, + ) + self.write(TokenValue.SEMI) + self.write(NEWLINE) + + def walk_Range(self, t: Range): + self.print(f'"{t.from_}"' if isinstance(t.from_, str) else t.from_) + if t.to: + self.print( + WHITESPACE, + TokenValue.TO, + WHITESPACE, + f'"{t.to}"' if isinstance(t.to, str) else t.to, + ) + + def walk_Type(self, t: Type): + self.print(t.value) + + def walk_KeyType(self, t: KeyType): + self.print(t.value) + + def write_field_options(self, options: List[Option]): + def write_option(option: Option): + self.print( + TokenValue.LPAREN, + option.name, + TokenValue.RPAREN, + WHITESPACE, + TokenValue.EQ, + WHITESPACE, + f'"{option.value}"' if isinstance(option.value, str) else option.value, + ) + + if not options: + return + self.write(WHITESPACE) + self.write(TokenValue.LBRACK) + self.interleave( + lambda: self.write(TokenValue.COMMA + WHITESPACE), write_option, options + ) + self.write(TokenValue.RBRACK) + + +def print_node_to_string( + node, + config: Config = Config(), +) -> str: + """Print a proto node to string io with `config`""" + out = io.StringIO() + Printer(config, out).walk(node) + return out.getvalue() diff --git a/internal/kclvm_py/encoding/protobuf/protobuf.py b/internal/kclvm_py/encoding/protobuf/protobuf.py new file mode 100644 index 000000000..fe48fb303 --- /dev/null +++ b/internal/kclvm_py/encoding/protobuf/protobuf.py @@ -0,0 +1,249 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import cast, List, Union +from io import StringIO + +import kclvm.kcl.ast as ast +import kclvm.kcl.types as types +import kclvm.api.object as objpkg +import kclvm.tools.printer as printer +import kclvm.compiler.parser.parser as parser + +from .parser import ( + parse_code, + Proto, + Message, + Field, + Map, + Enum, + EnumField, + Type, + KeyType, + OneOf, + OneOfField, +) +from .types import ( + PROTOBUF_SCALAR_TYPE_TO_KCL_MAPPING, + KCL_SCALAR_TYPE_TO_PROTOBUF_TYPE_MAPPING, + KCL_SCALAR_TYPE_TO_PROTOBUF_KEY_TYPE_MAPPING, +) +from .printer import print_node_to_string + + +CODE_INPUT = "" +PROTO3_SYNTAX = "proto3" + + +def protobuf_to_kcl(proto_code: str) -> str: + """Covert a protobuf code string to a KCL code string. + + Parameters + ---------- + proto_code : str. The protobuf code string. + + Returns + ------- + kcl_code : str. The KCL code string. + """ + kcl_module = protobuf_to_kcl_ast(proto_code) + buf = StringIO() + printer.PrintAST(kcl_module, buf) + return buf.getvalue() + + +def kcl_to_protobuf(kcl_code: str) -> str: + """Covert a a KCL code string to a protobuf code string. + + Parameters + ---------- + kcl_code : str. The KCL code string. + + Returns + ------- + proto_code : str. The protobuf code string. + """ + ast_program = parser.LoadProgram(CODE_INPUT, k_code_list=[kcl_code]) + types.ResolveProgram(ast_program) + main_ast = ast_program.pkgs[ast.Program.MAIN_PKGPATH][0] + proto = Proto( + syntax=PROTO3_SYNTAX, + statements=[ + convert_schema_to_message(schema) for schema in main_ast.GetSchemaList() + ], + ) + return print_node_to_string(proto) + + +def protobuf_to_kcl_ast(proto_code: str) -> ast.Module: + """Covert a protobuf code string to a KCL code string. + + Parameters + ---------- + proto_code : str. The protobuf code string. + + Returns + ------- + kcl_ast : ast.Module. The KCL Module AST node. + """ + proto = parse_code(proto_code) + kcl_module = ast.Module(line=1, column=1) + for statement in proto.statements: + if isinstance(statement, Message): + message = cast(Message, statement) + kcl_module.body.extend(convert_message_to_schema_list(message)) + elif isinstance(statement, Enum): + enum = cast(Enum, statement) + kcl_module.body.append(convert_enum_to_type_alias(enum)) + # TODO: Import node on multi proto files + return kcl_module + + +def convert_proto_type_to_kcl_type(tpe: Union[Type, str]) -> str: + """Convert a proto type to a KCL type""" + return ( + PROTOBUF_SCALAR_TYPE_TO_KCL_MAPPING.get(tpe.value) + if isinstance(tpe, (Type, KeyType)) + else (tpe or "any") + ) + + +def convert_kcl_type_to_proto_type( + tpe: types.Type, is_proto_key_type: bool = False +) -> Union[str, KeyType, Type]: + """Convert a kcl type to a proto type""" + if isinstance(tpe, objpkg.KCLNamedTypeObject): + return tpe.name + elif isinstance( + tpe, + ( + objpkg.KCLIntTypeObject, + objpkg.KCLFloatTypeObject, + objpkg.KCLStringTypeObject, + objpkg.KCLBoolTypeObject, + ), + ): + return ( + KCL_SCALAR_TYPE_TO_PROTOBUF_KEY_TYPE_MAPPING.get(tpe.type_str()) + if is_proto_key_type + else KCL_SCALAR_TYPE_TO_PROTOBUF_TYPE_MAPPING.get(tpe.type_str()) + ) + raise ValueError(f"Unsupported KCL type {tpe} to proto type") + + +def convert_message_to_schema_list(message: Message) -> List[ast.Stmt]: + """Convert proto Message to KCL schema list because a + proto Message can be nested definition. + """ + if not message or not isinstance(message, Message): + raise ValueError(f"Invalid parameter message {message}, expected Message") + schema = ast.SchemaStmt() + # Mapping the message name to the schema name + schema.name = message.name + results = [] + results.append(schema) + for node in message.body: + schema_attr = ast.SchemaAttr() + if isinstance(node, Field): + field = cast(Field, node) + schema_attr.name = field.name + kcl_type_str = convert_proto_type_to_kcl_type(field.type) + schema_attr.type_str = ( + f"[{kcl_type_str}]" if field.repeated else kcl_type_str + ) + schema.body.append(schema_attr) + elif isinstance(node, Map): + field = cast(Map, node) + schema_attr.name = field.name + kcl_key_type_str = convert_proto_type_to_kcl_type(field.key_type) + kcl_value_type_str = convert_proto_type_to_kcl_type(field.type) + schema_attr.type_str = ( + "{" + kcl_key_type_str + ":" + kcl_value_type_str + "}" + ) + schema.body.append(schema_attr) + elif isinstance(node, OneOf): + field = cast(OneOf, node) + schema_attr.name = field.name + # OneOfField + schema_attr.type_str = " | ".join( + [convert_proto_type_to_kcl_type(field.type) for field in field.fields] + ) + schema.body.append(schema_attr) + elif isinstance(node, Message): + results.extend(convert_message_to_schema_list(node)) + return results + + +def convert_enum_to_type_alias(enum: Enum) -> ast.TypeAliasStmt: + """Convert a proto Enum to a KCL TypeAliasStmt""" + if not enum or not isinstance(enum, Enum): + raise ValueError(f"Invalid parameter Enum {enum}, expected Enum") + type_alias = ast.TypeAliasStmt() + type_alias.type_name = ast.ASTFactory.get_ast_identifier(enum.name) + values = [str(node.value) for node in enum.body if isinstance(node, EnumField)] + type_alias.type_value = ast.Type() + type_alias.type_value.plain_type_str = " | ".join(values) + return type_alias + + +def convert_schema_to_message(schema: ast.SchemaStmt) -> Message: + """Convert KCL schema to a proto Message""" + if not schema or not isinstance(schema, ast.SchemaStmt): + raise ValueError(f"Invalid parameter schema {schema}, expected ast.SchemaStmt") + return Message( + name=schema.name, + body=[ + convert_schema_attr_to_message_body(schema_attr, i + 1) + for i, schema_attr in enumerate(schema.body) + if isinstance(schema_attr, ast.SchemaAttr) + ], + ) + + +def convert_schema_attr_to_message_body( + schema_attr: ast.SchemaAttr, + number: int = 1, +) -> Union[Field, Map, OneOf]: + """Convert KCL schema attr to a proto Message body item""" + if not schema_attr or not isinstance(schema_attr, ast.SchemaAttr): + raise ValueError( + f"Invalid parameter schema {schema_attr}, expected ast.SchemaAttr" + ) + type_node = types.parse_type_str(schema_attr.type_str) + if isinstance(type_node, objpkg.KCLDictTypeObject): + field = Map( + name=schema_attr.name, + key_type=convert_kcl_type_to_proto_type(type_node.key_type, True), + type=convert_kcl_type_to_proto_type(type_node.value_type), + number=number, + options=[], + ) + elif isinstance(type_node, objpkg.KCLUnionTypeObject): + # Convert union type to one of + field = OneOf( + name=schema_attr.name, + fields=[], + ) + filed_name_prefix = schema_attr.name + for i, tpe in enumerate(type_node.types): + field.fields.append( + OneOfField( + name=filed_name_prefix + f"{i + 1}", + number=i + 1, + type=types.type_to_kcl_type_annotation_str(tpe), + options=[], + ) + ) + else: + field = Field( + name=schema_attr.name, + type="", + repeated=False, + options=[], + number=number, + ) + if isinstance(type_node, objpkg.KCLListTypeObject): + field.repeated = True + field.type = convert_kcl_type_to_proto_type(type_node.item_type) + else: + field.type = convert_kcl_type_to_proto_type(type_node) + return field diff --git a/internal/kclvm_py/encoding/protobuf/token.py b/internal/kclvm_py/encoding/protobuf/token.py new file mode 100644 index 000000000..5bcdb61a5 --- /dev/null +++ b/internal/kclvm_py/encoding/protobuf/token.py @@ -0,0 +1,29 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + + +class TokenValue: + SEMI = ";" + COMMA = "," + EQ = "=" + LPAREN = "(" + RPAREN = ")" + LBRACK = "[" + RBRACK = "]" + LBRACE = "{" + RBRACE = "}" + LANGLE_BRACK = "<" + RANGLE_BRACK = ">" + SYNTAX = "syntax" + IMPORT = "import" + MESSAGE = "message" + ENUM = "enum" + PACKAGE = "package" + OPTION = "option" + ONEOF = "oneof" + MAP = "map" + REPEATED = "repeated" + SERVICE = "service" + RPC = "rpc" + RETURNS = "returns" + TO = "to" + RESERVED = "reserved" diff --git a/internal/kclvm_py/encoding/protobuf/types.py b/internal/kclvm_py/encoding/protobuf/types.py new file mode 100644 index 000000000..9c4b2579d --- /dev/null +++ b/internal/kclvm_py/encoding/protobuf/types.py @@ -0,0 +1,35 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import Dict + +from .parser import Type, KeyType + +PROTOBUF_SCALAR_TYPE_TO_KCL_MAPPING: Dict[str, str] = { + "int32": "int", + "int64": "int", + "uint32": "int", + "uint64": "int", + "double": "float", + "float": "float", + "fixed32": "int", + "fixed64": "int", + "sfixed32": "int", + "sfixed64": "int", + "bool": "bool", + "string": "str", + "bytes": "str", +} + +KCL_SCALAR_TYPE_TO_PROTOBUF_TYPE_MAPPING: Dict[str, Type] = { + "int": Type.INT64, + "float": Type.DOUBLE, + "bool": Type.BOOL, + "str": Type.STRING, +} + +KCL_SCALAR_TYPE_TO_PROTOBUF_KEY_TYPE_MAPPING: Dict[str, Type] = { + "int": KeyType.INT64, + "float": KeyType.INT64, + "bool": KeyType.BOOL, + "str": KeyType.STRING, +} diff --git a/internal/kclvm_py/internal/__init__.py b/internal/kclvm_py/internal/__init__.py new file mode 100644 index 000000000..d74aaf8ad --- /dev/null +++ b/internal/kclvm_py/internal/__init__.py @@ -0,0 +1 @@ +# Copyright 2021 The KCL Authors. All rights reserved. diff --git a/internal/kclvm_py/internal/gpyrpc/__init__.py b/internal/kclvm_py/internal/gpyrpc/__init__.py new file mode 100644 index 000000000..d74aaf8ad --- /dev/null +++ b/internal/kclvm_py/internal/gpyrpc/__init__.py @@ -0,0 +1 @@ +# Copyright 2021 The KCL Authors. All rights reserved. diff --git a/internal/kclvm_py/internal/gpyrpc/gpyrpc.py b/internal/kclvm_py/internal/gpyrpc/gpyrpc.py new file mode 100644 index 000000000..35ebcb146 --- /dev/null +++ b/internal/kclvm_py/internal/gpyrpc/gpyrpc.py @@ -0,0 +1,132 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import json +import sys +import traceback + +from typing import List, Dict, Callable + +import google.protobuf.json_format as json_format + +import kclvm.kcl.error as kcl_error + +from .gpyrpc_pb2 import ( + Error, + Request, + Response, + ListMethod_Result, + Ping_Args, + Ping_Result, +) + +# https://developers.google.com/protocol-buffers/docs/reference/google.protobuf +# https://developers.google.com/protocol-buffers/docs/reference/python-generated +# https://github.com/protocolbuffers/protobuf/tree/master/src/google/protobuf + +_rpc_method_table: Dict[str, Callable[[Request], Response]] = {} + + +def _gpyrpcCallProxy(method: str, jsonpb_Request: str) -> str: + assert method + assert jsonpb_Request + + try: + fn = _rpc_method_table[method] + assert fn + + req = Request() + json_format.Parse(jsonpb_Request, req) + + resp = Response() + fn_result = fn(req) + resp.CopyFrom(fn_result) + + jsonpb_Response: str = json_format.MessageToJson( + resp, including_default_value_fields=True, preserving_proto_field_name=True + ) + return jsonpb_Response + + except kcl_error.KCLException as err: + errMessage = f"{err}" + resp = Response(err=Error(message=errMessage)) + return json_format.MessageToJson( + resp, including_default_value_fields=True, preserving_proto_field_name=True + ) + except OSError as err: + errMessage = f"OSError: {err}" + resp = Response(err=Error(message=errMessage)) + return json_format.MessageToJson( + resp, including_default_value_fields=True, preserving_proto_field_name=True + ) + except AssertionError as err: + ex_type, ex_val, ex_stack = sys.exc_info() + tb_info = traceback.extract_tb(ex_stack)[-1] + + filename = tb_info.filename + line = tb_info.lineno + + errMessage = f"AssertionError: {err}" + resp = Response(err=Error(message=errMessage, filename=filename, line=line)) + return json_format.MessageToJson( + resp, including_default_value_fields=True, preserving_proto_field_name=True + ) + except Exception as err: + errMessage = f"Exception: Internal Error! Please report a bug to us: method={method}, err={err}, stack trace={traceback.format_exc()}" + resp = Response(err=Error(message=errMessage)) + return json_format.MessageToJson( + resp, including_default_value_fields=True, preserving_proto_field_name=True + ) + + +def gpyrpcCallProxy(method: str, jsonRequest: str) -> str: + if not method: + errMessage = "method is empty" + resp = Response(err=Error(message=errMessage)) + return json_format.MessageToJson( + resp, including_default_value_fields=True, preserving_proto_field_name=True + ) + + if method not in _rpc_method_table: + errMessage = f"method '{method}' not found" + resp = Response(err=Error(message=errMessage)) + return json_format.MessageToJson( + resp, including_default_value_fields=True, preserving_proto_field_name=True + ) + resp: Response = {"error": f"method '{method}' not found"} + return str(json.dumps(resp)) + + return _gpyrpcCallProxy(method, jsonRequest) + + +def RegisterMethod(method: str, fn: Callable[[Request], Response]): + assert method + assert fn + + assert method not in _rpc_method_table + _rpc_method_table[method] = fn + + +# args: gpyrpc.Ping_Args +# result: gpyrpc.Ping_Result +def _gpyrpcPing(req: Request) -> Response: + args = Ping_Args() + req.args.Unpack(args) + + resp = Response() + resp.result.Pack(Ping_Result(value=args.value)) + return resp + + +# args: gpyrpc.ListMethod_Args +# result: gpyrpc.ListMethod_Result +def _gpyrpcListMethod(req: Request) -> Response: + keys: List[str] = list(sorted(_rpc_method_table)) + + resp = Response() + resp.result.Pack(ListMethod_Result(method_name_list=keys)) + + return resp + + +RegisterMethod("gpyrpc.BuiltinService.Ping", _gpyrpcPing) +RegisterMethod("gpyrpc.BuiltinService.ListMethod", _gpyrpcListMethod) diff --git a/internal/kclvm_py/internal/gpyrpc/gpyrpc_pb2.py b/internal/kclvm_py/internal/gpyrpc/gpyrpc_pb2.py new file mode 100644 index 000000000..1e9ec3278 --- /dev/null +++ b/internal/kclvm_py/internal/gpyrpc/gpyrpc_pb2.py @@ -0,0 +1,609 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: gpyrpc/gpyrpc.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13gpyrpc/gpyrpc.proto\x12\x06gpyrpc\x1a\x19google/protobuf/any.proto\x1a google/protobuf/descriptor.proto\")\n\nCmdArgSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"[\n\x0f\x43mdOverrideSpec\x12\x0f\n\x07pkgpath\x18\x01 \x01(\t\x12\x12\n\nfield_path\x18\x02 \x01(\t\x12\x13\n\x0b\x66ield_value\x18\x03 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x04 \x01(\t\"f\n\x0cRestResponse\x12$\n\x06result\x18\x01 \x01(\x0b\x32\x14.google.protobuf.Any\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12!\n\x07kcl_err\x18\x03 \x01(\x0b\x32\x10.gpyrpc.KclError\"`\n\x08KclError\x12\x0e\n\x06\x65wcode\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0b\n\x03msg\x18\x03 \x01(\t\x12)\n\x0b\x65rror_infos\x18\x04 \x03(\x0b\x32\x14.gpyrpc.KclErrorInfo\"w\n\x0cKclErrorInfo\x12\x11\n\terr_level\x18\x01 \x01(\t\x12\x0f\n\x07\x61rg_msg\x18\x02 \x01(\t\x12\x10\n\x08\x66ilename\x18\x03 \x01(\t\x12\x10\n\x08src_code\x18\x04 \x01(\t\x12\x0f\n\x07line_no\x18\x05 \x01(\t\x12\x0e\n\x06\x63ol_no\x18\x06 \x01(\t\"\x1a\n\tPing_Args\x12\r\n\x05value\x18\x01 \x01(\t\"\x1c\n\x0bPing_Result\x12\r\n\x05value\x18\x01 \x01(\t\"\x11\n\x0fListMethod_Args\"-\n\x11ListMethod_Result\x12\x18\n\x10method_name_list\x18\x01 \x03(\t\"Z\n\x17ParseFile_LarkTree_Args\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x13\n\x0bsource_code\x18\x02 \x01(\t\x12\x18\n\x10ignore_file_line\x18\x03 \x01(\x08\"3\n\x19ParseFile_LarkTree_Result\x12\x16\n\x0elark_tree_json\x18\x01 \x01(\t\";\n\x12ParseFile_AST_Args\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x13\n\x0bsource_code\x18\x02 \x01(\t\"(\n\x14ParseFile_AST_Result\x12\x10\n\x08\x61st_json\x18\x01 \x01(\t\"0\n\x15ParseProgram_AST_Args\x12\x17\n\x0fk_filename_list\x18\x01 \x03(\t\"+\n\x17ParseProgram_AST_Result\x12\x10\n\x08\x61st_json\x18\x01 \x01(\t\"\xe0\x02\n\x10\x45xecProgram_Args\x12\x10\n\x08work_dir\x18\x01 \x01(\t\x12\x17\n\x0fk_filename_list\x18\x02 \x03(\t\x12\x13\n\x0bk_code_list\x18\x03 \x03(\t\x12 \n\x04\x61rgs\x18\x04 \x03(\x0b\x32\x12.gpyrpc.CmdArgSpec\x12*\n\toverrides\x18\x05 \x03(\x0b\x32\x17.gpyrpc.CmdOverrideSpec\x12\x1b\n\x13\x64isable_yaml_result\x18\x06 \x01(\x08\x12\x1a\n\x12print_override_ast\x18\x07 \x01(\x08\x12\x1a\n\x12strict_range_check\x18\x08 \x01(\x08\x12\x14\n\x0c\x64isable_none\x18\t \x01(\x08\x12\x0f\n\x07verbose\x18\n \x01(\x05\x12\r\n\x05\x64\x65\x62ug\x18\x0b \x01(\x05\x12\x11\n\tsort_keys\x18\x0c \x01(\x08\x12 \n\x18include_schema_type_path\x18\r \x01(\x08\"T\n\x12\x45xecProgram_Result\x12\x13\n\x0bjson_result\x18\x01 \x01(\t\x12\x13\n\x0byaml_result\x18\x02 \x01(\t\x12\x14\n\x0c\x65scaped_time\x18\x65 \x01(\t\"\'\n\x10ResetPlugin_Args\x12\x13\n\x0bplugin_root\x18\x01 \x01(\t\"\x14\n\x12ResetPlugin_Result\"!\n\x0f\x46ormatCode_Args\x12\x0e\n\x06source\x18\x01 \x01(\t\"&\n\x11\x46ormatCode_Result\x12\x11\n\tformatted\x18\x01 \x01(\x0c\"\x1f\n\x0f\x46ormatPath_Args\x12\x0c\n\x04path\x18\x01 \x01(\t\")\n\x11\x46ormatPath_Result\x12\x14\n\x0c\x63hangedPaths\x18\x01 \x03(\t\"\x1d\n\rLintPath_Args\x12\x0c\n\x04path\x18\x01 \x01(\t\"\"\n\x0fLintPath_Result\x12\x0f\n\x07results\x18\x01 \x03(\t\"F\n\x11OverrideFile_Args\x12\x0c\n\x04\x66ile\x18\x01 \x01(\t\x12\r\n\x05specs\x18\x02 \x03(\t\x12\x14\n\x0cimport_paths\x18\x03 \x03(\t\"%\n\x13OverrideFile_Result\x12\x0e\n\x06result\x18\x01 \x01(\x08\"\x1d\n\rEvalCode_Args\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\"&\n\x0f\x45valCode_Result\x12\x13\n\x0bjson_result\x18\x02 \x01(\t\" \n\x10ResolveCode_Args\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\"%\n\x12ResolveCode_Result\x12\x0f\n\x07success\x18\x01 \x01(\x08\"E\n\x12GetSchemaType_Args\x12\x0c\n\x04\x66ile\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x13\n\x0bschema_name\x18\x03 \x01(\t\"A\n\x14GetSchemaType_Result\x12)\n\x10schema_type_list\x18\x01 \x03(\x0b\x32\x0f.gpyrpc.KclType\"g\n\x11ValidateCode_Args\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0e\n\x06schema\x18\x03 \x01(\t\x12\x16\n\x0e\x61ttribute_name\x18\x04 \x01(\t\x12\x0e\n\x06\x66ormat\x18\x05 \x01(\t\";\n\x13ValidateCode_Result\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x0b\x65rr_message\x18\x02 \x01(\t\"+\n\x0b\x43odeSnippet\x12\x0e\n\x06schema\x18\x01 \x01(\t\x12\x0c\n\x04rule\x18\x02 \x01(\t\"<\n\x0fSpliceCode_Args\x12)\n\x0c\x63odeSnippets\x18\x01 \x03(\x0b\x32\x13.gpyrpc.CodeSnippet\"\'\n\x11SpliceCode_Result\x12\x12\n\nspliceCode\x18\x01 \x01(\t\":\n\x08Position\x12\x0c\n\x04line\x18\x01 \x01(\x03\x12\x0e\n\x06\x63olumn\x18\x02 \x01(\x03\x12\x10\n\x08\x66ilename\x18\x03 \x01(\t\"J\n\rComplete_Args\x12\x1d\n\x03pos\x18\x01 \x01(\x0b\x32\x10.gpyrpc.Position\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0c\n\x04\x63ode\x18\x03 \x01(\t\"(\n\x0f\x43omplete_Result\x12\x15\n\rcompleteItems\x18\x01 \x01(\t\";\n\x0cGoToDef_Args\x12\x1d\n\x03pos\x18\x01 \x01(\x0b\x32\x10.gpyrpc.Position\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\"#\n\x0eGoToDef_Result\x12\x11\n\tlocations\x18\x01 \x01(\t\"1\n\x13\x44ocumentSymbol_Args\x12\x0c\n\x04\x66ile\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\"\'\n\x15\x44ocumentSymbol_Result\x12\x0e\n\x06symbol\x18\x01 \x01(\t\"9\n\nHover_Args\x12\x1d\n\x03pos\x18\x01 \x01(\x0b\x32\x10.gpyrpc.Position\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\"#\n\x0cHover_Result\x12\x13\n\x0bhoverResult\x18\x01 \x01(\t\"i\n\x11ListDepFiles_Args\x12\x10\n\x08work_dir\x18\x01 \x01(\t\x12\x14\n\x0cuse_abs_path\x18\x02 \x01(\x08\x12\x13\n\x0binclude_all\x18\x03 \x01(\x08\x12\x17\n\x0fuse_fast_parser\x18\x04 \x01(\x08\"F\n\x13ListDepFiles_Result\x12\x0f\n\x07pkgroot\x18\x01 \x01(\t\x12\x0f\n\x07pkgpath\x18\x02 \x01(\t\x12\r\n\x05\x66iles\x18\x03 \x03(\t\"9\n\x16LoadSettingsFiles_Args\x12\x10\n\x08work_dir\x18\x01 \x01(\t\x12\r\n\x05\x66iles\x18\x02 \x03(\t\"q\n\x18LoadSettingsFiles_Result\x12*\n\x0fkcl_cli_configs\x18\x01 \x01(\x0b\x32\x11.gpyrpc.CliConfig\x12)\n\x0bkcl_options\x18\x02 \x03(\x0b\x32\x14.gpyrpc.KeyValuePair\"\xa6\x01\n\tCliConfig\x12\r\n\x05\x66iles\x18\x01 \x03(\t\x12\x0e\n\x06output\x18\x02 \x01(\t\x12\x11\n\toverrides\x18\x03 \x03(\t\x12\x15\n\rpath_selector\x18\x04 \x03(\t\x12\x1a\n\x12strict_range_check\x18\x05 \x01(\x08\x12\x14\n\x0c\x64isable_none\x18\x06 \x01(\x08\x12\x0f\n\x07verbose\x18\x07 \x01(\x03\x12\r\n\x05\x64\x65\x62ug\x18\x08 \x01(\x08\"*\n\x0cKeyValuePair\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"\xf4\x02\n\x07KclType\x12\x0c\n\x04type\x18\x01 \x01(\t\x12$\n\x0bunion_types\x18\x02 \x03(\x0b\x32\x0f.gpyrpc.KclType\x12\x0f\n\x07\x64\x65\x66\x61ult\x18\x03 \x01(\t\x12\x13\n\x0bschema_name\x18\x04 \x01(\t\x12\x12\n\nschema_doc\x18\x05 \x01(\t\x12\x33\n\nproperties\x18\x06 \x03(\x0b\x32\x1f.gpyrpc.KclType.PropertiesEntry\x12\x10\n\x08required\x18\x07 \x03(\t\x12\x1c\n\x03key\x18\x08 \x01(\x0b\x32\x0f.gpyrpc.KclType\x12\x1d\n\x04item\x18\t \x01(\x0b\x32\x0f.gpyrpc.KclType\x12\x0c\n\x04line\x18\n \x01(\x05\x12%\n\ndecorators\x18\x0b \x03(\x0b\x32\x11.gpyrpc.Decorator\x1a\x42\n\x0fPropertiesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0f.gpyrpc.KclType:\x02\x38\x01\"\x90\x01\n\tDecorator\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x11\n\targuments\x18\x02 \x03(\t\x12\x31\n\x08keywords\x18\x03 \x03(\x0b\x32\x1f.gpyrpc.Decorator.KeywordsEntry\x1a/\n\rKeywordsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x32\x82\x01\n\x0e\x42uiltinService\x12.\n\x04Ping\x12\x11.gpyrpc.Ping_Args\x1a\x13.gpyrpc.Ping_Result\x12@\n\nListMethod\x12\x17.gpyrpc.ListMethod_Args\x1a\x19.gpyrpc.ListMethod_Result2\xb4\x0b\n\x0cKclvmService\x12.\n\x04Ping\x12\x11.gpyrpc.Ping_Args\x1a\x13.gpyrpc.Ping_Result\x12X\n\x12ParseFile_LarkTree\x12\x1f.gpyrpc.ParseFile_LarkTree_Args\x1a!.gpyrpc.ParseFile_LarkTree_Result\x12I\n\rParseFile_AST\x12\x1a.gpyrpc.ParseFile_AST_Args\x1a\x1c.gpyrpc.ParseFile_AST_Result\x12R\n\x10ParseProgram_AST\x12\x1d.gpyrpc.ParseProgram_AST_Args\x1a\x1f.gpyrpc.ParseProgram_AST_Result\x12\x43\n\x0b\x45xecProgram\x12\x18.gpyrpc.ExecProgram_Args\x1a\x1a.gpyrpc.ExecProgram_Result\x12\x43\n\x0bResetPlugin\x12\x18.gpyrpc.ResetPlugin_Args\x1a\x1a.gpyrpc.ResetPlugin_Result\x12@\n\nFormatCode\x12\x17.gpyrpc.FormatCode_Args\x1a\x19.gpyrpc.FormatCode_Result\x12@\n\nFormatPath\x12\x17.gpyrpc.FormatPath_Args\x1a\x19.gpyrpc.FormatPath_Result\x12:\n\x08LintPath\x12\x15.gpyrpc.LintPath_Args\x1a\x17.gpyrpc.LintPath_Result\x12\x46\n\x0cOverrideFile\x12\x19.gpyrpc.OverrideFile_Args\x1a\x1b.gpyrpc.OverrideFile_Result\x12:\n\x08\x45valCode\x12\x15.gpyrpc.EvalCode_Args\x1a\x17.gpyrpc.EvalCode_Result\x12\x43\n\x0bResolveCode\x12\x18.gpyrpc.ResolveCode_Args\x1a\x1a.gpyrpc.ResolveCode_Result\x12I\n\rGetSchemaType\x12\x1a.gpyrpc.GetSchemaType_Args\x1a\x1c.gpyrpc.GetSchemaType_Result\x12\x46\n\x0cValidateCode\x12\x19.gpyrpc.ValidateCode_Args\x1a\x1b.gpyrpc.ValidateCode_Result\x12@\n\nSpliceCode\x12\x17.gpyrpc.SpliceCode_Args\x1a\x19.gpyrpc.SpliceCode_Result\x12:\n\x08\x43omplete\x12\x15.gpyrpc.Complete_Args\x1a\x17.gpyrpc.Complete_Result\x12\x37\n\x07GoToDef\x12\x14.gpyrpc.GoToDef_Args\x1a\x16.gpyrpc.GoToDef_Result\x12L\n\x0e\x44ocumentSymbol\x12\x1b.gpyrpc.DocumentSymbol_Args\x1a\x1d.gpyrpc.DocumentSymbol_Result\x12\x31\n\x05Hover\x12\x12.gpyrpc.Hover_Args\x1a\x14.gpyrpc.Hover_Result\x12\x46\n\x0cListDepFiles\x12\x19.gpyrpc.ListDepFiles_Args\x1a\x1b.gpyrpc.ListDepFiles_Result\x12U\n\x11LoadSettingsFiles\x12\x1e.gpyrpc.LoadSettingsFiles_Args\x1a .gpyrpc.LoadSettingsFiles_ResultB0Z.kusionstack.io/kclvm-go/pkg/spec/gpyrpc;gpyrpcb\x06proto3') + + + +_CMDARGSPEC = DESCRIPTOR.message_types_by_name['CmdArgSpec'] +_CMDOVERRIDESPEC = DESCRIPTOR.message_types_by_name['CmdOverrideSpec'] +_RESTRESPONSE = DESCRIPTOR.message_types_by_name['RestResponse'] +_KCLERROR = DESCRIPTOR.message_types_by_name['KclError'] +_KCLERRORINFO = DESCRIPTOR.message_types_by_name['KclErrorInfo'] +_PING_ARGS = DESCRIPTOR.message_types_by_name['Ping_Args'] +_PING_RESULT = DESCRIPTOR.message_types_by_name['Ping_Result'] +_LISTMETHOD_ARGS = DESCRIPTOR.message_types_by_name['ListMethod_Args'] +_LISTMETHOD_RESULT = DESCRIPTOR.message_types_by_name['ListMethod_Result'] +_PARSEFILE_LARKTREE_ARGS = DESCRIPTOR.message_types_by_name['ParseFile_LarkTree_Args'] +_PARSEFILE_LARKTREE_RESULT = DESCRIPTOR.message_types_by_name['ParseFile_LarkTree_Result'] +_PARSEFILE_AST_ARGS = DESCRIPTOR.message_types_by_name['ParseFile_AST_Args'] +_PARSEFILE_AST_RESULT = DESCRIPTOR.message_types_by_name['ParseFile_AST_Result'] +_PARSEPROGRAM_AST_ARGS = DESCRIPTOR.message_types_by_name['ParseProgram_AST_Args'] +_PARSEPROGRAM_AST_RESULT = DESCRIPTOR.message_types_by_name['ParseProgram_AST_Result'] +_EXECPROGRAM_ARGS = DESCRIPTOR.message_types_by_name['ExecProgram_Args'] +_EXECPROGRAM_RESULT = DESCRIPTOR.message_types_by_name['ExecProgram_Result'] +_RESETPLUGIN_ARGS = DESCRIPTOR.message_types_by_name['ResetPlugin_Args'] +_RESETPLUGIN_RESULT = DESCRIPTOR.message_types_by_name['ResetPlugin_Result'] +_FORMATCODE_ARGS = DESCRIPTOR.message_types_by_name['FormatCode_Args'] +_FORMATCODE_RESULT = DESCRIPTOR.message_types_by_name['FormatCode_Result'] +_FORMATPATH_ARGS = DESCRIPTOR.message_types_by_name['FormatPath_Args'] +_FORMATPATH_RESULT = DESCRIPTOR.message_types_by_name['FormatPath_Result'] +_LINTPATH_ARGS = DESCRIPTOR.message_types_by_name['LintPath_Args'] +_LINTPATH_RESULT = DESCRIPTOR.message_types_by_name['LintPath_Result'] +_OVERRIDEFILE_ARGS = DESCRIPTOR.message_types_by_name['OverrideFile_Args'] +_OVERRIDEFILE_RESULT = DESCRIPTOR.message_types_by_name['OverrideFile_Result'] +_EVALCODE_ARGS = DESCRIPTOR.message_types_by_name['EvalCode_Args'] +_EVALCODE_RESULT = DESCRIPTOR.message_types_by_name['EvalCode_Result'] +_RESOLVECODE_ARGS = DESCRIPTOR.message_types_by_name['ResolveCode_Args'] +_RESOLVECODE_RESULT = DESCRIPTOR.message_types_by_name['ResolveCode_Result'] +_GETSCHEMATYPE_ARGS = DESCRIPTOR.message_types_by_name['GetSchemaType_Args'] +_GETSCHEMATYPE_RESULT = DESCRIPTOR.message_types_by_name['GetSchemaType_Result'] +_VALIDATECODE_ARGS = DESCRIPTOR.message_types_by_name['ValidateCode_Args'] +_VALIDATECODE_RESULT = DESCRIPTOR.message_types_by_name['ValidateCode_Result'] +_CODESNIPPET = DESCRIPTOR.message_types_by_name['CodeSnippet'] +_SPLICECODE_ARGS = DESCRIPTOR.message_types_by_name['SpliceCode_Args'] +_SPLICECODE_RESULT = DESCRIPTOR.message_types_by_name['SpliceCode_Result'] +_POSITION = DESCRIPTOR.message_types_by_name['Position'] +_COMPLETE_ARGS = DESCRIPTOR.message_types_by_name['Complete_Args'] +_COMPLETE_RESULT = DESCRIPTOR.message_types_by_name['Complete_Result'] +_GOTODEF_ARGS = DESCRIPTOR.message_types_by_name['GoToDef_Args'] +_GOTODEF_RESULT = DESCRIPTOR.message_types_by_name['GoToDef_Result'] +_DOCUMENTSYMBOL_ARGS = DESCRIPTOR.message_types_by_name['DocumentSymbol_Args'] +_DOCUMENTSYMBOL_RESULT = DESCRIPTOR.message_types_by_name['DocumentSymbol_Result'] +_HOVER_ARGS = DESCRIPTOR.message_types_by_name['Hover_Args'] +_HOVER_RESULT = DESCRIPTOR.message_types_by_name['Hover_Result'] +_LISTDEPFILES_ARGS = DESCRIPTOR.message_types_by_name['ListDepFiles_Args'] +_LISTDEPFILES_RESULT = DESCRIPTOR.message_types_by_name['ListDepFiles_Result'] +_LOADSETTINGSFILES_ARGS = DESCRIPTOR.message_types_by_name['LoadSettingsFiles_Args'] +_LOADSETTINGSFILES_RESULT = DESCRIPTOR.message_types_by_name['LoadSettingsFiles_Result'] +_CLICONFIG = DESCRIPTOR.message_types_by_name['CliConfig'] +_KEYVALUEPAIR = DESCRIPTOR.message_types_by_name['KeyValuePair'] +_KCLTYPE = DESCRIPTOR.message_types_by_name['KclType'] +_KCLTYPE_PROPERTIESENTRY = _KCLTYPE.nested_types_by_name['PropertiesEntry'] +_DECORATOR = DESCRIPTOR.message_types_by_name['Decorator'] +_DECORATOR_KEYWORDSENTRY = _DECORATOR.nested_types_by_name['KeywordsEntry'] +CmdArgSpec = _reflection.GeneratedProtocolMessageType('CmdArgSpec', (_message.Message,), { + 'DESCRIPTOR' : _CMDARGSPEC, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.CmdArgSpec) + }) +_sym_db.RegisterMessage(CmdArgSpec) + +CmdOverrideSpec = _reflection.GeneratedProtocolMessageType('CmdOverrideSpec', (_message.Message,), { + 'DESCRIPTOR' : _CMDOVERRIDESPEC, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.CmdOverrideSpec) + }) +_sym_db.RegisterMessage(CmdOverrideSpec) + +RestResponse = _reflection.GeneratedProtocolMessageType('RestResponse', (_message.Message,), { + 'DESCRIPTOR' : _RESTRESPONSE, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.RestResponse) + }) +_sym_db.RegisterMessage(RestResponse) + +KclError = _reflection.GeneratedProtocolMessageType('KclError', (_message.Message,), { + 'DESCRIPTOR' : _KCLERROR, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.KclError) + }) +_sym_db.RegisterMessage(KclError) + +KclErrorInfo = _reflection.GeneratedProtocolMessageType('KclErrorInfo', (_message.Message,), { + 'DESCRIPTOR' : _KCLERRORINFO, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.KclErrorInfo) + }) +_sym_db.RegisterMessage(KclErrorInfo) + +Ping_Args = _reflection.GeneratedProtocolMessageType('Ping_Args', (_message.Message,), { + 'DESCRIPTOR' : _PING_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.Ping_Args) + }) +_sym_db.RegisterMessage(Ping_Args) + +Ping_Result = _reflection.GeneratedProtocolMessageType('Ping_Result', (_message.Message,), { + 'DESCRIPTOR' : _PING_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.Ping_Result) + }) +_sym_db.RegisterMessage(Ping_Result) + +ListMethod_Args = _reflection.GeneratedProtocolMessageType('ListMethod_Args', (_message.Message,), { + 'DESCRIPTOR' : _LISTMETHOD_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ListMethod_Args) + }) +_sym_db.RegisterMessage(ListMethod_Args) + +ListMethod_Result = _reflection.GeneratedProtocolMessageType('ListMethod_Result', (_message.Message,), { + 'DESCRIPTOR' : _LISTMETHOD_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ListMethod_Result) + }) +_sym_db.RegisterMessage(ListMethod_Result) + +ParseFile_LarkTree_Args = _reflection.GeneratedProtocolMessageType('ParseFile_LarkTree_Args', (_message.Message,), { + 'DESCRIPTOR' : _PARSEFILE_LARKTREE_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ParseFile_LarkTree_Args) + }) +_sym_db.RegisterMessage(ParseFile_LarkTree_Args) + +ParseFile_LarkTree_Result = _reflection.GeneratedProtocolMessageType('ParseFile_LarkTree_Result', (_message.Message,), { + 'DESCRIPTOR' : _PARSEFILE_LARKTREE_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ParseFile_LarkTree_Result) + }) +_sym_db.RegisterMessage(ParseFile_LarkTree_Result) + +ParseFile_AST_Args = _reflection.GeneratedProtocolMessageType('ParseFile_AST_Args', (_message.Message,), { + 'DESCRIPTOR' : _PARSEFILE_AST_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ParseFile_AST_Args) + }) +_sym_db.RegisterMessage(ParseFile_AST_Args) + +ParseFile_AST_Result = _reflection.GeneratedProtocolMessageType('ParseFile_AST_Result', (_message.Message,), { + 'DESCRIPTOR' : _PARSEFILE_AST_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ParseFile_AST_Result) + }) +_sym_db.RegisterMessage(ParseFile_AST_Result) + +ParseProgram_AST_Args = _reflection.GeneratedProtocolMessageType('ParseProgram_AST_Args', (_message.Message,), { + 'DESCRIPTOR' : _PARSEPROGRAM_AST_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ParseProgram_AST_Args) + }) +_sym_db.RegisterMessage(ParseProgram_AST_Args) + +ParseProgram_AST_Result = _reflection.GeneratedProtocolMessageType('ParseProgram_AST_Result', (_message.Message,), { + 'DESCRIPTOR' : _PARSEPROGRAM_AST_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ParseProgram_AST_Result) + }) +_sym_db.RegisterMessage(ParseProgram_AST_Result) + +ExecProgram_Args = _reflection.GeneratedProtocolMessageType('ExecProgram_Args', (_message.Message,), { + 'DESCRIPTOR' : _EXECPROGRAM_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ExecProgram_Args) + }) +_sym_db.RegisterMessage(ExecProgram_Args) + +ExecProgram_Result = _reflection.GeneratedProtocolMessageType('ExecProgram_Result', (_message.Message,), { + 'DESCRIPTOR' : _EXECPROGRAM_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ExecProgram_Result) + }) +_sym_db.RegisterMessage(ExecProgram_Result) + +ResetPlugin_Args = _reflection.GeneratedProtocolMessageType('ResetPlugin_Args', (_message.Message,), { + 'DESCRIPTOR' : _RESETPLUGIN_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ResetPlugin_Args) + }) +_sym_db.RegisterMessage(ResetPlugin_Args) + +ResetPlugin_Result = _reflection.GeneratedProtocolMessageType('ResetPlugin_Result', (_message.Message,), { + 'DESCRIPTOR' : _RESETPLUGIN_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ResetPlugin_Result) + }) +_sym_db.RegisterMessage(ResetPlugin_Result) + +FormatCode_Args = _reflection.GeneratedProtocolMessageType('FormatCode_Args', (_message.Message,), { + 'DESCRIPTOR' : _FORMATCODE_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.FormatCode_Args) + }) +_sym_db.RegisterMessage(FormatCode_Args) + +FormatCode_Result = _reflection.GeneratedProtocolMessageType('FormatCode_Result', (_message.Message,), { + 'DESCRIPTOR' : _FORMATCODE_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.FormatCode_Result) + }) +_sym_db.RegisterMessage(FormatCode_Result) + +FormatPath_Args = _reflection.GeneratedProtocolMessageType('FormatPath_Args', (_message.Message,), { + 'DESCRIPTOR' : _FORMATPATH_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.FormatPath_Args) + }) +_sym_db.RegisterMessage(FormatPath_Args) + +FormatPath_Result = _reflection.GeneratedProtocolMessageType('FormatPath_Result', (_message.Message,), { + 'DESCRIPTOR' : _FORMATPATH_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.FormatPath_Result) + }) +_sym_db.RegisterMessage(FormatPath_Result) + +LintPath_Args = _reflection.GeneratedProtocolMessageType('LintPath_Args', (_message.Message,), { + 'DESCRIPTOR' : _LINTPATH_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.LintPath_Args) + }) +_sym_db.RegisterMessage(LintPath_Args) + +LintPath_Result = _reflection.GeneratedProtocolMessageType('LintPath_Result', (_message.Message,), { + 'DESCRIPTOR' : _LINTPATH_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.LintPath_Result) + }) +_sym_db.RegisterMessage(LintPath_Result) + +OverrideFile_Args = _reflection.GeneratedProtocolMessageType('OverrideFile_Args', (_message.Message,), { + 'DESCRIPTOR' : _OVERRIDEFILE_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.OverrideFile_Args) + }) +_sym_db.RegisterMessage(OverrideFile_Args) + +OverrideFile_Result = _reflection.GeneratedProtocolMessageType('OverrideFile_Result', (_message.Message,), { + 'DESCRIPTOR' : _OVERRIDEFILE_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.OverrideFile_Result) + }) +_sym_db.RegisterMessage(OverrideFile_Result) + +EvalCode_Args = _reflection.GeneratedProtocolMessageType('EvalCode_Args', (_message.Message,), { + 'DESCRIPTOR' : _EVALCODE_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.EvalCode_Args) + }) +_sym_db.RegisterMessage(EvalCode_Args) + +EvalCode_Result = _reflection.GeneratedProtocolMessageType('EvalCode_Result', (_message.Message,), { + 'DESCRIPTOR' : _EVALCODE_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.EvalCode_Result) + }) +_sym_db.RegisterMessage(EvalCode_Result) + +ResolveCode_Args = _reflection.GeneratedProtocolMessageType('ResolveCode_Args', (_message.Message,), { + 'DESCRIPTOR' : _RESOLVECODE_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ResolveCode_Args) + }) +_sym_db.RegisterMessage(ResolveCode_Args) + +ResolveCode_Result = _reflection.GeneratedProtocolMessageType('ResolveCode_Result', (_message.Message,), { + 'DESCRIPTOR' : _RESOLVECODE_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ResolveCode_Result) + }) +_sym_db.RegisterMessage(ResolveCode_Result) + +GetSchemaType_Args = _reflection.GeneratedProtocolMessageType('GetSchemaType_Args', (_message.Message,), { + 'DESCRIPTOR' : _GETSCHEMATYPE_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.GetSchemaType_Args) + }) +_sym_db.RegisterMessage(GetSchemaType_Args) + +GetSchemaType_Result = _reflection.GeneratedProtocolMessageType('GetSchemaType_Result', (_message.Message,), { + 'DESCRIPTOR' : _GETSCHEMATYPE_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.GetSchemaType_Result) + }) +_sym_db.RegisterMessage(GetSchemaType_Result) + +ValidateCode_Args = _reflection.GeneratedProtocolMessageType('ValidateCode_Args', (_message.Message,), { + 'DESCRIPTOR' : _VALIDATECODE_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ValidateCode_Args) + }) +_sym_db.RegisterMessage(ValidateCode_Args) + +ValidateCode_Result = _reflection.GeneratedProtocolMessageType('ValidateCode_Result', (_message.Message,), { + 'DESCRIPTOR' : _VALIDATECODE_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ValidateCode_Result) + }) +_sym_db.RegisterMessage(ValidateCode_Result) + +CodeSnippet = _reflection.GeneratedProtocolMessageType('CodeSnippet', (_message.Message,), { + 'DESCRIPTOR' : _CODESNIPPET, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.CodeSnippet) + }) +_sym_db.RegisterMessage(CodeSnippet) + +SpliceCode_Args = _reflection.GeneratedProtocolMessageType('SpliceCode_Args', (_message.Message,), { + 'DESCRIPTOR' : _SPLICECODE_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.SpliceCode_Args) + }) +_sym_db.RegisterMessage(SpliceCode_Args) + +SpliceCode_Result = _reflection.GeneratedProtocolMessageType('SpliceCode_Result', (_message.Message,), { + 'DESCRIPTOR' : _SPLICECODE_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.SpliceCode_Result) + }) +_sym_db.RegisterMessage(SpliceCode_Result) + +Position = _reflection.GeneratedProtocolMessageType('Position', (_message.Message,), { + 'DESCRIPTOR' : _POSITION, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.Position) + }) +_sym_db.RegisterMessage(Position) + +Complete_Args = _reflection.GeneratedProtocolMessageType('Complete_Args', (_message.Message,), { + 'DESCRIPTOR' : _COMPLETE_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.Complete_Args) + }) +_sym_db.RegisterMessage(Complete_Args) + +Complete_Result = _reflection.GeneratedProtocolMessageType('Complete_Result', (_message.Message,), { + 'DESCRIPTOR' : _COMPLETE_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.Complete_Result) + }) +_sym_db.RegisterMessage(Complete_Result) + +GoToDef_Args = _reflection.GeneratedProtocolMessageType('GoToDef_Args', (_message.Message,), { + 'DESCRIPTOR' : _GOTODEF_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.GoToDef_Args) + }) +_sym_db.RegisterMessage(GoToDef_Args) + +GoToDef_Result = _reflection.GeneratedProtocolMessageType('GoToDef_Result', (_message.Message,), { + 'DESCRIPTOR' : _GOTODEF_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.GoToDef_Result) + }) +_sym_db.RegisterMessage(GoToDef_Result) + +DocumentSymbol_Args = _reflection.GeneratedProtocolMessageType('DocumentSymbol_Args', (_message.Message,), { + 'DESCRIPTOR' : _DOCUMENTSYMBOL_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.DocumentSymbol_Args) + }) +_sym_db.RegisterMessage(DocumentSymbol_Args) + +DocumentSymbol_Result = _reflection.GeneratedProtocolMessageType('DocumentSymbol_Result', (_message.Message,), { + 'DESCRIPTOR' : _DOCUMENTSYMBOL_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.DocumentSymbol_Result) + }) +_sym_db.RegisterMessage(DocumentSymbol_Result) + +Hover_Args = _reflection.GeneratedProtocolMessageType('Hover_Args', (_message.Message,), { + 'DESCRIPTOR' : _HOVER_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.Hover_Args) + }) +_sym_db.RegisterMessage(Hover_Args) + +Hover_Result = _reflection.GeneratedProtocolMessageType('Hover_Result', (_message.Message,), { + 'DESCRIPTOR' : _HOVER_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.Hover_Result) + }) +_sym_db.RegisterMessage(Hover_Result) + +ListDepFiles_Args = _reflection.GeneratedProtocolMessageType('ListDepFiles_Args', (_message.Message,), { + 'DESCRIPTOR' : _LISTDEPFILES_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ListDepFiles_Args) + }) +_sym_db.RegisterMessage(ListDepFiles_Args) + +ListDepFiles_Result = _reflection.GeneratedProtocolMessageType('ListDepFiles_Result', (_message.Message,), { + 'DESCRIPTOR' : _LISTDEPFILES_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.ListDepFiles_Result) + }) +_sym_db.RegisterMessage(ListDepFiles_Result) + +LoadSettingsFiles_Args = _reflection.GeneratedProtocolMessageType('LoadSettingsFiles_Args', (_message.Message,), { + 'DESCRIPTOR' : _LOADSETTINGSFILES_ARGS, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.LoadSettingsFiles_Args) + }) +_sym_db.RegisterMessage(LoadSettingsFiles_Args) + +LoadSettingsFiles_Result = _reflection.GeneratedProtocolMessageType('LoadSettingsFiles_Result', (_message.Message,), { + 'DESCRIPTOR' : _LOADSETTINGSFILES_RESULT, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.LoadSettingsFiles_Result) + }) +_sym_db.RegisterMessage(LoadSettingsFiles_Result) + +CliConfig = _reflection.GeneratedProtocolMessageType('CliConfig', (_message.Message,), { + 'DESCRIPTOR' : _CLICONFIG, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.CliConfig) + }) +_sym_db.RegisterMessage(CliConfig) + +KeyValuePair = _reflection.GeneratedProtocolMessageType('KeyValuePair', (_message.Message,), { + 'DESCRIPTOR' : _KEYVALUEPAIR, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.KeyValuePair) + }) +_sym_db.RegisterMessage(KeyValuePair) + +KclType = _reflection.GeneratedProtocolMessageType('KclType', (_message.Message,), { + + 'PropertiesEntry' : _reflection.GeneratedProtocolMessageType('PropertiesEntry', (_message.Message,), { + 'DESCRIPTOR' : _KCLTYPE_PROPERTIESENTRY, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.KclType.PropertiesEntry) + }) + , + 'DESCRIPTOR' : _KCLTYPE, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.KclType) + }) +_sym_db.RegisterMessage(KclType) +_sym_db.RegisterMessage(KclType.PropertiesEntry) + +Decorator = _reflection.GeneratedProtocolMessageType('Decorator', (_message.Message,), { + + 'KeywordsEntry' : _reflection.GeneratedProtocolMessageType('KeywordsEntry', (_message.Message,), { + 'DESCRIPTOR' : _DECORATOR_KEYWORDSENTRY, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.Decorator.KeywordsEntry) + }) + , + 'DESCRIPTOR' : _DECORATOR, + '__module__' : 'gpyrpc.gpyrpc_pb2' + # @@protoc_insertion_point(class_scope:gpyrpc.Decorator) + }) +_sym_db.RegisterMessage(Decorator) +_sym_db.RegisterMessage(Decorator.KeywordsEntry) + +_BUILTINSERVICE = DESCRIPTOR.services_by_name['BuiltinService'] +_KCLVMSERVICE = DESCRIPTOR.services_by_name['KclvmService'] +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'Z.kusionstack.io/kclvm-go/pkg/spec/gpyrpc;gpyrpc' + _KCLTYPE_PROPERTIESENTRY._options = None + _KCLTYPE_PROPERTIESENTRY._serialized_options = b'8\001' + _DECORATOR_KEYWORDSENTRY._options = None + _DECORATOR_KEYWORDSENTRY._serialized_options = b'8\001' + _CMDARGSPEC._serialized_start=92 + _CMDARGSPEC._serialized_end=133 + _CMDOVERRIDESPEC._serialized_start=135 + _CMDOVERRIDESPEC._serialized_end=226 + _RESTRESPONSE._serialized_start=228 + _RESTRESPONSE._serialized_end=330 + _KCLERROR._serialized_start=332 + _KCLERROR._serialized_end=428 + _KCLERRORINFO._serialized_start=430 + _KCLERRORINFO._serialized_end=549 + _PING_ARGS._serialized_start=551 + _PING_ARGS._serialized_end=577 + _PING_RESULT._serialized_start=579 + _PING_RESULT._serialized_end=607 + _LISTMETHOD_ARGS._serialized_start=609 + _LISTMETHOD_ARGS._serialized_end=626 + _LISTMETHOD_RESULT._serialized_start=628 + _LISTMETHOD_RESULT._serialized_end=673 + _PARSEFILE_LARKTREE_ARGS._serialized_start=675 + _PARSEFILE_LARKTREE_ARGS._serialized_end=765 + _PARSEFILE_LARKTREE_RESULT._serialized_start=767 + _PARSEFILE_LARKTREE_RESULT._serialized_end=818 + _PARSEFILE_AST_ARGS._serialized_start=820 + _PARSEFILE_AST_ARGS._serialized_end=879 + _PARSEFILE_AST_RESULT._serialized_start=881 + _PARSEFILE_AST_RESULT._serialized_end=921 + _PARSEPROGRAM_AST_ARGS._serialized_start=923 + _PARSEPROGRAM_AST_ARGS._serialized_end=971 + _PARSEPROGRAM_AST_RESULT._serialized_start=973 + _PARSEPROGRAM_AST_RESULT._serialized_end=1016 + _EXECPROGRAM_ARGS._serialized_start=1019 + _EXECPROGRAM_ARGS._serialized_end=1371 + _EXECPROGRAM_RESULT._serialized_start=1373 + _EXECPROGRAM_RESULT._serialized_end=1457 + _RESETPLUGIN_ARGS._serialized_start=1459 + _RESETPLUGIN_ARGS._serialized_end=1498 + _RESETPLUGIN_RESULT._serialized_start=1500 + _RESETPLUGIN_RESULT._serialized_end=1520 + _FORMATCODE_ARGS._serialized_start=1522 + _FORMATCODE_ARGS._serialized_end=1555 + _FORMATCODE_RESULT._serialized_start=1557 + _FORMATCODE_RESULT._serialized_end=1595 + _FORMATPATH_ARGS._serialized_start=1597 + _FORMATPATH_ARGS._serialized_end=1628 + _FORMATPATH_RESULT._serialized_start=1630 + _FORMATPATH_RESULT._serialized_end=1671 + _LINTPATH_ARGS._serialized_start=1673 + _LINTPATH_ARGS._serialized_end=1702 + _LINTPATH_RESULT._serialized_start=1704 + _LINTPATH_RESULT._serialized_end=1738 + _OVERRIDEFILE_ARGS._serialized_start=1740 + _OVERRIDEFILE_ARGS._serialized_end=1810 + _OVERRIDEFILE_RESULT._serialized_start=1812 + _OVERRIDEFILE_RESULT._serialized_end=1849 + _EVALCODE_ARGS._serialized_start=1851 + _EVALCODE_ARGS._serialized_end=1880 + _EVALCODE_RESULT._serialized_start=1882 + _EVALCODE_RESULT._serialized_end=1920 + _RESOLVECODE_ARGS._serialized_start=1922 + _RESOLVECODE_ARGS._serialized_end=1954 + _RESOLVECODE_RESULT._serialized_start=1956 + _RESOLVECODE_RESULT._serialized_end=1993 + _GETSCHEMATYPE_ARGS._serialized_start=1995 + _GETSCHEMATYPE_ARGS._serialized_end=2064 + _GETSCHEMATYPE_RESULT._serialized_start=2066 + _GETSCHEMATYPE_RESULT._serialized_end=2131 + _VALIDATECODE_ARGS._serialized_start=2133 + _VALIDATECODE_ARGS._serialized_end=2236 + _VALIDATECODE_RESULT._serialized_start=2238 + _VALIDATECODE_RESULT._serialized_end=2297 + _CODESNIPPET._serialized_start=2299 + _CODESNIPPET._serialized_end=2342 + _SPLICECODE_ARGS._serialized_start=2344 + _SPLICECODE_ARGS._serialized_end=2404 + _SPLICECODE_RESULT._serialized_start=2406 + _SPLICECODE_RESULT._serialized_end=2445 + _POSITION._serialized_start=2447 + _POSITION._serialized_end=2505 + _COMPLETE_ARGS._serialized_start=2507 + _COMPLETE_ARGS._serialized_end=2581 + _COMPLETE_RESULT._serialized_start=2583 + _COMPLETE_RESULT._serialized_end=2623 + _GOTODEF_ARGS._serialized_start=2625 + _GOTODEF_ARGS._serialized_end=2684 + _GOTODEF_RESULT._serialized_start=2686 + _GOTODEF_RESULT._serialized_end=2721 + _DOCUMENTSYMBOL_ARGS._serialized_start=2723 + _DOCUMENTSYMBOL_ARGS._serialized_end=2772 + _DOCUMENTSYMBOL_RESULT._serialized_start=2774 + _DOCUMENTSYMBOL_RESULT._serialized_end=2813 + _HOVER_ARGS._serialized_start=2815 + _HOVER_ARGS._serialized_end=2872 + _HOVER_RESULT._serialized_start=2874 + _HOVER_RESULT._serialized_end=2909 + _LISTDEPFILES_ARGS._serialized_start=2911 + _LISTDEPFILES_ARGS._serialized_end=3016 + _LISTDEPFILES_RESULT._serialized_start=3018 + _LISTDEPFILES_RESULT._serialized_end=3088 + _LOADSETTINGSFILES_ARGS._serialized_start=3090 + _LOADSETTINGSFILES_ARGS._serialized_end=3147 + _LOADSETTINGSFILES_RESULT._serialized_start=3149 + _LOADSETTINGSFILES_RESULT._serialized_end=3262 + _CLICONFIG._serialized_start=3265 + _CLICONFIG._serialized_end=3431 + _KEYVALUEPAIR._serialized_start=3433 + _KEYVALUEPAIR._serialized_end=3475 + _KCLTYPE._serialized_start=3478 + _KCLTYPE._serialized_end=3850 + _KCLTYPE_PROPERTIESENTRY._serialized_start=3784 + _KCLTYPE_PROPERTIESENTRY._serialized_end=3850 + _DECORATOR._serialized_start=3853 + _DECORATOR._serialized_end=3997 + _DECORATOR_KEYWORDSENTRY._serialized_start=3950 + _DECORATOR_KEYWORDSENTRY._serialized_end=3997 + _BUILTINSERVICE._serialized_start=4000 + _BUILTINSERVICE._serialized_end=4130 + _KCLVMSERVICE._serialized_start=4133 + _KCLVMSERVICE._serialized_end=5593 +# @@protoc_insertion_point(module_scope) diff --git a/internal/kclvm_py/internal/gpyrpc/gpyrpc_pb_protorpc.py b/internal/kclvm_py/internal/gpyrpc/gpyrpc_pb_protorpc.py new file mode 100644 index 000000000..26da1ede4 --- /dev/null +++ b/internal/kclvm_py/internal/gpyrpc/gpyrpc_pb_protorpc.py @@ -0,0 +1,393 @@ +# Code generated by protoc-gen-protorpc-py. DO NOT EDIT. +# +# plugin: https://github.com/chai2010/protorpc/protoc-gen-plugin +# plugin: https://github.com/chai2010/protorpc-py/protoc-gen-protorpc-py +# +# source: gpyrpc.proto + +import abc +import sys +import typing + +from google.protobuf import message as _message + +from .protorpc import ServiceMeta as _ServiceMeta +from .protorpc import Server as _Server + +from .gpyrpc_pb2 import ( + Complete_Args, + Complete_Result, + DocumentSymbol_Args, + DocumentSymbol_Result, + EvalCode_Args, + EvalCode_Result, + ExecProgram_Args, + ExecProgram_Result, + FormatCode_Args, + FormatCode_Result, + FormatPath_Args, + FormatPath_Result, + GetSchemaType_Args, + GetSchemaType_Result, + GoToDef_Args, + GoToDef_Result, + Hover_Args, + Hover_Result, + LintPath_Args, + LintPath_Result, + ListDepFiles_Args, + ListDepFiles_Result, + ListMethod_Args, + ListMethod_Result, + LoadSettingsFiles_Args, + LoadSettingsFiles_Result, + OverrideFile_Args, + OverrideFile_Result, + ParseFile_AST_Args, + ParseFile_AST_Result, + ParseFile_LarkTree_Args, + ParseFile_LarkTree_Result, + ParseProgram_AST_Args, + ParseProgram_AST_Result, + Ping_Args, + Ping_Result, + ResetPlugin_Args, + ResetPlugin_Result, + ResolveCode_Args, + ResolveCode_Result, + SpliceCode_Args, + SpliceCode_Result, + ValidateCode_Args, + ValidateCode_Result, +) + + +class BuiltinService(metaclass=abc.ABCMeta): + @abc.abstractmethod + def Ping(self, args: Ping_Args) -> Ping_Result: + pass + + @abc.abstractmethod + def ListMethod(self, args: ListMethod_Args) -> ListMethod_Result: + pass + + +class KclvmService(metaclass=abc.ABCMeta): + @abc.abstractmethod + def Ping(self, args: Ping_Args) -> Ping_Result: + pass + + @abc.abstractmethod + def ParseFile_LarkTree( + self, args: ParseFile_LarkTree_Args + ) -> ParseFile_LarkTree_Result: + pass + + @abc.abstractmethod + def ParseFile_AST(self, args: ParseFile_AST_Args) -> ParseFile_AST_Result: + pass + + @abc.abstractmethod + def ParseProgram_AST(self, args: ParseProgram_AST_Args) -> ParseProgram_AST_Result: + pass + + @abc.abstractmethod + def ExecProgram(self, args: ExecProgram_Args) -> ExecProgram_Result: + pass + + @abc.abstractmethod + def ResetPlugin(self, args: ResetPlugin_Args) -> ResetPlugin_Result: + pass + + @abc.abstractmethod + def FormatCode(self, args: FormatCode_Args) -> FormatCode_Result: + pass + + @abc.abstractmethod + def FormatPath(self, args: FormatPath_Args) -> FormatPath_Result: + pass + + @abc.abstractmethod + def LintPath(self, args: LintPath_Args) -> LintPath_Result: + pass + + @abc.abstractmethod + def OverrideFile(self, args: OverrideFile_Args) -> OverrideFile_Result: + pass + + @abc.abstractmethod + def EvalCode(self, args: EvalCode_Args) -> EvalCode_Result: + pass + + @abc.abstractmethod + def ResolveCode(self, args: ResolveCode_Args) -> ResolveCode_Result: + pass + + @abc.abstractmethod + def GetSchemaType(self, args: GetSchemaType_Args) -> GetSchemaType_Result: + pass + + @abc.abstractmethod + def ValidateCode(self, args: ValidateCode_Args) -> ValidateCode_Result: + pass + + @abc.abstractmethod + def SpliceCode(self, args: SpliceCode_Args) -> SpliceCode_Result: + pass + + @abc.abstractmethod + def Complete(self, args: Complete_Args) -> Complete_Result: + pass + + @abc.abstractmethod + def GoToDef(self, args: GoToDef_Args) -> GoToDef_Result: + pass + + @abc.abstractmethod + def DocumentSymbol(self, args: DocumentSymbol_Args) -> DocumentSymbol_Result: + pass + + @abc.abstractmethod + def Hover(self, args: Hover_Args) -> Hover_Result: + pass + + @abc.abstractmethod + def ListDepFiles(self, args: ListDepFiles_Args) -> ListDepFiles_Result: + pass + + @abc.abstractmethod + def LoadSettingsFiles( + self, args: LoadSettingsFiles_Args + ) -> LoadSettingsFiles_Result: + pass + + +class BuiltinService_Meta(_ServiceMeta): + def __init__(self, instance: BuiltinService): + super().__init__() + self._instance = instance + + def get_service_name(self) -> str: + return "BuiltinService" + + def get_method_list(self) -> typing.List[str]: + return [ + "Ping", + "ListMethod", + ] + + def create_method_req_message(self, method: str) -> _message.Message: + if method in ["Ping", "BuiltinService.Ping"]: + return Ping_Args() + if method in ["ListMethod", "BuiltinService.ListMethod"]: + return ListMethod_Args() + raise Exception(f"unknown method: {method}") + + def create_method_resp_message(self, method: str) -> _message.Message: + if method in ["Ping", "BuiltinService.Ping"]: + return Ping_Result() + if method in ["ListMethod", "BuiltinService.ListMethod"]: + return ListMethod_Result() + raise Exception(f"unknown method: {method}") + + def get_service_instance(self) -> _message.Message: + return typing.cast(_message.Message, self._instance) + + def call_method(self, method: str, req: _message.Message) -> _message.Message: + if method in ["Ping", "BuiltinService.Ping"]: + return self._instance.Ping(req) + if method in ["ListMethod", "BuiltinService.ListMethod"]: + return self._instance.ListMethod(req) + raise Exception(f"unknown method: {method}") + + +class KclvmService_Meta(_ServiceMeta): + def __init__(self, instance: KclvmService): + super().__init__() + self._instance = instance + + def get_service_name(self) -> str: + return "KclvmService" + + def get_method_list(self) -> typing.List[str]: + return [ + "Ping", + "ParseFile_LarkTree", + "ParseFile_AST", + "ParseProgram_AST", + "ExecProgram", + "ResetPlugin", + "FormatCode", + "FormatPath", + "LintPath", + "OverrideFile", + "EvalCode", + "ResolveCode", + "GetSchemaType", + "ValidateCode", + "SpliceCode", + "Complete", + "GoToDef", + "DocumentSymbol", + "Hover", + "ListDepFiles", + "LoadSettingsFiles", + ] + + def create_method_req_message(self, method: str) -> _message.Message: + if method in ["Ping", "KclvmService.Ping"]: + return Ping_Args() + if method in ["ParseFile_LarkTree", "KclvmService.ParseFile_LarkTree"]: + return ParseFile_LarkTree_Args() + if method in ["ParseFile_AST", "KclvmService.ParseFile_AST"]: + return ParseFile_AST_Args() + if method in ["ParseProgram_AST", "KclvmService.ParseProgram_AST"]: + return ParseProgram_AST_Args() + if method in ["ExecProgram", "KclvmService.ExecProgram"]: + return ExecProgram_Args() + if method in ["ResetPlugin", "KclvmService.ResetPlugin"]: + return ResetPlugin_Args() + if method in ["FormatCode", "KclvmService.FormatCode"]: + return FormatCode_Args() + if method in ["FormatPath", "KclvmService.FormatPath"]: + return FormatPath_Args() + if method in ["LintPath", "KclvmService.LintPath"]: + return LintPath_Args() + if method in ["OverrideFile", "KclvmService.OverrideFile"]: + return OverrideFile_Args() + if method in ["EvalCode", "KclvmService.EvalCode"]: + return EvalCode_Args() + if method in ["ResolveCode", "KclvmService.ResolveCode"]: + return ResolveCode_Args() + if method in ["GetSchemaType", "KclvmService.GetSchemaType"]: + return GetSchemaType_Args() + if method in ["ValidateCode", "KclvmService.ValidateCode"]: + return ValidateCode_Args() + if method in ["SpliceCode", "KclvmService.SpliceCode"]: + return SpliceCode_Args() + if method in ["Complete", "KclvmService.Complete"]: + return Complete_Args() + if method in ["GoToDef", "KclvmService.GoToDef"]: + return GoToDef_Args() + if method in ["DocumentSymbol", "KclvmService.DocumentSymbol"]: + return DocumentSymbol_Args() + if method in ["Hover", "KclvmService.Hover"]: + return Hover_Args() + if method in ["ListDepFiles", "KclvmService.ListDepFiles"]: + return ListDepFiles_Args() + if method in ["LoadSettingsFiles", "KclvmService.LoadSettingsFiles"]: + return LoadSettingsFiles_Args() + raise Exception(f"unknown method: {method}") + + def create_method_resp_message(self, method: str) -> _message.Message: + if method in ["Ping", "KclvmService.Ping"]: + return Ping_Result() + if method in ["ParseFile_LarkTree", "KclvmService.ParseFile_LarkTree"]: + return ParseFile_LarkTree_Result() + if method in ["ParseFile_AST", "KclvmService.ParseFile_AST"]: + return ParseFile_AST_Result() + if method in ["ParseProgram_AST", "KclvmService.ParseProgram_AST"]: + return ParseProgram_AST_Result() + if method in ["ExecProgram", "KclvmService.ExecProgram"]: + return ExecProgram_Result() + if method in ["ResetPlugin", "KclvmService.ResetPlugin"]: + return ResetPlugin_Result() + if method in ["FormatCode", "KclvmService.FormatCode"]: + return FormatCode_Result() + if method in ["FormatPath", "KclvmService.FormatPath"]: + return FormatPath_Result() + if method in ["LintPath", "KclvmService.LintPath"]: + return LintPath_Result() + if method in ["OverrideFile", "KclvmService.OverrideFile"]: + return OverrideFile_Result() + if method in ["EvalCode", "KclvmService.EvalCode"]: + return EvalCode_Result() + if method in ["ResolveCode", "KclvmService.ResolveCode"]: + return ResolveCode_Result() + if method in ["GetSchemaType", "KclvmService.GetSchemaType"]: + return GetSchemaType_Result() + if method in ["ValidateCode", "KclvmService.ValidateCode"]: + return ValidateCode_Result() + if method in ["SpliceCode", "KclvmService.SpliceCode"]: + return SpliceCode_Result() + if method in ["Complete", "KclvmService.Complete"]: + return Complete_Result() + if method in ["GoToDef", "KclvmService.GoToDef"]: + return GoToDef_Result() + if method in ["DocumentSymbol", "KclvmService.DocumentSymbol"]: + return DocumentSymbol_Result() + if method in ["Hover", "KclvmService.Hover"]: + return Hover_Result() + if method in ["ListDepFiles", "KclvmService.ListDepFiles"]: + return ListDepFiles_Result() + if method in ["LoadSettingsFiles", "KclvmService.LoadSettingsFiles"]: + return LoadSettingsFiles_Result() + raise Exception(f"unknown method: {method}") + + def get_service_instance(self) -> _message.Message: + return typing.cast(_message.Message, self._instance) + + def call_method(self, method: str, req: _message.Message) -> _message.Message: + if method in ["Ping", "KclvmService.Ping"]: + return self._instance.Ping(req) + if method in ["ParseFile_LarkTree", "KclvmService.ParseFile_LarkTree"]: + return self._instance.ParseFile_LarkTree(req) + if method in ["ParseFile_AST", "KclvmService.ParseFile_AST"]: + return self._instance.ParseFile_AST(req) + if method in ["ParseProgram_AST", "KclvmService.ParseProgram_AST"]: + return self._instance.ParseProgram_AST(req) + if method in ["ExecProgram", "KclvmService.ExecProgram"]: + return self._instance.ExecProgram(req) + if method in ["ResetPlugin", "KclvmService.ResetPlugin"]: + return self._instance.ResetPlugin(req) + if method in ["FormatCode", "KclvmService.FormatCode"]: + return self._instance.FormatCode(req) + if method in ["FormatPath", "KclvmService.FormatPath"]: + return self._instance.FormatPath(req) + if method in ["LintPath", "KclvmService.LintPath"]: + return self._instance.LintPath(req) + if method in ["OverrideFile", "KclvmService.OverrideFile"]: + return self._instance.OverrideFile(req) + if method in ["EvalCode", "KclvmService.EvalCode"]: + return self._instance.EvalCode(req) + if method in ["ResolveCode", "KclvmService.ResolveCode"]: + return self._instance.ResolveCode(req) + if method in ["GetSchemaType", "KclvmService.GetSchemaType"]: + return self._instance.GetSchemaType(req) + if method in ["ValidateCode", "KclvmService.ValidateCode"]: + return self._instance.ValidateCode(req) + if method in ["SpliceCode", "KclvmService.SpliceCode"]: + return self._instance.SpliceCode(req) + if method in ["Complete", "KclvmService.Complete"]: + return self._instance.Complete(req) + if method in ["GoToDef", "KclvmService.GoToDef"]: + return self._instance.GoToDef(req) + if method in ["DocumentSymbol", "KclvmService.DocumentSymbol"]: + return self._instance.DocumentSymbol(req) + if method in ["Hover", "KclvmService.Hover"]: + return self._instance.Hover(req) + if method in ["ListDepFiles", "KclvmService.ListDepFiles"]: + return self._instance.ListDepFiles(req) + if method in ["LoadSettingsFiles", "KclvmService.LoadSettingsFiles"]: + return self._instance.LoadSettingsFiles(req) + raise Exception(f"unknown method: {method}") + + +class BuiltinService_Server: + def __init__(self, instance: BuiltinService): + self.instance = instance + + def run(self, *, stdin=sys.stdin, stdout=sys.stdout): + rpc_server = _Server() + rpc_server.register_service(BuiltinService_Meta(self.instance)) + rpc_server.run(stdin=stdin, stdout=stdout) + + +class KclvmService_Server: + def __init__(self, instance: KclvmService): + self.instance = instance + + def run(self, *, stdin=sys.stdin, stdout=sys.stdout): + rpc_server = _Server() + rpc_server.register_service(KclvmService_Meta(self.instance)) + rpc_server.run(stdin=stdin, stdout=stdout) diff --git a/internal/kclvm_py/internal/gpyrpc/protorpc.py b/internal/kclvm_py/internal/gpyrpc/protorpc.py new file mode 100644 index 000000000..1a8bd0916 --- /dev/null +++ b/internal/kclvm_py/internal/gpyrpc/protorpc.py @@ -0,0 +1,284 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import abc +import sys +import traceback +import typing + +from dataclasses import dataclass + +import kclvm.kcl.error as kcl_error +import kclvm.internal.gpyrpc.varint as varint + +from google.protobuf import message as _message +import google.protobuf.json_format as _json_format + +from .protorpc_wire_pb2 import ( + MAX_REQUEST_HEADER_LEN, + RequestHeader, + ResponseHeader, +) + + +@dataclass +class Status: + error: str = "" + + +class Channel(object): + def __init__(self, *, stdin=sys.stdin, stdout=sys.stdout): + self.stdin = stdin + self.stdout = stdout + self.next_id = 1 + + def get_next_id(self) -> int: + next_id = self.next_id + self.next_id = self.next_id + 1 + return next_id + + def send_frame(self, data: bytes): + self.stdout.buffer.write(varint.encode(len(data))) + self.stdout.buffer.write(data) + self.stdout.flush() + + def recv_frame(self, _max_size: int = 0) -> bytes: + size = varint.decode_stream(self.stdin.buffer) + + if size > _max_size > 0: + raise Exception(f"protorpc: varint overflows maxSize({_max_size})") + + data = self.stdin.buffer.read(size) + return data + + def write_request(self, method: str, req: _message.Message): + body = req.SerializeToString() + + hdr = RequestHeader( + id=self.get_next_id(), method=method, raw_request_len=len(body) + ) + self.send_frame(hdr.SerializeToString()) + self.send_frame(body) + + def read_request_header(self) -> RequestHeader: + data = self.recv_frame(MAX_REQUEST_HEADER_LEN) + hdr = RequestHeader() + hdr.ParseFromString(data) + + if hdr.snappy_compressed_request_len != 0: + raise Exception("py: unsupport snappy compressed request") + if hdr.checksum != 0: + raise Exception("py: unsupport checksum request") + + return hdr + + def read_request_body(self, header: RequestHeader, body: _message.Message): + data = self.recv_frame( + max(header.raw_request_len, header.snappy_compressed_request_len) + ) + body.ParseFromString(data) + + def write_response(self, id_: int, error: str, response: _message.Message): + body = response.SerializeToString() + hdr = ResponseHeader(id=id_, error=error, raw_response_len=len(body)) + self.send_frame(hdr.SerializeToString()) + self.send_frame(body) + + def read_response_header(self) -> ResponseHeader: + data = self.recv_frame(0) + hdr = ResponseHeader() + hdr.ParseFromString(data) + + if hdr.snappy_compressed_response_len != 0: + raise Exception("py: unsupport snappy compressed response") + if hdr.checksum != 0: + raise Exception("py: unsupport checksum response") + + return hdr + + def read_response_body(self, header: ResponseHeader, body: _message.Message): + data = self.recv_frame( + max(header.raw_response_len, header.snappy_compressed_response_len) + ) + body.ParseFromString(data) + + def call_method( + self, method: str, req: _message.Message, resp: _message.Message + ) -> Status: + self.write_request(method, req) + resp_hdr = self.read_response_header() + self.read_response_body(resp_hdr, resp) + return Status(error=resp_hdr.error) + + +class ServiceMeta(metaclass=abc.ABCMeta): + @abc.abstractmethod + def get_service_name(self) -> str: + pass + + @abc.abstractmethod + def get_method_list(self) -> typing.List[str]: + pass + + @abc.abstractmethod + def create_method_req_message(self, method: str) -> _message.Message: + pass + + @abc.abstractmethod + def create_method_resp_message(self, method: str) -> _message.Message: + pass + + @abc.abstractmethod + def get_service_instance(self) -> _message.Message: + pass + + @abc.abstractmethod + def call_method(self, method: str, req: _message.Message) -> _message.Message: + pass + + +class Server: + def __init__(self): + self.srv_table: typing.Dict[str, ServiceMeta] = {} + self.chan: typing.Optional[Channel] = None + + def register_service(self, srv: ServiceMeta): + self.srv_table[srv.get_service_name()] = srv + + def get_service_name_list(self) -> typing.List[str]: + name_list: typing.List[str] = [] + for s in self.srv_table.keys(): + name_list.append(s) + name_list.sort() + return name_list + + def get_method_name_list(self) -> typing.List[str]: + name_list: typing.List[str] = [] + for srv in self.srv_table.values(): + srv_name = srv.get_service_name() + for method_name in srv.get_method_list(): + name_list.append(f"{srv_name}.{method_name}") + name_list.sort() + return name_list + + def run(self, *, stdin=sys.stdin, stdout=sys.stdout): + self.chan = Channel(stdin=stdin, stdout=stdout) + while True: + self._accept_one_call() + + def run_once(self, *, stdin=sys.stdin, stdout=sys.stdout): + self.chan = Channel(stdin=stdin, stdout=stdout) + self._accept_one_call() + + def call_method( + self, method_path: str, req_body: bytes, *, encoding: str = "json" + ) -> dict: + if encoding not in ["json", "protobuf"]: + raise Exception(f"encoding '{encoding}' not support") + + service_name = method_path[: method_path.rfind(".")] + method_name = method_path[method_path.rfind(".") + 1 :] + + if service_name not in self.srv_table: + raise Exception(f"service '{service_name}' not found") + + service = self.srv_table[service_name] + + req = service.create_method_req_message(method_name) + + if encoding == "json": + _json_format.Parse(req_body, req) + else: + req.ParseFromString(req_body) + + try: + resp = service.call_method(method_name, req) + + except kcl_error.KCLException as err: + raise err + + except OSError as err: + err_message = f"OSError: {err}" + raise Exception(err_message) + + except AssertionError as err: + err_message = f"AssertionError: {err}" + raise Exception(err_message) + + except Exception as err: + err_message = f"Exception: Internal Error! Please report a bug to us: method={method_name}, err={err}, stack trace={traceback.format_exc()}" + raise Exception(err_message) + + # return response + # https://googleapis.dev/python/protobuf/latest/google/protobuf/json_format.html + return _json_format.MessageToDict( + resp, + including_default_value_fields=True, + preserving_proto_field_name=True, + ) + + def _accept_one_call(self): + hdr = self._read_req_header() + + service_name = hdr.method[: hdr.method.rfind(".")] + method_name = hdr.method[hdr.method.rfind(".") + 1 :] + + if service_name not in self.srv_table: + raise Exception(f"service '{service_name}' not found") + + service = self.srv_table[service_name] + + req = self._read_req(service, hdr) + + try: + resp = service.call_method(method_name, req) + self._write_resp(hdr.id, "", resp) + return + + except kcl_error.KCLException as err: + resp = service.create_method_resp_message(method_name) + + err_message = f"{err}" + self._write_resp(hdr.id, err_message, resp) + return + + except OSError as err: + resp = service.create_method_resp_message(method_name) + + err_message = f"OSError: {err}" + self._write_resp(hdr.id, err_message, resp) + return + + except AssertionError as err: + resp = service.create_method_resp_message(method_name) + + err_message = f"AssertionError: {err}" + self._write_resp(hdr.id, err_message, resp) + return + + except Exception as err: + resp = service.create_method_resp_message(method_name) + + err_message = f"Exception: Internal Error! Please report a bug to us: method={method_name}, err={err}, stack trace={traceback.format_exc()}" + self._write_resp(hdr.id, err_message, resp) + return + + def _read_req_header(self) -> RequestHeader: + return self.chan.read_request_header() + + def _read_req(self, service: ServiceMeta, hdr: RequestHeader) -> _message.Message: + req = service.create_method_req_message(hdr.method) + self.chan.read_request_body(hdr, req) + return req + + def _write_resp(self, id_: int, error: str, resp: _message.Message): + self.chan.write_response(id_, error, resp) + + +class Client: + def __init__(self, chan: Channel = None): + self.chan: Channel = chan + + def call_method( + self, method: str, req: _message.Message, resp: _message.Message + ) -> Status: + return self.chan.call_method(method, req, resp) diff --git a/internal/kclvm_py/internal/gpyrpc/protorpc_wire_pb2.py b/internal/kclvm_py/internal/gpyrpc/protorpc_wire_pb2.py new file mode 100644 index 000000000..5e14f945f --- /dev/null +++ b/internal/kclvm_py/internal/gpyrpc/protorpc_wire_pb2.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: gpyrpc/protorpc_wire.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1agpyrpc/protorpc_wire.proto\x12\rprotorpc_wire\"}\n\rRequestHeader\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0e\n\x06method\x18\x02 \x01(\t\x12\x17\n\x0fraw_request_len\x18\x03 \x01(\r\x12%\n\x1dsnappy_compressed_request_len\x18\x04 \x01(\r\x12\x10\n\x08\x63hecksum\x18\x05 \x01(\r\"\x7f\n\x0eResponseHeader\x12\n\n\x02id\x18\x01 \x01(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12\x18\n\x10raw_response_len\x18\x03 \x01(\r\x12&\n\x1esnappy_compressed_response_len\x18\x04 \x01(\r\x12\x10\n\x08\x63hecksum\x18\x05 \x01(\r*.\n\x05\x43onst\x12\x08\n\x04ZERO\x10\x00\x12\x1b\n\x16MAX_REQUEST_HEADER_LEN\x10\x80\x08\x62\x06proto3') + +_CONST = DESCRIPTOR.enum_types_by_name['Const'] +Const = enum_type_wrapper.EnumTypeWrapper(_CONST) +ZERO = 0 +MAX_REQUEST_HEADER_LEN = 1024 + + +_REQUESTHEADER = DESCRIPTOR.message_types_by_name['RequestHeader'] +_RESPONSEHEADER = DESCRIPTOR.message_types_by_name['ResponseHeader'] +RequestHeader = _reflection.GeneratedProtocolMessageType('RequestHeader', (_message.Message,), { + 'DESCRIPTOR' : _REQUESTHEADER, + '__module__' : 'gpyrpc.protorpc_wire_pb2' + # @@protoc_insertion_point(class_scope:protorpc_wire.RequestHeader) + }) +_sym_db.RegisterMessage(RequestHeader) + +ResponseHeader = _reflection.GeneratedProtocolMessageType('ResponseHeader', (_message.Message,), { + 'DESCRIPTOR' : _RESPONSEHEADER, + '__module__' : 'gpyrpc.protorpc_wire_pb2' + # @@protoc_insertion_point(class_scope:protorpc_wire.ResponseHeader) + }) +_sym_db.RegisterMessage(ResponseHeader) + +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _CONST._serialized_start=301 + _CONST._serialized_end=347 + _REQUESTHEADER._serialized_start=45 + _REQUESTHEADER._serialized_end=170 + _RESPONSEHEADER._serialized_start=172 + _RESPONSEHEADER._serialized_end=299 +# @@protoc_insertion_point(module_scope) diff --git a/internal/kclvm_py/internal/gpyrpc/varint.py b/internal/kclvm_py/internal/gpyrpc/varint.py new file mode 100644 index 000000000..c06f3466f --- /dev/null +++ b/internal/kclvm_py/internal/gpyrpc/varint.py @@ -0,0 +1,71 @@ +"""Varint encoder/decoder + +varints are a common encoding for variable length integer data, used in +libraries such as sqlite, protobuf, v8, and more. + +Here's a quick and dirty module to help avoid reimplementing the same thing +over and over again. +""" + +# byte-oriented StringIO was moved to io.BytesIO in py3k +try: + from io import BytesIO +except ImportError: + from StringIO import StringIO as BytesIO + +import sys + +if sys.version > "3": + + def _byte(b): + return bytes((b,)) + + +else: + + def _byte(b): + return chr(b) + + +def encode(number): + """Pack `number` into varint bytes""" + buf = b"" + while True: + towrite = number & 0x7F + number >>= 7 + if number: + buf += _byte(towrite | 0x80) + else: + buf += _byte(towrite) + break + return buf + + +def decode_stream(stream): + """Read a varint from `stream`""" + shift = 0 + result = 0 + while True: + i = _read_one(stream) + result |= (i & 0x7F) << shift + shift += 7 + if not (i & 0x80): + break + + return result + + +def decode_bytes(buf): + """Read a varint from from `buf` bytes""" + return decode_stream(BytesIO(buf)) + + +def _read_one(stream): + """Read a byte from the file (as an integer) + + raises EOFError if the stream ends while reading bytes. + """ + c = stream.read(1) + if c == b"": + raise EOFError("Unexpected EOF while reading bytes") + return ord(c) diff --git a/internal/kclvm_py/internal/kclvm_internal/__init__.py b/internal/kclvm_py/internal/kclvm_internal/__init__.py new file mode 100644 index 000000000..d936bb260 --- /dev/null +++ b/internal/kclvm_py/internal/kclvm_internal/__init__.py @@ -0,0 +1,9 @@ +import kclvm.config as config +import kclvm.api.version + +from .main import Main + +__all__ = ["Main"] + +__version__ = kclvm.api.version.VERSION +config.version = __version__ diff --git a/internal/kclvm_py/internal/kclvm_internal/main.py b/internal/kclvm_py/internal/kclvm_internal/main.py new file mode 100644 index 000000000..c43f58051 --- /dev/null +++ b/internal/kclvm_py/internal/kclvm_internal/main.py @@ -0,0 +1,245 @@ +"""Expose KCLVM command via ``python -m kclvm``, or ``kcl`` for short.""" +import sys +import os +import argparse +import cProfile +import typing + +import kclvm.config +import kclvm.kcl.ast as kcl_ast +import kclvm.program.exec as kclvm_exec +import kclvm.compiler.parser +import kclvm.kcl.error as kcl_error +import kclvm.internal.log as klog +import kclvm.tools.format +import kclvm.tools.docs +import kclvm.api.version +import kclvm.vm.planner as planner + +from kclvm.api.object.internal import kcl_option_init_all +from kclvm.tools.list_attribute.utils import ListAttributePrinter + + +def Main(): + try: + parser = argparse.ArgumentParser( + prog="kcl", description="K Configuration Language Virtual Machine" + ) + parser.add_argument( + "-D", + "--argument", + default=[], + action=kclvm.config.KCLTopLevelArgumentAction, + help="Specify the top-level argument", + required=False, + ) + parser.add_argument( + "-S", + "--path-selector", + default=[], + action=kclvm.config.KCLPathSelectorAction, + help="Specify the path selector", + required=False, + ) + parser.add_argument( + "-O", + "--overrides", + default=[], + action=kclvm.config.KCLOverrideAction, + help="Specify the configuration override path and value", + required=False, + ) + parser.add_argument( + "-Y", + "--setting", + help="Specify the command line setting file", + nargs="*", + required=False, + ) + parser.add_argument( + "-o", "--output", help="Specify the output file", required=False + ) + parser.add_argument( + "-n", + "--disable-none", + help="Disable dumping None values", + action="store_true", + required=False, + ) + parser.add_argument( + "--sort", + help="Sort result keys", + dest="sort_keys", + action="store_true", + required=False, + ) + parser.add_argument( + "-r", + "--strict-range-check", + help="Do perform strict numeric range check", + action="store_true", + required=False, + ) + parser.add_argument( + "-c", + "--compile-only", + help="Compile only", + action="store_true", + required=False, + ) + parser.add_argument( + "-s", + "--save-temps", + help="Save intermediate files", + action="store_true", + required=False, + ) + parser.add_argument( + "-v", + "--verbose", + help="Run in verbose mode", + action="count", + default=0, + required=False, + ) + parser.add_argument( + "-d", + "--debug", + help="Run in debug mode (for developers only)", + action="count", + default=0, + required=False, + ) + parser.add_argument( + "-p", + "--profile", + help="Perform profiling", + action="store_true", + required=False, + ) + parser.add_argument( + "-L", + "--list-attributes", + dest="show_attribute_list", + help="Show schema attributes list", + action="store_true", + ) + parser.add_argument( + "-l", + "--list-options", + dest="list_option_mode", + default=0, + action="count", + help="Show kcl options list", + ) + parser.add_argument( + "-V", + "--version", + help="Show the kclvm version", + action="version", + version=f"kclvm version is {kclvm.api.version.VERSION}; " + f"checksum: {kclvm.api.version.CHECKSUM}", + ) + parser.add_argument("file", help="Input compile file", nargs="*") + parser.add_argument( + "--target", + help="Specify the target type", + type=str, + default="", + choices=["native", "wasm"], + required=False, + ) + + args = parser.parse_args() + if len(sys.argv) == 1: + parser.print_help(sys.stdout) + sys.exit(0) + + argsdict = vars(args) + kclvm.config.current_path = os.getcwd() + # 1. Deal KCL CLI using settings file + kclvm.config.arguments = kclvm.config.KCLCLISettingAction().deal( + argsdict["setting"] + ) + # 2. Deal KCL CLI config using CLI arguments + kclvm.config.parse_config(argsdict) + + compile_only = argsdict["compile_only"] + target = argsdict["target"] + + if args.list_option_mode > 0: + kclvm.config.list_option_mode = args.list_option_mode + kclvm.config.disable_schema_check = True + + kclvm.config.dump() + + def kcl_main(): + kcl_option_init_all() + if kclvm.config.input_file: + files = kclvm.config.input_file + if args.show_attribute_list: + for file in files: + ListAttributePrinter(file).print() + exit(0) + + overrides: typing.List[kcl_ast.CmdOverrideSpec] = [] + for x in kclvm.config.overrides: + if len(x) == 4: + overrides.append( + kcl_ast.CmdOverrideSpec( + pkgpath=x[0], + field_path=x[1], + field_value=x[2], + action=x[3], + ) + ) + + output = kclvm_exec.Run( + files, + cmd_overrides=overrides, + print_override_ast=len(overrides) > 0 and kclvm.config.debug, + target=f"{target}", + ) + + if kclvm.config.list_option_mode > 0: + print(kclvm.config.options_help_message) + exit(0) + if not compile_only: + output = planner.YAMLPlanner(sort_keys=args.sort_keys).plan( + output.filter_by_path_selector( + to_kcl=not ( + kclvm.config.is_target_native + or kclvm.config.is_target_wasm + ) + ), + to_py=not ( + kclvm.config.is_target_native or kclvm.config.is_target_wasm + ), + ) + klog.write_out(output) + return + + if argsdict["profile"]: + cProfile.runctx("kcl_main()", None, locals()) + else: + kcl_main() + except kcl_error.KCLException as err: + if kclvm.config.debug and kclvm.config.verbose > 2: + raise err + kcl_error.print_kcl_error_message(err, file=sys.stderr) + sys.exit(1) + except OSError as err: + if kclvm.config.debug and kclvm.config.verbose > 2: + raise err + kcl_error.print_common_error_message(err, file=sys.stderr) + sys.exit(1) + except AssertionError as err: + kcl_error.print_internal_error_message(err, file=sys.stderr) + raise + except Exception: + kcl_error.print_internal_error_message(file=sys.stderr) + raise + + +if __name__ == "__main__": + Main() diff --git a/internal/kclvm_py/internal/kclx/__init__.py b/internal/kclvm_py/internal/kclx/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/internal/kclvm_py/internal/kclx/transformer.py b/internal/kclvm_py/internal/kclx/transformer.py new file mode 100644 index 000000000..3d5322d77 --- /dev/null +++ b/internal/kclvm_py/internal/kclx/transformer.py @@ -0,0 +1,973 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing +from dataclasses import dataclass + +import kclvm.config +import kclvm.kcl.error as kcl_error +import kclvm.kcl.info as kcl_info +import kclvm.kcl.ast as ast + + +KCLX_NODE_FIELD = "node" +BIN_OP_MAPPING = { + ast.BinOp.Add: "Add", + ast.BinOp.Sub: "Sub", + ast.BinOp.Mul: "Mul", + ast.BinOp.Div: "Div", + ast.BinOp.Mod: "Mod", + ast.BinOp.Pow: "Pow", + ast.BinOp.LShift: "LShift", + ast.BinOp.RShift: "RShift", + ast.BinOp.BitOr: "BitOr", + ast.BinOp.BitAnd: "BitAnd", + ast.BinOp.BitXor: "BitXor", + ast.BinOp.FloorDiv: "FloorDiv", + ast.BinOp.As: "As", + ast.BinOp.And: "And", + ast.BinOp.Or: "Or", +} +AUG_OP_MAPPING = { + ast.AugOp.Assign: "Assign", + ast.AugOp.Add: "Add", + ast.AugOp.Sub: "Sub", + ast.AugOp.Mul: "Mul", + ast.AugOp.Div: "Div", + ast.AugOp.Mod: "Mod", + ast.AugOp.Pow: "Pow", + ast.AugOp.LShift: "LShift", + ast.AugOp.RShift: "RShift", + ast.AugOp.BitOr: "BitOr", + ast.AugOp.BitXor: "BitXor", + ast.AugOp.BitAnd: "BitAnd", + ast.AugOp.FloorDiv: "FloorDiv", +} +UNARY_OP_MAPPING = { + ast.UnaryOp.UAdd: "UAdd", + ast.UnaryOp.USub: "USub", + ast.UnaryOp.Invert: "Invert", + ast.UnaryOp.Not: "Not", +} +CMP_OP_MAPPING = { + ast.CmpOp.Eq: "Eq", + ast.CmpOp.NotEq: "NotEq", + ast.CmpOp.Lt: "Lt", + ast.CmpOp.LtE: "LtE", + ast.CmpOp.Gt: "Gt", + ast.CmpOp.GtE: "GtE", + ast.CmpOp.Is: "Is", + ast.CmpOp.In: "In", + ast.CmpOp.Not: "Not", + ast.CmpOp.IsNot: "IsNot", + ast.CmpOp.NotIn: "NotIn", +} +QUANT_OP_MAPPING = { + ast.QuantOperation.ALL: "All", + ast.QuantOperation.ANY: "Any", + ast.QuantOperation.FILTER: "Filter", + ast.QuantOperation.MAP: "Map", +} +CONFIG_ENTRY_OP_MAPPING = { + ast.ConfigEntryOperation.UNION: "Union", + ast.ConfigEntryOperation.OVERRIDE: "Override", + ast.ConfigEntryOperation.INSERT: "Insert", +} +EXPR_CTX_MAPPING = { + ast.ExprContext.LOAD: "Load", + ast.ExprContext.STORE: "Store", + ast.ExprContext.AUGLOAD: "Load", + ast.ExprContext.AUGSTORE: "Store", + ast.ExprContext.DEL: "Del", +} +OVERRIDE_ACTION_MAPPING = { + ast.OverrideAction.CREATE_OR_UPDATE: "CreateOrUpdate", + ast.OverrideAction.DELETE: "Delete", +} +TYPE_KCLX_ENUM_MAPPING = { + # Stmt + "TypeAliasStmt": "TypeAlias", + "UnificationStmt": "Unification", + "AssignStmt": "Assign", + "AugAssignStmt": "AugAssign", + "AssertStmt": "Assert", + "IfStmt": "If", + "ImportStmt": "Import", + "SchemaIndexSignature": "SchemaIndexSignature", + "SchemaAttr": "SchemaAttr", + "SchemaStmt": "Schema", + "RuleStmt": "Rule", + # Expr + "Identifier": "Identifier", + "UnaryExpr": "Unary", + "BinaryExpr": "Binary", + "IfExpr": "If", + "SelectorExpr": "Selector", + "CallExpr": "Call", + "ParenExpr": "Paren", + "QuantExpr": "Quant", + "ListExpr": "List", + "ListIfItemExpr": "ListIfItem", + "ListComp": "ListComp", + "StarredExpr": "Starred", + "DictComp": "DictComp", + "ConfigIfEntryExpr": "ConfigIfEntry", + "CompClause": "CompClause", + "SchemaExpr": "Schema", + "ConfigExpr": "Config", + "ConfigEntry": "ConfigEntry", + "CheckExpr": "Check", + "LambdaExpr": "Lambda", + "Decorator": "Decorator", + "Subscript": "Subscript", + "Keyword": "Keyword", + "Arguments": "Arguments", + "Compare": "Compare", + "NumberLit": "NumberLit", + "StringLit": "StringLit", + "NameConstantLit": "NameConstantLit", + "JoinedString": "JoinedString", + "FormattedValue": "FormattedValue", +} +INIT_FILENAME = "" +INIT_POS = 1 + + +@dataclass +class KCLxNode: + filename: str + line: int + column: int + end_line: int + end_column: int + + +class BaseKCLxASTTransformer(ast.TreeWalker): + @staticmethod + def ast_meta_to_dict(t: ast.AST) -> dict: + return KCLxNode( + filename=t.filename or INIT_FILENAME, + line=t.line or INIT_POS, + column=t.column or INIT_POS, + end_line=t.end_line or t.line or INIT_POS, + end_column=t.end_column or t.end_column or INIT_POS, + ).__dict__ + + def get_node_name(self, t: ast.AST): + return t.type + + def stmts(self, stmts: typing.List[ast.Stmt]): + return [self.stmt(stmt) for stmt in stmts or []] + + def exprs(self, exprs: typing.List[ast.Expr], with_enum_name: bool = False): + return [self.expr(expr, with_enum_name) for expr in exprs or []] + + def expr(self, expr: ast.Expr, with_enum_name: bool = False): + expr_value = self.walk(expr) if expr else None + if with_enum_name and expr_value and KCLX_NODE_FIELD in expr_value: + expr_value[KCLX_NODE_FIELD] = { + TYPE_KCLX_ENUM_MAPPING[expr._ast_type]: expr_value[KCLX_NODE_FIELD] + } + return expr_value + + def stmt(self, stmt: ast.Expr): + return self.walk(stmt) if stmt else None + + +class KCLxASTTransformer(BaseKCLxASTTransformer): + """TODO: Transform the Python KCL AST to the KCLx AST""" + + def walk_Module(self, t: ast.Module): + data = self.ast_meta_to_dict(t) + data.update( + { + "filename": t.filename, + "pkg": t.pkg, + "doc": t.doc, + "name": t.name, + "body": self.stmts(t.body), + "comments": self.exprs(t.comments), + } + ) + return data + + def walk_TypeAliasStmt(self, t: ast.TypeAliasStmt): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + TYPE_KCLX_ENUM_MAPPING[t._ast_type]: { + "type_name": self.expr(t.type_name), + "type_value": { + KCLX_NODE_FIELD: t.type_value.plain_type_str, + **self.ast_meta_to_dict(t), + }, + } + } + } + ) + return data + + def walk_ExprStmt(self, t: ast.ExprStmt): + data = self.ast_meta_to_dict(t) + data.update({KCLX_NODE_FIELD: {"Expr": {"exprs": self.exprs(t.exprs, True)}}}) + return data + + def walk_UnificationStmt(self, t: ast.UnificationStmt): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + TYPE_KCLX_ENUM_MAPPING[t._ast_type]: { + "target": self.expr(t.target), + "value": self.expr(t.value), + } + } + } + ) + return data + + def walk_AssignStmt(self, t: ast.AssignStmt): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + TYPE_KCLX_ENUM_MAPPING[t._ast_type]: { + "targets": self.exprs(t.targets), + "value": self.expr(t.value, True), + "type_annotation": { + KCLX_NODE_FIELD: t.type_annotation, + **self.ast_meta_to_dict(t), + } + if t.type_annotation + else None, + } + } + } + ) + return data + + def walk_AugAssignStmt(self, t: ast.AugAssignStmt): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + TYPE_KCLX_ENUM_MAPPING[t._ast_type]: { + "target": self.expr(t.target), + "value": self.expr(t.value, True), + "op": AUG_OP_MAPPING[t.op], + } + } + } + ) + return data + + def walk_AssertStmt(self, t: ast.AssertStmt): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + TYPE_KCLX_ENUM_MAPPING[t._ast_type]: { + "test": self.expr(t.test, True), + "if_cond": self.expr(t.if_cond, True), + "msg": self.expr(t.msg, True), + } + } + } + ) + return data + + def walk_IfStmt(self, t: ast.IfStmt): + data = self.ast_meta_to_dict(t) + elif_stmt = None + if t.elif_cond and t.elif_body: + elif_stmt = ast.IfStmt() + elif_stmt.set_ast_position(t) + elif_stmt.cond = t.elif_cond[0] + elif_stmt.body = t.elif_body[0] + elif_stmt.elif_cond = t.elif_cond[1:] + elif_stmt.elif_body = t.elif_body[1:] + elif_stmt.else_body = t.else_body + data.update( + { + KCLX_NODE_FIELD: { + TYPE_KCLX_ENUM_MAPPING[t._ast_type]: { + "cond": self.expr(t.cond, True), + "body": self.stmts(t.body), + "orelse": self.stmts([elif_stmt]) + if elif_stmt + else self.stmts(t.else_body), + } + } + } + ) + return data + + def walk_ImportStmt(self, t: ast.ImportStmt): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + TYPE_KCLX_ENUM_MAPPING[t._ast_type]: { + "path": t.path, + "rawpath": t.rawpath, + "name": t.name, + "asname": t.asname, + } + } + } + ) + return data + + def walk_SchemaIndexSignature(self, t: ast.SchemaIndexSignature): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "key_name": t.key_name, + "value": self.expr(t.value, True), + "key_type": { + KCLX_NODE_FIELD: t.key_type, + **self.ast_meta_to_dict(t), + }, + "value_type": { + KCLX_NODE_FIELD: t.value_type, + **self.ast_meta_to_dict(t.value_type_node), + }, + "any_other": t.any_other, + } + } + ) + return data + + def walk_SchemaAttr(self, t: ast.SchemaAttr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + TYPE_KCLX_ENUM_MAPPING[t._ast_type]: { + "doc": t.doc, + "name": { + KCLX_NODE_FIELD: t.name, + **self.ast_meta_to_dict(t), + }, + "type_str": { + KCLX_NODE_FIELD: t.type_str, + **self.ast_meta_to_dict(t), + }, + "value": self.expr(t.value, True), + "op": {"Bin": BIN_OP_MAPPING[t.op]} + if isinstance(t.op, ast.BinOp) + else ( + {"Aug": AUG_OP_MAPPING[t.op]} + if isinstance(t.op, ast.AugOp) + else None + ), + "is_optional": t.is_optional, + "decorators": self.exprs(t.decorators), + } + } + } + ) + return data + + def walk_SchemaStmt(self, t: ast.SchemaStmt): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + TYPE_KCLX_ENUM_MAPPING[t._ast_type]: { + "doc": t.doc, + "name": { + KCLX_NODE_FIELD: t.name, + **self.ast_meta_to_dict(t.name_node), + }, + "parent_name": self.expr(t.parent_name), + "for_host_name": self.expr(t.for_host_name), + "is_mixin": t.is_mixin, + "is_protocol": t.is_protocol, + "args": self.expr(t.args), + "mixins": self.exprs(t.mixins), + "body": self.stmts(t.body), + "decorators": self.exprs(t.decorators), + "checks": self.exprs(t.checks), + "index_signature": self.stmt(t.index_signature), + } + } + } + ) + return data + + def walk_RuleStmt(self, t: ast.RuleStmt): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + TYPE_KCLX_ENUM_MAPPING[t._ast_type]: { + "doc": t.doc, + "name": { + KCLX_NODE_FIELD: t.name, + **self.ast_meta_to_dict(t.name_node), + }, + "parent_rules": self.exprs(t.parent_rules), + "for_host_name": self.expr(t.for_host_name), + "args": self.expr(t.args), + "decorators": self.exprs(t.decorators), + "checks": self.exprs(t.checks), + } + } + } + ) + return data + + def walk_Identifier(self, t: ast.Identifier): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "names": t.names, + "pkgpath": t.names[0] + if t.names[0].startswith("@") + else (t.pkgpath or ""), + "ctx": EXPR_CTX_MAPPING.get(t.ctx, "Load"), + } + } + ) + return data + + def walk_UnaryExpr(self, t: ast.UnaryExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "op": UNARY_OP_MAPPING[t.op], + "operand": self.expr(t.operand, True), + } + } + ) + return data + + def walk_BinaryExpr(self, t: ast.BinaryExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "left": self.expr(t.left, True), + "op": {"Bin": BIN_OP_MAPPING[t.op]} + if isinstance(t.op, ast.BinOp) + else {"Cmp": CMP_OP_MAPPING[t.op]}, + "right": self.expr(t.right, True), + } + } + ) + return data + + def walk_IfExpr(self, t: ast.IfExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "body": self.expr(t.body, True), + "cond": self.expr(t.cond, True), + "orelse": self.expr(t.orelse, True), + } + } + ) + return data + + def walk_SelectorExpr(self, t: ast.SelectorExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "value": self.expr(t.value, True), + "attr": self.expr(t.attr), + "ctx": EXPR_CTX_MAPPING[t.ctx], + "has_question": t.has_question, + } + } + ) + return data + + def walk_CallExpr(self, t: ast.CallExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "func": self.expr(t.func, True), + "args": self.exprs(t.args, True), + "keywords": self.exprs(t.keywords), + } + } + ) + return data + + def walk_ParenExpr(self, t: ast.ParenExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "expr": self.expr(t.expr, True), + } + } + ) + return data + + def walk_QuantExpr(self, t: ast.QuantExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "target": self.expr(t.target, True), + "variables": self.exprs(t.variables), + "op": QUANT_OP_MAPPING[t.op], + "test": self.expr(t.test, True), + "if_cond": self.expr(t.if_cond, True), + "ctx": EXPR_CTX_MAPPING[t.ctx], + } + } + ) + return data + + def walk_ListExpr(self, t: ast.ListExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "elts": self.exprs(t.elts, True), + "ctx": EXPR_CTX_MAPPING[t.ctx], + } + } + ) + return data + + def walk_ListIfItemExpr(self, t: ast.ListIfItemExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "if_cond": self.expr(t.if_cond, True), + "exprs": self.exprs(t.exprs, True), + "orelse": self.expr(t.orelse, True), + } + } + ) + return data + + def walk_ListComp(self, t: ast.ListComp): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "elt": self.expr(t.elt, True), + "generators": self.exprs(t.generators), + } + } + ) + return data + + def walk_StarredExpr(self, t: ast.StarredExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "value": self.expr(t.value, True), + "ctx": EXPR_CTX_MAPPING[t.ctx], + } + } + ) + return data + + def walk_DictComp(self, t: ast.DictComp): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "entry": { + "key": self.expr(t.key, True), + "value": self.expr(t.value, True), + "operation": CONFIG_ENTRY_OP_MAPPING[t.operation], + "insert_index": -1, + }, + "generators": self.exprs(t.generators), + } + } + ) + return data + + def walk_ConfigIfEntryExpr(self, t: ast.ConfigIfEntryExpr): + data = self.ast_meta_to_dict(t) + keys = [] + values = [] + for key, value, operation in zip(t.keys, t.values, t.operations): + is_nest_key = isinstance(key, ast.Identifier) and len(key.names) > 1 + if is_nest_key: + names = key.names + key.names = [key.names[0]] + for i, name in enumerate(names[1:][::-1]): + is_last_item = i == 0 + name_node = ast.Identifier( + names=[name], line=key.line, column=key.column + ) + name_node.filename = t.filename + entry_value = ast.ASTFactory.get_ast_configentry( + name_node, + value, + operation if is_last_item else ast.ConfigEntryOperation.UNION, + t.filename, + ) + value = ast.ConfigExpr(line=key.line, column=key.column) + value.filename = t.filename + value.items.append(entry_value) + keys.append(key) + values.append(value) + t.keys = keys + t.values = values + items = [] + for key, value, op in zip(t.keys, t.values, t.operations): + items.append( + ast.ConfigEntry( + line=key.line if key else value.line, + column=key.column if key else value.column, + key=key, + value=value, + operation=op, + ) + ) + data.update( + { + KCLX_NODE_FIELD: { + "if_cond": self.expr(t.if_cond, True), + "items": self.exprs(items), + "orelse": self.expr(t.orelse, True), + } + } + ) + return data + + def walk_CompClause(self, t: ast.CompClause): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "targets": self.exprs(t.targets), + "iter": self.expr(t.iter, True), + "ifs": self.exprs(t.ifs, True), + } + } + ) + return data + + def walk_SchemaExpr(self, t: ast.SchemaExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "name": self.expr(t.name), + "args": self.exprs(t.args, True), + "kwargs": self.exprs(t.kwargs), + "config": self.expr(t.config, True), + } + } + ) + return data + + def walk_ConfigExpr(self, t: ast.ConfigExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "items": self.exprs(t.items), + } + } + ) + return data + + def walk_ConfigEntry(self, t: ast.ConfigEntry): + # Unpack the nest var form `a.b.c = 1` to `a: {b: {c = 1}}` + is_nest_key = isinstance(t.key, ast.Identifier) and len(t.key.names) > 1 + if is_nest_key: + names = t.key.names + value = t.value + t.key.names = [t.key.names[0]] + for i, name in enumerate(names[1:][::-1]): + is_last_item = i == 0 + name_node = ast.Identifier( + names=[name], line=t.key.line, column=t.key.column + ) + name_node.filename = t.filename + entry_value = ast.ASTFactory.get_ast_configentry( + name_node, + value, + t.operation if is_last_item else ast.ConfigEntryOperation.UNION, + t.filename, + ) + value = ast.ConfigExpr(line=t.key.line, column=t.key.column) + value.filename = t.filename + value.items.append(entry_value) + t.value = value + t.operation = ast.ConfigEntryOperation.UNION + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "key": self.expr(t.key, True), + "value": self.expr(t.value, True), + "operation": CONFIG_ENTRY_OP_MAPPING[ + ast.ConfigEntryOperation.UNION if is_nest_key else t.operation + ], + "insert_index": t.insert_index, + } + } + ) + return data + + def walk_CheckExpr(self, t: ast.CheckExpr): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "test": self.expr(t.test, True), + "if_cond": self.expr(t.if_cond, True), + "msg": self.expr(t.msg, True), + } + } + ) + return data + + def walk_LambdaExpr(self, t: ast.LambdaExpr): + """ast.AST: LambdaExpr + + Parameters + ---------- + - args: Optional[Arguments] + - return_type_str: Optional[str] + - return_type_node: Optional[Type] + - body: List[Stmt] + """ + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "args": self.expr(t.args), + "return_type_str": t.return_type_str, + "body": self.stmts(t.body), + } + } + ) + return data + + def walk_Decorator(self, t: ast.Decorator): + name = self.expr(t.name, True) + call_expr = self.expr(t.args) + if call_expr: + call_expr[KCLX_NODE_FIELD]["func"] = name + return call_expr + else: + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "func": name, + "args": [], + "keywords": [], + } + } + ) + return data + + def walk_Subscript(self, t: ast.Subscript): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "value": self.expr(t.value, True), + "index": self.expr(t.index, True), + "lower": self.expr(t.lower, True), + "upper": self.expr(t.upper, True), + "step": self.expr(t.step, True), + "ctx": EXPR_CTX_MAPPING[t.ctx], + "has_question": t.has_question, + } + } + ) + return data + + def walk_Keyword(self, t: ast.Keyword): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "arg": self.expr(t.arg), + "value": self.expr(t.value, True), + } + } + ) + return data + + def walk_Arguments(self, t: ast.Arguments): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "args": self.exprs(t.args), + "defaults": self.exprs(t.defaults, True), + "type_annotation_list": [ + {KCLX_NODE_FIELD: tpe_str, **self.ast_meta_to_dict(t)} + if tpe_str + else None + for tpe_str, tpe_node in zip( + t.type_annotation_list, t.type_annotation_node_list + ) + ], + } + } + ) + return data + + def walk_Compare(self, t: ast.Compare): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "left": self.expr(t.left, True), + "ops": [CMP_OP_MAPPING[op] for op in t.ops], + "comparators": self.exprs(t.comparators, True), + } + } + ) + return data + + def walk_JoinedString(self, t: ast.JoinedString): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "is_long_string": t.is_long_string, + "values": self.exprs(t.values, True), + "raw_value": t.raw_value, + } + } + ) + return data + + def walk_FormattedValue(self, t: ast.FormattedValue): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "is_long_string": t.is_long_string, + "value": self.expr(t.value, True), + "format_spec": t.format_spec, + } + } + ) + return data + + def walk_NumberLit(self, t: ast.NumberLit): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "binary_suffix": t.binary_suffix, + "value": {"Int": t.value} + if isinstance(t.value, int) + else {"Float": t.value}, + } + } + ) + return data + + def walk_StringLit(self, t: ast.StringLit): + data = self.ast_meta_to_dict(t) + data.update( + { + KCLX_NODE_FIELD: { + "is_long_string": t.is_long_string, + "raw_value": t.raw_value or t.value, + "value": t.value, + } + } + ) + return data + + def walk_NameConstantLit(self, t: ast.NameConstantLit): + data = self.ast_meta_to_dict(t) + value = "Undefined" + value = "None" if t.value is None else value + value = "True" if t.value is True else value + value = "False" if t.value is False else value + data.update({KCLX_NODE_FIELD: {"value": value}}) + return data + + def walk_Comment(self, t: ast.Comment): + data = self.ast_meta_to_dict(t) + data.update({KCLX_NODE_FIELD: {"text": t.text}}) + return data + + +def transform_ast_to_kclx_ast_json_str(program: ast.Program) -> str: + check_number_lit_range(program) + for pkgpath in program.pkgs: + for i, module in enumerate(program.pkgs[pkgpath]): + program.pkgs[pkgpath][i] = KCLxASTTransformer().walk_Module(module) + return program.to_json(indent=None) + + +def check_number_lit_range(program: ast.Program): + strict_range_check = kclvm.config.strict_range_check + check_bit = 32 if strict_range_check else 64 + int_min = kcl_info.INT32_MIN if strict_range_check else kcl_info.INT64_MIN + int_max = kcl_info.INT32_MAX if strict_range_check else kcl_info.INT64_MAX + float_min = kcl_info.FLOAT32_MIN if strict_range_check else kcl_info.FLOAT64_MIN + float_max = kcl_info.FLOAT32_MAX if strict_range_check else kcl_info.FLOAT64_MAX + + def walk_lit(t: ast.AST) -> typing.Optional[typing.Callable]: + if isinstance(t, (ast.NumberLit)): + numberLit = typing.cast(ast.NumberLit, t) + value = numberLit.value + + if isinstance(value, int): + if not (int_min <= value <= int_max): + kcl_error.report_exception( + err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=numberLit.filename, line_no=numberLit.line + ) + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format( + str(value), check_bit + ), + ) + elif isinstance(value, float): + abs_var = abs(value) + if 0 < abs_var < float_min: + kcl_error.report_exception( + err_type=kcl_error.ErrType.FloatUnderflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=numberLit.filename, line_no=numberLit.line + ) + ], + arg_msg=kcl_error.FLOAT_UNDER_FLOW_MSG.format( + str(value), check_bit + ), + ) + elif abs_var > float_max: + kcl_error.report_exception( + err_type=kcl_error.ErrType.FloatOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=numberLit.filename, line_no=numberLit.line + ) + ], + arg_msg=kcl_error.FLOAT_OVER_FLOW_MSG.format( + str(value), check_bit + ), + ) + + return walk_lit + + for pkgpath in program.pkgs: + for i, module in enumerate(program.pkgs[pkgpath]): + ast.WalkTree(module, walk_lit) diff --git a/internal/kclvm_py/internal/log/__init__.py b/internal/kclvm_py/internal/log/__init__.py new file mode 100644 index 000000000..dccdd4668 --- /dev/null +++ b/internal/kclvm_py/internal/log/__init__.py @@ -0,0 +1,9 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .write_out import write_out, write_log, indent_log + +__all__ = [ + "write_out", + "write_log", + "indent_log", +] diff --git a/internal/kclvm_py/internal/log/write_out.py b/internal/kclvm_py/internal/log/write_out.py new file mode 100644 index 000000000..62a050c93 --- /dev/null +++ b/internal/kclvm_py/internal/log/write_out.py @@ -0,0 +1,30 @@ +#! /usr/bin/env python3 + +import kclvm.config + + +def write_out(inputs): + outputs = inputs + if kclvm.config.output: + with open(kclvm.config.output, "w") as f: + f.write(outputs) + else: + print(outputs, end="") + + +LOG_INDENT_STRING = " " +log_indent = 0 + + +def write_log(message, level=1): + """Write log message whose level is no less than the verbosity level""" + if kclvm.config.verbose >= level: + for _ in range(log_indent): + print(LOG_INDENT_STRING, end="") + print(message) + + +def indent_log(step=1): + """Adjust the indentation level of log messages""" + global log_indent + log_indent += step diff --git a/internal/kclvm_py/internal/util/__init__.py b/internal/kclvm_py/internal/util/__init__.py new file mode 100644 index 000000000..f6bd5207a --- /dev/null +++ b/internal/kclvm_py/internal/util/__init__.py @@ -0,0 +1,34 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .util import dotdict, hash, merge_option_same_keys, safe_call +from .check_utils import ( + check_allow_none, + check_all_allow_none, + check_not_none, + check_all_not_none, + PreCheck, + PostCheck, + CheckRules, + CHECK_MODE, + alert_internal_bug, + check_type_not_none, + check_type_allow_none, +) + +__all__ = [ + "dotdict", + "hash", + "merge_option_same_keys", + "check_allow_none", + "check_all_allow_none", + "check_not_none", + "check_all_not_none", + "PreCheck", + "PostCheck", + "CheckRules", + "CHECK_MODE", + "alert_internal_bug", + "check_type_not_none", + "check_type_allow_none", + "safe_call", +] diff --git a/internal/kclvm_py/internal/util/check_utils.py b/internal/kclvm_py/internal/util/check_utils.py new file mode 100644 index 000000000..421008b30 --- /dev/null +++ b/internal/kclvm_py/internal/util/check_utils.py @@ -0,0 +1,302 @@ +""" +The `check_utils` file mainly contains some methods for defensive programming. + +Method `PreCheck` can be used for pre-checking the method, +mainly to verify whether the incoming parameters of the method meet conditions. + +Method `PostCheck` can be used for post-checking the method, +mainly to verify whether the return of the method meets conditions. + +Method `PostSimpleExprCheck` can be used for post-checking the method, +Compared with `PostCheck`, `PostSimpleExprCheck` supports verifying +some simple relationship between the return value and the input parameters + +For example: + +# Check whether the type of incoming parameter "a" is int +@PreCheck((lambda v: isinstance(v, int)), "a") +# Check whether the type of incoming parameter "b" is int +@PreCheck((lambda v: isinstance(v, int)), "b") +# Check whether the type of return value is int +@PostCheck((lambda v: isinstance(v, int))) +# Check whether the return value is equal to the sum of input parameters +@PostSimpleExprCheck((lambda inputs, result: result == inputs["a"] + inputs["b"]), ["a", "b"]) +def add(a, b): + return a + b + +Class `CheckRules` and some other global methods are built-in check rules +that can be used in `PreCheck` and `PostCheck`. + +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" +import locale +import typing +import functools +from inspect import Signature +from typing import Callable, Any, List + +# CHECK_MODE is a switch, +# you can return the parameters directly by turning the CHECK_MODE = False + +CHECK_MODE = False + + +class CheckRules: + @staticmethod + def check_list_len_equal(all_lists: list): + if not CHECK_MODE: + return + assert all_lists + assert isinstance(all_lists, list) + assert all( + result is True + for result in [isinstance(list_inner, list) for list_inner in all_lists] + ) + assert all( + result == len(all_lists[0]) + for result in [len(list_inner) for list_inner in all_lists] + ) + + @staticmethod + def check_locale(lang: str): + if not CHECK_MODE: + return True + LOCALE_LIST = list(locale.locale_alias.keys()) + if not lang or not isinstance(lang, str) or lang not in LOCALE_LIST: + return False + return True + + @staticmethod + def check_type_not_none(item, *tpes) -> bool: + if CHECK_MODE: + return item is not None and isinstance(item, tpes) + return True + + @staticmethod + def check_type_allow_none(item, *tpes) -> bool: + if CHECK_MODE: + return item is None or isinstance(item, tpes) + return True + + @staticmethod + def check_list_item_type_allow_none(item, *tpes) -> bool: + if CHECK_MODE: + check_all_allow_none(list, item, *tpes) + return True + + @staticmethod + def check_int_range_allow_none(target: int, low: int, high: int) -> bool: + if CHECK_MODE: + if target is None: + return True + check_type_not_none(target, int) + check_type_not_none(low, int) + check_type_not_none(high, int) + return target in range(low, high) + else: + return True + + @staticmethod + def check_str_len_not_none(target: str, length: int) -> bool: + if CHECK_MODE: + check_type_not_none(target, str) + check_type_not_none(length, int) + return len(target) == length + else: + return True + + @staticmethod + def check_str_len_allow_none(target: str, length: int) -> bool: + if CHECK_MODE: + if target is None: + return True + check_type_not_none(target, str) + check_type_not_none(length, int) + return len(target) == length + else: + return True + + +def check_allow_none(node, tpe): + if node and CHECK_MODE: + assert isinstance(node, tpe) + return typing.cast(tpe, node) + + +def check_all_allow_none(set_tpe: typing.Type, nodes, *item_tpes): + if nodes and CHECK_MODE: + assert isinstance(nodes, set_tpe) + assert isinstance(nodes, (list, tuple)) and all( + isinstance(item, item_tpes) for item in nodes + ) + return typing.cast(set_tpe, nodes) + + +def check_not_none(node, *tpes): + if CHECK_MODE: + assert node and isinstance(node, tpes) + return typing.cast(tpes, node) + + +def check_all_not_none(set_tpe: typing.Type, nodes, *item_tpes): + if CHECK_MODE: + assert nodes and isinstance(nodes, set_tpe) + assert isinstance(nodes, (list, tuple)) and all( + isinstance(item, item_tpes) for item in nodes + ) + return typing.cast(set_tpe, nodes) + + +def check_type_allow_none(node, *tpes): + if node and CHECK_MODE: + assert isinstance(node, tpes) + return node + + +def check_type_not_none(node, *tpes): + if CHECK_MODE: + assert node is not None and isinstance(node, tpes) + return node + + +def alert_internal_bug(): + if CHECK_MODE: + assert False, "Here is unreachable unless a bug occurs" + + +def PreCheck(condition: Callable[[Any], bool], param_name: str, param_pos: int = None): + def conditioner(func): + @functools.wraps(func) + def check_condition(*args, **kwargs): + if not CHECK_MODE: + return func(*args, **kwargs) + check_type_not_none(condition, Callable) + check_type_not_none(func, Callable) + check_type_not_none(CHECK_MODE, bool) + check_type_not_none(param_name, str) + check_type_allow_none(param_pos, int) + param_names_list = [ + i[0] + for i in Signature.from_callable(func).parameters.items() + if len(i) > 0 + ] + if param_name not in param_names_list: + raise AssertionError( + f"Pre-Condition failed: " + f"There is no parameter named {param_name} in function {func.__name__}. " + f"The function parameters list: {param_names_list}." + ) + try: + param_value = kwargs[ + param_name + ] # if the param in kwargs for the function + except KeyError: + if not CheckRules.check_int_range_allow_none( + param_pos, 0, len(param_names_list) + ): + raise AssertionError( + f"Pre-Condition failed: param_pos: {param_pos} is out of range. " + f"There are only {len(param_names_list)} parameters in {func.__name__}" + ) + if param_names_list.index(param_name) < len( + args + ): # if the param in args for the function + param_value = ( + args[param_pos] + if param_pos + else args[param_names_list.index(param_name)] + ) + else: + param_value = None + + if condition(param_value): + return func(*args, **kwargs) + else: + raise AssertionError( + f"Pre-Condition failed: {func.__name__}({param_name} = {param_value}), " + f"Check Condition: {condition.__name__}" + ) + + return check_condition + + return conditioner + + +def PostCheck(condition: Callable[[Any], bool]): + def conditioner(func): + @functools.wraps(func) + def check_condition(*args, **kwargs): + if not CHECK_MODE: + return func(*args, **kwargs) + check_type_not_none(condition, Callable) + check_type_not_none(func, Callable) + check_type_not_none(CHECK_MODE, bool) + result = func(*args, **kwargs) + if condition(result): + return result + else: + raise AssertionError( + f"Post-Condition failed: {func.__name__} with returned {result}," + f"Check Condition: {condition.__name__}" + ) + + return check_condition + + return conditioner + + +def PostSimpleExprCheck( + condition: Callable[[Any, Any], bool], dependent_params: List[str] +): + def conditioner(func): + @functools.wraps(func) + def check_condition(*args, **kwargs): + if not CHECK_MODE: + return func(*args, **kwargs) + check_type_not_none(condition, Callable) + check_type_not_none(func, Callable) + check_type_not_none(CHECK_MODE, bool) + check_all_not_none(list, dependent_params, str) + sig_params = {} + param_names_list = [ + i[0] + for i in Signature.from_callable(func).parameters.items() + if len(i) > 0 + ] + for (k, v) in Signature.from_callable(func).parameters.items(): + sig_params[k] = (k, v) + inputs = {} + + for param in dependent_params: + try: + inputs[param] = kwargs[ + param + ] # if the param in kwargs for the function + except KeyError: + try: + inputs[param] = args[ # if the param in args for the function + param_names_list.index(sig_params[param][0]) + ] + except IndexError: + assert ( + len(sig_params[param]) > 0 + ) # if the param in the function has default value + inputs[param] = sig_params[param][1].default + except KeyError: + raise AssertionError( + f"Post-Condition failed: " + f"There is no parameter named {param} in function {func.__name__}. " + f"The function parameters list: {param_names_list}." + ) + + result = func(*args, **kwargs) + if condition(inputs, result): + return result + else: + raise AssertionError( + f"Post-condition failed: {func.__name__} with inputs: {inputs}, returned {result}" + ) + + return check_condition + + return conditioner diff --git a/internal/kclvm_py/internal/util/util.py b/internal/kclvm_py/internal/util/util.py new file mode 100644 index 000000000..15ce423d7 --- /dev/null +++ b/internal/kclvm_py/internal/util/util.py @@ -0,0 +1,32 @@ +import hashlib +import typing + + +class dotdict(dict): + """dot.notation access to dictionary attributes""" + + __getattr__ = dict.get + __setattr__ = dict.__setitem__ + __delattr__ = dict.__delitem__ + + +def hash(input_str): + return hashlib.md5(input_str.encode("utf-8")).hexdigest() + + +def merge_option_same_keys(args): + """ + merge kcl -D and -Y argument with the same keys + """ + if not args: + return {} + return {k: v for k, v in args} + + +def safe_call(fn: typing.Callable, *args, **kwargs) -> (typing.Any, Exception): + result = None + try: + result = fn(*args, **kwargs) + return result, None + except Exception as err: + return result, err diff --git a/internal/kclvm_py/kcl/README.md b/internal/kclvm_py/kcl/README.md new file mode 100644 index 000000000..caa7ca22b --- /dev/null +++ b/internal/kclvm_py/kcl/README.md @@ -0,0 +1,3 @@ +# KCL + +This module provides the K Configuration Language definitions. diff --git a/internal/kclvm_py/kcl/__init__.py b/internal/kclvm_py/kcl/__init__.py new file mode 100644 index 000000000..d74aaf8ad --- /dev/null +++ b/internal/kclvm_py/kcl/__init__.py @@ -0,0 +1 @@ +# Copyright 2021 The KCL Authors. All rights reserved. diff --git a/internal/kclvm_py/kcl/ast/0_gen.go b/internal/kclvm_py/kcl/ast/0_gen.go new file mode 100644 index 000000000..1e0899ddc --- /dev/null +++ b/internal/kclvm_py/kcl/ast/0_gen.go @@ -0,0 +1,5 @@ +// Copyright 2020 The KCL Authors. All rights reserved. + +//go:generate go run aa_gen_lark_token.go + +package ast diff --git a/internal/kclvm_py/kcl/ast/Makefile b/internal/kclvm_py/kcl/ast/Makefile new file mode 100644 index 000000000..4c3f56a72 --- /dev/null +++ b/internal/kclvm_py/kcl/ast/Makefile @@ -0,0 +1,6 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +gen: + go run aa_gen_lark_token.go + +clean: diff --git a/internal/kclvm_py/kcl/ast/__init__.py b/internal/kclvm_py/kcl/ast/__init__.py new file mode 100644 index 000000000..52c2067b8 --- /dev/null +++ b/internal/kclvm_py/kcl/ast/__init__.py @@ -0,0 +1,40 @@ +"""The `ast` module mainly defines the abstract nodes of all +KCL syntax and the corresponding supporting tools that make +working with the trees simpler. + +The syntax tree can be generated through functions such as +ParseFile/LoadProgram, and the result will be a tree of +objects whose classes all inherit from `ast.AST`. + +In addition to the grammar model itself, the `ast` module +also defines the priority of all KCL operators, as well as +the walker and transformer modules that help process the AST. +The former is used to better traverse the AST, and the latter +is used to modify the existing AST more quickly. + +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" + +from .ast import * +from .precedence import OP_PREC_MAP, precedence +from .lark_token import TokenValue +from .walker import TreeWalker, WalkTree +from .transformer import TreeTransformer +from .fields_map import iter_fields + +__all__ = [ + "ast", + "BinOp", + "CmpOp", + "UnaryOp", + "AugOp", + "ExprContext", + "OP_PREC_MAP", + "precedence", + "TokenValue", + "TreeWalker", + "WalkTree", + "TreeTransformer", + "iter_fields", + "ASTFactory", +] diff --git a/internal/kclvm_py/kcl/ast/aa_gen_lark_token.go b/internal/kclvm_py/kcl/ast/aa_gen_lark_token.go new file mode 100644 index 000000000..6405244aa --- /dev/null +++ b/internal/kclvm_py/kcl/ast/aa_gen_lark_token.go @@ -0,0 +1,152 @@ +// Copyright 2020 The KCL Authors. All rights reserved. + +//go:build ignore +// +build ignore + +package main + +import ( + "bytes" + "flag" + "fmt" + "io/ioutil" + "log" + "regexp" + "strings" + "unicode" +) + +var ( + flagFilename = flag.String("file", "../grammar/kcl.lark", "set lark file") + flagOutput = flag.String("output", "lark_token.py", "set output file") +) + +func main() { + flag.Parse() + + larkData, err := ioutil.ReadFile(*flagFilename) + if err != nil { + log.Fatal(err) + } + + var buf = new(bytes.Buffer) + + fmt.Fprintln(buf, "# Copyright 2020 The KCL Authors. All rights reserved.") + fmt.Fprintln(buf) + + fmt.Fprintln(buf, "# Auto generated by {gen_lark_token.go & kcl.lark}; DO NOT EDIT!!!") + fmt.Fprintln(buf) + fmt.Fprintln(buf) + + fmt.Fprintln(buf, "class LarkToken:") + fmt.Fprintln(buf) + + var names, comments = getLarkNames(string(larkData)) + + var rule_list []string + var tok_list []string + + for _, s := range names { + if unicode.IsLower(rune(s[0])) { + rule_list = append(rule_list, s) + } else { + tok_list = append(tok_list, s) + } + } + + fmt.Fprintf(buf, " # kcl.lark rules and tokens (len=%d)\n", len(names)) + for i, s := range names { + if strings.HasPrefix(comments[i], "type: ") { + comments[i] = strings.Replace(comments[i], "type: ", "@type: ", 1) + } + fmt.Fprintf(buf, " L_%s = \"%s\" # %s ...\n", s, s, comments[i]) + } + fmt.Fprintln(buf) + + //fmt.Fprintf(buf, " # Lark rule alias name (=> f'LL_{rule_name.upper()}'\n") + //for _, s := range rule_list { + // fmt.Fprintf(buf, " LL_%s = L_%s\n", strings.ToUpper(s), s) + //} + //fmt.Fprintln(buf) + + fmt.Fprintf(buf, " # kcl.lark tokens list (len=%d)\n", len(tok_list)) + + fmt.Fprintln(buf, " LL_token_list = [") + for _, s := range tok_list { + fmt.Fprintf(buf, " L_%s,\n", s) + } + fmt.Fprintln(buf, " ]") + + fmt.Fprintln(buf) + fmt.Fprintf(buf, " # kcl.lark rules list (len=%d)\n", len(rule_list)) + + fmt.Fprintln(buf, " LL_rule_list = [") + for _, s := range rule_list { + fmt.Fprintf(buf, " L_%s,\n", s) + } + fmt.Fprintln(buf, " ]") + + fmt.Fprintln(buf) + fmt.Fprintf(buf, " # kcl.lark tokens string value map\n") + fmt.Fprintln(buf, " LL_token_str_value_map = {") + for i, s := range names { + if unicode.IsUpper(rune(s[0])) { + if val := getTokenStrValue(comments[i]); val != "" { + fmt.Fprintf(buf, " L_%s: \"%s\",\n", s, val) + } + } + } + fmt.Fprintln(buf, " }") + + fmt.Fprintln(buf) + fmt.Fprintln(buf) + fmt.Fprintln(buf, "class TokenValue:") + for i, s := range names { + if unicode.IsUpper(rune(s[0])) { + if val := getTokenStrValue(comments[i]); val != "" { + fmt.Fprintf(buf, " %s = \"%s\"\n", s, val) + } + } + } + + err = ioutil.WriteFile(*flagOutput, buf.Bytes(), 0666) + if err != nil { + log.Fatal(err) + } +} + +func getLarkNames(larkData string) (names []string, comments []string) { + lines := strings.Split(larkData, "\n") + for i, line := range lines { + line := strings.Trim(line, "? \t") + if matched, _ := regexp.MatchString(`^\w+(\.|:)`, line); matched { + if idx := strings.Index(line, ":"); idx > 0 { + line = line[:idx] + } + if idx := strings.Index(line, "."); idx > 0 { + line = line[:idx] + } + if line != "" { + names = append(names, line) + comments = append(comments, lines[i]) + } + } + } + return +} + +func getTokenStrValue(tok_comment string) string { + // FALSE: "False" ... + if idx := strings.Index(tok_comment, ":"); idx >= 0 { + tok_comment = tok_comment[idx+1:] + } + + // IMAG_NUMBER.2: /\d+j/i | FLOAT_NUMBER "j"i ... + tok_comment = strings.TrimSpace(tok_comment) + if s := tok_comment; s == "" || s[0] != '"' { + return "" + } + + tok_comment = strings.Trim(tok_comment, `'"`) + return tok_comment +} diff --git a/internal/kclvm_py/kcl/ast/ast.py b/internal/kclvm_py/kcl/ast/ast.py new file mode 100644 index 000000000..6edaa7cc6 --- /dev/null +++ b/internal/kclvm_py/kcl/ast/ast.py @@ -0,0 +1,1639 @@ +"""The `ast` file contains the definitions of all KCL AST nodes +and operators and all AST nodes are derived from the `AST` class. +The main structure of a KCL program is as follows: + +┌─────────────────────────────────────────────────────────────────┐ +│ Program │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ Main Package │ │ Package1 │ │ Package2 │ │ +│ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ +│ │ │ Module1 │ │ │ │ Module1 │ │ │ │ Module1 │ │ │ +│ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │ +│ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ +│ │ │ Module2 │ │ │ │ Module2 │ │ │ │ Module2 │ │ │ +│ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │ +│ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ +│ │ │ ... │ │ │ │ ... │ │ │ │ ... │ │ │ +│ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │ +│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ + +A single KCL file represents a module, which records file information, +package path information, and module document information, which is +mainly composed of all the statements in the KCL file. + +The combination of multiple KCL files is regarded as a complete KCL +Program. For example, a single KCL file can be imported into KCL +files in other packages through statements such as import. Therefore, +the Program is composed of multiple modules, and each module is +associated with it. Corresponding to the package path. + +:note: When the definition of any AST node is modified or the AST node +is added/deleted, it is necessary to modify the corresponding processing +in the compiler and regenerate the walker code. +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" + +import os +import json +import hashlib +import typing + +from enum import Enum +from abc import ABC +from typing import List, Optional, Union, Dict +from pathlib import PosixPath + +import kclvm.kcl.ast.lark_token as lark_token +import kclvm.kcl.ast as ast +import kclvm.internal.util.check_utils as check_utils + +from .lark_token import TokenValue + + +class CmdArgSpec: + """KCL command line argument spec, e.g. `kcl main.k -D name=value`""" + + def __init__(self, *, name: str = "", value: any = None): + self.name: str = name + self.value: any = value + + +class OverrideAction(Enum): + CREATE_OR_UPDATE = "CreateOrUpdate" + DELETE = "Delete" + + +class CmdOverrideSpec: + """KCL command line override spec, e.g. `kcl main.k -O pkgpath:path.to.field=field_value`""" + + def __init__( + self, + *, + pkgpath: str = "", + field_path: str = "", + field_value: str = "", + action: OverrideAction = OverrideAction.CREATE_OR_UPDATE, + ): + self.pkgpath: str = pkgpath + self.field_path: str = field_path + self.field_value: str = field_value + self.action: OverrideAction = action + + +class LarkToken(lark_token.LarkToken): + @staticmethod + def is_string(token: str): + return token in [ + LarkToken.L_STRING, + LarkToken.L_LONG_STRING, + ] + + @staticmethod + def is_int_number(token: str): + return token in [ + LarkToken.L_DEC_NUMBER, + LarkToken.L_HEX_NUMBER, + LarkToken.L_BIN_NUMBER, + LarkToken.L_OCT_NUMBER, + ] + + @staticmethod + def is_float_number(token: str): + return token == LarkToken.L_FLOAT_NUMBER + + @staticmethod + def is_name_constant(token: str): + return token in [ + LarkToken.L_TRUE, + LarkToken.L_FALSE, + LarkToken.L_NONE, + LarkToken.L_UNDEFINED, + ] + + @staticmethod + def get_token_value(token: str) -> str: + return LarkToken.LL_token_str_value_map[token] + + +class BinOp(Enum): + """BinOp is the set of all binary operators in KCL.""" + + Add = LarkToken.L_PLUS + Sub = LarkToken.L_MINUS + Mul = LarkToken.L_MULTIPLY + Div = LarkToken.L_DIVIDE + Mod = LarkToken.L_MOD + Pow = LarkToken.L_DOUBLE_STAR + LShift = LarkToken.L_SHIFT_LEFT + RShift = LarkToken.L_SHIFT_RIGHT + BitOr = LarkToken.L_OR + BitXor = LarkToken.L_XOR + BitAnd = LarkToken.L_AND + FloorDiv = LarkToken.L_DOUBLE_DIVIDE + As = LarkToken.L_AS + + And = LarkToken.L_L_AND # True and False + Or = LarkToken.L_L_OR # True or False + + @classmethod + def enum_value_list(cls) -> [str]: + return list(map(lambda c: c.value, cls)) + + @classmethod + def enum_key_list(cls) -> [str]: + return list(map(lambda c: c, cls)) + + +class AugOp(Enum): + Assign = LarkToken.L_ASSIGN + Add = LarkToken.L_COMP_PLUS + Sub = LarkToken.L_COMP_MINUS + Mul = LarkToken.L_COMP_MULTIPLY + Div = LarkToken.L_COMP_DIVIDE + Mod = LarkToken.L_COMP_MOD + Pow = LarkToken.L_COMP_DOUBLE_STAR + LShift = LarkToken.L_COMP_SHIFT_LEFT + RShift = LarkToken.L_COMP_SHIFT_RIGHT + BitOr = LarkToken.L_COMP_OR + BitXor = LarkToken.L_COMP_XOR + BitAnd = LarkToken.L_COMP_AND + FloorDiv = LarkToken.L_COMP_DOUBLE_DIVIDE + + @classmethod + def enum_value_list(cls) -> [str]: + return list(map(lambda c: c.value, cls)) + + @classmethod + def enum_key_list(cls) -> [str]: + return list(map(lambda c: c, cls)) + + +class UnaryOp(Enum): + UAdd = LarkToken.L_PLUS + USub = LarkToken.L_MINUS + Invert = LarkToken.L_NOT + Not = LarkToken.L_L_NOT + + @classmethod + def enum_value_list(cls) -> [str]: + return list(map(lambda c: c.value, cls)) + + @classmethod + def enum_key_list(cls) -> [str]: + return list(map(lambda c: c, cls)) + + +def judge_compare_op(optype: str) -> bool: + return optype in CmpOp.enum_value_list() + + +CMP_OP_VALUE_LIST = [ + LarkToken.L_EQUAL_TO, + LarkToken.L_NOT_EQUAL_TO, + LarkToken.L_LESS_THAN, + LarkToken.L_LESS_THAN_OR_EQUAL_TO, + LarkToken.L_GREATER_THAN, + LarkToken.L_GREATER_THAN_OR_EQUAL_TO, + LarkToken.L_IS, + LarkToken.L_IN, + LarkToken.L_L_NOT, +] + + +class CmpOp(Enum): + Eq = LarkToken.L_EQUAL_TO + NotEq = LarkToken.L_NOT_EQUAL_TO + Lt = LarkToken.L_LESS_THAN + LtE = LarkToken.L_LESS_THAN_OR_EQUAL_TO + Gt = LarkToken.L_GREATER_THAN + GtE = LarkToken.L_GREATER_THAN_OR_EQUAL_TO + Is = LarkToken.L_IS + In = LarkToken.L_IN + + Not = LarkToken.L_L_NOT + + IsNot = f"{LarkToken.L_IS} {LarkToken.L_L_NOT}" # "IS NOT" + NotIn = f"{LarkToken.L_L_NOT} {LarkToken.L_IN}" # "NOT IN" + + @classmethod + def enum_value_list(cls) -> [str]: + return CMP_OP_VALUE_LIST + + @classmethod + def enum_key_list(cls) -> [str]: + return list(map(lambda c: c, cls)) + + +class ExprContext(Enum): + LOAD = "LOAD" + STORE = "STORE" + DEL = "DEL" + AUGLOAD = "AUGLOAD" + AUGSTORE = "AUGSTORE" + PARAM = "PARAM" + + @classmethod + def enum_value_list(cls) -> [str]: + return list(map(lambda c: c.value, cls)) + + +AST_ENUM_LIST = { + "BinOp": BinOp, + "AugOp": AugOp, + "UnaryOp": UnaryOp, + "CmpOp": CmpOp, + "ExprContext": ExprContext, + "OverrideAction": OverrideAction, +} + +OPERATOR_VALUE_MAP = { + AugOp.Assign: TokenValue.ASSIGN, + AugOp.Add: TokenValue.COMP_PLUS, + AugOp.Sub: TokenValue.COMP_MINUS, + AugOp.Mul: TokenValue.COMP_MULTIPLY, + AugOp.Div: TokenValue.COMP_DIVIDE, + AugOp.Mod: TokenValue.COMP_MOD, + AugOp.Pow: TokenValue.COMP_DOUBLE_STAR, + AugOp.LShift: TokenValue.COMP_SHIFT_LEFT, + AugOp.RShift: TokenValue.COMP_SHIFT_RIGHT, + AugOp.BitOr: TokenValue.COMP_OR, + AugOp.BitXor: TokenValue.COMP_XOR, + AugOp.BitAnd: TokenValue.COMP_AND, + AugOp.FloorDiv: TokenValue.COMP_DOUBLE_DIVIDE, + BinOp.Add: TokenValue.PLUS, + BinOp.Sub: TokenValue.MINUS, + BinOp.Mul: TokenValue.MULTIPLY, + BinOp.Div: TokenValue.DIVIDE, + BinOp.Mod: TokenValue.MOD, + BinOp.Pow: TokenValue.DOUBLE_STAR, + BinOp.LShift: TokenValue.SHIFT_LEFT, + BinOp.RShift: TokenValue.SHIFT_RIGHT, + BinOp.BitOr: TokenValue.OR, + BinOp.BitXor: TokenValue.XOR, + BinOp.BitAnd: TokenValue.AND, + BinOp.FloorDiv: TokenValue.DOUBLE_DIVIDE, + BinOp.And: TokenValue.L_AND, + BinOp.Or: TokenValue.L_OR, + BinOp.As: TokenValue.AS, + CmpOp.Eq: TokenValue.EQUAL_TO, + CmpOp.NotEq: TokenValue.NOT_EQUAL_TO, + CmpOp.Lt: TokenValue.LESS_THAN, + CmpOp.LtE: TokenValue.LESS_THAN_OR_EQUAL_TO, + CmpOp.Gt: TokenValue.GREATER_THAN, + CmpOp.GtE: TokenValue.GREATER_THAN_OR_EQUAL_TO, + CmpOp.Is: TokenValue.IS, + CmpOp.In: TokenValue.IN, + CmpOp.Not: TokenValue.NOT, + CmpOp.IsNot: " ".join([TokenValue.IS, TokenValue.L_NOT]), + CmpOp.NotIn: " ".join([TokenValue.L_NOT, TokenValue.IN]), + UnaryOp.UAdd: TokenValue.PLUS, + UnaryOp.USub: TokenValue.MINUS, + UnaryOp.Invert: TokenValue.NOT, + UnaryOp.Not: TokenValue.L_NOT, +} + + +class Position: + """Position describes an arbitrary source position including the filename, + line, and column location. + + A Position is valid if the line number is > 0. + The line and column are both 1 based. + """ + + def __init__(self, filename: str = None, line: int = None, column: int = None): + self.filename: str = filename + self.line: int = line + self.column: int = column + + def is_valid(self) -> bool: + return self.filename is not None and self.line is not None and self.line > 0 + + def less(self, pos: "Position") -> bool: + if not self.is_valid() or not pos or not pos.is_valid(): + return False + if self.filename != pos.filename: + return False + if self.line < pos.line: + return True + if self.line == pos.line: + return self.column < pos.column + return False + + def less_equal(self, pos: "Position") -> bool: + if not self.is_valid() or not pos or not pos.is_valid(): + return False + if self.less(pos): + return True + return self == pos + + def __eq__(self, other: "Position") -> bool: + return ( + self.filename == other.filename + and self.line == other.line + and self.column == other.column + ) + + def __str__(self) -> str: + return f"<{self.filename}, ({self.line}, {self.column})>" + + +class AST: + """ + All KCL node types implement the KCL AST interface + """ + + _line_offset: int = 0 + _column_offset: int = 0 + + def __init__( + self, + line: Optional[int] = 0, + column: Optional[int] = 0, + end_line: Optional[int] = 0, + end_column: Optional[int] = 0, + ) -> None: + self.filename: str = None + self.relative_filename: str = None + self.line: int = line + self.column: int = column + self.end_line: int = end_line + self.end_column: int = end_column + self.parent: Optional[AST] = None + + def __str__(self) -> str: + return f"<{self.type}, ({self.line}, {self.column})>" + + def __repr__(self) -> str: + return self.__str__() + + def get_line(self) -> int: + """ + Get the node line, which is 1 based + """ + return self.line + + def get_column(self) -> int: + """ + Get the node column, which is 1 based + """ + return self.column + + def get_end_line(self) -> int: + """ + Get the node end_line + """ + return self.end_line + + def get_end_column(self) -> int: + """ + Get the node end_column + """ + return self.end_column + + def set_ast_position(self, node, filename=None): + import kclvm.compiler.parser.lark_pb2 as lark_pb + + check_utils.check_type_not_none(node, AST, lark_pb.Tree) + check_utils.check_type_allow_none(filename, str, PosixPath) + self.filename = filename if isinstance(node, lark_pb.Tree) else node.filename + self.line = node.line + self._line_offset + self.column = node.column + self._column_offset + self.end_line = node.end_line + self._line_offset + self.end_column = node.end_column + self._column_offset + + return self + + def set_column(self, column: int): + assert isinstance(column, int) and column >= 0 + self.column = column + + def set_line(self, line: int): + assert isinstance(line, int) and line >= 0 + self.line = line + + def set_end_line_column(self, ast_node): + assert ast_node and isinstance(ast_node, AST) + self.end_line = ast_node.end_line + self.end_column = ast_node.end_column + + def set_end_line(self, end_line: int): + assert isinstance(end_line, int) and end_line >= 0 + self.end_line = end_line + + def set_end_column(self, end_column: int): + assert isinstance(end_column, int) and end_column >= 0 + self.end_column = end_column + + def offset_column(self, offset_column: int): + assert isinstance(offset_column, int) + self.column += offset_column + + def offset_line(self, offset_line: int): + assert isinstance(offset_line, int) + self.line += offset_line + + def offset_end_line(self, offset_end_line: int): + assert isinstance(offset_end_line, int) + self.end_line += offset_end_line + + def offset_end_column(self, offset_end_column: int): + assert isinstance(offset_end_column, int) + self.end_column += offset_end_column + + @classmethod + def set_offset(cls, line_offset: int = 0, column_offset=0): + cls._line_offset = line_offset + cls._column_offset = column_offset + + @classmethod + def reset_offset(cls): + cls._line_offset = 0 + cls._column_offset = 0 + + @property + def type(self) -> str: + return self.__class__.__name__ + + @property + def pos(self) -> Position: + return Position(filename=self.filename, line=self.line, column=self.column) + + @property + def end_pos(self) -> Position: + return Position( + filename=self.filename, line=self.end_line, column=self.end_column + ) + + def to_json(self, indent=4, sort_keys=False): + return json.dumps( + self, + default=lambda o: o.value + if type(o) in AST_ENUM_LIST.values() + else {k: v for k, v in o.__dict__.items() if k != "parent"}, + indent=indent, + sort_keys=sort_keys, + ) + + def contains_pos(self, pos: Position) -> bool: + """ + check if current node contains a position + :param pos: the given position + :return: if current node contains the given position + """ + start_pos = Position( + filename=self.filename, + line=self.line, + column=self.column, + ) + end_pos = Position( + filename=self.filename, + line=self.end_line, + column=self.end_column, + ) + return start_pos.less_equal(pos) and pos.less_equal(end_pos) + + def get_children(self) -> List["AST"]: + def walk_field(inner: typing.Union[typing.List, typing.Dict, AST]): + if isinstance(inner, list): + [walk_field(item) for item in inner] + return + if isinstance(inner, dict): + [walk_field(v) for _, v in inner] + return + if isinstance(inner, AST): + children.append(inner) + + children = [] + [walk_field(field) for _, field in ast.iter_fields(self)] + return children + + def find_leaf_by_pos(self, pos: Position) -> Optional["AST"]: + if pos and pos.is_valid() and self.contains_pos(pos): + children = self.get_children() + if len(children) == 0: + return self + else: + for child in children: + leaf = child.find_leaf_by_pos(pos) + if leaf: + return leaf + return None + + def find_nearest_parent_by_type(self, tpe: typing.Type["AST"]) -> Optional["AST"]: + parent = self.parent + while parent: + if parent.type == tpe.__name__: + return typing.cast(tpe, parent) + parent = parent.parent + return None + + +class Stmt(AST): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "Stmt" + + +class Expr(AST): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "Expr" + + +class Name(Expr, ABC): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + value: Optional[str] = None, + ) -> None: + super().__init__(line, column) + self._ast_type = "Name" + self.value: str = value + + +class TypeAliasStmt(Stmt): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "TypeAliasStmt" + self.type_name: Optional[Identifier] = None + self.type_value: Optional[Type] = None + + +class ExprStmt(Stmt): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "ExprStmt" + self.exprs: List[Expr] = [] + + +class UnificationStmt(Stmt): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "UnificationStmt" + self.target: Identifier = None + self.value: SchemaExpr = None + + +class AssignStmt(Stmt): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "AssignStmt" + self.targets: List[Identifier] = [] + self.value: Optional[Expr] = None + self.type_annotation: str = "" + self.type_annotation_node: Optional[Type] = None + + def __str__(self): + return super().__str__()[:-1] + f" targets: {self.targets} value: {self.value}>" + + +class AugAssignStmt(Stmt): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "AugAssignStmt" + self.op: Optional[AugOp] = None + self.target: Optional[Identifier] = None + self.value: Optional[Expr] = None + + def __str__(self): + return ( + super().__str__()[:-1] + + f" target: {self.target} augop: {self.op} value: {self.value}>" + ) + + +class AssertStmt(Stmt): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "AssertStmt" + self.test: Optional[Expr] = None + self.if_cond: Optional[Expr] = None + self.msg: Optional[Expr] = None + + +class IfStmt(Stmt): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "IfStmt" + self.cond: Optional[Expr] = None + self.body: List[Stmt] = [] + self.elif_cond: List[Expr] = [] + self.elif_body: List[List[Stmt]] = [] + self.else_body: List[Stmt] = [] + + def __str__(self): + return ( + super().__str__()[:-1] + + f" cond: {self.cond} body: {self.body} elif_cond: {self.elif_cond} elifbody: {self.elif_body} elsebody: {self.else_body}>" + ) + + +class ImportStmt(Stmt): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "ImportStmt" + self.path: Optional[str] = None + self.name: Optional[str] = None + self.asname: Optional[str] = None + self.path_nodes: [Name] = [] + self.as_name_node: Optional[Name] = None + + self.rawpath: Optional[str] = None # only for error message + + def __str__(self): + return super().__str__()[:-1] + f" name: {self.name}>" + + @property + def pkg_name(self) -> str: + return self.asname or self.name + + +class SchemaIndexSignature(Stmt): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "SchemaIndexSignature" + self.key_name: Optional[str] = None + self.key_type: Optional[str] = "str" + self.value_type: Optional[str] = "" + self.value: Optional[Expr] = None + self.any_other: bool = False + self.name_node: Optional[Name] = None + self.value_type_node: Optional[Type] = None + + +class SchemaAttr(Stmt): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "SchemaAttr" + self.doc: str = "" + self.name: str = "" + self.type_str: str = "" + self.op: Optional[Union[BinOp, AugOp]] = None + self.value: Optional[Expr] = None + self.is_optional: bool = False + self.decorators: List[Decorator] = [] + self.name_node: Optional[Name] = None + self.type_node: Optional[Type] = None + + def __str__(self): + return ( + super().__str__()[:-1] + + f" name: {self.name} type: {self.type_str} value: {self.value}>" + ) + + +class SchemaStmt(Stmt): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "SchemaStmt" + self.doc: str = "" + self.name: str = "" + self.parent_name: Optional[Identifier] = None + self.for_host_name: Optional[Identifier] = None + self.is_mixin: bool = False + self.is_protocol: bool = False + self.args: Optional[Arguments] = None + self.mixins: List[Identifier] = [] + self.body: List[Union[SchemaAttr, Stmt]] = [] + self.decorators: List[Decorator] = [] + self.checks: List[CheckExpr] = [] + self.index_signature: Optional[SchemaIndexSignature] = None + self.name_node: Optional[Name] = None + + def __str__(self): + return super().__str__()[:-1] + f" name: {self.name} body: {self.body}>" + + def has_only_attribute_definitions(self) -> bool: + return not bool( + self.args + or self.mixins + or self.checks + or self.index_signature + or any( + [not isinstance(p, (SchemaAttr, UnificationStmt)) for p in self.body] + ) + ) + + def GetAttrList(self) -> List[SchemaAttr]: + attr_list = [] + + for attr in self.body or []: + if isinstance(attr, (SchemaAttr, UnificationStmt)): + attr_list.append(attr) + + return attr_list + + def GetAttrNameList(self) -> List[str]: + attr_list = self.GetAttrList() + return [attr.name for attr in attr_list] + + def GetIndexSignatureAttrName(self) -> Optional[str]: + return self.index_signature.key_name if self.index_signature else None + + def GetStmtList(self) -> List[Stmt]: + stmt_list = [] + + for attr in self.body: + if not isinstance(attr, SchemaAttr): + stmt_list.append(attr) + + return stmt_list + + def GetLeftIdentifierList(self): + """Get schema full attribute list including + un-exported attributes and relaxed attributes + """ + attr_list = [] + + def loop_body(body: List[Stmt]): + """Get the l-values recursively and add them into schema attr list""" + if not body: + return + for stmt in body: + if isinstance(stmt, AssignStmt): + for target in stmt.targets: + add_name(target.get_first_name()) + elif isinstance(stmt, (AugAssignStmt, UnificationStmt)): + add_name(stmt.target.get_first_name()) + elif isinstance(stmt, IfStmt): + loop_body(stmt.body) + for body in stmt.elif_body: + loop_body(body) + loop_body(stmt.else_body) + elif isinstance(stmt, SchemaAttr): + add_name(stmt.name) + + def add_name(name: str): + """Add the `name` into schema attr list""" + if not name or not isinstance(name, str): + return + attr_list.append(name) + + loop_body(self.body) + return attr_list + + +class RuleStmt(Stmt): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "RuleStmt" + self.doc: str = "" + self.name: str = "" + self.parent_rules: List[Identifier] = [] + self.decorators: List[Decorator] = [] + self.checks: List[CheckExpr] = [] + self.name_node: Optional[Name] = None + self.args: Optional[Arguments] = None + self.for_host_name: Optional[Identifier] = None + + +class IfExpr(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "IfExpr" + + # body if cond else orelse + self.body: Optional[Expr] = None # self.body + self.cond: Optional[Expr] = None # self.cond + self.orelse: Optional[Expr] = None # self.orelse + + +class UnaryExpr(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "UnaryExpr" + self.op: Optional[UnaryOp] = None + self.operand: Optional[Expr] = None + + +class BinaryExpr(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "BinaryExpr" + self.left: Optional[Expr] = None + self.op: Optional[Union[BinOp, CmpOp]] = None + self.right: Optional[Expr] = None + + def __str__(self): + return ( + super().__str__()[:-1] + + f" left: {self.left} op: {self.op} right: {self.right}>" + ) + + +class SelectorExpr(Expr): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + ctx: ExprContext = ExprContext.LOAD, + ) -> None: + super().__init__(line, column) + self._ast_type = "SelectorExpr" + self.value: Optional[Expr] = None + self.attr: Optional[Identifier] = None + self.ctx: Optional[ExprContext] = ctx + self.has_question: bool = False + + +class CallExpr(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "CallExpr" + self.func: Optional[Expr] = None + self.args: List[Expr] = [] + self.keywords: List[Keyword] = [] + + def __str__(self): + return super().__str__()[:-1] + f" func: {self.func} args: {self.args}>" + + +class ParenExpr(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "ParenExpr" + self.expr: Optional[Expr] = None + + +class QuantExpr(Expr): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + ctx: ExprContext = ExprContext.LOAD, + ) -> None: + super().__init__(line, column) + self._ast_type = "QuantExpr" + self.target: Optional[Expr] = None + self.variables: List[Identifier] = [] + self.op: Optional[int] = None + self.test: Optional[Expr] = None + self.if_cond: Optional[Expr] = None + self.ctx: ExprContext = ctx + + +class QuantOperation: + ALL = 1 + ANY = 2 + FILTER = 3 + MAP = 4 + + MAPPING = { + "any": ANY, + "all": ALL, + "map": MAP, + "filter": FILTER, + } + + +class ListExpr(Expr): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + ctx: ExprContext = ExprContext.LOAD, + ) -> None: + super().__init__(line, column) + self._ast_type = "ListExpr" + self.elts: List[Expr] = [] + self.ctx: ExprContext = ctx + + def __str__(self): + return super().__str__()[:-1] + f" values: {self.elts}>" + + +class ListIfItemExpr(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "ListIfItemExpr" + self.if_cond: Optional[Expr] = None + self.exprs: List[Expr] = [] + self.orelse: Optional[Expr] = None + + +class ListComp(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "ListComp" + self.elt: Optional[Expr] = None + self.generators: List[CompClause] = [] + + +class StarredExpr(Expr): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + ctx: ExprContext = ExprContext.LOAD, + ) -> None: + super().__init__(line, column) + self._ast_type = "StarredExpr" + self.value: Optional[Expr] = None + self.ctx: ExprContext = ctx + + +class DictComp(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "DictComp" + self.key: Optional[Expr] = None + self.value: Optional[Expr] = None + self.operation: Optional[ConfigEntryOperation] = None + self.generators: List[CompClause] = [] + + +class ConfigIfEntryExpr(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "ConfigIfEntryExpr" + self.if_cond: Optional[Expr] = None + self.keys: List[Expr] = [] + self.values: List[Expr] = [] + self.operations: List[int] = [] + self.orelse: Optional[Expr] = None + + +class CompClause(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "CompClause" + self.targets: List[Identifier] = [] + self.iter: Optional[Expr] = None + self.ifs: List[Expr] = [] + + +class SchemaExpr(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "SchemaExpr" + self.name: Optional[Identifier] = None + self.args: List[Expr] = [] + self.kwargs: List[Keyword] = [] + self.config: Optional[ConfigExpr] = None + + def __str__(self): + return super().__str__()[:-1] + f" name: {self.name} config: {self.config}>" + + +class ConfigExpr(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "ConfigExpr" + self.items: List[ConfigEntry] = [] + + @property + def keys(self) -> List[Expr]: + return [item.key for item in self.items] + + @property + def values(self) -> List[Expr]: + return [item.value for item in self.items] + + @property + def operations(self) -> List[int]: + return [item.operation for item in self.items] + + def __str__(self): + return super().__str__()[:-1] + f" keys: {self.keys} values: {self.values}>" + + +class ConfigEntryOperation: + UNION = 0 + OVERRIDE = 1 + INSERT = 2 + UNIQUE = 3 + UNIFICATION = 4 + MAPPING = { + LarkToken.L_COLON: UNION, + LarkToken.L_ASSIGN: OVERRIDE, + LarkToken.L_COMP_PLUS: INSERT, + } + + @staticmethod + def get_min(): + return ConfigEntryOperation.UNION + + @staticmethod + def get_max(): + return ConfigEntryOperation.UNIFICATION + + +class ConfigEntry(AST): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + key: Expr = None, + value: Expr = None, + operation: int = ConfigEntryOperation.UNION, + ) -> None: + super().__init__(line, column) + self._ast_type = "ConfigEntry" + self.key: Expr = key + self.value: Expr = value + self.operation: int = operation + self.insert_index: int = -1 + + def __str__(self): + return super().__str__()[:-1] + f" key: {self.key} value: {self.value}>" + + +class CheckExpr(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "CheckExpr" + self.test: Optional[Expr] = None + self.if_cond: Optional[Expr] = None + self.msg: Optional[Expr] = None + + +class LambdaExpr(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "LambdaExpr" + self.args: Optional[Arguments] = None + self.return_type_str: Optional[str] = None + self.return_type_node: Optional[Type] = None + self.body: List[Stmt] = [] + + +class Decorator(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "Decorator" + self.name: Optional[Identifier] = None + self.args: Optional[CallExpr] = None + + +class Subscript(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "Subscript" + self.value: Optional[Expr] = None + self.index: Optional[Expr] = None + self.lower: Optional[Expr] = None + self.upper: Optional[Expr] = None + self.step: Optional[Expr] = None + self.ctx: ExprContext = ExprContext.LOAD + self.has_question: bool = False + + +class Keyword(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "Keyword" + self.arg: Optional[Identifier] = None + self.value: Optional[Expr] = None + + +class Arguments(Expr): + def __init__( + self, line: Optional[int] = None, column: Optional[int] = None + ) -> None: + super().__init__(line, column) + self._ast_type = "Arguments" + self.args: List[Identifier] = [] # arg0, arg1, ... + self.defaults: List[Expr] = [] + self.type_annotation_list: List[str] = [] + self.type_annotation_node_list: List[Type] = [] + + def __str__(self): + return super().__str__()[:-1] + f" args: {self.args}>" + + def GetArgName(self, i: int) -> str: + if 0 <= i < len(self.args): + return self.args[i].get_name() + return "" + + def GetArgType(self, i: int) -> str: + if 0 <= i < len(self.type_annotation_list): + return self.type_annotation_list[i] + return "" + + def SetArgType(self, i: int, tpe: str): + if 0 <= i < len(self.type_annotation_list): + self.type_annotation_list[i] = tpe + + def GetArgDefault(self, i: int) -> Optional[str]: + if 0 <= i < len(self.defaults): + return ( + typing.cast(StringLit, self.defaults[i]).value + if self.defaults[i] + else None + ) + return None + + +class Compare(Expr): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + ) -> None: + super().__init__(line, column) + self._ast_type = "Compare" + self.left: Optional[Expr] = None + self.ops: List[CmpOp] = [] + self.comparators: List[Expr] = [] + + +class Identifier(Expr): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + names: List[str] = None, + ctx: ExprContext = ExprContext.LOAD, + ) -> None: + super().__init__(line, column) + self._ast_type = "Identifier" + self.names: List[str] = names if names else [] + self.pkgpath: str = "" + self.ctx: ExprContext = ctx + self.name_nodes: List[Name] = [] + + def set_filename(self, filename: str) -> Expr: + self.filename = filename + return self + + def get_name(self, wrapper=True): + return ".".join(self.names) if wrapper else self.names[-1] + + def get_first_name(self): + return self.names[0] + + def set_ctx(self, ctx: ExprContext): + self.ctx = ctx + + def __str__(self): + return super().__str__()[:-1] + f" name: {self.get_name()}>" + + +class Literal(Expr): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + value=None, + ) -> None: + super().__init__(line, column) + self._ast_type = "Literal" + self.value = value + + def __str__(self): + return super().__str__()[:-1] + f" value: {self.value}>" + + +class NumberLit(Literal): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + value: Optional[int] = None, + ) -> None: + super().__init__(line, column, value) + self._ast_type = "NumberLit" + self.binary_suffix: Optional[str] = None + + +class NumberBinarySuffix: + n = "n" + u = "u" + m = "m" + k = "k" + K = "K" + M = "M" + G = "G" + T = "T" + P = "P" + Ki = "Ki" + Mi = "Mi" + Gi = "Gi" + Ti = "Ti" + Pi = "Pi" + + +class StringLit(Literal): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + value: Optional[str] = None, + ) -> None: + super().__init__(line, column, value) + self._ast_type = "StringLit" + self.is_long_string: bool = False + self.raw_value: Optional[str] = None + + +class NameConstantLit(Literal): + """ + Name constant: True, False, None + """ + + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + value: Optional[bool] = None, + ) -> None: + super().__init__(line, column, value) + self._ast_type = "NameConstantLit" + + +class JoinedString(Expr): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + ) -> None: + super().__init__(line, column) + self._ast_type = "JoinedString" + self.is_long_string: bool = False + self.values: List[Union[StringLit, FormattedValue]] = [] + self.raw_value: Optional[str] = None + + +class FormattedValue(Expr): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + value: Optional[Expr] = None, + format_spec: str = None, + ) -> None: + super().__init__(line, column) + self._ast_type = "FormattedValue" + self.is_long_string: bool = False + self.value: Optional[Expr] = value + self.format_spec: str = format_spec + + +class Comment(AST): + def __init__( + self, + filename: Optional[str] = None, + line: Optional[int] = None, + column: Optional[int] = None, + end_line: Optional[int] = None, + end_column: Optional[int] = None, + text: Optional[str] = None, + ) -> None: + super().__init__(line, column, end_line=end_line, end_column=end_column) + self.filename = filename + self._ast_type = "Comment" + self.text: Optional[str] = text + + def __str__(self): + return super().__str__()[:-1] + f" text: {self.text}>" + + +class CommentGroup(AST): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + ) -> None: + super().__init__(line, column) + self._ast_type = "CommentGroup" + self.comments: List[Comment] = [] + + +class Type(AST): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + ) -> None: + super().__init__(line, column) + self._ast_type = "Type" + # type_element: schema_type | basic_type | compound_type | literal_type + self.type_elements: List[ + Union[Identifier, BasicType, ListType, DictType, LiteralType] + ] = [] + self.plain_type_str: str = "" + + +class BasicType(AST): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + ) -> None: + super().__init__(line, column) + self._ast_type = "BasicType" + self.type_name: str = "" + + +class ListType(AST): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + ) -> None: + super().__init__(line, column) + self._ast_type = "ListType" + self.inner_type: Optional[Type] = None + self.plain_type_str: str = "" + + +class DictType(AST): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + ) -> None: + super().__init__(line, column) + self._ast_type = "DictType" + self.key_type: Optional[Type] = None + self.value_type: Optional[Type] = None + self.plain_type_str: str = "" + + +class LiteralType(AST): + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + ) -> None: + super().__init__(line, column) + self._ast_type = "LiteralType" + self.plain_value: str = "" + self.value_type: str = "" + self.string_value: Optional[StringLit] = None + self.number_value: Optional[NumberLit] = None + + +class Module(AST): + """Module is an abstract syntax tree for a single KCL file.""" + + def __init__( + self, + line: Optional[int] = None, + column: Optional[int] = None, + pkg: str = "", + filename: str = "", + ) -> None: + super().__init__(line, column) + self._ast_type = "Module" + self.pkg: str = pkg + self.filename: str = filename + self.body: List[Stmt] = [] + + self.doc: str = "" + self.name = self.pkg # {__main__} or same as {self.pkg} + + self.global_names: List[str] = [] + self.local_names: Dict[str, List[str]] = {} + + self.comments: List[ + Comment + ] = [] # List of all comments in the source KCL module + + def __str__(self): + return ( + super().__str__()[:-1] + + f" pkg: {self.pkg} filename: {self.filename} body: {self.body}>" + ) + + def GetPkgpath(self, asname: str) -> str: + for stmt in self.body or []: + if isinstance(stmt, ImportStmt): + import_spec = typing.cast(ImportStmt, stmt) + if import_spec.asname == asname: + return import_spec.path + return "" + + def GetImportList(self) -> List[ImportStmt]: + import_list: List[ImportStmt] = [] + + for stmt in self.body or []: + if isinstance(stmt, ImportStmt): + import_list.append(stmt) + + return import_list + + def GetSchemaList(self) -> List[SchemaStmt]: + schema_list: List[SchemaStmt] = [] + + for stmt in self.body or []: + if isinstance(stmt, SchemaStmt): + schema_list.append(stmt) + + return schema_list + + def GetSchemaAndRuleList(self) -> List[Union[SchemaStmt, RuleStmt]]: + schema_rule_list: List[Union[SchemaStmt, RuleStmt]] = [] + + for stmt in self.body or []: + if isinstance(stmt, (SchemaStmt, RuleStmt)): + schema_rule_list.append(stmt) + + return schema_rule_list + + def GetFileName(self, root: str = "") -> str: + """# Construct the filename from the root path and relative file.""" + if root and self.relative_filename and self.relative_filename[0] == ".": + filename = root + self.relative_filename[1:] + else: + filename = self.filename + return filename + + def GetFirstExprInExprStmt(self) -> Expr: + if self.body and len(self.body) > 0: + if isinstance(self.body[0], ExprStmt) and len(self.body[0].exprs) > 0: + return self.body[0].exprs[0] + return None + + +class ASTFactory: + @staticmethod + def get_ast_module(node, pkg, filename, name) -> Module: + check_utils.check_type_allow_none(filename, str, PosixPath) + check_utils.check_allow_none(pkg, str) + + p = Module() + p.line = 1 + p.column = 1 + p.pkg = pkg + p.set_ast_position(node, filename) + p.name = name + return p + + @staticmethod + def get_ast_configentry(key, value, operation, filename) -> ConfigEntry: + check_utils.check_type_allow_none(key, Identifier, StringLit) + check_utils.check_allow_none(value, Expr) + check_utils.check_type_allow_none(filename, str, PosixPath) + p = ConfigEntry( + line=key.line if key else value.line, + column=key.column if key else value.column, + key=key, + value=value, + operation=operation, + ) + p.filename = filename + return p + + @staticmethod + def get_ast_identifier(value: str) -> Identifier: + check_utils.check_not_none(value, str) + p = Identifier() + p.names = value.split(".") + return p + + @staticmethod + def get_ast_literal(tpe: typing.Type[AST], line, column, value, filename): + assert tpe + check_utils.check_allow_none(line, int) + check_utils.check_allow_none(column, int) + check_utils.check_type_allow_none(filename, str, PosixPath) + p = tpe(line, column, value) + p.filename = filename + return typing.cast(tpe, p) + + @staticmethod + def get_ast(tpe: typing.Type[AST], node, filename): + assert tpe + check_utils.check_type_allow_none(filename, str, PosixPath) + p = tpe().set_ast_position(node, filename) + return typing.cast(tpe, p) + + @staticmethod + def get_op(tpe: typing.Type[Enum], op_type: str): + assert tpe + check_utils.check_allow_none(op_type, str) + p = tpe(op_type) if op_type else None + return typing.cast(tpe, p) + + @staticmethod + def get_ast_formatted_value(value, format_spec, filename): + check_utils.check_type_allow_none(filename, str, PosixPath) + check_utils.check_allow_none(value, Expr) + check_utils.check_allow_none(format_spec, str) + p = FormattedValue(value=value, format_spec=format_spec) + p.set_ast_position(value, filename) + return p + + +class Program: + """Program is the AST collection of all files of the running KCL program.""" + + MAIN_PKGPATH = "__main__" + + def __init__( + self, + *, + root: str = "", + main: str = "", + pkgs: Dict[str, List[Module]] = None, + cmd_args: List[CmdArgSpec] = None, + cmd_overrides: List[CmdOverrideSpec] = None, + ): + self.root: str = root if root else "" + self.main: str = main if main else "" + self.pkgs: Dict[str, List[Module]] = pkgs if pkgs else {} + + self.cmd_args: List[CmdArgSpec] = cmd_args if cmd_args else [] + self.cmd_overrides: List[CmdOverrideSpec] = ( + cmd_overrides if cmd_overrides else [] + ) + + def get_check_sum(self, root: str = "") -> str: + """Get the AST program all file md5 sum""" + check_sum = hashlib.md5() + for modules in self.pkgs.values(): + for module in modules: + if ( + root + and module.relative_filename + and module.relative_filename[0] == "." + ): + filename = root + module.relative_filename[1:] + else: + filename = module.filename + if os.path.isfile(filename): + # Encoding the filename into the checksum + check_sum.update( + (filename.replace(root, ".", 1) if root else filename).encode( + encoding="utf-8" + ) + ) + with open(filename, "rb") as f: + check_sum.update(f.read()) + return check_sum.hexdigest() + + def to_json(self, indent=4, sort_keys=False): + return json.dumps( + self, + default=lambda o: o.value + if type(o) in AST_ENUM_LIST.values() + else o.__dict__, + indent=indent, + sort_keys=sort_keys, + ) diff --git a/internal/kclvm_py/kcl/ast/fields_map.py b/internal/kclvm_py/kcl/ast/fields_map.py new file mode 100644 index 000000000..57210e13f --- /dev/null +++ b/internal/kclvm_py/kcl/ast/fields_map.py @@ -0,0 +1,104 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +from typing import Tuple, Union, List + +import kclvm.kcl.ast as ast + +# Auto Generated +AST_FIELDS_MAP = { + # Stmt + "ExprStmt": ["exprs"], + "UnificationStmt": ["target", "value"], + "TypeAliasStmt": ["type_name"], + "AssignStmt": ["targets", "value", "type_annotation_node"], + "AugAssignStmt": ["target", "op", "value"], + "AssertStmt": ["test", "if_cond", "msg"], + "IfStmt": ["cond", "body", "elif_cond", "elif_body", "else_body"], + "ImportStmt": ["path", "name", "asname", "rawpath", "path_nodes", "as_name_node"], + "SchemaAttr": [ + "doc", + "name", + "type_str", + "op", + "value", + "is_optional", + "decorators", + "name_node", + "type_node", + ], + "SchemaStmt": [ + "doc", + "name", + "parent_name", + "is_mixin", + "args", + "mixins", + "body", + "decorators", + "checks", + "index_signature", + "name_node", + "for_host_name", + ], + "RuleStmt": [ + "doc", + "name", + "parent_rules", + "decorators", + "args", + "checks", + "name_node", + "for_host_name", + ], + # Expr + "QuantExpr": ["target", "variables", "test", "if_cond"], + "SchemaIndexSignature": ["value", "name_node", "value_type_node"], + "IfExpr": ["body", "cond", "orelse"], + "UnaryExpr": ["op", "operand"], + "BinaryExpr": ["left", "op", "right"], + "SelectorExpr": ["value", "attr", "has_question"], + "CallExpr": ["func", "args", "keywords"], + "Subscript": ["value", "index", "lower", "upper", "step", "has_question"], + "ParenExpr": ["expr"], + "Operand": ["value"], + "ListExpr": ["elts"], + "ListComp": ["elt", "generators"], + "ListIfItemExpr": ["if_cond", "exprs", "orelse"], + "StarredExpr": ["value"], + "DictComp": ["key", "value", "generators"], + "ConfigIfEntryExpr": ["if_cond", "keys", "values", "orelse"], + "CompClause": ["targets", "iter", "ifs"], + "SchemaExpr": ["name", "args", "kwargs", "config"], + "ConfigExpr": ["items"], + "ConfigEntry": ["key", "value", "operation", "insert_index"], + "CheckExpr": ["test", "if_cond", "msg"], + "LambdaExpr": ["args", "body"], + "Decorator": ["name", "args"], + "Keyword": ["arg", "value"], + "Arguments": ["args", "defaults", "type_annotation_node_list"], + "Compare": ["left", "ops", "comparators"], + "Identifier": ["names", "name_nodes"], + "Name": ["value"], + "Literal": ["value"], + "NumberLit": ["value"], + "StringLit": ["value", "is_long_string"], + "NameConstantLit": ["value"], + "JoinedString": ["values", "is_long_string"], + "FormattedValue": ["value", "is_long_string", "format_spec"], + "Comment": ["text"], + "CommentGroup": ["comments"], + "Module": ["body", "comments"], + # Type + "Type": ["type_elements"], + "BasicType": ["type_name"], + "ListType": ["inner_type"], + "DictType": ["key_type", "value_type"], + "LiteralType": ["string_value", "number_value"], +} + + +def iter_fields(t: ast.AST) -> List[Tuple[str, Union[ast.AST, List[ast.AST]]]]: + """Return ast node attribute-value AST pair""" + if not t or not isinstance(t, ast.AST): + return [] + return [(field, getattr(t, field)) for field in AST_FIELDS_MAP[t.type]] diff --git a/internal/kclvm_py/kcl/ast/lark_token.py b/internal/kclvm_py/kcl/ast/lark_token.py new file mode 100644 index 000000000..f2f4a208b --- /dev/null +++ b/internal/kclvm_py/kcl/ast/lark_token.py @@ -0,0 +1,581 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +# Auto generated by {gen_lark_token.go & kcl.lark}; DO NOT EDIT!!! + + +class LarkToken: + + # kcl.lark rules and tokens (len=186) + L_start = "start" # start: (NEWLINE | statement)* ... + L_statement = "statement" # statement: simple_stmt | compound_stmt ... + L_simple_stmt = "simple_stmt" # simple_stmt: (assign_stmt | unification_stmt | expr_stmt | assert_stmt | import_stmt | type_alias_stmt) NEWLINE ... + L_compound_stmt = "compound_stmt" # compound_stmt: if_stmt | schema_stmt | rule_stmt ... + L_import_stmt = "import_stmt" # import_stmt: IMPORT dot_name (AS NAME)? ... + L_dot_name = "dot_name" # dot_name: (leading_dots identifier) | identifier ... + L_leading_dots = "leading_dots" # leading_dots: DOT+ ... + L_assert_stmt = "assert_stmt" # assert_stmt: ASSERT simple_expr (IF simple_expr)? (COMMA test)? ... + L_if_stmt = "if_stmt" # if_stmt: IF test COLON execution_block (ELIF test COLON execution_block)* (ELSE COLON execution_block)? ... + L_execution_block = "execution_block" # execution_block: if_simple_stmt | NEWLINE _INDENT schema_init_stmt+ _DEDENT ... + L_if_simple_stmt = "if_simple_stmt" # if_simple_stmt: (simple_assign_stmt | unification_stmt | expr_stmt | assert_stmt) NEWLINE ... + L_assign_stmt = "assign_stmt" # assign_stmt: identifier [COLON type] (ASSIGN identifier)* ASSIGN test ... + L_simple_assign_stmt = "simple_assign_stmt" # simple_assign_stmt: identifier ASSIGN test ... + L_unification_stmt = "unification_stmt" # unification_stmt: identifier COLON schema_expr ... + L_schema_stmt = "schema_stmt" # schema_stmt: [decorators] (SCHEMA|MIXIN|PROTOCOL) NAME [LEFT_BRACKETS [schema_arguments] RIGHT_BRACKETS] [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] [for_host] COLON NEWLINE [schema_body] ... + L_schema_arguments = "schema_arguments" # schema_arguments: schema_argument (COMMA schema_argument)* ... + L_schema_argument = "schema_argument" # schema_argument: NAME [COLON type] [ASSIGN test] ... + L_schema_body = "schema_body" # schema_body: _INDENT (string NEWLINE)* [mixin_stmt] (schema_attribute_stmt|schema_init_stmt|schema_index_signature)* [check_block] _DEDENT ... + L_schema_attribute_stmt = "schema_attribute_stmt" # schema_attribute_stmt: attribute_stmt NEWLINE ... + L_attribute_stmt = "attribute_stmt" # attribute_stmt: [decorators] identifier [QUESTION] COLON type [(ASSIGN|COMP_OR) test] ... + L_schema_init_stmt = "schema_init_stmt" # schema_init_stmt: if_simple_stmt | if_stmt ... + L_schema_index_signature = "schema_index_signature" # schema_index_signature: LEFT_BRACKETS [NAME COLON] [ELLIPSIS] basic_type RIGHT_BRACKETS COLON type [ASSIGN test] NEWLINE ... + L_rule_stmt = "rule_stmt" # rule_stmt: [decorators] RULE NAME [LEFT_BRACKETS [schema_arguments] RIGHT_BRACKETS] [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] [for_host] COLON NEWLINE [rule_body] ... + L_rule_body = "rule_body" # rule_body: _INDENT (string NEWLINE)* check_expr+ _DEDENT ... + L_for_host = "for_host" # for_host: FOR identifier ... + L_decorators = "decorators" # decorators: (AT decorator_expr NEWLINE)+ ... + L_decorator_expr = "decorator_expr" # decorator_expr: identifier [call_suffix] ... + L_type = "type" # @type: type_element (OR type_element)* ... + L_type_element = "type_element" # type_element: schema_type | basic_type | compound_type | literal_type ... + L_schema_type = "schema_type" # schema_type: identifier ... + L_basic_type = "basic_type" # basic_type: STRING_TYPE | INT_TYPE | FLOAT_TYPE | BOOL_TYPE | ANY_TYPE ... + L_compound_type = "compound_type" # compound_type: list_type | dict_type ... + L_list_type = "list_type" # list_type: LEFT_BRACKETS (type)? RIGHT_BRACKETS ... + L_dict_type = "dict_type" # dict_type: LEFT_BRACE (type)? COLON (type)? RIGHT_BRACE ... + L_literal_type = "literal_type" # literal_type: string | number | TRUE | FALSE | NONE ... + L_type_alias_stmt = "type_alias_stmt" # type_alias_stmt: TYPE NAME ASSIGN type ... + L_check_block = "check_block" # check_block: CHECK COLON NEWLINE _INDENT check_expr+ _DEDENT ... + L_check_expr = "check_expr" # check_expr: simple_expr [IF simple_expr] [COMMA primary_expr] NEWLINE ... + L_mixin_stmt = "mixin_stmt" # mixin_stmt: MIXIN LEFT_BRACKETS [mixins | multiline_mixins] RIGHT_BRACKETS NEWLINE ... + L_multiline_mixins = "multiline_mixins" # multiline_mixins: NEWLINE _INDENT mixins NEWLINE _DEDENT ... + L_mixins = "mixins" # mixins: identifier (COMMA (NEWLINE mixins | identifier))* ... + L_expr_stmt = "expr_stmt" # expr_stmt: testlist_expr ... + L_testlist_expr = "testlist_expr" # testlist_expr: test (COMMA test)* ... + L_test = "test" # test: if_expr | simple_expr ... + L_if_expr = "if_expr" # if_expr: simple_expr IF simple_expr ELSE test ... + L_simple_expr = "simple_expr" # simple_expr: unary_expr | binary_expr | primary_expr ... + L_unary_expr = "unary_expr" # unary_expr: un_op simple_expr ... + L_binary_expr = "binary_expr" # binary_expr: simple_expr bin_op simple_expr ... + L_bin_op = "bin_op" # bin_op: L_OR | L_AND ... + L_un_op = "un_op" # un_op: L_NOT | PLUS | MINUS | NOT ... + L_primary_expr = "primary_expr" # primary_expr: identifier call_suffix | operand | primary_expr select_suffix | primary_expr call_suffix | primary_expr slice_suffix ... + L_operand = "operand" # operand: identifier | number | string | constant | quant_expr | list_expr | list_comp | config_expr | dict_comp | schema_expr | lambda_expr | LEFT_PARENTHESES test RIGHT_PARENTHESES ... + L_select_suffix = "select_suffix" # select_suffix: [QUESTION] DOT NAME ... + L_call_suffix = "call_suffix" # call_suffix: LEFT_PARENTHESES [arguments [COMMA]] RIGHT_PARENTHESES ... + L_slice_suffix = "slice_suffix" # slice_suffix: [QUESTION] LEFT_BRACKETS (test | [test] COLON [test] [COLON [test]]) RIGHT_BRACKETS ... + L_arguments = "arguments" # arguments: argument (COMMA argument)* ... + L_argument = "argument" # argument: test | NAME ASSIGN test | MULTIPLY test | DOUBLE_STAR test ... + L_identifier = "identifier" # identifier: NAME (DOT NAME)* ... + L_quant_expr = "quant_expr" # quant_expr: quant_op [ identifier COMMA ] identifier IN quant_target LEFT_BRACE (simple_expr [IF simple_expr] | NEWLINE _INDENT simple_expr [IF simple_expr] NEWLINE _DEDENT)? RIGHT_BRACE ... + L_quant_target = "quant_target" # quant_target: string | identifier | list_expr | list_comp | config_expr | dict_comp ... + L_quant_op = "quant_op" # quant_op: ALL | ANY | FILTER | MAP ... + L_list_expr = "list_expr" # list_expr: LEFT_BRACKETS [list_items | NEWLINE [_INDENT list_items _DEDENT]] RIGHT_BRACKETS ... + L_list_items = "list_items" # list_items: list_item ((COMMA [NEWLINE] | [NEWLINE]) list_item)* [COMMA] [NEWLINE] ... + L_list_item = "list_item" # list_item: test | star_expr | if_item ... + L_list_comp = "list_comp" # list_comp: LEFT_BRACKETS (list_item comp_clause+ | NEWLINE _INDENT list_item comp_clause+ _DEDENT) RIGHT_BRACKETS ... + L_dict_comp = "dict_comp" # dict_comp: LEFT_BRACE (entry comp_clause+ | NEWLINE _INDENT entry comp_clause+ _DEDENT) RIGHT_BRACE ... + L_entry = "entry" # entry: test (COLON | ASSIGN | COMP_PLUS) test ... + L_comp_clause = "comp_clause" # comp_clause: FOR loop_variables [COMMA] IN simple_expr [NEWLINE] [IF test [NEWLINE]] ... + L_if_entry = "if_entry" # if_entry: IF test COLON if_entry_exec_block (ELIF test COLON if_entry_exec_block)* (ELSE COLON if_entry_exec_block)? ... + L_if_entry_exec_block = "if_entry_exec_block" # if_entry_exec_block: (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry) [NEWLINE] | NEWLINE _INDENT (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry) ((COMMA [NEWLINE] | [NEWLINE]) (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry))* [COMMA] [NEWLINE] _DEDENT ... + L_if_item = "if_item" # if_item: IF test COLON if_item_exec_block (ELIF test COLON if_item_exec_block)* (ELSE COLON if_item_exec_block)? ... + L_if_item_exec_block = "if_item_exec_block" # if_item_exec_block: list_item [NEWLINE] | NEWLINE _INDENT list_item ((COMMA [NEWLINE] | NEWLINE) list_item)* [COMMA] [NEWLINE] _DEDENT ... + L_star_expr = "star_expr" # star_expr: MULTIPLY primary_expr ... + L_double_star_expr = "double_star_expr" # double_star_expr: DOUBLE_STAR primary_expr ... + L_loop_variables = "loop_variables" # loop_variables: primary_expr (COMMA primary_expr)* ... + L_schema_expr = "schema_expr" # schema_expr: identifier (LEFT_PARENTHESES [arguments] RIGHT_PARENTHESES)? config_expr ... + L_config_expr = "config_expr" # config_expr: LEFT_BRACE [config_entries | NEWLINE [_INDENT config_entries _DEDENT]] RIGHT_BRACE ... + L_config_entries = "config_entries" # config_entries: config_entry ((COMMA [NEWLINE] | [NEWLINE]) config_entry)* [COMMA] [NEWLINE] ... + L_config_entry = "config_entry" # config_entry: test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry ... + L_lambda_expr = "lambda_expr" # lambda_expr: LAMBDA [schema_arguments] [RIGHT_ARROW type] LEFT_BRACE [expr_stmt | NEWLINE _INDENT schema_init_stmt+ _DEDENT] RIGHT_BRACE ... + L_number = "number" # number: DEC_NUMBER [multiplier] | HEX_NUMBER | BIN_NUMBER | OCT_NUMBER | FLOAT_NUMBER ... + L_multiplier = "multiplier" # multiplier: SI_N_L | SI_U_L | SI_M_L | SI_K_L | SI_K | SI_M | SI_G | SI_T | SI_P ... + L_string = "string" # string: STRING | LONG_STRING ... + L_ASSIGN = "ASSIGN" # ASSIGN: "=" ... + L_COLON = "COLON" # COLON: ":" ... + L_SEMI_COLON = "SEMI_COLON" # SEMI_COLON: ";" ... + L_COMMA = "COMMA" # COMMA: "," ... + L_QUESTION = "QUESTION" # QUESTION: "?" ... + L_ELLIPSIS = "ELLIPSIS" # ELLIPSIS: "..." ... + L_RIGHT_ARROW = "RIGHT_ARROW" # RIGHT_ARROW: "->" ... + L_LEFT_PARENTHESES = "LEFT_PARENTHESES" # LEFT_PARENTHESES: "(" ... + L_RIGHT_PARENTHESES = "RIGHT_PARENTHESES" # RIGHT_PARENTHESES: ")" ... + L_LEFT_BRACKETS = "LEFT_BRACKETS" # LEFT_BRACKETS: "[" ... + L_RIGHT_BRACKETS = "RIGHT_BRACKETS" # RIGHT_BRACKETS: "]" ... + L_LEFT_BRACE = "LEFT_BRACE" # LEFT_BRACE: "{" ... + L_RIGHT_BRACE = "RIGHT_BRACE" # RIGHT_BRACE: "}" ... + L_PLUS = "PLUS" # PLUS: "+" ... + L_MINUS = "MINUS" # MINUS: "-" ... + L_MULTIPLY = "MULTIPLY" # MULTIPLY: "*" ... + L_DIVIDE = "DIVIDE" # DIVIDE: "/" ... + L_MOD = "MOD" # MOD: "%" ... + L_DOT = "DOT" # DOT: "." ... + L_AND = "AND" # AND: "&" ... + L_OR = "OR" # OR: "|" ... + L_XOR = "XOR" # XOR: "^" ... + L_NOT = "NOT" # NOT: "~" ... + L_LESS_THAN = "LESS_THAN" # LESS_THAN: "<" ... + L_GREATER_THAN = "GREATER_THAN" # GREATER_THAN: ">" ... + L_EQUAL_TO = "EQUAL_TO" # EQUAL_TO: "==" ... + L_NOT_EQUAL_TO = "NOT_EQUAL_TO" # NOT_EQUAL_TO: "!=" ... + L_GREATER_THAN_OR_EQUAL_TO = "GREATER_THAN_OR_EQUAL_TO" # GREATER_THAN_OR_EQUAL_TO: ">=" ... + L_LESS_THAN_OR_EQUAL_TO = "LESS_THAN_OR_EQUAL_TO" # LESS_THAN_OR_EQUAL_TO: "<=" ... + L_DOUBLE_STAR = "DOUBLE_STAR" # DOUBLE_STAR: "**" ... + L_DOUBLE_DIVIDE = "DOUBLE_DIVIDE" # DOUBLE_DIVIDE: "//" ... + L_SHIFT_LEFT = "SHIFT_LEFT" # SHIFT_LEFT: "<<" ... + L_SHIFT_RIGHT = "SHIFT_RIGHT" # SHIFT_RIGHT: ">>" ... + L_AT = "AT" # AT: "@" ... + L_COMP_PLUS = "COMP_PLUS" # COMP_PLUS: "+=" ... + L_COMP_MINUS = "COMP_MINUS" # COMP_MINUS: "-=" ... + L_COMP_MULTIPLY = "COMP_MULTIPLY" # COMP_MULTIPLY: "*=" ... + L_COMP_DIVIDE = "COMP_DIVIDE" # COMP_DIVIDE: "/=" ... + L_COMP_MOD = "COMP_MOD" # COMP_MOD: "%=" ... + L_COMP_AND = "COMP_AND" # COMP_AND: "&=" ... + L_COMP_OR = "COMP_OR" # COMP_OR: "|=" ... + L_COMP_XOR = "COMP_XOR" # COMP_XOR: "^=" ... + L_COMP_DOUBLE_STAR = "COMP_DOUBLE_STAR" # COMP_DOUBLE_STAR: "**=" ... + L_COMP_DOUBLE_DIVIDE = "COMP_DOUBLE_DIVIDE" # COMP_DOUBLE_DIVIDE: "//=" ... + L_COMP_SHIFT_LEFT = "COMP_SHIFT_LEFT" # COMP_SHIFT_LEFT: "<<=" ... + L_COMP_SHIFT_RIGHT = "COMP_SHIFT_RIGHT" # COMP_SHIFT_RIGHT: ">>=" ... + L_IMPORT = "IMPORT" # IMPORT: "import" ... + L_AS = "AS" # AS: "as" ... + L_RULE = "RULE" # RULE: "rule" ... + L_SCHEMA = "SCHEMA" # SCHEMA: "schema" ... + L_MIXIN = "MIXIN" # MIXIN: "mixin" ... + L_PROTOCOL = "PROTOCOL" # PROTOCOL: "protocol" ... + L_CHECK = "CHECK" # CHECK: "check" ... + L_FOR = "FOR" # FOR: "for" ... + L_ASSERT = "ASSERT" # ASSERT: "assert" ... + L_IF = "IF" # IF: "if" ... + L_ELIF = "ELIF" # ELIF: "elif" ... + L_ELSE = "ELSE" # ELSE: "else" ... + L_L_OR = "L_OR" # L_OR: "or" ... + L_L_AND = "L_AND" # L_AND: "and" ... + L_L_NOT = "L_NOT" # L_NOT: "not" ... + L_IN = "IN" # IN: "in" ... + L_IS = "IS" # IS: "is" ... + L_LAMBDA = "LAMBDA" # LAMBDA: "lambda" ... + L_ALL = "ALL" # ALL: "all" ... + L_ANY = "ANY" # ANY: "any" ... + L_FILTER = "FILTER" # FILTER: "filter" ... + L_MAP = "MAP" # MAP: "map" ... + L_TYPE = "TYPE" # TYPE: "type" ... + L_ANY_TYPE = "ANY_TYPE" # ANY_TYPE: "any" ... + L_STRING_TYPE = "STRING_TYPE" # STRING_TYPE: "str" ... + L_INT_TYPE = "INT_TYPE" # INT_TYPE: "int" ... + L_FLOAT_TYPE = "FLOAT_TYPE" # FLOAT_TYPE: "float" ... + L_BOOL_TYPE = "BOOL_TYPE" # BOOL_TYPE: "bool" ... + L_TRUE = "TRUE" # TRUE: "True" ... + L_FALSE = "FALSE" # FALSE: "False" ... + L_NONE = "NONE" # NONE: "None" ... + L_UNDEFINED = "UNDEFINED" # UNDEFINED: "Undefined" ... + L_SI_N_L = "SI_N_L" # SI_N_L: "n" ... + L_SI_U_L = "SI_U_L" # SI_U_L: "u" ... + L_SI_M_L = "SI_M_L" # SI_M_L: "m" ... + L_SI_K_L = "SI_K_L" # SI_K_L: "k" ... + L_SI_K = "SI_K" # SI_K: "K" ... + L_SI_M = "SI_M" # SI_M: "M" ... + L_SI_G = "SI_G" # SI_G: "G" ... + L_SI_T = "SI_T" # SI_T: "T" ... + L_SI_P = "SI_P" # SI_P: "P" ... + L_SI_K_IEC = "SI_K_IEC" # SI_K_IEC: "Ki" ... + L_SI_M_IEC = "SI_M_IEC" # SI_M_IEC: "Mi" ... + L_SI_G_IEC = "SI_G_IEC" # SI_G_IEC: "Gi" ... + L_SI_T_IEC = "SI_T_IEC" # SI_T_IEC: "Ti" ... + L_SI_P_IEC = "SI_P_IEC" # SI_P_IEC: "Pi" ... + L_IEC = "IEC" # IEC: "i" ... + L_NAME = "NAME" # NAME: /\$?[a-zA-Z_]\w*/ ... + L_COMMENT = "COMMENT" # COMMENT: /#[^\n]*/ ... + L_NEWLINE = "NEWLINE" # NEWLINE: ( /\r?\n[\t ]*/ | COMMENT )+ ... + L_STRING = "STRING" # STRING: /r?("(?!"").*?(?", + L_LEFT_PARENTHESES: "(", + L_RIGHT_PARENTHESES: ")", + L_LEFT_BRACKETS: "[", + L_RIGHT_BRACKETS: "]", + L_LEFT_BRACE: "{", + L_RIGHT_BRACE: "}", + L_PLUS: "+", + L_MINUS: "-", + L_MULTIPLY: "*", + L_DIVIDE: "/", + L_MOD: "%", + L_DOT: ".", + L_AND: "&", + L_OR: "|", + L_XOR: "^", + L_NOT: "~", + L_LESS_THAN: "<", + L_GREATER_THAN: ">", + L_EQUAL_TO: "==", + L_NOT_EQUAL_TO: "!=", + L_GREATER_THAN_OR_EQUAL_TO: ">=", + L_LESS_THAN_OR_EQUAL_TO: "<=", + L_DOUBLE_STAR: "**", + L_DOUBLE_DIVIDE: "//", + L_SHIFT_LEFT: "<<", + L_SHIFT_RIGHT: ">>", + L_AT: "@", + L_COMP_PLUS: "+=", + L_COMP_MINUS: "-=", + L_COMP_MULTIPLY: "*=", + L_COMP_DIVIDE: "/=", + L_COMP_MOD: "%=", + L_COMP_AND: "&=", + L_COMP_OR: "|=", + L_COMP_XOR: "^=", + L_COMP_DOUBLE_STAR: "**=", + L_COMP_DOUBLE_DIVIDE: "//=", + L_COMP_SHIFT_LEFT: "<<=", + L_COMP_SHIFT_RIGHT: ">>=", + L_IMPORT: "import", + L_AS: "as", + L_RULE: "rule", + L_SCHEMA: "schema", + L_MIXIN: "mixin", + L_PROTOCOL: "protocol", + L_CHECK: "check", + L_FOR: "for", + L_ASSERT: "assert", + L_IF: "if", + L_ELIF: "elif", + L_ELSE: "else", + L_L_OR: "or", + L_L_AND: "and", + L_L_NOT: "not", + L_IN: "in", + L_IS: "is", + L_LAMBDA: "lambda", + L_ALL: "all", + L_ANY: "any", + L_FILTER: "filter", + L_MAP: "map", + L_TYPE: "type", + L_ANY_TYPE: "any", + L_STRING_TYPE: "str", + L_INT_TYPE: "int", + L_FLOAT_TYPE: "float", + L_BOOL_TYPE: "bool", + L_TRUE: "True", + L_FALSE: "False", + L_NONE: "None", + L_UNDEFINED: "Undefined", + L_SI_N_L: "n", + L_SI_U_L: "u", + L_SI_M_L: "m", + L_SI_K_L: "k", + L_SI_K: "K", + L_SI_M: "M", + L_SI_G: "G", + L_SI_T: "T", + L_SI_P: "P", + L_SI_K_IEC: "Ki", + L_SI_M_IEC: "Mi", + L_SI_G_IEC: "Gi", + L_SI_T_IEC: "Ti", + L_SI_P_IEC: "Pi", + L_IEC: "i", + } + + +class TokenValue: + ASSIGN = "=" + COLON = ":" + SEMI_COLON = ";" + COMMA = "," + QUESTION = "?" + ELLIPSIS = "..." + RIGHT_ARROW = "->" + LEFT_PARENTHESES = "(" + RIGHT_PARENTHESES = ")" + LEFT_BRACKETS = "[" + RIGHT_BRACKETS = "]" + LEFT_BRACE = "{" + RIGHT_BRACE = "}" + PLUS = "+" + MINUS = "-" + MULTIPLY = "*" + DIVIDE = "/" + MOD = "%" + DOT = "." + AND = "&" + OR = "|" + XOR = "^" + NOT = "~" + LESS_THAN = "<" + GREATER_THAN = ">" + EQUAL_TO = "==" + NOT_EQUAL_TO = "!=" + GREATER_THAN_OR_EQUAL_TO = ">=" + LESS_THAN_OR_EQUAL_TO = "<=" + DOUBLE_STAR = "**" + DOUBLE_DIVIDE = "//" + SHIFT_LEFT = "<<" + SHIFT_RIGHT = ">>" + AT = "@" + COMP_PLUS = "+=" + COMP_MINUS = "-=" + COMP_MULTIPLY = "*=" + COMP_DIVIDE = "/=" + COMP_MOD = "%=" + COMP_AND = "&=" + COMP_OR = "|=" + COMP_XOR = "^=" + COMP_DOUBLE_STAR = "**=" + COMP_DOUBLE_DIVIDE = "//=" + COMP_SHIFT_LEFT = "<<=" + COMP_SHIFT_RIGHT = ">>=" + IMPORT = "import" + AS = "as" + RULE = "rule" + SCHEMA = "schema" + MIXIN = "mixin" + PROTOCOL = "protocol" + CHECK = "check" + FOR = "for" + ASSERT = "assert" + IF = "if" + ELIF = "elif" + ELSE = "else" + L_OR = "or" + L_AND = "and" + L_NOT = "not" + IN = "in" + IS = "is" + LAMBDA = "lambda" + ALL = "all" + ANY = "any" + FILTER = "filter" + MAP = "map" + TYPE = "type" + ANY_TYPE = "any" + STRING_TYPE = "str" + INT_TYPE = "int" + FLOAT_TYPE = "float" + BOOL_TYPE = "bool" + TRUE = "True" + FALSE = "False" + NONE = "None" + UNDEFINED = "Undefined" + SI_N_L = "n" + SI_U_L = "u" + SI_M_L = "m" + SI_K_L = "k" + SI_K = "K" + SI_M = "M" + SI_G = "G" + SI_T = "T" + SI_P = "P" + SI_K_IEC = "Ki" + SI_M_IEC = "Mi" + SI_G_IEC = "Gi" + SI_T_IEC = "Ti" + SI_P_IEC = "Pi" + IEC = "i" diff --git a/internal/kclvm_py/kcl/ast/precedence.py b/internal/kclvm_py/kcl/ast/precedence.py new file mode 100644 index 000000000..7a2a4662a --- /dev/null +++ b/internal/kclvm_py/kcl/ast/precedence.py @@ -0,0 +1,139 @@ +from enum import IntEnum +from typing import Union + +import kclvm.kcl.ast.ast as ast + + +class OpPrecedence(IntEnum): + """Operator precedence in grammar + + assign_stmt: // precedence 0 + or_test: and_test (L_OR and_test)* // precedence 1 + and_test: not_test (L_AND not_test)* // precedence 2 + not_test: L_NOT not_test | comparison // precedence 3 + comparison: expr (comp_op expr)* // precedence 4 + expr: xor_expr (OR xor_expr)* // precedence 5 + xor_expr: and_expr (XOR and_expr)* // precedence 6 + and_expr: shift_expr (AND shift_expr)* // precedence 7 + shift_expr: arith_expr ((SHIFT_LEFT|SHIFT_RIGHT) arith_expr)* // precedence 8 + arith_expr: term ((PLUS|MINUS) term)* // precedence 9 + term: factor ((MULTIPLY|DIVIDE|MOD|DOUBLE_DIVIDE) factor)* // precedence 10 + factor: (PLUS|MINUS|NOT) factor | power // precedence 11 + power: primary_expr (DOUBLE_STAR factor)? // precedence 12 + """ + + LOWEST = 0 + HIGHEST = 12 + + +OP_PREC_MAP = { + ast.AugOp.Assign: 0, + ast.AugOp.Add: 0, + ast.AugOp.Sub: 0, + ast.AugOp.Mul: 0, + ast.AugOp.Div: 0, + ast.AugOp.Mod: 0, + ast.AugOp.Pow: 0, + ast.AugOp.LShift: 0, + ast.AugOp.RShift: 0, + ast.AugOp.BitOr: 0, + ast.AugOp.BitXor: 0, + ast.AugOp.BitAnd: 0, + ast.AugOp.FloorDiv: 0, + ast.BinOp.Add: 9, + ast.BinOp.Sub: 9, + ast.BinOp.Mul: 10, + ast.BinOp.Div: 10, + ast.BinOp.Mod: 10, + ast.BinOp.Pow: 12, + ast.BinOp.LShift: 8, + ast.BinOp.RShift: 8, + ast.BinOp.BitOr: 6, + ast.BinOp.BitXor: 5, + ast.BinOp.BitAnd: 7, + ast.BinOp.FloorDiv: 10, + ast.BinOp.As: 4, + ast.BinOp.And: 2, + ast.BinOp.Or: 1, + ast.CmpOp.Eq: 4, + ast.CmpOp.NotEq: 4, + ast.CmpOp.Lt: 4, + ast.CmpOp.LtE: 4, + ast.CmpOp.Gt: 4, + ast.CmpOp.GtE: 4, + ast.CmpOp.Is: 4, + ast.CmpOp.In: 4, + ast.CmpOp.Not: 4, + ast.CmpOp.IsNot: 4, + ast.CmpOp.NotIn: 4, + ast.UnaryOp.UAdd: 11, + ast.UnaryOp.USub: 11, + ast.UnaryOp.Invert: 11, + ast.UnaryOp.Not: 3, +} + + +def precedence(op: Union[ast.BinOp, ast.AugOp, ast.UnaryOp, ast.CmpOp]) -> int: + """Return KCL operator precedence""" + if not op: + return int(OpPrecedence.LOWEST) + + # x ** y -> 12 + if op == ast.BinOp.Pow: + return int(OpPrecedence.HIGHEST) + + # +x, -x, ~x -> 11 + if op == ast.UnaryOp.UAdd or op == ast.UnaryOp.USub or op == ast.UnaryOp.Invert: + return OpPrecedence.HIGHEST - 1 + + # x * y, x / y, x // y, x % y -> 10 + if ( + op == ast.BinOp.Mul + or op == ast.BinOp.Div + or op == ast.BinOp.FloorDiv + or op == ast.BinOp.Mod + ): + return OpPrecedence.HIGHEST - 2 + + # x + y, x - y -> 9 + if op == ast.BinOp.Add or op == ast.BinOp.Sub: + return OpPrecedence.HIGHEST - 3 + + # x >> y, x << y -> 8 + if op == ast.BinOp.LShift or op == ast.BinOp.RShift: + return OpPrecedence.HIGHEST - 4 + + # x & y -> 7 + if op == ast.BinOp.BitAnd: + return OpPrecedence.HIGHEST - 5 + + # x | y -> 6 + if op == ast.BinOp.BitOr: + return OpPrecedence.HIGHEST - 6 + + # x ^ y -> 5 + if op == ast.BinOp.BitXor: + return OpPrecedence.HIGHEST - 7 + + # x > y, x < y, etc. -> 4 + if isinstance(op, ast.CmpOp): + return OpPrecedence.HIGHEST - 8 + + # not x -> 3 + if op == ast.UnaryOp.Not: + return OpPrecedence.HIGHEST - 9 + + # x and y -> 2 + if op == ast.BinOp.And: + return OpPrecedence.HIGHEST - 10 + + # x or y -> 1 + if op == ast.BinOp.Or: + return OpPrecedence.HIGHEST - 11 + + # x = y, x += y, x -= y -> 0 + if isinstance(op, ast.AugOp): + assert OpPrecedence.HIGHEST - 12 == OpPrecedence.LOWEST + return OpPrecedence.HIGHEST - 12 + + return int(OpPrecedence.LOWEST) diff --git a/internal/kclvm_py/kcl/ast/transformer.py b/internal/kclvm_py/kcl/ast/transformer.py new file mode 100644 index 000000000..ac2ba217e --- /dev/null +++ b/internal/kclvm_py/kcl/ast/transformer.py @@ -0,0 +1,69 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import kclvm.kcl.ast as ast + + +class TreeTransformer(ast.TreeWalker): + """The TreeTransformer subclass that walks the abstract syntax tree and + allows modification of nodes. + + The `TreeTransformer` will walk the AST and use the return value of the + walker methods to replace or remove the old node. If the return value of + the walker method is ``None``, the node will be removed from its location, + otherwise it is replaced with the return value. The return value may be the + original node in which case no replacement takes place. + + Keep in mind that if the node you're operating on has child nodes you must + either transform the child nodes yourself or call the :meth:`generic_walk` + method for the node first. + + For nodes that were part of a collection of statements (that applies to all + statement nodes), the walker may also return a list of nodes rather than + just a single node, e.g., `module` AST contains the `body` attribute, which + is a list of statement + + Example: + + class ChangeSchemaExprNameTransformer(TreeTransformer): + def walk_SchemaExpr(self, t: ast.SchemaExpr): + if t.name.get_name() == 'Person': + t.name.set_name('PersonNew') + return t + + Usually we can use the transformer like this:: + + node = YourTransformer().walk(node) + + """ + + # Base transformer functions + + def get_node_name(self, t: ast.AST): + """Get the ast.AST node name""" + assert isinstance(t, ast.AST) + return t.type + + def generic_walk(self, t: ast.AST): + """Called if no explicit walker function exists for a node.""" + for field, old_value in ast.iter_fields(t): + if isinstance(old_value, list): + new_values = [] + for value in old_value: + if isinstance(value, ast.AST): + value = self.walk(value) + if value is None: + continue + elif not isinstance(value, ast.AST): + new_values.extend(value) + continue + elif isinstance(value, list): + value = [self.walk(v) for v in value] + new_values.append(value) + old_value[:] = new_values + elif isinstance(old_value, ast.AST): + new_node = self.walk(old_value) + if new_node is None: + delattr(t, field) + else: + setattr(t, field, new_node) + return t diff --git a/internal/kclvm_py/kcl/ast/walker.py b/internal/kclvm_py/kcl/ast/walker.py new file mode 100644 index 000000000..5ba542ee6 --- /dev/null +++ b/internal/kclvm_py/kcl/ast/walker.py @@ -0,0 +1,397 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import typing +from abc import abstractmethod + +import kclvm.kcl.ast as ast + + +class TreeWalker: + """The TreeWalk class can be used as a superclass in order + to walk an AST or similar tree. + + This class is meant to be subclassed, with the subclass adding walker + methods. + + Per default the walker functions for the nodes are ``'walk_'`` + + class name of the node. So a `expr_stmt` node visit function would + be `walk_expr_stmt`. This behavior can be changed by overriding + the `walk` method. If no walker function exists for a node + (return value `None`) the `generic_walker` walker is used instead. + """ + + _WALK_FUNCTION_PREFIX = "walk_" + + def walk(self, node) -> None: + """Visit a node.""" + method = self._WALK_FUNCTION_PREFIX + self.get_node_name(node) + walker = getattr(self, method, self.generic_walk) + return walker(node) + + @abstractmethod + def generic_walk(self, node): + """Called if no explicit walker function exists for a node.""" + pass + + @abstractmethod + def get_node_name(self, node): + """Called if no explicit walker function exists for a node.""" + pass + + +# ----------------------------------------------------------------------------- +# WalkTree +# ----------------------------------------------------------------------------- + + +def _WalkTree( + t: ast.AST, walk_fn: typing.Callable[[ast.AST], typing.Optional[typing.Callable]] +): + if t and callable(walk_fn): + walk_fn = walk_fn(t) + if (not t) or (not callable(walk_fn)): + return + + if isinstance(t, ast.TypeAliasStmt): + return + + if isinstance(t, ast.ExprStmt): + node = typing.cast(ast.ExprStmt, t) + for x in node.exprs or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.UnificationStmt): + node = typing.cast(ast.UnificationStmt, t) + if node.target: + _WalkTree(node.target, walk_fn) + if node.value: + _WalkTree(node.value, walk_fn) + return + + if isinstance(t, ast.AssignStmt): + node = typing.cast(ast.AssignStmt, t) + for x in node.targets or []: + _WalkTree(x, walk_fn) + if node.value: + _WalkTree(node.value, walk_fn) + return + + if isinstance(t, ast.AugAssignStmt): + node = typing.cast(ast.AugAssignStmt, t) + if node.target: + _WalkTree(node.target, walk_fn) + if node.value: + _WalkTree(node.value, walk_fn) + return + + if isinstance(t, ast.AssertStmt): + node = typing.cast(ast.AssertStmt, t) + if node.test: + _WalkTree(node.test, walk_fn) + if node.if_cond: + _WalkTree(node.if_cond, walk_fn) + if node.msg: + _WalkTree(node.msg, walk_fn) + return + + if isinstance(t, ast.IfStmt): + node = typing.cast(ast.IfStmt, t) + if node.cond: + _WalkTree(node.cond, walk_fn) + for x in node.body or []: + _WalkTree(x, walk_fn) + for x in node.elif_cond or []: + _WalkTree(x, walk_fn) + for if_body in node.elif_body or []: + for x in if_body: + _WalkTree(x, walk_fn) + for x in node.else_body or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.ImportStmt): + return + + if isinstance(t, ast.SchemaIndexSignature): + node = typing.cast(ast.SchemaIndexSignature, t) + if node.value: + _WalkTree(node.value, walk_fn) + return + + if isinstance(t, ast.SchemaAttr): + node = typing.cast(ast.SchemaAttr, t) + if node.value: + _WalkTree(node.value, walk_fn) + for x in node.decorators or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.SchemaStmt): + node = typing.cast(ast.SchemaStmt, t) + if node.parent_name: + _WalkTree(node.parent_name, walk_fn) + if node.args: + _WalkTree(node.args, walk_fn) + if node.for_host_name: + _WalkTree(node.for_host_name, walk_fn) + if node.index_signature: + _WalkTree(node.index_signature, walk_fn) + for x in node.mixins or []: + _WalkTree(x, walk_fn) + for x in node.body or []: + _WalkTree(x, walk_fn) + for x in node.decorators or []: + _WalkTree(x, walk_fn) + for x in node.checks or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.RuleStmt): + node = typing.cast(ast.RuleStmt, t) + for x in node.parent_rules or []: + _WalkTree(x, walk_fn) + for x in node.decorators or []: + _WalkTree(x, walk_fn) + if node.args: + _WalkTree(node.args, walk_fn) + if node.for_host_name: + _WalkTree(node.for_host_name, walk_fn) + for x in node.checks or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.IfExpr): + node = typing.cast(ast.IfExpr, t) + if node.body: + _WalkTree(node.body, walk_fn) + if node.cond: + _WalkTree(node.cond, walk_fn) + if node.orelse: + _WalkTree(node.orelse, walk_fn) + return + + if isinstance(t, ast.UnaryExpr): + node = typing.cast(ast.UnaryExpr, t) + if node.operand: + _WalkTree(node.operand, walk_fn) + return + + if isinstance(t, ast.BinaryExpr): + node = typing.cast(ast.BinaryExpr, t) + if node.left: + _WalkTree(node.left, walk_fn) + if node.right: + _WalkTree(node.right, walk_fn) + return + + if isinstance(t, ast.SelectorExpr): + node = typing.cast(ast.SelectorExpr, t) + if node.value: + _WalkTree(node.value, walk_fn) + if node.attr: + _WalkTree(node.attr, walk_fn) + return + + if isinstance(t, ast.CallExpr): + node = typing.cast(ast.CallExpr, t) + if node.func: + _WalkTree(node.func, walk_fn) + for x in node.args or []: + _WalkTree(x, walk_fn) + for x in node.keywords or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.ParenExpr): + node = typing.cast(ast.ParenExpr, t) + _WalkTree(node.expr, walk_fn) + return + + if isinstance(t, ast.QuantExpr): + node = typing.cast(ast.QuantExpr, t) + for var in node.variables: + _WalkTree(var, walk_fn) + if node.target: + _WalkTree(node.target, walk_fn) + if node.test: + _WalkTree(node.test, walk_fn) + if node.if_cond: + _WalkTree(node.if_cond, walk_fn) + return + + if isinstance(t, ast.ListExpr): + node = typing.cast(ast.ListExpr, t) + for x in node.elts or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.ListComp): + node = typing.cast(ast.ListComp, t) + if node.elt: + _WalkTree(node.elt, walk_fn) + for x in node.generators or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.ListIfItemExpr): + node = typing.cast(ast.ListIfItemExpr, t) + if node.if_cond: + _WalkTree(node.if_cond, walk_fn) + for expr in node.exprs or []: + _WalkTree(expr, walk_fn) + if node.orelse: + _WalkTree(node.orelse, walk_fn) + return + + if isinstance(t, ast.StarredExpr): + node = typing.cast(ast.StarredExpr, t) + if node.value: + _WalkTree(node.value, walk_fn) + return + + if isinstance(t, ast.ConfigIfEntryExpr): + node = typing.cast(ast.ConfigIfEntryExpr, t) + if node.if_cond: + _WalkTree(node.if_cond, walk_fn) + for key in node.keys or []: + _WalkTree(key, walk_fn) + for value in node.values or []: + _WalkTree(value, walk_fn) + if node.orelse: + _WalkTree(node.orelse, walk_fn) + return + + if isinstance(t, ast.ConfigExpr): + node = typing.cast(ast.ConfigExpr, t) + for x in node.keys or []: + _WalkTree(x, walk_fn) + for x in node.values or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.DictComp): + node = typing.cast(ast.DictComp, t) + if node.key: + _WalkTree(node.key, walk_fn) + if node.value: + _WalkTree(node.value, walk_fn) + for x in node.generators or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.CompClause): + node = typing.cast(ast.CompClause, t) + for x in node.targets or []: + _WalkTree(x, walk_fn) + if node.iter: + _WalkTree(node.iter, walk_fn) + for x in node.ifs or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.SchemaExpr): + node = typing.cast(ast.SchemaExpr, t) + if node.name: + _WalkTree(node.name, walk_fn) + for x in node.args or []: + _WalkTree(x, walk_fn) + for x in node.kwargs or []: + _WalkTree(x, walk_fn) + if node.config: + _WalkTree(node.config, walk_fn) + return + + if isinstance(t, ast.CheckExpr): + node = typing.cast(ast.CheckExpr, t) + if node.test: + _WalkTree(node.test, walk_fn) + if node.if_cond: + _WalkTree(node.if_cond, walk_fn) + if node.msg: + _WalkTree(node.msg, walk_fn) + return + + if isinstance(t, ast.LambdaExpr): + node = typing.cast(ast.LambdaExpr, t) + if node.args: + _WalkTree(node.args, walk_fn) + for x in node.body or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.Decorator): + node = typing.cast(ast.Decorator, t) + if node.name: + _WalkTree(node.name, walk_fn) + if node.args: + _WalkTree(node.args, walk_fn) + return + + if isinstance(t, ast.Subscript): + node = typing.cast(ast.Subscript, t) + if node.value: + _WalkTree(node.value, walk_fn) + if node.index: + _WalkTree(node.index, walk_fn) + if node.lower: + _WalkTree(node.lower, walk_fn) + if node.upper: + _WalkTree(node.upper, walk_fn) + if node.step: + _WalkTree(node.step, walk_fn) + return + + if isinstance(t, ast.Keyword): + node = typing.cast(ast.Keyword, t) + if node.arg: + _WalkTree(node.arg, walk_fn) + if node.value: + _WalkTree(node.value, walk_fn) + return + + if isinstance(t, ast.Arguments): + node = typing.cast(ast.Arguments, t) + for x in node.args or []: + _WalkTree(x, walk_fn) + for x in node.defaults or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.Compare): + node = typing.cast(ast.Compare, t) + if node.left: + _WalkTree(node.left, walk_fn) + for x in node.comparators or []: + _WalkTree(x, walk_fn) + return + + if isinstance(t, ast.Identifier): + _ = typing.cast(ast.Identifier, t) + return + + if isinstance(t, ast.Literal): + _ = typing.cast(ast.Literal, t) + return + if isinstance(t, ast.JoinedString): + node = typing.cast(ast.JoinedString, t) + for x in node.values or []: + _WalkTree(x, walk_fn) + return + if isinstance(t, ast.FormattedValue): + node = typing.cast(ast.FormattedValue, t) + if node.value: + _WalkTree(node.value, walk_fn) + return + + if isinstance(t, ast.Module): + node = typing.cast(ast.Module, t) + for x in node.body or []: + _WalkTree(x, walk_fn) + return + + assert False, f"_WalkTree: t = {t}" + + +def WalkTree(m: ast.AST, walk_fn: typing.Callable[[ast.AST], None]): + _WalkTree(m, walk_fn) diff --git a/internal/kclvm_py/kcl/error/__init__.py b/internal/kclvm_py/kcl/error/__init__.py new file mode 100644 index 000000000..c53bc2c48 --- /dev/null +++ b/internal/kclvm_py/kcl/error/__init__.py @@ -0,0 +1,107 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .kcl_error import ( + KCLError, + KCLException, + KCLSyntaxException, + KCLRuntimeException, + KCLCompileException, + KCLTypeException, + UnKnownDecoratorError, + DeprecatedError, + InvalidDecoratorTargetError, + KCLNameError, + TypeRuntimeError, + EvaluationError, + KCLTabError, + InvalidSyntaxError, + CompileError, + CannotFindModule, + print_kcl_error_message, + print_kcl_warning_message, + print_common_error_message, + print_internal_error_message, + report_exception, + report_warning, + get_exception, + ErrType, +) + +from .kcl_err_theme import KCLErrMsgTheme, KCLErrMsgThemeDefault + +from .kcl_err_template import ( + ErrLevel, + ErrFileMsg, + FileCache, +) + +from .kcl_err_msg import ( + ErrEwcode, + KCL_ERR_MSG, + INT_OVER_FLOW_MSG, + FLOAT_OVER_FLOW_MSG, + FLOAT_UNDER_FLOW_MSG, + DEPRECATED_WARNING_MSG, + SCHEMA_CHECK_FILE_MSG_ERR, + SCHEMA_CHECK_FILE_MSG_COND, + RECURSIVE_LOADING_MODULE_MSG, + CANNOT_FIND_MODULE_MSG, + UNIQUE_KEY_MSG, + CANNOT_ADD_MEMBERS_MSG, + MULTI_INHERIT_MSG, + INDENTATION_ERROR_MSG, + UNKNOWN_DECORATOR_MSG, + INVALID_DECORATOR_TARGET_MSG, + NAME_ERROR_MSG, + INVALID_FORMAT_SPEC_MSG, +) + +__all__ = [ + "ErrLevel", + "report_exception", + "report_warning", + "ErrType", + "get_exception", + "INT_OVER_FLOW_MSG", + "FLOAT_OVER_FLOW_MSG", + "FLOAT_UNDER_FLOW_MSG", + "DEPRECATED_WARNING_MSG", + "SCHEMA_CHECK_FILE_MSG_ERR", + "SCHEMA_CHECK_FILE_MSG_COND", + "UNIQUE_KEY_MSG", + "CANNOT_ADD_MEMBERS_MSG", + "RECURSIVE_LOADING_MODULE_MSG", + "CANNOT_FIND_MODULE_MSG", + "MULTI_INHERIT_MSG", + "INDENTATION_ERROR_MSG", + "UNKNOWN_DECORATOR_MSG", + "INVALID_DECORATOR_TARGET_MSG", + "NAME_ERROR_MSG", + "INVALID_FORMAT_SPEC_MSG", + "ErrFileMsg", + "ErrEwcode", + "print_kcl_error_message", + "print_kcl_warning_message", + "print_common_error_message", + "print_internal_error_message", + "KCLException", + "KCLSyntaxException", + "KCLCompileException", + "KCLRuntimeException", + "KCLTypeException", + "UnKnownDecoratorError", + "InvalidDecoratorTargetError", + "KCLNameError", + "TypeRuntimeError", + "EvaluationError", + "KCLError", + "DeprecatedError", + "KCLTabError", + "InvalidSyntaxError", + "CompileError", + "CannotFindModule", + "KCLErrMsgTheme", + "KCLErrMsgThemeDefault", + "KCL_ERR_MSG", + "FileCache", +] diff --git a/internal/kclvm_py/kcl/error/kcl_err_msg.py b/internal/kclvm_py/kcl/error/kcl_err_msg.py new file mode 100644 index 000000000..c5c71627f --- /dev/null +++ b/internal/kclvm_py/kcl/error/kcl_err_msg.py @@ -0,0 +1,613 @@ +""" +The `kcl_err_msg` file mainly contains constants used in KCL error messages. + +INT_OVER_FLOW_MSG: String constants ending with "_MSG" are the recommended string template when raising KCL exceptions. + +ErrEwcode: All ewcodes for KCL exception. + +ErrMsgContent_EN: A dict for kcl exception message, + = + +ErrArgMsgDefault_EN: A dict for default arguments provided when a kcl exception is raised, + = + +ErrName_EN: A dict for kcl exception names, + = + +KCLErrMsgManager: A singleton class that encapsulates some methods of obtaining KCL exception information + +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" +import sys +from abc import ABCMeta, abstractmethod +from kclvm.internal.util import PreCheck, CheckRules, PostCheck +from kclvm.internal.util.check_utils import PostSimpleExprCheck + +ERROR = "E" +WARNING = "W" + +SYNTAX = "1" +COMPLIER = "2" +RUNTIME = "3" + +ATTRIBUTE = "A" +SCHEMA = "B" +MIXIN = "C" +INHERIT = "D" +IMPORT = "F" +TYPE = "G" +DECORATOR = "H" +ARGUMENT = "I" +OVERFLOW = "K" +COMPLING = "L" +RUNNING = "M" +DEPRECATED = "N" +DOC = "P" +IMMUTABLE = "Q" + +INT_OVER_FLOW_MSG = "{}: A {} bit integer overflow" +FLOAT_OVER_FLOW_MSG = "{}: A {}-bit floating point number overflow" +FLOAT_UNDER_FLOW_MSG = "{}: A {}-bit floating point number underflow" +DEPRECATED_WARNING_MSG = "{} was deprecated {}" + +SCHEMA_CHECK_FILE_MSG_ERR = "Instance check failed" +SCHEMA_CHECK_FILE_MSG_COND = "Check failed on the condition" + +UNIQUE_KEY_MSG = "Variable name '{}' must be unique in package context" +CANNOT_ADD_MEMBERS_MSG = "{}: No such member in the schema '{}'" +RECURSIVE_LOADING_MODULE_MSG = "In module {}, recursively loading modules: {}" +CANNOT_FIND_MODULE_MSG = "Cannot find the module {} from {}" +MULTI_INHERIT_MSG = "Multiple inheritance of {} is prohibited" +INDENTATION_ERROR_MSG = "Unindent {} does not match any outer indentation level" +UNKNOWN_DECORATOR_MSG = "UnKnown decorator {}" +INVALID_DECORATOR_TARGET_MSG = "Invalid decorator target {}" +NAME_ERROR_MSG = "Name error {}" +INVALID_FORMAT_SPEC_MSG = "{} is invalid format spec" + + +class ErrEwcode: + KCLException_Ew = "00000" # 00000 + KCLError_Ew = ERROR + "0000" # E0000 + KCLWarning_Ew = WARNING + "0000" # W0000 + + KCLSyntaxException_Ew = "0" + SYNTAX + "000" # 01000 + KCLCompileException_Ew = "0" + COMPLIER + "000" # 02000 + KCLRuntimeException_Ew = "0" + RUNTIME + "000" # 03000 + + KCLAttributeException_Ew = "00" + ATTRIBUTE + "00" # 00A00 + KCLSchemaException_Ew = "00" + SCHEMA + "00" # 00B00 + KCLMixinException_Ew = "00" + MIXIN + "00" # 00C00 + KCLInheritException_Ew = "00" + INHERIT + "00" # 00D00 + KCLImportException_Ew = "00" + IMPORT + "00" # 00F00 + KCLTypeException_Ew = "00" + TYPE + "00" # 00G00 + KCLDecoratorException_Ew = "00" + DECORATOR + "00" # 00H00 + KCLArgumentException_Ew = "00" + ARGUMENT + "00" # 00I00 + KCLOverflowException_Ew = "00" + OVERFLOW + "00" # 00K00 + KCLComplingException_Ew = "00" + COMPLING + "00" # 00L00 + KCLRunningException_Ew = "00" + RUNNING + "00" # 00M00 + KCLDeprecatedException_Ew = "00" + DEPRECATED + "00" # 00N00 + KCLDocException_Ew = "00" + DOC + "00" # 00P00 + KCLImmutableException_Ew = "00" + IMMUTABLE + "00" # 00Q00 + + InvalidSyntax_Ew = ERROR + SYNTAX + "001" # E1001 + KCLTabError_Ew = ERROR + SYNTAX + "002" # E1002 + KCLIndentationError_Ew = ERROR + SYNTAX + "003" # E1003 + + CannotFindModule_Ew = ERROR + COMPLIER + IMPORT + "04" # E2F04 + FailedLoadModule_Ew = ERROR + COMPLIER + IMPORT + "05" # E2F05 + RecursiveLoad_Ew = ERROR + COMPLIER + IMPORT + "06" # E3F06 + + FloatOverflow_Ew = ERROR + RUNTIME + OVERFLOW + "07" # E3K04 + FloatUnderflow_Ew = WARNING + COMPLIER + OVERFLOW + "08" # W2K08 + IntOverflow_Ew = ERROR + RUNTIME + OVERFLOW + "09" # E3K09 + + InvalidDocstring_Ew = WARNING + COMPLIER + DOC + "10" # W2P10 + + Deprecated_Ew = ERROR + RUNTIME + DEPRECATED + "11" # E3N11 + DeprecatedWarning_Ew = WARNING + COMPLIER + DEPRECATED + "12" # W2N12 + + UnKnownDecorator_Ew = ERROR + COMPLIER + DECORATOR + "13" # E2H13 + InvalidDecoratorTarget_Ew = ERROR + COMPLIER + DECORATOR + "14" # E2H14 + + MixinNamingError_Ew = ERROR + COMPLIER + MIXIN + "15" # E2C15 + MixinStructureIllegal_Ew = ERROR + COMPLIER + MIXIN + "16" # E2C16 + + SchemaCheckFailure_Ew = ERROR + RUNTIME + SCHEMA + "17" # E3B17 + CannotAddMembersComplieError_Ew = ERROR + COMPLIER + SCHEMA + "18" # E2B17 + CannotAddMembersRuntimeError_Ew = ERROR + RUNTIME + SCHEMA + "19" # E3B19 + IndexSignatureError_Ew = ERROR + COMPLIER + SCHEMA + "20" # E2B20 + + TypeRuntimeError_Ew = ERROR + RUNTIME + TYPE + "21" # E3G21 + TypeComplieError_Ew = ERROR + COMPLIER + TYPE + "22" # E2G22 + + CompileError_Ew = ERROR + COMPLIER + COMPLING + "23" # E2L23 + SelectorError_Ew = ERROR + COMPLIER + COMPLING + "24" # E2L24 + KCLNameError_Ew = ERROR + COMPLIER + COMPLING + "25" # E2L25 + KCLValueError_Ew = ERROR + COMPLIER + COMPLING + "26" # E2L26 + KCLKeyError_Ew = ERROR + COMPLIER + COMPLING + "27" # E2L27 + UniqueKeyError_Ew = ERROR + COMPLIER + COMPLING + "28" # E2L28 + + KCLAttributeComplieError_Ew = ERROR + COMPLIER + ATTRIBUTE + "29" # E2A29 + KCLAttributeRuntimeError_Ew = ERROR + RUNTIME + ATTRIBUTE + "30" # E3A30 + IllegalAttributeError_Ew = ERROR + COMPLIER + ATTRIBUTE + "31" # E2A31 + + MultiInheritError_Ew = ERROR + COMPLIER + INHERIT + "32" # E2D32 + CycleInheritError_Ew = ERROR + COMPLIER + INHERIT + "33" # E2D33 + IllegalInheritError_Ew = ERROR + COMPLIER + INHERIT + "34" # E2D34 + + IllegalArgumentRuntimeError_Ew = ERROR + RUNTIME + ARGUMENT + "35" # E3I35 + IllegalArgumentComplieError_Ew = ERROR + COMPLIER + ARGUMENT + "36" # E2I36 + IllegalArgumentSyntaxError_Ew = ERROR + SYNTAX + ARGUMENT + "37" # E1I37 + + EvaluationError_Ew = ERROR + RUNTIME + RUNNING + "38" # E3M38 + InvalidFormatSpec_Ew = ERROR + RUNTIME + RUNNING + "39" # E3M39 + KCLAssertionError_Ew = ERROR + RUNTIME + RUNNING + "40" # E3M40 + + ImmutableCompileError_Ew = ERROR + COMPLIER + COMPLING + "41" # E3L41 + KCLRecursionError_Ew = ERROR + RUNTIME + RUNNING + "42" # E3M42 + PlanError_Ew = ERROR + RUNTIME + RUNNING + "43" # E3M43 + ImmutableRuntimeError_Ew = ERROR + RUNTIME + RUNNING + "44" # E3M44 + + @staticmethod + def contains(ewcode: str): + return ewcode in ErrEwcode.__dict__.values() + + +ErrMsgContent_EN: dict = { + ErrEwcode.KCLException_Ew: "Exception", + ErrEwcode.KCLError_Ew: "Error", + ErrEwcode.KCLWarning_Ew: "Warning", + ErrEwcode.KCLSyntaxException_Ew: "Syntax", + ErrEwcode.KCLCompileException_Ew: "Compile", + ErrEwcode.KCLRuntimeException_Ew: "Runtime", + ErrEwcode.KCLAttributeException_Ew: "An attribute exception occurs", + ErrEwcode.KCLSchemaException_Ew: "A schema exception occurs", + ErrEwcode.KCLMixinException_Ew: "A mixin exception occurs", + ErrEwcode.KCLInheritException_Ew: "An inherit exception occurs", + ErrEwcode.KCLImportException_Ew: "An import exception occurs", + ErrEwcode.KCLTypeException_Ew: "A type exception occurs", + ErrEwcode.KCLDecoratorException_Ew: "A decorator exception occurs", + ErrEwcode.KCLArgumentException_Ew: "An argument exception occurs", + ErrEwcode.KCLOverflowException_Ew: "An overflow exception occurs", + ErrEwcode.KCLComplingException_Ew: "An compling exception occurs", + ErrEwcode.KCLRunningException_Ew: "An running exception occurs", + ErrEwcode.KCLDeprecatedException_Ew: "A deprecated exception occurs", + ErrEwcode.KCLDocException_Ew: "A doc exception occurs", + ErrEwcode.KCLImmutableException_Ew: "A Immutable exception occurs", + ErrEwcode.InvalidSyntax_Ew: "Invalid syntax", + ErrEwcode.KCLTabError_Ew: "Tab Error", + ErrEwcode.KCLIndentationError_Ew: "Indentation Error", + ErrEwcode.CannotFindModule_Ew: "Cannot find the module", + ErrEwcode.FailedLoadModule_Ew: "Failed to load module", + ErrEwcode.RecursiveLoad_Ew: "Recursively loading module", + ErrEwcode.FloatOverflow_Ew: "Float overflow", + ErrEwcode.FloatUnderflow_Ew: "Float underflow", + ErrEwcode.IntOverflow_Ew: "Integer overflow", + ErrEwcode.InvalidDocstring_Ew: "Invalid docstring", + ErrEwcode.Deprecated_Ew: "Deprecated error", + ErrEwcode.DeprecatedWarning_Ew: "Deprecated warning", + ErrEwcode.UnKnownDecorator_Ew: "UnKnown decorator", + ErrEwcode.InvalidDecoratorTarget_Ew: "Invalid Decorator Target", + ErrEwcode.MixinNamingError_Ew: "Illegal mixin naming", + ErrEwcode.MixinStructureIllegal_Ew: "Illegal mixin structure", + ErrEwcode.SchemaCheckFailure_Ew: "Schema check is failed to check condition", + ErrEwcode.CannotAddMembersComplieError_Ew: "Cannot add members to a schema", + ErrEwcode.CannotAddMembersRuntimeError_Ew: "Cannot add members to a schema", + ErrEwcode.IndexSignatureError_Ew: "Invalid index signature", + ErrEwcode.TypeRuntimeError_Ew: "The type got is inconsistent with the type expected", + ErrEwcode.TypeComplieError_Ew: "The type got is inconsistent with the type expected", + ErrEwcode.CompileError_Ew: "A complie error occurs during compiling", + ErrEwcode.SelectorError_Ew: "Selector Error", + ErrEwcode.KCLNameError_Ew: "Name Error", + ErrEwcode.KCLValueError_Ew: "Value Error", + ErrEwcode.KCLKeyError_Ew: "Key Error", + ErrEwcode.UniqueKeyError_Ew: "Unique key error", + ErrEwcode.KCLAttributeComplieError_Ew: "Attribute error occurs during compiling", + ErrEwcode.KCLAttributeRuntimeError_Ew: "Attribute error occurs at runtime", + ErrEwcode.IllegalAttributeError_Ew: "Illegal attribute", + ErrEwcode.MultiInheritError_Ew: "Multiple inheritance is illegal", + ErrEwcode.CycleInheritError_Ew: "Cycle Inheritance is illegal", + ErrEwcode.IllegalInheritError_Ew: "Illegal inheritance", + ErrEwcode.IllegalArgumentRuntimeError_Ew: "Illegal command line argument at runtime", + ErrEwcode.IllegalArgumentComplieError_Ew: "Illegal argument during compiling", + ErrEwcode.IllegalArgumentSyntaxError_Ew: "Illegal argument syntax", + ErrEwcode.EvaluationError_Ew: "Evaluation failure", + ErrEwcode.InvalidFormatSpec_Ew: "Invalid format specification", + ErrEwcode.KCLAssertionError_Ew: "Assertion failure", + ErrEwcode.ImmutableCompileError_Ew: "Immutable variable is modified during compiling", + ErrEwcode.ImmutableRuntimeError_Ew: "Immutable variable is modified at runtime", + ErrEwcode.KCLRecursionError_Ew: "Recursively reference", + ErrEwcode.PlanError_Ew: "Plan Error", +} + +ErrArgMsgDefault_EN: dict = { + ErrEwcode.KCLException_Ew: "Exception", + ErrEwcode.KCLError_Ew: "Error", + ErrEwcode.KCLWarning_Ew: "Warning", + ErrEwcode.KCLSyntaxException_Ew: "Syntax", + ErrEwcode.KCLCompileException_Ew: "Complie", + ErrEwcode.KCLRuntimeException_Ew: "Runtime", + ErrEwcode.KCLAttributeException_Ew: "An attribute exception occurs", + ErrEwcode.KCLSchemaException_Ew: "A schema exception occurs", + ErrEwcode.KCLMixinException_Ew: "A mixin exception occurs", + ErrEwcode.KCLInheritException_Ew: "An inherit exception occurs", + ErrEwcode.KCLImportException_Ew: "An import exception occurs", + ErrEwcode.KCLTypeException_Ew: "A type exception occurs", + ErrEwcode.KCLDecoratorException_Ew: "A decorator exception occurs", + ErrEwcode.KCLArgumentException_Ew: "An argument exception occurs", + ErrEwcode.KCLOverflowException_Ew: "An overflow exception occurs", + ErrEwcode.KCLComplingException_Ew: "An compling exception occurs", + ErrEwcode.KCLRunningException_Ew: "An running exception occurs", + ErrEwcode.KCLDeprecatedException_Ew: "A deprecated exception occurs", + ErrEwcode.KCLDocException_Ew: "A doc exception occurs", + ErrEwcode.KCLImmutableException_Ew: "A Immutable exception occurs", + ErrEwcode.InvalidSyntax_Ew: "Invalid syntax", + ErrEwcode.KCLTabError_Ew: "Inconsistent use of tabs and spaces in indentation", + ErrEwcode.KCLIndentationError_Ew: "Indentation Error", + ErrEwcode.CannotFindModule_Ew: "Cannot find the module", + ErrEwcode.FailedLoadModule_Ew: "Failed to load module", + ErrEwcode.RecursiveLoad_Ew: "Recursively loading module", + ErrEwcode.FloatOverflow_Ew: "Float overflow", + ErrEwcode.FloatUnderflow_Ew: "Float underflow", + ErrEwcode.IntOverflow_Ew: "Integer overflow", + ErrEwcode.InvalidDocstring_Ew: "Invalid docstring", + ErrEwcode.Deprecated_Ew: "Deprecated error", + ErrEwcode.DeprecatedWarning_Ew: "Deprecated warning", + ErrEwcode.UnKnownDecorator_Ew: "UnKnown decorator", + ErrEwcode.InvalidDecoratorTarget_Ew: "Invalid Decorator Target", + ErrEwcode.MixinNamingError_Ew: "Illegal mixin naming", + ErrEwcode.MixinStructureIllegal_Ew: "Illegal mixin structure", + ErrEwcode.SchemaCheckFailure_Ew: "Check failed on check conditions", + ErrEwcode.CannotAddMembersComplieError_Ew: "Cannot add members to a schema", + ErrEwcode.CannotAddMembersRuntimeError_Ew: "Cannot add members to a schema", + ErrEwcode.IndexSignatureError_Ew: "Invalid index signature", + ErrEwcode.TypeRuntimeError_Ew: "The type got is inconsistent with the type expected", + ErrEwcode.TypeComplieError_Ew: "The type got is inconsistent with the type expected", + ErrEwcode.CompileError_Ew: "A complie error occurs during compiling", + ErrEwcode.SelectorError_Ew: "Selector Error", + ErrEwcode.KCLNameError_Ew: "Name Error", + ErrEwcode.KCLValueError_Ew: "Value Error", + ErrEwcode.KCLKeyError_Ew: "Key Error", + ErrEwcode.UniqueKeyError_Ew: "Unique key error", + ErrEwcode.KCLAttributeComplieError_Ew: "Attribute error occurs during compiling", + ErrEwcode.KCLAttributeRuntimeError_Ew: "Attribute error occurs at runtime", + ErrEwcode.IllegalAttributeError_Ew: "Illegal attribute", + ErrEwcode.MultiInheritError_Ew: "Multiple inheritance is illegal", + ErrEwcode.CycleInheritError_Ew: "Cycle Inheritance is illegal", + ErrEwcode.IllegalInheritError_Ew: "Illegal inheritance", + ErrEwcode.IllegalArgumentRuntimeError_Ew: "Illegal command line argument at runtime", + ErrEwcode.IllegalArgumentComplieError_Ew: "Illegal argument during compiling", + ErrEwcode.IllegalArgumentSyntaxError_Ew: "Illegal argument syntax", + ErrEwcode.EvaluationError_Ew: "Evaluation failure", + ErrEwcode.InvalidFormatSpec_Ew: "Invalid format specification", + ErrEwcode.KCLAssertionError_Ew: "Assertion failure", + ErrEwcode.ImmutableCompileError_Ew: "Immutable variable is modified during compiling", + ErrEwcode.ImmutableRuntimeError_Ew: "Immutable variable is modified at runtime", + ErrEwcode.KCLRecursionError_Ew: "maximum recursion depth exceeded", + ErrEwcode.PlanError_Ew: "Plan Error", +} + +ErrName_EN: dict = { + ErrEwcode.KCLException_Ew: "KCLException", + ErrEwcode.KCLError_Ew: "KCLError", + ErrEwcode.KCLWarning_Ew: "KCLWarning", + ErrEwcode.KCLSyntaxException_Ew: "KCLSyntaxException", + ErrEwcode.KCLCompileException_Ew: "KCLCompileException", + ErrEwcode.KCLRuntimeException_Ew: "KCLRuntimeException", + ErrEwcode.KCLAttributeException_Ew: "KCLAttributeException", + ErrEwcode.KCLSchemaException_Ew: "KCLSchemaException", + ErrEwcode.KCLMixinException_Ew: "KCLMixinException", + ErrEwcode.KCLInheritException_Ew: "KCLInheritException", + ErrEwcode.KCLImportException_Ew: "KCLImportException", + ErrEwcode.KCLTypeException_Ew: "KCLTypeException", + ErrEwcode.KCLDecoratorException_Ew: "KCLDecoratorException", + ErrEwcode.KCLArgumentException_Ew: "KCLArgumentException", + ErrEwcode.KCLOverflowException_Ew: "KCLOverflowException", + ErrEwcode.KCLComplingException_Ew: "KCLComplingException", + ErrEwcode.KCLRunningException_Ew: "KCLRunningException", + ErrEwcode.KCLDeprecatedException_Ew: "KCLDeprecatedException", + ErrEwcode.KCLDocException_Ew: "KCLDocException", + ErrEwcode.KCLImmutableException_Ew: "KCLImmutableException", + ErrEwcode.InvalidSyntax_Ew: "InvalidSyntax", + ErrEwcode.KCLTabError_Ew: "KCLTabError", + ErrEwcode.KCLIndentationError_Ew: "KCLIndentationError", + ErrEwcode.CannotFindModule_Ew: "CannotFindModule", + ErrEwcode.FailedLoadModule_Ew: "FailedLoadModule", + ErrEwcode.RecursiveLoad_Ew: "RecursiveLoad", + ErrEwcode.FloatOverflow_Ew: "FloatOverflow", + ErrEwcode.FloatUnderflow_Ew: "FloatUnderflow", + ErrEwcode.IntOverflow_Ew: "IntOverflow", + ErrEwcode.InvalidDocstring_Ew: "InvalidDocstring", + ErrEwcode.Deprecated_Ew: "Deprecated", + ErrEwcode.DeprecatedWarning_Ew: "DeprecatedWarning", + ErrEwcode.UnKnownDecorator_Ew: "UnKnownDecorator", + ErrEwcode.InvalidDecoratorTarget_Ew: "InvalidDecoratorTarget", + ErrEwcode.MixinNamingError_Ew: "MixinNamingError", + ErrEwcode.MixinStructureIllegal_Ew: "MixinStructureIllegal", + ErrEwcode.SchemaCheckFailure_Ew: "SchemaCheckFailure", + ErrEwcode.CannotAddMembersComplieError_Ew: "CannotAddMembersComplieError", + ErrEwcode.CannotAddMembersRuntimeError_Ew: "CannotAddMembersRuntimeError", + ErrEwcode.IndexSignatureError_Ew: "IndexSignatureError", + ErrEwcode.TypeRuntimeError_Ew: "TypeRuntimeError", + ErrEwcode.TypeComplieError_Ew: "TypeComplieError", + ErrEwcode.CompileError_Ew: "CompileError", + ErrEwcode.SelectorError_Ew: "SelectorError", + ErrEwcode.KCLNameError_Ew: "KCLNameError", + ErrEwcode.KCLValueError_Ew: "KCLValueError", + ErrEwcode.KCLKeyError_Ew: "KCLKeyError", + ErrEwcode.UniqueKeyError_Ew: "UniqueKeyError", + ErrEwcode.KCLAttributeComplieError_Ew: "KCLAttributeComplieError", + ErrEwcode.KCLAttributeRuntimeError_Ew: "KCLAttributeRuntimeError", + ErrEwcode.IllegalAttributeError_Ew: "IllegalAttributeError", + ErrEwcode.MultiInheritError_Ew: "MultiInheritError", + ErrEwcode.CycleInheritError_Ew: "CycleInheritError", + ErrEwcode.IllegalInheritError_Ew: "IllegalInheritError", + ErrEwcode.IllegalArgumentRuntimeError_Ew: "IllegalArgumentRuntimeError", + ErrEwcode.IllegalArgumentComplieError_Ew: "IllegalArgumentComplieError", + ErrEwcode.IllegalArgumentSyntaxError_Ew: "IllegalArgumentSyntaxError", + ErrEwcode.EvaluationError_Ew: "EvaluationError", + ErrEwcode.InvalidFormatSpec_Ew: "InvalidFormatSpec", + ErrEwcode.KCLAssertionError_Ew: "KCLAssertionError", + ErrEwcode.ImmutableCompileError_Ew: "ImmutableCompileError", + ErrEwcode.ImmutableRuntimeError_Ew: "ImmutableRuntimeError", + ErrEwcode.KCLRecursionError_Ew: "KCLRecursionError", + ErrEwcode.PlanError_Ew: "PlanError", +} + + +class ErrMsgLang: + EN_US_UTF_8 = "c.utf8" + + +class ErrMsg(metaclass=ABCMeta): + @abstractmethod + def get_defaule_ewcode(self) -> str: + pass + + @abstractmethod + def get_err_cate_ewcode(self, err_id: str) -> str: + pass + + @abstractmethod + def get_err_type_ewcode(self, err_id: str) -> str: + pass + + @abstractmethod + def get_err_cate_msg_by_errid(self, err_id: str) -> str: + pass + + @abstractmethod + def get_err_type_msg_by_errid(self, err_id: str) -> str: + pass + + @abstractmethod + def get_err_msg_by_errid(self, err_id: str) -> str: + pass + + @abstractmethod + def get_defaule_arg_msg_by_errid(self, err_id: str) -> str: + pass + + @abstractmethod + def get_err_code_by_errid(self, err_id: str) -> str: + pass + + @abstractmethod + def is_warning(self, err_id: str) -> bool: + pass + + @abstractmethod + def is_syntax_error(self, err_id: str) -> bool: + pass + + @abstractmethod + def is_compiler_error(self, err_id: str) -> bool: + pass + + @abstractmethod + def is_runtime_error(self, err_id: str) -> bool: + pass + + @abstractmethod + def get_err_name_by_ewcode(self, err_id: str): + pass + + +class KCLErrMsg_EN(ErrMsg): + @PostCheck(lambda result: ErrEwcode.contains(result)) + def get_defaule_ewcode(self) -> str: + return ErrEwcode.KCLException_Ew + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: ErrEwcode.contains(result)) + @PostSimpleExprCheck( + (lambda inputs, result: result == inputs["err_id"][:1] + "0000"), ["err_id"] + ) + def get_err_cate_ewcode(self, err_id: str) -> str: + return err_id[:1] + "0000" + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: ErrEwcode.contains(result)) + @PostSimpleExprCheck( + (lambda inputs, result: result == "0" + inputs["err_id"][1:2] + "000"), + ["err_id"], + ) + def get_err_type_ewcode(self, err_id: str) -> str: + return "0" + err_id[1:2] + "000" + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: result in ErrMsgContent_EN.values()) + def get_err_cate_msg_by_errid(self, err_id: str) -> str: + return ErrMsgContent_EN[self.get_err_cate_ewcode(err_id)] + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: result in ErrMsgContent_EN.values()) + def get_err_type_msg_by_errid(self, err_id: str) -> str: + return ErrMsgContent_EN[self.get_err_type_ewcode(err_id)] + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: result in ErrMsgContent_EN.values()) + def get_err_msg_by_errid(self, err_id: str) -> str: + return ErrMsgContent_EN[err_id] + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: result in ErrArgMsgDefault_EN.values()) + def get_defaule_arg_msg_by_errid(self, err_id: str) -> str: + return ErrArgMsgDefault_EN[err_id] + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, str)) + @PostSimpleExprCheck( + (lambda inputs, result: result == "[" + inputs["err_id"] + "]"), ["err_id"] + ) + def get_err_code_by_errid(self, err_id: str) -> str: + return "[" + err_id + "]" + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, bool)) + def is_warning(self, err_id: str) -> bool: + return err_id[:1] == WARNING + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, bool)) + def is_syntax_error(self, err_id: str) -> bool: + return err_id[1:2] == SYNTAX + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, bool)) + def is_compiler_error(self, err_id: str) -> bool: + return err_id[1:2] == COMPLIER + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, bool)) + def is_runtime_error(self, err_id: str) -> bool: + return err_id[1:2] == RUNTIME + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: result in ErrName_EN.values()) + def get_err_name_by_ewcode(self, err_id: str) -> str: + return ErrName_EN[err_id] + + +class KCLErrMsgManager(ErrMsg): + _instance = None + + def __new__(cls): + if cls._instance is None: + cls._instance = object.__new__(cls) + return cls._instance + + def __init__(self, lang: ErrMsgLang = ErrMsgLang.EN_US_UTF_8): + self.ALL_KCL_ERROR_MSGS: dict = {ErrMsgLang.EN_US_UTF_8: KCLErrMsg_EN()} + self.lang = lang + self.KCL_ERROR_MSG = self.ALL_KCL_ERROR_MSGS[self.lang] + + @PreCheck((lambda v: CheckRules.check_locale(v)), "lang") + def switch_lang(self, lang: str): + try: + self.KCL_ERROR_MSG = self.ALL_KCL_ERROR_MSGS[lang] + except KeyError: + print( + f"KCLVM does not support '{lang}', " + f"KCLVM have automatically switched to {ErrMsgLang.EN_US_UTF_8}", + file=sys.stderr, + ) + self.lang = ErrMsgLang.EN_US_UTF_8 + self.KCL_ERROR_MSG = self.ALL_KCL_ERROR_MSGS[self.lang] + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, ErrMsg)), "err_msg") + @PreCheck((lambda v: CheckRules.check_locale(v)), "lang") + def append_lang(self, lang: str, err_msg: ErrMsg): + if lang in self.ALL_KCL_ERROR_MSGS.keys(): + print( + f"KCLVM currently supports {lang}, " + f"If you want to change the language pack, " + f"please use the method update_lang", + file=sys.stderr, + ) + return + else: + self.ALL_KCL_ERROR_MSGS[lang] = err_msg + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, ErrMsg)), "err_msg") + @PreCheck((lambda v: CheckRules.check_locale(v)), "lang") + def update_lang(self, lang: str, err_msg: ErrMsg): + self.ALL_KCL_ERROR_MSGS[lang] = err_msg + + @PostCheck(lambda result: CheckRules.check_type_not_none(result, str)) + @PostCheck(lambda result: result == ErrEwcode.KCLException_Ew) + def get_defaule_ewcode(self) -> str: + return self.KCL_ERROR_MSG.get_defaule_ewcode() + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, str)) + @PostSimpleExprCheck( + (lambda inputs, result: result == inputs["err_id"][:1] + "0000"), ["err_id"] + ) + def get_err_cate_ewcode(self, err_id: str) -> str: + return self.KCL_ERROR_MSG.get_err_cate_ewcode(err_id) + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, str)) + @PostSimpleExprCheck( + (lambda inputs, result: result == "0" + inputs["err_id"][1:2] + "000"), + ["err_id"], + ) + def get_err_type_ewcode(self, err_id: str) -> str: + return self.KCL_ERROR_MSG.get_err_type_ewcode(err_id) + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, str)) + def get_err_cate_msg_by_errid(self, err_id: str) -> str: + return self.KCL_ERROR_MSG.get_err_cate_msg_by_errid(err_id) + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, str)) + def get_err_type_msg_by_errid(self, err_id: str) -> str: + return self.KCL_ERROR_MSG.get_err_type_msg_by_errid(err_id) + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, str)) + def get_err_msg_by_errid(self, err_id: str) -> str: + return self.KCL_ERROR_MSG.get_err_msg_by_errid(err_id) + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, str)) + def get_defaule_arg_msg_by_errid(self, err_id: str) -> str: + return self.KCL_ERROR_MSG.get_defaule_arg_msg_by_errid(err_id) + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, str)) + @PostSimpleExprCheck( + (lambda inputs, result: result == "[" + inputs["err_id"] + "]"), ["err_id"] + ) + def get_err_code_by_errid(self, err_id: str) -> str: + return self.KCL_ERROR_MSG.get_err_code_by_errid(err_id) + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, bool)) + def is_warning(self, err_id: str) -> bool: + return self.KCL_ERROR_MSG.is_warning(err_id) + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, bool)) + def is_syntax_error(self, err_id: str) -> bool: + return self.KCL_ERROR_MSG.is_syntax_error(err_id) + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, bool)) + def is_compiler_error(self, err_id: str) -> bool: + return self.KCL_ERROR_MSG.is_compiler_error(err_id) + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, bool)) + def is_runtime_error(self, err_id: str) -> bool: + return self.KCL_ERROR_MSG.is_runtime_error(err_id) + + @PreCheck((lambda v: ErrEwcode.contains(v)), "err_id") + @PostCheck(lambda result: CheckRules.check_type_not_none(result, str)) + def get_err_name_by_ewcode(self, err_id: str): + return self.KCL_ERROR_MSG.get_err_name_by_ewcode(err_id) + + +KCL_ERR_MSG = KCLErrMsgManager() diff --git a/internal/kclvm_py/kcl/error/kcl_err_template.py b/internal/kclvm_py/kcl/error/kcl_err_template.py new file mode 100644 index 000000000..c2c2d0f78 --- /dev/null +++ b/internal/kclvm_py/kcl/error/kcl_err_template.py @@ -0,0 +1,590 @@ +""" +The `kcl_err_template` file mainly contains some string templates to organize the structure of the KCL error message. + +The KCL error message comes from kcl_err_msg.py + +KCLErrMsgTemplate: An abstract class, which specifies the methods required by KCL error message template classes. + +KCLErrMsgTemplateDefault: The default KCL template class implementation. + +Provide four file message templates in KCLErrMsgTemplateDefault: + + get_hint_msg_common: +---------------------------------------------------------------------------- + KCL Compile Error[E2L23] : A complie error occurs during compiling + ---> File /main.k:2:14 + 2 | name: str + 14 ^ -> Failure + An error occurs +---------------------------------------------------------------------------- + + get_hint_msg_tiny: +---------------------------------------------------------------------------- + KCL Compile Error[E2L23] : A compile error occurs during compiling + An error occurs +---------------------------------------------------------------------------- + + get_hint_msg_summary: +---------------------------------------------------------------------------- + KCL Compile Error[E2L23] : A complie error occurs during compiling + ---> File /main.k:2 + 2 | name: str -> Failure + An error occurs +---------------------------------------------------------------------------- + + get_hint_msg_detail: +---------------------------------------------------------------------------- + KCL Compile Error[E2L23] : A complie error occurs during compiling + ---> File /main.k:2:5 + 2 | name: str + 5 ^^^^^^^^^ -> Failure + An error occurs +---------------------------------------------------------------------------- + +Provide one error message templates in KCLErrMsgTemplateDefault: + + err_msg_template: +---------------------------------------------------------------------------- + KCL Compile Error[E2L23] : A compile error occurs during compiling +---------------------------------------------------------------------------- + +color_txt_err_msg(), color_txt_err_file_msg() in KCLErrMsgTemplateDefault +will call methods provided in kcl_err_template.py to highlight some fields +in the kcl error message + +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" +import os +import typing +import threading + +from enum import unique, Enum +from pathlib import PosixPath +from string import Template +from abc import ABCMeta, abstractmethod +from kclvm.kcl.error.kcl_err_theme import ( + ColorOption, + KCLErrMsgTheme, + KCLErrMsgThemeDefault, +) +from kclvm.kcl.error.kcl_err_msg import KCL_ERR_MSG, ErrEwcode +from kclvm.internal.util import PreCheck, CheckRules, PostCheck, check_utils + +DEFAULT_MSG_2 = "Failure" + + +@unique +class ErrLevel(Enum): + SERIOUS = 1 + ORDINARY = 2 + + +@unique +class MsgId(Enum): + ERR_TYPE = 1 + ERR_CATE = 2 + EWCODE = 3 + MSG_1 = 4 + MSG_2 = 5 + + FILENAME = 8 + SRC_CODE_LINE = 9 + LINE_NO = 10 + COL_NO = 11 + ERR_ARG = 12 + MARK = 13 + + +@unique +class ErrMsgFmt(Enum): + COLOR_TXT = 1 + NO_COLOR_TXT = 2 + + +class FileCache: + """File cache to store the filename and code mapping""" + + _file_cache: typing.Dict[str, str] = {} + _lock = threading.RLock() + + @staticmethod + def clear(): + FileCache._file_cache.clear() + + @staticmethod + def put(file: str, code: str): + FileCache._lock.acquire() + FileCache._file_cache[file] = code + FileCache._lock.release() + + @staticmethod + def get(file: str) -> str: + return FileCache._file_cache.get(file, "") + + +UP_ARROW = "^" +WAVY = "~" + +MARK_SYMBOL = {ErrLevel.SERIOUS: UP_ARROW, ErrLevel.ORDINARY: WAVY} + +TAB = " " +COLON = ":" +COMMA = ", " +WHITE_SPACE = " " +SPARATOER = " |" +NEW_LINE = "\n" +HINT_MSG = "{}" +FILE_PREFIX = "---> File " +MSG_PREFIX = "KCL " + + +class KCLErrMsgTemplate(metaclass=ABCMeta): + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, ErrMsgFmt)), "fmt") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, KCLErrMsgTheme)), "theme") + def __init__( + self, + fmt: ErrMsgFmt = ErrMsgFmt.NO_COLOR_TXT, + theme: KCLErrMsgTheme = KCLErrMsgThemeDefault(), + ): + self.fmt = fmt + self.theme = theme + + @abstractmethod + def get_hint_msg_common( + self, + filename: str, + line_no: int, + col_no: int, + src_code_line: str, + indent_count: int = 0, + mark: str = MARK_SYMBOL[ErrLevel.SERIOUS], + ) -> str: + pass + + @abstractmethod + def get_hint_msg_tiny(self, filename: str, indent_count: int = 0) -> str: + pass + + @abstractmethod + def get_hint_msg_summary( + self, filename: str, line_no: int, src_code_line: str, indent_count: int = 0 + ) -> str: + pass + + @abstractmethod + def get_hint_msg_detail( + self, + filename: str, + line_no: int, + col_no: int, + end_col_no: int, + src_code_line: str, + indent_count: int = 0, + mark: str = MARK_SYMBOL[ErrLevel.SERIOUS], + ) -> str: + pass + + @abstractmethod + def err_msg_template( + self, err_type: str, err_cate: str, ewcode_fmt: str, msg_1: str + ): + pass + + @abstractmethod + def color_txt_err_file_msg(self, mark_level: ErrLevel): + pass + + @abstractmethod + def color_txt_err_msg(self, err_type: str, err_cate: str): + pass + + +class KCLErrMsgTemplateDefault(KCLErrMsgTemplate): + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, ErrMsgFmt)), "fmt") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, KCLErrMsgTheme)), "theme") + def __init__( + self, + fmt: ErrMsgFmt = ErrMsgFmt.NO_COLOR_TXT, + theme: KCLErrMsgTheme = KCLErrMsgThemeDefault(), + ): + super().__init__(fmt, theme) + self.fmt = fmt + self.theme = theme + + self.ErrMsgLables = { + MsgId.ERR_TYPE: "${err_type}", + MsgId.ERR_CATE: "${err_cate}", + MsgId.EWCODE: "${ewcode}", + MsgId.MSG_1: "${msg_1}", + MsgId.MSG_2: "${msg_2}", + MsgId.FILENAME: "${filename}", + MsgId.SRC_CODE_LINE: "${src_code_line}", + MsgId.LINE_NO: "${line_no}", + MsgId.COL_NO: "${col_no}", + MsgId.ERR_ARG: "${err_arg}", + MsgId.MARK: "${mark}", + } + self.FILENAME = self.ErrMsgLables[MsgId.FILENAME] + self.LINE_NO = self.ErrMsgLables[MsgId.LINE_NO] + self.COL_NO = self.ErrMsgLables[MsgId.COL_NO] + self.MARK = self.ErrMsgLables[MsgId.MARK] + + self.EWCODE = self.ErrMsgLables[MsgId.EWCODE] + self.ERR_TYPE = self.ErrMsgLables[MsgId.ERR_TYPE] + self.ERR_CATE = self.ErrMsgLables[MsgId.ERR_CATE] + self.MSG_1 = self.ErrMsgLables[MsgId.MSG_1] + self.MSG_2 = self.ErrMsgLables[MsgId.MSG_2] + + self.SRC_CODE_LINE = self.ErrMsgLables[MsgId.SRC_CODE_LINE] + self.ERR_ARG = self.ErrMsgLables[MsgId.ERR_ARG] + + def clean_color(self): + self.FILENAME = self.ErrMsgLables[MsgId.FILENAME] + self.LINE_NO = self.ErrMsgLables[MsgId.LINE_NO] + self.COL_NO = self.ErrMsgLables[MsgId.COL_NO] + self.MARK = self.ErrMsgLables[MsgId.MARK] + + self.EWCODE = self.ErrMsgLables[MsgId.EWCODE] + self.ERR_TYPE = self.ErrMsgLables[MsgId.ERR_TYPE] + self.ERR_CATE = self.ErrMsgLables[MsgId.ERR_CATE] + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "filename") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, int)), "line_no") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, int)), "col_no") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "src_code_line") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, int)), "indent_count") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "mark") + @PostCheck(lambda v: CheckRules.check_type_not_none(v, str)) + def get_hint_msg_common( + self, + filename: str, + line_no: int, + col_no: int, + src_code_line: str, + indent_count: int = 0, + mark: str = MARK_SYMBOL[ErrLevel.SERIOUS], + ) -> str: + HINT_MSG_COMMON = ( + NEW_LINE + + TAB * indent_count + + FILE_PREFIX + + self.FILENAME + + COLON + + self.LINE_NO + + COLON + + self.COL_NO + + NEW_LINE + + TAB * indent_count + + self.LINE_NO + + SPARATOER + + self.SRC_CODE_LINE + + NEW_LINE + + TAB * indent_count + + (self.COL_NO + WHITE_SPACE).rjust( + len(str(line_no)) + 1 + col_no + len(self.COL_NO) - len(str(col_no)), + WHITE_SPACE, + ) + + self.MARK + + WHITE_SPACE + ) + hint_msg_args = { + "filename": filename, + "line_no": line_no, + "col_no": col_no, + "src_code_line": src_code_line, + "mark": mark, + } + return Template(HINT_MSG_COMMON).substitute(hint_msg_args) + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "filename") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, int)), "indent_count") + @PostCheck(lambda v: CheckRules.check_type_not_none(v, str)) + def get_hint_msg_tiny(self, filename: str, indent_count: int = 0) -> str: + HINT_MSG_TINY = NEW_LINE + TAB * indent_count + FILE_PREFIX + self.FILENAME + hint_msg_args = {"filename": filename} + return Template(HINT_MSG_TINY).substitute(hint_msg_args) + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "filename") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, int)), "line_no") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "src_code_line") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, int)), "indent_count") + @PostCheck(lambda v: CheckRules.check_type_not_none(v, str)) + def get_hint_msg_summary( + self, filename: str, line_no: int, src_code_line: str, indent_count: int = 0 + ) -> str: + HINT_MSG_SUMMARY = ( + NEW_LINE + + TAB * indent_count + + FILE_PREFIX + + self.FILENAME + + COLON + + self.LINE_NO + + NEW_LINE + + TAB * indent_count + + self.LINE_NO + + SPARATOER + + self.SRC_CODE_LINE + ) + hint_msg_args = { + "filename": filename, + "line_no": line_no, + "src_code_line": src_code_line, + } + return Template(HINT_MSG_SUMMARY).substitute(hint_msg_args) + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "filename") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, int)), "line_no") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, int)), "col_no") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, int)), "end_col_no") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "src_code_line") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, int)), "indent_count") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "mark") + @PostCheck(lambda v: CheckRules.check_type_not_none(v, str)) + def get_hint_msg_detail( + self, + filename: str, + line_no: int, + col_no: int, + end_col_no: int, + src_code_line: str, + indent_count: int = 0, + mark: str = MARK_SYMBOL[ErrLevel.SERIOUS], + ) -> str: + HINT_MSG_DETAIL = ( + NEW_LINE + + TAB * indent_count + + FILE_PREFIX + + self.FILENAME + + COLON + + self.LINE_NO + + COLON + + self.COL_NO + + NEW_LINE + + TAB * indent_count + + self.LINE_NO + + SPARATOER + + self.SRC_CODE_LINE + + NEW_LINE + + TAB * indent_count + + (self.COL_NO + WHITE_SPACE).rjust( + len(str(line_no)) + 1 + col_no + len(self.COL_NO) - len(str(col_no)), + WHITE_SPACE, + ) + + self.MARK * (end_col_no - col_no) + + WHITE_SPACE + ) + hint_msg_args = { + "filename": filename, + "line_no": line_no, + "col_no": col_no, + "src_code_line": src_code_line, + "mark": mark, + } + return Template(HINT_MSG_DETAIL).substitute(hint_msg_args) + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "err_type") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "err_cate") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "ewcode_fmt") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "msg_1") + @PostCheck(lambda v: CheckRules.check_type_not_none(v, str)) + def err_msg_template( + self, err_type: str, err_cate: str, ewcode_fmt: str, msg_1: str + ): + SIMPLE_TEMPLATE_DOC = ( + MSG_PREFIX + + self.ERR_TYPE + + WHITE_SPACE + + self.ERR_CATE + + self.EWCODE + + WHITE_SPACE + + COLON + + WHITE_SPACE + + self.MSG_1 + ) + simple_args = { + "err_type": err_type, + "err_cate": err_cate, + "ewcode": ewcode_fmt, + "msg_1": msg_1, + } + return Template(SIMPLE_TEMPLATE_DOC).substitute(simple_args) + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, ErrLevel)), "mark_level") + def color_txt_err_file_msg(self, mark_level: ErrLevel): + self.MARK = self.theme.color_mark(self.MARK, mark_level.value) + self.FILENAME = self.theme.color(self.FILENAME, ColorOption.FILE_NAME) + self.LINE_NO = self.theme.color(self.LINE_NO, ColorOption.LINE_COLUMN) + self.COL_NO = self.theme.color(self.COL_NO, ColorOption.LINE_COLUMN) + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "err_type") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "err_cate") + def color_txt_err_msg(self, err_type: str, err_cate: str): + self.EWCODE = self.theme.color(self.EWCODE, ColorOption.EWCODE) + self.ERR_TYPE = self.color_err_msg(slot=self.ERR_TYPE, msg=err_type) + self.ERR_CATE = self.color_err_msg(slot=self.ERR_CATE, msg=err_cate) + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "msg") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "slot") + @PostCheck(lambda v: CheckRules.check_type_not_none(v, str)) + def color_err_msg(self, slot: str, msg: str): + msg_theme_map: dict = { + KCL_ERR_MSG.get_err_msg_by_errid(ErrEwcode.KCLError_Ew): ColorOption.ERROR, + KCL_ERR_MSG.get_err_msg_by_errid( + ErrEwcode.KCLWarning_Ew + ): ColorOption.WARNING, + KCL_ERR_MSG.get_err_msg_by_errid( + ErrEwcode.KCLSyntaxException_Ew + ): ColorOption.SYNTAX, + KCL_ERR_MSG.get_err_msg_by_errid( + ErrEwcode.KCLCompileException_Ew + ): ColorOption.COMPLIE, + KCL_ERR_MSG.get_err_msg_by_errid( + ErrEwcode.KCLRuntimeException_Ew + ): ColorOption.RUNTIME, + KCL_ERR_MSG.get_err_msg_by_errid( + ErrEwcode.KCLException_Ew + ): ColorOption.ERROR, + } + try: + return self.theme.color(slot, msg_theme_map[msg]) + except KeyError: + check_utils.alert_internal_bug() + + +class ErrFileMsg: + @PreCheck( + (lambda v: CheckRules.check_type_allow_none(v, str, PosixPath)), "filename" + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, int)), "line_no") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, int)), "col_no") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, int)), "end_col_no") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "src_code") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, int)), "indent_count") + def __init__( + self, + filename: typing.Union[str, PosixPath] = None, + line_no: int = None, + col_no: int = None, + end_col_no: int = None, + src_code: str = None, + arg_msg: str = DEFAULT_MSG_2, + indent_count: int = 0, + err_level: ErrLevel = ErrLevel.SERIOUS, + ): + self.filename = filename + self.line_no = line_no + self.col_no = col_no + self.end_col_no = end_col_no + self.src_code = src_code + self.err_level = err_level if err_level else ErrLevel.SERIOUS + self.arg_msg = arg_msg if arg_msg else DEFAULT_MSG_2 + self.indent_count = indent_count + + +@PreCheck((lambda v: CheckRules.check_type_not_none(v, ErrFileMsg)), "file_msg") +@PostCheck(lambda v: CheckRules.check_type_not_none(v, str)) +def get_src_code(file_msg: ErrFileMsg) -> str: + """Produce formatted error hint messages""" + src_line = "" + if file_msg.filename: + source_line: str = "" + if file_msg.line_no: + if file_msg.src_code: + lines = file_msg.src_code.split("\n") + else: + lines = ( + open(file_msg.filename, "r", encoding="utf8").read().split("\n") + if os.path.exists(file_msg.filename) + else FileCache.get(file_msg.filename).split("\n") + ) + + if 0 < file_msg.line_no <= len(lines): + source_line = lines[file_msg.line_no - 1] + return source_line + elif file_msg.src_code and file_msg.line_no: + lines = file_msg.src_code.split("\n") + if 0 < file_msg.line_no <= len(lines): + src_line = lines[file_msg.line_no - 1] + return src_line + return "" + + +@PreCheck((lambda v: CheckRules.check_type_not_none(v, ErrFileMsg)), "file_msg") +@PostCheck(lambda v: CheckRules.check_type_not_none(v, str)) +def get_hint_msg( + file_msg: ErrFileMsg, msg_tem: KCLErrMsgTemplate = KCLErrMsgTemplateDefault() +) -> str: + if msg_tem is None: + msg_tem = KCLErrMsgTemplateDefault() + msg_tem.clean_color() + if msg_tem.fmt == ErrMsgFmt.COLOR_TXT: + msg_tem.color_txt_err_file_msg(file_msg.err_level) + result = "" + arg = " -> " + file_msg.arg_msg if file_msg.arg_msg else "" + mark = MARK_SYMBOL[file_msg.err_level] + if file_msg.filename: + if file_msg.line_no: + src_code = get_src_code(file_msg) + if file_msg.col_no and file_msg.end_col_no: + result = ( + msg_tem.get_hint_msg_detail( + file_msg.filename, + file_msg.line_no, + file_msg.col_no, + file_msg.end_col_no, + src_code, + indent_count=file_msg.indent_count, + mark=mark, + ) + + arg + ) + elif file_msg.col_no and not file_msg.end_col_no: + result = ( + msg_tem.get_hint_msg_common( + file_msg.filename, + file_msg.line_no, + file_msg.col_no, + src_code, + indent_count=file_msg.indent_count, + mark=mark, + ) + + arg + ) + elif not file_msg.col_no and not file_msg.end_col_no: + result = ( + msg_tem.get_hint_msg_summary( + file_msg.filename, + file_msg.line_no, + src_code, + indent_count=file_msg.indent_count, + ) + + arg + ) + else: + result = ( + msg_tem.get_hint_msg_tiny( + file_msg.filename, indent_count=file_msg.indent_count + ) + + arg + ) + + return result + + +@PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "ewcode") +@PostCheck(lambda v: CheckRules.check_type_not_none(v, str)) +def get_err_msg( + ewcode: str, msg_tem: KCLErrMsgTemplate = KCLErrMsgTemplateDefault() +) -> str: + if msg_tem is None: + msg_tem = KCLErrMsgTemplateDefault() + msg_tem.clean_color() + err_type = KCL_ERR_MSG.get_err_type_msg_by_errid(ewcode) + err_cate = KCL_ERR_MSG.get_err_cate_msg_by_errid(ewcode) + ewcode_fmt = KCL_ERR_MSG.get_err_code_by_errid(ewcode) + err_msg = KCL_ERR_MSG.get_err_msg_by_errid(ewcode) + + if msg_tem.fmt == ErrMsgFmt.COLOR_TXT: + msg_tem.color_txt_err_msg(err_type, err_cate) + return msg_tem.err_msg_template(err_type, err_cate, ewcode_fmt, err_msg) diff --git a/internal/kclvm_py/kcl/error/kcl_err_theme.py b/internal/kclvm_py/kcl/error/kcl_err_theme.py new file mode 100644 index 000000000..741ffdfff --- /dev/null +++ b/internal/kclvm_py/kcl/error/kcl_err_theme.py @@ -0,0 +1,91 @@ +""" +The `kcl_err_theme` file mainly contains the color constants needed to highlight the KCL error message. + +ThemeId (Enum): The color theme of highlight KCL error message, currently there is only one `DEFAULT`. + +ColorOption (Enum): Fields that can be highlighted in the KCL error message. + +KCLErrMsgTheme: An abstract class, which specifies the methods required by KCL error message theme classes. + +KCLErrMsgThemeDefault: It is the default implementation class of KCLErrMsgTheme. + +Default highlight: + + EWCODE: green + ERROR: red + WARNING: yellow + FILE_NAME: blue + LINE_COLUMN: cyan + SYNTAX: dark yellow + COMPLIE: dark green + RUNTIME: dark blue + MARK: { + 1: red + 2: purple + }, + +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" +from enum import Enum, unique +from kclvm.internal.util import PreCheck, CheckRules +from abc import ABCMeta, abstractmethod + + +@unique +class ThemeId(Enum): + DEFAULT = 0 + OTHER = 1 + + +@unique +class ColorOption(Enum): + EWCODE = 0 + ERROR = 1 + WARNING = 2 + FILE_NAME = 3 + LINE_COLUMN = 4 + MARK = 5 + SYNTAX = 6 + COMPLIE = 7 + RUNTIME = 8 + + +class KCLErrMsgTheme(metaclass=ABCMeta): + @abstractmethod + def color_mark(self, mark: str, mark_level: int = 1): + pass + + @abstractmethod + def color(self, content: str, color_option: ColorOption): + pass + + +class KCLErrMsgThemeDefault(KCLErrMsgTheme): + def __init__(self): + self.KCL_THEME: dict = { + ColorOption.EWCODE: "\033[0;92m{}\033[0m", # green + ColorOption.ERROR: "\033[0;91m{}\033[0m", # red + ColorOption.WARNING: "\033[0;93m{}\033[0m", # yellow + ColorOption.FILE_NAME: "\033[0;94m{}\033[0m", # blue + ColorOption.LINE_COLUMN: "\033[0;96m{}\033[0m", # cyan + ColorOption.SYNTAX: "\033[0;33m{}\033[0m", # dark yellow + ColorOption.COMPLIE: "\033[0;32m{}\033[0m", # dark green + ColorOption.RUNTIME: "\033[0;34m{}\033[0m", # dark blue + ColorOption.MARK: { + 1: "\033[0;31m{}\033[0m", # red + 2: "\033[0;35m{}\033[0m", # purple + }, + } + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "mark") + @PreCheck((lambda v: CheckRules.check_type_not_none(v, int)), "mark_level") + @PreCheck((lambda v: CheckRules.check_int_range_allow_none(v, 1, 3)), "mark_level") + def color_mark(self, mark: str, mark_level: int = 1): + return self.KCL_THEME[ColorOption.MARK][mark_level].format(mark) + + @PreCheck((lambda v: CheckRules.check_type_not_none(v, str)), "content") + @PreCheck( + (lambda v: CheckRules.check_type_not_none(v, ColorOption)), "color_option" + ) + def color(self, content: str, color_option: ColorOption): + return self.KCL_THEME[color_option].format(content) diff --git a/internal/kclvm_py/kcl/error/kcl_error.py b/internal/kclvm_py/kcl/error/kcl_error.py new file mode 100644 index 000000000..921937e29 --- /dev/null +++ b/internal/kclvm_py/kcl/error/kcl_error.py @@ -0,0 +1,1611 @@ +""" +The `kcl_error` file mainly contains all KCL exceptions. + +KCLException is the top-level exception class, all KCL exceptions inherit from KCLException. + +KCLException.err_info_stack : +A file information stack is built in KCLException to save file information when an exception occurs. + +KCLException.gen_err_msg : +Method `gen_err_msg` generates KCL error message without highlighting +by calling the method provided in kcl_err_template.py + +KCLException.show_msg_with_theme : +Method `show_msg_with_theme` generates KCL error message with highlighting +by calling the method provided in kcl_err_template.py + +:note: At present, the KCL error message templates of the two methods `gen_err_msg` and `show_msg_with_theme` +are created by default arguments, and they are not synchronized. +If the template of one method is changed, +the template of the other method also needs to be replaced manually, +otherwise the message output by the two methods will be different. + +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" +import sys +import typing +from enum import Enum, unique + +import kclvm.kcl.error.kcl_err_template as err_template + +from kclvm.kcl.error.kcl_err_msg import KCL_ERR_MSG, ErrEwcode +from kclvm.internal.util import PreCheck, CheckRules, PostCheck + + +class KCLException(Exception): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "ewcode") + @PreCheck( + ( + lambda v: CheckRules.check_str_len_allow_none( + v, len(KCL_ERR_MSG.get_defaule_ewcode()) + ) + ), + "ewcode", + ) + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + ewcode: str = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLException_Ew if not ewcode else ewcode + self.name = KCL_ERR_MSG.get_err_name_by_ewcode(self.ewcode) + self.err_info_stack: typing.List[err_template.ErrFileMsg] = [] + if file_msgs: + self.err_info_stack.extend(file_msgs) + self.arg_msg = ( + arg_msg + if arg_msg + else KCL_ERR_MSG.get_defaule_arg_msg_by_errid(self.ewcode) + ) + + def __str__(self): + return self.gen_err_msg() + + def show_msg_with_theme( + self, + color_template=err_template.KCLErrMsgTemplateDefault( + fmt=err_template.ErrMsgFmt.COLOR_TXT + ), + ) -> str: + if not color_template: + color_template = err_template.KCLErrMsgTemplateDefault( + fmt=err_template.ErrMsgFmt.COLOR_TXT + ) + return self.gen_err_msg(msg_tem=color_template) + + def gen_err_msg(self, msg_tem=err_template.KCLErrMsgTemplateDefault()) -> str: + result = "" + result += err_template.get_err_msg(self.ewcode, msg_tem) + count = 0 + for file in reversed(self.err_info_stack): + if file and file.filename: + file.indent_count = count + result += err_template.get_hint_msg(file, msg_tem) + count += 1 + return result + "\n" + self.arg_msg if self.arg_msg else result + + def no_err_msg(self) -> bool: + return self.err_info_stack is None or len(self.err_info_stack) == 0 + + def append_err_info(self, einfo: err_template.ErrFileMsg): + self.err_info_stack.append(einfo) + + def pop_err_info(self) -> err_template.ErrFileMsg: + return ( + self.err_info_stack.pop(-1) + if len(self.err_info_stack) > 0 + else err_template.ErrFileMsg() + ) + + @property + def filename(self): + return self.err_info_stack[-1].filename if len(self.err_info_stack) > 0 else "" + + @property + def lineno(self): + return self.err_info_stack[-1].line_no if len(self.err_info_stack) > 0 else "" + + @property + def colno(self): + return self.err_info_stack[-1].col_no if len(self.err_info_stack) > 0 else "" + + +class KCLError(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLWarning(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLWarning_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLSyntaxException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLSyntaxException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLCompileException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLCompileException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLRuntimeException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLRuntimeException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLAttributeException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLAttributeException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLSchemaException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLSchemaException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLMixinException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLMixinException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLInheritException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLInheritException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLImportException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLImportException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLTypeException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLTypeException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLDecoratorException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLDecoratorException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLArgumentException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLArgumentException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLOverflowException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLOverflowException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLComplingException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLComplingException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLRunningException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLRunningException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLDeprecatedException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLDeprecatedException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLDocException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLDocException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLImmutableException(KCLException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLImmutableException_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class InvalidSyntaxError(KCLError, KCLSyntaxException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.InvalidSyntax_Ew + self.accepts_lark = [] + self.accepts_msg = [] + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLTabError(KCLError, KCLSyntaxException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLTabError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLIndentationError(KCLError, KCLSyntaxException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLIndentationError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class CannotFindModule(KCLError, KCLCompileException, KCLImportException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.CannotFindModule_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class FailedLoadModule(KCLError, KCLCompileException, KCLImportException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.FailedLoadModule_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class RecursiveLoad(KCLError, KCLCompileException, KCLImportException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.RecursiveLoad_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class FloatOverflow(KCLError, KCLRuntimeException, KCLOverflowException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.FloatOverflow_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class FloatUnderflow(KCLWarning, KCLCompileException, KCLOverflowException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.FloatUnderflow_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class IntOverflow(KCLError, KCLRuntimeException, KCLOverflowException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.IntOverflow_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class InvalidDocstring(KCLWarning, KCLCompileException, KCLDocException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.InvalidDocstring_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class DeprecatedError(KCLError, KCLRuntimeException, KCLDeprecatedException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.Deprecated_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class DeprecatedWarning(KCLWarning, KCLDeprecatedException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.DeprecatedWarning_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class UnKnownDecoratorError(KCLError, KCLCompileException, KCLDecoratorException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.UnKnownDecorator_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class InvalidDecoratorTargetError(KCLError, KCLCompileException, KCLDecoratorException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.InvalidDecoratorTarget_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class MixinNamingError(KCLError, KCLCompileException, KCLMixinException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.MixinNamingError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class MixinStructureIllegal(KCLError, KCLCompileException, KCLMixinException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.MixinStructureIllegal_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class SchemaCheckFailure(KCLError, KCLRuntimeException, KCLSchemaException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.SchemaCheckFailure_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class CannotAddMembersComplieError(KCLError, KCLCompileException, KCLSchemaException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.CannotAddMembersComplieError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class CannotAddMembersRuntimeError(KCLError, KCLRuntimeException, KCLSchemaException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.CannotAddMembersRuntimeError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class IndexSignatureError(KCLError, KCLCompileException, KCLSchemaException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.IndexSignatureError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class TypeRuntimeError(KCLError, KCLRuntimeException, KCLTypeException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.TypeRuntimeError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class TypeComplieError(KCLError, KCLCompileException, KCLTypeException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.TypeComplieError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class CompileError(KCLError, KCLCompileException, KCLComplingException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.CompileError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class SelectorError(KCLError, KCLCompileException, KCLComplingException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.SelectorError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLNameError(KCLError, KCLCompileException, KCLComplingException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLNameError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLValueError(KCLError, KCLCompileException, KCLComplingException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLValueError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLKeyError(KCLError, KCLCompileException, KCLComplingException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLKeyError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class UniqueKeyError(KCLError, KCLCompileException, KCLComplingException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.UniqueKeyError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLAttributeComplieError(KCLError, KCLCompileException, KCLAttributeException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLAttributeComplieError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLAttributeRuntimeError(KCLError, KCLRuntimeException, KCLAttributeException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLAttributeRuntimeError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class IllegalAttributeError(KCLError, KCLCompileException, KCLAttributeException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.IllegalAttributeError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class MultiInheritError(KCLError, KCLCompileException, KCLInheritException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.MultiInheritError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class CycleInheritError(KCLError, KCLCompileException, KCLInheritException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.CycleInheritError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class IllegalInheritError(KCLError, KCLCompileException, KCLInheritException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.IllegalInheritError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class IllegalArgumentRuntimeError(KCLError, KCLRuntimeException, KCLArgumentException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.IllegalArgumentRuntimeError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class IllegalArgumentComplieError(KCLError, KCLCompileException, KCLArgumentException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.IllegalArgumentComplieError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class IllegalArgumentSyntaxError(KCLError, KCLSyntaxException, KCLArgumentException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.IllegalArgumentSyntaxError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class EvaluationError(KCLError, KCLRuntimeException, KCLRunningException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.EvaluationError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class InvalidFormatSpec(KCLError, KCLRuntimeException, KCLRunningException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.InvalidFormatSpec_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLAssertionError(KCLError, KCLRuntimeException, KCLRunningException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLAssertionError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class ImmutableRuntimeError(KCLError, KCLCompileException, KCLImmutableException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.ImmutableRuntimeError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class ImmutableCompileError(KCLError, KCLCompileException, KCLImmutableException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.ImmutableCompileError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class KCLRecursionError(KCLError, KCLRuntimeException, KCLRunningException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.KCLRecursionError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +class PlanError(KCLError, KCLRuntimeException, KCLRunningException): + @PreCheck( + ( + lambda v: CheckRules.check_list_item_type_allow_none( + v, err_template.ErrFileMsg + ) + ), + "file_msgs", + ) + @PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") + def __init__( + self, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, + ): + self.ewcode = ErrEwcode.PlanError_Ew + KCLException.__init__( + self, file_msgs=file_msgs, ewcode=self.ewcode, arg_msg=arg_msg + ) + + +@unique +class ErrType(Enum): + InvalidSyntax_TYPE = (0,) + TabError_TYPE = (1,) + IndentationError_TYPE = (2,) + CannotFindModule_TYPE = (3,) + FailedLoadModule_TYPE = (4,) + CompileError_TYPE = (5,) + EvaluationError_TYPE = (6,) + RecursiveLoad_TYPE = (7,) + FloatOverflow_TYPE = (8,) + FloatUnderflow_TYPE = (9,) + IntOverflow_TYPE = (10,) + InvalidDocstring_TYPE = (11,) + Deprecated_TYPE = (12,) + UnKnownDecorator_TYPE = (13,) + InvalidDecoratorTarget_TYPE = (14,) + InvalidFormatSpec_TYPE = (15,) + SelectorError_TYPE = (16,) + SchemaCheckFailure_TYPE = (17,) + MixinNamingError_TYPE = (18,) + MixinStructureIllegal_TYPE = (19,) + + IndexSignatureError_TYPE = (20,) + TypeError_Runtime_TYPE = (21,) + TypeError_Compile_TYPE = (22,) + NameError_TYPE = (23,) + ValueError_TYPE = (24,) + KeyError_TYPE = (25,) + UniqueKeyError_TYPE = (26,) + AttributeError_TYPE = (27,) + AttributeError_Runtime_TYPE = (28,) + AssertionError_TYPE = (29,) + ImmutableCompileError_TYPE = (30,) + ImmutableRuntimeError_TYPE = (31,) + MultiInheritError_TYPE = (32,) + CycleInheritError_TYPE = (33,) + IllegalInheritError_TYPE = (34,) + IllegalAttributeError_TYPE = (35,) + IllegalArgumentError_TYPE = (36,) + IllegalArgumentError_Complie_TYPE = (37,) + IllegalArgumentError_Syntax_TYPE = (38,) + RecursionError_TYPE = (39,) + PlanError_TYPE = (40,) + Deprecated_Warning_TYPE = (41,) + CannotAddMembers_TYPE = (42,) + CannotAddMembers_Runtime_TYPE = (43,) + + +ERR_TYPE_EWCODE_MAP = { + ErrType.InvalidSyntax_TYPE: InvalidSyntaxError, + ErrType.TabError_TYPE: KCLTabError, + ErrType.IndentationError_TYPE: KCLIndentationError, + ErrType.CannotFindModule_TYPE: CannotFindModule, + ErrType.FailedLoadModule_TYPE: FailedLoadModule, + ErrType.CompileError_TYPE: CompileError, + ErrType.EvaluationError_TYPE: EvaluationError, + ErrType.RecursiveLoad_TYPE: RecursiveLoad, + ErrType.FloatOverflow_TYPE: FloatOverflow, + ErrType.FloatUnderflow_TYPE: FloatUnderflow, + ErrType.IntOverflow_TYPE: IntOverflow, + ErrType.InvalidDocstring_TYPE: InvalidDocstring, + ErrType.Deprecated_TYPE: DeprecatedError, + ErrType.Deprecated_Warning_TYPE: DeprecatedWarning, + ErrType.UnKnownDecorator_TYPE: UnKnownDecoratorError, + ErrType.InvalidDecoratorTarget_TYPE: InvalidDecoratorTargetError, + ErrType.InvalidFormatSpec_TYPE: InvalidFormatSpec, + ErrType.SelectorError_TYPE: SelectorError, + ErrType.SchemaCheckFailure_TYPE: SchemaCheckFailure, + ErrType.MixinNamingError_TYPE: MixinNamingError, + ErrType.MixinStructureIllegal_TYPE: MixinStructureIllegal, + ErrType.CannotAddMembers_TYPE: CannotAddMembersComplieError, + ErrType.CannotAddMembers_Runtime_TYPE: CannotAddMembersRuntimeError, + ErrType.IndexSignatureError_TYPE: IndexSignatureError, + ErrType.TypeError_Runtime_TYPE: TypeRuntimeError, + ErrType.TypeError_Compile_TYPE: TypeComplieError, + ErrType.NameError_TYPE: KCLNameError, + ErrType.ValueError_TYPE: KCLValueError, + ErrType.KeyError_TYPE: KCLKeyError, + ErrType.UniqueKeyError_TYPE: UniqueKeyError, + ErrType.AttributeError_TYPE: KCLAttributeComplieError, + ErrType.AttributeError_Runtime_TYPE: KCLAttributeRuntimeError, + ErrType.AssertionError_TYPE: KCLAssertionError, + ErrType.ImmutableCompileError_TYPE: ImmutableCompileError, + ErrType.ImmutableRuntimeError_TYPE: ImmutableRuntimeError, + ErrType.MultiInheritError_TYPE: MultiInheritError, + ErrType.CycleInheritError_TYPE: CycleInheritError, + ErrType.IllegalInheritError_TYPE: IllegalInheritError, + ErrType.IllegalAttributeError_TYPE: IllegalAttributeError, + ErrType.IllegalArgumentError_TYPE: IllegalArgumentRuntimeError, + ErrType.IllegalArgumentError_Complie_TYPE: IllegalArgumentComplieError, + ErrType.IllegalArgumentError_Syntax_TYPE: IllegalArgumentSyntaxError, + ErrType.RecursionError_TYPE: KCLRecursionError, + ErrType.PlanError_TYPE: PlanError, +} + + +@PreCheck( + (lambda v: CheckRules.check_list_item_type_allow_none(v, err_template.ErrFileMsg)), + "file_msgs", +) +@PreCheck((lambda v: CheckRules.check_type_not_none(v, ErrType)), "err_type") +@PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") +@PostCheck(lambda result: CheckRules.check_type_not_none(result, KCLException)) +def get_exception( + err_type: ErrType = None, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, +): + return ERR_TYPE_EWCODE_MAP[err_type](file_msgs=file_msgs, arg_msg=arg_msg) + + +@PreCheck( + (lambda v: CheckRules.check_list_item_type_allow_none(v, err_template.ErrFileMsg)), + "file_msgs", +) +@PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") +@PreCheck((lambda v: CheckRules.check_type_not_none(v, ErrType)), "err_type") +def report_exception( + err_type: ErrType, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, +): + raise get_exception(err_type, file_msgs, arg_msg) + + +@PreCheck( + (lambda v: CheckRules.check_list_item_type_allow_none(v, err_template.ErrFileMsg)), + "file_msgs", +) +@PreCheck((lambda v: CheckRules.check_type_allow_none(v, str)), "arg_msg") +@PreCheck((lambda v: CheckRules.check_type_not_none(v, ErrType)), "err_type") +def report_warning( + err_type: ErrType, + file_msgs: typing.List[err_template.ErrFileMsg] = None, + arg_msg: str = None, +): + print_kcl_warning_message( + get_exception(err_type, file_msgs, arg_msg), file=sys.stderr + ) + + +@PreCheck((lambda v: CheckRules.check_type_not_none(v, KCLException)), "err") +def print_kcl_error_message(err: KCLException, file=sys.stderr): + err_msg = err.show_msg_with_theme() if file.isatty() else (str(err)) + print(err_msg, file=file) + + +@PreCheck((lambda v: CheckRules.check_type_not_none(v, KCLWarning)), "err") +def print_kcl_warning_message(err: KCLWarning, file=sys.stderr): + err_msg = err.show_msg_with_theme() if file.isatty() else (str(err)) + print(err_msg, file=file) + + +@PreCheck((lambda v: CheckRules.check_type_not_none(v, Exception)), "err") +def print_common_error_message(err: Exception, file=sys.stderr): + print("Error: {0}".format(err), file=file) + + +@PreCheck((lambda v: CheckRules.check_type_allow_none(v, Exception)), "err") +def print_internal_error_message(err: Exception = None, file=sys.stderr): + if err: + print(err, file=file) + print("Internal Error! Please report a bug to us.", file=file) diff --git a/internal/kclvm_py/kcl/error/readme.md b/internal/kclvm_py/kcl/error/readme.md new file mode 100644 index 000000000..00c4300f3 --- /dev/null +++ b/internal/kclvm_py/kcl/error/readme.md @@ -0,0 +1,139 @@ +# KCL excpetion +This readme mainly introduces the KCL exceptions and KCL exception message work flow. + +## 1. Work Flow +``` + ---------------- ---------------- ----------------------- ------------------ + | | | | | | | | + | kcl_error.py | --> | kcl_error.py | ---> | kcl_err_template.py | ---> | kcl_err_msg.py | + no color | | | | | | | | + KCLXXXError.gen_err_msg() <-------- | | | | | no color | | | + color | KCLXXXError | <-- | KCLException | <--- | <---------------- | <--- | | + KCLXXXError.show_msg_with_theme() <-------- | | | | | | | | + ---------------- ---------------- ----------------------- ------------------ + ^ | color + | v + ----------------------- + | kcl_err_theme.py | + | | + ----------------------- + + +``` +- KCLXXXError.gen_err_msg() + 1. `KCLXXXError` will call method `gen_err_msg()` of the parent class `KCLException` + and get KCL error message without highlighting. + + 2. The `gen_err_msg()` of `KCLException` will call methods provided in `kcl_err_template.py` + to obtain the structured KCL error message. + + 3. `kcl_err_template.py` will obtain KCL error message constants through methods provided by `kcl_err_msg.py` + and return the structured error messages to `KCLException`. + + 4. Because `gen_err_msg()` sets the highlight flag to False, + so `kcl_err_template.py` will return KCL error messages without highlight. + + +- KCLXXXError.show_msg_with_theme() + 1. `KCLXXXError` will call method `show_msg_with_theme()` of the parent class `KCLException` + and get KCL error message with highlighting. + + 2. The `gen_err_msg()` of `KCLException` will call methods provided in `kcl_err_template.py` + to obtain the structured KCL error message. + + 3. `kcl_err_template.py` will obtain KCL error message constants through methods provided by `kcl_err_msg.py` + and return the structured and highlight error messages to `KCLException`. + + 4. Because `show_msg_with_theme()` sets the highlight flag to True, + so before returning KCL error messages to `KCLException`, + `kcl_err_template.py` will call the method provided in `kcl_err_theme.py` to highlight KCL error messages. + Then `kcl_err_template.py` will return KCL error messages with highlight. + +## 2. KCL exceptions + +This section lists all the exceptions in KCLVM. +They can be divided into a three-level inheritance structure. +The top-level KCLException inherits Python Exception, +the second level contains 12 categories of KCL exceptions, +and the last level contains 44 exceptions inherits the second level exceptions。 + +Each exception in KCL contains a unique identifier `ewcode`. +Each bit in ewcode shows different information in this exception. + +For example: the ewcode of exception `FailedLoadModuleError` in kclvm is `E2F05`. +``` +E: Error : This exception is an error. +2: Compile : This exception occured during compiling +F: Import : This exception occurred when importing the package +05: FailedLoadModule : This exception occurred because the module failed to load +``` + +All exceptions, the inheritance relationship between exceptions, +and ewcode are shown in the following table. + +| ewcode | KCL exception | parent exception | +| ---- | ---- | ---- | +| 00000 | KCLException | Exception | +| E0000 | KCLError | KCLException | +| W0000 | KCLWarning | KCLException | +| 01000 | KCLSyntaxException | KCLException | +| 02000 | KCLCompileException | KCLException | +| 03000 | KCLRuntimeException | KCLException | +| 00A00 | KCLAttributeException | KCLException | +| 00B00 | KCLSchemaException | KCLException | +| 00C00 | KCLMixinException | KCLException | +| 00D00 | KCLInheritException | KCLException | +| 00F00 | KCLImportException | KCLException | +| 00G00 | KCLTypeException | KCLException | +| 00H00 | KCLDecoratorException | KCLException | +| 00I00 | KCLArgumentException | KCLException | +| 00K00 | KCLOverflowException | KCLException | +| 00L00 | KCLComplingException | KCLException | +| 00M00 | KCLRunningException| KCLException | +| 00N00 | KCLDeprecatedException | KCLException | +| 00P00 | KCLDocException | KCLException | +| 00Q00 | KCLImmutableException | KCLException | +| E1001 | InvalidSyntaxError | KCLError, KCLSyntaxException | +| E1002 | KCLTabError | KCLError, KCLSyntaxException | +| E1003 | KCLIndentationError | KCLError, KCLSyntaxException | +| E2F04 | CannotFindModule | KCLError, KCLCompileException, KCLImportException | +| E2F05 | FailedLoadModule | KCLError, KCLCompileException, KCLImportException | +| E3F06 | RecursiveLoad | KCLError, KCLRuntimeException, KCLImportException | +| E3K04 | FloatOverflow | KCLError, KCLRuntimeException, KCLOverflowException | +| W2K04 | FloatUnderflow | KCLWarning, KCLCompileException, KCLOverflowException | +| E3K09 | IntOverflow | KCLError, KCLRuntimeException, KCLOverflowException | +| W2P10 | InvalidDocstring | KCLWarning, KCLCompileException, KCLDocException | +| E3N11 | DeprecatedError | KCLError, KCLRuntimeException, KCLDeprecatedException | +| W2N12 | DeprecatedWarning | KCLWarning, KCLDeprecatedException | +| E2H13 | UnKnownDecoratorError | KCLError, KCLCompileException, KCLDecoratorException | +| E2H14 | InvalidDecoratorTargetError | KCLError, KCLCompileException, KCLDecoratorException | +| E2C15 | MixinNamingError | KCLError, KCLCompileException, KCLMixinException | +| E2C16 | MixinStructureIllegal | KCLError, KCLCompileException, KCLMixinException | +| E3B17 | SchemaCheckFailure | KCLError, KCLRuntimeException, KCLSchemaException | +| E2B17 | CannotAddMembersComplieError | KCLError, KCLCompileException, KCLSchemaException | +| E3B19 | CannotAddMembersRuntimeError | KCLError, KCLRuntimeException, KCLSchemaException | +| E2B20 | IndexSignatureError | KCLError, KCLCompileException, KCLSchemaException | +| E3G21 | TypeRuntimeError | KCLError, KCLRuntimeException, KCLTypeException | +| E2G22 | TypeComplieError | KCLError, KCLCompileException, KCLTypeException | +| E2L23 | CompileError | KCLError, KCLCompileException, KCLComplingException | +| E2L24 | SelectorError | KCLError, KCLCompileException, KCLComplingException | +| E2L25 | KCLNameError | KCLError, KCLCompileException, KCLComplingException | +| E2L26 | KCLValueError | KCLError, KCLCompileException, KCLComplingException | +| E2L27 | KCLKeyError | KCLError, KCLCompileException, KCLComplingException | +| E2L28 | UniqueKeyError | KCLError, KCLCompileException, KCLComplingException | +| E2A29 | KCLAttributeComplieError | KCLError, KCLCompileException, KCLAttributeException | +| E3A30 | KCLAttributeRuntimeError | KCLError, KCLRuntimeException, KCLAttributeException | +| E2A31 | IllegalAttributeError | KCLError, KCLCompileException, KCLAttributeException | +| E2D32 | MultiInheritError | KCLError, KCLCompileException, KCLInheritException | +| E2D33 | CycleInheritError | KCLError, KCLRuntimeException, KCLInheritException | +| E2D34 | IllegalInheritError | KCLError, KCLCompileException, KCLInheritException | +| E3I35 | IllegalArgumentRuntimeError | KCLError, KCLRuntimeException, KCLArgumentException | +| E2I36 | IllegalArgumentComplieError | KCLError, KCLCompileException, KCLArgumentException | +| E1I37 | IllegalArgumentSyntaxError | KCLError, KCLSyntaxException, KCLArgumentException | +| E3M38 | EvaluationError | KCLError, KCLRuntimeException, KCLRunningException | +| E3M39 | InvalidFormatSpec | KCLError, KCLRuntimeException, KCLRunningException | +| E3M40 | KCLAssertionError | KCLError, KCLRuntimeException, KCLRunningException | +| E3M41 | ImmutableCompileError | KCLError, KCLCompileException, KCLImmutableException | +| E3M44 | ImmutableRuntimeError | KCLError, KCLRuntimeException, KCLImmutableException | +| E3M42 | KCLRecursionError | KCLError, KCLRuntimeException, KCLRunningException | +| E3M43 | PlanError | KCLError, KCLRuntimeException, KCLRunningException | diff --git a/internal/kclvm_py/kcl/grammar/kcl.lark b/internal/kclvm_py/kcl/grammar/kcl.lark new file mode 100644 index 000000000..398fed0e1 --- /dev/null +++ b/internal/kclvm_py/kcl/grammar/kcl.lark @@ -0,0 +1,263 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//////////// KCL grammar //////////// +start: (NEWLINE | statement)* + +statement: simple_stmt | compound_stmt +simple_stmt: (assign_stmt | unification_stmt | expr_stmt | assert_stmt | import_stmt | type_alias_stmt) NEWLINE +compound_stmt: if_stmt | schema_stmt | rule_stmt + +//////////// import_stmt //////////// +import_stmt: IMPORT dot_name (AS NAME)? +dot_name: (leading_dots identifier) | identifier +leading_dots: DOT+ + +/////////// assert_stmt //////////// +assert_stmt: ASSERT simple_expr (IF simple_expr)? (COMMA test)? + +//////////// if_stmt //////////// +if_stmt: IF test COLON execution_block (ELIF test COLON execution_block)* (ELSE COLON execution_block)? +execution_block: if_simple_stmt | NEWLINE _INDENT schema_init_stmt+ _DEDENT +if_simple_stmt: (simple_assign_stmt | unification_stmt | expr_stmt | assert_stmt) NEWLINE + +//////////// assign_stmt //////////// +assign_stmt: identifier [COLON type] (ASSIGN identifier)* ASSIGN test + | identifier (COMP_PLUS | COMP_MINUS | COMP_MULTIPLY | COMP_DOUBLE_STAR | COMP_DIVIDE + | COMP_DOUBLE_DIVIDE | COMP_MOD | COMP_AND | COMP_OR | COMP_XOR | COMP_SHIFT_LEFT + | COMP_SHIFT_RIGHT) test + +simple_assign_stmt: identifier ASSIGN test + | identifier (COMP_PLUS | COMP_MINUS | COMP_MULTIPLY | COMP_DOUBLE_STAR | COMP_DIVIDE + | COMP_DOUBLE_DIVIDE | COMP_MOD | COMP_AND | COMP_OR | COMP_XOR | COMP_SHIFT_LEFT + | COMP_SHIFT_RIGHT) test + +//////////// unification_stmt //////////// +unification_stmt: identifier COLON schema_expr + +//////////// schema_stmt //////////// +schema_stmt: [decorators] (SCHEMA|MIXIN|PROTOCOL) NAME [LEFT_BRACKETS [schema_arguments] RIGHT_BRACKETS] [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] [for_host] COLON NEWLINE [schema_body] +schema_arguments: schema_argument (COMMA schema_argument)* +schema_argument: NAME [COLON type] [ASSIGN test] +schema_body: _INDENT (string NEWLINE)* [mixin_stmt] (schema_attribute_stmt|schema_init_stmt|schema_index_signature)* [check_block] _DEDENT +schema_attribute_stmt: attribute_stmt NEWLINE +attribute_stmt: [decorators] identifier [QUESTION] COLON type [(ASSIGN|COMP_OR) test] +schema_init_stmt: if_simple_stmt | if_stmt +schema_index_signature: LEFT_BRACKETS [NAME COLON] [ELLIPSIS] basic_type RIGHT_BRACKETS COLON type [ASSIGN test] NEWLINE + +//////////// rule_stmt //////////// +rule_stmt: [decorators] RULE NAME [LEFT_BRACKETS [schema_arguments] RIGHT_BRACKETS] [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] [for_host] COLON NEWLINE [rule_body] +rule_body: _INDENT (string NEWLINE)* check_expr+ _DEDENT + +for_host: FOR identifier + +/////////// decorators //////////// +decorators: (AT decorator_expr NEWLINE)+ +decorator_expr: identifier [call_suffix] + +//////////// type //////////// +type: type_element (OR type_element)* +type_element: schema_type | basic_type | compound_type | literal_type +schema_type: identifier +basic_type: STRING_TYPE | INT_TYPE | FLOAT_TYPE | BOOL_TYPE | ANY_TYPE +compound_type: list_type | dict_type +list_type: LEFT_BRACKETS (type)? RIGHT_BRACKETS +dict_type: LEFT_BRACE (type)? COLON (type)? RIGHT_BRACE +literal_type: string | number | TRUE | FALSE | NONE + +//////////// type alias //////////// +type_alias_stmt: TYPE NAME ASSIGN type + +//////////// check_stmt //////////// +check_block: CHECK COLON NEWLINE _INDENT check_expr+ _DEDENT +check_expr: simple_expr [IF simple_expr] [COMMA primary_expr] NEWLINE + +//////////// mixin_stmt //////////// +mixin_stmt: MIXIN LEFT_BRACKETS [mixins | multiline_mixins] RIGHT_BRACKETS NEWLINE +multiline_mixins: NEWLINE _INDENT mixins NEWLINE _DEDENT +mixins: identifier (COMMA (NEWLINE mixins | identifier))* + + +//////////// expression_stmt //////////// +expr_stmt: testlist_expr +testlist_expr: test (COMMA test)* +test: if_expr | simple_expr +if_expr: simple_expr IF simple_expr ELSE test + +simple_expr: unary_expr | binary_expr | primary_expr + +unary_expr: un_op simple_expr +binary_expr: simple_expr bin_op simple_expr + +bin_op: L_OR | L_AND + | OR | XOR | AND + | SHIFT_LEFT | SHIFT_RIGHT + | PLUS | MINUS | MULTIPLY | DIVIDE | MOD | DOUBLE_DIVIDE + | DOUBLE_STAR + | EQUAL_TO | NOT_EQUAL_TO + | LESS_THAN | GREATER_THAN | LESS_THAN_OR_EQUAL_TO | GREATER_THAN_OR_EQUAL_TO + | IN | L_NOT IN | IS | IS L_NOT | L_NOT | AS +un_op: L_NOT | PLUS | MINUS | NOT + +primary_expr: identifier call_suffix | operand | primary_expr select_suffix | primary_expr call_suffix | primary_expr slice_suffix +operand: identifier | number | string | constant | quant_expr | list_expr | list_comp | config_expr | dict_comp | schema_expr | lambda_expr | LEFT_PARENTHESES test RIGHT_PARENTHESES +select_suffix: [QUESTION] DOT NAME +call_suffix: LEFT_PARENTHESES [arguments [COMMA]] RIGHT_PARENTHESES +slice_suffix: [QUESTION] LEFT_BRACKETS (test | [test] COLON [test] [COLON [test]]) RIGHT_BRACKETS +arguments: argument (COMMA argument)* +argument: test | NAME ASSIGN test | MULTIPLY test | DOUBLE_STAR test + + +//////////// operand //////////// +identifier: NAME (DOT NAME)* +quant_expr: quant_op [ identifier COMMA ] identifier IN quant_target LEFT_BRACE (simple_expr [IF simple_expr] | NEWLINE _INDENT simple_expr [IF simple_expr] NEWLINE _DEDENT)? RIGHT_BRACE +quant_target: string | identifier | list_expr | list_comp | config_expr | dict_comp +quant_op: ALL | ANY | FILTER | MAP +list_expr: LEFT_BRACKETS [list_items | NEWLINE [_INDENT list_items _DEDENT]] RIGHT_BRACKETS +list_items: list_item ((COMMA [NEWLINE] | [NEWLINE]) list_item)* [COMMA] [NEWLINE] +list_item: test | star_expr | if_item +list_comp: LEFT_BRACKETS (list_item comp_clause+ | NEWLINE _INDENT list_item comp_clause+ _DEDENT) RIGHT_BRACKETS +dict_comp: LEFT_BRACE (entry comp_clause+ | NEWLINE _INDENT entry comp_clause+ _DEDENT) RIGHT_BRACE +entry: test (COLON | ASSIGN | COMP_PLUS) test +comp_clause: FOR loop_variables [COMMA] IN simple_expr [NEWLINE] [IF test [NEWLINE]] +if_entry: IF test COLON if_entry_exec_block (ELIF test COLON if_entry_exec_block)* (ELSE COLON if_entry_exec_block)? +if_entry_exec_block: (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry) [NEWLINE] | NEWLINE _INDENT (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry) ((COMMA [NEWLINE] | [NEWLINE]) (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry))* [COMMA] [NEWLINE] _DEDENT +if_item: IF test COLON if_item_exec_block (ELIF test COLON if_item_exec_block)* (ELSE COLON if_item_exec_block)? +if_item_exec_block: list_item [NEWLINE] | NEWLINE _INDENT list_item ((COMMA [NEWLINE] | NEWLINE) list_item)* [COMMA] [NEWLINE] _DEDENT + +star_expr: MULTIPLY primary_expr +double_star_expr: DOUBLE_STAR primary_expr +loop_variables: primary_expr (COMMA primary_expr)* +schema_expr: identifier (LEFT_PARENTHESES [arguments] RIGHT_PARENTHESES)? config_expr +config_expr: LEFT_BRACE [config_entries | NEWLINE [_INDENT config_entries _DEDENT]] RIGHT_BRACE +config_entries: config_entry ((COMMA [NEWLINE] | [NEWLINE]) config_entry)* [COMMA] [NEWLINE] +config_entry: test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry + +//////////// lambda_expr //////////// +lambda_expr: LAMBDA [schema_arguments] [RIGHT_ARROW type] LEFT_BRACE [expr_stmt | NEWLINE _INDENT schema_init_stmt+ _DEDENT] RIGHT_BRACE + +//////////// misc //////////// +number: DEC_NUMBER [multiplier] | HEX_NUMBER | BIN_NUMBER | OCT_NUMBER | FLOAT_NUMBER +multiplier: SI_N_L | SI_U_L | SI_M_L | SI_K_L | SI_K | SI_M | SI_G | SI_T | SI_P + | SI_K_IEC | SI_M_IEC | SI_G_IEC | SI_T_IEC | SI_P_IEC +string: STRING | LONG_STRING +constant : TRUE | FALSE | NONE | UNDEFINED +// Tokens + +ASSIGN: "=" +COLON: ":" +SEMI_COLON: ";" +COMMA: "," +QUESTION: "?" +ELLIPSIS: "..." +RIGHT_ARROW: "->" +LEFT_PARENTHESES: "(" +RIGHT_PARENTHESES: ")" +LEFT_BRACKETS: "[" +RIGHT_BRACKETS: "]" +LEFT_BRACE: "{" +RIGHT_BRACE: "}" +PLUS: "+" +MINUS: "-" +MULTIPLY: "*" +DIVIDE: "/" +MOD: "%" +DOT: "." +AND: "&" +OR: "|" +XOR: "^" +NOT: "~" +LESS_THAN: "<" +GREATER_THAN: ">" +EQUAL_TO: "==" +NOT_EQUAL_TO: "!=" +GREATER_THAN_OR_EQUAL_TO: ">=" +LESS_THAN_OR_EQUAL_TO: "<=" +DOUBLE_STAR: "**" +DOUBLE_DIVIDE: "//" +SHIFT_LEFT: "<<" +SHIFT_RIGHT: ">>" +AT: "@" + +COMP_PLUS: "+=" +COMP_MINUS: "-=" +COMP_MULTIPLY: "*=" +COMP_DIVIDE: "/=" +COMP_MOD: "%=" +COMP_AND: "&=" +COMP_OR: "|=" +COMP_XOR: "^=" +COMP_DOUBLE_STAR: "**=" +COMP_DOUBLE_DIVIDE: "//=" +COMP_SHIFT_LEFT: "<<=" +COMP_SHIFT_RIGHT: ">>=" + +// Special tokens +IMPORT: "import" +AS: "as" +RULE: "rule" +SCHEMA: "schema" +MIXIN: "mixin" +PROTOCOL: "protocol" +CHECK: "check" +FOR: "for" +ASSERT: "assert" +IF: "if" +ELIF: "elif" +ELSE: "else" +L_OR: "or" +L_AND: "and" +L_NOT: "not" +IN: "in" +IS: "is" +LAMBDA: "lambda" +ALL: "all" +ANY: "any" +FILTER: "filter" +MAP: "map" +TYPE: "type" + +ANY_TYPE: "any" +STRING_TYPE: "str" +INT_TYPE: "int" +FLOAT_TYPE: "float" +BOOL_TYPE: "bool" + +// Constant tokens +TRUE: "True" +FALSE: "False" +NONE: "None" +UNDEFINED: "Undefined" + +// Binary prefix +SI_N_L: "n" +SI_U_L: "u" +SI_M_L: "m" +SI_K_L: "k" +SI_K: "K" +SI_M: "M" +SI_G: "G" +SI_T: "T" +SI_P: "P" +SI_K_IEC: "Ki" +SI_M_IEC: "Mi" +SI_G_IEC: "Gi" +SI_T_IEC: "Ti" +SI_P_IEC: "Pi" +IEC: "i" + +NAME: /\$?[a-zA-Z_]\w*/ +COMMENT: /#[^\n]*/ +NEWLINE: ( /\r?\n[\t ]*/ | COMMENT )+ + +STRING: /r?("(?!"").*?(? len( + MANGLE_PREFIX + ), "Internal Error: Demangling failure. Please report a bug to us." + demangled_name += name[len(MANGLE_PREFIX) :] + else: + demangled_name = demangle(name[:dot]) + "." + demangle(name[dot + 1 :]) + else: + demangled_name = name + return demangled_name + + +def ismangled(name): + """Check if a name is mangled""" + if name.startswith(MANGLE_PREFIX): + return True + return False + + +def tagging(tag, name=None): + """tagging a name""" + return TAGGING_PREFIX + tag + "_" + name + + +def detagging(tag, name=None): + """Detagging a name if it is tagged""" + if istagged(name): + return name[len(TAGGING_PREFIX) + len(tag) + 1 :] + return name + + +def istagged(name): + """Check if a name is tagged""" + if name.startswith(TAGGING_PREFIX): + return True + return False + + +def isprivate_field(name): + return name.startswith("_") diff --git a/internal/kclvm_py/kcl/types/__init__.py b/internal/kclvm_py/kcl/types/__init__.py new file mode 100644 index 000000000..162ee042f --- /dev/null +++ b/internal/kclvm_py/kcl/types/__init__.py @@ -0,0 +1,62 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .scope import ( + Scope, + ProgramScope, + PackageScope, + BUILTIN_SCOPE, + PLUGIN_SCOPE_MAPPING, +) +from .checker import ResolveProgram, CheckConfig +from .type import ( + Type, + VOID_TYPE, + NONE_TYPE, + INT_TYPE, + FLOAT_TYPE, + STR_TYPE, + BOOL_TYPE, + ANY_TYPE, + TRUE_LIT_TYPE, + FALSE_LIT_TYPE, + DICT_STR_ANY_TYPE, + DICT_STR_STR_TYPE, + DICT_ANY_ANY_TYPE, + INT_OR_STR_TYPE, + sup, + assignable_to, + is_upper_bound, + type_to_kcl_type_annotation_str, +) +from .type_parser import parse_type_str +from .type_convension import type_convert + +__all__ = [ + "Scope", + "ProgramScope", + "PackageScope", + "BUILTIN_SCOPE", + "PLUGIN_SCOPE_MAPPING", + "ResolveProgram", + "CheckConfig", + "Type", + "VOID_TYPE", + "NONE_TYPE", + "INT_TYPE", + "FLOAT_TYPE", + "STR_TYPE", + "BOOL_TYPE", + "ANY_TYPE", + "TRUE_LIT_TYPE", + "FALSE_LIT_TYPE", + "DICT_STR_ANY_TYPE", + "DICT_STR_STR_TYPE", + "DICT_ANY_ANY_TYPE", + "INT_OR_STR_TYPE", + "sup", + "assignable_to", + "is_upper_bound", + "type_to_kcl_type_annotation_str", + "parse_type_str", + "type_convert", +] diff --git a/internal/kclvm_py/kcl/types/calculation.py b/internal/kclvm_py/kcl/types/calculation.py new file mode 100644 index 000000000..3330c1ffb --- /dev/null +++ b/internal/kclvm_py/kcl/types/calculation.py @@ -0,0 +1,344 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +from typing import Union, List, Optional + +import kclvm.kcl.error as kcl_error +import kclvm.kcl.ast as ast +import kclvm.api.object as objpkg + +from .type import ( + Type, + ANY_TYPE, + INT_TYPE, + FLOAT_TYPE, + STR_TYPE, + BOOL_TYPE, + FALSE_LIT_TYPE, + NONE_TYPE, + NUMBER_TYPE_KINDS, + ITERABLE_KINDS, + STR_KINDS, + INT_KINDS, + BOOL_KINDS, + BUILTIN_KINDS, + sup, + has_any_type, + is_upper_bound, + infer_to_variable_type, + literal_union_type_to_variable_type, + is_kind_type_or_kind_union_type, +) + + +ZERO_LIT_TYPES: List[Type] = [ + objpkg.KCLIntLitTypeObject(0), + objpkg.KCLFloatLitTypeObject(0.0), + FALSE_LIT_TYPE, +] + + +def binary( + t1: Type, + t2: Type, + op: Union[ast.BinOp, ast.AugOp], + filename: Optional[str] = None, + line: Optional[int] = None, + column: Optional[int] = None, +) -> Type: + """Binary operator calculation table. + + Arithmetic (int or float; result has type float unless both operands have type int) + number + number # addition + number - number # subtraction + number * number # multiplication + number / number # real division (result is always a float) + number // number # floored division + number % number # remainder of floored division + number ^ number # bitwise XOR + number << number # bitwise left shift + number >> number # bitwise right shift + + Concatenation + string + string + list + list + + Repetition (string/list) + int * sequence + sequence * int + + Union + int | int + list | list + dict | dict + schema | schema + schema | dict + + Add: number + number, str + str, list + list + Sub: number - number + Mul: number * number, number * list, list * number + Div: number / number + FloorDiv: number // number + Mod: number % number + Pow: number ** number + LShift: int >> int + RShift: int << int + BitOr: int | int, list | list, dict | dict, schema | schema, schema | dict + BitXOr: int ^ int + BitAdd int & int + + And: any_type and any_type -> bool + Or: any_type1 or any_type1 -> sup([any_type1, any_type2]) + """ + + def number_binary() -> Type: + return ( + FLOAT_TYPE + if ( + t1.type_kind() + in [objpkg.KCLTypeKind.FloatKind, objpkg.KCLTypeKind.FloatLitKind] + or t2.type_kind() + in [objpkg.KCLTypeKind.FloatKind, objpkg.KCLTypeKind.FloatLitKind] + ) + else INT_TYPE + ) + + if has_any_type([t1, t2]): + return ANY_TYPE + + t1 = literal_union_type_to_variable_type(t1) + t2 = literal_union_type_to_variable_type(t2) + + if op == ast.BinOp.Add or op == ast.AugOp.Add: + if t1.type_kind() in NUMBER_TYPE_KINDS and t2.type_kind() in NUMBER_TYPE_KINDS: + return number_binary() + if t1.type_kind() in STR_KINDS and t2.type_kind() in STR_KINDS: + return STR_TYPE + if isinstance(t1, objpkg.KCLListTypeObject) and isinstance( + t2, objpkg.KCLListTypeObject + ): + return objpkg.KCLListTypeObject(item_type=sup([t1.item_type, t2.item_type])) + elif op == ast.BinOp.Mul or op == ast.AugOp.Mul: + if t1.type_kind() in NUMBER_TYPE_KINDS and t2.type_kind() in NUMBER_TYPE_KINDS: + return number_binary() + if t1.type_kind() in INT_KINDS and ( + is_kind_type_or_kind_union_type( + t2, STR_KINDS + NUMBER_TYPE_KINDS + [objpkg.KCLTypeKind.ListKind] + ) + ): + return t2 + if ( + is_kind_type_or_kind_union_type( + t1, STR_KINDS + NUMBER_TYPE_KINDS + [objpkg.KCLTypeKind.ListKind] + ) + ) and t2.type_kind() in INT_KINDS: + return t1 + elif op == ast.BinOp.Sub or op == ast.AugOp.Sub: + if t1.type_kind() in NUMBER_TYPE_KINDS and t2.type_kind() in NUMBER_TYPE_KINDS: + return number_binary() + elif op == ast.BinOp.Div or op == ast.AugOp.Div: + if t1.type_kind() in NUMBER_TYPE_KINDS and t2.type_kind() in NUMBER_TYPE_KINDS: + if t2 in ZERO_LIT_TYPES: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=line, col_no=column + ) + ], + arg_msg="integer division or modulo by zero", + ) + return number_binary() + elif op == ast.BinOp.Mod or op == ast.AugOp.Mod: + if t1.type_kind() in NUMBER_TYPE_KINDS and t2.type_kind() in NUMBER_TYPE_KINDS: + if t2 in ZERO_LIT_TYPES: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=line, col_no=column + ) + ], + arg_msg="integer division or modulo by zero", + ) + return INT_TYPE + elif op == ast.BinOp.FloorDiv or op == ast.AugOp.FloorDiv: + if t1.type_kind() in NUMBER_TYPE_KINDS and t2.type_kind() in NUMBER_TYPE_KINDS: + if t2 in ZERO_LIT_TYPES: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=line, col_no=column + ) + ], + arg_msg="integer division or modulo by zero", + ) + return number_binary() + elif op == ast.BinOp.Pow or op == ast.AugOp.Pow: + if t1.type_kind() in NUMBER_TYPE_KINDS and t2.type_kind() in NUMBER_TYPE_KINDS: + return number_binary() + elif op == ast.BinOp.BitOr or op == ast.AugOp.BitOr: + if t1.type_kind() in INT_KINDS and t2.type_kind() in INT_KINDS: + return INT_TYPE + if t2 == NONE_TYPE: + return t1 + if t1 == NONE_TYPE: + return t2 + if isinstance(t1, objpkg.KCLListTypeObject) and isinstance( + t2, objpkg.KCLListTypeObject + ): + return objpkg.KCLListTypeObject(item_type=sup([t1.item_type, t2.item_type])) + if isinstance(t1, objpkg.KCLDictTypeObject) and isinstance( + t2, objpkg.KCLDictTypeObject + ): + return objpkg.KCLDictTypeObject( + key_type=sup([t1.key_type, t2.key_type]), + value_type=sup([t1.value_type, t2.value_type]), + ) + if isinstance(t1, objpkg.KCLSchemaTypeObject) and isinstance( + t2, (objpkg.KCLSchemaTypeObject, objpkg.KCLDictTypeObject) + ): + return t1 + elif op == ast.BinOp.LShift or op == ast.AugOp.LShift: + if t1.type_kind() in INT_KINDS and t2.type_kind() in INT_KINDS: + return INT_TYPE + elif op == ast.BinOp.RShift or op == ast.AugOp.RShift: + if t1.type_kind() in INT_KINDS and t2.type_kind() in INT_KINDS: + return INT_TYPE + elif op == ast.BinOp.BitXor or op == ast.AugOp.BitXor: + if t1.type_kind() in INT_KINDS and t2.type_kind() in INT_KINDS: + return INT_TYPE + elif op == ast.BinOp.BitAnd or op == ast.AugOp.BitAnd: + if t1.type_kind() in INT_KINDS and t2.type_kind() in INT_KINDS: + return INT_TYPE + elif op == ast.BinOp.And: + return BOOL_TYPE + elif op == ast.BinOp.Or: + return sup([t1, t2]) + elif op == ast.BinOp.As: + if not is_upper_bound(infer_to_variable_type(t1), t2): + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg(filename=filename, line_no=line, col_no=column) + ], + arg_msg=f"Conversion of type '{t1.type_str()}' to type '{t2.type_str()}' " + "may be a mistake because neither type sufficiently overlaps with the other", + ) + return t2.schema_type if isinstance(t2, objpkg.KCLSchemaDefTypeObject) else t2 + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg(filename=filename, line_no=line, col_no=column) + ], + arg_msg=f"unsupported operand type(s) for {ast.OPERATOR_VALUE_MAP.get(op)}: '{t1.type_str()}' and '{t2.type_str()}'", + ) + + +def compare( + t1: Type, + t2: Type, + op: ast.CmpOp, + filename: Optional[str] = None, + line: Optional[int] = None, + column: Optional[int] = None, +) -> Type: + """Compare operator calculation table + + bool # False < True False < True + int # mathematical 1 < 2 + float # as defined by IEEE 754 1.0 < 2.0 + string # lexicographical "1" < 2 + list # lexicographical [1] == [2] + iterable # 1 in [1, 2, 3], "s" in "ss", "key" in Schema + """ + + t1 = literal_union_type_to_variable_type(t1) + t2 = literal_union_type_to_variable_type(t2) + + if has_any_type([t1, t2]): + return ANY_TYPE + if ( + is_kind_type_or_kind_union_type(t1, NUMBER_TYPE_KINDS) + and is_kind_type_or_kind_union_type(t2, NUMBER_TYPE_KINDS) + and op not in [ast.CmpOp.In, ast.CmpOp.NotIn] + ): + return BOOL_TYPE + if ( + is_kind_type_or_kind_union_type(t1, STR_KINDS) + and is_kind_type_or_kind_union_type(t2, STR_KINDS) + and op not in [ast.CmpOp.Eq, ast.CmpOp.NotEq] + ): + return BOOL_TYPE + if ( + is_kind_type_or_kind_union_type(t1, BUILTIN_KINDS) + and is_kind_type_or_kind_union_type(t2, BUILTIN_KINDS) + and op in [ast.CmpOp.Eq, ast.CmpOp.NotEq] + ): + return BOOL_TYPE + if isinstance(t1, objpkg.KCLListTypeObject) and isinstance( + t2, objpkg.KCLListTypeObject + ): + return BOOL_TYPE + if ( + isinstance(t1, (objpkg.KCLDictTypeObject, objpkg.KCLSchemaTypeObject)) + and isinstance(t2, (objpkg.KCLDictTypeObject, objpkg.KCLSchemaTypeObject)) + and op in [ast.CmpOp.Eq, ast.CmpOp.NotEq] + ): + return BOOL_TYPE + if op in [ast.CmpOp.In, ast.CmpOp.NotIn] and t2.type_kind() in ITERABLE_KINDS: + return BOOL_TYPE + if (t1 == NONE_TYPE or t2 == NONE_TYPE) and op in [ + ast.CmpOp.Eq, + ast.CmpOp.NotEq, + ast.CmpOp.Is, + ast.CmpOp.IsNot, + ast.CmpOp.Not, + ]: + return BOOL_TYPE + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg(filename=filename, line_no=line, col_no=column) + ], + arg_msg=f"unsupported operand type(s) for {ast.OPERATOR_VALUE_MAP.get(op)}: '{t1.type_str()}' and '{t2.type_str()}'", + ) + + +def unary( + t: Type, + op: ast.UnaryOp, + filename: Optional[str] = None, + line: Optional[int] = None, + column: Optional[int] = None, +) -> Type: + """Unary operator calculation table + + + number unary positive (int, float) + - number unary negation (int, float) + ~ number unary bitwise inversion (int) + not x logical negation (any type) + """ + if has_any_type([t]): + return ANY_TYPE + + t = literal_union_type_to_variable_type(t) + + if op == ast.UnaryOp.UAdd: + if t.type_kind() in NUMBER_TYPE_KINDS: + return t + if op == ast.UnaryOp.USub: + if t.type_kind() in NUMBER_TYPE_KINDS: + return t + if op == ast.UnaryOp.Invert: + if t.type_kind() in (INT_KINDS + BOOL_KINDS): + return INT_TYPE + if op == ast.UnaryOp.Not: + return BOOL_TYPE + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg(filename=filename, line_no=line, col_no=column) + ], + arg_msg=f"bad operand type for unary {ast.OPERATOR_VALUE_MAP.get(op)}: '{t.type_str()}'", + ) diff --git a/internal/kclvm_py/kcl/types/checker.py b/internal/kclvm_py/kcl/types/checker.py new file mode 100644 index 000000000..357ee5738 --- /dev/null +++ b/internal/kclvm_py/kcl/types/checker.py @@ -0,0 +1,2618 @@ +"""The `checker` file mainly contains the function `ResolveProgram` +based on the AST walker, which is used to run semantic checking and +type checking of KCL. + +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" + +import os +import pathlib +import typing +from enum import IntEnum +from typing import cast, Union, List, Dict, Tuple, Optional +from dataclasses import dataclass + +import kclvm.api.object as objpkg +import kclvm.api.object.internal as internal +import kclvm.kcl.error as kcl_error +import kclvm.kcl.info as kcl_info +import kclvm.kcl.ast as ast +import kclvm.compiler.astutil as astutil +import kclvm.compiler.extension.builtin as builtin +import kclvm.compiler.extension.plugin as plugin + +from kclvm.compiler.build.utils import units +from kclvm.kcl.types.scope import ( + Scope, + PackageScope, + ScopeObject, + ProgramScope, + BUILTIN_SCOPE, + DECORATOR_SCOPE, + PLUGIN_SCOPE_MAPPING, + MODULE_SCOPE_MAPPING, + SCHEMA_TYPE_MEMBER_SCOPE, + STR_TYPE_MEMBER_SCOPE, +) +from kclvm.kcl.types.type import ( + Type, + ANY_TYPE, + VOID_TYPE, + NONE_TYPE, + STR_TYPE, + BOOL_TYPE, + INT_TYPE, + TRUE_LIT_TYPE, + FALSE_LIT_TYPE, + DICT_STR_ANY_TYPE, + RESERVED_TYPE_IDENTIFIERS, + KEY_KINDS, + sup, + assignable_to, + is_upper_bound, + infer_to_variable_type, + is_kind_type_or_kind_union_type, + type_to_kcl_type_annotation_str, +) +from kclvm.kcl.types.type_parser import parse_type_str +from kclvm.kcl.types.calculation import binary, compare, unary +from kclvm.kcl.types.walker import WalkType +from kclvm.internal.util import check_utils + +ITER_TYPES = ( + objpkg.KCLAnyTypeObject, + objpkg.KCLListTypeObject, + objpkg.KCLStringTypeObject, + objpkg.KCLStringLitTypeObject, + objpkg.KCLDictTypeObject, + objpkg.KCLSchemaTypeObject, +) +MAX_SCOPE_SCAN_COUNT = 3 +VALID_FORMAT_SPEC_SET = {"#json", "#yaml"} +TYPE_KIND_BUILTIN_FUNCTION_MAPPING = { + objpkg.KCLTypeKind.BoolKind: "bool", + objpkg.KCLTypeKind.IntKind: "int", + objpkg.KCLTypeKind.FloatKind: "float", + objpkg.KCLTypeKind.StrKind: "str", + objpkg.KCLTypeKind.ListKind: "list", + objpkg.KCLTypeKind.DictKind: "dict", +} + + +class SwitchConfigExprContextState(IntEnum): + SWITCH_CONFIG_EXPR_ONCE = 1 + KEEP_CONFIG_EXPR_UNCHANGED = 0 + + +@dataclass +class CheckConfig: + raise_err: bool = True + config_attr_auto_fix: bool = False + + +class BaseTypeChecker(ast.TreeWalker): + def __init__(self, program: ast.Program, config: CheckConfig = CheckConfig()): + # The type checker config + self.config: CheckConfig = config + # The AST program reference + self.program: ast.Program = program + # Current package path, default is the main package path + self.pkgpath = ast.Program.MAIN_PKGPATH + # Current filename + self.filename = "" + # The scope mapping between `pkgpath` and `scope` + self.scope_map: Dict[str, Scope] = {} + # Current scope + self.scope: Optional[Scope] = None + # Current schema type + self.in_schema_type: Optional[Type] = None + # Current schema expr type stack + self.config_expr_context: List[Optional[ScopeObject]] = [] + # Check error list + self.errs: List[kcl_error.KCLException] = [] + # Local vars + self._local_vars: List[str] = [] + # Schema type reference graph + self.schema_reference: objpkg.SchemaTypeRefGraph = objpkg.SchemaTypeRefGraph() + # Schema types mapping + self.schema_mapping: Dict[str, Type] = {} + # Package path import reference graph + self.import_reference: objpkg.RefGraph = objpkg.RefGraph() + # In lambda expression level + self._is_in_lambda_expr: List[bool] = [False] + # Reset scope status + self.reset_scopes() + # Set __main_ package context + self.change_package_context(self.pkgpath, self.filename) + + @staticmethod + def reset_scopes(): + BUILTIN_SCOPE.children = [] + + def new_config_expr_context_item( + self, + name: str = None, + type_obj: Optional[Type] = None, + node: Optional[ast.AST] = None, + ) -> ScopeObject: + return ScopeObject( + name=name, + node=node, + type=type_obj, + pos=ast.Position(filename=node.filename, line=node.line, column=node.column) + if node + else None, + end=ast.Position( + filename=node.filename, line=node.end_line, column=node.end_column + ) + if node + else None, + ) + + def find_schema_attr_obj_from_schema_expr_stack( + self, key_name: str + ) -> Optional[ScopeObject]: + """Finds the items needed to switch the context by name 'key_name' + + At present, only when the top item of the stack is 'KCLSchemaTypeObject' or 'KCLDictTypeObject', + it will return the next item (the attribute named 'key_name' in 'KCLSchemaTypeObject' + or the value of 'key_name' in 'KCLDictTypeObject') needed to be pushed. + If the top item of the stack is not 'KCLSchemaTypeObject' or 'KCLDictTypeObject', + it will return 'None'. + + Args: + key_name: The name of the item needed to be pushed onto the 'config_expr_context' stack + + Returns: + The item needed to be pushed onto the 'config_expr_context' stack + + """ + if ( + not key_name + or not self.config_expr_context + or not self.config_expr_context[-1] + ): + return None + if not isinstance(self.config_expr_context[-1], ScopeObject): + check_utils.alert_internal_bug() + + if isinstance(self.config_expr_context[-1].type, objpkg.KCLSchemaTypeObject): + schema_type = typing.cast( + objpkg.KCLSchemaTypeObject, self.config_expr_context[-1].type + ) + attr_type_obj = schema_type.get_obj_of_attr(key_name) + if not attr_type_obj and schema_type.index_signature: + ctx_obj = self.new_config_expr_context_item( + name=key_name, + type_obj=schema_type.index_signature.value_kcl_type, + node=schema_type.index_signature.node, + ) + elif attr_type_obj: + ctx_obj = self.new_config_expr_context_item( + name=key_name, + type_obj=attr_type_obj.attr_type, + node=attr_type_obj.attr_node, + ) + else: + return None + return ctx_obj + if isinstance(self.config_expr_context[-1].type, objpkg.KCLDictTypeObject): + dict_type = typing.cast( + objpkg.KCLDictTypeObject, self.config_expr_context[-1].type + ) + ctx_obj = self.new_config_expr_context_item( + name=key_name, + type_obj=dict_type.value_type, + node=self.config_expr_context[-1].node, + ) + return ctx_obj + return None + + def switch_config_expr_context_by_key(self, key: ast.Expr) -> int: + """Switch the context in 'config_expr_context' stack by AST nodes 'Identifier', 'Subscript' or 'Literal' + + Args: + key: AST nodes 'Identifier', 'Subscript' or 'Literal' + + Returns: + push stack times + + """ + names = [] + if not key: + return SwitchConfigExprContextState.KEEP_CONFIG_EXPR_UNCHANGED + if isinstance(key, ast.Identifier): + names = key.names + elif isinstance(key, ast.Subscript): + if isinstance(key.value, ast.Identifier) and isinstance( + key.index, ast.NumberLit + ): + names = key.value.names + elif isinstance(key, ast.Literal): + names = [key.value] + else: + return SwitchConfigExprContextState.KEEP_CONFIG_EXPR_UNCHANGED + return self.switch_config_expr_context_by_names(names) + + def switch_config_expr_context_by_names( + self, names: List[Union[str, float, int]] + ) -> int: + """Switch the context in 'config_expr_context' stack by names + + Traverse all name in 'names', find the next item that needs to be pushed into the stack, + according to name and the top context of the stack, and push the item into the stack. + + Args: + names: A list of string containing the names of items to be pushed + + Returns: + push stack times + + """ + stack_depth = 0 + for name in names: + stack_depth += self.switch_config_expr_context_by_name(name) + return stack_depth + + def switch_config_expr_context_by_name(self, name: str) -> int: + """Switch the context in 'config_expr_context' stack by name + + find the next item that needs to be pushed into the stack, + according to name and the top context of the stack, and push the item into the stack. + + Args: + name: the name of item to be pushed + + Returns: + push stack times + + """ + ctx_obj = self.find_schema_attr_obj_from_schema_expr_stack(name) + return self.switch_config_expr_context(ctx_obj) + + def switch_config_expr_context( + self, config_ctx_obj: ScopeObject + ) -> SwitchConfigExprContextState: + """Push method for the 'config_expr_context' stack + + Args: + config_ctx_obj: the item needed to be pushed + + Returns: + push stack times + + """ + self.config_expr_context.append(config_ctx_obj) + return SwitchConfigExprContextState.SWITCH_CONFIG_EXPR_ONCE + + def restore_config_expr_context(self) -> Optional[objpkg.KCLSchemaTypeObject]: + """Pop method for the 'config_expr_context' stack + + Returns: + the item poped from stack + + """ + return self.config_expr_context.pop(-1) if self.config_expr_context else None + + def clear_config_expr_context(self, stack_depth: int = 0, clear_all: bool = False): + """Pop_All method for the 'config_expr_context' stack + + Args: + stack_depth: 'stack_depth' is the number of stacks that need to be popped + clear_all: 'clear_all' is True to clear all the items of the stack + + """ + if clear_all: + self.config_expr_context.clear() + else: + while stack_depth > 0: + stack_depth -= 1 + self.restore_config_expr_context() + + def check_config_expr_by_key_name( + self, name: str, key: ast.AST, check_rules: List[typing.Callable] + ): + """Check whether the key of config expr meets the constraints of schema attributes such as final, defined. + + Args: + name: the name of key + key: the ast node of key + check_rules: the constraints, such as 'check_defined' + + """ + if name and self.config_expr_context and self.config_expr_context[-1]: + self.check_attr( + attr=name, + node=key, + obj=self.config_expr_context[-1].type, + check_rules=check_rules, + ) + + def check_config_entry( + self, key: ast.Expr, value: ast.Expr, check_rules: List[typing.Callable] + ) -> typing.Optional[ast.Expr]: + """Check the key-value in 'ConfigExpr', such as check_defined and check_type + + Notes: + If the top item of the 'config_expr_context' stack is 'None', the check will be skipped. + + Args: + key: the key of 'ConfigExpr'. + value: the value of 'ConfigExpr'. + check_rules: Some checks on the key individually,such as check_defined. + + """ + if not (key and self.config_expr_context and self.config_expr_context[-1]): + return + names: List[Union[str, float, int]] = [] + has_index = False + + def _check() -> typing.Optional[ast.Expr]: + stack_depth = 0 + fix_call_expr = None + for name in names: + self.check_config_expr_by_key_name(name, key, check_rules) + stack_depth += self.switch_config_expr_context_by_name(name) + value_tpe = self.expr(value) + if len(names) > 1: + for _ in range(len(names) - 1): + value_tpe = objpkg.KCLDictTypeObject( + key_type=STR_TYPE, value_type=value_tpe + ) + if has_index: + value_tpe = ( + objpkg.KCLListTypeObject(value_tpe) + if value_tpe and isinstance(value_tpe, objpkg.KCLBaseTypeObject) + else None + ) + if self.config.config_attr_auto_fix: + try: + _check_type(value_tpe) + # Type check error and fix the attr type with the builtin functions + except Exception: + expected_attr_type = self.config_expr_context[-1].type.type_kind() + if expected_attr_type in TYPE_KIND_BUILTIN_FUNCTION_MAPPING: + func_name = TYPE_KIND_BUILTIN_FUNCTION_MAPPING[ + expected_attr_type + ] + fix_call_expr = ast.CallExpr(line=key.line, column=key.column) + fix_call_expr.func = ast.ASTFactory.get_ast_identifier( + func_name + ) + fix_call_expr.args = [value] + else: + _check_type(value_tpe) + self.clear_config_expr_context(stack_depth=stack_depth) + return fix_call_expr + + def _check_type(value_tpe: Type): + if value_tpe and self.config_expr_context and self.config_expr_context[-1]: + self.must_assignable_to( + node=key, + tpe=value_tpe or ANY_TYPE, + expected_type=self.config_expr_context[-1].type, + expected_node=self.config_expr_context[-1].node, + ) + + if isinstance(key, ast.Identifier): + names = key.names + elif isinstance(key, ast.Subscript): + if isinstance(key.value, ast.Identifier) and isinstance( + key.index, ast.NumberLit + ): + names = key.value.names + has_index = True + else: + return + elif isinstance(key, ast.Literal): + names = [key.value] + else: + return + return _check() + + def get_node_name(self, t: ast.AST): + """Get the ast.AST node name""" + assert isinstance(t, ast.AST), str(type(t)) + return t.type + + def generic_walk(self, t: ast.AST): + """Called if no explicit walker function exists for a node.""" + raise Exception( + f"The function walk_{t.type} is not defined in the type checker." + ) + + def raise_err( + self, + nodes: [ast.AST], + category: kcl_error.ErrType = kcl_error.ErrType.CompileError_TYPE, + msg: str = "", + file_msgs=None, + file_levels=None, + ): + """Raise a KCL compile error""" + err = kcl_error.get_exception( + err_type=category, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=node.filename, + line_no=node.line, + col_no=node.column, + arg_msg=(file_msgs[i] if i < len(file_msgs) else file_msgs[-1]) + if file_msgs + else None, + err_level=( + file_levels[i] if i < len(file_levels) else file_levels[-1] + ) + if file_levels + else None, + ) + for i, node in enumerate(nodes) + if node + ], + arg_msg=msg, + ) + if self.config.raise_err: + raise err + self.errs.append(err) + + def change_package_context(self, pkgpath: str, filename: str): + """Change the package scope context with pkgpath and filename""" + if not pkgpath: + return + if pkgpath not in self.scope_map: + self.scope_map[pkgpath] = PackageScope( + parent=BUILTIN_SCOPE, + file_begin_position_map={ + module.filename: ast.Position( + filename=module.filename, + line=module.line, + column=module.column, + ) + for module in self.program.pkgs[pkgpath] + }, + file_end_position_map={ + module.filename: ast.Position( + filename=module.filename, + line=module.end_line, + column=module.end_column, + ) + for module in self.program.pkgs[pkgpath] + }, + ) + BUILTIN_SCOPE.children.append(self.scope_map[pkgpath]) + self.pkgpath = pkgpath + self.filename = filename + self.scope = self.scope_map[pkgpath] + + def check(self, pkgpath: str = ast.Program.MAIN_PKGPATH) -> ProgramScope: + """The check main function""" + self.check_import(pkgpath) + self.init_global_types() + for module in self.program.pkgs[pkgpath]: + self.filename = module.filename + self.walk(module) + self.scope_map[pkgpath] = self.scope + return ProgramScope( + scope_map=self.scope_map, + schema_reference=self.schema_reference, + ) + + def check_import(self, pkgpath: str = ast.Program.MAIN_PKGPATH): + """The import check function""" + self.pkgpath = pkgpath + self.change_package_context(pkgpath, self.filename) + self.init_import_list() + + def build_rule_protocol_type( + self, t: ast.RuleStmt + ) -> Optional[objpkg.KCLSchemaDefTypeObject]: + if t.for_host_name and isinstance(t.for_host_name, ast.Identifier): + if len(t.for_host_name.names) > 2: + self.raise_err( + category=kcl_error.ErrType.MultiInheritError_TYPE, + nodes=[t.for_host_name], + msg=kcl_error.MULTI_INHERIT_MSG.format(t.name), + ) + return None + tpe = self.expr(t.for_host_name) + if not isinstance(tpe, objpkg.KCLSchemaDefTypeObject): + self.raise_err( + category=kcl_error.ErrType.IllegalInheritError_TYPE, + nodes=[t], + msg=f"invalid schema inherit object type '{tpe.type_str()}'", + ) + return None + return cast(objpkg.KCLSchemaDefTypeObject, tpe) + return None + + def build_schema_protocol_type( + self, t: ast.SchemaStmt + ) -> Optional[objpkg.KCLSchemaDefTypeObject]: + # Mixin type check with protocol + if not t.is_mixin and t.for_host_name: + self.raise_err( + category=kcl_error.ErrType.IllegalInheritError_TYPE, + nodes=[t.for_host_name], + msg="only schema mixin can inherit from protocol", + ) + return None + if t.is_mixin and t.for_host_name: + if len(t.for_host_name.names) > 2: + self.raise_err( + category=kcl_error.ErrType.MultiInheritError_TYPE, + nodes=[t.for_host_name], + msg=kcl_error.MULTI_INHERIT_MSG.format(t.name), + ) + return None + tpe = self.expr(t.for_host_name) + if not isinstance(tpe, objpkg.KCLSchemaDefTypeObject): + self.raise_err( + category=kcl_error.ErrType.IllegalInheritError_TYPE, + nodes=[t], + msg=f"invalid schema inherit object type '{tpe.type_str()}'", + ) + return None + return cast(objpkg.KCLSchemaDefTypeObject, tpe) + return None + + def build_schema_parent_type( + self, t: ast.SchemaStmt + ) -> Optional[objpkg.KCLSchemaDefTypeObject]: + if t.parent_name: + if len(t.parent_name.names) > 2: + self.raise_err( + category=kcl_error.ErrType.MultiInheritError_TYPE, + nodes=[t.parent_name], + msg=kcl_error.MULTI_INHERIT_MSG.format(t.name), + ) + return None + schema_parent_type = self.expr(t.parent_name) + if not isinstance(schema_parent_type, objpkg.KCLSchemaDefTypeObject): + self.raise_err( + category=kcl_error.ErrType.IllegalInheritError_TYPE, + nodes=[t], + msg=f"illegal schema inherit object type '{schema_parent_type.type_str()}'", + ) + return None + return schema_parent_type + return None + + def build_schema_type( + self, + t: ast.SchemaStmt, + base_def: objpkg.KCLSchemaDefTypeObject = None, + protocol_def: objpkg.KCLSchemaDefTypeObject = None, + should_add_schema_ref: bool = False, + ) -> objpkg.KCLSchemaDefTypeObject: + """Build a schema type and check""" + # Base schema: get the parent type obj of the schema if exist + if t.name in RESERVED_TYPE_IDENTIFIERS: + self.raise_err( + [t], + kcl_error.ErrType.IllegalInheritError_TYPE, + "schema name '{}' cannot be the same as the built-in types ({})".format( + t.name, ", ".join(RESERVED_TYPE_IDENTIFIERS) + ), + ) + if t.is_protocol and not t.has_only_attribute_definitions(): + self.raise_err( + [t], + kcl_error.ErrType.CompileError_TYPE, + msg="a protocol is only allowed to define attributes in it", + ) + base = base_def.schema_type if base_def else None + protocol = protocol_def.schema_type if protocol_def else None + parent_name_str = t.parent_name.get_name() if t.parent_name else "" + if parent_name_str.endswith("Mixin"): + self.raise_err( + [t], + kcl_error.ErrType.IllegalInheritError_TYPE, + f"mixin inheritance {parent_name_str} is prohibited", + ) + schema_attr_names = t.GetLeftIdentifierList() + # Index signature + index_sign_name = t.GetIndexSignatureAttrName() + index_sign_obj = None + if index_sign_name and index_sign_name in schema_attr_names: + self.raise_err( + nodes=[t.index_signature], + category=kcl_error.ErrType.IndexSignatureError_TYPE, + msg=f"index signature attribute name '{index_sign_name}' " + "cannot have the same name as schema attributes", + ) + if t.index_signature: + key_kcl_type = self.parse_type_str_with_scope( + t.index_signature.key_type, t.index_signature + ) + if not is_kind_type_or_kind_union_type(key_kcl_type, KEY_KINDS): + self.raise_err( + nodes=[t.index_signature], + category=kcl_error.ErrType.IndexSignatureError_TYPE, + msg=f"invalid index signature key type: '{key_kcl_type.type_str()}'", + ) + value_kcl_type = self.parse_type_str_with_scope( + t.index_signature.value_type, t.index_signature + ) + index_sign_obj = objpkg.KCLSchemaIndexSignatureObject( + key_name=t.index_signature.key_name, + key_type=t.index_signature.key_type, + value_type=t.index_signature.value_type, + any_other=t.index_signature.any_other, + key_kcl_type=key_kcl_type, + value_kcl_type=value_kcl_type, + node=t.index_signature, + ) + t.index_signature.key_type = type_to_kcl_type_annotation_str(key_kcl_type) + t.index_signature.value_type = type_to_kcl_type_annotation_str( + value_kcl_type + ) + + # Schema attr type map + attr_obj_map = { + objpkg.SCHEMA_SETTINGS_ATTR_NAME: objpkg.KCLSchemaAttrObject( + attr_type=DICT_STR_ANY_TYPE + ) + } + for attr in t.GetAttrList(): + name = ( + attr.name + if isinstance(attr, ast.SchemaAttr) + else attr.target.get_first_name() + ) + if isinstance(attr, ast.SchemaAttr): + tpe = self.parse_type_str_with_scope(attr.type_str, attr) + attr.type_str = type_to_kcl_type_annotation_str(tpe) + else: + tpe = self.parse_type_str_with_scope( + attr.value.name.get_first_name(), attr + ) + attr.value.name.names = [type_to_kcl_type_annotation_str(tpe)] + base_tpe = (base.get_type_of_attr(name) if base else None) or ANY_TYPE + if name not in attr_obj_map: + existed_attr = base.get_obj_of_attr(name) if base else None + attr_obj_map[name] = objpkg.KCLSchemaAttrObject( + is_optional=existed_attr.is_optional + if existed_attr + else isinstance(attr, ast.SchemaAttr) and attr.is_optional, + is_final=False, + has_default=( + (isinstance(attr, ast.SchemaAttr) and attr.value is not None) + or (existed_attr and existed_attr.has_default) + ), + attr_type=tpe, + attr_node=attr, + ) + if not is_upper_bound( + attr_obj_map[name].attr_type, tpe + ) or not is_upper_bound(base_tpe, tpe): + self.raise_err( + [attr], + kcl_error.ErrType.TypeError_Compile_TYPE, + f"can't change schema field type of '{name}'", + ) + if ( + isinstance(attr, ast.SchemaAttr) + and attr.is_optional + and not attr_obj_map[name].is_optional + ): + self.raise_err( + [attr], + msg=f"can't change the required schema attribute of '{name}' to optional", + ) + if ( + index_sign_obj + and not index_sign_obj.any_other + and not is_upper_bound(index_sign_obj.value_kcl_type, tpe) + ): + self.raise_err( + nodes=[attr], + category=kcl_error.ErrType.IndexSignatureError_TYPE, + msg=f"the type '{tpe.type_str()}' of schema attribute '{name}' " + f"does not meet the index signature definition {index_sign_obj.def_str()}", + ) + + mixin_name_list = [] + for mixin in t.mixins or []: + mixin_names = mixin.names + if mixin.pkgpath: + mixin_names[0] = f"@{mixin.pkgpath}" + if not mixin_names[-1].endswith("Mixin"): + self.raise_err( + [mixin], + kcl_error.ErrType.MixinNamingError_TYPE, + f"a valid mixin name should end with 'Mixin', got '{mixin_names[-1]}'", + ) + mixin_name_list.append(mixin.get_name()) + mixin_type = self.expr(mixin) + if mixin_type == ANY_TYPE: + continue + if not isinstance(mixin_type, objpkg.KCLSchemaDefTypeObject): + self.raise_err( + [mixin], + kcl_error.ErrType.CompileError_TYPE, + msg=f"illegal schema mixin object type '{mixin_type.type_str()}'", + ) + else: + for name, attr_obj in mixin_type.schema_type.attr_obj_map.items(): + if name not in attr_obj_map: + attr_obj_map[name] = attr_obj + + params: List[objpkg.Parameter] = [] + # Schema arguments + if t.args: + for i, arg in enumerate(t.args.args): + name = arg.get_name() + if name in schema_attr_names: + self.raise_err( + [arg], + msg=f"Unexpected parameter name '{name}' " + "with the same name as the schema attribute", + ) + type_annotation = t.args.GetArgType(i) + type_node = self.parse_type_str_with_scope( + type_annotation, t.args.args[i] + ) + default = t.args.GetArgDefault(i) + params.append( + objpkg.Parameter( + name=name, + value=objpkg.to_kcl_obj(default), + type_annotation=type_annotation, + type=type_node, + ) + ) + t.args.SetArgType(i, type_to_kcl_type_annotation_str(type_node)) + runtime_type = objpkg.KCLSchemaTypeObject.schema_runtime_type( + t.name, self.pkgpath + ) + if should_add_schema_ref and self.schema_reference.add_node_judge_cycle( + runtime_type, base.runtime_type if base else "" + ): + base_name = base.name if base else "" + self.raise_err( + [t], + kcl_error.ErrType.CycleInheritError_TYPE, + f"{t.name} and {base_name}", + ) + schema_type = objpkg.KCLSchemaTypeObject( + name=t.name, + is_mixin=t.is_mixin, + is_protocol=t.is_protocol, + pkgpath=self.pkgpath, + filename=self.filename, + doc=t.doc, + base=base, + protocol=protocol, + runtime_type=runtime_type, + mixins_names=mixin_name_list, + attr_list=schema_attr_names, + attr_obj_map=attr_obj_map, + node_ref=t, + settings={ + objpkg.SETTINGS_OUTPUT_KEY: objpkg.SETTINGS_OUTPUT_INLINE + if not kcl_info.isprivate_field(t.name) + else objpkg.SETTINGS_OUTPUT_IGNORE + }, + index_signature=index_sign_obj, + func=objpkg.KCLCompiledFunctionObject( + name=t.name, + params=params, + ), + ) + self.schema_mapping[runtime_type] = schema_type + return objpkg.KCLSchemaDefTypeObject(schema_type=schema_type) + + def build_rule_type( + self, + t: ast.RuleStmt, + protocol_def: objpkg.KCLSchemaDefTypeObject = None, + should_add_schema_ref: bool = False, + ) -> objpkg.KCLSchemaDefTypeObject: + """Build a schema type using the rule statement""" + if t.name in RESERVED_TYPE_IDENTIFIERS: + self.raise_err( + [t], + kcl_error.ErrType.IllegalInheritError_TYPE, + "rule name '{}' cannot be the same as the built-in types ({})".format( + t.name, ", ".join(RESERVED_TYPE_IDENTIFIERS) + ), + ) + protocol = protocol_def.schema_type if protocol_def else None + mixin_name_list = [] + for mixin in t.parent_rules or []: + mixin_names = mixin.names + if mixin.pkgpath: + mixin_names[0] = f"@{mixin.pkgpath}" + mixin_name_list.append(mixin.get_name()) + + params: List[objpkg.Parameter] = [] + # Schema arguments + if t.args: + for i, arg in enumerate(t.args.args): + type_annotation = t.args.GetArgType(i) + type_node = self.parse_type_str_with_scope( + type_annotation, t.args.args[i] + ) + default = t.args.GetArgDefault(i) + params.append( + objpkg.Parameter( + name=arg.names[0], + value=objpkg.to_kcl_obj(default), + type_annotation=type_annotation, + type=type_node, + ) + ) + t.args.SetArgType(i, type_to_kcl_type_annotation_str(type_node)) + runtime_type = objpkg.KCLSchemaTypeObject.schema_runtime_type( + t.name, self.pkgpath + ) + + return objpkg.KCLSchemaDefTypeObject( + schema_type=objpkg.KCLSchemaTypeObject( + name=t.name, + is_rule=True, + pkgpath=self.pkgpath, + filename=self.filename, + protocol=protocol, + runtime_type=runtime_type, + mixins_names=mixin_name_list, + attr_list=[], + attr_obj_map={}, + settings={ + objpkg.SETTINGS_OUTPUT_KEY: objpkg.SETTINGS_OUTPUT_INLINE + if not kcl_info.isprivate_field(t.name) + else objpkg.SETTINGS_OUTPUT_IGNORE + }, + func=objpkg.KCLCompiledFunctionObject( + name=t.name, + params=params, + ), + ) + ) + + @staticmethod + def is_builtin_or_plugin_module(path: str) -> bool: + """Whether is a builtin system module or a plugin module""" + if not path or not isinstance(path, str): + return False + return path in builtin.STANDARD_SYSTEM_MODULES or path.startswith( + plugin.PLUGIN_MODULE_NAME + ) + + def init_global_types(self): + """Init global types including top-level global variable types and schema types + TODO: optimize the function with twice scan + """ + # 1. Scan all schema type symbol + for module in self.program.pkgs[self.pkgpath]: + self.change_package_context(self.pkgpath, module.filename) + for stmt in module.GetSchemaAndRuleList(): + if stmt.name in self.scope.elems: + self.raise_err( + [stmt], + kcl_error.ErrType.UniqueKeyError_TYPE, + kcl_error.UNIQUE_KEY_MSG.format(stmt.name), + ) + continue + schema_type_obj = objpkg.KCLSchemaTypeObject( + name=stmt.name, + runtime_type=objpkg.KCLSchemaTypeObject.schema_runtime_type( + stmt.name, self.pkgpath + ), + filename=self.filename, + pkgpath=self.pkgpath, + ) + self.scope.elems[stmt.name] = ScopeObject( + name=stmt.name, + node=stmt, + type=objpkg.KCLSchemaDefTypeObject( + schema_type=schema_type_obj, + ), + pos=ast.Position( + filename=self.filename, + line=stmt.line, + column=stmt.column, + ), + end=ast.Position( + filename=self.filename, + line=stmt.end_line, + column=stmt.end_column, + ), + ) + # 2. Scan all variable type symbol + self.init_global_var_types() + # 3. Build all schema types + for i in range(MAX_SCOPE_SCAN_COUNT): + for k, o in self.scope.elems.items(): + if isinstance(o.node, ast.SchemaStmt): + self.filename = o.type.schema_type.filename + schema_parent_type = self.build_schema_parent_type( + self.scope.elems[k].node + ) + schema_protocol_type = self.build_schema_protocol_type( + self.scope.elems[k].node + ) + self.scope.elems[k].type = self.build_schema_type( + self.scope.elems[k].node, + schema_parent_type, + schema_protocol_type, + i == MAX_SCOPE_SCAN_COUNT - 1, + ) + self.scope.elems[k].type = cast( + objpkg.KCLSchemaDefTypeObject, self.scope.elems[k].type + ) + elif isinstance(o.node, ast.RuleStmt): + self.filename = o.type.schema_type.filename + schema_protocol_type = self.build_rule_protocol_type( + self.scope.elems[k].node + ) + self.scope.elems[k].type = self.build_rule_type( + self.scope.elems[k].node, + schema_protocol_type, + i == MAX_SCOPE_SCAN_COUNT - 1, + ) + self.scope.elems[k].type = cast( + objpkg.KCLSchemaDefTypeObject, self.scope.elems[k].type + ) + # 4. Build all variable types + self.init_global_var_types(False) + + def do_import_stmt_check(self, t: ast.ImportStmt): + """Do import check and store the module object into the map""" + pkgpath = f"@{t.path}" + for name in [pkgpath, t.pkg_name]: + if name in self.scope.elems: + self.scope.elems[name].type.imported_filenames.append(self.filename) + else: + module_object = objpkg.KCLModuleTypeObject( + pkgpath=t.path, + imported_filenames=[self.filename], + is_user_module=not self.is_builtin_or_plugin_module(t.path), + is_system_module=t.path in builtin.STANDARD_SYSTEM_MODULES, + is_plugin_module=t.path.startswith(plugin.PLUGIN_MODULE_NAME), + ) + self.scope.elems[name] = ScopeObject( + name=t.path, + node=t, + type=module_object, + pos=ast.Position( + filename=self.filename, + line=t.line, + column=t.column, + ), + end=ast.Position( + filename=self.filename, + line=t.end_line, + column=t.end_column, + ), + ) + if not self.scope.elems[pkgpath].type.is_user_module: + return + # Save current pkgpath and filename + current_pkg_path = self.pkgpath + current_filename = self.filename + # Recursive import check + if self.import_reference.add_node_judge_cycle(self.pkgpath, t.path): + kcl_error.report_exception( + err_type=kcl_error.ErrType.RecursiveLoad_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=self.filename, + line_no=t.line, + ), + ], + arg_msg=kcl_error.RECURSIVE_LOADING_MODULE_MSG.format( + current_pkg_path, + ", ".join([self.pkgpath, t.path]), + ), + ) + # Switch pkgpath context + if t.path not in self.scope_map: + self.check(t.path) + # Restore the current context + self.change_package_context(current_pkg_path, current_filename) + + def parse_type_str_with_scope(self, type_str: str, node: ast.AST) -> Type: + # Type str to Type + tpe = parse_type_str(type_str) + + # If a named type, find it from scope to get the specific type + def walk_fn(t: Type): + if isinstance(t, objpkg.KCLNamedTypeObject): + if "." in t.name and t.name.rsplit(".", 1)[0] == f"@{self.pkgpath}": + t.name = t.name.replace(f"@{self.pkgpath}.", "", 1) + tpe = self.expr( + ast.Identifier( + names=t.name.rsplit(".", 1) + if t.name.startswith("@") + else t.name.split("."), + line=node.line, + ).set_filename(node.filename) + ) + if isinstance(tpe, objpkg.KCLSchemaDefTypeObject): + return tpe.schema_type + elif isinstance(tpe, objpkg.KCLNumberMultiplierTypeObject): + return tpe + elif hasattr(tpe, "is_type_alias") and tpe.is_type_alias: + return tpe + else: + self.raise_err( + [node], + msg=f"'{t.name}' refers to a value, but is being used as a type here", + ) + return t + + return WalkType(tpe, walk_fn) + + def init_import_list(self): + """Init import list and store the module scope object into the scope map""" + for module in self.program.pkgs[self.pkgpath]: + self.filename = module.filename + import_stmt_list = module.GetImportList() + for t in import_stmt_list: + self.do_import_stmt_check(t) + + def init_global_var_types(self, unique_check: bool = True): + """Init all global variable types""" + + def get_top_level_assign_list(module: ast.Module) -> List[ast.AssignStmt]: + + attr_list = [] + + def loop_body(body: List[ast.Stmt]): + """Get the l-values recursively and add them into schema attr list""" + if not body: + return + for stmt in body: + if isinstance(stmt, ast.AssignStmt): + attr_list.append(stmt) + elif isinstance(stmt, ast.IfStmt): + loop_body(stmt.body) + for body in stmt.elif_body: + loop_body(body) + loop_body(stmt.else_body) + + loop_body(module.body) + return attr_list + + def init_scope_with_assign_stmt(assign_stmt: ast.AssignStmt): + for target in assign_stmt.targets: + name = target.names[0] + if ( + name in self.scope.elems + and not kcl_info.isprivate_field(name) + and unique_check + ): + self.raise_err( + [target], + kcl_error.ErrType.ImmutableCompileError_TYPE, + ) + continue + if assign_stmt.type_annotation: + annotation_type = self.parse_type_str_with_scope( + assign_stmt.type_annotation, assign_stmt + ) + assign_stmt.type_annotation = type_to_kcl_type_annotation_str( + annotation_type + ) + if name in self.scope.elems: + origin_type = self.scope.elems[name].type + if not is_upper_bound(origin_type, annotation_type): + self.raise_err( + nodes=[self.scope.elems[name].node, target], + category=kcl_error.ErrType.TypeError_Compile_TYPE, + msg=f"can not change type of {name}", + file_msgs=[ + f"expect {origin_type.type_str()}", + f"got {annotation_type.type_str()}", + ], + file_levels=[ + kcl_error.ErrLevel.ORDINARY, + kcl_error.ErrLevel.SERIOUS, + ], + ) + continue + elif name in self.scope.elems: + annotation_type = self.scope.elems[name].type or ANY_TYPE + else: + annotation_type = ANY_TYPE + self.scope.elems[name] = ScopeObject( + name=name, + node=target, + type=annotation_type, + pos=ast.Position( + filename=self.filename, + line=target.line, + column=target.column, + ), + end=ast.Position( + filename=self.filename, + line=target.end_line, + column=target.end_column, + ), + ) + + for module in self.program.pkgs[self.pkgpath]: + self.change_package_context(self.pkgpath, module.filename) + for stmt in astutil.filter_stmt(module, ast.TypeAliasStmt): + self.walk(stmt) + for stmt in get_top_level_assign_list(module): + init_scope_with_assign_stmt(stmt) + + def dict_assignable_to_schema( + self, + node: ast.AST, + dict_type: Union[objpkg.KCLDictTypeObject, objpkg.KCLAnyTypeObject], + schema_type: objpkg.KCLSchemaTypeObject, + relaxed_key_type_mapping: Optional[Dict[str, Type]] = None, + ) -> bool: + """Judge a dict can be converted to schema in compile time""" + # Do relaxed schema check key and value type check + if relaxed_key_type_mapping and not False and not schema_type.index_signature: + self.raise_err( + nodes=[node], + category=kcl_error.ErrType.CannotAddMembers_TYPE, + msg=kcl_error.CANNOT_ADD_MEMBERS_MSG.format( + ",".join(relaxed_key_type_mapping.keys()), schema_type.name + ), + ) + return False + if dict_type == ANY_TYPE: + return True + if schema_type.index_signature: + schema_key_type = schema_type.index_signature.key_kcl_type + schema_value_type = schema_type.index_signature.value_kcl_type + for k in relaxed_key_type_mapping or {}: + tpe = relaxed_key_type_mapping[k] + if not assignable_to(tpe, schema_value_type): + self.raise_err( + [node], + msg=f"expected schema index signature value type {schema_value_type.type_str()}, " + f"got {tpe.type_str()} of the key '{k}'", + ) + if not schema_type.index_signature.any_other: + return assignable_to( + dict_type.key_type, schema_key_type + ) and assignable_to(dict_type.value_type, schema_value_type) + return True + + def load_attr_type( + self, + node: ast.AST, + obj: Type, + attr: str, + ) -> Type: + if obj == ANY_TYPE: + return ANY_TYPE + if isinstance(obj, objpkg.KCLDictTypeObject): + return obj.value_type + if isinstance(obj, objpkg.KCLSchemaDefTypeObject): + # Schema type member functions + if attr in objpkg.KCLSchemaTypeObject.MEMBER_FUNCTIONS: + return SCHEMA_TYPE_MEMBER_SCOPE.elems[attr].type + self.raise_err( + [node], + kcl_error.ErrType.AttributeError_TYPE, + f"schema '{obj.type_str()}' attribute '{attr}' not found", + ) + return ANY_TYPE + if isinstance(obj, objpkg.KCLSchemaTypeObject): + # Schema attribute + if obj.get_type_of_attr(attr) is None: + if not obj.should_add_additional_key: + self.raise_err( + [node], + kcl_error.ErrType.AttributeError_TYPE, + f"schema '{obj.type_str()}' attribute '{attr}' not found", + ) + return ANY_TYPE + return obj.get_type_of_attr(attr) + if isinstance(obj, (objpkg.KCLStringTypeObject, objpkg.KCLStringLitTypeObject)): + if attr not in objpkg.KCLStringObject.MEMBER_FUNCTIONS: + self.raise_err( + [node], + kcl_error.ErrType.AttributeError_TYPE, + f"str object has no attribute '{attr}'", + ) + return ANY_TYPE + return STR_TYPE_MEMBER_SCOPE.elems[attr].type + if isinstance(obj, objpkg.KCLUnionTypeObject): + return ANY_TYPE + # TODO: union type load attr based the type guard. e.g, a: str|int; if a is str: xxx; if a is int: xxx; + # return sup([self.load_attr_type(t, attr, filename, line, column) for t in obj.types]) + self.raise_err( + [node], + kcl_error.ErrType.AttributeError_TYPE, + f"{obj.type_str() if obj else None} has no attribute '{attr}'", + ) + return ANY_TYPE + + def check_attr( + self, node: ast.AST, obj: Type, attr: str, check_rules: List[typing.Callable] + ): + if obj and isinstance(obj, objpkg.KCLSchemaTypeObject): + for check_rule in check_rules: + check_rule(name=attr, node=node, schema_type=obj) + + def check_defined( + self, + name: Optional[str], + node: ast.AST, + schema_type: objpkg.KCLSchemaTypeObject, + ): + schema_type = self.schema_mapping.get(schema_type.runtime_type) or schema_type + if ( + isinstance(schema_type, objpkg.KCLSchemaTypeObject) + and not schema_type.get_obj_of_attr(name) + and not schema_type.can_add_members() + and not self._is_in_lambda_expr[-1] + ): + self.raise_err( + nodes=[node], + category=kcl_error.ErrType.CannotAddMembers_TYPE, + msg=f"Cannot add member '{name}' to schema '{schema_type.name}'", + file_msgs=[f"'{name}' is not defined in schema '{schema_type.name}'"], + ) + + def check_type(self, node: ast.AST, tpe: Type, expected_type: Type) -> bool: + if type is None: + return False + if ( + tpe.type_kind() == objpkg.KCLTypeKind.ListKind + and expected_type.type_kind() == objpkg.KCLTypeKind.ListKind + ): + return self.check_type(node, tpe.item_type, expected_type.item_type) + elif ( + tpe.type_kind() == objpkg.KCLTypeKind.DictKind + and expected_type.type_kind() == objpkg.KCLTypeKind.DictKind + ): + return self.check_type( + node, tpe.key_type, expected_type.key_type + ) and self.check_type(node, tpe.value_type, expected_type.value_type) + elif tpe.type_kind() == objpkg.KCLTypeKind.UnionKind: + return all([self.check_type(node, t, expected_type) for t in tpe.types]) + if ( + tpe.type_kind() == objpkg.KCLTypeKind.DictKind + and expected_type.type_kind() == objpkg.KCLTypeKind.SchemaKind + ): + return self.dict_assignable_to_schema(node, tpe, expected_type) + if expected_type.type_kind() == objpkg.KCLTypeKind.UnionKind: + return any([self.check_type(node, tpe, t) for t in expected_type.types]) + else: + return assignable_to(tpe, expected_type) + + def must_assignable_to( + self, + node: ast.AST, + tpe: Type, + expected_type: Type, + err_category=kcl_error.ErrType.TypeError_Compile_TYPE, + expected_node: ast.AST = None, + ): + expect_type_str = ( + expected_type.type_str() if expected_type is not None else None + ) + tpe_str = tpe.type_str() if tpe is not None else None + if tpe is None or not self.check_type(node, tpe, expected_type): + self.raise_err( + nodes=[expected_node, node] if expected_node else [node], + category=err_category, + msg=f"expect {expect_type_str}, got {tpe_str}", + file_msgs=[f"expect {expect_type_str}", f"got {tpe_str}"] + if expected_node + else [f"got {tpe_str}"], + file_levels=[kcl_error.ErrLevel.ORDINARY, kcl_error.ErrLevel.SERIOUS] + if expected_node + else [kcl_error.ErrLevel.SERIOUS], + ) + + def must_be_type(self, node: ast.AST, expected_type: Type): + if node and isinstance(node, ast.AST): + tpe = self.walk(node) + self.must_assignable_to(node, tpe, expected_type) + + def enter_scope(self, node: ast.AST): + if node and isinstance(node, ast.AST): + scope = Scope( + parent=self.scope, + node=node, + pos=ast.Position( + filename=self.filename, + line=node.line, + column=node.column, + ), + end=ast.Position( + filename=self.filename, + line=node.end_line, + column=node.end_column, + ), + ) + else: + scope = Scope(self.scope, node) + self.scope.children.append(scope) + self.scope = scope + + def leave_scope(self): + self.scope = self.scope.parent + self._local_vars = [] + + def stmts(self, stmts: List[ast.Stmt]): + stmt_types = [self.stmt(stmt) for stmt in stmts or []] + return stmt_types[-1] if stmt_types else ANY_TYPE + + def exprs(self, exprs: List[ast.Expr]): + return [self.expr(expr) for expr in exprs or []] + + def expr(self, node: ast.Expr): + return self.walk(node) + + def expr_or_any_type(self, node: ast.Expr): + return self.walk(node) if node else ANY_TYPE + + def stmt(self, node: ast.Stmt): + return self.walk(node) + + def lookup_type_from_scope(self, name: str, node: ast.AST) -> Optional[Type]: + tpe = self.find_type_in_scope(name) + if tpe: + return tpe + self.raise_err( + [node], msg="name '{}' is not defined".format(name.replace("@", "")) + ) + return ANY_TYPE + + def find_type_in_scope(self, name: str) -> Optional[Type]: + scope = self.find_scope(name) + return scope.type or ANY_TYPE if scope else None + + def find_scope(self, name: str) -> Optional[Scope]: + scope = self.scope + while scope and name not in scope.elems: + scope = scope.parent + if scope: + return scope.elems[name] or None + return None + + def set_type_to_scope(self, name: str, tpe: Type, node: ast.AST): + if not name: + return + scope = self.scope + while scope and name not in scope.elems: + scope = scope.parent + if scope: + scope.elems[name].type = infer_to_variable_type(tpe) + return + self.raise_err([node], msg=f"name '{name}' is not defined") + + def do_arguments_type_check( + self, + args: List[ast.Expr], + kwargs: List[ast.Keyword], + params: List[objpkg.Parameter], + ): + """Do schema argument type check""" + arg_types: List[Type] = self.exprs(args) + kwarg_types: List[Tuple[str, Type]] = [] + check_table = set() + for kw in kwargs or []: + arg_name = kw.arg.names[0] + if arg_name in check_table: + self.raise_err( + [kw], msg=f"duplicated keyword argument {kw.arg.get_name()}" + ) + check_table.add(arg_name) + arg_value_type = self.expr(kw.value) + kwarg_types.append((arg_name, arg_value_type)) + + if params: + for i, value in enumerate(arg_types): + arg_name = params[i].name + expected_type = params[i].type + self.must_assignable_to( + args[i], + value, + expected_type, + kcl_error.ErrType.TypeError_Compile_TYPE, + ) + for i, kwarg in enumerate(kwarg_types): + arg_name, value = kwarg + if arg_name not in [p.name for p in params]: + self.raise_err( + [kwargs[i]], + msg=f"arguments got an unexpected keyword argument '{arg_name}'", + ) + expected_types = [p.type for p in params if arg_name == p.name] + expected_type = expected_types[0] if expected_types else ANY_TYPE + self.must_assignable_to( + kwargs[i], + value, + expected_type, + kcl_error.ErrType.TypeError_Compile_TYPE, + ) + + def do_loop_type_check( + self, + t: ast.AST, + target_node: ast.AST, + key_name: str, + val_name: str, + iter_type: Type, + ): + """Do loop type check including quant and comp for expression""" + if isinstance(iter_type, objpkg.KCLUnionTypeObject): + types = iter_type.types + else: + types = [iter_type] + key_type, value_type = ANY_TYPE, ANY_TYPE + last_key_type, last_value_type = VOID_TYPE, VOID_TYPE + for iter_type in types: + if not isinstance(iter_type, ITER_TYPES): + self.raise_err( + [t], msg=f"'{iter_type.type_str()}' object is not iterable" + ) + if isinstance(iter_type, objpkg.KCLListTypeObject): + # Two variables + if val_name: + key_type, value_type = sup([INT_TYPE, last_key_type]), sup( + [iter_type.item_type, last_value_type] + ) + self.set_type_to_scope(key_name, key_type, target_node) + self.set_type_to_scope(val_name, value_type, target_node) + else: + key_type = sup([iter_type.item_type, last_key_type]) + self.set_type_to_scope(key_name, key_type, target_node) + elif isinstance( + iter_type, (objpkg.KCLDictTypeObject, objpkg.KCLSchemaTypeObject) + ): + key_type, value_type = sup([iter_type.key_type, last_key_type]), sup( + [iter_type.value_type, last_value_type] + ) + self.set_type_to_scope(key_name, key_type, target_node) + self.set_type_to_scope(val_name, value_type, target_node) + elif isinstance( + iter_type, (objpkg.KCLStringTypeObject, objpkg.KCLStringLitTypeObject) + ): + if val_name: + key_type, value_type = sup([INT_TYPE, last_key_type]), sup( + [STR_TYPE, last_value_type] + ) + self.set_type_to_scope(key_name, key_type, target_node) + self.set_type_to_scope(val_name, value_type, target_node) + else: + key_type = sup([STR_TYPE, last_key_type]) + self.set_type_to_scope(key_name, key_type, target_node) + last_key_type, last_value_type = key_type, value_type + + +class TypeChecker(BaseTypeChecker): + def walk_Module(self, t: ast.Module): + return self.stmts(t.body) + + def walk_ExprStmt(self, t: ast.ExprStmt): + expr_types = self.exprs(t.exprs) + return expr_types[-1] if expr_types else ANY_TYPE + + def walk_AssertStmt(self, t: ast.AssertStmt): + self.expr(t.test) + # Check type in if_cond expression + self.expr_or_any_type(t.if_cond) + self.must_be_type(t.msg, STR_TYPE) + return ANY_TYPE + + def walk_IfStmt(self, t: ast.IfStmt): + self.expr(t.cond) + self.stmts(t.body) + for elif_cond, elif_body in zip(t.elif_cond, t.elif_body): + self.expr(elif_cond) + self.stmts(elif_body) + self.stmts(t.else_body) + return ANY_TYPE + + def walk_ImportStmt(self, t: ast.ImportStmt): + """import as """ + # Add package name into the scope + if t.path not in self.scope_map: + self.do_import_stmt_check(t) + return ANY_TYPE + + def walk_RuleStmt(self, t: ast.RuleStmt): + self.in_schema_type = cast( + objpkg.KCLSchemaDefTypeObject, self.lookup_type_from_scope(t.name, t) + ).schema_type + # Rule Decorators + self.exprs(t.decorators) + self.enter_scope(t) + # Rule args + for param in self.in_schema_type.func.params or []: + self.scope.elems[param.name] = ScopeObject( + name=param.name, + node=t.args, + type=param.type, + pos=ast.Position( + filename=self.filename, + line=t.line, + column=t.column, + ), + end=ast.Position( + filename=self.filename, + line=t.end_line, + column=t.end_column, + ), + ) + # Rule check expressions + self.exprs(t.checks) + self.leave_scope() + self.in_schema_type = None + return ANY_TYPE + + def walk_SchemaStmt(self, t: ast.SchemaStmt): + self.in_schema_type = cast( + objpkg.KCLSchemaDefTypeObject, self.lookup_type_from_scope(t.name, t) + ).schema_type + # Schema Decorators + self.exprs(t.decorators) + self.enter_scope(t) + # Schema args + for param in self.in_schema_type.func.params or []: + self.scope.elems[param.name] = ScopeObject( + name=param.name, + node=t.args, + type=param.type, + pos=ast.Position( + filename=self.filename, + line=t.line, + column=t.column, + ), + end=ast.Position( + filename=self.filename, + line=t.end_line, + column=t.end_column, + ), + ) + # Schema index signature + if ( + self.in_schema_type.index_signature + and self.in_schema_type.index_signature.key_name + ): + self.scope.elems[ + self.in_schema_type.index_signature.key_name + ] = ScopeObject( + name=self.in_schema_type.index_signature.key_name, + node=t.index_signature, + type=self.in_schema_type.index_signature.key_kcl_type, + pos=ast.Position( + filename=self.filename, + line=t.index_signature.line, + column=t.index_signature.column, + ), + end=ast.Position( + filename=self.filename, + line=t.index_signature.end_line, + column=t.index_signature.end_column, + ), + ) + schema_attr_names = t.GetLeftIdentifierList() + for name in schema_attr_names: + if name not in self.scope.elems: + self.scope.elems[name] = ScopeObject( + name=name, + node=None, + type=ANY_TYPE, + ) + # Schema body + self.stmts(t.body) + # Schema check block + self.exprs(t.checks) + self.leave_scope() + self.in_schema_type = None + return ANY_TYPE + + def walk_SchemaAttr(self, t: ast.SchemaAttr): + self._local_vars = [] + if "." in t.name: + self.raise_err([t], msg="schema attribute can not be selected") + expected_type = self.in_schema_type.get_type_of_attr(t.name) or ANY_TYPE + # Schema attribute decorators + self.exprs(t.decorators) + self.scope.elems[t.name] = ScopeObject( + name=t.name, + node=t, + type=expected_type, + pos=ast.Position( + filename=self.filename, + line=t.line, + column=t.column, + ), + end=ast.Position( + filename=self.filename, + line=t.end_line, + column=t.end_column, + ), + ) + # Do not check type if no default value + if t.value: + if isinstance(expected_type, objpkg.KCLSchemaTypeObject): + init_stack_depth = self.switch_config_expr_context( + self.new_config_expr_context_item( + name=expected_type.name, type_obj=expected_type, node=t + ) + ) + value_type = self.expr(t.value) + self.clear_config_expr_context(stack_depth=init_stack_depth) + else: + value_type = self.expr(t.value) + # Assign + if not t.op or t.op == ast.AugOp.Assign: + self.must_assignable_to( + t, + value_type, + expected_type, + kcl_error.ErrType.TypeError_Compile_TYPE, + ) + else: + self.must_assignable_to( + t, + binary(expected_type, value_type, t.op), + expected_type, + kcl_error.ErrType.TypeError_Compile_TYPE, + ) + return ANY_TYPE + + def walk_Decorator(self, t: ast.Decorator): + decorator_name = t.name.get_name() + # Judge invalid decorator + internal.decorator_factory.get( + decorator_name, + internal.DecoratorTargetType.SCHEMA_TYPE, + filename=self.filename, + lineno=t.line, + columnno=t.column, + ) + # Decorator args type check according to decorator lookup table + decorator_func_type = DECORATOR_SCOPE.elems.get(decorator_name, ANY_TYPE).type + if isinstance(decorator_func_type, objpkg.KCLFunctionTypeObject) and t.args: + self.do_arguments_type_check( + t.args.args, t.args.keywords, decorator_func_type.params + ) + return ANY_TYPE + + def walk_IfExpr(self, t: ast.IfExpr): + """ if else -> sup([body, orelse])""" + self.expr(t.cond) + types = self.exprs([t.body, t.orelse]) + return sup(types) + + def walk_UnaryExpr(self, t: ast.UnaryExpr): + return unary(self.expr(t.operand), t.op, self.filename, t.line) + + def walk_BinaryExpr(self, t: ast.BinaryExpr): + left_type, right_type = self.expr(t.left), self.expr(t.right) + if t.op == ast.BinOp.As: + if not isinstance(t.right, ast.Identifier): + self.raise_err( + [t.right], msg="Keyword 'as' right operand must be a type" + ) + return self.expr(t.left) + # Replace with type alias + right_type = self.parse_type_str_with_scope(t.right.get_name(), t.right) + type_annotation_str = type_to_kcl_type_annotation_str(right_type) + if ( + "." in type_annotation_str + and type_annotation_str.rsplit(".", 1)[0] == f"@{self.pkgpath}" + ): + type_annotation_str = type_annotation_str.replace( + f"@{self.pkgpath}.", "", 1 + ) + t.right.names = type_annotation_str.rsplit(".", 1) + return binary(left_type, right_type, t.op, self.filename, t.line) + + def walk_Compare(self, t: ast.Compare): + for t1, t2, op in zip([t.left] + t.comparators, t.comparators, t.ops): + compare(self.expr(t1), self.expr(t2), op, self.filename, t.line) + return BOOL_TYPE + + def walk_SelectorExpr(self, t: ast.SelectorExpr): + value_type = self.expr(t.value) + for name in t.attr.names: + value_type = self.load_attr_type(t, value_type, name) + return value_type + + def walk_CallExpr(self, t: ast.CallExpr): + func_type = self.expr(t.func) + if ( + func_type != ANY_TYPE + and func_type.type_kind() != objpkg.KCLTypeKind.FuncKind + and not isinstance(func_type, objpkg.KCLSchemaDefTypeObject) + ): + self.raise_err([t], msg=f"'{func_type.type_str()}' object is not callable") + return ANY_TYPE + if func_type == ANY_TYPE: + self.do_arguments_type_check( + t.args, + t.keywords, + [], + ) + return ANY_TYPE + self.do_arguments_type_check( + t.args, + t.keywords, + func_type.schema_type.func.params + if isinstance(func_type, objpkg.KCLSchemaDefTypeObject) + else func_type.params, + ) + return ( + func_type.schema_type + if isinstance(func_type, objpkg.KCLSchemaDefTypeObject) + else func_type.return_type + ) + + def walk_ParenExpr(self, t: ast.ParenExpr): + return self.expr(t.expr) + + def walk_QuantExpr(self, t: ast.QuantExpr): + """ + self.target: Optional[Expr] = None + self.variables: List[Identifier] = [] + self.op: Optional[int] = None + self.test: Optional[Expr] = None + self.if_cond: Optional[Expr] = None + """ + iter_type = self.expr(t.target) + if iter_type == ANY_TYPE: + return ANY_TYPE + self.enter_scope(t) + key_name = None + val_name = None + target_node = None + for i, target in enumerate(t.variables): + target_node = target + name = target.names[0] + key_name = name if i == 0 else key_name + val_name = name if i == 1 else val_name + self._local_vars.append(name) + self.scope.elems[name] = ScopeObject( + name=name, + node=target, + type=ANY_TYPE, + ) + self.do_loop_type_check(t, target_node, key_name, val_name, iter_type) + self.expr_or_any_type(t.if_cond) + item_type = self.expr(t.test) + return_type = ANY_TYPE + if t.op in [ast.QuantOperation.ALL, ast.QuantOperation.ANY]: + return_type = BOOL_TYPE + elif t.op == ast.QuantOperation.MAP: + return_type = objpkg.KCLListTypeObject(item_type=item_type) + elif t.op == ast.QuantOperation.FILTER: + return_type = iter_type + else: + self.raise_err([t], msg=f"Invalid quantifier expression op {t.op}") + self.leave_scope() + return return_type + + def walk_ListExpr(self, t: ast.ListExpr): + item_type = sup(self.exprs(t.elts)) + return objpkg.KCLListTypeObject(item_type=item_type) + + def walk_StarredExpr(self, t: ast.StarredExpr): + """Single star unpack expression *t.value""" + value_type = self.expr(t.value) + if value_type == ANY_TYPE: + return ANY_TYPE + if isinstance(value_type, objpkg.KCLNoneTypeObject): + return NONE_TYPE + if isinstance(value_type, objpkg.KCLListTypeObject): + return value_type.item_type + if isinstance( + value_type, (objpkg.KCLDictTypeObject, objpkg.KCLSchemaTypeObject) + ): + return value_type.key_type + if is_kind_type_or_kind_union_type( + value_type, + [objpkg.KCLTypeKind.DictKind, objpkg.KCLTypeKind.SchemaKind], + ): + return sup([tpe.key_type for tpe in value_type.types]) + self.raise_err( + [t.value], + msg=f"only list, dict, schema object can be used * unpacked, got {value_type.type_str()}", + ) + return ANY_TYPE + + def walk_ListComp(self, t: ast.ListComp): + self.enter_scope(t) + self.exprs(t.generators) + if isinstance(t.elt, ast.StarredExpr): + self.raise_err( + [t.elt], + msg="list unpacking cannot be used in list comprehension", + ) + item_type = self.expr(t.elt) + self.leave_scope() + return objpkg.KCLListTypeObject(item_type=item_type) + + def walk_DictComp(self, t: ast.DictComp): + self.enter_scope(t) + self.exprs(t.generators) + if not t.key: + self.raise_err( + [t.value], + msg="dict unpacking cannot be used in dict comprehension", + ) + key_type, value_type = self.expr(t.key), self.expr(t.value) + self.leave_scope() + return objpkg.KCLDictTypeObject(key_type=key_type, value_type=value_type) + + def walk_CompClause(self, t: ast.CompClause): + iter_type = self.expr(t.iter) + key_name = None + val_name = None + target_node = None + for i, target in enumerate(t.targets): + target_node = target + name = target.names[0] + key_name = name if i == 0 else key_name + val_name = name if i == 1 else val_name + self._local_vars.append(name) + self.scope.elems[name] = ScopeObject( + name=name, + node=target, + type=ANY_TYPE, + pos=ast.Position( + filename=self.filename, + line=target.line, + column=target.column, + ), + end=ast.Position( + filename=self.filename, + line=target.end_line, + column=target.end_column, + ), + ) + if iter_type == ANY_TYPE: + return ANY_TYPE + self.do_loop_type_check(t, target_node, key_name, val_name, iter_type) + self.exprs(t.ifs) + return ANY_TYPE + + def walk_Subscript(self, t: ast.Subscript): + value_type = self.expr(t.value) + if value_type == ANY_TYPE: + return ANY_TYPE + if isinstance( + value_type, (objpkg.KCLSchemaTypeObject, objpkg.KCLDictTypeObject) + ): + if not t.index: + self.raise_err([t], msg="unhashable type: 'slice'") + return ANY_TYPE + key_type = self.expr(t.index) + if key_type == ANY_TYPE or key_type == NONE_TYPE: + return value_type.value_type + if not is_kind_type_or_kind_union_type(key_type, KEY_KINDS): + self.raise_err( + [t.index], + msg=f"invalid dict/schema key type: '{key_type.type_str()}'", + ) + return ANY_TYPE + return ( + self.load_attr_type(t, value_type, t.index.value) + if isinstance(t.index, ast.StringLit) + else value_type.value_type + ) + elif isinstance( + value_type, + ( + objpkg.KCLListTypeObject, + objpkg.KCLStringTypeObject, + objpkg.KCLStringLitTypeObject, + ), + ): + if t.index: + self.must_be_type(t.index, INT_TYPE) + return ( + value_type.item_type + if isinstance(value_type, objpkg.KCLListTypeObject) + else STR_TYPE + ) + else: + self.must_be_type(t.lower, INT_TYPE) + self.must_be_type(t.upper, INT_TYPE) + self.must_be_type(t.step, INT_TYPE) + return ( + value_type + if isinstance(value_type, objpkg.KCLListTypeObject) + else STR_TYPE + ) + self.raise_err( + [t.value], msg=f"'{value_type.type_str()}' object is not subscriptable" + ) + return ANY_TYPE + + def walk_SchemaExpr(self, t: ast.SchemaExpr): + # Auto append schema import statements. + if self.config.config_attr_auto_fix: + try: + schema_def_type = self.walk(t.name) + except Exception: + # Match the schema package path. + pkgpaths = [ + schema.pkgpath + for schema in self.schema_mapping.values() + if t.name.get_name() == schema.name + ] + if pkgpaths: + # Select the first matched path. + pkgpath = pkgpaths[0] + import_list = self.program.pkgs[self.pkgpath][0].GetImportList() + imported_pkgpath = [stmt.path for stmt in import_list] + # Exists import + try: + index = imported_pkgpath.index(pkgpath) + t.name.names = [import_list[index].pkg_name] + t.name.names + # Not exists import, append the import stmt. + except ValueError: + if pkgpath and pkgpath != ast.Program.MAIN_PKGPATH: + name = pkgpath.rsplit(".")[-1] + import_stmt = ast.ImportStmt(1, 1) + import_stmt.path = pkgpath + import_stmt.name = name + self.program.pkgs[self.pkgpath][0].body.insert( + 0, import_stmt + ) + t.name.names = [name] + t.name.names + return ANY_TYPE + else: + schema_def_type = self.walk(t.name) + if isinstance(schema_def_type, objpkg.KCLSchemaDefTypeObject): + schema_type_annotation_str = type_to_kcl_type_annotation_str( + schema_def_type + ) + if ( + "." in schema_type_annotation_str + and schema_type_annotation_str.rsplit(".", 1)[0] == f"@{self.pkgpath}" + ): + schema_type_annotation_str = schema_type_annotation_str.replace( + f"@{self.pkgpath}.", "", 1 + ) + t.name.names = schema_type_annotation_str.rsplit(".", 1) + schema_type = cast(objpkg.KCLSchemaTypeObject, schema_def_type.schema_type) + init_stack_depth = self.switch_config_expr_context( + self.new_config_expr_context_item( + name=schema_type.name, type_obj=schema_type, node=t + ) + ) + self.expr(t.config) + self.clear_config_expr_context(stack_depth=init_stack_depth) + # Do schema argument type check + self.do_arguments_type_check(t.args, t.kwargs, schema_type.func.params) + return schema_type + elif isinstance(schema_def_type, objpkg.KCLSchemaTypeObject): + schema_type = schema_def_type + init_stack_depth = self.switch_config_expr_context( + self.new_config_expr_context_item( + name=schema_type.name, type_obj=schema_type, node=t + ) + ) + self.expr(t.config) + self.clear_config_expr_context(stack_depth=init_stack_depth) + if t.args or t.kwargs: + self.raise_err( + [t.name], + msg="Arguments cannot be used in the schema modification expression", + ) + return schema_type + elif isinstance(schema_def_type, objpkg.KCLDictTypeObject): + dict_type = schema_def_type + init_stack_depth = self.switch_config_expr_context( + self.new_config_expr_context_item(type_obj=dict_type, node=t) + ) + config_type = self.expr(t.config) + self.clear_config_expr_context(stack_depth=init_stack_depth) + return binary( + dict_type, config_type, ast.BinOp.BitOr, self.filename, t.line + ) + self.raise_err( + [t.name], + msg=f"Invalid schema type '{schema_def_type.type_str() if schema_def_type else None}'", + ) + return ANY_TYPE + + def walk_ConfigExpr(self, t: ast.ConfigExpr): + value_types = [] + key_types = [] + fix_values = [] + for i, key, value, op in zip( + range(len(t.keys)), t.keys, t.values, t.operations + ): + stack_depth = 0 + fix_value = self.check_config_entry(key, value, [self.check_defined]) + fix_values.append((i, fix_value)) + stack_depth += self.switch_config_expr_context_by_key(key) + value_type = ANY_TYPE + has_insert_index = False + # Double_star expr and dict_if_entry expr + if not key: + value_type = self.expr(value) + if value_type == ANY_TYPE or value_type == NONE_TYPE: + value_types.append(value_type) + elif isinstance( + value_type, (objpkg.KCLDictTypeObject, objpkg.KCLSchemaTypeObject) + ): + key_types.append(value_type.key_type) + value_types.append(value_type.value_type) + elif is_kind_type_or_kind_union_type( + value_type, + [objpkg.KCLTypeKind.DictKind, objpkg.KCLTypeKind.SchemaKind], + ): + key_types.append(sup([tpe.key_type for tpe in value_type.types])) + value_types.append( + sup([tpe.value_type for tpe in value_type.types]) + ) + else: + self.raise_err( + nodes=[value], + msg=f"only dict and schema can be used ** unpack, got '{value_type.type_str()}'", + ) + elif isinstance(key, ast.Identifier): + # Nest identifier key -> str key + if len(key.names) == 1: + name = key.get_name(False) + key_type = self.expr(key) if name in self._local_vars else STR_TYPE + if key_type != ANY_TYPE and key_type.type_kind() not in KEY_KINDS: + self.raise_err( + nodes=[key], + category=kcl_error.ErrType.IllegalAttributeError_TYPE, + msg=f"type '{key_type.type_str()}'", + ) + else: + key_type = STR_TYPE + key_types.append(key_type) + value_type = self.expr(value) + nest_value_type = value_type + for _ in range(len(key.names) - 1): + nest_value_type = objpkg.KCLDictTypeObject( + key_type=STR_TYPE, value_type=nest_value_type + ) + value_types.append(nest_value_type) + elif isinstance(key, ast.Subscript): + if isinstance(key.value, ast.Identifier) and isinstance( + key.index, ast.NumberLit + ): + has_insert_index = True + value_type = self.expr(value) + key_types.append(STR_TYPE) + value_types.append(objpkg.KCLListTypeObject(value_type)) + else: + key_type, value_type = self.expr(key), self.expr(value) + if key_type != ANY_TYPE and key_type.type_kind() not in KEY_KINDS: + self.raise_err( + [key], + kcl_error.ErrType.IllegalAttributeError_TYPE, + f"type '{key_type.type_str()}'", + ) + key_types.append(key_type) + value_types.append(value_type) + if ( + op == ast.ConfigEntryOperation.INSERT + and not has_insert_index + and value_type != ANY_TYPE + and not isinstance(value_type, objpkg.KCLListTypeObject) + ): + self.raise_err( + [value], + kcl_error.ErrType.IllegalAttributeError_TYPE, + f"only list type can in inserted, got '{value_type.type_str()}'", + ) + self.clear_config_expr_context(stack_depth=stack_depth) + key_type = sup(key_types) + value_type = sup(value_types) + for i, fix_value in fix_values: + if fix_value: + t.items[i].value = fix_value + # self.clear_config_expr_context(stack_depth=init_stack_depth) + return objpkg.KCLDictTypeObject(key_type=key_type, value_type=value_type) + + def walk_CheckExpr(self, t: ast.CheckExpr): + self.must_be_type(t.msg, STR_TYPE) + # Check type in if_cond expression + self.expr_or_any_type(t.if_cond) + return self.expr(t.test) + + def walk_LambdaExpr(self, t: ast.LambdaExpr): + """ast.AST: LambdaExpr + + Parameters + ---------- + - args: Optional[Arguments] + - return_type_str: Optional[str] + - return_type_node: Optional[Type] + - body: List[Stmt] + """ + params = [] + return_type = ANY_TYPE + if t.args: + for i, arg in enumerate(t.args.args): + name = arg.get_name() + type_annotation = t.args.GetArgType(i) + type_node = self.parse_type_str_with_scope( + type_annotation, t.args.args[i] + ) + default = t.args.GetArgDefault(i) + params.append( + objpkg.Parameter( + name=name, + value=objpkg.to_kcl_obj(default), + type_annotation=type_annotation, + type=type_node, + ) + ) + t.args.SetArgType(i, type_to_kcl_type_annotation_str(type_node)) + if t.return_type_str: + return_type = self.parse_type_str_with_scope(t.return_type_str, t) + t.return_type_str = type_to_kcl_type_annotation_str(return_type) + self.enter_scope(t) + self._is_in_lambda_expr.append(True) + # Lambda args + for param in params or []: + self.scope.elems[param.name] = ScopeObject( + name=param.name, + node=t.args, + type=param.type, + pos=ast.Position( + filename=self.filename, + line=t.line, + column=t.column, + ), + end=ast.Position( + filename=self.filename, + line=t.end_line, + column=t.end_column, + ), + ) + real_return_type = self.stmts(t.body) + self.leave_scope() + self._is_in_lambda_expr.pop() + self.must_assignable_to( + t.body[-1] if t.body else t, real_return_type, return_type + ) + if ( + real_return_type != ANY_TYPE + and return_type == ANY_TYPE + and not t.return_type_str + ): + return_type = real_return_type + return objpkg.KCLFunctionTypeObject( + name="", + params=params, + self_type=ANY_TYPE, + return_type=return_type, + doc="", + ) + + def walk_Identifier(self, t: ast.Identifier): + assert len(t.names) >= 1 + if t.pkgpath: + t.names[0] = f"@{t.pkgpath}" + if t.ctx == ast.ExprContext.STORE or t.ctx == ast.ExprContext.AUGSTORE: + self.raise_err( + [t], msg="only schema and dict object can be updated attribute" + ) + name = t.names[0] + if os.getenv("KCL_FEATURE_GATEWAY_STRONG_MUTABLE"): + if t.ctx == ast.ExprContext.STORE or t.ctx == ast.ExprContext.AUGSTORE: + if self.in_schema_type: + if not kcl_info.isprivate_field(name) and ( + name in self.scope.elems + and self.scope.elems[name].pos + and ( + t.line != self.scope.elems[name].pos.line + or t.column != self.scope.elems[name].pos.column + ) + ): + self.raise_err( + [t], + kcl_error.ErrType.ImmutableCompileError_TYPE, + ) + if len(t.names) == 1: + if self.in_schema_type: + # Load from schema if in schema + tpe = self.in_schema_type.get_type_of_attr(name) + if t.ctx == ast.ExprContext.LOAD or t.ctx == ast.ExprContext.AUGLOAD: + scope_type = self.find_type_in_scope(name) + if name in self._local_vars: + return scope_type or ANY_TYPE + if tpe and tpe != ANY_TYPE: + return tpe + return scope_type or ANY_TYPE + # TODO: Enhanced Mixin with protocol + # return tpe or self.lookup_type_from_scope(name, t) + elif ( + t.ctx == ast.ExprContext.STORE or t.ctx == ast.ExprContext.AUGSTORE + ): + if name not in self.scope.elems or not tpe: + self.scope.elems[name] = ScopeObject( + name=name, + node=t, + type=ANY_TYPE, + pos=ast.Position( + filename=self.filename, + line=t.line, + column=t.column, + ), + end=ast.Position( + filename=self.filename, + line=t.end_line, + column=t.end_column, + ), + ) + if not tpe: + self.in_schema_type.set_type_of_attr(name, ANY_TYPE) + return ANY_TYPE + self.check_attr( + t, + self.in_schema_type, + name, + [self.check_defined], + ) + return tpe if tpe else self.lookup_type_from_scope(name, t) + else: + if t.ctx == ast.ExprContext.LOAD or t.ctx == ast.ExprContext.AUGLOAD: + return self.lookup_type_from_scope(name, t) + elif ( + t.ctx == ast.ExprContext.STORE or t.ctx == ast.ExprContext.AUGSTORE + ): + if name not in self.scope.elems and not self.in_schema_type: + self.scope.elems[name] = ScopeObject( + name=name, + node=t, + type=ANY_TYPE, + pos=ast.Position( + filename=self.filename, + line=t.line, + column=t.column, + ), + end=ast.Position( + filename=self.filename, + line=t.end_line, + column=t.end_column, + ), + ) + return ANY_TYPE + return self.lookup_type_from_scope(name, t) + return ANY_TYPE + else: + names = t.names + if t.ctx != ast.ExprContext.AUGSTORE: + tpe = self.expr( + ast.Identifier( + names=[names[0]], line=t.line, column=t.column + ).set_filename(t.filename) + ) + else: + tpe = self.lookup_type_from_scope(t.names[0], t) + + for name in names[1:]: + if isinstance(tpe, objpkg.KCLModuleTypeObject): + if tpe.is_user_module: + if tpe.pkgpath not in self.scope_map: + self.raise_err( + [t], msg=f"name '{tpe.pkgpath}' is not defined" + ) + return ANY_TYPE + elif name not in self.scope_map[tpe.pkgpath].elems: + self.raise_err( + [t], + kcl_error.ErrType.AttributeError_TYPE, + f"module '{tpe.pkgpath}' has no attribute '{name}'", + ) + tpe = ANY_TYPE + elif ( + self.filename not in tpe.imported_filenames + and self.pkgpath != ast.Program.MAIN_PKGPATH + ): + self.raise_err( + [t], msg=f"name '{tpe.pkgpath}' is not defined" + ) + tpe = ANY_TYPE + else: + tpe = self.scope_map[tpe.pkgpath].elems[name].type + if isinstance(tpe, objpkg.KCLModuleTypeObject): + self.raise_err( + [t], + kcl_error.ErrType.CompileError_TYPE, + f"can not import the attribute '{name}' from the module '{t.names[0]}'", + file_msgs=[f"'{name}' is a module attribute"], + ) + elif tpe.is_plugin_module: + tpe = ( + PLUGIN_SCOPE_MAPPING[tpe.pkgpath].elems[name].type + if ( + tpe.pkgpath in PLUGIN_SCOPE_MAPPING + and name in PLUGIN_SCOPE_MAPPING[tpe.pkgpath].elems + ) + else ANY_TYPE + ) + elif tpe.is_system_module: + members = builtin.get_system_module_members(tpe.pkgpath) + if name not in members: + self.raise_err( + [t], + kcl_error.ErrType.AttributeError_TYPE, + f"module '{tpe.pkgpath}' has no attribute '{name}'", + ) + tpe = ( + MODULE_SCOPE_MAPPING[tpe.pkgpath].elems[name].type + if ( + tpe.pkgpath in MODULE_SCOPE_MAPPING + and name in MODULE_SCOPE_MAPPING[tpe.pkgpath].elems + ) + else ANY_TYPE + ) + else: + tpe = ANY_TYPE + elif ( + t.ctx == ast.ExprContext.STORE or t.ctx == ast.ExprContext.AUGSTORE + ): + self.check_attr( + t, + tpe, + name, + [self.check_defined], + ) + tpe = self.load_attr_type(t, tpe, name) + else: + tpe = self.load_attr_type(t, tpe, name) + + return tpe + + def walk_NumberLit(self, t: ast.AST): + if t.binary_suffix: + value = units.cal_num(t.value, t.binary_suffix) + return objpkg.KCLNumberMultiplierTypeObject( + value=value, + raw_value=t.value, + binary_suffix=t.binary_suffix, + ) + return ( + objpkg.KCLIntLitTypeObject(t.value) + if isinstance(t.value, int) + else objpkg.KCLFloatLitTypeObject(t.value) + ) + + def walk_StringLit(self, t: ast.StringLit): + return objpkg.KCLStringLitTypeObject(t.value) + + def walk_NameConstantLit(self, t: ast.NameConstantLit): + if t.value is None or isinstance(t.value, internal.UndefinedType): + return NONE_TYPE + return TRUE_LIT_TYPE if t.value is True else FALSE_LIT_TYPE + + def walk_FormattedValue(self, t: ast.FormattedValue): + if ( + t.format_spec + and isinstance(t.format_spec, str) + and t.format_spec.lower() not in VALID_FORMAT_SPEC_SET + ): + self.raise_err([t], msg=f"{t.format_spec} is a invalid format spec") + return self.expr(t.value) + + def walk_JoinedString(self, t: ast.JoinedString): + self.exprs(t.values) + return STR_TYPE + + def walk_TypeAliasStmt(self, t: ast.TypeAliasStmt): + """ast.AST: TypeAliasStmt + + Parameters + ---------- + - type_name: Identifier + - type_value: Type + """ + tpe = self.parse_type_str_with_scope(t.type_value.plain_type_str, t.type_value) + if isinstance(tpe, objpkg.KCLSchemaTypeObject): + tpe = objpkg.KCLSchemaDefTypeObject(schema_type=tpe) + tpe.is_type_alias = True + name = t.type_name.get_name() + if name in RESERVED_TYPE_IDENTIFIERS: + self.raise_err( + [t], + kcl_error.ErrType.IllegalInheritError_TYPE, + "type alias '{}' cannot be the same as the built-in types ({})".format( + name, ", ".join(RESERVED_TYPE_IDENTIFIERS) + ), + ) + self.scope.elems[name] = ScopeObject( + name=name, + node=t, + type=tpe, + pos=ast.Position( + filename=self.filename, + line=t.line, + column=t.column, + ), + end=ast.Position( + filename=self.filename, + line=t.end_line, + column=t.end_column, + ), + ) + return tpe + + def walk_UnificationStmt(self, t: ast.UnificationStmt): + if len(t.target.names) > 1: + self.raise_err([t.target], msg="unification identifier can not be selected") + name = t.target.names[0] + tpe = self.expr(t.target) + value_tpe = self.expr(t.value) + self.must_assignable_to(t.target, value_tpe, tpe) + if value_tpe != ANY_TYPE and tpe == ANY_TYPE: + self.set_type_to_scope(name, value_tpe, t.target) + return value_tpe + + def walk_AssignStmt(self, t: ast.AssignStmt): + """id: T = E""" + self._local_vars = [] + for target in t.targets: + name = target.names[0] + if len(target.names) == 1: + tpe = self.expr(target) + if isinstance(tpe, objpkg.KCLSchemaTypeObject): + init_stack_depth = self.switch_config_expr_context( + self.new_config_expr_context_item( + name=tpe.name, type_obj=tpe, node=t + ) + ) + value_tpe = self.expr(t.value) + self.clear_config_expr_context(stack_depth=init_stack_depth) + else: + value_tpe = self.expr(t.value) + self.must_assignable_to(target, value_tpe, tpe) + if value_tpe != ANY_TYPE and tpe == ANY_TYPE and not t.type_annotation: + self.set_type_to_scope(name, value_tpe, target) + if self.in_schema_type: + # Set attr type if in schema + tpe = self.in_schema_type.set_type_of_attr( + name, infer_to_variable_type(value_tpe) + ) + else: + self.lookup_type_from_scope(name, target) + tpe = self.expr(target) + value_tpe = self.expr(t.value) + self.must_assignable_to(target, value_tpe, tpe) + return value_tpe + + def walk_AugAssignStmt(self, t: ast.AugAssignStmt): + t.target.ctx = ast.ExprContext.LOAD + new_target_type = binary( + self.expr(t.target), self.expr(t.value), t.op, self.filename, t.line + ) + t.target.ctx = ast.ExprContext.STORE + expected_type = self.expr(t.target) + self.must_assignable_to(t.target, new_target_type, expected_type) + return new_target_type + + def walk_ListIfItemExpr(self, t: ast.ListIfItemExpr): + self.expr_or_any_type(t.if_cond) + or_else_type = self.expr_or_any_type(t.orelse) + exprs_type = sup(self.exprs(t.exprs)) + return sup([or_else_type, exprs_type]) + + def walk_ConfigIfEntryExpr(self, t: ast.ConfigIfEntryExpr): + self.expr_or_any_type(t.if_cond) + key_types, value_types = [], [] + for key, value in zip(t.keys, t.values): + stack_depth = 0 + self.check_config_entry(key, value, [self.check_defined]) + stack_depth += self.switch_config_expr_context_by_key(key) + if not key: + key_type = ANY_TYPE + value_type = self.expr(value) + if value_type == ANY_TYPE or value_type == NONE_TYPE: + value_types.append(value_type) + elif isinstance( + value_type, (objpkg.KCLDictTypeObject, objpkg.KCLSchemaTypeObject) + ): + key_type = value_type.key_type + value_type = value_type.value_type + elif is_kind_type_or_kind_union_type( + value_type, + [objpkg.KCLTypeKind.DictKind, objpkg.KCLTypeKind.SchemaKind], + ): + key_type = sup([tpe.key_type for tpe in value_type.types]) + value_type = sup([tpe.value_type for tpe in value_type.types]) + else: + self.raise_err( + nodes=[value], + msg=f"only dict and schema can be used ** unpack, got '{value_type.type_str()}'", + ) + elif isinstance(key, ast.Identifier): + key_type = STR_TYPE + value_type = self.expr(value) + for _ in range(len(key.names) - 1): + value_type = objpkg.KCLDictTypeObject( + key_type=STR_TYPE, value_type=value_type + ) + else: + key_type = self.expr(key) + value_type = self.expr(value) + key_types.append(key_type) + value_types.append(value_type) + self.clear_config_expr_context(stack_depth=stack_depth) + dict_type = objpkg.KCLDictTypeObject( + key_type=sup(key_types), + value_type=sup(value_types), + ) + or_else_type = self.expr_or_any_type(t.orelse) + return sup([dict_type, or_else_type]) + + +def ResolveProgramImport(prog: ast.Program): + """Check import error""" + if not prog or not isinstance(prog, ast.Program): + return + root = prog.root + main_files = [module.filename for module in prog.pkgs[ast.Program.MAIN_PKGPATH]] + for pkgpath in prog.pkgs or []: + for m in prog.pkgs[pkgpath]: + for import_spec in m.GetImportList(): + pkgpath = import_spec.path + if pkgpath in builtin.STANDARD_SYSTEM_MODULES: + continue + + if pkgpath.startswith(plugin.PLUGIN_MODULE_NAME): + plugin_name = pkgpath.replace(plugin.PLUGIN_MODULE_NAME, "") + if plugin_name not in plugin.get_plugin_names(): + kcl_error.report_exception( + err_type=kcl_error.ErrType.CannotFindModule_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=import_spec.filename or m.filename, + line_no=import_spec.line, + col_no=import_spec.column, + end_col_no=import_spec.end_column, + ) + ], + arg_msg=kcl_error.CANNOT_FIND_MODULE_MSG.format( + import_spec.rawpath, plugin.get_plugin_root(plugin_name) + ), + ) + continue + + if pkgpath not in prog.pkgs: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CannotFindModule_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=import_spec.filename or m.filename, + line_no=import_spec.line, + col_no=import_spec.column, + end_col_no=import_spec.end_column, + ) + ], + arg_msg=kcl_error.CANNOT_FIND_MODULE_MSG.format( + import_spec.rawpath, + str(pathlib.Path(prog.root) / (pkgpath.replace(".", "/"))), + ), + ) + + if os.path.isfile(f"{root}/{pkgpath.replace('.', '/')}.k"): + file = f"{root}/{pkgpath.replace('.', '/')}.k" + if file in main_files or []: + kcl_error.report_exception( + file_msgs=[ + kcl_error.ErrFileMsg( + filename=import_spec.filename, + line_no=import_spec.line, + col_no=import_spec.column, + ) + ], + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg=f"Cannot import {file} in the main package", + ) + + +def ResolveProgram( + program: ast.Program, config: CheckConfig = CheckConfig() +) -> ProgramScope: + """Resolve program including the import check and the type check""" + ResolveProgramImport(program) + return TypeChecker(program, config).check() diff --git a/internal/kclvm_py/kcl/types/scope.py b/internal/kclvm_py/kcl/types/scope.py new file mode 100644 index 000000000..1d3ab97ae --- /dev/null +++ b/internal/kclvm_py/kcl/types/scope.py @@ -0,0 +1,1203 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +from typing import List, Dict, Optional, cast +from dataclasses import dataclass, field + +import kclvm.kcl.ast as ast +import kclvm.api.object as objpkg +import kclvm.api.object.internal as internal +import kclvm.compiler.extension.builtin.builtin as builtin +import kclvm.compiler.extension.plugin as plugin + +from .type import ( + Type, + ANY_TYPE, + STR_TYPE, + BOOL_TYPE, + INT_TYPE, + FLOAT_TYPE, + LIST_ANY_TYPE, + LIST_STR_TYPE, + DICT_ANY_ANY_TYPE, + ITERABLE_TYPE_STR, + ITERABLE_TYPE, + NUMBER_TYPE_STR, + NUMBER_TYPE, +) + + +@dataclass +class ScopeObject: + """The object stored in the scope + + Parameters + ---------- + name: str + The scope object name. + node: ast.AST + The scope object AST node reference. + type: Type + The type of the scope object. + pos: ast.Position + The scope object start position. + end: ast.Position + The scope object end position. + """ + + name: str + node: Optional[ast.AST] + type: Type + pos: ast.Position = None + end: ast.Position = None + + def check_pos_valid(self): + return ( + self.node + and self.node.pos + and self.node.pos.is_valid() + and self.node.end_pos + and self.node.end_pos.is_valid() + ) + + +@dataclass +class Scope: + """A Scope maintains a set of objects and links to its containing + (parent) and contained (children) scopes. Objects may be inserted + and looked up by name. The zero value for Scope is a ready-to-use + empty scope. + + Parameters + ---------- + parent: Scope + The parent scope. + node: ast.AST + The scope AST node reference. + children: Type: + The child scope list. + elems: Dict[str, ScopeObject] + The scope object mapping with its name. + pos: ast.Position + The scope start position. + end: ast.Position + The scope end position. + """ + + parent: "Scope" = None + node: ast.AST = None + children: List["Scope"] = field(default_factory=list) + elems: Dict[str, ScopeObject] = field(default_factory=dict) + pos: ast.Position = None + end: ast.Position = None + + def contains_pos(self, pos: ast.Position) -> bool: + """ + check if current scope contains a position + :param pos: the given position + :return: if current scope contains the given position + """ + if isinstance(self.node, ast.SchemaStmt): + for item in [ + *(self.node.body if self.node.body else []), + *(self.node.checks if self.node.checks else []), + self.node.index_signature, + ]: + if item and item.contains_pos(pos): + return True + return False + elif isinstance(self.node, ast.RuleStmt): + for item in self.node.checks or []: + if item and item.contains_pos(pos): + return True + return False + return self.pos and self.pos.less_equal(pos) and pos.less_equal(self.end) + + def inner_most(self, pos: ast.Position) -> Optional["Scope"]: + # self is BUILTIN_SCOPE + if self.parent is None: + for child in self.children or []: + if child.contains_pos(pos): + return child.inner_most(pos) + return None + # self is not BUILTIN_SCOPE + if self.contains_pos(pos): + for child in self.children or []: + if child.contains_pos(pos): + return child.inner_most(pos) + return self + return None + + def get_enclosing_scope(self) -> Optional["Scope"]: + return self.parent + + def get_parent_schema_scope( + self, program_scope: "ProgramScope" + ) -> Optional["Scope"]: + if ( + self.node + and isinstance(self.node, ast.SchemaStmt) + and self.node.parent_name + ): + schema = self.parent.elems[self.node.name] + if not isinstance(schema.type, objpkg.KCLSchemaDefTypeObject): + return None + schema_type_def_obj = cast(objpkg.KCLSchemaDefTypeObject, schema.type) + if ( + not schema_type_def_obj.schema_type + or not schema_type_def_obj.schema_type.base + ): + return None + base_type_obj = schema_type_def_obj.schema_type.base + # the schema and its base schema are in the same scope + if schema_type_def_obj.schema_type.pkgpath == base_type_obj.pkgpath: + return self.parent.search_child_scope_by_name(base_type_obj.name) + # the schema and its base schema are in the different scopes + base_pkg_scope = program_scope.scope_map.get(base_type_obj.pkgpath) + if not base_pkg_scope: + return None + return base_pkg_scope.search_child_scope_by_name(base_type_obj.name) + return None + + def search_child_scope_by_name(self, name: str) -> Optional["Scope"]: + assert name + if name not in self.elems: + return None + elem: ScopeObject = self.elems[name] + for child in self.children: + if child.node == elem.node: + return child + return None + + +@dataclass +class PackageScope(Scope): + file_begin_position_map: Dict[str, ast.Position] = field(default_factory=dict) + file_end_position_map: Dict[str, ast.Position] = field(default_factory=dict) + + def contains_pos(self, pos: ast.Position) -> bool: + """ + check if current package scope contains a position + :param pos: the given position + :return: if current package scope contains the given position + """ + assert pos.filename + file_begin_pos = self.file_begin_position_map.get(pos.filename) + file_end_pos = self.file_end_position_map.get(pos.filename) + return ( + file_begin_pos + and file_end_pos + and pos + and file_begin_pos.less_equal(pos) + and pos.less_equal(file_end_pos) + ) + + +# Decorator scope including decorator function type such as `deprecated`. +DECORATOR_SCOPE: Scope = Scope( + elems={ + internal.Deprecated.NAME: ScopeObject( + name=internal.Deprecated.NAME, + node=None, + type=objpkg.KCLFunctionTypeObject( + name=internal.Deprecated.NAME, + params=[ + objpkg.Parameter( + name="version", + type_annotation="str", + type=STR_TYPE, + ), + objpkg.Parameter( + name="reason", + type_annotation="str", + type=STR_TYPE, + ), + objpkg.Parameter( + name="strict", + type_annotation="bool", + type=BOOL_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=ANY_TYPE, + doc=internal.Deprecated.__doc__, + ), + ), + internal.Info.NAME: ScopeObject( + name=internal.Info.NAME, + node=None, + type=objpkg.KCLFunctionTypeObject( + name=internal.Info.NAME, + params=[], + self_type=ANY_TYPE, + return_type=ANY_TYPE, + doc=internal.Info.__doc__, + ), + ), + } +) + + +# Builtin-function types table +BUILTIN_SCOPE: Scope = Scope( + elems={ + # option(key: str, *, type: str = "", required: bool = False, default: any = None, help: str = "", file: str = "", line: int = 0) -> any + "option": ScopeObject( + name="option", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="option", + params=[ + objpkg.Parameter( + name="key", + value=None, + type_annotation="str", + type=STR_TYPE, + ), + objpkg.Parameter( + name="type", + value=objpkg.KCLStringObject(""), + type_annotation="str", + type=STR_TYPE, + ), + objpkg.Parameter( + name="required", + value=objpkg.KCLFalseObject.instance(), + type_annotation="bool", + type=BOOL_TYPE, + ), + objpkg.Parameter( + name="default", + value=objpkg.KCLNoneObject.instance(), + type_annotation="any", + type=ANY_TYPE, + ), + objpkg.Parameter( + name="help", + value=objpkg.KCLStringObject(""), + type_annotation="str", + type=STR_TYPE, + ), + objpkg.Parameter( + name="help", + value=objpkg.KCLStringObject(""), + type_annotation="str", + type=STR_TYPE, + ), + objpkg.Parameter( + name="file", + value=objpkg.KCLStringObject(""), + type_annotation="str", + type=STR_TYPE, + ), + objpkg.Parameter( + name="line", + value=objpkg.KCLIntObject(0), + type_annotation="int", + type=INT_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=ANY_TYPE, + doc=builtin.KMANGLED_option.__doc__, + ), + ), + # print(value, ..., sep=' ', end='\n') -> NONE_TYPE + "print": ScopeObject( + name="print", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="print", + params=[], + self_type=ANY_TYPE, + return_type=ANY_TYPE, + doc=builtin.KMANGLED_print.__doc__, + is_variadic=True, + ), + ), + # multiplyof(a: int, b: int) -> bool + "multiplyof": ScopeObject( + name="multiplyof", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="multiplyof", + params=[ + objpkg.Parameter( + name="a", + value=None, + type_annotation="int", + type=INT_TYPE, + ), + objpkg.Parameter( + name="b", + value=None, + type_annotation="int", + type=INT_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=INT_TYPE, + doc=builtin.KMANGLED_multiplyof.__doc__, + ), + ), + # isunique(inval: List[Any]) -> bool + "isunique": ScopeObject( + name="isunique", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="isunique", + params=[ + objpkg.Parameter( + name="inval", + value=None, + type_annotation="[]", + type=LIST_ANY_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=BOOL_TYPE, + doc=builtin.KMANGLED_isunique.__doc__, + ), + ), + # len(inval: Union[dict, list, schema, str]) -> int + "len": ScopeObject( + name="len", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="len", + params=[ + objpkg.Parameter( + name="inval", + value=None, + type_annotation=ITERABLE_TYPE_STR, + type=ITERABLE_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=INT_TYPE, + doc=builtin.KMANGLED_len.__doc__, + ), + ), + # abs(inval: Union[int, float, bool]) -> Union[int, float, bool] + "abs": ScopeObject( + name="abs", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="abs", + params=[ + objpkg.Parameter( + name="inval", + value=None, + type_annotation="any", + type=ANY_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=ANY_TYPE, + doc=builtin.KMANGLED_abs.__doc__, + ), + ), + # all_true(inval: Union[dict, list, schema, str]) -> bool + "all_true": ScopeObject( + name="all_true", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="all_true", + params=[ + objpkg.Parameter( + name="inval", + value=None, + type_annotation="[]", + type=LIST_ANY_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=BOOL_TYPE, + doc=builtin.KMANGLED_all_true.__doc__, + ), + ), + # any_true(inval: Union[dict, list, schema, str]) -> bool + "any_true": ScopeObject( + name="any_true", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="any_true", + params=[ + objpkg.Parameter( + name="inval", + value=None, + type_annotation="[]", + type=LIST_ANY_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=BOOL_TYPE, + doc=builtin.KMANGLED_any_true.__doc__, + ), + ), + # hex(number: int) -> str + "hex": ScopeObject( + name="hex", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="hex", + params=[ + objpkg.Parameter( + name="number", + value=None, + type_annotation="int", + type=INT_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=STR_TYPE, + doc=builtin.KMANGLED_hex.__doc__, + ), + ), + # bin(number: int) -> str + "bin": ScopeObject( + name="bin", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="bin", + params=[ + objpkg.Parameter( + name="number", + value=None, + type_annotation="int", + type=INT_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=STR_TYPE, + doc=builtin.KMANGLED_bin.__doc__, + ), + ), + # oct(number: int) -> str + "oct": ScopeObject( + name="oct", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="oct", + params=[ + objpkg.Parameter( + name="number", + value=None, + type_annotation="int", + type=INT_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=STR_TYPE, + doc=builtin.KMANGLED_oct.__doc__, + ), + ), + # ord(c: str) -> int + "ord": ScopeObject( + name="ord", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="ord", + params=[ + objpkg.Parameter( + name="number", + value=None, + type_annotation="int", + type=STR_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=INT_TYPE, + doc=builtin.KMANGLED_ord.__doc__, + ), + ), + # sorted(inval: Union[dict, list, schema, str]) -> List[Any] + "sorted": ScopeObject( + name="sorted", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="sorted", + params=[ + objpkg.Parameter( + name="inval", + value=None, + type_annotation=ITERABLE_TYPE_STR, + type=ITERABLE_TYPE, + ), + objpkg.Parameter( + name="reverse", + value=objpkg.KCLFalseObject.instance(), + type_annotation="bool", + type=BOOL_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=LIST_ANY_TYPE, + doc=builtin.KMANGLED_sorted.__doc__, + ), + ), + # range(start: int, stop: int, step: int = None) -> list + "range": ScopeObject( + name="range", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="range", + params=[ + objpkg.Parameter( + name="start", + value=None, + type_annotation="int", + type=INT_TYPE, + ), + objpkg.Parameter( + name="stop", + value=None, + type_annotation="int", + type=INT_TYPE, + ), + objpkg.Parameter( + name="step", + value=objpkg.KCLNoneObject.instance(), + type_annotation="int", + type=INT_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=objpkg.KCLListTypeObject(item_type=INT_TYPE), + doc=builtin.KMANGLED_range.__doc__, + ), + ), + # max(iterable: List[Any]) -> Any + "max": ScopeObject( + name="max", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="max", + params=[], + self_type=ANY_TYPE, + return_type=ANY_TYPE, + doc=builtin.KMANGLED_max.__doc__, + ), + ), + # min(iterable: List[Any]) -> Any + "min": ScopeObject( + name="min", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="min", + params=[], + self_type=ANY_TYPE, + return_type=ANY_TYPE, + doc=builtin.KMANGLED_min.__doc__, + ), + ), + # sum(iterable: List[Any], start: Any = 0) -> Any + "sum": ScopeObject( + name="sum", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="sum", + params=[ + objpkg.Parameter( + name="iterable", + value=None, + type_annotation="[]", + type=LIST_ANY_TYPE, + ), + objpkg.Parameter( + name="start", + value=objpkg.KCLIntObject(0), + type_annotation="any", + type=ANY_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=ANY_TYPE, + doc=builtin.KMANGLED_sum.__doc__, + ), + ), + # pow(x: number, y: number, z: number) -> number + "pow": ScopeObject( + name="pow", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="pow", + params=[ + objpkg.Parameter( + name="x", + value=None, + type_annotation=NUMBER_TYPE_STR, + type=NUMBER_TYPE, + ), + objpkg.Parameter( + name="y", + value=None, + type_annotation=NUMBER_TYPE_STR, + type=NUMBER_TYPE, + ), + objpkg.Parameter( + name="z", + value=None, + type_annotation=NUMBER_TYPE_STR, + type=NUMBER_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=NUMBER_TYPE, + doc=builtin.KMANGLED_pow.__doc__, + ), + ), + # round(number: number, ndigits: int) -> number + "round": ScopeObject( + name="round", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="round", + params=[ + objpkg.Parameter( + name="number", + value=None, + type_annotation=NUMBER_TYPE_STR, + type=NUMBER_TYPE, + ), + objpkg.Parameter( + name="ndigits", + value=objpkg.KCLNoneObject.instance(), + type_annotation="int", + type=INT_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=NUMBER_TYPE, + doc=builtin.KMANGLED_round.__doc__, + ), + ), + # zip(*args) -> List[Any] + "zip": ScopeObject( + name="zip", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="zip", + params=[], + self_type=ANY_TYPE, + return_type=LIST_ANY_TYPE, + doc=builtin.KMANGLED_zip.__doc__, + is_variadic=True, + ), + ), + # int(number: number|str) -> int + "int": ScopeObject( + name="int", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="int", + params=[ + objpkg.Parameter( + name="number", + value=None, + type_annotation="any", + type=ANY_TYPE, + ), + objpkg.Parameter( + name="base", + value=objpkg.KCLIntObject(10), + type_annotation="int", + type=ANY_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=INT_TYPE, + doc=builtin.KMANGLED_int.__doc__, + ), + ), + # float(number: number|str) -> float + "float": ScopeObject( + name="float", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="float", + params=[ + objpkg.Parameter( + name="number", + value=None, + type_annotation="any", + type=ANY_TYPE, + ) + ], + self_type=ANY_TYPE, + return_type=FLOAT_TYPE, + doc=builtin.KMANGLED_float.__doc__, + ), + ), + # list(x: any) -> [] + "list": ScopeObject( + name="list", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="list", + params=[ + objpkg.Parameter( + name="x", + value=None, + type_annotation="any", + type=ANY_TYPE, + ) + ], + self_type=ANY_TYPE, + return_type=LIST_ANY_TYPE, + doc=builtin.KMANGLED_list.__doc__, + ), + ), + # dict(x: any) -> {:} + "dict": ScopeObject( + name="dict", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="dict", + params=[ + objpkg.Parameter( + name="x", + value=None, + type_annotation="any", + type=ANY_TYPE, + ) + ], + self_type=ANY_TYPE, + return_type=DICT_ANY_ANY_TYPE, + doc=builtin.KMANGLED_dict.__doc__, + ), + ), + # bool(x: any) -> bool + "bool": ScopeObject( + name="bool", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="bool", + params=[ + objpkg.Parameter( + name="x", + value=None, + type_annotation="any", + type=ANY_TYPE, + ) + ], + self_type=ANY_TYPE, + return_type=BOOL_TYPE, + doc=builtin.KMANGLED_bool.__doc__, + ), + ), + # str(obj: any) -> str + "str": ScopeObject( + name="str", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="str", + params=[ + objpkg.Parameter( + name="x", + value=None, + type_annotation="any", + type=ANY_TYPE, + ) + ], + self_type=ANY_TYPE, + return_type=STR_TYPE, + doc=builtin.KMANGLED_str.__doc__, + ), + ), + # typeof(x: any, *, full_name: bool = False) -> str: + "typeof": ScopeObject( + name="typeof", + node=None, + type=objpkg.KCLFunctionTypeObject( + name="typeof", + params=[ + objpkg.Parameter( + name="x", + value=None, + type_annotation="any", + type=ANY_TYPE, + ), + objpkg.Parameter( + name="full_name", + value=objpkg.KCLFalseObject.instance(), + type_annotation="bool", + type=BOOL_TYPE, + ), + ], + self_type=ANY_TYPE, + return_type=STR_TYPE, + doc=builtin.KMANGLED_typeof.__doc__, + ), + ), + } +) + +# System module types table +MODULE_SCOPE_MAPPING: Dict[str, Scope] = { + **{name: Scope(elems={}) for name in builtin.STANDARD_SYSTEM_MODULES}, + **{ + "units": Scope( + elems={ + "NumberMultiplier": ScopeObject( + name="NumberMultiplier", + node=None, + type=objpkg.KCLNumberMultiplierTypeObject(), + ), + } + ), + }, +} + +try: + # Plugin module types table + PLUGIN_SCOPE_MAPPING: Dict[str, Scope] = { + "{}".format(plugin.PLUGIN_MODULE_NAME + name): Scope(elems={}) + for name in plugin.get_plugin_names() + } +except Exception: + PLUGIN_SCOPE_MAPPING: Dict[str, Scope] = {} + + +# Member function or property types table e.g., str.replace and schema.instances +SCHEMA_TYPE_MEMBER_SCOPE: Scope = Scope( + elems={ + "instances": ScopeObject( + name=objpkg.KCLSchemaTypeObject.instances.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name=objpkg.KCLSchemaTypeObject.instances.__name__, + params=[], + self_type=DICT_ANY_ANY_TYPE, + return_type=LIST_ANY_TYPE, + doc=objpkg.KCLSchemaTypeObject.instances.__doc__, + ), + ) + } +) + +STR_TYPE_MEMBER_SCOPE: Scope = Scope( + elems={ + "capitalize": ScopeObject( + name="".capitalize.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".capitalize.__name__, + params=[], + self_type=STR_TYPE, + return_type=STR_TYPE, + doc="".capitalize.__doc__, + ), + ), + "count": ScopeObject( + name="".count.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".count.__name__, + params=[], + self_type=STR_TYPE, + return_type=INT_TYPE, + doc="".count.__doc__, + ), + ), + "endswith": ScopeObject( + name="".endswith.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".endswith.__name__, + params=[], + self_type=STR_TYPE, + return_type=BOOL_TYPE, + doc="".endswith.__doc__, + ), + ), + "find": ScopeObject( + name="".find.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".find.__name__, + params=[], + self_type=STR_TYPE, + return_type=INT_TYPE, + doc="".find.__doc__, + ), + ), + "format": ScopeObject( + name="".format.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".format.__name__, + params=[], + self_type=STR_TYPE, + return_type=STR_TYPE, + doc="".format.__doc__, + ), + ), + "index": ScopeObject( + name="".index.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".index.__name__, + params=[], + self_type=STR_TYPE, + return_type=INT_TYPE, + doc="".index.__doc__, + ), + ), + "isalnum": ScopeObject( + name="".isalnum.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".isalnum.__name__, + params=[], + self_type=STR_TYPE, + return_type=BOOL_TYPE, + doc="".isalnum.__doc__, + ), + ), + "isalpha": ScopeObject( + name="".isalpha.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".isalpha.__name__, + params=[], + self_type=STR_TYPE, + return_type=BOOL_TYPE, + doc="".isalpha.__doc__, + ), + ), + "isdigit": ScopeObject( + name="".isdigit.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".isdigit.__name__, + params=[], + self_type=STR_TYPE, + return_type=BOOL_TYPE, + doc="".isdigit.__doc__, + ), + ), + "islower": ScopeObject( + name="".islower.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".islower.__name__, + params=[], + self_type=STR_TYPE, + return_type=BOOL_TYPE, + doc="".islower.__doc__, + ), + ), + "isspace": ScopeObject( + name="".isspace.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".isspace.__name__, + params=[], + self_type=STR_TYPE, + return_type=BOOL_TYPE, + doc="".isspace.__doc__, + ), + ), + "istitle": ScopeObject( + name="".istitle.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".istitle.__name__, + params=[], + self_type=STR_TYPE, + return_type=BOOL_TYPE, + doc="".istitle.__doc__, + ), + ), + "isupper": ScopeObject( + name="".isupper.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".isupper.__name__, + params=[], + self_type=STR_TYPE, + return_type=BOOL_TYPE, + doc="".isupper.__doc__, + ), + ), + "join": ScopeObject( + name="".join.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".join.__name__, + params=[], + self_type=STR_TYPE, + return_type=STR_TYPE, + doc="".join.__doc__, + ), + ), + "lower": ScopeObject( + name="".lower.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".lower.__name__, + params=[], + self_type=STR_TYPE, + return_type=STR_TYPE, + doc="".lower.__doc__, + ), + ), + "upper": ScopeObject( + name="".upper.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".upper.__name__, + params=[], + self_type=STR_TYPE, + return_type=STR_TYPE, + doc="".upper.__doc__, + ), + ), + "lstrip": ScopeObject( + name="".lstrip.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".lstrip.__name__, + params=[], + self_type=STR_TYPE, + return_type=STR_TYPE, + doc="".lstrip.__doc__, + ), + ), + "rstrip": ScopeObject( + name="".rstrip.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".rstrip.__name__, + params=[], + self_type=STR_TYPE, + return_type=STR_TYPE, + doc="".rstrip.__doc__, + ), + ), + "replace": ScopeObject( + name="".replace.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".replace.__name__, + params=[], + self_type=STR_TYPE, + return_type=STR_TYPE, + doc="".replace.__doc__, + ), + ), + "rfind": ScopeObject( + name="".rfind.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".rfind.__name__, + params=[], + self_type=STR_TYPE, + return_type=INT_TYPE, + doc="".rfind.__doc__, + ), + ), + "rindex": ScopeObject( + name="".rindex.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".rindex.__name__, + params=[], + self_type=STR_TYPE, + return_type=INT_TYPE, + doc="".rindex.__doc__, + ), + ), + "rsplit": ScopeObject( + name="".rsplit.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".rsplit.__name__, + params=[], + self_type=STR_TYPE, + return_type=LIST_STR_TYPE, + doc="".rsplit.__doc__, + ), + ), + "split": ScopeObject( + name="".split.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".split.__name__, + params=[], + self_type=STR_TYPE, + return_type=LIST_STR_TYPE, + doc="".split.__doc__, + ), + ), + "splitlines": ScopeObject( + name="".splitlines.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".splitlines.__name__, + params=[], + self_type=STR_TYPE, + return_type=LIST_STR_TYPE, + doc="".splitlines.__doc__, + ), + ), + "startswith": ScopeObject( + name="".startswith.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".startswith.__name__, + params=[], + self_type=STR_TYPE, + return_type=BOOL_TYPE, + doc="".startswith.__doc__, + ), + ), + "strip": ScopeObject( + name="".strip.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".strip.__name__, + params=[], + self_type=STR_TYPE, + return_type=STR_TYPE, + doc="".strip.__doc__, + ), + ), + "title": ScopeObject( + name="".title.__name__, + node=None, + type=objpkg.KCLFunctionTypeObject( + name="".title.__name__, + params=[], + self_type=STR_TYPE, + return_type=STR_TYPE, + doc="".title.__doc__, + ), + ), + } +) + + +@dataclass +class ProgramScope: + scope_map: Dict[str, Scope] + builtin_scope: Scope = BUILTIN_SCOPE + plugin_scope_map: Scope = field(default_factory=lambda: PLUGIN_SCOPE_MAPPING) + system_module_scope_map: Dict[str, Scope] = field( + default_factory=lambda: MODULE_SCOPE_MAPPING + ) + member_scope_map: Dict[str, Scope] = field( + default_factory=lambda: { + objpkg.KCLTypeKind.StrKind: SCHEMA_TYPE_MEMBER_SCOPE, + objpkg.KCLTypeKind.SchemaKind: STR_TYPE_MEMBER_SCOPE, + } + ) + schema_reference: objpkg.SchemaTypeRefGraph = None + + @property + def main_scope(self) -> Scope: + return self.scope_map[ast.Program.MAIN_PKGPATH] + + @property + def pkgpaths(self) -> List[str]: + return list(self.scope_map.keys()) diff --git a/internal/kclvm_py/kcl/types/type.py b/internal/kclvm_py/kcl/types/type.py new file mode 100644 index 000000000..3eab448ac --- /dev/null +++ b/internal/kclvm_py/kcl/types/type.py @@ -0,0 +1,286 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +from typing import cast, List + +import kclvm.api.object as objpkg +import kclvm.unification.subsume as subsume + +# ------------------------------------ +# Type alias +# ------------------------------------ + +Type = objpkg.KCLBaseTypeObject + +# ------------------------------------ +# Type annotation str constants +# ------------------------------------ + +ANY_TYPE_STR = "any" +INT_TYPE_STR = "int" +FLOAT_TYPE_STR = "float" +STR_TYPE_STR = "str" +BOOL_TYPE_STR = "bool" +ITERABLE_TYPE_STR = "str|{:}|[]" +NUMBER_TYPE_STR = "int|float|bool" +NUM_OR_STR_TYPE_STR = "int|float|bool|str" +RESERVED_TYPE_IDENTIFIERS = [ + ANY_TYPE_STR, + INT_TYPE_STR, + FLOAT_TYPE_STR, + STR_TYPE_STR, + BOOL_TYPE_STR, +] + +# ------------------------------------ +# Type constants +# ------------------------------------ + +VOID_TYPE: Type = objpkg.KCLVoidTypeObject() +NONE_TYPE: Type = objpkg.KCLNoneTypeObject() +INT_TYPE: Type = objpkg.KCLIntTypeObject() +FLOAT_TYPE: Type = objpkg.KCLFloatTypeObject() +STR_TYPE: Type = objpkg.KCLStringTypeObject() +BOOL_TYPE: Type = objpkg.KCLBoolTypeObject() +ANY_TYPE: Type = objpkg.KCLAnyTypeObject() +TRUE_LIT_TYPE: Type = objpkg.KCLBoolLitTypeObject(value=True) +FALSE_LIT_TYPE: Type = objpkg.KCLBoolLitTypeObject(value=False) +NUMBER_TYPE: Type = objpkg.KCLUnionTypeObject(types=[INT_TYPE, FLOAT_TYPE, BOOL_TYPE]) +NUM_OR_STR_TYPE: Type = objpkg.KCLUnionTypeObject( + types=[INT_TYPE, FLOAT_TYPE, BOOL_TYPE, STR_TYPE] +) +LIST_ANY_TYPE: Type = objpkg.KCLListTypeObject(item_type=ANY_TYPE) +LIST_STR_TYPE: Type = objpkg.KCLListTypeObject(item_type=STR_TYPE) +DICT_ANY_ANY_TYPE: Type = objpkg.KCLDictTypeObject( + key_type=ANY_TYPE, value_type=ANY_TYPE +) +DICT_STR_ANY_TYPE: Type = objpkg.KCLDictTypeObject( + key_type=STR_TYPE, value_type=ANY_TYPE +) +DICT_STR_STR_TYPE: Type = objpkg.KCLDictTypeObject( + key_type=STR_TYPE, value_type=STR_TYPE +) +INT_OR_STR_TYPE: Type = objpkg.KCLUnionTypeObject(types=[INT_TYPE, STR_TYPE]) +ITERABLE_TYPE: Type = objpkg.KCLUnionTypeObject( + types=[LIST_ANY_TYPE, DICT_ANY_ANY_TYPE, STR_TYPE] +) +LIT_TYPE_KIND_MAPPING = { + objpkg.KCLTypeKind.StrLitKind: STR_TYPE, + objpkg.KCLTypeKind.IntLitKind: INT_TYPE, + objpkg.KCLTypeKind.FloatLitKind: FLOAT_TYPE, + objpkg.KCLTypeKind.BoolLitKind: BOOL_TYPE, +} + +# ------------------------------------ +# Type kind constants +# ------------------------------------ + +NUMBER_TYPE_KINDS = [ + objpkg.KCLTypeKind.IntKind, + objpkg.KCLTypeKind.FloatKind, + objpkg.KCLTypeKind.IntLitKind, + objpkg.KCLTypeKind.FloatLitKind, + # Please note that the True/False name constant can be used to 1 + True + objpkg.KCLTypeKind.BoolKind, + objpkg.KCLTypeKind.BoolLitKind, +] +STR_KINDS = [ + objpkg.KCLTypeKind.StrKind, + objpkg.KCLTypeKind.StrLitKind, +] +INT_KINDS = [ + objpkg.KCLTypeKind.IntKind, + objpkg.KCLTypeKind.IntLitKind, +] +FLOAT_KINDS = [ + objpkg.KCLTypeKind.FloatKind, + objpkg.KCLTypeKind.FloatLitKind, +] +BOOL_KINDS = [ + objpkg.KCLTypeKind.BoolKind, + objpkg.KCLTypeKind.BoolLitKind, +] +ITERABLE_KINDS = [ + objpkg.KCLTypeKind.ListKind, + objpkg.KCLTypeKind.DictKind, + objpkg.KCLTypeKind.SchemaKind, + objpkg.KCLTypeKind.StrKind, + objpkg.KCLTypeKind.StrLitKind, +] +KEY_KINDS = STR_KINDS +BUILTIN_KINDS = NUMBER_TYPE_KINDS + STR_KINDS + +# ----------------------- +# Type functions +# ----------------------- + + +def sup(types: List[Type]) -> Type: + """The sup function returns the minimum supremum of all types in an array of types""" + return typeof(types, should_remove_sub_types=True) + + +def typeof(types: List[Type], should_remove_sub_types: bool = False) -> Type: + """Build a sup type from types [T1, T2, ... , Tn]""" + assert isinstance(types, list) + # 1. Initialize an ordered set to store the type array + type_set = [] + # 2. Add the type array to the ordered set for sorting by the type id and de-duplication + add_types_to_type_set(type_set, types) + # 3. Remove sub types according to partial order relation rules e.g. sub schema types + if should_remove_sub_types: + type_set = remove_sub_types(type_set) + if len(type_set) == 0: + return ANY_TYPE + if len(type_set) == 1: + return type_set[0] + type_set.sort(key=lambda t: t.type_kind()) + return objpkg.KCLUnionTypeObject(types=type_set) + + +def add_types_to_type_set(type_set: List[Type], types: List[Type]): + """Add types into the type set""" + for tpe in types or []: + add_type_to_type_set(type_set, tpe) + + +def add_type_to_type_set(type_set: List[Type], tpe: Type): + """Add a type into the type set""" + if isinstance(tpe, objpkg.KCLUnionTypeObject): + tpe = cast(objpkg.KCLUnionTypeObject, tpe) + add_types_to_type_set(type_set, tpe.types) + # Ignore 'void' types in unions + elif not isinstance(tpe, objpkg.KCLVoidTypeObject): + if tpe not in type_set: + type_set.append(tpe) + + +def remove_sub_types(type_set: List[Type]) -> List[Type]: + """Remove sub types from the type set""" + remove_index_list = set() + for i, source in enumerate(type_set): + for j, target in enumerate(type_set): + if i != j: + is_subsume = subsume.type_subsume(source, target) + if is_subsume: + remove_index_list.add(i) + return [tpe for i, tpe in enumerate(type_set) if i not in remove_index_list] + + +def assignable_to(tpe: Type, expected_type: Type) -> bool: + """Judge type `tpe` can be assigned the expected type""" + if tpe.type_kind() >= objpkg.KCLTypeKind.VoidKind: + return False + if ( + tpe.type_kind() + == expected_type.type_kind() + == objpkg.KCLTypeKind.NumberMultiplierKind + ): + if expected_type.is_literal(): + return expected_type.type_str() == tpe.type_str() + else: + return True + return subsume.type_subsume(tpe, expected_type, check_left_any=True) + + +def is_upper_bound(type1: Type, type2: Type) -> bool: + """Whether `type1` is the upper bound of the `type2`""" + return subsume.type_subsume(type2, type1) + + +def has_any_type(types: List[Type]): + """Whether a type array contains any type""" + return any([t == ANY_TYPE for t in types]) + + +def infer_to_variable_type(tpe: Type): + """Infer the value type to the variable type""" + if tpe is None: + return tpe + # Literal type to its named type e.g., 1 -> int, "s" -> str + if tpe.type_kind() in LIT_TYPE_KIND_MAPPING: + return LIT_TYPE_KIND_MAPPING[tpe.type_kind()] + # Union type e.g., 1|2|"s" -> int|str + if tpe.type_kind() == objpkg.KCLTypeKind.UnionKind: + return sup([infer_to_variable_type(t) for t in tpe.types]) + # List type e.g., [1|2] -> [int] + if tpe.type_kind() == objpkg.KCLTypeKind.ListKind: + tpe.item_type = infer_to_variable_type(tpe.item_type) + # Dict type e.g., {str:1|2} -> {str:int} + if tpe.type_kind() == objpkg.KCLTypeKind.DictKind: + tpe.key_type = infer_to_variable_type(tpe.key_type) + tpe.value_type = infer_to_variable_type(tpe.value_type) + # None/Undefined type to any type e.g., None -> any + if tpe == NONE_TYPE: + return ANY_TYPE + return tpe + + +def literal_union_type_to_variable_type(tpe: Type): + """Convert the literal union type to its variable type + e.g., 1|2 -> int, 's'|'ss' -> str. + """ + if tpe.type_kind() == objpkg.KCLTypeKind.UnionKind: + return infer_to_variable_type(tpe) + return tpe + + +def is_kind_type_or_kind_union_type(tpe: Type, kind_list: List[int]): + """Judge a type kind in the type kind list or the union + type kinds are all in the type kind. + """ + result = False + if tpe.type_kind() == objpkg.KCLTypeKind.UnionKind: + result = all([t.type_kind() in kind_list for t in tpe.types]) + elif tpe.type_kind() in kind_list: + result = True + return result + + +def type_to_kcl_type_annotation_str(tpe: Type) -> str: + """Convert type to a kcl type annotation string""" + if not tpe or not isinstance(tpe, Type): + raise ValueError(f"Parameter type must be a valid type, got {tpe}") + if isinstance(tpe, objpkg.KCLUnionTypeObject): + return "|".join([type_to_kcl_type_annotation_str(t) for t in tpe.types]) + elif isinstance(tpe, objpkg.KCLIntTypeObject): + return "int" + elif isinstance(tpe, objpkg.KCLFloatTypeObject): + return "float" + elif isinstance(tpe, objpkg.KCLStringTypeObject): + return "str" + elif isinstance(tpe, objpkg.KCLBoolTypeObject): + return "bool" + elif isinstance(tpe, objpkg.KCLAnyTypeObject): + return "any" + elif isinstance(tpe, objpkg.KCLStringLitTypeObject): + return '"{}"'.format(tpe.value.replace('"', '\\"')) + elif isinstance( + tpe, + ( + objpkg.KCLIntLitTypeObject, + objpkg.KCLFloatLitTypeObject, + objpkg.KCLBoolLitTypeObject, + ), + ): + return str(tpe.value) + elif isinstance(tpe, objpkg.KCLListTypeObject): + return "[" + type_to_kcl_type_annotation_str(tpe.item_type) + "]" + elif isinstance(tpe, objpkg.KCLDictTypeObject): + return ( + "{" + + type_to_kcl_type_annotation_str(tpe.key_type) + + ":" + + type_to_kcl_type_annotation_str(tpe.value_type) + + "}" + ) + elif isinstance(tpe, objpkg.KCLSchemaDefTypeObject): + return tpe.schema_type.type_str_with_pkgpath() + elif isinstance(tpe, objpkg.KCLSchemaTypeObject): + return tpe.type_str_with_pkgpath() + elif isinstance(tpe, objpkg.KCLNumberMultiplierTypeObject): + return ( + f"{tpe.raw_value}{tpe.binary_suffix}" + if tpe.is_literal() + else "units.NumberMultiplier" + ) + return "" diff --git a/internal/kclvm_py/kcl/types/type_convension.py b/internal/kclvm_py/kcl/types/type_convension.py new file mode 100644 index 000000000..20f5fd21f --- /dev/null +++ b/internal/kclvm_py/kcl/types/type_convension.py @@ -0,0 +1,71 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import kclvm.kcl.error as kcl_error +import kclvm.api.object as objpkg + +from .type import Type + + +def type_convert(obj: objpkg.KCLObject, tpe: Type) -> objpkg.KCLObject: + """The type of `obj` is converted to another type. + + Raise an runtime error occurs in the type conversion. + """ + if not obj or not isinstance(obj, objpkg.KCLObject): + raise ValueError("Invalid parameter obj, expected KCL object") + if not tpe or not isinstance(tpe, Type): + raise ValueError("Invalid parameter tpe, expected KCL type object") + if isinstance(obj, (objpkg.KCLNoneObject, objpkg.KCLUndefinedObject)): + return obj + if isinstance(tpe, objpkg.KCLAnyTypeObject): + return obj + if isinstance(tpe, objpkg.KCLIntTypeObject) and isinstance( + obj, (objpkg.KCLIntObject, objpkg.KCLFloatObject) + ): + return objpkg.KCLIntObject(int(obj.value)) + if isinstance(tpe, objpkg.KCLFloatTypeObject) and isinstance( + obj, (objpkg.KCLIntObject, objpkg.KCLFloatObject) + ): + return objpkg.KCLFloatObject(float(obj.value)) + if isinstance(tpe, objpkg.KCLStringTypeObject) and isinstance( + obj, objpkg.KCLStringObject + ): + return obj + if isinstance(tpe, objpkg.KCLBoolTypeObject) and isinstance( + obj, objpkg.KCLNameConstantObject + ): + return obj + if isinstance(tpe, objpkg.KCLListTypeObject) and isinstance( + obj, objpkg.KCLListObject + ): + return objpkg.KCLListObject( + items=[type_convert(item, tpe.item_type) for item in obj.items] + ) + if isinstance(tpe, objpkg.KCLDictTypeObject) and isinstance( + obj, objpkg.KCLDictObject + ): + if isinstance(obj, objpkg.KCLSchemaConfigObject): + return objpkg.KCLSchemaConfigObject( + operation_map=obj.operation_map, + insert_index_map=obj.insert_index_map, + value={ + k: type_convert(obj.value[k], tpe.value_type) for k in obj.value + }, + ) + else: + return objpkg.KCLDictObject( + value={k: type_convert(obj.value[k], tpe.value_type) for k in obj.value} + ) + if ( + isinstance(tpe, objpkg.KCLSchemaTypeObject) + and isinstance(obj, objpkg.KCLSchemaObject) + and obj.runtime_type == tpe.runtime_type + ): + return obj + if isinstance(tpe, objpkg.KCLSchemaDefTypeObject): + return type_convert(obj, tpe.schema_type) + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[kcl_error.ErrFileMsg(filename=None, line_no=None, col_no=None)], + arg_msg=f"Cannot convert type '{obj.type_str()}' to '{tpe.type_str()}'", + ) diff --git a/internal/kclvm_py/kcl/types/type_parser.py b/internal/kclvm_py/kcl/types/type_parser.py new file mode 100644 index 000000000..e1c05c7d6 --- /dev/null +++ b/internal/kclvm_py/kcl/types/type_parser.py @@ -0,0 +1,182 @@ +"""The `type_parser` file mainly contains the function `parse_type_str` +which is used to parser a type string to a KCL type object + +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" + +import re as _re + +from typing import Dict +from ast import literal_eval + +import kclvm.api.object as objpkg +import kclvm.api.object.internal.common as common +import kclvm.compiler.build.utils.units as units + +from .type import ( + Type, + INT_TYPE, + FLOAT_TYPE, + STR_TYPE, + BOOL_TYPE, + ANY_TYPE, + TRUE_LIT_TYPE, + FALSE_LIT_TYPE, + DICT_STR_ANY_TYPE, + DICT_STR_STR_TYPE, + sup, +) + +BUILTIN_TYPES = ["str", "bool", "int", "float"] +_KCL_TYPE_any = "any" +_KCL_TYPE_True = "True" +_KCL_TYPE_False = "False" + + +TYPES_MAPPING: Dict[str, Type] = { + "int": INT_TYPE, + "float": FLOAT_TYPE, + "str": STR_TYPE, + "bool": BOOL_TYPE, + "any": ANY_TYPE, + "[]": objpkg.KCLListTypeObject(item_type=ANY_TYPE), + "[any]": objpkg.KCLListTypeObject(item_type=ANY_TYPE), + "[str]": objpkg.KCLListTypeObject(item_type=STR_TYPE), + "{:}": objpkg.KCLDictTypeObject(key_type=ANY_TYPE, value_type=ANY_TYPE), + "{str:}": DICT_STR_ANY_TYPE, + "{str:any}": DICT_STR_ANY_TYPE, + "{str:str}": DICT_STR_STR_TYPE, +} + + +def parse_type_str(tpe_str: str) -> Type: + """Parser a type string to a type object""" + if tpe_str is None or tpe_str == "": + return ANY_TYPE + if not isinstance(tpe_str, str): + raise ValueError(f"Argument tpe_str must be str, not {type(tpe_str)}") + # Remove the space in the type string + tpe_str = tpe_str.strip(" \t\f\v\r\n") + if tpe_str in TYPES_MAPPING: + return TYPES_MAPPING[tpe_str] + # Union type + if is_union_type_str(tpe_str): + return parse_union_type_str(tpe_str) + # Bultin literal type + elif is_lit_type_str(tpe_str): + return parse_lit_type_str(tpe_str) + # Number multiplier literal type + elif is_number_multiplier_literal_type(tpe_str): + return parse_number_multiplier_literal_type(tpe_str) + # Dict type + elif is_dict_type_str(tpe_str): + k_type_str, v_type_str = common.separate_kv(common.dereferencetype(tpe_str)) + return objpkg.KCLDictTypeObject( + key_type=parse_type_str(k_type_str), + value_type=parse_type_str(v_type_str), + ) + # List type + elif is_list_type_str(tpe_str): + return objpkg.KCLListTypeObject( + item_type=parse_type_str(common.dereferencetype(tpe_str)) + ) + # Schema type or pkg.Schema type or named type + return parse_named_type(tpe_str) + + +# ----------------------- +# Judge type string +# ----------------------- + + +def is_union_type_str(tpe: str) -> bool: + """Whether a type string is a union type string, e.g. A|B|C, + and detect '|' in type string except '|' in dict or list. + """ + return common.is_type_union(tpe) + + +def is_list_type_str(tpe: str) -> bool: + """Whether a type string is a list type string""" + return common.islisttype(tpe) + + +def is_dict_type_str(tpe: str) -> bool: + """Whether a type string is a dict type string""" + return common.isdicttype(tpe) + + +def is_lit_type_str(tpe: str) -> bool: + """Whether a type string is a literal type string""" + if tpe in [_KCL_TYPE_True, _KCL_TYPE_False]: + return True + + # str + if tpe.startswith('"'): + return tpe.endswith('"') + if tpe.startswith("'"): + return tpe.endswith("'") + + # int or float + try: + float(tpe) + return True + except ValueError: + pass + + # non literal type + return False + + +def is_number_multiplier_literal_type(tpe: str) -> bool: + """Whether a type string is a number multiplier literal type string""" + return bool(_re.match(units.NUMBER_MULTIPLIER_REGEX, tpe)) + + +# ----------------------- +# Parse type string +# ----------------------- + + +def parse_union_type_str(tpe_str: str) -> Type: + """Parse union type string""" + type_str_list = common.split_type_union(tpe_str) + types = [parse_type_str(tpe) for tpe in type_str_list] + return sup(types) + + +def parse_lit_type_str(tpe_str: str) -> Type: + """Parse literal type string""" + type_val = literal_eval(tpe_str) + if isinstance(type_val, bool): + return TRUE_LIT_TYPE if type_val else FALSE_LIT_TYPE + if isinstance(type_val, str): + return objpkg.KCLStringLitTypeObject(value=type_val) + if isinstance(type_val, int): + return objpkg.KCLIntLitTypeObject(value=type_val) + if isinstance(type_val, float): + return objpkg.KCLFloatLitTypeObject(value=type_val) + raise ValueError(f"Invalid argument tpe_str {tpe_str}") + + +def parse_number_multiplier_literal_type(tpe_str: str) -> Type: + """Parse number multiplier literal type""" + if tpe_str[-1] == units.IEC_SUFFIX: + value, suffix = int(tpe_str[:-2]), tpe_str[-2:] + else: + value, suffix = int(tpe_str[:-1]), tpe_str[-1] + return objpkg.KCLNumberMultiplierTypeObject( + value=units.cal_num(value, suffix), + raw_value=value, + binary_suffix=suffix, + ) + + +def parse_named_type(tpe_str: str) -> Type: + # Please note Named type to find it in the scope (e.g. schema type, type alias) + return objpkg.KCLNamedTypeObject(name=tpe_str) + + +# ----------------------- +# END +# ----------------------- diff --git a/internal/kclvm_py/kcl/types/walker.py b/internal/kclvm_py/kcl/types/walker.py new file mode 100644 index 000000000..c7cdaa968 --- /dev/null +++ b/internal/kclvm_py/kcl/types/walker.py @@ -0,0 +1,30 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +from typing import Callable, cast + +import kclvm.api.object as objpkg + +from .type import Type + + +def WalkType(tpe: Type, walk_fn: Callable): + """Walk one type recursively and deal the type using the `walk_fn`""" + if not tpe or not isinstance(tpe, Type): + return tpe + tpe = walk_fn(tpe) + if tpe.type_kind() == objpkg.KCLTypeKind.UnionKind: + tpe = cast(objpkg.KCLUnionTypeObject, tpe) + types = [] + for t in tpe.types: + t = WalkType(t, walk_fn) + if t: + types.append(t) + tpe.types = types + elif tpe.type_kind() == objpkg.KCLTypeKind.ListKind: + tpe = cast(objpkg.KCLListTypeObject, tpe) + tpe.item_type = WalkType(tpe.item_type, walk_fn) + elif tpe.type_kind() == objpkg.KCLTypeKind.DictKind: + tpe = cast(objpkg.KCLDictTypeObject, tpe) + tpe.key_type = WalkType(tpe.key_type, walk_fn) + tpe.value_type = WalkType(tpe.value_type, walk_fn) + return tpe diff --git a/internal/kclvm_py/program/eval/__init__.py b/internal/kclvm_py/program/eval/__init__.py new file mode 100644 index 000000000..528fe3d27 --- /dev/null +++ b/internal/kclvm_py/program/eval/__init__.py @@ -0,0 +1,3 @@ +from .eval import EvalCode, EvalAST + +__all__ = ["EvalCode", "EvalAST"] diff --git a/internal/kclvm_py/program/eval/eval.py b/internal/kclvm_py/program/eval/eval.py new file mode 100644 index 000000000..e084113ce --- /dev/null +++ b/internal/kclvm_py/program/eval/eval.py @@ -0,0 +1,31 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import Optional + +import kclvm.kcl.ast as ast +import kclvm.compiler.parser.parser as parser +import kclvm.vm as vm +import kclvm.vm.planner as planner +import kclvm.compiler.build.compiler as compiler + +MAIN_PKG_NAME = "__main__" + + +def EvalCode(filename: str, code: Optional[str] = None) -> str: + # Parser + module = parser.ParseFile(filename, code) + return EvalAST(module) + + +def EvalAST(module: ast.Module) -> str: + module.pkg = MAIN_PKG_NAME + # Compiler + bytecode = compiler.CompileProgram( + ast.Program( + root=MAIN_PKG_NAME, main=MAIN_PKG_NAME, pkgs={MAIN_PKG_NAME: [module]} + ) + ) + # VM run + result = vm.Run(bytecode) + # YAML plan + return planner.YAMLPlanner().plan(result.filter_by_path_selector()) diff --git a/internal/kclvm_py/program/eval/readme.md b/internal/kclvm_py/program/eval/readme.md new file mode 100644 index 000000000..1333ed77b --- /dev/null +++ b/internal/kclvm_py/program/eval/readme.md @@ -0,0 +1 @@ +TODO diff --git a/internal/kclvm_py/program/exec/__init__.py b/internal/kclvm_py/program/exec/__init__.py new file mode 100644 index 000000000..dd31f19f5 --- /dev/null +++ b/internal/kclvm_py/program/exec/__init__.py @@ -0,0 +1,7 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .runner import Run + +__all__ = [ + "Run", +] diff --git a/internal/kclvm_py/program/exec/kclvm_cli.py b/internal/kclvm_py/program/exec/kclvm_cli.py new file mode 100644 index 000000000..9626522fd --- /dev/null +++ b/internal/kclvm_py/program/exec/kclvm_cli.py @@ -0,0 +1,199 @@ +# Copyright 2022 The KCL Authors. All rights reserved. + +import os +import sys +import platform +import typing +import json +import inspect + +from ctypes import * + +import google.protobuf.json_format as json_format + +import kclvm.compiler.extension.plugin.plugin as kcl_plugin +import kclvm.kcl.info as kcl_info +import kclvm.internal.gpyrpc.gpyrpc_pb2 as pb2 +import kclvm.api.object as objpkg +import kclvm.kcl.error as kcl_error +import kclvm.config + + +kclvm_PANIC_INFO_KEY = "$__kcl_PanicInfo__$" + + +_cli_dll = None + + +def init_cli_dll(): + global _cli_dll + + if _cli_dll: + return + + if platform.system() == "Darwin": + _exe_root = os.path.dirname(os.path.dirname(sys.executable)) + _cli_dll_path = f"{_exe_root}/bin/libkclvm_cli.dylib" + _cli_dll = CDLL(_cli_dll_path) + elif platform.system() == "Linux": + _exe_root = os.path.dirname(os.path.dirname(sys.executable)) + _cli_dll_path = f"{_exe_root}/bin/libkclvm_cli.so" + _cli_dll = CDLL(_cli_dll_path) + elif platform.system() == "Windows": + _exe_root = os.path.dirname(sys.executable) + _cli_dll_path = f"{_exe_root}/kclvm_cli.dll" + _cli_dll = CDLL(_cli_dll_path) + else: + raise f"unknown os: {platform.system()}" + + +class PluginContex: + def __init__(self): + self._plugin_dict: typing.Dict[str, any] = {} + + def call_method(self, name: str, args_json: str, kwargs_json: str) -> str: + return self._call_py_method(name, args_json, kwargs_json) + + def _call_py_method(self, name: str, args_json: str, kwargs_json: str) -> str: + try: + return self._call_py_method_unsafe(name, args_json, kwargs_json) + except Exception as e: + return json.dumps({"$__kcl_PanicInfo__$": f"{e}"}) + + def _get_plugin(self, plugin_name: str) -> typing.Optional[any]: + if plugin_name in self._plugin_dict: + return self._plugin_dict[plugin_name] + + module = kcl_plugin.get_plugin(plugin_name) + self._plugin_dict[plugin_name] = module + return module + + def _call_py_method_unsafe( + self, name: str, args_json: str, kwargs_json: str + ) -> str: + dotIdx = name.rfind(".") + if dotIdx < 0: + return "" + + modulePath = name[:dotIdx] + mathodName = name[dotIdx + 1 :] + + plugin_name = modulePath[modulePath.rfind(".") + 1 :] + + module = self._get_plugin(plugin_name) + mathodFunc = None + + for func_name, func in inspect.getmembers(module): + if func_name == kcl_info.demangle(mathodName): + mathodFunc = func + break + + args = [] + kwargs = {} + + if args_json: + args = json.loads(args_json) + if not isinstance(args, list): + return "" + if kwargs_json: + kwargs = json.loads(kwargs_json) + if not isinstance(kwargs, dict): + return "" + + result = mathodFunc(*args, **kwargs) + return json.dumps(result) + + +__plugin_context__ = PluginContex() +__plugin_method_agent_buffer__ = create_string_buffer(1024) + + +@CFUNCTYPE(c_char_p, c_char_p, c_char_p, c_char_p) +def plugin_method_agent(method: str, args_json: str, kwargs_json: str) -> c_char_p: + method = str(method, encoding="utf-8") + args_json = str(args_json, encoding="utf-8") + kwargs_json = str(kwargs_json, encoding="utf-8") + + json_result = __plugin_context__.call_method(method, args_json, kwargs_json) + + global __plugin_method_agent_buffer__ + __plugin_method_agent_buffer__ = create_string_buffer(json_result.encode("utf-8")) + return addressof(__plugin_method_agent_buffer__) + + +def kclvm_cli_run(args: pb2.ExecProgram_Args) -> str: + init_cli_dll() + + _cli_dll.kclvm_cli_run.restype = c_char_p + _cli_dll.kclvm_cli_run.argtypes = [c_char_p, c_void_p] + + args_json = json_format.MessageToJson( + args, including_default_value_fields=True, preserving_proto_field_name=True + ) + + result_json = _cli_dll.kclvm_cli_run(args_json.encode("utf-8"), plugin_method_agent) + return result_json.decode(encoding="utf-8") + + +def kclvm_cli_native_run_dylib(args: pb2.ExecProgram_Args) -> objpkg.KCLResult: + json_result = kclvm_cli_run(args) + warn_json_result = "" + + if json_result.startswith("ERROR:"): + warn_json_result = json_result[len("ERROR:") :] + json_result = "{}" + + data = json.loads(json_result) + + panic_info = {} + if kclvm_PANIC_INFO_KEY in data: + panic_info = data + else: + if warn_json_result: + panic_info = json.loads(warn_json_result) + else: + panic_info = {} + + # check panic_info + if panic_info.get(kclvm_PANIC_INFO_KEY): + err_type_code = panic_info["err_type_code"] + if err_type_code: + err_type = kcl_error.ErrType((err_type_code,)) + else: + err_type = kcl_error.ErrType.EvaluationError_TYPE + + file_msg = [ + kcl_error.ErrFileMsg( + filename=panic_info.get("kcl_file"), + line_no=panic_info.get("kcl_line"), + col_no=panic_info.get("kcl_col"), + arg_msg=panic_info.get("kcl_arg_msg"), + ) + ] + if kclvm.config.debug and kclvm.config.verbose >= 2: + rust_filename = panic_info.get("rust_file") + rust_line = panic_info.get("rust_line") + rust_col = panic_info.get("rust_col") + print(f"Rust error info: {rust_filename}:{rust_line}:{rust_col}") + + config_meta_file_msg = kcl_error.ErrFileMsg( + filename=panic_info.get("kcl_config_meta_file"), + line_no=panic_info.get("kcl_config_meta_line"), + col_no=panic_info.get("kcl_config_meta_col"), + arg_msg=panic_info.get("kcl_config_meta_arg_msg"), + ) + if config_meta_file_msg.arg_msg: + file_msg.append(config_meta_file_msg) + + if panic_info.get("is_warning") or panic_info.get("is_warnning"): + kcl_error.report_warning( + err_type=err_type, file_msgs=[], arg_msg=panic_info.get("message") + ) + else: + kcl_error.report_exception( + err_type=err_type, + file_msgs=file_msg, + arg_msg=panic_info.get("message"), + ) + + return objpkg.KCLResult(data, os.path.abspath(args.k_filename_list[-1])) diff --git a/internal/kclvm_py/program/exec/native_runner.py b/internal/kclvm_py/program/exec/native_runner.py new file mode 100644 index 000000000..d49c7b4f9 --- /dev/null +++ b/internal/kclvm_py/program/exec/native_runner.py @@ -0,0 +1,457 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import os +import typing +import sys +import subprocess +import pathlib +import json +import glob +import hashlib +import platform +import shutil +import filelock + +import kclvm.config +import kclvm.kcl.ast as ast +import kclvm.api.object as objpkg +import kclvm.compiler.build.compiler as compiler +import kclvm.compiler.vfs as vfs +import kclvm.kcl.error as kcl_error +import kclvm.api.version as ver_pkg + + +CLANG = "clang" +LL_FILE_PATTERN = "*.ll" +BC_FILE_PATTERN = "*.bc" +LL_FILE_SUFFIX = ".ll" +BC_FILE_SUFFIX = ".bc" +LOCK_FILE_SUFFIX = ".lock" +KCLVM_PANIC_INFO_KEY = "$__kcl_PanicInfo__$" +NATIVE_CACHE_DIR = ".kclvm/native_cache" +KCLVM_CLI_SUB_CMD = "build" +CACHE_OPTION = vfs.CacheOption(cache_dir=NATIVE_CACHE_DIR) + + +def native_run( + path_list: typing.List[str], *, ast_prog: ast.Program +) -> objpkg.KCLResult: + + from kclvm.internal.kclx.transformer import transform_ast_to_kclx_ast_json_str + + # Config + + _no_link = True + _exe = ".exe" if os.name == "nt" else "" + _executable_root = os.path.dirname(os.path.dirname(sys.executable)) + _kclvm_cli = f"{_executable_root}/bin/kclvm-cli{_exe}" + _clang = f"{_executable_root}/tools/clang/bin/{CLANG}{_exe}" + _clang = _clang if os.path.exists(_clang) else f"{CLANG}{_exe}" + _rust_libstd_name = ( + pathlib.Path(f"{_executable_root}/lib/rust-libstd-name.txt").read_text().strip() + ) + _rust_libstd_dylib = f"{_executable_root}/lib/{_rust_libstd_name}" + _kclvm_bc = f"{_executable_root}/include/_kclvm.bc" + _a_out_ast_json = "_a.out.ast.json" + _a_out_bc = "_a.out.ll" + _lib_suffix = get_dylib_suffix() + _a_out_dylib = f"_a.out.{_lib_suffix}" + _out_bc_files = [] + + # Resolve Program + compiler.CompileProgram(ast_prog) + # Build Program with kclvm-cli, clang with cache + root = ast_prog.root + modfile = vfs.LoadModFile(root) + enable_cache = modfile.build.enable_pkg_cache + if enable_cache: + build_paths = [] + check_sum = ast_prog.get_check_sum(root) + cache_dir = f"{root}/{NATIVE_CACHE_DIR}/{ver_pkg.VERSION}-{ver_pkg.CHECKSUM}" + pathlib.Path(cache_dir).mkdir(parents=True, exist_ok=True) + for pkgpath in ast_prog.pkgs: + is_main_pkg = pkgpath == ast.Program.MAIN_PKGPATH + compile_prog = ast.Program( + root=root, main=ast_prog.main, pkgs={pkgpath: ast_prog.pkgs[pkgpath]} + ) + if is_main_pkg: + check_sum = compile_prog.get_check_sum(root) + main_virtual_filename = f"{check_sum}.k" + else: + main_virtual_filename = "" + bc_path = ( + f"{cache_dir}/{pkgpath}" + if not is_main_pkg + else f"{cache_dir}/{check_sum}" + ) + with filelock.FileLock(bc_path + LOCK_FILE_SUFFIX): + dylib_relative_path = ( + vfs.LoadMainPkgCache( + root, main_virtual_filename, option=CACHE_OPTION + ) + if is_main_pkg + else vfs.LoadPkgCache(root, pkgpath, option=CACHE_OPTION) + ) + # If AST module has been modified, ignore the dylib cache + if ast_prog.cmd_overrides and is_main_pkg: + dylib_relative_path = None + if dylib_relative_path is None: + # Build dylib + ast_json = transform_ast_to_kclx_ast_json_str(compile_prog) + with open(_a_out_ast_json, "w") as file: + file.write(ast_json) + dylib_path = ( + f"{cache_dir}/{pkgpath}.{_lib_suffix}" + if not is_main_pkg + else f"{cache_dir}/{check_sum}.{_lib_suffix}" + ) + if kclvm.config.verbose > 3: + print(f"Compiling {pkgpath}") + if os.path.exists(bc_path): + os.remove(bc_path) + process = subprocess.run( + [ + _kclvm_cli, + KCLVM_CLI_SUB_CMD, + _a_out_ast_json, + "--bc", + _kclvm_bc, + "-o", + bc_path, + "--linkmode", + "no_link", + ] + ) + if process.returncode != 0: + raise Exception( + f"stdout: {process.stdout}, stderr: {process.stderr}" + ) + process = subprocess.run( + [ + _clang, + "-Wno-override-module", + "-Wno-error=unused-command-line-argument", + "-Wno-unused-command-line-argument", + "-shared", + "-undefined", + "dynamic_lookup", + f"-Wl,-rpath,{_executable_root}/lib", + f"-L{_executable_root}/lib", + "-lkclvm_native_shared", + f"-I{_executable_root}/include", + bc_path + LL_FILE_SUFFIX, + _rust_libstd_dylib, + "-fPIC", + "-o", + dylib_path, + ] + ) + if process.returncode != 0: + raise Exception( + f"stdout: {process.stdout}, stderr: {process.stderr}" + ) + dylib_relative_path = dylib_path.replace(root, ".", 1) + if not is_main_pkg: + vfs.SavePkgCache( + root, pkgpath, dylib_relative_path, option=CACHE_OPTION + ) + else: + vfs.SaveMainPkgCache( + root, + main_virtual_filename, + dylib_relative_path, + option=CACHE_OPTION, + ) + _out_bc_files.append(bc_path + LL_FILE_SUFFIX) + else: + if dylib_relative_path.startswith("."): + dylib_path = dylib_relative_path.replace(".", root, 1) + build_paths.append(dylib_path) + _a_out_dylib = f"{cache_dir}/{check_sum}.out.{_lib_suffix}" + + process = subprocess.run( + [ + _clang, + "-Wno-override-module", + "-Wno-error=unused-command-line-argument", + "-Wno-unused-command-line-argument", + "-shared", + "-undefined", + "dynamic_lookup", + f"-Wl,-rpath,{_executable_root}/lib", + f"-L{_executable_root}/lib", + "-lkclvm_native_shared", + f"-I{_executable_root}/include", + *build_paths, + _rust_libstd_dylib, + "-fPIC", + "-o", + _a_out_dylib, + ] + ) + if process.returncode != 0: + raise Exception(f"stdout: {process.stdout}, stderr: {process.stderr}") + else: + # Transfrom Program + ast_json = transform_ast_to_kclx_ast_json_str(ast_prog) + with open(_a_out_ast_json, "w") as file: + file.write(ast_json) + if _no_link and os.path.exists(_a_out_bc): + os.remove(_a_out_bc) + if _no_link: + _out_bc_files = glob.glob(_a_out_bc + LL_FILE_PATTERN) + for file in _out_bc_files: + if os.path.exists(file): + os.remove(file) + # kclvm compile + process = subprocess.run( + [ + _kclvm_cli, + KCLVM_CLI_SUB_CMD, + _a_out_ast_json, + "--bc", + _kclvm_bc, + "-o", + _a_out_bc, + *(["--linkmode", "no_link"] if _no_link else []), + ] + ) + if process.returncode != 0: + raise Exception(f"stdout: {process.stdout}, stderr: {process.stderr}") + _out_bc_files = glob.glob(_a_out_bc + LL_FILE_PATTERN) + # clang + if _no_link: + process = subprocess.run( + [ + _clang, + "-Wno-override-module", + "-Wno-error=unused-command-line-argument", + "-Wno-unused-command-line-argument", + "-shared", + "-undefined", + "dynamic_lookup", + f"-Wl,-rpath,{_executable_root}/lib", + f"-L{_executable_root}/lib", + "-lkclvm_native", + f"-I{_executable_root}/include", + *_out_bc_files, + _rust_libstd_dylib, + f"{_executable_root}/lib/libkclvm.{_lib_suffix}", + "-fno-lto", + "-fPIC", + "-o", + _a_out_dylib, + ] + ) + else: + process = subprocess.run( + [ + _clang, + "-Wno-override-module", + "-Wno-error=unused-command-line-argument", + "-Wno-unused-command-line-argument", + "-shared", + "-undefined", + "dynamic_lookup", + f"-Wl,-rpath,{_executable_root}/lib", + f"-L{_executable_root}/lib", + "-lkclvm_native", + f"-I{_executable_root}/include", + _a_out_bc, + _rust_libstd_dylib, + f"{_executable_root}/lib/libkclvm.{_lib_suffix}", + "-fno-lto", + "-fPIC", + "-o", + _a_out_dylib, + ] + ) + if process.returncode != 0: + raise Exception(f"stdout: {process.stdout}, stderr: {process.stderr}") + + # run app + result = native_run_dylib(path_list, _a_out_dylib) + + if not kclvm.config.debug: + if os.path.exists(_a_out_ast_json): + os.remove(_a_out_ast_json) + if not enable_cache: + if _no_link: + for file in _out_bc_files: + if os.path.exists(file): + os.remove(file) + else: + if os.path.exists(_a_out_bc): + os.remove(_a_out_bc) + if os.path.exists(_a_out_dylib): + os.remove(_a_out_dylib) + + return result + + +def native_run_dylib( + path_list: typing.List[str], dylib_path: str, should_exit: bool = False +) -> objpkg.KCLResult: + """Native run with dylib""" + import kclvm_plugin as kclvm_plugin + + # run app + ctx = kclvm_plugin.AppContext(os.path.abspath(dylib_path)) + + # init options + ctx.InitOptions(kclvm.config.arguments) + + # run app + json_result = ctx.RunApp( + strict_range_check=kclvm.config.strict_range_check, + disable_none=kclvm.config.disable_none, + disable_schema_check=kclvm.config.disable_schema_check, + list_option_mode=kclvm.config.list_option_mode, + debug_mode=kclvm.config.debug, + ) + warn_json_result = ctx.GetWarn() + + if kclvm.config.list_option_mode: + print(json_result, end="") + return objpkg.KCLResult({}) + + data = json.loads(json_result) + + panic_info = {} + if KCLVM_PANIC_INFO_KEY in data: + panic_info = data + else: + if warn_json_result: + panic_info = json.loads(warn_json_result) + else: + panic_info = {} + + # check panic_info + if panic_info.get(KCLVM_PANIC_INFO_KEY): + err_type_code = panic_info["err_type_code"] + if err_type_code: + err_type = kcl_error.ErrType((err_type_code,)) + else: + err_type = kcl_error.ErrType.EvaluationError_TYPE + + file_msg = [ + kcl_error.ErrFileMsg( + filename=panic_info.get("kcl_file"), + line_no=panic_info.get("kcl_line"), + col_no=panic_info.get("kcl_col"), + arg_msg=panic_info.get("kcl_arg_msg"), + ) + ] + if kclvm.config.debug and kclvm.config.verbose >= 2: + rust_filename = panic_info.get("rust_file") + rust_line = panic_info.get("rust_line") + rust_col = panic_info.get("rust_col") + print(f"Rust error info: {rust_filename}:{rust_line}:{rust_col}") + + config_meta_file_msg = kcl_error.ErrFileMsg( + filename=panic_info.get("kcl_config_meta_file"), + line_no=panic_info.get("kcl_config_meta_line"), + col_no=panic_info.get("kcl_config_meta_col"), + arg_msg=panic_info.get("kcl_config_meta_arg_msg"), + ) + if config_meta_file_msg.arg_msg: + file_msg.append(config_meta_file_msg) + + if panic_info.get("is_warning") or panic_info.get("is_warnning"): + kcl_error.report_warning( + err_type=err_type, file_msgs=[], arg_msg=panic_info.get("message") + ) + else: + kcl_error.report_exception( + err_type=err_type, + file_msgs=file_msg, + arg_msg=panic_info.get("message"), + ) + + return objpkg.KCLResult(data, os.path.abspath(path_list[-1])) + + +def native_try_run_dylib( + root, path_list: typing.List[str], dylib_path: str +) -> typing.Optional[objpkg.KCLResult]: + if os.path.exists(dylib_path): + if is_linux_platform(): + # Run ldd + _ldd = "ldd" + _so_not_found_flag = " => not found" + try: + process = subprocess.run( + [ + _ldd, + dylib_path, + ], + capture_output=True, + ) + if process.returncode != 0: + return None + linked_so_path_list = [ + p.strip("\t ") + for p in str(process.stdout, encoding="utf-8").split("\n") + if p + ] + not_found_so_path_list = [ + p.replace(_so_not_found_flag, "").strip() + for p in linked_so_path_list + if _so_not_found_flag in p + ] + target_path = not_found_so_path_list[-1].rsplit("/", 1)[0] + source_path = ( + f"{root}/{NATIVE_CACHE_DIR}/{ver_pkg.VERSION}-{ver_pkg.CHECKSUM}" + ) + + if os.path.exists(target_path): + shutil.rmtree(target_path) + os.makedirs(target_path) + if os.path.exists(source_path): + for root, dirs, files in os.walk(source_path): + for file in files: + src_file = os.path.join(root, file) + shutil.copy(src_file, target_path) + + return native_run_dylib(path_list, dylib_path, should_exit=True) + except Exception: + return None + return None + + +def get_path_list_dylib_path(root: str, path_list: typing.List[str]) -> str: + check_sum = hashlib.md5() + if not path_list or not root: + return "" + for filename in path_list: + if os.path.isfile(filename): + filename = os.path.abspath(filename) + check_sum.update( + (filename.replace(root, ".", 1) if root else filename).encode( + encoding="utf-8" + ) + ) + with open(filename, "rb") as f: + check_sum.update(f.read()) + cache_dir = f"{root}/{NATIVE_CACHE_DIR}/{ver_pkg.VERSION}-{ver_pkg.CHECKSUM}" + dylib_path = "{}/{}.out.{}".format( + cache_dir, check_sum.hexdigest(), get_dylib_suffix() + ) + return dylib_path + + +def get_dylib_suffix() -> str: + """Get dylib suffix on diffrent platform""" + sysstr = platform.system() + if sysstr == "Windows": + lib_suffix = "dll" + elif sysstr == "Linux": + lib_suffix = "so" + else: + lib_suffix = "dylib" + return lib_suffix + + +def is_linux_platform() -> bool: + """Platform is linux""" + return platform.system() == "Linux" diff --git a/internal/kclvm_py/program/exec/native_runner_wasm32.py b/internal/kclvm_py/program/exec/native_runner_wasm32.py new file mode 100644 index 000000000..ba8b10c1a --- /dev/null +++ b/internal/kclvm_py/program/exec/native_runner_wasm32.py @@ -0,0 +1,342 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import os +import typing +import sys +import subprocess +import glob +import platform +import json +import inspect + +import kclvm.config +import kclvm.kcl.ast as ast +import kclvm.kcl.info as kcl_info +import kclvm.api.object as objpkg +import kclvm.compiler.build.compiler as compiler +import kclvm.compiler.extension.plugin.plugin as kcl_plugin +import kclvm.kcl.error as kcl_error + +from .native_runner import ( + LL_FILE_PATTERN, + KCLVM_PANIC_INFO_KEY, +) + + +def native_run_wasm32( + path_list: typing.List[str], *, ast_prog: ast.Program +) -> objpkg.KCLResult: + from kclvm.internal.kclx.transformer import transform_ast_to_kclx_ast_json_str + + # Config + + if platform.system() == "Windows": + _no_link = True + _executable_root = os.path.dirname(sys.executable) + _kclvm_cli = f"{_executable_root}\\kclvm_cli.exe" + _clang = f"{_executable_root}\\tools\\clang\\bin\\clang.exe" + _clang = _clang if os.path.exists(_clang) else "clang.exe" + + _kclvm_bc = f"{_executable_root}\\include\\_kclvm.bc" + _kclvm_lib_path = f"{_executable_root}\\libs" + _kclvm_lib_name = "kclvm_wasm32" + _kclvm_undefined_file = f"{_executable_root}\\libs\\_kclvm_undefined_wasm.txt" + + else: + _no_link = True + _executable_root = os.path.dirname(os.path.dirname(sys.executable)) + _kclvm_cli = f"{_executable_root}/bin/kclvm_cli" + _clang = f"{_executable_root}/tools/clang/bin/clang" + _clang = _clang if os.path.exists(_clang) else "clang" + + _kclvm_bc = f"{_executable_root}/include/_kclvm.bc" + _kclvm_lib_path = f"{_executable_root}/lib" + _kclvm_lib_name = "kclvm_wasm32" + _kclvm_undefined_file = f"{_executable_root}/lib/_kclvm_undefined_wasm.txt" + + _a_out_ast_json = "_a.out.ast.json" + _a_out_ll = "_a.out.ll" + _a_out_wasm = "_a.out.wasm" + _out_bc_files = [] + + # Resolve Program + compiler.CompileProgram(ast_prog) + + # Build Program with kclvm_cli, windows donot support cache + + if True: + # Transfrom Program + ast_json = transform_ast_to_kclx_ast_json_str(ast_prog) + with open(_a_out_ast_json, "w") as file: + file.write(ast_json) + if _no_link and os.path.exists(_a_out_ll): + os.remove(_a_out_ll) + if _no_link: + _out_bc_files = glob.glob(_a_out_ll + LL_FILE_PATTERN) + for file in _out_bc_files: + if os.path.exists(file): + os.remove(file) + + # kclvm compile + try: + args = [ + _kclvm_cli, + "build", + _a_out_ast_json, + "--bc", + _kclvm_bc, + "-o", + _a_out_ll + ".tmp.ll", + ] + subprocess.check_call(args) + + fix_ll_local_name(_a_out_ll, _a_out_ll + ".tmp.ll") + os.remove(_a_out_ll + ".tmp.ll") + + except subprocess.CalledProcessError as e: + raise e + _out_bc_files = glob.glob(_a_out_ll + LL_FILE_PATTERN) + + # clang + try: + args = [ + _clang, + "--target=wasm32-unknown-unknown-wasm", + "-Wno-override-module", + "-nostdlib", + "-Wl,--no-entry", + "-Wl,--export-all", + f"-Wl,--allow-undefined-file={_kclvm_undefined_file}", + "-O3", + f"-L{_kclvm_lib_path}", + f"-l{_kclvm_lib_name}", + _a_out_ll, + "-o", + _a_out_wasm, + ] + + if platform.system() == "Windows": + subprocess.check_call(args, stdout=open(os.devnull, "wb")) + else: + subprocess.check_call(args) + + except subprocess.CalledProcessError as e: + raise e + + # run wasm + result = WasmApp(path_list, _a_out_wasm).run() + if not kclvm.config.debug: + if os.path.exists(_a_out_ast_json): + os.remove(_a_out_ast_json) + if os.path.exists(_a_out_ll): + os.remove(_a_out_ll) + if _no_link: + for file in _out_bc_files: + if os.path.exists(file): + os.remove(file) + if os.path.exists(_a_out_wasm): + os.remove(_a_out_wasm) + + return result + + +class WasmApp: + def __init__( + self, path_list: typing.List[str], wasm_path: str, should_exit: bool = False + ): + import wasmer + import wasmer_compiler_cranelift + + self.path_list = path_list + self.wasm_path = wasm_path + self.should_exit = should_exit + + self.store = wasmer.Store(wasmer.engine.JIT(wasmer_compiler_cranelift.Compiler)) + self.module = wasmer.Module(self.store, open(wasm_path, "rb").read()) + + def kclvm_plugin_invoke_json_wasm(method: int, args: int, kwargs: int) -> int: + return self._invoke_json_wasm(method, args, kwargs) + + self.import_object = wasmer.ImportObject() + self.import_object.register( + "env", + { + "kclvm_plugin_invoke_json_wasm": wasmer.Function( + self.store, kclvm_plugin_invoke_json_wasm + ), + }, + ) + + self.instance = wasmer.Instance(self.module, self.import_object) + self.memory = self.instance.exports.memory + + self._invoke_json_buffer = self.instance.exports.kclvm_value_Str(0) + self.instance.exports.kclvm_value_Str_resize( + self._invoke_json_buffer, 1024 * 1024 * 1024 + ) + + def _invoke_json_wasm(self, method_ptr: int, args_ptr: int, kwargs_ptr: int) -> int: + method_len = self.instance.exports.kclvm_strlen(method_ptr) + args_len = self.instance.exports.kclvm_strlen(args_ptr) + kwargs_len = self.instance.exports.kclvm_strlen(kwargs_ptr) + + reader = bytearray(self.memory.buffer) + + method = reader[method_ptr : method_ptr + method_len].decode() + args = reader[args_ptr : args_ptr + args_len].decode() + kwargs = reader[kwargs_ptr : kwargs_ptr + kwargs_len].decode() + + json_result = self._call_py_method(method, args, kwargs) + + bytes_result = json_result.encode(encoding="utf8") + self.instance.exports.kclvm_value_Str_resize( + self._invoke_json_buffer, len(bytes_result) + 1 + ) + + buf_ptr = self.instance.exports.kclvm_value_Str_ptr(self._invoke_json_buffer) + buf_len = self.instance.exports.kclvm_value_Str_len(self._invoke_json_buffer) + assert buf_len == len(bytes_result) + 1 + + mem = self.memory.uint8_view() + for i in range(len(bytes_result)): + mem[buf_ptr + i] = bytes_result[i] + mem[buf_ptr + len(bytes_result)] = 0 + + return buf_ptr + + def run(self) -> objpkg.KCLResult: + ctx = self.instance.exports.kclvm_context_new() + result = self.instance.exports.kclvm_main(ctx) + + c_str_ptr = self.instance.exports.kclvm_value_Str_ptr(result) + c_str_len = self.instance.exports.kclvm_value_len(result) + + reader = bytearray(self.memory.buffer) + result = reader[c_str_ptr : c_str_ptr + c_str_len].decode() + + return self.json_to_object(result, None) + + def json_to_object( + self, json_result: str, warn_json_result: str = None + ) -> objpkg.KCLResult: + if kclvm.config.list_option_mode: + print(json_result, end="") + return objpkg.KCLResult({}) + + data = json.loads(json_result) + + panic_info = {} + if KCLVM_PANIC_INFO_KEY in data: + panic_info = data + else: + if warn_json_result: + panic_info = json.loads(warn_json_result) + else: + panic_info = {} + + # check panic_info + if panic_info.get(KCLVM_PANIC_INFO_KEY): + err_type_code = panic_info["err_type_code"] + if err_type_code: + err_type = kcl_error.ErrType((err_type_code,)) + else: + err_type = kcl_error.ErrType.EvaluationError_TYPE + + file_msg = [ + kcl_error.ErrFileMsg( + filename=panic_info.get("kcl_file"), + line_no=panic_info.get("kcl_line"), + col_no=panic_info.get("kcl_col"), + arg_msg=panic_info.get("kcl_arg_msg"), + ) + ] + if kclvm.config.debug and kclvm.config.verbose >= 2: + rust_filename = panic_info.get("rust_file") + rust_line = panic_info.get("rust_line") + rust_col = panic_info.get("rust_col") + print(f"Rust error info: {rust_filename}:{rust_line}:{rust_col}") + + config_meta_file_msg = kcl_error.ErrFileMsg( + filename=panic_info.get("kcl_config_meta_file"), + line_no=panic_info.get("kcl_config_meta_line"), + col_no=panic_info.get("kcl_config_meta_col"), + arg_msg=panic_info.get("kcl_config_meta_arg_msg"), + ) + if config_meta_file_msg.arg_msg: + file_msg.append(config_meta_file_msg) + + if panic_info.get("is_warning") or panic_info.get("is_warnning"): + kcl_error.report_warning( + err_type=err_type, file_msgs=[], arg_msg=panic_info.get("message") + ) + else: + kcl_error.report_exception( + err_type=err_type, + file_msgs=file_msg, + arg_msg=panic_info.get("message"), + ) + + return objpkg.KCLResult(data, os.path.abspath(self.path_list[-1])) + + def _call_py_method(self, name: str, args_json: str, kwargs_json: str) -> str: + try: + return self._call_py_method_unsafe(name, args_json, kwargs_json) + except Exception as e: + return json.dumps({"$__kcl_PanicInfo__$": f"{e}"}) + + def _call_py_method_unsafe( + self, name: str, args_json: str, kwargs_json: str + ) -> str: + dotIdx = name.rfind(".") + if dotIdx < 0: + return "" + + modulePath = name[:dotIdx] + mathodName = name[dotIdx + 1 :] + + plugin_name = modulePath[modulePath.rfind(".") + 1 :] + + module = kcl_plugin.get_plugin(plugin_name) + mathodFunc = None + + for func_name, func in inspect.getmembers(module): + if func_name == kcl_info.demangle(mathodName): + mathodFunc = func + break + + args = [] + kwargs = {} + + if args_json: + args = json.loads(args_json) + if not isinstance(args, list): + return "" + if kwargs_json: + kwargs = json.loads(kwargs_json) + if not isinstance(kwargs, dict): + return "" + + result = mathodFunc(*args, **kwargs) + return json.dumps(result) + + +def fix_ll_local_name(dst_path, src_path: str): + replaceArgs_old = [] + replaceArgs_new = [] + + for i in range(0, 10): + replaceArgs_old.append(f"%{i}") + replaceArgs_new.append(f"%local_{i}") + + replaceArgs_old.append(f"\n{i}") + replaceArgs_new.append(f"\nlocal_{i}") + + with open(src_path, "r") as file: + code = file.read() + + if platform.system() == "Windows": + for i in range(0, len(replaceArgs_old)): + code = code.replace(replaceArgs_old[i], replaceArgs_new[i], -1) + + with open(dst_path, "w") as f: + f.write(code) diff --git a/internal/kclvm_py/program/exec/native_runner_windows_amd64.py b/internal/kclvm_py/program/exec/native_runner_windows_amd64.py new file mode 100644 index 000000000..338562617 --- /dev/null +++ b/internal/kclvm_py/program/exec/native_runner_windows_amd64.py @@ -0,0 +1,140 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import os +import typing +import sys +import subprocess +import glob +import platform + +import kclvm.config +import kclvm.kcl.ast as ast +import kclvm.api.object as objpkg +import kclvm.compiler.build.compiler as compiler + +from .native_runner import ( + LL_FILE_PATTERN, + native_run_dylib, +) + + +def native_run_windows( + path_list: typing.List[str], *, ast_prog: ast.Program +) -> objpkg.KCLResult: + if platform.system() != "Windows": + raise "native_run_windows only for windows" + + from kclvm.internal.kclx.transformer import transform_ast_to_kclx_ast_json_str + + # Config + + _no_link = True + _executable_root = os.path.dirname(sys.executable) + _kclvm_cli = f"{_executable_root}\\kclvm-cli.exe" + _clang = f"{_executable_root}\\tools\\clang\\bin\\clang.exe" + _clang = _clang if os.path.exists(_clang) else "clang.exe" + + _kclvm_main_win_c = f"{_executable_root}\\libs\\_kclvm_main_win.c" + _kclvm_dll_lib = f"{_executable_root}\\libs\\kclvm.dll.lib" + _kclvm_bc = f"{_executable_root}\\libs\\_kclvm.bc" + _a_out_ast_json = "_a.out.ast.json" + _a_out_ll = "_a.out.ll" + _a_out_dylib = "_a.out.dll" + _out_bc_files = [] + + # Resolve Program + compiler.CompileProgram(ast_prog) + + # Build Program with kclvm-cli, windows donot support cache + + if True: + # Transfrom Program + ast_json = transform_ast_to_kclx_ast_json_str(ast_prog) + with open(_a_out_ast_json, "w") as file: + file.write(ast_json) + if _no_link and os.path.exists(_a_out_ll): + os.remove(_a_out_ll) + if _no_link: + _out_bc_files = glob.glob(_a_out_ll + LL_FILE_PATTERN) + for file in _out_bc_files: + if os.path.exists(file): + os.remove(file) + + # kclvm compile + try: + args = [ + _kclvm_cli, + "build", + _a_out_ast_json, + "--bc", + _kclvm_bc, + "-o", + _a_out_ll + ".tmp.ll", + ] + subprocess.check_call(args) + + fix_ll_local_name(_a_out_ll, _a_out_ll + ".tmp.ll") + os.remove(_a_out_ll + ".tmp.ll") + + except subprocess.CalledProcessError as e: + raise e + _out_bc_files = glob.glob(_a_out_ll + LL_FILE_PATTERN) + + # clang + try: + args = [ + _clang, + "-Wno-override-module", + "-shared", + _a_out_ll, + _kclvm_main_win_c, + _kclvm_dll_lib, + "-lws2_32", + "-lbcrypt", + "-lAdvapi32", + "-lUserenv", + "-o", + _a_out_dylib, + ] + subprocess.check_call(args, stdout=open(os.devnull, "wb")) + + except subprocess.CalledProcessError as e: + raise e + + # run app + result = native_run_dylib(path_list, _a_out_dylib) + if not kclvm.config.debug: + if os.path.exists(_a_out_ast_json): + os.remove(_a_out_ast_json) + if _no_link: + for file in _out_bc_files: + if os.path.exists(file): + os.remove(file) + else: + if os.path.exists(_a_out_ll): + os.remove(_a_out_ll) + if os.path.exists(_a_out_dylib): + os.remove(_a_out_dylib) + + return result + + +def fix_ll_local_name(dst_path, src_path: str): + replaceArgs_old = [] + replaceArgs_new = [] + + for i in range(0, 10): + replaceArgs_old.append(f"%{i}") + replaceArgs_new.append(f"%local_{i}") + + replaceArgs_old.append(f"\n{i}") + replaceArgs_new.append(f"\nlocal_{i}") + + with open(src_path, "r") as file: + code = file.read() + + for i in range(0, len(replaceArgs_old)): + code = code.replace(replaceArgs_old[i], replaceArgs_new[i], -1) + + with open(dst_path, "w") as f: + f.write(code) diff --git a/internal/kclvm_py/program/exec/runner.py b/internal/kclvm_py/program/exec/runner.py new file mode 100644 index 000000000..4822f3acb --- /dev/null +++ b/internal/kclvm_py/program/exec/runner.py @@ -0,0 +1,189 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import os +import platform +import typing +import json + +from ast import literal_eval + +import kclvm.config +import kclvm.kcl.ast as ast +import kclvm.api.object as objpkg +import kclvm.tools.query as query +import kclvm.compiler.parser.parser as parser +import kclvm.compiler.build.compiler as compiler +import kclvm.compiler.vfs as vfs +import kclvm.vm as vm +import kclvm.internal.gpyrpc.gpyrpc_pb2 as pb2 + +from kclvm.api.object.internal import ( + kcl_option_reset, + kcl_option_init_all, +) +from .native_runner import native_run, is_linux_platform +from .kclvm_cli import kclvm_cli_native_run_dylib + + +KCLVM_PANIC_INFO_KEY = "$__kcl_PanicInfo__$" +KCLVM_RUN_MODE_WITHIN_CACHE_ENV = "KCLVM_RUN_MODE_WITHIN_CACHE" + + +def Run( + path_list: typing.List[str], + *, + work_dir: str = "", + k_code_list: typing.List[str] = None, + cmd_args: typing.List[ast.CmdArgSpec] = None, + cmd_overrides: typing.List[ast.CmdOverrideSpec] = None, + # -r --strict-range-check + strict_range_check: bool = None, + # -n --disable-none + disable_none: bool = None, + # -v --verbose + verbose: int = None, + # -d --debug + debug: int = None, + print_override_ast: bool = False, + # --target + target: str = "", +) -> objpkg.KCLResult: + assert len(path_list) > 0 + + if not work_dir and not k_code_list: + for s in path_list: + if os.path.isdir(s): + work_dir = s + if not work_dir and not k_code_list: + work_dir = kclvm.config.current_path or os.path.dirname(path_list[0]) + + root = vfs.MustGetPkgRoot(path_list) + modfile = vfs.LoadModFile(root) + target = (target or modfile.build.target or "").lower() + + kclvm.config.input_file = path_list + kclvm.config.current_path = work_dir + kclvm.config.is_target_native = target == "native" + kclvm.config.is_target_wasm = target == "wasm" + + if strict_range_check is not None: + kclvm.config.strict_range_check = strict_range_check + if disable_none is not None: + kclvm.config.disable_none = disable_none + if verbose is not None: + kclvm.config.verbose = verbose + if debug is not None: + kclvm.config.debug = debug + + if cmd_args: + kclvm.config.arguments = [] + for x in cmd_args or []: + try: + better_value = literal_eval(x.value) + kclvm.config.arguments.append((x.name, better_value)) + except Exception: + kclvm.config.arguments.append((x.name, x.value)) + + # rust: build/link/run + if target == "native" or target == "wasm": + kclvm.config.is_target_native = True + + args = pb2.ExecProgram_Args() + args.work_dir = work_dir + args.k_filename_list.extend(path_list) + args.k_code_list.extend(k_code_list) + + for kv in kclvm.config.arguments or []: + key, value = kv + if isinstance(value, (bool, list, dict)): + value = json.dumps(value) + elif isinstance(value, str): + value = '"{}"'.format(value.replace('"', '\\"')) + else: + value = str(value) + args.args.append( + pb2.CmdArgSpec( + name=key, + value=value, + ) + ) + + for x in cmd_overrides or []: + args.overrides.append( + pb2.CmdOverrideSpec( + pkgpath=x.pkgpath, + field_path=x.field_path, + field_value=x.field_value, + action=x.action.value, + ) + ) + + args.print_override_ast = print_override_ast or False + args.strict_range_check = strict_range_check or False + args.disable_none = disable_none or False + args.verbose = verbose or 0 + args.debug = debug or 0 + + return kclvm_cli_native_run_dylib(args) + + # Only for linux debug directly run + from .native_runner import ( + get_path_list_dylib_path, + native_run_dylib, + native_try_run_dylib, + ) + + if ( + target == "native" + and is_linux_platform() + and not cmd_overrides + and os.environ.get(KCLVM_RUN_MODE_WITHIN_CACHE_ENV) + ): + dylib_path = get_path_list_dylib_path(root, path_list) + if os.path.exists(dylib_path): + try: + return native_run_dylib(path_list, dylib_path, should_exit=True) + except Exception: + result = native_try_run_dylib(root, path_list, dylib_path) + if result: + return result + + ast_prog = parser.LoadProgram( + *path_list, + work_dir=work_dir, + k_code_list=k_code_list, + mode=parser.ParseMode.ParseComments if cmd_overrides else parser.ParseMode.Null, + ) + ast_prog.cmd_args = cmd_args if cmd_args else [] + ast_prog.cmd_overrides = cmd_overrides if cmd_overrides else [] + + # Apply argument + kcl_option_reset() + kcl_option_init_all() + + if target == "native": + if platform.system() == "Windows": + from .native_runner_windows_amd64 import native_run_windows + + result = native_run_windows(path_list, ast_prog=ast_prog) + else: + result = native_run(path_list, ast_prog=ast_prog) + + elif target == "wasm": + from .native_runner_wasm32 import native_run_wasm32 + + result = native_run_wasm32(path_list, ast_prog=ast_prog) + + else: + # AST to bytecode list + bin_prog = compiler.CompileProgram( + ast_prog, enable_cache=not bool(ast_prog.cmd_overrides) + ) + + # Run bytecode list + result = vm.Run(bin_prog) + + # If cmd overrides are used and config.debug is True, write back KCL files + if print_override_ast: + query.PrintOverridesAST() + return result diff --git a/internal/kclvm_py/program/repl/readme.md b/internal/kclvm_py/program/repl/readme.md new file mode 100644 index 000000000..1333ed77b --- /dev/null +++ b/internal/kclvm_py/program/repl/readme.md @@ -0,0 +1 @@ +TODO diff --git a/internal/kclvm_py/program/rpc-server/__init__.py b/internal/kclvm_py/program/rpc-server/__init__.py new file mode 100644 index 000000000..d74aaf8ad --- /dev/null +++ b/internal/kclvm_py/program/rpc-server/__init__.py @@ -0,0 +1 @@ +# Copyright 2021 The KCL Authors. All rights reserved. diff --git a/internal/kclvm_py/scripts/build-cpython.sh b/internal/kclvm_py/scripts/build-cpython.sh new file mode 100755 index 000000000..c83f26f31 --- /dev/null +++ b/internal/kclvm_py/scripts/build-cpython.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +# Stop on error. +set -e + +prepare_dirs () { + cpython_build_dir="$topdir/_build/build/$os/cpython" + mkdir -p "$cpython_build_dir" + cpython_install_dir="$topdir/_build/dist/$os/cpython" + mkdir -p "$cpython_install_dir" +} + +# Switch configuration options. +config_option="Default" +if [ "$os" != "" ]; then + config_option=$os +fi + +# python version +py_ver_major="3" +py_ver_minor="7" +py_ver_micro="6" + +for config in "$config_option" +do + case $config in + "Default" | "centos") + config_envs="LANG=C.UTF-8" + config_options="--enable-optimizations --with-ssl" + echo "$REPLY: The configuration is $config: config_envs=$config_envs config_options=$config_options" + break + ;; + "Darwin") + if [ "$sslpath" == "" ]; then + sslpath=$(brew --prefix openssl@1.1) + fi + + if [ x"$(uname -m)" == x"arm64" ]; then + py_ver_major="3" + py_ver_minor="9" + py_ver_micro="12" + fi + + config_envs="LANG=C.UTF-8" + config_options="--enable-optimizations --with-openssl=$sslpath --with-ssl-default-suites=python" + echo "$REPLY: The configuration is $config: config_envs=$config_envs config_options=$config_options" + break + ;; + "ubuntu" | "debian" | "Ubuntu" |"Debian" | "Static-Debian" | "Cood1-Debian" | "Cood1Shared-Debian") + config_envs="CFLAGS=-Wno-coverage-mismatch" + config_options="--enable-optimizations --with-ssl" + echo "$REPLY: The configuration is $config: config_envs=$config_envs config_options=$config_options" + break + ;; + *) echo "Invalid config option $REPLY:$config" + exit 1 + break + ;; + esac +done + +# py_ver_str="$(python3 -c 'import os; print(os.path.basename(os.path.dirname(os.__file__)))')" +py_ver_str="${py_ver_major}.${py_ver_minor}.${py_ver_micro}" + +# wget python +mkdir -p $topdir/_build/3rdparty +wget -P $topdir/_build/3rdparty "https://www.python.org/ftp/python/${py_ver_str}/Python-${py_ver_str}.tgz" +tar zxvf $topdir/_build/3rdparty/Python-${py_ver_str}.tgz -C $topdir/_build/3rdparty + +prepare_dirs +prefix_option="--prefix=$cpython_install_dir" +cpython_source_dir="$topdir/_build/3rdparty/Python-${py_ver_str}" + +# Perform the configuration/make/make install process. +set -x +cd $cpython_build_dir +eval $config_envs $cpython_source_dir/configure $prefix_option $config_options +# The make -j command may fail on some OS. +# make -j "$(nproc)" +make -j8 build_all +make -j8 altinstall +set +x + +# Print the summary. +echo "================ Summary ================" +echo " CPython is built into $cpython_build_dir" \ No newline at end of file diff --git a/internal/kclvm_py/scripts/build-kclvm.sh b/internal/kclvm_py/scripts/build-kclvm.sh new file mode 100755 index 000000000..71ef4ff76 --- /dev/null +++ b/internal/kclvm_py/scripts/build-kclvm.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +# Stop on error. +set -e + +prepare_dirs () { + cpython_build_dir="$topdir/_build/dist/$os/cpython" + kclvm_packages_dir="$topdir/_build/packages" + kclvm_install_dir="$topdir/_build/dist/$os/kclvm" + mkdir -p "$kclvm_install_dir" + mkdir -p "$kclvm_packages_dir" +} + +prepare_dirs +kclvm_source_dir="$topdir/internal/kclvm_py" + +# python exe name +py_exe_name="python3.7" +if [ -d "${cpython_build_dir}/lib/python3.9" ]; then + py_exe_name="python3.9" +fi + +# py_lib_basename: python3.x +py_lib_basename="python3.7" +if [ -d "${cpython_build_dir}/lib/python3.9" ]; then + py_lib_basename="python3.9" +fi + +# Perform the build process. +set -x + +# Copy files from CPython. +cd $kclvm_install_dir +mkdir -p bin +mkdir -p lib +cp $cpython_build_dir/bin/${py_exe_name} $kclvm_install_dir/bin/kclvm +cp -r $cpython_build_dir/lib/${py_lib_basename} $kclvm_install_dir/lib/ +cp -r $cpython_build_dir/include $kclvm_install_dir/ + +# Copy KCLVM. +cp "$topdir/internal/kclvm_py/scripts/cli/kcl" $kclvm_install_dir/bin/ +cp "$topdir/internal/kclvm_py/scripts/cli/kcl-plugin" $kclvm_install_dir/bin/ +cp "$topdir/internal/kclvm_py/scripts/cli/kcl-doc" $kclvm_install_dir/bin/ +cp "$topdir/internal/kclvm_py/scripts/cli/kcl-test" $kclvm_install_dir/bin/ +cp "$topdir/internal/kclvm_py/scripts/cli/kcl-lint" $kclvm_install_dir/bin/ +cp "$topdir/internal/kclvm_py/scripts/cli/kcl-fmt" $kclvm_install_dir/bin/ +cp "$topdir/internal/kclvm_py/scripts/cli/kcl-vet" $kclvm_install_dir/bin/ +chmod +x $kclvm_install_dir/bin/kcl +chmod +x $kclvm_install_dir/bin/kcl-plugin +chmod +x $kclvm_install_dir/bin/kcl-doc +chmod +x $kclvm_install_dir/bin/kcl-test +chmod +x $kclvm_install_dir/bin/kcl-lint +chmod +x $kclvm_install_dir/bin/kcl-fmt +chmod +x $kclvm_install_dir/bin/kcl-vet + +if [ -d $kclvm_install_dir/lib/${py_lib_basename}/kclvm ]; then + rm -rf $kclvm_install_dir/lib/${py_lib_basename}/kclvm +fi +cp -r $kclvm_source_dir $kclvm_install_dir/lib/${py_lib_basename} +mv $kclvm_install_dir/lib/${py_lib_basename}/kclvm_py $kclvm_install_dir/lib/${py_lib_basename}/kclvm + +# Get site-packages. +chmod +x $topdir/internal/kclvm_py/scripts/kcllib-install.sh +$topdir/internal/kclvm_py/scripts/kcllib-install.sh + +# Install plugins +cp -rf $topdir/plugins $kclvm_install_dir/ + +set +x + +# Print the summary. +echo "================ Summary ================" +echo " KCLVM is installed into $kclvm_install_dir" diff --git a/internal/kclvm_py/scripts/build.sh b/internal/kclvm_py/scripts/build.sh new file mode 100755 index 000000000..a7f223c53 --- /dev/null +++ b/internal/kclvm_py/scripts/build.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +os=$os topdir=$topdir sslpath=$sslpath $topdir/internal/kclvm_py/scripts/build-cpython.sh +os=$os topdir=$topdir $topdir/internal/kclvm_py/scripts/build-kclvm.sh diff --git a/internal/kclvm_py/scripts/cli/kcl b/internal/kclvm_py/scripts/cli/kcl new file mode 100755 index 000000000..63cd3eaea --- /dev/null +++ b/internal/kclvm_py/scripts/cli/kcl @@ -0,0 +1,5 @@ +#!/bin/sh + +kclvm_path=$(cd `dirname $0`; pwd)/kclvm +export PATHONPATH='' +$kclvm_path -m kclvm "$@" diff --git a/internal/kclvm_py/scripts/cli/kcl-doc b/internal/kclvm_py/scripts/cli/kcl-doc new file mode 100755 index 000000000..e49fd8e15 --- /dev/null +++ b/internal/kclvm_py/scripts/cli/kcl-doc @@ -0,0 +1,5 @@ +#!/bin/sh + +kclvm_path=$(cd `dirname $0`; pwd)/kclvm +export PATHONPATH='' +$kclvm_path -m kclvm.tools.docs "$@" diff --git a/internal/kclvm_py/scripts/cli/kcl-fmt b/internal/kclvm_py/scripts/cli/kcl-fmt new file mode 100755 index 000000000..db13541a8 --- /dev/null +++ b/internal/kclvm_py/scripts/cli/kcl-fmt @@ -0,0 +1,5 @@ +#!/bin/sh + +kclvm_path=$(cd `dirname $0`; pwd)/kclvm +export PATHONPATH='' +$kclvm_path -m kclvm.tools.format "$@" diff --git a/internal/kclvm_py/scripts/cli/kcl-lint b/internal/kclvm_py/scripts/cli/kcl-lint new file mode 100755 index 000000000..cdde2eb1f --- /dev/null +++ b/internal/kclvm_py/scripts/cli/kcl-lint @@ -0,0 +1,5 @@ +#!/bin/sh + +kclvm_path=$(cd `dirname $0`; pwd)/kclvm +export PATHONPATH='' +$kclvm_path -m kclvm.tools.lint.lint "$@" diff --git a/internal/kclvm_py/scripts/cli/kcl-plugin b/internal/kclvm_py/scripts/cli/kcl-plugin new file mode 100755 index 000000000..a6b35c267 --- /dev/null +++ b/internal/kclvm_py/scripts/cli/kcl-plugin @@ -0,0 +1,5 @@ +#!/bin/sh + +kclvm_path=$(cd `dirname $0`; pwd)/kclvm +export PATHONPATH='' +$kclvm_path -m kclvm.tools.plugin "$@" diff --git a/internal/kclvm_py/scripts/cli/kcl-test b/internal/kclvm_py/scripts/cli/kcl-test new file mode 100755 index 000000000..05327dbff --- /dev/null +++ b/internal/kclvm_py/scripts/cli/kcl-test @@ -0,0 +1,9 @@ +#!/bin/sh + +kcl_go_path=$(cd `dirname $0`; pwd)/kcl-go +if [[ ! -f $kcl_go_path ]]; then + echo "kcl-go not found, please check the installation" + exit 1 +fi +export PATHONPATH='' +$kcl_go_path test "$@" diff --git a/internal/kclvm_py/scripts/cli/kcl-vet b/internal/kclvm_py/scripts/cli/kcl-vet new file mode 100755 index 000000000..723a923f5 --- /dev/null +++ b/internal/kclvm_py/scripts/cli/kcl-vet @@ -0,0 +1,5 @@ +#!/bin/sh + +kclvm_path=$(cd `dirname $0`; pwd)/kclvm +export PATHONPATH='' +$kclvm_path -m kclvm.tools.validation "$@" diff --git a/internal/kclvm_py/scripts/format.sh b/internal/kclvm_py/scripts/format.sh new file mode 100755 index 000000000..8e83370f8 --- /dev/null +++ b/internal/kclvm_py/scripts/format.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# Stop on error. +set -e + +root="$topdir" +kclvm_src="$topdir/internal/kclvm_py" + +# 使用中文环境 +#export LANGUAGE=zh_CN.utf-8 + +# or English Env +export LANGUAGE=en_US.utf-8 + +# Install black format tools +python3 -m pip install black==21.5b1 + +# Run the black format +python3 -m black $kclvm_src --extend-exclude .*?_pb2.py\|lark_token.py + +# Print the summary. +echo "================ Format Summary ================" +echo "black format done successfully in $root" diff --git a/internal/kclvm_py/scripts/kcllib-install.sh b/internal/kclvm_py/scripts/kcllib-install.sh new file mode 100755 index 000000000..1b5e74ad5 --- /dev/null +++ b/internal/kclvm_py/scripts/kcllib-install.sh @@ -0,0 +1,7 @@ + +# kclvm path +kclvm=$topdir/_build/dist/$os/kclvm/bin/kclvm +install_list=$topdir/internal/kclvm_py/scripts/requirements.txt + +# kclvm pip install all libs +$kclvm -m pip install -r $install_list diff --git a/internal/kclvm_py/scripts/lint-check.sh b/internal/kclvm_py/scripts/lint-check.sh new file mode 100755 index 000000000..131c286ea --- /dev/null +++ b/internal/kclvm_py/scripts/lint-check.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# Stop on error. +set -e + +root="$topdir" +kclvm_install_dir="$topdir/_build/dist/$os/kclvm" + +# or English Env +export LANGUAGE=en_US.utf-8 + +# Install flake8 lint tools and run linting. +$kclvm_install_dir/bin/kclvm -m pip install flake8==4.0.0 +$kclvm_install_dir/bin/kclvm -m flake8 --config ./.flake8 ./internal/kclvm_py + +# Print the summary. +echo "================ Lint Summary ================" +echo " Lint done successfully in $root" diff --git a/internal/kclvm_py/scripts/release.sh b/internal/kclvm_py/scripts/release.sh new file mode 100755 index 000000000..658e3370c --- /dev/null +++ b/internal/kclvm_py/scripts/release.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +kclvm_release_file="kclvm-$os-latest.tar.gz" +kclvm_release_path="$topdir/_build" +kclvm_package_dir="$topdir/_build/dist/$os" +kclvm_install_dir="kclvm" + +rm $kclvm_release_file + +cd $kclvm_package_dir +tar -czvf $kclvm_release_file $kclvm_install_dir + +mv $kclvm_package_dir/$kclvm_release_file $kclvm_release_path/$kclvm_release_file + +# Print the summary. +echo "================ Summary ================" +echo " $kclvm_release_path/$kclvm_release_file has been created" diff --git a/internal/kclvm_py/scripts/requirements.txt b/internal/kclvm_py/scripts/requirements.txt new file mode 100644 index 000000000..07a269b7b --- /dev/null +++ b/internal/kclvm_py/scripts/requirements.txt @@ -0,0 +1,25 @@ +wheel==0.34.2 +twine==3.2.0 +pyyaml==5.3 +pytest-xdist==2.2.1 +lark-parser==0.11.3 +filelock==3.6.0 +yapf==0.29.0 +pytest==6.2.2 +pypeg2==2.15.2 +protobuf==3.19.4 +gevent==20.9 +grequests +schema +coverage +ruamel.yaml +toml +numpydoc +pygls==0.10.3 +fastapi +uvicorn +gunicorn==20.1.0 +parsy==1.3.0 +wasmer==1.0.0 +wasmer_compiler_cranelift==1.0.0 +pyopenssl diff --git a/internal/kclvm_py/scripts/test.sh b/internal/kclvm_py/scripts/test.sh new file mode 100755 index 000000000..08de06049 --- /dev/null +++ b/internal/kclvm_py/scripts/test.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +RED='\033[0;31m' +function red() { + printf "${RED}$@${NC}\n" +} + +if [ "$?" -ne 0 ]; then + echo $(red update gitsubmodule failed! exit...) + exit 1 +fi + +# Options +help_message=$(cat <<-END + Usage: + test.sh -h + Print this help message + test.sh -a [action] + Perform a test + test.sh + Perform a test interactively + Available actions: + test_unit + trigger unit test + test_grammar + trigger grammar test + all + trigger all tests +END +) +action= +while getopts "a:h:s:" opt; do + case $opt in + a) + action="$OPTARG" + ;; + h) + echo "$help_message" + exit 1 + ;; + \?) echo "Invalid option -$OPTARG" + ;; + esac +done + +if [ "$action" == "" ]; then + PS3='Please select the test scope: ' + options=("test_unit" "test_grammar" "all") + select action in "${options[@]}" + do + case $action in + "test_unit") + $topdir/internal/kclvm_py/scripts/test_unit.sh + break + ;; + "test_grammar") + $topdir/internal/kclvm_py/scripts/test_grammar.sh + break + ;; + "all") + $topdir/internal/kclvm_py/scripts/test_unit.sh && $topdir/internal/kclvm_py/scripts/test_grammar.sh + break + ;; + *) echo "Invalid action $REPLY:$action" + exit 1 + break + ;; + esac + done +fi diff --git a/internal/kclvm_py/scripts/test_grammar.sh b/internal/kclvm_py/scripts/test_grammar.sh new file mode 100755 index 000000000..168d8e3a2 --- /dev/null +++ b/internal/kclvm_py/scripts/test_grammar.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +topdir=$(realpath $(dirname $0)/../../../) +kclvm_install_dir="$topdir/_build/dist/$os/kclvm" +kclvm_source_dir="$topdir" + +echo PATH=$PATH:$kclvm_source_dir/_build/dist/ubuntu/kclvm/bin >> ~/.bash_profile +source ~/.bash_profile + +# Grammar test +cd $kclvm_source_dir/test/grammar +kclvm -m pytest -v -n 10 diff --git a/internal/kclvm_py/scripts/test_unit.sh b/internal/kclvm_py/scripts/test_unit.sh new file mode 100755 index 000000000..245927847 --- /dev/null +++ b/internal/kclvm_py/scripts/test_unit.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +topdir=$(realpath $(dirname $0)/../../../) +kclvm_install_dir="$topdir/_build/dist/$os/kclvm" +kclvm_source_dir="$topdir" + +echo PATH=$PATH:$kclvm_source_dir/_build/dist/ubuntu/kclvm/bin >> ~/.bash_profile +source ~/.bash_profile + +# Install the dependency +kclvm -m pip install nose==1.3.7 + +# Unit test +cd $kclvm_source_dir/test/test_units/ +kclvm -m nose diff --git a/internal/kclvm_py/scripts/unittest_coverage.sh b/internal/kclvm_py/scripts/unittest_coverage.sh new file mode 100755 index 000000000..f1ad80de1 --- /dev/null +++ b/internal/kclvm_py/scripts/unittest_coverage.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# --------------------------------------------------------------------------------- +# Show the unit test coverage report +# For more info, see the +# [Python Coverage Documents](https://coverage.readthedocs.io/en/latest/) +# TODO: Using more mature tools and practices. e.g. +# https://github.com/CleanCut/green +# https://github.com/oldani/HtmlTestRunner +# --------------------------------------------------------------------------------- + +src="$(realpath $(dirname $0))/../" +unittest_path=$src/test/test_units/ +package_name=kclvm +xunit_file=TEST-kclvm.xml + +# Install the dependency +kclvm -m pip install nose==1.3.7 +# Run test with coverage output +kclvm -m nose --cover-package $package_name --with-coverage --cover-xml --cover-html --cover-erase --with-xunit --xunit-file=$xunit_file --tests=$unittest_path diff --git a/internal/kclvm_py/scripts/update-kclvm.sh b/internal/kclvm_py/scripts/update-kclvm.sh new file mode 100755 index 000000000..a0fa345b9 --- /dev/null +++ b/internal/kclvm_py/scripts/update-kclvm.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env bash + +# Stop on error. +set -e + +prepare_dirs () { + cpython_build_dir="$topdir/_build/dist/$os/cpython" + kclvm_packages_dir="$topdir/_build/packages" + kclvm_install_dir="$topdir/_build/dist/$os/kclvm" + mkdir -p "$kclvm_install_dir" + mkdir -p "$kclvm_packages_dir" +} + +prepare_dirs +kclvm_source_dir="$topdir/internal" + +# Perform the build process. +set -x + +# Copy KCLVM. +cp "$topdir/internal/kclvm_py/scripts/cli/kcl" $kclvm_install_dir/bin/ +cp "$topdir/internal/kclvm_py/scripts/cli/kcl-plugin" $kclvm_install_dir/bin/ +cp "$topdir/internal/kclvm_py/scripts/cli/kcl-doc" $kclvm_install_dir/bin/ +cp "$topdir/internal/kclvm_py/scripts/cli/kcl-test" $kclvm_install_dir/bin/ +cp "$topdir/internal/kclvm_py/scripts/cli/kcl-lint" $kclvm_install_dir/bin/ +cp "$topdir/internal/kclvm_py/scripts/cli/kcl-fmt" $kclvm_install_dir/bin/ +cp "$topdir/internal/kclvm_py/scripts/cli/kcl-vet" $kclvm_install_dir/bin/ +chmod +x $kclvm_install_dir/bin/kcl +chmod +x $kclvm_install_dir/bin/kcl-plugin +chmod +x $kclvm_install_dir/bin/kcl-doc +chmod +x $kclvm_install_dir/bin/kcl-test +chmod +x $kclvm_install_dir/bin/kcl-lint +chmod +x $kclvm_install_dir/bin/kcl-fmt +chmod +x $kclvm_install_dir/bin/kcl-vet + +kclvm_lib_dir=$kclvm_install_dir/lib/python3.7/ +if [ -d $kclvm_install_dir/lib/python3.9/ ]; then + kclvm_lib_dir=$kclvm_install_dir/lib/python3.9/ +fi + +if [ -d $kclvm_lib_dir/kclvm ]; then + rm -rf $kclvm_lib_dir/kclvm +fi +cp -r $kclvm_source_dir/kclvm_py $kclvm_lib_dir/kclvm + +set +x + +# build kclvm-cli + +cd $topdir/kclvm +cargo build --release + +touch $kclvm_install_dir/bin/kclvm_cli +rm $kclvm_install_dir/bin/kclvm_cli +cp ./target/release/kclvm_cli $kclvm_install_dir/bin/kclvm_cli + +# libkclvm_cli + +# Darwin dylib +if [ -e target/release/libkclvm_cli.dylib ]; then + touch $kclvm_install_dir/bin/libkclvm_cli.dylib + rm $kclvm_install_dir/bin/libkclvm_cli.dylib + cp target/release/libkclvm_cli.dylib $kclvm_install_dir/bin/libkclvm_cli.dylib +fi +# Linux so +if [ -e target/release/libkclvm_cli.so ]; then + touch $kclvm_install_dir/bin/libkclvm_cli.so + rm $kclvm_install_dir/bin/libkclvm_cli.so + cp target/release/libkclvm_cli.so $kclvm_install_dir/bin/libkclvm_cli.so +fi +# Windows dll +if [ -e target/release/libkclvm_cli.dll ]; then + touch $kclvm_install_dir/bin/libkclvm_cli.dll + rm $kclvm_install_dir/bin/libkclvm_cli.dll + cp target/release/libkclvm_cli.dll $kclvm_install_dir/bin/libkclvm_cli.dll +fi + + +# build rust std lib + +RUST_SYS_ROOT=`rustc --print sysroot` + +# libstd-*.dylib or libstd-*.so +cd $RUST_SYS_ROOT/lib +RUST_LIBSTD=`find libstd-*.*` + +mkdir -p $kclvm_install_dir/lib +cp "$RUST_SYS_ROOT/lib/$RUST_LIBSTD" $kclvm_install_dir/lib/$RUST_LIBSTD +echo "$RUST_LIBSTD" > $kclvm_install_dir/lib/rust-libstd-name.txt + +# Build kclvm runtime + +cd $topdir/kclvm/runtime +## Native +cargo build --release +cp target/release/libkclvm.a $kclvm_install_dir/lib/libkclvm_native.a + +# Darwin dylib +if [ -e target/release/libkclvm.dylib ]; then + touch $kclvm_install_dir/lib/libkclvm.dylib + rm $kclvm_install_dir/lib/libkclvm.dylib + cp target/release/libkclvm.dylib $kclvm_install_dir/lib/ + cp target/release/libkclvm.dylib $kclvm_install_dir/lib/libkclvm_native_shared.dylib +fi +# Linux so +if [ -e target/release/libkclvm.so ]; then + touch $kclvm_install_dir/lib/libkclvm.so + rm $kclvm_install_dir/lib/libkclvm.so + cp target/release/libkclvm.so $kclvm_install_dir/lib/ + cp target/release/libkclvm.so $kclvm_install_dir/lib/libkclvm_native_shared.so +fi +# Windows dll +if [ -e target/release/libkclvm.dll ]; then + touch $kclvm_install_dir/lib/libkclvm.dll + rm $kclvm_install_dir/lib/libkclvm.dll + cp target/release/libkclvm.dll $kclvm_install_dir/lib/ + cp target/release/libkclvm.dll $kclvm_install_dir/lib/libkclvm_native_shared.dll +fi + +# WASM +cargo build --release --target wasm32-unknown-unknown +cp target/wasm32-unknown-unknown/release/libkclvm.a $kclvm_install_dir/lib/libkclvm_wasm32.a +cp src/_kclvm_undefined_wasm.txt $kclvm_install_dir/lib/_kclvm_undefined_wasm.txt + +cp src/_kclvm.bc $kclvm_install_dir/include/_kclvm.bc +cp src/_kclvm.h $kclvm_install_dir/include/_kclvm.h + +cd $kclvm_install_dir/include + +# build kclvm_plugin python module + +cd $topdir/kclvm/plugin +kclvm setup.py install_lib + +# Print the summary. +echo "================ Summary ================" +echo " KCLVM is updated into $kclvm_install_dir" diff --git a/internal/kclvm_py/spec/Makefile b/internal/kclvm_py/spec/Makefile new file mode 100644 index 000000000..6f98429a4 --- /dev/null +++ b/internal/kclvm_py/spec/Makefile @@ -0,0 +1,43 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +default: + go install github.com/golang/protobuf/protoc-gen-go@latest + + mkdir -p ./_output + + # modfile + protoc -I. --python_out=./_output ./modfile/modfile.proto + + cp ./_output/modfile/modfile_pb2.py \ + ../config/modfile_pb2.py + + # gpyrpc + protoc -I. --python_out=./_output ./gpyrpc/gpyrpc.proto ./gpyrpc/protorpc_wire.proto + + mkdir -p ../internal/gpyrpc + cp ./_output/gpyrpc/gpyrpc_pb2.py \ + ../internal/gpyrpc/gpyrpc_pb2.py + cp ./_output/gpyrpc/protorpc_wire_pb2.py \ + ../internal/gpyrpc/protorpc_wire_pb2.py + + # protorpc + protoc -I. --python_out=./_output ./gpyrpc/protorpc_wire.proto + make protorpc-stub + + -rm -rf ./_output + +protorpc-stub: + cd ./gpyrpc/protoc-gen-protorpc-py && go fmt \ + && go install github.com/golang/protobuf/protoc-gen-go@latest \ + && go install . + + cd gpyrpc && protoc --protorpc-py_out=. gpyrpc.proto + cp ./gpyrpc/kusionstack.io/kclvm-go/pkg/spec/gpyrpc/gpyrpc.pb.protorpc.py \ + ../internal/gpyrpc/gpyrpc_pb_protorpc.py + + -rm -rf ./gpyrpc/kusionstack.io + + cd .. && make format + +clean: + -rm -rf ./_output diff --git a/internal/kclvm_py/spec/gpyrpc/gpyrpc.proto b/internal/kclvm_py/spec/gpyrpc/gpyrpc.proto new file mode 100644 index 000000000..d9a6dfecf --- /dev/null +++ b/internal/kclvm_py/spec/gpyrpc/gpyrpc.proto @@ -0,0 +1,395 @@ +// Copyright 2021 The KCL Authors. All rights reserved. +// +// This file defines the request parameters and return structure of the KCLVM RPC server. +// We can use the following command to start a KCLVM RPC server. +// +// ``` +// kclvm -m kclvm.program.rpc-server -http=:2021 +// ``` +// +// The service can then be requested via the POST protocol: +// +// ``` +// $ curl -X POST http://127.0.0.1:2021/api:protorpc/BuiltinService.Ping --data '{}' +// { +// "error": "", +// "result": {} +// } +// ``` + +syntax = "proto3"; + +package gpyrpc; + +option go_package = "kusionstack.io/kclvm-go/pkg/spec/gpyrpc;gpyrpc"; + +import "google/protobuf/any.proto"; +import "google/protobuf/descriptor.proto"; + +// ---------------------------------------------------------------------------- + +// kcl main.k -D name=value +message CmdArgSpec { + string name = 1; + string value = 2; // TODO: any? +} + +// kcl main.k -O pkgpath:path.to.field=field_value +message CmdOverrideSpec { + string pkgpath = 1; + string field_path = 2; + string field_value = 3; + string action = 4; +} + +// ---------------------------------------------------------------------------- +// gpyrpc request/response/error types +// ---------------------------------------------------------------------------- + +message RestResponse { + google.protobuf.Any result = 1; + string error = 2; + KclError kcl_err = 3; +} + +message KclError { + string ewcode = 1; // See kclvm/kcl/error/kcl_err_msg.py + string name = 2; + string msg = 3; + repeated KclErrorInfo error_infos = 4; +} + +message KclErrorInfo { + string err_level = 1; + string arg_msg = 2; + string filename = 3; + string src_code = 4; + string line_no = 5; + string col_no = 6; +} + +// ---------------------------------------------------------------------------- +// service requset/response +// ---------------------------------------------------------------------------- + +// gpyrpc.BuiltinService +service BuiltinService { + rpc Ping(Ping_Args) returns(Ping_Result); + rpc ListMethod(ListMethod_Args) returns(ListMethod_Result); +} + +// gpyrpc.KclvmService +service KclvmService { + rpc Ping(Ping_Args) returns(Ping_Result); + + rpc ParseFile_LarkTree(ParseFile_LarkTree_Args) returns(ParseFile_LarkTree_Result); + rpc ParseFile_AST(ParseFile_AST_Args) returns(ParseFile_AST_Result); + rpc ParseProgram_AST(ParseProgram_AST_Args) returns(ParseProgram_AST_Result); + + rpc ExecProgram(ExecProgram_Args) returns(ExecProgram_Result); + + rpc ResetPlugin(ResetPlugin_Args) returns(ResetPlugin_Result); + + rpc FormatCode(FormatCode_Args) returns(FormatCode_Result); + rpc FormatPath(FormatPath_Args) returns(FormatPath_Result); + rpc LintPath(LintPath_Args) returns(LintPath_Result); + rpc OverrideFile(OverrideFile_Args) returns (OverrideFile_Result); + + rpc EvalCode(EvalCode_Args) returns(EvalCode_Result); + rpc ResolveCode(ResolveCode_Args) returns(ResolveCode_Result); + rpc GetSchemaType(GetSchemaType_Args) returns(GetSchemaType_Result); + rpc ValidateCode(ValidateCode_Args) returns(ValidateCode_Result); + rpc SpliceCode(SpliceCode_Args) returns(SpliceCode_Result); + + rpc Complete(Complete_Args) returns(Complete_Result); + rpc GoToDef(GoToDef_Args) returns(GoToDef_Result); + rpc DocumentSymbol(DocumentSymbol_Args) returns(DocumentSymbol_Result); + rpc Hover(Hover_Args) returns(Hover_Result); + + rpc ListDepFiles(ListDepFiles_Args) returns(ListDepFiles_Result); + rpc LoadSettingsFiles(LoadSettingsFiles_Args) returns(LoadSettingsFiles_Result); +} + +message Ping_Args { + string value = 1; +} +message Ping_Result { + string value = 1; +} + +message ListMethod_Args { + // empty +} +message ListMethod_Result { + repeated string method_name_list = 1; +} + +message ParseFile_LarkTree_Args { + string filename = 1; + string source_code = 2; + bool ignore_file_line = 3; +} +message ParseFile_LarkTree_Result { + string lark_tree_json = 1; +} + +message ParseFile_AST_Args { + string filename = 1; + string source_code = 2; +} +message ParseFile_AST_Result { + string ast_json = 1; // json value +} + +message ParseProgram_AST_Args { + repeated string k_filename_list = 1; +} +message ParseProgram_AST_Result { + string ast_json = 1; // json value +} + +message ExecProgram_Args { + string work_dir = 1; + + repeated string k_filename_list = 2; + repeated string k_code_list = 3; + + repeated CmdArgSpec args = 4; + repeated CmdOverrideSpec overrides = 5; + + bool disable_yaml_result = 6; + + bool print_override_ast = 7; + + // -r --strict-range-check + bool strict_range_check = 8; + + // -n --disable-none + bool disable_none = 9; + // -v --verbose + int32 verbose = 10; + + // -d --debug + int32 debug = 11; + + // yaml/json: sort keys + bool sort_keys = 12; + // include schema type path in JSON/YAML result + bool include_schema_type_path = 13; +} +message ExecProgram_Result { + string json_result = 1; + string yaml_result = 2; + + string escaped_time = 101; +} + +message ResetPlugin_Args { + string plugin_root = 1; +} +message ResetPlugin_Result { + // empty +} + +message FormatCode_Args { + string source = 1; +} + +message FormatCode_Result { + bytes formatted = 1; +} + +message FormatPath_Args { + string path = 1; +} + +message FormatPath_Result { + repeated string changedPaths = 1; +} + +message LintPath_Args { + string path = 1; +} + +message LintPath_Result { + repeated string results = 1; +} + +message OverrideFile_Args { + string file = 1; + repeated string specs = 2; + repeated string import_paths = 3; +} + +message OverrideFile_Result { + bool result = 1; +} + +message EvalCode_Args { + string code = 1; +} +message EvalCode_Result { + string json_result = 2; +} + +message ResolveCode_Args { + string code = 1; +} + +message ResolveCode_Result { + bool success = 1; +} + +message GetSchemaType_Args { + string file = 1; + string code = 2; + string schema_name = 3; // emtry is all +} +message GetSchemaType_Result { + repeated KclType schema_type_list = 1; +} + +message ValidateCode_Args { + string data = 1; + string code = 2; + string schema = 3; + string attribute_name = 4; + string format = 5; +} + +message ValidateCode_Result { + bool success = 1; + string err_message = 2; +} + +message CodeSnippet { + string schema = 1; + string rule = 2; +} + +message SpliceCode_Args { + repeated CodeSnippet codeSnippets = 1; +} + +message SpliceCode_Result { + string spliceCode = 1; +} + +message Position { + int64 line = 1; + int64 column = 2; + string filename = 3; +} + +message Complete_Args { + Position pos = 1; + string name = 2; + string code = 3; +} + +message Complete_Result { + string completeItems = 1; +} + +message GoToDef_Args { + Position pos = 1; + string code = 2; +} + +message GoToDef_Result { + string locations = 1; +} + +message DocumentSymbol_Args { + string file = 1; + string code = 2; +} + +message DocumentSymbol_Result { + string symbol = 1; +} + +message Hover_Args { + Position pos = 1; + string code = 2; +} + +message Hover_Result { + string hoverResult = 1; +} + +message ListDepFiles_Args { + string work_dir = 1; + bool use_abs_path = 2; + bool include_all = 3; + bool use_fast_parser = 4; +} + +message ListDepFiles_Result { + string pkgroot = 1; + string pkgpath = 2; + repeated string files = 3; +} + +// --------------------------------------------------------------------------------- +// LoadSettingsFiles API +// Input work dir and setting files and return the merged kcl singleton config. +// --------------------------------------------------------------------------------- + +message LoadSettingsFiles_Args { + string work_dir = 1; + repeated string files = 2; +} + +message LoadSettingsFiles_Result { + CliConfig kcl_cli_configs = 1; + repeated KeyValuePair kcl_options = 2; +} + +message CliConfig { + repeated string files = 1; + string output = 2; + repeated string overrides = 3; + repeated string path_selector = 4; + bool strict_range_check = 5; + bool disable_none = 6; + int64 verbose = 7; + bool debug = 8; +} + +message KeyValuePair { + string key = 1; + string value = 2; +} + +// ---------------------------------------------------------------------------- +// JSON Schema Lit +// ---------------------------------------------------------------------------- + +message KclType { + string type = 1; // schema, dict, list, str, int, float, bool, null, type_string + repeated KclType union_types = 2 ; // union types + string default = 3; // default value + + string schema_name = 4; // schema name + string schema_doc = 5; // schema doc + map properties = 6; // schema properties + repeated string required = 7; // required schema properties, [property_name1, property_name2] + + KclType key = 8; // dict key type + KclType item = 9; // dict/list item type + + int32 line = 10; + + repeated Decorator decorators = 11; // schema decorators +} + +message Decorator { + string name = 1; + repeated string arguments = 2; + map keywords = 3; +} + +// ---------------------------------------------------------------------------- +// END +// ---------------------------------------------------------------------------- diff --git a/internal/kclvm_py/spec/gpyrpc/protoc-gen-protorpc-py/go.mod b/internal/kclvm_py/spec/gpyrpc/protoc-gen-protorpc-py/go.mod new file mode 100644 index 000000000..a177520de --- /dev/null +++ b/internal/kclvm_py/spec/gpyrpc/protoc-gen-protorpc-py/go.mod @@ -0,0 +1,8 @@ +module github.com/chai2010/protorpc-py/protoc-gen-protorpc-py + +go 1.16 + +require ( + github.com/chai2010/protorpc v1.1.1 + github.com/golang/protobuf v1.0.0 // indirect +) diff --git a/internal/kclvm_py/spec/gpyrpc/protoc-gen-protorpc-py/go.sum b/internal/kclvm_py/spec/gpyrpc/protoc-gen-protorpc-py/go.sum new file mode 100644 index 000000000..357b14bc7 --- /dev/null +++ b/internal/kclvm_py/spec/gpyrpc/protoc-gen-protorpc-py/go.sum @@ -0,0 +1,5 @@ +github.com/chai2010/protorpc v1.1.1 h1:UQIj4l0At5C9zDbQJFlvIcZeREexylWvc3rH9ZSlfso= +github.com/chai2010/protorpc v1.1.1/go.mod h1:/wO0kiyVdu7ug8dCMrA2yDr2vLfyhsLEuzLa9J2HJ+I= +github.com/golang/protobuf v1.0.0 h1:lsek0oXi8iFE9L+EXARyHIjU5rlWIhhTkjDz3vHhWWQ= +github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= diff --git a/internal/kclvm_py/spec/gpyrpc/protoc-gen-protorpc-py/main.go b/internal/kclvm_py/spec/gpyrpc/protoc-gen-protorpc-py/main.go new file mode 100644 index 000000000..c393b1c52 --- /dev/null +++ b/internal/kclvm_py/spec/gpyrpc/protoc-gen-protorpc-py/main.go @@ -0,0 +1,225 @@ +package main + +import ( + "bytes" + "fmt" + "log" + "os" + "path/filepath" + "sort" + "strings" + "text/template" + + plugin "github.com/chai2010/protorpc/protoc-gen-plugin" + "github.com/golang/protobuf/protoc-gen-go/descriptor" + "github.com/golang/protobuf/protoc-gen-go/generator" +) + +func init() { + plugin.RegisterCodeGenerator(new(protorpcPlugin)) +} + +func main() { + if len(os.Args) > 1 && (os.Args[1] == "-h" || os.Args[1] == "-help") { + fmt.Println("usage: protoc-gen-protorpc-py") + fmt.Println(" # install protoc") + fmt.Println(" go install github.com/golang/protobuf/protoc-gen-go") + fmt.Println(" go install github.com/chai2010/protorpc/protoc-gen-protorpc") + fmt.Println(" # install protoc-gen-protorpc-py") + fmt.Println(" protoc --protorpc-py_out=. gpyrpc.proto") + fmt.Println(" protoc-gen-protorpc-py -h") + return + } + plugin.Main() +} + +type protorpcPlugin struct{} + +func (p *protorpcPlugin) Name() string { return "protorpc-py" } +func (p *protorpcPlugin) FileNameExt() string { return ".pb.protorpc.py" } + +func (p *protorpcPlugin) HeaderCode(g *generator.Generator, file *generator.FileDescriptor) string { + moduleName := strings.TrimSuffix(filepath.Base(file.GetName()), ".proto") + "_pb2" + + var serviceList []*ServiceSpec + for _, svc := range file.Service { + serviceList = append(serviceList, p.buildServiceSpec(g, svc)) + } + + var messageMap = make(map[string]bool) + for _, svc := range serviceList { + for _, method := range svc.MethodList { + messageMap[method.InputTypeName] = true + messageMap[method.OutputTypeName] = true + } + } + + var messageList []string + for k := range messageMap { + messageList = append(messageList, k) + } + sort.Strings(messageList) + + var fnMap = template.FuncMap{ + "hello": func() string { return "hello" }, + } + + var buf bytes.Buffer + t := template.Must(template.New("").Funcs(fnMap).Parse(tmpl)) + err := t.Execute(&buf, + struct { + G *generator.Generator + File *generator.FileDescriptor + ModuleName string + ServiceList []*ServiceSpec + MessageList []string + }{ + G: g, + File: file, + ModuleName: moduleName, + ServiceList: serviceList, + MessageList: messageList, + }, + ) + if err != nil { + log.Fatal(err) + } + + return buf.String() +} + +func (p *protorpcPlugin) ServiceCode(g *generator.Generator, file *generator.FileDescriptor, svc *descriptor.ServiceDescriptorProto) string { + return "" +} + +func (p *protorpcPlugin) MessageCode(g *generator.Generator, file *generator.FileDescriptor, msg *descriptor.DescriptorProto) string { + return "" +} + +type ServiceSpec struct { + ServiceName string + ServiceRawName string + + MethodList []ServiceMethodSpec +} + +type ServiceMethodSpec struct { + MethodName string + MethodRawName string + + InputTypeName string + OutputTypeName string +} + +func (p *protorpcPlugin) buildServiceSpec(g *generator.Generator, svc *descriptor.ServiceDescriptorProto) *ServiceSpec { + spec := &ServiceSpec{ + ServiceName: generator.CamelCase(svc.GetName()), + ServiceRawName: svc.GetName(), + } + + for _, m := range svc.Method { + if m.GetClientStreaming() || m.GetServerStreaming() { + continue + } + spec.MethodList = append(spec.MethodList, ServiceMethodSpec{ + MethodName: generator.CamelCase(m.GetName()), + MethodRawName: m.GetName(), + + InputTypeName: g.TypeName(g.ObjectNamed(m.GetInputType())), + OutputTypeName: g.TypeName(g.ObjectNamed(m.GetOutputType())), + }) + } + + return spec +} + +const tmpl = ` +{{- $G := .G -}} +{{- $File := .File -}} +{{- $ModuleName := .ModuleName -}} +{{- $ServiceList := .ServiceList -}} +{{- $MessageList := .MessageList -}} + +# Code generated by protoc-gen-protorpc-py. DO NOT EDIT. +# +# plugin: https://github.com/chai2010/protorpc/protoc-gen-plugin +# plugin: https://github.com/chai2010/protorpc-py/protoc-gen-protorpc-py +# +# source: {{$File.GetName}} + +import abc +import sys +import typing + +from google.protobuf import message as _message + +from .protorpc import ServiceMeta as _ServiceMeta +from .protorpc import Server as _Server + +from .{{$ModuleName}} import ({{range $k, $v := $MessageList}} + {{$v}}, +{{- end}}) + +{{range $k, $svc := $ServiceList}} +class {{$svc.ServiceName}}(metaclass=abc.ABCMeta): + {{- range $sss, $method := $svc.MethodList}} + + @abc.abstractmethod + def {{$method.MethodName}}(self, args: {{$method.InputTypeName}}) -> {{$method.OutputTypeName}}: + pass + {{- end}} +{{end}} + +{{range $k, $svc := $ServiceList}} +class {{$svc.ServiceName}}_Meta(_ServiceMeta): + + def __init__(self, instance: {{$svc.ServiceName}}): + super().__init__() + self._instance = instance + + def get_service_name(self) -> str: + return "{{$svc.ServiceName}}" + + def get_method_list(self) -> typing.List[str]: + return [ + {{- range $_, $method := $svc.MethodList}} + "{{$method.MethodRawName}}", + {{- end}} + ] + + def create_method_req_message(self, method: str) -> _message.Message: + {{- range $_, $method := $svc.MethodList}} + if method in ["{{$method.MethodRawName}}", "{{$svc.ServiceName}}.{{$method.MethodRawName}}"]: + return {{$method.InputTypeName}}() + {{- end}} + raise Exception(f"unknown method: {method}") + + def create_method_resp_message(self, method: str) -> _message.Message: + {{- range $_, $method := $svc.MethodList}} + if method in ["{{$method.MethodRawName}}", "{{$svc.ServiceName}}.{{$method.MethodRawName}}"]: + return {{$method.OutputTypeName}}() + {{- end}} + raise Exception(f"unknown method: {method}") + + def get_service_instance(self) -> _message.Message: + return typing.cast(_message.Message, self._instance) + + def call_method(self, method: str, req: _message.Message) -> _message.Message: + {{- range $_, $method := $svc.MethodList}} + if method in ["{{$method.MethodRawName}}", "{{$svc.ServiceName}}.{{$method.MethodRawName}}"]: + return self._instance.{{$method.MethodName}}(req) + {{- end}} + raise Exception(f"unknown method: {method}") +{{end}} + +{{range $k, $svc := $ServiceList}} +class {{$svc.ServiceName}}_Server: + def __init__(self, instance: {{$svc.ServiceName}}): + self.instance = instance + + def run(self, *, stdin=sys.stdin, stdout=sys.stdout): + rpc_server = _Server() + rpc_server.register_service({{$svc.ServiceName}}_Meta(self.instance)) + rpc_server.run(stdin=stdin, stdout=stdout) +{{end}} +` diff --git a/internal/kclvm_py/spec/gpyrpc/protorpc.py b/internal/kclvm_py/spec/gpyrpc/protorpc.py new file mode 100644 index 000000000..66ae46ace --- /dev/null +++ b/internal/kclvm_py/spec/gpyrpc/protorpc.py @@ -0,0 +1,170 @@ +import abc +import sys +import typing + +import varint + +from google.protobuf import message as _message + +from .protorpc_wire_pb2 import ( + RequestHeader, + ResponseHeader, +) + + +class Channel(object): + def __init__(self, *, stdin=sys.stdin, stdout=sys.stdout): + self.stdin = stdin + self.stdout = stdout + self.next_id = 1 + + def get_next_id(self) -> int: + next_id = self.next_id + self.next_id = self.next_id + 1 + return next_id + + def send_frame(self, data: bytes): + self.stdout.buffer.write(varint.encode(len(data))) + self.stdout.buffer.write(data) + self.stdout.flush() + + def recv_frame(self, _max_size: int = 0) -> bytes: + size = varint.decode_stream(self.stdin.buffer) + data = self.stdin.buffer.read(size) + return data + + def write_request(self, method: str, req: _message.Message): + body = req.SerializeToString() + + hdr = RequestHeader( + id=self.get_next_id(), method=method, raw_request_len=len(body) + ) + self.send_frame(hdr.SerializeToString()) + self.send_frame(body) + + def read_request_header(self) -> RequestHeader: + data = self.recv_frame() + hdr = RequestHeader() + hdr.ParseFromString(data) + return hdr + + def read_request_body(self, _header: RequestHeader, body: _message.Message): + data = self.recv_frame() + body.ParseFromString(data) + + def write_response(self, id_: int, error: str, response: _message.Message): + if not error: + body = response.SerializeToString() + else: + body = "" + + hdr = ResponseHeader(id=id_, error=error, raw_response_len=len(body)) + self.send_frame(hdr.SerializeToString()) + + if not error: + self.send_frame(body) + + def read_response_header(self) -> ResponseHeader: + data = self.recv_frame() + hdr = ResponseHeader() + hdr.ParseFromString(data) + return hdr + + def read_response_body(self, header: ResponseHeader, body: _message.Message): + if header.error: + raise header.error + data = self.recv_frame() + body.ParseFromString(data) + + def call_method(self, method: str, req: _message.Message, resp: _message.Message): + self.write_request(method, req) + resp_hdr = self.read_response_header() + self.read_response_body(resp_hdr, resp) + + +class ServiceMeta(metaclass=abc.ABCMeta): + def __init__(self, instance: _message.Message): + self._instance = instance + + def get_service_instance(self) -> _message.Message: + return self._instance + + def call_method(self, method: str, req: _message.Message) -> _message.Message: + return getattr(self.get_service_instance(), method[method.rfind(".") + 1:])( + req + ) + + @abc.abstractmethod + def get_service_name(self) -> str: + pass + + @abc.abstractmethod + def get_method_list(self) -> typing.List[str]: + pass + + @abc.abstractmethod + def create_method_req_message(self, method: str) -> _message.Message: + pass + + @abc.abstractmethod + def create_method_resp_message(self, method: str) -> _message.Message: + pass + + +class Server: + def __init__(self): + self.srv_table: typing.Dict[str, ServiceMeta] = {} + self.chan: typing.Optional[Channel] = None + + def register_service(self, srv: ServiceMeta): + self.srv_table[srv.get_service_name()] = srv + + def get_service_name_list(self) -> typing.List[str]: + name_list: typing.List[str] = [] + for s in self.srv_table.keys(): + name_list.append(s) + name_list.sort() + return name_list + + def get_method_name_list(self) -> typing.List[str]: + name_list: typing.List[str] = [] + for s in self.srv_table.values(): + name_list.extend(s.get_method_list()) + name_list.sort() + return name_list + + def run(self, *, stdin=sys.stdin, stdout=sys.stdout): + self.chan = Channel(stdin=stdin, stdout=stdout) + while True: + self._accept_one_call() + + def _accept_one_call(self): + hdr = self._read_req_header() + + service_name = hdr.method[: hdr.method.rfind(".")] + method_name = hdr.method[hdr.method.rfind(".") + 1:] + service = self.srv_table[service_name] + + req = self._read_req(service, hdr) + resp = service.call_method(method_name, req) + + self._write_resp(hdr.id, "", resp) + + def _read_req_header(self) -> RequestHeader: + return self.chan.read_request_header() + + def _read_req(self, service: ServiceMeta, hdr: RequestHeader) -> _message.Message: + req = service.create_method_req_message(hdr.method) + self.chan.read_request_body(hdr, req) + return req + + def _write_resp(self, id_: int, error: str, resp: _message.Message): + self.chan.write_response(id_, error, resp) + + +class Client: + def __init__(self, chan: Channel = None): + self.chan: Channel = chan + + def call_method(self, method: str, req: _message.Message, resp: _message.Message): + self.chan.call_method(method, req, resp) diff --git a/internal/kclvm_py/spec/gpyrpc/protorpc_wire.proto b/internal/kclvm_py/spec/gpyrpc/protorpc_wire.proto new file mode 100644 index 000000000..75a5078db --- /dev/null +++ b/internal/kclvm_py/spec/gpyrpc/protorpc_wire.proto @@ -0,0 +1,53 @@ +// Copyright 2013 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +syntax = "proto3"; + +// +// protorpc wire format wrapper +// +// 0. Frame Format +// len : uvarint64 +// data: byte[len] +// +// 1. Client Send Request +// Send RequestHeader: sendFrame(zsock, hdr, len(hdr)) +// Send Request: sendFrame(zsock, body, hdr.snappy_compressed_request_len) +// +// 2. Server Recv Request +// Recv RequestHeader: recvFrame(zsock, hdr, max_hdr_len, 0) +// Recv Request: recvFrame(zsock, body, hdr.snappy_compressed_request_len, 0) +// +// 3. Server Send Response +// Send ResponseHeader: sendFrame(zsock, hdr, len(hdr)) +// Send Response: sendFrame(zsock, body, hdr.snappy_compressed_response_len) +// +// 4. Client Recv Response +// Recv ResponseHeader: recvFrame(zsock, hdr, max_hdr_len, 0) +// Recv Response: recvFrame(zsock, body, hdr.snappy_compressed_response_len, 0) +// +package protorpc_wire; + +enum Const { + ZERO = 0; + MAX_REQUEST_HEADER_LEN = 1024; +} + +message RequestHeader { + uint64 id = 1; + string method = 2; + + uint32 raw_request_len = 3; + uint32 snappy_compressed_request_len = 4; + uint32 checksum = 5; +} + +message ResponseHeader { + uint64 id = 1; + string error = 2; + + uint32 raw_response_len = 3; + uint32 snappy_compressed_response_len = 4; + uint32 checksum = 5; +} diff --git a/internal/kclvm_py/spec/modfile/modfile.proto b/internal/kclvm_py/spec/modfile/modfile.proto new file mode 100644 index 000000000..e90dfd738 --- /dev/null +++ b/internal/kclvm_py/spec/modfile/modfile.proto @@ -0,0 +1,29 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +syntax = "proto3"; + +package kclvm.modfile; + +// kcl.mod 文件对应的内存格式 +// kcl.mod 文件为TOML格式, 字段名字和类型保持一致 +message KclModFile { + string root = 1; // 根目录路径, 由程序填充(TODO) + string root_pkg = 2; // 根包import路径, 对应所有子包的前缀, 可以忽略(TODO) + + KclModFile_build_section build = 3; // build 配置 + KclModFile_expected_section expected = 4; // expected 配置 +} + +message KclModFile_build_section { + bool enable_pkg_cache = 1; // 启动pkg缓存 + string cached_pkg_prefix = 2; // 缓存的前缀路径 + string target = 3; // 编译的目标,可选 native, wasm +} + +message KclModFile_expected_section { + string min_build_time = 1; // 期望构建时间下界 2021-08-14 20:30:08 + string max_build_time = 2; // 期望构建时间上界 2021-08-16 20:30:08 + string kclvm_version = 3; // KCLVM 版本依赖 + string kcl_plugin_version = 4; // KCLVM Plugin 版本依赖 + string global_version = 5; // 全局版本 +} diff --git a/internal/kclvm_py/tools/__init__.py b/internal/kclvm_py/tools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/internal/kclvm_py/tools/docs/__init__.py b/internal/kclvm_py/tools/docs/__init__.py new file mode 100644 index 000000000..cea1fe5cc --- /dev/null +++ b/internal/kclvm_py/tools/docs/__init__.py @@ -0,0 +1,11 @@ +from .doc import ( + kcl_doc_generate, + kcl_i18n_init, + kcl_i18n_info, +) + +__all__ = [ + "kcl_doc_generate", + "kcl_i18n_init", + "kcl_i18n_info", +] diff --git a/internal/kclvm_py/tools/docs/checker.py b/internal/kclvm_py/tools/docs/checker.py new file mode 100644 index 000000000..1e62bdea0 --- /dev/null +++ b/internal/kclvm_py/tools/docs/checker.py @@ -0,0 +1,128 @@ +from typing import List, Dict + +import kclvm.kcl.error as kcl_error +import kclvm.tools.docs.model_pb2 as model + + +class SchemaDocStringChecker: + """KCL schema docstring verification + + Verify that the schema docstring is consistent with + the schema definition + + Parameters + ---------- + schema: model.SchemaDoc + The schema AST node + + model: model.SchemaDoc + The schema document model + """ + + ATTR_NOT_FOUND_IN_DOC = ( + "Missing schema attribute description in schema: '{}', attribute: '{}'" + ) + ATTR_NOT_FOUND_IN_SCHEMA = ( + "Redundant schema attribute description in schema: '{}', attribute: '{}'" + ) + ATTR_INFO_INCONSISTENT_TYPE = "Inconsistent schema attribute info: schema: '{}', attribute: '{}', type in docstring: '{}', actual type : '{}'" + + ATTR_INFO_INCONSISTENT_OPTIONAL = "Inconsistent schema attribute info: schema: '{}', attribute: '{}', is_optional in docstring: '{}', actual is_optional : '{}'" + + def __init__(self, source_code: model.SchemaDoc, doc_string: model.SchemaDoc): + self._source_code = source_code + self._doc_string = doc_string + + def _check_attribute_diff( + self, + attr_map: Dict[str, model.SchemaAttributeDoc], + attr_doc_map: Dict[str, model.SchemaAttributeDoc], + ): + """Verify that the schema docstring Attributes section describes exactly the same attributes as the schema body defines: + 1. all the attributes defined in the schema body must have the corresponding description in the docstring Attributes section + 2. all the attributes described in the docstring Attributes section must have the corresponding definition in the schema body + """ + + attr_not_in_doc = set(attr_map) - set(attr_doc_map) + attr_not_in_schema = set(attr_doc_map) - set(attr_map) + if attr_not_in_schema: + kcl_error.report_warning( + err_type=kcl_error.ErrType.InvalidDocstring_TYPE, + arg_msg=self.ATTR_NOT_FOUND_IN_SCHEMA.format( + self._source_code.name, + ",".join(attr_not_in_schema), + ), + ) + if attr_not_in_doc: + kcl_error.report_warning( + err_type=kcl_error.ErrType.InvalidDocstring_TYPE, + arg_msg=self.ATTR_NOT_FOUND_IN_DOC.format( + self._source_code.name, + ",".join(attr_not_in_doc), + ), + ) + + def _check_attribute_def( + self, + attr_map: Dict[str, model.SchemaAttributeDoc], + attr_doc_map: Dict[str, model.SchemaAttributeDoc], + ): + """Verify that each attribute in the schema docstring Attributes section is consistent with the corresponding attribute in schema body. + Following features of the attribute will be verified: + 1. the attribute's type + 2. the attribute's optional info + todo: the default value is not checked, since the code representation can be various, it can only be checked semantically. + """ + common_attr_list = set(attr_map) & set(attr_doc_map) + for attr in common_attr_list: + schema_attr = attr_map[attr] + schema_attr_doc = attr_doc_map[attr] + if schema_attr.type.type_str.replace( + " ", "" + ) != schema_attr_doc.type.type_str.replace(" ", ""): + kcl_error.report_warning( + err_type=kcl_error.ErrType.InvalidDocstring_TYPE, + arg_msg=self.ATTR_INFO_INCONSISTENT_TYPE.format( + self._source_code.name, + attr, + schema_attr_doc.type.type_str, + schema_attr.type.type_str, + ), + ) + if schema_attr.is_optional != schema_attr_doc.is_optional: + kcl_error.report_warning( + err_type=kcl_error.ErrType.InvalidDocstring_TYPE, + arg_msg=self.ATTR_INFO_INCONSISTENT_OPTIONAL.format( + self._source_code.name, + attr, + schema_attr_doc.is_optional, + schema_attr.is_optional, + ), + ) + + def _check( + self, + schema_attr_list: List[model.SchemaAttributeDoc], + attr_doc_list: List[model.SchemaAttributeDoc], + ): + if not schema_attr_list or not attr_doc_list: + return + # Grouped by the schema attribute name + attr_map = {attr.name: attr for attr in schema_attr_list} + attr_doc_map = {attr.name: attr for attr in attr_doc_list} + # Check attribute diff between schema docstring and schema AST + self._check_attribute_diff(attr_map, attr_doc_map) + # Check attribute definition between schema docstring and schema AST + self._check_attribute_def(attr_map, attr_doc_map) + + def check(self): + if ( + self._source_code is None + or self._doc_string is None + or not isinstance(self._source_code, model.SchemaDoc) + or not isinstance(self._doc_string, model.SchemaDoc) + ): + return + schema_attr_list = self._source_code.attributes + attr_doc_list = self._doc_string.attributes + self._check(schema_attr_list, attr_doc_list) diff --git a/internal/kclvm_py/tools/docs/doc.py b/internal/kclvm_py/tools/docs/doc.py new file mode 100644 index 000000000..616cebcb9 --- /dev/null +++ b/internal/kclvm_py/tools/docs/doc.py @@ -0,0 +1,393 @@ +"""Module doc extracts source code documentation from a KCL AST +""" +import io +import os + +from typing import Union, List, Dict, Optional +from pathlib import Path + +import kclvm.kcl.error as kcl_error +import kclvm.compiler.vfs as vfs +import kclvm.compiler.parser.parser as parser +import kclvm.tools.docs.formats as doc_formats +import kclvm.kcl.types.checker as type_checker +import kclvm.api.object as obj_pkg +import kclvm.kcl.ast as ast +import kclvm.tools.docs.model_pb2 as model +import kclvm.tools.docs.writer as writer +import kclvm.tools.docs.reader as reader +import kclvm.tools.docs.link_resolver as link_resolver +import kclvm.tools.docs.doc_escaper as doc_escaper +import kclvm.tools.docs.doc_parser as doc_parser +import kclvm.tools.docs.i18n as i18n +import kclvm.tools.docs.utils as utils + +# --------------------------------------------------- +# Constants +# --------------------------------------------------- + +KCL_SUFFIX = "*.k" +INVALID_OUTPUT_FORMAT_MSG = "invalid output format, expected: yaml, json or markdown" +INVALID_I18N_FORMAT_MSG = "invalid i18n metadata format, expected: yaml, json" + +# --------------------------------------------------- +# User interface functions used by kcl-doc cli +# --------------------------------------------------- + + +def kcl_doc_generate( + kcl_files: List[str], + output: str, + format: str = doc_formats.KCLDocFormat.MARKDOWN, + locale: str = "en", + recursively=False, + repo_url: str = None, + i18n_path: str = None, + with_locale_suffix: bool = False, +) -> None: + """ + generate a displayable doc file of a kcl file + :param kcl_files: the kcl file paths to generate doc on + :param output: the dir path to output the generated doc file + :param format: the document format to generate + :param locale: the document locale to generate + :param recursively: if search for the kcl files to generate doc on recursively + :param repo_url: the url to the source code repo + :param i18n_path: the i18n input path + """ + # check if the format and locale is valid + locale = locale.lower() + format_upper = format.upper() + if format_upper not in doc_formats.KCLDocFormat.MAPPING: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg=INVALID_OUTPUT_FORMAT_MSG, + ) + i18n.check_locale(locale) + # check if all the files exist + if len(kcl_files) == 0: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CannotFindModule_TYPE, + arg_msg="Empty list of input KCL files", + ) + for kcl_file in kcl_files: + if not Path(kcl_file).exists(): + kcl_error.report_exception( + err_type=kcl_error.ErrType.CannotFindModule_TYPE, + arg_msg=f"Cannot find the kcl file, please check whether the file path {kcl_file} exists" + if kcl_file + else f"The file path {kcl_file} is None", + ) + # parse module docs + module_docs = _parse_file_docs( + kcl_files, format_upper, locale, recursively, repo_url, with_locale_suffix + ) + # write to output + path = Path(output).resolve() + for _, doc in module_docs.items(): + # if the i18n file exists, use the doc content decoded from the existing file + i18n_file_path, i18n_format = _existed_i18n_file_path( + i18n_path, path, doc.name, locale + ) + if i18n_file_path and i18n_format: + doc = _read_from_file(i18n_format, i18n_file_path) + _escape_text({doc.name: doc}, format_upper) + doc_file_path = _doc_file_path( + path / Path(doc.relative_path).parent, + doc.name, + locale, + format_upper, + with_locale_suffix, + ) + _write_to_file(doc, format_upper, doc_file_path) + + +def kcl_i18n_init( + kcl_files: List[str], + output: str, + format: str, + locale: str = "en", + recursively=False, + with_locale_suffix: bool = False, +) -> None: + """ + init an i18n source doc file of a KCL file. Users can then modify the document part of it and generate docs in other formats based on it + :param kcl_files: the kcl file paths to generate doc on + :param output: the dir path to output the inited i18n file + :param format: the document format to init + :param locale: the document locale to init + :param recursively: if search for the kcl files to generate doc on recursively + """ + # check if the format and locale is valid + locale = locale.lower() + format_upper = format.upper() + if format_upper not in doc_formats.KCLI18NFormat.MAPPING: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg=INVALID_I18N_FORMAT_MSG, + ) + i18n.check_locale(locale) + # check if all the files exist + if len(kcl_files) == 0: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CannotFindModule_TYPE, + arg_msg="Empty list of input KCL files", + ) + for kcl_file in kcl_files: + if not Path(kcl_file).exists(): + kcl_error.report_exception( + err_type=kcl_error.ErrType.CannotFindModule_TYPE, + arg_msg=f"Cannot find the kcl file, please check whether the file path {kcl_file} exists" + if kcl_file + else f"The file path {kcl_file} is None", + ) + # parse module docs + module_docs = _parse_file_docs( + kcl_files, + format_upper, + locale, + recursively, + with_locale_suffix=with_locale_suffix, + ) + # write to output + path = Path(output).resolve() + path.mkdir(parents=True, exist_ok=True) + for _, doc in module_docs.items(): + i18n_path = _i18n_file_path(path, doc.name, locale, format_upper) + # reset doc content before gen i18n file + _write_to_file(doc, format_upper, i18n_path) + + +def kcl_i18n_info(kcl_files: List[str], format: str, locale: str) -> None: + """ + show an i18n source doc of a kcl file + :param kcl_files: the kcl files to show i18n info + :param format: the i18n file format to display + :param locale: the i18n file locale to display + """ + # check if the format and locale is valid + locale = locale.lower() + format_upper = format.upper() + if format_upper not in doc_formats.KCLI18NFormat.MAPPING: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg=INVALID_I18N_FORMAT_MSG, + ) + i18n.check_locale(locale) + + for kcl_file in kcl_files: + # calculate the i18n file path + path = _i18n_file_path( + Path(kcl_file).parent, utils.module_name(kcl_file), locale, format_upper + ) + # display the doc content read from i18n file to console + print(f"i18n file: {path} \ncontent:") + path = Path(path) + if path.exists(): + module_doc = _read_from_file(format_upper, path) + if module_doc: + output = io.StringIO() + _write_to_io(module_doc, format_upper, output) + print(output.getvalue()) + else: + print("Failed to parse i18n locale file, get nothing.") + else: + print( + f"I18n local file not exist. \n KCL file path: {kcl_file}, format: {format}, locale: {locale}" + ) + + +# --------------------------------------------------- +# Internal functions +# --------------------------------------------------- + + +def _read_from_file(format: str, in_file: Union[str, Path]) -> model.ModuleDoc: + with Path(in_file).open(mode="r", encoding="utf-8") as in_io: + module_doc = _read_from_io(format, in_io) + return module_doc + + +def _read_from_io(format: str, in_io: io.TextIOBase) -> model.ModuleDoc: + doc_reader = reader.factory.get(format, in_io) + return doc_reader.read_doc() + + +def _write_to_file( + doc: model.ModuleDoc, format: str, out_file: Union[str, Path] +) -> None: + Path(out_file).parent.mkdir(parents=True, exist_ok=True) + with Path(out_file).open(mode="w", encoding="utf-8") as out: + _write_to_io(doc, format, out) + + +def _write_to_io(doc: model.ModuleDoc, format: str, out: io.TextIOBase) -> None: + doc_writer = writer.factory.get(format, out) + doc_writer.write_doc(doc) + + +def _doc_file_name( + module_name: str, locale: str, format: str, with_locale_suffix: bool = False +) -> str: + return ( + f"doc_{module_name}_{locale}" + doc_formats.KCLDocSuffix.TO_SUFFIX[format] + if with_locale_suffix + else f"doc_{module_name}{doc_formats.KCLDocSuffix.TO_SUFFIX[format]}" + ) + + +def _doc_file_path( + path: Path, name: str, locale: str, format: str, with_locale_suffix: bool = False +) -> Path: + return path / _doc_file_name( + module_name=name, + locale=locale, + format=format, + with_locale_suffix=with_locale_suffix, + ) + + +def _i18n_file_path(path: Path, name: str, locale: str, format: str) -> Path: + return path / (f"i18n_{name}_{locale}" + doc_formats.KCLDocSuffix.TO_SUFFIX[format]) + + +def _existed_i18n_file_path( + i18n_path: str, path: Path, name: str, locale: str +) -> (Optional[Path], Optional[str]): + """ + check if there are existed i18n files for current source file path. + When there exists more than one possible i18n files, the priority to return is: KCL -> YAML -> JSON + + :param i18n_path: the i18n input file path + :param path: the dir path that contains KCL source file + :param name: the KCL module name to generate doc on + :param locale: the target locale + :return: the i18n file path if existed, else return None. + """ + if i18n_path: + i18n_path = Path(i18n_path) + if not i18n_path.exists(): + return None, None + if ( + i18n_path.is_file() + and i18n_path.suffix in doc_formats.KCLI18NFormat.FROM_SUFFIX + ): + return i18n_path, doc_formats.KCLI18NFormat.FROM_SUFFIX[i18n_path.suffix] + elif i18n_path.is_dir(): + path = i18n_path + else: + return None, None + yaml_i18n_path = _i18n_file_path(path, name, locale, doc_formats.KCLI18NFormat.YAML) + if yaml_i18n_path.exists(): + return yaml_i18n_path, doc_formats.KCLI18NFormat.YAML + + json_i18n_path = _i18n_file_path(path, name, locale, doc_formats.KCLI18NFormat.JSON) + if json_i18n_path.exists(): + return json_i18n_path, doc_formats.KCLI18NFormat.JSON + + return None, None + + +def _escape_text(module_docs: Dict[str, model.ModuleDoc], format: str): + escaper = doc_escaper.factory.get(format) + for path, module in module_docs.items(): + module_docs[path] = escaper.escape(module) + + +def _resolve_link( + root: str, + module_docs: Dict[str, model.ModuleDoc], + format: str, + locale: str, + with_locale_suffix: bool = False, +): + resolver = link_resolver.factory.get(format, _doc_file_name) + for path, module in module_docs.items(): + module_docs[path] = resolver.resolve(module, root, locale, with_locale_suffix) + + +def _parse_file_docs( + kcl_files: List[str], + format: str, + locale: str, + recursively: bool, + repo_url: str = None, + with_locale_suffix: bool = False, +) -> Dict[str, model.ModuleDoc]: + pkgs: List[str] = [] + if recursively: + for kcl_file in kcl_files: + pkgs.extend(_find_kcl_pkgs_recursively(Path(kcl_file))) + pkgs = list(set(pkgs)) + else: + pkgs = kcl_files + if len(pkgs) == 0: + return {} + root: str = vfs.GetPkgRoot(pkgs[0]) or os.path.dirname(pkgs[0]) + module_docs: Dict[str, model.ModuleDoc] = {} + trigger_lines = [ + f"import .{str(Path(pkg).relative_to(root)).replace('/', '.').rstrip('.k')}\n" + for pkg in pkgs + ] + trigger_file = Path(root) / "trigger_doc_gen.k" + with open(trigger_file, "w") as f: + f.writelines(trigger_lines) + try: + prog = parser.LoadProgram( + str(trigger_file), + work_dir=root, + ) + type_checker.ResolveProgramImport(prog) + checker = type_checker.TypeChecker(prog, type_checker.CheckConfig()) + checker.check_import(prog.MAIN_PKGPATH) + checker.init_global_types() + del prog.pkgs[prog.MAIN_PKGPATH] + finally: + os.remove(trigger_file) + pkg_docs = _parse_program_docs(prog, checker, repo_url) + _escape_text(pkg_docs, format) + _resolve_link(prog.root, pkg_docs, format, locale, with_locale_suffix) + + module_docs.update(pkg_docs) + return module_docs + + +def _parse_program_docs( + program: ast.Program, checker: type_checker.TypeChecker, repo_url: str = None +) -> Dict[str, model.ModuleDoc]: + + pkgs: Dict[str, List[ast.Module]] = program.pkgs + module_docs: Dict[str, model.ModuleDoc] = {} + for pkgpath, modules in pkgs.items(): + for m in modules: + schema_docs: list = [] + schema_list = m.GetSchemaList() + # only generate module doc if the module contains schemas + if schema_list: + for schema in schema_list: + current_scope = checker.scope_map[pkgpath] + schema_obj_type = current_scope.elems[schema.name].type + if isinstance(schema_obj_type, obj_pkg.KCLSchemaDefTypeObject): + schema_doc = doc_parser.SchemaDocParser( + schema=schema, + schema_type=schema_obj_type.schema_type, + root=program.root, + ).doc + schema_docs.append(schema_doc) + module_doc = model.ModuleDoc( + name=utils.module_name(m.filename), + relative_path=m.relative_filename, + doc=m.doc, + schemas=schema_docs, + source_code_url=repo_url, + ) + module_docs[m.relative_filename] = module_doc + return module_docs + + +def _find_kcl_pkgs_recursively(file: Path) -> List[str]: + if file.is_file(): + return [str(file)] + all_files = file.rglob("*.k") + pkgs: List[str] = [str(f.parent) for f in all_files] + return list(set(pkgs)) diff --git a/internal/kclvm_py/tools/docs/doc_escaper.py b/internal/kclvm_py/tools/docs/doc_escaper.py new file mode 100644 index 000000000..3742c2f59 --- /dev/null +++ b/internal/kclvm_py/tools/docs/doc_escaper.py @@ -0,0 +1,60 @@ +from abc import ABC, abstractmethod + +import kclvm.tools.docs.factory as factory +import kclvm.tools.docs.formats as doc_formats +import kclvm.tools.docs.model_pb2 as model + + +class DocEscaper(ABC): + """ + doc escaper checks the special character in the doc and escape it + """ + + @abstractmethod + def escape(self, doc: model.ModuleDoc) -> model.ModuleDoc: + pass + + +class MarkdownDocEscaper(DocEscaper): + def escape(self, module: model.ModuleDoc) -> model.ModuleDoc: + # remove line breaks at the beginning and the end + module.doc = self.escape_special_symbol(module.doc.strip("\n")) + for schema in module.schemas: + schema.name = self.escape_special_symbol(schema.name) + schema.doc = self.escape_special_symbol(schema.doc.strip("\n")) + if schema.attributes: + for attr in schema.attributes: + attr.name = self.escape_special_symbol(attr.name) + attr.doc = self.escape_special_symbol(attr.doc.strip("\n")) + attr.type.type_str = self.escape_special_symbol(attr.type.type_str) + if schema.examples: + schema.examples = schema.examples.strip("\n") + return module + + @staticmethod + def escape_special_symbol(name: str) -> str: + return ( + name.replace("_", "\\_") + .replace("*", "\\*") + .replace("#", "\\#") + .replace("|", "|") + .replace("<", "\\<") + .replace(">", "\\>") + .replace("\n", "
") + ) + + +class YamlDocEscaper(DocEscaper): + def escape(self, module: model.ModuleDoc) -> None: + return module + + +class JsonDocEscaper(DocEscaper): + def escape(self, module: model.ModuleDoc) -> None: + return module + + +factory = factory.DocEscaperFactory() +factory.register_format(doc_formats.KCLDocFormat.MARKDOWN, MarkdownDocEscaper) +factory.register_format(doc_formats.KCLDocFormat.YAML, YamlDocEscaper) +factory.register_format(doc_formats.KCLDocFormat.JSON, JsonDocEscaper) diff --git a/internal/kclvm_py/tools/docs/doc_parser.py b/internal/kclvm_py/tools/docs/doc_parser.py new file mode 100644 index 000000000..a2a71df78 --- /dev/null +++ b/internal/kclvm_py/tools/docs/doc_parser.py @@ -0,0 +1,314 @@ +""" +Docstring parser for KCL models. + +Basically, we follow the docstring standard of numpy doc, use the related tools as underlying libs +and extend the part which it does not cover, such as default value. + +Reference: ++ https://numpydoc.readthedocs.io/ +""" + +import re +import typing +from numpydoc.docscrape import NumpyDocString + +import kclvm.kcl.ast as ast +import kclvm.api.object as objpkg +import kclvm.tools.docs.checker as checker +import kclvm.tools.docs.model_pb2 as model + +# Try to match type, optional, and default value. +# Note: there is no standard way to define default value in numpydoc, we define it as below. +TYPE_OPTIONAL_DEFAULT_REGEX = re.compile( + r"(?P.*?)(,?\s*[Dd]efault(?: is | = |: |s to |)\s*(?P.*?))?(,?\s*(?Poptional|\(optional\)|required|\(required\)).?)?$" +) + + +class SchemaDocParser: + """Schema doc parser. + + The schema doc is defined in model.SchemaDoc + + :param schema. The schema ast. + :param doc. The schema docstring + """ + + def __init__( + self, + schema: ast.SchemaStmt, + schema_type: objpkg.KCLSchemaTypeObject, + doc=None, + root: str = None, + checker=checker.SchemaDocStringChecker, + ): + self._schema = schema + self.root = root + self.schema_type = schema_type + + if doc is None: + if schema is None: + raise ValueError("No schema or documentation string given") + + import inspect + + doc = inspect.cleandoc(schema.doc) + schema_doc = NumpyDocString(doc) + + # Base Schema relation + base_schema: model.Type = None + if self._schema.parent_name: + assert self.schema_type.base + base_schema = model.Type( + type_str=self._schema.parent_name.get_name(), + type_category=model.Type.TypeCategory.SCHEMA, + schema_type=model.SchemaType( + name=self.schema_type.base.name, + relative_path=self.schema_type.base.filename.replace( + self.root, ".", 1 + ), + ), + ) + + doc = model.SchemaDoc(name=schema.name, base_schema=base_schema) + + # Summary + summary = "" + for sum_line in schema_doc["Summary"]: + summary += sum_line + "\n" + doc.doc = summary + + # Attributes + # From docstring: collect the attribute map from the Attributes docstring + doc_attr_map: typing.Dict[str, model.SchemaAttributeDoc] = {} + for attr in schema_doc["Attributes"]: + doc_attr_name = attr[0] + doc_attr_type = attr[1] + doc_attr_desc = "" + doc_attr_default = None + doc_attr_optional = False + + if attr[2]: + for desc in attr[2]: + doc_attr_desc += desc + "\n" + + # To prevent the doc pattern e.g., `name: type`, and modify it with kcl format tool + if ":" in doc_attr_name: + name_type_parts = doc_attr_name.split(":", 1) + if name_type_parts and len(name_type_parts) >= 2: + doc_attr_name = name_type_parts[0].strip() + doc_attr_type = name_type_parts[1].strip() + doc_attr_type + doc_attr_name = doc_attr_name.lstrip("$") + if doc_attr_type: + match = TYPE_OPTIONAL_DEFAULT_REGEX.match(doc_attr_type) + if ( + match is None + or match.group(0) == match.group(2) + or match.group(0) == match.group(3) + ): + # if there is no type defined, optional(group2) or default(group3) + # may be treated as type(group0) + # fixme: hacky, find a better way to check type + doc_attr_type = "" + else: + doc_attr_type = match.group("type") + # reset on all false cases + doc_attr_type = "" if not doc_attr_type else doc_attr_type + if match is not None: + doc_attr_default = match.group("value") + optional = match.group("optional") + if optional is not None: + doc_attr_optional = "optional" in optional + doc_attr_map[doc_attr_name] = model.SchemaAttributeDoc( + name=doc_attr_name, + type=model.Type(type_str=doc_attr_type), + is_optional=doc_attr_optional, + default_value=doc_attr_default, + doc=doc_attr_desc, + ) + + # From source code: collect the attribute map from the schema stmt: get each attribute's type, default value, optional info + code_attr_map: typing.Dict[str, model.SchemaAttributeDoc] = {} + for attr_name, attr_obj in self.schema_type.attr_obj_map.items(): + if attr_name == objpkg.SCHEMA_SETTINGS_ATTR_NAME: + # ignore __settings__ attribute + # todo: show __settings__ config in schema summary info + continue + code_attr_type: model.Type = self.type_doc(attr_obj.attr_type) + # fixme: hacky. the schema type str should be calculated recursively + if ( + code_attr_type.type_category == model.Type.TypeCategory.SCHEMA + and attr_obj.attr_node is not None + and attr_obj.attr_node.type_node is not None + ): + attr_node = typing.cast(ast.SchemaAttr, attr_obj.attr_node) + code_attr_type.type_str = attr_node.type_node.plain_type_str + code_attr_optional: bool = attr_obj.is_optional + # todo: get default value from ast + code_default_value: str = "" + code_attr_map[attr_name] = model.SchemaAttributeDoc( + name=attr_name, + type=code_attr_type, + is_optional=code_attr_optional, + default_value=code_default_value, + doc="", + ) + + # Merge attributes from docstring and source code + # According to source code: + # the attribute list, the type and the optional info of each attribute + # According to docstring: + # the default value and the description of each attribute + for attr_name, code_attr in code_attr_map.items(): + code_attr.doc = ( + doc_attr_map[attr_name].doc if attr_name in doc_attr_map else "" + ) + code_attr.default_value = ( + doc_attr_map[attr_name].default_value + if attr_name in doc_attr_map + else "" + ) + doc.attributes.append(code_attr) + + # Examples + examples = "" + for example in schema_doc["Examples"]: + examples += example + "\n" + doc.examples = examples + self.doc = doc + # Validate schema attr + if checker: + checker( + model.SchemaDoc( + name=schema.name, + attributes=[code_attr_map[attr] for attr in code_attr_map], + ), + model.SchemaDoc( + name=schema.name, + attributes=[doc_attr_map[attr] for attr in doc_attr_map], + ), + ).check() + + def type_doc(self, tpe: objpkg.KCLBaseTypeObject) -> model.Type: + def _short_schema_tpe(tpe: objpkg.KCLBaseTypeObject) -> str: + fullname = tpe.type_str() + parts = fullname.rsplit(".", 2) + return f"{parts[1]}.{parts[2]}" if len(parts) > 2 else fullname + + def _get_type_str(tpe: objpkg.KCLBaseTypeObject) -> str: + if not tpe: + return "" + if tpe.type_kind() in type_str_mapping: + return type_str_mapping[tpe.type_kind()](tpe) + else: + return tpe.type_str() + + type_str_mapping = { + objpkg.KCLTypeKind.StrLitKind: lambda t: f'"{t.value}"', + objpkg.KCLTypeKind.IntLitKind: lambda t: f"{t.value}", + objpkg.KCLTypeKind.BoolLitKind: lambda t: f"{t.value}", + objpkg.KCLTypeKind.FloatLitKind: lambda t: f"{t.value}", + objpkg.KCLTypeKind.NoneKind: lambda t: "None", + objpkg.KCLTypeKind.SchemaKind: _short_schema_tpe, + objpkg.KCLTypeKind.ListKind: lambda t: f"[{_get_type_str(t.item_type)}]", + objpkg.KCLTypeKind.DictKind: lambda t: f"{{{_get_type_str(t.key_type)}: {_get_type_str(t.value_type)}}}" + if t.value_type + else f"{{{_get_type_str(t.key_type)}:}}", + objpkg.KCLTypeKind.UnionKind: lambda t: " | ".join( + [_get_type_str(inner_type) for inner_type in t.types] + ), + } + + type_mapping = { + # ant type + objpkg.KCLTypeKind.AnyKind: lambda t: model.Type( + type_str=t.type_str(), type_category=model.Type.TypeCategory.ANY + ), + # builtin type + objpkg.KCLTypeKind.StrKind: lambda t: model.Type( + type_str=t.type_str(), + type_category=model.Type.TypeCategory.BUILTIN, + builtin_type=model.Type.BuiltinType.STRING, + ), + objpkg.KCLTypeKind.IntKind: lambda t: model.Type( + type_str=t.type_str(), + type_category=model.Type.TypeCategory.BUILTIN, + builtin_type=model.Type.BuiltinType.INT, + ), + objpkg.KCLTypeKind.BoolKind: lambda t: model.Type( + type_str=t.type_str(), + type_category=model.Type.TypeCategory.BUILTIN, + builtin_type=model.Type.BuiltinType.BOOL, + ), + objpkg.KCLTypeKind.FloatKind: lambda t: model.Type( + type_str=t.type_str(), + type_category=model.Type.TypeCategory.BUILTIN, + builtin_type=model.Type.BuiltinType.FLOAT, + ), + # lit type + objpkg.KCLTypeKind.StrLitKind: lambda t: model.Type( + type_str=_get_type_str(t), + type_category=model.Type.TypeCategory.LIT, + lit_type=model.LitType(string_lit=t.value), + ), + objpkg.KCLTypeKind.IntLitKind: lambda t: model.Type( + type_str=_get_type_str(t), + type_category=model.Type.TypeCategory.LIT, + lit_type=model.LitType(int_lit=t.value), + ), + objpkg.KCLTypeKind.BoolLitKind: lambda t: model.Type( + type_str=_get_type_str(t), + type_category=model.Type.TypeCategory.LIT, + lit_type=model.LitType(bool_lit=t.value), + ), + objpkg.KCLTypeKind.FloatLitKind: lambda t: model.Type( + type_str=_get_type_str(t), + type_category=model.Type.TypeCategory.LIT, + lit_type=model.LitType(float_lit=t.value), + ), + # name constant type + objpkg.KCLTypeKind.NoneKind: lambda t: model.Type( + type_str=_get_type_str(t), + type_category=model.Type.TypeCategory.NAMED_CONSTANT, + named_constant=model.Type.NamedConstant.NONE, + ), + # number multiplier type + objpkg.KCLTypeKind.NumberMultiplierKind: lambda t: model.Type( + type_str=_get_type_str(t), + type_category=model.Type.TypeCategory.NUMBER_MULTIPLIER, + ), + # schema type + objpkg.KCLTypeKind.SchemaKind: lambda t: model.Type( + type_str=_get_type_str(t), + type_category=model.Type.TypeCategory.SCHEMA, + schema_type=model.SchemaType( + name=t.name, + relative_path=t.filename.replace(self.root, ".", 1), + ), + ), + # list type + objpkg.KCLTypeKind.ListKind: lambda t: model.Type( + type_str=_get_type_str(t), + type_category=model.Type.TypeCategory.LIST, + list_type=model.ListType(item_type=self.type_doc(t.item_type)), + ), + # dict type + objpkg.KCLTypeKind.DictKind: lambda t: model.Type( + type_str=_get_type_str(t), + type_category=model.Type.TypeCategory.DICT, + dict_type=model.DictType( + key_type=self.type_doc(t.key_type), + value_type=self.type_doc(t.value_type), + ), + ), + # union type + objpkg.KCLTypeKind.UnionKind: lambda t: model.Type( + type_str=_get_type_str(t), + type_category=model.Type.TypeCategory.UNION, + union_type=model.UnionType( + types=[self.type_doc(inner_type) for inner_type in t.types] + ), + ), + } + if tpe.type_kind() in type_mapping: + return type_mapping[tpe.type_kind()](tpe) + raise TypeError diff --git a/internal/kclvm_py/tools/docs/factory.py b/internal/kclvm_py/tools/docs/factory.py new file mode 100644 index 000000000..0c651f3dd --- /dev/null +++ b/internal/kclvm_py/tools/docs/factory.py @@ -0,0 +1,70 @@ +import io +import kclvm.kcl.error as kcl_error +import kclvm.tools.docs.templater as templater + +# --------------------------------------------------- +# Constants +# --------------------------------------------------- + + +INVALID_FORMAT_MSG = "an unsupported format, expected {}" + + +# --------------------------------------------------- +# Factory +# --------------------------------------------------- + + +class IOFactory: + def __init__(self): + self._creators = {} + + def register_format(self, format: str, creator): + self._creators[format] = creator + + def get(self, format: str, io: io.TextIOBase): + creator = self._creators.get(format) + if not creator: + formats = ",".join(self._creators.keys()) + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg=INVALID_FORMAT_MSG.format(formats), + ) + return creator(io) + + +class PathResolverFactory: + def __init__(self): + self._resolvers = {} + + def register_format(self, format: str, resolver): + self._resolvers[format] = resolver + + def get(self, format: str, doc_name_formatter): + resolver = self._resolvers.get(format) + if not resolver: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg=INVALID_FORMAT_MSG.format(",".join(self._resolvers.keys())), + ) + return resolver( + doc_name_formatter=doc_name_formatter, + schema_section_render=templater.md_schema_section_render, + ) # todo, get templater from factory + + +class DocEscaperFactory: + def __init__(self): + self._escapers = {} + + def register_format(self, format: str, escaper): + self._escapers[format] = escaper + + def get(self, format: str): + escaper = self._escapers.get(format) + if not escaper: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg=INVALID_FORMAT_MSG.format(",".join(self._escapers.keys())), + ) + return escaper() diff --git a/internal/kclvm_py/tools/docs/formats.py b/internal/kclvm_py/tools/docs/formats.py new file mode 100644 index 000000000..142eb8730 --- /dev/null +++ b/internal/kclvm_py/tools/docs/formats.py @@ -0,0 +1,45 @@ +class KCLDocFormat: + """ + KCL document formats including yaml, json, markdown, reST, HTML5, etc. + TODO: RST, HTML5 style document generation. + """ + + YAML: str = "YAML" + JSON: str = "JSON" + MARKDOWN: str = "MARKDOWN" + + MAPPING = { + YAML: YAML, + JSON: JSON, + MARKDOWN: MARKDOWN, + } + + +class KCLDocSuffix: + """ + KCL document suffix including .yaml, .json, .md, .rst, .html, etc. + TODO: RST, HTML5 style document generation. + """ + + YAML: str = ".yaml" + JSON: str = ".json" + MARKDOWN: str = ".md" + TO_SUFFIX = { + KCLDocFormat.YAML: YAML, + KCLDocFormat.JSON: JSON, + KCLDocFormat.MARKDOWN: MARKDOWN, + } + + +class KCLI18NFormat: + """ + KCL i18n meta file formats including yaml, json. + """ + + YAML: str = KCLDocFormat.YAML + JSON: str = KCLDocFormat.JSON + MAPPING = { + YAML: YAML, + JSON: JSON, + } + FROM_SUFFIX = {KCLDocSuffix.YAML: YAML, KCLDocSuffix.JSON: JSON} diff --git a/internal/kclvm_py/tools/docs/i18n.py b/internal/kclvm_py/tools/docs/i18n.py new file mode 100644 index 000000000..cfa055be7 --- /dev/null +++ b/internal/kclvm_py/tools/docs/i18n.py @@ -0,0 +1,25 @@ +import locale as _locale + +import kclvm.kcl.error as kcl_error + +LOCALE_LIST = list(_locale.locale_alias.keys()) +INVALID_I18N_LOCALE_MSG = "invalid i18n locale, expected {}" + + +def check_locale(locale: str): + """Check a locale string is a valid locale + + Parameters + ---------- + locale: locale string + """ + if ( + not locale + or not isinstance(locale, str) + or locale not in LOCALE_LIST + or locale.replace("-", "_") not in LOCALE_LIST + ): + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg=INVALID_I18N_LOCALE_MSG.format(", ".join(LOCALE_LIST)), + ) diff --git a/internal/kclvm_py/tools/docs/link_resolver.py b/internal/kclvm_py/tools/docs/link_resolver.py new file mode 100644 index 000000000..ba360d23c --- /dev/null +++ b/internal/kclvm_py/tools/docs/link_resolver.py @@ -0,0 +1,143 @@ +import os +import pathlib +from abc import ABC, abstractmethod + +import kclvm.tools.docs.factory as factory +import kclvm.tools.docs.formats as doc_formats +import kclvm.tools.docs.model_pb2 as model +import kclvm.tools.docs.utils as utils + + +class LinkResolver(ABC): + """ + path resolver refills the path links in Type.type_str + """ + + def __init__(self, doc_name_formatter, schema_section_render): + self.doc_name_formatter = doc_name_formatter + self.schema_section_render = schema_section_render + + @abstractmethod + def resolve( + self, + doc: model.ModuleDoc, + root: str, + locale: str, + with_locale_suffix: bool = False, + ) -> model.ModuleDoc: + pass + + +class MarkdownLinkResolver(LinkResolver): + def resolve( + self, + module: model.ModuleDoc, + root: str, + locale: str, + with_locale_suffix: bool = False, + ) -> model.ModuleDoc: + def add_link( + tpe: model.Type, current_module: model.ModuleDoc, root: str + ) -> str: + def to_md_anchor(original: str) -> str: + return "-".join(original.lower().split()) + + def resolve_relative_path(to_dir: str, from_dir: str) -> str: + if to_dir == from_dir: + return "" + common_path = os.path.commonpath([from_dir, to_dir]) + upper_count = len(from_dir.strip("/").split("/")) - len( + common_path.strip("/").split("/") + ) + + return ( + "../" * upper_count + + to_dir.replace(common_path, "", 1).lstrip("/") + + ("" if to_dir == common_path else "/") + ) + + if tpe.type_category in [ + model.Type.TypeCategory.ANY, + model.Type.TypeCategory.BUILTIN, + model.Type.TypeCategory.LIT, + model.Type.TypeCategory.NAMED_CONSTANT, + ]: + return tpe.type_str + if tpe.type_category == model.Type.TypeCategory.SCHEMA: + assert tpe.schema_type + section_link = to_md_anchor( + self.schema_section_render(tpe.schema_type.name) + ) + from_path = pathlib.Path(current_module.relative_path) + to_path = pathlib.Path(tpe.schema_type.relative_path) + + file_link = "" + if str(from_path) != str(to_path): + # defines in different file + relative_dir = resolve_relative_path( + to_dir=str(to_path.parent), from_dir=str(from_path.parent) + ) + file_link = f"{relative_dir}{self.doc_name_formatter(utils.module_name(tpe.schema_type.relative_path), locale, doc_formats.KCLDocFormat.MARKDOWN, with_locale_suffix)}" + # remove .md file extension suffix + file_link = file_link[:-3] + return f"[{tpe.type_str}]({file_link}#{section_link})" + if tpe.type_category == model.Type.TypeCategory.UNION: + assert tpe.union_type + return " \\| ".join( + [ + add_link(t, current_module=current_module, root=root) + for t in tpe.union_type.types + ] + ) + if tpe.type_category == model.Type.TypeCategory.DICT: + assert tpe.dict_type + key_type_str = add_link( + tpe.dict_type.key_type, current_module=current_module, root=root + ) + value_type_str = add_link( + tpe.dict_type.value_type, current_module=current_module, root=root + ) + return ( + f"{{{key_type_str}: {value_type_str}}}" + if value_type_str + else f"{{{key_type_str}:}}" + ) + if tpe.type_category == model.Type.TypeCategory.LIST: + assert tpe.list_type + return f"[{add_link(tpe.list_type.item_type, current_module=current_module, root=root)}]" + return tpe.type_str + + for schema in module.schemas: + for attr in schema.attributes: + attr.type.type_str = add_link(attr.type, module, root) + if schema.base_schema: + schema.base_schema.type_str = add_link(schema.base_schema, module, root) + return module + + +class YamlLinkResolver(LinkResolver): + def resolve( + self, + module: model.ModuleDoc, + root: str, + locale: str, + with_locale_suffix: bool = False, + ) -> model.ModuleDoc: + return module + + +class JsonLinkResolver(LinkResolver): + def resolve( + self, + module: model.ModuleDoc, + root: str, + locale: str, + with_locale_suffix: bool = False, + ) -> model.ModuleDoc: + return module + + +factory = factory.PathResolverFactory() +factory.register_format(doc_formats.KCLDocFormat.MARKDOWN, MarkdownLinkResolver) +factory.register_format(doc_formats.KCLDocFormat.YAML, YamlLinkResolver) +factory.register_format(doc_formats.KCLDocFormat.JSON, JsonLinkResolver) diff --git a/internal/kclvm_py/tools/docs/makefile b/internal/kclvm_py/tools/docs/makefile new file mode 100644 index 000000000..c4198e47d --- /dev/null +++ b/internal/kclvm_py/tools/docs/makefile @@ -0,0 +1,4 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +default: + protoc --proto_path=. --python_out=. model.proto diff --git a/internal/kclvm_py/tools/docs/model.proto b/internal/kclvm_py/tools/docs/model.proto new file mode 100644 index 000000000..9002693c6 --- /dev/null +++ b/internal/kclvm_py/tools/docs/model.proto @@ -0,0 +1,86 @@ +// Copyright 2020 The KCL Authors. All rights reserved. + +syntax = "proto3"; + +package kclvm.doc.model; + +message ModuleDoc { + string name = 1; // KCL module name + string relative_path = 2; // KCL module relative path + string doc = 3; // KCL module top-level document string + repeated SchemaDoc schemas = 4; // Schema doc list in the module + string source_code_url = 5; // The url of the KCL module source code +} + +message SchemaDoc { + string name = 1; // Schema name + string doc = 2; // Doc string of schema itself + repeated SchemaAttributeDoc attributes = 3; // Schema attribute doc list in the schema + string examples = 4; // Example string + Type base_schema = 5; // Base Schema +} + +message SchemaAttributeDoc { + string name = 1; // Schema attribute + string doc = 2; // Schema attribute document desc string + Type type = 4; // Schema attribute type + bool is_optional = 5; // If the attribute must have a value + string default_value = 6; // Default value of the attribute +} + +message Type { + string type_str = 1; + enum TypeCategory { + UNION = 0; + LIST = 1; + DICT = 2; + SCHEMA = 3; + BUILTIN = 4; + LIT = 5; + NAMED_CONSTANT = 6; + ANY = 7; + NUMBER_MULTIPLIER = 8; + } + TypeCategory type_category = 2; // the category of the type + optional UnionType union_type = 3; // optional, if the type is a union type + optional ListType list_type = 4; // optional, if the type is a list type + optional DictType dict_type = 5; // optional, if the type is a dict type + optional SchemaType schema_type = 6; // optional, if the type is a schema type + enum BuiltinType { + INT = 0; + FLOAT = 1; + STRING = 2; + BOOL = 3; + } + optional BuiltinType builtin_type = 7; // optional, if the type is a builtin type + optional LitType lit_type = 8; // optional, if the type is a literal type + enum NamedConstant { + NONE = 0; + } + optional NamedConstant named_constant = 9; // optional, if the type is a named constant +} + +message UnionType { + repeated Type types = 1; // the union types +} + +message ListType { + Type item_type = 1; // the item type +} + +message DictType { + Type key_type = 1; // the key type + Type value_type = 2; // the value type +} + +message SchemaType { + string name = 1; // the schema name + string relative_path = 2; // the relative path of the module that defines the schema +} + +message LitType { + optional string string_lit = 1; // optional, if the type is a string literal + optional int64 int_lit = 2; // optional, if the type is a int literal + optional float float_lit = 3; // optional, if the type is a float literal + optional bool bool_lit = 4; // optional, if the type is a float literal +} diff --git a/internal/kclvm_py/tools/docs/model_pb2.py b/internal/kclvm_py/tools/docs/model_pb2.py new file mode 100644 index 000000000..3ba02c924 --- /dev/null +++ b/internal/kclvm_py/tools/docs/model_pb2.py @@ -0,0 +1,790 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: model.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='model.proto', + package='kclvm.doc.model', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x0bmodel.proto\x12\x0fkclvm.doc.model\"\x83\x01\n\tModuleDoc\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x15\n\rrelative_path\x18\x02 \x01(\t\x12\x0b\n\x03\x64oc\x18\x03 \x01(\t\x12+\n\x07schemas\x18\x04 \x03(\x0b\x32\x1a.kclvm.doc.model.SchemaDoc\x12\x17\n\x0fsource_code_url\x18\x05 \x01(\t\"\x9d\x01\n\tSchemaDoc\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0b\n\x03\x64oc\x18\x02 \x01(\t\x12\x37\n\nattributes\x18\x03 \x03(\x0b\x32#.kclvm.doc.model.SchemaAttributeDoc\x12\x10\n\x08\x65xamples\x18\x04 \x01(\t\x12*\n\x0b\x62\x61se_schema\x18\x05 \x01(\x0b\x32\x15.kclvm.doc.model.Type\"\x80\x01\n\x12SchemaAttributeDoc\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0b\n\x03\x64oc\x18\x02 \x01(\t\x12#\n\x04type\x18\x04 \x01(\x0b\x32\x15.kclvm.doc.model.Type\x12\x13\n\x0bis_optional\x18\x05 \x01(\x08\x12\x15\n\rdefault_value\x18\x06 \x01(\t\"\x9c\x06\n\x04Type\x12\x10\n\x08type_str\x18\x01 \x01(\t\x12\x39\n\rtype_category\x18\x02 \x01(\x0e\x32\".kclvm.doc.model.Type.TypeCategory\x12\x33\n\nunion_type\x18\x03 \x01(\x0b\x32\x1a.kclvm.doc.model.UnionTypeH\x00\x88\x01\x01\x12\x31\n\tlist_type\x18\x04 \x01(\x0b\x32\x19.kclvm.doc.model.ListTypeH\x01\x88\x01\x01\x12\x31\n\tdict_type\x18\x05 \x01(\x0b\x32\x19.kclvm.doc.model.DictTypeH\x02\x88\x01\x01\x12\x35\n\x0bschema_type\x18\x06 \x01(\x0b\x32\x1b.kclvm.doc.model.SchemaTypeH\x03\x88\x01\x01\x12<\n\x0c\x62uiltin_type\x18\x07 \x01(\x0e\x32!.kclvm.doc.model.Type.BuiltinTypeH\x04\x88\x01\x01\x12/\n\x08lit_type\x18\x08 \x01(\x0b\x32\x18.kclvm.doc.model.LitTypeH\x05\x88\x01\x01\x12@\n\x0enamed_constant\x18\t \x01(\x0e\x32#.kclvm.doc.model.Type.NamedConstantH\x06\x88\x01\x01\"\x83\x01\n\x0cTypeCategory\x12\t\n\x05UNION\x10\x00\x12\x08\n\x04LIST\x10\x01\x12\x08\n\x04\x44ICT\x10\x02\x12\n\n\x06SCHEMA\x10\x03\x12\x0b\n\x07\x42UILTIN\x10\x04\x12\x07\n\x03LIT\x10\x05\x12\x12\n\x0eNAMED_CONSTANT\x10\x06\x12\x07\n\x03\x41NY\x10\x07\x12\x15\n\x11NUMBER_MULTIPLIER\x10\x08\"7\n\x0b\x42uiltinType\x12\x07\n\x03INT\x10\x00\x12\t\n\x05\x46LOAT\x10\x01\x12\n\n\x06STRING\x10\x02\x12\x08\n\x04\x42OOL\x10\x03\"\x19\n\rNamedConstant\x12\x08\n\x04NONE\x10\x00\x42\r\n\x0b_union_typeB\x0c\n\n_list_typeB\x0c\n\n_dict_typeB\x0e\n\x0c_schema_typeB\x0f\n\r_builtin_typeB\x0b\n\t_lit_typeB\x11\n\x0f_named_constant\"1\n\tUnionType\x12$\n\x05types\x18\x01 \x03(\x0b\x32\x15.kclvm.doc.model.Type\"4\n\x08ListType\x12(\n\titem_type\x18\x01 \x01(\x0b\x32\x15.kclvm.doc.model.Type\"^\n\x08\x44ictType\x12\'\n\x08key_type\x18\x01 \x01(\x0b\x32\x15.kclvm.doc.model.Type\x12)\n\nvalue_type\x18\x02 \x01(\x0b\x32\x15.kclvm.doc.model.Type\"1\n\nSchemaType\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x15\n\rrelative_path\x18\x02 \x01(\t\"\x9d\x01\n\x07LitType\x12\x17\n\nstring_lit\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07int_lit\x18\x02 \x01(\x03H\x01\x88\x01\x01\x12\x16\n\tfloat_lit\x18\x03 \x01(\x02H\x02\x88\x01\x01\x12\x15\n\x08\x62ool_lit\x18\x04 \x01(\x08H\x03\x88\x01\x01\x42\r\n\x0b_string_litB\n\n\x08_int_litB\x0c\n\n_float_litB\x0b\n\t_bool_litb\x06proto3' +) + + + +_TYPE_TYPECATEGORY = _descriptor.EnumDescriptor( + name='TypeCategory', + full_name='kclvm.doc.model.Type.TypeCategory', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='UNION', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LIST', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DICT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SCHEMA', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BUILTIN', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LIT', index=5, number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='NAMED_CONSTANT', index=6, number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ANY', index=7, number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='NUMBER_MULTIPLIER', index=8, number=8, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=931, + serialized_end=1062, +) +_sym_db.RegisterEnumDescriptor(_TYPE_TYPECATEGORY) + +_TYPE_BUILTINTYPE = _descriptor.EnumDescriptor( + name='BuiltinType', + full_name='kclvm.doc.model.Type.BuiltinType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='INT', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='FLOAT', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STRING', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BOOL', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=1064, + serialized_end=1119, +) +_sym_db.RegisterEnumDescriptor(_TYPE_BUILTINTYPE) + +_TYPE_NAMEDCONSTANT = _descriptor.EnumDescriptor( + name='NamedConstant', + full_name='kclvm.doc.model.Type.NamedConstant', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='NONE', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=1121, + serialized_end=1146, +) +_sym_db.RegisterEnumDescriptor(_TYPE_NAMEDCONSTANT) + + +_MODULEDOC = _descriptor.Descriptor( + name='ModuleDoc', + full_name='kclvm.doc.model.ModuleDoc', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='kclvm.doc.model.ModuleDoc.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='relative_path', full_name='kclvm.doc.model.ModuleDoc.relative_path', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='doc', full_name='kclvm.doc.model.ModuleDoc.doc', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='schemas', full_name='kclvm.doc.model.ModuleDoc.schemas', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='source_code_url', full_name='kclvm.doc.model.ModuleDoc.source_code_url', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=33, + serialized_end=164, +) + + +_SCHEMADOC = _descriptor.Descriptor( + name='SchemaDoc', + full_name='kclvm.doc.model.SchemaDoc', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='kclvm.doc.model.SchemaDoc.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='doc', full_name='kclvm.doc.model.SchemaDoc.doc', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='attributes', full_name='kclvm.doc.model.SchemaDoc.attributes', index=2, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='examples', full_name='kclvm.doc.model.SchemaDoc.examples', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='base_schema', full_name='kclvm.doc.model.SchemaDoc.base_schema', index=4, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=167, + serialized_end=324, +) + + +_SCHEMAATTRIBUTEDOC = _descriptor.Descriptor( + name='SchemaAttributeDoc', + full_name='kclvm.doc.model.SchemaAttributeDoc', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='kclvm.doc.model.SchemaAttributeDoc.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='doc', full_name='kclvm.doc.model.SchemaAttributeDoc.doc', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type', full_name='kclvm.doc.model.SchemaAttributeDoc.type', index=2, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_optional', full_name='kclvm.doc.model.SchemaAttributeDoc.is_optional', index=3, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='default_value', full_name='kclvm.doc.model.SchemaAttributeDoc.default_value', index=4, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=327, + serialized_end=455, +) + + +_TYPE = _descriptor.Descriptor( + name='Type', + full_name='kclvm.doc.model.Type', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='type_str', full_name='kclvm.doc.model.Type.type_str', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type_category', full_name='kclvm.doc.model.Type.type_category', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='union_type', full_name='kclvm.doc.model.Type.union_type', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='list_type', full_name='kclvm.doc.model.Type.list_type', index=3, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='dict_type', full_name='kclvm.doc.model.Type.dict_type', index=4, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='schema_type', full_name='kclvm.doc.model.Type.schema_type', index=5, + number=6, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='builtin_type', full_name='kclvm.doc.model.Type.builtin_type', index=6, + number=7, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lit_type', full_name='kclvm.doc.model.Type.lit_type', index=7, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='named_constant', full_name='kclvm.doc.model.Type.named_constant', index=8, + number=9, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _TYPE_TYPECATEGORY, + _TYPE_BUILTINTYPE, + _TYPE_NAMEDCONSTANT, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='_union_type', full_name='kclvm.doc.model.Type._union_type', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + _descriptor.OneofDescriptor( + name='_list_type', full_name='kclvm.doc.model.Type._list_type', + index=1, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + _descriptor.OneofDescriptor( + name='_dict_type', full_name='kclvm.doc.model.Type._dict_type', + index=2, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + _descriptor.OneofDescriptor( + name='_schema_type', full_name='kclvm.doc.model.Type._schema_type', + index=3, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + _descriptor.OneofDescriptor( + name='_builtin_type', full_name='kclvm.doc.model.Type._builtin_type', + index=4, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + _descriptor.OneofDescriptor( + name='_lit_type', full_name='kclvm.doc.model.Type._lit_type', + index=5, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + _descriptor.OneofDescriptor( + name='_named_constant', full_name='kclvm.doc.model.Type._named_constant', + index=6, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=458, + serialized_end=1254, +) + + +_UNIONTYPE = _descriptor.Descriptor( + name='UnionType', + full_name='kclvm.doc.model.UnionType', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='types', full_name='kclvm.doc.model.UnionType.types', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1256, + serialized_end=1305, +) + + +_LISTTYPE = _descriptor.Descriptor( + name='ListType', + full_name='kclvm.doc.model.ListType', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='item_type', full_name='kclvm.doc.model.ListType.item_type', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1307, + serialized_end=1359, +) + + +_DICTTYPE = _descriptor.Descriptor( + name='DictType', + full_name='kclvm.doc.model.DictType', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key_type', full_name='kclvm.doc.model.DictType.key_type', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value_type', full_name='kclvm.doc.model.DictType.value_type', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1361, + serialized_end=1455, +) + + +_SCHEMATYPE = _descriptor.Descriptor( + name='SchemaType', + full_name='kclvm.doc.model.SchemaType', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='kclvm.doc.model.SchemaType.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='relative_path', full_name='kclvm.doc.model.SchemaType.relative_path', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1457, + serialized_end=1506, +) + + +_LITTYPE = _descriptor.Descriptor( + name='LitType', + full_name='kclvm.doc.model.LitType', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='string_lit', full_name='kclvm.doc.model.LitType.string_lit', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='int_lit', full_name='kclvm.doc.model.LitType.int_lit', index=1, + number=2, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='float_lit', full_name='kclvm.doc.model.LitType.float_lit', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='bool_lit', full_name='kclvm.doc.model.LitType.bool_lit', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='_string_lit', full_name='kclvm.doc.model.LitType._string_lit', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + _descriptor.OneofDescriptor( + name='_int_lit', full_name='kclvm.doc.model.LitType._int_lit', + index=1, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + _descriptor.OneofDescriptor( + name='_float_lit', full_name='kclvm.doc.model.LitType._float_lit', + index=2, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + _descriptor.OneofDescriptor( + name='_bool_lit', full_name='kclvm.doc.model.LitType._bool_lit', + index=3, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1509, + serialized_end=1666, +) + +_MODULEDOC.fields_by_name['schemas'].message_type = _SCHEMADOC +_SCHEMADOC.fields_by_name['attributes'].message_type = _SCHEMAATTRIBUTEDOC +_SCHEMADOC.fields_by_name['base_schema'].message_type = _TYPE +_SCHEMAATTRIBUTEDOC.fields_by_name['type'].message_type = _TYPE +_TYPE.fields_by_name['type_category'].enum_type = _TYPE_TYPECATEGORY +_TYPE.fields_by_name['union_type'].message_type = _UNIONTYPE +_TYPE.fields_by_name['list_type'].message_type = _LISTTYPE +_TYPE.fields_by_name['dict_type'].message_type = _DICTTYPE +_TYPE.fields_by_name['schema_type'].message_type = _SCHEMATYPE +_TYPE.fields_by_name['builtin_type'].enum_type = _TYPE_BUILTINTYPE +_TYPE.fields_by_name['lit_type'].message_type = _LITTYPE +_TYPE.fields_by_name['named_constant'].enum_type = _TYPE_NAMEDCONSTANT +_TYPE_TYPECATEGORY.containing_type = _TYPE +_TYPE_BUILTINTYPE.containing_type = _TYPE +_TYPE_NAMEDCONSTANT.containing_type = _TYPE +_TYPE.oneofs_by_name['_union_type'].fields.append( + _TYPE.fields_by_name['union_type']) +_TYPE.fields_by_name['union_type'].containing_oneof = _TYPE.oneofs_by_name['_union_type'] +_TYPE.oneofs_by_name['_list_type'].fields.append( + _TYPE.fields_by_name['list_type']) +_TYPE.fields_by_name['list_type'].containing_oneof = _TYPE.oneofs_by_name['_list_type'] +_TYPE.oneofs_by_name['_dict_type'].fields.append( + _TYPE.fields_by_name['dict_type']) +_TYPE.fields_by_name['dict_type'].containing_oneof = _TYPE.oneofs_by_name['_dict_type'] +_TYPE.oneofs_by_name['_schema_type'].fields.append( + _TYPE.fields_by_name['schema_type']) +_TYPE.fields_by_name['schema_type'].containing_oneof = _TYPE.oneofs_by_name['_schema_type'] +_TYPE.oneofs_by_name['_builtin_type'].fields.append( + _TYPE.fields_by_name['builtin_type']) +_TYPE.fields_by_name['builtin_type'].containing_oneof = _TYPE.oneofs_by_name['_builtin_type'] +_TYPE.oneofs_by_name['_lit_type'].fields.append( + _TYPE.fields_by_name['lit_type']) +_TYPE.fields_by_name['lit_type'].containing_oneof = _TYPE.oneofs_by_name['_lit_type'] +_TYPE.oneofs_by_name['_named_constant'].fields.append( + _TYPE.fields_by_name['named_constant']) +_TYPE.fields_by_name['named_constant'].containing_oneof = _TYPE.oneofs_by_name['_named_constant'] +_UNIONTYPE.fields_by_name['types'].message_type = _TYPE +_LISTTYPE.fields_by_name['item_type'].message_type = _TYPE +_DICTTYPE.fields_by_name['key_type'].message_type = _TYPE +_DICTTYPE.fields_by_name['value_type'].message_type = _TYPE +_LITTYPE.oneofs_by_name['_string_lit'].fields.append( + _LITTYPE.fields_by_name['string_lit']) +_LITTYPE.fields_by_name['string_lit'].containing_oneof = _LITTYPE.oneofs_by_name['_string_lit'] +_LITTYPE.oneofs_by_name['_int_lit'].fields.append( + _LITTYPE.fields_by_name['int_lit']) +_LITTYPE.fields_by_name['int_lit'].containing_oneof = _LITTYPE.oneofs_by_name['_int_lit'] +_LITTYPE.oneofs_by_name['_float_lit'].fields.append( + _LITTYPE.fields_by_name['float_lit']) +_LITTYPE.fields_by_name['float_lit'].containing_oneof = _LITTYPE.oneofs_by_name['_float_lit'] +_LITTYPE.oneofs_by_name['_bool_lit'].fields.append( + _LITTYPE.fields_by_name['bool_lit']) +_LITTYPE.fields_by_name['bool_lit'].containing_oneof = _LITTYPE.oneofs_by_name['_bool_lit'] +DESCRIPTOR.message_types_by_name['ModuleDoc'] = _MODULEDOC +DESCRIPTOR.message_types_by_name['SchemaDoc'] = _SCHEMADOC +DESCRIPTOR.message_types_by_name['SchemaAttributeDoc'] = _SCHEMAATTRIBUTEDOC +DESCRIPTOR.message_types_by_name['Type'] = _TYPE +DESCRIPTOR.message_types_by_name['UnionType'] = _UNIONTYPE +DESCRIPTOR.message_types_by_name['ListType'] = _LISTTYPE +DESCRIPTOR.message_types_by_name['DictType'] = _DICTTYPE +DESCRIPTOR.message_types_by_name['SchemaType'] = _SCHEMATYPE +DESCRIPTOR.message_types_by_name['LitType'] = _LITTYPE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +ModuleDoc = _reflection.GeneratedProtocolMessageType('ModuleDoc', (_message.Message,), { + 'DESCRIPTOR' : _MODULEDOC, + '__module__' : 'model_pb2' + # @@protoc_insertion_point(class_scope:kclvm.doc.model.ModuleDoc) + }) +_sym_db.RegisterMessage(ModuleDoc) + +SchemaDoc = _reflection.GeneratedProtocolMessageType('SchemaDoc', (_message.Message,), { + 'DESCRIPTOR' : _SCHEMADOC, + '__module__' : 'model_pb2' + # @@protoc_insertion_point(class_scope:kclvm.doc.model.SchemaDoc) + }) +_sym_db.RegisterMessage(SchemaDoc) + +SchemaAttributeDoc = _reflection.GeneratedProtocolMessageType('SchemaAttributeDoc', (_message.Message,), { + 'DESCRIPTOR' : _SCHEMAATTRIBUTEDOC, + '__module__' : 'model_pb2' + # @@protoc_insertion_point(class_scope:kclvm.doc.model.SchemaAttributeDoc) + }) +_sym_db.RegisterMessage(SchemaAttributeDoc) + +Type = _reflection.GeneratedProtocolMessageType('Type', (_message.Message,), { + 'DESCRIPTOR' : _TYPE, + '__module__' : 'model_pb2' + # @@protoc_insertion_point(class_scope:kclvm.doc.model.Type) + }) +_sym_db.RegisterMessage(Type) + +UnionType = _reflection.GeneratedProtocolMessageType('UnionType', (_message.Message,), { + 'DESCRIPTOR' : _UNIONTYPE, + '__module__' : 'model_pb2' + # @@protoc_insertion_point(class_scope:kclvm.doc.model.UnionType) + }) +_sym_db.RegisterMessage(UnionType) + +ListType = _reflection.GeneratedProtocolMessageType('ListType', (_message.Message,), { + 'DESCRIPTOR' : _LISTTYPE, + '__module__' : 'model_pb2' + # @@protoc_insertion_point(class_scope:kclvm.doc.model.ListType) + }) +_sym_db.RegisterMessage(ListType) + +DictType = _reflection.GeneratedProtocolMessageType('DictType', (_message.Message,), { + 'DESCRIPTOR' : _DICTTYPE, + '__module__' : 'model_pb2' + # @@protoc_insertion_point(class_scope:kclvm.doc.model.DictType) + }) +_sym_db.RegisterMessage(DictType) + +SchemaType = _reflection.GeneratedProtocolMessageType('SchemaType', (_message.Message,), { + 'DESCRIPTOR' : _SCHEMATYPE, + '__module__' : 'model_pb2' + # @@protoc_insertion_point(class_scope:kclvm.doc.model.SchemaType) + }) +_sym_db.RegisterMessage(SchemaType) + +LitType = _reflection.GeneratedProtocolMessageType('LitType', (_message.Message,), { + 'DESCRIPTOR' : _LITTYPE, + '__module__' : 'model_pb2' + # @@protoc_insertion_point(class_scope:kclvm.doc.model.LitType) + }) +_sym_db.RegisterMessage(LitType) + + +# @@protoc_insertion_point(module_scope) diff --git a/internal/kclvm_py/tools/docs/pb.py b/internal/kclvm_py/tools/docs/pb.py new file mode 100644 index 000000000..ac1347a14 --- /dev/null +++ b/internal/kclvm_py/tools/docs/pb.py @@ -0,0 +1,13 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import google.protobuf.json_format as json_format + + +def FromJson(text: str, message): + return json_format.Parse(text, message) + + +def ToJson(message) -> str: + return json_format.MessageToJson( + message, including_default_value_fields=True, preserving_proto_field_name=True + ) diff --git a/internal/kclvm_py/tools/docs/reader.py b/internal/kclvm_py/tools/docs/reader.py new file mode 100644 index 000000000..42f439aae --- /dev/null +++ b/internal/kclvm_py/tools/docs/reader.py @@ -0,0 +1,82 @@ +import ruamel.yaml as yaml +import json +import io + +from abc import ABC, abstractmethod + +import kclvm.tools.docs.pb as pb +import kclvm.tools.docs.model_pb2 as model +import kclvm.tools.docs.factory as factory +import kclvm.tools.docs.formats as doc_formats + +# --------------------------------------------------- +# Constants +# --------------------------------------------------- + + +ENDLINE = "\n" + + +# ------------------------------------------------------- +# Document reader from KCL ModuleDoc type ot markdown type +# ------------------------------------------------------- + + +class ModuleDocReader(ABC): + """Module document reader class. + + This class provides basic functions for reader KCL Module from the IO. + """ + + def __init__(self, io_in: io.TextIOBase): + self.io_in: io.TextIOBase = io_in + + @abstractmethod + def read_doc(self) -> model.ModuleDoc: + pass + + def read(self) -> str: + return self.io_in.read() + + +class JsonDocReader(ModuleDocReader): + """JSON document reader based on ModuleDocReader. + + The class is used to read KCL module document from JSON file. + """ + + def _load_module_from_json(self, data) -> model.ModuleDoc: + return pb.FromJson(data, model.ModuleDoc()) + + def read_doc(self) -> model.ModuleDoc: + json_data = self.read() + return self._load_module_from_json(json_data) + + +class YamlDocReader(JsonDocReader): + """YAML document reader based on JsonDocReader. + + The class is used to read KCL module document from YAML file + """ + + def read_doc(self) -> model.ModuleDoc: + dict_data = yaml.safe_load(self.io_in) + json_data = json.dumps(dict_data) + return self._load_module_from_json(json_data) + + +class MarkDownDocReader(YamlDocReader): + """Markdown document reader based on KCLModuleDocReader. + + The class is used to read KCL module document from markdown file. + """ + + def read_doc(self) -> model.ModuleDoc: + """TODO: Parse a Markdown string to KCL module document model.""" + raise NotImplementedError() + + +factory = factory.IOFactory() +factory.register_format(doc_formats.KCLDocFormat.JSON, JsonDocReader) +factory.register_format(doc_formats.KCLDocFormat.YAML, YamlDocReader) +factory.register_format(doc_formats.KCLDocFormat.MARKDOWN, MarkDownDocReader) diff --git a/internal/kclvm_py/tools/docs/templater.py b/internal/kclvm_py/tools/docs/templater.py new file mode 100644 index 000000000..c73b53078 --- /dev/null +++ b/internal/kclvm_py/tools/docs/templater.py @@ -0,0 +1,78 @@ +# class DocTemplater +import kclvm.tools.docs.model_pb2 as model + + +def md_schema_section_render(schema_name: str): + return f"Schema {schema_name}" + + +def md_module_doc_templater(module: model.ModuleDoc) -> str: + def table(records, headings) -> str: + """ + Generate a Markdown table from records. + records -- Iterable. Rows will be generated from this. + headings -- List of column headings. + """ + table_heading = ( + f"|{'|'.join(headings)}|\n|{'|'.join(['-' * len(h) for h in headings])}|\n" + ) + table_rows = "\n".join( + [f"|{record[0]}|{record[1]}|{record[2]}|{record[3]}|" for record in records] + ) + return f"{table_heading}{table_rows}\n" + + # module header + header = f"# {module.name}\n\n" + # source code link + file_name = module.relative_path.replace("./", "") + source_code = ( + f"Source: [{file_name}]({module.source_code_url}/{file_name})\n\n" + if module.source_code_url + else "" + ) + # module doc string + module_doc = f"{module.doc}\n\n" if module.doc else "" + # schemas header + schemas_section = "" + if module.schemas: + for schema in module.schemas: + _schema_header = f"## Schema {schema.name}\n\n" + _schema_docstring = f"{schema.doc}\n\n" if schema.doc else "" + + _base_schema_section = "" + if schema.base_schema and schema.base_schema.type_str: + _base_schema_section = ( + f"### Base Schema\n{schema.base_schema.type_str}\n\n" + ) + + attributes_section = "" + if schema.attributes: + attributes_header = "### Attributes\n\n" + attributes_table = table( + [ + [ + f"**{a.name}**
{a.doc}" if a.doc else f"**{a.name}**", + a.type.type_str, + a.default_value if a.default_value else "Undefined", + "optional" if a.is_optional else "**required**", + ] + for a in schema.attributes + ], + [ + "Name and Description", + "Type", + "Default Value", + "Required", + ], + ) + attributes_section = f"{attributes_header}{attributes_table}" + # Examples + _examples = ( + f"### Examples\n```python\n{schema.examples}\n```\n\n" + if schema.examples + else "" + ) + schemas_section = f"{schemas_section}{_schema_header}{_schema_docstring}{_base_schema_section}{attributes_section}{_examples}" + # not editable footer + not_editable = "" + return f"{header}{source_code}{module_doc}{schemas_section}{not_editable}" diff --git a/internal/kclvm_py/tools/docs/templates/md.mako b/internal/kclvm_py/tools/docs/templates/md.mako new file mode 100644 index 000000000..e69de29bb diff --git a/internal/kclvm_py/tools/docs/utils.py b/internal/kclvm_py/tools/docs/utils.py new file mode 100644 index 000000000..e09306f79 --- /dev/null +++ b/internal/kclvm_py/tools/docs/utils.py @@ -0,0 +1,5 @@ +from pathlib import Path + + +def module_name(file_path: str) -> str: + return Path(file_path).with_suffix("").name diff --git a/internal/kclvm_py/tools/docs/writer.py b/internal/kclvm_py/tools/docs/writer.py new file mode 100644 index 000000000..cd5380935 --- /dev/null +++ b/internal/kclvm_py/tools/docs/writer.py @@ -0,0 +1,108 @@ +from ruamel.yaml import YAML +import io + +from abc import ABC, abstractmethod + +import kclvm.tools.docs.pb as pb +import kclvm.tools.docs.model_pb2 as model +import kclvm.tools.docs.factory as factory +import kclvm.tools.docs.formats as doc_formats +import kclvm.tools.docs.templater as templater + + +# --------------------------------------------------- +# Constants +# --------------------------------------------------- + + +ENDLINE = "\n" + + +# ------------------------------------------------------- +# Document writer from KCL ModuleDoc type ot markdown type +# ------------------------------------------------------- + + +class ModuleDocWriter(ABC): + """Module document writer class. + + This class provides basic functions for write KCL Module to the IO. + """ + + def __init__(self, out: io.TextIOBase): + self.out: io.TextIOBase = out + + @abstractmethod + def write_doc(self, module: model.ModuleDoc) -> None: + pass + + def write(self, content: str = ""): + """Write the content to the string io""" + self.out.write(content) + + def writeln(self, content: str = ""): + """Write the content with an endline to the string io""" + self.write(content + ENDLINE) + + +class JsonDocWriter(ModuleDocWriter): + """JSON document writer based on ModuleDocWriter. + + The class is used to generate JSON file from KCL module document string. + """ + + def _dump_json_str(self, module: model.ModuleDoc) -> str: + return pb.ToJson(module) + + def write_doc(self, module: model.ModuleDoc) -> None: + json_str = self._dump_json_str(module) + self.write(json_str) + + +class YamlDocWriter(JsonDocWriter): + """YAML document writer based on JsonDocWriter. + + The class is used to generate YAML file from KCL module document string. + """ + + def write_doc(self, module: model.ModuleDoc) -> None: + def set_style(d, flow): + if isinstance(d, dict): + if flow: + d.fa.set_flow_style() + else: + d.fa.set_block_style() + for k in d: + set_style(d[k], flow) + elif isinstance(d, list): + if flow: + d.fa.set_flow_style() + else: + d.fa.set_block_style() + for item in d: + set_style(item, flow) + + json_str = self._dump_json_str(module) + yaml = YAML() + data = yaml.load(json_str) + set_style(data, flow=False) + yaml.dump(data, self.out) + + +class MarkDownDocWriter(ModuleDocWriter): + """Markdown document writer based on KCLModuleDocWriter. + + The class is used to generate markdown file from KCL module document string. + """ + + def write_doc(self, module: model.ModuleDoc) -> None: + """ + Write document + """ + self.writeln(templater.md_module_doc_templater(module)) + + +factory = factory.IOFactory() +factory.register_format(doc_formats.KCLDocFormat.JSON, JsonDocWriter) +factory.register_format(doc_formats.KCLDocFormat.YAML, YamlDocWriter) +factory.register_format(doc_formats.KCLDocFormat.MARKDOWN, MarkDownDocWriter) diff --git a/internal/kclvm_py/tools/format/__init__.py b/internal/kclvm_py/tools/format/__init__.py new file mode 100644 index 000000000..1eb89b48f --- /dev/null +++ b/internal/kclvm_py/tools/format/__init__.py @@ -0,0 +1,21 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .format import ( + TextAdapterWalker, + Formatter, + kcl_ast_to_fmt_file, + kcl_fmt_source, + kcl_fmt_dir, + kcl_fmt_file, + kcl_fmt, +) + +__all__ = [ + "TextAdapterWalker", + "Formatter", + "kcl_ast_to_fmt_file", + "kcl_fmt_source", + "kcl_fmt_dir", + "kcl_fmt_file", + "kcl_fmt", +] diff --git a/internal/kclvm_py/tools/format/format.py b/internal/kclvm_py/tools/format/format.py new file mode 100644 index 000000000..0389d02ba --- /dev/null +++ b/internal/kclvm_py/tools/format/format.py @@ -0,0 +1,812 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import re +import pathlib + +from io import StringIO +from typing import List, Union + +import kclvm.kcl.error as kcl_error +import kclvm.internal.log as klog + +from kclvm.compiler.parser.lark_tree import get_lark_tree_from_expr +from kclvm.compiler.parser.lark_tree import ( + Token, + Tree, + AstType, + TreeWalker, + TOKEN_TYPE, + TREE_TYPE, + OPERATOR_TOKENS, +) + +_INVALID_NEWLINE_STRING_MSG = "invalid NEWLINE token string value {}" +_INLINE_COMMENT_REGEX = "#[^\n]*\n" +_INLINE_COMMENT_WITH_MULTILINE_REGEX = "#[^\n]*[\n\t ]*" +_NODE_WITH_NEWLINE_EXPRS = [ + Tree.CONFIG_EXPR, + Tree.DICT_COMP, + Tree.DICT_EXPR, + Tree.LIST_COMP, + Tree.LIST_EXPR, + Tree.COMP_CLAUSE, + Tree.SIMPLE_STMT, + Tree.SCHEMA_MEMBER_STMT, + Tree.MIXINS, +] +SEPARATOR_TOKEN = " " +EMPTY_TOKEN = "" +ENDLINE_TOKEN = "\n" +COMMENT_START_TOKEN = "#" + + +class TextAdapterWalker(TreeWalker): + """ + Walker adapted for text processing, can be used as a semantic-independent + walker super class such as formatter and linter + """ + + def __init__(self): + super().__init__() + self.printer = StringIO() # Printer used to write expressions and tokens + self.indent_level: int = 0 # Now indent level + self.indent_queue = [0] # Indent queue + self.indent_space_count: int = 4 # Default indent space count + + def write(self, text: str) -> None: + """Append a piece of text to the current line.""" + self.printer.write(text) + + def write_token_separator(self) -> None: + """Print the separator between tokens.""" + self.write(SEPARATOR_TOKEN) + + def walk_node(self, node: Union[AstType, str]) -> None: + """Write node""" + if isinstance(node, str): + self.write(node) + else: + self.walk(node) + + def fill(self, text: str = "") -> None: + """Append a piece of text to the current line.""" + self.printer.write(ENDLINE_TOKEN) + self.write_indent() + self.printer.write(text) + + def write_indent(self) -> None: + """Append indent white space according indent level""" + self.printer.write( + self.indent_space_count * self.indent_level * SEPARATOR_TOKEN + ) + + def count_blank_line(self, text: str) -> int: + """Blank line count in a NEWLINE token""" + return re.sub(_INLINE_COMMENT_REGEX, EMPTY_TOKEN, text).count(ENDLINE_TOKEN) - 1 + + def count_indent(self, text: str) -> int: + """ + Count the indent by number of white spaces for a leading text + e.g. NEWLINE "\n " its indent count is 4 + e.g. NEWLINE "\n # inline comment\n " its indent count is 3 + """ + if ENDLINE_TOKEN not in text: + return 0 + line = text.split(ENDLINE_TOKEN)[-1] + temptext = line.replace(SEPARATOR_TOKEN, EMPTY_TOKEN) + count = len(line) if len(temptext) == 0 else 0 + if count not in self.indent_queue: + # Indent increase + self.indent_queue.append(count) + else: + # Indent decrease + idx = self.indent_queue.index(count) + del self.indent_queue[idx + 1 :] + idx = self.indent_queue.index(count) + self.indent_level = idx + return count + + def generic_walk(self, node: AstType) -> None: + """Called if no explicit walker function exists for a node.""" + if node["type"] == TOKEN_TYPE: + self.walk_pre_token(node) + self.walk_token(node) + self.walk_post_token(node) + elif node["type"] == TREE_TYPE: + for n in node["children"]: + self.walk(n) + else: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg="Unknown format node type", + ) + + def walk_pre_token(self, node: AstType) -> None: + """Deal after token""" + pass + + def walk_post_token(self, node: AstType) -> None: + """Deal after token""" + pass + + def walk_token(self, node: AstType) -> None: + """AST: token""" + pass + + +class Formatter(TextAdapterWalker): + def __init__(self) -> None: + super().__init__() + self.last_token: str = EMPTY_TOKEN # Last token name + self.last_token_value: str = EMPTY_TOKEN # Last token value + self.single_comment_spaces: int = 2 # Inline comment after its expressions + self.max_blank_line: int = 1 # Maximum number of blank lines + self._is_in_arguments = False # Mark is in arguments + self._is_in_collection_if = False # Mark is in collection if + self._is_colon_without_space = False # Mark is in expr with colon except space + + # Base + + def generic_walk(self, node: AstType) -> None: + """Formatter special walk""" + if node["name"] in _NODE_WITH_NEWLINE_EXPRS: + self.write_node_with_newline(node) + else: + super().generic_walk(node) + + # Tokens + + def add_blank_line(self) -> None: + """Blank line before and after schema_stmt""" + if ( + self.last_token == Token.NEWLINE + and self.count_blank_line(self.last_token_value) < self.max_blank_line + ): + self.write_token(ENDLINE_TOKEN) + self.last_token = Token.NEWLINE + self.last_token_value = ENDLINE_TOKEN * 2 + + def split_newline_value(self, value: str) -> List[str]: + """Split a NEWLINE token value into newline parts and inline comment parts + + Input: "\n \n # comment \n # comment \n\n # comment \n" + Output: ["\n \n ", "# comment ", "\n ", "# comment ", "\n\n ", "# comment ", "\n"] + """ + if not value: + return [] + parts = [] + # Mark containing COMMENT token + index = value.find(COMMENT_START_TOKEN) + if index == -1: + return [value] # Single NEWLINE without COMMENT + elif index > 0: + parts.append(value[:index]) # Add first NEWLINE token + inline_comments = re.findall(_INLINE_COMMENT_WITH_MULTILINE_REGEX, value) + for comment in inline_comments: + index = comment.find(ENDLINE_TOKEN) + if index == -1: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg=_INVALID_NEWLINE_STRING_MSG.format(comment), + ) + parts.append(comment[:index]) # Add COMMENT token + parts.append(comment[index:]) # Add NEWLINE token + if len(parts) > 1 and ENDLINE_TOKEN not in parts[-1]: + # Last part is not NEWLINE, raise an error + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg=_INVALID_NEWLINE_STRING_MSG.format(value), + ) + return parts + + def write_newline(self, value: str) -> None: + """ + Write newline except inline comment + """ + blank_line_count = self.count_blank_line(value) + value = value.replace(SEPARATOR_TOKEN, "") + newline_count = 0 + for i, c in enumerate(value): + if c == ENDLINE_TOKEN: + newline_count += 1 + # Consecutive blank lines + if newline_count < blank_line_count: + pass + # If there is a blank line, keep it + elif self.count_blank_line(value[i + 1 :]): + self.fill() + # First NEWLINE in last expr + else: + self.write(ENDLINE_TOKEN) + + def walk_newline(self, token: AstType) -> None: + """ + Format NEWLINE token contains '#', '\n' and ' ' + """ + # Record indent level and Windows line break handling and 1 tab <-> 4 spaces + value = token["value"].replace("\r", ENDLINE_TOKEN).replace("\t", " ") + # Remove start blank lines except comments and first token is NEWLINE + if self.last_token == "": + # Get all inline comments + self.write("".join(re.findall(_INLINE_COMMENT_REGEX, value))) + return + # Record indent + self.count_indent(value) + parts = self.split_newline_value(value) + for part in parts: + if part.startswith(COMMENT_START_TOKEN): + self.write(part) + else: + self.write_newline(part) + + def write_string(self, value: str) -> None: + """Print KCL string with prefix""" + quota = False + for c in value: + # The string prefix is uniformly lowercase + if not quota: + c = c.lower() + # String start quotation marks + if c == '"' or c == "'": + quota = True + self.write(c) + + def write_token(self, node: Union[str, AstType]) -> None: + """Write token node or token string""" + if not node: + return + if isinstance(node, str): + self.write(node) + else: + self.walk_token(node) + + def walk_post_token(self, token: AstType) -> None: + """Deal after token""" + name, value = token["name"], token["value"] + if name == Token.ASSIGN and self._is_in_arguments: + # Do not write space between '=' in kwargs + pass + elif ( + name == Token.COLON + and self._is_colon_without_space + and not self._is_in_arguments + ): + # Do not write space after colon ':' e.g. check: and schema Name: + pass + elif ( + name in [Token.PLUS, Token.MINUS, Token.NOT] + and self.last_token in OPERATOR_TOKENS + ): + # Do not write space between unary operator + pass + elif name in OPERATOR_TOKENS: + self.write_token_separator() + elif name == Token.FOR and self.last_token != Token.NEWLINE: + self.write_token_separator() + elif name in [Token.IF, Token.ELIF] and self._is_in_collection_if: + self.write_token_separator() + # Write space after , and : except NEWLINE behind it and write space behind keyword + elif name in [ + Token.IMPORT, + Token.LAMBDA, + Token.SCHEMA, + Token.RELAXED, + Token.ASSERT, + Token.FINAL, + Token.IN, + Token.AS, + Token.IS, + Token.MIXIN, + Token.PROTOCOL, + Token.TYPE, + Token.COMMA, + Token.COLON, + Token.L_AND, + Token.L_OR, + Token.L_L_NOT, + Token.SEMI_COLON, + Token.ALL, + Token.ANY, + Token.MAP, + Token.FILTER, + ]: + self.write_token_separator() + self.last_token, self.last_token_value = name, value + + def walk_pre_token(self, token: AstType) -> None: + """Deal before token""" + name = token["name"] + value = token["value"] + if name == Token.ASSIGN and self._is_in_arguments: + # Do not write space between '=' in kwargs + pass + elif ( + name in [Token.PLUS, Token.MINUS, Token.NOT] + and self.last_token in OPERATOR_TOKENS + ): + # Do not write space between unary operator + pass + elif name in OPERATOR_TOKENS: + self.write_token_separator() + elif name == Token.FOR and self.last_token != Token.NEWLINE: + self.write_token_separator() + elif name in [Token.IN, Token.AS, Token.IS, Token.L_AND, Token.L_OR]: + self.write_token_separator() + elif ( + name == Token.NEWLINE + and value.startswith(COMMENT_START_TOKEN) + and self.last_token != "" + ): + # Two spaces between expr and inline comment # + self.write(SEPARATOR_TOKEN * self.single_comment_spaces) + + def walk_token(self, token: AstType) -> None: + """AST: token node""" + name = token["name"] + value = token["value"] + if name == Token.NEWLINE: + self.walk_newline(token) + elif Token.is_string(name): + self.write_string(value) + else: + self.write_token(value) + + # Exprs + + def write_expr(self, node: AstType) -> None: + """Write expr""" + if node: + self.walk_nodes(*node["children"]) + + def write_arguments(self, nodes: List[AstType]) -> None: + """Arguments in call_expr, str_expr and schema_expr""" + self._is_in_arguments = True + self.walk_nodes(*nodes) + self._is_in_arguments = False + + def walk_call_suffix(self, node: AstType) -> None: + """AST: call_suffix + + call_suffix: LEFT_PARENTHESES [arguments [COMMA]] RIGHT_PARENTHESES + """ + self.write_arguments(node["children"]) + + def walk_schema_expr(self, node: AstType) -> None: + """AST: schema_expr + + schema_expr: identifier (LEFT_PARENTHESES [arguments] RIGHT_PARENTHESES)? dict_expr + """ + self.write_expr(self.get(node, Tree.IDENTIFIER)) + self.write_arguments( + self.get_internal(node, Token.LEFT_PARENTHESES, Token.RIGHT_PARENTHESES) + ) + # Write space between 'SchemaName' and '{}' in schema_expr + self.write_token_separator() + self.write_expr(self.get(node, Tree.CONFIG_EXPR)) + + def walk_star_expr(self, node: AstType) -> None: + """AST: star_expr + + star_expr: MULTIPLY primary_expr + """ + # Do not write space between * in list iter and var + if self.get(node, Token.MULTIPLY): + self.write("*") + self.write_expr(self.get(node, Tree.PRIMARY_EXPR)) + + def walk_double_star_expr(self, node: AstType) -> None: + """AST: double_star_expr + + double_star_expr: DOUBLE_STAR primary_expr + """ + # Do not write space between ** in dict iter and var + if self.get(node, Token.DOUBLE_STAR): + self.write("**") + self.write_expr(self.get(node, Tree.PRIMARY_EXPR)) + + def walk_lambda_expr(self, node: AstType) -> None: + """AST: if_expr + + lambda_expr: LAMBDA [schema_arguments] [RIGHT_ARROW type] LEFT_BRACE [expr_stmt | NEWLINE _INDENT schema_init_stmt+ _DEDENT] RIGHT_BRACE + """ + for n in node["children"]: + self.walk_node(n) + if n["name"] in [Tree.SCHEMA_ARGUMENTS, Token.RIGHT_ARROW, Tree.TYPE]: + self.write_token_separator() + + def walk_if_expr(self, node: AstType) -> None: + """AST: if_expr + + if_expr: or_test IF or_test ELSE test + """ + self.write_expr(self.get(node, Tree.SIMPLE_EXPR)) + while self.get(node, Token.IF): + self.write_token_separator() + self.write_token("if") + self.write_token_separator() + self.write_expr(self.get(node, Tree.SIMPLE_EXPR)) + self.write_token_separator() + self.write_token("else") + self.write_token_separator() + self.write_expr(self.get(node, Tree.TEST)) + + def walk_comp_clause(self, node: AstType) -> None: + """AST: comp_clause + + comp_clause: FOR loop_variables [COMMA] IN or_test [NEWLINE] [IF test [NEWLINE]] + """ + for n in node["children"]: + if n["name"] == Token.IF and self.last_token != Token.NEWLINE: + self.write_token_separator() + self.walk_node(n) + if n["name"] == Token.IF: + self.write_token_separator() + + def walk_quant_expr(self, node: AstType) -> None: + """AST: quant_expr + + quant_expr: quant_op [ identifier COMMA ] identifier IN quant_target LEFT_BRACE (simple_expr [IF simple_expr] | NEWLINE _INDENT simple_expr [IF simple_expr] NEWLINE _DEDENT)? RIGHT_BRACE + """ + for n in node["children"]: + if n["name"] == Token.IF and self.last_token != Token.NEWLINE: + self.write_token_separator() + if n["name"] == Token.LEFT_BRACE: + self.write_token_separator() + self.walk_node(n) + if n["name"] == Token.IF: + self.write_token_separator() + + def write_node_with_newline(self, node: AstType) -> None: + """ + Write node with , ; and NEWLINE + such as list_comp and dict_comp. + """ + children = node["children"] + for i, n in enumerate(children): + if ( + n["name"] in [Token.COMMA, Token.SEMI_COLON] + and i < len(children) - 1 + and children[i + 1]["name"] == Token.NEWLINE + ): + self.write(n["value"]) + else: + self.walk(n) + + def write_colon_without_space(self, node: AstType) -> None: + """ + Write expr with colon : without space + """ + self._is_colon_without_space = True + self.write_expr(node) + self._is_colon_without_space = False + + def walk_mixins(self, node: AstType) -> None: + """AST: mixins + + mixins: identifier (COMMA (NEWLINE mixins | identifier))* + """ + self.write_node_with_newline(node) + + def walk_list_items(self, node: AstType) -> None: + """AST: list_items + + list_items: list_item ((COMMA [NEWLINE] | NEWLINE) list_item)* [COMMA] [NEWLINE] + """ + self.write_node_with_newline(node) + + def walk_entries(self, node: AstType) -> None: + """AST: entries + + entries: entry ((COMMA [NEWLINE] | NEWLINE) entry)* [COMMA] [NEWLINE] + """ + self.write_node_with_newline(node) + + def walk_config_entries(self, node: AstType) -> None: + """AST: config_entries + + entries: entry ((COMMA [NEWLINE] | NEWLINE) entry)* [COMMA] [NEWLINE] + """ + self.write_node_with_newline(node) + + def walk_subscript_suffix(self, node: AstType) -> None: + """AST: subscript_suffix + + subscript_suffix: LEFT_BRACKETS (test | [test] COLON [test] [COLON [test]]) RIGHT_BRACKETS + """ + self.write_colon_without_space(node) + + def walk_slice_suffix(self, node: AstType) -> None: + """AST: slice_suffix + + slice_suffix: LEFT_BRACKETS (test | [test] COLON [test] [COLON [test]]) RIGHT_BRACKETS + """ + self.write_colon_without_space(node) + + def walk_bin_op(self, node: AstType) -> None: + """AST: comp_op""" + children = node["children"] + for i, n in enumerate(children): + # IN | L_NOT IN | IS | IS L_NOT | L_NOT + if n["name"] == Token.IS: + self.write_token_separator() + self.write(n["value"]) + if i + 1 < len(children) and children[i + 1]["name"] != Token.L_L_NOT: + self.write_token_separator() + elif n["name"] == Token.L_L_NOT: + self.write_token_separator() + if i == 0: + self.write(n["value"]) + if i + 1 < len(children) and children[i + 1]["name"] != Token.IN: + self.write_token_separator() + if i == 1: + self.write("not") + self.write_token_separator() + else: + self.walk(n) + + def walk_not_test(self, node: AstType) -> None: + """AST: not_test""" + if self.get(node, Token.L_NOT): + self.write_token("not") + self.write_token_separator() + self.write_expr(node) + + def walk_check_block(self, node: AstType) -> None: + """AST: check_block + + check_block: CHECK COLON NEWLINE _INDENT check_expr+ _DEDENT + """ + self.write_token(self.get(node, Token.CHECK)) + self.write_token(self.get(node, Token.COLON)) + self.write_expr(node) + + def walk_check_expr(self, node: AstType) -> None: + """AST: check_expr + + check_expr: simple_expr [IF simple_expr] [COMMA primary_expr] NEWLINE + """ + self.write_expr(self.get(node, Tree.SIMPLE_EXPR)) + if self.get(node, Token.IF): + self.write_token_separator() + self.write_token("if") + self.write_token_separator() + self.write_expr(self.get(node, Tree.SIMPLE_EXPR)) + for n in node["children"]: + self.walk(n) + + def walk_dict_type(self, node: AstType) -> None: + """AST: dict_type""" + self.write_colon_without_space(node) + + # Statements + + def write_stmt(self, node: AstType) -> None: + """Write stmt""" + self.write_token(self.get(node, Token.NEWLINE)) + self.walk_nodes(*node["children"]) + + def write_condition_and_body(self, node: AstType) -> None: + """Write if and elif condition and body""" + self.write_token_separator() + self.write_expr(self.get(node, Tree.TEST)) + self.write_token(":") + self.write_stmt(self.get(node, Tree.EXECUTION_BLOCK)) + + def walk_start(self, node: AstType) -> None: + """node: start + start: (NEWLINE | statement)* + statement: simple_stmt | compound_stmt + simple_stmt: (assign_stmt | unification_stmt | expr_stmt | assert_stmt | import_stmt | type_alias_stmt) NEWLINE + """ + last_stmt_is_import = False + for n in node["children"]: + if n["name"] != Token.NEWLINE: + stmt_name = n["children"][0]["children"][0]["name"] + # Add a blank line after consecutive import statements + if last_stmt_is_import and stmt_name != Tree.IMPORT_STMT: + self.add_blank_line() + last_stmt_is_import = stmt_name == Tree.IMPORT_STMT + self.walk_node(n) + + def walk_if_stmt(self, node: AstType) -> None: + """AST: if_stmt""" + self.write_token("if") + self.write_condition_and_body(node) + while self.get(node, Token.ELIF): + self.write_token("elif") + self.write_condition_and_body(node) + if self.get(node, Token.ELSE): + self.write_token("else:") + self.write_stmt(self.get(node, Tree.EXECUTION_BLOCK)) + + def walk_assert_stmt(self, node: AstType) -> None: + """Syntax + assert_stmt: ASSERT simple_expr (IF simple_expr)? (COMMA test)? + """ + self.write_token("assert") + self.write_token_separator() + self.write_expr(self.get(node, Tree.SIMPLE_EXPR)) + if self.get(node, Token.IF): + self.write_token_separator() + self.write_token("if") + self.write_token_separator() + self.write_expr(self.get(node, Tree.SIMPLE_EXPR)) + test_node = self.get(node, Tree.TEST) + if test_node: + self.write_token(", ") + self.write_expr(test_node) + + def walk_schema_stmt(self, node: AstType) -> None: + """AST: schema_stmt + + schema_stmt: [decorators] (SCHEMA|MIXIN|PROTOCOL) [RELAXED] NAME [LEFT_BRACKETS [schema_arguments] RIGHT_BRACKETS] [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] COLON NEWLINE [schema_body] + schema_body: _INDENT (string NEWLINE)* [mixin_stmt] (schema_attribute_stmt|schema_init_stmt)* [check_block] _DEDENT + schema_attribute_stmt: attribute_stmt NEWLINE + attribute_stmt: [decorators] (FINAL)? NAME COLON type [(ASSIGN|COMP_OR) test] + schema_init_stmt: if_simple_stmt | if_stmt + """ + + self.add_blank_line() + self._is_colon_without_space = True + keywords = [Token.SCHEMA, Token.MIXIN, Token.PROTOCOL] + for keyword in keywords: + if self.has(node, keyword): + self.walk_nodes(*self.get_internal(node, keyword, Token.COLON)) + break + self._is_colon_without_space = False + self.write_expr(node) + self.add_blank_line() + + def walk_schema_argument(self, node: AstType): + """AST: schema_argument + + schema_argument: NAME [COLON type] [ASSIGN test] + """ + nodes = node["children"] + if self.has(node, Token.COLON): + self.walk_nodes(*nodes) + else: + self.write_arguments(nodes) + + def walk_if_item(self, node: AstType): + """Syntax + if_item: IF test COLON if_item_exec_block (ELIF test COLON if_item_exec_block)* (ELSE COLON if_item_exec_block)? + """ + self._is_in_collection_if = True + self.write_colon_without_space(node) + self._is_in_collection_if = False + + def walk_if_entry(self, node: AstType): + """Syntax + if_entry: IF test COLON if_entry_exec_block (ELIF test COLON if_entry_exec_block)* (ELSE COLON if_entry_exec_block)? + """ + self._is_in_collection_if = True + for n in node["children"]: + if n["name"] == Token.COLON: + self.write(":") + else: + self.walk_node(n) + self._is_in_collection_if = False + + # User interfaces + + def fmt_ast(self, ast: AstType) -> str: + """Format main function and return the format string buffer""" + if not isinstance(ast, dict): + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, arg_msg="Invalid ast type" + ) + self.walk_node(ast) + # Append file last blank line + if self.last_token_value == ENDLINE_TOKEN: + self.write(ENDLINE_TOKEN) + self.printer.seek(0) + return self.printer.read()[:-1] + + +def _get_kcl_files(file_or_dir: pathlib.Path, recursively=False): + """ + Get files in dir + """ + kcl_file_selector = "*.k" + return ( + file_or_dir.rglob(kcl_file_selector) + if recursively + else file_or_dir.glob(kcl_file_selector) + ) + + +def kcl_ast_to_fmt_file(json_ast: AstType) -> str: + """ + Update a kcl ast to a fmt file + """ + return Formatter().fmt_ast(json_ast) + + +def kcl_fmt_source(source: str) -> (str, bool): + """ + Format kcl code string and return formatted string + """ + formatted_code = Formatter().fmt_ast(get_lark_tree_from_expr(source, False)) + return formatted_code, source != formatted_code + + +def kcl_fmt_dir(file_or_dir: pathlib.Path, _recursively=False) -> int: + """ + Format all kcl files in the input directory. + If 'recursive' is 'True', recursively search kcl files from all its + sub directories. + + Return + ------ + Number of formatted files + """ + return len([kcl_fmt_file(file) for file in _get_kcl_files(file_or_dir)]) + + +def kcl_fmt_file(filepath: pathlib.Path, is_stdout=False) -> bool: + """ + Format single kcl file + """ + source, is_formatted = kcl_fmt_source(filepath.read_text(encoding="utf-8")) + if is_stdout: + print(source, end="") + else: + filepath.write_text(source) + return is_formatted + + +def kcl_fmt(input_file_or_path: str, is_stdout=False, recursively=False) -> List[str]: + """ + Format kcl file or path contains kcl files + + Parameters + ---------- + - input_file_or_path: Input filename or pathname string + """ + try: + changed_paths: List[str] = [] + formatting_filename = None + path = pathlib.Path(input_file_or_path).resolve() + if path.is_dir(): + for i, file in enumerate(_get_kcl_files(path, recursively)): + formatting_filename = file + if kcl_fmt_file(file, is_stdout): + changed_paths.append(str(file.name)) + elif path.is_file(): + formatting_filename = path + if kcl_fmt_file(path, is_stdout): + changed_paths.append(str(path.name)) + if not is_stdout: + format_count = len(changed_paths) + klog.write_out( + "KCL format done and {} formatted:\n".format( + str(format_count) + " file was" + if format_count <= 1 + else str(format_count) + " files were" + ) + ) + [klog.write_out(f"{p}\n") for p in changed_paths] + return changed_paths + except kcl_error.KCLSyntaxException as err: + # TODO: Support _Formatter filename context and remove this except. + # Add filename, line, column and raise + kcl_error.report_exception( + err_type=kcl_error.ErrType.InvalidSyntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=formatting_filename, line_no=err.lineno, col_no=err.colno + ) + ], + ) + + except Exception as err: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=formatting_filename, + ) + ], + arg_msg=str(err), + ) diff --git a/internal/kclvm_py/tools/langserver/__init__.py b/internal/kclvm_py/tools/langserver/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/internal/kclvm_py/tools/langserver/common.py b/internal/kclvm_py/tools/langserver/common.py new file mode 100644 index 000000000..75437146e --- /dev/null +++ b/internal/kclvm_py/tools/langserver/common.py @@ -0,0 +1,181 @@ +import typing +import pathlib + +import kclvm.compiler.parser.parser as parser +import kclvm.kcl.ast as ast +import kclvm.kcl.types.scope as scope +import kclvm.kcl.error as kcl_error + +from kclvm.kcl.types import ResolveProgram, ProgramScope, CheckConfig +from pygls.lsp.types.basic_structures import Location, Range, Position +from pygls.lsp.types.language_features.completion import ( + CompletionItemKind, + CompletionItem, +) +from kclvm.api.object import KCLTypeKind +from kclvm.compiler.vfs.vfs import get_pkg_realpath_from_pkgpath as get_realpath + +INNER_TYPE_2_COMPLETION_ITEM_KIND = { + # any type + KCLTypeKind.AnyKind: CompletionItemKind.Text, + # base types + KCLTypeKind.BoolKind: CompletionItemKind.Value, + KCLTypeKind.IntKind: CompletionItemKind.Value, + KCLTypeKind.FloatKind: CompletionItemKind.Value, + KCLTypeKind.StrKind: CompletionItemKind.Value, + # constants + KCLTypeKind.NoneKind: CompletionItemKind.Constant, + KCLTypeKind.BoolLitKind: CompletionItemKind.Constant, + KCLTypeKind.FloatLitKind: CompletionItemKind.Constant, + KCLTypeKind.IntLitKind: CompletionItemKind.Constant, + KCLTypeKind.StrLitKind: CompletionItemKind.Constant, + # schema + KCLTypeKind.SchemaKind: CompletionItemKind.Struct, + KCLTypeKind.SchemaDefKind: CompletionItemKind.Struct, + # built in function + KCLTypeKind.FuncKind: CompletionItemKind.Function, +} + + +def pos_to_node( + pos: ast.Position, code: str = None +) -> (typing.Optional[ast.Program], typing.Optional[ast.AST]): + if not pos or not pos.is_valid(): + return None, None + prog = file_to_prog(pos.filename, code) + if not prog: + return None, None + for module in prog.pkgs[ast.Program.MAIN_PKGPATH]: + leaf_node = module.find_leaf_by_pos(pos) + if leaf_node: + return prog, leaf_node + return prog, None + + +def pos_to_scope( + pos: ast.Position, code: str = None +) -> ( + typing.Optional[ast.Program], + typing.Optional[scope.Scope], + typing.Optional[scope.Scope], +): + if not pos or not pos.is_valid(): + return None, None, None + prog, prog_scope = file_or_prog_to_scope(None, pos.filename, code) + if not prog_scope or not prog_scope.main_scope: + return None, None, None + return prog, prog_scope.main_scope.inner_most(pos), prog_scope + + +def file_or_prog_to_scope( + prog: typing.Optional[ast.Program] = None, file_path: str = None, code: str = None +) -> (typing.Optional[ast.Program], typing.Optional[ProgramScope]): + prog = prog or file_to_prog(file_path, code) + if not prog: + return None, None + no_raise = CheckConfig() + no_raise.raise_err = False + try: + return prog, ResolveProgram(prog, config=no_raise) + except Exception: + return None, None + + +def file_to_prog(file_path: str, code: str = None) -> typing.Optional[ast.Program]: + code_list = [code] if code is not None else [] + try: + prog = parser.LoadProgram( + file_path, + mode=parser.ParseMode.Null, + k_code_list=code_list, + set_ast_parent=True, + ) + return prog + except kcl_error.KCLError: + return None + except Exception: + return None + + +def file_to_ast(file_path: str, code: str = None) -> typing.Optional[ast.Module]: + try: + module = parser.ParseFile(file_path, code) + return module + except Exception: + return None + + +def scope_obj_to_location(obj: scope.ScopeObject) -> typing.Optional[Location]: + if obj and obj.check_pos_valid(): + if obj.node.type in [ + "SchemaStmt", + "RuleStmt", + "SchemaAttr", + "SchemaIndexSignature", + ]: + return node_to_location(obj.node.name_node) + if obj.node.type == "Identifier": + return node_to_location(obj.node.name_nodes[0]) + if obj.node.type == "ImportStmt": + return node_to_location( + obj.node.as_name_node + if obj.node.as_name_node + else obj.node.path_nodes[-1] + ) + return None + + +def node_to_location(node: ast.AST) -> typing.Optional[Location]: + return ( + Location( + uri=str(pathlib.Path(node.filename)), + range=Range( + start=kcl_pos_to_lsp_pos(node.pos), + end=kcl_pos_to_lsp_pos(node.end_pos), + ), + ) + if node + and node.pos + and node.pos.is_valid() + and node.end_pos + and node.end_pos.is_valid() + else None + ) + + +def kcl_pos_to_lsp_pos(pos: ast.Position) -> Position: + return Position(line=pos.line - 1, character=pos.column - 1) + + +def scope_obj_to_completion_item(obj: scope.ScopeObject) -> CompletionItem: + assert obj and obj.name and obj.type + return CompletionItem( + label=obj.name, + kind=INNER_TYPE_2_COMPLETION_ITEM_KIND.get(obj.type.type_kind()), + ) + + +def pkgpath_to_location(root: str, pkgpath: str) -> typing.Optional[Location]: + filepath = get_realpath_from_pkgpath(root, pkgpath) + return file_to_location(filepath) + + +def file_to_location(filepath: str) -> typing.Optional[Location]: + if not filepath or not is_kcl_file(filepath): + return None + return Location(uri=filepath, range=emptyRange()) + + +def is_kcl_file(filepath: str) -> bool: + return filepath.endswith(".k") + + +def emptyRange() -> Range: + return Range( + start=Position(line=0, character=0), + end=Position(line=0, character=0), + ) + + +def get_realpath_from_pkgpath(root: str, pkgpath: str) -> str: + return str(pathlib.Path(get_realpath(root, pkgpath)).absolute()) diff --git a/internal/kclvm_py/tools/langserver/complete.py b/internal/kclvm_py/tools/langserver/complete.py new file mode 100644 index 000000000..d693f6a32 --- /dev/null +++ b/internal/kclvm_py/tools/langserver/complete.py @@ -0,0 +1,20 @@ +import pygls.lsp.types.language_features.completion as completion +import typing +import kclvm.kcl.ast as ast +from kclvm.tools.langserver.common import pos_to_scope, scope_obj_to_completion_item + + +def complete( + pos: ast.Position, name: str, code: str = None +) -> typing.List[completion.CompletionItem]: + _, inner_most, _ = pos_to_scope(pos, code) + _scope = inner_most + completion_list: completion.CompletionList = completion.CompletionList( + isIncomplete=False + ) + while _scope is not None: + for key, obj in _scope.elems.items(): + if key.startswith(name): + completion_list.add_item(scope_obj_to_completion_item(obj)) + _scope = _scope.parent + return completion_list.items diff --git a/internal/kclvm_py/tools/langserver/document_symbol.py b/internal/kclvm_py/tools/langserver/document_symbol.py new file mode 100644 index 000000000..4fc24b92b --- /dev/null +++ b/internal/kclvm_py/tools/langserver/document_symbol.py @@ -0,0 +1,153 @@ +""" +https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol +DocumentSymbol[] which is a hierarchy of symbols found in a given text document. +class DocumentSymbol(Model): + name: str # The name of this symbol. Will be displayed in the user interface + and therefore must not be an empty string or a string only + consisting of white spaces. + kind: SymbolKind # The kind of this symbol. + range: Range # The range enclosing this symbol not including leading/trailing + whitespace but everything else like comments. This information + is typically used to determine if the clients cursor is inside + the symbol to reveal in the symbol in the UI. + selection_range: Range # The range that should be selected and revealed when this symbol + is being picked, e.g. the name of a function. Must be contained + by the `range`. + detail: Optional[str] = None # More detail for this symbol, e.g the signature of a function. + children: Optional[List['DocumentSymbol']] = None # Children of this symbol, e.g. properties of a class. + deprecated: Optional[bool] = False # Indicates if this symbol is deprecated. + +In KCl, we select variables and schema definitions as symbols,, and the schema attributes and mixins in the schema will +be the child nodes of the schema, e.g: + +a = b = 1 a +schema Person: b + mixin [ Person + nameMixin mixin + ] -> nameMixin + age: int = 1 age + person +person = Person{ + ... +} +""" +from typing import List +from pygls.lsp.types.language_features.document_symbol import DocumentSymbol, SymbolKind +from pygls.lsp.types.basic_structures import Range, Position + +import kclvm.kcl.ast as ast +from kclvm.tools.langserver.common import file_to_ast + + +def range_check(s: DocumentSymbol): + """ + DocumentSymbol.selection_range must be contained by the DocumentSymbol.range + a = { ... } + ^^ ^ + ││ └-range_end + │└---selection_range_end + │ + range_start, selection_range_start + + """ + assert isinstance(s, DocumentSymbol) + range = s.range + selection_range = s.selection_range + assert ( + (range.start.line <= selection_range.start.line) + & (selection_range.start.line <= selection_range.end.line) + & (selection_range.end.line <= range.end.line) + ) + if range.start.line == selection_range.start.line: + assert selection_range.start.character >= range.start.character + if selection_range.end.line == range.end.line: + assert selection_range.start.character >= range.start.character + if s.children: + for child in s.children: + range_check(child) + + +def ast_position_to_range(node: ast.AST) -> Range: + assert isinstance(node, ast.AST) + return Range( + start=Position(line=node.line - 1, character=node.column - 1), + end=Position(line=node.end_line - 1, character=node.end_column - 1), + ) + + +def identifier_to_document_symbol(node: ast.Identifier) -> DocumentSymbol: + assert isinstance(node, ast.Identifier) + range = Range( + start=Position(line=node.line - 1, character=node.column - 1), + end=Position(line=node.end_line - 1, character=node.end_column - 1), + ) + return DocumentSymbol( + name=".".join(node.names), + range=range, + selection_range=range, + kind=SymbolKind.Variable, + ) + + +def schema_attr_to_document_symbol(node: ast.SchemaAttr) -> DocumentSymbol: + assert isinstance(node, ast.SchemaAttr) + return DocumentSymbol( + name=node.name, + range=ast_position_to_range(node), + kind=SymbolKind.Property, + selection_range=ast_position_to_range(node.name_node), + ) + + +def schema_stmt_to_document_symbol(node: ast.SchemaStmt) -> DocumentSymbol: + assert isinstance(node, ast.SchemaStmt) + symbol = DocumentSymbol( + name=node.name, + kind=SymbolKind.Struct, + range=ast_position_to_range(node), + selection_range=ast_position_to_range(node.name_node), + children=[], + ) + if len(node.mixins): + range = Range( + start=Position(line=node.line, character=4), + end=Position(line=node.line, character=9), + ) + symbol.children.append( + DocumentSymbol( + name="mixin", + kind=SymbolKind.Property, + range=range, + selection_range=range, + children=[identifier_to_document_symbol(id) for id in node.mixins], + ) + ) + symbol.children += [ + schema_attr_to_document_symbol(attr) for attr in node.GetAttrList() + ] + return symbol + + +def assign_stmt_to_document_symbol(node: ast.AssignStmt) -> DocumentSymbol: + assert isinstance(node, ast.AssignStmt) + id_symbols = [] + for identifier in node.targets: + symbol = identifier_to_document_symbol(identifier) + symbol.range = ast_position_to_range(node) + id_symbols.append(symbol) + return id_symbols + + +def document_symbol(file: str, code: str = None) -> List[DocumentSymbol]: + symbols = [] + module = file_to_ast(file, code) + if not module or not isinstance(module, ast.Module): + return [] + for stmt in module.body or []: + if isinstance(stmt, ast.SchemaStmt): + symbols.append(schema_stmt_to_document_symbol(stmt)) + if isinstance(stmt, ast.AssignStmt): + symbols.extend(assign_stmt_to_document_symbol(stmt)) + for s in symbols: + range_check(s) + return symbols diff --git a/internal/kclvm_py/tools/langserver/go_to_def.py b/internal/kclvm_py/tools/langserver/go_to_def.py new file mode 100644 index 000000000..9ecf5da76 --- /dev/null +++ b/internal/kclvm_py/tools/langserver/go_to_def.py @@ -0,0 +1,201 @@ +from typing import List, cast, Optional +from pygls.lsp.types.basic_structures import Location + +import kclvm.kcl.ast as ast +import kclvm.kcl.types.scope as scope +import kclvm.tools.langserver.common as common +from kclvm.api.object.object import KCLTypeKind, KCLModuleTypeObject +from kclvm.api.object.schema import KCLSchemaTypeObject, KCLSchemaDefTypeObject + + +def definition( + pos: ast.Position, code: str = None +) -> (Optional[ast.AST], Optional[scope.ScopeObject]): + prog: ast.Program + prog, leaf_node = common.pos_to_node(pos, code) + if not leaf_node: + # no name node at the position + return None, None + parent: ast.AST = leaf_node.parent + if isinstance(leaf_node, ast.Name): + if ( + parent.type == "Identifier" + and parent.parent.type == "ConfigEntry" + and parent is parent.parent.key + ): + identifier: ast.Identifier = cast(ast.Identifier, parent) + _, prog_scope = common.file_or_prog_to_scope(prog) + schema_expr: ast.SchemaExpr = leaf_node.find_nearest_parent_by_type( + ast.SchemaExpr + ) + if schema_expr: + schema_name: ast.Identifier = schema_expr.name + schema_scope_obj = find_declaration( + schema_name, schema_name.name_nodes[-1], prog_scope + ) + top_attr = find_inner_name( + schema_scope_obj, identifier.name_nodes[0], prog_scope + ) + result_obj = find_declaration_by_scope_obj( + identifier=identifier, + name_node=leaf_node, + top_name_obj=top_attr, + prog_scope=prog_scope, + ) + return leaf_node, result_obj + if parent.type == "Identifier" and ( + parent.parent.type != "ConfigEntry" or parent is parent.parent.value + ): + identifier: ast.Identifier = cast(ast.Identifier, parent) + _, prog_scope = common.file_or_prog_to_scope(prog) + declaration = find_declaration(identifier, leaf_node, prog_scope) + return leaf_node, declaration + return leaf_node, None + + +def go_to_def(pos: ast.Position, code: str = None) -> List[Location]: + prog: ast.Program + prog, leaf_node = common.pos_to_node(pos, code) + if not leaf_node: + # no name node at the position + return [] + parent: ast.AST = leaf_node.parent + if isinstance(leaf_node, ast.Name): + if parent.type == "ImportStmt": + import_stmt: ast.ImportStmt = cast(ast.ImportStmt, parent) + if leaf_node in import_stmt.path_nodes: + index = import_stmt.path_nodes.index(leaf_node) + if index == len(import_stmt.path_nodes) - 1: + # this might be a module name, return the target module file path + loc = common.pkgpath_to_location( + root=prog.root, pkgpath=import_stmt.path + ) + return [loc] if loc else [] + return [common.node_to_location(leaf_node)] + if ( + parent.type == "Identifier" + and parent.parent.type == "ConfigEntry" + and parent is parent.parent.key + ): + identifier: ast.Identifier = cast(ast.Identifier, parent) + _, prog_scope = common.file_or_prog_to_scope(prog) + schema_expr: ast.SchemaExpr = leaf_node.find_nearest_parent_by_type( + ast.SchemaExpr + ) + if schema_expr: + schema_name: ast.Identifier = schema_expr.name + schema_scope_obj = find_declaration( + schema_name, schema_name.name_nodes[-1], prog_scope + ) + top_attr = find_inner_name( + schema_scope_obj, identifier.name_nodes[0], prog_scope + ) + result_obj = find_declaration_by_scope_obj( + identifier=identifier, + name_node=leaf_node, + top_name_obj=top_attr, + prog_scope=prog_scope, + ) + loc = common.scope_obj_to_location(result_obj) + return [loc] if loc else [] + if parent.type == "Identifier" and ( + parent.parent.type != "ConfigEntry" or parent is parent.parent.value + ): + identifier: ast.Identifier = cast(ast.Identifier, parent) + _, prog_scope = common.file_or_prog_to_scope(prog) + declaration = find_declaration(identifier, leaf_node, prog_scope) + loc = common.scope_obj_to_location(declaration) + return [loc] if loc else [] + return [common.node_to_location(leaf_node)] + + +def find_declaration( + identifier: ast.Identifier, name_node: ast.Name, prog_scope: scope.ProgramScope +) -> Optional[scope.ScopeObject]: + if not identifier or not name_node or not prog_scope: + return None + top_name = identifier.name_nodes[0] + top_name_obj = find_declaration_obj_by_pos_and_name( + top_name.pos, top_name.value, prog_scope + ) + return find_declaration_by_scope_obj( + identifier, name_node, top_name_obj, prog_scope + ) + + +def find_declaration_by_scope_obj( + identifier: ast.Identifier, + name_node: ast.Name, + top_name_obj: scope.ScopeObject, + prog_scope: scope.ProgramScope, +) -> Optional[scope.ScopeObject]: + if not identifier or not name_node or not top_name_obj or not prog_scope: + return None + index = identifier.name_nodes.index(name_node) + i = 0 + obj = top_name_obj + while i < index: + i = i + 1 + obj = find_inner_name(obj, identifier.name_nodes[i], prog_scope) + if not obj: + return None + return obj + + +def find_declaration_obj_by_pos_and_name( + pos: ast.Position, name: str, prog_scope: scope.ProgramScope +) -> Optional[scope.ScopeObject]: + if not pos or not pos.is_valid() or not name or not prog_scope: + return None + inner_most = prog_scope.main_scope.inner_most(pos) + if not inner_most or not inner_most.elems: + return None + scope_obj = inner_most.elems.get(name) + if scope_obj is not None: + return scope_obj + # 1. search through the parent schema scope tree + parent_scope = inner_most.get_parent_schema_scope(prog_scope) + while parent_scope is not None: + scope_obj = parent_scope.elems.get(name) + if scope_obj is not None: + return scope_obj + parent_scope = parent_scope.get_parent_schema_scope(prog_scope) + # 2. search through the enclosing scope tree + while inner_most is not None: + scope_obj = inner_most.elems.get(name) + if scope_obj is not None: + return scope_obj + inner_most = inner_most.get_enclosing_scope() + return None + + +def find_inner_name( + out_name_obj: scope.ScopeObject, + inner_name: ast.Name, + prog_scope: scope.ProgramScope, +) -> Optional[scope.ScopeObject]: + if not out_name_obj or not inner_name or not prog_scope: + return None + if out_name_obj.type.type_kind() == KCLTypeKind.SchemaKind: + return find_attr_by_name(inner_name.value, out_name_obj.type, prog_scope) + if out_name_obj.type.type_kind() == KCLTypeKind.ModuleKind: + out_type = cast(KCLModuleTypeObject, out_name_obj.type) + if out_type.is_user_module: + pkg_scope = prog_scope.scope_map.get(out_type.pkgpath) + return pkg_scope.elems.get(inner_name.value) if pkg_scope else None + if out_name_obj.type.type_kind() == KCLTypeKind.SchemaDefKind: + out_type = cast(KCLSchemaDefTypeObject, out_name_obj.type) + return find_attr_by_name(inner_name.value, out_type.schema_type, prog_scope) + + +def find_attr_by_name( + attr_name: str, schema_type: KCLSchemaTypeObject, prog_scope: scope.ProgramScope +) -> Optional[scope.ScopeObject]: + while schema_type: + if attr_name in schema_type.attr_list: + # todo: support jump to schema index signature + pkg_scope = prog_scope.scope_map.get(schema_type.pkgpath) + schema_scope = pkg_scope.search_child_scope_by_name(schema_type.name) + return schema_scope.elems.get(attr_name) if schema_scope else None + schema_type = schema_type.base + return None diff --git a/internal/kclvm_py/tools/langserver/grpc_wrapper.py b/internal/kclvm_py/tools/langserver/grpc_wrapper.py new file mode 100644 index 000000000..ad21f22d3 --- /dev/null +++ b/internal/kclvm_py/tools/langserver/grpc_wrapper.py @@ -0,0 +1,63 @@ +import json +from typing import Union, Optional + +from kclvm.internal.gpyrpc.gpyrpc_pb2 import Position +import kclvm.kcl.ast as ast +from .complete import complete +from .go_to_def import go_to_def +from .document_symbol import document_symbol +from .hover import hover + + +def go_to_def_wrapper(pos: Position, code: str = None) -> str: + pos_wrapper = proto_pos_to_ast_pos(pos) + result = go_to_def(pos=pos_wrapper, code=code) + return json.dumps(obj=result, default=lambda x: x.__dict__) + + +def complete_wrapper(pos: Position, name: str, code: str = None) -> str: + pos_wrapper = proto_pos_to_ast_pos(pos) + result = complete(pos=pos_wrapper, name=name, code=code) + return json.dumps(obj=result, default=lambda x: x.__dict__) + + +def proto_pos_to_ast_pos(pos: Position) -> ast.Position: + return ast.Position(filename=pos.filename, line=pos.line + 1, column=pos.column + 1) + + +class SnakeToCamel(json.JSONEncoder): + """Class attributes need to be converted to camel-case notation because client is expecting that.""" + + def to_camel(self, s: str): + return "".join( + word.capitalize() if idx > 0 else word + for idx, word in enumerate(s.split("_")) + ) + + def convert_key( + self, instance: Union[int, float, str, bool, list, dict, tuple] + ) -> Optional[Union[int, float, str, bool, list, dict, tuple]]: + if instance is None: + return None + if isinstance(instance, (bool, int, float, str)): + return instance + elif isinstance(instance, (list, set, tuple)): + return [self.convert_key(v) for v in instance] + elif isinstance(instance, dict): + return {self.to_camel(k): self.convert_key(v) for k, v in instance.items()} + else: + return self.convert_key(instance.__dict__) + + def default(self, obj): + return self.convert_key(obj.__dict__) + + +def document_symbol_wrapper(file: str, code: str = None) -> str: + result = document_symbol(file=file, code=code) + return json.dumps(obj=result, cls=SnakeToCamel) + + +def hover_wrapper(pos: Position, code: str) -> str: + pos_wrapper = proto_pos_to_ast_pos(pos) + result = hover(pos=pos_wrapper, code=code) + return json.dumps(obj=result, default=lambda x: x.__dict__) diff --git a/internal/kclvm_py/tools/langserver/hover.py b/internal/kclvm_py/tools/langserver/hover.py new file mode 100644 index 000000000..559fd0e1e --- /dev/null +++ b/internal/kclvm_py/tools/langserver/hover.py @@ -0,0 +1,67 @@ +import typing +import pygls.lsp.types.language_features.hover as pygls_hover +from pygls.lsp.types.basic_structures import MarkupContent, MarkupKind, Range +import kclvm.kcl.ast as ast +import kclvm.api.object as objpkg +from kclvm.kcl.types.scope import ScopeObject +import kclvm.tools.langserver.common as common +from . import go_to_def + + +def hover(pos: ast.Position, code: str = None) -> typing.Optional[pygls_hover.Hover]: + def_node, def_obj = go_to_def.definition(pos, code) + if not def_node: + return None + if def_obj: + return scope_obj_desc(def_node, def_obj) + else: + return ast_node_desc(def_node) + + +def ast_node_desc(node: ast.AST) -> typing.Optional[pygls_hover.Hover]: + if isinstance(node, ast.Name): + return pygls_hover.Hover( + contents=MarkupContent( + kind=MarkupKind.PlainText, + value=node.value, + ), + range=Range( + start=common.kcl_pos_to_lsp_pos(node.pos), + end=common.kcl_pos_to_lsp_pos(node.end_pos), + ), + ) + return None + + +def scope_obj_desc( + node: ast.AST, obj: ScopeObject +) -> typing.Optional[pygls_hover.Hover]: + if isinstance(node, ast.AST) and isinstance(obj, ScopeObject): + # 针对每种类型的 scope object,进行不同的显示 + if not obj.node and isinstance(obj.type, objpkg.KCLFunctionTypeObject): + # the target scope object is a built-in function name + msg = ( + f"(built-in) {obj.name}(" + + ", ".join([param.param_doc() for param in obj.type.params]) + + f"): {obj.type.return_type.type_str() if obj.type.return_type else 'any'}" + + f"\n{obj.type.doc}" + if obj.type.doc + else "" + ) + else: + msg = ( + f"{obj.name}\ntype: {obj.type.type_str()}\ndefined in:{obj.node.filename}" + if obj.node + else obj.name + ) + return pygls_hover.Hover( + contents=MarkupContent( + kind=MarkupKind.PlainText, + value=msg, + ), + range=Range( + start=common.kcl_pos_to_lsp_pos(node.pos), + end=common.kcl_pos_to_lsp_pos(node.end_pos), + ), + ) + return None diff --git a/internal/kclvm_py/tools/lint/checkers/__init__.py b/internal/kclvm_py/tools/lint/checkers/__init__.py new file mode 100644 index 000000000..7625e276c --- /dev/null +++ b/internal/kclvm_py/tools/lint/checkers/__init__.py @@ -0,0 +1,16 @@ +""" +Base id of standard checkers (used in msg and report ids): +01: basic +02: Schema +03: format +04: import +05: misc +06: variables +07: +08: +09: syntax +10-50: not yet used: reserved for future internal checkers. +51-99: perhaps used: reserved for external checkers +""" + +__all__ = ["base_checker", "imports", "misc", "basic"] diff --git a/internal/kclvm_py/tools/lint/checkers/base_checker.py b/internal/kclvm_py/tools/lint/checkers/base_checker.py new file mode 100644 index 000000000..681b5d4de --- /dev/null +++ b/internal/kclvm_py/tools/lint/checkers/base_checker.py @@ -0,0 +1,57 @@ +from typing import Any +from abc import abstractmethod + +import kclvm.kcl.ast as ast + + +class BaseChecker(ast.TreeWalker): + # checker name (you may reuse an existing one) + name: str = "" + # options level (0 will be displaying in --help, 1 in --long-help) + level = 1 + # ordered list of options to control the checker behaviour + options: Any = {} + # messages constant to display + MSGS: Any = {} + # messages issued by this checker + msgs: Any = [] + # mark this checker as enabled or not. + enabled: bool = True + + def __init__(self, linter=None) -> None: + """ + checker instances should have the linter as argument + + :param linter: is an object implementing KCLLinter. + """ + if self.name is not None: + self.name = self.name.lower() + self.linter = linter + self.options = linter.config if linter else None + + def __eq__(self, other) -> bool: + return self.name == other.name and self.linter == other.linter + + def generic_walk(self, t: ast.AST) -> None: + """Called if no explicit walker function exists for a node.""" + for _, value in ast.iter_fields(t): + if isinstance(value, list): + for v in value: + # IfStmt.elif_body: List[List[Stmt]] + if isinstance(v, list): + for v1 in v: + self.walk(v1) + if isinstance(v, ast.AST): + self.walk(v) + elif isinstance(value, ast.AST): + self.walk(value) + + def get_node_name(self, t: ast.AST) -> str: + """Get the ast.AST node name""" + assert isinstance(t, ast.AST) + return t.type + + @abstractmethod + def check(self, prog: ast.Program, code: str): + """Should be overridden by subclass""" + raise NotImplementedError() diff --git a/internal/kclvm_py/tools/lint/checkers/basic.py b/internal/kclvm_py/tools/lint/checkers/basic.py new file mode 100644 index 000000000..111cf9220 --- /dev/null +++ b/internal/kclvm_py/tools/lint/checkers/basic.py @@ -0,0 +1,336 @@ +"""basic checker for Python code""" +import re +from typing import Pattern + +import kclvm.tools.lint.message.message as message +import kclvm.kcl.ast as ast +import kclvm.tools.lint.lint.utils as utils +from kclvm.tools.lint.checkers.base_checker import BaseChecker + + +MSGS = { + "C0103": ( + '%s name "%s" doesn\'t conform to %s.', + "Invalid-name.", + "{0} name {1} doesn't conform to {2}.", + ), + "C0104": ( + 'Disallowed name "%s".', + "Disallowed-name.", + 'Disallowed name "{0}."', + ), +} + + +class NamingStyle: + ANY: Pattern[str] = re.compile(".*") + MOD_NAME_RGX: Pattern[str] = ANY + PKG_NAME_RGX: Pattern[str] = ANY + SCHEMA_NAME_RGX: Pattern[str] = ANY + MIXIN_NAME_RGX: Pattern[str] = ANY + PROTOCOL_NAME_RGX: Pattern[str] = ANY + SCHEMA_ATTRIBUTE_RGX: Pattern[str] = ANY + VARIABLE_RGX: Pattern[str] = ANY + ARGUMENT_RGX: Pattern[str] = ANY + DEFAULT_NAME_RGX: Pattern[str] = ANY + + @classmethod + def get_regex(cls, name_type): + return { + "module": cls.MOD_NAME_RGX, + "package": cls.PKG_NAME_RGX, + "schema": cls.SCHEMA_NAME_RGX, + "mixin": cls.MIXIN_NAME_RGX, + "protocol": cls.PROTOCOL_NAME_RGX, + "argument": cls.ARGUMENT_RGX, + "variable": cls.VARIABLE_RGX, + "schema_attribute": cls.SCHEMA_ATTRIBUTE_RGX, + }[name_type] + + +class SnakeCaseStyle(NamingStyle): + ANY: Pattern[str] = re.compile(".*") + MOD_NAME_RGX: Pattern[str] = re.compile(r"^\$?[a-z][a-z_]*$") + PKG_NAME_RGX: Pattern[str] = re.compile(r"^\$?[a-z][a-z_]*$") + SCHEMA_NAME_RGX: Pattern[str] = ANY + MIXIN_NAME_RGX: Pattern[str] = ANY + PROTOCOL_NAME_RGX: Pattern[str] = ANY + VARIABLE_RGX: Pattern[str] = ANY + SCHEMA_ATTRIBUTE_RGX: Pattern[str] = ANY + ARGUMENT_RGX: Pattern[str] = ANY + DEFAULT_NAME_RGX: Pattern[str] = ANY + + +class CamelCaseStyle(NamingStyle): + """Regex rules for camelCase naming style.""" + + ANY: Pattern[str] = re.compile(".*") + MOD_NAME_RGX: Pattern[str] = ANY + PKG_NAME_RGX: Pattern[str] = ANY + SCHEMA_NAME_RGX: Pattern[str] = ANY + MIXIN_NAME_RGX: Pattern[str] = ANY + PROTOCOL_NAME_RGX: Pattern[str] = ANY + VARIABLE_RGX: Pattern[str] = ANY + SCHEMA_ATTRIBUTE_RGX: Pattern[str] = re.compile(r"^\$?[a-z][a-zA-Z]*$") + ARGUMENT_RGX: Pattern[str] = re.compile(r"^\$?[a-z][a-zA-Z]*$") + DEFAULT_NAME_RGX: Pattern[str] = re.compile(r"^\$?[a-z][a-zA-Z]*$") + + +class PascalCaseStyle(NamingStyle): + """Regex rules for PascalCase naming style.""" + + ANY: Pattern[str] = re.compile(".*") + MOD_NAME_RGX: Pattern[str] = ANY + PKG_NAME_RGX: Pattern[str] = ANY + SCHEMA_NAME_RGX: Pattern[str] = re.compile(r"^\$?[A-Z][a-zA-Z\d]*$") + MIXIN_NAME_RGX: Pattern[str] = re.compile(r"^\$?[A-Z][a-zA-Z\d]*Mixin$") + PROTOCOL_NAME_RGX: Pattern[str] = re.compile(r"^\$?[A-Z][a-zA-Z\d]*Protocol$") + VARIABLE_RGX: Pattern[str] = ANY + SCHEMA_ATTRIBUTE_RGX: Pattern[str] = ANY + ARGUMENT_RGX: Pattern[str] = ANY + DEFAULT_NAME_RGX: Pattern[str] = ANY + + +class UpperCaseStyle(NamingStyle): + """Regex rules for UPPER_CASE naming style.""" + + ANY: Pattern[str] = re.compile(".*") + MOD_NAME_RGX: Pattern[str] = ANY + PKG_NAME_RGX: Pattern[str] = ANY + SCHEMA_NAME_RGX: Pattern[str] = ANY + MIXIN_NAME_RGX: Pattern[str] = ANY + PROTOCOL_NAME_RGX: Pattern[str] = ANY + VARIABLE_RGX: Pattern[str] = ANY + SCHEMA_ATTRIBUTE_RGX: Pattern[str] = ANY + ARGUMENT_RGX: Pattern[str] = ANY + DEFAULT_NAME_RGX: Pattern[str] = re.compile(r"^\$?[^\W\da-z_][^\Wa-z]*$") + + +class AnyStyle(NamingStyle): + pass + + +NAMING_STYLES = { + "snake_case": SnakeCaseStyle, + "camelCase": CamelCaseStyle, + "PascalCase": PascalCaseStyle, + "UPPER_CASE": UpperCaseStyle, + "ANY": AnyStyle, +} + + +KNOWN_NAME_TYPES = { + "module", + "package", + "schema", + "mixin", + "protocol", + "argument", + "variable", + "schema_attribute", +} + + +class BasicChecker(BaseChecker): + def __init__(self, linter) -> None: + super().__init__(linter) + self.name = "BaseCheck" + self.code = None + self.MSGS = MSGS + self.module = None + self.naming_rules = None + self.bad_names = None + self.prog = None + + def reset(self) -> None: + self.msgs.clear() + self.code = None + self.module = None + self.naming_rules = None + self.bad_names = None + + def get_module(self, prog: ast.Program, code: str) -> None: + assert isinstance(prog, ast.Program) + assert code is not None + self.prog = prog + self.module = prog.pkgs["__main__"][0] + self.code = code + self.naming_rules = self._create_naming_rules() + self.bad_names = self.options.bad_names + + def check(self, prog: ast.Program, code: str) -> None: + assert isinstance(prog, ast.Program) + assert code is not None + self.reset() + self.get_module(prog, code) + self.walk(self.module) + + def _create_naming_rules(self): + regexps = {} + for name_type in KNOWN_NAME_TYPES: + # naming rgx in config, .e.g module-rgx : [^\W\dA-Z][^\WA-Z]+$ + custom_regex_setting_name = f"{name_type}_rgx" + if getattr(self.options, custom_regex_setting_name): + regexps[name_type] = re.compile( + getattr(self.options, custom_regex_setting_name) + ) + else: + # naming rgx in config, .e.g module-rgx : [^\W\dA-Z][^\WA-Z]+$ + naming_style_name = f"{name_type}_naming_style" + regexps[name_type] = NAMING_STYLES[ + getattr(self.options, naming_style_name) + ].get_regex(name_type) + return regexps + + def _check_name(self, name: str, name_type: str): + return self.naming_rules[name_type].search(name) + + def _disallowed_name(self, name: str): + return name in self.bad_names + + def _get_name_style_or_rgx(self, name_type): + name_rgx = f"{name_type}_rgx" + name_style = f"{name_type}_naming_style" + if getattr(self.options, name_rgx): + return getattr(self.options, name_rgx) + else: + return getattr(self.options, name_style) + " naming style" + + def walk_RuleStmt(self, t: ast.RuleStmt) -> None: + assert isinstance(t, ast.RuleStmt) + if not self._check_name(t.name, "schema"): + name_style_or_rgx = self._get_name_style_or_rgx("schema") + self.msgs.append( + message.Message( + "C0103", + self.module.filename, + MSGS["C0103"][0] % ("Schema", t.name, name_style_or_rgx), + utils.get_source_code( + self.module.filename, t.name_node.line, self.code + ), + (t.name_node.line, t.name_node.column), + ["Schema", t.name, name_style_or_rgx], + ) + ) + self.generic_walk(t) + + def walk_SchemaStmt(self, t: ast.SchemaStmt) -> None: + assert isinstance(t, ast.SchemaStmt) + if t.is_mixin: + if not self._check_name(t.name, "mixin"): + name_style_or_rgx = self._get_name_style_or_rgx("mixin") + self.msgs.append( + message.Message( + "C0103", + self.module.filename, + MSGS["C0103"][0] % ("Mixin", t.name, name_style_or_rgx), + utils.get_source_code( + self.module.filename, t.name_node.line, self.code + ), + (t.name_node.line, t.name_node.column), + ["Mixin", t.name, name_style_or_rgx], + ) + ) + elif t.is_protocol: + if not self._check_name(t.name, "protocol"): + name_style_or_rgx = self._get_name_style_or_rgx("schema") + self.msgs.append( + message.Message( + "C0103", + self.module.filename, + MSGS["C0103"][0] % ("Protocol", t.name, name_style_or_rgx), + utils.get_source_code( + self.module.filename, t.name_node.line, self.code + ), + (t.name_node.line, t.name_node.column), + ["Protocol", t.name, name_style_or_rgx], + ) + ) + else: + if not self._check_name(t.name, "schema"): + name_style_or_rgx = self._get_name_style_or_rgx("schema") + self.msgs.append( + message.Message( + "C0103", + self.module.filename, + MSGS["C0103"][0] % ("Schema", t.name, name_style_or_rgx), + utils.get_source_code( + self.module.filename, t.name_node.line, self.code + ), + (t.name_node.line, t.name_node.column), + ["Schema", t.name, name_style_or_rgx], + ) + ) + self.generic_walk(t) + + def walk_Arguments(self, t: ast.Arguments) -> None: + assert isinstance(t, ast.Arguments) + for arg in t.args: + for name in arg.name_nodes: + if not self._check_name(name.value, "argument"): + name_style_or_rgx = self._get_name_style_or_rgx("argument") + self.msgs.append( + message.Message( + "C0103", + self.module.filename, + MSGS["C0103"][0] + % ("Argument", name.value, name_style_or_rgx), + utils.get_source_code( + self.module.filename, t.line, self.code + ), + (t.line, t.column), + ["Argument", name.value, name_style_or_rgx], + ) + ) + self.generic_walk(t) + + def walk_SchemaAttr(self, t: ast.SchemaAttr) -> None: + assert isinstance(t, ast.SchemaAttr) + if not self._check_name(t.name, "schema_attribute"): + name_style_or_rgx = self._get_name_style_or_rgx("schema_attribute") + self.msgs.append( + message.Message( + "C0103", + self.module.filename, + MSGS["C0103"][0] % ("Schema attribute", t.name, name_style_or_rgx), + utils.get_source_code(self.module.filename, t.line, self.code), + (t.line, t.column), + ["Schema attribute", t.name, name_style_or_rgx], + ) + ) + self.generic_walk(t) + + def walk_AssignStmt(self, t: ast.AssignStmt) -> None: + assert isinstance(t, ast.AssignStmt) + for target in t.targets: + for name in target.name_nodes: + if not self._check_name(name.value, "variable"): + name_style_or_rgx = self._get_name_style_or_rgx("variable") + self.msgs.append( + message.Message( + "C0103", + self.module.filename, + MSGS["C0103"][0] + % ("Variable", name.value, name_style_or_rgx), + utils.get_source_code( + self.module.filename, t.line, self.code + ), + (name.line, name.column), + ["Variable", name.value, name_style_or_rgx], + ) + ) + self.generic_walk(t) + + def walk_Name(self, t: ast.Name) -> None: + assert isinstance(t, ast.Name) + if self._disallowed_name(t.value): + self.msgs.append( + message.Message( + "C0104", + self.module.filename, + MSGS["C0104"][0] % t.value, + utils.get_source_code(self.module.filename, t.line, self.code), + (t.line, t.column), + [t.value], + ) + ) diff --git a/internal/kclvm_py/tools/lint/checkers/imports.py b/internal/kclvm_py/tools/lint/checkers/imports.py new file mode 100644 index 000000000..e3dce0220 --- /dev/null +++ b/internal/kclvm_py/tools/lint/checkers/imports.py @@ -0,0 +1,228 @@ +import os +from typing import Union + +import kclvm.kcl.info as kcl_info +import kclvm.tools.lint.message.message as message +import kclvm.compiler.vfs as vfs +import kclvm.kcl.ast as ast +import kclvm.compiler.extension.plugin.plugin_model as plugin +import kclvm.compiler.extension.builtin.builtin as builtin +import kclvm.tools.lint.lint.utils as utils +from kclvm.tools.lint.checkers.base_checker import BaseChecker +import re + +MSGS = { + "E0401": ("Unable to import %s.", "Unable to import.", "Unable to import '{0}'."), + "W0404": ( + "%s is reimported multiple times.", + "Module reimported.", + "'{0}' is reimported multiple times.", + ), + "E0406": ( + "Module import itself.", + "Module import itself.", + "Module import itself.", + ), + "W0411": ( + "%s imported but unused.", + "Module imported but unused.", + "'{0}' imported but unused.", + ), + "E0413": ( + "Import %s should be placed at the top of the module.", + "ImportStmt is not at the top of the file.", + "Import '{0}' should be placed at the top of the module.", + ), +} + +IMPORT_POSITION_CHECK_LIST = [ + "AssignStmt", + "AugAssignStmt", + "AssertStmt", + "IfStmt", + "TypeAliasStmt", + "SchemaStmt", + "RuleStmt", +] + + +class ImportsChecker(BaseChecker): + def __init__(self, linter=None) -> None: + super().__init__(linter) + self.name = "ImportCheck" + self.MSGS = MSGS + self.prog = None + self.module = None + self.code = None + self.root: str = None + # for reimport check + self.has_imported_modules = None + # for unused import check + self.import_names_map = None + # for import position check + self.import_position_check = True + + def reset(self) -> None: + self.msgs.clear() + self.module = None + self.code = None + self.root = None + if self.import_names_map: + self.import_names_map.clear() + if self.has_imported_modules: + self.has_imported_modules.clear() + self.import_position_check = True + + def get_module(self, prog: ast.Program, code: str) -> None: + assert isinstance(prog, ast.Program) + assert code is not None + self.prog = prog + self.module = prog.pkgs["__main__"][0] + self.code = code + self.root = prog.root + self.has_imported_modules = [] + self.import_names_map = {} + + def check(self, prog: ast.Program, code: str) -> None: + assert isinstance(prog, ast.Program) + assert code is not None + self.reset() + self.get_module(prog, code) + self.walk(self.module) + for k, v in self.import_names_map.items(): + self.msgs.append( + message.Message( + "W0411", + self.module.filename, + MSGS["W0411"][0] % k, + utils.get_source_code(self.module.filename, v.line, self.code), + (v.line, v.column), + [k], + ) + ) + + def check_import_file_exist(self, t: ast.ImportStmt, abs_path: str) -> bool: + assert isinstance(t, ast.ImportStmt) + if os.path.isdir(abs_path) or os.path.isfile( + abs_path + kcl_info.KCL_FILE_SUFFIX + ): + return True + else: + self.msgs.append( + message.Message( + "E0401", + self.module.filename, + MSGS["E0401"][0] % t.path, + utils.get_source_code(self.module.filename, t.line, self.code), + (t.line, t.column), + [t.path], + ) + ) + return False + + def check_import_position(self, t: ast.AST) -> None: + assert isinstance(t, ast.AST) + if self.import_position_check: + if t._ast_type in IMPORT_POSITION_CHECK_LIST: + self.import_position_check = False + else: + if isinstance(t, ast.ImportStmt): + self.msgs.append( + message.Message( + "E0413", + self.module.filename, + MSGS["E0413"][0] % t.pkg_name, + utils.get_source_code(self.module.filename, t.line, self.code), + (t.line, t.column), + [t.pkg_name], + ) + ) + + def check_unused_import(self, t: Union[ast.Identifier, str]) -> None: + if isinstance(t, ast.Identifier): + if t.get_first_name() in self.import_names_map.keys(): + self.import_names_map.pop(t.get_first_name()) + else: + # SchemaAttr.types, A|B, [A|B], {A|B:C} + type_list = re.split(r"[|:\[\]\{\}]", t) + for type in type_list: + names = type.split(".") + first_name = names[0] + if first_name in self.import_names_map.keys(): + self.import_names_map.pop(first_name) + + def check_import_itself(self, t: ast.ImportStmt, abs_path: str) -> None: + assert isinstance(t, ast.ImportStmt) + if os.path.isdir(abs_path): + return + abs_path += kcl_info.KCL_FILE_SUFFIX + # normpath: a/./b -> a/b + if abs_path == str(os.path.normpath(self.module.filename)): + self.msgs.append( + message.Message( + "E0406", + self.module.filename, + MSGS["E0406"][0], + utils.get_source_code(self.module.filename, t.line, self.code), + (t.line, t.column), + [], + ) + ) + + def check_reimport(self, t: ast.ImportStmt, abs_path: str) -> None: + assert isinstance(t, ast.ImportStmt) + if abs_path in self.has_imported_modules: + self.msgs.append( + message.Message( + "W0404", + self.module.filename, + MSGS["W0404"][0] % t.pkg_name, + utils.get_source_code(self.module.filename, t.line, self.code), + (t.line, t.column), + [t.pkg_name], + ) + ) + else: + self.import_names_map[t.pkg_name] = t + self.has_imported_modules.append(abs_path) + + def generic_walk(self, t: ast.AST) -> None: + """Called if no explicit walker function exists for a node.""" + self.check_import_position(t) + for field, value in ast.iter_fields(t): + if isinstance(value, list): + for v in value: + # IfStmt.elif_body: List[List[Stmt]] + if isinstance(v, list): + for v1 in v: + self.walk(v1) + if isinstance(v, ast.AST): + self.walk(v) + elif isinstance(value, ast.AST): + self.walk(value) + + def walk_Identifier(self, t: ast.Identifier) -> None: + assert isinstance(t, ast.Identifier) + self.check_unused_import(t) + + def walk_SchemaAttr(self, t: ast.SchemaAttr) -> None: + assert isinstance(t, ast.SchemaAttr) + self.check_unused_import(t.type_str) + self.generic_walk(t) + + def walk_ImportStmt(self, t: ast.ImportStmt) -> None: + assert isinstance(t, ast.ImportStmt) + self.check_import_position(t) + if ( + t.path.startswith(plugin.PLUGIN_MODULE_NAME) + or t.name in builtin.STANDARD_SYSTEM_MODULES + ): + self.check_reimport(t, t.path) + return + fix_path = vfs.FixImportPath(self.root, self.module.filename, t.path).replace( + ".", "/" + ) + abs_path = os.path.join(self.root, fix_path) + if self.check_import_file_exist(t, abs_path): + self.check_import_itself(t, abs_path) + self.check_reimport(t, abs_path) diff --git a/internal/kclvm_py/tools/lint/checkers/misc.py b/internal/kclvm_py/tools/lint/checkers/misc.py new file mode 100644 index 000000000..709a86bfc --- /dev/null +++ b/internal/kclvm_py/tools/lint/checkers/misc.py @@ -0,0 +1,66 @@ +from typing import List + +import kclvm.kcl.ast.ast as ast +import kclvm.tools.lint.message.message as message +from kclvm.tools.lint.checkers.base_checker import BaseChecker + +MSGS = { + "E0501": ( + "line too long (%d > %d characters).", + "Line too long.", + "line too long ('{0}' > '{1}' characters).", + ) +} + + +class MiscChecker(BaseChecker): + def __init__(self, linter) -> None: + super().__init__(linter) + self.name = "MiscCheck" + self.code = None + self.MSGS = MSGS + self.module = None + self.prog = None + self.work_dir = None + self.root: str = None + + def reset(self) -> None: + self.msgs.clear() + self.code = None + self.module = None + self.root = None + + def get_module(self, prog: ast.Program, code: str) -> None: + assert isinstance(prog, ast.Program) + assert code is not None + self.prog = prog + self.module = prog.pkgs["__main__"][0] + self.code = code + self.root: str = prog.root + + def check(self, prog: ast.Program, code: str) -> None: + assert isinstance(prog, ast.Program) + assert code is not None + self.reset() + self.get_module(prog, code) + code_lines = self.code.split("\n") + self.check_line_too_long(code_lines) + + # todo: check in ast instead of code or file + def check_line_too_long(self, code_lines: List[str]) -> None: + for i, v in enumerate(code_lines): + if len(v) > self.options.max_line_length: + self.msgs.append( + message.Message( + "E0501", + self.module.filename, + MSGS["E0501"][0] + % ( + len(v), + self.options.max_line_length, + ), + v.strip(), + (i + 1, 1), + [len(v), self.options.max_line_length], + ) + ) diff --git a/internal/kclvm_py/tools/lint/lint/KCLLint.py b/internal/kclvm_py/tools/lint/lint/KCLLint.py new file mode 100644 index 000000000..f1820d2b5 --- /dev/null +++ b/internal/kclvm_py/tools/lint/lint/KCLLint.py @@ -0,0 +1,334 @@ +""" +KCLLinter class controls all inspection processes of lint: loading config, checking and generating reports. + +The workflow of KCLLinter is as follows: +1. Load config. +2. Find all KCL files under the 'path' from CLI arguments, and parse them to ast.Program. +3. Register checker and reporter according to config +4. Distribute ast to each checker for checking, and generate Message,which represents the result of check. +5. Linter collects Messages from all checkers. +6. Distribute Message to each reporter as output +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ KCLLinter │ +│ │ +│ ┌───────────┐ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ KCL file │ │ Checker │ │ +│ └───────────┘ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ +│ ↓ │ │ importChecker │ │ schemaChecker │ │ ... │ │ │ +│ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ +│ │ ast.Prog │ → │ │ │ Message │ │ │ │ Message │ │ │ │ Message │ │ │ │ +│ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │ │ +│ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ +│ │ │ │ Message │ │ │ │ Message │ │ │ │ Message │ │ │ │ +│ │ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │ │ +│ ┌──────────────────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ +│ │ Config │ │ │ │ ... │ │ │ │ ... │ │ │ │ ... │ │ │ │ +│ │ │ │ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │ │ +│ │ 1 config │ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ +│ │ 2 .kcllint │ └─────────────────────────────────────────────────────────────────┘ │ +│ │ 3 default_config │ │ +│ │ │ ↓ │ +│ │ │ msgs_map -> MessageID: count │ +│ └──────────────────────┘ msgs -> ┌────────────────────────────────────────────────────┐ │ +│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ +│ │ │ Message │ │ Message │ │ Message │ │ │ +│ │ └───────────┘ └───────────┘ └───────────┘ │ │ +│ └────────────────────────────────────────────────────┘ │ +│ │ +│ ↓ │ +│ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ Reporter │ │ +│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ +│ │ │ stdout │ │ sarif │ │ file │ │ ... │ │ │ +│ │ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────┘ │ +│ │ +│ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +""" +import os +import glob +import ruamel.yaml as yaml +from typing import Dict, Any, Optional, List + +import kclvm.compiler.parser.parser as parser +from kclvm.tools.lint.checkers import * +import kclvm.tools.lint.reporters.base_reporter as base_reporter +import kclvm.tools.lint.reporters.stdout_reporter as stdout_reporter +import kclvm.tools.lint.reporters.file_reporter as file_reporter +import kclvm.tools.lint.reporters.sarif_reporter as sarif_reporter +import kclvm.kcl.error.kcl_error as error +import kclvm.tools.lint.message.message as message +import kclvm.tools.lint.lint.exceptions as exceptions +import kclvm.kcl.info as kcl_info +import kclvm.kcl.ast as ast + +LINT_CONFIG_SUFFIX = ".kcllint" +DEFAULT_CONFIG = { + "check_list": ["import", "misc", "basic"], + "ignore": [], + "max_line_length": 200, + "output": ["stdout"], + "output_path": None, + "module_naming_style": "ANY", + "package_naming_style": "ANY", + "schema_naming_style": "PascalCase", + "mixin_naming_style": "PascalCase", + "protocol_naming_style": "PascalCase", + "argument_naming_style": "camelCase", + "variable_naming_style": "ANY", + "schema_attribute_naming_style": "ANY", + "module_rgx": None, + "package_rgx": None, + "schema_rgx": None, + "mixin_rgx": None, + "protocol_rgx": None, + "argument_rgx": None, + "variable_rgx": None, + "schema_attribute_rgx": None, + "bad_names": ["foo", "bar", "baz", "toto", "tata", "tutu", "I", "l", "O"], +} + +MSGS = {"E0999": ("Parse failed:%s.", "Parse failed.", "Parse failed:'{0}'.")} +PARSE_FAILED_MSG_ID = "E0999" + + +class CheckerFactory: + @staticmethod + def get_checker(checker: str, linter=None) -> Optional[base_checker.BaseChecker]: + if checker == "import": + return imports.ImportsChecker(linter) + elif checker == "misc": + return misc.MiscChecker(linter) + elif checker == "basic": + return basic.BasicChecker(linter) + else: + raise exceptions.InvalidCheckerError(checker) + + +class ReporterFactory: + @staticmethod + def get_reporter( + reporter: str, linter=None + ) -> Optional[base_reporter.BaseReporter]: + if reporter == "stdout": + return stdout_reporter.STDOUTReporter(linter) + elif reporter == "file": + return file_reporter.FileReporter(linter) + elif reporter == "sarif": + return sarif_reporter.SARIFReporter(linter) + else: + raise exceptions.InvalidReporterError(reporter) + + +class LinterConfig: + def __init__(self): + self.check_list = ["import", "misc", "basic"] + self.ignore = [] + self.max_line_length = 200 + self.output = ["stdout"] + self.output_path = None + self.module_naming_style = "ANY" + self.package_naming_style = "ANY" + self.schema_naming_style = "PascalCase" + self.mixin_naming_style = "PascalCase" + self.protocol_naming_style = "PascalCase" + self.argument_naming_style = "camelCase" + self.variable_naming_style = "ANY" + self.schema_attribute_naming_style = "ANY" + self.module_rgx = None + self.package_rgx = None + self.schema_rgx = None + self.mixin_rgx = None + self.protocol_rgx = None + self.argument_rgx = None + self.variable_rgx = None + self.schema_attribute_rgx = None + self.bad_names = ["foo", "bar", "baz", "toto", "tata", "tutu", "I", "l", "O"] + + def update(self, config: {}): + for k, v in config.items(): + if hasattr(self, k): + self.__setattr__(k, v) + + +class KCLLinter: + def __init__( + self, + *path: str, + config: Dict[str, Any] = None, + k_code_list: List[str] = None, + ) -> None: + self.path = path or [] + self.k_code_list = k_code_list or [] + self.file_list = [] + self.programs_list = [] + self.checkers = [] + self.reporters = [] + self.config = LinterConfig() + self.msgs = [] + self.MSGS = MSGS + self.msgs_map = {} + + path_list = [x for x in path] + for i, s in enumerate(path_list): + s = os.path.abspath(s) + if os.path.isfile(s): + self.file_list.append(s) + elif os.path.isdir(s): + self.file_list += glob.glob( + os.path.join(s, "**", kcl_info.KCL_FILE_PATTERN), + recursive=True, + ) + else: + raise FileNotFoundError(s) + + self._load_config(config) + + def reset(self): + self.reporters.clear() + self.checkers.clear() + self.MSGS = MSGS + self.msgs.clear() + self.msgs_map = {} + + def run(self) -> None: + self.reset() + self._register_checkers(self.config.check_list) + self._register_reporters(self.config.output) + self._get_programs(self.file_list, self.k_code_list) + self._check(self.programs_list, self.checkers, self.k_code_list) + self._display() + + def check(self) -> None: + self.reset() + self._register_checkers(self.config.check_list) + self._register_reporters(self.config.output) + self._get_programs(self.file_list, self.k_code_list) + self._check(self.programs_list, self.checkers, self.k_code_list) + + def _load_config(self, config) -> None: + for s in self.path: + if os.path.isfile(s): + kcllint_path = os.path.join( + os.path.abspath(os.path.dirname(s)), LINT_CONFIG_SUFFIX + ) + elif os.path.isdir(s): + kcllint_path = os.path.join(os.path.abspath(s), LINT_CONFIG_SUFFIX) + if os.path.isfile(kcllint_path): + with open(kcllint_path, "r", encoding="utf-8") as f: + kcllint_config = f.read() + self.config.update(yaml.safe_load(kcllint_config)) + break + if config: + self.config.update(config) + + def _register_checker(self, checker: base_checker.BaseChecker) -> None: + self.checkers.append(checker) + if hasattr(checker, "MSGS"): + self.MSGS.update(checker.MSGS) + + def _register_checkers(self, checkers: List[str]) -> None: + factory = CheckerFactory() + for s in checkers: + self._register_checker(factory.get_checker(s, self)) + + def _register_reporters(self, reporters: List[str]) -> None: + if not reporters or len(reporters) == 0: + raise exceptions.EmptyReporterError + if "file" in reporters or "sarif" in reporters: + assert self.config.output_path, "Without ouput file path" + factory = ReporterFactory() + self.reporters = [factory.get_reporter(s, self) for s in reporters] + + def _get_programs( + self, file_list: List[str], k_code_list: List[str] + ) -> List[ast.Program]: + for i, file in enumerate(file_list): + _code = k_code_list[i] if (i < len(k_code_list)) else None + _k_code_list = [_code] if _code else None + try: + prog = parser.LoadProgram(file, k_code_list=_k_code_list) + self.programs_list.append(prog) + except error.KCLException as err: + if not _code: + with open(err.filename) as f: + _code = f.read() + source_line = _code.split("\n")[err.lineno - 1] + msg = message.Message( + PARSE_FAILED_MSG_ID, + err.filename, + MSGS[PARSE_FAILED_MSG_ID][0] % err.name, + source_line, + (err.lineno, err.colno), + [err.name], + ) + if (msg not in self.msgs) and ( + PARSE_FAILED_MSG_ID not in self.config.ignore + ): + self.msgs.append(msg) + self.msgs_map[PARSE_FAILED_MSG_ID] = ( + self.msgs_map.setdefault(PARSE_FAILED_MSG_ID, 0) + 1 + ) + + def _check( + self, + progs: List[ast.Program], + checkers: List[base_checker.BaseChecker], + k_code_list: List[str], + ): + for i, prog in enumerate(progs): + if i < len(k_code_list): + _code = k_code_list[i] + else: + module = prog.pkgs["__main__"][0] + with open(module.filename) as f: + _code = f.read() + for checker in checkers: + checker.check(prog, _code) + # collect msgs to linter + for msg in checker.msgs: + if msg.msg_id in self.config.ignore: + continue + if msg not in self.msgs: + self.msgs.append(msg) + self.msgs_map[msg.msg_id] = ( + self.msgs_map.setdefault(msg.msg_id, 0) + 1 + ) + + def _display(self) -> None: + for reporter in self.reporters: + reporter.display() + + +def kcl_lint(*path: str, config: Dict[str, Any] = None) -> List[message.Message]: + """ + Check kcl files or all kcl files in dirs + :param path: str, path of a kcl file or dir + :param config: Dict[str, Any], config of lint + :return: List[Message] result of lint check + """ + lint = KCLLinter(*path, config=config) + lint.check() + return lint.msgs + + +def kcl_lint_code( + *path: str, + k_code_list: List[str], + config: Dict[str, Any] = None, +) -> List[message.Message]: + """ + Check individual code of string type or some code in kcl file + if pararm:file not None, file must be a path of kcl file and code should be part of the file, + .e.g select some code for checking in ide + :param k_code_list: code of string type or some code in kcl file + :param config: Dict[str, Any], config of lint + :param path: path of kcl file + :return: List[Message] result of lint check + """ + assert len(k_code_list) > 0 + lint = KCLLinter(*path, config=config, k_code_list=k_code_list) + lint.check() + return lint.msgs diff --git a/internal/kclvm_py/tools/lint/lint/__init__.py b/internal/kclvm_py/tools/lint/lint/__init__.py new file mode 100644 index 000000000..ecac76d94 --- /dev/null +++ b/internal/kclvm_py/tools/lint/lint/__init__.py @@ -0,0 +1,3 @@ +from .KCLLint import KCLLinter, kcl_lint, kcl_lint_code + +__all__ = ["KCLLinter", "kcl_lint", "kcl_lint_code"] diff --git a/internal/kclvm_py/tools/lint/lint/exceptions.py b/internal/kclvm_py/tools/lint/lint/exceptions.py new file mode 100644 index 000000000..89a717dfa --- /dev/null +++ b/internal/kclvm_py/tools/lint/lint/exceptions.py @@ -0,0 +1,25 @@ +class InvalidCheckerError(Exception): + """raised when selected reporter is invalid (e.g. not found)""" + + def __init__(self, checker: str): + self.checker_name = checker + + def __str__(self): + return f"Args wrong, checker {self.checker_name} not found" + + +class EmptyReporterError(Exception): + """raised when reporter list is empty and so should not be displayed""" + + def __str__(self): + return "Without output reporter" + + +class InvalidReporterError(Exception): + """raised when selected reporter is invalid (e.g. not found)""" + + def __init__(self, reporter: str): + self.reporter_name = reporter + + def __str__(self): + return f"Args wrong, reporter {self.reporter_name} not found" diff --git a/internal/kclvm_py/tools/lint/lint/utils.py b/internal/kclvm_py/tools/lint/lint/utils.py new file mode 100644 index 000000000..e9a854ba3 --- /dev/null +++ b/internal/kclvm_py/tools/lint/lint/utils.py @@ -0,0 +1,39 @@ +import os +from io import StringIO +from typing import Union, Optional +import pathlib + +import kclvm.kcl.ast.ast as ast +import kclvm.tools.printer.printer as printer +import kclvm.kcl.info as kcl_info + + +def get_source_code( + file: Optional[Union[str, pathlib.PosixPath]], line: int, code: Optional[str] = None +) -> str: + if code: + lines = code.split("\n") + assert line <= len(lines) + source_line = lines[line - 1] + else: + _file = str(file) + assert is_kcl_file(_file) + assert line > 0 + with open(_file, "r", encoding="utf8") as source_file: + lines = source_file.read().split("\n") + assert line <= len(lines) + source_line = lines[line - 1] + return source_line + + +def is_kcl_file(file: str) -> bool: + assert isinstance(file, str) + return os.path.isfile(file) and file.endswith(kcl_info.KCL_FILE_SUFFIX) + + +def get_code_from_module(module: ast.Module) -> str: + assert isinstance(module, ast.Module) + with StringIO() as IO: + printer.PrintAST(module, IO) + code = IO.getvalue() + return code diff --git a/internal/kclvm_py/tools/lint/message/message.py b/internal/kclvm_py/tools/lint/message/message.py new file mode 100644 index 000000000..2b1a8ee0a --- /dev/null +++ b/internal/kclvm_py/tools/lint/message/message.py @@ -0,0 +1,49 @@ +import pathlib +from typing import Tuple, Union, Optional, List, Any + + +class Message: + """This class represent a message to be issued by the reporters""" + + def __init__( + self, + msg_id: str, + file: Optional[Union[str, pathlib.PosixPath]], + msg: str, + source_code: str, + pos: Tuple[int, int], + arguments: List[Any], + ): + self.msg_id = msg_id + self.level = msg_id[0] + self.file = str(file) if file else None + self.msg = msg + self.source_code = source_code + self.pos = pos + self.arguments = [str(arg) for arg in arguments] + + def __str__(self): + return ( + f"{self.file}:{self.pos[0]}:{self.pos[1]}: {self.msg_id} {self.msg}\n" + + self.source_code + + "\n" + + (self.pos[1] - 1) * " " + + "^" + ) + + def __eq__(self, other): + return ( + self.msg_id, + self.file, + self.msg, + self.source_code, + self.pos, + self.arguments, + ) == ( + other.msg_id, + other.file, + other.msg, + other.source_code, + other.pos, + other.arguments, + ) diff --git a/internal/kclvm_py/tools/lint/reporters/base_reporter.py b/internal/kclvm_py/tools/lint/reporters/base_reporter.py new file mode 100644 index 000000000..5ae374f98 --- /dev/null +++ b/internal/kclvm_py/tools/lint/reporters/base_reporter.py @@ -0,0 +1,35 @@ +from typing import List + +from kclvm.tools.lint.message.message import Message + + +class BaseReporter: + name: str = "" + """base class for reporters""" + + def __init__(self, linter, output=None, encoding=None): + self.linter = linter + self.out = None + self.out_encoding = None + self.set_output(output, encoding) + + def __eq__(self, other): + return self.name == other.name and self.linter == other.linter + + def set_output(self, output=None, encoding="utf-8"): + """ + set output stream + todo: + The output property is not used yet, and replaced by open_output_stream. + When reporter display the result, the 'open_output_stream' function is + called to open the output stream. + """ + self.out = output + self.out_encoding = encoding + + def display(self): + self.print_msg(self.linter.msgs) + + def print_msg(self, msgs: List[Message] = None): + """Should be overridden by subclass""" + raise NotImplementedError() diff --git a/internal/kclvm_py/tools/lint/reporters/file_reporter.py b/internal/kclvm_py/tools/lint/reporters/file_reporter.py new file mode 100644 index 000000000..5f7e279d4 --- /dev/null +++ b/internal/kclvm_py/tools/lint/reporters/file_reporter.py @@ -0,0 +1,26 @@ +import sys +from typing import List + +from kclvm.tools.lint.reporters.base_reporter import BaseReporter +from kclvm.tools.lint.message.message import Message + + +class FileReporter(BaseReporter): + def __init__(self, linter, output=None, encoding=None): + self.name = "file_reporter" + self.output_file = linter.config.output_path + super().__init__(linter, output, encoding) + + def print_msg(self, msgs: List[Message] = None): + assert self.output_file + with open(self.output_file, "w") as f: + current = sys.stdout + sys.stdout = f + for msg in msgs: + print(msg) + print() + print("Check total {} files:".format(len(self.linter.file_list))) + for k, v in self.linter.msgs_map.items(): + print("{:<8}{}: {}".format(v, k, self.linter.MSGS[k][1])) + print(f"KCL Lint: {len(self.linter.msgs)} problems") + sys.stdout = current diff --git a/internal/kclvm_py/tools/lint/reporters/sarif_reporter.py b/internal/kclvm_py/tools/lint/reporters/sarif_reporter.py new file mode 100644 index 000000000..02f624bb2 --- /dev/null +++ b/internal/kclvm_py/tools/lint/reporters/sarif_reporter.py @@ -0,0 +1,81 @@ +import sys +from typing import List, Dict, Tuple +import json +from kclvm.tools.lint.reporters.base_reporter import BaseReporter +from kclvm.tools.lint.message.message import Message + + +LEVEL_MAP = {"E": "error", "W": "waring", "C": "note"} + + +class SARIFMeta: + VERSION = "2.1.0" + SCHEMA = "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cs01/schemas/sarif-schema-2.1.0.json" + NAME = "kcl-lint" + KCLLINT_VERSION = "0.0.1" + INFORMATION_URI = "https://kusion-docs.com/docs/reference/cli/kcl/lint" + + +class Rule: + def __init__(self, id: str, default: str, short: str): + self.id = id + self.messageStrings = { + "default": {"text": default}, + "shortStrings": {"text": short}, + } + + +class Result: + def __init__(self, m: Message): + self.ruleId = m.msg_id + self.level = LEVEL_MAP[m.msg_id[0]] if (m.msg_id[0] in LEVEL_MAP) else "note" + self.message = {"id": "default", "arguments": m.arguments} + self.locations = [ + { + "physicalLocation": { + "artifactLocation": {"uri": m.file}, + "region": {"startLine": m.pos[0], "startColumn": m.pos[1]}, + } + } + ] + + +class Tool: + def __init__(self, ids: List[str], MSGS: Dict[str, Tuple[str, str]]): + self.driver = { + "name": SARIFMeta.NAME, + "version": SARIFMeta.KCLLINT_VERSION, + "informationUri": SARIFMeta.INFORMATION_URI, + "rules": [Rule(id, MSGS[id][2], MSGS[id][1]) for id in ids], + } + + +class SarifLog(object): + """Static Analysis Results Format (SARIF) Version 2.1.0 JSON Schema: a standard format for the output of static analysis tools.""" + + def __init__(self, msgs: List[Message], MSGS: Dict[str, Tuple[str, str]]): + self.version = SARIFMeta.VERSION + # self.$schema = SARIFMeta.SCHEMA + msg_ids = list(set([m.msg_id for m in msgs])) + self.runs = [ + {"tool": Tool(msg_ids, MSGS), "results": [Result(m) for m in msgs]} + ] + + +class SARIFReporter(BaseReporter): + def __init__(self, linter, output=None, encoding=None): + self.name = "sarif_reporter" + self.output_file = linter.config.output_path + super().__init__(linter, output, encoding) + + def print_msg(self, msgs: List[Message] = None): + assert self.output_file + sarif_log = SarifLog(msgs, self.linter.MSGS) + json_str = json.dumps( + sarif_log, default=lambda o: o.__dict__, sort_keys=True, indent=2 + ) + with open(self.output_file, "w") as f: + current = sys.stdout + sys.stdout = f + print(json_str) + sys.stdout = current diff --git a/internal/kclvm_py/tools/lint/reporters/stdout_reporter.py b/internal/kclvm_py/tools/lint/reporters/stdout_reporter.py new file mode 100644 index 000000000..302001fff --- /dev/null +++ b/internal/kclvm_py/tools/lint/reporters/stdout_reporter.py @@ -0,0 +1,78 @@ +import sys +from typing import List + +from kclvm.tools.lint.reporters.base_reporter import BaseReporter +from kclvm.tools.lint.message.message import Message + +LINT_THEME: dict = { + "ID": "\033[0;92m{}\033[0m", # green + "ERROR": "\033[0;91m{}\033[0m", # red + "WARNING": "\033[0;93m{}\033[0m", # yellow + "FILE_NAME": "\033[0;94m{}\033[0m", # blue + "LINE_COLUMN": "\033[0;96m{}\033[0m", # cyan + "MARK": "\033[0;31m{}\033[0m", # red + "NUMBER": "\033[0;31m{}\033[0m", # red +} + + +def color(content: str, content_type: str): + return LINT_THEME[content_type].format(content) + + +def msg_with_color(msg: Message): + return ( + color(msg.file, "FILE_NAME") + + ":" + + color(msg.pos[0], "LINE_COLUMN") + + ":" + + color(msg.pos[1], "LINE_COLUMN") + + ": " + + color(msg.msg_id, "ID") + + " " + + msg.msg + + "\n" + + msg.source_code + + "\n" + + (msg.pos[1] - 1) * " " + + color("^", "MARK") + ) + + +class STDOUTReporter(BaseReporter): + def __init__(self, linter, output=None, encoding=None): + self.name = "stdout_reporter" + super().__init__(linter, output, encoding) + + def print_msg(self, msgs: List[Message], file=sys.stdout): + """ + Print msgs with color.Because CI cannot parse color information, e.g. [0;31m{, + it is not enabled temporarily + + for msg in msgs: + print((msg_with_color(msg) if file.isatty() else str(msg)) + "\n") + print( + "Check total " + + ( + color(len(self.linter.file_list), "NUMBER") + if file.isatty() + else str(len(self.linter.file_list)) + ) + + " files:" + ) + for k, v in self.linter.msgs_map.items(): + print(("{:<19}".format(color(v, "NUMBER")) + + color(k, "ID") + + ": " + + self.linter.MSGS[k][1] + ) if file.isatty() else ("{:<8}{}: {}".format(v, k, self.linter.MSGS[k][1]))) + print("KCL Lint: " + + (color(len(self.linter.msgs), "NUMBER") if file.isatty() else str(len(self.linter.msgs))) + + " problems") + """ + for msg in msgs: + print(msg) + print() + print(f"Check total {len(self.linter.file_list)} files:") + for k, v in self.linter.msgs_map.items(): + print("{:<8}{}: {}".format(v, k, self.linter.MSGS[k][1])) + print(f"KCL Lint: {len(self.linter.msgs)} problems") diff --git a/internal/kclvm_py/tools/list_attribute/schema.py b/internal/kclvm_py/tools/list_attribute/schema.py new file mode 100644 index 000000000..034dc3f1b --- /dev/null +++ b/internal/kclvm_py/tools/list_attribute/schema.py @@ -0,0 +1,205 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import io + +from typing import List, Dict, Set + +import kclvm.compiler.parser as parser +import kclvm.api.object as objpkg +import kclvm.kcl.ast as ast +import kclvm.kcl.types as types +import kclvm.internal.gpyrpc.gpyrpc_pb2 as pb2 +import kclvm.tools.printer as printer + + +def get_schema_type_from_code( + file: str, code: str, schema_name: str = None +) -> List[pb2.KclType]: + """Get schema types from a kcl file or code. + + Parameters + ---------- + file: str + The kcl filename + code: str + The kcl code string + schema_name: str + The schema name got, when the schema name is empty, all schemas are returned. + + Returns + ------- + schema_types: List[pb2.KclType] + + KclType: + string type = 1; // schema, dict, list, str, int, float, bool, int(), float() str(), bool() + repeated KclType union_types = 2 ; // union types + string default = 3; // default value + + string schema_name = 4; // schema name + string schema_doc = 5; // schema doc + map properties = 6; // schema properties + repeated string required = 7; // required schema properties, [property_name1, property_name2] + + KclType key = 8; // dict key type + KclType item = 9; // dict/list item type + + int32 line = 10; + """ + result = [] + program = parser.LoadProgram( + file or "", k_code_list=[code] if code else None + ) + for name, o in types.ResolveProgram(program).main_scope.elems.items(): + if isinstance(o.type, objpkg.KCLSchemaDefTypeObject): + if not schema_name or name == schema_name: + result.append(kcl_type_obj_to_pb_kcl_type(o.type)) + return result + + +def kcl_type_obj_to_pb_kcl_type(tpe: types.Type) -> pb2.KclType: + """any, schema, dict, list, str, int, float, bool, int(), float() str(), bool(), union""" + if isinstance(tpe, objpkg.KCLSchemaDefTypeObject): + return kcl_type_obj_to_pb_kcl_type(tpe.schema_type) + elif isinstance(tpe, objpkg.KCLSchemaTypeObject): + return schema_type_obj_to_pb_kcl_type(tpe) + elif isinstance(tpe, objpkg.KCLAnyTypeObject): + return pb2.KclType( + type="any", + ) + elif isinstance(tpe, objpkg.KCLDictTypeObject): + return pb2.KclType( + type="dict", + key=kcl_type_obj_to_pb_kcl_type(tpe.key_type), + item=kcl_type_obj_to_pb_kcl_type(tpe.value_type), + ) + elif isinstance(tpe, objpkg.KCLListTypeObject): + return pb2.KclType( + type="list", + item=kcl_type_obj_to_pb_kcl_type(tpe.item_type), + ) + elif isinstance( + tpe, + ( + objpkg.KCLIntTypeObject, + objpkg.KCLFloatTypeObject, + objpkg.KCLStringTypeObject, + objpkg.KCLBoolTypeObject, + objpkg.KCLIntLitTypeObject, + objpkg.KCLFloatLitTypeObject, + objpkg.KCLStringLitTypeObject, + objpkg.KCLBoolLitTypeObject, + ), + ): + return pb2.KclType( + type=tpe.type_str(), + ) + elif isinstance(tpe, objpkg.KCLUnionTypeObject): + return pb2.KclType( + type="union", + union_types=[kcl_type_obj_to_pb_kcl_type(t) for t in tpe.types], + ) + return ( + pb2.KclType( + type=tpe.type_str(), + ) + if isinstance(tpe, types.Type) + else None + ) + + +def schema_type_obj_to_pb_kcl_type( + schema_type_obj: objpkg.KCLSchemaTypeObject, +) -> pb2.KclType: + """Convert the schema type object to the protobuf kcl type.""" + return pb2.KclType( + type="schema", + schema_name=schema_type_obj.name, + schema_doc=schema_type_obj.doc, + properties=get_schema_type_obj_properties(schema_type_obj), + decorators=[ + kcl_decorator_to_pb_decorator(decorator) + for decorator in schema_type_obj.node_ref.decorators or [] + ], + required=list(sorted(get_schema_type_obj_required_attributes(schema_type_obj))), + ) + + +def get_schema_type_obj_properties( + schema_type_obj: objpkg.KCLSchemaTypeObject, +) -> Dict[str, pb2.KclType]: + """Get schema properties from a schema type object""" + result_map = {} + base_result_map = ( + get_schema_type_obj_properties(schema_type_obj.base) + if schema_type_obj.base + else {} + ) + for attr, attr_obj in schema_type_obj.attr_obj_map.items(): + if attr != objpkg.SCHEMA_SETTINGS_ATTR_NAME: + type_node = attr_obj.attr_type + result_map[attr] = kcl_type_obj_to_pb_kcl_type(type_node) + result_map[attr].default = ( + value_to_string(attr_obj.attr_node.value) + if attr_obj.attr_node.value + else "" + ) + result_map[attr].decorators.extend( + [ + kcl_decorator_to_pb_decorator(decorator) + for decorator in attr_obj.attr_node.decorators + ] + if isinstance(attr_obj.attr_node, ast.SchemaAttr) + else [] + ) + base_result_map.update(result_map) + line = 1 + for k in base_result_map: + base_result_map[k].line = line + line += 1 + return base_result_map + + +def get_schema_type_obj_required_attributes( + schema_type_obj: objpkg.KCLSchemaTypeObject, +) -> Set[str]: + """Get the required attributes from the schema type object""" + base_attribute_set = ( + get_schema_type_obj_required_attributes(schema_type_obj.base) + if schema_type_obj.base + else set() + ) + required_attribute_set = { + attr + for attr, attr_obj in schema_type_obj.attr_obj_map.items() + if attr != objpkg.SCHEMA_SETTINGS_ATTR_NAME and not attr_obj.is_optional + } + required_attribute_set.update(base_attribute_set) + return required_attribute_set + + +def kcl_decorator_to_pb_decorator(node: ast.Decorator) -> pb2.Decorator: + """Convert the decorator node to the protobuf decorator type.""" + + return pb2.Decorator( + name=node.name.get_name(), + arguments=[value_to_string(arg) for arg in node.args.args or [] if arg] + if node.args + else [], + keywords={ + keyword.arg.get_name(): value_to_string(keyword.value) + for keyword in node.args.keywords or [] + if keyword + } + if node.args + else {}, + ) + + +def value_to_string(node: ast.AST) -> str: + """AST value to string""" + if isinstance(node, ast.StringLit): + return node.value + else: + buffer = io.StringIO() + printer.PrintAST(node, out=buffer) + return buffer.getvalue() diff --git a/internal/kclvm_py/tools/list_attribute/utils.py b/internal/kclvm_py/tools/list_attribute/utils.py new file mode 100644 index 000000000..14ba360f8 --- /dev/null +++ b/internal/kclvm_py/tools/list_attribute/utils.py @@ -0,0 +1,285 @@ +import os +import glob +import pathlib +from io import StringIO +from typing import List, Dict, Optional +from dataclasses import dataclass + +import kclvm.compiler.parser.parser as parser +import kclvm.compiler.vfs as vfs +import kclvm.compiler.extension.plugin.plugin_model as plugin +import kclvm.compiler.extension.builtin.builtin as builtin +import kclvm.kcl.ast.ast as ast +import kclvm.kcl.info as kcl_info +import kclvm.tools.printer.printer as printer + + +@dataclass +class Config: + name_len: int = 30 + type_len: int = 30 + default_len: int = 30 + final_len: int = 10 + optional_len: int = 10 + + +def get_import_module( + module: ast.Module, result: Dict[str, ast.Module] = None +) -> Optional[Dict[str, ast.Module]]: + """Get all import module in a module.""" + if not module: + return None + assert isinstance(module, ast.Module) + if not result: + result = {} + import_file_list = [] + import_stmt_list = module.GetImportList() + work_dir = os.path.dirname(module.filename) + root: str = vfs.GetPkgRoot(work_dir) + if not root: + root = work_dir + for stmt in import_stmt_list or []: + if ( + stmt.path.startswith(plugin.PLUGIN_MODULE_NAME) + or stmt.name in builtin.STANDARD_SYSTEM_MODULES + ): + continue + # import_path to abs_path + fix_path = vfs.FixImportPath(root, module.filename, stmt.path).replace(".", "/") + abs_path = os.path.join(root, fix_path) + # Get all .k file if path is a folder + if os.path.isdir(abs_path): + file_glob = os.path.join(abs_path, "**", kcl_info.KCL_FILE_PATTERN) + import_file_list += glob.glob(file_glob, recursive=True) + else: + abs_path += kcl_info.KCL_FILE_SUFFIX + import_file_list.append(abs_path) + for file in import_file_list: + # Skip `_*.k` and `*_test.k` kcl files + if os.path.basename(file).startswith("_"): + continue + if file.endswith("_test.k"): + continue + if file not in result: + import_module = parser.ParseFile(file) + result[file] = import_module + if import_module.GetImportList(): + get_import_module(import_module, result) + return result + + +def get_import_schema( + module: ast.Module, +) -> Optional[Dict[ast.Module, List[ast.SchemaStmt]]]: + """Get all import schema in a module.""" + if not module: + return None + assert isinstance(module, ast.Module) + import_module_list = get_import_module(module).values() + import_schema_map = {m: m.GetSchemaList() for m in import_module_list} + return import_schema_map + + +class FullSchema(ast.SchemaStmt): + """ + Schema with base schema's attr. + todo: mixin attr + """ + + def __init__(self, schema: ast.SchemaStmt, module: ast.Module) -> None: + super().__init__(schema.line, schema.column) + self.self_schema = schema + self.parent_attr: Dict[str, List[ast.SchemaAttr]] = get_parent_attr_map( + schema, module, {} + ) + + def __str__(self): + s = self.self_schema.name + ", attr:[" + for name in self.self_schema.GetAttrNameList(): + s += f"{name}, " + s = s[:-2] + "]," + for p in self.parent_attr: + s += f" parent:{p}, attr:[" + for attr in self.parent_attr[p]: + s += f"{attr.name}, " + s = s[:-2] + "]," + + return s + + +def get_parent_attr_map( + ori_schema: ast.SchemaStmt, + module: ast.Module, + result: Dict[str, List[ast.SchemaAttr]] = None, +) -> Optional[Dict[str, List[ast.SchemaAttr]]]: + if not ori_schema or not module: + return None + assert isinstance(ori_schema, ast.SchemaStmt) + assert isinstance(module, ast.Module) + if not result: + result = {} + if not ori_schema.parent_name: + return result + else: + # Current module and schema. + full_schema_map: Dict[ast.Module, List[ast.SchemaStmt]] = { + module: module.GetSchemaList() + } + # Import module and schemas. + full_schema_map.update(get_import_schema(module)) + # key : module , value: List[ast.SchemaStmt] + for key, value in full_schema_map.items(): + for schema in value: + if schema.name == ori_schema.parent_name.get_name(): + result[schema.name] = schema.GetAttrList() + if schema.parent_name: + get_parent_attr_map(schema, key, result) + break + else: + continue + break + return result + + +def get_full_schema_list(module: ast.Module) -> List[FullSchema]: + """Get all FullSchema in a module""" + schema_list = module.GetSchemaList() + full_schema_list = [FullSchema(schema, module) for schema in schema_list] + return full_schema_list + + +class ListAttributePrinter: + def __init__(self, file: str = None, config: Config = Config()) -> None: + self.file = file + self.name_len = config.name_len + self.type_len = config.type_len + self.default_len = config.default_len + self.final_len = config.final_len + self.optional_len = config.optional_len + self.module = None + self.schema_list = None + self.import_schema_list = None + self.full_schema_list = None + + def build_full_schema_list(self): + self.module = parser.ParseFile(self.file) + self.schema_list = self.module.GetSchemaList() + self.import_schema_list = get_import_schema(self.module) + self.full_schema_list = get_full_schema_list(self.module) + + def print(self): + self.build_full_schema_list() + if self.module: + self.print_schema_list() + self.print_schema_structures() + + def print_schema_list(self): + print("------------ schema list ------------") + file_path = self.module.filename + file_name = pathlib.Path(file_path).name + print("Here are schemas defined in {}:".format(file_name)) + for schema in self.schema_list: + print("- " + schema.name) + print("Here are schemas imported to {}:".format(file_name)) + for key, value in self.import_schema_list.items(): + import_file_path = key.filename + import_file_name = pathlib.Path(import_file_path).name + if len(value) > 0: + print("imported from {}".format(import_file_name)) + for schema in value: + print("- " + schema.name) + + def print_schema_structures(self): + print("------------ schema structures ------------") + for full_schema in self.full_schema_list: + print("schema {}:".format(full_schema.self_schema.name)) + self.print_header() + for attr in full_schema.self_schema.GetAttrList(): + self.print_schema_attr(attr) + + for key, value in full_schema.parent_attr.items(): + print("attrs inherited from {}".format(key)) + for attr in value: + self.print_schema_attr(attr) + print() + + def _print_schema_attr(self, attr: ast.SchemaAttr, default: str): + print( + "{:<{}}{:<{}}{:<{}}{:<{}}{:<{}}".format( + # name + attr.name + if len(attr.name) <= self.name_len + else attr.name[: self.name_len - 3] + "...", + self.name_len, + # type + attr.type_str + if len(attr.type_str) <= self.type_len + else attr.type_str[: self.type_len - 3] + "...", + self.type_len, + # default + default, + self.default_len, + "", + self.final_len, + # is_optional + "" if attr.is_optional else "Required", + self.optional_len, + ) + ) + + def print_schema_attr(self, attr: ast.SchemaAttr): + if not attr: + return + assert isinstance(attr, ast.SchemaAttr) + if attr.value and isinstance(attr.value, ast.SchemaExpr): + """ + Because ast node SchemaExpr is too long to print, + when the default value of attr.value is a SchemaExpr,just print schema name,e.g.: + schema Name: + firstName : str + lastName : str + + schema Person: + name: Name = Name { + firstName = "hello" + lastName = "world" + } + ------------------------------------- + schema Person: + name type default ... + name Name -> Name{...} + """ + default = ( + attr.type_str + if len(attr.type_str) <= (self.default_len - 5) + else attr.type_str[: self.default_len - 5] + ) + "{...}" + self._print_schema_attr(attr, default) + return + with StringIO() as expr: + printer.PrintAST(attr.value, expr) + default_str = expr.getvalue() + if len(default_str) > self.default_len or ("\n" in default_str): + default_str = "..." + self._print_schema_attr(attr, default_str) + + def print_header(self): + print( + "{:<{}}{:<{}}{:<{}}{:<{}}{:<{}}".format( + # name + "name", + self.name_len, + # type + "type", + self.type_len, + # default + "default", + self.default_len, + # is_final + "is_final", + self.final_len, + # is_optional + "is_optional", + self.optional_len, + ) + ) diff --git a/internal/kclvm_py/tools/plugin/__init__.py b/internal/kclvm_py/tools/plugin/__init__.py new file mode 100644 index 000000000..d74aaf8ad --- /dev/null +++ b/internal/kclvm_py/tools/plugin/__init__.py @@ -0,0 +1 @@ +# Copyright 2021 The KCL Authors. All rights reserved. diff --git a/internal/kclvm_py/tools/printer/__init__.py b/internal/kclvm_py/tools/printer/__init__.py new file mode 100644 index 000000000..ffffacad3 --- /dev/null +++ b/internal/kclvm_py/tools/printer/__init__.py @@ -0,0 +1,9 @@ +from .printer import PrintAST, Config +from .splice import SchemaRuleCodeSnippet, splice_schema_with_rule + +__all__ = [ + "PrintAST", + "Config", + "SchemaRuleCodeSnippet", + "splice_schema_with_rule", +] diff --git a/internal/kclvm_py/tools/printer/printer.py b/internal/kclvm_py/tools/printer/printer.py new file mode 100644 index 000000000..3ee7b47e4 --- /dev/null +++ b/internal/kclvm_py/tools/printer/printer.py @@ -0,0 +1,1290 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import io +import re +import sys +from enum import IntEnum +from dataclasses import dataclass +from typing import List, Dict, Union, Tuple, Optional + +import kclvm.kcl.ast as ast +import kclvm.compiler.astutil.fix as fix + +# --------------------------------------------------- +# Constants +# --------------------------------------------------- + + +class Indentation(IntEnum): + Indent = 0 + Dedent = 1 + Newline = 2 + IndentWithNewline = 3 + DedentWithNewline = 4 + Fill = 5 + + +_INVALID_AST_MSG = "Invalid AST Node" +_TEMP_ROOT = "" + +WHITESPACE = " " +TAB = "\t" +NEWLINE = "\n" +COMMA_WHITESPACE = ast.TokenValue.COMMA + WHITESPACE +IDENTIFIER_REGEX = r"^\$?[a-zA-Z_]\w*$" +QUANT_OP_TOKEN_VAL_MAPPING = { + ast.QuantOperation.ALL: ast.TokenValue.ALL, + ast.QuantOperation.ANY: ast.TokenValue.ANY, + ast.QuantOperation.MAP: ast.TokenValue.MAP, + ast.QuantOperation.FILTER: ast.TokenValue.FILTER, +} + +# --------------------------------------------------- +# Printer config +# --------------------------------------------------- + + +@dataclass +class Config: + tab_len: int = 4 + indent_len: int = 4 + use_spaces: bool = True + is_fix: bool = False + + +# --------------------------------------------------- +# Printer +# --------------------------------------------------- + + +class BasePrinter(ast.TreeWalker): + def __init__(self, config: Config, out: io.TextIOBase): + self.output: str = "" + self.indent = 0 + self.cfg: Config = config + self.out: io.TextIOBase = out + # Print comments + self.last_ast_line: int = 0 + self.comments: List[ast.Comment] = [] + self.import_spec: Dict[str, str] = {} + + # Base walker functions + + def get_node_name(self, t: ast.AST): + """Get the ast.AST node name""" + assert isinstance(t, ast.AST) + return t.type + + def generic_walk(self, t: ast.AST): + """Called if no explicit walker function exists for a node.""" + if not isinstance(t, ast.AST): + raise Exception(_INVALID_AST_MSG, t) + else: + self.walk(t) + + def write_ast_comments(self, t: ast.AST): + if not t or not isinstance(t, ast.AST): + return + if t.line and t.line > self.last_ast_line: + self.last_ast_line = t.line + if self.comments: + index = -1 + for i, comment in enumerate(self.comments): + if comment.line <= t.line: + index = i + self.write(comment.text + NEWLINE) + self.fill() + else: + break + if index >= 0: + del self.comments[: index + 1] + + # ----------------- + # Expr and Stmt walker functions + # ----------------- + + def expr(self, expr: ast.Expr): + if not expr: + return + self.write_ast_comments(expr) + self.generic_walk(expr) + + def stmt(self, stmt: ast.Stmt): + if not stmt: + return + self.fill() + self.write_ast_comments(stmt) + self.generic_walk(stmt) + + def stmts(self, stmts: List[ast.Stmt]): + for stmt in stmts or []: + self.stmt(stmt) + + def exprs(self, exprs: List[ast.Expr]): + for expr in exprs or []: + self.expr(expr) + + # -------------------------- + # Indent and scope functions + # -------------------------- + + def enter(self): + self.indent += 1 + + def leave(self): + self.indent -= 1 + + # -------------------------- + # Write functions + # -------------------------- + + @staticmethod + def interleave(inter, f, seq): + """Call f on each item in seq, calling inter() in between.""" + if not seq: + return + seq = iter(seq) + try: + f(next(seq)) + except StopIteration: + pass + else: + for x in seq: + inter() + f(x) + + def write(self, text: str = ""): + self.out.write(text) + + def writeln(self, text: str = ""): + self.write(text + NEWLINE) + self.fill() + + def write_with_spaces(self, text: str = ""): + self.write(" " + text + " ") + + def fill(self, text: str = ""): + """Indent a piece of text, according to the current indentation level""" + if self.cfg.use_spaces: + self.write(WHITESPACE * self.indent * self.cfg.indent_len + text) + else: + self.write(TAB * self.indent + text) + + def print(self, *values): + for value in values or []: + if isinstance(value, ast.Expr): + self.expr(value) + elif isinstance(value, ast.Stmt): + self.stmt(value) + elif isinstance(value, ast.AST): + self.generic_walk(value) + elif value == Indentation.Indent: + self.enter() + elif value == Indentation.Dedent: + self.leave() + elif value == Indentation.Newline: + self.writeln() + elif value == Indentation.IndentWithNewline: + self.enter() + self.writeln() + elif value == Indentation.DedentWithNewline: + self.leave() + self.writeln() + elif value == Indentation.Fill: + self.fill() + elif isinstance(value, str): + self.write(value) + elif isinstance(value, (int, float, bool)): + self.write(str(value)) + + def print_ast(self, t: ast.AST): + if not t: + return + if isinstance(t, ast.Module) and self.cfg.is_fix: + fix.fix_and_get_module_import_list( + _TEMP_ROOT, t, is_fix=True, reversed=True + ) + self.walk(t) + for comment in self.comments or []: + self.write(comment.text + NEWLINE) + self.fill() + + +class Printer(BasePrinter): + def __init__(self, config: Config, out: io.TextIOBase): + super().__init__(config, out) + + def walk_Module(self, t: ast.Module): + """ast.AST: Module + + Parameters + ---------- + - body: List[Stmt] + """ + assert isinstance(t, ast.Module) + self.comments = t.comments + self.stmts(t.body) + + def walk_ExprStmt(self, t: ast.ExprStmt): + """ast.AST: ExprStmt + + Parameters + ---------- + - exprs: List[Expr] + """ + assert isinstance(t, ast.ExprStmt) + self.interleave(lambda: self.write(COMMA_WHITESPACE), self.expr, t.exprs) + self.writeln() + + def walk_AssertStmt(self, t: ast.AssertStmt): + """ast.AST: AssertStmt + + Parameters + ---------- + - test: Expr + - if_cond: Expr + - msg: Expr + """ + assert isinstance(t, ast.AssertStmt) and t.test + self.print( + ast.TokenValue.ASSERT, + WHITESPACE, + t.test, + ) + if t.if_cond: + self.print(WHITESPACE, ast.TokenValue.IF, WHITESPACE, t.if_cond) + if t.msg: + self.print( + COMMA_WHITESPACE, + t.msg, + ) + self.print(NEWLINE) + + def walk_IfStmt(self, t: ast.IfStmt): + """ast.AST: IfStmt + + Parameters + ---------- + - cond: Expr + - body: List[Stmt] + - elif_cond: List[Expr] + - elif_body: List[List[Stmt]] + - else_body: List[Stmt] + """ + assert isinstance(t, ast.IfStmt) + assert t.cond + assert t.body + self.print( + ast.TokenValue.IF, + WHITESPACE, + t.cond, + ast.TokenValue.COLON, + NEWLINE, + Indentation.Indent, + ) + self.stmts(t.body) + self.print(Indentation.Dedent) + if t.elif_cond: + for cond, body in zip(t.elif_cond, t.elif_body): + self.print( + ast.TokenValue.ELIF, + WHITESPACE, + cond, + ast.TokenValue.COLON, + NEWLINE, + Indentation.Indent, + ) + self.stmts(body) + self.print(Indentation.Dedent) + if t.else_body: + self.print( + ast.TokenValue.ELSE, + ast.TokenValue.COLON, + NEWLINE, + Indentation.Indent, + ) + self.stmts(t.else_body) + self.print(Indentation.Dedent) + self.print(NEWLINE) + + def walk_ImportStmt(self, t: ast.ImportStmt): + """ast.AST: ImportStmt + + Parameters + --------- + - path: str + - name: str + - asname: str + """ + assert isinstance(t, ast.ImportStmt) + assert t.pkg_name + self.print( + ast.TokenValue.IMPORT, + WHITESPACE, + t.path, + ) + if t.asname: + self.print( + WHITESPACE, + ast.TokenValue.AS, + WHITESPACE, + t.asname, + ) + self.import_spec[t.path] = t.pkg_name + self.writeln() + + def walk_SchemaStmt(self, t: ast.SchemaStmt): + """ast.AST: SchemaStmt + + Parameters + ---------- + - doc: str + - name: str + - parent_name: Identifier + - is_mixin: bool + - is_protocol: bool + - args: Arguments + - settings: dict + - mixins: List[str] + - body: List[Union[SchemaAttr, Stmt]] + - decorators: List[Decorator] + - checks: List[CheckExpr] + - for_host_name: Optional[Identifier] = None + - index_signature: Optional[SchemaIndexSignature] = None + """ + assert isinstance(t, ast.SchemaStmt) + self.exprs(t.decorators) + tok = ast.TokenValue.SCHEMA + if t.is_mixin: + tok = ast.TokenValue.MIXIN + elif t.is_protocol: + tok = ast.TokenValue.PROTOCOL + self.print( + tok, + WHITESPACE, + ) + self.print(t.name) + if t.args: + self.print( + ast.TokenValue.LEFT_BRACKETS, + t.args, + ast.TokenValue.RIGHT_BRACKETS, + ) + if t.parent_name: + self.print( + ast.TokenValue.LEFT_PARENTHESES, + t.parent_name, + ast.TokenValue.RIGHT_PARENTHESES, + ) + if t.for_host_name: + self.print( + WHITESPACE, + ast.TokenValue.FOR, + WHITESPACE, + t.for_host_name, + ) + self.print( + ast.TokenValue.COLON, + NEWLINE, + Indentation.Indent, # Schema Stmt indent + ) + if t.doc: + self.print( + Indentation.Fill, + '"""', + t.doc.replace('"', '\\"'), + '"""', + NEWLINE, + ) + if t.mixins: + self.print( + Indentation.Fill, + ast.TokenValue.MIXIN, + WHITESPACE, + ast.TokenValue.LEFT_BRACKETS, + Indentation.IndentWithNewline, + ) + self.interleave( + lambda: self.print(ast.TokenValue.COMMA, Indentation.Newline), + self.print, + t.mixins, + ) + self.print( + Indentation.Dedent, + Indentation.Newline, + ast.TokenValue.RIGHT_BRACKETS, + NEWLINE, + ) + if t.index_signature: + self.print(t.index_signature) + self.print(NEWLINE) + self.stmts(t.body) + self.write(NEWLINE) + if t.checks: + self.print( + Indentation.Fill, + ast.TokenValue.CHECK, + ast.TokenValue.COLON, + Indentation.IndentWithNewline, # Schema check indent + ) + self.interleave( + lambda: self.print(Indentation.Newline), + self.print, + t.checks, + ) + self.write(NEWLINE) + # Schema check dedent + self.print(Indentation.Dedent) + self.write(NEWLINE) + # Schema Stmt dedent + self.print(Indentation.Dedent) + + def walk_SchemaIndexSignature(self, t: ast.SchemaIndexSignature): + """ast.AST: SchemaIndexSignature + + Parameters + ---------- + - key_name: Optional[str] + - key_type: Optional[str] + - value_type: Optional[str] + - value: Optional[Expr] + - any_other: bool + - name_node: Optional[Name] + - value_type_node: Optional[Type] + """ + self.write(ast.TokenValue.LEFT_BRACKETS) + if t.any_other: + self.write("...") + if t.key_name: + self.print( + t.key_name, + ast.TokenValue.COLON, + WHITESPACE, + ) + self.print( + t.key_type, + ast.TokenValue.RIGHT_BRACKETS, + ast.TokenValue.COLON, + WHITESPACE, + t.value_type, + ) + if t.value: + self.print(WHITESPACE, ast.TokenValue.ASSIGN, WHITESPACE, t.value) + + def walk_RuleStmt(self, t: ast.RuleStmt): + """ast.AST: RuleStmt + + Parameters + ---------- + self.doc: str = "" + self.name: str = "" + self.parent_rules: List[Identifier] = [] + self.decorators: List[Decorator] = [] + self.checks: List[CheckExpr] = [] + self.name_node: Optional[Name] = None + self.args: Optional[Arguments] = None + self.for_host_name: Optional[Identifier] = None + """ + assert isinstance(t, ast.RuleStmt) + self.exprs(t.decorators) + self.print( + ast.TokenValue.RULE, + WHITESPACE, + ) + self.print(t.name) + if t.args: + self.print( + ast.TokenValue.LEFT_BRACKETS, + t.args, + ast.TokenValue.RIGHT_BRACKETS, + ) + if t.parent_rules: + self.print(ast.TokenValue.LEFT_PARENTHESES) + self.interleave( + lambda: self.print(ast.TokenValue.COMMA, WHITESPACE), + self.print, + t.parent_rules, + ) + self.print(ast.TokenValue.RIGHT_PARENTHESES) + if t.for_host_name: + self.print( + WHITESPACE, + ast.TokenValue.FOR, + WHITESPACE, + t.for_host_name, + ) + self.print( + ast.TokenValue.COLON, + Indentation.IndentWithNewline, # Schema Stmt indent + ) + if t.doc: + self.print( + '"""', + t.doc.replace('"', '\\"'), + '"""', + Indentation.Newline, + ) + if t.checks: + self.interleave( + lambda: self.print(Indentation.Newline), + self.print, + t.checks, + ) + self.write(NEWLINE) + # Schema Stmt dedent + self.print(Indentation.Dedent) + + def walk_Decorator(self, t: ast.Decorator): + """ast.AST: Decorator + + Parameters + ---------- + - name: Optional[Identifier] + - args: Optional[CallExpr] + """ + assert isinstance(t, ast.Decorator) + self.print( + ast.TokenValue.AT, + t.name, + ) + if t.args: + self.print( + ast.TokenValue.LEFT_PARENTHESES, + t.args, + ast.TokenValue.RIGHT_PARENTHESES, + ) + self.writeln() + + def walk_Arguments(self, t: ast.Arguments): + """ast.AST: Arguments + + Parameters + ---------- + - args: List[Identifier] = [] + - type_annotation_list: List[str] = [] + - defaults: List[Expr] = [] + """ + assert isinstance(t, ast.Arguments) + + def write_argument( + para: Tuple[ast.Identifier, Optional[str], Optional[ast.Expr]] + ): + arg, type_str, default = para + self.print( + arg, + (": " + type_str) if type_str else "", + ) + if default: + self.print(WHITESPACE, ast.TokenValue.ASSIGN, WHITESPACE, default) + + self.interleave( + lambda: self.write(COMMA_WHITESPACE), + write_argument, + zip(t.args, t.type_annotation_list, t.defaults), + ) + + def walk_SchemaAttr(self, t: ast.SchemaAttr): + """ast.AST: SchemaAttr + + Parameters + ---------- + - doc: str + - name: str + - type_str: str + - is_optional: bool + - value: Expr + - decorators: List[Decorator] + - op: Union[AugOp, Assign] + """ + assert isinstance(t, ast.SchemaAttr) + self.exprs(t.decorators) + self.print( + t.name, + ast.TokenValue.QUESTION if t.is_optional else "", + ) + self.print(ast.TokenValue.COLON, WHITESPACE, t.type_str) + if t.op: + self.print( + WHITESPACE, + ast.OPERATOR_VALUE_MAP[t.op], + WHITESPACE, + t.value, + ) + self.write(NEWLINE) + + def walk_IfExpr(self, t: ast.IfExpr): + """ast.AST: IfExpr + + Parameters + ---------- + - cond: Expr + - body: Expr + - orelse: Expr + """ + assert isinstance(t, ast.IfExpr) + self.print( + t.body, + WHITESPACE, + ast.TokenValue.IF, + WHITESPACE, + t.cond, + WHITESPACE, + ast.TokenValue.ELSE, + WHITESPACE, + t.orelse, + ) + + def walk_UnaryExpr(self, t: ast.UnaryExpr): + """ast.AST: UnaryExpr(Expr) + + Parameters + ---------- + - op: UnaryOp + - operand: Expr + """ + assert isinstance(t, ast.UnaryExpr) + self.print( + ast.OPERATOR_VALUE_MAP[t.op], + t.operand, + ) + + def walk_BinaryExpr(self, t: ast.BinaryExpr): + """ast.AST: BinaryExpr + + Parameters + ---------- + - left: Expr + - right: Expr + - op: BinaryOperator + """ + assert isinstance(t, ast.BinaryExpr) and t.left and t.right and t.op + self.print( + t.left, + WHITESPACE, + ast.OPERATOR_VALUE_MAP[t.op], + WHITESPACE, + t.right, + ) + + def walk_SelectorExpr(self, t: ast.SelectorExpr): + """ast.AST: SelectorExpr + + Parameters + ---------- + - value: Expr + - attr: Identifier + - ctx: ExprContext + - has_question: bool + """ + assert isinstance(t, ast.SelectorExpr) + self.print( + t.value, + ast.TokenValue.QUESTION if t.has_question else "", + ast.TokenValue.DOT, + t.attr, + ) + + def walk_CallExpr(self, t: ast.CallExpr): + """ast.AST: CallExpr + + Parameters + ---------- + - func: Expr + - args: List[Expr] + - keywords: List[Keyword] + """ + assert isinstance(t, ast.CallExpr) + if t.func: + self.print( + t.func, + ast.TokenValue.LEFT_PARENTHESES, + ) + self.write_args_and_kwargs(t.args, t.keywords) + if t.func: + self.print( + ast.TokenValue.RIGHT_PARENTHESES, + ) + + def walk_ParenExpr(self, t: ast.ParenExpr): + """ast.AST: ParenExpr + + Parameters + ---------- + - expr: Expr + """ + assert isinstance(t, ast.ParenExpr) + self.print( + ast.TokenValue.LEFT_PARENTHESES, + t.expr, + ast.TokenValue.RIGHT_PARENTHESES, + ) + + def walk_ListExpr(self, t: ast.ListExpr): + """ast.AST: ListExpr + + Parameters + ---------- + - elts: List[Expr] + """ + assert isinstance(t, ast.ListExpr) + in_one_line = len(set(map(lambda e: e.line, t.elts)).union([t.line])) == 1 + self.write(ast.TokenValue.LEFT_BRACKETS) + if t.elts: + self.print(Indentation.IndentWithNewline if not in_one_line else "") + splits = COMMA_WHITESPACE if in_one_line else Indentation.Newline + self.interleave( + lambda: self.print(splits), + self.expr, + t.elts, + ) + self.print(Indentation.DedentWithNewline if not in_one_line else "") + self.write(ast.TokenValue.RIGHT_BRACKETS) + + def walk_StarredExpr(self, t: ast.StarredExpr): + assert isinstance(t, ast.StarredExpr) and t.value + self.print( + ast.TokenValue.MULTIPLY, + t.value, + ) + + def walk_ListComp(self, t: ast.ListComp): + """ast.AST: ListComp + + Parameters + ---------- + - elt: Expr + - generators: List[CompClause] + - targets: List[Expr] + - iter: Expr + - ifs: List[Expr] + """ + assert isinstance(t, ast.ListComp) + self.write(ast.TokenValue.LEFT_BRACKETS) + self.expr(t.elt) + for gen in t.generators: + self.expr(gen) + self.write(ast.TokenValue.RIGHT_BRACKETS) + + def walk_DictComp(self, t: ast.DictComp): + """ast.AST: DictComp + + Parameters + ---------- + - key: Expr + - value: Expr + - generators: List[CompClause] + """ + assert isinstance(t, ast.DictComp) + self.write(ast.TokenValue.LEFT_BRACE) + self.print( + t.key, + ast.TokenValue.COLON, + WHITESPACE, + t.value, + ) + for gen in t.generators: + self.expr(gen) + self.write(ast.TokenValue.RIGHT_BRACE) + + def walk_CompClause(self, t: ast.CompClause): + """ast.AST: CompClause + + Parameters + ---------- + - targets: List[Expr] + - iter: Expr + - ifs: List[Expr] + """ + assert isinstance(t, ast.CompClause) + self.write_with_spaces(ast.TokenValue.FOR) + self.interleave(lambda: self.write(COMMA_WHITESPACE), self.expr, t.targets) + self.write_with_spaces(ast.TokenValue.IN) + self.expr(t.iter) + for if_clause in t.ifs: + self.write_with_spaces(ast.TokenValue.IF) + self.expr(if_clause) + + def walk_QuantExpr(self, t: ast.QuantExpr): + """ast.AST: QuantExpr + + Parameters + ---------- + - target: Optional[Expr] = None + - variables: List[Identifier] = [] + - op: Optional[int] = None + - test: Optional[Expr] = None + - if_cond: Optional[Expr] = None + - ctx: ExprContext = ctx + """ + in_one_line = t.test.line == t.line + self.print( + QUANT_OP_TOKEN_VAL_MAPPING[t.op], + WHITESPACE, + ) + self.interleave(lambda: self.write(COMMA_WHITESPACE), self.expr, t.variables) + self.write_with_spaces(ast.TokenValue.IN) + self.expr(t.target) + self.write(WHITESPACE) + self.write(ast.TokenValue.LEFT_BRACE) + self.print(Indentation.IndentWithNewline if not in_one_line else "") + self.expr(t.test) + if t.if_cond: + self.print(WHITESPACE, ast.TokenValue.IF, WHITESPACE, t.if_cond) + self.print(Indentation.DedentWithNewline if not in_one_line else "") + self.write(ast.TokenValue.RIGHT_BRACE) + + def walk_Subscript(self, t: ast.Subscript): + """ast.AST: Subscript + + Parameters + ---------- + - value: Optional[Expr] = None + - index: Optional[Expr] = None + - lower: Optional[Expr] = None + - upper: Optional[Expr] = None + - step: Optional[Expr] = None + - ctx: ExprContext = ExprContext.LOAD + - has_question: bool = False + """ + assert isinstance(t, ast.Subscript) + self.print( + t.value, + ast.TokenValue.QUESTION if t.has_question else "", + ast.TokenValue.LEFT_BRACKETS, + ) + if t.index: + self.expr(t.index) + else: + self.print( + t.lower, + ast.TokenValue.COLON, + t.upper, + ast.TokenValue.COLON, + t.step, + ) + self.print(ast.TokenValue.RIGHT_BRACKETS) + + def walk_SchemaExpr(self, t: ast.SchemaExpr): + """ast.AST: SchemaExpr + + Parameters + ---------- + - name: Identifier + - config: ConfigExpr + - args: List[Expr] = [] + - kwargs: List[Keyword] = [] + """ + assert isinstance(t, ast.SchemaExpr) + self.print(t.name) + + if t.args or t.kwargs: + self.write(ast.TokenValue.LEFT_PARENTHESES) + self.write_args_and_kwargs(t.args, t.kwargs) + self.write(ast.TokenValue.RIGHT_PARENTHESES) + + self.print(WHITESPACE, t.config) + + def walk_ConfigExpr(self, t: ast.ConfigExpr): + """ast.AST: ConfigExpr + + Parameters + ---------- + - items: List[ConfigEntry] = [] + - key: Expr = key + - value: Expr = value + - operation: int = ConfigEntryOperation.UNION + - insert_index: Union[int, str] = -1 + """ + + def write_config_key(key: ast.AST) -> int: + """Write config key and return need right brace""" + if isinstance(key, ast.Identifier): + self.write_ast_comments(key) + names = key.names + # Judge contains string identifier, e.g., "x-y-z" + need_right_brace = not all( + [bool(re.match(IDENTIFIER_REGEX, n)) for n in names] + ) + if need_right_brace: + # a: {b: {c op value}} + self.print( + ": {".join( + ['"{}"'.format(n.replace('"', '\\"')) for n in names] + ) + ) + return len(names) - 1 + else: + # a.b.c op value + self.expr(key) + return 0 + else: + self.expr(key) + return 0 + + def write_item(item: ast.ConfigEntry): + if item.key is None: + # for dictionary unpacking operator in dicts {**{'y': 2}} + # see PEP 448 for details + if not isinstance(item.value, ast.ConfigIfEntryExpr): + self.print(ast.TokenValue.DOUBLE_STAR) + self.print(item.value) + else: + tok = ast.TokenValue.COLON + if item.operation == ast.ConfigEntryOperation.INSERT: + tok = ast.TokenValue.COMP_PLUS + elif item.operation == ast.ConfigEntryOperation.OVERRIDE: + tok = ast.TokenValue.ASSIGN + print_right_brace_count = write_config_key(item.key) + if item.insert_index is not None and item.insert_index >= 0: + self.print( + ast.TokenValue.LEFT_BRACKETS, + item.insert_index, + ast.TokenValue.RIGHT_BRACKETS, + ) + if tok != ast.TokenValue.COLON: + self.print(WHITESPACE) + self.print( + tok, + WHITESPACE, + item.value, + ) + self.print(ast.TokenValue.RIGHT_BRACE * (print_right_brace_count or 0)) + + in_one_line = len(set(map(lambda e: e.line, t.items)).union([t.line])) == 1 + self.write(ast.TokenValue.LEFT_BRACE) + if t.items: + self.print(Indentation.IndentWithNewline if not in_one_line else "") + self.interleave( + lambda: self.print(COMMA_WHITESPACE) if in_one_line else self.writeln(), + write_item, + t.items, + ) + self.print(Indentation.DedentWithNewline if not in_one_line else "") + self.write(ast.TokenValue.RIGHT_BRACE) + + def walk_CheckExpr(self, t: ast.CheckExpr): + """ast.AST: CheckExpr + + Parameters + ---------- + - test: Expr + - if_cond: Expr + - msg: Expr + """ + assert isinstance(t, ast.CheckExpr) and t.test + self.expr(t.test) + if t.if_cond: + self.print(WHITESPACE, ast.TokenValue.IF, WHITESPACE, t.if_cond) + if t.msg: + self.print( + COMMA_WHITESPACE, + t.msg, + ) + + def walk_LambdaExpr(self, t: ast.LambdaExpr): + """ast.AST: LambdaExpr + + Parameters + ---------- + - args: Optional[Arguments] + - return_type_str: Optional[str] + - return_type_node: Optional[Type] + - body: List[Stmt] + """ + self.print(ast.TokenValue.LAMBDA) + if t.args: + self.print( + WHITESPACE, + t.args, + ) + if t.return_type_str: + self.print( + WHITESPACE, + ast.TokenValue.RIGHT_ARROW, + WHITESPACE, + t.return_type_str, + ) + self.print( + WHITESPACE, + ast.TokenValue.LEFT_BRACE, + NEWLINE, + Indentation.Indent, + ) + self.stmts(t.body) + self.print( + Indentation.Dedent, + NEWLINE, + ast.TokenValue.RIGHT_BRACE, + ) + + def walk_Compare(self, t: ast.Compare): + """ast.AST: Compare + + Parameters + ---------- + - left: Optional[Expr] = None + - ops: List[CmpOp] = [] + - comparators: List[Expr] = [] + """ + assert isinstance(t, ast.Compare) + self.expr(t.left) + for op, expr in zip(t.ops, t.comparators): + self.print( + WHITESPACE, + ast.OPERATOR_VALUE_MAP[op], + WHITESPACE, + expr, + ) + + def walk_Identifier(self, t: ast.Identifier): + """ast.AST: Identifier + + Parameters + ---------- + - names: List[str] + """ + assert isinstance(t, ast.Identifier) and t.ctx + # Convert pkgpath qualified name to a normal identifier + if t.names[0].startswith("@"): + pkgpath = t.names[0][1:] + t.names[0] = self.import_spec.get(pkgpath) or pkgpath + self.write(t.get_name()) + + def walk_NumberLit(self, t: ast.AST): + """ast.AST: NumberLit + + Parameters + ---------- + - value: int + """ + assert isinstance(t, ast.NumberLit) + self.write(str(t.value)) + + def walk_StringLit(self, t: ast.StringLit): + """ast.AST: StringLit + + Parameters + ---------- + - value: str + - raw_value: str + - is_long_string = False + """ + assert isinstance(t, ast.StringLit) + self.write(t.raw_value or '"{}"'.format(t.value.replace('"', '\\"'))) + + def walk_NameConstantLit(self, t: ast.NameConstantLit): + """ast.AST: NameConstantLit + + Parameters + ---------- + - value + """ + assert isinstance(t, ast.NameConstantLit) + # None, Undefined, True, False + self.write(str(t.value)) + + def walk_JoinedString(self, t: ast.JoinedString): + """ast.AST: JoinedString + + Parameters + ---------- + - values: List[Union[Expr, StringLit]] + + TOS + --- + - format_spec + - formatted expr list + + Operand + ------- + Formatted expr list count + """ + assert isinstance(t, ast.JoinedString) + assert t.values + self.print('"') + for value in t.values: + if isinstance(value, ast.FormattedValue): + self.print( + "$", + ast.TokenValue.LEFT_BRACE, + value.value, + ) + if value.format_spec: + self.print( + ast.TokenValue.COLON, + WHITESPACE, + value.format_spec, + ) + self.print( + ast.TokenValue.RIGHT_BRACE, + ) + elif isinstance(value, ast.StringLit): + self.write(value.raw_value or '{}'.format(value.value.replace('"', '\\"'))) + elif isinstance(value, ast.Expr): + self.expr(value) + else: + raise Exception("Invalid AST JoinedString children") + self.print('"') + + def walk_TypeAliasStmt(self, t: ast.TypeAliasStmt): + """ast.AST: TypeAliasStmt + + Parameters + ---------- + - type_name: Identifier + - type_value: Type + """ + self.print( + ast.TokenValue.TYPE, + WHITESPACE, + t.type_name, + WHITESPACE, + ast.TokenValue.ASSIGN, + WHITESPACE, + t.type_value.plain_type_str, + NEWLINE, + ) + + def walk_UnificationStmt(self, t: ast.UnificationStmt): + """ast.AST: UnificationStmt + + Parameters + ---------- + - target: Identifier + - value: SchemaExpr + """ + self.print(t.target, ast.TokenValue.COLON, WHITESPACE, t.value, NEWLINE) + + def walk_AssignStmt(self, t: ast.AssignStmt): + """ast.AST: AssignStmt + + Parameters + ---------- + - targets: List[Identifier] + - value: Expr + """ + assert isinstance(t, ast.AssignStmt) and t.targets and t.value + for i, target in enumerate(t.targets): + self.print(target) + if i == 0 and t.type_annotation: + self.print(ast.TokenValue.COLON, WHITESPACE, t.type_annotation) + self.print(WHITESPACE, ast.TokenValue.ASSIGN, WHITESPACE) + self.print(t.value, NEWLINE) + if isinstance(t.value, ast.SchemaExpr): + self.print(NEWLINE) + + def walk_AugAssignStmt(self, t: ast.AugAssignStmt): + """ast.AST: AugAssignStmt + + Parameters + ---------- + - target: Identifier + - value: Expr + - op: AugOp + """ + assert isinstance(t, ast.AugAssignStmt) and t.target and t.value and t.op + self.print( + t.target, + WHITESPACE, + ast.OPERATOR_VALUE_MAP[t.op], + WHITESPACE, + t.value, + NEWLINE, + ) + + def write_args_and_kwargs(self, args: List[ast.Expr], keywords: List[ast.Keyword]): + def print_arg_assign_value(keyword: ast.Keyword): + self.print( + keyword.arg, + ast.TokenValue.ASSIGN, + keyword.value, + ) + + self.interleave(lambda: self.write(COMMA_WHITESPACE), self.expr, args) + if args and keywords: + self.print(COMMA_WHITESPACE) + self.interleave( + lambda: self.write(COMMA_WHITESPACE), print_arg_assign_value, keywords + ) + + def walk_ListIfItemExpr(self, t: ast.ListIfItemExpr): + assert isinstance(t, ast.ListIfItemExpr) + self.print( + ast.TokenValue.IF, + WHITESPACE, + t.if_cond, + ast.TokenValue.COLON, + Indentation.IndentWithNewline, + ) + self.interleave(lambda: self.print(NEWLINE), self.print, t.exprs) + self.print(Indentation.DedentWithNewline) + if t.orelse: + if isinstance(t.orelse, ast.ListIfItemExpr): + self.print("el") + self.expr(t.orelse) + elif isinstance(t.orelse, ast.ListExpr): + self.print( + ast.TokenValue.ELSE, + ast.TokenValue.COLON, + Indentation.IndentWithNewline, + ) + self.interleave(lambda: self.print(NEWLINE), self.print, t.orelse.elts) + self.print(Indentation.Dedent) + + def walk_ConfigIfEntryExpr(self, t: ast.ConfigIfEntryExpr): + assert isinstance(t, ast.ConfigIfEntryExpr) + self.print( + ast.TokenValue.IF, + WHITESPACE, + t.if_cond, + ast.TokenValue.COLON, + Indentation.IndentWithNewline, + ) + + def write_item(item): + key, value, operation = item + tok = ast.TokenValue.COLON + if operation == ast.ConfigEntryOperation.INSERT: + tok = ast.TokenValue.COMP_PLUS + elif operation == ast.ConfigEntryOperation.OVERRIDE: + tok = ast.TokenValue.ASSIGN + self.print(key) + if tok != ast.TokenValue.COLON: + self.print(WHITESPACE) + self.print( + tok, + WHITESPACE, + value, + ) + + self.interleave( + lambda: self.writeln(), + write_item, + zip(t.keys, t.values, t.operations), + ) + self.print(Indentation.DedentWithNewline) + if t.orelse: + if isinstance(t.orelse, ast.ConfigIfEntryExpr): + self.print("el") + self.expr(t.orelse) + elif isinstance(t.orelse, ast.ConfigExpr): + self.print( + ast.TokenValue.ELSE, + ast.TokenValue.COLON, + Indentation.IndentWithNewline, + ) + self.interleave( + lambda: self.print(Indentation.Newline), + write_item, + zip(t.orelse.keys, t.orelse.values, t.orelse.operations), + ) + self.print(Indentation.Dedent) + + +def PrintAST( + node: ast.AST, + out: Union[io.TextIOBase, io.StringIO] = sys.stdout, + config: Config = Config(), +): + """Print a KCL AST Module to `out` io with `config`""" + Printer(config, out).print_ast(node) diff --git a/internal/kclvm_py/tools/printer/splice.py b/internal/kclvm_py/tools/printer/splice.py new file mode 100644 index 000000000..ba6669769 --- /dev/null +++ b/internal/kclvm_py/tools/printer/splice.py @@ -0,0 +1,86 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import io +from dataclasses import dataclass +from typing import List + +import kclvm.kcl.ast as ast +import kclvm.compiler.parser as parser + +from .printer import PrintAST + + +NEWLINE = "\n" +MOCK_SCHEMA = "MockSchema" + + +@dataclass +class SchemaRuleCodeSnippet: + """Schema and rule code snippet structure""" + + schema: str = "" + rule: str = "" + + +def splice_schema_with_rule(code_snippets: List[SchemaRuleCodeSnippet]) -> str: + """Splice schema with rule code + + Returns a string result denoting the splicing code. + + Parameters + ---------- + code_snippets : List[SchemaRuleCodeSnippet] + A list of schema and rule code snippet structure + """ + if not isinstance(code_snippets, list): + raise ValueError(f"Invalid parameter {code_snippets}, expected list") + with io.StringIO() as buf: + for code in code_snippets: + if not code or not isinstance(code, SchemaRuleCodeSnippet): + raise ValueError( + f"Invalid parameter {code}, expected SchemaRuleCodeSnippet" + ) + module = parser.ParseFile( + ".k", code.schema, mode=parser.ParseMode.ParseComments + ) + module_schema_rule = parser.ParseFile( + ".k", + build_rule_check_block_str( + MOCK_SCHEMA, + code.rule, + ), + mode=parser.ParseMode.ParseComments, + ) + schema_rule_list = module_schema_rule.GetSchemaList() + if schema_rule_list: + for i, stmt in enumerate(module.body): + if isinstance(module.body[i], ast.SchemaStmt): + module.body[i].checks = schema_rule_list[0].checks + for comment in module_schema_rule.comments: + comment.line = module.body[i].end_line + 1 + module.comments.append(comment) + PrintAST(module, buf) + return buf.getvalue().rstrip(NEWLINE) + NEWLINE + + +def add_indent_to_code_string(code: str, indent: int = 4) -> str: + """Add indent to code string""" + if not code or not isinstance(code, str): + return "" + lines = code.split(NEWLINE) + return NEWLINE.join([" " * indent + line for line in lines]) + + +def build_rule_check_block_str(schema_name: str, rule_code: str) -> str: + """Build rule check block string using the rule code string""" + if not schema_name or not isinstance(rule_code, str): + return "" + if not rule_code or not isinstance(rule_code, str): + return "" + return ( + f"schema {schema_name}:" + + NEWLINE + + add_indent_to_code_string("check:", 4) + + NEWLINE + + add_indent_to_code_string(rule_code, 8) + ) diff --git a/internal/kclvm_py/tools/query/__init__.py b/internal/kclvm_py/tools/query/__init__.py new file mode 100644 index 000000000..45c094c9e --- /dev/null +++ b/internal/kclvm_py/tools/query/__init__.py @@ -0,0 +1,16 @@ +from .override import ( + ApplyOverrides, + FixModuleOverride, + PrintOverridesAST, + OverrideInfo, + override_file, +) + + +__all__ = [ + "ApplyOverrides", + "FixModuleOverride", + "PrintOverridesAST", + "OverrideInfo", + "override_file", +] diff --git a/internal/kclvm_py/tools/query/override.py b/internal/kclvm_py/tools/query/override.py new file mode 100644 index 000000000..636381bc3 --- /dev/null +++ b/internal/kclvm_py/tools/query/override.py @@ -0,0 +1,390 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing +import copy +import os +import pathlib +from dataclasses import dataclass + +import kclvm.kcl.ast as ast +import kclvm.kcl.types as types +import kclvm.kcl.error as kcl_error +import kclvm.unification as unification +import kclvm.compiler.astutil as astutil +import kclvm.compiler.parser as parser +from kclvm.tools.printer import PrintAST, Config +from kclvm.tools.format import kcl_fmt_file + + +KCL_FEATURE_GATE_OVERRIDE_AUTO_FIX_ENV = "KCL_FEATURE_GATE_OVERRIDE_AUTO_FIX" + + +@dataclass +class OverrideInfo: + pkgpath: str = None + filename: str = None + module: ast.Module = None + + # --------------- + # Static members + # --------------- + + MODIFIED = [] + + +class OverrideTransformer(ast.TreeTransformer): + def __init__( + self, + target_id: str, + field_path: str, + override_key: ast.Identifier, + override_value: ast.Literal, + override_action: ast.OverrideAction = ast.OverrideAction.CREATE_OR_UPDATE, + ): + super().__init__() + self.target_id: str = target_id + self.field_path: str = field_path + self.override_key: ast.Identifier = override_key + self.override_value: ast.Literal = override_value + self.override_target_count: int = 0 + self.has_override: bool = False + self.override_action: ast.OverrideAction = override_action + + def walk_UnificationStmt(self, t: ast.UnificationStmt): + """ast.AST: UnificationStmt + + Parameters + ---------- + - target: Identifier + - value: SchemaExpr + """ + name = t.target.names[0] + if name != self.target_id: + return t + self.override_target_count = 1 + self.has_override = True + self.walk(t.value) + return t + + def walk_AssignStmt(self, t: ast.AssignStmt): + if not isinstance(t.value, ast.SchemaExpr): + return t + self.override_target_count = 0 + for target in t.targets: + if not isinstance(target, ast.Identifier): + continue + assign_target = typing.cast(ast.Identifier, target) + if len(assign_target.names) != 1: + continue + if assign_target.names[0] != self.target_id: + continue + self.override_target_count = self.override_target_count + 1 + if self.override_target_count == 0: + return t + self.has_override = True + schema_expr_old: ast.SchemaExpr = copy.deepcopy(t.value) + schema_expr_new: ast.SchemaExpr = t.value + self.walk_SchemaExpr(schema_expr_new) + + if len(t.targets) == 1: + return t + + # Fix multiple assign + assign_stmt_list = [] + for target in t.targets: + x: ast.AssignStmt = copy.deepcopy(t) + x.targets = [target] + if len(target.names) == 1 and target.names[0] == self.target_id: + x.value = schema_expr_new + else: + x.value = schema_expr_old + assign_stmt_list.append(x) + return assign_stmt_list + + def walk_SchemaExpr(self, t: ast.SchemaExpr): + if self.override_target_count <= 0: + return t + if not self._find_schema_config_and_replace( + t, self.field_path, self.override_value + ): + # Not exist and append an override value when the action is CREATE_OR_UPDATE + if self.override_action == ast.OverrideAction.CREATE_OR_UPDATE: + t.config.items.append( + ast.ConfigEntry( + key=self.override_key, + value=self.override_value, + operation=ast.ConfigEntryOperation.OVERRIDE, + ) + ) + self.override_target_count = 0 + return t + + def walk_SchemaStmt(self, t: ast.SchemaStmt): + """Do not override AssignStmt in SchemaStmt""" + return t + + def _get_config_field_paths( + self, + config: typing.Union[ast.SchemaExpr, ast.ConfigExpr], + ) -> typing.Tuple[typing.List[str], typing.List[str]]: + def _get_key_value_paths( + key, value + ) -> typing.Tuple[typing.List[str], typing.List[str]]: + _paths, _paths_with_id = [], [] + if isinstance(key, ast.Identifier): + path = key.get_name() + elif isinstance(key, ast.StringLit): + path = key.value + else: + return _paths, _paths_with_id + _paths.append(f"{path}") + _paths_with_id.append(f"{path}") + value_paths, value_paths_with_id = self._get_config_field_paths(value) + if value_paths: + _paths.extend([f"{path}.{value_path}" for value_path in value_paths]) + _paths_with_id.extend( + [f"{path}|{value_path}" for value_path in value_paths_with_id] + ) + return _paths, _paths_with_id + + paths, paths_with_id = [], [] + if isinstance(config, ast.SchemaExpr): + for item in config.config.items: + _paths, _paths_with_id = _get_key_value_paths(item.key, item.value) + paths.extend(_paths) + paths_with_id.extend(_paths_with_id) + elif isinstance(config, ast.ConfigExpr): + for key, value in zip(config.keys, config.values): + _paths, _paths_with_id = _get_key_value_paths(key, value) + paths.extend(_paths) + paths_with_id.extend(_paths_with_id) + return paths, paths_with_id + + def _replace_with_id_path( + self, + schema_config: typing.Union[ast.SchemaExpr, ast.ConfigExpr], + path_with_id: str, + override_value: ast.Literal, + ) -> typing.Optional[ast.SchemaExpr]: + if not path_with_id or not schema_config: + return None + parts = path_with_id.split("|") + config = schema_config + + def _get_path_from_key(key: ast.AST) -> str: + path = "" + if isinstance(key, ast.Identifier): + path = key.get_name() + elif isinstance(key, ast.StringLit): + path = key.value + return path + + for i, part in enumerate(parts): + if isinstance(config, ast.SchemaExpr): + delete_index_list = [] + config_ref = config + for j, item in enumerate(config.config.items): + path = _get_path_from_key(item.key) + if path == part: + if self.override_action == ast.OverrideAction.CREATE_OR_UPDATE: + if i == len(parts) - 1: + override_value.set_ast_position(config) + item.value = override_value + config = item.value + elif self.override_action == ast.OverrideAction.DELETE: + delete_index_list.append(j) + continue + config_ref.config.items = [ + item + for j, item in enumerate(config_ref.config.items) + if j not in delete_index_list + ] + elif isinstance(config, ast.ConfigExpr): + key_value_pairs = zip(config.keys, config.values) + delete_index_list = [] + config_ref = config + for j, key_value in enumerate(key_value_pairs): + key, value = key_value + path = _get_path_from_key(key) + if path == part: + if self.override_action == ast.OverrideAction.CREATE_OR_UPDATE: + if i == len(parts) - 1: + override_value.set_ast_position(config) + config.items[j].value = override_value + config = value + elif self.override_action == ast.OverrideAction.DELETE: + delete_index_list.append(j) + continue + config_ref.items = [ + item + for j, item in enumerate(config_ref.items) + if j not in delete_index_list + ] + if override_value: + override_value.set_ast_position(config) + return schema_config + + def _find_schema_config_and_replace( + self, schema_config: ast.SchemaExpr, field_path: str, value: typing.Any + ) -> bool: + if not schema_config: + raise Exception("override schema config can't be None") + # Find field_path by nested identifier + paths, paths_with_id = self._get_config_field_paths(schema_config) + if field_path not in paths: + return False + path_with_id = paths_with_id[paths.index(field_path)] + self._replace_with_id_path(schema_config, path_with_id, value) + return True + + +def ApplyOverrides( + prog: ast.Program, + overrides: typing.List[ast.CmdOverrideSpec], + import_paths: typing.List[str] = None, +): + for override in overrides or []: + pkgpath = override.pkgpath if override.pkgpath else prog.main + if pkgpath in prog.pkgs: + for mx in prog.pkgs[pkgpath]: + if FixModuleOverride(mx, override): + OverrideInfo.MODIFIED.append( + OverrideInfo( + pkgpath=pkgpath, + filename=mx.GetFileName(root=prog.root), + module=mx, + ) + ) + ModuleAddImportPaths(mx, import_paths) + # Override type check and to auto fix + if os.getenv(KCL_FEATURE_GATE_OVERRIDE_AUTO_FIX_ENV): + types.ResolveProgram(prog, types.CheckConfig(config_attr_auto_fix=True)) + # Put AST module modified deepcopy + OverrideInfo.MODIFIED = [copy.deepcopy(m) for m in OverrideInfo.MODIFIED] + + +def PrintOverridesAST(): + """Print override AST program""" + if OverrideInfo.MODIFIED: + for value in OverrideInfo.MODIFIED: + with open(value.filename, "w") as f: + f.flush() + os.fsync(f.fileno()) + PrintAST(value.module, f, Config(is_fix=True)) + kcl_fmt_file(pathlib.Path(value.filename)) + + +def ModuleAddImportPaths( + m: ast.Module, import_paths: typing.List[str], ignore_exist: bool = False +) -> ast.Module: + """Add more import paths into the AST module.""" + if not import_paths: + return m + import_stmt_list = [] + exist_import_set = [stmt.path for stmt in m.GetImportList()] + line = 1 + for path in import_paths or []: + if not ignore_exist and path in exist_import_set: + continue + import_stmt = ast.ImportStmt(line, 1) + import_stmt.path = path + import_stmt.name = path.rsplit(".")[-1] + import_stmt_list.append(import_stmt) + line += 1 + m.body = import_stmt_list + m.body + + +def FixModuleOverride(m: ast.Module, override: ast.CmdOverrideSpec) -> bool: + assert m + assert override + + ss = override.field_path.split(".") + if len(ss) <= 1: + return False + + target_id: str = ss[0] + field: str = ".".join(ss[1:]) + value: str = override.field_value + + key = ast.Identifier(names=[s for s in field.split(".")], ctx=ast.ExprContext.STORE) + val = astutil.BuildNodeFromString(value) + + transformer = OverrideTransformer( + target_id, field, key, val, override_action=override.action + ) + transformer.walk(m) + return transformer.has_override + + +def override_file( + file: str, specs: typing.List[str], import_paths: typing.List[str] = None +) -> bool: + """Override and rewrite a file with override spec + + Parameters + ---------- + file: str + The File that need to be overridden + specs: List[str] + List of specs that need to be overridden. + Each spec string satisfies the form: := or :- + When the pkgpath is '__main__', it can be omitted. + + Return + ------ + result: bool + Whether override is successful + """ + overrides = [spec_str_to_override(spec) for spec in specs or []] + program = parser.LoadProgram( + file, + mode=parser.ParseMode.ParseComments, + load_packages=bool(os.getenv(KCL_FEATURE_GATE_OVERRIDE_AUTO_FIX_ENV)), + ) + # Config unification + program.pkgs[ast.Program.MAIN_PKGPATH] = unification.MergeASTList( + program.pkgs[ast.Program.MAIN_PKGPATH] + ) + OverrideInfo.MODIFIED = [] + ApplyOverrides(program, overrides, import_paths) + PrintOverridesAST() + return True + + +def spec_str_to_override(spec: str) -> ast.CmdOverrideSpec: + """Override spec string to override structure""" + + def report_exception(): + kcl_error.report_exception( + err_type=kcl_error.ErrType.IllegalArgumentError_TYPE, + arg_msg=f"Invalid spec format '{spec}', expected := or :-", + ) + + # Create or update the override value + if "=" in spec: + split_values = spec.split("=", 1) + paths = split_values[0].split(":", 1) + if len(split_values) < 2 or len(paths) > 2: + report_exception() + paths.append(split_values[1]) + paths = paths if len(paths) == 3 else ["", *paths] + return ast.CmdOverrideSpec( + pkgpath=paths[0], + field_path=paths[1], + field_value=paths[2], + action=ast.OverrideAction.CREATE_OR_UPDATE, + ) + # Delete the override value + elif spec.endswith("-"): + paths = spec[:-1].split(":", 1) + if len(paths) > 2: + report_exception() + paths = paths if len(paths) == 2 else ["", *paths] + return ast.CmdOverrideSpec( + pkgpath=paths[0], + field_path=paths[1], + field_value="", + action=ast.OverrideAction.DELETE, + ) + + report_exception() diff --git a/internal/kclvm_py/tools/validation/__init__.py b/internal/kclvm_py/tools/validation/__init__.py new file mode 100644 index 000000000..49f02dc27 --- /dev/null +++ b/internal/kclvm_py/tools/validation/__init__.py @@ -0,0 +1,8 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .validation import validate_code, validate_code_with_attr_data + +__all__ = [ + "validate_code", + "validate_code_with_attr_data", +] diff --git a/internal/kclvm_py/tools/validation/validation.py b/internal/kclvm_py/tools/validation/validation.py new file mode 100644 index 000000000..faef2d35e --- /dev/null +++ b/internal/kclvm_py/tools/validation/validation.py @@ -0,0 +1,214 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import Optional, Any, List + +import kclvm.kcl.error as kcl_error +import kclvm.kcl.ast as ast +import kclvm.compiler.astutil as astutil +import kclvm.compiler.parser as parser +import kclvm.compiler.extension.builtin.system_module.json as json +import kclvm.compiler.extension.builtin.system_module.yaml as yaml +import kclvm.program.eval as eval + + +class ValidationMeta: + TEMP_FILE = "validationTempKCLCode.k" + + +class ValidationDataFormat: + """ + KCL validation data formats including yaml and json. + """ + + YAML: str = "yaml" + JSON: str = "json" + MAPPING = { + "YAML": yaml.KMANGLED_decode, + "JSON": json.KMANGLED_decode, + } + + +def validate_code( + data: str, + code: str, + schema: Optional[str] = None, + attribute_name: str = "value", + format: str = "json", + filename: str = None, +) -> bool: + """Validate the data string using the schema code string, when the parameter + `schema` is omitted, use the first scheam appeared in the code, when the schema + if not found, raise an schema not found error. + + Returns a bool result denoting whether validating success, raise an error + when validating failed because of the file not found error, schema not found + error, syntax error, check error, etc. + + Parameters + ---------- + data : str + A JSON or YAML data string. + code : str + A KCL code string. + schema : str + The schema name required for verification. + attribute_name : str + The validation attribute name, default is `value`. + format: str, default is "json" + The data format, suppored json, JSON, yaml and YAML. + filename: str, default is None + The filename of the KCL code. + + Examples + -------- + >>> data = '{"key": "value"}' # A JSON data string + >>> code = ''' + schema Person: + key: str + + check: + "value" in key # `key` is required and `key` must contain "value" + ''' + >>> validate_code(data, code) + True + + """ + check_validation_para(data, code, format) + # 1. Parse kcl code string to the AST module. + module = parser.ParseFile(filename=filename or ValidationMeta.TEMP_FILE, code=code) + schema_list = astutil.filter_stmt(module, ast.SchemaStmt) + # 2. Deserialize data str and covert it to a KCL AST node. + decoder = ValidationDataFormat.MAPPING.get(format.upper()) + value = decoder(data) + schema_name = schema or (schema_list[0].name if schema_list else None) + node_list = validate_value_to_ast_node_list(value, schema_name, attribute_name) + # 3. Insert the value AST node into the module and eval + module.body = node_list + module.body + eval.EvalAST(module) + return True + + +def validate_code_with_attr_data( + data: str, + code: str, + schema: Optional[str] = None, + format: str = "json", +) -> bool: + """Validate the data string using the schema code string, when the parameter + `schema` is omitted, use the first scheam appeared in the code, when the schema + if not found, raise an schema not found error. + + Returns a bool result denoting whether validating success, raise an error + when validating failed because of the file not found error, schema not found + error, syntax error, check error, etc. + + Parameters + ---------- + data : str + A JSON or YAML data string including the attribute key + code : str + A KCL code string. + schema : str + The schema name required for verification. + format: str, default is "json" + The data format, suppored json, JSON, yaml and YAML. + + Examples + -------- + >>> data = '{"attr": {"key": "value"}}' # A JSON data string including the attribute name + >>> code = ''' + schema Person: + key: str + + check: + "value" in key # `key` is required and `key` must contain "value" + ''' + >>> validate_code_with_attr_data(data, code) + True + """ + check_validation_para(data, code, format) + decoder = ValidationDataFormat.MAPPING.get(format.upper()) + value = decoder(data) + if not value or not isinstance(value, dict) or len(value) != 1: + raise ValueError( + f"Invalid parameter data: {data}, expected a dict with only one attribute" + ) + attribute_name = list(value.keys())[0] + data = json.KMANGLED_encode(value[attribute_name]) + return validate_code( + data=data, + code=code, + schema=schema, + attribute_name=attribute_name, + format=format, + ) + + +def check_validation_para(data: str, code: str, format: str): + if data is None or not isinstance(data, str): + raise ValueError(f"Invalid parameter data: {data}") + if code is None or not isinstance(code, str): + raise ValueError(f"Invalid parameter code: {code}") + if ( + format is None + or not isinstance(format, str) + or format.upper() not in ValidationDataFormat.MAPPING + ): + raise ValueError( + f"Invalid parameter format: {format}, expected one of {ValidationDataFormat.MAPPING.keys()}" + ) + + +def validate_value_to_ast_node_list( + value: Any, schema_name: str = None, attribute_name: str = "value" +) -> List[ast.AST]: + """Covert a validation value to a KCL AST node""" + + def build_assign_node(attribute_name: str, node: ast.AST) -> List[ast.AssignStmt]: + if not attribute_name: + raise ValueError(f"Invalid parameter attribute_name: {attribute_name}") + assign_stmt = ast.AssignStmt() + assign_stmt.value = node + assign_stmt.targets = [ + ast.Identifier( + names=[attribute_name], + ctx=ast.ExprContext.STORE, + ) + ] + return [assign_stmt] + + if isinstance(value, (int, float, bool, str, list, tuple, set, dict)): + node = value_to_ast(value, schema_name) + return build_assign_node(attribute_name, node) + else: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg=f"invalid validation data value {value}", + ) + + +def value_to_ast(value: Any, schema_name: Optional[str] = None) -> ast.AST: + node = None + if value is None: + node = ast.NameConstantLit(value=None) + elif isinstance(value, (list, tuple, set)): + node = ast.ListExpr() + node.elts = [value_to_ast(v, schema_name) for v in value] + elif isinstance(value, dict): + config = ast.ConfigExpr() + if schema_name: + node = ast.SchemaExpr() + node.name = ast.Identifier(names=[schema_name]) + node.config = config + else: + node = config + for k, v in value.items(): + config.items.append( + ast.ConfigEntry( + key=value_to_ast(k, schema_name), + value=value_to_ast(v), + ) + ) + elif isinstance(value, (bool, int, float, str)): + node = astutil.BuildLitNodeFromValue(value) + return node diff --git a/internal/kclvm_py/unification/__init__.py b/internal/kclvm_py/unification/__init__.py new file mode 100644 index 000000000..fadee117b --- /dev/null +++ b/internal/kclvm_py/unification/__init__.py @@ -0,0 +1,15 @@ +from .merge import MergeAST, MergeASTList, MergeASTToVertex, MergeStrategy +from .vertex import Vertex +from .unifier import Unifier +from .subsume import value_subsume, type_subsume + +__all__ = [ + "MergeASTList", + "MergeAST", + "MergeASTToVertex", + "MergeStrategy", + "Vertex", + "Unifier", + "value_subsume", + "type_subsume", +] diff --git a/internal/kclvm_py/unification/merge.py b/internal/kclvm_py/unification/merge.py new file mode 100644 index 000000000..4a930be5e --- /dev/null +++ b/internal/kclvm_py/unification/merge.py @@ -0,0 +1,342 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from dataclasses import dataclass +from abc import ABC, abstractmethod +from copy import deepcopy +from enum import IntEnum +from collections import defaultdict +from typing import Tuple, List, Optional, cast + +import kclvm.kcl.ast as ast +import kclvm.compiler.astutil as astutil + +from .vertex import Vertex +from .unifier import Unifier, UnifierConfig + + +INVALID_STRATEGY_MSG = "invalid strategy {}" + + +class BaseMerger(ABC): + def __init__(self, module: ast.Module): + self.module: ast.Module = module + + @abstractmethod + def merge(self) -> Tuple[Vertex, Vertex, ast.Module]: + raise NotImplementedError + + +@dataclass +class UnifyMerger(BaseMerger): + def __init__(self, module: ast.Module): + super().__init__(module) + + def unify_ast_module( + self, config: UnifierConfig = UnifierConfig() + ) -> Tuple[Optional[Vertex], Optional[Vertex], ast.Module]: + """Unify vertex according to the AST Module""" + if not self.module: + return None, None, self.module + vertex = Vertex.ast_to_vertex(self.module) + unify_vertex = Unifier(config).unify(vertex) + merged_module = cast(ast.Module, unify_vertex.vertex_to_ast()) + return vertex, unify_vertex, merged_module + + def merge(self) -> Tuple[Vertex, Vertex, ast.Module]: + """Merge the AST Module + + Input + ----- + None + + Output + ------ + vertex: Vertex formed by AST before merging + unify_vertex: Vertex formed by AST after merging + module: The merged AST + """ + vertex, unify_vertex, merged_module = self.unify_ast_module() + self.module = _deal_origin_ast_module(merged_module, self.module) + return vertex, unify_vertex, self.module + + +@dataclass +class UniqueUnifyMerger(UnifyMerger): + def __init__(self, module: ast.Module): + super().__init__(module) + + def merge(self) -> Tuple[Vertex, Vertex, ast.Module]: + """Merge the AST Module + + Input + ----- + None + + Output + ------ + vertex: Vertex formed by AST before merging + unify_vertex: Vertex formed by AST after merging + module: The merged AST + """ + vertex, unify_vertex, merged_module = self.unify_ast_module( + config=UnifierConfig(check_unique=True) + ) + self.module = _deal_origin_ast_module(merged_module, self.module) + return vertex, unify_vertex, self.module + + +@dataclass +class Overrider(UnifyMerger): + def __init__(self, module: ast.Module): + super().__init__(module) + + def merge(self) -> Tuple[Vertex, Vertex, ast.Module]: + vertex, unify_vertex, merged_module = self.unify_ast_module( + config=UnifierConfig(override=True) + ) + self.module = _deal_origin_ast_module(merged_module, self.module) + return vertex, unify_vertex, self.module + + +class MergeStrategy(IntEnum): + UNION = 1 # `:` + OVERRIDE = 2 # `=` + UNIQUE = 3 # `!` + + +class MergeStrategyFactory: + + MAPPING = { + MergeStrategy.UNION: UnifyMerger, + MergeStrategy.OVERRIDE: Overrider, + MergeStrategy.UNIQUE: UniqueUnifyMerger, + } + + @staticmethod + def get(strategy: MergeStrategy): + return MergeStrategyFactory.MAPPING.get(strategy, UnifyMerger) + + +def _deal_origin_ast_module( + merged_module: ast.Module, origin_module: ast.Module, all_visited: bool = False +) -> Optional[ast.Module]: + """Deal origin AST Module according to merged_module""" + if not merged_module or not isinstance(merged_module, ast.Module): + return None + merged_declaration_list = astutil.filter_declarations(merged_module, ast.SchemaExpr) + merged_declaration_map = defaultdict(list) + for d in merged_declaration_list: + d.value.filename = d.filename + merged_declaration_map[d.name].append(d.value) + merged_declaration_visited = {d.name: False for d in merged_declaration_list} + # Reverse traversal + origin_module.body = origin_module.body[::-1] + i = 0 + # TODO: Optimize the while loop using AST Transformer. + while i < len(origin_module.body): + stmt = origin_module.body[i] + if isinstance(stmt, ast.UnificationStmt): + target = stmt.target + name = target.get_first_name() + if name in merged_declaration_map: + del origin_module.body[i] + i -= 1 + values = merged_declaration_map[name] + if values and not merged_declaration_visited[name] and not all_visited: + identifier = ast.Identifier( + line=target.line, + column=target.column, + names=[name], + ctx=ast.ExprContext.STORE, + ) + identifier.pkgpath = target.pkgpath + identifier.filename = target.filename + identifier.end_line, identifier.end_column = ( + target.end_line, + target.end_column, + ) + insert_unification_stmt = ast.UnificationStmt( + line=stmt.line, column=stmt.column + ) + insert_unification_stmt.target = identifier + ( + insert_unification_stmt.end_line, + insert_unification_stmt.end_column, + ) = ( + stmt.end_line, + stmt.end_column, + ) + for value in values: + stmt_copy = deepcopy(insert_unification_stmt) + stmt_copy.value = value + stmt_copy.filename = value.filename + stmt_copy.line = value.line + stmt_copy.column = value.column + stmt_copy.end_line = value.end_line + stmt_copy.end_column = value.end_column + i += 1 + origin_module.body.insert(i, stmt_copy) + merged_declaration_visited[name] = True + elif isinstance(stmt, ast.AssignStmt) and isinstance( + stmt.value, ast.SchemaExpr + ): + if not stmt.targets: + del origin_module.body[i] + i -= 1 + j = 0 + while j < len(stmt.targets): + target = stmt.targets[j] + name = target.get_first_name() + if name in merged_declaration_map: + del stmt.targets[j] + j -= 1 + if len(stmt.targets) == 0: + del origin_module.body[i] + i -= 1 + values = merged_declaration_map[name] + if ( + values + and not merged_declaration_visited[name] + and not all_visited + ): + identifier = ast.Identifier( + line=target.line, + column=target.column, + names=[name], + ctx=ast.ExprContext.STORE, + ) + identifier.pkgpath = target.pkgpath + identifier.filename = target.filename + identifier.end_line, identifier.end_column = ( + target.end_line, + target.end_column, + ) + insert_assign_stmt = ast.AssignStmt( + line=stmt.line, column=stmt.column + ) + insert_assign_stmt.targets = [identifier] + insert_assign_stmt.end_line, insert_assign_stmt.end_column = ( + stmt.end_line, + stmt.end_column, + ) + for value in values: + stmt_copy = deepcopy(insert_assign_stmt) + stmt_copy.value = value + stmt_copy.filename = value.filename + stmt_copy.line = value.line + stmt_copy.column = value.column + stmt_copy.end_line = value.end_line + stmt_copy.end_column = value.end_column + i += 1 + origin_module.body.insert(i, stmt_copy) + merged_declaration_visited[name] = True + j += 1 + i += 1 + # Remove empty targets assignment + origin_module.body = [ + m + for m in reversed(origin_module.body) + if not isinstance(m, ast.AssignStmt) or m.targets + ] + return origin_module + + +def MergeASTList( + modules: List[ast.Module], strategy: MergeStrategy = MergeStrategy.UNION +) -> List[ast.Module]: + """Merge the configurations of the same name in the + AST Module list, and the ones that cannot be merged + will be handed over to the VM for calculation. + """ + if not modules or not isinstance(modules, list): + return [] + # AST module filename list + filenames = [m.filename for m in modules] + # Config need to be merged + file_configs = [ + stmt + for m in modules[1:] + for stmt in m.body + if (isinstance(stmt, ast.AssignStmt) and isinstance(stmt.value, ast.SchemaExpr)) + or isinstance(stmt, ast.UnificationStmt) + or isinstance(stmt, ast.ImportStmt) + ] + # Config filename meta + files_meta = [ + (m.filename, stmt.line, stmt.column, stmt.end_line, stmt.end_column) + for m in modules[1:] + for stmt in m.body + if (isinstance(stmt, ast.AssignStmt) and isinstance(stmt.value, ast.SchemaExpr)) + or isinstance(stmt, ast.UnificationStmt) + or isinstance(stmt, ast.ImportStmt) + ] + # Record the statement filename + for i, config in enumerate(file_configs): + ( + file_configs[i].filename, + file_configs[i].line, + file_configs[i].column, + file_configs[i].end_line, + file_configs[i].end_column, + ) = files_meta[i] + if not modules[0].body: + modules[0].body = [] + modules[0].body += file_configs + MergeAST(modules[0], strategy) + # Other file config list + file_configs = [ + stmt + for stmt in modules[0].body + if stmt.filename is not None + and stmt.filename != modules[0].filename + and not isinstance(stmt, ast.ImportStmt) + ] + # Origin module + modules[0].body = [stmt for stmt in modules[0].body if stmt not in file_configs] + # Filter all config except the first file + for i, _ in enumerate(modules[1:]): + modules[i + 1].body = [ + stmt + for stmt in modules[i + 1].body + if ( + not isinstance(stmt, ast.AssignStmt) + or not isinstance(stmt.value, ast.SchemaExpr) + ) + and not isinstance(stmt, ast.UnificationStmt) + ] + # Insert the merged configuration into different files + for config in file_configs: + index = filenames.index(config.filename) + line = config.line + insert_index = len(modules[index].body) + for i, stmt in enumerate(modules[index].body): + if stmt.line >= line: + insert_index = i + break + modules[index].body.insert(insert_index, config) + # Return the merged multi-file modules + return modules + + +def MergeAST( + module: ast.Module, strategy: MergeStrategy = MergeStrategy.UNION +) -> ast.Module: + """Merge the configurations of the same name in the single + AST module, and the ones that cannot be merged will be handed + over to the VM for calculation. + """ + _, _, merged_module = MergeASTToVertex(module, strategy) + return merged_module + + +def MergeASTToVertex( + module: ast.Module, strategy: MergeStrategy = MergeStrategy.UNION +) -> Tuple[Optional[Vertex], Optional[Vertex], ast.Module]: + """Merge the configurations of the same name in the AST and + return the merged vertices, and the ones that cannot be merged + will be handed over to the VM for calculation. + """ + if not module: + return None, None, module + merger = MergeStrategyFactory.get(strategy) + return merger(module).merge() diff --git a/internal/kclvm_py/unification/subsume.py b/internal/kclvm_py/unification/subsume.py new file mode 100644 index 000000000..bf7cc6b68 --- /dev/null +++ b/internal/kclvm_py/unification/subsume.py @@ -0,0 +1,161 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import cast + +import kclvm.api.object as obj + + +def value_subsume( + value1: obj.KCLObject, value2: obj.KCLObject, should_recursive_check: bool = True +) -> bool: + """Calculate the partial order relationship between `KCL value objects` + and judge whether the value1 object ∈ the value2 object. + + Please note that The type and value of KCL are defined and used separately, + so the partial order relationship calculation is also divided into two types, + type and value, and there is no partial order relationship between type + objects and value objects. + """ + if not value1 or not value2: + return False + if not isinstance(value1, obj.KCLObject) or not isinstance(value2, obj.KCLObject): + return False + if value1 == value2 or value1 is value2: + return True + if isinstance(value1, (obj.KCLNoneObject, obj.KCLUndefinedObject)): + return True + if isinstance(value2, (obj.KCLNoneObject, obj.KCLUndefinedObject)): + return True + if isinstance(value1, obj.KCLIntObject): + return isinstance(value2, obj.KCLIntObject) and value1.value == value2.value + if isinstance(value1, obj.KCLFloatObject): + return isinstance(value2, obj.KCLFloatObject) and value1.value == value2.value + if isinstance(value1, obj.KCLNameConstantObject): + return ( + isinstance(value2, obj.KCLNameConstantObject) + and value1.value == value2.value + ) + if isinstance(value1, (obj.KCLListObject, obj.KCLTupleObject)): + return ( + isinstance(value2, (obj.KCLListObject, obj.KCLTupleObject)) + and len(value1.items) == len(value2.items) + and all( + [ + value_subsume(item1, item2, should_recursive_check) + for item1, item2 in zip(value1.items, value2.items) + ] + ) + ) + if isinstance(value1, (obj.KCLDictObject, obj.KCLSchemaObject)): + if isinstance(value2, (obj.KCLDictObject, obj.KCLSchemaObject)): + value1_dict = {k: value1.get(k) for k in sorted(list(value1.value.keys()))} + value2_dict = {k: value2.get(k) for k in sorted(list(value2.value.keys()))} + + if len(value1_dict) == 0: + return True + + if all([key not in value2_dict for key in value1_dict]): + return True + + if should_recursive_check: + for key1, value1 in value1_dict.items(): + if key1 not in value2_dict: + continue + value2 = value2_dict.get(key1) + if not value_subsume(value1, value2, should_recursive_check): + return False + return True + return False + + +def type_subsume( + value1: obj.KCLObject, value2: obj.KCLObject, check_left_any: bool = False +) -> bool: + """Calculate the partial order relationship between `KCL type objects` + and judge whether the value1 object ∈ the value2 object. + + Please note that The type and value of KCL are defined and used separately, + so the partial order relationship calculation is also divided into two types, + type and value, and there is no partial order relationship between type + objects and value objects. + """ + if not value1 or not value2: + return False + if not isinstance(value1, obj.KCLObject) or not isinstance(value2, obj.KCLObject): + return False + if value1 == value2 or value1 is value2: + return True + if check_left_any and isinstance(value1, obj.KCLAnyTypeObject): + return True + if isinstance(value2, obj.KCLAnyTypeObject): + return True + if isinstance(value1, obj.KCLNoneTypeObject): + return True + if isinstance(value1, obj.KCLUnionTypeObject): + return all([type_subsume(tpe, value2) for tpe in value1.types]) + if isinstance(value2, obj.KCLUnionTypeObject): + return any([type_subsume(value1, tpe) for tpe in value2.types]) + if isinstance(value1, obj.KCLSchemaTypeObject): + if not isinstance(value2, obj.KCLSchemaTypeObject): + return False + value1 = cast(obj.KCLSchemaTypeObject, value1) + value2 = cast(obj.KCLSchemaTypeObject, value2) + return value1.is_sub_schema_of(value2) + if isinstance(value1, obj.KCLIntTypeObject) and isinstance( + value2, obj.KCLFloatTypeObject + ): + return True + if isinstance(value1, obj.KCLBuiltinTypeObject): + return ( + isinstance(value2, obj.KCLBuiltinTypeObject) + and value1.type_kind() == value2.type_kind() + ) + if isinstance( + value1, + ( + obj.KCLStringLitTypeObject, + obj.KCLNumberLitTypeObject, + obj.KCLBoolLitTypeObject, + ), + ): + if isinstance( + value2, + ( + obj.KCLStringLitTypeObject, + obj.KCLNumberLitTypeObject, + obj.KCLBoolLitTypeObject, + ), + ): + return ( + value1.type_kind() == value2.type_kind() + and value1.value == value2.value + ) + elif isinstance(value2, obj.KCLBuiltinTypeObject): + # float_lit -> float + # int_lit -> int + # bool_lit -> bool + # str_lit -> str + # int_lit/bool_lit -> float + if isinstance(value2, obj.KCLFloatTypeObject) and not isinstance( + value1, obj.KCLStringLitTypeObject + ): + return True + return value2.type_str() in value1.type_str() + if isinstance(value1, obj.KCLListTypeObject): + return isinstance(value2, obj.KCLListTypeObject) and type_subsume( + value1.item_type, value2.item_type, check_left_any + ) + if isinstance(value1, obj.KCLDictTypeObject): + return ( + isinstance(value2, obj.KCLDictTypeObject) + and type_subsume(value1.key_type, value2.key_type, check_left_any) + and type_subsume(value1.value_type, value2.value_type, check_left_any) + ) + if isinstance(value1, obj.KCLNumberMultiplierTypeObject) and isinstance( + value2, obj.KCLNumberMultiplierTypeObject + ): + if value1.is_literal(): + return not value2.is_literal() and value1.type_str() == value2.type_str() + else: + return True + return False diff --git a/internal/kclvm_py/unification/unifier.py b/internal/kclvm_py/unification/unifier.py new file mode 100644 index 000000000..8e7fe0e48 --- /dev/null +++ b/internal/kclvm_py/unification/unifier.py @@ -0,0 +1,118 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import List +from dataclasses import dataclass +from collections import defaultdict + +import kclvm.kcl.error as kcl_error + +from .vertex import Vertex + + +NAME_NONE_BUCKET_KEY = "$name_none" + + +@dataclass +class UnifierConfig: + """The vertex unification config""" + + check_unique: bool = False + override: bool = False + + +class Unifier: + def __init__(self, config: UnifierConfig = UnifierConfig()): + self.config: UnifierConfig = config + + def unify(self, vertex: Vertex) -> Vertex: + """The vertex unification function""" + if not vertex or not isinstance(vertex, Vertex): + return vertex + # Using bucket map to check unique/merge option and store values + bucket = defaultdict(list) + for v in vertex.adjs or []: + self.append_vertex_into_bucket(bucket, v) + # Using the following configuration meta (filename/line/column) with the same name + # to override the previous configuration e.g., `stack config -> base config` + for k, v_list in bucket.items(): + if v_list: + for j in range(len(v_list)): + v_list[j].meta = v_list[-1].meta + # Merge vertices in the vertex list the with the same name + bucket[k] = self.merge_vertices(v_list) + # Merge the vertex adjs + vertex.adjs = sum(bucket.values(), []) + return vertex + + def merge_vertices(self, vertices: List[Vertex]) -> List[Vertex]: + """Merge a vertex list with same names""" + if not vertices or not isinstance(vertices, list): + return [] + vertex_list = [] + # Merge all adjs in vertex with the same name, + # if the adjs is None, append it into the vertex list + total_adjs = sum([v.adjs or [] for v in vertices], []) + is_unified = False + meta_names = [] + for v in vertices: + # If there are vertices in the list without adjs, they may have the + # conflict values and put them into the vertex list + # and deal the value conflict in VM + if v.adjs is None and v.node: + vertex_list.append(v) + elif not is_unified: + v.adjs = total_adjs + vertex_list.append(self.unify(v)) + is_unified = True + if v.config_meta.name and v.config_meta.name not in meta_names: + if v.option.is_override: + meta_names = [v.config_meta.name] + else: + meta_names.append(v.config_meta.name) + if len(meta_names) >= 2 and v.option.is_union: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=v.meta.filename, + line_no=v.meta.line, + col_no=v.meta.column, + ) + ], + arg_msg=f"conflict unification types between {meta_names[-1]} and {meta_names[0]}", + ) + # Return the merged the vertex list + return vertex_list + + def append_vertex_into_bucket(self, bucket: dict, v: Vertex): + """Append the vertex into the bucket map with unique and override option""" + if not isinstance(bucket, dict) or not isinstance(v, Vertex): + return + # Check unique key error + if self.config.check_unique and len(bucket[v.name]) > 1 and v.option.is_unique: + kcl_error.report_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=v.meta.filename, + line_no=v.node.line, + col_no=v.node.column, + end_col_no=v.node.end_column, + ) + ], + arg_msg=f"config of '{v.name}' must be unique", + ) + # Put the different value into the bucket with the same `name`. + # Please note that then the vertex key is a runtime variable + # such as string interpolation, the name is None e.g., `{"${name}": "Alice"}` + # else the name is the key name such as `name` in `{"name": "Alice"}` + if v.name is not None: + # Override the value in the bucket + if self.config.override: + bucket[v.name] = [v] + # Append the multiple values into the bucket and calculate these values in VM + else: + bucket[v.name].append(v) + # Store the name none node including unpack expression and collection if expression + else: + bucket[NAME_NONE_BUCKET_KEY].append(v) diff --git a/internal/kclvm_py/unification/vertex.py b/internal/kclvm_py/unification/vertex.py new file mode 100644 index 000000000..87700686a --- /dev/null +++ b/internal/kclvm_py/unification/vertex.py @@ -0,0 +1,420 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from io import StringIO +from dataclasses import dataclass +from typing import cast, List, Optional, Union + +import kclvm.kcl.ast as ast +import kclvm.compiler.astutil as astutil + + +VERTEX_ROOT_NAME = "@root" + + +@dataclass +class Meta: + """The filename, line and column info""" + + filename: Optional[str] = None + line: Optional[int] = None + column: Optional[int] = None + end_line: Optional[int] = None + end_column: Optional[int] = None + + @staticmethod + def from_ast_node(node: ast.AST) -> "Meta": + return ( + Meta( + filename=node.filename, + line=node.line, + column=node.column, + end_line=node.end_line, + end_column=node.end_column, + ) + if node and isinstance(node, ast.AST) + else Meta() + ) + + +@dataclass +class ConfigMeta: + """The schema config name, args and kwargs AST node references""" + + name: str = None + pkgpath: str = None + args: List[ast.Expr] = None + kwargs: List[ast.Keyword] = None + + +@dataclass +class VertexOption: + """The vertex unification options""" + + is_union: bool = False # `:` + is_override: bool = False # `=` + is_append: bool = False # `+=` + is_unique: bool = False # `!` + is_strategy_merge: bool = False # `?` + + +@dataclass +class Vertex: + """ + Parameters + ---------- + name: Union[int, float, str, ast.AST] + Vertex node name. When the name is a variable needed to be calculated in VM, + it is a AST type + adjs: List["Vertex"] + The list of downstream nodes of the vertex + node: ast.AST + The AST node reference + meta: Mate + The filename, line and column info + config_meta: ConfigMeta: + The schema config name, args and kwargs AST node references + option: + The vertex unification options + + Methods + ------- + vertex_to_ast: + Vertex -> AST + ast_to_vertex: + AST -> Vertex + """ + + name: Union[int, float, str, ast.AST] + adjs: Optional[List["Vertex"]] + node: ast.AST = None + meta: Optional[Meta] = Meta() + config_meta: Optional[ConfigMeta] = ConfigMeta() + option: Optional[VertexOption] = VertexOption() + + # --------------- + # Member method + # --------------- + + def pretty(self, indent: int = 1) -> str: + """Pretty print to show the vertex structure""" + with StringIO() as buf: + buf.write("name: " + str(self.name) + "\n") + buf.write("is_unique: " + str(self.option.is_unique) + "\n") + if self.config_meta.name: + buf.write("config_name: " + self.config_meta.name + "\n") + if self.adjs: + buf.write("adjs: \n") + for v in self.adjs: + lines = v.pretty(indent).split("\n") + buf.write( + "\n".join([" " * indent * 4 + line for line in lines]) + "\n" + ) + else: + buf.write("value: " + str(self.node)) + return buf.getvalue().rstrip(" \n") + "\n" + + def vertex_to_ast(self) -> Optional[ast.AST]: + """Vertex to KCL AST""" + + def append_config_key_value( + t: Union[ast.SchemaExpr, ast.ConfigExpr], + key: Union[int, float, str, Optional[ast.AST]], + value: ast.AST, + op: int = ast.ConfigEntryOperation.UNION, + meta: Meta = Meta(), + ): + if isinstance(t, ast.SchemaExpr): + if not t.config: + t.config = ast.ConfigExpr(line=t.line, column=t.column) + t.config.filename = meta.filename + if not t.config.items: + t.config.items = [] + # If `key` is None, it may be a double star expr + key_node = None + if key and isinstance(key, ast.AST): + key_node = key + elif isinstance(key, str): + key_node = ast.Identifier( + line=meta.line, column=meta.column, names=[key] + ).set_filename(meta.filename) + key_node.end_line, key_node.end_column = ( + meta.end_line, + meta.end_column, + ) + elif isinstance(key, (int, float)): + key_node = ast.NumberLit( + line=meta.line, column=meta.column, value=key + ) + key_node.end_line, key_node.end_column = ( + meta.end_line, + meta.end_column, + ) + if isinstance(key_node, ast.AST): + key_node.filename = meta.filename + value = cast(ast.Expr, value) + t.config.items.append( + ast.ConfigEntry( + key=key_node, + value=value, + operation=op, + ) + ) + elif isinstance(t, ast.ConfigExpr): + if not t.items: + t.items = [] + # If `key` is None, it may be a double star expr + key_node = None + if key and isinstance(key, ast.AST): + key_node = key + elif isinstance(key, str): + key_node = ast.StringLit( + line=meta.line, column=meta.column, value=key + ) + key_node.end_line, key_node.end_column = ( + meta.end_line, + meta.end_column, + ) + elif isinstance(key, (int, float)): + key_node = ast.NumberLit( + line=meta.line, column=meta.column, value=key + ) + key_node.end_line, key_node.end_column = ( + meta.end_line, + meta.end_column, + ) + if isinstance(key_node, ast.AST): + key_node.filename = meta.filename + key_node = cast(ast.Expr, key_node) + t.items.append( + ast.ConfigEntry( + key=key_node, + value=value, + operation=op, + ) + ) + + # Get root vertex + if isinstance(self.name, str) and self.name == VERTEX_ROOT_NAME: + module = ast.Module(filename=self.meta.filename, line=1, column=1) + for v in self.adjs: + assign_stmt = ast.AssignStmt( + line=v.meta.line, + column=v.meta.column, + ) + assign_stmt.targets = [ + ast.Identifier( + line=v.node.line, + column=v.node.column, + names=[v.name], + ctx=ast.ExprContext.STORE, + ).set_filename(v.node.filename) + ] + assign_stmt.end_line = v.meta.end_line + assign_stmt.end_column = v.meta.end_column + assign_stmt.value = v.vertex_to_ast() + assign_stmt.filename = v.meta.filename + module.body.append(assign_stmt) + return module + # Get normal vertex such as in the right assignment + else: + # SchemaExpr config + if self.config_meta.name: + config_expr = ast.SchemaExpr( + line=self.meta.line, + column=self.meta.column, + ) + name = ast.Identifier( + line=self.meta.line, + column=self.meta.column, + names=self.config_meta.name.split("."), + ).set_filename(self.meta.filename) + name.end_line, name.end_column = ( + self.meta.end_line, + self.meta.end_column, + ) + config_expr.name = name + config_expr.name.pkgpath = self.config_meta.pkgpath + config_expr.args = self.config_meta.args + config_expr.kwargs = self.config_meta.kwargs + # ConfigExpr config + else: + config_expr = ast.ConfigExpr( + line=self.meta.line, + column=self.meta.column, + ) + config_expr.end_line, config_expr.end_column = ( + self.meta.end_line, + self.meta.end_column, + ) + if self.adjs: + for vv in self.adjs: + op = ast.ConfigEntryOperation.UNION + op = ( + ast.ConfigEntryOperation.OVERRIDE + if vv.option.is_override + else op + ) + op = ast.ConfigEntryOperation.INSERT if vv.option.is_append else op + if vv.adjs: + append_config_key_value( + config_expr, vv.name, vv.vertex_to_ast(), op, vv.meta + ) + elif vv.node: + append_config_key_value( + config_expr, vv.name, vv.node, op, vv.meta + ) + return config_expr + elif self.node: + return self.node + return None + + # --------------- + # Static method + # --------------- + + @staticmethod + def update_vertex_option(v: "Vertex", op: int): + """Update the vertex option using the schema config operation""" + if not isinstance(v, Vertex): + return + v.option = VertexOption( + is_override=op == ast.ConfigEntryOperation.OVERRIDE, + is_append=op == ast.ConfigEntryOperation.INSERT, + is_union=op == ast.ConfigEntryOperation.UNION, + ) + + @staticmethod + def ast_to_vertex( + t: ast.AST, + name: Optional[Union[int, float, str, ast.AST]] = None, + is_in_schema: bool = False, + ) -> Optional["Vertex"]: + """Build a vertex from AST""" + if not t or not isinstance(t, ast.AST): + return None + if isinstance(t, ast.Module): + t = cast(ast.Module, t) + root = Vertex.new_root( + node=t, + adjs=[], + ) + declarations = astutil.filter_declarations(t) + for d in declarations: + if "." not in d.name: + vertex = Vertex.ast_to_vertex(d.value, d.name) + vertex.meta.filename = d.filename + vertex.option.is_union = d.is_union + root.adjs.append(vertex) + return root + elif isinstance(t, ast.SchemaExpr): + vertex = Vertex.ast_to_vertex(t.config, name, True) + vertex.meta = Meta.from_ast_node(t) + vertex.name = name + vertex.config_meta = ConfigMeta( + name=t.name.get_name(), + pkgpath=t.name.pkgpath, + args=t.args, + kwargs=t.kwargs, + ) + if not vertex.adjs and isinstance(vertex.node, ast.ConfigExpr): + vertex.node = t + return vertex + elif isinstance(t, ast.ConfigExpr): + vertex = Vertex( + node=t, + name=name, + adjs=[], + meta=Meta.from_ast_node(t), + ) + for key, value, operation in zip( + t.keys, + t.values, + t.operations + if isinstance(t, ast.ConfigExpr) + else [ast.ConfigEntryOperation.UNION] * len(t.keys), + ): + # Double star expression + if not key: + value_vertex = Vertex.ast_to_vertex( + value, name=None, is_in_schema=is_in_schema + ) + value_vertex.node = value + value_vertex.meta = Meta.from_ast_node(value) + Vertex.update_vertex_option(value_vertex, operation) + vertex.adjs.append(value_vertex) + elif isinstance(key, ast.Identifier) and ( + isinstance(t, ast.ConfigExpr) or is_in_schema + ): + nest_key_len = len(key.names) + nest_vertex_list = [ + Vertex( + name=key.names[i], + node=key, + adjs=[], + meta=Meta.from_ast_node(key), + ) + for i in range(nest_key_len - 1) + ] + final_vertex = Vertex.ast_to_vertex( + value, name=key.names[-1], is_in_schema=is_in_schema + ) + final_vertex.meta = Meta.from_ast_node(key) + if nest_key_len > 1: + # Link all vertex in nest vertex list + for i in range(nest_key_len - 2): + nest_vertex_list[i].adjs = [nest_vertex_list[i + 1]] + nest_vertex_list[-1].adjs = [final_vertex] + value_vertex = ( + final_vertex if nest_key_len == 1 else nest_vertex_list[0] + ) + value_vertex.meta = Meta.from_ast_node(key) + if isinstance(value, ast.SchemaExpr): + value_vertex.config_meta = ConfigMeta( + name=value.name.get_name(), + args=value.args, + kwargs=value.kwargs, + ) + Vertex.update_vertex_option( + value_vertex, ast.ConfigEntryOperation.UNION + ) + Vertex.update_vertex_option(final_vertex, operation) + vertex.adjs.append(value_vertex) + else: + # Variable attributes that cannot be clearly expressed + # Such as string interpolation which denotes a runtime value + value_vertex = Vertex.ast_to_vertex( + value, + name=key.value + if isinstance(key, (ast.StringLit, ast.NumberLit)) + else key, + is_in_schema=is_in_schema, + ) + value_vertex.meta = Meta.from_ast_node(key) + Vertex.update_vertex_option(value_vertex, operation) + vertex.adjs.append(value_vertex) + return vertex + # Vertex end node and its adjs is None + return Vertex( + name=name, + node=t, + adjs=None, + meta=Meta.from_ast_node(t), + ) + + @staticmethod + def new_root( + node: ast.Module = None, adjs: Optional[List["Vertex"]] = None + ) -> "Vertex": + """New a empty vertex root node""" + return Vertex( + name=VERTEX_ROOT_NAME, + node=node, + adjs=adjs, + meta=Meta( + filename=node.filename, + line=1, + column=1, + end_line=1, + end_column=1, + ), + ) diff --git a/internal/kclvm_py/vm/__init__.py b/internal/kclvm_py/vm/__init__.py new file mode 100644 index 000000000..328a8f4a0 --- /dev/null +++ b/internal/kclvm_py/vm/__init__.py @@ -0,0 +1,9 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from .vm import VirtualMachine, Run, Frame + +__all__ = [ + "VirtualMachine", + "Run", + "Frame", +] diff --git a/internal/kclvm_py/vm/code/__init__.py b/internal/kclvm_py/vm/code/__init__.py new file mode 100644 index 000000000..06cdb363a --- /dev/null +++ b/internal/kclvm_py/vm/code/__init__.py @@ -0,0 +1,41 @@ +""" The `code` module mainly contains the definition of KCL +bytecode. The bytecode factory is used for display debugging +in the `code_factory` file and the corresponding bytecode +execution function is in the `code_actions` file. + +Each bytecode corresponds to an execution function. For example, +a binary addition operation corresponds to `Opcode.BINARY_ADD`. + +KCL Bytecodes are mainly divided into three categories: +* Stack operation bytecodes +* KCL semantic related bytecode +* Internal bytecodes for debugging + +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" + +from .code import ( + Opcode, + Label, + JumpAbs, + JumpRel, + Instruction, + InstructionWithArg, + EmittedInstruction, + CompilationScope, +) +from .code_factory import SchemaBodyOpcodeFactory +from .code_actions import VM_OP_ACTIONS + +__all__ = [ + "Opcode", + "Label", + "Instruction", + "InstructionWithArg", + "JumpAbs", + "JumpRel", + "EmittedInstruction", + "CompilationScope", + "SchemaBodyOpcodeFactory", + "VM_OP_ACTIONS", +] diff --git a/internal/kclvm_py/vm/code/code.py b/internal/kclvm_py/vm/code/code.py new file mode 100644 index 000000000..ba04d02aa --- /dev/null +++ b/internal/kclvm_py/vm/code/code.py @@ -0,0 +1,255 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from dataclasses import dataclass +from typing import List, Optional +from enum import IntEnum + + +class Opcode(IntEnum): + """Opcode class is used in KCL virtual machine. + + Virtual machine operator code can be divided into two categories + - Stack opcode + - Semantics opcode + """ + + INVALID = -1 # Invalid opcode + + POP_TOP = 1 # Stack pops 1 operand from the top of the stack + ROT_TWO = 2 # Stack rotates 2 operands + ROT_THREE = 3 # Stack rotates 3 operands + DUP_TOP = 4 # Copy the reference of the top element of the Stack to a copy and put it on the top of the stack + DUP_TOP_TWO = 5 # Copy the references of the first two elements on the top of the Stack to one copy and put it on the top of the stack. + COPY_TOP = 6 # Copy the value of the top element of the Stack to a copy and put it on the top of the stack + NOP = 9 # No operation + + UNARY_POSITIVE = 10 # +a + UNARY_NEGATIVE = 11 # -a + UNARY_NOT = 12 # not a + UNARY_INVERT = 13 # ~a + + MEMBER_SHIP_AS = 16 # a as b + + BINARY_POWER = 20 # a ** b + BINARY_MULTIPLY = 21 # a * b + BINARY_MODULO = 22 # a % b + BINARY_ADD = 23 # a + b + BINARY_SUBTRACT = 24 # a - b + BINARY_SUBSCR = 25 # b[a] + BINARY_FLOOR_DIVIDE = 26 # a // b + BINARY_TRUE_DIVIDE = 27 # a / b + BINARY_LSHIFT = 28 # `a << b` + BINARY_RSHIFT = 29 # `a >> b` + BINARY_AND = 30 # `a & b` + BINARY_XOR = 31 # `a ^ b` + BINARY_OR = 32 # `a | b` + BINARY_LOGIC_AND = 33 # `a and b` + BINARY_LOGIC_OR = 34 # `a or b` + + INPLACE_FLOOR_DIVIDE = 40 # a //= b + INPLACE_TRUE_DIVIDE = 41 # a /= b + INPLACE_ADD = 42 # a += b + INPLACE_SUBTRACT = 43 # `a -= b` + INPLACE_MULTIPLY = 44 # `a *= b` + INPLACE_MODULO = 45 # a %= b + INPLACE_POWER = 46 # a **= b + INPLACE_LSHIFT = 47 # a <<= b + INPLACE_RSHIFT = 48 # a >>= b + INPLACE_AND = 49 # a &= b + INPLACE_XOR = 50 # a ^= b + INPLACE_OR = 51 # a |= b + + COMPARE_EQUAL_TO = 60 # a == b + COMPARE_NOT_EQUAL_TO = 61 # a != b + COMPARE_LESS_THAN = 62 # a < b + COMPARE_LESS_THAN_OR_EQUAL_TO = 63 # a <= b + COMPARE_GREATER_THAN = 64 # a > b + COMPARE_GREATER_THAN_OR_EQUAL_TO = 65 # a >= b + COMPARE_IS = 66 # a is b + COMPARE_IS_NOT = 67 # a is not b + COMPARE_IN = 68 # a in b + COMPARE_NOT_IN = 69 # a not in b + + STORE_MAP = 70 # Put a dict entry into the dict object + STORE_SUBSCR = 71 # Put a subscript of collection + DELETE_SUBSCR = 72 # Delete a subscript of collection + BUILD_SCHEMA_CONFIG = 73 # Generate an empty schema config object + STORE_SCHEMA_CONFIG = 74 # Put a schema config entry into the schema config object + + PRINT_EXPR = 80 # Print expression to stdout + EMIT_EXPR = 81 # Emit a schema expression to the output + + SCHEMA_NOP = 90 # Expressions in the schema interval operation + RETURN_VALUE = 91 # Return value in the schema + RETURN_LAST_VALUE = 92 # Return the last value in the lambda expression + + HAVE_ARGUMENT = 99 # Opcodes from here have an argument + + STORE_NAME = 100 # Index in name list + UNPACK_SEQUENCE = 101 # Number of sequence items + GET_ITER = 102 # Get a element from a iterator + FOR_ITER = 103 # Get a iterator from str/list/dict/schema + STORE_ATTR = 105 # Index in name list + DELETE_ATTR = 106 # Delete a attribute + STORE_GLOBAL = 107 # Store a global variable + DELETE_GLOBAL = 108 # Delete a global variable + LOAD_GLOBAL = 109 # Index in name list + LOAD_CONST = 110 # Index in const list + LOAD_NAME = 111 # Index in name list + LOAD_LOCAL = 112 # Local variable number + STORE_LOCAL = 113 # Local variable number + DELETE_LOCAL = 114 # Local variable number + LOAD_FREE = 115 # Load from closure cell + STORE_FREE = 116 # Store into cell + DELETE_FREE = 117 # Delete closure cell + BUILD_TUPLE = 118 # Number of tuple items + BUILD_LIST = 119 # Number of list items + BUILD_SET = 120 # Number of set items + BUILD_MAP = 121 # Always zero for now + LOAD_ATTR = 123 # Index in name list + LOAD_BUILT_IN = 124 # Index in built-in list + IMPORT_NAME = 126 # Index in name list + COMPARE_OP = 127 # Comparison operator + + JUMP_FORWARD = 130 # Number of bytes to skip + JUMP_IF_FALSE_OR_POP = 131 # Target byte offset from beginning of code + JUMP_IF_TRUE_OR_POP = 132 # Target byte offset from beginning of code + JUMP_ABSOLUTE = 133 # Target byte offset from beginning of code + POP_JUMP_IF_FALSE = 134 # Target byte offset from beginning of code + POP_JUMP_IF_TRUE = 135 # Target byte offset from beginning of code + + CALL_FUNCTION = 140 # #args + (#kwargs<<8) CALL_FUNCTION_XXX opcodes defined below depend on this definition + MAKE_FUNCTION = 141 # #defaults + #kwdefaults<<8 + #annotations<<16 + BUILD_SLICE = 142 # Number of items + MAKE_CLOSURE = 143 # same as MAKE_FUNCTION + LOAD_CLOSURE = 144 # Load free variable from closure + RAISE_VARARGS = 145 # Number of raise arguments (1, 2 or 3) + RAISE_CHECK = 146 # Expressions in the check block + + LIST_APPEND = 150 # Append a item into the list used in the comprehension + SET_ADD = 151 # Append a item into the set used in the comprehension + MAP_ADD = 152 # Append a item into the dict used in the comprehension + DELETE_ITEM = 153 # Delete a item into the dict used in the filter expression + + MAKE_SCHEMA = 160 # Build schema construct function + BUILD_SCHEMA = 161 # Build a schema instance + LOAD_BUILD_SCHEMA = 162 # Load schema + SCHEMA_ATTR = 163 # Declare a schema attribute + SCHEMA_LOAD_ATTR = 164 # Load attribute in the schema + SCHEMA_UPDATE_ATTR = 165 # Update attribute in the schema + MAKE_DECORATOR = 166 # Build a decorator in the schema + + FORMAT_VALUES = 170 # Format value in the string interpolation + + DEBUG_STACK = 180 # Debug stack + DEBUG_LOCALS = 181 # Debug VM locals + DEBUG_GLOBALS = 182 # Debug VM globals + DEBUG_NAMES = 183 # Debug VM names + + @staticmethod + def has_arg(code: int) -> bool: + return code > Opcode.HAVE_ARGUMENT + + +@dataclass +class Pos: + number: int = 0 + pos: int = 0 + filename: str = None + lineno: int = None + colno: int = None + + def get_pos(self) -> int: + return self.pos + + def get_number(self) -> int: + return self.number + + def get_lineno(self) -> int: + return self.lineno + + def set_lineno(self, lineno: int): + self.lineno = lineno + + def set_pos(self, number: int, pos: int) -> bool: + self.number = number + old_pos = self.pos + self.pos = pos + return old_pos != pos + + +@dataclass +class Instruction(Pos): + op: Opcode = None + + @staticmethod + def size() -> int: + return 1 + + def output(self) -> List[int]: + return [self.op] + + +@dataclass +class Label(Pos): + @staticmethod + def size() -> int: + return 0 + + def output(self) -> List[int]: + return [] + + def stack_effect(self): + return 0 + + +@dataclass +class InstructionWithArg(Instruction): + arg: int = 0 + + def byte(self, arg) -> int: + return arg & 0xFF + + @staticmethod + def size() -> int: + return 4 + + def output(self) -> List[int]: + out = [ + self.byte(self.op), + self.byte(self.arg), + self.byte(self.arg >> 8), + self.byte(self.arg >> 16), + ] + return out + + +@dataclass +class JumpRel(InstructionWithArg): + dest: Label = Label() + + def output(self) -> List[int]: + self.arg = self.dest.get_pos() + return super().output() + + +@dataclass +class JumpAbs(InstructionWithArg): + dest: Label = Label() + + def output(self) -> List[int]: + self.arg = self.dest.get_pos() + return super().output() + + +@dataclass +class EmittedInstruction: + opcode: Opcode = Opcode.INVALID + position: int = 0 + + +@dataclass +class CompilationScope: + instructions: list + last_instruction: Optional[EmittedInstruction] = EmittedInstruction() + previous_instruction: Optional[EmittedInstruction] = EmittedInstruction() diff --git a/internal/kclvm_py/vm/code/code_actions.py b/internal/kclvm_py/vm/code/code_actions.py new file mode 100644 index 000000000..5bb3618a8 --- /dev/null +++ b/internal/kclvm_py/vm/code/code_actions.py @@ -0,0 +1,1088 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import time +from typing import Optional +from copy import deepcopy + +import kclvm.api.object as obj +import kclvm.kcl.error as kcl +import kclvm.kcl.ast as ast +import kclvm.kcl.types as types +import kclvm.config +import kclvm.compiler.extension.builtin + +from kclvm.vm.runtime.evaluator.eval import Evaluator +from kclvm.vm.code import Opcode +from kclvm.compiler.check.check_type import ( + type_pack_and_check, + runtime_types, +) +from kclvm.unification import value_subsume + +_evaluator_inst = Evaluator() + + +def is_kcl_debug(): + return kclvm.config.debug and kclvm.config.verbose > 2 + + +def debug_stack(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + idx = int(arg & 0xFF) + at = int((arg >> 8) & 0xFF) + return vm.debug_stack(idx, at) + + +def debug_locals(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + idx = int(arg & 0xFF) + at = int((arg >> 8) & 0xFF) + return vm.debug_locals(idx, at) + + +def debug_globals(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + idx = int(arg & 0xFF) + at = int((arg >> 8) & 0xFF) + return vm.debug_globals(idx, at) + + +def debug_names(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + idx = int(arg & 0xFF) + at = int((arg >> 8) & 0xFF) + return vm.debug_names(idx, at) + + +def unpack_operand(arg: int): + return int(arg & 0xFF), int((arg >> 8) & 0xFF), int((arg >> 16) & 0xFF) + + +def bin_action(vm, code: int, _arg: int) -> Optional[obj.KCLObject]: + right, left = vm.pop(), vm.top() + result = _evaluator_inst.eval_binary_op(left, right, code, vm=vm) + vm.set_top(result) + return result + + +def unary_action(vm, code: int, _arg: int) -> Optional[obj.KCLObject]: + expr_obj = vm.top() + result = _evaluator_inst.eval_unary_op(expr_obj, code) + vm.set_top(result) + return result + + +def inplace_action(vm, code: int, _arg: int) -> Optional[obj.KCLObject]: + value, target = vm.pop(), vm.top() + result = _evaluator_inst.eval_inplace_op(target, value, code, vm=vm) + vm.set_top(result) + return result + + +def bin_add(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def bin_substract(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def bin_floor_divide(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def bin_true_divide(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def build_map(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + dict_obj = obj.KCLDictObject(value={}) + vm.push(dict_obj) + return dict_obj + + +def load_free(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + free_obj = obj.NONE_INSTANCE + index = vm.frame_index + name = vm.names[arg] + # Search the local variables from the inside to the outside schema + while index >= 1: + index -= 1 + if name in vm.frames[index].locals: + free_obj = vm.frames[index].locals[name] + vm.push(free_obj) + return free_obj + + +def load_closure(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + # free_obj = vm.ctx.free_vars[arg] + closure_obj = ( + vm.ctx.free_vars[arg] if arg < len(vm.ctx.free_vars) else obj.UNDEFINED_INSTANCE + ) + vm.push(closure_obj) + return closure_obj + + +def make_function(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return make_compiled_function(vm, code, arg) + + +def make_closure(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return make_compiled_function(vm, code, arg, is_closure=True) + + +def make_compiled_function( + vm, _code: int, arg: int, is_closure: bool = False +) -> Optional[obj.KCLObject]: + """ + Pushes a new function object on the stack. From bottom to top, the consumed stack must consist of values if the argument carries a specified flag value + + 0x01 a tuple of default values for positional-only and positional-or-keyword parameters in positional order + + 0x02 a dictionary of keyword-only parameters’ default values + + 0x04 an annotation dictionary + + 0x08 a tuple containing cells for free variables, making a closure + + the code associated with the function (at TOS1) + + the qualified name of the function (at TOS) + """ + # Pop the function name + name = vm.pop().value + # Pop code placeholder + runtime_code = vm.pop() + closure = vm.pop() if is_closure else None + args, _, _ = unpack_operand(arg) + # var params []grumpy.Param + params: list = [0] * args + index = 0 + for index in range(args): + # positional args placeholders to fit grumpy arg validate + arg_value = vm.pop() + arg_type = vm.pop().value + arg_name = vm.pop().value + params[index] = obj.Parameter( + name=arg_name, type_annotation=arg_type, value=arg_value + ) + + func = obj.KCLCompiledFunctionObject( + name=name, + instructions=runtime_code.codes, + names=runtime_code.names, + constants=runtime_code.constants, + num_locals=0, + num_parameters=arg, + params=params[::-1], + pkgpath=vm.cur_run_pkg, + closure=closure, + ) + vm.push(func) + return func + + +def pop_top(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + return vm.pop() + + +def rot_two(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + v = vm.pop() + w = vm.pop() + vm.push(v) + vm.push(w) + return w + + +def rot_three(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + v = vm.pop() + w = vm.pop() + u = vm.pop() + vm.push(v) + vm.push(w) + vm.push(u) + return u + + +def dup_top(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + vm.push(vm.peek()) + return vm.peek() + + +def copy_top(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + vm.push(deepcopy(vm.peek())) + return vm.peek() + + +def dup_top_two(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + v = vm.pop() + w = vm.pop() + vm.push(w) + vm.push(v) + vm.push(w) + vm.push(v) + return v + + +def nop(_vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + """Empty opcode action""" + pass + + +def unary_pos(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return unary_action(vm, code, arg) + + +def unary_neg(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return unary_action(vm, code, arg) + + +def unary_not(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return unary_action(vm, code, arg) + + +def unary_invert(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return unary_action(vm, code, arg) + + +def bin_power(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def bin_mul(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def bin_mod(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def bin_subscr(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def inplace_floor_div(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return inplace_action(vm, code, arg) + + +def inplace_true_div(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return inplace_action(vm, code, arg) + + +def store_map(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + v, k = vm.pop(), vm.pop() + config_ref = vm.peek() + if v.type() == obj.KCLObjectType.UNPACK and k.type() in [ + obj.KCLObjectType.NONE, + obj.KCLObjectType.UNDEFINED, + ]: + config_ref.append_unpack(v.unpack()) + else: + config_ref.update_key_value(k, v) + return config_ref + + +def inplace_add(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return inplace_action(vm, code, arg) + + +def inplace_sub(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return inplace_action(vm, code, arg) + + +def inplace_mul(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return inplace_action(vm, code, arg) + + +def inplace_mod(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return inplace_action(vm, code, arg) + + +def store_subscr(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + """v[w] = u""" + w = vm.pop() + v = vm.pop() + u = vm.pop() + _evaluator_inst.set_item(v, w, u) + return v + + +def bin_lshift(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def bin_rshift(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def bin_and(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def bin_xor(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def bin_or(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def bin_logic_and(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def bin_logic_or(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return bin_action(vm, code, arg) + + +def inplace_pow(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return inplace_action(vm, code, arg) + + +def get_iter(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + assert 0 < _arg <= 2 + iter_obj = obj.KCLIterObject.build_iter(vm.top(), _arg) + vm.set_top(iter_obj) + return None + + +def print_expr(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + expr_obj = vm.pop() + print(expr_obj.value) + return expr_obj + + +def emit_expr(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + emitted_obj = vm.pop() + if isinstance(emitted_obj, obj.KCLSchemaObject): + output_type = ( + emitted_obj.get(obj.SCHEMA_SETTINGS_ATTR_NAME) + .get(obj.SETTINGS_OUTPUT_KEY) + .value + ) + if ( + output_type == obj.SETTINGS_OUTPUT_STANDALONE + or output_type == obj.SETTINGS_OUTPUT_IGNORE + ): + # Internal magic variable + vm.update_global(f"$EMIT_VAR_{time.time()}", emitted_obj) + return emitted_obj + + +def inplace_lshift(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return inplace_action(vm, code, arg) + + +def inplace_rlshift(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return inplace_action(vm, code, arg) + + +def inplace_and(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return inplace_action(vm, code, arg) + + +def inplace_xor(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return inplace_action(vm, code, arg) + + +def inplace_or(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + return inplace_action(vm, code, arg) + + +def return_value(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + vm.pop_frame() + return vm.peek() + + +def return_last_value(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + frame = vm.pop_frame() + variables = list(frame.locals.values()) + return_obj = variables[-1] if variables else obj.NONE_INSTANCE + vm.push(return_obj) + return return_obj + + +def store_name(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + return vm.store_name(arg) + + +def unpack_sequence(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + sequence_obj = vm.pop() + unpack_obj = obj.KCLUnpackObject(sequence_obj, arg != 1) + vm.push(unpack_obj) + return unpack_obj + + +def for_iter(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + """ + TOS is an iterator. Call its iter_next() method. If this yields a new value, + push it on the stack (leaving the iterator below it). + If the iterator indicates it is exhausted TOS is popped, + and the byte code counter is incremented by 'arg' argument. + """ + try: + iter_next_obj = _evaluator_inst.iter_next(vm.peek()) + assert isinstance(iter_next_obj, obj.KCLTupleObject) + # Push loop variables + for o in iter_next_obj.value[::-1]: + vm.push(obj.to_kcl_obj(o)) + except StopIteration: + it = vm.pop() + assert isinstance(it, obj.KCLIterObject) + vm.set_instruction_pointer(arg) + except Exception as err: + kcl.report_exception( + err_type=kcl.ErrType.EvaluationError_TYPE, arg_msg=str(err) + ) + + return None + + +def store_attr(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + """v.w = u""" + w = vm.names[arg] + v = vm.pop() + u = vm.pop() + + _evaluator_inst.set_attr(v, obj.KCLStringObject(w), u, vm=vm) + + return v + + +def store_global(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + return vm.store_name(arg) + + +def load_const(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + return vm.load_const(arg) + + +def load_name(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + return vm.load_name(arg) + + +def build_list(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + items = [vm.pop() for _i in range(arg)][::-1] + list_obj = obj.KCLListObject(items=[]) + for item in items: + if item.type() == obj.KCLObjectType.UNPACK: + list_obj.append_unpack(item.unpack()) + else: + list_obj.append(item) + vm.push(list_obj) + return list_obj + + +def schema_attr(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + """ + Define a schema attr into schema type object + """ + n_decorators = arg + decorators_obj = [vm.pop() for _i in range(n_decorators)][::-1] + expected_types = [vm.pop().value] + attr, value, has_default, is_final, is_optional, op_code = ( + vm.pop().value, + vm.pop(), + vm.pop().value, + vm.pop().value, + vm.pop().value, + vm.pop().value, + ) + config_meta = vm.lazy_eval_ctx.config_meta + config_obj = vm.ctx.locals[obj.SCHEMA_CONFIG_VALUE_KEY] + schema_obj = vm.ctx.locals[obj.SCHEMA_SELF_VALUE_KEY] + config_value = config_obj.get(attr) + schema_obj.set_attr_type(attr, expected_types) + schema_obj.set_attr_optional(attr, is_optional) + attr_runtime_types = runtime_types(expected_types, vm=vm) + if attr_runtime_types: + schema_obj.set_attr_runtime_type(attr, attr_runtime_types) + for decorator in decorators_obj: + schema_obj.add_decorator(attr, decorator) + + operation = ( + config_obj.get_operation(attr) + if isinstance(config_obj, obj.KCLSchemaConfigObject) + else ast.ConfigEntryOperation.UNION + ) + insert_index = ( + config_obj.get_insert_index(attr) + if isinstance(config_obj, obj.KCLSchemaConfigObject) + else None + ) + + # Update and union with config_value + if has_default: + target = ( + schema_obj.get(attr) if attr in schema_obj else obj.KCLNoneObject.instance() + ) + if op_code is None: + op_value = value + else: + op_value = _evaluator_inst.eval_inplace_op(target, value, op_code, vm=vm) + op_value = type_pack_and_check(op_value, expected_types, vm=vm) + if schema_obj.get_immutable_flag(attr) and target.value != op_value.value: + kcl.report_exception( + err_type=kcl.ErrType.ImmutableRuntimeError_TYPE, + arg_msg=f"final schema field '{attr}'", + ) + vm.lazy_eval_ctx.set_value(attr, op_value) + elif attr not in schema_obj: + schema_obj.update({attr: obj.KCLUndefinedObject.instance()}) + + if attr in config_obj: + conf_meta = config_meta.get(attr, {}) + filename = conf_meta.get("filename") + lineno = conf_meta.get("lineno") + columnno = conf_meta.get("columnno") + if is_final and obj.to_python_obj(config_value) != obj.to_python_obj( + schema_obj.attrs.get(attr) + ): + kcl.report_exception( + err_type=kcl.ErrType.ImmutableRuntimeError_TYPE, + file_msgs=[ + kcl.ErrFileMsg(filename=filename, line_no=lineno, col_no=columnno) + ], + arg_msg=f"final schema field '{attr}'", + ) + _evaluator_inst.update_schema_attr( + attr=attr, + schema_obj=schema_obj, + config_value=config_value, + conf_meta=conf_meta, + expected_types=expected_types, + operation=operation, + index=insert_index, + vm=vm, + filename=filename, + lineno=lineno, + columnno=columnno, + ) + vm.lazy_eval_ctx.set_value(attr, schema_obj.get(attr)) + + # Update mutable flag + schema_obj.set_immutable_flag(attr, is_final) + vm.update_local(attr, schema_obj.get(attr)) + return schema_obj + + +def schema_update_attr(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + name = vm.pop().value + value = vm.pop() + + schema_obj = vm.ctx.locals[obj.SCHEMA_SELF_VALUE_KEY] + config_obj = vm.ctx.locals[obj.SCHEMA_CONFIG_VALUE_KEY] + config_meta = vm.lazy_eval_ctx.config_meta + config_value = config_obj.get(name) + + # A private schema attribute can be added into schema object whether it's relaxed or not + if schema_obj.get_immutable_flag(name): + kcl.report_exception( + err_type=kcl.ErrType.ImmutableRuntimeError_TYPE, + arg_msg=f"final schema field '{name}'", + ) + + conf_meta = config_meta.get(name, {}) + filename = conf_meta.get("filename") + lineno = conf_meta.get("lineno") + columnno = conf_meta.get("columnno") + if name not in schema_obj or not schema_obj.get_attr_type(name): + schema_obj.set_attr_optional(name, True) + vm.lazy_eval_ctx.set_value( + name, type_pack_and_check(value, schema_obj.get_attr_type(name), vm=vm) + ) + + if name in config_obj: + config_value = type_pack_and_check( + config_value, + schema_obj.get_attr_type(name), + vm=vm, + filename=filename, + lineno=lineno, + columno=columnno, + config_meta=conf_meta.get("$conf_meta"), + ) + cfg_obj = obj.KCLSchemaConfigObject(value={name: config_value}) + cfg_obj.update_attr_op_using_obj(config_obj) + schema_obj.union_with(cfg_obj) + vm.lazy_eval_ctx.set_value(name, schema_obj.get(name)) + + +def make_decorator(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + decorator = vm.pop() + assert isinstance(decorator, obj.KCLDecoratorObject) + args, kwargs = _evaluator_inst.call_vars_and_keywords(arg, vm) + decorator.resolve(args, kwargs) + vm.push(decorator) + return decorator + + +def schema_load_attr(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + name_obj = vm.top() + vm.set_top(vm.lazy_eval_ctx.get_value(name_obj.value)) + return name_obj + + +def import_name(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + return vm.import_name(_code, arg) + + +def jump_forward(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + vm.set_instruction_pointer(arg) + return None + + +def jump_if_false_or_pop(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + condition_obj = vm.peek() + if not obj.KCLObject.is_truthy(condition_obj): + vm.set_instruction_pointer(arg) + else: + vm.pop() + return None + + +def jump_if_true_or_pop(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + if obj.KCLObject.is_truthy(vm.peek()): + vm.set_instruction_pointer(arg) + else: + vm.pop() + return None + + +def jump_absolute(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + vm.set_instruction_pointer(arg) + return None + + +def pop_jump_if_false(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + condition_obj = vm.pop() + if not obj.KCLObject.is_truthy(condition_obj): + vm.set_instruction_pointer(arg) + return None + + +def pop_jump_if_true(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + condition_obj = vm.pop() + if obj.KCLObject.is_truthy(condition_obj): + vm.set_instruction_pointer(arg) + return None + + +def load_global(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + vm.load_name(arg) + return None + + +def raise_varargs(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + if arg != 1: + raise Exception(f"invalid raise_varargs opcode arg {arg}") + msg_obj = vm.pop() + msg = msg_obj.value if msg_obj.type() == obj.KCLObjectType.STRING else "" + if arg == 1: + kcl.report_exception( + err_type=kcl.ErrType.AssertionError_TYPE, arg_msg=msg if msg else "" + ) + return None + + +def raise_check(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + if arg != 1: + raise Exception(f"invalid raise_check opcode arg {arg}") + msg_obj = vm.pop() + msg = msg_obj.value if msg_obj.type() == obj.KCLObjectType.STRING else "" + if arg == 1: + config_meta = obj.to_python_obj(vm.ctx.locals[obj.SCHEMA_CONFIG_META_KEY]) + conf_filename, conf_line, conf_column = ( + config_meta.get("$filename"), + config_meta.get("$lineno"), + config_meta.get("$columnno"), + ) + filename, line, _ = vm.get_info(True) + kcl.report_exception( + err_type=kcl.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl.ErrFileMsg( + filename=filename, + line_no=line, + arg_msg=kcl.SCHEMA_CHECK_FILE_MSG_COND, + ), + kcl.ErrFileMsg( + filename=conf_filename, + line_no=conf_line, + col_no=conf_column, + arg_msg=kcl.SCHEMA_CHECK_FILE_MSG_ERR, + ), + ], + arg_msg=msg if msg else "", + ) + return None + + +def load_local(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + vm.load_local(arg) + return None + + +def store_local(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + vm.store_local(arg) + return None + + +def call_function(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + _evaluator_inst.eval_call(code, arg, vm) + return None + + +def build_slice(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + if arg != 2 and arg != 3: + raise Exception(f"invalid slice argc {arg}") + step = obj.KCLNoneObject.instance() + if arg == 3: + step = vm.pop() + stop = vm.pop() + start = vm.top() + vm.set_top(obj.KCLSliceObject(start=start, stop=stop, step=step)) + return None + + +def list_append(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + item = vm.pop() + list_obj = vm.peek_nth(arg) + _evaluator_inst.list_append(list_obj, item) + return list_obj + + +def map_add(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + operation = vm.pop().value + key = vm.pop() + value = vm.pop() + config_ref = vm.peek_nth(arg) + data_obj = obj.KCLSchemaConfigObject(value={key.value: value}) + if operation is None or operation == ast.ConfigEntryOperation.UNION: + config_ref.union_with(data_obj) + elif operation == ast.ConfigEntryOperation.OVERRIDE: + config_ref.update(data_obj) + elif operation == ast.ConfigEntryOperation.INSERT: + config_ref.insert_with(data_obj, -1) + else: + config_ref.update(data_obj) + return config_ref + + +def delete_item(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + collection_obj = vm.peek_nth(arg) + is_two_var = vm.pop().value + value = vm.pop() + item = vm.pop() + if isinstance(collection_obj, (obj.KCLSchemaObject, obj.KCLDictObject)): + collection_obj.delete(item) + elif isinstance(collection_obj, obj.KCLListObject): + if is_two_var: + collection_obj.remove(value) + else: + collection_obj.remove(item) + else: + kcl.report_exception( + err_type=kcl.ErrType.EvaluationError_TYPE, + arg_msg=f"illegal quantifier expression type '{collection_obj.type_str()}'", + ) + return collection_obj + + +def schema_nop(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + """Schema nop between scheam update attributes""" + schema_obj = vm.ctx.locals[obj.SCHEMA_SELF_VALUE_KEY] + return schema_obj + + +def make_schema(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + """ + Stack Layout + ------------ + TOS + - 6. index signature + - 5. decorator list + - 4. check func + - 3. schema_body_func + - 2. mixin type object list + - 1. parent_type_obj + - 0. schema_type_obj + """ + decorator_count, mixin_count, _ = unpack_operand(arg) + index_signature = None + index_signature_key_type = vm.pop().value + if index_signature_key_type: + index_signature = obj.KCLSchemaIndexSignatureObject( + key_type=index_signature_key_type, + value_type=vm.pop().value, + key_name=vm.pop().value, + any_other=vm.pop().value, + value=vm.pop(), + ) + decorators = [vm.pop() for _i in range(decorator_count)][::-1] + schema_check_func = vm.pop() + schema_body_func = vm.pop() + mixins = [vm.pop() for _i in range(mixin_count)][::-1] + parent_schema_type = vm.pop() + if parent_schema_type.type() not in [ + obj.KCLObjectType.SCHEMA_TYPE, + obj.KCLObjectType.NONE, + obj.KCLObjectType.UNDEFINED, + ]: + kcl.report_exception( + err_type=kcl.ErrType.EvaluationError_TYPE, + arg_msg="illegal schema inherit object type", + ) + self_schema_type = vm.pop() + + type_obj = obj.KCLSchemaTypeObject.new( + self_schema_type.name, + parent_schema_type + if isinstance(parent_schema_type, obj.KCLSchemaTypeObject) + else None, + self_schema_type.protocol, + filename=vm.get_info()[0], + is_mixin=self_schema_type.is_mixin, + pkgpath=self_schema_type.pkgpath, + attr_list=self_schema_type.attr_list, + index_signature=index_signature, + is_relaxed=bool(index_signature), + vm=vm, + ) + + vm.define_schema_type( + f"{self_schema_type.pkgpath}.{self_schema_type.name}", type_obj + ) + + if isinstance(schema_body_func, obj.KCLCompiledFunctionObject): + schema_body_func.name = self_schema_type.name + type_obj.set_func(schema_body_func) + else: + assert isinstance(schema_body_func, (obj.KCLNoneObject, obj.KCLUndefinedObject)) + + if isinstance(schema_check_func, obj.KCLCompiledFunctionObject): + schema_check_func.name = self_schema_type.name + type_obj.set_check_func(schema_check_func) + else: + assert isinstance( + schema_check_func, (obj.KCLNoneObject, obj.KCLUndefinedObject) + ) + + type_obj.add_decorators(decorators) + + if len(mixins) > 0: + if isinstance(mixins[0], obj.KCLStringObject): + type_obj.mixins_names = [x.value for x in mixins] + elif isinstance(mixins[0], obj.KCLSchemaTypeObject): + type_obj.mixins = mixins + else: + assert False, f"make_schema: mixins[0] type: {type(mixins[0])}" + type_obj.update_mixins(vm=vm) + vm.push(type_obj) + return type_obj + + +def build_schema(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + schema_obj = vm.pop() + config_obj = vm.pop() + config_meta = obj.to_python_obj(vm.pop()) + args, kwargs = _evaluator_inst.call_vars_and_keywords(arg, vm) + # Schema type object + if isinstance(schema_obj, obj.KCLSchemaTypeObject): + if isinstance(config_obj, obj.KCLDictObject): + inst = schema_obj.new_instance(config_obj, config_meta, args, kwargs, vm) + else: + inst = config_obj + # Schema value object + else: + inst = _evaluator_inst.eval_binary_op( + schema_obj, config_obj, Opcode.BINARY_OR, vm=vm + ) + vm.push(inst) + return inst + + +def build_schema_config(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + config_obj = obj.KCLSchemaConfigObject( + value={}, operation_map={}, insert_index_map={} + ) + vm.push(config_obj) + return config_obj + + +def store_schema_config(vm, _code: int, _arg: int) -> Optional[obj.KCLObject]: + insert_index, operation, is_nest_key = ( + vm.pop().value, + vm.pop().value, + vm.pop().value, + ) + v, k = vm.pop(), vm.pop() + config_ref = vm.peek() + if v.type() == obj.KCLObjectType.UNPACK and k.type() in [ + obj.KCLObjectType.NONE, + obj.KCLObjectType.UNDEFINED, + ]: + config_ref.append_unpack(v.unpack()) + else: + # Deal the nest var config e.g., {a.b.c: "123"} -> {a: {b: {c: "123"}}} + if k.type() == obj.KCLObjectType.STRING and is_nest_key: + data_obj = obj.KCLSchemaConfigObject() + obj_ref = data_obj + nest_keys = k.value.split(".") + for i, key in enumerate(nest_keys): + obj_ref.value[key] = ( + v if i == len(nest_keys) - 1 else obj.KCLSchemaConfigObject() + ) + if i == len(nest_keys) - 1: + obj_ref.add_operation(key, operation, insert_index) + obj_ref = obj_ref.value[key] + operation = ast.ConfigEntryOperation.UNION + insert_index = None + else: + data_obj = obj.KCLSchemaConfigObject(value={k.value: v}) + config_ref.add_operation(k.value, operation, insert_index) + if operation is None or operation == ast.ConfigEntryOperation.UNION: + config_ref.union_with(data_obj) + elif operation == ast.ConfigEntryOperation.OVERRIDE: + config_ref.update(data_obj) + elif operation == ast.ConfigEntryOperation.INSERT: + config_ref.insert_with(data_obj, insert_index) + elif operation == ast.ConfigEntryOperation.UNIQUE: + config_ref.unique_merge_with(data_obj) + elif operation == ast.ConfigEntryOperation.UNIFICATION: + if value_subsume(data_obj, config_ref): + config_ref.union_with(data_obj) + else: + kcl.report_exception( + err_type=kcl.ErrType.EvaluationError_TYPE, + arg_msg="unification conflict", + ) + else: + config_ref.union_with(data_obj) + return config_ref + + +def load_attr(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + attr_obj = vm.top() + name = vm.names[arg] + value = _evaluator_inst.load_attr(attr_obj, name) + vm.set_top(value) + return value + + +def load_builtin(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + return vm.load_builtin(arg) + + +def format_values(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + assert arg >= 0 + formatted_str_obj_list = [] + format_spec = vm.pop().value + for i in range(arg): + format_obj = vm.pop() + format_value = _evaluator_inst.format_value(format_obj, format_spec) + formatted_str_obj_list.append(format_value) + for str_obj in formatted_str_obj_list[::-1]: + vm.push(str_obj) + return vm.peek() + + +def member_ship_as(vm, code: int, arg: int) -> Optional[obj.KCLObject]: + # Pop the type object + type_object = vm.pop() + vm.set_top(types.type_convert(vm.top(), type_object)) + return vm.top() + + +def compare_op(vm, _code: int, arg: int) -> Optional[obj.KCLObject]: + left, right = vm.pop(), vm.top() + result = _evaluator_inst.eval_compare_op(left, right, arg, vm=vm) + vm.set_top(result) + return result + + +VM_OP_ACTIONS = { + Opcode.BINARY_ADD: bin_add, + Opcode.BINARY_SUBTRACT: bin_substract, + Opcode.BINARY_MULTIPLY: bin_mul, + Opcode.BINARY_FLOOR_DIVIDE: bin_floor_divide, + Opcode.BINARY_TRUE_DIVIDE: bin_true_divide, + Opcode.BUILD_SCHEMA_CONFIG: build_schema_config, + Opcode.STORE_SCHEMA_CONFIG: store_schema_config, + Opcode.POP_TOP: pop_top, + Opcode.ROT_TWO: rot_two, + Opcode.ROT_THREE: rot_three, + Opcode.DUP_TOP: dup_top, + Opcode.DUP_TOP_TWO: dup_top_two, + Opcode.COPY_TOP: copy_top, + Opcode.NOP: nop, + Opcode.UNARY_POSITIVE: unary_pos, + Opcode.UNARY_NEGATIVE: unary_neg, + Opcode.UNARY_NOT: unary_not, + Opcode.UNARY_INVERT: unary_invert, + Opcode.BINARY_POWER: bin_power, + Opcode.BINARY_MODULO: bin_mod, + Opcode.BINARY_SUBSCR: bin_subscr, + Opcode.INPLACE_FLOOR_DIVIDE: inplace_floor_div, + Opcode.INPLACE_TRUE_DIVIDE: inplace_true_div, + Opcode.STORE_MAP: store_map, + Opcode.INPLACE_ADD: inplace_add, + Opcode.INPLACE_SUBTRACT: inplace_sub, + Opcode.INPLACE_MULTIPLY: inplace_mul, + Opcode.INPLACE_MODULO: inplace_mod, + Opcode.STORE_SUBSCR: store_subscr, + Opcode.BINARY_LSHIFT: bin_lshift, + Opcode.BINARY_RSHIFT: bin_rshift, + Opcode.BINARY_AND: bin_and, + Opcode.BINARY_XOR: bin_xor, + Opcode.BINARY_OR: bin_or, + Opcode.BINARY_LOGIC_AND: bin_logic_and, + Opcode.BINARY_LOGIC_OR: bin_logic_or, + Opcode.INPLACE_POWER: inplace_pow, + Opcode.GET_ITER: get_iter, + Opcode.PRINT_EXPR: print_expr, + Opcode.EMIT_EXPR: emit_expr, + Opcode.INPLACE_LSHIFT: inplace_lshift, + Opcode.INPLACE_RSHIFT: inplace_rlshift, + Opcode.INPLACE_AND: inplace_and, + Opcode.INPLACE_XOR: inplace_xor, + Opcode.INPLACE_OR: inplace_or, + Opcode.RETURN_VALUE: return_value, + Opcode.RETURN_LAST_VALUE: return_last_value, + Opcode.STORE_NAME: store_name, + Opcode.UNPACK_SEQUENCE: unpack_sequence, + Opcode.FOR_ITER: for_iter, + Opcode.STORE_ATTR: store_attr, + Opcode.STORE_GLOBAL: store_global, + Opcode.LOAD_CONST: load_const, + Opcode.LOAD_NAME: load_name, + Opcode.BUILD_LIST: build_list, + Opcode.BUILD_MAP: build_map, + Opcode.LOAD_ATTR: load_attr, + Opcode.IMPORT_NAME: import_name, + Opcode.JUMP_FORWARD: jump_forward, + Opcode.JUMP_IF_FALSE_OR_POP: jump_if_false_or_pop, + Opcode.JUMP_IF_TRUE_OR_POP: jump_if_true_or_pop, + Opcode.JUMP_ABSOLUTE: jump_absolute, + Opcode.POP_JUMP_IF_FALSE: pop_jump_if_false, + Opcode.POP_JUMP_IF_TRUE: pop_jump_if_true, + Opcode.LOAD_GLOBAL: load_global, + Opcode.RAISE_VARARGS: raise_varargs, + Opcode.RAISE_CHECK: raise_check, + Opcode.LOAD_LOCAL: load_local, + Opcode.STORE_LOCAL: store_local, + Opcode.LOAD_FREE: load_free, + Opcode.CALL_FUNCTION: call_function, + Opcode.MAKE_FUNCTION: make_function, + Opcode.BUILD_SLICE: build_slice, + Opcode.LOAD_CLOSURE: load_closure, + Opcode.MAKE_CLOSURE: make_closure, + Opcode.LIST_APPEND: list_append, + Opcode.MAP_ADD: map_add, + Opcode.DELETE_ITEM: delete_item, + Opcode.MAKE_SCHEMA: make_schema, + Opcode.BUILD_SCHEMA: build_schema, + Opcode.SCHEMA_ATTR: schema_attr, + Opcode.LOAD_BUILT_IN: load_builtin, + Opcode.COMPARE_OP: compare_op, + Opcode.MAKE_DECORATOR: make_decorator, + Opcode.SCHEMA_LOAD_ATTR: schema_load_attr, + Opcode.SCHEMA_UPDATE_ATTR: schema_update_attr, + Opcode.SCHEMA_NOP: schema_nop, + Opcode.FORMAT_VALUES: format_values, + Opcode.MEMBER_SHIP_AS: member_ship_as, + Opcode.DEBUG_STACK: debug_stack, + Opcode.DEBUG_LOCALS: debug_locals, + Opcode.DEBUG_GLOBALS: debug_globals, + Opcode.DEBUG_NAMES: debug_names, +} diff --git a/internal/kclvm_py/vm/code/code_factory.py b/internal/kclvm_py/vm/code/code_factory.py new file mode 100644 index 000000000..8905b3199 --- /dev/null +++ b/internal/kclvm_py/vm/code/code_factory.py @@ -0,0 +1,243 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import Optional, List, Tuple +from dataclasses import dataclass, field + +from .code import Opcode + + +@dataclass +class OpcodeContent: + op: Opcode + arg: Optional[int] + arg_list: List[int] + index: int + meta: Tuple[str, int, int] + + @property + def begin_index(self): + return self.index + + @property + def end_index(self): + return self.index + len(self.arg_list) + 1 + + +@dataclass +class OpcodeFactory: + pkgpath: Optional[str] + values: List[OpcodeContent] = field(default_factory=list) + + @property + def begin_index(self): + return self.values[0].begin_index + + @property + def end_index(self): + return self.values[-1].end_index + + def pretty_print(self): + if not self.values: + return + for value in self.values: + print( + ">>> ", + value.op, + "arg:", + value.arg, + "index:", + value.index, + "line", + value.meta[1], + ) + + # --------------------- + # Static methods + # --------------------- + + @staticmethod + def build_from_codes(codes: list, pkgpath: str) -> "OpcodeFactory": + f = OpcodeFactory(pkgpath=pkgpath) + isp = 0 + while codes and isp < len(codes): + code = codes[isp] + opcode_index = isp + isp += 1 + # Skip the invalid opcode + if code == Opcode.INVALID: + continue + arg = None + arg_list = [] + if Opcode.has_arg(code): + arg = codes[isp] + (codes[isp + 1] << 8) + (codes[isp + 2] << 16) + arg_list = [codes[isp], codes[isp + 1], codes[isp + 2]] + isp += 3 + info = codes[isp] + isp += 1 + f.values.append( + OpcodeContent( + op=Opcode(code), + arg=arg, + arg_list=arg_list, + index=opcode_index, + meta=info, + ) + ) + return f + + @staticmethod + def to_codes(contents: List[OpcodeContent]) -> list: + return sum( + [ + [content.op] + + ( + ([content.op] * len(content.arg_list or [])) + if content.op == Opcode.INVALID + else (content.arg_list or []) + ) + + [content.op if content.op == Opcode.INVALID else content.meta] + for content in contents or [] + ], + [], + ) + + +@dataclass +class SchemaBodyOpcodeFactory(OpcodeFactory): + schema_name: str = None + begin_code: List[OpcodeContent] = None + end_code: List[OpcodeContent] = None + + def validate(self) -> bool: + return ( + self.values + and self.values[0].op == Opcode.STORE_LOCAL + and self.values[-1].op == Opcode.RETURN_VALUE + ) + + def update_start_end_code(self) -> "SchemaBodyOpcodeFactory": + if not self.validate(): + raise ValueError("Invalid schema opcode factory") + for i, value in enumerate(self.values): + if value.op == Opcode.SCHEMA_NOP and not self.begin_code: + self.begin_code = self.values[:i] + if value.op == Opcode.RETURN_VALUE: + self.end_code = [value] + return self + + def split_to_schema_attr_codes(self) -> List["SchemaBodyOpcodeFactory"]: + if not self.validate(): + raise ValueError("Invalid schema opcode factory") + results = [] + last_nop_index = 0 + next_nop_index = -1 + if_stack = [False] + if not self.end_code or not self.begin_code: + self.update_start_end_code() + for i, value in enumerate(self.values): + if value.op == Opcode.POP_JUMP_IF_FALSE: + if_stack.append(True) + if value.op == Opcode.JUMP_FORWARD: + if_stack.pop() + if value.op == Opcode.SCHEMA_NOP: + last_nop_index = i + if SchemaBodyOpcodeFactory.is_schema_line_op(value.op): + for j, v in enumerate(self.values[i + 1 :]): + if v.op == Opcode.SCHEMA_NOP: + next_nop_index = i + j + 1 + break + values_append = [] + if if_stack[-1]: + for v in self.values[last_nop_index : next_nop_index + 1]: + if value.index < v.index < self.values[next_nop_index].index: + values_append.append( + OpcodeContent( + op=Opcode.INVALID, + arg=v.arg, + arg_list=v.arg_list, + index=v.index, + meta=v.meta, + ) + ) + else: + values_append.append(v) + else: + values_append = self.values[last_nop_index : next_nop_index + 1] + results.append( + SchemaBodyOpcodeFactory( + pkgpath=self.pkgpath, + schema_name=self.schema_name, + values=values_append, + begin_code=self.begin_code, + end_code=self.end_code, + ) + ) + return results + + def to_run_code_list(self) -> list: + assert self.end_code and self.begin_code + # Single attribute code + begin_codes = OpcodeFactory.to_codes(self.begin_code) + end_codes = OpcodeFactory.to_codes(self.end_code) + codes = OpcodeFactory.to_codes(self.values) + start_to_code_count = self.values[0].index - self.begin_code[-1].end_index - 1 + code_to_end_count = self.end_code[0].index - self.values[-1].end_index - 1 + return ( + begin_codes + + [Opcode.INVALID] * start_to_code_count + + codes + + [Opcode.INVALID] * code_to_end_count + + end_codes + ) + + def pretty_print(self): + if not self.values: + return + RED = "\033[31m" + GREEN = "\033[32m" + BLUE = "\033[34m" + BOLD = "\033[1m" + RESET = "\033[m" + found_nop = False + for value in self.values: + if value.op == Opcode.SCHEMA_NOP: + found_nop = True + print(f"{BOLD}{GREEN}", end="") + elif value.op == Opcode.RETURN_VALUE or not found_nop: + print(f"{BOLD}{RED}", end="") + else: + print(f"{BOLD}{BLUE}", end="") + print( + ">>> ", + value.op, + "arg:", + value.arg, + "index:", + value.index, + "line", + value.meta[1], + ) + print(f"{RESET}") + + # --------------------- + # Static methods + # --------------------- + + @staticmethod + def is_schema_line_op(op: int): + return ( + op == Opcode.SCHEMA_ATTR + or op == Opcode.SCHEMA_UPDATE_ATTR + or op == Opcode.STORE_ATTR + ) + + @staticmethod + def build_from_codes( + codes: list, pkgpath: str, schema_name: str + ) -> "SchemaBodyOpcodeFactory": + ctx = OpcodeFactory.build_from_codes(codes, pkgpath) + return SchemaBodyOpcodeFactory( + pkgpath=ctx.pkgpath, + schema_name=schema_name, + values=ctx.values, + ) diff --git a/internal/kclvm_py/vm/planner/__init__.py b/internal/kclvm_py/vm/planner/__init__.py new file mode 100644 index 000000000..f03b91f76 --- /dev/null +++ b/internal/kclvm_py/vm/planner/__init__.py @@ -0,0 +1,8 @@ +from .plan import ( + ObjectPlanner, + JSONPlanner, + YAMLPlanner, + plan, +) + +__all__ = ["ObjectPlanner", "JSONPlanner", "YAMLPlanner", "plan"] diff --git a/internal/kclvm_py/vm/planner/plan.py b/internal/kclvm_py/vm/planner/plan.py new file mode 100644 index 000000000..db1fb3e83 --- /dev/null +++ b/internal/kclvm_py/vm/planner/plan.py @@ -0,0 +1,309 @@ +import io +import json +import inspect + +from dataclasses import dataclass +from collections import OrderedDict +from typing import Dict, List +from ruamel.yaml import YAML + +import kclvm +import kclvm.config +import kclvm.api.object as obj + +from kclvm.api.object.internal import Undefined, UndefinedType +from kclvm.api.object.schema import ( + SETTINGS_OUTPUT_KEY, + SCHEMA_SETTINGS_ATTR_NAME, +) + + +KCL_PLAN_TYPE = [ + obj.KCLObjectType.INTEGER, # int + obj.KCLObjectType.FLOAT, # float + obj.KCLObjectType.STRING, # string + obj.KCLObjectType.BOOLEAN, # True, False + obj.KCLObjectType.NUMBER_MULTIPLIER, # 1M, 1Ki + obj.KCLObjectType.NONE, # None + obj.KCLObjectType.UNDEFINED, # Undefined + obj.KCLObjectType.DICT, # dict + obj.KCLObjectType.LIST, # list + obj.KCLObjectType.SCHEMA, # dict with __settings__ +] +LIST_DICT_TEMP_KEY = "$" + + +def is_kcl_schema(value: dict): + return ( + value is not None + and isinstance(value, dict) + and SCHEMA_SETTINGS_ATTR_NAME in value + ) + + +def order_dict(d: any) -> dict: + result = {} + for k, v in sorted(d.items()): + if isinstance(v, dict): + result[k] = order_dict(v) + else: + result[k] = v + return result + + +def handle_schema(value: dict): + # on kcl schema + filtered = filter_results(value) + if filtered is None: + return filtered, False + settings = SCHEMA_SETTINGS_ATTR_NAME + output_type = SETTINGS_OUTPUT_KEY + if settings in value and value[settings][output_type] == obj.SETTINGS_OUTPUT_IGNORE: + if len(filtered) <= 1: + return None, False + else: + return filtered[1:], True + standalone = False + if ( + settings in value + and value[settings][output_type] == obj.SETTINGS_OUTPUT_STANDALONE + ): + standalone = True + return filtered, standalone + + +def filter_results(keyvalues: dict) -> List[dict]: + if keyvalues is None: + return None + + # index 0 for in-line keyvalues output, index 1: for standalone keyvalues outputs + results = [OrderedDict()] + + for key, value in keyvalues.items(): + if value is None and kclvm.config.disable_none: + continue + if isinstance(key, str) and key.startswith("_"): + pass + elif value is Undefined or isinstance(value, UndefinedType): + pass + elif inspect.isclass(value): + pass + elif inspect.isfunction(value): + pass + elif inspect.ismodule(value): + pass + elif inspect.isfunction(value): + pass + elif is_kcl_schema(value): + filtered, standalone = handle_schema(value) + if filtered is not None: + if standalone: + # if the instance is marked as 'STANDALONE', treat it as a separate one and + # extend it and derived STANDALONE instances to results. + results.extend(filtered) + else: + # else put it as the value of the key of results + if len(results) > 0 and len(filtered) > 0: + results[0][key] = filtered[0] + if len(filtered) > 1: + # if the value has derived 'STANDALONE' instances, extend them + results.extend(filtered[1:]) + elif isinstance(value, dict): + filtered = filter_results(value) + if len(results) > 0 and len(filtered) > 0: + results[0][key] = filtered[0] + if len(results) > 0 and len(filtered) > 1: + # if the value has derived 'STANDALONE' instances, extend them + results.extend(filtered[1:]) + elif isinstance(value, list): + filtered_list = [] + standalone_list = [] + ignore_schema_count = 0 + for i in value: + if is_kcl_schema(i): + filtered, standalone = handle_schema(i) + if filtered is None: + ignore_schema_count += 1 + continue + if filtered: + if standalone: + standalone_list.extend(filtered) + else: + filtered_list.extend(filtered) + elif isinstance(i, dict): + filtered = filter_results(i) + if filtered: + filtered_list.extend(filtered) + elif i is None and kclvm.config.disable_none: + continue + elif not isinstance(i, UndefinedType): + # Filter list elements + filtered = filter_results({LIST_DICT_TEMP_KEY: i}) + if ( + len(results) > 0 + and len(filtered) > 0 + and LIST_DICT_TEMP_KEY in filtered[0] + ): + filtered_list.append(filtered[0][LIST_DICT_TEMP_KEY]) + if len(results) > 0 and len(filtered) > 1: + # if the value has derived 'STANDALONE' instances, extend them + results.extend(filtered[1:]) + schema_in_list_count = ignore_schema_count + len(standalone_list) + if len(results) > 0 and 0 <= schema_in_list_count < len(value): + results[0][key] = filtered_list + if standalone_list: + results.extend(standalone_list) + else: + results[0][key] = value + return results + + +@dataclass +class ObjectPlanner: + """Planner is used to modify VM exec result to YAML""" + + def __init__( + self, *, sort_keys: bool = False, include_schema_type_path: bool = False + ) -> None: + self.sort_keys = sort_keys + self.include_schema_type_path = include_schema_type_path + + def to_python(self, v): + if isinstance(v, obj.KCLObject): + if isinstance(v, obj.KCLLiteralObject): + return v.value + elif isinstance(v, obj.KCLSchemaObject): + result = {_k: self.to_python(_v) for _k, _v in v.attrs.items()} + if self.include_schema_type_path: + result["@type"] = v.full_type_str() + return result + elif isinstance(v, (obj.KCLListObject, obj.KCLTupleObject)): + return [self.to_python(_v) for _v in v.value] + elif isinstance(v, obj.KCLDictObject): + return {_k: self.to_python(_v) for _k, _v in v.value.items()} + elif isinstance(v, (obj.KCLUndefinedObject, obj.KCLFunctionObject)): + return Undefined + elif isinstance(v, obj.KCLNameConstantObject): + return v.value + else: + return Undefined + elif isinstance(v, (list, tuple, set)): + return [self.to_python(_v) for _v in v] + elif isinstance(v, dict): + return {_k: self.to_python(_v) for _k, _v in v.items()} + elif isinstance(v, (int, float, str, bool)) or v is None: + return v + elif v is Undefined or isinstance(v, UndefinedType): + return v + else: + raise Exception("Invalid KCL Object") + + def plan(self, var_dict: Dict[str, obj.KCLObject]) -> Dict[str, any]: + assert isinstance(var_dict, dict) + data = { + k: self.to_python(v) + for k, v in var_dict.items() + if v and v.type() in KCL_PLAN_TYPE + } + return data + + +@dataclass +class YAMLPlanner(ObjectPlanner): + def __init__( + self, *, sort_keys: bool = False, include_schema_type_path: bool = False + ) -> None: + super().__init__( + sort_keys=sort_keys, include_schema_type_path=include_schema_type_path + ) + + def plan(self, var_dict: Dict[str, obj.KCLObject], to_py: bool = True) -> str: + assert isinstance(var_dict, dict) + plan_obj = super().plan(var_dict) if to_py else var_dict + # Represent OrderedDict as dict. + yaml = YAML() + yaml.representer.add_representer( + OrderedDict, + lambda dumper, data: dumper.represent_mapping( + "tag:yaml.org,2002:map", data + ), + ) + # Convert tuple to list. + yaml.representer.add_representer( + tuple, + lambda dumper, data: dumper.represent_sequence( + "tag:yaml.org,2002:seq", data + ), + ) + # Convert None to null + yaml.representer.add_representer( + type(None), + lambda dumper, data: dumper.represent_scalar( + u"tag:yaml.org,2002:null", u"null" + ), + ) + yaml.representer.add_representer( + str, + lambda dumper, data: dumper.represent_scalar( + u"tag:yaml.org,2002:str", data, style="|" + ) + if "\n" in data + else dumper.represent_str(data), + ) + results = filter_results(plan_obj) + if self.sort_keys: + results = [order_dict(r) for r in results] + outputs = "" + with io.StringIO() as buf: + if results: + for result in results[:-1]: + if result: + yaml.dump(result, buf) + buf.write("---\n") + if results[-1]: + yaml.dump(results[-1], buf) + outputs = buf.getvalue() + return outputs + + +class JSONPlanner(ObjectPlanner): + def __init__( + self, *, sort_keys: bool = False, include_schema_type_path: bool = False + ) -> None: + super().__init__( + sort_keys=sort_keys, include_schema_type_path=include_schema_type_path + ) + + def plan( + self, + var_dict: Dict[str, obj.KCLObject], + *, + only_first=False, + to_py: bool = True + ) -> str: + assert isinstance(var_dict, dict) + plan_obj = super().plan(var_dict) if to_py else var_dict + results = filter_results(plan_obj) + if self.sort_keys: + results = [order_dict(r) for r in results] + results = [r for r in results if r] + return ( + json.dumps( + results if not only_first else results[0], + default=lambda o: o.__dict__, + indent=4, + ) + if results + else "" + ) + + +def plan( + val_map: Dict[str, obj.KCLObject], + *, + sort_keys: bool = False, + include_schema_type_path: bool = False +) -> str: + return YAMLPlanner( + sort_keys=sort_keys, include_schema_type_path=include_schema_type_path + ).plan(val_map) diff --git a/internal/kclvm_py/vm/runtime/README.md b/internal/kclvm_py/vm/runtime/README.md new file mode 100644 index 000000000..312aa053b --- /dev/null +++ b/internal/kclvm_py/vm/runtime/README.md @@ -0,0 +1,3 @@ +# KCLVM Runtime + +This module provides the runtime for KCLVM. diff --git a/internal/kclvm_py/vm/runtime/__init__.py b/internal/kclvm_py/vm/runtime/__init__.py new file mode 100644 index 000000000..d74aaf8ad --- /dev/null +++ b/internal/kclvm_py/vm/runtime/__init__.py @@ -0,0 +1 @@ +# Copyright 2021 The KCL Authors. All rights reserved. diff --git a/internal/kclvm_py/vm/runtime/evaluator/__init__.py b/internal/kclvm_py/vm/runtime/evaluator/__init__.py new file mode 100644 index 000000000..d72cbbed1 --- /dev/null +++ b/internal/kclvm_py/vm/runtime/evaluator/__init__.py @@ -0,0 +1,12 @@ +from .union import union, merge +from .common import handle_subscript +from .lazy import ValueCache, Backtracking, SchemaEvalContext + +__all__ = [ + "union", + "merge", + "handle_subscript", + "ValueCache", + "SchemaEvalContext", + "Backtracking", +] diff --git a/internal/kclvm_py/vm/runtime/evaluator/common.py b/internal/kclvm_py/vm/runtime/evaluator/common.py new file mode 100644 index 000000000..275866281 --- /dev/null +++ b/internal/kclvm_py/vm/runtime/evaluator/common.py @@ -0,0 +1,25 @@ +from kclvm.api.object import ( + KCLObject, + KCLStringObject, + KCLListObject, + KCLDictObject, + KCLSchemaObject, + to_python_obj, + to_kcl_obj, +) + + +def plus(left: KCLObject, right: KCLObject): + if isinstance(left, KCLStringObject) and isinstance(right, KCLStringObject): + return KCLStringObject(value=left.value + right.value) + if isinstance(left, KCLListObject) and isinstance(right, KCLListObject): + return KCLListObject(items=left.items + right.items) + return to_kcl_obj(to_python_obj(left) + to_python_obj(right)) + + +def handle_subscript(obj: KCLObject, slice: KCLObject): + return to_kcl_obj( + obj.get(to_python_obj(slice)) + if isinstance(obj, (KCLDictObject, KCLSchemaObject)) + else obj.value[to_python_obj(slice)] + ) diff --git a/internal/kclvm_py/vm/runtime/evaluator/eval.py b/internal/kclvm_py/vm/runtime/evaluator/eval.py new file mode 100644 index 000000000..3e6cbbfb4 --- /dev/null +++ b/internal/kclvm_py/vm/runtime/evaluator/eval.py @@ -0,0 +1,429 @@ +from dataclasses import dataclass +from typing import List, Tuple, Union, cast +from copy import deepcopy + +import kclvm.kcl.error as kcl +import kclvm.kcl.ast as ast +import kclvm.api.object as objpkg +from kclvm.vm.code import Opcode +from kclvm.compiler.check.check_type import type_pack_and_check +from kclvm.compiler.extension.builtin.system_module import json as _json +from kclvm.compiler.extension.builtin.system_module import yaml as _yaml + +from .union import union, resolve_schema_obj +from .common import plus, handle_subscript + + +BINARY_FUNCTIONS = { + Opcode.BINARY_ADD: lambda x, y: plus(x, y), + Opcode.BINARY_SUBTRACT: lambda x, y: x - y, + Opcode.BINARY_MULTIPLY: lambda x, y: x * y, + Opcode.BINARY_TRUE_DIVIDE: lambda x, y: x / y, + Opcode.BINARY_FLOOR_DIVIDE: lambda x, y: x // y, + Opcode.BINARY_LSHIFT: lambda x, y: x << y, + Opcode.BINARY_RSHIFT: lambda x, y: x >> y, + Opcode.BINARY_POWER: lambda x, y: x ** y, + Opcode.BINARY_SUBSCR: lambda x, y: handle_subscript(x, y), + Opcode.BINARY_XOR: lambda x, y: x ^ y, + Opcode.BINARY_AND: lambda x, y: x & y, + Opcode.BINARY_OR: lambda x, y: union(x, y), + Opcode.BINARY_MODULO: lambda x, y: x % y, + Opcode.BINARY_LOGIC_AND: lambda x, y: x and y, + Opcode.BINARY_LOGIC_OR: lambda x, y: x or y, + Opcode.COMPARE_EQUAL_TO: lambda x, y: x == y, + Opcode.COMPARE_NOT_EQUAL_TO: lambda x, y: x != y, + Opcode.COMPARE_LESS_THAN: lambda x, y: x < y, + Opcode.COMPARE_LESS_THAN_OR_EQUAL_TO: lambda x, y: x <= y, + Opcode.COMPARE_GREATER_THAN: lambda x, y: x > y, + Opcode.COMPARE_GREATER_THAN_OR_EQUAL_TO: lambda x, y: x >= y, + Opcode.COMPARE_IS: lambda x, y: x is y, + Opcode.COMPARE_IS_NOT: lambda x, y: x is not y, + Opcode.COMPARE_IN: lambda x, y: x in y, + Opcode.COMPARE_NOT_IN: lambda x, y: x not in y, +} + +UNARY_FUNCTIONS = { + Opcode.UNARY_INVERT: lambda x: ~x, + Opcode.UNARY_NOT: lambda x: not x, + Opcode.UNARY_POSITIVE: lambda x: +x, + Opcode.UNARY_NEGATIVE: lambda x: -x, +} + +INPLACE_FUNCTIONS = { + Opcode.INPLACE_ADD: lambda x, y: plus(x, y), + Opcode.INPLACE_SUBTRACT: lambda x, y: x - y, + Opcode.INPLACE_MULTIPLY: lambda x, y: x * y, + Opcode.INPLACE_TRUE_DIVIDE: lambda x, y: x / y, + Opcode.INPLACE_FLOOR_DIVIDE: lambda x, y: x // y, + Opcode.INPLACE_LSHIFT: lambda x, y: x << y, + Opcode.INPLACE_RSHIFT: lambda x, y: x >> y, + Opcode.INPLACE_POWER: lambda x, y: x ** y, + Opcode.INPLACE_XOR: lambda x, y: x ^ y, + Opcode.INPLACE_AND: lambda x, y: x & y, + Opcode.INPLACE_OR: lambda x, y: union(x, y), + Opcode.INPLACE_MODULO: lambda x, y: x % y, +} + + +@dataclass +class Evaluator: + """Evaluator is a class responsible for parsing + KCL objects and performing interpretation to get results + """ + + # Binary + + def eval_binary_op( + self, + left: objpkg.KCLObject, + right: objpkg.KCLObject, + code: Union[int, Opcode], + vm=None, + ) -> objpkg.KCLObject: + if not left or not right or not code: + raise Exception(f"invalid binary opcode action {left}, {right} and {code}") + func = BINARY_FUNCTIONS.get(code) + if not func: + raise Exception(f"invalid binary opcode {code}") + if code == Opcode.BINARY_OR: + result = union( + deepcopy(left), + right, + or_mode=True, + should_config_resolve=True, + should_idempotent_check=True, + vm=vm, + ) + elif code == Opcode.BINARY_ADD or code == Opcode.BINARY_SUBSCR: + result = func(left, right) + else: + result = objpkg.to_kcl_obj( + func(objpkg.to_python_obj(left), objpkg.to_python_obj(right)) + ) + return result + + # Unary + + def eval_unary_op( + self, obj: objpkg.KCLObject, code: Union[int, Opcode] + ) -> objpkg.KCLObject: + if not obj or not code: + raise Exception(f"invalid binary opcode action {obj} and {code}") + func = UNARY_FUNCTIONS.get(code) + if not func: + raise Exception(f"invalid unary opcode {code}") + r = objpkg.to_kcl_obj(func(obj.value)) + return r + + # Inplace operator + + def eval_inplace_op( + self, + left: objpkg.KCLObject, + right: objpkg.KCLObject, + code: Union[int, Opcode], + vm=None, + ) -> objpkg.KCLObject: + if not left or not right or not code: + raise Exception(f"invalid inpalce opcode action {left}, {right} and {code}") + func = INPLACE_FUNCTIONS.get(code) + if not func: + raise Exception(f"invalid inplace opcode {code}") + if code == Opcode.INPLACE_OR: + result = union( + left, + right, + or_mode=True, + should_config_resolve=True, + should_idempotent_check=True, + vm=vm, + ) + elif code == Opcode.INPLACE_ADD: + result = func(left, right) + else: + result = objpkg.to_kcl_obj( + func( + objpkg.to_python_obj(left.value), objpkg.to_python_obj(right.value) + ) + ) + return result + + # Compare + + def eval_compare_op( + self, + left: objpkg.KCLObject, + right: objpkg.KCLObject, + code: Union[int, Opcode], + vm=None, + ) -> objpkg.KCLObject: + # Avoid the overhead of boxing and unboxing large objects. + if isinstance(right, (objpkg.KCLDictObject, objpkg.KCLSchemaObject)): + if code == Opcode.COMPARE_IN: + return objpkg.to_kcl_obj(left in right) + elif code == Opcode.COMPARE_NOT_IN: + return objpkg.to_kcl_obj(left not in right) + else: + return self.eval_binary_op(left, right, code, vm=vm) + else: + return self.eval_binary_op(left, right, code, vm=vm) + + # Iterable + + def iter_next(self, obj: objpkg.KCLObject) -> objpkg.KCLObject: + if obj.type() != objpkg.KCLObjectType.ITER: + kcl.report_exception( + err_type=kcl.ErrType.EvaluationError_TYPE, + arg_msg="only iterable object has next function", + ) + return objpkg.to_kcl_obj(obj.next()) + + # Functions + + def call_vars_and_keywords( + self, argc: int, vm + ) -> Tuple[List[objpkg.KCLObject], List[objpkg.KWArg]]: + n_args = int(argc & 0xFF) + n_kwargs = int((argc >> 8) & 0xFF) + p, q = len(vm.stack) - 2 * n_kwargs, len(vm.stack) + args = [] + kwargs = [] + for i in range((q - p) // 2 - 1, -1, -1): + v = vm.pop() + kstr = vm.pop() + kwargs.append(objpkg.KWArg(name=kstr, value=v)) + p, q = p - n_args, p + for i in range(q - p - 1, -1, -1): + arg = vm.pop() + args.append(arg) + return args[::-1], kwargs[::-1] + + def eval_call(self, code: Union[int, Opcode], argc: int, vm) -> objpkg.KCLObject: + if code == Opcode.CALL_FUNCTION: + # Function callable without `*args` and `**kwargs` + args, kwargs = self.call_vars_and_keywords(argc, vm) + callable_obj = vm.pop() + if isinstance(callable_obj, objpkg.KCLCompiledFunctionObject): + vm.push_frame_using_callable( + callable_obj.pkgpath, + callable_obj, + (args if args else []), + kwargs, + args_len=len(args), + ) + return objpkg.NONE_INSTANCE + elif isinstance(callable_obj, objpkg.KCLFunctionObject): + result_obj = callable_obj.call(args, kwargs, vm) + vm.push(result_obj) + return result_obj + elif isinstance(callable_obj, objpkg.KCLSchemaTypeObject): + schema_type_obj = cast(objpkg.KCLSchemaTypeObject, callable_obj) + inst = schema_type_obj.new_instance({}, {}, args, kwargs, vm) + vm.push(inst) + return inst + elif isinstance( + callable_obj, (objpkg.KCLNoneObject, objpkg.KCLUndefinedObject) + ): + # Ignore the user None callable + vm.push(callable_obj) + return callable_obj + else: + kcl.report_exception( + err_type=kcl.ErrType.EvaluationError_TYPE, + arg_msg=f"'{callable_obj.type()}' object is not callable", + ) + + # Attribute and subscript + + def load_attr(self, obj: objpkg.KCLObject, attr: str) -> objpkg.KCLObject: + """Get attribute value of a KCL object""" + if obj.type() in [ + objpkg.KCLObjectType.DICT, + objpkg.KCLObjectType.SCHEMA, + objpkg.KCLObjectType.MODULE, + ]: + return obj.get(attr) + elif obj.type() in [ + objpkg.KCLObjectType.STRING, + objpkg.KCLObjectType.SCHEMA_TYPE, + ]: + return obj.get_member_method(attr) + else: + kcl.report_exception( + err_type=kcl.ErrType.EvaluationError_TYPE, + arg_msg=f"'{obj.type_str()}' object has no attribute '{attr}'", + ) + + def set_attr( + self, + obj: objpkg.KCLObject, + item: objpkg.KCLObject, + value: objpkg.KCLObject, + vm=None, + ) -> objpkg.KCLObject: + """Set attribute value of KCL object""" + if not isinstance(obj, (objpkg.KCLDictObject, objpkg.KCLSchemaObject)): + kcl.report_exception( + err_type=kcl.ErrType.EvaluationError_TYPE, + arg_msg="only schema and dict object can be updated attribute", + ) + obj.update_key_value(item, value) + if isinstance(obj, objpkg.KCLSchemaObject): + obj = resolve_schema_obj(obj, obj.config_keys | {item.value}, vm=vm) + return obj + + def set_item( + self, obj: objpkg.KCLObject, item: objpkg.KCLObject, value: objpkg.KCLObject + ) -> objpkg.KCLObject: + """Set subscript value of a KCL object""" + obj.value[item.value] = value + return obj + + # List + + def list_append( + self, obj: objpkg.KCLObject, item: objpkg.KCLObject + ) -> objpkg.KCLObject: + """Append an item into list""" + if obj.type() != objpkg.KCLObjectType.LIST: + kcl.report_exception( + err_type=kcl.ErrType.EvaluationError_TYPE, + arg_msg="only list object can append value", + ) + obj.append(item) + return obj + + # Dict + + def dict_append( + self, obj: objpkg.KCLObject, key: objpkg.KCLObject, value: objpkg.KCLObject + ) -> objpkg.KCLObject: + """Append an key-value pair into dict""" + if obj.type() != objpkg.KCLObjectType.DICT: + if obj.type() != objpkg.KCLObjectType.LIST: + kcl.report_exception( + err_type=kcl.ErrType.EvaluationError_TYPE, + arg_msg="only dict object can append key-value pair", + ) + obj.update_key_value(key, value) + return obj + + # String Format value + + def format_value( + self, obj: objpkg.KCLObject, format_spec: str + ) -> objpkg.KCLStringObject: + if not format_spec: + value = "{}".format(objpkg.to_python_obj(obj)) + return objpkg.KCLStringObject(value=value) + assert isinstance(format_spec, str) + if format_spec.lower() == "#json": + value = _json.KMANGLED_encode(objpkg.to_python_obj(obj)) + return objpkg.KCLStringObject(value=value) + if format_spec.lower() == "#yaml": + value = _yaml.KMANGLED_encode(objpkg.to_python_obj(obj)) + return objpkg.KCLStringObject(value=value) + kcl.report_exception( + err_type=kcl.ErrType.InvalidFormatSpec_TYPE, + arg_msg=kcl.INVALID_FORMAT_SPEC_MSG.format(format_spec), + ) + + # Schema config operation expression + + def update_schema_attr( + self, + attr: str, + schema_obj: objpkg.KCLSchemaObject, + config_value: objpkg.KCLObject, + conf_meta: dict, + expected_types: List[str], + operation=ast.ConfigEntryOperation.UNION, + index=None, + vm=None, + filename=None, + lineno=None, + columnno=None, + ): + """Update schema attr value with config dict""" + if index is None or index < 0: + config_value_checked = type_pack_and_check( + config_value, + expected_types, + vm=vm, + filename=filename, + lineno=lineno, + columno=columnno, + config_meta=conf_meta.get("$conf_meta"), + ) + else: + value = schema_obj.get(attr) + if not isinstance(value, objpkg.KCLListObject): + kcl.report_exception( + err_type=kcl.ErrType.EvaluationError_TYPE, + arg_msg="only list attribute can be inserted value", + ) + config_value_checked = type_pack_and_check( + objpkg.KCLListObject(items=[config_value]), + expected_types, + vm=vm, + filename=filename, + lineno=lineno, + columno=columnno, + config_meta=conf_meta.get("$conf_meta"), + ) + config_value_checked = cast( + objpkg.KCLListObject, config_value_checked + ).items[0] + SCHEMA_CONFIG_OP_MAPPING = { + ast.ConfigEntryOperation.UNION: self.union_schema_attr, + ast.ConfigEntryOperation.OVERRIDE: self.override_schema_attr, + ast.ConfigEntryOperation.INSERT: self.insert_schema_attr, + } + func = SCHEMA_CONFIG_OP_MAPPING.get(operation) + if not func: + raise Exception(f"Invalid schema config object operation: {operation}") + func(attr, schema_obj, config_value_checked, index, vm) + + def union_schema_attr( + self, + attr: str, + schema_obj: objpkg.KCLSchemaObject, + config_value: objpkg.KCLObject, + index=None, + vm=None, + ): + if index is None or index < 0: + # TODO: modify `should_idempotent_check` to False after Konfig code update finish + schema_obj.union_with({attr: config_value}, should_idempotent_check=False) + else: + # Union with list internal index value + value = schema_obj.get(attr) + value.items[index] = union( + value.items[index], config_value, should_idempotent_check=False, vm=vm + ) + schema_obj.union_with({attr: value}, should_idempotent_check=False) + + def override_schema_attr( + self, + attr: str, + schema_obj: objpkg.KCLSchemaObject, + config_value: objpkg.KCLObject, + index=None, + vm=None, + ): + if index is None or index < 0: + schema_obj.update_key_value(attr, config_value) + else: + schema_obj.list_key_override(attr, config_value, index) + + def insert_schema_attr( + self, + attr: str, + schema_obj: objpkg.KCLSchemaObject, + config_value: objpkg.KCLObject, + index=None, + vm=None, + ): + if index is not None and index >= 0: + config_value = objpkg.KCLListObject(items=[config_value]) + schema_obj.insert_with(attr, config_value, index) diff --git a/internal/kclvm_py/vm/runtime/evaluator/lazy.py b/internal/kclvm_py/vm/runtime/evaluator/lazy.py new file mode 100644 index 000000000..01a59365c --- /dev/null +++ b/internal/kclvm_py/vm/runtime/evaluator/lazy.py @@ -0,0 +1,461 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import inspect + +from dataclasses import dataclass, field +from typing import List, Dict, Union, Optional, Callable, TypeVar +from enum import IntEnum + +import kclvm.kcl.error as kcl_error +import kclvm.api.object as obj +import kclvm.vm.code as vm_code +import kclvm.compiler.extension.builtin as builtin + + +# -------------------- +# Type alias +# -------------------- + +SchemaKeyNativeType = Union[str, int, float] +SchemaKeyObjectType = Union[obj.KCLStringObject, obj.KCLIntObject, obj.KCLFloatObject] +SchemaKeyType = Union[SchemaKeyNativeType, SchemaKeyObjectType] +VirtualMachine = TypeVar("vm.VirtualMachine") + + +# ------------------------------------ +# Schema attribute value place holder +# ------------------------------------ + + +class ValuePlaceHolderPriority(IntEnum): + """ + Value place holder override priority (Ascending) + ------------------------------------------------ + 1. base_default (default value of the base class schema attribute) + 2. base_templating (base schema writing general expression context) + 3. base_mixin (base class schema attribute) + 4. sub_default (the default value of the schema attribute of the subclass) + 5. sub_templating (subclass schema writing general expression context) + 6. sub_mixin (context of subclass schema) + 7. config (schema instantiation configuration value) + """ + + BASE_DEFAULT = 1 + BASE_TEMPLATING = 2 + BASE_MIXIN = 3 + SUB_DEFAULT = 4 + SUB_TEMPLATING = 5 + SUB_MIXIN = 6 + CONFIG = 7 + + +@dataclass +class ValuePlaceHolder: + """The real value of the schema attribute can be regarded + as a placeholder for a non-literal expression before it is calculated + + Please note that all optional attribute checks, relaxed attr append, + schema index signature and decorator execution and check block are executed + after the calculation of the above process is completed. + """ + + name: str # Attribute name + priority: ValuePlaceHolderPriority + types: List[str] = field(default_factory=list) # Attribute type annotation list + value: obj.KCLObject = None # Actual value + config_value: obj.KCLObject = None # Config value + codes: List[vm_code.SchemaBodyOpcodeFactory] = field( + default_factory=list + ) # VM eval byte codes + + +# -------------------- +# Cache definition +# -------------------- + + +class ValueCache: + """Key-value pair cache + - The key is a valid key type including `str`, `int` and `float` + - The value is a KCL object + """ + + def __init__(self): + self._cache: Dict[SchemaKeyNativeType, obj.KCLObject] = {} + + def __getitem__(self, key): + return self.get(key) + + def __contains__(self, key): + return key in self._cache + + def clear(self): + self._cache.clear() + + def get( + self, key: SchemaKeyNativeType, eval_func: Callable = None, *args, **kwargs + ) -> Optional[obj.KCLObject]: + """Get the value from the cache. When the parameter `eval_func` is + provided and there is no such value in the cache, the value is calculated + by calling `eval_func` + """ + if key not in self._cache and eval_func and inspect.isfunction(eval_func): + self._cache[key] = eval_func(*args, **kwargs) + return self._cache.get(key) + + def set(self, key: SchemaKeyNativeType, value: obj.KCLObject): + if not key: + raise ValueError(f"Invalid key {key}") + if not value or not isinstance(value, obj.KCLObject): + raise ValueError(f"Invalid kcl object {value}") + self._cache[key] = value + + +# -------------------- +# Backtracking +# -------------------- + + +@dataclass +class Backtracking: + """Backtracking calculation set + + `__enter__` and `__exit__` function can be used as follows: + + backtracking = Backtracking() + with backtracking.catch(name): + assert Backtracking.tracking_level(name) == 1 + with backtracking.catch(name): + assert Backtracking.tracking_level(name) == 2 + assert Backtracking.tracking_level(name) == 1 + assert Backtracking.tracking_level(name) == 0 + """ + + _set: dict = field(default_factory=dict) + _key: Optional[str] = None + + def __enter__(self): + if self._key not in self._set: + self._set[self._key] = 1 + else: + self._set[self._key] += 1 + + def __exit__(self, e_t, e_v, t_b): + if self._key in self._set: + self._set[self._key] -= 1 + if self._set[self._key] == 0: + del self._set[self._key] + + def catch(self, key: str): + self._key = key + return self + + def is_backtracking(self, key: str): + return key in self._set and self.tracking_level(key) > 0 + + def tracking_level(self, key: str): + return self._set.get(key, 0) + + def reset(self): + self._set.clear() + + +# -------------------- +# Schema eval context +# -------------------- + + +@dataclass +class SchemaEvalContext: + """Schema irrelevant order calculation context + + TODO: Need to be combined with configuration merging technology and + move it into the compiler stage. + """ + + type_obj: obj.KCLSchemaTypeObject + schema_obj: obj.KCLSchemaObject + config: obj.KCLDictObject + config_meta: dict + args: List[obj.KCLObject] + kwargs: List[obj.KWArg] + vm: VirtualMachine + cache: ValueCache = ValueCache() + + place_holder_map: Dict[str, ValuePlaceHolder] = None + backtracking: Backtracking = Backtracking() + + def eval_reset(self): + """Eval status reset to prevent reference interference""" + self.cache.clear() + self.backtracking.reset() + + # ---------------------------- + # Code to value place holders + # ---------------------------- + + def code_to_place_holder( + self, + code: vm_code.SchemaBodyOpcodeFactory, + name: str, + priority: ValuePlaceHolderPriority, + ) -> ValuePlaceHolder: + """Covert schema body code to value place holder""" + assert ( + name + and isinstance(name, str) + and code + and isinstance(code, vm_code.SchemaBodyOpcodeFactory) + ) + place_holder = ValuePlaceHolder( + name=name, + priority=priority, + types=self.schema_obj.get_attr_type(name), + codes=[code], + config_value=self.config.get(name), + ) + return place_holder + + def get_all_place_holders(self) -> Dict[str, ValuePlaceHolder]: + """Get all schema attribute value place holders""" + self.place_holder_map = self.get_place_holders_by_type_obj( + self.type_obj, {}, ValuePlaceHolderPriority.SUB_DEFAULT + ) + return self.place_holder_map + + def get_place_holders_by_type_obj( + self, + type_obj: obj.KCLSchemaTypeObject, + map_ref: dict, + priority: ValuePlaceHolderPriority, + ) -> Dict[str, ValuePlaceHolder]: + """Get schema place holders using the schema type object""" + if not type_obj or not isinstance(type_obj, obj.KCLSchemaTypeObject): + return {} + # Get the base schema place holders + if type_obj.base: + self.get_place_holders_by_type_obj( + type_obj.base, map_ref, ValuePlaceHolderPriority.BASE_DEFAULT + ) + code_factory = vm_code.SchemaBodyOpcodeFactory.build_from_codes( + type_obj.func.instructions, + type_obj.pkgpath, + type_obj.name, + ) + # Split opcodes into key-value pair `{attr_name}-{opcode_list}` + codes = code_factory.split_to_schema_attr_codes() + assert len(codes) == len( + type_obj.attr_list + ), f"{len(codes)} != {len(type_obj.attr_list)}" + for i, code in enumerate(codes): + place_holder = self.code_to_place_holder( + code, type_obj.attr_list[i], priority + 1 + ) + if ( + place_holder + and place_holder.name in map_ref + and place_holder.priority >= map_ref[place_holder.name].priority + ): + # Schema attribute multi-override + map_ref[place_holder.name].codes.extend(place_holder.codes) + else: + # First default value place holder + map_ref[place_holder.name] = place_holder + # Get the mixin place holders + for mixin in type_obj.mixins or []: + self.get_place_holders_by_type_obj(mixin, map_ref, priority + 2) + return map_ref + + # ---------------------------- + # Back track + # ---------------------------- + + def back_track(self, name: str, place_holder: ValuePlaceHolder): + if not name or not place_holder: + return + try: + if self.backtracking.is_backtracking(name): + pass + """ + # Please note that KCL variable reference is not considered a self circular reference, + # and the value may be overwritten such as `a = 1; a += 2` will be treated as `a = 1 + 2` + raise kcl_error.KCLRuntimeError( + "RecursionError", None, None, None, f"Attribute '{name}' reference cycle" + ) + """ + with self.backtracking.catch(name): + # Number of variable backtracking + level = self.backtracking.tracking_level(name) + # Get the place holder code. Please note the negative index `-level` + # because the later value place holders are placed at the end of the array + code: vm_code.SchemaBodyOpcodeFactory = place_holder.codes[-level] + # Run schema attribute place holder code + self.vm_run_schema_code(code) + # When the traceback ends, save the value to the cache + self.cache.set(name, self.schema_obj.get(name)) + except IndexError: + kcl_error.report_exception( + err_type=kcl_error.ErrType.RecursionError_TYPE, + arg_msg=f"Attribute '{name}' reference cycle", + ) + + # ------------------------------- + # Schema Attribute Getter/Setter + # ------------------------------- + + def set_value(self, name: SchemaKeyNativeType, value: obj.KCLObject): + self.schema_obj.update({name: value}) + # If the attribute has only one place holder, its value is also the cache value + if name in self.place_holder_map: + if len(self.place_holder_map[name].codes) <= 1: + self.cache.set(name, value) + elif ( + self.place_holder_map[name].codes[-1].begin_index + < self.vm.ctx.isp + < self.place_holder_map[name].codes[-1].end_index + and self.place_holder_map[name].codes[-1].schema_name + == self.vm.ctx.name + ): + self.cache.set(name, value) + + def get_value(self, name: SchemaKeyNativeType) -> Optional[obj.KCLObject]: + if not name: + raise ValueError(f"Invalid name: {name}") + # Deal in-place modify and return it self immediately + if self.is_target_attr(name) and not self.backtracking.is_backtracking(name): + return self.schema_obj.get(name) + + if name in self.cache: + return self.cache[name] + elif name in self.place_holder_map: + self.back_track(name, self.place_holder_map.get(name)) + + # 1. Load from schema self + if name in self.schema_obj: + value = self.schema_obj.get(name) + return value + + # 2. Load from frame locals such as loop variables and schema args + value = self.get_from_frame_locals(name) + if value is not None: + return value + + # 3. Load from globals + value = self.get_from_frame_globals(name) + if value is not None: + return value + + # 4. Load from builtin + built_obj_list = builtin.get_builtin_func_objects() + for value in built_obj_list: + if value.name == name: + return value + + # 5. Load from pkg because the name may be a package variable that starts with `@` character + pkgpath = name[1:] if name.startswith("@") else name + if pkgpath in self.vm.state.modules: + value = self.vm.state.modules[pkgpath] + return value + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"'{name}' is not defined", + ) + + def is_target_attr(self, key: str) -> bool: + if key not in self.place_holder_map: + return False + place_holders = self.place_holder_map.get(key) + for code in place_holders.codes: + if code.schema_name != self.vm.ctx.name: + continue + if code.begin_index <= self.vm.ctx.isp <= code.end_index: + return True + return False + + # ------------------------------------- + # VM schema body code runner + # ------------------------------------- + + def vm_run_schema_code(self, code: vm_code.SchemaBodyOpcodeFactory): + if not code or not isinstance(code, vm_code.SchemaBodyOpcodeFactory): + return + codes_run: list = code.to_run_code_list() + func = obj.KCLCompiledFunctionObject( + name=code.schema_name, + params=self.type_obj.func.params, + names=self.type_obj.func.names, + constants=self.type_obj.func.constants, + ) + func.instructions = codes_run + self.vm.push_frame_using_callable( + code.pkgpath, + func, + [*self.args, self.config_meta, self.config, self.schema_obj] + if self.args + else [self.config_meta, self.config, self.schema_obj], + self.kwargs, + args_len=len(self.args), + ) + # Run the schema compiled function body + self.vm.run(run_current=True, ignore_nop=True) + + # ------------------------------------- + # VM frame locals/globals + # ------------------------------------- + + def get_from_frame_locals(self, key: str) -> Optional[obj.KCLDictObject]: + """Get kcl object from vm frame locals dict""" + if self.key_is_in_frame_locals(key): + return self.vm.ctx.locals[key] + index = self.vm.frame_index + # Search the local variables from the inside to the outside schema + while index >= self.vm.frame_index: + index -= 1 + if key in self.vm.frames[index].locals: + return self.vm.frames[index].locals[key] + return None + + def get_from_frame_globals(self, key: str) -> Optional[obj.KCLDictObject]: + """Get kcl object from vm frame locals dict""" + return self.vm.ctx.globals[key] if self.key_is_in_frame_globals(key) else None + + def key_is_in_frame_locals(self, key: str) -> bool: + return key and isinstance(key, str) and key in self.vm.ctx.locals + + def key_is_in_frame_globals(self, key: str) -> bool: + return key and isinstance(key, str) and key in self.vm.ctx.globals + + # --------------- + # Static methods + # --------------- + + @staticmethod + def build_from_vm( + vm: VirtualMachine, + type_obj: obj.KCLSchemaTypeObject, + schema_obj: obj.KCLSchemaObject, + config: obj.KCLDictObject, + config_meta: dict, + args: List[obj.KCLObject], + kwargs: List[obj.KWArg], + ) -> "SchemaEvalContext": + if not type_obj or not isinstance(type_obj, obj.KCLSchemaTypeObject): + raise ValueError(f"Invalid kcl type object {type_obj}") + context = SchemaEvalContext( + type_obj=type_obj, + schema_obj=schema_obj + or obj.KCLSchemaObject( + attrs={ + obj.SCHEMA_SETTINGS_ATTR_NAME: obj.to_kcl_obj(type_obj.settings) + }, + name=type_obj.name, + runtime_type=type_obj.runtime_type, + ), + config=config, + config_meta=config_meta, + args=args, + kwargs=kwargs, + vm=vm, + cache=ValueCache(), + ) + return context diff --git a/internal/kclvm_py/vm/runtime/evaluator/union.py b/internal/kclvm_py/vm/runtime/evaluator/union.py new file mode 100644 index 000000000..ce9d9524c --- /dev/null +++ b/internal/kclvm_py/vm/runtime/evaluator/union.py @@ -0,0 +1,290 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +from typing import List, Union, cast + +import kclvm.kcl.error as kcl_error +import kclvm.kcl.ast as ast +from kclvm.api.object import ( + KCLObject, + KCLSchemaObject, + KCLSchemaTypeObject, + KCLListObject, + KCLDictObject, + KCLNoneObject, + KCLUndefinedObject, + KCLStringObject, + KCLLiteralObject, + KCLSchemaConfigObject, + KCLConfigObjectMixin, + to_python_obj, + to_kcl_obj, +) +from kclvm.api.object.internal import Undefined, UndefinedType +from kclvm.compiler.check.check_type import check_type_builtin +from kclvm.unification import value_subsume + + +def override_config_attr( + attr: str, + obj: Union[KCLDictObject, KCLSchemaObject], + config_value: KCLObject, + index: int = None, +): + if index is None or index < 0: + obj.update_key_value(attr, config_value) + else: + obj.list_key_override(attr, config_value, index) + + +def insert_config_attr( + attr: str, + obj: Union[KCLDictObject, KCLSchemaObject], + config_value: KCLObject, + index: int = None, +): + if index is not None and index >= 0: + config_value = KCLListObject(items=[config_value]) + obj.insert_with_key(attr, config_value, index) + + +def resolve_schema_obj( + schema_obj: KCLSchemaObject, keys: set, vm=None +) -> KCLSchemaObject: + """Using a schema object config to resolve and generate a new schema""" + if not vm or not schema_obj or not isinstance(schema_obj, KCLSchemaObject): + return schema_obj + schema_type_obj = cast( + KCLSchemaTypeObject, + vm.all_schema_types.get(f"{schema_obj.pkgpath}.{schema_obj.name}"), + ) + if not schema_type_obj: + return schema_obj + filename, line, column = vm.get_info() + config_meta = { + "$filename": filename, + "$lineno": line, + "$columnno": column, + } + config = KCLSchemaConfigObject( + value={k: schema_obj.attrs[k] for k in schema_obj.attrs if k in keys}, + operation_map=schema_obj.operation_map, + ) + return schema_type_obj.new_instance(config, config_meta, [], [], vm) + + +def do_union( + obj: KCLObject, + delta: KCLObject, + should_list_override: bool = False, + should_idempotent_check: bool = False, + should_config_resolve: bool = False, + vm=None, +) -> KCLObject: + """ + Union delta to obj recursively + """ + obj_tpe = obj.type_str() + delta_tpe = delta.type_str() + if isinstance(delta, KCLStringObject): + delta_tpe = "str" + if isinstance(obj, KCLListObject): + if not isinstance(delta, KCLListObject): + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + arg_msg=f"union failure, expect list, got {delta_tpe}", + ) + + length = ( + len(obj.value) if len(obj.value) > len(delta.value) else len(delta.value) + ) + if should_list_override: + return delta + result_list = obj + for idx in range(length): + if idx >= len(obj.value): + result_list.value.append(delta.value[idx]) + elif idx < len(delta.value): + result_list.value[idx] = union( + result_list.value[idx], + delta.value[idx], + should_list_override=should_list_override, + should_idempotent_check=should_idempotent_check, + should_config_resolve=should_config_resolve, + vm=vm, + ) + return result_list + if isinstance(obj, KCLDictObject): + if not isinstance(delta, KCLDictObject): + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + arg_msg=f"union failure, expect dict, got {delta_tpe}", + ) + result_dict = obj + if isinstance(obj, KCLConfigObjectMixin): + obj.update_attr_op_using_obj(delta) + for k in delta.value: + operation = ( + delta.get_operation(k) + if isinstance(delta, KCLConfigObjectMixin) + else ast.ConfigEntryOperation.UNION + ) + insert_index = ( + delta.get_insert_index(k) + if isinstance(delta, KCLConfigObjectMixin) + else None + ) + if k not in obj.value: + result_dict.value[k] = delta.value[k] + else: + if operation == ast.ConfigEntryOperation.OVERRIDE: + override_config_attr(k, result_dict, delta.value[k], insert_index) + if operation == ast.ConfigEntryOperation.INSERT: + insert_config_attr(k, result_dict, delta.value[k], insert_index) + else: + if ( + should_idempotent_check + and k in obj.value + and not value_subsume(delta.value[k], obj.value[k], False) + ): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"conflicting values on the attribute '{k}' between " + f"{to_python_obj(delta)} and {to_python_obj(obj)}", + ) + result_dict.value[k] = union( + obj.value[k], + delta.value[k], + should_list_override=should_list_override, + should_idempotent_check=should_idempotent_check, + should_config_resolve=should_config_resolve, + vm=vm, + ) + return result_dict + if isinstance(obj, KCLSchemaObject): + delta_dict = {} + if isinstance(delta, (KCLDictObject, KCLSchemaObject)): + delta_dict = delta.value + else: + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + arg_msg=f"union failure, expect {obj_tpe}, got {delta_tpe}", + ) + if should_config_resolve: + common_keys = obj.config_keys | delta.config_keys + if isinstance(obj, KCLConfigObjectMixin): + obj.update_attr_op_using_obj(delta) + for k in delta_dict: + if should_config_resolve and k not in common_keys: + continue + operation = ( + delta.get_operation(k) + if isinstance(delta, KCLConfigObjectMixin) + else ast.ConfigEntryOperation.UNION + ) + insert_index = ( + delta.get_insert_index(k) + if isinstance(delta, KCLConfigObjectMixin) + else None + ) + if ( + should_config_resolve + and k not in obj.attrs + and not obj.should_add_attr(k) + ): + kcl_error.report_exception( + err_type=kcl_error.ErrType.CannotAddMembers_Runtime_TYPE, + arg_msg=kcl_error.CANNOT_ADD_MEMBERS_MSG.format(k, obj.name), + ) + if operation == ast.ConfigEntryOperation.OVERRIDE: + override_config_attr(k, obj, delta_dict[k], insert_index) + if operation == ast.ConfigEntryOperation.INSERT: + insert_config_attr(k, obj, delta_dict[k], insert_index) + else: + if ( + should_idempotent_check + and k in obj.attrs + and not value_subsume(delta_dict[k], obj.attrs[k], False) + ): + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + arg_msg=f"conflicting values on the attribute '{k}' between " + f"{to_python_obj(obj)} and {to_python_obj(delta_dict)}", + ) + obj.attrs[k] = union( + obj.attrs.get(k), + delta_dict[k], + should_list_override=should_list_override, + should_idempotent_check=should_idempotent_check, + should_config_resolve=should_config_resolve, + vm=vm, + ) + # Do type check and pack + if isinstance(obj.attrs[k], KCLLiteralObject): + check_type_builtin(obj.attrs[k], obj.get_attr_type(k)) + if should_config_resolve: + obj = resolve_schema_obj(obj, common_keys, vm=vm) + obj.config_keys = common_keys + return obj + if type(obj) != type(delta): + kcl_error.report_exception( + err_type=kcl_error.ErrType.TypeError_Runtime_TYPE, + arg_msg=f"union failure, expect {obj_tpe}, got {delta_tpe}", + ) + return delta + + +def union( + obj: KCLObject, + delta: KCLObject, + or_mode: bool = False, + should_list_override: bool = False, + should_idempotent_check: bool = False, + should_config_resolve: bool = False, + vm=None, +) -> KCLObject: + if ( + obj is None + or obj is Undefined + or isinstance(obj, UndefinedType) + or isinstance(obj, (KCLNoneObject, KCLUndefinedObject)) + ): + return delta + if ( + delta is None + or delta is Undefined + or isinstance(delta, UndefinedType) + or isinstance(delta, (KCLNoneObject, KCLUndefinedObject)) + ): + return obj + if isinstance(obj, (KCLListObject, KCLSchemaObject, KCLDictObject)) or isinstance( + delta, (KCLListObject, KCLSchemaObject, KCLDictObject) + ): + return do_union( + obj, + delta, + should_list_override=should_list_override, + should_idempotent_check=should_idempotent_check, + should_config_resolve=should_config_resolve, + vm=vm, + ) + if or_mode: + return to_kcl_obj(to_python_obj(obj) | to_python_obj(delta)) + else: + return obj if isinstance(delta, (KCLNoneObject, KCLUndefinedObject)) else delta + + +def merge(objs: List[KCLObject], vm=None) -> KCLObject: + """Merge all objects recursively + + - literal variables, override + - list variables, override + - dict/schema variables, union + """ + initial_object = KCLNoneObject.instance() + if not objs: + return initial_object + for obj in objs: + if not obj or isinstance(obj, (KCLNoneObject, KCLUndefinedObject)): + continue + initial_object = union(initial_object, obj, should_list_override=True, vm=vm) + return initial_object diff --git a/internal/kclvm_py/vm/vm.py b/internal/kclvm_py/vm/vm.py new file mode 100644 index 000000000..195b68320 --- /dev/null +++ b/internal/kclvm_py/vm/vm.py @@ -0,0 +1,476 @@ +"""The `vm` file mainly contains the function `Run` which is used +# Copyright 2021 The KCL Authors. All rights reserved. +to execute the KCL bytecode obtained by the compiler module into +KCL result used to generate YAML/JSON data. +The KCL virtual machine receives a set of bytecode and uses its +own stack structure to store variables and the calculation results +of the variables, which includes `Frame` for context switching and +`VMState` for storing the import cache and executes the program +according to the corresponding opcode sequence and its operands. +When all the operation codes are executed, the entire KCL program +is also executed. +:copyright: Copyright 2020 The KCL Authors. All rights reserved. +""" + +import pathlib +import dataclasses +import typing + +import kclvm.kcl.error as kcl_error +import kclvm.config +import kclvm.compiler.extension.builtin +import kclvm.compiler.extension.plugin +import kclvm.compiler.check.check_type + +from kclvm.api.object import ( + KCLObject, + KCLProgram, + KCLResult, + KCLCompiledFunctionObject, + KCLModuleObject, + KCLSchemaTypeObject, + KWArg, +) + +from .code import Opcode, VM_OP_ACTIONS + + +@dataclasses.dataclass +class Frame: + isp: int = 0 + name: str = None + filename: str = None + colno: int = 0 + lineno: int = 0 + pkgpath: str = None + locals: dict = None + globals: dict = None # Global symbol and KCL object reference + free_vars: list = None # Free symbols + codes: typing.List[int] = None + + def update_info(self, filename: str, lineno: int, colno: int): + self.filename, self.lineno, self.colno = filename, lineno, colno + + def get_info(self) -> typing.Tuple[str, int, int]: + return self.filename, self.lineno, self.colno + + +class VMState: + def __init__(self): + self.modules: typing.Dict[str, KCLModuleObject] = {} # {pkgpath:module} + self.globals_table: typing.Dict[ + str, typing.Dict[str, KCLObject] + ] = {} # {pkgpath:globals} + + +class VirtualMachine: + """KCL Virtual Machine""" + + pkgpath_stack: typing.List[str] = [] + + @staticmethod + def RunApp(app: KCLProgram, *, pkg: str = None) -> KCLResult: + # Reset cache + KCLSchemaTypeObject._eval_cache = {} + VirtualMachine.pkgpath_stack = [] + return VirtualMachine(app).Run(pkg=pkg) + + def __init__(self, app: KCLProgram, state: VMState = None): + super().__init__() + + self.app: KCLProgram = app + self.state: VMState = state or VMState() + + self.names: typing.List[str] = [] + self.constants: typing.List[KCLObject] = [] + self.stack: typing.List[KCLObject] = [] + self.last_obj: typing.Optional[KCLObject] = None + self.ctx: typing.Optional[Frame] = None + self.frames: typing.List[Frame] = [] + self.frame_index = 1 + self.last_popped_frame = None + self.sp: int = 0 # Stack Pointer + + self.cur_run_pkg: str = "" + + self.all_schema_types: typing.Dict[ + str, KCLSchemaTypeObject + ] = {} # [f"{pkgpath}.{name}": obj] + + self.lazy_eval_ctx = None + + self._reset(app.main) + + def Run(self, *, pkg: str = None) -> KCLResult: + pkgpath = pkg if pkg else self.app.main + if pkgpath in self.pkgpath_stack: + kcl_error.report_exception( + err_type=kcl_error.ErrType.RecursiveLoad_TYPE, + arg_msg=kcl_error.RECURSIVE_LOADING_MODULE_MSG.format( + pkgpath, ", ".join(self.pkgpath_stack) + ), + ) + if pkg: + self.pkgpath_stack.append(pkgpath) + self._reset(pkgpath) + return KCLResult(self.run(), self.get_filename()) + + def define_schema_type(self, absname: str, schema_type: KCLSchemaTypeObject): + if absname.startswith("@"): + absname = absname[1:] + self.all_schema_types[absname] = schema_type + + def find_schema_type(self, absname: str) -> typing.Optional[KCLSchemaTypeObject]: + if absname.startswith("@"): + absname = absname[1:] + return self.all_schema_types.get(absname) + + def import_name(self, _code: int, arg: int) -> typing.Optional[KCLModuleObject]: + self.pop() + + pkgpath = self.names[arg] + asname = self.names[arg + 1] + + assert pkgpath and asname + + if pkgpath.startswith("@"): + pkgpath = pkgpath[1:] + if asname.startswith("@"): + asname = asname[1:] + + if pkgpath in self.state.modules: + # Read from module cache + module = self.state.modules[pkgpath] + + self.update_local(asname, module) + self.update_global(f"@{pkgpath}", module) + self.push(module) + + return module + + if pkgpath in kclvm.compiler.extension.builtin.STANDARD_SYSTEM_MODULES: + module = KCLModuleObject( + name=pkgpath, + asname=asname, + value=kclvm.compiler.extension.builtin.get_system_module_func_objects( + pkgpath + ), + ) + + self.update_local(asname, module) + self.update_global(f"@{pkgpath}", module) + self.push(module) + + # Module cache + self.state.modules[pkgpath] = module + self.state.globals_table[pkgpath] = {} + + return module + + if pkgpath.startswith(kclvm.compiler.extension.plugin.PLUGIN_MODULE_NAME): + module = KCLModuleObject( + name=pkgpath.replace( + kclvm.compiler.extension.plugin.PLUGIN_MODULE_NAME, "" + ), + asname=asname, + value=kclvm.compiler.extension.plugin.get_plugin_func_objects(pkgpath), + ) + + self.update_local(asname, module) + self.update_global(f"@{pkgpath}", module) + self.push(module) + + self.state.modules[pkgpath] = module + self.state.globals_table[pkgpath] = {} + + return module + + if pkgpath in self.app.pkgs: + pkg_vm = VirtualMachine(self.app, self.state) + pkg_vm.all_schema_types = self.all_schema_types + + result = pkg_vm.Run(pkg=pkgpath) + + module = KCLModuleObject(name=pkgpath, asname=asname, value=result.m) + + self.update_local(asname, module) + self.update_global(f"@{pkgpath}", module) + self.push(module) + + self.state.modules[pkgpath] = module + self.state.globals_table[pkgpath] = result.m + + return module + kcl_error.report_exception( + err_type=kcl_error.ErrType.CannotFindModule_TYPE, + arg_msg=kcl_error.CANNOT_FIND_MODULE_MSG.format( + pkgpath, + str( + pathlib.Path(self.get_filename()).parent + / (pkgpath.replace(".", "/")) + ), + ), + ) + + def _reset(self, pkg: str): + assert pkg in self.app.pkgs + + self.cur_run_pkg = pkg + bytecode = self.app.pkgs[pkg] + + self.names: typing.List[str] = bytecode.names + self.constants: typing.List[KCLObject] = bytecode.constants + self.stack: typing.List[KCLObject] = [] + self.last_obj: typing.Optional[KCLObject] = None + self.ctx: typing.Optional[Frame] = None + self.frames: typing.List[Frame] = [ + Frame( + codes=bytecode.instructions, + pkgpath=pkg, + locals={}, + globals={}, + free_vars=[], + ) + ] + self.frame_index = 1 + self.last_popped_frame = None + self.sp: int = 0 # Stack Pointer + + def run(self, run_current=False, ignore_nop=False): + try: + self.ctx = self.current_frame() + while self.ctx.codes and self.ctx.isp < len(self.ctx.codes): + codes = self.ctx.codes + code = self.ctx.codes[self.ctx.isp] + self.ctx.isp += 1 + # Skip the invalid opcode + if code == Opcode.INVALID: + continue + arg = None + if Opcode.has_arg(code): + arg = ( + codes[self.ctx.isp] + + (codes[self.ctx.isp + 1] << 8) + + (codes[self.ctx.isp + 2] << 16) + ) + self.ctx.isp += 3 + info = self.ctx.codes[self.ctx.isp] + info = typing.cast(tuple, info) + self.ctx.isp += 1 + self.ctx.update_info(*info) + action = VM_OP_ACTIONS.get(code) + if ignore_nop and (code == Opcode.NOP or code == Opcode.SCHEMA_NOP): + continue + if not action: + raise Exception(f"invalid opcode {code}") + action(self, code, arg) + if code == Opcode.RETURN_VALUE and run_current: + break + except kcl_error.KCLException as err: + if not err.lineno: + filename, line, _ = self.get_info() + errfile = err.pop_err_info() + errfile.filename, errfile.line_no = filename, line or None + err.append_err_info(errfile) + raise err + except Exception as err: + if kclvm.config.debug and kclvm.config.verbose > 2: + raise + filename, lineno, _ = self.get_info() + if isinstance(err, AttributeError): + kcl_error.report_exception( + err_type=kcl_error.ErrType.AttributeError_Runtime_TYPE, + file_msgs=[kcl_error.ErrFileMsg(filename=filename, line_no=lineno)], + arg_msg=str(err), + ) + if isinstance(err, RecursionError): + kcl_error.report_exception( + err_type=kcl_error.ErrType.RecursionError_TYPE, + file_msgs=[kcl_error.ErrFileMsg(filename=filename, line_no=lineno)], + arg_msg=str(err), + ) + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[kcl_error.ErrFileMsg(filename=filename, line_no=lineno)], + arg_msg=str(err), + ) + + return self.ctx.globals + + def get_info(self, with_column=False) -> typing.Tuple[str, int, int]: + filename, line, column = self.ctx.get_info() + return filename, line, column if with_column else None + + def get_filename(self) -> str: + filename, _, _ = self.ctx.get_info() + return filename + + def load_const(self, index: int) -> KCLObject: + cst = self.constants[index] + self.push(cst) + return cst + + def load_name(self, index: int) -> KCLObject: + name = self.names[index] + self.push(self.ctx.globals[name]) + return self.ctx.globals[name] + + def store_name(self, index: int): + name = self.names[index] + self.ctx.globals[name] = kclvm.compiler.check.check_type.check( + self.stack_top(), *self.get_info() + ) + + def load_local(self, index: int): + name = self.names[index] + self.push(self.ctx.locals[name]) + + def store_local(self, index: int): + name = self.names[index] + self.ctx.locals[name] = self.stack_top() + + def update_local(self, name: str, value: KCLObject): + self.ctx.locals[name] = value + + def update_global(self, name: str, value: KCLObject): + self.ctx.globals[name] = kclvm.compiler.check.check_type.check( + value, *self.get_info() + ) + + def load_builtin(self, index: int): + built_obj_list = kclvm.compiler.extension.builtin.get_builtin_func_objects() + self.push(built_obj_list[index]) + + def set_instruction_pointer(self, index: int): + self.ctx.isp = int(index) + + def stack_top(self) -> KCLObject: + return self.stack[-1] + + def current_frame(self) -> Frame: + return self.frames[-1] + + def push_frame(self, frame: Frame, names=None, constants=None): + self.frames.append(frame) + self.ctx = self.frames[-1] + self.frame_index += 1 + + def push_frame_using_callable( + self, + pkgpath: str, + func: KCLCompiledFunctionObject, + args: typing.List[KCLObject], + kwargs: typing.List[KWArg], + args_len: int = 0, + ): + assert isinstance(func, KCLCompiledFunctionObject) + + filename, line, column = self.get_info() + + ctx_globals = self.frames[-1].globals + if pkgpath in self.state.globals_table: + ctx_globals = self.state.globals_table[pkgpath] + self.cur_run_pkg = pkgpath + + self.push_frame( + Frame( + name=func.name, + codes=func.instructions, + pkgpath=pkgpath, + locals={}, + globals=ctx_globals, + free_vars=[], + ), + func.names, + func.constants, + ) + arg_index = 0 + for arg in args[args_len:] or []: + self.push(arg) + for default_arg in func.params: + if default_arg.value: + self.update_local(default_arg.name, default_arg.value) + # args[3:] - schema args + for arg in args[:args_len]: + name = func.params[arg_index].name + self.update_local(name, arg) + arg_index += 1 + for kwarg in kwargs or []: + name = kwarg.name.value + if name not in [p.name for p in func.params]: + kcl_error.report_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=filename, line_no=line, col_no=column + ) + ], + arg_msg=f"schema arguments got an unexpected keyword argument '{name}'", + ) + self.update_local(name, kwarg.value) + + def pop_frame(self) -> Frame: + self.last_popped_frame = self.frames.pop() + self.ctx = self.frames[-1] + self.frame_index -= 1 + return self.last_popped_frame + + def push_function(self, index: int): + func_obj = self.constants[index] + self.push(func_obj) + + def push(self, obj: KCLObject): + self.stack.append(obj) + + def pop(self): + self.last_obj = self.stack.pop() + return self.last_obj + + def peek(self) -> KCLObject: + return self.stack[-1] + + def peek_nth(self, index: int) -> KCLObject: + """View the Nth top item on the stack from index 0""" + return self.stack[-index] + + def top(self) -> KCLObject: + """Get the top of the VM stack""" + return self.stack[-1] + + def set_top(self, obj: KCLObject): + """Set the top of the VM stack""" + self.stack[-1] = obj + + def clear(self): + self.stack.clear() + + def last_popped_obj(self) -> KCLObject: + return self.last_obj + + def debug_stack(self, idx: int, at: int = 0): + if at > 0: + print(f"vm.stack[{-1-idx}]={self.stack[-1-idx]} # at({at})") + else: + print(f"vm.stack[{-1-idx}]={self.stack[-1-idx]}") + + def debug_locals(self, _arg: int, at: int = 0): + if at > 0: + print(f"vm.ctx.locals={self.ctx.locals} # at({at})") + else: + print(f"vm.ctx.locals={self.ctx.locals}") + + def debug_globals(self, _arg: int, at: int = 0): + if at > 0: + print(f"vm.ctx.globals={self.ctx.globals} # at({at})") + else: + print(f"vm.ctx.globals={self.ctx.globals}") + + def debug_names(self, _arg: int, at: int = 0): + if at > 0: + print(f"vm.names={self.names} # at({at})") + else: + print(f"vm.names={self.names}") + + +def Run(app: KCLProgram, *, pkg: str = None) -> KCLResult: + return VirtualMachine.RunApp(app, pkg=pkg) diff --git a/kclvm/3rdparty/rustc_data_structures/Cargo.lock b/kclvm/3rdparty/rustc_data_structures/Cargo.lock new file mode 100644 index 000000000..71a2500cd --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/Cargo.lock @@ -0,0 +1,445 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", + "rustc-rayon", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "proc-macro2" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871372391786ccec00d3c5d3d6608905b3d4db263639cfe075d3b60a736d115a" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-rayon" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" +dependencies = [ + "crossbeam-deque", + "either", + "rustc-rayon-core", +] + +[[package]] +name = "rustc-rayon-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc_data_structures" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ena", + "indexmap", + "jobserver", + "libc", + "memmap2", + "parking_lot", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "stable_deref_trait", + "stacker", + "tempfile", + "tracing", + "winapi", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "tracing" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/kclvm/3rdparty/rustc_data_structures/Cargo.toml b/kclvm/3rdparty/rustc_data_structures/Cargo.toml new file mode 100644 index 000000000..971e19a87 --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "rustc_data_structures" +version = "0.0.0" +edition = "2021" + +[lib] +doctest = false + +[dependencies] +arrayvec = { version = "0.7", default-features = false } +ena = "0.14" +indexmap = { version = "1.8.0", features = ["rustc-rayon"] } +tracing = "0.1" +jobserver_crate = { version = "0.1.13", package = "jobserver" } + +cfg-if = "0.1.2" +stable_deref_trait = "1.0.0" +rayon = { version = "0.3.2", package = "rustc-rayon" } +rayon-core = { version = "0.3.2", package = "rustc-rayon-core" } +rustc-hash = "1.1.0" +bitflags = "1.2.1" +libc = "0.2" +stacker = "0.1.14" +tempfile = "3.2" + +[dependencies.parking_lot] +version = "0.12" + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["fileapi", "psapi", "winerror"] } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +memmap2 = "0.2.1" diff --git a/kclvm/3rdparty/rustc_data_structures/src/LICENSE b/kclvm/3rdparty/rustc_data_structures/src/LICENSE new file mode 100644 index 000000000..8467a0168 --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/LICENSE @@ -0,0 +1,231 @@ +Short version for non-lawyers: + +The Rust Project is dual-licensed under Apache 2.0 and MIT +terms. + + +Longer version: + +Copyrights in the Rust project are retained by their contributors. No +copyright assignment is required to contribute to the Rust project. + +Some files include explicit copyright notices and/or license notices. +For full authorship information, see the version control history or +https://thanks.rust-lang.org + +Except as otherwise noted (below and/or in individual files), Rust is +licensed under the Apache License, Version 2.0 or + or the MIT license + or , at your option. + + +The Rust Project includes packages written by third parties. +The following third party packages are included, and carry +their own copyright notices and license terms: + +* LLVM. Code for this package is found in src/llvm-project. + + Copyright (c) 2003-2013 University of Illinois at + Urbana-Champaign. All rights reserved. + + Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + + Permission is hereby granted, free of charge, to any + person obtaining a copy of this software and associated + documentation files (the "Software"), to deal with the + Software without restriction, including without + limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following + conditions: + + * Redistributions of source code must retain the + above copyright notice, this list of conditions + and the following disclaimers. + + * Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimers in the documentation + and/or other materials provided with the + distribution. + + * Neither the names of the LLVM Team, University of + Illinois at Urbana-Champaign, nor the names of its + contributors may be used to endorse or promote + products derived from this Software without + specific prior written permission. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE + FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT + OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS WITH THE SOFTWARE. + +* Additional libraries included in LLVM carry separate + BSD-compatible licenses. See src/llvm-project/llvm/LICENSE.TXT + for details. + +* compiler-rt, in src/compiler-rt is dual licensed under + LLVM's license and MIT: + + Copyright (c) 2009-2014 by the contributors listed in + CREDITS.TXT + + All rights reserved. + + Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + + Permission is hereby granted, free of charge, to any + person obtaining a copy of this software and associated + documentation files (the "Software"), to deal with the + Software without restriction, including without + limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following + conditions: + + * Redistributions of source code must retain the + above copyright notice, this list of conditions + and the following disclaimers. + + * Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimers in the documentation + and/or other materials provided with the + distribution. + + * Neither the names of the LLVM Team, University of + Illinois at Urbana-Champaign, nor the names of its + contributors may be used to endorse or promote + products derived from this Software without + specific prior written permission. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE + FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT + OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS WITH THE SOFTWARE. + + ======================================================== + + Copyright (c) 2009-2014 by the contributors listed in + CREDITS.TXT + + Permission is hereby granted, free of charge, to any + person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the + Software without restriction, including without + limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice + shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +* Portions of the FFI code for interacting with the native ABI + is derived from the Clay programming language, which carries + the following license. + + Copyright (C) 2008-2010 Tachyon Technologies. + All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, are permitted provided that the + following conditions are met: + + 1. Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + + 2. Redistributions in binary form must reproduce the + above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or + other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH DAMAGE. + +* libbacktrace, under src/libbacktrace: + + Copyright (C) 2012-2014 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + + Redistribution and use in source and binary forms, with + or without modification, are permitted provided that the + following conditions are met: + + (1) Redistributions of source code must retain the + above copyright notice, this list of conditions and + the following disclaimer. + + (2) Redistributions in binary form must reproduce + the above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the + distribution. + + (3) The name of the author may not be used to + endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH DAMAGE. */ \ No newline at end of file diff --git a/kclvm/3rdparty/rustc_data_structures/src/atomic_ref.rs b/kclvm/3rdparty/rustc_data_structures/src/atomic_ref.rs new file mode 100644 index 000000000..eeb1b3092 --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/atomic_ref.rs @@ -0,0 +1,26 @@ +use std::marker::PhantomData; +use std::sync::atomic::{AtomicPtr, Ordering}; + +/// This is essentially an `AtomicPtr` but is guaranteed to always be valid +pub struct AtomicRef(AtomicPtr, PhantomData<&'static T>); + +impl AtomicRef { + pub const fn new(initial: &'static T) -> AtomicRef { + AtomicRef(AtomicPtr::new(initial as *const T as *mut T), PhantomData) + } + + pub fn swap(&self, new: &'static T) -> &'static T { + // We never allow storing anything but a `'static` reference so it's safe to + // return it for the same. + unsafe { &*self.0.swap(new as *const T as *mut T, Ordering::SeqCst) } + } +} + +impl std::ops::Deref for AtomicRef { + type Target = T; + fn deref(&self) -> &Self::Target { + // We never allow storing anything but a `'static` reference so it's safe to lend + // it out for any amount of time. + unsafe { &*self.0.load(Ordering::SeqCst) } + } +} diff --git a/kclvm/3rdparty/rustc_data_structures/src/base_n.rs b/kclvm/3rdparty/rustc_data_structures/src/base_n.rs new file mode 100644 index 000000000..3c7bea271 --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/base_n.rs @@ -0,0 +1,42 @@ +/// Converts unsigned integers into a string representation with some base. +/// Bases up to and including 36 can be used for case-insensitive things. +use std::str; + +#[cfg(test)] +mod tests; + +pub const MAX_BASE: usize = 64; +pub const ALPHANUMERIC_ONLY: usize = 62; +pub const CASE_INSENSITIVE: usize = 36; + +const BASE_64: &[u8; MAX_BASE as usize] = + b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@$"; + +#[inline] +pub fn push_str(mut n: u128, base: usize, output: &mut String) { + debug_assert!(base >= 2 && base <= MAX_BASE); + let mut s = [0u8; 128]; + let mut index = 0; + + let base = base as u128; + + loop { + s[index] = BASE_64[(n % base) as usize]; + index += 1; + n /= base; + + if n == 0 { + break; + } + } + s[0..index].reverse(); + + output.push_str(str::from_utf8(&s[0..index]).unwrap()); +} + +#[inline] +pub fn encode(n: u128, base: usize) -> String { + let mut s = String::new(); + push_str(n, base, &mut s); + s +} diff --git a/kclvm/3rdparty/rustc_data_structures/src/captures.rs b/kclvm/3rdparty/rustc_data_structures/src/captures.rs new file mode 100644 index 000000000..677ccb314 --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/captures.rs @@ -0,0 +1,8 @@ +/// "Signaling" trait used in impl trait to tag lifetimes that you may +/// need to capture but don't really need for other reasons. +/// Basically a workaround; see [this comment] for details. +/// +/// [this comment]: https://github.com/rust-lang/rust/issues/34511#issuecomment-373423999 +pub trait Captures<'a> {} + +impl<'a, T: ?Sized> Captures<'a> for T {} diff --git a/kclvm/3rdparty/rustc_data_structures/src/flock.rs b/kclvm/3rdparty/rustc_data_structures/src/flock.rs new file mode 100644 index 000000000..293ef4caa --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/flock.rs @@ -0,0 +1,231 @@ +//! Simple file-locking apis for each OS. +//! +//! This is not meant to be in the standard library, it does nothing with +//! green/native threading. This is just a bare-bones enough solution for +//! librustdoc, it is not production quality at all. + +#![allow(non_camel_case_types)] +#![allow(nonstandard_style)] + +use std::fs::{File, OpenOptions}; +use std::io; +use std::path::Path; + +cfg_if! { + // We use `flock` rather than `fcntl` on Linux, because WSL1 does not support + // `fcntl`-style advisory locks properly (rust-lang/rust#72157). + // + // For other Unix targets we still use `fcntl` because it's more portable than + // `flock`. + if #[cfg(target_os = "linux")] { + use std::os::unix::prelude::*; + + #[derive(Debug)] + pub struct Lock { + _file: File, + } + + impl Lock { + pub fn new(p: &Path, + wait: bool, + create: bool, + exclusive: bool) + -> io::Result { + let file = OpenOptions::new() + .read(true) + .write(true) + .create(create) + .mode(libc::S_IRWXU as u32) + .open(p)?; + + let mut operation = if exclusive { + libc::LOCK_EX + } else { + libc::LOCK_SH + }; + if !wait { + operation |= libc::LOCK_NB + } + + let ret = unsafe { libc::flock(file.as_raw_fd(), operation) }; + if ret == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(Lock { _file: file }) + } + } + + pub fn error_unsupported(err: &io::Error) -> bool { + matches!(err.raw_os_error(), Some(libc::ENOTSUP) | Some(libc::ENOSYS)) + } + } + + // Note that we don't need a Drop impl to execute `flock(fd, LOCK_UN)`. Lock acquired by + // `flock` is associated with the file descriptor and closing the file release it + // automatically. + } else if #[cfg(unix)] { + use std::mem; + use std::os::unix::prelude::*; + + #[derive(Debug)] + pub struct Lock { + file: File, + } + + impl Lock { + pub fn new(p: &Path, + wait: bool, + create: bool, + exclusive: bool) + -> io::Result { + let file = OpenOptions::new() + .read(true) + .write(true) + .create(create) + .mode(libc::S_IRWXU as u32) + .open(p)?; + + let lock_type = if exclusive { + libc::F_WRLCK + } else { + libc::F_RDLCK + }; + + let mut flock: libc::flock = unsafe { mem::zeroed() }; + flock.l_type = lock_type as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + flock.l_start = 0; + flock.l_len = 0; + + let cmd = if wait { libc::F_SETLKW } else { libc::F_SETLK }; + let ret = unsafe { + libc::fcntl(file.as_raw_fd(), cmd, &flock) + }; + if ret == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(Lock { file }) + } + } + + pub fn error_unsupported(err: &io::Error) -> bool { + matches!(err.raw_os_error(), Some(libc::ENOTSUP) | Some(libc::ENOSYS)) + } + } + + impl Drop for Lock { + fn drop(&mut self) { + let mut flock: libc::flock = unsafe { mem::zeroed() }; + flock.l_type = libc::F_UNLCK as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + flock.l_start = 0; + flock.l_len = 0; + + unsafe { + libc::fcntl(self.file.as_raw_fd(), libc::F_SETLK, &flock); + } + } + } + } else if #[cfg(windows)] { + use std::mem; + use std::os::windows::prelude::*; + + use winapi::shared::winerror::ERROR_INVALID_FUNCTION; + use winapi::um::minwinbase::{OVERLAPPED, LOCKFILE_FAIL_IMMEDIATELY, LOCKFILE_EXCLUSIVE_LOCK}; + use winapi::um::fileapi::LockFileEx; + use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE}; + + #[derive(Debug)] + pub struct Lock { + _file: File, + } + + impl Lock { + pub fn new(p: &Path, + wait: bool, + create: bool, + exclusive: bool) + -> io::Result { + assert!(p.parent().unwrap().exists(), + "Parent directory of lock-file must exist: {}", + p.display()); + + let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; + + let mut open_options = OpenOptions::new(); + open_options.read(true) + .share_mode(share_mode); + + if create { + open_options.create(true) + .write(true); + } + + debug!("attempting to open lock file `{}`", p.display()); + let file = match open_options.open(p) { + Ok(file) => { + debug!("lock file opened successfully"); + file + } + Err(err) => { + debug!("error opening lock file: {}", err); + return Err(err) + } + }; + + let ret = unsafe { + let mut overlapped: OVERLAPPED = mem::zeroed(); + + let mut dwFlags = 0; + if !wait { + dwFlags |= LOCKFILE_FAIL_IMMEDIATELY; + } + + if exclusive { + dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; + } + + debug!("attempting to acquire lock on lock file `{}`", + p.display()); + LockFileEx(file.as_raw_handle(), + dwFlags, + 0, + 0xFFFF_FFFF, + 0xFFFF_FFFF, + &mut overlapped) + }; + if ret == 0 { + let err = io::Error::last_os_error(); + debug!("failed acquiring file lock: {}", err); + Err(err) + } else { + debug!("successfully acquired lock"); + Ok(Lock { _file: file }) + } + } + + pub fn error_unsupported(err: &io::Error) -> bool { + err.raw_os_error() == Some(ERROR_INVALID_FUNCTION as i32) + } + } + + // Note that we don't need a Drop impl on the Windows: The file is unlocked + // automatically when it's closed. + } else { + #[derive(Debug)] + pub struct Lock(()); + + impl Lock { + pub fn new(_p: &Path, _wait: bool, _create: bool, _exclusive: bool) + -> io::Result + { + let msg = "file locks not supported on this platform"; + Err(io::Error::new(io::ErrorKind::Other, msg)) + } + + pub fn error_unsupported(_err: &io::Error) -> bool { + true + } + } + } +} diff --git a/kclvm/3rdparty/rustc_data_structures/src/frozen.rs b/kclvm/3rdparty/rustc_data_structures/src/frozen.rs new file mode 100644 index 000000000..2daf5b041 --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/frozen.rs @@ -0,0 +1,63 @@ +//! An immutable, owned value (except for interior mutability). +//! +//! The purpose of `Frozen` is to make a value immutable for the sake of defensive programming. For example, +//! suppose we have the following: +//! +//! ```rust +//! struct Bar { /* some data */ } +//! +//! struct Foo { +//! /// Some computed data that should never change after construction. +//! pub computed: Bar, +//! +//! /* some other fields */ +//! } +//! +//! impl Bar { +//! /// Mutate the `Bar`. +//! pub fn mutate(&mut self) { } +//! } +//! ``` +//! +//! Now suppose we want to pass around a mutable `Foo` instance but, we want to make sure that +//! `computed` does not change accidentally (e.g. somebody might accidentally call +//! `foo.computed.mutate()`). This is what `Frozen` is for. We can do the following: +//! +//! ```rust +//! use rustc_data_structures::frozen::Frozen; +//! +//! struct Foo { +//! /// Some computed data that should never change after construction. +//! pub computed: Frozen, +//! +//! /* some other fields */ +//! } +//! ``` +//! +//! `Frozen` impls `Deref`, so we can ergonomically call methods on `Bar`, but it doesn't `impl +//! DerefMut`. Now calling `foo.compute.mutate()` will result in a compile-time error stating that +//! `mutate` requires a mutable reference but we don't have one. +//! +//! # Caveats +//! +//! - `Frozen` doesn't try to defend against interior mutability (e.g. `Frozen>`). +//! - `Frozen` doesn't pin it's contents (e.g. one could still do `foo.computed = +//! Frozen::freeze(new_bar)`). + +/// An owned immutable value. +#[derive(Debug)] +pub struct Frozen(T); + +impl Frozen { + pub fn freeze(val: T) -> Self { + Frozen(val) + } +} + +impl std::ops::Deref for Frozen { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} diff --git a/kclvm/3rdparty/rustc_data_structures/src/fx.rs b/kclvm/3rdparty/rustc_data_structures/src/fx.rs new file mode 100644 index 000000000..bbeb193db --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/fx.rs @@ -0,0 +1,14 @@ +use std::hash::BuildHasherDefault; + +pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; + +pub type FxIndexMap = indexmap::IndexMap>; +pub type FxIndexSet = indexmap::IndexSet>; + +#[macro_export] +macro_rules! define_id_collections { + ($map_name:ident, $set_name:ident, $key:ty) => { + pub type $map_name = $crate::fx::FxHashMap<$key, T>; + pub type $set_name = $crate::fx::FxHashSet<$key>; + }; +} diff --git a/kclvm/3rdparty/rustc_data_structures/src/lib.rs b/kclvm/3rdparty/rustc_data_structures/src/lib.rs new file mode 100644 index 000000000..aa2ec1022 --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/lib.rs @@ -0,0 +1,78 @@ +//! Various data structures used by the Rust compiler. The intention +//! is that code in here should be not be *specific* to rustc, so that +//! it can be easily unit tested and so forth. +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![allow(rustc::default_hash_types)] +#![deny(unaligned_references)] +#![allow(rustc::potential_query_instability)] + +extern crate tracing; +#[macro_use] +extern crate cfg_if; + +#[inline(never)] +#[cold] +pub fn cold_path R, R>(f: F) -> R { + f() +} + +#[macro_export] +macro_rules! likely { + ($e:expr) => { + match $e { + #[allow(unused_unsafe)] + e => unsafe { std::intrinsics::likely(e) }, + } + }; +} + +pub mod base_n; + +pub mod captures; +pub mod flock; +pub mod fx; + +pub mod macros; +pub mod stable_map; +pub use ena::snapshot_vec; +pub mod stable_set; +#[macro_use] + +mod atomic_ref; +pub mod stack; +pub mod sync; +pub use atomic_ref::AtomicRef; +pub mod frozen; + +pub mod temp_dir; +pub mod unhash; + +pub use ena::undo_log; +pub use ena::unify; + +pub struct OnDrop(pub F); + +impl OnDrop { + /// Forgets the function which prevents it from running. + /// Ensure that the function owns no memory, otherwise it will be leaked. + #[inline] + pub fn disable(self) { + std::mem::forget(self); + } +} + +impl Drop for OnDrop { + #[inline] + fn drop(&mut self) { + (self.0)(); + } +} + +// See comments in src/librustc_middle/lib.rs +#[doc(hidden)] +pub fn __noop_fix_for_27438() {} diff --git a/kclvm/3rdparty/rustc_data_structures/src/macros.rs b/kclvm/3rdparty/rustc_data_structures/src/macros.rs new file mode 100644 index 000000000..e05491f6f --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/macros.rs @@ -0,0 +1,37 @@ +#[macro_export] +macro_rules! enum_from_u32 { + ($(#[$attr:meta])* pub enum $name:ident { + $($(#[$var_attr:meta])* $variant:ident = $e:expr,)* + }) => { + $(#[$attr])* + pub enum $name { + $($(#[$var_attr])* $variant = $e),* + } + + impl $name { + pub fn from_u32(u: u32) -> Option<$name> { + $(if u == $name::$variant as u32 { + return Some($name::$variant) + })* + None + } + } + }; + ($(#[$attr:meta])* pub enum $name:ident { + $($(#[$var_attr:meta])* $variant:ident,)* + }) => { + $(#[$attr])* + pub enum $name { + $($(#[$var_attr])* $variant,)* + } + + impl $name { + pub fn from_u32(u: u32) -> Option<$name> { + $(if u == $name::$variant as u32 { + return Some($name::$variant) + })* + None + } + } + } +} diff --git a/kclvm/3rdparty/rustc_data_structures/src/stable_map.rs b/kclvm/3rdparty/rustc_data_structures/src/stable_map.rs new file mode 100644 index 000000000..670452d0d --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/stable_map.rs @@ -0,0 +1,100 @@ +pub use rustc_hash::FxHashMap; +use std::borrow::Borrow; +use std::collections::hash_map::Entry; +use std::fmt; +use std::hash::Hash; + +/// A deterministic wrapper around FxHashMap that does not provide iteration support. +/// +/// It supports insert, remove, get and get_mut functions from FxHashMap. +/// It also allows to convert hashmap to a sorted vector with the method `into_sorted_vector()`. +#[derive(Clone)] +pub struct StableMap { + base: FxHashMap, +} + +impl Default for StableMap +where + K: Eq + Hash, +{ + fn default() -> StableMap { + StableMap::new() + } +} + +impl fmt::Debug for StableMap +where + K: Eq + Hash + fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.base) + } +} + +impl PartialEq for StableMap +where + K: Eq + Hash, + V: PartialEq, +{ + fn eq(&self, other: &StableMap) -> bool { + self.base == other.base + } +} + +impl Eq for StableMap +where + K: Eq + Hash, + V: Eq, +{ +} + +impl StableMap +where + K: Eq + Hash, +{ + pub fn new() -> StableMap { + StableMap { base: FxHashMap::default() } + } + + pub fn into_sorted_vector(self) -> Vec<(K, V)> + where + K: Ord + Copy, + { + let mut vector = self.base.into_iter().collect::>(); + vector.sort_unstable_by_key(|pair| pair.0); + vector + } + + pub fn entry(&mut self, k: K) -> Entry<'_, K, V> { + self.base.entry(k) + } + + pub fn get(&self, k: &Q) -> Option<&V> + where + K: Borrow, + Q: Hash + Eq, + { + self.base.get(k) + } + + pub fn get_mut(&mut self, k: &Q) -> Option<&mut V> + where + K: Borrow, + Q: Hash + Eq, + { + self.base.get_mut(k) + } + + pub fn insert(&mut self, k: K, v: V) -> Option { + self.base.insert(k, v) + } + + pub fn remove(&mut self, k: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq, + { + self.base.remove(k) + } +} diff --git a/kclvm/3rdparty/rustc_data_structures/src/stable_set.rs b/kclvm/3rdparty/rustc_data_structures/src/stable_set.rs new file mode 100644 index 000000000..c7ca74f5f --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/stable_set.rs @@ -0,0 +1,77 @@ +pub use rustc_hash::FxHashSet; +use std::borrow::Borrow; +use std::fmt; +use std::hash::Hash; + +/// A deterministic wrapper around FxHashSet that does not provide iteration support. +/// +/// It supports insert, remove, get functions from FxHashSet. +/// It also allows to convert hashset to a sorted vector with the method `into_sorted_vector()`. +#[derive(Clone)] +pub struct StableSet { + base: FxHashSet, +} + +impl Default for StableSet +where + T: Eq + Hash, +{ + fn default() -> StableSet { + StableSet::new() + } +} + +impl fmt::Debug for StableSet +where + T: Eq + Hash + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.base) + } +} + +impl PartialEq> for StableSet +where + T: Eq + Hash, +{ + fn eq(&self, other: &StableSet) -> bool { + self.base == other.base + } +} + +impl Eq for StableSet where T: Eq + Hash {} + +impl StableSet { + pub fn new() -> StableSet { + StableSet { base: FxHashSet::default() } + } + + pub fn into_sorted_vector(self) -> Vec + where + T: Ord, + { + let mut vector = self.base.into_iter().collect::>(); + vector.sort_unstable(); + vector + } + + pub fn get(&self, value: &Q) -> Option<&T> + where + T: Borrow, + Q: Hash + Eq, + { + self.base.get(value) + } + + pub fn insert(&mut self, value: T) -> bool { + self.base.insert(value) + } + + pub fn remove(&mut self, value: &Q) -> bool + where + T: Borrow, + Q: Hash + Eq, + { + self.base.remove(value) + } +} diff --git a/kclvm/3rdparty/rustc_data_structures/src/stack.rs b/kclvm/3rdparty/rustc_data_structures/src/stack.rs new file mode 100644 index 000000000..3bdd67512 --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/stack.rs @@ -0,0 +1,18 @@ +// This is the amount of bytes that need to be left on the stack before increasing the size. +// It must be at least as large as the stack required by any code that does not call +// `ensure_sufficient_stack`. +const RED_ZONE: usize = 100 * 1024; // 100k + +// Only the first stack that is pushed, grows exponentially (2^n * STACK_PER_RECURSION) from then +// on. This flag has performance relevant characteristics. Don't set it too high. +const STACK_PER_RECURSION: usize = 1 * 1024 * 1024; // 1MB + +/// Grows the stack on demand to prevent stack overflow. Call this in strategic locations +/// to "break up" recursive calls. E.g. almost any call to `visit_expr` or equivalent can benefit +/// from this. +/// +/// Should not be sprinkled around carelessly, as it causes a little bit of overhead. +#[inline] +pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { + stacker::maybe_grow(RED_ZONE, STACK_PER_RECURSION, f) +} diff --git a/kclvm/3rdparty/rustc_data_structures/src/sync.rs b/kclvm/3rdparty/rustc_data_structures/src/sync.rs new file mode 100644 index 000000000..3f0908da1 --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/sync.rs @@ -0,0 +1,110 @@ +//! This module defines types which are thread safe if cfg!(parallel_compiler) is true. +//! +//! `Lrc` is an alias of `Arc` if cfg!(parallel_compiler) is true, `Rc` otherwise. +//! +//! `Lock` is a mutex. +//! It internally uses `parking_lot::Mutex` if cfg!(parallel_compiler) is true, +//! `RefCell` otherwise. +//! +//! `RwLock` is a read-write lock. +//! It internally uses `parking_lot::RwLock` if cfg!(parallel_compiler) is true, +//! `RefCell` otherwise. +//! +//! `MTLock` is a mutex which disappears if cfg!(parallel_compiler) is false. +//! +//! `MTRef` is an immutable reference if cfg!(parallel_compiler), and a mutable reference otherwise. +//! +//! `rustc_erase_owner!` erases an OwningRef owner into Erased or Erased + Send + Sync +//! depending on the value of cfg!(parallel_compiler). + +use std::collections::HashMap; +use std::hash::{BuildHasher, Hash}; + +pub use std::sync::atomic::Ordering; +pub use std::sync::atomic::Ordering::SeqCst; + + pub use std::marker::Send as Send; + pub use std::marker::Sync as Sync; + + pub use parking_lot::RwLockReadGuard as ReadGuard; + pub use parking_lot::MappedRwLockReadGuard as MappedReadGuard; + pub use parking_lot::RwLockWriteGuard as WriteGuard; + pub use parking_lot::MappedRwLockWriteGuard as MappedWriteGuard; + + pub use parking_lot::MutexGuard as LockGuard; + pub use parking_lot::MappedMutexGuard as MappedLockGuard; + + pub use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU32, AtomicU64}; + + pub use std::sync::Arc as Lrc; + pub use std::sync::Weak as Weak; + + pub type MTRef<'a, T> = &'a T; + + pub use rayon::{join, scope}; + + /// Runs a list of blocks in parallel. The first block is executed immediately on + /// the current thread. Use that for the longest running block. + #[macro_export] + macro_rules! parallel { + (impl $fblock:tt [$($c:tt,)*] [$block:tt $(, $rest:tt)*]) => { + parallel!(impl $fblock [$block, $($c,)*] [$($rest),*]) + }; + (impl $fblock:tt [$($blocks:tt,)*] []) => { + ::rustc_data_structures::sync::scope(|s| { + $( + s.spawn(|_| $blocks); + )* + $fblock; + }) + }; + ($fblock:tt, $($blocks:tt),*) => { + // Reverse the order of the later blocks since Rayon executes them in reverse order + // when using a single thread. This ensures the execution order matches that + // of a single threaded rustc + parallel!(impl $fblock [] [$($blocks),*]); + }; + } + + pub use rayon_core::WorkerLocal; + + pub use rayon::iter::ParallelIterator; + use rayon::iter::IntoParallelIterator; + + pub fn par_iter(t: T) -> T::Iter { + t.into_par_iter() + } + + pub fn par_for_each_in( + t: T, + for_each: impl Fn(T::Item) + Sync + Send, + ) { + t.into_par_iter().for_each(for_each) + } + + #[macro_export] + macro_rules! rustc_erase_owner { + ($v:expr) => {{ + let v = $v; + ::rustc_data_structures::sync::assert_send_val(&v); + v.erase_send_sync_owner() + }} + } + + +pub fn assert_sync() {} +pub fn assert_send() {} +pub fn assert_send_val(_t: &T) {} +pub fn assert_send_sync_val(_t: &T) {} + +pub trait HashMapExt { + /// Same as HashMap::insert, but it may panic if there's already an + /// entry for `key` with a value not equal to `value` + fn insert_same(&mut self, key: K, value: V); +} + +impl HashMapExt for HashMap { + fn insert_same(&mut self, key: K, value: V) { + self.entry(key).and_modify(|old| assert!(*old == value)).or_insert(value); + } +} diff --git a/kclvm/3rdparty/rustc_data_structures/src/temp_dir.rs b/kclvm/3rdparty/rustc_data_structures/src/temp_dir.rs new file mode 100644 index 000000000..a780d2386 --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/temp_dir.rs @@ -0,0 +1,34 @@ +use std::mem::ManuallyDrop; +use std::path::Path; +use tempfile::TempDir; + +/// This is used to avoid TempDir being dropped on error paths unintentionally. +#[derive(Debug)] +pub struct MaybeTempDir { + dir: ManuallyDrop, + // Whether the TempDir should be deleted on drop. + keep: bool, +} + +impl Drop for MaybeTempDir { + fn drop(&mut self) { + // SAFETY: We are in the destructor, and no further access will + // occur. + let dir = unsafe { ManuallyDrop::take(&mut self.dir) }; + if self.keep { + dir.into_path(); + } + } +} + +impl AsRef for MaybeTempDir { + fn as_ref(&self) -> &Path { + self.dir.path() + } +} + +impl MaybeTempDir { + pub fn new(dir: TempDir, keep_on_drop: bool) -> MaybeTempDir { + MaybeTempDir { dir: ManuallyDrop::new(dir), keep: keep_on_drop } + } +} diff --git a/kclvm/3rdparty/rustc_data_structures/src/unhash.rs b/kclvm/3rdparty/rustc_data_structures/src/unhash.rs new file mode 100644 index 000000000..48e21a9da --- /dev/null +++ b/kclvm/3rdparty/rustc_data_structures/src/unhash.rs @@ -0,0 +1,29 @@ +use std::collections::{HashMap, HashSet}; +use std::hash::{BuildHasherDefault, Hasher}; + +pub type UnhashMap = HashMap>; +pub type UnhashSet = HashSet>; + +/// This no-op hasher expects only a single `write_u64` call. It's intended for +/// map keys that already have hash-like quality, like `Fingerprint`. +#[derive(Default)] +pub struct Unhasher { + value: u64, +} + +impl Hasher for Unhasher { + #[inline] + fn finish(&self) -> u64 { + self.value + } + + fn write(&mut self, _bytes: &[u8]) { + unimplemented!("use write_u64"); + } + + #[inline] + fn write_u64(&mut self, value: u64) { + debug_assert_eq!(0, self.value, "Unhasher doesn't mix values!"); + self.value = value; + } +} diff --git a/kclvm/3rdparty/rustc_span/Cargo.lock b/kclvm/3rdparty/rustc_span/Cargo.lock new file mode 100644 index 000000000..04210119d --- /dev/null +++ b/kclvm/3rdparty/rustc_span/Cargo.lock @@ -0,0 +1,586 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", + "rustc-rayon", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest", +] + +[[package]] +name = "measureme" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd460fad6e55ca82fa0cd9dab0d315294188fd9ec6efbf4105e5635d4872ef9c" +dependencies = [ + "log", + "memmap2", + "parking_lot", + "perf-event-open-sys", + "rustc-hash", + "smallvec", +] + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "perf-event-open-sys" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce9bedf5da2c234fdf2391ede2b90fabf585355f33100689bc364a3ea558561a" +dependencies = [ + "libc", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "proc-macro2" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871372391786ccec00d3c5d3d6608905b3d4db263639cfe075d3b60a736d115a" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-rayon" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" +dependencies = [ + "crossbeam-deque", + "either", + "rustc-rayon-core", +] + +[[package]] +name = "rustc-rayon-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc_data_structures" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ena", + "indexmap", + "jobserver", + "libc", + "measureme", + "memmap2", + "parking_lot", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "stable_deref_trait", + "stacker", + "tempfile", + "tracing", + "winapi", +] + +[[package]] +name = "rustc_span" +version = "0.0.0" +dependencies = [ + "cfg-if 0.1.10", + "md-5", + "rustc_data_structures", + "scoped-tls", + "sha-1", + "sha2", + "tracing", + "unicode-width", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "tracing" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/kclvm/3rdparty/rustc_span/Cargo.toml b/kclvm/3rdparty/rustc_span/Cargo.toml new file mode 100644 index 000000000..134fe0143 --- /dev/null +++ b/kclvm/3rdparty/rustc_span/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "rustc_span" +version = "0.0.0" +edition = "2021" + +[lib] +doctest = false + +[dependencies] +rustc_data_structures = { path = "../rustc_data_structures" } +scoped-tls = "1.0" +unicode-width = "0.1.4" +cfg-if = "0.1.2" +tracing = "0.1" +sha1 = { package = "sha-1", version = "0.10.0" } +sha2 = "0.10.1" +md5 = { package = "md-5", version = "0.10.0" } \ No newline at end of file diff --git a/kclvm/3rdparty/rustc_span/src/LICENSE b/kclvm/3rdparty/rustc_span/src/LICENSE new file mode 100644 index 000000000..8467a0168 --- /dev/null +++ b/kclvm/3rdparty/rustc_span/src/LICENSE @@ -0,0 +1,231 @@ +Short version for non-lawyers: + +The Rust Project is dual-licensed under Apache 2.0 and MIT +terms. + + +Longer version: + +Copyrights in the Rust project are retained by their contributors. No +copyright assignment is required to contribute to the Rust project. + +Some files include explicit copyright notices and/or license notices. +For full authorship information, see the version control history or +https://thanks.rust-lang.org + +Except as otherwise noted (below and/or in individual files), Rust is +licensed under the Apache License, Version 2.0 or + or the MIT license + or , at your option. + + +The Rust Project includes packages written by third parties. +The following third party packages are included, and carry +their own copyright notices and license terms: + +* LLVM. Code for this package is found in src/llvm-project. + + Copyright (c) 2003-2013 University of Illinois at + Urbana-Champaign. All rights reserved. + + Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + + Permission is hereby granted, free of charge, to any + person obtaining a copy of this software and associated + documentation files (the "Software"), to deal with the + Software without restriction, including without + limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following + conditions: + + * Redistributions of source code must retain the + above copyright notice, this list of conditions + and the following disclaimers. + + * Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimers in the documentation + and/or other materials provided with the + distribution. + + * Neither the names of the LLVM Team, University of + Illinois at Urbana-Champaign, nor the names of its + contributors may be used to endorse or promote + products derived from this Software without + specific prior written permission. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE + FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT + OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS WITH THE SOFTWARE. + +* Additional libraries included in LLVM carry separate + BSD-compatible licenses. See src/llvm-project/llvm/LICENSE.TXT + for details. + +* compiler-rt, in src/compiler-rt is dual licensed under + LLVM's license and MIT: + + Copyright (c) 2009-2014 by the contributors listed in + CREDITS.TXT + + All rights reserved. + + Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + + Permission is hereby granted, free of charge, to any + person obtaining a copy of this software and associated + documentation files (the "Software"), to deal with the + Software without restriction, including without + limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following + conditions: + + * Redistributions of source code must retain the + above copyright notice, this list of conditions + and the following disclaimers. + + * Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimers in the documentation + and/or other materials provided with the + distribution. + + * Neither the names of the LLVM Team, University of + Illinois at Urbana-Champaign, nor the names of its + contributors may be used to endorse or promote + products derived from this Software without + specific prior written permission. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE + FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT + OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS WITH THE SOFTWARE. + + ======================================================== + + Copyright (c) 2009-2014 by the contributors listed in + CREDITS.TXT + + Permission is hereby granted, free of charge, to any + person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the + Software without restriction, including without + limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice + shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +* Portions of the FFI code for interacting with the native ABI + is derived from the Clay programming language, which carries + the following license. + + Copyright (C) 2008-2010 Tachyon Technologies. + All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, are permitted provided that the + following conditions are met: + + 1. Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + + 2. Redistributions in binary form must reproduce the + above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or + other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH DAMAGE. + +* libbacktrace, under src/libbacktrace: + + Copyright (C) 2012-2014 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + + Redistribution and use in source and binary forms, with + or without modification, are permitted provided that the + following conditions are met: + + (1) Redistributions of source code must retain the + above copyright notice, this list of conditions and + the following disclaimer. + + (2) Redistributions in binary form must reproduce + the above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the + distribution. + + (3) The name of the author may not be used to + endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH DAMAGE. */ \ No newline at end of file diff --git a/kclvm/3rdparty/rustc_span/src/README.md b/kclvm/3rdparty/rustc_span/src/README.md new file mode 100644 index 000000000..045516fbd --- /dev/null +++ b/kclvm/3rdparty/rustc_span/src/README.md @@ -0,0 +1,12 @@ +Porting ['rustc_span'] code here to enable code reuse due to the unstable and unreusable of the ['rustc_span'] crate now. +We mainly reuse helper structs and functions like `rustc_span::span`, `rustc_span::spandata`, `rustc_span::sourcemap` to manage source positions in KCLVM. + +Note: the structs and functions here exist as implementations and will not be exposed to other crates directly. + +We remove features on porting code: ++ remove RUST specific features, such as edition and macro hygiene. ++ remove features using unstable Rust features. + +Rewrite or use of other implementation projects may be considered in the future. + +If anyone feels uncomfortable, please feel free to contact us. \ No newline at end of file diff --git a/kclvm/3rdparty/rustc_span/src/analyze_source_file.rs b/kclvm/3rdparty/rustc_span/src/analyze_source_file.rs new file mode 100644 index 000000000..d9d49f95c --- /dev/null +++ b/kclvm/3rdparty/rustc_span/src/analyze_source_file.rs @@ -0,0 +1,274 @@ +use super::*; +use unicode_width::UnicodeWidthChar; + +/// Finds all newlines, multi-byte characters, and non-narrow characters in a +/// SourceFile. +/// +/// This function will use an SSE2 enhanced implementation if hardware support +/// is detected at runtime. +pub fn analyze_source_file( + src: &str, + source_file_start_pos: BytePos, +) -> (Vec, Vec, Vec) { + let mut lines = vec![source_file_start_pos]; + let mut multi_byte_chars = vec![]; + let mut non_narrow_chars = vec![]; + + // Calls the right implementation, depending on hardware support available. + analyze_source_file_dispatch( + src, + source_file_start_pos, + &mut lines, + &mut multi_byte_chars, + &mut non_narrow_chars, + ); + + // The code above optimistically registers a new line *after* each \n + // it encounters. If that point is already outside the source_file, remove + // it again. + if let Some(&last_line_start) = lines.last() { + let source_file_end = source_file_start_pos + BytePos::from_usize(src.len()); + assert!(source_file_end >= last_line_start); + if last_line_start == source_file_end { + lines.pop(); + } + } + + (lines, multi_byte_chars, non_narrow_chars) +} + +cfg_if::cfg_if! { + if #[cfg(all(any(target_arch = "x86", target_arch = "x86_64")))] { + fn analyze_source_file_dispatch(src: &str, + source_file_start_pos: BytePos, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + non_narrow_chars: &mut Vec) { + if is_x86_feature_detected!("sse2") { + unsafe { + analyze_source_file_sse2(src, + source_file_start_pos, + lines, + multi_byte_chars, + non_narrow_chars); + } + } else { + analyze_source_file_generic(src, + src.len(), + source_file_start_pos, + lines, + multi_byte_chars, + non_narrow_chars); + + } + } + + /// Checks 16 byte chunks of text at a time. If the chunk contains + /// something other than printable ASCII characters and newlines, the + /// function falls back to the generic implementation. Otherwise it uses + /// SSE2 intrinsics to quickly find all newlines. + #[target_feature(enable = "sse2")] + unsafe fn analyze_source_file_sse2(src: &str, + output_offset: BytePos, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + non_narrow_chars: &mut Vec) { + #[cfg(target_arch = "x86")] + use std::arch::x86::*; + #[cfg(target_arch = "x86_64")] + use std::arch::x86_64::*; + + const CHUNK_SIZE: usize = 16; + + let src_bytes = src.as_bytes(); + + let chunk_count = src.len() / CHUNK_SIZE; + + // This variable keeps track of where we should start decoding a + // chunk. If a multi-byte character spans across chunk boundaries, + // we need to skip that part in the next chunk because we already + // handled it. + let mut intra_chunk_offset = 0; + + for chunk_index in 0 .. chunk_count { + let ptr = src_bytes.as_ptr() as *const __m128i; + // We don't know if the pointer is aligned to 16 bytes, so we + // use `loadu`, which supports unaligned loading. + let chunk = _mm_loadu_si128(ptr.add(chunk_index)); + + // For character in the chunk, see if its byte value is < 0, which + // indicates that it's part of a UTF-8 char. + let multibyte_test = _mm_cmplt_epi8(chunk, _mm_set1_epi8(0)); + // Create a bit mask from the comparison results. + let multibyte_mask = _mm_movemask_epi8(multibyte_test); + + // If the bit mask is all zero, we only have ASCII chars here: + if multibyte_mask == 0 { + assert!(intra_chunk_offset == 0); + + // Check if there are any control characters in the chunk. All + // control characters that we can encounter at this point have a + // byte value less than 32 or ... + let control_char_test0 = _mm_cmplt_epi8(chunk, _mm_set1_epi8(32)); + let control_char_mask0 = _mm_movemask_epi8(control_char_test0); + + // ... it's the ASCII 'DEL' character with a value of 127. + let control_char_test1 = _mm_cmpeq_epi8(chunk, _mm_set1_epi8(127)); + let control_char_mask1 = _mm_movemask_epi8(control_char_test1); + + let control_char_mask = control_char_mask0 | control_char_mask1; + + if control_char_mask != 0 { + // Check for newlines in the chunk + let newlines_test = _mm_cmpeq_epi8(chunk, _mm_set1_epi8(b'\n' as i8)); + let newlines_mask = _mm_movemask_epi8(newlines_test); + + if control_char_mask == newlines_mask { + // All control characters are newlines, record them + let mut newlines_mask = 0xFFFF0000 | newlines_mask as u32; + let output_offset = output_offset + + BytePos::from_usize(chunk_index * CHUNK_SIZE + 1); + + loop { + let index = newlines_mask.trailing_zeros(); + + if index >= CHUNK_SIZE as u32 { + // We have arrived at the end of the chunk. + break + } + + lines.push(BytePos(index) + output_offset); + + // Clear the bit, so we can find the next one. + newlines_mask &= (!1) << index; + } + + // We are done for this chunk. All control characters were + // newlines and we took care of those. + continue + } else { + // Some of the control characters are not newlines, + // fall through to the slow path below. + } + } else { + // No control characters, nothing to record for this chunk + continue + } + } + + // The slow path. + // There are control chars in here, fallback to generic decoding. + let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset; + intra_chunk_offset = analyze_source_file_generic( + &src[scan_start .. ], + CHUNK_SIZE - intra_chunk_offset, + BytePos::from_usize(scan_start) + output_offset, + lines, + multi_byte_chars, + non_narrow_chars + ); + } + + // There might still be a tail left to analyze + let tail_start = chunk_count * CHUNK_SIZE + intra_chunk_offset; + if tail_start < src.len() { + analyze_source_file_generic(&src[tail_start as usize ..], + src.len() - tail_start, + output_offset + BytePos::from_usize(tail_start), + lines, + multi_byte_chars, + non_narrow_chars); + } + } + } else { + + // The target (or compiler version) does not support SSE2 ... + fn analyze_source_file_dispatch(src: &str, + source_file_start_pos: BytePos, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + non_narrow_chars: &mut Vec) { + analyze_source_file_generic(src, + src.len(), + source_file_start_pos, + lines, + multi_byte_chars, + non_narrow_chars); + } + } +} + +// `scan_len` determines the number of bytes in `src` to scan. Note that the +// function can read past `scan_len` if a multi-byte character start within the +// range but extends past it. The overflow is returned by the function. +fn analyze_source_file_generic( + src: &str, + scan_len: usize, + output_offset: BytePos, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + non_narrow_chars: &mut Vec, +) -> usize { + assert!(src.len() >= scan_len); + let mut i = 0; + let src_bytes = src.as_bytes(); + + while i < scan_len { + let byte = unsafe { + // We verified that i < scan_len <= src.len() + *src_bytes.get_unchecked(i as usize) + }; + + // How much to advance in order to get to the next UTF-8 char in the + // string. + let mut char_len = 1; + + if byte < 32 { + // This is an ASCII control character, it could be one of the cases + // that are interesting to us. + + let pos = BytePos::from_usize(i) + output_offset; + + match byte { + b'\n' => { + lines.push(pos + BytePos(1)); + } + b'\t' => { + non_narrow_chars.push(NonNarrowChar::Tab(pos)); + } + _ => { + non_narrow_chars.push(NonNarrowChar::ZeroWidth(pos)); + } + } + } else if byte >= 127 { + // The slow path: + // This is either ASCII control character "DEL" or the beginning of + // a multibyte char. Just decode to `char`. + let c = (&src[i..]).chars().next().unwrap(); + char_len = c.len_utf8(); + + let pos = BytePos::from_usize(i) + output_offset; + + if char_len > 1 { + assert!((2..=4).contains(&char_len)); + let mbc = MultiByteChar { + pos, + bytes: char_len as u8, + }; + multi_byte_chars.push(mbc); + } + + // Assume control characters are zero width. + // FIXME: How can we decide between `width` and `width_cjk`? + let char_width = UnicodeWidthChar::width(c).unwrap_or(0); + + if char_width != 1 { + non_narrow_chars.push(NonNarrowChar::new(pos, char_width)); + } + } + + i += char_len; + } + + i - scan_len +} diff --git a/kclvm/3rdparty/rustc_span/src/caching_source_map_view.rs b/kclvm/3rdparty/rustc_span/src/caching_source_map_view.rs new file mode 100644 index 000000000..2ab9b3f96 --- /dev/null +++ b/kclvm/3rdparty/rustc_span/src/caching_source_map_view.rs @@ -0,0 +1,301 @@ +use crate::source_map::SourceMap; +use crate::{BytePos, SourceFile, SpanData}; +use rustc_data_structures::sync::Lrc; +use std::ops::Range; + +#[derive(Clone)] +struct CacheEntry { + time_stamp: usize, + line_number: usize, + // The line's byte position range in the `SourceMap`. This range will fail to contain a valid + // position in certain edge cases. Spans often start/end one past something, and when that + // something is the last character of a file (this can happen when a file doesn't end in a + // newline, for example), we'd still like for the position to be considered within the last + // line. However, it isn't according to the exclusive upper bound of this range. We cannot + // change the upper bound to be inclusive, because for most lines, the upper bound is the same + // as the lower bound of the next line, so there would be an ambiguity. + // + // Since the containment aspect of this range is only used to see whether or not the cache + // entry contains a position, the only ramification of the above is that we will get cache + // misses for these rare positions. A line lookup for the position via `SourceMap::lookup_line` + // after a cache miss will produce the last line number, as desired. + line: Range, + file: Lrc, + file_index: usize, +} + +impl CacheEntry { + #[inline] + fn update( + &mut self, + new_file_and_idx: Option<(Lrc, usize)>, + pos: BytePos, + time_stamp: usize, + ) { + if let Some((file, file_idx)) = new_file_and_idx { + self.file = file; + self.file_index = file_idx; + } + + let line_index = self.file.lookup_line(pos).unwrap(); + let line_bounds = self.file.line_bounds(line_index); + self.line_number = line_index + 1; + self.line = line_bounds; + self.touch(time_stamp); + } + + #[inline] + fn touch(&mut self, time_stamp: usize) { + self.time_stamp = time_stamp; + } +} + +#[derive(Clone)] +pub struct CachingSourceMapView<'sm> { + source_map: &'sm SourceMap, + line_cache: [CacheEntry; 3], + time_stamp: usize, +} + +impl<'sm> CachingSourceMapView<'sm> { + pub fn new(source_map: &'sm SourceMap) -> CachingSourceMapView<'sm> { + let files = source_map.files(); + let first_file = files[0].clone(); + let entry = CacheEntry { + time_stamp: 0, + line_number: 0, + line: BytePos(0)..BytePos(0), + file: first_file, + file_index: 0, + }; + + CachingSourceMapView { + source_map, + line_cache: [entry.clone(), entry.clone(), entry], + time_stamp: 0, + } + } + + pub fn byte_pos_to_line_and_col( + &mut self, + pos: BytePos, + ) -> Option<(Lrc, usize, BytePos)> { + self.time_stamp += 1; + + // Check if the position is in one of the cached lines + let cache_idx = self.cache_entry_index(pos); + if cache_idx != -1 { + let cache_entry = &mut self.line_cache[cache_idx as usize]; + cache_entry.touch(self.time_stamp); + + return Some(( + cache_entry.file.clone(), + cache_entry.line_number, + pos - cache_entry.line.start, + )); + } + + // No cache hit ... + let oldest = self.oldest_cache_entry_index(); + + // If the entry doesn't point to the correct file, get the new file and index. + let new_file_and_idx = if !file_contains(&self.line_cache[oldest].file, pos) { + Some(self.file_for_position(pos)?) + } else { + None + }; + + let cache_entry = &mut self.line_cache[oldest]; + cache_entry.update(new_file_and_idx, pos, self.time_stamp); + + Some(( + cache_entry.file.clone(), + cache_entry.line_number, + pos - cache_entry.line.start, + )) + } + + pub fn span_data_to_lines_and_cols( + &mut self, + span_data: &SpanData, + ) -> Option<(Lrc, usize, BytePos, usize, BytePos)> { + self.time_stamp += 1; + + // Check if lo and hi are in the cached lines. + let lo_cache_idx = self.cache_entry_index(span_data.lo); + let hi_cache_idx = self.cache_entry_index(span_data.hi); + + if lo_cache_idx != -1 && hi_cache_idx != -1 { + // Cache hit for span lo and hi. Check if they belong to the same file. + let result = { + let lo = &self.line_cache[lo_cache_idx as usize]; + let hi = &self.line_cache[hi_cache_idx as usize]; + + if lo.file_index != hi.file_index { + return None; + } + + ( + lo.file.clone(), + lo.line_number, + span_data.lo - lo.line.start, + hi.line_number, + span_data.hi - hi.line.start, + ) + }; + + self.line_cache[lo_cache_idx as usize].touch(self.time_stamp); + self.line_cache[hi_cache_idx as usize].touch(self.time_stamp); + + return Some(result); + } + + // No cache hit or cache hit for only one of span lo and hi. + let oldest = if lo_cache_idx != -1 || hi_cache_idx != -1 { + let avoid_idx = if lo_cache_idx != -1 { + lo_cache_idx + } else { + hi_cache_idx + }; + self.oldest_cache_entry_index_avoid(avoid_idx as usize) + } else { + self.oldest_cache_entry_index() + }; + + // If the entry doesn't point to the correct file, get the new file and index. + // Return early if the file containing beginning of span doesn't contain end of span. + let new_file_and_idx = if !file_contains(&self.line_cache[oldest].file, span_data.lo) { + let new_file_and_idx = self.file_for_position(span_data.lo)?; + if !file_contains(&new_file_and_idx.0, span_data.hi) { + return None; + } + + Some(new_file_and_idx) + } else { + let file = &self.line_cache[oldest].file; + if !file_contains(&file, span_data.hi) { + return None; + } + + None + }; + + // Update the cache entries. + let (lo_idx, hi_idx) = match (lo_cache_idx, hi_cache_idx) { + // Oldest cache entry is for span_data.lo line. + (-1, -1) => { + let lo = &mut self.line_cache[oldest]; + lo.update(new_file_and_idx, span_data.lo, self.time_stamp); + + if !lo.line.contains(&span_data.hi) { + let new_file_and_idx = Some((lo.file.clone(), lo.file_index)); + let next_oldest = self.oldest_cache_entry_index_avoid(oldest); + let hi = &mut self.line_cache[next_oldest]; + hi.update(new_file_and_idx, span_data.hi, self.time_stamp); + (oldest, next_oldest) + } else { + (oldest, oldest) + } + } + // Oldest cache entry is for span_data.lo line. + (-1, _) => { + let lo = &mut self.line_cache[oldest]; + lo.update(new_file_and_idx, span_data.lo, self.time_stamp); + let hi = &mut self.line_cache[hi_cache_idx as usize]; + hi.touch(self.time_stamp); + (oldest, hi_cache_idx as usize) + } + // Oldest cache entry is for span_data.hi line. + (_, -1) => { + let hi = &mut self.line_cache[oldest]; + hi.update(new_file_and_idx, span_data.hi, self.time_stamp); + let lo = &mut self.line_cache[lo_cache_idx as usize]; + lo.touch(self.time_stamp); + (lo_cache_idx as usize, oldest) + } + _ => { + panic!(); + } + }; + + let lo = &self.line_cache[lo_idx]; + let hi = &self.line_cache[hi_idx]; + + // Span lo and hi may equal line end when last line doesn't + // end in newline, hence the inclusive upper bounds below. + assert!(span_data.lo >= lo.line.start); + assert!(span_data.lo <= lo.line.end); + assert!(span_data.hi >= hi.line.start); + assert!(span_data.hi <= hi.line.end); + assert!(lo.file.contains(span_data.lo)); + assert!(lo.file.contains(span_data.hi)); + assert_eq!(lo.file_index, hi.file_index); + + Some(( + lo.file.clone(), + lo.line_number, + span_data.lo - lo.line.start, + hi.line_number, + span_data.hi - hi.line.start, + )) + } + + fn cache_entry_index(&self, pos: BytePos) -> isize { + for (idx, cache_entry) in self.line_cache.iter().enumerate() { + if cache_entry.line.contains(&pos) { + return idx as isize; + } + } + + -1 + } + + fn oldest_cache_entry_index(&self) -> usize { + let mut oldest = 0; + + for idx in 1..self.line_cache.len() { + if self.line_cache[idx].time_stamp < self.line_cache[oldest].time_stamp { + oldest = idx; + } + } + + oldest + } + + fn oldest_cache_entry_index_avoid(&self, avoid_idx: usize) -> usize { + let mut oldest = if avoid_idx != 0 { 0 } else { 1 }; + + for idx in 0..self.line_cache.len() { + if idx != avoid_idx + && self.line_cache[idx].time_stamp < self.line_cache[oldest].time_stamp + { + oldest = idx; + } + } + + oldest + } + + fn file_for_position(&self, pos: BytePos) -> Option<(Lrc, usize)> { + if !self.source_map.files().is_empty() { + let file_idx = self.source_map.lookup_source_file_idx(pos); + let file = &self.source_map.files()[file_idx]; + + if file_contains(file, pos) { + return Some((file.clone(), file_idx)); + } + } + + None + } +} + +#[inline] +fn file_contains(file: &SourceFile, pos: BytePos) -> bool { + // `SourceMap::lookup_source_file_idx` and `SourceFile::contains` both consider the position + // one past the end of a file to belong to it. Normally, that's what we want. But for the + // purposes of converting a byte position to a line and column number, we can't come up with a + // line and column number if the file is empty, because an empty file doesn't contain any + // lines. So for our purposes, we don't consider empty files to contain any byte position. + file.contains(pos) && !file.is_empty() +} diff --git a/kclvm/3rdparty/rustc_span/src/fatal_error.rs b/kclvm/3rdparty/rustc_span/src/fatal_error.rs new file mode 100644 index 000000000..b3fa8af20 --- /dev/null +++ b/kclvm/3rdparty/rustc_span/src/fatal_error.rs @@ -0,0 +1,22 @@ +/// Used as a return value to signify a fatal error occurred. (It is also +/// used as the argument to panic at the moment, but that will eventually +/// not be true.) +#[derive(Copy, Clone, Debug)] +#[must_use] +pub struct FatalError; + +pub struct FatalErrorMarker; + +impl FatalError { + pub fn raise(self) -> ! { + std::panic::resume_unwind(Box::new(FatalErrorMarker)) + } +} + +impl std::fmt::Display for FatalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "parser fatal error") + } +} + +impl std::error::Error for FatalError {} diff --git a/kclvm/3rdparty/rustc_span/src/lib.rs b/kclvm/3rdparty/rustc_span/src/lib.rs new file mode 100644 index 000000000..83d6f1a24 --- /dev/null +++ b/kclvm/3rdparty/rustc_span/src/lib.rs @@ -0,0 +1,1195 @@ +//! Source positions and related helper functions. +//! +//! Important concepts in this module include: +//! +//! - the *span*, represented by [`SpanData`] and related types; +//! - source code as represented by a [`SourceMap`]; and +//! - interned strings, represented by [`Symbol`]s, with some common symbols available statically in the [`sym`] module. +//! +//! Unlike most compilers, the span contains not only the position in the source code, but also various other metadata, +//! such as the edition and macro hygiene. This metadata is stored in [`SyntaxContext`] and [`ExpnData`]. +//! +//! ## Note +//! +//! This API is completely unstable and subject to change. + +mod caching_source_map_view; +mod fatal_error; +pub mod source_map; +pub use self::caching_source_map_view::CachingSourceMapView; +use rustc_data_structures::sync::Lrc; +pub use source_map::SourceMap; + +mod span_encoding; +pub use span_encoding::{Span, DUMMY_SP}; + +mod analyze_source_file; + +use std::borrow::Cow; +use std::cmp::{self, Ordering}; +use std::collections::hash_map::DefaultHasher; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::ops::{Add, Range, Sub}; +use std::path::{Path, PathBuf}; +use std::rc::Rc; +use std::str::FromStr; + +use md5::Digest; +use md5::Md5; +use sha1::Sha1; +use sha2::Sha256; + +use tracing::debug; + +// FIXME: We should use this enum or something like it to get rid of the +// use of magic `/rust/1.x/...` paths across the board. +#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd)] +pub enum RealFileName { + LocalPath(PathBuf), + /// For remapped paths (namely paths into libstd that have been mapped + /// to the appropriate spot on the local host's file system, and local file + /// system paths that have been remapped with `FilePathMapping`), + Remapped { + /// `local_path` is the (host-dependent) local path to the file. This is + /// None if the file was imported from another crate + local_path: Option, + /// `virtual_name` is the stable path rustc will store internally within + /// build artifacts. + virtual_name: PathBuf, + }, +} + +impl Hash for RealFileName { + fn hash(&self, state: &mut H) { + // To prevent #70924 from happening again we should only hash the + // remapped (virtualized) path if that exists. This is because + // virtualized paths to sysroot crates (/rust/$hash or /rust/$version) + // remain stable even if the corresponding local_path changes + self.remapped_path_if_available().hash(state) + } +} + +impl RealFileName { + /// Returns the path suitable for reading from the file system on the local host, + /// if this information exists. + /// Avoid embedding this in build artifacts; see `remapped_path_if_available()` for that. + pub fn local_path(&self) -> Option<&Path> { + match self { + RealFileName::LocalPath(p) => Some(p), + RealFileName::Remapped { + local_path: p, + virtual_name: _, + } => p.as_ref().map(PathBuf::as_path), + } + } + + /// Returns the path suitable for reading from the file system on the local host, + /// if this information exists. + /// Avoid embedding this in build artifacts; see `remapped_path_if_available()` for that. + pub fn into_local_path(self) -> Option { + match self { + RealFileName::LocalPath(p) => Some(p), + RealFileName::Remapped { + local_path: p, + virtual_name: _, + } => p, + } + } + + /// Returns the path suitable for embedding into build artifacts. This would still + /// be a local path if it has not been remapped. A remapped path will not correspond + /// to a valid file system path: see `local_path_if_available()` for something that + /// is more likely to return paths into the local host file system. + pub fn remapped_path_if_available(&self) -> &Path { + match self { + RealFileName::LocalPath(p) + | RealFileName::Remapped { + local_path: _, + virtual_name: p, + } => &p, + } + } + + /// Returns the path suitable for reading from the file system on the local host, + /// if this information exists. Otherwise returns the remapped name. + /// Avoid embedding this in build artifacts; see `remapped_path_if_available()` for that. + pub fn local_path_if_available(&self) -> &Path { + match self { + RealFileName::LocalPath(path) + | RealFileName::Remapped { + local_path: None, + virtual_name: path, + } + | RealFileName::Remapped { + local_path: Some(path), + virtual_name: _, + } => path, + } + } + + pub fn to_string_lossy(&self, display_pref: FileNameDisplayPreference) -> Cow<'_, str> { + match display_pref { + FileNameDisplayPreference::Local => self.local_path_if_available().to_string_lossy(), + FileNameDisplayPreference::Remapped => { + self.remapped_path_if_available().to_string_lossy() + } + } + } +} + +/// Differentiates between real files and common virtual files. +#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)] +pub enum FileName { + Real(RealFileName), + /// Call to `quote!`. + QuoteExpansion(u64), + /// Command line. + Anon(u64), + /// Hack in `src/librustc_ast/parse.rs`. + // FIXME(jseyfried) + MacroExpansion(u64), + ProcMacroSourceCode(u64), + /// Strings provided as `--cfg [cfgspec]` stored in a `crate_cfg`. + CfgSpec(u64), + /// Strings provided as crate attributes in the CLI. + CliCrateAttr(u64), + /// Custom sources for explicit parser calls from plugins and drivers. + Custom(String), + DocTest(PathBuf, isize), + /// Post-substitution inline assembly from LLVM. + InlineAsm(u64), +} + +impl From for FileName { + fn from(p: PathBuf) -> Self { + assert!(!p.to_string_lossy().ends_with('>')); + FileName::Real(RealFileName::LocalPath(p)) + } +} + +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] +pub enum FileNameDisplayPreference { + Remapped, + Local, +} + +pub struct FileNameDisplay<'a> { + inner: &'a FileName, + display_pref: FileNameDisplayPreference, +} + +impl fmt::Display for FileNameDisplay<'_> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use FileName::*; + match *self.inner { + Real(ref name) => { + write!(fmt, "{}", name.to_string_lossy(self.display_pref)) + } + QuoteExpansion(_) => write!(fmt, ""), + MacroExpansion(_) => write!(fmt, ""), + Anon(_) => write!(fmt, ""), + ProcMacroSourceCode(_) => write!(fmt, ""), + CfgSpec(_) => write!(fmt, ""), + CliCrateAttr(_) => write!(fmt, ""), + Custom(ref s) => write!(fmt, "<{}>", s), + DocTest(ref path, _) => write!(fmt, "{}", path.display()), + InlineAsm(_) => write!(fmt, ""), + } + } +} + +impl FileNameDisplay<'_> { + pub fn to_string_lossy(&self) -> Cow<'_, str> { + match self.inner { + FileName::Real(ref inner) => inner.to_string_lossy(self.display_pref), + _ => Cow::from(format!("{}", self)), + } + } +} + +impl FileName { + pub fn is_real(&self) -> bool { + use FileName::*; + match *self { + Real(_) => true, + Anon(_) + | MacroExpansion(_) + | ProcMacroSourceCode(_) + | CfgSpec(_) + | CliCrateAttr(_) + | Custom(_) + | QuoteExpansion(_) + | DocTest(_, _) + | InlineAsm(_) => false, + } + } + + pub fn prefer_remapped(&self) -> FileNameDisplay<'_> { + FileNameDisplay { + inner: self, + display_pref: FileNameDisplayPreference::Remapped, + } + } + + // This may include transient local filesystem information. + // Must not be embedded in build outputs. + pub fn prefer_local(&self) -> FileNameDisplay<'_> { + FileNameDisplay { + inner: self, + display_pref: FileNameDisplayPreference::Local, + } + } + + pub fn display(&self, display_pref: FileNameDisplayPreference) -> FileNameDisplay<'_> { + FileNameDisplay { + inner: self, + display_pref, + } + } +} + +/// Represents a span. +/// +/// Spans represent a region of code, used for error reporting. Positions in spans +/// are *absolute* positions from the beginning of the [`SourceMap`], not positions +/// relative to [`SourceFile`]s. Methods on the `SourceMap` can be used to relate spans back +/// to the original source. +/// +/// You must be careful if the span crosses more than one file, since you will not be +/// able to use many of the functions on spans in source_map and you cannot assume +/// that the length of the span is equal to `span.hi - span.lo`; there may be space in the +/// [`BytePos`] range between files. +/// +/// `SpanData` is public because `Span` uses a thread-local interner and can't be +/// sent to other threads, but some pieces of performance infra run in a separate thread. +/// Using `Span` is generally preferred. +#[derive(Clone, Copy, Hash, PartialEq, Eq)] +pub struct SpanData { + pub lo: BytePos, + pub hi: BytePos, +} + +// Order spans by position in the file. +impl Ord for SpanData { + fn cmp(&self, other: &Self) -> Ordering { + let SpanData { lo: s_lo, hi: s_hi } = self; + let SpanData { lo: o_lo, hi: o_hi } = other; + + (s_lo, s_hi).cmp(&(o_lo, o_hi)) + } +} + +impl PartialOrd for SpanData { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl SpanData { + #[inline] + pub fn span(&self) -> Span { + Span::new(self.lo, self.hi) + } + #[inline] + pub fn with_lo(&self, lo: BytePos) -> Span { + Span::new(lo, self.hi) + } + #[inline] + pub fn with_hi(&self, hi: BytePos) -> Span { + Span::new(self.lo, hi) + } + /// Returns `true` if this is a dummy span with any hygienic context. + #[inline] + pub fn is_dummy(self) -> bool { + self.lo.0 == 0 && self.hi.0 == 0 + } + /// Returns `true` if `self` fully encloses `other`. + pub fn contains(self, other: Self) -> bool { + self.lo <= other.lo && other.hi <= self.hi + } +} + +impl PartialOrd for Span { + fn partial_cmp(&self, rhs: &Self) -> Option { + PartialOrd::partial_cmp(&self.data(), &rhs.data()) + } +} +impl Ord for Span { + fn cmp(&self, rhs: &Self) -> Ordering { + Ord::cmp(&self.data(), &rhs.data()) + } +} + +impl Span { + #[inline] + pub fn lo(self) -> BytePos { + self.data().lo + } + #[inline] + pub fn with_lo(self, lo: BytePos) -> Span { + self.data().with_lo(lo) + } + #[inline] + pub fn hi(self) -> BytePos { + self.data().hi + } + #[inline] + pub fn with_hi(self, hi: BytePos) -> Span { + self.data().with_hi(hi) + } + + /// Returns `true` if this is a dummy span with any hygienic context. + #[inline] + pub fn is_dummy(self) -> bool { + self.data_untracked().is_dummy() + } + + /// Returns a new span representing an empty span at the beginning of this span. + #[inline] + pub fn shrink_to_lo(self) -> Span { + let span = self.data_untracked(); + span.with_hi(span.lo) + } + /// Returns a new span representing an empty span at the end of this span. + #[inline] + pub fn shrink_to_hi(self) -> Span { + let span = self.data_untracked(); + span.with_lo(span.hi) + } + + #[inline] + /// Returns `true` if `hi == lo`. + pub fn is_empty(self) -> bool { + let span = self.data_untracked(); + span.hi == span.lo + } + + /// Returns `self` if `self` is not the dummy span, and `other` otherwise. + pub fn substitute_dummy(self, other: Span) -> Span { + if self.is_dummy() { + other + } else { + self + } + } + + /// Returns `true` if `self` fully encloses `other`. + pub fn contains(self, other: Span) -> bool { + let span = self.data(); + let other = other.data(); + span.contains(other) + } + + /// Returns `true` if `self` touches `other`. + pub fn overlaps(self, other: Span) -> bool { + let span = self.data(); + let other = other.data(); + span.lo < other.hi && other.lo < span.hi + } + + /// Returns `true` if the spans are equal with regards to the source text. + /// + /// Use this instead of `==` when either span could be generated code, + /// and you only care that they point to the same bytes of source text. + pub fn source_equal(self, other: Span) -> bool { + let span = self.data(); + let other = other.data(); + span.lo == other.lo && span.hi == other.hi + } + + /// Returns `Some(span)`, where the start is trimmed by the end of `other`. + pub fn trim_start(self, other: Span) -> Option { + let span = self.data(); + let other = other.data(); + if span.hi > other.hi { + Some(span.with_lo(cmp::max(span.lo, other.hi))) + } else { + None + } + } + + /// Returns a `Span` that would enclose both `self` and `end`. + /// + /// ```text + /// ____ ___ + /// self lorem ipsum end + /// ^^^^^^^^^^^^^^^^^^^^ + /// ``` + pub fn to(self, end: Span) -> Span { + let span_data = self.data(); + let end_data = end.data(); + Span::new( + cmp::min(span_data.lo, end_data.lo), + cmp::max(span_data.hi, end_data.hi), + ) + } + + /// Returns a `Span` between the end of `self` to the beginning of `end`. + /// + /// ```text + /// ____ ___ + /// self lorem ipsum end + /// ^^^^^^^^^^^^^ + /// ``` + pub fn between(self, end: Span) -> Span { + let span = self.data(); + let end = end.data(); + Span::new(span.hi, end.lo) + } + + /// Returns a `Span` from the beginning of `self` until the beginning of `end`. + /// + /// ```text + /// ____ ___ + /// self lorem ipsum end + /// ^^^^^^^^^^^^^^^^^ + /// ``` + pub fn until(self, end: Span) -> Span { + // Most of this function's body is copied from `to`. + // We can't just do `self.to(end.shrink_to_lo())`, + // because to also does some magic where it uses min/max so + // it can handle overlapping spans. Some advanced mis-use of + // `until` with different ctxts makes this visible. + let span_data = self.data(); + let end_data = end.data(); + Span::new(span_data.lo, end_data.lo) + } + + pub fn from_inner(self, inner: InnerSpan) -> Span { + let span = self.data(); + Span::new( + span.lo + BytePos::from_usize(inner.start), + span.lo + BytePos::from_usize(inner.end), + ) + } +} + +impl Default for Span { + fn default() -> Self { + DUMMY_SP + } +} + +impl fmt::Debug for SpanData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&Span::new(self.lo, self.hi), f) + } +} + +/// Identifies an offset of a multi-byte character in a `SourceFile`. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct MultiByteChar { + /// The absolute offset of the character in the `SourceMap`. + pub pos: BytePos, + /// The number of bytes, `>= 2`. + pub bytes: u8, +} + +/// Identifies an offset of a non-narrow character in a `SourceFile`. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum NonNarrowChar { + /// Represents a zero-width character. + ZeroWidth(BytePos), + /// Represents a wide (full-width) character. + Wide(BytePos), + /// Represents a tab character, represented visually with a width of 4 characters. + Tab(BytePos), +} + +impl NonNarrowChar { + fn new(pos: BytePos, width: usize) -> Self { + match width { + 0 => NonNarrowChar::ZeroWidth(pos), + 2 => NonNarrowChar::Wide(pos), + 4 => NonNarrowChar::Tab(pos), + _ => panic!("width {} given for non-narrow character", width), + } + } + + /// Returns the absolute offset of the character in the `SourceMap`. + pub fn pos(&self) -> BytePos { + match *self { + NonNarrowChar::ZeroWidth(p) | NonNarrowChar::Wide(p) | NonNarrowChar::Tab(p) => p, + } + } + + /// Returns the width of the character, 0 (zero-width) or 2 (wide). + pub fn width(&self) -> usize { + match *self { + NonNarrowChar::ZeroWidth(_) => 0, + NonNarrowChar::Wide(_) => 2, + NonNarrowChar::Tab(_) => 4, + } + } +} + +impl Add for NonNarrowChar { + type Output = Self; + + fn add(self, rhs: BytePos) -> Self { + match self { + NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos + rhs), + NonNarrowChar::Wide(pos) => NonNarrowChar::Wide(pos + rhs), + NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos + rhs), + } + } +} + +impl Sub for NonNarrowChar { + type Output = Self; + + fn sub(self, rhs: BytePos) -> Self { + match self { + NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos - rhs), + NonNarrowChar::Wide(pos) => NonNarrowChar::Wide(pos - rhs), + NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos - rhs), + } + } +} + +/// Identifies an offset of a character that was normalized away from `SourceFile`. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct NormalizedPos { + /// The absolute offset of the character in the `SourceMap`. + pub pos: BytePos, + /// The difference between original and normalized string at position. + pub diff: u32, +} + +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum ExternalSource { + /// No external source has to be loaded, since the `SourceFile` represents a local crate. + Unneeded, + Foreign { + kind: ExternalSourceKind, + /// This SourceFile's byte-offset within the source_map of its original crate. + original_start_pos: BytePos, + /// The end of this SourceFile within the source_map of its original crate. + original_end_pos: BytePos, + }, +} + +/// The state of the lazy external source loading mechanism of a `SourceFile`. +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum ExternalSourceKind { + /// The external source has been loaded already. + Present(Rc), + /// No attempt has been made to load the external source. + AbsentOk, + /// A failed attempt has been made to load the external source. + AbsentErr, + Unneeded, +} + +impl ExternalSource { + pub fn get_source(&self) -> Option<&Rc> { + match self { + ExternalSource::Foreign { + kind: ExternalSourceKind::Present(ref src), + .. + } => Some(src), + _ => None, + } + } +} + +#[derive(Debug)] +pub struct OffsetOverflowError; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum SourceFileHashAlgorithm { + Md5, + Sha1, + Sha256, +} + +impl FromStr for SourceFileHashAlgorithm { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "md5" => Ok(SourceFileHashAlgorithm::Md5), + "sha1" => Ok(SourceFileHashAlgorithm::Sha1), + "sha256" => Ok(SourceFileHashAlgorithm::Sha256), + _ => Err(()), + } + } +} + +/// The hash of the on-disk source file used for debug info. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct SourceFileHash { + pub kind: SourceFileHashAlgorithm, + value: [u8; 32], +} + +impl SourceFileHash { + pub fn new(kind: SourceFileHashAlgorithm, src: &str) -> SourceFileHash { + let mut hash = SourceFileHash { + kind, + value: Default::default(), + }; + let len = hash.hash_len(); + let value = &mut hash.value[..len]; + let data = src.as_bytes(); + match kind { + SourceFileHashAlgorithm::Md5 => { + value.copy_from_slice(&Md5::digest(data)); + } + SourceFileHashAlgorithm::Sha1 => { + value.copy_from_slice(&Sha1::digest(data)); + } + SourceFileHashAlgorithm::Sha256 => { + value.copy_from_slice(&Sha256::digest(data)); + } + } + hash + } + + /// Check if the stored hash matches the hash of the string. + pub fn matches(&self, src: &str) -> bool { + Self::new(self.kind, src) == *self + } + + /// The bytes of the hash. + pub fn hash_bytes(&self) -> &[u8] { + let len = self.hash_len(); + &self.value[..len] + } + + fn hash_len(&self) -> usize { + match self.kind { + SourceFileHashAlgorithm::Md5 => 16, + SourceFileHashAlgorithm::Sha1 => 20, + SourceFileHashAlgorithm::Sha256 => 32, + } + } +} + +/// A single source in the [`SourceMap`]. +#[derive(Clone)] +pub struct SourceFile { + /// The name of the file that the source came from. Source that doesn't + /// originate from files has names between angle brackets by convention + /// (e.g., ``). + pub name: FileName, + /// The complete source code. + pub src: Option>, + /// The source code's hash. + pub src_hash: SourceFileHash, + /// The start position of this source in the `SourceMap`. + pub start_pos: BytePos, + /// The end position of this source in the `SourceMap`. + pub end_pos: BytePos, + /// Locations of lines beginnings in the source code. + pub lines: Vec, + /// Locations of multi-byte characters in the source code. + pub multibyte_chars: Vec, + /// Width of characters that are not narrow in the source code. + pub non_narrow_chars: Vec, + /// Locations of characters removed during normalization. + pub normalized_pos: Vec, + /// A hash of the filename, used for speeding up hashing in incremental compilation. + pub name_hash: u64, +} + +impl fmt::Debug for SourceFile { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "SourceFile({:?})", self.name) + } +} + +impl SourceFile { + pub fn new( + name: FileName, + mut src: String, + start_pos: BytePos, + hash_kind: SourceFileHashAlgorithm, + ) -> Self { + // Compute the file hash before any normalization. + let src_hash = SourceFileHash::new(hash_kind, &src); + let normalized_pos = normalize_src(&mut src, start_pos); + + let name_hash = { + let mut hasher = DefaultHasher::new(); + name.hash(&mut hasher); + hasher.finish() + }; + let end_pos = start_pos.to_usize() + src.len(); + assert!(end_pos <= u32::MAX as usize); + + let (lines, multibyte_chars, non_narrow_chars) = + analyze_source_file::analyze_source_file(&src, start_pos); + + SourceFile { + name, + src: Some(Rc::new(src)), + src_hash, + start_pos, + end_pos: Pos::from_usize(end_pos), + lines, + multibyte_chars, + non_narrow_chars, + normalized_pos, + name_hash, + } + } + + /// Returns the `BytePos` of the beginning of the current line. + pub fn line_begin_pos(&self, pos: BytePos) -> BytePos { + let line_index = self.lookup_line(pos).unwrap(); + self.lines[line_index] + } + + /// Gets a line from the list of pre-computed line-beginnings. + /// The line number here is 0-based. + pub fn get_line(&self, line_number: usize) -> Option> { + fn get_until_newline(src: &str, begin: usize) -> &str { + // We can't use `lines.get(line_number+1)` because we might + // be parsing when we call this function and thus the current + // line is the last one we have line info for. + let slice = &src[begin..]; + match slice.find('\n') { + Some(e) => &slice[..e], + None => slice, + } + } + + let begin = { + let line = self.lines.get(line_number)?; + let begin: BytePos = *line - self.start_pos; + begin.to_usize() + }; + + if let Some(ref src) = self.src { + Some(Cow::from(get_until_newline(src, begin))) + } else { + None + } + } + + pub fn is_real_file(&self) -> bool { + self.name.is_real() + } + + pub fn is_imported(&self) -> bool { + self.src.is_none() + } + + pub fn count_lines(&self) -> usize { + self.lines.len() + } + + /// Finds the line containing the given position. The return value is the + /// index into the `lines` array of this `SourceFile`, not the 1-based line + /// number. If the source_file is empty or the position is located before the + /// first line, `None` is returned. + pub fn lookup_line(&self, pos: BytePos) -> Option { + match self.lines.binary_search(&pos) { + Ok(idx) => Some(idx), + Err(0) => None, + Err(idx) => Some(idx - 1), + } + } + + pub fn line_bounds(&self, line_index: usize) -> Range { + if self.is_empty() { + return self.start_pos..self.end_pos; + } + + assert!(line_index < self.lines.len()); + if line_index == (self.lines.len() - 1) { + self.lines[line_index]..self.end_pos + } else { + self.lines[line_index]..self.lines[line_index + 1] + } + } + + /// Returns whether or not the file contains the given `SourceMap` byte + /// position. The position one past the end of the file is considered to be + /// contained by the file. This implies that files for which `is_empty` + /// returns true still contain one byte position according to this function. + #[inline] + pub fn contains(&self, byte_pos: BytePos) -> bool { + byte_pos >= self.start_pos && byte_pos <= self.end_pos + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.start_pos == self.end_pos + } + + /// Calculates the original byte position relative to the start of the file + /// based on the given byte position. + pub fn original_relative_byte_pos(&self, pos: BytePos) -> BytePos { + // Diff before any records is 0. Otherwise use the previously recorded + // diff as that applies to the following characters until a new diff + // is recorded. + let diff = match self.normalized_pos.binary_search_by(|np| np.pos.cmp(&pos)) { + Ok(i) => self.normalized_pos[i].diff, + Err(i) if i == 0 => 0, + Err(i) => self.normalized_pos[i - 1].diff, + }; + + BytePos::from_u32(pos.0 - self.start_pos.0 + diff) + } + + /// Converts an absolute `BytePos` to a `CharPos` relative to the `SourceFile`. + pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos { + // The number of extra bytes due to multibyte chars in the `SourceFile`. + let mut total_extra_bytes = 0; + + for mbc in self.multibyte_chars.iter() { + debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos); + if mbc.pos < bpos { + // Every character is at least one byte, so we only + // count the actual extra bytes. + total_extra_bytes += mbc.bytes as u32 - 1; + // We should never see a byte position in the middle of a + // character. + assert!(bpos.to_u32() >= mbc.pos.to_u32() + mbc.bytes as u32); + } else { + break; + } + } + + assert!(self.start_pos.to_u32() + total_extra_bytes <= bpos.to_u32()); + CharPos(bpos.to_usize() - self.start_pos.to_usize() - total_extra_bytes as usize) + } + + /// Looks up the file's (1-based) line number and (0-based `CharPos`) column offset, for a + /// given `BytePos`. + pub fn lookup_file_pos(&self, pos: BytePos) -> (usize, CharPos) { + let chpos = self.bytepos_to_file_charpos(pos); + match self.lookup_line(pos) { + Some(a) => { + let line = a + 1; // Line numbers start at 1 + let linebpos = self.lines[a]; + let linechpos = self.bytepos_to_file_charpos(linebpos); + let col = chpos - linechpos; + debug!( + "byte pos {:?} is on the line at byte pos {:?}", + pos, linebpos + ); + debug!( + "char pos {:?} is on the line at char pos {:?}", + chpos, linechpos + ); + debug!("byte is on line: {}", line); + assert!(chpos >= linechpos); + (line, col) + } + None => (0, chpos), + } + } + + /// Looks up the file's (1-based) line number, (0-based `CharPos`) column offset, and (0-based) + /// column offset when displayed, for a given `BytePos`. + pub fn lookup_file_pos_with_col_display(&self, pos: BytePos) -> (usize, CharPos, usize) { + let (line, col_or_chpos) = self.lookup_file_pos(pos); + if line > 0 { + let col = col_or_chpos; + let linebpos = self.lines[line - 1]; + let col_display = { + let start_width_idx = self + .non_narrow_chars + .binary_search_by_key(&linebpos, |x| x.pos()) + .unwrap_or_else(|x| x); + let end_width_idx = self + .non_narrow_chars + .binary_search_by_key(&pos, |x| x.pos()) + .unwrap_or_else(|x| x); + let special_chars = end_width_idx - start_width_idx; + let non_narrow: usize = self.non_narrow_chars[start_width_idx..end_width_idx] + .iter() + .map(|x| x.width()) + .sum(); + col.0 - special_chars + non_narrow + }; + (line, col, col_display) + } else { + let chpos = col_or_chpos; + let col_display = { + let end_width_idx = self + .non_narrow_chars + .binary_search_by_key(&pos, |x| x.pos()) + .unwrap_or_else(|x| x); + let non_narrow: usize = self.non_narrow_chars[0..end_width_idx] + .iter() + .map(|x| x.width()) + .sum(); + chpos.0 - end_width_idx + non_narrow + }; + (0, chpos, col_display) + } + } +} + +/// Normalizes the source code and records the normalizations. +fn normalize_src(src: &mut String, start_pos: BytePos) -> Vec { + let mut normalized_pos = vec![]; + remove_bom(src, &mut normalized_pos); + normalize_newlines(src, &mut normalized_pos); + + // Offset all the positions by start_pos to match the final file positions. + for np in &mut normalized_pos { + np.pos.0 += start_pos.0; + } + + normalized_pos +} + +/// Removes UTF-8 BOM, if any. +fn remove_bom(src: &mut String, normalized_pos: &mut Vec) { + if src.starts_with('\u{feff}') { + src.drain(..3); + normalized_pos.push(NormalizedPos { + pos: BytePos(0), + diff: 3, + }); + } +} + +/// Replaces `\r\n` with `\n` in-place in `src`. +/// +/// Returns error if there's a lone `\r` in the string. +fn normalize_newlines(src: &mut String, normalized_pos: &mut Vec) { + if !src.as_bytes().contains(&b'\r') { + return; + } + + // We replace `\r\n` with `\n` in-place, which doesn't break utf-8 encoding. + // While we *can* call `as_mut_vec` and do surgery on the live string + // directly, let's rather steal the contents of `src`. This makes the code + // safe even if a panic occurs. + + let mut buf = std::mem::replace(src, String::new()).into_bytes(); + let mut gap_len = 0; + let mut tail = buf.as_mut_slice(); + let mut cursor = 0; + let original_gap = normalized_pos.last().map_or(0, |l| l.diff); + loop { + let idx = match find_crlf(&tail[gap_len..]) { + None => tail.len(), + Some(idx) => idx + gap_len, + }; + tail.copy_within(gap_len..idx, 0); + tail = &mut tail[idx - gap_len..]; + if tail.len() == gap_len { + break; + } + cursor += idx - gap_len; + gap_len += 1; + normalized_pos.push(NormalizedPos { + pos: BytePos::from_usize(cursor + 1), + diff: original_gap + gap_len as u32, + }); + } + + // Account for removed `\r`. + // After `set_len`, `buf` is guaranteed to contain utf-8 again. + let new_len = buf.len() - gap_len; + unsafe { + buf.set_len(new_len); + *src = String::from_utf8_unchecked(buf); + } + + fn find_crlf(src: &[u8]) -> Option { + let mut search_idx = 0; + while let Some(idx) = find_cr(&src[search_idx..]) { + if src[search_idx..].get(idx + 1) != Some(&b'\n') { + search_idx += idx + 1; + continue; + } + return Some(search_idx + idx); + } + None + } + + fn find_cr(src: &[u8]) -> Option { + src.iter().position(|&b| b == b'\r') + } +} + +// _____________________________________________________________________________ +// Pos, BytePos, CharPos +// + +pub trait Pos { + fn from_usize(n: usize) -> Self; + fn to_usize(&self) -> usize; + fn from_u32(n: u32) -> Self; + fn to_u32(&self) -> u32; +} + +macro_rules! impl_pos { + ( + $( + $(#[$attr:meta])* + $vis:vis struct $ident:ident($inner_vis:vis $inner_ty:ty); + )* + ) => { + $( + $(#[$attr])* + $vis struct $ident($inner_vis $inner_ty); + + impl Pos for $ident { + #[inline(always)] + fn from_usize(n: usize) -> $ident { + $ident(n as $inner_ty) + } + + #[inline(always)] + fn to_usize(&self) -> usize { + self.0 as usize + } + + #[inline(always)] + fn from_u32(n: u32) -> $ident { + $ident(n as $inner_ty) + } + + #[inline(always)] + fn to_u32(&self) -> u32 { + self.0 as u32 + } + } + + impl Add for $ident { + type Output = $ident; + + #[inline(always)] + fn add(self, rhs: $ident) -> $ident { + $ident(self.0 + rhs.0) + } + } + + impl Sub for $ident { + type Output = $ident; + + #[inline(always)] + fn sub(self, rhs: $ident) -> $ident { + $ident(self.0 - rhs.0) + } + } + + impl fmt::Display for $ident { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } + } + )* + }; +} + +impl_pos! { + /// A byte offset. + /// + /// Keep this small (currently 32-bits), as AST contains a lot of them. + #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] + pub struct BytePos(pub u32); + + /// A character offset. + /// + /// Because of multibyte UTF-8 characters, a byte offset + /// is not equivalent to a character offset. The [`SourceMap`] will convert [`BytePos`] + /// values to `CharPos` values as necessary. + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] + pub struct CharPos(pub usize); +} + +impl BytePos {} + +// _____________________________________________________________________________ +// Loc, SourceFileAndLine, SourceFileAndBytePos +// + +/// A source code location used for error reporting. +#[derive(Debug, Clone)] +pub struct Loc { + /// Information about the original source. + pub file: Lrc, + /// The (1-based) line number. + pub line: usize, + /// The (0-based) column offset. + pub col: CharPos, + /// The (0-based) column offset when displayed. + pub col_display: usize, +} + +// Used to be structural records. +#[derive(Debug)] +pub struct SourceFileAndLine { + pub sf: Lrc, + /// Index of line, starting from 0. + pub line: usize, +} +#[derive(Debug)] +pub struct SourceFileAndBytePos { + pub sf: Lrc, + pub pos: BytePos, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct LineInfo { + /// Index of line, starting from 0. + pub line_index: usize, + + /// Column in line where span begins, starting from 0. + pub start_col: CharPos, + + /// Column in line where span ends, starting from 0, exclusive. + pub end_col: CharPos, +} + +pub struct FileLines { + pub file: Lrc, + pub lines: Vec, +} + +// _____________________________________________________________________________ +// SpanLinesError, SpanSnippetError, DistinctSources, MalformedSourceMapPositions +// + +pub type FileLinesResult = Result; + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum SpanLinesError { + DistinctSources(DistinctSources), +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum SpanSnippetError { + IllFormedSpan(Span), + DistinctSources(DistinctSources), + MalformedForSourcemap(MalformedSourceMapPositions), + SourceNotAvailable { filename: FileName }, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct DistinctSources { + pub begin: (FileName, BytePos), + pub end: (FileName, BytePos), +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct MalformedSourceMapPositions { + pub name: FileName, + pub source_len: usize, + pub begin_pos: BytePos, + pub end_pos: BytePos, +} + +/// Range inside of a `Span` used for diagnostics when we only have access to relative positions. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct InnerSpan { + pub start: usize, + pub end: usize, +} + +impl InnerSpan { + pub fn new(start: usize, end: usize) -> InnerSpan { + InnerSpan { start, end } + } +} diff --git a/kclvm/3rdparty/rustc_span/src/source_map.rs b/kclvm/3rdparty/rustc_span/src/source_map.rs new file mode 100644 index 000000000..e66b24557 --- /dev/null +++ b/kclvm/3rdparty/rustc_span/src/source_map.rs @@ -0,0 +1,1024 @@ +//! Types for tracking pieces of source code within a crate. +//! +//! The [`SourceMap`] tracks all the source code used within a single crate, mapping +//! from integer byte positions to the original source code location. Each bit +//! of source parsed during crate parsing (typically files, in-memory strings, +//! or various bits of macro expansion) cover a continuous range of bytes in the +//! `SourceMap` and are represented by [`SourceFile`]s. Byte positions are stored in +//! [`Span`] and used pervasively in the compiler. They are absolute positions +//! within the `SourceMap`, which upon request can be converted to line and column +//! information, source code snippets, etc. + +use crate::{CharPos, FileLines, LineInfo, Pos}; + +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::{AtomicU32, Lrc}; +use std::borrow::BorrowMut; +use std::cell::RefCell; +use std::clone::Clone; +use std::collections::hash_map::DefaultHasher; +use std::convert::TryFrom; +use std::hash::{Hash, Hasher}; +use std::path::{Path, PathBuf}; +use std::sync::atomic::Ordering; + +use std::fs; +use std::io; +use tracing::{debug, trace}; + +use super::{ + BytePos, DistinctSources, FileLinesResult, FileName, FileNameDisplay, + FileNameDisplayPreference, Loc, MalformedSourceMapPositions, MultiByteChar, NonNarrowChar, + NormalizedPos, OffsetOverflowError, RealFileName, SourceFile, SourceFileAndBytePos, + SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm, SpanLinesError, SpanSnippetError, + DUMMY_SP, +}; +use crate::span_encoding::Span; + +pub mod monotonic { + use std::ops::Deref; + + /// A `MonotonicVec` is a `Vec` which can only be grown. + /// Once inserted, an element can never be removed or swapped, + /// guaranteeing that any indices into a `MonotonicVec` are stable + // This is declared in its own module to ensure that the private + // field is inaccessible + pub struct MonotonicVec(Vec); + impl MonotonicVec { + pub fn new(val: Vec) -> MonotonicVec { + MonotonicVec(val) + } + + pub fn push(&mut self, val: T) { + self.0.push(val); + } + } + + impl Default for MonotonicVec { + fn default() -> Self { + MonotonicVec::new(vec![]) + } + } + + impl Deref for MonotonicVec { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } + } +} + +#[derive(Clone, Debug, Copy)] +pub struct Spanned { + pub node: T, + pub span: Span, +} + +pub fn respan(sp: Span, t: T) -> Spanned { + Spanned { node: t, span: sp } +} + +pub fn dummy_spanned(t: T) -> Spanned { + respan(DUMMY_SP, t) +} + +// _____________________________________________________________________________ +// SourceFile, MultiByteChar, FileName, FileLines +// + +/// An abstraction over the fs operations used by the Parser. +pub trait FileLoader { + /// Query the existence of a file. + fn file_exists(&self, path: &Path) -> bool; + + /// Read the contents of a UTF-8 file into memory. + fn read_file(&self, path: &Path) -> io::Result; +} + +/// A FileLoader that uses std::fs to load real files. +pub struct RealFileLoader; + +impl FileLoader for RealFileLoader { + fn file_exists(&self, path: &Path) -> bool { + path.exists() + } + + fn read_file(&self, path: &Path) -> io::Result { + fs::read_to_string(path) + } +} + +/// This is a [SourceFile] identifier that is used to correlate source files between +/// subsequent compilation sessions (which is something we need to do during +/// incremental compilation). +/// +/// The [StableSourceFileId] also contains the CrateNum of the crate the source +/// file was originally parsed for. This way we get two separate entries in +/// the [SourceMap] if the same file is part of both the local and an upstream +/// crate. Trying to only have one entry for both cases is problematic because +/// at the point where we discover that there's a local use of the file in +/// addition to the upstream one, we might already have made decisions based on +/// the assumption that it's an upstream file. Treating the two files as +/// different has no real downsides. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct StableSourceFileId { + // A hash of the source file's FileName. This is hash so that it's size + // is more predictable than if we included the actual FileName value. + pub file_name_hash: u64, +} + +// FIXME: we need a more globally consistent approach to the problem solved by +// StableSourceFileId, perhaps built atop source_file.name_hash. +impl StableSourceFileId { + pub fn new(source_file: &SourceFile) -> StableSourceFileId { + StableSourceFileId::new_from_name(&source_file.name) + } + + fn new_from_name(name: &FileName) -> StableSourceFileId { + let mut hasher = DefaultHasher::new(); + name.hash(&mut hasher); + StableSourceFileId { + file_name_hash: hasher.finish(), + } + } +} + +// _____________________________________________________________________________ +// SourceMap +// + +#[derive(Default)] +pub(super) struct SourceMapFiles { + source_files: monotonic::MonotonicVec>, + stable_id_to_source_file: FxHashMap>, +} + +pub struct SourceMap { + /// The address space below this value is currently used by the files in the source map. + used_address_space: AtomicU32, + + files: RefCell, + file_loader: Box, + // This is used to apply the file path remapping as specified via + // `--remap-path-prefix` to all `SourceFile`s allocated within this `SourceMap`. + path_mapping: FilePathMapping, + + /// The algorithm used for hashing the contents of each source file. + hash_kind: SourceFileHashAlgorithm, +} + +impl SourceMap { + pub fn new(path_mapping: FilePathMapping) -> SourceMap { + Self::with_file_loader_and_hash_kind( + Box::new(RealFileLoader), + path_mapping, + SourceFileHashAlgorithm::Md5, + ) + } + + pub fn with_file_loader_and_hash_kind( + file_loader: Box, + path_mapping: FilePathMapping, + hash_kind: SourceFileHashAlgorithm, + ) -> SourceMap { + SourceMap { + used_address_space: AtomicU32::new(0), + files: Default::default(), + file_loader, + path_mapping, + hash_kind, + } + } + + pub fn path_mapping(&self) -> &FilePathMapping { + &self.path_mapping + } + + pub fn file_exists(&self, path: &Path) -> bool { + self.file_loader.file_exists(path) + } + + pub fn load_file(&self, path: &Path) -> io::Result> { + let src = self.file_loader.read_file(path)?; + let filename = path.to_owned().into(); + Ok(self.new_source_file(filename, src)) + } + + /// Loads source file as a binary blob. + /// + /// Unlike `load_file`, guarantees that no normalization like BOM-removal + /// takes place. + pub fn load_binary_file(&self, path: &Path) -> io::Result> { + // Ideally, this should use `self.file_loader`, but it can't + // deal with binary files yet. + let bytes = fs::read(path)?; + + // We need to add file to the `SourceMap`, so that it is present + // in dep-info. There's also an edge case that file might be both + // loaded as a binary via `include_bytes!` and as proper `SourceFile` + // via `mod`, so we try to use real file contents and not just an + // empty string. + let text = std::str::from_utf8(&bytes).unwrap_or("").to_string(); + self.new_source_file(path.to_owned().into(), text); + Ok(bytes) + } + + // By returning a `Vec`, we ensure that consumers cannot invalidate + // any existing indices pointing into `files`. + pub fn files(&self) -> Vec> { + self.files.borrow().source_files.clone() + } + + pub fn source_file_by_stable_id( + &self, + stable_id: StableSourceFileId, + ) -> Option> { + self.files + .borrow() + .stable_id_to_source_file + .get(&stable_id) + .cloned() + } + + pub fn source_file_by_filename(&self, filename: &str) -> Option> { + self.files + .borrow() + .source_files + .iter() + .find(|&source_file| { + let file_name: FileName = PathBuf::from(filename).into(); + file_name == source_file.name + }) + .cloned() + } + + fn allocate_address_space(&self, size: usize) -> Result { + let size = u32::try_from(size).map_err(|_| OffsetOverflowError)?; + + loop { + let current = self.used_address_space.load(Ordering::Relaxed); + let next = current + .checked_add(size) + // Add one so there is some space between files. This lets us distinguish + // positions in the `SourceMap`, even in the presence of zero-length files. + .and_then(|next| next.checked_add(1)) + .ok_or(OffsetOverflowError)?; + + if self + .used_address_space + .compare_exchange(current, next, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() + { + return Ok(usize::try_from(current).unwrap()); + } + } + } + + /// Creates a new `SourceFile`. + /// If a file already exists in the `SourceMap` with the same ID, that file is returned + /// unmodified. + pub fn new_source_file(&self, filename: FileName, src: String) -> Lrc { + self.try_new_source_file(filename, src) + .unwrap_or_else(|OffsetOverflowError| { + eprintln!("fatal error: rustc does not support files larger than 4GB"); + crate::fatal_error::FatalError.raise() + }) + } + + fn try_new_source_file( + &self, + filename: FileName, + src: String, + ) -> Result, OffsetOverflowError> { + // Note that filename may not be a valid path, eg it may be `` etc, + // but this is okay because the directory determined by `path.pop()` will + // be empty, so the working directory will be used. + let (filename, _) = self.path_mapping.map_filename_prefix(&filename); + + let file_id = StableSourceFileId::new_from_name(&filename); + + let lrc_sf = match self.source_file_by_stable_id(file_id) { + Some(lrc_sf) => lrc_sf, + None => { + let start_pos = self.allocate_address_space(src.len())?; + + let source_file = Lrc::new(SourceFile::new( + filename, + src, + Pos::from_usize(start_pos), + self.hash_kind, + )); + + // Let's make sure the file_id we generated above actually matches + // the ID we generate for the SourceFile we just created. + debug_assert_eq!(StableSourceFileId::new(&source_file), file_id); + + let mut files = self.files.borrow_mut(); + + files.source_files.push(source_file.clone()); + files + .stable_id_to_source_file + .insert(file_id, source_file.clone()); + + source_file + } + }; + Ok(lrc_sf) + } + + /// Allocates a new `SourceFile` representing a source file from an external + /// crate. The source code of such an "imported `SourceFile`" is not available, + /// but we still know enough to generate accurate debuginfo location + /// information for things inlined from other crates. + pub fn new_imported_source_file( + &self, + filename: FileName, + src_hash: SourceFileHash, + name_hash: u64, + source_len: usize, + mut file_local_lines: Vec, + mut file_local_multibyte_chars: Vec, + mut file_local_non_narrow_chars: Vec, + mut file_local_normalized_pos: Vec, + _original_start_pos: BytePos, + _original_end_pos: BytePos, + ) -> Lrc { + let start_pos = self + .allocate_address_space(source_len) + .expect("not enough address space for imported source file"); + + let end_pos = Pos::from_usize(start_pos + source_len); + let start_pos = Pos::from_usize(start_pos); + + for pos in &mut file_local_lines { + *pos = *pos + start_pos; + } + + for mbc in &mut file_local_multibyte_chars { + mbc.pos = mbc.pos + start_pos; + } + + for swc in &mut file_local_non_narrow_chars { + *swc = *swc + start_pos; + } + + for nc in &mut file_local_normalized_pos { + nc.pos = nc.pos + start_pos; + } + + let source_file = Lrc::new(SourceFile { + name: filename, + src: None, + src_hash, + start_pos, + end_pos, + lines: file_local_lines, + multibyte_chars: file_local_multibyte_chars, + non_narrow_chars: file_local_non_narrow_chars, + normalized_pos: file_local_normalized_pos, + name_hash, + }); + + let mut files = self.files.borrow_mut(); + + files.borrow_mut().source_files.push(source_file.clone()); + files + .stable_id_to_source_file + .insert(StableSourceFileId::new(&source_file), source_file.clone()); + + source_file + } + + // If there is a doctest offset, applies it to the line. + pub fn doctest_offset_line(&self, file: &FileName, orig: usize) -> usize { + match file { + FileName::DocTest(_, offset) => { + if *offset < 0 { + orig - (-(*offset)) as usize + } else { + orig + *offset as usize + } + } + _ => orig, + } + } + + /// Return the SourceFile that contains the given `BytePos` + pub fn lookup_source_file(&self, pos: BytePos) -> Lrc { + let idx = self.lookup_source_file_idx(pos); + (*self.files.borrow().source_files)[idx].clone() + } + + /// Looks up source information about a `BytePos`. + pub fn lookup_char_pos(&self, pos: BytePos) -> Loc { + let sf = self.lookup_source_file(pos); + let (line, col, col_display) = sf.lookup_file_pos_with_col_display(pos); + Loc { + file: sf, + line, + col, + col_display, + } + } + + // If the corresponding `SourceFile` is empty, does not return a line number. + pub fn lookup_line(&self, pos: BytePos) -> Result> { + let f = self.lookup_source_file(pos); + + match f.lookup_line(pos) { + Some(line) => Ok(SourceFileAndLine { sf: f, line }), + None => Err(f), + } + } + + fn span_to_string(&self, sp: Span, filename_display_pref: FileNameDisplayPreference) -> String { + if self.files.borrow().source_files.is_empty() || sp.is_dummy() { + return "no-location".to_string(); + } + + let lo = self.lookup_char_pos(sp.lo()); + let hi = self.lookup_char_pos(sp.hi()); + format!( + "{}:{}:{}: {}:{}", + lo.file.name.display(filename_display_pref), + lo.line, + lo.col.to_usize() + 1, + hi.line, + hi.col.to_usize() + 1, + ) + } + + /// Format the span location suitable for embedding in build artifacts + pub fn span_to_embeddable_string(&self, sp: Span) -> String { + self.span_to_string(sp, FileNameDisplayPreference::Remapped) + } + + /// Format the span location to be printed in diagnostics. Must not be emitted + /// to build artifacts as this may leak local file paths. Use span_to_embeddable_string + /// for string suitable for embedding. + pub fn span_to_diagnostic_string(&self, sp: Span) -> String { + self.span_to_string(sp, self.path_mapping.filename_display_for_diagnostics) + } + + pub fn span_to_filename(&self, sp: Span) -> FileName { + self.lookup_char_pos(sp.lo()).file.name.clone() + } + + pub fn filename_for_diagnostics<'a>(&self, filename: &'a FileName) -> FileNameDisplay<'a> { + filename.display(self.path_mapping.filename_display_for_diagnostics) + } + + pub fn is_multiline(&self, sp: Span) -> bool { + let lo = self.lookup_source_file_idx(sp.lo()); + let hi = self.lookup_source_file_idx(sp.hi()); + if lo != hi { + return true; + } + let f = (*self.files.borrow().source_files)[lo].clone(); + f.lookup_line(sp.lo()) != f.lookup_line(sp.hi()) + } + + pub fn is_valid_span(&self, sp: Span) -> Result<(Loc, Loc), SpanLinesError> { + let lo = self.lookup_char_pos(sp.lo()); + trace!(?lo); + let hi = self.lookup_char_pos(sp.hi()); + trace!(?hi); + if lo.file.start_pos != hi.file.start_pos { + return Err(SpanLinesError::DistinctSources(DistinctSources { + begin: (lo.file.name.clone(), lo.file.start_pos), + end: (hi.file.name.clone(), hi.file.start_pos), + })); + } + Ok((lo, hi)) + } + + pub fn is_line_before_span_empty(&self, sp: Span) -> bool { + match self.span_to_prev_source(sp) { + Ok(s) => s + .rsplit_once('\n') + .unwrap_or(("", &s)) + .1 + .trim_start() + .is_empty(), + Err(_) => false, + } + } + + pub fn span_to_lines(&self, sp: Span) -> FileLinesResult { + debug!("span_to_lines(sp={:?})", sp); + let (lo, hi) = self.is_valid_span(sp)?; + assert!(hi.line >= lo.line); + + if sp.is_dummy() { + return Ok(FileLines { + file: lo.file, + lines: Vec::new(), + }); + } + + let mut lines = Vec::with_capacity(hi.line - lo.line + 1); + + // The span starts partway through the first line, + // but after that it starts from offset 0. + let mut start_col = lo.col; + + // For every line but the last, it extends from `start_col` + // and to the end of the line. Be careful because the line + // numbers in Loc are 1-based, so we subtract 1 to get 0-based + // lines. + // + // FIXME: now that we handle DUMMY_SP up above, we should consider + // asserting that the line numbers here are all indeed 1-based. + let hi_line = hi.line.saturating_sub(1); + for line_index in lo.line.saturating_sub(1)..hi_line { + let line_len = lo + .file + .get_line(line_index) + .map_or(0, |s| s.chars().count()); + lines.push(LineInfo { + line_index, + start_col, + end_col: CharPos::from_usize(line_len), + }); + start_col = CharPos::from_usize(0); + } + + // For the last line, it extends from `start_col` to `hi.col`: + lines.push(LineInfo { + line_index: hi_line, + start_col, + end_col: hi.col, + }); + + Ok(FileLines { + file: lo.file, + lines, + }) + } + + /// Extracts the source surrounding the given `Span` using the `extract_source` function. The + /// extract function takes three arguments: a string slice containing the source, an index in + /// the slice for the beginning of the span and an index in the slice for the end of the span. + fn span_to_source(&self, sp: Span, extract_source: F) -> Result + where + F: Fn(&str, usize, usize) -> Result, + { + let local_begin = self.lookup_byte_offset(sp.lo()); + let local_end = self.lookup_byte_offset(sp.hi()); + + if local_begin.sf.start_pos != local_end.sf.start_pos { + Err(SpanSnippetError::DistinctSources(DistinctSources { + begin: (local_begin.sf.name.clone(), local_begin.sf.start_pos), + end: (local_end.sf.name.clone(), local_end.sf.start_pos), + })) + } else { + let start_index = local_begin.pos.to_usize(); + let end_index = local_end.pos.to_usize(); + let source_len = (local_begin.sf.end_pos - local_begin.sf.start_pos).to_usize(); + + if start_index > end_index || end_index > source_len { + return Err(SpanSnippetError::MalformedForSourcemap( + MalformedSourceMapPositions { + name: local_begin.sf.name.clone(), + source_len, + begin_pos: local_begin.pos, + end_pos: local_end.pos, + }, + )); + } + + if let Some(ref src) = local_begin.sf.src { + extract_source(src, start_index, end_index) + } else { + Err(SpanSnippetError::SourceNotAvailable { + filename: local_begin.sf.name.clone(), + }) + } + } + } + + /// Returns whether or not this span points into a file + /// in the current crate. This may be `false` for spans + /// produced by a macro expansion, or for spans associated + /// with the definition of an item in a foreign crate + pub fn is_local_span(&self, sp: Span) -> bool { + let local_begin = self.lookup_byte_offset(sp.lo()); + let local_end = self.lookup_byte_offset(sp.hi()); + // This might be a weird span that covers multiple files + local_begin.sf.src.is_some() && local_end.sf.src.is_some() + } + + /// Returns the source snippet as `String` corresponding to the given `Span`. + pub fn span_to_snippet(&self, sp: Span) -> Result { + self.span_to_source(sp, |src, start_index, end_index| { + src.get(start_index..end_index) + .map(|s| s.to_string()) + .ok_or(SpanSnippetError::IllFormedSpan(sp)) + }) + } + + pub fn span_to_margin(&self, sp: Span) -> Option { + Some(self.indentation_before(sp)?.len()) + } + + pub fn indentation_before(&self, sp: Span) -> Option { + self.span_to_source(sp, |src, start_index, _| { + let before = &src[..start_index]; + let last_line = before.rsplit_once('\n').map_or(before, |(_, last)| last); + Ok(last_line + .split_once(|c: char| !c.is_whitespace()) + .map_or(last_line, |(indent, _)| indent) + .to_string()) + }) + .ok() + } + + /// Returns the source snippet as `String` before the given `Span`. + pub fn span_to_prev_source(&self, sp: Span) -> Result { + self.span_to_source(sp, |src, start_index, _| { + src.get(..start_index) + .map(|s| s.to_string()) + .ok_or(SpanSnippetError::IllFormedSpan(sp)) + }) + } + + /// Extends the given `Span` to just after the previous occurrence of `c`. Return the same span + /// if no character could be found or if an error occurred while retrieving the code snippet. + pub fn span_extend_to_prev_char(&self, sp: Span, c: char, accept_newlines: bool) -> Span { + if let Ok(prev_source) = self.span_to_prev_source(sp) { + let prev_source = prev_source.rsplit(c).next().unwrap_or(""); + if !prev_source.is_empty() && (accept_newlines || !prev_source.contains('\n')) { + return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32)); + } + } + + sp + } + + /// Extends the given `Span` to just after the previous occurrence of `pat` when surrounded by + /// whitespace. Returns None if the pattern could not be found or if an error occurred while + /// retrieving the code snippet. + pub fn span_extend_to_prev_str( + &self, + sp: Span, + pat: &str, + accept_newlines: bool, + include_whitespace: bool, + ) -> Option { + // assure that the pattern is delimited, to avoid the following + // fn my_fn() + // ^^^^ returned span without the check + // ---------- correct span + let prev_source = self.span_to_prev_source(sp).ok()?; + for ws in &[" ", "\t", "\n"] { + let pat = pat.to_owned() + ws; + if let Some(pat_pos) = prev_source.rfind(&pat) { + let just_after_pat_pos = pat_pos + pat.len() - 1; + let just_after_pat_plus_ws = if include_whitespace { + just_after_pat_pos + + prev_source[just_after_pat_pos..] + .find(|c: char| !c.is_whitespace()) + .unwrap_or(0) + } else { + just_after_pat_pos + }; + let len = prev_source.len() - just_after_pat_plus_ws; + let prev_source = &prev_source[just_after_pat_plus_ws..]; + if accept_newlines || !prev_source.trim_start().contains('\n') { + return Some(sp.with_lo(BytePos(sp.lo().0 - len as u32))); + } + } + } + + None + } + + /// Returns the source snippet as `String` after the given `Span`. + pub fn span_to_next_source(&self, sp: Span) -> Result { + self.span_to_source(sp, |src, _, end_index| { + src.get(end_index..) + .map(|s| s.to_string()) + .ok_or(SpanSnippetError::IllFormedSpan(sp)) + }) + } + + /// Extends the given `Span` while the next character matches the predicate + pub fn span_extend_while( + &self, + span: Span, + f: impl Fn(char) -> bool, + ) -> Result { + self.span_to_source(span, |s, _start, end| { + let n = s[end..] + .char_indices() + .find(|&(_, c)| !f(c)) + .map_or(s.len() - end, |(i, _)| i); + Ok(span.with_hi(span.hi() + BytePos(n as u32))) + }) + } + + /// Extends the given `Span` to just after the next occurrence of `c`. + pub fn span_extend_to_next_char(&self, sp: Span, c: char, accept_newlines: bool) -> Span { + if let Ok(next_source) = self.span_to_next_source(sp) { + let next_source = next_source.split(c).next().unwrap_or(""); + if !next_source.is_empty() && (accept_newlines || !next_source.contains('\n')) { + return sp.with_hi(BytePos(sp.hi().0 + next_source.len() as u32)); + } + } + + sp + } + + /// Given a `Span`, tries to get a shorter span ending before the first occurrence of `char` + /// `c`. + pub fn span_until_char(&self, sp: Span, c: char) -> Span { + match self.span_to_snippet(sp) { + Ok(snippet) => { + let snippet = snippet.split(c).next().unwrap_or("").trim_end(); + if !snippet.is_empty() && !snippet.contains('\n') { + sp.with_hi(BytePos(sp.lo().0 + snippet.len() as u32)) + } else { + sp + } + } + _ => sp, + } + } + + /// Given a `Span`, tries to get a shorter span ending just after the first occurrence of `char` + /// `c`. + pub fn span_through_char(&self, sp: Span, c: char) -> Span { + if let Ok(snippet) = self.span_to_snippet(sp) { + if let Some(offset) = snippet.find(c) { + return sp.with_hi(BytePos(sp.lo().0 + (offset + c.len_utf8()) as u32)); + } + } + sp + } + + /// Given a `Span`, gets a new `Span` covering the first token and all its trailing whitespace + /// or the original `Span`. + /// + /// If `sp` points to `"let mut x"`, then a span pointing at `"let "` will be returned. + pub fn span_until_non_whitespace(&self, sp: Span) -> Span { + let mut whitespace_found = false; + + self.span_take_while(sp, |c| { + if !whitespace_found && c.is_whitespace() { + whitespace_found = true; + } + + !whitespace_found || c.is_whitespace() + }) + } + + /// Given a `Span`, gets a new `Span` covering the first token without its trailing whitespace + /// or the original `Span` in case of error. + /// + /// If `sp` points to `"let mut x"`, then a span pointing at `"let"` will be returned. + pub fn span_until_whitespace(&self, sp: Span) -> Span { + self.span_take_while(sp, |c| !c.is_whitespace()) + } + + /// Given a `Span`, gets a shorter one until `predicate` yields `false`. + pub fn span_take_while

(&self, sp: Span, predicate: P) -> Span + where + P: for<'r> FnMut(&'r char) -> bool, + { + if let Ok(snippet) = self.span_to_snippet(sp) { + let offset = snippet + .chars() + .take_while(predicate) + .map(|c| c.len_utf8()) + .sum::(); + + sp.with_hi(BytePos(sp.lo().0 + (offset as u32))) + } else { + sp + } + } + + /// Given a `Span`, return a span ending in the closest `{`. This is useful when you have a + /// `Span` enclosing a whole item but we need to point at only the head (usually the first + /// line) of that item. + /// + /// *Only suitable for diagnostics.* + pub fn guess_head_span(&self, sp: Span) -> Span { + // FIXME: extend the AST items to have a head span, or replace callers with pointing at + // the item's ident when appropriate. + self.span_until_char(sp, '{') + } + + pub fn get_source_file(&self, filename: &FileName) -> Option> { + // Remap filename before lookup + let filename = self.path_mapping().map_filename_prefix(filename).0; + for sf in self.files.borrow().source_files.iter() { + if filename == sf.name { + return Some(sf.clone()); + } + } + None + } + + /// For a global `BytePos`, computes the local offset within the containing `SourceFile`. + pub fn lookup_byte_offset(&self, bpos: BytePos) -> SourceFileAndBytePos { + let idx = self.lookup_source_file_idx(bpos); + let sf = (*self.files.borrow().source_files)[idx].clone(); + let offset = bpos - sf.start_pos; + SourceFileAndBytePos { sf, pos: offset } + } + + // Returns the index of the `SourceFile` (in `self.files`) that contains `pos`. + // This index is guaranteed to be valid for the lifetime of this `SourceMap`, + // since `source_files` is a `MonotonicVec` + pub fn lookup_source_file_idx(&self, pos: BytePos) -> usize { + self.files + .borrow() + .source_files + .binary_search_by_key(&pos, |key| key.start_pos) + .unwrap_or_else(|p| p - 1) + } + + pub fn count_lines(&self) -> usize { + self.files().iter().fold(0, |a, f| a + f.count_lines()) + } + + pub fn generate_fn_name_span(&self, span: Span) -> Option { + let prev_span = self + .span_extend_to_prev_str(span, "fn", true, true) + .unwrap_or(span); + if let Ok(snippet) = self.span_to_snippet(prev_span) { + debug!( + "generate_fn_name_span: span={:?}, prev_span={:?}, snippet={:?}", + span, prev_span, snippet + ); + + if snippet.is_empty() { + return None; + }; + + let len = snippet + .find(|c: char| !c.is_alphanumeric() && c != '_') + .expect("no label after fn"); + Some(prev_span.with_hi(BytePos(prev_span.lo().0 + len as u32))) + } else { + None + } + } + + /// Takes the span of a type parameter in a function signature and try to generate a span for + /// the function name (with generics) and a new snippet for this span with the pointed type + /// parameter as a new local type parameter. + /// + /// For instance: + /// ```rust,ignore (pseudo-Rust) + /// // Given span + /// fn my_function(param: T) + /// // ^ Original span + /// + /// // Result + /// fn my_function(param: T) + /// // ^^^^^^^^^^^ Generated span with snippet `my_function` + /// ``` + /// + /// Attention: The method used is very fragile since it essentially duplicates the work of the + /// parser. If you need to use this function or something similar, please consider updating the + /// `SourceMap` functions and this function to something more robust. + pub fn generate_local_type_param_snippet(&self, span: Span) -> Option<(Span, String)> { + // Try to extend the span to the previous "fn" keyword to retrieve the function + // signature. + if let Some(sugg_span) = self.span_extend_to_prev_str(span, "fn", false, true) { + if let Ok(snippet) = self.span_to_snippet(sugg_span) { + // Consume the function name. + let mut offset = snippet + .find(|c: char| !c.is_alphanumeric() && c != '_') + .expect("no label after fn"); + + // Consume the generics part of the function signature. + let mut bracket_counter = 0; + let mut last_char = None; + for c in snippet[offset..].chars() { + match c { + '<' => bracket_counter += 1, + '>' => bracket_counter -= 1, + '(' => { + if bracket_counter == 0 { + break; + } + } + _ => {} + } + offset += c.len_utf8(); + last_char = Some(c); + } + + // Adjust the suggestion span to encompass the function name with its generics. + let sugg_span = sugg_span.with_hi(BytePos(sugg_span.lo().0 + offset as u32)); + + // Prepare the new suggested snippet to append the type parameter that triggered + // the error in the generics of the function signature. + let mut new_snippet = if last_char == Some('>') { + format!("{}, ", &snippet[..(offset - '>'.len_utf8())]) + } else { + format!("{}<", &snippet[..offset]) + }; + new_snippet.push_str( + &self + .span_to_snippet(span) + .unwrap_or_else(|_| "T".to_string()), + ); + new_snippet.push('>'); + + return Some((sugg_span, new_snippet)); + } + } + + None + } + + pub fn is_imported(&self, sp: Span) -> bool { + let source_file_index = self.lookup_source_file_idx(sp.lo()); + let source_file = &self.files()[source_file_index]; + source_file.is_imported() + } + + /// Tries to find the span of the semicolon of a macro call statement. + /// The input must be the *call site* span of a statement from macro expansion. + /// + /// v output + /// mac!(); + /// ^^^^^^ input + pub fn mac_call_stmt_semi_span(&self, mac_call: Span) -> Option { + let span = self.span_extend_while(mac_call, char::is_whitespace).ok()?; + let span = span + .shrink_to_hi() + .with_hi(BytePos(span.hi().0.checked_add(1)?)); + if self.span_to_snippet(span).as_deref() != Ok(";") { + return None; + } + Some(span) + } +} + +#[derive(Clone)] +pub struct FilePathMapping { + mapping: Vec<(PathBuf, PathBuf)>, + filename_display_for_diagnostics: FileNameDisplayPreference, +} + +impl FilePathMapping { + pub fn empty() -> FilePathMapping { + FilePathMapping::new(Vec::new()) + } + + pub fn new(mapping: Vec<(PathBuf, PathBuf)>) -> FilePathMapping { + let filename_display_for_diagnostics = if mapping.is_empty() { + FileNameDisplayPreference::Local + } else { + FileNameDisplayPreference::Remapped + }; + + FilePathMapping { + mapping, + filename_display_for_diagnostics, + } + } + + /// Applies any path prefix substitution as defined by the mapping. + /// The return value is the remapped path and a boolean indicating whether + /// the path was affected by the mapping. + pub fn map_prefix(&self, path: PathBuf) -> (PathBuf, bool) { + // NOTE: We are iterating over the mapping entries from last to first + // because entries specified later on the command line should + // take precedence. + for &(ref from, ref to) in self.mapping.iter().rev() { + if let Ok(rest) = path.strip_prefix(from) { + return (to.join(rest), true); + } + } + + (path, false) + } + + fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) { + match file { + filename @ FileName::Real(realfile) => { + if let RealFileName::LocalPath(local_path) = realfile { + let (mapped_path, mapped) = self.map_prefix(local_path.to_path_buf()); + let realfile = if mapped { + RealFileName::Remapped { + local_path: Some(local_path.clone()), + virtual_name: mapped_path, + } + } else { + realfile.clone() + }; + (FileName::Real(realfile), mapped) + } else { + (filename.clone(), false) + } + } + other => (other.clone(), false), + } + } +} diff --git a/kclvm/3rdparty/rustc_span/src/span_encoding.rs b/kclvm/3rdparty/rustc_span/src/span_encoding.rs new file mode 100644 index 000000000..29e33131d --- /dev/null +++ b/kclvm/3rdparty/rustc_span/src/span_encoding.rs @@ -0,0 +1,99 @@ +// Spans are encoded using 1-bit tag and 2 different encoding formats (one for each tag value). +// One format is used for keeping span data inline, +// another contains index into an out-of-line span interner. +// The encoding format for inline spans were obtained by optimizing over crates in rustc/libstd. +// See https://internals.rust-lang.org/t/rfc-compiler-refactoring-spans/1357/28 + +use crate::{BytePos, SpanData}; + +/// A compressed span. +/// +/// Whereas [`SpanData`] is 12 bytes, which is a bit too big to stick everywhere, `Span` +/// is a form that only takes up 8 bytes, with less space for the length and +/// context. The vast majority (99.9%+) of `SpanData` instances will fit within +/// those 8 bytes; any `SpanData` whose fields don't fit into a `Span` are +/// stored in a separate interner table, and the `Span` will index into that +/// table. Interning is rare enough that the cost is low, but common enough +/// that the code is exercised regularly. +/// +/// An earlier version of this code used only 4 bytes for `Span`, but that was +/// slower because only 80--90% of spans could be stored inline (even less in +/// very large crates) and so the interner was used a lot more. +/// +/// Inline (compressed) format: +/// - `span.base_or_index == span_data.lo` +/// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`) +/// - `span.ctxt == span_data.ctxt` (must be `<= MAX_CTXT`) +/// +/// Interned format: +/// - `span.base_or_index == index` (indexes into the interner table) +/// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero) +/// - `span.ctxt == 0` +/// +/// The inline form uses 0 for the tag value (rather than 1) so that we don't +/// need to mask out the tag bit when getting the length, and so that the +/// dummy span can be all zeroes. +/// +/// Notes about the choice of field sizes: +/// - `base` is 32 bits in both `Span` and `SpanData`, which means that `base` +/// values never cause interning. The number of bits needed for `base` +/// depends on the crate size. 32 bits allows up to 4 GiB of code in a crate. +/// - `len` is 15 bits in `Span` (a u16, minus 1 bit for the tag) and 32 bits +/// in `SpanData`, which means that large `len` values will cause interning. +/// The number of bits needed for `len` does not depend on the crate size. +/// The most common numbers of bits for `len` are from 0 to 7, with a peak usually +/// at 3 or 4, and then it drops off quickly from 8 onwards. 15 bits is enough +/// for 99.99%+ of cases, but larger values (sometimes 20+ bits) might occur +/// dozens of times in a typical crate. +/// - `ctxt` is 16 bits in `Span` and 32 bits in `SpanData`, which means that +/// large `ctxt` values will cause interning. The number of bits needed for +/// `ctxt` values depend partly on the crate size and partly on the form of +/// the code. No crates in `rustc-perf` need more than 15 bits for `ctxt`, +/// but larger crates might need more than 16 bits. +/// +/// In order to reliably use parented spans in incremental compilation, +/// the dependency to the parent definition's span. This is performed +/// using the callback `SPAN_TRACK` to access the query engine. +/// +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct Span { + base_or_index: u32, + len_or_tag: u16, +} + +/// Dummy span, both position and length are zero, syntax context is zero as well. +pub const DUMMY_SP: Span = Span { + base_or_index: 0, + len_or_tag: 0, +}; + +impl Span { + #[inline] + pub fn new(mut lo: BytePos, mut hi: BytePos) -> Self { + if lo > hi { + std::mem::swap(&mut lo, &mut hi); + } + + let (base, len) = (lo.0, hi.0 - lo.0); + + Span { + base_or_index: base, + len_or_tag: len as u16, + } + } + + #[inline] + pub fn data(self) -> SpanData { + self.data_untracked() + } + + /// Internal function to translate between an encoded span and the expanded representation. + /// This function must not be used outside the incremental engine. + #[inline] + pub fn data_untracked(self) -> SpanData { + SpanData { + lo: BytePos(self.base_or_index), + hi: BytePos(self.base_or_index + self.len_or_tag as u32), + } + } +} diff --git a/kclvm/Cargo.lock b/kclvm/Cargo.lock new file mode 100644 index 000000000..1f15d49a9 --- /dev/null +++ b/kclvm/Cargo.lock @@ -0,0 +1,1702 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "annotate-snippets" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5" + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + +[[package]] +name = "cc" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "enquote" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c36cb11dbde389f4096111698d8b567c0720e3452fd5ac3e6b4e47e1939932" +dependencies = [ + "thiserror", +] + +[[package]] +name = "fancy-regex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "fixedbitset" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" + +[[package]] +name = "fslock" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", + "rustc-rayon", +] + +[[package]] +name = "inkwell" +version = "0.1.0" +source = "git+https://github.com/TheDan64/inkwell?branch=master#bff378bee02bcbb5bed35f47e9ed69e6642e9188" +dependencies = [ + "either", + "inkwell_internals", + "libc", + "llvm-sys", + "once_cell", + "parking_lot", +] + +[[package]] +name = "inkwell_internals" +version = "0.5.0" +source = "git+https://github.com/TheDan64/inkwell?branch=master#bff378bee02bcbb5bed35f47e9ed69e6642e9188" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "instant" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "json_minimal" +version = "0.1.3" + +[[package]] +name = "kclvm" +version = "0.1.0" +dependencies = [ + "chrono", + "clap", + "fslock", + "glob", + "indexmap", + "kclvm-ast", + "kclvm-compiler", + "kclvm-config", + "kclvm-parser", + "kclvm-runner", + "kclvm-runtime", + "kclvm-sema", + "kclvm-tools", + "kclvm-version", + "libc", + "libloading", + "serde", + "serde_json", + "threadpool", + "walkdir", +] + +[[package]] +name = "kclvm-ast" +version = "0.1.0" +dependencies = [ + "kclvm-span", + "rustc_span", + "serde", + "serde_json", +] + +[[package]] +name = "kclvm-compiler" +version = "0.1.0" +dependencies = [ + "ahash", + "bit-set", + "bitflags", + "fancy-regex", + "indexmap", + "inkwell", + "kclvm-ast", + "kclvm-error", + "kclvm-runtime", + "kclvm-sema", + "once_cell", + "phf", + "time", + "unicode_names2", +] + +[[package]] +name = "kclvm-config" +version = "0.1.0" +dependencies = [ + "ahash", + "chrono", + "fslock", + "glob", + "indexmap", + "kclvm-version", + "pathdiff", + "ron", + "rust-crypto", + "serde", + "serde_yaml", + "toml", +] + +[[package]] +name = "kclvm-error" +version = "0.1.0" +dependencies = [ + "annotate-snippets", + "atty", + "indexmap", + "kclvm-span", + "rustc_span", + "termcolor", + "termize", + "tracing", +] + +[[package]] +name = "kclvm-lexer" +version = "0.1.0" +dependencies = [ + "kclvm-error", + "rustc_lexer", + "unic-emoji-char", +] + +[[package]] +name = "kclvm-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "kclvm-parser" +version = "0.1.0" +dependencies = [ + "bstr", + "either", + "enquote", + "kclvm-ast", + "kclvm-config", + "kclvm-error", + "kclvm-lexer", + "kclvm-sema", + "kclvm-span", + "num-bigint", + "rustc_data_structures", + "rustc_lexer", + "rustc_span", + "serde", + "serde_json", + "tracing", + "unicode_names2", +] + +[[package]] +name = "kclvm-runner" +version = "0.1.0" +dependencies = [ + "clap", + "fslock", + "glob", + "indexmap", + "kclvm-ast", + "kclvm-compiler", + "kclvm-config", + "kclvm-parser", + "kclvm-runtime", + "kclvm-sema", + "kclvm-version", + "libc", + "libloading", + "serde", + "serde_json", + "walkdir", +] + +[[package]] +name = "kclvm-runtime" +version = "0.1.0" +dependencies = [ + "ahash", + "base64", + "bstr", + "chrono", + "fancy-regex", + "indexmap", + "itertools", + "json_minimal", + "kclvm_runtime_internal_macros", + "libc", + "md5", + "num-integer", + "phf", + "regex", + "serde_json", + "serde_yaml", + "sha1", + "sha2 0.9.8", + "unic-ucd-bidi", + "unic-ucd-category", + "unicode-casing", +] + +[[package]] +name = "kclvm-sema" +version = "0.1.0" +dependencies = [ + "ahash", + "bit-set", + "bitflags", + "fancy-regex", + "indexmap", + "kclvm-ast", + "kclvm-error", + "kclvm-runtime", + "kclvm-span", + "once_cell", + "petgraph", + "phf", + "unicode_names2", +] + +[[package]] +name = "kclvm-span" +version = "0.1.0" +dependencies = [ + "kclvm-macros", + "rustc_span", + "scoped-tls", +] + +[[package]] +name = "kclvm-tools" +version = "0.1.0" +dependencies = [ + "fancy-regex", + "indexmap", + "kclvm-ast", + "kclvm-error", + "kclvm-parser", +] + +[[package]] +name = "kclvm-version" +version = "0.1.0" + +[[package]] +name = "kclvm_runtime_internal_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" + +[[package]] +name = "libloading" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "llvm-sys" +version = "120.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b716322964966a62377cf86e64f00ca7043505fdf27bd2ec7d41ae6682d1e7" +dependencies = [ + "cc", + "lazy_static", + "libc", + "regex", + "semver", +] + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest 0.10.3", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "petgraph" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" +dependencies = [ + "phf_shared", + "rand 0.8.4", +] + +[[package]] +name = "phf_macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "ppv-lite86" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871372391786ccec00d3c5d3d6608905b3d4db263639cfe075d3b60a736d115a" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.3", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.3", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ron" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b861ecaade43ac97886a512b360d01d66be9f41f3c61088b42cedf92e03d678" +dependencies = [ + "base64", + "bitflags", + "serde", +] + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +dependencies = [ + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-rayon" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" +dependencies = [ + "crossbeam-deque", + "either", + "rustc-rayon-core", +] + +[[package]] +name = "rustc-rayon-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" + +[[package]] +name = "rustc_data_structures" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ena", + "indexmap", + "jobserver", + "libc", + "memmap2", + "parking_lot", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "stable_deref_trait", + "stacker", + "tempfile", + "tracing", + "winapi", +] + +[[package]] +name = "rustc_lexer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86aae0c77166108c01305ee1a36a1e77289d7dc6ca0a3cd91ff4992de2d16a5" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "rustc_span" +version = "0.0.0" +dependencies = [ + "cfg-if 0.1.10", + "md-5", + "rustc_data_structures", + "scoped-tls", + "sha-1", + "sha2 0.10.2", + "tracing", + "unicode-width", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + +[[package]] +name = "sha2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "siphasher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termize" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-category" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8d4591f5fcfe1bd4453baaf803c40e1b1e69ff8455c47620440b46efef91c0" +dependencies = [ + "matches", + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-casing" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "623f59e6af2a98bdafeb93fa277ac8e1e40440973001ca15cf4ae1541cd16d56" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "unicode_names2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d6678d7916394abad0d4b19df4d3802e1fd84abd7d701f39b75ee71b9e8cf1" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/kclvm/Cargo.toml b/kclvm/Cargo.toml new file mode 100644 index 000000000..29890fe7a --- /dev/null +++ b/kclvm/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "kclvm" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib"] +path = "src/lib.rs" +name = "kclvm_cli" + +[[bin]] +path = "src/main.rs" +name = "kclvm_cli" + +[dependencies] +clap = "2.33.3" +serde_json = "1.0" +serde = { version = "1", features = ["derive"] } +glob = "0.3.0" +walkdir = "2" +libc = "0.2.112" +indexmap = "1.0" +fslock = "0.2.1" +libloading = "0.7.3" +chrono = "0.4.19" +threadpool = "1.0" + +kclvm-ast = {path = "./ast", version = "0.1.0"} +kclvm-runner = {path = "./runner", version = "0.1.0"} +kclvm-parser = {path = "./parser", version = "0.1.0"} +kclvm-compiler = {path = "./compiler", version = "0.1.0"} +kclvm-config = {path = "./config", version = "0.1.0"} +kclvm-runtime = {path = "./runtime", version = "0.1.0"} +kclvm-sema = {path = "./sema", version = "0.1.0"} +kclvm-tools = {path = "./tools", version = "0.1.0"} +kclvm-version = {path = "./version", version = "0.1.0"} diff --git a/kclvm/README.md b/kclvm/README.md new file mode 100644 index 000000000..4cb36dfde --- /dev/null +++ b/kclvm/README.md @@ -0,0 +1,66 @@ +# KCLVM + +A high-performance implementation of KCL written in Rust that uses LLVM as the compiler backend. + +## Building and Testing + +Firstly, see [KCLVM CONTRIBUTING](../CONTRIBUTING.md) to build KCLVM. Secondly, we need to download the [Rust](https://www.rust-lang.org/), [SWIG](http://www.swig.org/), [LLVM 12](https://releases.llvm.org/download.html), and add the LLVM installation location to `LLVM_SYS_120_PREFIX` and the `$PATH`. + +``` +export LLVM_SYS_120_PREFIX= +export PATH=/bin:$PATH +``` + +Thirdly, install wasm target dependencies. + +``` +make install-rustc-wasm +``` + +To build everything, run: + +``` +make +``` + +After building, we can add the following command line parameters to use the KCL high-performance version: + +``` +kcl --target native main.k +``` + +To test, run: + +``` +make test +``` + +## Building and Testing in Docker + +1. `make -C .. sh-in-docker` +2. `./run.sh -a build-cpython` only once +3. `./run.sh -a build-kclvm` +4. `export PATH=$PATH:/root/kclvm/_build/dist/ubuntu/kclvm/bin` +5. `which kcl` +6. `kcl hello.k` +7. `kcl hello.k --target native` +8. `cd kclvm && make test-grammar` + +## IDE + +You can choose any IDE you like for development, but we recommend a combination of [VS Code](https://code.visualstudio.com/) and the [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer) plugin. + +## Notes + +1. If you encounter problems compiling KCLVM and using LLVM 12 on Apple M1, you can refer to the [documentation](./docs/m1-mac-setup.md) +2. If you wanna start over, you `MUST` clean up all cached building files, such as `LLVM build files`, `kclvm/target`, etc. +3. If your updating-cargo-index is extremely slow, setup `~/.cargo/config` file. + +``` +[source.crates-io] +registry = "https://github.com/rust-lang/crates.io-index" +replace-with = 'ustc' + +[source.ustc] +registry = "git://mirrors.ustc.edu.cn/crates.io-index" +``` diff --git a/kclvm/ast/Cargo.lock b/kclvm/ast/Cargo.lock new file mode 100644 index 000000000..d7fa326c8 --- /dev/null +++ b/kclvm/ast/Cargo.lock @@ -0,0 +1,687 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", + "rustc-rayon", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "kclvm-ast" +version = "0.1.0" +dependencies = [ + "kclvm-span", + "rustc_span", + "serde", + "serde_json", +] + +[[package]] +name = "kclvm-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "kclvm-span" +version = "0.1.0" +dependencies = [ + "kclvm-macros", + "rustc_span", + "scoped-tls", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest", +] + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "proc-macro2" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871372391786ccec00d3c5d3d6608905b3d4db263639cfe075d3b60a736d115a" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-rayon" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" +dependencies = [ + "crossbeam-deque", + "either", + "rustc-rayon-core", +] + +[[package]] +name = "rustc-rayon-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc_data_structures" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ena", + "indexmap", + "jobserver", + "libc", + "memmap2", + "parking_lot", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "stable_deref_trait", + "stacker", + "tempfile", + "tracing", + "winapi", +] + +[[package]] +name = "rustc_span" +version = "0.0.0" +dependencies = [ + "cfg-if 0.1.10", + "md-5", + "rustc_data_structures", + "scoped-tls", + "sha-1", + "sha2", + "tracing", + "unicode-width", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "tracing" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" diff --git a/kclvm/ast/Cargo.toml b/kclvm/ast/Cargo.toml new file mode 100644 index 000000000..e6cc20a37 --- /dev/null +++ b/kclvm/ast/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "kclvm-ast" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rustc_span = { path = "../3rdparty/rustc_span" } +serde = { version = "1", features = ["derive"] } +serde_json = "1.0" + +kclvm-span = {path = "../span", version = "0.1.0"} diff --git a/kclvm/ast/src/ast.rs b/kclvm/ast/src/ast.rs new file mode 100644 index 000000000..5e14a40cc --- /dev/null +++ b/kclvm/ast/src/ast.rs @@ -0,0 +1,1496 @@ +//! """The `ast` file contains the definitions of all KCL AST nodes +//! and operators and all AST nodes are derived from the `AST` class. +//! The main structure of a KCL program is as follows: +//! +//! ┌─────────────────────────────────────────────────────────────────┐ +//! │ Program │ +//! │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ +//! │ │ Main Package │ │ Package1 │ │ Package2 │ │ +//! │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ +//! │ │ │ Module1 │ │ │ │ Module1 │ │ │ │ Module1 │ │ │ +//! │ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │ +//! │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ +//! │ │ │ Module2 │ │ │ │ Module2 │ │ │ │ Module2 │ │ │ +//! │ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │ +//! │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ +//! │ │ │ ... │ │ │ │ ... │ │ │ │ ... │ │ │ +//! │ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ │ +//! │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ +//! └─────────────────────────────────────────────────────────────────┘ + +//! A single KCL file represents a module, which records file information, +//! package path information, and module document information, which is +//! mainly composed of all the statements in the KCL file. + +//! The combination of multiple KCL files is regarded as a complete KCL +//! Program. For example, a single KCL file can be imported into KCL +//! files in other packages through statements such as import. Therefore, +//! the Program is composed of multiple modules, and each module is +//! associated with it. Corresponding to the package path. + +//! :note: When the definition of any AST node is modified or the AST node +//! is added/deleted, it is necessary to modify the corresponding processing +//! in the compiler and regenerate the walker code. +//! :copyright: Copyright 2020 The KCL Authors. All rights reserved. +//! +//! todo: remove type_str fields after python frontend removed. + +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use kclvm_span::Loc; +use rustc_span::Pos; + +use super::token; + +/// Node is the file, line and column number information +/// that all AST nodes need to contain. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Node { + pub node: T, + pub filename: String, + pub line: u64, + pub column: u64, + pub end_line: u64, + pub end_column: u64, +} + +impl Node { + pub fn new( + node: T, + filename: String, + line: u64, + column: u64, + end_line: u64, + end_column: u64, + ) -> Self { + Self { + node, + filename, + line, + column, + end_line, + end_column, + } + } + + pub fn dummy_node(node: T) -> Self { + Self { + node, + filename: "".to_string(), + line: 1, + column: 1, + end_line: 1, + end_column: 1, + } + } + + pub fn node(node: T, (lo, hi): (Loc, Loc)) -> Self { + Self { + node, + filename: format!("{}", lo.file.name.prefer_remapped()), + line: lo.line as u64, + column: lo.col.to_usize() as u64, + end_line: hi.line as u64, + end_column: hi.col.to_usize() as u64, + } + } + + pub fn node_with_pos(node: T, pos: (String, u64, u64, u64, u64)) -> Self { + Self { + node, + filename: pos.0.clone(), + line: pos.1, + column: pos.2, + end_line: pos.3, + end_column: pos.4, + } + } + + pub fn pos(&self) -> (String, u64, u64, u64, u64) { + ( + self.filename.clone(), + self.line, + self.column, + self.end_line, + self.end_column, + ) + } + + pub fn set_pos(&mut self, pos: (String, u64, u64, u64, u64)) { + self.filename = pos.0.clone(); + self.line = pos.1; + self.column = pos.2; + self.end_line = pos.3; + self.end_column = pos.4; + } +} + +impl TryInto> for Node { + type Error = &'static str; + + fn try_into(self) -> Result, Self::Error> { + match self.node { + Expr::Identifier(ident) => Ok(Node { + node: ident, + filename: self.filename, + line: self.line, + column: self.column, + end_line: self.end_line, + end_column: self.end_column, + }), + _ => Err("invalid identifier"), + } + } +} + +impl TryInto> for Node { + type Error = &'static str; + + fn try_into(self) -> Result, Self::Error> { + match self.node { + Expr::Schema(schema_expr) => Ok(Node { + node: schema_expr, + filename: self.filename, + line: self.line, + column: self.column, + end_line: self.end_line, + end_column: self.end_column, + }), + _ => Err("invalid schema expr"), + } + } +} + +/// NodeRef is the Box reference of Node with the +/// AST node type T +pub type NodeRef = Box>; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum ParseMode { + Null, + ParseComments, +} + +/// KCL command line argument spec, e.g. `kcl main.k -D name=value` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct CmdArgSpec { + pub name: String, + pub value: String, +} + +/// KCL command line override spec, e.g. `kcl main.k -O pkgpath:path.to.field=field_value` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct CmdOverrideSpec { + pub pkgpath: String, + pub field_path: String, + pub field_value: String, + pub action: OverrideAction, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum OverrideAction { + CreateOrUpdate, + Delete, +} + +/// Program is the AST collection of all files of the running KCL program. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Program { + pub root: String, + pub main: String, + pub pkgs: HashMap>, + pub cmd_args: Vec, + pub cmd_overrides: Vec, +} + +impl Program { + /// Get main entry files. + pub fn get_main_files(&self) -> Vec { + match self.pkgs.get(crate::MAIN_PKG) { + Some(modules) => modules.iter().map(|m| m.filename.clone()).collect(), + None => vec![], + } + } +} + +/// Module is an abstract syntax tree for a single KCL file. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Module { + pub filename: String, + pub pkg: String, + pub doc: String, + pub name: String, + pub body: Vec>, + pub comments: Vec>, +} + +/* + * Stmt + */ + +/// A statement +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum Stmt { + TypeAlias(TypeAliasStmt), + Expr(ExprStmt), + Unification(UnificationStmt), + Assign(AssignStmt), + AugAssign(AugAssignStmt), + Assert(AssertStmt), + If(IfStmt), + Import(ImportStmt), + SchemaAttr(SchemaAttr), + Schema(SchemaStmt), + Rule(RuleStmt), +} + +/// TypeAliasStmt represents a type alias statement, e.g. +/// ```kcl +/// type StrOrInt = str | int +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct TypeAliasStmt { + pub type_name: NodeRef, + pub type_value: NodeRef, + + #[serde(skip_serializing)] + pub ty: Option>, +} + +/// ExprStmt represents a expression statement, e.g. +/// ```kcl +/// 1 +/// """A long string""" +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ExprStmt { + pub exprs: Vec>, +} + +/// UnificationStmt represents a declare statement with the union operator, e.g. +/// ```kcl +/// data: ASchema {} +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct UnificationStmt { + pub target: NodeRef, + pub value: NodeRef, +} + +/// AssignStmt represents an assignment, e.g. +/// ```kcl +/// a: int = 1 +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AssignStmt { + pub targets: Vec>, + pub value: NodeRef, + pub type_annotation: Option>, + + #[serde(skip_serializing)] + pub ty: Option>, +} + +/// AugAssignStmt represents an argument assignment, e.g. +/// ```kcl +/// a += 1 +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AugAssignStmt { + pub target: NodeRef, + pub value: NodeRef, + pub op: AugOp, +} + +/// AssertStmt represents an assert statement, e.g. +/// ```kcl +/// assert True if condition, "Assert failed message" +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AssertStmt { + pub test: NodeRef, + pub if_cond: Option>, + pub msg: Option>, +} + +/// IfStmt, e.g. +/// ```kcl +/// if condition1: +/// if condition2: +/// a = 1 +/// elif condition3: +/// b = 2 +/// else: +/// c = 3 +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct IfStmt { + pub body: Vec>, + pub cond: NodeRef, + pub orelse: Vec>, +} + +/// ImportStmt, e.g. +/// ```kcl +/// import pkg as pkg_alias +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ImportStmt { + pub path: String, + pub rawpath: String, + pub name: String, + pub asname: Option, +} + +/// SchemaStmt, e.g. +/// ```kcl +/// schema BaseSchema: +/// +/// schema SchemaExample(BaseSchema)[arg: str]: +/// """Schema documents""" +/// attr?: str = arg +/// check: +/// len(attr) > 3 if attr, "Check failed message" +/// +/// mixin MixinExample for ProtocolExample: +/// attr: int +/// +/// protocol ProtocolExample: +/// attr: int +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SchemaStmt { + pub doc: String, + pub name: NodeRef, + pub parent_name: Option>, + pub for_host_name: Option>, + pub is_mixin: bool, + pub is_protocol: bool, + pub args: Option>, + pub mixins: Vec>, + pub body: Vec>, + pub decorators: Vec>, + pub checks: Vec>, + pub index_signature: Option>, +} + +impl SchemaStmt { + /// Get schema full attribute list (line, column, name) including + /// un-exported attributes. + pub fn get_left_identifier_list(&self) -> Vec<(u64, u64, String)> { + let mut attr_list: Vec<(u64, u64, String)> = vec![]; + fn loop_body(body: &[NodeRef], attr_list: &mut Vec<(u64, u64, String)>) { + for stmt in body { + match &stmt.node { + Stmt::Unification(unification_stmt) => { + attr_list.push(( + unification_stmt.target.line, + unification_stmt.target.column, + unification_stmt.target.node.names[0].to_string(), + )); + } + Stmt::Assign(assign_stmt) => { + for target in &assign_stmt.targets { + attr_list.push(( + target.line, + target.column, + target.node.names[0].to_string(), + )); + } + } + Stmt::AugAssign(aug_assign_stmt) => { + attr_list.push(( + aug_assign_stmt.target.line, + aug_assign_stmt.target.column, + aug_assign_stmt.target.node.names[0].to_string(), + )); + } + Stmt::If(if_stmt) => { + loop_body(&if_stmt.body, attr_list); + loop_body(&if_stmt.orelse, attr_list); + } + Stmt::SchemaAttr(schema_attr) => { + attr_list.push(( + schema_attr.name.line, + schema_attr.name.column, + schema_attr.name.node.to_string(), + )); + } + _ => {} + } + } + } + loop_body(&self.body, &mut attr_list); + attr_list + } + + /// Whether the schema contains only attribute definitions. + pub fn has_only_attribute_definitions(&self) -> bool { + self.args.is_none() + && self.mixins.is_empty() + && self.checks.is_empty() + && self + .body + .iter() + .all(|stmt| matches!(stmt.node, Stmt::SchemaAttr(_))) + } +} + +/// SchemaIndexSignature, e.g. +/// ```kcl +/// schema SchemaIndexSignatureExample: +/// [str]: int +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SchemaIndexSignature { + pub key_name: Option, + pub key_type: NodeRef, + pub value_type: NodeRef, + pub value: Option>, + pub any_other: bool, + + #[serde(skip_serializing)] + pub value_ty: Option>, +} + +/// SchemaAttr, e.g. +/// ```kcl +/// schema SchemaAttrExample: +/// x: int +/// y: str +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SchemaAttr { + pub doc: String, + pub name: NodeRef, + pub type_str: NodeRef, + pub op: Option, + pub value: Option>, + pub is_optional: bool, + pub decorators: Vec>, + + #[serde(skip_serializing)] + pub ty: Option>, +} + +/// RuleStmt, e.g. +/// ```kcl +/// rule RuleExample: +/// a > 1 +/// b < 0 +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RuleStmt { + pub doc: String, + pub name: NodeRef, + pub parent_rules: Vec>, + pub decorators: Vec>, + pub checks: Vec>, + pub args: Option>, + pub for_host_name: Option>, +} + +/// A expression +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum Expr { + Identifier(Identifier), + Unary(UnaryExpr), + Binary(BinaryExpr), + If(IfExpr), + Selector(SelectorExpr), + Call(CallExpr), + Paren(ParenExpr), + Quant(QuantExpr), + List(ListExpr), + ListIfItem(ListIfItemExpr), + ListComp(ListComp), + Starred(StarredExpr), + DictComp(DictComp), + ConfigIfEntry(ConfigIfEntryExpr), + CompClause(CompClause), + Schema(SchemaExpr), + Config(ConfigExpr), + Check(CheckExpr), + Lambda(LambdaExpr), + Subscript(Subscript), + Keyword(Keyword), + Arguments(Arguments), + Compare(Compare), + NumberLit(NumberLit), + StringLit(StringLit), + NameConstantLit(NameConstantLit), + JoinedString(JoinedString), + FormattedValue(FormattedValue), +} + +/// Identifier, e.g. +/// ```kcl +/// a +/// b +/// _c +/// pkg.a +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Identifier { + pub names: Vec, + pub pkgpath: String, + pub ctx: ExprContext, +} + +impl Identifier { + pub fn get_name(&self) -> String { + self.names.join(".") + } +} + +/// UnaryExpr, e.g. +/// ```kcl +/// +1 +/// -2 +/// ~3 +/// not True +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct UnaryExpr { + pub op: UnaryOp, + pub operand: NodeRef, +} + +/// BinaryExpr, e.g. +/// ```kcl +/// 1 + 1 +/// 3 - 2 +/// 5 / 2 +/// a is None +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct BinaryExpr { + pub left: NodeRef, + pub op: BinOrCmpOp, + pub right: NodeRef, +} + +/// IfExpr, e.g. +/// ```kcl +/// 1 if condition else 2 +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct IfExpr { + pub body: NodeRef, + pub cond: NodeRef, + pub orelse: NodeRef, +} + +/// SelectorExpr, e.g. +/// ```kcl +/// x.y +/// x?.y +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SelectorExpr { + pub value: NodeRef, + pub attr: NodeRef, + pub ctx: ExprContext, + pub has_question: bool, +} + +/// CallExpr, e.g. +/// ```kcl +/// func1() +/// func2(1) +/// func3(x=2) +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct CallExpr { + pub func: NodeRef, + pub args: Vec>, + pub keywords: Vec>, +} + +/// ParenExpr, e.g. +/// ```kcl +/// 1 + (2 - 3) +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ParenExpr { + pub expr: NodeRef, +} + +/// QuantExpr, e.g. +/// ```kcl +/// all x in collection {x > 0} +/// any y in collection {y < 0} +/// map x in collection {x + 1} +/// filter x in collection {x > 1} +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct QuantExpr { + pub target: NodeRef, + pub variables: Vec>, + pub op: QuantOperation, + pub test: NodeRef, + pub if_cond: Option>, + pub ctx: ExprContext, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum QuantOperation { + All, + Any, + Filter, + Map, +} + +impl Into for QuantOperation { + fn into(self) -> String { + let s = match self { + QuantOperation::All => "all", + QuantOperation::Any => "any", + QuantOperation::Filter => "filter", + QuantOperation::Map => "map", + }; + + s.to_string() + } +} + +/// ListExpr, e.g. +/// ```kcl +/// [1, 2, 3] +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ListExpr { + pub elts: Vec>, + pub ctx: ExprContext, +} + +/// ListIfItemExpr, e.g. +/// ```kcl +/// [1, if condition: 2, 3] +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ListIfItemExpr { + pub if_cond: NodeRef, + pub exprs: Vec>, + pub orelse: Option>, +} + +pub enum CompType { + List, + Dict, +} + +/// ListComp, e.g. +/// ```kcl +/// [x ** 2 for x in [1, 2, 3]] +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ListComp { + pub elt: NodeRef, + pub generators: Vec>, +} + +/// StarredExpr, e.g. +/// ```kcl +/// [1, 2, *[3, 4]] +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct StarredExpr { + pub value: NodeRef, + pub ctx: ExprContext, +} + +/// DictComp, e.g. +/// ```kcl +/// {k: v + 1 for k, v in {k1 = 1, k2 = 2}} +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct DictComp { + pub entry: ConfigEntry, + pub generators: Vec>, +} + +/// ConfigIfEntryExpr, e.g. +/// ```kcl +/// { +/// k1 = 1 +/// if condition: +/// k2 = 2 +/// } +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ConfigIfEntryExpr { + pub if_cond: NodeRef, + pub items: Vec>, + pub orelse: Option>, +} + +/// CompClause, e.g. +/// ```kcl +/// i, a in [1, 2, 3] if i > 1 and a > 1 +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct CompClause { + pub targets: Vec>, + pub iter: NodeRef, + pub ifs: Vec>, +} + +/// SchemaExpr, e.g. +/// ```kcl +/// ASchema(arguments) { +/// attr1 = 1 +/// attr2 = 2 +/// } +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SchemaExpr { + pub name: NodeRef, + pub args: Vec>, + pub kwargs: Vec>, + pub config: NodeRef, +} + +/// ConfigExpr, e.g. +/// ```kcl +/// { +/// attr1 = 1 +/// attr2 = 2 +/// } +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ConfigExpr { + pub items: Vec>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum ConfigEntryOperation { + Union, + Override, + Insert, +} + +impl ConfigEntryOperation { + pub fn value(&self) -> i32 { + match self { + ConfigEntryOperation::Union => 0, + ConfigEntryOperation::Override => 1, + ConfigEntryOperation::Insert => 2, + } + } + + pub fn symbol(&self) -> &'static str { + match self { + ConfigEntryOperation::Union => ":", + ConfigEntryOperation::Override => "=", + ConfigEntryOperation::Insert => "+=", + } + } +} + +/// ConfigEntry, e.g. +/// ```kcl +/// { +/// a = 1 +/// b: 1 +/// c += [0] +/// } +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ConfigEntry { + pub key: Option>, + pub value: NodeRef, + pub operation: ConfigEntryOperation, + pub insert_index: isize, +} + +/// CheckExpr, e.g. +/// ```kcl +/// len(attr) > 3 if attr, "Check failed message" +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct CheckExpr { + pub test: NodeRef, + pub if_cond: Option>, + pub msg: Option>, +} + +/// LambdaExpr, e.g. +/// ```kcl +/// lambda x, y { +/// z = 2 * x +/// z + y +/// } +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct LambdaExpr { + pub args: Option>, + pub return_type_str: Option, + pub body: Vec>, + + #[serde(skip_serializing)] + pub return_ty: Option>, +} + +/// Subscript, e.g. +/// ```kcl +/// a[0] +/// b["k"] +/// c?[1] +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Subscript { + pub value: NodeRef, + pub index: Option>, + pub lower: Option>, + pub upper: Option>, + pub step: Option>, + pub ctx: ExprContext, + pub has_question: bool, +} + +/// Keyword, e.g. +/// ```kcl +/// arg=value +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Keyword { + pub arg: NodeRef, + pub value: Option>, +} + +/// Arguments, e.g. +/// ```kcl +/// lambda x: int = 1, y: int = 1 { +/// x + y +/// } +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Arguments { + pub args: Vec>, + pub defaults: Vec>>, + pub type_annotation_list: Vec>>, + + #[serde(skip_serializing)] + #[serde(default)] + pub ty_list: Vec>>, +} + +impl Arguments { + pub fn get_arg_type(&self, i: usize) -> Type { + self.ty_list[i] + .as_ref() + .map_or(Type::Any, |ty| ty.node.clone()) + } +} + +/// Compare, e.g. +/// ```kcl +/// 0 < a < 10 +/// b is not None +/// c != d +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Compare { + pub left: NodeRef, + pub ops: Vec, + pub comparators: Vec>, +} + +/// Literal, e.g. +/// ```kcl +/// 1 +/// 2.0 +/// "string literal" +/// """long string literal""" +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum Literal { + Number(NumberLit), + String(StringLit), + NameConstant(NameConstantLit), +} + +#[allow(non_camel_case_types)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum NumberBinarySuffix { + n, + u, + m, + k, + K, + M, + G, + T, + P, + Ki, + Mi, + Gi, + Ti, + Pi, +} + +impl TryFrom<&str> for NumberBinarySuffix { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + match value { + "n" => Ok(NumberBinarySuffix::n), + "u" => Ok(NumberBinarySuffix::u), + "m" => Ok(NumberBinarySuffix::m), + "k" => Ok(NumberBinarySuffix::k), + "K" => Ok(NumberBinarySuffix::K), + "M" => Ok(NumberBinarySuffix::M), + "G" => Ok(NumberBinarySuffix::G), + "T" => Ok(NumberBinarySuffix::T), + "P" => Ok(NumberBinarySuffix::P), + "Ki" => Ok(NumberBinarySuffix::Ki), + "Mi" => Ok(NumberBinarySuffix::Mi), + "Gi" => Ok(NumberBinarySuffix::Gi), + "Ti" => Ok(NumberBinarySuffix::Ti), + "Pi" => Ok(NumberBinarySuffix::Pi), + _ => Err("invalid number binary suffix"), + } + } +} + +impl NumberBinarySuffix { + pub fn value(&self) -> String { + format!("{:?}", self) + } + /// Get all names of NumberBinarySuffix + #[inline] + pub const fn all_names() -> &'static [&'static str] { + &[ + "n", "u", "m", "k", "K", "M", "G", "T", "P", "Ki", "Mi", "Gi", "Ti", "Pi", "i", + ] + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum NumberLitValue { + Int(i64), + Float(f64), +} + +/// NumberLit, e.g. +/// ```kcl +/// 1m +/// 1K +/// 1Mi +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct NumberLit { + pub binary_suffix: Option, + pub value: NumberLitValue, +} + +/// StringLit, e.g. +/// ```kcl +/// "string literal" +/// """long string literal""" +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct StringLit { + pub is_long_string: bool, + pub raw_value: String, + pub value: String, +} + +/// NameConstant, e.g. +/// ```kcl +/// True +/// False +/// None +/// Undefined +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum NameConstant { + True, + False, + None, + Undefined, +} + +impl NameConstant { + pub fn symbol(&self) -> &'static str { + match self { + NameConstant::True => "True", + NameConstant::False => "False", + NameConstant::None => "None", + NameConstant::Undefined => "Undefined", + } + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct NameConstantLit { + pub value: NameConstant, +} + +/// JoinedString, e.g. abc in the string interpolation "${var1} abc ${var2}" +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct JoinedString { + pub is_long_string: bool, + pub values: Vec>, + pub raw_value: String, +} + +/// FormattedValue, e.g. var1 and var2 in the string interpolation "${var1} abc ${var2}" +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct FormattedValue { + pub is_long_string: bool, + pub value: NodeRef, + pub format_spec: Option, +} + +/// Comment, e.g. +/// ```kcl +/// # This is a comment +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Comment { + pub text: String, +} + +/* + * Operators and context + */ + +/// BinOp is the set of all binary operators in KCL. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum BinOp { + /// The `+` operator (addition) + Add, + /// The `-` operator (subtraction) + Sub, + /// The `*` operator (multiplication) + Mul, + /// The `/` operator (division) + Div, + /// The `%` operator (modulus) + Mod, + /// The `**` operator (power) + Pow, + /// The `//` operator (floor division) + FloorDiv, + /// The `<<` operator (shift left) + LShift, + /// The `>>` operator (shift right) + RShift, + /// The `^` operator (bitwise xor) + BitXor, + /// The `&` operator (bitwise and) + BitAnd, + /// The `|` operator (bitwise or) + BitOr, + /// The `and` operator (logical and) + And, + /// The `or` operator (logical or) + Or, + /// The `as` operator (type cast) + As, +} + +impl BinOp { + pub fn symbol(&self) -> &'static str { + match self { + BinOp::Add => "+", + BinOp::Sub => "-", + BinOp::Mul => "*", + BinOp::Div => "/", + BinOp::Mod => "%", + BinOp::Pow => "**", + BinOp::FloorDiv => "//", + BinOp::LShift => "<<", + BinOp::RShift => ">>", + BinOp::BitXor => "^", + BinOp::BitAnd => "&", + BinOp::BitOr => "|", + BinOp::And => "and", + BinOp::Or => "or", + BinOp::As => "as", + } + } +} + +/// BinOp is the set of all argument operators in KCL. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum AugOp { + // The `=` operator (assign) + Assign, + /// The `+=` operator (addition) + Add, + /// The `-=` operator (subtraction) + Sub, + /// The `*=` operator (multiplication) + Mul, + /// The `/=` operator (division) + Div, + /// The `%=` operator (modulus) + Mod, + /// The `**=` operator (power) + Pow, + /// The `//=` operator (floor division) + FloorDiv, + /// The `<<=` operator (shift left) + LShift, + /// The `>>=` operator (shift right) + RShift, + /// The `^=` operator (bitwise xor) + BitXor, + /// The `&=` operator (bitwise and) + BitAnd, + /// The `|=` operator (bitwise or) + BitOr, +} + +impl AugOp { + pub fn symbol(&self) -> &'static str { + match self { + AugOp::Assign => "=", + AugOp::Add => "+=", + AugOp::Sub => "-=", + AugOp::Mul => "*=", + AugOp::Div => "/=", + AugOp::Mod => "%=", + AugOp::Pow => "**=", + AugOp::FloorDiv => "//=", + AugOp::LShift => "<<=", + AugOp::RShift => ">>=", + AugOp::BitXor => "^=", + AugOp::BitAnd => "&=", + AugOp::BitOr => "|=", + } + } +} + +impl TryInto for AugOp { + type Error = &'static str; + + fn try_into(self) -> Result { + match self { + AugOp::Add => Ok(BinOp::Add), + AugOp::Sub => Ok(BinOp::Sub), + AugOp::Mul => Ok(BinOp::Mul), + AugOp::Div => Ok(BinOp::Div), + AugOp::Mod => Ok(BinOp::Mod), + AugOp::Pow => Ok(BinOp::Pow), + AugOp::FloorDiv => Ok(BinOp::FloorDiv), + AugOp::LShift => Ok(BinOp::LShift), + AugOp::RShift => Ok(BinOp::RShift), + AugOp::BitXor => Ok(BinOp::BitXor), + AugOp::BitAnd => Ok(BinOp::And), + AugOp::BitOr => Ok(BinOp::BitOr), + _ => Err("aug assign op can not into bin op"), + } + } +} + +/// UnaryOp is the set of all unary operators in KCL. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum UnaryOp { + /// The `+` operator for positive + UAdd, + /// The `-` operator for negation + USub, + /// The `~` operator for bitwise negation + Invert, + /// The `not` operator for logical inversion + Not, +} + +impl UnaryOp { + pub fn symbol(&self) -> &'static str { + match self { + UnaryOp::UAdd => "+", + UnaryOp::USub => "-", + UnaryOp::Invert => "~", + UnaryOp::Not => "not", + } + } +} + +/// CmpOp is the set of all comparison operators in KCL. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum CmpOp { + /// The `==` operator (equality) + Eq, + /// The `!=` operator (not equal to) + NotEq, + /// The `<` operator (less than) + Lt, + /// The `<=` operator (less than or equal to) + LtE, + /// The `>` operator (greater than) + Gt, + /// The `>=` operator (greater than or equal to) + GtE, + /// The `is` operator (greater than or equal to) + Is, + /// The `in` operator + In, + /// The `not in` operator + NotIn, + /// The `not` operator + Not, + /// The `is not` operator + IsNot, +} + +impl CmpOp { + pub fn symbol(&self) -> &'static str { + match self { + CmpOp::Eq => "==", + CmpOp::NotEq => "!=", + CmpOp::Lt => "<", + CmpOp::LtE => "<=", + CmpOp::Gt => ">", + CmpOp::GtE => ">=", + CmpOp::Is => "is", + CmpOp::In => "in", + CmpOp::NotIn => "not in", + CmpOp::Not => "not", + CmpOp::IsNot => "is not", + } + } +} + +/// BinOrCmpOp is the set of all binary and comparison operators in KCL. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum BinOrCmpOp { + Bin(BinOp), + Cmp(CmpOp), +} + +/// BinOrAugOp is the set of all binary and argument operators in KCL. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum BinOrAugOp { + Bin(BinOp), + Aug(AugOp), +} + +/// ExprContext represents the location information of the AST node. +/// The left side of the assignment symbol represents `Store`, +/// and the right side represents `Load`. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum ExprContext { + Load, + Store, +} + +/// A expression +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum Type { + Any, + Named(Identifier), + Basic(BasicType), + List(ListType), + Dict(DictType), + Union(UnionType), + Literal(LiteralType), +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum BasicType { + Bool, + Int, + Float, + Str, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ListType { + pub inner_type: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct DictType { + pub key_type: Option>, + pub value_type: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct UnionType { + pub type_elements: Vec>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum LiteralType { + Bool(bool), + Int(i64, Option), // value + suffix + Float(f64), + Str(String), +} + +impl ToString for Type { + fn to_string(&self) -> String { + fn to_str(typ: &Type, w: &mut String) { + match typ { + Type::Any => w.push_str("any"), + Type::Named(x) => { + w.push_str(&x.names.join(".")); + } + Type::Basic(x) => { + w.push_str(match x { + BasicType::Bool => "bool", + BasicType::Int => "int", + BasicType::Float => "float", + BasicType::Str => "str", + }); + } + Type::List(x) => { + w.push('['); + if let Some(t) = &x.inner_type { + to_str(&t.node, w); + } + w.push(']'); + } + Type::Dict(x) => { + w.push('{'); + if let Some(t) = &x.key_type { + to_str(&t.node, w); + } + w.push(':'); + if let Some(t) = &x.value_type { + to_str(&t.node, w); + } + w.push('}'); + } + Type::Union(x) => { + if x.type_elements.is_empty() { + w.push_str("any"); + return; + } + for (i, t) in x.type_elements.iter().enumerate() { + if i > 0 { + w.push('|'); + } + to_str(&t.node, w); + } + } + + Type::Literal(x) => match x { + LiteralType::Bool(v) => { + if *v { + w.push_str("True"); + } else { + w.push_str("False"); + } + } + LiteralType::Int(v, suffix) => { + if let Some(suffix) = suffix { + w.push_str(&format!("{}{}", v, suffix.value())); + } else { + w.push_str(&v.to_string()); + } + } + LiteralType::Float(v) => { + let mut float_str = v.to_string(); + if !float_str.contains('.') { + float_str.push_str(".0"); + } + w.push_str(&float_str); + } + LiteralType::Str(v) => { + w.push_str(&format!("\"{}\"", v.replace('"', "\\\""))); + } + }, + } + } + + let mut result = "".to_string(); + to_str(self, &mut result); + result + } +} + +impl From for AugOp { + fn from(op_kind: token::BinOpToken) -> Self { + match op_kind { + token::BinOpToken::Plus => AugOp::Add, + token::BinOpToken::Minus => AugOp::Sub, + token::BinOpToken::Star => AugOp::Mul, + token::BinOpToken::Slash => AugOp::Div, + token::BinOpToken::Percent => AugOp::Mod, + token::BinOpToken::StarStar => AugOp::Pow, + token::BinOpToken::SlashSlash => AugOp::Add, + token::BinOpToken::Caret => AugOp::BitXor, + token::BinOpToken::And => AugOp::BitAnd, + token::BinOpToken::Or => AugOp::BitOr, + token::BinOpToken::Shl => AugOp::LShift, + token::BinOpToken::Shr => AugOp::RShift, + } + } +} + +impl TryFrom for UnaryOp { + type Error = (); + + fn try_from(token: token::Token) -> Result { + use kclvm_span::symbol::kw; + + match token.kind { + token::TokenKind::UnaryOp(token::UnaryOpToken::UTilde) => Ok(UnaryOp::Invert), + token::TokenKind::UnaryOp(token::UnaryOpToken::UNot) => Ok(UnaryOp::Not), + token::TokenKind::BinOp(token::BinOpToken::Plus) => Ok(UnaryOp::UAdd), + token::TokenKind::BinOp(token::BinOpToken::Minus) => Ok(UnaryOp::USub), + _ => { + if token.is_keyword(kw::Not) { + Ok(UnaryOp::Not) + } else { + Err(()) + } + } + } + } +} + +impl TryFrom for BinOrCmpOp { + type Error = (); + + fn try_from(token: token::Token) -> Result { + use kclvm_span::symbol::kw; + + match token.kind { + token::TokenKind::BinOp(ot) => match ot { + token::BinOpToken::Plus => Ok(BinOrCmpOp::Bin(BinOp::Add)), + token::BinOpToken::Minus => Ok(BinOrCmpOp::Bin(BinOp::Sub)), + token::BinOpToken::Star => Ok(BinOrCmpOp::Bin(BinOp::Mul)), + token::BinOpToken::Slash => Ok(BinOrCmpOp::Bin(BinOp::Div)), + token::BinOpToken::Percent => Ok(BinOrCmpOp::Bin(BinOp::Mod)), + token::BinOpToken::StarStar => Ok(BinOrCmpOp::Bin(BinOp::Pow)), + token::BinOpToken::SlashSlash => Ok(BinOrCmpOp::Bin(BinOp::FloorDiv)), + token::BinOpToken::Caret => Ok(BinOrCmpOp::Bin(BinOp::BitXor)), + token::BinOpToken::And => Ok(BinOrCmpOp::Bin(BinOp::BitAnd)), + token::BinOpToken::Or => Ok(BinOrCmpOp::Bin(BinOp::BitOr)), + token::BinOpToken::Shl => Ok(BinOrCmpOp::Bin(BinOp::LShift)), + token::BinOpToken::Shr => Ok(BinOrCmpOp::Bin(BinOp::RShift)), + }, + token::TokenKind::BinCmp(ct) => match ct { + token::BinCmpToken::Eq => Ok(BinOrCmpOp::Cmp(CmpOp::Eq)), + token::BinCmpToken::NotEq => Ok(BinOrCmpOp::Cmp(CmpOp::NotEq)), + token::BinCmpToken::Lt => Ok(BinOrCmpOp::Cmp(CmpOp::Lt)), + token::BinCmpToken::LtEq => Ok(BinOrCmpOp::Cmp(CmpOp::LtE)), + token::BinCmpToken::Gt => Ok(BinOrCmpOp::Cmp(CmpOp::Gt)), + token::BinCmpToken::GtEq => Ok(BinOrCmpOp::Cmp(CmpOp::GtE)), + }, + _ => { + if token.is_keyword(kw::As) { + Ok(BinOrCmpOp::Bin(BinOp::As)) + } else if token.is_keyword(kw::Or) { + Ok(BinOrCmpOp::Bin(BinOp::Or)) + } else if token.is_keyword(kw::And) { + Ok(BinOrCmpOp::Bin(BinOp::And)) + } else if token.is_keyword(kw::In) { + Ok(BinOrCmpOp::Cmp(CmpOp::In)) + } else if token.is_keyword(kw::Is) { + Ok(BinOrCmpOp::Cmp(CmpOp::Is)) + } else { + Err(()) + } + } + } + } +} diff --git a/kclvm/ast/src/lib.rs b/kclvm/ast/src/lib.rs new file mode 100644 index 000000000..5ade77563 --- /dev/null +++ b/kclvm/ast/src/lib.rs @@ -0,0 +1,11 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod ast; +pub mod token; +pub mod token_stream; +pub mod walker; + +#[cfg(test)] +mod tests; + +pub const MAIN_PKG: &str = "__main__"; diff --git a/kclvm/ast/src/tests.rs b/kclvm/ast/src/tests.rs new file mode 100644 index 000000000..57981695a --- /dev/null +++ b/kclvm/ast/src/tests.rs @@ -0,0 +1,142 @@ +use crate::ast; +use crate::walker::MutSelfMutWalker; + +fn get_dummy_assign_ast() -> ast::Node { + let filename = "main.k"; + let line = 1; + let column = 1; + let end_line = 1; + let end_column = 2; + ast::Node::new( + ast::AssignStmt { + targets: vec![Box::new(ast::Node::new( + ast::Identifier { + names: vec![String::from("a")], + pkgpath: String::from(filename), + ctx: ast::ExprContext::Load, + }, + String::from(filename), + line, + column, + end_line, + end_column, + ))], + value: Box::new(ast::Node::new( + ast::Expr::StringLit(ast::StringLit { + is_long_string: false, + raw_value: String::from("s"), + value: String::from("s"), + }), + String::from(filename), + line, + column, + end_line, + end_column, + )), + type_annotation: None, + ty: None, + }, + String::from(filename), + line, + column, + end_line, + end_column, + ) +} + +fn get_dummy_assign_binary_ast() -> ast::Node { + let filename = "main.k"; + let line = 1; + let column = 1; + let end_line = 1; + let end_column = 2; + ast::Node::new( + ast::AssignStmt { + targets: vec![Box::new(ast::Node::new( + ast::Identifier { + names: vec![String::from("a")], + pkgpath: String::from(filename), + ctx: ast::ExprContext::Load, + }, + String::from(filename), + line, + column, + end_line, + end_column, + ))], + value: Box::new(ast::Node::new( + ast::Expr::Binary(ast::BinaryExpr { + op: ast::BinOrCmpOp::Bin(ast::BinOp::Add), + left: Box::new(ast::Node::new( + ast::Expr::Identifier(ast::Identifier { + names: vec![String::from("a")], + pkgpath: String::from(filename), + ctx: ast::ExprContext::Load, + }), + String::from(filename), + line, + column, + end_line, + end_column, + )), + right: Box::new(ast::Node::new( + ast::Expr::Identifier(ast::Identifier { + names: vec![String::from("a")], + pkgpath: String::from(filename), + ctx: ast::ExprContext::Load, + }), + String::from(filename), + line, + column, + end_line, + end_column, + )), + }), + String::from(filename), + line, + column, + end_line, + end_column, + )), + type_annotation: None, + ty: None, + }, + String::from(filename), + line, + column, + end_line, + end_column, + ) +} + +#[test] +fn test_ast_print_assign() { + let assign_stmt = get_dummy_assign_ast(); + println!("{:?}", assign_stmt); + let json_str = serde_json::to_string(&assign_stmt).unwrap(); + println!("{:?}", json_str); +} + +#[test] +fn test_ast_print_assign_binary() { + let assign_stmt = get_dummy_assign_binary_ast(); + println!("{:?}", assign_stmt); + let json_str = serde_json::to_string(&assign_stmt).unwrap(); + println!("{:?}", json_str); +} + +#[test] +fn test_mut_walker() { + pub struct VarMutSelfMutWalker; + impl<'ctx> MutSelfMutWalker<'ctx> for VarMutSelfMutWalker { + fn walk_identifier(&mut self, identifier: &'ctx mut ast::Identifier) { + if identifier.names[0] == "a" { + let id_mut = identifier.names.get_mut(0).unwrap(); + *id_mut = "x".to_string(); + } + } + } + let mut assign_stmt = get_dummy_assign_ast(); + VarMutSelfMutWalker {}.walk_assign_stmt(&mut assign_stmt.node); + assert_eq!(assign_stmt.node.targets[0].node.names[0], "x") +} diff --git a/kclvm/ast/src/token.rs b/kclvm/ast/src/token.rs new file mode 100644 index 000000000..b925c2e55 --- /dev/null +++ b/kclvm/ast/src/token.rs @@ -0,0 +1,349 @@ +//! KCL AST Affinity tokens. +//! +//! Tokens are designed based on the KCL AST. +//! Including indent and dedent tokens. +//! Not Include some tokens of low level tokens, such as ';', '..', '..=', '<-'. +pub use BinCmpToken::*; +pub use BinCmpToken::*; +pub use BinOpToken::*; +pub use DelimToken::*; +pub use LitKind::*; +pub use TokenKind::*; +pub use UnaryOpToken::*; + +use kclvm_span::symbol::{Ident, Symbol}; +use kclvm_span::{Span, DUMMY_SP}; + +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum CommentKind { + /// "#" + Line(Symbol), +} + +#[derive(Clone, PartialEq, Hash, Debug, Copy)] +pub enum UnaryOpToken { + /// "~" + UTilde, + + /// "not" + UNot, +} + +#[derive(Clone, PartialEq, Hash, Debug, Copy)] +pub enum BinOpToken { + /// "+" + Plus, + + /// "-" + Minus, + + /// "*" + Star, + + /// "/" + Slash, + + /// "%" + Percent, + + /// "**" + StarStar, + + /// "//" + SlashSlash, + + /// "^" + Caret, + + /// "&" + And, + + /// "|" + Or, + + /// "<<" + Shl, + + /// ">>" + Shr, +} + +#[derive(Clone, PartialEq, Hash, Debug, Copy)] +pub enum BinCmpToken { + /// "==" + Eq, + + /// "!=" + NotEq, + + /// "<" + Lt, + + /// "<=" + LtEq, + + /// ">" + Gt, + + /// ">=" + GtEq, +} + +/// A delimiter token. +#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)] +pub enum DelimToken { + /// A round parenthesis (i.e., `(` or `)`). + Paren, + /// A square bracket (i.e., `[` or `]`). + Bracket, + /// A curly brace (i.e., `{` or `}`). + Brace, + /// An empty delimiter. + NoDelim, +} + +/// A literal token. +#[derive(Clone, Copy, PartialEq, Debug)] +pub struct Lit { + pub kind: LitKind, + pub symbol: Symbol, + pub suffix: Option, + pub raw: Option, +} + +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum LitKind { + Bool, + Integer, + Float, + Str { is_long_string: bool, is_raw: bool }, + None, + Undefined, + Err, +} + +impl Into for LitKind { + fn into(self) -> String { + let s = match self { + Bool => "bool", + Integer => "int", + Float => "float", + Str { .. } => "str", + None => "None", + Undefined => "Undefined", + Err => "error", + }; + + s.to_string() + } +} + +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum TokenKind { + /* Expression-operator symbols. */ + UnaryOp(UnaryOpToken), + BinOp(BinOpToken), + BinOpEq(BinOpToken), + BinCmp(BinCmpToken), + + /* Structural symbols */ + /// '@' + At, + /// '.' + Dot, + /// '...' + DotDotDot, + /// ',' + Comma, + /// ':' + Colon, + /// '->' + RArrow, + /// '$' + Dollar, + /// '?' + Question, + /// '=' + Assign, + /// An opening delimiter (e.g., `{`). + OpenDelim(DelimToken), + /// A closing delimiter (e.g., `}`). + CloseDelim(DelimToken), + + /* Literals */ + Literal(Lit), + + /// Identifier token. + Ident(Symbol), + + /// A comment token. + DocComment(CommentKind), + + /// '\t' or ' ' + Indent, + + /// Remove an indent + Dedent, + + /// '\n' + Newline, + + Dummy, + + Eof, +} + +impl TokenKind { + pub fn ident_value() -> String { + "identifier".to_string() + } + + pub fn literal_value() -> String { + "literal".to_string() + } +} + +impl Into for TokenKind { + fn into(self) -> String { + let s = match self { + UnaryOp(unary_op) => match unary_op { + UTilde => "~", + UNot => "not", + }, + BinOp(bin_op) => match bin_op { + Plus => "+", + Minus => "-", + Star => "*", + Slash => "/", + Percent => "%", + StarStar => "**", + SlashSlash => "//", + Caret => "^", + And => "&", + Or => "|", + Shl => "<<", + Shr => ">>", + }, + BinOpEq(bin_op_eq) => match bin_op_eq { + Plus => "+=", + Minus => "-=", + Star => "*=", + Slash => "/=", + Percent => "%=", + StarStar => "**=", + SlashSlash => "//=", + Caret => "^=", + And => "&=", + Or => "|=", + Shl => "<<=", + Shr => ">>=", + }, + BinCmp(bin_cmp) => match bin_cmp { + Eq => "==", + NotEq => "!=", + Lt => "<", + LtEq => "<=", + Gt => ">", + GtEq => ">=", + }, + At => "@", + Dot => ".", + DotDotDot => "...", + Comma => ",", + Colon => ":", + RArrow => "->", + Dollar => "$", + Question => "?", + Assign => "=", + OpenDelim(delim) => match delim { + Paren => "(", + Bracket => "[", + Brace => "{", + NoDelim => "open_no_delim", + }, + CloseDelim(delim) => match delim { + Paren => ")", + Bracket => "]", + Brace => "}", + NoDelim => "close_no_delim", + }, + Literal(lit) => match lit.kind { + Bool => "bool", + Integer => "integer", + Float => "float", + Str { .. } => "string", + None => "None", + Undefined => "Undefined", + Err => "err", + }, + TokenKind::Ident(_) => "identifier", + DocComment(kind) => match kind { + CommentKind::Line(_) => "inline_comment", + }, + Indent => "indent", + Dedent => "dedent", + Newline => "newline", + Dummy => "dummy", + Eof => "eof", + }; + s.to_string() + } +} + +#[derive(Clone, Copy, PartialEq, Debug)] +pub struct Token { + pub kind: TokenKind, + pub span: Span, +} + +impl Into for Token { + fn into(self) -> String { + match self.kind { + Literal(lk) => { + let sym = lk.symbol.as_str().to_string(); + + match lk.suffix { + Some(suf) => sym + suf.as_str(), + _other_none => sym, + } + } + _ => self.kind.into(), + } + } +} + +impl Token { + pub fn new(kind: TokenKind, span: Span) -> Self { + Token { kind, span } + } + + /// Some token that will be thrown away later. + pub fn dummy() -> Self { + Token::new(TokenKind::Dummy, DUMMY_SP) + } + + /// Returns an identifier if this token is an identifier. + pub fn ident(&self) -> Option { + match self.kind { + Ident(name) => Some(Ident::new(name, self.span)), + _ => std::option::Option::None, + } + } + + pub fn is_keyword(&self, kw: Symbol) -> bool { + self.run_on_ident(|id| id.name == kw) + } + + fn run_on_ident(&self, pred: impl FnOnce(Ident) -> bool) -> bool { + match self.ident() { + Some(id) => pred(id), + _ => false, + } + } +} + +impl PartialEq for Token { + fn eq(&self, rhs: &TokenKind) -> bool { + self.kind == *rhs + } +} diff --git a/kclvm/ast/src/token_stream.rs b/kclvm/ast/src/token_stream.rs new file mode 100644 index 000000000..920cd867e --- /dev/null +++ b/kclvm/ast/src/token_stream.rs @@ -0,0 +1,64 @@ +use std::ops::Deref; + +use crate::token::Token; + +/// A `TokenStream` is an abstract sequence of tokens. +#[derive(Clone, Debug, Default)] +pub struct TokenStream(pub(crate) Vec); + +impl TokenStream { + pub fn new(streams: Vec) -> TokenStream { + TokenStream(streams) + } + + pub fn cursor(self) -> Cursor { + Cursor::new(self) + } +} + +impl Deref for TokenStream { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Clone)] +pub struct Cursor { + /// Token stream + pub stream: TokenStream, + /// Cursor index + index: usize, +} + +impl Cursor { + fn new(stream: TokenStream) -> Self { + Cursor { stream, index: 0 } + } + + pub fn index(&self) -> usize { + self.index + } + + pub fn peek(&self) -> Option { + if self.index < self.stream.len() { + Some(self.stream[self.index]) + } else { + None + } + } +} + +impl Iterator for Cursor { + type Item = Token; + + fn next(&mut self) -> Option { + if self.index < self.stream.len() { + self.index += 1; + Some(self.stream[self.index - 1]) + } else { + None + } + } +} diff --git a/kclvm/ast/src/walker.rs b/kclvm/ast/src/walker.rs new file mode 100644 index 000000000..365825eb6 --- /dev/null +++ b/kclvm/ast/src/walker.rs @@ -0,0 +1,992 @@ +//! AST walker. Each overridden walk method has full control over what +//! happens with its node, it can do its own traversal of the node's children, +//! call `walker::walk_*` to apply the default traversal algorithm, or prevent +//! deeper traversal by doing nothing. +//! +//! According to different usage scenarios, walker can be roughly divided into two categories +//! - Whether we need to modify the AST. If so, it means that we need a 'MutWalker'. +//! For example, when we need to pre process and desugate AST, which we can use 'MutWalker'. +//! - Whether each AST traversing method the ast node needs to return a value. +//! If so, it means that we need a `TypedResultWalker`. For example, when we need to traverse +//! the ast for type checking, we expect each ast node to calculate a type return value, +//! which we can use `TypedResultWalker`. + +use super::ast; + +#[macro_export] +macro_rules! walk_list { + ($walker: expr, $method: ident, $list: expr) => { + for elem in &$list { + $walker.$method(&elem.node) + } + }; +} + +#[macro_export] +macro_rules! walk_if { + ($walker: expr, $method: ident, $value: expr) => { + match &$value { + Some(v) => $walker.$method(&v.node), + None => (), + } + }; +} + +#[macro_export] +macro_rules! walk_list_mut { + ($walker: ident, $method: ident, $list: expr) => { + for elem in $list.iter_mut() { + $walker.$method(&mut elem.node) + } + }; +} + +#[macro_export] +macro_rules! walk_if_mut { + ($walker: ident, $method: ident, $value: expr) => { + match $value.as_deref_mut() { + Some(v) => $walker.$method(&mut v.node), + None => (), + } + }; +} + +/// Each method of the `TypedResultWalker` trait +/// returns a `Result` +pub trait TypedResultWalker<'ctx>: Sized { + type Result; + + /* + * Module + */ + + fn walk_module(&self, module: &'ctx ast::Module) -> Self::Result; + + /* + * Stmt + */ + + fn walk_stmt(&self, stmt: &'ctx ast::Node) -> Self::Result; + fn walk_expr_stmt(&self, expr_stmt: &'ctx ast::ExprStmt) -> Self::Result; + fn walk_unification_stmt(&self, unification_stmt: &'ctx ast::UnificationStmt) -> Self::Result; + fn walk_type_alias_stmt(&self, type_alias_stmt: &'ctx ast::TypeAliasStmt) -> Self::Result; + fn walk_assign_stmt(&self, assign_stmt: &'ctx ast::AssignStmt) -> Self::Result; + fn walk_aug_assign_stmt(&self, aug_assign_stmt: &'ctx ast::AugAssignStmt) -> Self::Result; + fn walk_assert_stmt(&self, assert_stmt: &'ctx ast::AssertStmt) -> Self::Result; + fn walk_if_stmt(&self, if_stmt: &'ctx ast::IfStmt) -> Self::Result; + fn walk_import_stmt(&self, import_stmt: &'ctx ast::ImportStmt) -> Self::Result; + fn walk_schema_stmt(&self, schema_stmt: &'ctx ast::SchemaStmt) -> Self::Result; + fn walk_rule_stmt(&self, rule_stmt: &'ctx ast::RuleStmt) -> Self::Result; + + /* + * Expr + */ + + fn walk_expr(&self, expr: &'ctx ast::Node) -> Self::Result; + fn walk_quant_expr(&self, quant_expr: &'ctx ast::QuantExpr) -> Self::Result; + fn walk_schema_attr(&self, schema_attr: &'ctx ast::SchemaAttr) -> Self::Result; + fn walk_if_expr(&self, if_expr: &'ctx ast::IfExpr) -> Self::Result; + fn walk_unary_expr(&self, unary_expr: &'ctx ast::UnaryExpr) -> Self::Result; + fn walk_binary_expr(&self, binary_expr: &'ctx ast::BinaryExpr) -> Self::Result; + fn walk_selector_expr(&self, selector_expr: &'ctx ast::SelectorExpr) -> Self::Result; + fn walk_call_expr(&self, call_expr: &'ctx ast::CallExpr) -> Self::Result; + fn walk_subscript(&self, subscript: &'ctx ast::Subscript) -> Self::Result; + fn walk_paren_expr(&self, paren_expr: &'ctx ast::ParenExpr) -> Self::Result; + fn walk_list_expr(&self, list_expr: &'ctx ast::ListExpr) -> Self::Result; + fn walk_list_comp(&self, list_comp: &'ctx ast::ListComp) -> Self::Result; + fn walk_list_if_item_expr(&self, list_if_item_expr: &'ctx ast::ListIfItemExpr) -> Self::Result; + fn walk_starred_expr(&self, starred_expr: &'ctx ast::StarredExpr) -> Self::Result; + fn walk_dict_comp(&self, dict_comp: &'ctx ast::DictComp) -> Self::Result; + fn walk_config_if_entry_expr( + &self, + config_if_entry_expr: &'ctx ast::ConfigIfEntryExpr, + ) -> Self::Result; + fn walk_comp_clause(&self, comp_clause: &'ctx ast::CompClause) -> Self::Result; + fn walk_schema_expr(&self, schema_expr: &'ctx ast::SchemaExpr) -> Self::Result; + fn walk_config_expr(&self, config_expr: &'ctx ast::ConfigExpr) -> Self::Result; + fn walk_check_expr(&self, check_expr: &'ctx ast::CheckExpr) -> Self::Result; + fn walk_lambda_expr(&self, lambda_expr: &'ctx ast::LambdaExpr) -> Self::Result; + fn walk_keyword(&self, keyword: &'ctx ast::Keyword) -> Self::Result; + fn walk_arguments(&self, arguments: &'ctx ast::Arguments) -> Self::Result; + fn walk_compare(&self, compare: &'ctx ast::Compare) -> Self::Result; + fn walk_identifier(&self, identifier: &'ctx ast::Identifier) -> Self::Result; + fn walk_number_lit(&self, number_lit: &'ctx ast::NumberLit) -> Self::Result; + fn walk_string_lit(&self, string_lit: &'ctx ast::StringLit) -> Self::Result; + fn walk_name_constant_lit(&self, name_constant_lit: &'ctx ast::NameConstantLit) + -> Self::Result; + fn walk_joined_string(&self, joined_string: &'ctx ast::JoinedString) -> Self::Result; + fn walk_formatted_value(&self, formatted_value: &'ctx ast::FormattedValue) -> Self::Result; + fn walk_comment(&self, comment: &'ctx ast::Comment) -> Self::Result; +} + +/// Each method of the `MutSelfTypedResultWalker` trait returns a typed result. +/// We can use it to calculate some values while traversing the AST and return +/// them through methods. For example, in the process of type checking, we can +/// use it to calculate the type return value of each ast node. +pub trait MutSelfTypedResultWalker<'ctx>: Sized { + type Result; + + /* + * Module + */ + + fn walk_module(&mut self, module: &'ctx ast::Module) -> Self::Result; + + /* + * Stmt + */ + + fn walk_stmt(&mut self, stmt: &'ctx ast::Stmt) -> Self::Result { + match stmt { + ast::Stmt::TypeAlias(type_alias) => self.walk_type_alias_stmt(type_alias), + ast::Stmt::Expr(expr_stmt) => self.walk_expr_stmt(expr_stmt), + ast::Stmt::Unification(unification_stmt) => { + self.walk_unification_stmt(unification_stmt) + } + ast::Stmt::Assign(assign_stmt) => self.walk_assign_stmt(assign_stmt), + ast::Stmt::AugAssign(aug_assign_stmt) => self.walk_aug_assign_stmt(aug_assign_stmt), + ast::Stmt::Assert(assert_stmt) => self.walk_assert_stmt(assert_stmt), + ast::Stmt::If(if_stmt) => self.walk_if_stmt(if_stmt), + ast::Stmt::Import(import_stmt) => self.walk_import_stmt(import_stmt), + ast::Stmt::SchemaAttr(schema_attr) => self.walk_schema_attr(schema_attr), + ast::Stmt::Schema(schema_stmt) => self.walk_schema_stmt(schema_stmt), + ast::Stmt::Rule(rule_stmt) => self.walk_rule_stmt(rule_stmt), + } + } + fn walk_expr_stmt(&mut self, expr_stmt: &'ctx ast::ExprStmt) -> Self::Result; + fn walk_unification_stmt( + &mut self, + unification_stmt: &'ctx ast::UnificationStmt, + ) -> Self::Result; + fn walk_type_alias_stmt(&mut self, type_alias_stmt: &'ctx ast::TypeAliasStmt) -> Self::Result; + fn walk_assign_stmt(&mut self, assign_stmt: &'ctx ast::AssignStmt) -> Self::Result; + fn walk_aug_assign_stmt(&mut self, aug_assign_stmt: &'ctx ast::AugAssignStmt) -> Self::Result; + fn walk_assert_stmt(&mut self, assert_stmt: &'ctx ast::AssertStmt) -> Self::Result; + fn walk_if_stmt(&mut self, if_stmt: &'ctx ast::IfStmt) -> Self::Result; + fn walk_import_stmt(&mut self, import_stmt: &'ctx ast::ImportStmt) -> Self::Result; + fn walk_schema_stmt(&mut self, schema_stmt: &'ctx ast::SchemaStmt) -> Self::Result; + fn walk_rule_stmt(&mut self, rule_stmt: &'ctx ast::RuleStmt) -> Self::Result; + + /* + * Expr + */ + + fn walk_expr(&mut self, expr: &'ctx ast::Expr) -> Self::Result { + match expr { + ast::Expr::Identifier(identifier) => self.walk_identifier(identifier), + ast::Expr::Unary(unary_expr) => self.walk_unary_expr(unary_expr), + ast::Expr::Binary(binary_expr) => self.walk_binary_expr(binary_expr), + ast::Expr::If(if_expr) => self.walk_if_expr(if_expr), + ast::Expr::Selector(selector_expr) => self.walk_selector_expr(selector_expr), + ast::Expr::Call(call_expr) => self.walk_call_expr(call_expr), + ast::Expr::Paren(paren_expr) => self.walk_paren_expr(paren_expr), + ast::Expr::Quant(quant_expr) => self.walk_quant_expr(quant_expr), + ast::Expr::List(list_expr) => self.walk_list_expr(list_expr), + ast::Expr::ListIfItem(list_if_item_expr) => { + self.walk_list_if_item_expr(list_if_item_expr) + } + ast::Expr::ListComp(list_comp) => self.walk_list_comp(list_comp), + ast::Expr::Starred(starred_expr) => self.walk_starred_expr(starred_expr), + ast::Expr::DictComp(dict_comp) => self.walk_dict_comp(dict_comp), + ast::Expr::ConfigIfEntry(config_if_entry_expr) => { + self.walk_config_if_entry_expr(config_if_entry_expr) + } + ast::Expr::CompClause(comp_clause) => self.walk_comp_clause(comp_clause), + ast::Expr::Schema(schema_expr) => self.walk_schema_expr(schema_expr), + ast::Expr::Config(config_expr) => self.walk_config_expr(config_expr), + ast::Expr::Check(check) => self.walk_check_expr(check), + ast::Expr::Lambda(lambda) => self.walk_lambda_expr(lambda), + ast::Expr::Subscript(subscript) => self.walk_subscript(subscript), + ast::Expr::Keyword(keyword) => self.walk_keyword(keyword), + ast::Expr::Arguments(arguments) => self.walk_arguments(arguments), + ast::Expr::Compare(compare) => self.walk_compare(compare), + ast::Expr::NumberLit(number_lit) => self.walk_number_lit(number_lit), + ast::Expr::StringLit(string_lit) => self.walk_string_lit(string_lit), + ast::Expr::NameConstantLit(name_constant_lit) => { + self.walk_name_constant_lit(name_constant_lit) + } + ast::Expr::JoinedString(joined_string) => self.walk_joined_string(joined_string), + ast::Expr::FormattedValue(formatted_value) => { + self.walk_formatted_value(formatted_value) + } + } + } + fn walk_quant_expr(&mut self, quant_expr: &'ctx ast::QuantExpr) -> Self::Result; + fn walk_schema_attr(&mut self, schema_attr: &'ctx ast::SchemaAttr) -> Self::Result; + fn walk_if_expr(&mut self, if_expr: &'ctx ast::IfExpr) -> Self::Result; + fn walk_unary_expr(&mut self, unary_expr: &'ctx ast::UnaryExpr) -> Self::Result; + fn walk_binary_expr(&mut self, binary_expr: &'ctx ast::BinaryExpr) -> Self::Result; + fn walk_selector_expr(&mut self, selector_expr: &'ctx ast::SelectorExpr) -> Self::Result; + fn walk_call_expr(&mut self, call_expr: &'ctx ast::CallExpr) -> Self::Result; + fn walk_subscript(&mut self, subscript: &'ctx ast::Subscript) -> Self::Result; + fn walk_paren_expr(&mut self, paren_expr: &'ctx ast::ParenExpr) -> Self::Result; + fn walk_list_expr(&mut self, list_expr: &'ctx ast::ListExpr) -> Self::Result; + fn walk_list_comp(&mut self, list_comp: &'ctx ast::ListComp) -> Self::Result; + fn walk_list_if_item_expr( + &mut self, + list_if_item_expr: &'ctx ast::ListIfItemExpr, + ) -> Self::Result; + fn walk_starred_expr(&mut self, starred_expr: &'ctx ast::StarredExpr) -> Self::Result; + fn walk_dict_comp(&mut self, dict_comp: &'ctx ast::DictComp) -> Self::Result; + fn walk_config_if_entry_expr( + &mut self, + config_if_entry_expr: &'ctx ast::ConfigIfEntryExpr, + ) -> Self::Result; + fn walk_comp_clause(&mut self, comp_clause: &'ctx ast::CompClause) -> Self::Result; + fn walk_schema_expr(&mut self, schema_expr: &'ctx ast::SchemaExpr) -> Self::Result; + fn walk_config_expr(&mut self, config_expr: &'ctx ast::ConfigExpr) -> Self::Result; + fn walk_check_expr(&mut self, check_expr: &'ctx ast::CheckExpr) -> Self::Result; + fn walk_lambda_expr(&mut self, lambda_expr: &'ctx ast::LambdaExpr) -> Self::Result; + fn walk_keyword(&mut self, keyword: &'ctx ast::Keyword) -> Self::Result; + fn walk_arguments(&mut self, arguments: &'ctx ast::Arguments) -> Self::Result; + fn walk_compare(&mut self, compare: &'ctx ast::Compare) -> Self::Result; + fn walk_identifier(&mut self, identifier: &'ctx ast::Identifier) -> Self::Result; + fn walk_number_lit(&mut self, number_lit: &'ctx ast::NumberLit) -> Self::Result; + fn walk_string_lit(&mut self, string_lit: &'ctx ast::StringLit) -> Self::Result; + fn walk_name_constant_lit( + &mut self, + name_constant_lit: &'ctx ast::NameConstantLit, + ) -> Self::Result; + fn walk_joined_string(&mut self, joined_string: &'ctx ast::JoinedString) -> Self::Result; + fn walk_formatted_value(&mut self, formatted_value: &'ctx ast::FormattedValue) -> Self::Result; + fn walk_comment(&mut self, comment: &'ctx ast::Comment) -> Self::Result; +} + +/// Each method of the `MutSelfMutWalker` trait returns void type. +/// We can use it to traverse the AST and modify it at the same time. +/// Unlike `MutSelfTypedResultWalker`, each method of `MutSelfMutWalker` has no return value. +pub trait MutSelfMutWalker<'ctx> { + fn walk_expr_stmt(&mut self, expr_stmt: &'ctx mut ast::ExprStmt) { + for expr in expr_stmt.exprs.iter_mut() { + self.walk_expr(&mut expr.node) + } + } + fn walk_type_alias_stmt(&mut self, type_alias_stmt: &'ctx mut ast::TypeAliasStmt) { + self.walk_identifier(&mut type_alias_stmt.type_name.node); + } + fn walk_unification_stmt(&mut self, unification_stmt: &'ctx mut ast::UnificationStmt) { + self.walk_identifier(&mut unification_stmt.target.node); + self.walk_schema_expr(&mut unification_stmt.value.node); + } + fn walk_assign_stmt(&mut self, assign_stmt: &'ctx mut ast::AssignStmt) { + for target in assign_stmt.targets.iter_mut() { + self.walk_identifier(&mut target.node) + } + self.walk_expr(&mut assign_stmt.value.node); + } + fn walk_aug_assign_stmt(&mut self, aug_assign_stmt: &'ctx mut ast::AugAssignStmt) { + self.walk_identifier(&mut aug_assign_stmt.target.node); + self.walk_expr(&mut aug_assign_stmt.value.node); + } + fn walk_assert_stmt(&mut self, assert_stmt: &'ctx mut ast::AssertStmt) { + self.walk_expr(&mut assert_stmt.test.node); + walk_if_mut!(self, walk_expr, assert_stmt.if_cond); + walk_if_mut!(self, walk_expr, assert_stmt.msg); + } + fn walk_if_stmt(&mut self, if_stmt: &'ctx mut ast::IfStmt) { + self.walk_expr(&mut if_stmt.cond.node); + walk_list_mut!(self, walk_stmt, if_stmt.body); + walk_list_mut!(self, walk_stmt, if_stmt.orelse); + } + fn walk_import_stmt(&mut self, _import_stmt: &'ctx mut ast::ImportStmt) { + // Nothing to do + } + fn walk_schema_attr(&mut self, schema_attr: &'ctx mut ast::SchemaAttr) { + walk_list_mut!(self, walk_call_expr, schema_attr.decorators); + walk_if_mut!(self, walk_expr, schema_attr.value); + } + fn walk_schema_stmt(&mut self, schema_stmt: &'ctx mut ast::SchemaStmt) { + walk_if_mut!(self, walk_identifier, schema_stmt.parent_name); + walk_if_mut!(self, walk_identifier, schema_stmt.for_host_name); + walk_if_mut!(self, walk_arguments, schema_stmt.args); + if let Some(schema_index_signature) = schema_stmt.index_signature.as_deref_mut() { + let value = &mut schema_index_signature.node.value; + walk_if_mut!(self, walk_expr, value); + } + walk_list_mut!(self, walk_identifier, schema_stmt.mixins); + walk_list_mut!(self, walk_call_expr, schema_stmt.decorators); + walk_list_mut!(self, walk_check_expr, schema_stmt.checks); + walk_list_mut!(self, walk_stmt, schema_stmt.body); + } + fn walk_rule_stmt(&mut self, rule_stmt: &'ctx mut ast::RuleStmt) { + walk_list_mut!(self, walk_identifier, rule_stmt.parent_rules); + walk_list_mut!(self, walk_call_expr, rule_stmt.decorators); + walk_list_mut!(self, walk_check_expr, rule_stmt.checks); + walk_if_mut!(self, walk_arguments, rule_stmt.args); + walk_if_mut!(self, walk_identifier, rule_stmt.for_host_name); + } + fn walk_quant_expr(&mut self, quant_expr: &'ctx mut ast::QuantExpr) { + self.walk_expr(&mut quant_expr.target.node); + walk_list_mut!(self, walk_identifier, quant_expr.variables); + self.walk_expr(&mut quant_expr.test.node); + walk_if_mut!(self, walk_expr, quant_expr.if_cond); + } + fn walk_if_expr(&mut self, if_expr: &'ctx mut ast::IfExpr) { + self.walk_expr(&mut if_expr.cond.node); + self.walk_expr(&mut if_expr.body.node); + self.walk_expr(&mut if_expr.orelse.node); + } + fn walk_unary_expr(&mut self, unary_expr: &'ctx mut ast::UnaryExpr) { + self.walk_expr(&mut unary_expr.operand.node); + } + fn walk_binary_expr(&mut self, binary_expr: &'ctx mut ast::BinaryExpr) { + self.walk_expr(&mut binary_expr.left.node); + self.walk_expr(&mut binary_expr.right.node); + } + fn walk_selector_expr(&mut self, selector_expr: &'ctx mut ast::SelectorExpr) { + self.walk_expr(&mut selector_expr.value.node); + self.walk_identifier(&mut selector_expr.attr.node); + } + fn walk_call_expr(&mut self, call_expr: &'ctx mut ast::CallExpr) { + self.walk_expr(&mut call_expr.func.node); + walk_list_mut!(self, walk_expr, call_expr.args); + walk_list_mut!(self, walk_keyword, call_expr.keywords); + } + fn walk_subscript(&mut self, subscript: &'ctx mut ast::Subscript) { + self.walk_expr(&mut subscript.value.node); + walk_if_mut!(self, walk_expr, subscript.index); + walk_if_mut!(self, walk_expr, subscript.lower); + walk_if_mut!(self, walk_expr, subscript.upper); + walk_if_mut!(self, walk_expr, subscript.step); + } + fn walk_paren_expr(&mut self, paren_expr: &'ctx mut ast::ParenExpr) { + self.walk_expr(&mut paren_expr.expr.node); + } + fn walk_list_expr(&mut self, list_expr: &'ctx mut ast::ListExpr) { + walk_list_mut!(self, walk_expr, list_expr.elts); + } + fn walk_list_comp(&mut self, list_comp: &'ctx mut ast::ListComp) { + self.walk_expr(&mut list_comp.elt.node); + walk_list_mut!(self, walk_comp_clause, list_comp.generators); + } + fn walk_list_if_item_expr(&mut self, list_if_item_expr: &'ctx mut ast::ListIfItemExpr) { + self.walk_expr(&mut list_if_item_expr.if_cond.node); + walk_list_mut!(self, walk_expr, list_if_item_expr.exprs); + walk_if_mut!(self, walk_expr, list_if_item_expr.orelse); + } + fn walk_starred_expr(&mut self, starred_expr: &'ctx mut ast::StarredExpr) { + self.walk_expr(&mut starred_expr.value.node); + } + fn walk_dict_comp(&mut self, dict_comp: &'ctx mut ast::DictComp) { + if let Some(key) = &mut dict_comp.entry.key { + self.walk_expr(&mut key.node); + } + self.walk_expr(&mut dict_comp.entry.value.node); + walk_list_mut!(self, walk_comp_clause, dict_comp.generators); + } + fn walk_config_if_entry_expr( + &mut self, + config_if_entry_expr: &'ctx mut ast::ConfigIfEntryExpr, + ) { + self.walk_expr(&mut config_if_entry_expr.if_cond.node); + for config_entry in config_if_entry_expr.items.iter_mut() { + walk_if_mut!(self, walk_expr, config_entry.node.key); + self.walk_expr(&mut config_entry.node.value.node); + } + walk_if_mut!(self, walk_expr, config_if_entry_expr.orelse); + } + fn walk_comp_clause(&mut self, comp_clause: &'ctx mut ast::CompClause) { + walk_list_mut!(self, walk_identifier, comp_clause.targets); + self.walk_expr(&mut comp_clause.iter.node); + walk_list_mut!(self, walk_expr, comp_clause.ifs); + } + fn walk_schema_expr(&mut self, schema_expr: &'ctx mut ast::SchemaExpr) { + self.walk_identifier(&mut schema_expr.name.node); + walk_list_mut!(self, walk_expr, schema_expr.args); + walk_list_mut!(self, walk_keyword, schema_expr.kwargs); + self.walk_expr(&mut schema_expr.config.node); + } + fn walk_config_expr(&mut self, config_expr: &'ctx mut ast::ConfigExpr) { + for config_entry in config_expr.items.iter_mut() { + walk_if_mut!(self, walk_expr, config_entry.node.key); + self.walk_expr(&mut config_entry.node.value.node); + } + } + fn walk_check_expr(&mut self, check_expr: &'ctx mut ast::CheckExpr) { + self.walk_expr(&mut check_expr.test.node); + walk_if_mut!(self, walk_expr, check_expr.if_cond); + walk_if_mut!(self, walk_expr, check_expr.msg); + } + fn walk_lambda_expr(&mut self, lambda_expr: &'ctx mut ast::LambdaExpr) { + walk_if_mut!(self, walk_arguments, lambda_expr.args); + walk_list_mut!(self, walk_stmt, lambda_expr.body); + } + fn walk_keyword(&mut self, keyword: &'ctx mut ast::Keyword) { + self.walk_identifier(&mut keyword.arg.node); + if let Some(v) = keyword.value.as_deref_mut() { + self.walk_expr(&mut v.node) + } + } + fn walk_arguments(&mut self, arguments: &'ctx mut ast::Arguments) { + walk_list_mut!(self, walk_identifier, arguments.args); + for default in arguments.defaults.iter_mut() { + if let Some(d) = default.as_deref_mut() { + self.walk_expr(&mut d.node) + } + } + } + fn walk_compare(&mut self, compare: &'ctx mut ast::Compare) { + self.walk_expr(&mut compare.left.node); + walk_list_mut!(self, walk_expr, compare.comparators); + } + fn walk_identifier(&mut self, identifier: &'ctx mut ast::Identifier) { + // Nothing to do. + let _ = identifier; + } + fn walk_number_lit(&mut self, number_lit: &'ctx mut ast::NumberLit) { + let _ = number_lit; + } + fn walk_string_lit(&mut self, string_lit: &'ctx mut ast::StringLit) { + // Nothing to do. + let _ = string_lit; + } + fn walk_name_constant_lit(&mut self, name_constant_lit: &'ctx mut ast::NameConstantLit) { + // Nothing to do. + let _ = name_constant_lit; + } + fn walk_joined_string(&mut self, joined_string: &'ctx mut ast::JoinedString) { + walk_list_mut!(self, walk_expr, joined_string.values); + } + fn walk_formatted_value(&mut self, formatted_value: &'ctx mut ast::FormattedValue) { + self.walk_expr(&mut formatted_value.value.node); + } + fn walk_comment(&mut self, comment: &'ctx mut ast::Comment) { + // Nothing to do. + let _ = comment; + } + fn walk_module(&mut self, module: &'ctx mut ast::Module) { + walk_list_mut!(self, walk_stmt, module.body) + } + fn walk_stmt(&mut self, stmt: &'ctx mut ast::Stmt) { + match stmt { + ast::Stmt::TypeAlias(type_alias) => self.walk_type_alias_stmt(type_alias), + ast::Stmt::Expr(expr_stmt) => self.walk_expr_stmt(expr_stmt), + ast::Stmt::Unification(unification_stmt) => { + self.walk_unification_stmt(unification_stmt) + } + ast::Stmt::Assign(assign_stmt) => self.walk_assign_stmt(assign_stmt), + ast::Stmt::AugAssign(aug_assign_stmt) => self.walk_aug_assign_stmt(aug_assign_stmt), + ast::Stmt::Assert(assert_stmt) => self.walk_assert_stmt(assert_stmt), + ast::Stmt::If(if_stmt) => self.walk_if_stmt(if_stmt), + ast::Stmt::Import(import_stmt) => self.walk_import_stmt(import_stmt), + ast::Stmt::SchemaAttr(schema_attr) => self.walk_schema_attr(schema_attr), + ast::Stmt::Schema(schema_stmt) => self.walk_schema_stmt(schema_stmt), + ast::Stmt::Rule(rule_stmt) => self.walk_rule_stmt(rule_stmt), + } + } + fn walk_expr(&mut self, expr: &'ctx mut ast::Expr) { + match expr { + ast::Expr::Identifier(identifier) => self.walk_identifier(identifier), + ast::Expr::Unary(unary_expr) => self.walk_unary_expr(unary_expr), + ast::Expr::Binary(binary_expr) => self.walk_binary_expr(binary_expr), + ast::Expr::If(if_expr) => self.walk_if_expr(if_expr), + ast::Expr::Selector(selector_expr) => self.walk_selector_expr(selector_expr), + ast::Expr::Call(call_expr) => self.walk_call_expr(call_expr), + ast::Expr::Paren(paren_expr) => self.walk_paren_expr(paren_expr), + ast::Expr::Quant(quant_expr) => self.walk_quant_expr(quant_expr), + ast::Expr::List(list_expr) => self.walk_list_expr(list_expr), + ast::Expr::ListIfItem(list_if_item_expr) => { + self.walk_list_if_item_expr(list_if_item_expr) + } + ast::Expr::ListComp(list_comp) => self.walk_list_comp(list_comp), + ast::Expr::Starred(starred_expr) => self.walk_starred_expr(starred_expr), + ast::Expr::DictComp(dict_comp) => self.walk_dict_comp(dict_comp), + ast::Expr::ConfigIfEntry(config_if_entry_expr) => { + self.walk_config_if_entry_expr(config_if_entry_expr) + } + ast::Expr::CompClause(comp_clause) => self.walk_comp_clause(comp_clause), + ast::Expr::Schema(schema_expr) => self.walk_schema_expr(schema_expr), + ast::Expr::Config(config_expr) => self.walk_config_expr(config_expr), + ast::Expr::Check(check) => self.walk_check_expr(check), + ast::Expr::Lambda(lambda) => self.walk_lambda_expr(lambda), + ast::Expr::Subscript(subscript) => self.walk_subscript(subscript), + ast::Expr::Keyword(keyword) => self.walk_keyword(keyword), + ast::Expr::Arguments(arguments) => self.walk_arguments(arguments), + ast::Expr::Compare(compare) => self.walk_compare(compare), + ast::Expr::NumberLit(number_lit) => self.walk_number_lit(number_lit), + ast::Expr::StringLit(string_lit) => self.walk_string_lit(string_lit), + ast::Expr::NameConstantLit(name_constant_lit) => { + self.walk_name_constant_lit(name_constant_lit) + } + ast::Expr::JoinedString(joined_string) => self.walk_joined_string(joined_string), + ast::Expr::FormattedValue(formatted_value) => { + self.walk_formatted_value(formatted_value) + } + } + } +} + +/// Each method of the `Walker` trait is a hook to be potentially +/// overridden. Each method's default implementation recursively visits +/// the substructure of the input via the corresponding `walk` method; +/// e.g., the `walk_item` method by default calls `walker::walk_item`. +/// +/// If you want to ensure that your code handles every variant +/// explicitly, you need to override each method. (And you also need +/// to monitor future changes to `Walker` in case a new method with a +/// new default implementation gets introduced.) +pub trait Walker<'ctx>: TypedResultWalker<'ctx> { + fn walk_expr_stmt(&mut self, expr_stmt: &'ctx ast::ExprStmt) { + walk_expr_stmt(self, expr_stmt); + } + fn walk_unification_stmt(&mut self, unification_stmt: &'ctx ast::UnificationStmt) { + walk_unification_stmt(self, unification_stmt); + } + fn walk_type_alias_stmt(&mut self, type_alias_stmt: &'ctx ast::TypeAliasStmt) { + walk_type_alias_stmt(self, type_alias_stmt); + } + fn walk_assign_stmt(&mut self, assign_stmt: &'ctx ast::AssignStmt) { + walk_assign_stmt(self, assign_stmt); + } + fn walk_aug_assign_stmt(&mut self, aug_assign_stmt: &'ctx ast::AugAssignStmt) { + walk_aug_assign_stmt(self, aug_assign_stmt); + } + fn walk_assert_stmt(&mut self, assert_stmt: &'ctx ast::AssertStmt) { + walk_assert_stmt(self, assert_stmt); + } + fn walk_if_stmt(&mut self, if_stmt: &'ctx ast::IfStmt) { + walk_if_stmt(self, if_stmt); + } + fn walk_import_stmt(&mut self, import_stmt: &'ctx ast::ImportStmt) { + walk_import_stmt(self, import_stmt); + } + fn walk_schema_attr(&mut self, schema_attr: &'ctx ast::SchemaAttr) { + walk_schema_attr(self, schema_attr); + } + fn walk_schema_stmt(&mut self, schema_stmt: &'ctx ast::SchemaStmt) { + walk_schema_stmt(self, schema_stmt); + } + fn walk_rule_stmt(&mut self, rule_stmt: &'ctx ast::RuleStmt) { + walk_rule_stmt(self, rule_stmt); + } + fn walk_quant_expr(&mut self, quant_expr: &'ctx ast::QuantExpr) { + walk_quant_expr(self, quant_expr); + } + fn walk_if_expr(&mut self, if_expr: &'ctx ast::IfExpr) { + walk_if_expr(self, if_expr); + } + fn walk_unary_expr(&mut self, unary_expr: &'ctx ast::UnaryExpr) { + walk_unary_expr(self, unary_expr); + } + fn walk_binary_expr(&mut self, binary_expr: &'ctx ast::BinaryExpr) { + walk_binary_expr(self, binary_expr); + } + fn walk_selector_expr(&mut self, selector_expr: &'ctx ast::SelectorExpr) { + walk_selector_expr(self, selector_expr); + } + fn walk_call_expr(&mut self, call_expr: &'ctx ast::CallExpr) { + walk_call_expr(self, call_expr); + } + fn walk_subscript(&mut self, subscript: &'ctx ast::Subscript) { + walk_subscript(self, subscript); + } + fn walk_paren_expr(&mut self, paren_expr: &'ctx ast::ParenExpr) { + walk_paren_expr(self, paren_expr); + } + fn walk_list_expr(&mut self, list_expr: &'ctx ast::ListExpr) { + walk_list_expr(self, list_expr); + } + fn walk_list_comp(&mut self, list_comp: &'ctx ast::ListComp) { + walk_list_comp(self, list_comp); + } + fn walk_list_if_item_expr(&mut self, list_if_item_expr: &'ctx ast::ListIfItemExpr) { + walk_list_if_item_expr(self, list_if_item_expr); + } + fn walk_starred_expr(&mut self, starred_expr: &'ctx ast::StarredExpr) { + walk_starred_expr(self, starred_expr); + } + fn walk_dict_comp(&mut self, dict_comp: &'ctx ast::DictComp) { + walk_dict_comp(self, dict_comp); + } + fn walk_config_if_entry_expr(&mut self, config_if_entry_expr: &'ctx ast::ConfigIfEntryExpr) { + walk_config_if_entry_expr(self, config_if_entry_expr); + } + fn walk_comp_clause(&mut self, comp_clause: &'ctx ast::CompClause) { + walk_comp_clause(self, comp_clause); + } + fn walk_schema_expr(&mut self, schema_expr: &'ctx ast::SchemaExpr) { + walk_schema_expr(self, schema_expr); + } + fn walk_config_expr(&mut self, config_expr: &'ctx ast::ConfigExpr) { + walk_config_expr(self, config_expr); + } + fn walk_check_expr(&mut self, check_expr: &'ctx ast::CheckExpr) { + walk_check_expr(self, check_expr); + } + fn walk_lambda_expr(&mut self, lambda_expr: &'ctx ast::LambdaExpr) { + walk_lambda_expr(self, lambda_expr); + } + fn walk_keyword(&mut self, keyword: &'ctx ast::Keyword) { + walk_keyword(self, keyword); + } + fn walk_arguments(&mut self, arguments: &'ctx ast::Arguments) { + walk_arguments(self, arguments); + } + fn walk_compare(&mut self, compare: &'ctx ast::Compare) { + walk_compare(self, compare); + } + fn walk_identifier(&mut self, identifier: &'ctx ast::Identifier) { + walk_identifier(self, identifier); + } + fn walk_number_lit(&mut self, number_lit: &'ctx ast::NumberLit) { + walk_number_lit(self, number_lit); + } + fn walk_string_lit(&mut self, string_lit: &'ctx ast::StringLit) { + walk_string_lit(self, string_lit); + } + fn walk_name_constant_lit(&mut self, name_constant_lit: &'ctx ast::NameConstantLit) { + walk_name_constant_lit(self, name_constant_lit); + } + fn walk_joined_string(&mut self, joined_string: &'ctx ast::JoinedString) { + walk_joined_string(self, joined_string); + } + fn walk_formatted_value(&mut self, formatted_value: &'ctx ast::FormattedValue) { + walk_formatted_value(self, formatted_value); + } + fn walk_comment(&mut self, comment: &'ctx ast::Comment) { + walk_comment(self, comment); + } + fn walk_module(&mut self, module: &'ctx ast::Module) { + walk_module(self, module); + } + fn walk_stmt(&mut self, stmt: &'ctx ast::Stmt) { + walk_stmt(self, stmt) + } + fn walk_expr(&mut self, expr: &'ctx ast::Expr) { + walk_expr(self, expr) + } +} + +pub fn walk_expr<'ctx, V: Walker<'ctx>>(walker: &mut V, expr: &'ctx ast::Expr) { + match expr { + ast::Expr::Identifier(identifier) => walker.walk_identifier(identifier), + ast::Expr::Unary(unary_expr) => walker.walk_unary_expr(unary_expr), + ast::Expr::Binary(binary_expr) => walker.walk_binary_expr(binary_expr), + ast::Expr::If(if_expr) => walker.walk_if_expr(if_expr), + ast::Expr::Selector(selector_expr) => walker.walk_selector_expr(selector_expr), + ast::Expr::Call(call_expr) => walker.walk_call_expr(call_expr), + ast::Expr::Paren(paren_expr) => walker.walk_paren_expr(paren_expr), + ast::Expr::Quant(quant_expr) => walker.walk_quant_expr(quant_expr), + ast::Expr::List(list_expr) => walker.walk_list_expr(list_expr), + ast::Expr::ListIfItem(list_if_item_expr) => { + walker.walk_list_if_item_expr(list_if_item_expr) + } + ast::Expr::ListComp(list_comp) => walker.walk_list_comp(list_comp), + ast::Expr::Starred(starred_expr) => walker.walk_starred_expr(starred_expr), + ast::Expr::DictComp(dict_comp) => walker.walk_dict_comp(dict_comp), + ast::Expr::ConfigIfEntry(config_if_entry_expr) => { + walker.walk_config_if_entry_expr(config_if_entry_expr) + } + ast::Expr::CompClause(comp_clause) => walker.walk_comp_clause(comp_clause), + ast::Expr::Schema(schema_expr) => walker.walk_schema_expr(schema_expr), + ast::Expr::Config(config_expr) => walker.walk_config_expr(config_expr), + ast::Expr::Check(check) => walker.walk_check_expr(check), + ast::Expr::Lambda(lambda) => walker.walk_lambda_expr(lambda), + ast::Expr::Subscript(subscript) => walker.walk_subscript(subscript), + ast::Expr::Keyword(keyword) => walker.walk_keyword(keyword), + ast::Expr::Arguments(arguments) => walker.walk_arguments(arguments), + ast::Expr::Compare(compare) => walker.walk_compare(compare), + ast::Expr::NumberLit(number_lit) => walker.walk_number_lit(number_lit), + ast::Expr::StringLit(string_lit) => walker.walk_string_lit(string_lit), + ast::Expr::NameConstantLit(name_constant_lit) => { + walker.walk_name_constant_lit(name_constant_lit) + } + ast::Expr::JoinedString(joined_string) => walker.walk_joined_string(joined_string), + ast::Expr::FormattedValue(formatted_value) => walker.walk_formatted_value(formatted_value), + } +} + +pub fn walk_stmt<'ctx, V: Walker<'ctx>>(walker: &mut V, stmt: &'ctx ast::Stmt) { + match stmt { + ast::Stmt::TypeAlias(type_alias) => walker.walk_type_alias_stmt(type_alias), + ast::Stmt::Expr(expr_stmt) => walker.walk_expr_stmt(expr_stmt), + ast::Stmt::Unification(unification_stmt) => walker.walk_unification_stmt(unification_stmt), + ast::Stmt::Assign(assign_stmt) => walker.walk_assign_stmt(assign_stmt), + ast::Stmt::AugAssign(aug_assign_stmt) => walker.walk_aug_assign_stmt(aug_assign_stmt), + ast::Stmt::Assert(assert_stmt) => walker.walk_assert_stmt(assert_stmt), + ast::Stmt::If(if_stmt) => walker.walk_if_stmt(if_stmt), + ast::Stmt::Import(import_stmt) => walker.walk_import_stmt(import_stmt), + ast::Stmt::SchemaAttr(schema_attr) => walker.walk_schema_attr(schema_attr), + ast::Stmt::Schema(schema_stmt) => walker.walk_schema_stmt(schema_stmt), + ast::Stmt::Rule(rule_stmt) => walker.walk_rule_stmt(rule_stmt), + } +} + +pub fn walk_expr_stmt<'ctx, V: Walker<'ctx>>(walker: &mut V, expr_stmt: &'ctx ast::ExprStmt) { + walk_list!(walker, walk_expr, expr_stmt.exprs); +} + +pub fn walk_unification_stmt<'ctx, V: Walker<'ctx>>( + walker: &mut V, + unification_stmt: &'ctx ast::UnificationStmt, +) { + walker.walk_identifier(&unification_stmt.target.node); + walker.walk_schema_expr(&unification_stmt.value.node); +} + +pub fn walk_type_alias_stmt<'ctx, V: Walker<'ctx>>( + walker: &mut V, + type_alias_stmt: &'ctx ast::TypeAliasStmt, +) { + walker.walk_identifier(&type_alias_stmt.type_name.node); +} + +pub fn walk_assign_stmt<'ctx, V: Walker<'ctx>>(walker: &mut V, assign_stmt: &'ctx ast::AssignStmt) { + walk_list!(walker, walk_identifier, assign_stmt.targets); + walker.walk_expr(&assign_stmt.value.node); +} + +pub fn walk_aug_assign_stmt<'ctx, V: Walker<'ctx>>( + walker: &mut V, + aug_assign_stmt: &'ctx ast::AugAssignStmt, +) { + walker.walk_identifier(&aug_assign_stmt.target.node); + walker.walk_expr(&aug_assign_stmt.value.node); +} + +pub fn walk_assert_stmt<'ctx, V: Walker<'ctx>>(walker: &mut V, assert_stmt: &'ctx ast::AssertStmt) { + walker.walk_expr(&assert_stmt.test.node); + walk_if!(walker, walk_expr, assert_stmt.if_cond); + walk_if!(walker, walk_expr, assert_stmt.msg); +} + +pub fn walk_if_stmt<'ctx, V: Walker<'ctx>>(walker: &mut V, if_stmt: &'ctx ast::IfStmt) { + walker.walk_expr(&if_stmt.cond.node); + walk_list!(walker, walk_stmt, if_stmt.body); + walk_list!(walker, walk_stmt, if_stmt.orelse); +} + +pub fn walk_import_stmt<'ctx, V: Walker<'ctx>>(walker: &mut V, import_stmt: &'ctx ast::ImportStmt) { + let _ = walker; + let _ = import_stmt; +} + +pub fn walk_schema_attr<'ctx, V: Walker<'ctx>>(walker: &mut V, schema_attr: &'ctx ast::SchemaAttr) { + walk_list!(walker, walk_call_expr, schema_attr.decorators); + walk_if!(walker, walk_expr, schema_attr.value); +} + +pub fn walk_schema_stmt<'ctx, V: Walker<'ctx>>(walker: &mut V, schema_stmt: &'ctx ast::SchemaStmt) { + walk_if!(walker, walk_identifier, schema_stmt.parent_name); + walk_if!(walker, walk_identifier, schema_stmt.for_host_name); + walk_if!(walker, walk_arguments, schema_stmt.args); + if let Some(schema_index_signature) = &schema_stmt.index_signature { + walk_schema_index_signature(walker, &schema_index_signature.node); + } + walk_list!(walker, walk_identifier, schema_stmt.mixins); + walk_list!(walker, walk_call_expr, schema_stmt.decorators); + walk_list!(walker, walk_check_expr, schema_stmt.checks); + walk_list!(walker, walk_stmt, schema_stmt.body); +} + +pub fn walk_rule_stmt<'ctx, V: Walker<'ctx>>(walker: &mut V, rule_stmt: &'ctx ast::RuleStmt) { + walk_list!(walker, walk_identifier, rule_stmt.parent_rules); + walk_list!(walker, walk_call_expr, rule_stmt.decorators); + walk_list!(walker, walk_check_expr, rule_stmt.checks); + walk_if!(walker, walk_arguments, rule_stmt.args); + walk_if!(walker, walk_identifier, rule_stmt.for_host_name); +} + +pub fn walk_quant_expr<'ctx, V: Walker<'ctx>>(walker: &mut V, quant_expr: &'ctx ast::QuantExpr) { + walker.walk_expr(&quant_expr.target.node); + walk_list!(walker, walk_identifier, quant_expr.variables); + walker.walk_expr(&quant_expr.test.node); + walk_if!(walker, walk_expr, quant_expr.if_cond); +} + +pub fn walk_schema_index_signature<'ctx, V: Walker<'ctx>>( + walker: &mut V, + schema_index_signature: &'ctx ast::SchemaIndexSignature, +) { + walk_if!(walker, walk_expr, schema_index_signature.value); +} + +pub fn walk_if_expr<'ctx, V: Walker<'ctx>>(walker: &mut V, if_expr: &'ctx ast::IfExpr) { + walker.walk_expr(&if_expr.cond.node); + walker.walk_expr(&if_expr.body.node); + walker.walk_expr(&if_expr.orelse.node); +} + +pub fn walk_unary_expr<'ctx, V: Walker<'ctx>>(walker: &mut V, unary_expr: &'ctx ast::UnaryExpr) { + walker.walk_expr(&unary_expr.operand.node); +} + +pub fn walk_binary_expr<'ctx, V: Walker<'ctx>>(walker: &mut V, binary_expr: &'ctx ast::BinaryExpr) { + walker.walk_expr(&binary_expr.left.node); + walker.walk_expr(&binary_expr.right.node); +} + +pub fn walk_selector_expr<'ctx, V: Walker<'ctx>>( + walker: &mut V, + selector_expr: &'ctx ast::SelectorExpr, +) { + walker.walk_expr(&selector_expr.value.node); + walker.walk_identifier(&selector_expr.attr.node); +} + +pub fn walk_call_expr<'ctx, V: Walker<'ctx>>(walker: &mut V, call_expr: &'ctx ast::CallExpr) { + walker.walk_expr(&call_expr.func.node); + walk_list!(walker, walk_expr, call_expr.args); + walk_list!(walker, walk_keyword, call_expr.keywords); +} + +pub fn walk_subscript<'ctx, V: Walker<'ctx>>(walker: &mut V, subscript: &'ctx ast::Subscript) { + walker.walk_expr(&subscript.value.node); + walk_if!(walker, walk_expr, subscript.index); + walk_if!(walker, walk_expr, subscript.lower); + walk_if!(walker, walk_expr, subscript.upper); + walk_if!(walker, walk_expr, subscript.step); +} + +pub fn walk_paren_expr<'ctx, V: Walker<'ctx>>(walker: &mut V, paren_expr: &'ctx ast::ParenExpr) { + walker.walk_expr(&paren_expr.expr.node); +} + +pub fn walk_list_expr<'ctx, V: Walker<'ctx>>(walker: &mut V, list_expr: &'ctx ast::ListExpr) { + walk_list!(walker, walk_expr, list_expr.elts); +} + +pub fn walk_list_comp<'ctx, V: Walker<'ctx>>(walker: &mut V, list_comp: &'ctx ast::ListComp) { + walker.walk_expr(&list_comp.elt.node); + walk_list!(walker, walk_comp_clause, list_comp.generators); +} + +pub fn walk_list_if_item_expr<'ctx, V: Walker<'ctx>>( + walker: &mut V, + list_if_item_expr: &'ctx ast::ListIfItemExpr, +) { + walker.walk_expr(&list_if_item_expr.if_cond.node); + walk_list!(walker, walk_expr, list_if_item_expr.exprs); + walk_if!(walker, walk_expr, list_if_item_expr.orelse); +} + +pub fn walk_starred_expr<'ctx, V: Walker<'ctx>>( + walker: &mut V, + starred_expr: &'ctx ast::StarredExpr, +) { + walker.walk_expr(&starred_expr.value.node); +} + +pub fn walk_dict_comp<'ctx, V: Walker<'ctx>>(walker: &mut V, dict_comp: &'ctx ast::DictComp) { + if let Some(key) = &dict_comp.entry.key { + walker.walk_expr(&key.node); + } + walker.walk_expr(&dict_comp.entry.value.node); + walk_list!(walker, walk_comp_clause, dict_comp.generators); +} + +pub fn walk_config_if_entry_expr<'ctx, V: Walker<'ctx>>( + walker: &mut V, + config_if_entry_expr: &'ctx ast::ConfigIfEntryExpr, +) { + walker.walk_expr(&config_if_entry_expr.if_cond.node); + for config_entry in &config_if_entry_expr.items { + walk_if!(walker, walk_expr, config_entry.node.key); + walker.walk_expr(&config_entry.node.value.node); + } + walk_if!(walker, walk_expr, config_if_entry_expr.orelse); +} + +pub fn walk_comp_clause<'ctx, V: Walker<'ctx>>(walker: &mut V, comp_clause: &'ctx ast::CompClause) { + walk_list!(walker, walk_identifier, comp_clause.targets); + walker.walk_expr(&comp_clause.iter.node); + walk_list!(walker, walk_expr, comp_clause.ifs); +} + +pub fn walk_schema_expr<'ctx, V: Walker<'ctx>>(walker: &mut V, schema_expr: &'ctx ast::SchemaExpr) { + walker.walk_identifier(&schema_expr.name.node); + walk_list!(walker, walk_expr, schema_expr.args); + walk_list!(walker, walk_keyword, schema_expr.kwargs); + walker.walk_expr(&schema_expr.config.node); +} + +pub fn walk_config_expr<'ctx, V: Walker<'ctx>>(walker: &mut V, config_expr: &'ctx ast::ConfigExpr) { + for config_entry in &config_expr.items { + walk_if!(walker, walk_expr, config_entry.node.key); + walker.walk_expr(&config_entry.node.value.node); + } +} + +pub fn walk_check_expr<'ctx, V: Walker<'ctx>>(walker: &mut V, check_expr: &'ctx ast::CheckExpr) { + walker.walk_expr(&check_expr.test.node); + walk_if!(walker, walk_expr, check_expr.if_cond); + walk_if!(walker, walk_expr, check_expr.msg); +} + +pub fn walk_lambda_expr<'ctx, V: Walker<'ctx>>(walker: &mut V, lambda_expr: &'ctx ast::LambdaExpr) { + walk_if!(walker, walk_arguments, lambda_expr.args); + walk_list!(walker, walk_stmt, lambda_expr.body); +} + +pub fn walk_keyword<'ctx, V: Walker<'ctx>>(walker: &mut V, keyword: &'ctx ast::Keyword) { + walker.walk_identifier(&keyword.arg.node); + match &keyword.value { + Some(v) => walker.walk_expr(&v.node), + None => (), + } +} + +pub fn walk_arguments<'ctx, V: Walker<'ctx>>(walker: &mut V, arguments: &'ctx ast::Arguments) { + walk_list!(walker, walk_identifier, arguments.args); + for default in &arguments.defaults { + if let Some(d) = default.as_ref() { + walker.walk_expr(&d.node) + } + } +} + +pub fn walk_compare<'ctx, V: Walker<'ctx>>(walker: &mut V, compare: &'ctx ast::Compare) { + walker.walk_expr(&compare.left.node); + walk_list!(walker, walk_expr, compare.comparators); +} + +pub fn walk_identifier<'ctx, V: Walker<'ctx>>(walker: &mut V, identifier: &'ctx ast::Identifier) { + // Nothing to do. + let _ = walker; + let _ = identifier; +} + +pub fn walk_number_lit<'ctx, V: Walker<'ctx>>(walker: &mut V, number_lit: &'ctx ast::NumberLit) { + // Nothing to do. + let _ = walker; + let _ = number_lit; +} + +pub fn walk_string_lit<'ctx, V: Walker<'ctx>>(walker: &mut V, string_lit: &'ctx ast::StringLit) { + // Nothing to do. + let _ = walker; + let _ = string_lit; +} + +pub fn walk_name_constant_lit<'ctx, V: Walker<'ctx>>( + walker: &mut V, + name_constant_lit: &'ctx ast::NameConstantLit, +) { + // Nothing to do. + let _ = walker; + let _ = name_constant_lit; +} + +pub fn walk_joined_string<'ctx, V: Walker<'ctx>>( + walker: &mut V, + joined_string: &'ctx ast::JoinedString, +) { + walk_list!(walker, walk_expr, joined_string.values); +} + +pub fn walk_formatted_value<'ctx, V: Walker<'ctx>>( + walker: &mut V, + formatted_value: &'ctx ast::FormattedValue, +) { + walker.walk_expr(&formatted_value.value.node); +} + +pub fn walk_comment<'ctx, V: Walker<'ctx>>(walker: &mut V, comment: &'ctx ast::Comment) { + // Nothing to do. + let _ = walker; + let _ = comment; +} + +pub fn walk_module<'ctx, V: Walker<'ctx>>(walker: &mut V, module: &'ctx ast::Module) { + walk_list!(walker, walk_stmt, module.body) +} diff --git a/kclvm/compiler/Cargo.lock b/kclvm/compiler/Cargo.lock new file mode 100644 index 000000000..b5ac34666 --- /dev/null +++ b/kclvm/compiler/Cargo.lock @@ -0,0 +1,1325 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "annotate-snippets" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "fancy-regex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "fixedbitset" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", + "rustc-rayon", +] + +[[package]] +name = "inkwell" +version = "0.1.0" +source = "git+https://github.com/TheDan64/inkwell?branch=master#bff378bee02bcbb5bed35f47e9ed69e6642e9188" +dependencies = [ + "either", + "inkwell_internals", + "libc", + "llvm-sys", + "once_cell", + "parking_lot", +] + +[[package]] +name = "inkwell_internals" +version = "0.5.0" +source = "git+https://github.com/TheDan64/inkwell?branch=master#bff378bee02bcbb5bed35f47e9ed69e6642e9188" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "json_minimal" +version = "0.1.3" + +[[package]] +name = "kclvm-ast" +version = "0.1.0" +dependencies = [ + "kclvm-span", + "rustc_span", + "serde", + "serde_json", +] + +[[package]] +name = "kclvm-compiler" +version = "0.1.0" +dependencies = [ + "ahash", + "bit-set", + "bitflags", + "fancy-regex", + "indexmap", + "inkwell", + "kclvm-ast", + "kclvm-error", + "kclvm-runtime", + "kclvm-sema", + "once_cell", + "phf", + "time", + "unicode_names2", +] + +[[package]] +name = "kclvm-error" +version = "0.1.0" +dependencies = [ + "annotate-snippets", + "atty", + "indexmap", + "kclvm-span", + "rustc_span", + "termcolor", + "termize", + "tracing", +] + +[[package]] +name = "kclvm-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "kclvm-runtime" +version = "0.1.0" +dependencies = [ + "ahash", + "base64", + "bstr", + "chrono", + "fancy-regex", + "indexmap", + "itertools", + "json_minimal", + "kclvm_runtime_internal_macros", + "libc", + "md5", + "num-integer", + "phf", + "regex", + "serde_json", + "serde_yaml", + "sha1", + "sha2 0.9.8", + "unic-ucd-bidi", + "unic-ucd-category", + "unicode-casing", +] + +[[package]] +name = "kclvm-sema" +version = "0.1.0" +dependencies = [ + "ahash", + "bit-set", + "bitflags", + "fancy-regex", + "indexmap", + "kclvm-ast", + "kclvm-error", + "kclvm-runtime", + "kclvm-span", + "once_cell", + "petgraph", + "phf", + "unicode_names2", +] + +[[package]] +name = "kclvm-span" +version = "0.1.0" +dependencies = [ + "kclvm-macros", + "rustc_span", + "scoped-tls", +] + +[[package]] +name = "kclvm_runtime_internal_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "llvm-sys" +version = "120.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b716322964966a62377cf86e64f00ca7043505fdf27bd2ec7d41ae6682d1e7" +dependencies = [ + "cc", + "lazy_static", + "libc", + "regex", + "semver", +] + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest 0.10.3", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "petgraph" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "ppv-lite86" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871372391786ccec00d3c5d3d6608905b3d4db263639cfe075d3b60a736d115a" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-rayon" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" +dependencies = [ + "crossbeam-deque", + "either", + "rustc-rayon-core", +] + +[[package]] +name = "rustc-rayon-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc_data_structures" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ena", + "indexmap", + "jobserver", + "libc", + "memmap2", + "parking_lot", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "stable_deref_trait", + "stacker", + "tempfile", + "tracing", + "winapi", +] + +[[package]] +name = "rustc_span" +version = "0.0.0" +dependencies = [ + "cfg-if 0.1.10", + "md-5", + "rustc_data_structures", + "scoped-tls", + "sha-1", + "sha2 0.10.2", + "tracing", + "unicode-width", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + +[[package]] +name = "sha2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "siphasher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termize" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "tracing" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80b9fa4360528139bc96100c160b7ae879f5567f49f1782b0b02035b0358ebf3" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90442985ee2f57c9e1b548ee72ae842f4a9a20e3f417cc38dbc5dc684d9bb4ee" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-category" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8d4591f5fcfe1bd4453baaf803c40e1b1e69ff8455c47620440b46efef91c0" +dependencies = [ + "matches", + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-casing" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "623f59e6af2a98bdafeb93fa277ac8e1e40440973001ca15cf4ae1541cd16d56" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "unicode_names2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d6678d7916394abad0d4b19df4d3802e1fd84abd7d701f39b75ee71b9e8cf1" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/kclvm/compiler/Cargo.toml b/kclvm/compiler/Cargo.toml new file mode 100644 index 000000000..06027a04c --- /dev/null +++ b/kclvm/compiler/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "kclvm-compiler" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm12-0"] } +time = "0.1" +phf = { version = "0.9", features = ["macros"] } +ahash = "0.7.2" +indexmap = "1.0" +bit-set = "0.5.0" +bitflags = "1.2.1" +once_cell = "1.5.2" +fancy-regex = "0.7.1" +unicode_names2 = "0.4" +kclvm-ast = {path = "../ast", version = "0.1.0"} +kclvm-sema = {path = "../sema", version = "0.1.0"} +kclvm-runtime = {path = "../runtime", version = "0.1.0"} +kclvm-error = {path = "../error", version = "0.1.0"} diff --git a/kclvm/compiler/makefile b/kclvm/compiler/makefile new file mode 100644 index 000000000..c87b1b251 --- /dev/null +++ b/kclvm/compiler/makefile @@ -0,0 +1,16 @@ +default: + cargo run --release + +llvm: + clang++ -O3 -emit-llvm ./C/test.c -S -o ./C/test.ll + llvm-as ./C/test.ll + lli ./C/test.bc + +fmt: + cargo fmt + +check: + cargo check --release + +lint: + cargo clippy diff --git a/kclvm/compiler/src/codegen/abi.rs b/kclvm/compiler/src/codegen/abi.rs new file mode 100644 index 000000000..4b2b1e71c --- /dev/null +++ b/kclvm/compiler/src/codegen/abi.rs @@ -0,0 +1,217 @@ +//! Copyright 2021 The KCL Authors. All rights reserved. +//! Reference: https://github.com/rust-lang/rust/blob/master/compiler/rustc_target/src/lib.rs + +#![allow(dead_code)] + +use std::convert::TryInto; + +/// Operations may overflow and they are needed to be checked +#[derive(Copy, Clone)] +pub enum OverflowOp { + Add, + Sub, + Mul, +} + +/// An identifier that specifies the address space that some operation +/// should operate on. Special address spaces have an effect on code generation, +/// depending on the target and the address spaces it implements. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct AddressSpace(pub u32); + +impl AddressSpace { + /// The default address space, corresponding to data space. + pub const DATA: Self = AddressSpace(0); +} + +/// Alignment of a type in bytes (always a power of two). +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Align { + pow2: u8, +} + +impl Align { + pub const ONE: Align = Align { pow2: 0 }; + + #[inline] + pub fn from_bits(bits: u64) -> Result { + Align::from_bytes(Size::from_bits(bits).bytes()) + } + + #[inline] + pub fn from_bytes(align: u64) -> Result { + // Treat an alignment of 0 bytes like 1-byte alignment. + if align == 0 { + return Ok(Align::ONE); + } + + #[cold] + fn not_power_of_2(align: u64) -> String { + format!("`{}` is not a power of 2", align) + } + + #[cold] + fn too_large(align: u64) -> String { + format!("`{}` is too large", align) + } + + let mut bytes = align; + let mut pow2: u8 = 0; + while (bytes & 1) == 0 { + pow2 += 1; + bytes >>= 1; + } + if bytes != 1 { + return Err(not_power_of_2(align)); + } + if pow2 > 29 { + return Err(too_large(align)); + } + + Ok(Align { pow2 }) + } + + #[inline] + pub fn bytes(self) -> u64 { + 1 << self.pow2 + } + + #[inline] + pub fn bits(self) -> u64 { + self.bytes() * 8 + } + + /// Computes the best alignment possible for the given offset + /// (the largest power of two that the offset is a multiple of). + /// + /// N.B., for an offset of `0`, this happens to return `2^64`. + #[inline] + pub fn max_for_offset(offset: Size) -> Align { + Align { + pow2: offset.bytes().trailing_zeros() as u8, + } + } + + /// Lower the alignment, if necessary, such that the given offset + /// is aligned to it (the offset is a multiple of the alignment). + #[inline] + pub fn restrict_for_offset(self, offset: Size) -> Align { + self.min(Align::max_for_offset(offset)) + } +} + +/// Size of a type in bytes. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Size { + // The top 3 bits are ALWAYS zero. + raw: u64, +} + +impl Size { + pub const ZERO: Size = Size { raw: 0 }; + + /// Rounds `bits` up to the next-higher byte boundary, if `bits` is + /// is not aligned. + pub fn from_bits(bits: impl TryInto) -> Size { + let bits = bits.try_into().ok().unwrap(); + + #[cold] + fn overflow(bits: u64) -> ! { + panic!("Size::from_bits({}) has overflowed", bits); + } + + // This is the largest value of `bits` that does not cause overflow + // during rounding, and guarantees that the resulting number of bytes + // cannot cause overflow when multiplied by 8. + if bits > 0xffff_ffff_ffff_fff8 { + overflow(bits); + } + + // Avoid potential overflow from `bits + 7`. + Size { + raw: bits / 8 + ((bits % 8) + 7) / 8, + } + } + + #[inline] + pub fn from_bytes(bytes: impl TryInto) -> Size { + let bytes: u64 = bytes.try_into().ok().unwrap(); + Size { raw: bytes } + } + + #[inline] + pub fn bytes(self) -> u64 { + self.raw + } + + #[inline] + pub fn bytes_usize(self) -> usize { + self.bytes().try_into().unwrap() + } + + #[inline] + pub fn bits(self) -> u64 { + self.raw << 3 + } + + #[inline] + pub fn bits_usize(self) -> usize { + self.bits().try_into().unwrap() + } + + #[inline] + pub fn align_to(self, align: Align) -> Size { + let mask = align.bytes() - 1; + Size::from_bytes((self.bytes() + mask) & !mask) + } + + #[inline] + pub fn is_aligned(self, align: Align) -> bool { + let mask = align.bytes() - 1; + self.bytes() & mask == 0 + } + + /// Truncates `value` to `self` bits and then sign-extends it to 128 bits + /// (i.e., if it is negative, fill with 1's on the left). + #[inline] + pub fn sign_extend(self, value: u128) -> u128 { + let size = self.bits(); + if size == 0 { + // Truncated until nothing is left. + return 0; + } + // Sign-extend it. + let shift = 128 - size; + // Shift the unsigned value to the left, then shift back to the right as signed + // (essentially fills with sign bit on the left). + (((value << shift) as i128) >> shift) as u128 + } + + /// Truncates `value` to `self` bits. + #[inline] + pub fn truncate(self, value: u128) -> u128 { + let size = self.bits(); + if size == 0 { + // Truncated until nothing is left. + return 0; + } + let shift = 128 - size; + // Truncate (shift left to drop out leftover values, shift right to fill with zeroes). + (value << shift) >> shift + } + + #[inline] + pub fn signed_int_min(&self) -> i128 { + self.sign_extend(1_u128 << (self.bits() - 1)) as i128 + } + + #[inline] + pub fn signed_int_max(&self) -> i128 { + i128::MAX >> (128 - self.bits()) + } + + #[inline] + pub fn unsigned_int_max(&self) -> u128 { + u128::MAX >> (128 - self.bits()) + } +} diff --git a/kclvm/compiler/src/codegen/error.rs b/kclvm/compiler/src/codegen/error.rs new file mode 100644 index 000000000..50cc0fb2c --- /dev/null +++ b/kclvm/compiler/src/codegen/error.rs @@ -0,0 +1,61 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use std::error; +use std::fmt::{self, Debug}; + +pub(crate) const VALUE_TYPE_NOT_FOUND_MSG: &str = "Type is not found"; +pub(crate) const CONTEXT_VAR_NOT_FOUND_MSG: &str = "Context variable is not found"; +pub(crate) const FUNCTION_RETURN_VALUE_NOT_FOUND_MSG: &str = "Function return value is not found"; +pub(crate) const COMPILE_ERROR_MSG: &str = "Compile error"; +pub(crate) const INTERNAL_ERROR_MSG: &str = "Internal error, please report a bug to us"; +pub(crate) const CODE_GEN_ERROR_MSG: &str = "Code gen error"; +pub(crate) const INVALID_OPERATOR_MSG: &str = "Invalid operator"; +pub(crate) const INVALID_JOINED_STR_MSG: &str = "Invalid AST JoinedString value"; +pub(crate) const INVALID_STR_INTERPOLATION_SPEC_MSG: &str = + "Invalid string interpolation format specification"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum KCLErrorType { + Compile, + Runtime, +} + +#[derive(Debug, Clone)] +pub struct KCLError { + pub message: String, + pub ty: KCLErrorType, +} + +impl fmt::Display for KCLError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + f, + "{}: {}", + match self.ty { + KCLErrorType::Compile => "compile error", + KCLErrorType::Runtime => "runtime error", + }, + self.message + ) + } +} + +impl error::Error for KCLError {} + +impl Default for KCLError { + fn default() -> Self { + Self { + message: Default::default(), + ty: KCLErrorType::Compile, + } + } +} + +impl KCLError { + pub fn new(msg: &str) -> Self { + Self { + message: msg.to_string(), + ty: KCLErrorType::Compile, + } + } +} diff --git a/kclvm/compiler/src/codegen/llvm/context.rs b/kclvm/compiler/src/codegen/llvm/context.rs new file mode 100644 index 000000000..17f453686 --- /dev/null +++ b/kclvm/compiler/src/codegen/llvm/context.rs @@ -0,0 +1,1880 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use indexmap::IndexMap; +use inkwell::basic_block::BasicBlock; +use inkwell::builder::Builder; +use inkwell::context::Context; +use inkwell::module::{Linkage, Module}; +use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType}; +use inkwell::values::{ + BasicMetadataValueEnum, BasicValueEnum, FunctionValue, IntValue, PointerValue, +}; +use inkwell::{AddressSpace, IntPredicate}; +use phf::{phf_map, Map}; +use std::cell::RefCell; +use std::collections::{HashMap, HashSet}; +use std::error::Error; +use std::rc::Rc; +use std::str; + +use kclvm::{ApiFunc, MAIN_PKG_PATH, PKG_PATH_PREFIX}; +use kclvm_ast::ast; +use kclvm_ast::walker::TypedResultWalker; +use kclvm_error::*; +use kclvm_sema::builtin; +use kclvm_sema::plugin; + +use crate::codegen::abi::Align; +use crate::codegen::CodeGenContext; +use crate::codegen::{error as kcl_error, EmitOptions}; +use crate::codegen::{ + traits::*, ENTRY_NAME, GLOBAL_VAL_ALIGNMENT, KCL_CONTEXT_VAR_NAME, MODULE_NAME, + PKG_INIT_FUNCTION_SUFFIX, +}; +use crate::pkgpath_without_prefix; +use crate::value; + +/// Float type string width mapping +pub const FLOAT_TYPE_WIDTH_MAPPING: Map<&str, usize> = phf_map! { + "half" => 16, + "float" => 32, + "double" => 64, + "x86_fp80" => 80, + "ppc_fp128" => 128, + "fp128" => 128, +}; + +/// The compiler function result +pub type CompileResult<'a> = Result, kcl_error::KCLError>; + +/// The compiler scope. +pub struct Scope<'ctx> { + pub variables: RefCell>>, + pub closures: RefCell>>, +} + +/// Schema internal order independent computation backtracking meta information. +pub struct BacktrackMeta { + pub target: String, + pub level: usize, + pub count: usize, + pub stop: bool, +} + +/// The LLVM code generator +pub struct LLVMCodeGenContext<'ctx> { + pub context: &'ctx Context, + pub module: Module<'ctx>, + pub builder: Builder<'ctx>, + pub program: &'ctx ast::Program, + pub pkg_scopes: RefCell>>>>, + pub functions: RefCell>>>, + pub imported: RefCell>, + pub local_vars: RefCell>, + pub schema_stack: RefCell>, + pub lambda_stack: RefCell>, + pub pkgpath_stack: RefCell>, + pub filename_stack: RefCell>, + pub target_vars: RefCell>, + pub global_strings: RefCell>>>, + pub global_vars: RefCell>>>, + pub current_filename: RefCell, + pub current_line: RefCell, + pub handler: RefCell, + // Schema attr backtrack meta + pub backtrack_meta: RefCell>, + /// Import names mapping + pub import_names: IndexMap>, + // No link mode + pub no_link: bool, + pub modules: RefCell>>>, +} + +impl<'ctx> CodeGenObject for BasicValueEnum<'ctx> {} + +impl<'ctx> CodeGenObject for BasicTypeEnum<'ctx> {} + +impl<'ctx> BackendTypes for LLVMCodeGenContext<'ctx> { + type Value = BasicValueEnum<'ctx>; + type Type = BasicTypeEnum<'ctx>; + type BasicBlock = BasicBlock<'ctx>; + type Function = FunctionValue<'ctx>; + type FunctionLet = FunctionType<'ctx>; +} + +impl<'ctx> BuilderMethods for LLVMCodeGenContext<'ctx> { + /// SSA append a basic block named `name`. + #[inline] + fn append_block(&self, name: &str) -> Self::BasicBlock { + let cur_func = self.current_function(); + self.context.append_basic_block(cur_func, name) + } + /// SSA switch to the block. + #[inline] + fn switch_to_block(&self, block: Self::BasicBlock) { + self.builder.position_at_end(block); + } + /// SSA alloca instruction. + #[inline] + fn alloca(&self, ty: Self::Type, name: &str, _align: Option) -> Self::Value { + self.builder.build_alloca(ty, name).into() + } + /// SSA array alloca instruction. + #[inline] + fn array_alloca( + &self, + ty: Self::Type, + len: Self::Value, + name: &str, + _align: Align, + ) -> Self::Value { + self.builder + .build_array_alloca(ty, len.into_int_value(), name) + .into() + } + /// SSA ret instruction. + #[inline] + fn ret_void(&self) { + self.builder.build_return(None); + } + /// SSA ret instruction with returned value. + #[inline] + fn ret(&self, v: Self::Value) { + self.builder.build_return(Some(&v)); + } + /// SSA br instruction. + #[inline] + fn br(&self, dest: Self::BasicBlock) { + self.builder.build_unconditional_branch(dest); + } + /// SSA cond br instruction. + #[inline] + fn cond_br(&self, cond: Self::Value, then_bb: Self::BasicBlock, else_bb: Self::BasicBlock) { + self.builder + .build_conditional_branch(cond.into_int_value(), then_bb, else_bb); + } + /// SSA select instruction. + #[inline] + fn select( + &self, + cond: Self::Value, + then_val: Self::Value, + else_val: Self::Value, + ) -> Self::Value { + self.builder + .build_select(cond.into_int_value(), then_val, else_val, "") + } + /// SSA va arg instruction. + #[inline] + fn va_arg(&self, list: Self::Value, ty: Self::Type) -> Self::Value { + self.builder.build_va_arg(list.into_pointer_value(), ty, "") + } + /// SSA extract element instruction. + #[inline] + fn extract_element(&self, vec: Self::Value, idx: Self::Value) -> Self::Value { + self.builder + .build_extract_element(vec.into_vector_value(), idx.into_int_value(), "") + } + /// SSA extract value instruction. + #[inline] + fn extract_value(&self, agg_val: Self::Value, idx: u32) -> Self::Value { + self.builder + .build_extract_value(agg_val.into_array_value(), idx, "") + .expect(kcl_error::INTERNAL_ERROR_MSG) + } + /// SSA insert value instruction. + #[inline] + fn insert_value(&self, agg_val: Self::Value, elt: Self::Value, idx: u32) -> Self::Value { + self.builder + .build_insert_value(agg_val.into_array_value(), elt, idx, "") + .expect(kcl_error::INTERNAL_ERROR_MSG) + .into_array_value() + .into() + } + /// SSA function invoke instruction. + #[inline] + fn invoke( + &self, + _ty: Self::Type, + fn_value: Self::Function, + args: &[Self::Value], + then: Self::BasicBlock, + catch: Self::BasicBlock, + ) -> Self::Value { + self.builder + .build_invoke(fn_value, args, then, catch, "") + .try_as_basic_value() + .expect_left(kcl_error::INTERNAL_ERROR_MSG) + } + /// SSA function call instruction. + #[inline] + fn call(&self, _ty: Self::Type, fn_value: Self::Function, args: &[Self::Value]) -> Self::Value { + let args: Vec = args.iter().map(|arg| (*arg).into()).collect(); + self.builder + .build_call(fn_value, &args, "") + .try_as_basic_value() + .left() + .expect(kcl_error::FUNCTION_RETURN_VALUE_NOT_FOUND_MSG) + } + /// SSA load instruction. + #[inline] + fn load(&self, ptr: Self::Value, name: &str) -> Self::Value { + self.builder.build_load(ptr.into_pointer_value(), name) + } + /// SSA store instruction. + #[inline] + fn store(&self, ptr: Self::Value, val: Self::Value) { + self.builder.build_store(ptr.into_pointer_value(), val); + } + /// SSA gep instruction. + #[inline] + fn gep(&self, _ty: Self::Type, ptr: Self::Value, indices: &[Self::Value]) -> Self::Value { + let ordered_indexes: Vec = indices.iter().map(|v| v.into_int_value()).collect(); + unsafe { + self.builder + .build_gep(ptr.into_pointer_value(), &ordered_indexes, "") + .into() + } + } + /// SSA inbounds gep instruction. + #[inline] + fn inbounds_gep( + &self, + _ty: Self::Type, + ptr: Self::Value, + indices: &[Self::Value], + ) -> Self::Value { + let ordered_indexes: Vec = indices.iter().map(|v| v.into_int_value()).collect(); + unsafe { + self.builder + .build_in_bounds_gep(ptr.into_pointer_value(), &ordered_indexes, "") + .into() + } + } + /// SSA struct gep instruction. + #[inline] + fn struct_gep(&self, _ty: Self::Type, ptr: Self::Value, idx: u32) -> Self::Value { + self.builder + .build_struct_gep(ptr.into_pointer_value(), idx, "") + .expect(kcl_error::INTERNAL_ERROR_MSG) + .into() + } + /// SSA cast pointer to int. + #[inline] + fn ptr_to_int(&self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + self.builder + .build_ptr_to_int(val.into_pointer_value(), dest_ty.into_int_type(), "") + .into() + } + /// SSA cast int to pointer. + #[inline] + fn int_to_ptr(&self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + self.builder + .build_int_to_ptr(val.into_int_value(), dest_ty.into_pointer_type(), "") + .into() + } + /// SSA bit cast. + #[inline] + fn bit_cast(&self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + self.builder.build_bitcast(val, dest_ty, "") + } + /// SSA int cast. + #[inline] + fn int_cast(&self, val: Self::Value, dest_ty: Self::Type, _is_signed: bool) -> Self::Value { + self.builder + .build_int_cast(val.into_int_value(), dest_ty.into_int_type(), "") + .into() + } + /// SSA pointer cast. + #[inline] + fn ptr_cast(&self, val: Self::Value, dest_ty: Self::Type) -> Self::Value { + self.builder + .build_pointer_cast(val.into_pointer_value(), dest_ty.into_pointer_type(), "") + .into() + } + /// Lookup a known function named `name`. + fn lookup_function(&self, name: &str) -> Self::Function { + if self.no_link { + let pkgpath = self.current_pkgpath(); + let modules = self.modules.borrow_mut(); + let msg = format!("pkgpath {} is not found", pkgpath); + let module = modules.get(&pkgpath).expect(&msg).borrow_mut(); + if let Some(function) = module.get_function(name) { + function + } else { + let function = self + .module + .get_function(name) + .unwrap_or_else(|| panic!("known function {} not found", name)); + let fn_type = function.get_type(); + module.add_function(name, fn_type, Some(Linkage::External)) + } + } else { + self.module + .get_function(name) + .unwrap_or_else(|| panic!("known function {} not found", name)) + } + } + /// Add a function named `name`. + fn add_function(&self, name: &str) -> Self::Function { + let fn_ty = self.function_type(); + if self.no_link { + let pkgpath = self.current_pkgpath(); + let msg = format!("pkgpath {} is not found", pkgpath); + let modules = self.modules.borrow_mut(); + let module = modules.get(&pkgpath).expect(&msg).borrow_mut(); + module.add_function(name, fn_ty, None) + } else { + self.module.add_function(name, fn_ty, None) + } + } +} + +/* Value methods */ + +impl<'ctx> ValueMethods for LLVMCodeGenContext<'ctx> { + /// Construct a 64-bit int value using i64 + fn int_value(&self, v: i64) -> Self::Value { + let i64_type = self.context.i64_type(); + self.build_call( + &ApiFunc::kclvm_value_Int.name(), + &[i64_type.const_int(v as u64, false).into()], + ) + } + + /// Construct a 64-bit float value using f64 + fn float_value(&self, v: f64) -> Self::Value { + let f64_type = self.context.f64_type(); + self.build_call( + &ApiFunc::kclvm_value_Float.name(), + &[f64_type.const_float(v).into()], + ) + } + + /// Construct a string value using &str + fn string_value(&self, v: &str) -> Self::Value { + let string_ptr_value = self.native_global_string(v, ""); + self.build_call(&ApiFunc::kclvm_value_Str.name(), &[string_ptr_value.into()]) + } + + /// Construct a bool value + fn bool_value(&self, v: bool) -> Self::Value { + let i8_type = self.context.i8_type(); + self.build_call( + &ApiFunc::kclvm_value_Bool.name(), + &[i8_type.const_int(v as u64, false).into()], + ) + } + + /// Construct a None value + fn none_value(&self) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_None.name(), &[]) + } + + /// Construct a Undefined value + fn undefined_value(&self) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_Undefined.name(), &[]) + } + + /// Construct a empty kcl list value + fn list_value(&self) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_List.name(), &[]) + } + + /// Construct a list value with `n` elements + fn list_values(&self, values: &[Self::Value]) -> Self::Value { + self.build_call( + &format!("{}{}", ApiFunc::kclvm_value_List.name(), values.len()), + values, + ) + } + + /// Construct a empty kcl dict value. + fn dict_value(&self) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_Dict.name(), &[]) + } + + /// Construct a unit value + fn unit_value(&self, v: f64, raw: i64, unit: &str) -> Self::Value { + let f64_type = self.context.f64_type(); + let i64_type = self.context.i64_type(); + let unit_native_str = self.native_global_string(unit, ""); + self.build_call( + &ApiFunc::kclvm_value_Unit.name(), + &[ + f64_type.const_float(v).into(), + i64_type.const_int(raw as u64, false).into(), + unit_native_str.into(), + ], + ) + } + /// Construct a function value using a native function. + fn function_value(&self, function: FunctionValue<'ctx>) -> Self::Value { + let lambda_fn_ptr = self.builder.build_bitcast( + function.as_global_value().as_pointer_value(), + self.context.i64_type().ptr_type(AddressSpace::Generic), + "", + ); + self.build_call( + &ApiFunc::kclvm_value_Function_using_ptr.name(), + &[lambda_fn_ptr], + ) + } + /// Construct a closure function value with the closure variable. + fn closure_value(&self, function: FunctionValue<'ctx>, closure: Self::Value) -> Self::Value { + // Convert the function to a i64 pointer to store it into the function value. + let fn_ptr = self.builder.build_bitcast( + function.as_global_value().as_pointer_value(), + self.context.i64_type().ptr_type(AddressSpace::Generic), + "", + ); + let name = self.native_global_string("", "").into(); + self.build_call( + &ApiFunc::kclvm_value_Function.name(), + &[fn_ptr, closure, name], + ) + } + /// Construct a schema function value using native functions. + fn struct_function_value( + &self, + functions: &[FunctionValue<'ctx>], + runtime_type: &str, + ) -> Self::Value { + if functions.is_empty() { + return self.none_value(); + } + // Convert the function to a i64 pointer to store it into the function value. + let schema_body_fn_ptr = self.builder.build_bitcast( + functions[0].as_global_value().as_pointer_value(), + self.context.i64_type().ptr_type(AddressSpace::Generic), + "", + ); + // Convert the function to a i64 pointer to store it into the function value. + let check_block_fn_ptr = if functions.len() > 1 { + self.builder.build_bitcast( + functions[1].as_global_value().as_pointer_value(), + self.context.i64_type().ptr_type(AddressSpace::Generic), + "", + ) + } else { + self.context + .i64_type() + .ptr_type(AddressSpace::Generic) + .const_zero() + .into() + }; + let runtime_type_native_str = self.native_global_string_value(runtime_type); + self.builder + .build_call( + self.lookup_function(&ApiFunc::kclvm_value_schema_function.name()), + &[ + schema_body_fn_ptr.into(), + check_block_fn_ptr.into(), + runtime_type_native_str.into(), + ], + runtime_type, + ) + .try_as_basic_value() + .left() + .expect(kcl_error::FUNCTION_RETURN_VALUE_NOT_FOUND_MSG) + } + /// Construct a builtin function value using the function name. + fn builtin_function_value(&self, function_name: &str) -> Self::Value { + let mut function = self + .module + .get_function(function_name) + .unwrap_or_else(|| panic!("global function {} not found", function_name)); + if self.no_link { + let pkgpath = self.current_pkgpath(); + let modules = self.modules.borrow_mut(); + let msg = format!("pkgpath {} is not found", pkgpath); + let module = modules.get(&pkgpath).expect(&msg).borrow_mut(); + let fn_type = function.get_type(); + function = module.add_function(function_name, fn_type, Some(Linkage::External)); + } + self.function_value(function) + } + /// Get a global value pointer named `name`. + fn global_value_ptr(&self, name: &str) -> Self::Value { + let tpe = self.value_ptr_type(); + // Builtin function value is a global one + let global_var = if self.no_link { + let pkgpath = self.current_pkgpath(); + let msg = format!("pkgpath {} is not found", pkgpath); + let modules = self.modules.borrow_mut(); + let module = modules.get(&pkgpath).expect(&msg).borrow_mut(); + module.add_global(tpe, Some(AddressSpace::Generic), name) + } else { + self.module + .add_global(tpe, Some(AddressSpace::Generic), name) + }; + global_var.set_alignment(GLOBAL_VAL_ALIGNMENT); + global_var.set_initializer(&tpe.const_zero()); + global_var.as_pointer_value().into() + } + /// Get the global runtime context pointer. + fn global_ctx_ptr(&self) -> Self::Value { + if self.no_link { + self.build_call(&ApiFunc::kclvm_context_current.name(), &[]) + } else { + let ctx_ptr = self + .module + .get_global(KCL_CONTEXT_VAR_NAME) + .expect(kcl_error::CONTEXT_VAR_NOT_FOUND_MSG) + .as_pointer_value(); + self.builder.build_load(ctx_ptr, "") + } + } +} + +impl<'ctx> ValueCalculationMethods for LLVMCodeGenContext<'ctx> { + /// lhs + rhs + fn add(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_op_add.name(), &[lhs, rhs]) + } + /// lhs - rhs + fn sub(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_op_sub.name(), &[lhs, rhs]) + } + /// lhs * rhs + fn mul(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_op_mul.name(), &[lhs, rhs]) + } + /// lhs / rhs + fn div(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_op_div.name(), &[lhs, rhs]) + } + /// lhs // rhs + fn floor_div(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_op_floor_div.name(), &[lhs, rhs]) + } + /// lhs % rhs + fn r#mod(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_op_mod.name(), &[lhs, rhs]) + } + /// lhs ** rhs + fn pow(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_op_pow.name(), &[lhs, rhs]) + } + /// lhs << rhs + fn bit_lshift(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_op_bit_lshift.name(), &[lhs, rhs]) + } + /// lhs >> rhs + fn bit_rshift(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_op_bit_rshift.name(), &[lhs, rhs]) + } + /// lhs & rhs + fn bit_and(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_op_bit_and.name(), &[lhs, rhs]) + } + /// lhs | rhs + fn bit_or(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_op_bit_or.name(), &[lhs, rhs]) + } + /// lhs ^ rhs + fn bit_xor(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_op_bit_xor.name(), &[lhs, rhs]) + } + /// lhs and rhs + fn logic_and(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_logic_and.name(), &[lhs, rhs]) + } + /// lhs or rhs + fn logic_or(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_logic_or.name(), &[lhs, rhs]) + } + /// lhs == rhs + fn cmp_equal_to(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_cmp_equal_to.name(), &[lhs, rhs]) + } + /// lhs != rhs + fn cmp_not_equal_to(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_cmp_not_equal_to.name(), &[lhs, rhs]) + } + /// lhs > rhs + fn cmp_greater_than(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_cmp_greater_than.name(), &[lhs, rhs]) + } + /// lhs >= rhs + fn cmp_greater_than_or_equal(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call( + &ApiFunc::kclvm_value_cmp_greater_than_or_equal.name(), + &[lhs, rhs], + ) + } + /// lhs < rhs + fn cmp_less_than(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_cmp_less_than.name(), &[lhs, rhs]) + } + /// lhs <= rhs + fn cmp_less_than_or_equal(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call( + &ApiFunc::kclvm_value_cmp_less_than_or_equal.name(), + &[lhs, rhs], + ) + } + /// lhs as rhs + fn r#as(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_as.name(), &[lhs, rhs]) + } + /// lhs is rhs + fn is(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_is.name(), &[lhs, rhs]) + } + /// lhs is not rhs + fn is_not(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_is_not.name(), &[lhs, rhs]) + } + /// lhs in rhs + fn r#in(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_in.name(), &[lhs, rhs]) + } + /// lhs not in rhs + fn not_in(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_not_in.name(), &[lhs, rhs]) + } +} + +impl<'ctx> DerivedValueCalculationMethods for LLVMCodeGenContext<'ctx> { + /// Value subscript a[b] + #[inline] + fn value_subscript(&self, value: Self::Value, item: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_subscr.name(), &[value, item]) + } + /// Value is truth function, return i1 value. + fn value_is_truthy(&self, value: Self::Value) -> Self::Value { + let is_truth = self + .build_call(&ApiFunc::kclvm_value_is_truthy.name(), &[value]) + .into_int_value(); + self.builder + .build_int_compare(IntPredicate::NE, is_truth, self.native_i8_zero(), "") + .into() + } + /// Value deep copy + #[inline] + fn value_deep_copy(&self, value: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_value_deep_copy.name(), &[value]) + } + /// value_union unions two collection elements. + #[inline] + fn value_union(&self, lhs: Self::Value, rhs: Self::Value) { + self.build_void_call(&ApiFunc::kclvm_value_union.name(), &[lhs, rhs]); + } + // List get the item using the index. + #[inline] + fn list_get(&self, list: Self::Value, index: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_list_get.name(), &[list, index]) + } + // List set the item using the index. + #[inline] + fn list_set(&self, list: Self::Value, index: Self::Value, value: Self::Value) { + self.build_void_call(&ApiFunc::kclvm_list_set.name(), &[list, index, value]) + } + // List slice. + #[inline] + fn list_slice( + &self, + list: Self::Value, + start: Self::Value, + stop: Self::Value, + step: Self::Value, + ) -> Self::Value { + self.build_call( + &ApiFunc::kclvm_value_slice.name(), + &[list, start, stop, step], + ) + } + /// Append a item into the list. + #[inline] + fn list_append(&self, list: Self::Value, item: Self::Value) { + self.build_void_call(&ApiFunc::kclvm_list_append.name(), &[list, item]) + } + /// Append a list item and unpack it into the list. + #[inline] + fn list_append_unpack(&self, list: Self::Value, item: Self::Value) { + self.build_void_call(&ApiFunc::kclvm_list_append_unpack.name(), &[list, item]); + } + /// Runtime list value pop + #[inline] + fn list_pop(&self, list: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_list_pop.name(), &[list]) + } + /// Runtime list pop the first value + #[inline] + fn list_pop_first(&self, list: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_list_pop_first.name(), &[list]) + } + /// List clear value. + #[inline] + fn list_clear(&self, list: Self::Value) { + self.build_void_call(&ApiFunc::kclvm_list_clear.name(), &[list]) + } + /// Return number of occurrences of the list value. + #[inline] + fn list_count(&self, list: Self::Value, item: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_list_count.name(), &[list, item]) + } + /// Return first index of the list value. Panic if the value is not present. + #[inline] + fn list_find(&self, list: Self::Value, item: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_list_find.name(), &[list, item]) + } + /// Insert object before index of the list value. + #[inline] + fn list_insert(&self, list: Self::Value, index: Self::Value, value: Self::Value) { + self.build_void_call(&ApiFunc::kclvm_list_insert.name(), &[list, index, value]) + } + /// List length. + #[inline] + fn list_len(&self, list: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_list_len.name(), &[list]) + } + /// Dict get the value of the key. + #[inline] + fn dict_get(&self, dict: Self::Value, key: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_dict_get_value.name(), &[dict, key]) + } + /// Dict set the value of the key. + #[inline] + fn dict_set(&self, dict: Self::Value, key: Self::Value, value: Self::Value) { + self.build_void_call(&ApiFunc::kclvm_dict_set_value.name(), &[dict, key, value]) + } + /// Return all dict keys. + #[inline] + fn dict_keys(&self, dict: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_dict_keys.name(), &[dict]) + } + /// Return all dict values. + #[inline] + fn dict_values(&self, dict: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_dict_values.name(), &[dict]) + } + /// Dict clear value. + #[inline] + fn dict_clear(&self, dict: Self::Value) { + self.build_void_call(&ApiFunc::kclvm_dict_insert_value.name(), &[dict]) + } + /// Dict pop the value of the key. + #[inline] + fn dict_pop(&self, dict: Self::Value, key: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_dict_remove.name(), &[dict, key]) + } + /// Dict length. + #[inline] + fn dict_len(&self, dict: Self::Value) -> Self::Value { + self.build_call(&ApiFunc::kclvm_dict_len.name(), &[dict]) + } + /// Insert a dict entry including key, value, op and insert_index into the dict, + /// and the type of key is `&str` + #[inline] + fn dict_insert( + &self, + dict: Self::Value, + key: &str, + value: Self::Value, + op: i32, + insert_index: i32, + ) { + let name = self.native_global_string(key, "").into(); + let op = self.native_int_value(op); + let insert_index = self.native_int_value(insert_index); + self.build_void_call( + &ApiFunc::kclvm_dict_insert.name(), + &[dict, name, value, op, insert_index], + ); + } + + /// Insert a dict entry including key, value, op and insert_index into the dict. + /// and the type of key is `Self::Value` + #[inline] + fn dict_insert_with_key_value( + &self, + dict: Self::Value, + key: Self::Value, + value: Self::Value, + op: i32, + insert_index: i32, + ) { + let op = self.native_int_value(op); + let insert_index = self.native_int_value(insert_index); + self.build_void_call( + &ApiFunc::kclvm_dict_insert_value.name(), + &[dict, key, value, op, insert_index], + ); + } +} + +impl<'ctx> ValueCodeGen for LLVMCodeGenContext<'ctx> {} + +/* Type methods */ + +impl<'ctx> BaseTypeMethods for LLVMCodeGenContext<'ctx> { + /// Native i8 type + fn i8_type(&self) -> Self::Type { + self.context.i8_type().into() + } + /// Native i16 type + fn i16_type(&self) -> Self::Type { + self.context.i16_type().into() + } + /// Native i32 type + fn i32_type(&self) -> Self::Type { + self.context.i32_type().into() + } + /// Native i64 type + fn i64_type(&self) -> Self::Type { + self.context.i64_type().into() + } + /// Native i128 type + fn i128_type(&self) -> Self::Type { + self.context.i128_type().into() + } + /// Native f32 type + fn f32_type(&self) -> Self::Type { + self.context.f32_type().into() + } + /// Native f64 type + fn f64_type(&self) -> Self::Type { + self.context.f64_type().into() + } + /// Native struct type. + #[inline] + fn struct_type(&self, els: &[Self::Type], packed: bool) -> Self::Type { + self.context.struct_type(els, packed).into() + } + /// Native pointer type of `ty`. + #[inline] + fn ptr_type_to(&self, ty: Self::Type) -> Self::Type { + self.ptr_type_to_ext(ty, crate::codegen::abi::AddressSpace::DATA) + } + /// Native pointer type of `ty` with the address space. + #[inline] + fn ptr_type_to_ext( + &self, + ty: Self::Type, + address_space: crate::codegen::abi::AddressSpace, + ) -> Self::Type { + let address_space = + AddressSpace::try_from(address_space.0).expect(kcl_error::INTERNAL_ERROR_MSG); + let ptr_type = match ty { + BasicTypeEnum::ArrayType(a) => a.ptr_type(address_space), + BasicTypeEnum::FloatType(f) => f.ptr_type(address_space), + BasicTypeEnum::IntType(i) => i.ptr_type(address_space), + BasicTypeEnum::PointerType(p) => p.ptr_type(address_space), + BasicTypeEnum::StructType(s) => s.ptr_type(address_space), + BasicTypeEnum::VectorType(v) => v.ptr_type(address_space), + }; + ptr_type.into() + } + /// Native array element type. + #[inline] + fn element_type(&self, ty: Self::Type) -> Self::Type { + match ty { + BasicTypeEnum::ArrayType(a) => a.get_element_type(), + BasicTypeEnum::VectorType(v) => v.get_element_type(), + other => panic!("element_type called on unsupported type {:?}", other), + } + } + /// Returns the number of elements in `self` if it is a LLVM vector type. + #[inline] + fn vector_length(&self, ty: Self::Type) -> usize { + ty.into_vector_type().get_size() as usize + } + /// Retrieves the bit width of the float type `self`. + #[inline] + fn float_width(&self, ty: Self::Type) -> usize { + let ty_str = format!("{:?}", ty.into_float_type()); + for (float_ty, float_width) in FLOAT_TYPE_WIDTH_MAPPING.into_iter() { + if ty_str.contains(float_ty) { + return *float_width; + } + } + panic!("float_width called on unsupported type {:?}", ty); + } + /// Retrieves the bit width of the integer type `self`. + #[inline] + fn int_width(&self, ty: Self::Type) -> usize { + ty.into_int_type().get_bit_width() as usize + } + /// Get the value type. + #[inline] + fn val_type(&self, v: Self::Value) -> Self::Type { + v.get_type() + } + /// Native function type + #[inline] + fn function_let(&self, args: &[Self::Type], ret: Self::Type) -> Self::FunctionLet { + let args: Vec = args.iter().map(|arg| (*arg).into()).collect(); + ret.fn_type(&args, false) + } +} + +impl<'ctx> DerivedTypeMethods for LLVMCodeGenContext<'ctx> { + /// Lookup type by the type name. + #[inline] + fn get_intrinsic_type(&self, name: &str) -> Self::Type { + self.module + .get_struct_type(name) + .expect(kcl_error::VALUE_TYPE_NOT_FOUND_MSG) + .into() + } +} + +impl<'ctx> TypeCodeGen for LLVMCodeGenContext<'ctx> {} + +impl<'ctx> ProgramCodeGen for LLVMCodeGenContext<'ctx> { + /// Current package path + fn current_pkgpath(&self) -> String { + self.pkgpath_stack + .borrow_mut() + .last() + .expect(kcl_error::INTERNAL_ERROR_MSG) + .to_string() + } + + /// Current filename + fn current_filename(&self) -> String { + self.filename_stack + .borrow_mut() + .last() + .expect(kcl_error::INTERNAL_ERROR_MSG) + .to_string() + } + /// Init a scope named `pkgpath` with all builtin functions + fn init_scope(&self, pkgpath: &str) { + { + let mut pkg_scopes = self.pkg_scopes.borrow_mut(); + if pkg_scopes.contains_key(pkgpath) { + return; + } + let scopes = vec![Rc::new(Scope { + variables: RefCell::new(IndexMap::default()), + closures: RefCell::new(IndexMap::default()), + })]; + pkg_scopes.insert(String::from(pkgpath), scopes); + } + let msg = format!("pkgpath {} is not found", pkgpath); + // Init all global types including schema and rule + let module_list: &Vec = if self.program.pkgs.contains_key(pkgpath) { + self.program.pkgs.get(pkgpath).expect(&msg) + } else if pkgpath.starts_with(kclvm::PKG_PATH_PREFIX) + && self.program.pkgs.contains_key(&pkgpath[1..]) + { + self.program + .pkgs + .get(&pkgpath[1..]) + .expect(kcl_error::INTERNAL_ERROR_MSG) + } else { + panic!("pkgpath {} not found", pkgpath); + }; + for module in module_list { + for stmt in &module.body { + let name = match &stmt.node { + ast::Stmt::Schema(schema_stmt) => schema_stmt.name.node.clone(), + ast::Stmt::Rule(rule_stmt) => rule_stmt.name.node.clone(), + _ => "".to_string(), + }; + if !name.is_empty() { + let name = name.as_str(); + let var_name = format!("${}.${}", pkgpath_without_prefix!(pkgpath), name); + let global_var_ptr = self.new_global_kcl_value_ptr(&var_name); + self.add_variable(name, global_var_ptr); + } + } + } + // Init all builtin functions + for symbol in builtin::BUILTIN_FUNCTION_NAMES { + let function_name = + format!("{}_{}", builtin::KCL_BUILTIN_FUNCTION_MANGLE_PREFIX, symbol); + let function_value = self.builtin_function_value(function_name.as_str()); + let builtin_function_name = format!( + "{}_{}_{}", + builtin::BUILTIN_FUNCTION_PREFIX, + pkgpath_without_prefix!(pkgpath), + function_name + ); + let global_var_ptr = self.new_global_kcl_value_ptr(&builtin_function_name); + self.builder.build_store(global_var_ptr, function_value); + self.add_variable(symbol, global_var_ptr); + } + self.enter_scope(); + } + + /// Get the scope level + fn scope_level(&self) -> usize { + let current_pkgpath = self.current_pkgpath(); + let pkg_scopes = self.pkg_scopes.borrow_mut(); + let msg = format!("pkgpath {} is not found", current_pkgpath); + let scopes = pkg_scopes.get(¤t_pkgpath).expect(&msg); + // Sub the builtin global scope + scopes.len() - 1 + } + + /// Enter scope + fn enter_scope(&self) { + let current_pkgpath = self.current_pkgpath(); + let mut pkg_scopes = self.pkg_scopes.borrow_mut(); + let msg = format!("pkgpath {} is not found", current_pkgpath); + let scopes = pkg_scopes.get_mut(¤t_pkgpath).expect(&msg); + let scope = Rc::new(Scope { + variables: RefCell::new(IndexMap::default()), + closures: RefCell::new(IndexMap::default()), + }); + scopes.push(scope); + } + + /// Leave scope + fn leave_scope(&self) { + let current_pkgpath = self.current_pkgpath(); + let mut pkg_scopes = self.pkg_scopes.borrow_mut(); + let msg = format!("pkgpath {} is not found", current_pkgpath); + let scopes = pkg_scopes.get_mut(¤t_pkgpath).expect(&msg); + scopes.pop(); + } +} + +impl<'ctx> CodeGenContext for LLVMCodeGenContext<'ctx> { + /// Generate LLVM IR of ast module. + fn emit(&self, opt: &EmitOptions) -> Result<(), Box> { + self.emit_code(opt) + } +} + +impl<'ctx> LLVMCodeGenContext<'ctx> { + /// New aa LLVMCodeGenContext using the LLVM Context and AST Program + pub fn new( + context: &'ctx Context, + module: Module<'ctx>, + program: &'ctx ast::Program, + import_names: IndexMap>, + no_link: bool, + ) -> LLVMCodeGenContext<'ctx> { + LLVMCodeGenContext { + context, + module, + builder: context.create_builder(), + program, + pkg_scopes: RefCell::new(HashMap::new()), + functions: RefCell::new(vec![]), + imported: RefCell::new(HashSet::new()), + local_vars: RefCell::new(HashSet::new()), + schema_stack: RefCell::new(vec![]), + lambda_stack: RefCell::new(vec![false]), + pkgpath_stack: RefCell::new(vec![String::from(MAIN_PKG_PATH)]), + filename_stack: RefCell::new(vec![String::from("")]), + target_vars: RefCell::new(vec![String::from("")]), + global_strings: RefCell::new(IndexMap::default()), + global_vars: RefCell::new(IndexMap::default()), + current_filename: RefCell::new(String::new()), + current_line: RefCell::new(0), + handler: RefCell::new(Handler::default()), + backtrack_meta: RefCell::new(None), + import_names, + no_link, + modules: RefCell::new(HashMap::new()), + } + } + + /// Generate LLVM IR of ast module. + pub(crate) fn emit_code( + self: &LLVMCodeGenContext<'ctx>, + opt: &EmitOptions, + ) -> Result<(), Box> { + let tpe = self.value_ptr_type().into_pointer_type(); + let void_type = self.context.void_type(); + let context_ptr_type = self.context_ptr_type(); + let fn_type = tpe.fn_type(&[context_ptr_type.into()], false); + let void_fn_type = void_type.fn_type(&[context_ptr_type.into()], false); + let has_main_pkg = self.program.pkgs.contains_key(MAIN_PKG_PATH); + let function = if self.no_link { + let mut modules = self.modules.borrow_mut(); + let name = if has_main_pkg { + MAIN_PKG_PATH.to_string() + } else { + assert!(self.program.pkgs.len() == 1); + format!( + "{}{}", + kclvm::PKG_PATH_PREFIX, + self.program + .pkgs + .keys() + .next() + .expect(kcl_error::INTERNAL_ERROR_MSG) + ) + }; + let module = self.context.create_module(name.as_str()); + let function_name = if has_main_pkg { + MODULE_NAME.to_string() + } else { + format!( + "${}.{}", + pkgpath_without_prefix!(name), + PKG_INIT_FUNCTION_SUFFIX + ) + }; + let function = module.add_function( + // Function name + function_name.as_str(), + // Function type + if has_main_pkg { fn_type } else { void_fn_type }, + None, + ); + modules.insert(name.to_string(), RefCell::new(module)); + function + } else { + self.module.add_function( + // Function name + MODULE_NAME, + // Function type + fn_type, + None, + ) + }; + self.push_function(function); + // Add a block named entry into the function + let basic_block = self.append_block(ENTRY_NAME); + // Set position to the basic block + self.builder.position_at_end(basic_block); + // Get the runtime context + let ctx_value = function + .get_first_param() + .expect(kcl_error::INTERNAL_ERROR_MSG); + if self.no_link && !has_main_pkg { + for pkgpath in self.program.pkgs.keys() { + let pkgpath = format!("{}{}", kclvm::PKG_PATH_PREFIX, pkgpath); + self.pkgpath_stack.borrow_mut().push(pkgpath.clone()); + } + } + if !self.import_names.is_empty() { + let import_names = self.dict_value(); + for (k, v) in &self.import_names { + let map = self.dict_value(); + for (pkgname, pkgpath) in v { + self.dict_insert_override_item( + map, + pkgname, + self.string_value(&format!("@{}", pkgpath)), + ); + } + self.dict_insert_override_item(import_names, k, map); + } + self.build_void_call( + &ApiFunc::kclvm_context_set_import_names.name(), + &[ctx_value, import_names], + ); + } + // Store the runtime context to global + if !self.no_link { + let global_ctx = self.module.add_global( + context_ptr_type, + Some(AddressSpace::Generic), + KCL_CONTEXT_VAR_NAME, + ); + global_ctx.set_alignment(GLOBAL_VAL_ALIGNMENT); + global_ctx.set_initializer(&context_ptr_type.const_zero()); + self.builder + .build_store(global_ctx.as_pointer_value(), ctx_value); + } + if self.no_link && !has_main_pkg { + // When compiling a pkgpath separately, only one pkgpath is required in the AST Program + assert!(self.program.pkgs.len() == 1); + // pkgs may not contains main pkg in no link mode + for (pkgpath, modules) in &self.program.pkgs { + let pkgpath = format!("{}{}", kclvm::PKG_PATH_PREFIX, pkgpath); + self.pkgpath_stack.borrow_mut().push(pkgpath.clone()); + // Init all builtin functions. + self.init_scope(pkgpath.as_str()); + // Compile the ast module in the pkgpath. + for ast_module in modules { + { + self.filename_stack + .borrow_mut() + .push(ast_module.filename.clone()); + } + self.compile_module_import_and_types(ast_module) + } + for ast_module in modules { + { + self.filename_stack + .borrow_mut() + .push(ast_module.filename.clone()); + } + self.walk_stmts_except_import(&ast_module.body) + .expect(kcl_error::COMPILE_ERROR_MSG); + } + } + self.ret_void(); + } else { + // Init scope and all builtin functions + self.init_scope(MAIN_PKG_PATH); + let main_pkg_modules = self + .program + .pkgs + .get(MAIN_PKG_PATH) + .expect(kcl_error::INTERNAL_ERROR_MSG); + // Compile the AST Program to LLVM IR + for ast_module in main_pkg_modules { + { + self.filename_stack + .borrow_mut() + .push(ast_module.filename.clone()); + } + self.walk_module(ast_module) + .expect(kcl_error::COMPILE_ERROR_MSG); + } + // Get the JSON string including all global variables + let json_str_value = self.globals_to_json_str(); + // Build a return in the current block + self.pop_function(); + self.builder + .build_return(Some(&json_str_value.into_pointer_value())); + } + if let Some(path_str) = &opt.emit_path { + let path = std::path::Path::new(&path_str); + if opt.no_link { + let modules = self.modules.borrow_mut(); + for (index, (_, module)) in modules.iter().enumerate() { + let path = if modules.len() == 1 { + format!("{}.ll", path_str) + } else { + format!("{}_{}.ll", path_str, index) + }; + let path = std::path::Path::new(&path); + module + .borrow_mut() + .print_to_file(path) + .expect(kcl_error::CODE_GEN_ERROR_MSG); + } + } else { + self.module + .print_to_file(path) + .expect(kcl_error::CODE_GEN_ERROR_MSG); + } + } + Ok(()) + } +} + +impl<'ctx> LLVMCodeGenContext<'ctx> { + /// Get compiler default ok result + #[inline] + pub fn ok_result(&self) -> CompileResult<'ctx> { + let i32_type = self.context.i32_type(); + Ok(i32_type.const_int(0u64, false).into()) + } + + /// Build a void function call + #[inline] + pub fn build_void_call(&self, name: &str, args: &[BasicValueEnum]) { + let args: Vec = args.iter().map(|arg| (*arg).into()).collect(); + self.builder + .build_call(self.lookup_function(name), &args, ""); + } + + /// Build a function call with the return value + #[inline] + pub fn build_call(&self, name: &str, args: &[BasicValueEnum<'ctx>]) -> BasicValueEnum<'ctx> { + let args: Vec = args.iter().map(|arg| (*arg).into()).collect(); + self.builder + .build_call(self.lookup_function(name), &args, "") + .try_as_basic_value() + .left() + .expect(kcl_error::FUNCTION_RETURN_VALUE_NOT_FOUND_MSG) + } + + /// Creates global string in the llvm module with initializer + pub fn native_global_string(&self, value: &str, name: &str) -> PointerValue<'ctx> { + let mut global_string_maps = self.global_strings.borrow_mut(); + let pkgpath = self.current_pkgpath(); + let str_name = format!("${}_{}_str", pkgpath_without_prefix!(pkgpath), name); + if !global_string_maps.contains_key(&pkgpath) { + global_string_maps.insert(pkgpath.clone(), IndexMap::default()); + } + let msg = format!("pkgpath {} is not found", pkgpath); + let global_strings = global_string_maps.get_mut(&pkgpath).expect(&msg); + if let Some(ptr) = global_strings.get(value) { + *ptr + } else { + let gv = unsafe { self.builder.build_global_string(value, &str_name) }; + let ptr = self + .ptr_cast( + gv.as_pointer_value().into(), + self.ptr_type_to(self.i8_type()), + ) + .into_pointer_value(); + global_strings.insert(value.to_string(), ptr); + ptr + } + } + + /// Creates global string value in the llvm module with initializer + pub fn native_global_string_value(&self, value: &str) -> BasicValueEnum<'ctx> { + let pkgpath = self.current_pkgpath(); + let str_name = format!("${}_str", pkgpath_without_prefix!(pkgpath)); + self.native_global_string(value, &str_name).into() + } + + /// Get LLVM i8 zero value + pub fn native_i8_zero(&self) -> IntValue<'ctx> { + let i8_type = self.context.i8_type(); + i8_type.const_int(0u64, false) + } + + /// Get LLVM i8 zero value + pub fn native_i8(&self, v: i8) -> IntValue<'ctx> { + let i8_type = self.context.i8_type(); + i8_type.const_int(v as u64, false) + } + + /// Construct a LLVM int value using i32 + pub fn native_int_value(&self, v: i32) -> BasicValueEnum<'ctx> { + let i32_type = self.context.i32_type(); + i32_type.const_int(v as u64, false).into() + } + + /// Construct a global value pointer named `name` + pub fn new_global_kcl_value_ptr(&self, name: &str) -> PointerValue<'ctx> { + let tpe = self.value_ptr_type(); + // Builtin function value is a global one + let global_var = if self.no_link { + let pkgpath = self.current_pkgpath(); + let msg = format!("pkgpath {} is not found", pkgpath); + let modules = self.modules.borrow_mut(); + let module = modules.get(&pkgpath).expect(&msg).borrow_mut(); + module.add_global(tpe, Some(AddressSpace::Generic), name) + } else { + self.module + .add_global(tpe, Some(AddressSpace::Generic), name) + }; + global_var.set_alignment(GLOBAL_VAL_ALIGNMENT); + global_var.set_initializer(&tpe.const_zero()); + global_var.as_pointer_value() + } + + /// Append a variable into the scope + pub fn add_variable(&self, name: &str, pointer: PointerValue<'ctx>) { + let current_pkgpath = self.current_pkgpath(); + let mut pkg_scopes = self.pkg_scopes.borrow_mut(); + let msg = format!("pkgpath {} is not found", current_pkgpath); + let scopes = pkg_scopes.get_mut(¤t_pkgpath).expect(&msg); + if let Some(last) = scopes.last_mut() { + let mut variables = last.variables.borrow_mut(); + if !variables.contains_key(name) { + variables.insert(name.to_string(), pointer); + } + } + } + + /// Store the variable named `name` with `value` from the current scope, return false when not found + pub fn store_variable_in_current_scope(&self, name: &str, value: BasicValueEnum<'ctx>) -> bool { + // Find argument name in the scope + let current_pkgpath = self.current_pkgpath(); + let mut pkg_scopes = self.pkg_scopes.borrow_mut(); + let msg = format!("pkgpath {} is not found", current_pkgpath); + let scopes = pkg_scopes.get_mut(¤t_pkgpath).expect(&msg); + let index = scopes.len() - 1; + let variables_mut = scopes[index].variables.borrow_mut(); + if let Some(var) = variables_mut.get(&name.to_string()) { + self.builder.build_store(*var, value); + return true; + } + false + } + + /// Store the variable named `name` with `value` from the scope, return false when not found + pub fn store_variable(&self, name: &str, value: BasicValueEnum<'ctx>) -> bool { + // Find argument name in the scope + let current_pkgpath = self.current_pkgpath(); + let mut pkg_scopes = self.pkg_scopes.borrow_mut(); + let msg = format!("pkgpath {} is not found", current_pkgpath); + let scopes = pkg_scopes.get_mut(¤t_pkgpath).expect(&msg); + for i in 0..scopes.len() { + let index = scopes.len() - i - 1; + let variables_mut = scopes[index].variables.borrow_mut(); + if let Some(var) = variables_mut.get(&name.to_string()) { + self.builder.build_store(*var, value); + return true; + } + } + false + } + + /// Resolve variable in scope, return false when not found + pub fn resolve_variable(&self, name: &str) -> bool { + // Find argument name in the scope + let current_pkgpath = self.current_pkgpath(); + let mut pkg_scopes = self.pkg_scopes.borrow_mut(); + let msg = format!("pkgpath {} is not found", current_pkgpath); + let scopes = pkg_scopes.get_mut(¤t_pkgpath).expect(&msg); + let mut existed = false; + for i in 0..scopes.len() { + let index = scopes.len() - i - 1; + let variables_mut = scopes[index].variables.borrow_mut(); + if variables_mut.get(&name.to_string()).is_some() { + existed = true; + break; + } + } + existed + } + + /// Append a variable or update the existed variable + pub fn add_or_update_global_variable(&self, name: &str, value: BasicValueEnum<'ctx>) { + // Find argument name in the scope + let current_pkgpath = self.current_pkgpath(); + let mut pkg_scopes = self.pkg_scopes.borrow_mut(); + let msg = format!("pkgpath {} is not found", current_pkgpath); + let scopes = pkg_scopes.get_mut(¤t_pkgpath).expect(&msg); + let mut existed = false; + if let Some(last) = scopes.last_mut() { + let variables_mut = last.variables.borrow_mut(); + if let Some(var) = variables_mut.get(&name.to_string()) { + self.builder.build_store(*var, value); + existed = true; + } + } + if !existed { + if let Some(last) = scopes.last_mut() { + let mut variables = last.variables.borrow_mut(); + let pkgpath = self.current_pkgpath(); + let var_name = format!("${}.${}", pkgpath_without_prefix!(pkgpath), name); + let pointer = self.new_global_kcl_value_ptr(&var_name); + self.builder.build_store(pointer, value); + if !variables.contains_key(name) { + variables.insert(name.to_string(), pointer); + } + } + } + } + + /// Get the variable value named `name` from the scope, return Err when not found + pub fn get_variable(&self, name: &str) -> CompileResult<'ctx> { + let current_pkgpath = self.current_pkgpath(); + self.get_variable_in_pkgpath(name, ¤t_pkgpath) + } + + /// Get the variable value named `name` from the scope, return Err when not found + pub fn get_variable_in_schema(&self, name: &str) -> CompileResult<'ctx> { + let schema_value = self + .get_variable(value::SCHEMA_SELF_NAME) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let cal_map = self + .get_variable(value::SCHEMA_CAL_MAP) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let string_ptr_value = self.native_global_string(name, "").into(); + let cal_map_has_key = self + .build_call( + &ApiFunc::kclvm_dict_has_value.name(), + &[cal_map, string_ptr_value], + ) + .into_int_value(); + let schema_has_key = self + .build_call( + &ApiFunc::kclvm_dict_has_value.name(), + &[schema_value, string_ptr_value], + ) + .into_int_value(); + // has_key = cal_map_has_key or schema_has_key + let has_key = self + .builder + .build_int_add(cal_map_has_key, schema_has_key, ""); + let has_key = + self.builder + .build_int_compare(IntPredicate::NE, has_key, self.native_i8_zero(), ""); + let then_block = self.append_block(""); + let else_block = self.append_block(""); + let end_block = self.append_block(""); + self.builder + .build_conditional_branch(has_key, then_block, else_block); + self.builder.position_at_end(then_block); + let target_attr = self + .target_vars + .borrow_mut() + .last() + .expect(kcl_error::INTERNAL_ERROR_MSG) + .clone(); + let target_attr = self.native_global_string_value(&target_attr); + let config_attr_value = { + let config = self + .get_variable(value::SCHEMA_CONFIG_NAME) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let config_meta = self + .get_variable(value::SCHEMA_CONFIG_META_NAME) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let backtrack_level_map = self + .get_variable(value::BACKTRACK_LEVEL_MAP) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let backtrack_cache = self + .get_variable(value::BACKTRACK_CACHE) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let args = self + .get_variable(value::SCHEMA_ARGS) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let kwargs = self + .get_variable(value::SCHEMA_KWARGS) + .expect(kcl_error::INTERNAL_ERROR_MSG); + self.build_call( + &ApiFunc::kclvm_schema_get_value.name(), + &[ + schema_value, + string_ptr_value, + config, + config_meta, + cal_map, + target_attr, + backtrack_level_map, + backtrack_cache, + args, + kwargs, + ], + ) + }; + self.br(end_block); + self.builder.position_at_end(else_block); + let current_pkgpath = self.current_pkgpath(); + let result = self.get_variable_in_pkgpath(name, ¤t_pkgpath); + let value = match result { + Ok(v) => v, + Err(_) => self.undefined_value(), + }; + self.br(end_block); + self.builder.position_at_end(end_block); + let tpe = self.value_ptr_type(); + let phi = self.builder.build_phi(tpe, ""); + phi.add_incoming(&[(&value, else_block), (&config_attr_value, then_block)]); + let value = phi.as_basic_value(); + Ok(value) + } + + /// Get the variable value named `name` from the scope named `pkgpath`, return Err when not found + pub fn get_variable_in_pkgpath(&self, name: &str, pkgpath: &str) -> CompileResult<'ctx> { + let pkg_scopes = self.pkg_scopes.borrow_mut(); + let pkgpath = if !pkgpath.starts_with(kclvm::PKG_PATH_PREFIX) && pkgpath != MAIN_PKG_PATH { + format!("{}{}", kclvm::PKG_PATH_PREFIX, pkgpath) + } else { + pkgpath.to_string() + }; + let mut result = Err(kcl_error::KCLError { + message: format!("name '{}' is not defined", name), + ty: kcl_error::KCLErrorType::Compile, + }); + // System module + if builtin::STANDARD_SYSTEM_MODULE_NAMES_WITH_AT.contains(&pkgpath.as_str()) { + let pkgpath = &pkgpath[1..]; + let mangle_func_name = format!( + "{}{}_{}", + builtin::KCL_SYSTEM_MODULE_MANGLE_PREFIX, + pkgpath_without_prefix!(pkgpath), + name + ); + let value = if pkgpath == builtin::system_module::UNITS + && builtin::system_module::UNITS_FIELD_NAMES.contains(&name) + { + let value_float: f64 = kclvm::f64_unit_value(name); + let value_int: u64 = kclvm::u64_unit_value(name); + if value_int != 1 { + self.int_value(value_int as i64) + } else { + self.float_value(value_float) + } + } else { + let function = self.lookup_function(&mangle_func_name); + // Convert the function to a i64 pointer to store it into the function value. + let lambda_fn_ptr = self.builder.build_bitcast( + function.as_global_value().as_pointer_value(), + self.context.i64_type().ptr_type(AddressSpace::Generic), + "", + ); + let name = self.native_global_string("", "").into(); + let none_value = self.none_value(); + self.build_call( + &ApiFunc::kclvm_value_Function.name(), + &[lambda_fn_ptr, none_value, name], + ) + }; + Ok(value) + } + // Plugin pkgpath + else if pkgpath.starts_with(plugin::PLUGIN_PREFIX_WITH_AT) { + let null_fn_ptr = self + .context + .i64_type() + .ptr_type(AddressSpace::Generic) + .const_zero() + .into(); + let name = format!("{}.{}", &pkgpath[1..], name); + let name = self.native_global_string(&name, "").into(); + let none_value = self.none_value(); + return Ok(self.build_call( + &ApiFunc::kclvm_value_Function.name(), + &[null_fn_ptr, none_value, name], + )); + // User pkgpath + } else { + let scopes = pkg_scopes + .get(&pkgpath) + .unwrap_or_else(|| panic!("package {} is not found", pkgpath)); + // Scopes 0 is builtin scope, Scopes 1 is the global scope, Scopes 2~ are the local scopes + let scopes_len = scopes.len(); + let last_scopes = scopes.last().expect(kcl_error::INTERNAL_ERROR_MSG); + let mut closures_mut = last_scopes.closures.borrow_mut(); + for i in 0..scopes_len { + let index = scopes_len - i - 1; + let variables_mut = scopes[index].variables.borrow_mut(); + if let Some(var) = variables_mut.get(&name.to_string()) { + // Closure vars, 2 denotes the builtin scope and the global scope + let value = if i >= 1 && i < scopes_len - 2 { + closures_mut.insert(name.to_string(), *var); + let variables = last_scopes.variables.borrow(); + let ptr = variables.get(value::LAMBDA_CLOSURE); + // Lambda closure + match ptr { + Some(ptr) => { + let closure_map = self.builder.build_load(*ptr, ""); + let string_ptr_value = self.native_global_string(name, "").into(); + self.build_call( + &ApiFunc::kclvm_dict_get_value.name(), + &[closure_map, string_ptr_value], + ) + } + // Nest comp for + None => self.builder.build_load(*var, name), + } + } else { + self.builder.build_load(*var, name) + }; + result = Ok(value); + break; + } + } + match result { + Ok(_) => result, + Err(ref err) => { + let is_in_schema = self.schema_stack.borrow().len() > 0; + if !is_in_schema { + let mut handler = self.handler.borrow_mut(); + handler.add_compile_error( + &err.message, + Position { + filename: self.current_filename().clone(), + line: self.current_line.borrow().clone(), + column: None, + }, + ); + handler.abort_if_errors() + } else { + result + } + } + } + } + } + + /// Get the variable value named `name` from the scope named `pkgpath`, return Err when not found + pub fn get_external_variable_in_pkgpath( + &self, + name: &str, + pkgpath: &str, + ) -> CompileResult<'ctx> { + let ext_pkgpath = + if !pkgpath.starts_with(kclvm::PKG_PATH_PREFIX) && pkgpath != kclvm::MAIN_PKG_PATH { + format!("{}{}", kclvm::PKG_PATH_PREFIX, pkgpath) + } else { + pkgpath.to_string() + }; + // System module or plugin module + if builtin::STANDARD_SYSTEM_MODULE_NAMES_WITH_AT.contains(&ext_pkgpath.as_str()) + || ext_pkgpath.starts_with(plugin::PLUGIN_PREFIX_WITH_AT) + { + return self.get_variable_in_pkgpath(name, pkgpath); + } + // User module external variable + let external_var_name = format!("${}.${}", pkgpath_without_prefix!(ext_pkgpath), name); + let current_pkgpath = self.current_pkgpath(); + let modules = self.modules.borrow_mut(); + let msg = format!("pkgpath {} is not found", current_pkgpath); + let module = modules.get(¤t_pkgpath).expect(&msg).borrow_mut(); + let tpe = self.value_ptr_type(); + let mut global_var_maps = self.global_vars.borrow_mut(); + let pkgpath = self.current_pkgpath(); + if !global_var_maps.contains_key(&pkgpath) { + global_var_maps.insert(pkgpath.clone(), IndexMap::default()); + } + // Add or update a external variable + let global_vars = global_var_maps.get_mut(&pkgpath).expect(&msg); + let ptr = if let Some(ptr) = global_vars.get(external_var_name.as_str()) { + *ptr + } else { + let global_var = + module.add_global(tpe, Some(AddressSpace::Generic), &external_var_name); + global_var.set_alignment(GLOBAL_VAL_ALIGNMENT); + global_var.set_linkage(Linkage::External); + let ptr = global_var.as_pointer_value(); + global_vars.insert(external_var_name, ptr); + ptr + }; + let value = self.builder.build_load(ptr, ""); + Ok(value) + } + + /// Get closure dict in the current scope. + pub(crate) fn get_closure_dict_in_current_scope(&self) -> BasicValueEnum<'ctx> { + let is_in_schema = self.schema_stack.borrow().len() > 0; + // Get closures in the current scope + let dict_value = self.dict_value(); + { + let pkgpath = self.current_pkgpath(); + let pkgpath = if !pkgpath.starts_with(PKG_PATH_PREFIX) && pkgpath != MAIN_PKG_PATH { + format!("{}{}", PKG_PATH_PREFIX, pkgpath) + } else { + pkgpath + }; + let pkg_scopes = self.pkg_scopes.borrow_mut(); + let scopes = pkg_scopes + .get(&pkgpath) + .unwrap_or_else(|| panic!("package {} is not found", pkgpath)); + let closures_mut = scopes + .last() + .expect(kcl_error::INTERNAL_ERROR_MSG) + .closures + .borrow_mut(); + let variables_mut = scopes + .last() + .expect(kcl_error::INTERNAL_ERROR_MSG) + .variables + .borrow_mut(); + for (key, ptr) in &*closures_mut { + if variables_mut.contains_key(key) { + let value = self.builder.build_load(*ptr, ""); + self.dict_insert_override_item(dict_value, key.as_str(), value); + } + } + } + if is_in_schema { + let schema_value = self + .get_variable(value::SCHEMA_SELF_NAME) + .expect(kcl_error::INTERNAL_ERROR_MSG); + self.dict_insert_override_item(dict_value, value::SCHEMA_SELF_NAME, schema_value); + } + dict_value + } + + /// Push a function call frame into the function stack + #[inline] + pub fn push_function(&self, function: FunctionValue<'ctx>) { + self.functions.borrow_mut().push(Rc::new(function)); + } + + /// Pop a function from the function stack + #[inline] + pub fn pop_function(&self) { + self.functions.borrow_mut().pop(); + } + + /// Get the current function + #[inline] + pub fn current_function(&self) -> FunctionValue<'ctx> { + **self + .functions + .borrow() + .last() + .expect(kcl_error::INTERNAL_ERROR_MSG) + } + + /// Plan globals to a json string + pub fn globals_to_json_str(&self) -> BasicValueEnum<'ctx> { + let current_pkgpath = self.current_pkgpath(); + let mut pkg_scopes = self.pkg_scopes.borrow_mut(); + let msg = format!("pkgpath {} is not found", current_pkgpath); + let scopes = pkg_scopes.get_mut(¤t_pkgpath).expect(&msg); + let globals = scopes + .last_mut() + .expect(kcl_error::INTERNAL_ERROR_MSG) + .variables + .borrow_mut(); + let global_dict = self.dict_value(); + for (name, ptr) in globals.iter() { + // Omit private variables and function variables + if name.starts_with(kclvm::KCL_PRIVATE_VAR_PREFIX) { + continue; + } + let value = self.builder.build_load(*ptr, ""); + self.dict_safe_insert(global_dict, name.as_str(), value, 0, -1); + } + self.build_call(&ApiFunc::kclvm_value_plan_to_json.name(), &[global_dict]) + } + + /// Insert a dict entry including key, value, op and insert_index into the dict. + #[inline] + fn dict_safe_insert( + &self, + dict: BasicValueEnum<'ctx>, + key: &str, + value: BasicValueEnum<'ctx>, + op: i32, + insert_index: i32, + ) { + let name = self.native_global_string(key, "").into(); + let op = self.native_int_value(op); + let insert_index = self.native_int_value(insert_index); + self.build_void_call( + &ApiFunc::kclvm_dict_safe_insert.name(), + &[dict, name, value, op, insert_index], + ); + } + + /// Merge a dict entry including key, value, op and insert_index into the dict + /// without the idempotent check. + #[inline] + pub fn dict_merge( + &self, + dict: BasicValueEnum<'ctx>, + key: &str, + value: BasicValueEnum<'ctx>, + op: i32, + insert_index: i32, + ) { + let name = self.native_global_string(key, "").into(); + let op = self.native_int_value(op); + let insert_index = self.native_int_value(insert_index); + self.build_void_call( + &ApiFunc::kclvm_dict_merge.name(), + &[dict, name, value, op, insert_index], + ); + } + + /// default_dict(list) insert a key-value pair, and the value is a int pointer + #[inline] + pub fn default_collection_insert_int_pointer( + &self, + dict: BasicValueEnum<'ctx>, + key: &str, + value: BasicValueEnum<'ctx>, + ) { + let name = self.native_global_string(key, "").into(); + self.build_void_call( + ApiFunc::kclvm_default_collection_insert_int_pointer + .name() + .as_str(), + &[dict, name, value], + ); + } + + /// default_dict(list) insert a key-value pair + #[inline] + pub fn default_collection_insert_value( + &self, + dict: BasicValueEnum<'ctx>, + key: &str, + value: BasicValueEnum<'ctx>, + ) { + let name = self.native_global_string(key, "").into(); + self.build_void_call( + ApiFunc::kclvm_default_collection_insert_value + .name() + .as_str(), + &[dict, name, value], + ); + } +} diff --git a/kclvm/compiler/src/codegen/llvm/emit.rs b/kclvm/compiler/src/codegen/llvm/emit.rs new file mode 100644 index 000000000..89838f38b --- /dev/null +++ b/kclvm/compiler/src/codegen/llvm/emit.rs @@ -0,0 +1,39 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use indexmap::IndexMap; +use inkwell::module::Module; +use inkwell::{context::Context, memory_buffer::MemoryBuffer}; +use kclvm_ast::ast; +use std::error; + +use crate::codegen::{EmitOptions, MODULE_NAME}; + +use super::context::LLVMCodeGenContext; + +static RUNTIME_LLVM_BC: &[u8] = include_bytes!("../../../../runtime/src/_kclvm.bc"); + +/// Load runtime libraries and parse it to a module. +fn load_runtime(context: &'_ Context) -> Module<'_> { + let memory = MemoryBuffer::create_from_memory_range(RUNTIME_LLVM_BC, MODULE_NAME); + Module::parse_bitcode_from_buffer(&memory, context).unwrap() +} + +/// Generate LLVM IR of KCL ast module. +pub fn emit_code( + program: &ast::Program, + import_names: IndexMap>, + opt: &EmitOptions, +) -> Result<(), Box> { + // Create a LLVM context + let context = Context::create(); + // Create a LLVM module using an exist LLVM bitcode file + let module = if let Some(path) = &opt.from_path { + Module::parse_bitcode_from_path(std::path::Path::new(path), &context).unwrap() + } else { + load_runtime(&context) + }; + // Create a KCL LLVM code generator using the KCL AST and the LLVM module + let ctx = LLVMCodeGenContext::new(&context, module, program, import_names, opt.no_link); + // Generate user KCL code LLVM IR + crate::codegen::emit_code(ctx, opt) +} diff --git a/kclvm/compiler/src/codegen/llvm/mod.rs b/kclvm/compiler/src/codegen/llvm/mod.rs new file mode 100644 index 000000000..58faddb77 --- /dev/null +++ b/kclvm/compiler/src/codegen/llvm/mod.rs @@ -0,0 +1,15 @@ +//! The goal of this module is to translate KCL Program into LLVM IR code, where each AST corresponding to KCL +//! pkgpath corresponds to a module of LLVM. They share a global symbol table and LLVM context. Different LLVM +//! module modules pass extern and declare keys. Declare and call them in words, and finally use clang to link +//! them together. +//! +//! Copyright 2021 The KCL Authors. All rights reserved. + +mod context; +mod emit; +mod module; +mod node; +mod schema; +mod utils; + +pub use emit::emit_code; diff --git a/kclvm/compiler/src/codegen/llvm/module.rs b/kclvm/compiler/src/codegen/llvm/module.rs new file mode 100644 index 000000000..dc609d9da --- /dev/null +++ b/kclvm/compiler/src/codegen/llvm/module.rs @@ -0,0 +1,37 @@ +// Copyright 2021 The KCL Authors. All rights reserved. +use kclvm_ast::ast; +use kclvm_ast::walker::TypedResultWalker; + +use super::context::LLVMCodeGenContext; +use crate::codegen::error as kcl_error; +use crate::codegen::traits::ValueMethods; +use std::str; + +impl<'ctx> LLVMCodeGenContext<'ctx> { + pub fn compile_module_import_and_types(&self, module: &'ctx ast::Module) { + for stmt in &module.body { + match &stmt.node { + ast::Stmt::Import(import_stmt) => { + self.walk_import_stmt(import_stmt) + .expect(kcl_error::COMPILE_ERROR_MSG); + } + ast::Stmt::Schema(schema_stmt) => { + self.predefine_global_types(&schema_stmt.name.node); + } + ast::Stmt::Rule(rule_stmt) => { + self.predefine_global_types(&rule_stmt.name.node); + } + _ => {} + }; + } + } + pub fn predefine_global_types(&self, name: &str) { + // Store or add the variable in the scope + let function = self.undefined_value(); + if !self.store_variable(name, function) { + let global_var_ptr = self.new_global_kcl_value_ptr(""); + self.builder.build_store(global_var_ptr, function); + self.add_variable(name, global_var_ptr); + } + } +} diff --git a/kclvm/compiler/src/codegen/llvm/node.rs b/kclvm/compiler/src/codegen/llvm/node.rs new file mode 100644 index 000000000..83f7aade8 --- /dev/null +++ b/kclvm/compiler/src/codegen/llvm/node.rs @@ -0,0 +1,2800 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use std::cell::RefCell; +use std::collections::HashMap; +use std::convert::TryFrom; + +use inkwell::basic_block::BasicBlock; +use inkwell::module::Linkage; +use inkwell::values::{BasicValueEnum, CallableValue, FunctionValue}; +use inkwell::{AddressSpace, IntPredicate}; +use kclvm::{ApiFunc, PKG_PATH_PREFIX}; +use kclvm_ast::ast::{self, CallExpr}; +use kclvm_ast::walker::TypedResultWalker; + +use crate::codegen::error as kcl_error; +use crate::codegen::llvm::context::BacktrackMeta; +use crate::codegen::llvm::utils; +use crate::codegen::traits::*; +use crate::codegen::{ENTRY_NAME, GLOBAL_LEVEL, PKG_INIT_FUNCTION_SUFFIX, SCHEMA_LEVEL}; +use crate::{check_backtrack_stop, pkgpath_without_prefix}; + +use super::context::{CompileResult, LLVMCodeGenContext}; +use crate::value; +use kclvm_sema::builtin; +use kclvm_sema::plugin; + +/// Impl TypedResultWalker for LLVMCodeGenContext to visit AST nodes to emit LLVM IR. +impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { + type Result = CompileResult<'ctx>; + + /* + * Stmt + */ + + fn walk_stmt(&self, stmt: &'ctx ast::Node) -> Self::Result { + check_backtrack_stop!(self); + utils::update_ctx_filename(self, stmt); + utils::update_ctx_line_col(self, stmt); + self.target_vars.borrow_mut().clear(); + self.target_vars.borrow_mut().push("".to_string()); + match &stmt.node { + ast::Stmt::TypeAlias(type_alias) => self.walk_type_alias_stmt(type_alias), + ast::Stmt::Expr(expr_stmt) => self.walk_expr_stmt(expr_stmt), + ast::Stmt::Unification(unification_stmt) => { + self.walk_unification_stmt(unification_stmt) + } + ast::Stmt::Assign(assign_stmt) => self.walk_assign_stmt(assign_stmt), + ast::Stmt::AugAssign(aug_assign_stmt) => self.walk_aug_assign_stmt(aug_assign_stmt), + ast::Stmt::Assert(assert_stmt) => self.walk_assert_stmt(assert_stmt), + ast::Stmt::If(if_stmt) => self.walk_if_stmt(if_stmt), + ast::Stmt::Import(import_stmt) => self.walk_import_stmt(import_stmt), + ast::Stmt::SchemaAttr(schema_attr) => self.walk_schema_attr(schema_attr), + ast::Stmt::Schema(schema_stmt) => self.walk_schema_stmt(schema_stmt), + ast::Stmt::Rule(rule_stmt) => self.walk_rule_stmt(rule_stmt), + } + } + + fn walk_expr_stmt(&self, expr_stmt: &'ctx ast::ExprStmt) -> Self::Result { + check_backtrack_stop!(self); + let mut result = self.ok_result(); + for expr in &expr_stmt.exprs { + // Ignore the doc string + if !matches!(&expr.node, ast::Expr::StringLit(..)) { + result = self.walk_expr(expr); + } + } + result + } + + fn walk_unification_stmt(&self, unification_stmt: &'ctx ast::UnificationStmt) -> Self::Result { + check_backtrack_stop!(self); + self.local_vars.borrow_mut().clear(); + let name = &unification_stmt.target.node.names[0]; + self.target_vars.borrow_mut().push(name.clone()); + // The right value of the unification_stmt is a schema_expr. + let value = self + .walk_schema_expr(&unification_stmt.value.node) + .expect(kcl_error::COMPILE_ERROR_MSG); + if self.scope_level() == GLOBAL_LEVEL + || *self + .lambda_stack + .borrow_mut() + .last() + .expect(kcl_error::INTERNAL_ERROR_MSG) + { + if self.resolve_variable(name) { + let org_value = self + .walk_identifier_with_ctx( + &unification_stmt.target.node, + &ast::ExprContext::Load, + None, + ) + .expect(kcl_error::COMPILE_ERROR_MSG); + let fn_name = ApiFunc::kclvm_value_op_aug_bit_or; + let value = self.build_call(&fn_name.name(), &[org_value, value]); + // Store the identifier value + self.walk_identifier_with_ctx( + &unification_stmt.target.node, + &ast::ExprContext::Store, + Some(value), + ) + .expect(kcl_error::COMPILE_ERROR_MSG); + return Ok(value); + } else { + self.walk_identifier_with_ctx( + &unification_stmt.target.node, + &unification_stmt.target.node.ctx, + Some(value), + ) + .expect(kcl_error::COMPILE_ERROR_MSG); + return Ok(value); + } + // Local variables including schema/rule/lambda + } else if self.schema_stack.borrow().len() > 0 { + // Load the identifier value + let org_value = self + .walk_identifier_with_ctx( + &unification_stmt.target.node, + &ast::ExprContext::Load, + None, + ) + .expect(kcl_error::COMPILE_ERROR_MSG); + let fn_name = ApiFunc::kclvm_value_op_bit_or; + let value = self.build_call(&fn_name.name(), &[org_value, value]); + // Store the identifier value + self.walk_identifier_with_ctx( + &unification_stmt.target.node, + &ast::ExprContext::Store, + Some(value), + ) + .expect(kcl_error::COMPILE_ERROR_MSG); + return Ok(value); + } + Ok(value) + } + + fn walk_type_alias_stmt(&self, _type_alias_stmt: &'ctx ast::TypeAliasStmt) -> Self::Result { + // Nothing to do, because all type aliases have been replaced at compile time + self.ok_result() + } + + fn walk_assign_stmt(&self, assign_stmt: &'ctx ast::AssignStmt) -> Self::Result { + check_backtrack_stop!(self); + self.local_vars.borrow_mut().clear(); + for name in &assign_stmt.targets { + self.target_vars + .borrow_mut() + .push(name.node.names[0].clone()); + } + // Load the right value + let mut value = self + .walk_expr(&assign_stmt.value) + .expect(kcl_error::COMPILE_ERROR_MSG); + if let Some(type_annotation) = &assign_stmt.type_annotation { + let type_annotation = self.native_global_string_value(&type_annotation.node); + value = self.build_call( + &ApiFunc::kclvm_convert_collection_value.name(), + &[value, type_annotation], + ); + } + if assign_stmt.targets.len() == 1 { + let name = &assign_stmt.targets[0]; + self.walk_identifier_with_ctx(&name.node, &name.node.ctx, Some(value)) + .expect(kcl_error::COMPILE_ERROR_MSG); + } else { + // Store targets + for name in &assign_stmt.targets { + let value = self.value_deep_copy(value); + self.walk_identifier_with_ctx(&name.node, &name.node.ctx, Some(value)) + .expect(kcl_error::COMPILE_ERROR_MSG); + } + } + Ok(value) + } + + fn walk_aug_assign_stmt(&self, aug_assign_stmt: &'ctx ast::AugAssignStmt) -> Self::Result { + check_backtrack_stop!(self); + self.target_vars + .borrow_mut() + .push(aug_assign_stmt.target.node.names[0].clone()); + // Load the right value + let right_value = self + .walk_expr(&aug_assign_stmt.value) + .expect(kcl_error::COMPILE_ERROR_MSG); + // Load the identifier value + let org_value = self + .walk_identifier_with_ctx(&aug_assign_stmt.target.node, &ast::ExprContext::Load, None) + .expect(kcl_error::COMPILE_ERROR_MSG); + let fn_name = match aug_assign_stmt.op { + ast::AugOp::Add => ApiFunc::kclvm_value_op_aug_add, + ast::AugOp::Sub => ApiFunc::kclvm_value_op_aug_sub, + ast::AugOp::Mul => ApiFunc::kclvm_value_op_aug_mul, + ast::AugOp::Div => ApiFunc::kclvm_value_op_aug_div, + ast::AugOp::Mod => ApiFunc::kclvm_value_op_aug_mod, + ast::AugOp::Pow => ApiFunc::kclvm_value_op_aug_pow, + ast::AugOp::LShift => ApiFunc::kclvm_value_op_aug_bit_lshift, + ast::AugOp::RShift => ApiFunc::kclvm_value_op_aug_bit_rshift, + ast::AugOp::BitOr => ApiFunc::kclvm_value_op_bit_or, + ast::AugOp::BitXor => ApiFunc::kclvm_value_op_aug_bit_xor, + ast::AugOp::BitAnd => ApiFunc::kclvm_value_op_aug_bit_and, + ast::AugOp::FloorDiv => ApiFunc::kclvm_value_op_aug_floor_div, + ast::AugOp::Assign => { + return Err(kcl_error::KCLError::new(kcl_error::INVALID_OPERATOR_MSG)); + } + }; + let value = self.build_call(&fn_name.name(), &[org_value, right_value]); + // Store the identifier value + self.walk_identifier_with_ctx( + &aug_assign_stmt.target.node, + &ast::ExprContext::Store, + Some(value), + ) + .expect(kcl_error::COMPILE_ERROR_MSG); + Ok(value) + } + + fn walk_assert_stmt(&self, assert_stmt: &'ctx ast::AssertStmt) -> Self::Result { + check_backtrack_stop!(self); + let start_block = self.append_block(""); + let end_block = self.append_block(""); + if let Some(if_cond) = &assert_stmt.if_cond { + let if_value = self.walk_expr(if_cond).expect(kcl_error::COMPILE_ERROR_MSG); + let is_truth = self.value_is_truthy(if_value); + self.cond_br(is_truth, start_block, end_block); + } else { + self.br(start_block); + } + self.builder.position_at_end(start_block); + let assert_result = self + .walk_expr(&assert_stmt.test) + .expect(kcl_error::COMPILE_ERROR_MSG); + let msg = { + if let Some(msg) = &assert_stmt.msg { + self.walk_expr(msg).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.string_value("") + } + }; + self.build_void_call(&ApiFunc::kclvm_assert.name(), &[assert_result, msg]); + self.br(end_block); + self.builder.position_at_end(end_block); + self.ok_result() + } + + fn walk_if_stmt(&self, if_stmt: &'ctx ast::IfStmt) -> Self::Result { + check_backtrack_stop!(self); + let cond = self + .walk_expr(&if_stmt.cond) + .expect(kcl_error::COMPILE_ERROR_MSG); + let then_block = self.append_block(""); + let else_block = self.append_block(""); + let end_block = self.append_block(""); + let is_truth = self.value_is_truthy(cond); + self.cond_br(is_truth, then_block, else_block); + self.builder.position_at_end(then_block); + self.walk_stmts(&if_stmt.body) + .expect(kcl_error::COMPILE_ERROR_MSG); + self.br(end_block); + self.builder.position_at_end(else_block); + self.walk_stmts(&if_stmt.orelse) + .expect(kcl_error::COMPILE_ERROR_MSG); + self.br(end_block); + self.builder.position_at_end(end_block); + Ok(self.none_value()) + } + fn walk_import_stmt(&self, import_stmt: &'ctx ast::ImportStmt) -> Self::Result { + check_backtrack_stop!(self); + let pkgpath = import_stmt.path.as_str(); + { + let imported = self.imported.borrow_mut(); + if imported.contains(pkgpath) { + return self.ok_result(); + } + // Deref the borrow mut + } + if builtin::STANDARD_SYSTEM_MODULES.contains(&pkgpath) + || pkgpath.starts_with(plugin::PLUGIN_MODULE_PREFIX) + { + // Nothing to do on the builtin system module import because the check has been done. + return self.ok_result(); + } else { + let pkgpath = format!("{}{}", PKG_PATH_PREFIX, import_stmt.path); + self.pkgpath_stack.borrow_mut().push(pkgpath); + let pkgpath = format!("{}{}", PKG_PATH_PREFIX, import_stmt.path); + let has_pkgpath = self.program.pkgs.contains_key(&import_stmt.path); + let func_before_block = if self.no_link { + if has_pkgpath { + let func_before_block = self.append_block(""); + self.br(func_before_block); + let mut modules = self.modules.borrow_mut(); + let name = pkgpath.clone(); + let module = self.context.create_module(&name); + let module_name = format!( + "${}.{}", + pkgpath_without_prefix!(pkgpath), + PKG_INIT_FUNCTION_SUFFIX + ); + let tpe = self.context.void_type(); + let fn_type = tpe.fn_type(&[self.context_ptr_type().into()], false); + let function = module.add_function( + // Function name + &module_name, + // Function type + fn_type, + None, + ); + // Add a block named entry into the function + let basic_block = self.context.append_basic_block(function, ENTRY_NAME); + self.builder.position_at_end(basic_block); + self.push_function(function); + modules.insert(name, RefCell::new(module)); + Some(func_before_block) + } else { + None + } + } else { + None + }; + if has_pkgpath { + // Init all builtin functions. + self.init_scope(pkgpath.as_str()); + // Compile the ast module in the pkgpath. + for ast_module in self + .program + .pkgs + .get(&import_stmt.path) + .expect(kcl_error::INTERNAL_ERROR_MSG) + { + { + self.filename_stack + .borrow_mut() + .push(ast_module.filename.clone()); + } + self.compile_module_import_and_types(ast_module); + { + self.filename_stack.borrow_mut().pop(); + } + } + for ast_module in self + .program + .pkgs + .get(&import_stmt.path) + .expect(kcl_error::INTERNAL_ERROR_MSG) + { + { + self.filename_stack + .borrow_mut() + .push(ast_module.filename.clone()); + } + self.walk_stmts_except_import(&ast_module.body) + .expect(kcl_error::COMPILE_ERROR_MSG); + { + self.filename_stack.borrow_mut().pop(); + } + } + } + self.pkgpath_stack.borrow_mut().pop(); + if self.no_link { + let name = format!( + "${}.{}", + pkgpath_without_prefix!(pkgpath), + PKG_INIT_FUNCTION_SUFFIX + ); + let function = { + let pkgpath = self.current_pkgpath(); + let modules = self.modules.borrow_mut(); + let msg = format!("pkgpath {} is not found", pkgpath); + let module = modules.get(&pkgpath).expect(&msg).borrow_mut(); + if has_pkgpath { + self.ret_void(); + self.pop_function(); + self.builder.position_at_end( + func_before_block.expect(kcl_error::INTERNAL_ERROR_MSG), + ); + } + let tpe = self.context.void_type(); + let fn_type = tpe.fn_type(&[self.context_ptr_type().into()], false); + module.add_function(&name, fn_type, Some(Linkage::External)) + }; + let ctx = self.global_ctx_ptr(); + let pkgpath_value = self.native_global_string_value(&name); + let is_imported = self + .build_call( + &ApiFunc::kclvm_context_pkgpath_is_imported.name(), + &[pkgpath_value], + ) + .into_int_value(); + let is_not_imported = self.builder.build_int_compare( + IntPredicate::EQ, + is_imported, + self.native_i8_zero(), + "", + ); + let then_block = self.append_block(""); + let else_block = self.append_block(""); + self.builder + .build_conditional_branch(is_not_imported, then_block, else_block); + self.builder.position_at_end(then_block); + self.builder.build_call(function, &[ctx.into()], ""); + self.br(else_block); + self.builder.position_at_end(else_block); + } + }; + { + let mut imported = self.imported.borrow_mut(); + (*imported).insert(pkgpath.to_string()); + // Deref the borrow mut + } + self.ok_result() + } + + fn walk_schema_stmt(&self, schema_stmt: &'ctx ast::SchemaStmt) -> Self::Result { + check_backtrack_stop!(self); + let func_before_block = self.append_block(""); + self.br(func_before_block); + let value_ptr_type = self.value_ptr_type(); + let schema_name = &schema_stmt.name.node; + let schema_pkgpath = &self.current_pkgpath(); + let filename = &self.current_filename(); + let runtime_type = kclvm::schema_runtime_type(schema_name, schema_pkgpath); + // Build schema body function + let function = self.add_function(&format!( + "{}.{}", + value::SCHEMA_NAME, + pkgpath_without_prefix!(runtime_type) + )); + // Build the schema check function. + let check_function = self.add_function(&format!( + "{}.{}", + value::SCHEMA_CHECK_BLOCK_NAME, + pkgpath_without_prefix!(runtime_type), + )); + let mut place_holder_map: HashMap>> = HashMap::new(); + let mut body_map: HashMap>> = HashMap::new(); + // Enter the function + self.push_function(function); + // Lambda function body + let block = self.append_block(ENTRY_NAME); + self.builder.position_at_end(block); + self.build_void_call( + &ApiFunc::kclvm_context_set_kcl_filename.name(), + &[self.native_global_string_value(filename)], + ); + utils::update_ctx_pkgpath(self, schema_pkgpath); + let args = function + .get_nth_param(1) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let kwargs = function + .get_nth_param(2) + .expect(kcl_error::INTERNAL_ERROR_MSG); + self.enter_scope(); + let add_variable = |name: &str, value: BasicValueEnum| { + let var = self.builder.build_alloca(value_ptr_type, name); + self.builder.build_store(var, value); + self.add_variable(name, var); + }; + // Schema function closures + let instance_pkgpath = self.list_pop(args); + let record_instance = self.list_pop(args); + let backtrack_cache = self.list_pop(args); + let backtrack_level_map = self.list_pop(args); + let cal_map = self.list_pop(args); + let attr_optional_mapping = self.list_pop(args); + let schema_value = self.list_pop(args); + let schema_config = self.list_pop(args); + let schema_config_meta = self.list_pop(args); + let is_sub_schema = self.list_pop(args); + add_variable(value::BACKTRACK_CACHE, backtrack_cache); + add_variable(value::BACKTRACK_LEVEL_MAP, backtrack_level_map); + add_variable(value::SCHEMA_CAL_MAP, cal_map); + add_variable(value::SCHEMA_CONFIG_NAME, schema_config); + add_variable(value::SCHEMA_CONFIG_META_NAME, schema_config_meta); + add_variable(value::SCHEMA_ARGS, args); + add_variable(value::SCHEMA_KWARGS, kwargs); + add_variable(value::SCHEMA_RUNTIME_TYPE, self.string_value(&runtime_type)); + self.walk_arguments(&schema_stmt.args, args, kwargs); + let schema = value::SchemaType::new( + schema_name, + schema_pkgpath, + &runtime_type, + schema_stmt.is_mixin, + ); + let schema_value = if let Some(parent_name) = &schema_stmt.parent_name { + let base_constructor_func = self + .walk_identifier_with_ctx(&parent_name.node, &ast::ExprContext::Load, None) + .expect(kcl_error::COMPILE_ERROR_MSG); + // Schema function closures + let list_value = self.list_values(&[ + // is_sub_schema + self.bool_value(false), + schema_config_meta, + schema_config, + schema_value, + attr_optional_mapping, + cal_map, + backtrack_level_map, + backtrack_cache, + record_instance, + instance_pkgpath, + ]); + let dict_value = self.dict_value(); + let func_ptr = self.build_call( + &ApiFunc::kclvm_value_function_ptr.name(), + &[base_constructor_func], + ); + let fn_ty = self.function_type().ptr_type(AddressSpace::Generic); + let func_ptr_cast = self.builder.build_bitcast(func_ptr, fn_ty, ""); + self.builder + .build_call( + CallableValue::try_from(func_ptr_cast.into_pointer_value()) + .expect(kcl_error::INTERNAL_ERROR_MSG), + &[ + self.global_ctx_ptr().into(), + list_value.into(), + dict_value.into(), + ], + "", + ) + .try_as_basic_value() + .left() + .expect(kcl_error::FUNCTION_RETURN_VALUE_NOT_FOUND_MSG) + } else { + schema_value + }; + if schema_stmt.parent_name.is_some() { + self.build_void_call( + &ApiFunc::kclvm_context_set_kcl_filename.name(), + &[self.native_global_string_value(filename)], + ); + } + self.schema_stack.borrow_mut().push(schema); + add_variable(value::SCHEMA_SELF_NAME, schema_value); + self.emit_schema_left_identifiers( + &schema_stmt.body, + cal_map, + &runtime_type, + false, + &mut place_holder_map, + &mut body_map, + &mut vec![], + ); + let do_run_i1 = self.value_is_truthy(record_instance); + let do_run_block = self.append_block(""); + let end_run_block = self.append_block(""); + self.cond_br(do_run_i1, do_run_block, end_run_block); + self.builder.position_at_end(do_run_block); + // Run schema compiled function + for stmt in &schema_stmt.body { + self.walk_stmt(stmt).expect(kcl_error::COMPILE_ERROR_MSG); + } + // Schema decorators check + for decorator in &schema_stmt.decorators { + self.walk_decorator_with_name(&decorator.node, Some(schema_name), true) + .expect(kcl_error::COMPILE_ERROR_MSG); + } + // Append schema default settings + self.build_void_call( + &ApiFunc::kclvm_schema_default_settings.name(), + &[ + schema_value, + schema_config, + self.native_global_string_value(&runtime_type), + ], + ); + self.br(end_run_block); + self.builder.position_at_end(end_run_block); + // Schema mixin + for mixin in &schema_stmt.mixins { + let mixin_func = self + .walk_identifier_with_ctx(&mixin.node, &ast::ExprContext::Load, None) + .expect(kcl_error::COMPILE_ERROR_MSG); + // Schema function closures + let list_value = self.list_values(&[ + // is_sub_schema + self.bool_value(false), + schema_config_meta, + schema_config, + schema_value, + attr_optional_mapping, + cal_map, + backtrack_level_map, + backtrack_cache, + record_instance, + instance_pkgpath, + ]); + let dict_value = self.dict_value(); + let func_ptr = + self.build_call(&ApiFunc::kclvm_value_function_ptr.name(), &[mixin_func]); + let fn_ty = self.function_type().ptr_type(AddressSpace::Generic); + let func_ptr_cast = self.builder.build_bitcast(func_ptr, fn_ty, ""); + self.builder.build_call( + CallableValue::try_from(func_ptr_cast.into_pointer_value()) + .expect(kcl_error::INTERNAL_ERROR_MSG), + &[ + self.global_ctx_ptr().into(), + list_value.into(), + dict_value.into(), + ], + "", + ); + self.build_void_call( + &ApiFunc::kclvm_context_set_kcl_filename.name(), + &[self.native_global_string_value(filename)], + ); + } + // Schema Attribute optional check + for stmt in &schema_stmt.body { + if let ast::Stmt::SchemaAttr(schema_attr) = &stmt.node { + self.dict_insert_override_item( + attr_optional_mapping, + schema_attr.name.node.as_str(), + self.bool_value(schema_attr.is_optional), + ) + } + } + let is_sub_schema_i1 = self.value_is_truthy(is_sub_schema); + let do_check_block = self.append_block(""); + let end_check_block = self.append_block(""); + self.cond_br(is_sub_schema_i1, do_check_block, end_check_block); + self.builder.position_at_end(do_check_block); + let schema_name_native_str = self.native_global_string_value(&schema_stmt.name.node); + let schema_pkgpath_native_str = self.native_global_string_value(&self.current_pkgpath()); + // Schema runtime index signature and relaxed check + if let Some(index_signature) = &schema_stmt.index_signature { + let index_sign_value = if let Some(value) = &index_signature.node.value { + self.walk_expr(value).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.none_value() + }; + let key_name_str_ptr = if let Some(key_name) = &index_signature.node.key_name { + self.native_global_string(key_name.as_str(), "") + } else { + self.native_global_string("", "") + }; + self.build_void_call( + &ApiFunc::kclvm_schema_value_check.name(), + &[ + schema_value, + schema_config, + schema_config_meta, + schema_name_native_str, + index_sign_value, + key_name_str_ptr.into(), + self.native_global_string(index_signature.node.key_type.node.as_str(), "") + .into(), + self.native_global_string(index_signature.node.value_type.node.as_str(), "") + .into(), + self.native_i8(index_signature.node.any_other as i8).into(), + self.native_i8(false as i8).into(), + ], + ); + } else { + self.build_void_call( + &ApiFunc::kclvm_schema_value_check.name(), + &[ + schema_value, + schema_config, + schema_config_meta, + schema_name_native_str, + self.none_value(), + self.native_global_string("", "").into(), + self.native_global_string("", "").into(), + self.native_global_string("", "").into(), + self.native_i8(0).into(), + self.native_i8(false as i8).into(), + ], + ); + } + self.build_void_call( + &ApiFunc::kclvm_schema_optional_check.name(), + &[ + schema_value, + attr_optional_mapping, + schema_name_native_str, + schema_config_meta, + ], + ); + { + let index_sign_key_name = if let Some(index_signature) = &schema_stmt.index_signature { + if let Some(key_name) = &index_signature.node.key_name { + key_name + } else { + "" + } + } else { + "" + }; + let list_value = self.value_deep_copy(args); + let dict_value = self.value_deep_copy(kwargs); + // Schema check function closure + self.list_append(list_value, schema_config_meta); + self.list_append(list_value, schema_config); + self.list_append(list_value, schema_value); + self.list_append(list_value, cal_map); + self.list_append(list_value, backtrack_level_map); + self.list_append(list_value, backtrack_cache); + if index_sign_key_name.is_empty() { + // Call schema check block function + self.builder.build_call( + check_function, + &[ + self.global_ctx_ptr().into(), + list_value.into(), + dict_value.into(), + ], + "", + ); + } else { + // Call schema check block function with index sign attribute name loop set + let check_lambda_fn_ptr = self.builder.build_bitcast( + check_function.as_global_value().as_pointer_value(), + self.context.i64_type().ptr_type(AddressSpace::Generic), + "", + ); + let attr_name = self.native_global_string_value(index_sign_key_name); + self.build_void_call( + ApiFunc::kclvm_schema_do_check_with_index_sign_attr + .name() + .as_str(), + &[ + self.global_ctx_ptr(), + list_value, + dict_value, + check_lambda_fn_ptr, + attr_name, + ], + ); + } + } + self.br(end_check_block); + self.builder.position_at_end(end_check_block); + // Build a schema value and record instance + let schema_value = self.build_call( + &ApiFunc::kclvm_value_schema_with_config.name(), + &[ + schema_value, + schema_config, + schema_name_native_str, + schema_pkgpath_native_str, + is_sub_schema, + record_instance, + instance_pkgpath, + ], + ); + // Schema constructor function returns a schema + self.builder.build_return(Some(&schema_value)); + // Exist the function + self.builder.position_at_end(func_before_block); + // Build schema check function + { + self.push_function(check_function); + let check_block = self.append_block(ENTRY_NAME); + self.builder.position_at_end(check_block); + let args = function + .get_nth_param(1) + .expect(kcl_error::INTERNAL_ERROR_MSG); + // Schema check function closure + let backtrack_cache = self.list_pop(args); + let backtrack_level_map = self.list_pop(args); + let cal_map = self.list_pop(args); + let schema_value = self.list_pop(args); + let schema_config = self.list_pop(args); + let schema_config_meta = self.list_pop(args); + add_variable(value::BACKTRACK_CACHE, backtrack_cache); + add_variable(value::BACKTRACK_LEVEL_MAP, backtrack_level_map); + add_variable(value::SCHEMA_CAL_MAP, cal_map); + add_variable(value::SCHEMA_CONFIG_NAME, schema_config); + add_variable(value::SCHEMA_CONFIG_META_NAME, schema_config_meta); + add_variable(value::SCHEMA_SELF_NAME, schema_value); + add_variable(value::SCHEMA_ARGS, args); + add_variable(value::SCHEMA_KWARGS, kwargs); + add_variable(value::SCHEMA_RUNTIME_TYPE, self.string_value(&runtime_type)); + let schema = self + .schema_stack + .borrow_mut() + .pop() + .expect(kcl_error::INTERNAL_ERROR_MSG); + self.walk_arguments(&schema_stmt.args, args, kwargs); + self.schema_stack.borrow_mut().push(schema); + // Call base check function + if let Some(parent_name) = &schema_stmt.parent_name { + let base_constructor_func = self + .walk_identifier_with_ctx(&parent_name.node, &ast::ExprContext::Load, None) + .expect(kcl_error::COMPILE_ERROR_MSG); + let func_ptr = self.build_call( + &ApiFunc::kclvm_value_check_function_ptr.name(), + &[base_constructor_func], + ); + let fn_ty = self.function_type().ptr_type(AddressSpace::Generic); + let func_ptr_cast = self.builder.build_bitcast(func_ptr, fn_ty, ""); + // Schema check function closure + let list_value = self.list_values(&[ + schema_config_meta, + schema_config, + schema_value, + cal_map, + backtrack_level_map, + backtrack_cache, + ]); + let dict_value = self.dict_value(); + self.builder.build_call( + CallableValue::try_from(func_ptr_cast.into_pointer_value()) + .expect(kcl_error::INTERNAL_ERROR_MSG), + &[ + self.global_ctx_ptr().into(), + list_value.into(), + dict_value.into(), + ], + "", + ); + self.build_void_call( + &ApiFunc::kclvm_context_set_kcl_filename.name(), + &[self.native_global_string_value(filename)], + ); + } + // Call self check function + for check_expr in &schema_stmt.checks { + self.walk_check_expr(&check_expr.node) + .expect(kcl_error::COMPILE_ERROR_MSG); + } + // Call mixin check functions + for mixin in &schema_stmt.mixins { + let mixin_func = self + .walk_identifier_with_ctx(&mixin.node, &ast::ExprContext::Load, None) + .expect(kcl_error::COMPILE_ERROR_MSG); + let func_ptr = self.build_call( + &ApiFunc::kclvm_value_check_function_ptr.name(), + &[mixin_func], + ); + let fn_ty = self.function_type().ptr_type(AddressSpace::Generic); + let func_ptr_cast = self.builder.build_bitcast(func_ptr, fn_ty, ""); + // Schema check function closure + let list_value = self.list_values(&[ + schema_config_meta, + schema_config, + schema_value, + cal_map, + backtrack_level_map, + backtrack_cache, + ]); + let dict_value = self.dict_value(); + self.builder.build_call( + CallableValue::try_from(func_ptr_cast.into_pointer_value()) + .expect(kcl_error::INTERNAL_ERROR_MSG), + &[ + self.global_ctx_ptr().into(), + list_value.into(), + dict_value.into(), + ], + "", + ); + self.build_void_call( + &ApiFunc::kclvm_context_set_kcl_filename.name(), + &[self.native_global_string_value(filename)], + ); + } + self.builder.build_return(Some(&schema_value)); + self.builder.position_at_end(func_before_block); + self.pop_function(); + } + // Build schema attr backtrack functions + { + for (k, functions) in place_holder_map { + let stmt_list = body_map.get(&k).expect(kcl_error::INTERNAL_ERROR_MSG); + let mut if_level = 0; + for (attr_func, stmt) in functions.iter().zip(stmt_list) { + let function = *attr_func; + let name = function + .get_name() + .to_str() + .expect(kcl_error::INTERNAL_ERROR_MSG); + // Get schema attr function from the module + let function = self.lookup_function(name); + self.push_function(function); + self.enter_scope(); + let attr_block = self.append_block(ENTRY_NAME); + self.builder.position_at_end(attr_block); + let args = function + .get_nth_param(1) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let kwargs = function + .get_nth_param(2) + .expect(kcl_error::INTERNAL_ERROR_MSG); + // Schema attr function closure + let backtrack_cache = self.list_pop(args); + let backtrack_level_map = self.list_pop(args); + let cal_map = self.list_pop(args); + let schema_value = self.list_pop(args); + let schema_config = self.list_pop(args); + let schema_config_meta = self.list_pop(args); + // Store magic variable + add_variable(value::BACKTRACK_CACHE, backtrack_cache); + add_variable(value::BACKTRACK_LEVEL_MAP, backtrack_level_map); + add_variable(value::SCHEMA_CAL_MAP, cal_map); + add_variable(value::SCHEMA_CONFIG_NAME, schema_config); + add_variable(value::SCHEMA_CONFIG_META_NAME, schema_config_meta); + add_variable(value::SCHEMA_SELF_NAME, schema_value); + add_variable(value::SCHEMA_ARGS, args); + add_variable(value::SCHEMA_KWARGS, kwargs); + add_variable(value::SCHEMA_RUNTIME_TYPE, self.string_value(&runtime_type)); + self.build_void_call( + &ApiFunc::kclvm_context_set_kcl_filename.name(), + &[self.native_global_string_value(filename)], + ); + let schema = self + .schema_stack + .borrow_mut() + .pop() + .expect(kcl_error::INTERNAL_ERROR_MSG); + self.walk_arguments(&schema_stmt.args, args, kwargs); + self.schema_stack.borrow_mut().push(schema); + // Backtrack meta begin + if matches!(&stmt.node, ast::Stmt::If(..)) { + if_level += 1; + *self.backtrack_meta.borrow_mut() = Some(BacktrackMeta { + target: k.clone(), + level: if_level, + count: 0, + stop: false, + }); + } else { + if_level = 0; + } + self.walk_stmt(*stmt).expect(kcl_error::COMPILE_ERROR_MSG); + // Backtrack meta end + if matches!(&stmt.node, ast::Stmt::If(..)) { + *self.backtrack_meta.borrow_mut() = None + } + // Build return + self.builder.build_return(Some(&schema_value)); + // Position at global main function block + self.builder.position_at_end(func_before_block); + self.leave_scope(); + self.pop_function(); + } + } + } + let function = self.struct_function_value(&[function, check_function], &runtime_type); + self.leave_scope(); + self.pop_function(); + self.schema_stack.borrow_mut().pop(); + // Store or add the variable in the scope + if !self.store_variable(schema_name, function) { + let global_var_ptr = self.new_global_kcl_value_ptr(""); + self.builder.build_store(global_var_ptr, function); + self.add_variable(schema_name, global_var_ptr); + } + Ok(function) + } + + fn walk_rule_stmt(&self, rule_stmt: &'ctx ast::RuleStmt) -> Self::Result { + check_backtrack_stop!(self); + let func_before_block = self.append_block(""); + self.br(func_before_block); + let value_ptr_type = self.value_ptr_type(); + let name = &rule_stmt.name.node; + let pkgpath = &self.current_pkgpath(); + let filename = &self.current_filename(); + let runtime_type = kclvm::schema_runtime_type(name, pkgpath); + // Build schema body function + let function = self.add_function(&format!( + "{}.{}", + value::SCHEMA_NAME, + pkgpath_without_prefix!(runtime_type) + )); + // Build the schema check function. + let check_function = self.add_function(&format!( + "{}.{}", + value::SCHEMA_CHECK_BLOCK_NAME, + pkgpath_without_prefix!(runtime_type), + )); + // Enter the function + self.push_function(function); + // Lambda function body + let block = self.append_block(ENTRY_NAME); + self.builder.position_at_end(block); + self.build_void_call( + &ApiFunc::kclvm_context_set_kcl_filename.name(), + &[self.native_global_string_value(filename)], + ); + let args = function + .get_nth_param(1) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let kwargs = function + .get_nth_param(2) + .expect(kcl_error::INTERNAL_ERROR_MSG); + self.enter_scope(); + // Schema function closures + let _instance_pkgpath = self.list_pop(args); + let record_instance = self.list_pop(args); + let backtrack_cache = self.list_pop(args); + let backtrack_level_map = self.list_pop(args); + let cal_map = self.list_pop(args); + let _attr_optional_mapping = self.list_pop(args); + let schema_value = self.list_pop(args); + let schema_config = self.list_pop(args); + let schema_config_meta = self.list_pop(args); + let is_sub_schema = self.list_pop(args); + let add_variable = |name: &str, value: BasicValueEnum| { + let var = self.builder.build_alloca(value_ptr_type, name); + self.builder.build_store(var, value); + self.add_variable(name, var); + }; + add_variable(value::BACKTRACK_CACHE, backtrack_cache); + add_variable(value::BACKTRACK_LEVEL_MAP, backtrack_level_map); + add_variable(value::SCHEMA_CAL_MAP, cal_map); + add_variable(value::SCHEMA_CONFIG_NAME, schema_config); + add_variable(value::SCHEMA_CONFIG_META_NAME, schema_config_meta); + add_variable(value::SCHEMA_ARGS, args); + add_variable(value::SCHEMA_KWARGS, kwargs); + add_variable(value::SCHEMA_RUNTIME_TYPE, self.string_value(&runtime_type)); + self.walk_arguments(&rule_stmt.args, args, kwargs); + let schema = value::SchemaType::new(name, pkgpath, &runtime_type, false); + self.schema_stack.borrow_mut().push(schema); + add_variable(value::SCHEMA_SELF_NAME, schema_value); + let do_run_i1 = self.value_is_truthy(record_instance); + let do_run_block = self.append_block(""); + let end_run_block = self.append_block(""); + self.cond_br(do_run_i1, do_run_block, end_run_block); + self.builder.position_at_end(do_run_block); + // Rule decorators check + for decorator in &rule_stmt.decorators { + self.walk_decorator_with_name(&decorator.node, Some(name), true) + .expect(kcl_error::INTERNAL_ERROR_MSG); + } + self.br(end_run_block); + self.builder.position_at_end(end_run_block); + let is_sub_schema_i1 = self.value_is_truthy(is_sub_schema); + let do_check_block = self.append_block(""); + let end_check_block = self.append_block(""); + self.cond_br(is_sub_schema_i1, do_check_block, end_check_block); + self.builder.position_at_end(do_check_block); + { + // Schema check function closure + let list_value = self.list_values(&[ + schema_config_meta, + schema_config, + schema_value, + cal_map, + backtrack_level_map, + backtrack_cache, + ]); + let dict_value = self.dict_value(); + // Call schema check block function with index sign attribute name loop set + let check_lambda_fn_ptr = self.builder.build_bitcast( + check_function.as_global_value().as_pointer_value(), + self.context.i64_type().ptr_type(AddressSpace::Generic), + "", + ); + let attr_name = self.native_global_string_value(""); + self.build_void_call( + ApiFunc::kclvm_schema_do_check_with_index_sign_attr + .name() + .as_str(), + &[ + self.global_ctx_ptr(), + list_value, + dict_value, + check_lambda_fn_ptr, + attr_name, + ], + ); + } + self.br(end_check_block); + self.builder.position_at_end(end_check_block); + // Rule constructor function returns a rule + self.builder.build_return(Some(&schema_value)); + // Exist the function + self.builder.position_at_end(func_before_block); + // Build rule check function + { + self.push_function(check_function); + let check_block = self.append_block(ENTRY_NAME); + self.builder.position_at_end(check_block); + let args = function + .get_nth_param(1) + .expect(kcl_error::INTERNAL_ERROR_MSG); + // Schema check function closure + let backtrack_cache = self.list_pop(args); + let backtrack_level_map = self.list_pop(args); + let cal_map = self.list_pop(args); + let schema_value = self.list_pop(args); + let schema_config = self.list_pop(args); + let schema_config_meta = self.list_pop(args); + add_variable(value::BACKTRACK_CACHE, backtrack_cache); + add_variable(value::BACKTRACK_LEVEL_MAP, backtrack_level_map); + add_variable(value::SCHEMA_CAL_MAP, cal_map); + add_variable(value::SCHEMA_CONFIG_NAME, schema_config); + add_variable(value::SCHEMA_CONFIG_META_NAME, schema_config_meta); + add_variable(value::SCHEMA_SELF_NAME, schema_value); + add_variable(value::SCHEMA_ARGS, args); + add_variable(value::SCHEMA_KWARGS, kwargs); + add_variable(value::SCHEMA_RUNTIME_TYPE, self.string_value(&runtime_type)); + let schema = self + .schema_stack + .borrow_mut() + .pop() + .expect(kcl_error::INTERNAL_ERROR_MSG); + self.walk_arguments(&rule_stmt.args, args, kwargs); + self.schema_stack.borrow_mut().push(schema); + // Call base check function + for parent_name in &rule_stmt.parent_rules { + let base_constructor_func = self + .walk_identifier_with_ctx(&parent_name.node, &ast::ExprContext::Load, None) + .expect(kcl_error::COMPILE_ERROR_MSG); + let func_ptr = self.build_call( + &ApiFunc::kclvm_value_check_function_ptr.name(), + &[base_constructor_func], + ); + let fn_ty = self.function_type().ptr_type(AddressSpace::Generic); + let func_ptr_cast = self.builder.build_bitcast(func_ptr, fn_ty, ""); + // Schema check function closure + let list_value = self.list_values(&[ + schema_config_meta, + schema_config, + schema_value, + cal_map, + backtrack_level_map, + backtrack_cache, + ]); + let dict_value = self.dict_value(); + self.builder.build_call( + CallableValue::try_from(func_ptr_cast.into_pointer_value()) + .expect(kcl_error::INTERNAL_ERROR_MSG), + &[ + self.global_ctx_ptr().into(), + list_value.into(), + dict_value.into(), + ], + "", + ); + } + // Call self rule check expressions + for check_expr in &rule_stmt.checks { + self.walk_check_expr(&check_expr.node) + .expect(kcl_error::COMPILE_ERROR_MSG); + } + self.builder.build_return(Some(&schema_value)); + self.builder.position_at_end(func_before_block); + self.pop_function(); + } + let function = self.struct_function_value(&[function, check_function], &runtime_type); + self.leave_scope(); + self.pop_function(); + self.schema_stack.borrow_mut().pop(); + // Store or add the variable in the scope + if !self.store_variable(name, function) { + let global_var_ptr = self.new_global_kcl_value_ptr(&runtime_type); + self.builder.build_store(global_var_ptr, function); + self.add_variable(name, global_var_ptr); + } + Ok(function) + } + + /* + * Expr + */ + + fn walk_expr(&self, expr: &'ctx ast::Node) -> Self::Result { + check_backtrack_stop!(self); + utils::update_ctx_filename(self, expr); + utils::update_ctx_line_col(self, expr); + match &expr.node { + ast::Expr::Identifier(identifier) => self.walk_identifier(identifier), + ast::Expr::Unary(unary_expr) => self.walk_unary_expr(unary_expr), + ast::Expr::Binary(binary_expr) => self.walk_binary_expr(binary_expr), + ast::Expr::If(if_expr) => self.walk_if_expr(if_expr), + ast::Expr::Selector(selector_expr) => self.walk_selector_expr(selector_expr), + ast::Expr::Call(call_expr) => self.walk_call_expr(call_expr), + ast::Expr::Paren(paren_expr) => self.walk_paren_expr(paren_expr), + ast::Expr::Quant(quant_expr) => self.walk_quant_expr(quant_expr), + ast::Expr::List(list_expr) => self.walk_list_expr(list_expr), + ast::Expr::ListIfItem(list_if_item_expr) => { + self.walk_list_if_item_expr(list_if_item_expr) + } + ast::Expr::ListComp(list_comp) => self.walk_list_comp(list_comp), + ast::Expr::Starred(starred_expr) => self.walk_starred_expr(starred_expr), + ast::Expr::DictComp(dict_comp) => self.walk_dict_comp(dict_comp), + ast::Expr::ConfigIfEntry(config_if_entry_expr) => { + self.walk_config_if_entry_expr(config_if_entry_expr) + } + ast::Expr::CompClause(comp_clause) => self.walk_comp_clause(comp_clause), + ast::Expr::Schema(schema_expr) => self.walk_schema_expr(schema_expr), + ast::Expr::Config(config_expr) => self.walk_config_expr(config_expr), + ast::Expr::Check(check) => self.walk_check_expr(check), + ast::Expr::Lambda(lambda) => self.walk_lambda_expr(lambda), + ast::Expr::Subscript(subscript) => self.walk_subscript(subscript), + ast::Expr::Keyword(keyword) => self.walk_keyword(keyword), + ast::Expr::Arguments(..) => self.ok_result(), + ast::Expr::Compare(compare) => self.walk_compare(compare), + ast::Expr::NumberLit(number_lit) => self.walk_number_lit(number_lit), + ast::Expr::StringLit(string_lit) => self.walk_string_lit(string_lit), + ast::Expr::NameConstantLit(name_constant_lit) => { + self.walk_name_constant_lit(name_constant_lit) + } + ast::Expr::JoinedString(joined_string) => self.walk_joined_string(joined_string), + ast::Expr::FormattedValue(formatted_value) => { + self.walk_formatted_value(formatted_value) + } + } + } + + fn walk_quant_expr(&self, quant_expr: &'ctx ast::QuantExpr) -> Self::Result { + check_backtrack_stop!(self); + let result = match quant_expr.op { + ast::QuantOperation::All => self.bool_value(true), + ast::QuantOperation::Any => self.bool_value(false), + ast::QuantOperation::Map => self.list_value(), + ast::QuantOperation::Filter => self.value_deep_copy( + self.walk_expr(&quant_expr.target) + .expect(kcl_error::COMPILE_ERROR_MSG), + ), + }; + // Blocks + let start_block = self.append_block(""); + let next_value_block = self.append_block(""); + let continue_block = self.append_block(""); + let end_for_block = self.append_block(""); + let all_break_block = self.append_block(""); + let any_break_block = self.append_block(""); + let result_block = self.append_block(""); + // Iterator + let iter_host_value = if let ast::QuantOperation::Filter = quant_expr.op { + self.value_deep_copy(result) + } else { + self.walk_expr(&quant_expr.target) + .expect(kcl_error::COMPILE_ERROR_MSG) + }; + let iter_value = self.build_call(&ApiFunc::kclvm_value_iter.name(), &[iter_host_value]); + self.br(start_block); + self.builder.position_at_end(start_block); + self.enter_scope(); + let is_end = self + .build_call(&ApiFunc::kclvm_iterator_is_end.name(), &[iter_value]) + .into_int_value(); + let is_end = + self.builder + .build_int_compare(IntPredicate::NE, is_end, self.native_i8_zero(), ""); + self.builder + .build_conditional_branch(is_end, end_for_block, next_value_block); + self.builder.position_at_end(next_value_block); + let next_value = self.build_call( + &ApiFunc::kclvm_iterator_next_value.name(), + &[iter_value, iter_host_value], + ); + let key = self.build_call(&ApiFunc::kclvm_iterator_cur_key.name(), &[iter_value]); + let variables = &quant_expr.variables; + { + let mut local_vars = self.local_vars.borrow_mut(); + for v in variables { + let name = &v.node.names[0]; + local_vars.insert(name.clone()); + } + } + if variables.len() == 1 { + // Store the target + self.walk_identifier_with_ctx( + &variables.get(0).expect(kcl_error::INTERNAL_ERROR_MSG).node, + &ast::ExprContext::Store, + Some(next_value), + ) + .expect(kcl_error::COMPILE_ERROR_MSG); + } else if variables.len() == 2 { + let value = self.build_call(&ApiFunc::kclvm_iterator_cur_value.name(), &[iter_value]); + // Store the target + self.walk_identifier_with_ctx( + &variables.get(0).expect(kcl_error::INTERNAL_ERROR_MSG).node, + &ast::ExprContext::Store, + Some(key), + ) + .expect(kcl_error::COMPILE_ERROR_MSG); + self.walk_identifier_with_ctx( + &variables.get(1).expect(kcl_error::INTERNAL_ERROR_MSG).node, + &ast::ExprContext::Store, + Some(value), + ) + .expect(kcl_error::COMPILE_ERROR_MSG); + } else { + panic!( + "the number of loop variables is {}, which can only be 1 or 2", + variables.len() + ) + } + if let Some(if_expr) = &quant_expr.if_cond { + let if_truth = self.walk_expr(if_expr).expect(kcl_error::COMPILE_ERROR_MSG); + let is_truth = self.value_is_truthy(if_truth); + self.cond_br(is_truth, continue_block, start_block); + } else { + self.br(continue_block); + } + self.builder.position_at_end(continue_block); + // Body block + let test = &quant_expr.test; + let value = self.walk_expr(test).expect(kcl_error::COMPILE_ERROR_MSG); + let is_truth = self.value_is_truthy(value); + match quant_expr.op { + ast::QuantOperation::All => { + self.cond_br(is_truth, start_block, all_break_block); + } + ast::QuantOperation::Any => { + self.cond_br(is_truth, any_break_block, start_block); + } + ast::QuantOperation::Filter => { + let then_block = self.append_block(""); + self.cond_br(is_truth, start_block, then_block); + self.builder.position_at_end(then_block); + self.build_void_call( + &ApiFunc::kclvm_value_remove_item.name(), + &[result, next_value], + ); + self.br(start_block); + } + ast::QuantOperation::Map => { + self.list_append(result, value); + self.br(start_block); + } + } + self.builder.position_at_end(all_break_block); + let all_false_value = self.bool_value(false); + self.br(result_block); + self.builder.position_at_end(any_break_block); + let any_true_value = self.bool_value(true); + self.br(result_block); + self.builder.position_at_end(end_for_block); + let tpe = self.value_ptr_type(); + let ptr = self.builder.build_alloca(tpe, ""); + self.builder.build_store(ptr, result); + let value = self.builder.build_load(ptr, ""); + self.br(result_block); + self.builder.position_at_end(result_block); + let phi = self.builder.build_phi(tpe, ""); + phi.add_incoming(&[ + (&all_false_value, all_break_block), + (&any_true_value, any_break_block), + (&value, end_for_block), + ]); + self.leave_scope(); + self.local_vars.borrow_mut().clear(); + Ok(phi.as_basic_value()) + } + + fn walk_schema_attr(&self, schema_attr: &'ctx ast::SchemaAttr) -> Self::Result { + check_backtrack_stop!(self); + self.local_vars.borrow_mut().clear(); + let name = schema_attr.name.node.as_str(); + self.target_vars.borrow_mut().push(name.to_string()); + for decorator in &schema_attr.decorators { + self.walk_decorator_with_name(&decorator.node, Some(name), false) + .expect(kcl_error::COMPILE_ERROR_MSG); + } + let value = match &schema_attr.value { + Some(value) => self.walk_expr(value).expect(kcl_error::COMPILE_ERROR_MSG), + None => self.undefined_value(), + }; + let config_value = self + .get_variable(value::SCHEMA_CONFIG_NAME) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let schema_value = self + .get_variable(value::SCHEMA_SELF_NAME) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let string_ptr_value = self.native_global_string(name, "").into(); + let type_str_ptr_value = self + .native_global_string(&schema_attr.type_str.node, "") + .into(); + self.build_void_call( + &ApiFunc::kclvm_config_attr_map.name(), + &[schema_value, string_ptr_value, type_str_ptr_value], + ); + if let Some(op) = &schema_attr.op { + match op { + // Union + ast::BinOrAugOp::Aug(ast::AugOp::BitOr) => { + let org_value = self.build_call( + &ApiFunc::kclvm_dict_get_value.name(), + &[schema_value, string_ptr_value], + ); + let fn_name = ApiFunc::kclvm_value_op_bit_or; + let value = self.build_call(&fn_name.name(), &[org_value, value]); + self.dict_merge(schema_value, name, value, 1, -1); + } + // Assign + _ => self.dict_merge(schema_value, name, value, 1, -1), + } + } + let has_key = self + .build_call( + &ApiFunc::kclvm_dict_has_value.name(), + &[config_value, string_ptr_value], + ) + .into_int_value(); + let has_key = + self.builder + .build_int_compare(IntPredicate::NE, has_key, self.native_i8_zero(), ""); + let then_block = self.append_block(""); + let else_block = self.append_block(""); + self.builder + .build_conditional_branch(has_key, then_block, else_block); + self.builder.position_at_end(then_block); + let config_attr_value = self.build_call( + &ApiFunc::kclvm_dict_get_entry.name(), + &[config_value, string_ptr_value], + ); + self.value_union(schema_value, config_attr_value); + let cal_map = self + .get_variable(value::SCHEMA_CAL_MAP) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let backtrack_cache = self + .get_variable(value::BACKTRACK_CACHE) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let runtime_type = self + .get_variable(value::SCHEMA_RUNTIME_TYPE) + .expect(kcl_error::INTERNAL_ERROR_MSG); + self.build_void_call( + &ApiFunc::kclvm_schema_backtrack_cache.name(), + &[ + schema_value, + backtrack_cache, + cal_map, + string_ptr_value, + runtime_type, + ], + ); + // Update backtrack meta + { + if let Some(backtrack_meta) = self.backtrack_meta.borrow_mut().as_mut() { + if name == backtrack_meta.target { + backtrack_meta.count += 1; + if backtrack_meta.count >= backtrack_meta.level { + backtrack_meta.stop = true; + self.ret(schema_value); + return Ok(schema_value); + } + } + } + } + self.br(else_block); + self.builder.position_at_end(else_block); + Ok(schema_value) + } + + fn walk_if_expr(&self, if_expr: &'ctx ast::IfExpr) -> Self::Result { + check_backtrack_stop!(self); + let cond = self + .walk_expr(&if_expr.cond) + .expect(kcl_error::COMPILE_ERROR_MSG); + let then_block = self.append_block(""); + let else_block = self.append_block(""); + let end_block = self.append_block(""); + let is_truth = self.value_is_truthy(cond); + let tpe = self.value_ptr_type(); + self.cond_br(is_truth, then_block, else_block); + self.builder.position_at_end(then_block); + let then_value = self + .walk_expr(&if_expr.body) + .expect(kcl_error::COMPILE_ERROR_MSG); + let then_block = self.append_block(""); + self.br(then_block); + self.builder.position_at_end(then_block); + let ptr = self.builder.build_alloca(tpe, ""); + self.builder.build_store(ptr, then_value); + let then_value = self.builder.build_load(ptr, ""); + self.br(end_block); + self.builder.position_at_end(else_block); + let else_value = self + .walk_expr(&if_expr.orelse) + .expect(kcl_error::COMPILE_ERROR_MSG); + let else_block = self.append_block(""); + self.br(else_block); + self.builder.position_at_end(else_block); + let ptr = self.alloca(tpe, "", None).into_pointer_value(); + self.builder.build_store(ptr, else_value); + let else_value = self.builder.build_load(ptr, ""); + self.br(end_block); + self.builder.position_at_end(end_block); + let phi = self.builder.build_phi(tpe, ""); + phi.add_incoming(&[(&then_value, then_block), (&else_value, else_block)]); + Ok(phi.as_basic_value()) + } + + fn walk_unary_expr(&self, unary_expr: &'ctx ast::UnaryExpr) -> Self::Result { + check_backtrack_stop!(self); + let value = self + .walk_expr(&unary_expr.operand) + .expect(kcl_error::COMPILE_ERROR_MSG); + let fn_name = match unary_expr.op { + ast::UnaryOp::UAdd => ApiFunc::kclvm_value_unary_plus, + ast::UnaryOp::USub => ApiFunc::kclvm_value_unary_minus, + ast::UnaryOp::Invert => ApiFunc::kclvm_value_unary_not, + ast::UnaryOp::Not => ApiFunc::kclvm_value_unary_l_not, + }; + Ok(self.build_call(&fn_name.name(), &[value])) + } + + fn walk_binary_expr(&self, binary_expr: &'ctx ast::BinaryExpr) -> Self::Result { + check_backtrack_stop!(self); + let is_logic_op = matches!( + binary_expr.op, + ast::BinOrCmpOp::Bin(ast::BinOp::And) | ast::BinOrCmpOp::Bin(ast::BinOp::Or) + ); + let is_membership_as_op = matches!(binary_expr.op, ast::BinOrCmpOp::Bin(ast::BinOp::As)); + if !is_logic_op { + let left_value = self + .walk_expr(&binary_expr.left) + .expect(kcl_error::COMPILE_ERROR_MSG); + let right_value = if is_membership_as_op { + match &binary_expr.right.node { + ast::Expr::Identifier(id) => { + let name = id.names.join("."); + self.string_value(&name) + } + _ => self.none_value(), + } + } else { + self.walk_expr(&binary_expr.right) + .expect(kcl_error::COMPILE_ERROR_MSG) + }; + let value = match binary_expr.op { + ast::BinOrCmpOp::Bin(ast::BinOp::Add) => self.add(left_value, right_value), + ast::BinOrCmpOp::Bin(ast::BinOp::Sub) => self.sub(left_value, right_value), + ast::BinOrCmpOp::Bin(ast::BinOp::Mul) => self.mul(left_value, right_value), + ast::BinOrCmpOp::Bin(ast::BinOp::Div) => self.div(left_value, right_value), + ast::BinOrCmpOp::Bin(ast::BinOp::FloorDiv) => { + self.floor_div(left_value, right_value) + } + ast::BinOrCmpOp::Bin(ast::BinOp::Mod) => self.r#mod(left_value, right_value), + ast::BinOrCmpOp::Bin(ast::BinOp::Pow) => self.pow(left_value, right_value), + ast::BinOrCmpOp::Bin(ast::BinOp::LShift) => { + self.bit_lshift(left_value, right_value) + } + ast::BinOrCmpOp::Bin(ast::BinOp::RShift) => { + self.bit_rshift(left_value, right_value) + } + ast::BinOrCmpOp::Bin(ast::BinOp::BitAnd) => self.bit_and(left_value, right_value), + ast::BinOrCmpOp::Bin(ast::BinOp::BitOr) => self.bit_or(left_value, right_value), + ast::BinOrCmpOp::Bin(ast::BinOp::BitXor) => self.bit_xor(left_value, right_value), + ast::BinOrCmpOp::Bin(ast::BinOp::And) => self.logic_and(left_value, right_value), + ast::BinOrCmpOp::Bin(ast::BinOp::Or) => self.logic_or(left_value, right_value), + ast::BinOrCmpOp::Bin(ast::BinOp::As) => self.r#as(left_value, right_value), + ast::BinOrCmpOp::Cmp(ast::CmpOp::Eq) => self.cmp_equal_to(left_value, right_value), + ast::BinOrCmpOp::Cmp(ast::CmpOp::NotEq) => { + self.cmp_not_equal_to(left_value, right_value) + } + ast::BinOrCmpOp::Cmp(ast::CmpOp::Gt) => { + self.cmp_greater_than(left_value, right_value) + } + ast::BinOrCmpOp::Cmp(ast::CmpOp::GtE) => { + self.cmp_greater_than_or_equal(left_value, right_value) + } + ast::BinOrCmpOp::Cmp(ast::CmpOp::Lt) => self.cmp_less_than(left_value, right_value), + ast::BinOrCmpOp::Cmp(ast::CmpOp::LtE) => { + self.cmp_less_than_or_equal(left_value, right_value) + } + ast::BinOrCmpOp::Cmp(ast::CmpOp::Is) => self.is(left_value, right_value), + ast::BinOrCmpOp::Cmp(ast::CmpOp::IsNot) => self.is_not(left_value, right_value), + ast::BinOrCmpOp::Cmp(ast::CmpOp::Not) => self.is_not(left_value, right_value), + ast::BinOrCmpOp::Cmp(ast::CmpOp::NotIn) => self.not_in(left_value, right_value), + ast::BinOrCmpOp::Cmp(ast::CmpOp::In) => self.r#in(left_value, right_value), + }; + Ok(value) + } else { + let jump_if_false = matches!(binary_expr.op, ast::BinOrCmpOp::Bin(ast::BinOp::And)); + let start_block = self.append_block(""); + let value_block = self.append_block(""); + let end_block = self.append_block(""); + let left_value = self + .walk_expr(&binary_expr.left) + .expect(kcl_error::COMPILE_ERROR_MSG); + self.br(start_block); + self.builder.position_at_end(start_block); + let is_truth = self.value_is_truthy(left_value); + let tpe = self.value_ptr_type(); + if jump_if_false { + // Jump if false on logic and + self.cond_br(is_truth, value_block, end_block); + } else { + // Jump if true on logic or + self.cond_br(is_truth, end_block, value_block); + }; + self.builder.position_at_end(value_block); + let right_value = self + .walk_expr(&binary_expr.right) + .expect(kcl_error::COMPILE_ERROR_MSG); + let value_block = self.append_block(""); + self.br(value_block); + self.builder.position_at_end(value_block); + let ptr = self.builder.build_alloca(tpe, ""); + self.builder.build_store(ptr, right_value); + let right_value = self.builder.build_load(ptr, ""); + self.br(end_block); + self.builder.position_at_end(end_block); + let phi = self.builder.build_phi(tpe, ""); + phi.add_incoming(&[(&left_value, start_block), (&right_value, value_block)]); + Ok(phi.as_basic_value()) + } + } + + fn walk_selector_expr(&self, selector_expr: &'ctx ast::SelectorExpr) -> Self::Result { + check_backtrack_stop!(self); + let mut value = self + .walk_expr(&selector_expr.value) + .expect(kcl_error::COMPILE_ERROR_MSG); + let string_ptr_value = self + .native_global_string(selector_expr.attr.node.names[0].as_str(), "") + .into(); + let fn_name = if selector_expr.has_question { + &ApiFunc::kclvm_value_load_attr_option + } else { + &ApiFunc::kclvm_value_load_attr + }; + value = self.build_call(&fn_name.name(), &[value, string_ptr_value]); + for name in &selector_expr.attr.node.names[1..] { + let string_ptr_value = self.native_global_string(name, "").into(); + value = self.build_call( + &ApiFunc::kclvm_value_load_attr.name(), + &[value, string_ptr_value], + ); + } + Ok(value) + } + + fn walk_call_expr(&self, call_expr: &'ctx ast::CallExpr) -> Self::Result { + check_backtrack_stop!(self); + let func = self + .walk_expr(&call_expr.func) + .expect(kcl_error::COMPILE_ERROR_MSG); + // args + let list_value = self.list_value(); + for arg in &call_expr.args { + let value = self.walk_expr(arg).expect(kcl_error::COMPILE_ERROR_MSG); + self.list_append(list_value, value); + } + let dict_value = self.dict_value(); + // kwargs + for keyword in &call_expr.keywords { + let name = &keyword.node.arg.node.names[0]; + let value = if let Some(value) = &keyword.node.value { + self.walk_expr(value).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.none_value() + }; + self.dict_insert(dict_value, name.as_str(), value, 0, -1); + } + let pkgpath = self.native_global_string_value(&self.current_pkgpath()); + Ok(self.build_call( + &ApiFunc::kclvm_value_function_invoke.name(), + &[func, self.global_ctx_ptr(), list_value, dict_value, pkgpath], + )) + } + + fn walk_subscript(&self, subscript: &'ctx ast::Subscript) -> Self::Result { + check_backtrack_stop!(self); + let mut value = self + .walk_expr(&subscript.value) + .expect(kcl_error::COMPILE_ERROR_MSG); + if let Some(index) = &subscript.index { + // index + let index = self.walk_expr(index).expect(kcl_error::COMPILE_ERROR_MSG); + let fn_name = if subscript.has_question { + &ApiFunc::kclvm_value_subscr_option + } else { + &ApiFunc::kclvm_value_subscr + }; + value = self.build_call(&fn_name.name(), &[value, index]); + } else { + let lower = { + if let Some(lower) = &subscript.lower { + self.walk_expr(lower).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.none_value() + } + }; + let upper = { + if let Some(upper) = &subscript.upper { + self.walk_expr(upper).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.none_value() + } + }; + let step = { + if let Some(step) = &subscript.step { + self.walk_expr(step).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.none_value() + } + }; + let fn_name = if subscript.has_question { + &ApiFunc::kclvm_value_slice_option + } else { + &ApiFunc::kclvm_value_slice + }; + value = self.build_call(&fn_name.name(), &[value, lower, upper, step]); + } + Ok(value) + } + + fn walk_paren_expr(&self, paren_expr: &'ctx ast::ParenExpr) -> Self::Result { + check_backtrack_stop!(self); + self.walk_expr(&paren_expr.expr) + } + + fn walk_list_expr(&self, list_expr: &'ctx ast::ListExpr) -> Self::Result { + check_backtrack_stop!(self); + let list_value = self.list_value(); + for item in &list_expr.elts { + let value = self.walk_expr(item).expect(kcl_error::COMPILE_ERROR_MSG); + let fn_name = match &item.node { + ast::Expr::Starred(_) | ast::Expr::ListIfItem(_) => { + ApiFunc::kclvm_list_append_unpack + } + _ => ApiFunc::kclvm_list_append, + }; + self.build_void_call(&fn_name.name(), &[list_value, value]); + } + Ok(list_value) + } + + fn walk_list_if_item_expr(&self, list_if_item_expr: &'ctx ast::ListIfItemExpr) -> Self::Result { + check_backtrack_stop!(self); + let cond = self + .walk_expr(&list_if_item_expr.if_cond) + .expect(kcl_error::COMPILE_ERROR_MSG); + let then_block = self.append_block(""); + let else_block = self.append_block(""); + let end_block = self.append_block(""); + let is_truth = self.value_is_truthy(cond); + let tpe = self.value_ptr_type(); + self.cond_br(is_truth, then_block, else_block); + self.builder.position_at_end(then_block); + let then_value = self.list_value(); + for expr in &list_if_item_expr.exprs { + let value = self.walk_expr(expr).expect(kcl_error::COMPILE_ERROR_MSG); + match &expr.node { + ast::Expr::Starred(_) | ast::Expr::ListIfItem(_) => { + self.list_append_unpack(then_value, value) + } + _ => self.list_append(then_value, value), + }; + } + let then_block = self.append_block(""); + self.br(then_block); + self.builder.position_at_end(then_block); + let ptr = self.builder.build_alloca(tpe, ""); + self.builder.build_store(ptr, then_value); + let then_value = self.builder.build_load(ptr, ""); + self.br(end_block); + self.builder.position_at_end(else_block); + let else_value = if let Some(orelse) = &list_if_item_expr.orelse { + self.walk_expr(orelse).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.none_value() + }; + let else_block = self.append_block(""); + self.br(else_block); + self.builder.position_at_end(else_block); + let ptr = self.builder.build_alloca(tpe, ""); + self.builder.build_store(ptr, else_value); + let else_value = self.builder.build_load(ptr, ""); + self.br(end_block); + self.builder.position_at_end(end_block); + let phi = self.builder.build_phi(tpe, ""); + phi.add_incoming(&[(&then_value, then_block), (&else_value, else_block)]); + Ok(phi.as_basic_value()) + } + + fn walk_starred_expr(&self, starred_expr: &'ctx ast::StarredExpr) -> Self::Result { + check_backtrack_stop!(self); + self.walk_expr(&starred_expr.value) + } + + fn walk_list_comp(&self, list_comp: &'ctx ast::ListComp) -> Self::Result { + check_backtrack_stop!(self); + let collection_value = self.list_value(); + self.enter_scope(); + self.walk_generator( + &list_comp.generators, + &list_comp.elt, + None, + None, + 0, + collection_value, + ast::CompType::List, + ); + self.leave_scope(); + let tpe = self.value_ptr_type(); + let ptr = self.builder.build_alloca(tpe, ""); + self.builder.build_store(ptr, collection_value); + let value = self.builder.build_load(ptr, ""); + Ok(value) + } + + fn walk_dict_comp(&self, dict_comp: &'ctx ast::DictComp) -> Self::Result { + check_backtrack_stop!(self); + let collection_value = self.dict_value(); + self.enter_scope(); + let key = dict_comp + .entry + .key + .as_ref() + .expect(kcl_error::INTERNAL_ERROR_MSG); + self.walk_generator( + &dict_comp.generators, + key, + Some(&dict_comp.entry.value), + Some(&dict_comp.entry.operation), + 0, + collection_value, + ast::CompType::Dict, + ); + self.leave_scope(); + Ok(collection_value) + } + + fn walk_config_if_entry_expr( + &self, + config_if_entry_expr: &'ctx ast::ConfigIfEntryExpr, + ) -> Self::Result { + check_backtrack_stop!(self); + let cond = self + .walk_expr(&config_if_entry_expr.if_cond) + .expect(kcl_error::COMPILE_ERROR_MSG); + let then_block = self.append_block(""); + let else_block = self.append_block(""); + let end_block = self.append_block(""); + let is_truth = self.value_is_truthy(cond); + let tpe = self.value_ptr_type(); + self.cond_br(is_truth, then_block, else_block); + self.builder.position_at_end(then_block); + let then_value = self.dict_value(); + for item in &config_if_entry_expr.items { + let key = &item.node.key; + let value = &item.node.value; + let op = &item.node.operation; + let value = self.walk_expr(value).expect(kcl_error::COMPILE_ERROR_MSG); + if let Some(key) = key { + let mut insert_index = -1; + let key = match &key.node { + ast::Expr::Identifier(identifier) => { + let name = &identifier.names[0]; + self.string_value(name) + } + ast::Expr::StringLit(string_lit) => { + self.string_value(string_lit.value.as_str()) + } + ast::Expr::Subscript(subscript) => match &subscript.value.node { + ast::Expr::Identifier(identifier) => { + let has_index = match &subscript.index { + Some(index) => match &index.node { + ast::Expr::NumberLit(v) => match &v.value { + ast::NumberLitValue::Int(v) => { + insert_index = *v as i32; + true + } + _ => false, + }, + _ => false, + }, + _ => false, + }; + if has_index { + let name = &identifier.names[0]; + self.string_value(name) + } else { + self.walk_expr(key).expect(kcl_error::COMPILE_ERROR_MSG) + } + } + _ => self.walk_expr(key).expect(kcl_error::COMPILE_ERROR_MSG), + }, + _ => self.walk_expr(key).expect(kcl_error::COMPILE_ERROR_MSG), + }; + self.dict_insert_with_key_value(then_value, key, value, op.value(), insert_index); + } else { + self.build_void_call( + &ApiFunc::kclvm_dict_insert_unpack.name(), + &[then_value, value], + ); + } + } + let then_block = self.append_block(""); + self.br(then_block); + self.builder.position_at_end(then_block); + let ptr = self.builder.build_alloca(tpe, ""); + self.builder.build_store(ptr, then_value); + let then_value = self.builder.build_load(ptr, ""); + self.br(end_block); + self.builder.position_at_end(else_block); + let else_value = if let Some(orelse) = &config_if_entry_expr.orelse { + self.walk_expr(orelse).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.none_value() + }; + let else_block = self.append_block(""); + self.br(else_block); + self.builder.position_at_end(else_block); + let ptr = self.builder.build_alloca(tpe, ""); + self.builder.build_store(ptr, else_value); + let else_value = self.builder.build_load(ptr, ""); + self.br(end_block); + self.builder.position_at_end(end_block); + let phi = self.builder.build_phi(tpe, ""); + phi.add_incoming(&[(&then_value, then_block), (&else_value, else_block)]); + Ok(phi.as_basic_value()) + } + + fn walk_comp_clause(&self, _comp_clause: &'ctx ast::CompClause) -> Self::Result { + // Nothing to do on this AST node + self.ok_result() + } + + fn walk_schema_expr(&self, schema_expr: &'ctx ast::SchemaExpr) -> Self::Result { + check_backtrack_stop!(self); + let config_value = self + .walk_expr(&schema_expr.config) + .expect(kcl_error::COMPILE_ERROR_MSG); + let schema_type = self + .walk_identifier_with_ctx(&schema_expr.name.node, &schema_expr.name.node.ctx, None) + .expect(kcl_error::COMPILE_ERROR_MSG); + let config_expr = match &schema_expr.config.node { + ast::Expr::Config(config_expr) => config_expr, + _ => panic!("invalid schema config expr"), + }; + let config_meta = self.get_schema_config_meta(Some(&schema_expr.name), config_expr); + let list_value = self.list_value(); + for arg in &schema_expr.args { + let value = self.walk_expr(arg).expect(kcl_error::COMPILE_ERROR_MSG); + self.list_append(list_value, value); + } + let dict_value = self.dict_value(); + for keyword in &schema_expr.kwargs { + let name = &keyword.node.arg.node.names[0]; + let value = if let Some(value) = &keyword.node.value { + self.walk_expr(value).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.none_value() + }; + self.dict_insert(dict_value, name.as_str(), value, 0, -1); + } + let pkgpath = self.native_global_string_value(&self.current_pkgpath()); + let schema = self.build_call( + &ApiFunc::kclvm_schema_value_new.name(), + &[ + self.global_ctx_ptr(), + list_value, + dict_value, + schema_type, + config_value, + config_meta, + pkgpath, + ], + ); + utils::force_update_ctx_filename(self, &schema_expr.config); + Ok(schema) + } + + fn walk_config_expr(&self, config_expr: &'ctx ast::ConfigExpr) -> Self::Result { + check_backtrack_stop!(self); + let config_value = self.dict_value(); + for item in &config_expr.items { + let value = &item.node.value; + let op = &item.node.operation; + let value = self.walk_expr(value).expect(kcl_error::COMPILE_ERROR_MSG); + if let Some(key) = &item.node.key { + let mut insert_index = -1; + let key = match &key.node { + ast::Expr::Identifier(identifier) => { + let name = &identifier.names[0]; + self.string_value(name) + } + ast::Expr::StringLit(string_lit) => { + self.string_value(string_lit.value.as_str()) + } + ast::Expr::Subscript(subscript) => match &subscript.value.node { + ast::Expr::Identifier(identifier) => { + let has_index = match &subscript.index { + Some(index) => match &index.node { + ast::Expr::NumberLit(v) => match &v.value { + ast::NumberLitValue::Int(v) => { + insert_index = *v as i32; + true + } + _ => false, + }, + _ => false, + }, + _ => false, + }; + if has_index { + let name = &identifier.names[0]; + self.string_value(name) + } else { + self.walk_expr(key).expect(kcl_error::COMPILE_ERROR_MSG) + } + } + _ => self.walk_expr(key).expect(kcl_error::COMPILE_ERROR_MSG), + }, + _ => self.walk_expr(key).expect(kcl_error::COMPILE_ERROR_MSG), + }; + self.dict_insert_with_key_value(config_value, key, value, op.value(), insert_index); + } else { + self.build_void_call( + &ApiFunc::kclvm_dict_insert_unpack.name(), + &[config_value, value], + ); + } + } + Ok(config_value) + } + + fn walk_check_expr(&self, check_expr: &'ctx ast::CheckExpr) -> Self::Result { + check_backtrack_stop!(self); + let start_block = self.append_block(""); + let end_block = self.append_block(""); + if let Some(if_cond) = &check_expr.if_cond { + let if_value = self.walk_expr(if_cond).expect(kcl_error::COMPILE_ERROR_MSG); + let is_truth = self.value_is_truthy(if_value); + self.cond_br(is_truth, start_block, end_block); + } else { + self.br(start_block); + } + self.builder.position_at_end(start_block); + let check_result = self + .walk_expr(&check_expr.test) + .expect(kcl_error::COMPILE_ERROR_MSG); + let msg = { + if let Some(msg) = &check_expr.msg { + self.walk_expr(msg).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.string_value("") + } + }; + let schema_config_meta = self + .get_variable(value::SCHEMA_CONFIG_META_NAME) + .expect(kcl_error::COMPILE_ERROR_MSG); + utils::update_ctx_current_line(self); + self.build_void_call( + &ApiFunc::kclvm_schema_assert.name(), + &[check_result, msg, schema_config_meta], + ); + self.br(end_block); + self.builder.position_at_end(end_block); + self.ok_result() + } + + fn walk_lambda_expr(&self, lambda_expr: &'ctx ast::LambdaExpr) -> Self::Result { + check_backtrack_stop!(self); + let is_in_schema = self.schema_stack.borrow().len() > 0; + let func_before_block = self.append_block(""); + self.br(func_before_block); + let function = self.add_function(value::LAMBDA_NAME); + // Enter the function + self.push_function(function); + self.lambda_stack.borrow_mut().push(true); + // Lambda function body + let block = self.context.append_basic_block(function, ENTRY_NAME); + self.builder.position_at_end(block); + let args = function + .get_nth_param(1) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let kwargs = function + .get_nth_param(2) + .expect(kcl_error::INTERNAL_ERROR_MSG); + self.enter_scope(); + let closure_map = self.list_pop_first(args); + let tpe = self.value_ptr_type(); + let var = self.builder.build_alloca(tpe, value::LAMBDA_CLOSURE); + self.builder.build_store(var, closure_map); + self.add_variable(value::LAMBDA_CLOSURE, var); + if is_in_schema { + let string_ptr_value = self + .native_global_string(value::SCHEMA_SELF_NAME, "") + .into(); + let schema_value = self.build_call( + &ApiFunc::kclvm_dict_get_value.name(), + &[closure_map, string_ptr_value], + ); + let value_ptr_type = self.value_ptr_type(); + let var = self + .builder + .build_alloca(value_ptr_type, value::SCHEMA_SELF_NAME); + self.builder.build_store(var, schema_value); + self.add_variable(value::SCHEMA_SELF_NAME, var); + } + self.walk_arguments(&lambda_expr.args, args, kwargs); + let val = self + .walk_stmts(&lambda_expr.body) + .expect(kcl_error::COMPILE_ERROR_MSG); + self.builder.build_return(Some(&val)); + // Exist the function + self.builder.position_at_end(func_before_block); + let closure = self.list_value(); + let dict_value = self.get_closure_dict_in_current_scope(); + self.list_append(closure, dict_value); + let function = self.closure_value(function, closure); + self.leave_scope(); + self.pop_function(); + self.lambda_stack.borrow_mut().pop(); + Ok(function) + } + + fn walk_keyword(&self, _keyword: &'ctx ast::Keyword) -> Self::Result { + // Nothing to do + self.ok_result() + } + + fn walk_arguments(&self, _arguments: &'ctx ast::Arguments) -> Self::Result { + // Nothing to do + self.ok_result() + } + + fn walk_compare(&self, compare: &'ctx ast::Compare) -> Self::Result { + check_backtrack_stop!(self); + let mut left_value = self + .walk_expr(&compare.left) + .expect(kcl_error::COMPILE_ERROR_MSG); + if compare.comparators.len() > 1 { + let tpe = self.value_ptr_type(); + let mut next_block = self.append_block(""); + let end_block = self.append_block(""); + self.br(next_block); + self.builder.position_at_end(next_block); + let mut values_blocks: Vec<(BasicValueEnum, BasicBlock)> = vec![]; + for (i, op) in compare.ops.iter().enumerate() { + let has_next = i < (compare.ops.len() - 1); + let right_value = self + .walk_expr(&compare.comparators[i]) + .expect(kcl_error::COMPILE_ERROR_MSG); + let fn_name = match op { + ast::CmpOp::Eq => ApiFunc::kclvm_value_cmp_equal_to, + ast::CmpOp::NotEq => ApiFunc::kclvm_value_cmp_not_equal_to, + ast::CmpOp::Gt => ApiFunc::kclvm_value_cmp_greater_than, + ast::CmpOp::GtE => ApiFunc::kclvm_value_cmp_greater_than_or_equal, + ast::CmpOp::Lt => ApiFunc::kclvm_value_cmp_less_than, + ast::CmpOp::LtE => ApiFunc::kclvm_value_cmp_less_than_or_equal, + ast::CmpOp::Is => ApiFunc::kclvm_value_is, + ast::CmpOp::IsNot => ApiFunc::kclvm_value_is_not, + ast::CmpOp::Not => ApiFunc::kclvm_value_is_not, + ast::CmpOp::NotIn => ApiFunc::kclvm_value_not_in, + ast::CmpOp::In => ApiFunc::kclvm_value_in, + }; + let result_value = self.build_call(&fn_name.name(), &[left_value, right_value]); + let is_truth = self.value_is_truthy(result_value); + left_value = right_value; + // Get next value using a store/load temp block + let next_value_block = self.append_block(""); + self.br(next_value_block); + self.builder.position_at_end(next_value_block); + let ptr = self.builder.build_alloca(tpe, ""); + self.builder.build_store(ptr, result_value); + let result_value = self.builder.build_load(ptr, ""); + // Append a value-block pair in the vec + values_blocks.push((result_value, next_value_block)); + if has_next { + next_block = self.append_block(""); + self.cond_br(is_truth, next_block, end_block); + self.builder.position_at_end(next_block); + } else { + self.br(end_block); + } + } + self.builder.position_at_end(end_block); + let phi = self.builder.build_phi(tpe, ""); + for (value, block) in values_blocks { + phi.add_incoming(&[(&value, block)]); + } + Ok(phi.as_basic_value()) + } else { + let right_value = self + .walk_expr(&compare.comparators[0]) + .expect(kcl_error::COMPILE_ERROR_MSG); + let fn_name = match &compare.ops[0] { + ast::CmpOp::Eq => ApiFunc::kclvm_value_cmp_equal_to, + ast::CmpOp::NotEq => ApiFunc::kclvm_value_cmp_not_equal_to, + ast::CmpOp::Gt => ApiFunc::kclvm_value_cmp_greater_than, + ast::CmpOp::GtE => ApiFunc::kclvm_value_cmp_greater_than_or_equal, + ast::CmpOp::Lt => ApiFunc::kclvm_value_cmp_less_than, + ast::CmpOp::LtE => ApiFunc::kclvm_value_cmp_less_than_or_equal, + ast::CmpOp::Is => ApiFunc::kclvm_value_is, + ast::CmpOp::IsNot => ApiFunc::kclvm_value_is_not, + ast::CmpOp::Not => ApiFunc::kclvm_value_is_not, + ast::CmpOp::NotIn => ApiFunc::kclvm_value_not_in, + ast::CmpOp::In => ApiFunc::kclvm_value_in, + }; + left_value = self.build_call(&fn_name.name(), &[left_value, right_value]); + Ok(left_value) + } + } + + fn walk_identifier(&self, identifier: &'ctx ast::Identifier) -> Self::Result { + check_backtrack_stop!(self); + self.walk_identifier_with_ctx(identifier, &identifier.ctx, None) + } + + fn walk_number_lit(&self, number_lit: &'ctx ast::NumberLit) -> Self::Result { + check_backtrack_stop!(self); + match number_lit.value { + ast::NumberLitValue::Int(int_value) => match &number_lit.binary_suffix { + Some(binary_suffix) => { + let unit = binary_suffix.value(); + let value = kclvm::cal_num(int_value, unit.as_str()); + Ok(self.unit_value(value, int_value, &unit)) + } + None => Ok(self.int_value(int_value)), + }, + ast::NumberLitValue::Float(float_value) => Ok(self.float_value(float_value)), + } + } + + fn walk_string_lit(&self, string_lit: &'ctx ast::StringLit) -> Self::Result { + check_backtrack_stop!(self); + let string_ptr_value = self + .native_global_string(string_lit.value.as_str(), "") + .into(); + Ok(self.build_call(&ApiFunc::kclvm_value_Str.name(), &[string_ptr_value])) + } + + fn walk_name_constant_lit( + &self, + name_constant_lit: &'ctx ast::NameConstantLit, + ) -> Self::Result { + check_backtrack_stop!(self); + match name_constant_lit.value { + ast::NameConstant::True => Ok(self.bool_value(true)), + ast::NameConstant::False => Ok(self.bool_value(false)), + ast::NameConstant::None => Ok(self.none_value()), + ast::NameConstant::Undefined => Ok(self.undefined_value()), + } + } + + fn walk_joined_string(&self, joined_string: &'ctx ast::JoinedString) -> Self::Result { + check_backtrack_stop!(self); + let mut result_value = self.string_value(""); + for value in &joined_string.values { + let value = &value.node; + let value = match value { + ast::Expr::FormattedValue(formatted_value) => self + .walk_formatted_value(formatted_value) + .expect(kcl_error::INTERNAL_ERROR_MSG), + ast::Expr::StringLit(string_lit) => self + .walk_string_lit(string_lit) + .expect(kcl_error::INTERNAL_ERROR_MSG), + _ => panic!("{}", kcl_error::INVALID_JOINED_STR_MSG), + }; + result_value = + self.build_call(&ApiFunc::kclvm_value_op_add.name(), &[result_value, value]); + } + Ok(result_value) + } + + fn walk_formatted_value(&self, formatted_value: &'ctx ast::FormattedValue) -> Self::Result { + check_backtrack_stop!(self); + let formatted_expr_value = self + .walk_expr(&formatted_value.value) + .expect(kcl_error::COMPILE_ERROR_MSG); + let mut fn_name = ApiFunc::kclvm_value_to_str_value; + if let Some(spec) = &formatted_value.format_spec { + fn_name = match spec.to_lowercase().as_str() { + "#json" => ApiFunc::kclvm_value_to_json_value, + "#yaml" => ApiFunc::kclvm_value_to_yaml_value, + _ => panic!("{}", kcl_error::INVALID_STR_INTERPOLATION_SPEC_MSG), + }; + } + Ok(self.build_call(&fn_name.name(), &[formatted_expr_value])) + } + + fn walk_comment(&self, _comment: &'ctx ast::Comment) -> Self::Result { + // Nothing to do + self.ok_result() + } + + fn walk_module(&self, module: &'ctx ast::Module) -> Self::Result { + check_backtrack_stop!(self); + if !module.body.is_empty() { + utils::update_ctx_filename(self, &module.body[0]); + } + // Compile all schema and rule firstly + self.compile_module_import_and_types(module); + // Compile all statements of the module + self.walk_stmts_except_import(&module.body) + } +} + +impl<'ctx> LLVMCodeGenContext<'ctx> { + pub fn walk_stmts_except_import( + &self, + stmts: &'ctx [Box>], + ) -> CompileResult<'ctx> { + check_backtrack_stop!(self); + let mut result = self.ok_result(); + for stmt in stmts { + if !matches!(&stmt.node, ast::Stmt::Import(..)) { + result = self.walk_stmt(stmt); + } + } + result + } + + pub fn walk_stmts(&self, stmts: &'ctx [Box>]) -> CompileResult<'ctx> { + check_backtrack_stop!(self); + // Empty statements return None value + let mut result = Ok(self.none_value()); + for stmt in stmts { + result = self.walk_stmt(stmt); + } + result + } + + pub fn walk_identifier_with_ctx( + &self, + identifier: &'ctx ast::Identifier, + identifier_ctx: &ast::ExprContext, + right_value: Option>, + ) -> CompileResult<'ctx> { + check_backtrack_stop!(self); + let is_in_schema = self.schema_stack.borrow().len() > 0; + match identifier_ctx { + ast::ExprContext::Store => { + if identifier.names.len() == 1 { + let name = identifier.names[0].as_str(); + let tpe = self.value_ptr_type(); + // Global variables + if self.scope_level() == GLOBAL_LEVEL { + self.add_or_update_global_variable( + name, + right_value.expect(kcl_error::INTERNAL_ERROR_MSG), + ); + // Local variables including schema/rule/lambda + } else if *self + .lambda_stack + .borrow_mut() + .last() + .expect(kcl_error::INTERNAL_ERROR_MSG) + { + let value = right_value.expect(kcl_error::INTERNAL_ERROR_MSG); + // If variable exists in the scope and update it, if not, add it to the scope. + if !self.store_variable_in_current_scope(name, value) { + let var = self.builder.build_alloca(tpe, name); + self.builder.build_store(var, value); + self.add_variable(name, var); + self.store_variable(name, value); + } + } else { + let is_local_var = { + let local_vars = self.local_vars.borrow_mut(); + local_vars.contains(name) + }; + let value = if is_in_schema { + let value = right_value.expect(kcl_error::INTERNAL_ERROR_MSG); + let schema_value = self + .get_variable(value::SCHEMA_SELF_NAME) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let config_value = self + .get_variable(value::SCHEMA_CONFIG_NAME) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let string_ptr_value = self.native_global_string(name, "").into(); + let has_key = self + .build_call( + &ApiFunc::kclvm_dict_has_value.name(), + &[config_value, string_ptr_value], + ) + .into_int_value(); + let has_key = self.builder.build_int_compare( + IntPredicate::NE, + has_key, + self.native_i8_zero(), + "", + ); + let last_block = self.append_block(""); + let then_block = self.append_block(""); + let else_block = self.append_block(""); + self.br(last_block); + self.builder.position_at_end(last_block); + let none_value = self.none_value(); + self.builder + .build_conditional_branch(has_key, then_block, else_block); + self.builder.position_at_end(then_block); + let config_entry = self.build_call( + &ApiFunc::kclvm_dict_get_entry.name(), + &[config_value, string_ptr_value], + ); + self.br(else_block); + self.builder.position_at_end(else_block); + let tpe = self.value_ptr_type(); + let phi = self.builder.build_phi(tpe, ""); + phi.add_incoming(&[ + (&none_value, last_block), + (&config_entry, then_block), + ]); + let config_value = phi.as_basic_value(); + if self.scope_level() >= SCHEMA_LEVEL && !is_local_var { + self.dict_merge(schema_value, name, value, 1, -1); + self.value_union(schema_value, config_value); + let cal_map = self + .get_variable(value::SCHEMA_CAL_MAP) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let backtrack_cache = self + .get_variable(value::BACKTRACK_CACHE) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let runtime_type = self + .get_variable(value::SCHEMA_RUNTIME_TYPE) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let name_native_str = self.native_global_string_value(name); + self.build_void_call( + &ApiFunc::kclvm_schema_backtrack_cache.name(), + &[ + schema_value, + backtrack_cache, + cal_map, + name_native_str, + runtime_type, + ], + ); + // Update backtrack meta + { + if let Some(backtrack_meta) = + self.backtrack_meta.borrow_mut().as_mut() + { + if name == backtrack_meta.target { + backtrack_meta.count += 1; + if backtrack_meta.count == backtrack_meta.level { + backtrack_meta.stop = true; + self.ret(schema_value); + return Ok(schema_value); + } + } + } + } + } + value + } else { + right_value.expect(kcl_error::INTERNAL_ERROR_MSG) + }; + if is_local_var || !is_in_schema { + let var = self.builder.build_alloca(tpe, name); + self.builder.build_store(var, value); + self.add_variable(name, var); + } + } + } else { + let names = &identifier.names; + let name = names[0].as_str(); + let mut value = if is_in_schema { + self.get_variable_in_schema(name) + .expect(kcl_error::INTERNAL_ERROR_MSG) + } else { + self.get_variable(name) + .expect(kcl_error::INTERNAL_ERROR_MSG) + }; + for i in 0..names.len() - 1 { + let attr = names[i + 1].as_str(); + let ctx = if matches!(identifier_ctx, ast::ExprContext::Store) + && i != names.len() - 2 + && names.len() > 2 + { + &ast::ExprContext::Load + } else { + identifier_ctx + }; + match ctx { + ast::ExprContext::Load => { + let attr = self.native_global_string(attr, "").into(); + value = self.build_call( + &ApiFunc::kclvm_value_load_attr.name(), + &[value, attr], + ); + } + ast::ExprContext::Store => { + let attr = self.native_global_string(attr, "").into(); + self.build_void_call( + &ApiFunc::kclvm_dict_set_value.name(), + &[ + value, + attr, + right_value.expect(kcl_error::INTERNAL_ERROR_MSG), + ], + ); + } + } + } + } + Ok(right_value.expect(kcl_error::INTERNAL_ERROR_MSG)) + } + ast::ExprContext::Load => { + let name = identifier.names[0].as_str(); + let is_local_var = { + let local_vars = self.local_vars.borrow_mut(); + local_vars.contains(name) + }; + if identifier.names.len() == 1 { + if is_in_schema && !is_local_var { + self.get_variable_in_schema(name) + } else { + self.get_variable(name) + } + } else { + let names = &identifier.names; + let name = names[0].as_str(); + let mut value = if identifier.pkgpath.is_empty() { + if is_in_schema && !is_local_var { + self.get_variable_in_schema(name) + .expect(kcl_error::INTERNAL_ERROR_MSG) + } else { + self.get_variable(name) + .expect(kcl_error::INTERNAL_ERROR_MSG) + } + } else { + self.ok_result().expect(kcl_error::INTERNAL_ERROR_MSG) + }; + for i in 0..names.len() - 1 { + let attr = names[i + 1].as_str(); + let ctx = if matches!(identifier_ctx, ast::ExprContext::Store) + && i != names.len() - 2 + && names.len() > 2 + { + &ast::ExprContext::Load + } else { + identifier_ctx + }; + match ctx { + ast::ExprContext::Load => { + if i == 0 && !identifier.pkgpath.is_empty() { + value = if self.no_link { + self.get_external_variable_in_pkgpath( + attr, + &identifier.pkgpath, + ) + .expect(kcl_error::INTERNAL_ERROR_MSG) + } else { + self.get_variable_in_pkgpath(attr, &identifier.pkgpath) + .expect(kcl_error::INTERNAL_ERROR_MSG) + } + } else { + let attr = self.native_global_string(attr, "").into(); + value = self.build_call( + &ApiFunc::kclvm_value_load_attr.name(), + &[value, attr], + ); + } + } + ast::ExprContext::Store => { + let attr = self.native_global_string(attr, "").into(); + self.build_void_call( + &ApiFunc::kclvm_dict_set_value.name(), + &[ + value, + attr, + right_value.expect(kcl_error::INTERNAL_ERROR_MSG), + ], + ); + } + } + } + Ok(value) + } + } + } + } + + pub fn walk_decorator_with_name( + &self, + decorator: &'ctx CallExpr, + attr_name: Option<&str>, + is_schema_target: bool, + ) -> CompileResult<'ctx> { + check_backtrack_stop!(self); + let list_value = self.list_value(); + let dict_value = self.dict_value(); + let schema_config_meta = self + .get_variable(value::SCHEMA_CONFIG_META_NAME) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let schema_config_value = self + .get_variable(value::SCHEMA_CONFIG_NAME) + .expect(kcl_error::INTERNAL_ERROR_MSG); + for arg in &decorator.args { + let value = self.walk_expr(arg).expect(kcl_error::COMPILE_ERROR_MSG); + self.list_append(list_value, value); + } + for keyword in &decorator.keywords { + let name = &keyword.node.arg.node.names[0]; + let value = if let Some(value) = &keyword.node.value { + self.walk_expr(value).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.none_value() + }; + self.dict_insert(dict_value, name.as_str(), value, 0, -1); + } + let name = match &decorator.func.node { + ast::Expr::Identifier(ident) if ident.names.len() == 1 => ident.names[0].clone(), + _ => panic!("invalid decorator name, expect single identifier"), + }; + let attr_name = if let Some(v) = attr_name { v } else { "" }; + let attr_name = self.native_global_string_value(attr_name); + Ok(self.build_call( + &ApiFunc::kclvm_value_Decorator.name(), + &[ + self.native_global_string_value(name.as_str()), + list_value, + dict_value, + schema_config_meta, + attr_name, + schema_config_value, + self.bool_value(is_schema_target), + ], + )) + } + + pub fn walk_arguments( + &self, + arguments: &'ctx Option>, + args: BasicValueEnum<'ctx>, + kwargs: BasicValueEnum<'ctx>, + ) { + // Arguments names and defaults + let (arg_names, arg_defaults) = if let Some(args) = &arguments { + let names = &args.node.args; + let defaults = &args.node.defaults; + ( + names.iter().map(|identifier| &identifier.node).collect(), + defaults.iter().collect(), + ) + } else { + (vec![], vec![]) + }; + // Default parameter values + for (arg_name, value) in arg_names.iter().zip(arg_defaults.iter()) { + let arg_value = if let Some(value) = value { + self.walk_expr(value).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.none_value() + }; + self.walk_identifier_with_ctx(arg_name, &ast::ExprContext::Store, Some(arg_value)) + .expect(kcl_error::COMPILE_ERROR_MSG); + } + // for loop in 0..argument_len in LLVM begin + let argument_len = self.build_call(&ApiFunc::kclvm_list_len.name(), &[args]); + let end_block = self.append_block(""); + for (i, arg_name) in arg_names.iter().enumerate() { + // Positional arguments + let is_in_range = self.builder.build_int_compare( + IntPredicate::ULT, + self.native_int_value(i as i32).into_int_value(), + argument_len.into_int_value(), + "", + ); + let next_block = self.append_block(""); + self.builder + .build_conditional_branch(is_in_range, next_block, end_block); + self.builder.position_at_end(next_block); + let arg_value = self.build_call( + &ApiFunc::kclvm_list_get_option.name(), + &[args, self.native_int_value(i as i32)], + ); + self.store_variable(&arg_name.names[0], arg_value); + } + // for loop in 0..argument_len in LLVM end + self.br(end_block); + self.builder.position_at_end(end_block); + // Keyword arguments + for arg_name in arg_names.iter() { + let name = &arg_name.names[0]; + let string_ptr_value = self.native_global_string(name.as_str(), "").into(); + let has_key = self + .build_call( + &ApiFunc::kclvm_dict_has_value.name(), + &[kwargs, string_ptr_value], + ) + .into_int_value(); + let has_key = self.builder.build_int_compare( + IntPredicate::NE, + has_key, + self.native_i8_zero(), + "", + ); + let then_block = self.append_block(""); + let else_block = self.append_block(""); + self.builder + .build_conditional_branch(has_key, then_block, else_block); + self.builder.position_at_end(then_block); + let arg = self.build_call( + &ApiFunc::kclvm_dict_get_value.name(), + &[kwargs, string_ptr_value], + ); + // Find argument name in the scope + self.store_variable(&arg_name.names[0], arg); + self.br(else_block); + self.builder.position_at_end(else_block); + } + } + + pub fn walk_generator( + &self, + generators: &'ctx [Box>], + elt: &'ctx ast::Node, + val: Option<&'ctx ast::Node>, + op: Option<&'ctx ast::ConfigEntryOperation>, + gen_index: usize, + collection_value: BasicValueEnum<'ctx>, + comp_type: ast::CompType, + ) { + let start_block = self.append_block(""); + let next_value_block = self.append_block(""); + let continue_block = self.append_block(""); + let end_for_block = self.append_block(""); + let generator = &generators[gen_index]; + let iter_host_value = self + .walk_expr(&generator.node.iter) + .expect(kcl_error::COMPILE_ERROR_MSG); + let iter_value = self.build_call(&ApiFunc::kclvm_value_iter.name(), &[iter_host_value]); + self.br(start_block); + self.builder.position_at_end(start_block); + let is_end = self + .build_call(&ApiFunc::kclvm_iterator_is_end.name(), &[iter_value]) + .into_int_value(); + let is_end = + self.builder + .build_int_compare(IntPredicate::NE, is_end, self.native_i8_zero(), ""); + self.builder + .build_conditional_branch(is_end, end_for_block, next_value_block); + self.builder.position_at_end(next_value_block); + let next_value = self.build_call( + &ApiFunc::kclvm_iterator_next_value.name(), + &[iter_value, iter_host_value], + ); + let targets = &generator.node.targets; + { + let mut local_vars = self.local_vars.borrow_mut(); + for v in targets { + let name = &v.node.names[0]; + local_vars.insert(name.clone()); + } + } + if targets.len() == 1 { + // Store the target + self.walk_identifier_with_ctx( + &targets.get(0).expect(kcl_error::INTERNAL_ERROR_MSG).node, + &ast::ExprContext::Store, + Some(next_value), + ) + .expect(kcl_error::COMPILE_ERROR_MSG); + } else if targets.len() == 2 { + let key = self.build_call(&ApiFunc::kclvm_iterator_cur_key.name(), &[iter_value]); + let value = self.build_call(&ApiFunc::kclvm_iterator_cur_value.name(), &[iter_value]); + // Store the target + self.walk_identifier_with_ctx( + &targets.get(0).expect(kcl_error::INTERNAL_ERROR_MSG).node, + &ast::ExprContext::Store, + Some(key), + ) + .expect(kcl_error::COMPILE_ERROR_MSG); + self.walk_identifier_with_ctx( + &targets.get(1).expect(kcl_error::INTERNAL_ERROR_MSG).node, + &ast::ExprContext::Store, + Some(value), + ) + .expect(kcl_error::COMPILE_ERROR_MSG); + } else { + panic!( + "the number of loop variables is {}, which can only be 1 or 2", + generator.node.targets.len() + ) + } + for if_expr in &generator.node.ifs { + let is_truth = self.walk_expr(if_expr).expect(kcl_error::COMPILE_ERROR_MSG); + let is_truth = self.value_is_truthy(is_truth); + self.cond_br(is_truth, continue_block, start_block); + } + if generator.node.ifs.is_empty() { + self.br(continue_block); + } + self.builder.position_at_end(continue_block); + let next_gen_index = gen_index + 1; + if next_gen_index >= generators.len() { + match comp_type { + ast::CompType::List => { + let item = self.walk_expr(elt).expect(kcl_error::COMPILE_ERROR_MSG); + self.list_append(collection_value, item); + } + ast::CompType::Dict => { + let value = self + .walk_expr(val.expect(kcl_error::INTERNAL_ERROR_MSG)) + .expect(kcl_error::COMPILE_ERROR_MSG); + let key = self.walk_expr(elt).expect(kcl_error::COMPILE_ERROR_MSG); + let op = op.expect(kcl_error::INTERNAL_ERROR_MSG); + self.dict_insert_with_key_value(collection_value, key, value, op.value(), -1); + } + } + } else { + self.walk_generator( + generators, + elt, + val, + op, + next_gen_index, + collection_value, + comp_type, + ); + } + self.br(start_block); + self.builder.position_at_end(end_for_block); + self.local_vars.borrow_mut().clear(); + } +} diff --git a/kclvm/compiler/src/codegen/llvm/schema.rs b/kclvm/compiler/src/codegen/llvm/schema.rs new file mode 100644 index 000000000..379cef8dd --- /dev/null +++ b/kclvm/compiler/src/codegen/llvm/schema.rs @@ -0,0 +1,223 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use inkwell::values::{BasicValueEnum, FunctionValue}; +use inkwell::AddressSpace; +use kclvm_ast::ast; +use std::collections::HashMap; +use std::str; + +use super::context::LLVMCodeGenContext; +use crate::codegen::error as kcl_error; +use crate::codegen::traits::{BuilderMethods, DerivedValueCalculationMethods, ValueMethods}; +use crate::value; + +use crate::pkgpath_without_prefix; + +impl<'ctx> LLVMCodeGenContext<'ctx> { + /// Emit all schema left identifiers because all the schema attribute can be forward referenced + pub fn emit_schema_left_identifiers( + &self, + body: &'ctx [Box>], + cal_map: BasicValueEnum<'ctx>, + runtime_type: &str, + is_in_if: bool, + place_holder_map: &mut HashMap>>, + body_map: &mut HashMap>>, + in_if_names: &mut Vec, + ) { + let schema_value = self + .get_variable(value::SCHEMA_SELF_NAME) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let value = self.undefined_value(); + let add_stmt = + |name: &str, + stmt: &'ctx ast::Node, + place_holder_map: &mut HashMap>>, + body_map: &mut HashMap>>| { + let function = self.add_function(&format!( + "{}.{}.{}", + value::SCHEMA_ATTR_NAME, + pkgpath_without_prefix!(runtime_type), + name + )); + let lambda_fn_ptr = self.builder.build_bitcast( + function.as_global_value().as_pointer_value(), + self.context.i64_type().ptr_type(AddressSpace::Generic), + "", + ); + if !place_holder_map.contains_key(name) { + place_holder_map.insert(name.to_string(), vec![]); + } + let name_vec = place_holder_map + .get_mut(name) + .expect(kcl_error::INTERNAL_ERROR_MSG); + name_vec.push(function); + self.default_collection_insert_int_pointer(cal_map, name, lambda_fn_ptr); + self.default_collection_insert_value( + cal_map, + &format!("{}_{}", name, kclvm::CAL_MAP_RUNTIME_TYPE), + self.string_value(runtime_type), + ); + self.default_collection_insert_value( + cal_map, + &format!("{}_{}", name, kclvm::CAL_MAP_META_LINE), + self.int_value(stmt.line as i64), + ); + if !body_map.contains_key(name) { + body_map.insert(name.to_string(), vec![]); + } + let body_vec = body_map.get_mut(name).expect(kcl_error::INTERNAL_ERROR_MSG); + body_vec.push(stmt); + }; + for stmt in body { + match &stmt.node { + ast::Stmt::Unification(unification_stmt) => { + let name = &unification_stmt.target.node.names[0]; + self.dict_merge(schema_value, name, value, 0, -1); + if is_in_if { + in_if_names.push(name.to_string()); + } else { + add_stmt(name, stmt, place_holder_map, body_map); + } + } + ast::Stmt::Assign(assign_stmt) => { + for target in &assign_stmt.targets { + let name = &target.node.names[0]; + self.dict_merge(schema_value, name, value, 0, -1); + if is_in_if { + in_if_names.push(name.to_string()); + } else { + add_stmt(name, stmt, place_holder_map, body_map); + } + } + } + ast::Stmt::AugAssign(aug_assign_stmt) => { + let target = &aug_assign_stmt.target; + let name = &target.node.names[0]; + self.dict_merge(schema_value, name, value, 0, -1); + if is_in_if { + in_if_names.push(name.to_string()); + } else { + add_stmt(name, stmt, place_holder_map, body_map); + } + } + ast::Stmt::If(if_stmt) => { + let mut names: Vec = vec![]; + self.emit_schema_left_identifiers( + &if_stmt.body, + cal_map, + runtime_type, + true, + place_holder_map, + body_map, + &mut names, + ); + if is_in_if { + for name in &names { + in_if_names.push(name.to_string()); + } + } else { + for name in &names { + add_stmt(name, stmt, place_holder_map, body_map); + } + names.clear(); + } + self.emit_schema_left_identifiers( + &if_stmt.orelse, + cal_map, + runtime_type, + true, + place_holder_map, + body_map, + &mut names, + ); + if is_in_if { + for name in &names { + in_if_names.push(name.to_string()); + } + } else { + for name in &names { + add_stmt(name, stmt, place_holder_map, body_map); + } + names.clear(); + } + } + ast::Stmt::SchemaAttr(schema_attr) => { + let name = schema_attr.name.node.as_str(); + self.dict_merge(schema_value, name, value, 0, -1); + if is_in_if { + in_if_names.push(name.to_string()); + } else { + add_stmt(name, stmt, place_holder_map, body_map); + } + } + _ => {} + } + } + } + + pub fn get_schema_config_meta( + &self, + n: Option<&'ctx ast::Node>, + t: &'ctx ast::ConfigExpr, + ) -> BasicValueEnum<'ctx> { + let config_meta = self.dict_value(); + if let Some(n) = n { + let value = self.string_value(&n.filename); + self.dict_insert_override_item(config_meta, kclvm::CONFIG_META_FILENAME, value); + let value = self.int_value(n.line as i64); + self.dict_insert_override_item(config_meta, kclvm::CONFIG_META_LINE, value); + let value = self.int_value(n.column as i64); + self.dict_insert_override_item(config_meta, kclvm::CONFIG_META_COLUMN, value); + } + for item in &t.items { + if let Some(key) = &item.node.key { + let name = match &key.node { + ast::Expr::Identifier(t) => t.names[0].clone(), + ast::Expr::NumberLit(t) => match t.value { + ast::NumberLitValue::Int(i) => i.to_string(), + ast::NumberLitValue::Float(f) => f.to_string(), + }, + ast::Expr::StringLit(t) => t.value.clone(), + ast::Expr::NameConstantLit(t) => match t.value { + ast::NameConstant::True => kclvm::KCL_NAME_CONSTANT_TRUE.to_string(), + ast::NameConstant::False => kclvm::KCL_NAME_CONSTANT_FALSE.to_string(), + ast::NameConstant::None => kclvm::KCL_NAME_CONSTANT_NONE.to_string(), + ast::NameConstant::Undefined => { + kclvm::KCL_NAME_CONSTANT_UNDEFINED.to_string() + } + }, + _ => format!("{:?}", key.node), + }; + let config_item_meta = self.dict_value(); + let value = self.string_value(&key.filename); + self.dict_insert_override_item( + config_item_meta, + kclvm::CONFIG_ITEM_META_FILENAME, + value, + ); + let value = self.int_value(key.line as i64); + self.dict_insert_override_item( + config_item_meta, + kclvm::CONFIG_ITEM_META_LINE, + value, + ); + let value = self.int_value(key.column as i64); + self.dict_insert_override_item( + config_item_meta, + kclvm::CONFIG_ITEM_META_COLUMN, + value, + ); + let value = match &item.node.value.node { + ast::Expr::Config(config_expr) => { + self.get_schema_config_meta(None, config_expr) + } + _ => self.dict_value(), + }; + self.dict_insert_override_item(config_item_meta, kclvm::CONFIG_ITEM_META, value); + self.dict_insert_override_item(config_meta, &name, config_item_meta) + } + } + config_meta + } +} diff --git a/kclvm/compiler/src/codegen/llvm/utils.rs b/kclvm/compiler/src/codegen/llvm/utils.rs new file mode 100644 index 000000000..b89db9ea7 --- /dev/null +++ b/kclvm/compiler/src/codegen/llvm/utils.rs @@ -0,0 +1,83 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use inkwell::values::BasicValueEnum; +use kclvm::ApiFunc; +use kclvm_ast::ast; +use std::str; + +use crate::codegen::traits::ValueMethods; + +use super::context::LLVMCodeGenContext; + +/* + * Temporal functions + */ + +/// Update runtime context pkgpath +pub fn update_ctx_pkgpath<'ctx>(gen: &'ctx LLVMCodeGenContext, pkgpath: &str) { + gen.build_void_call( + &ApiFunc::kclvm_context_set_kcl_pkgpath.name(), + &[ + gen.global_ctx_ptr(), + gen.native_global_string_value(pkgpath), + ], + ); +} + +/// Update runtime context filename +pub fn update_ctx_filename<'ctx, T>(gen: &'ctx LLVMCodeGenContext, node: &'ctx ast::Node) { + let mut current_filename = gen.current_filename.borrow_mut(); + if node.filename != *current_filename && !node.filename.is_empty() { + *current_filename = node.filename.clone(); + gen.build_void_call( + &ApiFunc::kclvm_context_set_kcl_filename.name(), + &[gen.native_global_string_value(&node.filename)], + ); + } +} + +/// Force update runtime context filename +pub fn force_update_ctx_filename<'ctx, T>(gen: &'ctx LLVMCodeGenContext, node: &'ctx ast::Node) { + if !node.filename.is_empty() { + gen.build_void_call( + &ApiFunc::kclvm_context_set_kcl_filename.name(), + &[gen.native_global_string_value(&node.filename)], + ); + } +} + +/// Update runtime context line and column +pub fn update_ctx_line_col<'ctx, T>(gen: &'ctx LLVMCodeGenContext, node: &'ctx ast::Node) { + let mut current_line = gen.current_line.borrow_mut(); + if node.line != *current_line { + *current_line = node.line; + gen.build_void_call( + &ApiFunc::kclvm_context_set_kcl_line_col.name(), + &[ + gen.native_int_value(node.line as i32), + gen.native_int_value(0), + ], + ); + } +} + +/// Update runtime context line and column +pub fn update_ctx_current_line(gen: &LLVMCodeGenContext) { + let current_line = gen.current_line.borrow_mut(); + gen.build_void_call( + &ApiFunc::kclvm_context_set_kcl_line_col.name(), + &[ + gen.native_int_value(*current_line as i32), + gen.native_int_value(0), + ], + ); +} + +/// Runtime debug print value +#[allow(dead_code)] +pub fn runtime_print_value<'ctx>(gen: &'ctx LLVMCodeGenContext, value: BasicValueEnum<'ctx>) { + gen.build_void_call( + ApiFunc::kclvm_debug_print_value_json_string.name().as_str(), + &[value], + ); +} diff --git a/kclvm/compiler/src/codegen/mod.rs b/kclvm/compiler/src/codegen/mod.rs new file mode 100644 index 000000000..2e1d3b4e5 --- /dev/null +++ b/kclvm/compiler/src/codegen/mod.rs @@ -0,0 +1,49 @@ +//! Copyright 2021 The KCL Authors. All rights reserved. + +mod abi; +pub mod error; +pub mod llvm; +mod traits; + +/// The kclvm runner main function name. +pub(crate) const MODULE_NAME: &str = "kclvm_main"; +/// The kclvm runner main function entry block name. +pub(crate) const ENTRY_NAME: &str = "entry"; +/// The kclvm runtime value type name. +pub(crate) const VALUE_TYPE_NAME: &str = "kclvm_value_ref_t"; +/// The kclvm runtime context type name. +pub(crate) const CONTEXT_TYPE_NAME: &str = "kclvm_context_t"; +/// The kclvm context variable name. +pub(crate) const KCL_CONTEXT_VAR_NAME: &str = "context"; +/// Package init function name suffix +pub(crate) const PKG_INIT_FUNCTION_SUFFIX: &str = "init"; +/// Global level +pub(crate) const GLOBAL_LEVEL: usize = 1; +/// Schema level +pub(crate) const SCHEMA_LEVEL: usize = 2; +/// Global variable alignment +pub(crate) const GLOBAL_VAL_ALIGNMENT: u32 = 8; + +/// CodeGenContext is a trait used by the compiler to emit code to different targets. +pub trait CodeGenContext: traits::ProgramCodeGen { + fn emit(&self, opt: &EmitOptions) -> Result<(), Box>; +} + +/// EmitOptions represents the general emit options +#[derive(Debug, Default)] +pub struct EmitOptions<'a> { + /// Path to load exist module, if not set, create an empty module. + pub from_path: Option<&'a str>, + /// Path to emit module. + pub emit_path: Option<&'a str>, + /// no_link indicates whether to link the generated code of different KCL packages to the same module. + pub no_link: bool, +} + +/// Emit code with the options using CodeGenContext. +pub fn emit_code( + ctx: impl CodeGenContext, + opt: &EmitOptions, +) -> Result<(), Box> { + ctx.emit(opt) +} diff --git a/kclvm/compiler/src/codegen/traits/backend.rs b/kclvm/compiler/src/codegen/traits/backend.rs new file mode 100644 index 000000000..924724de7 --- /dev/null +++ b/kclvm/compiler/src/codegen/traits/backend.rs @@ -0,0 +1,22 @@ +//! Copyright 2021 The KCL Authors. All rights reserved. + +use std::fmt::Debug; + +/// CodeGenObject constrains the behavior that types and values need to satisfy +pub trait CodeGenObject: Copy + PartialEq + Debug {} + +/// BackendTypes define the value and type abstraction. +pub trait BackendTypes { + /// Value abstraction, there may be different implementations corresponding + /// to different compiler backends. + type Value: CodeGenObject; + /// Type abstraction, there may be different implementations corresponding + /// to different compiler backends. + type Type: CodeGenObject; + /// BasicBlock is a SSA basic block abstraction, for the construction of branch, jump, etc. instructions. + type BasicBlock: Copy; + /// Function is a SSA basic function value abstraction. + type Function: Copy; + /// FunctionLet is SSA basic function declaration abstraction. + type FunctionLet: Copy; +} diff --git a/kclvm/compiler/src/codegen/traits/builder.rs b/kclvm/compiler/src/codegen/traits/builder.rs new file mode 100644 index 000000000..ebaaad66a --- /dev/null +++ b/kclvm/compiler/src/codegen/traits/builder.rs @@ -0,0 +1,85 @@ +//! Copyright 2021 The KCL Authors. All rights reserved. + +use crate::codegen::abi::Align; + +use super::BackendTypes; +/// BuilderMethods defines SSA builder methods including calculation, condition, SSA instructions etc. +pub trait BuilderMethods: BackendTypes { + /// SSA append a basic block named `name`. + fn append_block(&self, name: &str) -> Self::BasicBlock; + /// SSA switch to the block. + fn switch_to_block(&self, block: Self::BasicBlock); + /// SSA alloca instruction. + fn alloca(&self, ty: Self::Type, name: &str, align: Option) -> Self::Value; + /// SSA array alloca instruction. + fn array_alloca( + &self, + ty: Self::Type, + len: Self::Value, + name: &str, + align: Align, + ) -> Self::Value; + /// SSA ret instruction. + fn ret_void(&self); + /// SSA ret instruction with returned value. + fn ret(&self, v: Self::Value); + /// SSA br instruction. + fn br(&self, dest: Self::BasicBlock); + /// SSA cond br instruction. + fn cond_br(&self, cond: Self::Value, then_bb: Self::BasicBlock, else_bb: Self::BasicBlock); + /// SSA select instruction. + fn select( + &self, + cond: Self::Value, + then_val: Self::Value, + else_val: Self::Value, + ) -> Self::Value; + /// SSA va arg instruction. + fn va_arg(&self, list: Self::Value, ty: Self::Type) -> Self::Value; + /// SSA extract element instruction. + fn extract_element(&self, vec: Self::Value, idx: Self::Value) -> Self::Value; + /// SSA extract value instruction. + fn extract_value(&self, agg_val: Self::Value, idx: u32) -> Self::Value; + /// SSA insert value instruction. + fn insert_value(&self, agg_val: Self::Value, elt: Self::Value, idx: u32) -> Self::Value; + /// SSA function invoke instruction. + fn invoke( + &self, + ty: Self::Type, + fn_value: Self::Function, + args: &[Self::Value], + then: Self::BasicBlock, + catch: Self::BasicBlock, + ) -> Self::Value; + /// SSA function call instruction. + fn call(&self, ty: Self::Type, fn_value: Self::Function, args: &[Self::Value]) -> Self::Value; + /// SSA load instruction. + fn load(&self, ptr: Self::Value, name: &str) -> Self::Value; + /// SSA store instruction. + fn store(&self, ptr: Self::Value, val: Self::Value); + /// SSA gep instruction. + fn gep(&self, ty: Self::Type, ptr: Self::Value, indices: &[Self::Value]) -> Self::Value; + /// SSA inbounds gep instruction. + fn inbounds_gep( + &self, + ty: Self::Type, + ptr: Self::Value, + indices: &[Self::Value], + ) -> Self::Value; + /// SSA struct gep instruction. + fn struct_gep(&self, ty: Self::Type, ptr: Self::Value, idx: u32) -> Self::Value; + /// SSA cast pointer to int. + fn ptr_to_int(&self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + /// SSA cast int to pointer. + fn int_to_ptr(&self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + /// SSA bit cast. + fn bit_cast(&self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + /// SSA int cast. + fn int_cast(&self, val: Self::Value, dest_ty: Self::Type, is_signed: bool) -> Self::Value; + /// SSA pointer cast. + fn ptr_cast(&self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + /// Lookup a known function named `name`. + fn lookup_function(&self, name: &str) -> Self::Function; + /// Add a function named `name`. + fn add_function(&self, name: &str) -> Self::Function; +} diff --git a/kclvm/compiler/src/codegen/traits/mod.rs b/kclvm/compiler/src/codegen/traits/mod.rs new file mode 100644 index 000000000..468680caf --- /dev/null +++ b/kclvm/compiler/src/codegen/traits/mod.rs @@ -0,0 +1,26 @@ +//! Copyright 2021 The KCL Authors. All rights reserved. + +mod backend; +mod builder; +mod r#type; +mod value; + +pub use backend::*; +pub use builder::*; +pub use r#type::*; +pub use value::*; + +pub trait ProgramCodeGen: TypeCodeGen + ValueCodeGen + BuilderMethods { + /// Current package path + fn current_pkgpath(&self) -> String; + /// Current filename + fn current_filename(&self) -> String; + /// Init a scope named `pkgpath` with all builtin functions + fn init_scope(&self, pkgpath: &str); + /// Get the scope level + fn scope_level(&self) -> usize; + /// Enter scope + fn enter_scope(&self); + /// Leave scope + fn leave_scope(&self); +} diff --git a/kclvm/compiler/src/codegen/traits/type.rs b/kclvm/compiler/src/codegen/traits/type.rs new file mode 100644 index 000000000..f1ed73d5f --- /dev/null +++ b/kclvm/compiler/src/codegen/traits/type.rs @@ -0,0 +1,68 @@ +//! Copyright 2021 The KCL Authors. All rights reserved. + +use crate::codegen::abi::AddressSpace; +use crate::codegen::{CONTEXT_TYPE_NAME, VALUE_TYPE_NAME}; + +use super::BackendTypes; + +/// BaseTypeMethods defines all native base type APIs, e.g. i8/i16/f32/f64 types, etc. +pub trait BaseTypeMethods: BackendTypes { + /// Native i8 type + fn i8_type(&self) -> Self::Type; + /// Native i16 type + fn i16_type(&self) -> Self::Type; + /// Native i32 type + fn i32_type(&self) -> Self::Type; + /// Native i64 type + fn i64_type(&self) -> Self::Type; + /// Native i128 type + fn i128_type(&self) -> Self::Type; + /// Native f32 type + fn f32_type(&self) -> Self::Type; + /// Native f64 type + fn f64_type(&self) -> Self::Type; + /// Native struct type. + fn struct_type(&self, els: &[Self::Type], packed: bool) -> Self::Type; + /// Native pointer type of `ty`. + fn ptr_type_to(&self, ty: Self::Type) -> Self::Type; + /// Native pointer type of `ty` with the address space. + fn ptr_type_to_ext(&self, ty: Self::Type, address_space: AddressSpace) -> Self::Type; + /// Native array element type. + fn element_type(&self, ty: Self::Type) -> Self::Type; + /// Returns the number of elements in `self`. + fn vector_length(&self, ty: Self::Type) -> usize; + /// Retrieves the bit width of the float type `self`. + fn float_width(&self, ty: Self::Type) -> usize; + /// Retrieves the bit width of the integer type `self`. + fn int_width(&self, ty: Self::Type) -> usize; + /// Get the value type. + fn val_type(&self, v: Self::Value) -> Self::Type; + /// Native function type + fn function_let(&self, args: &[Self::Type], ret: Self::Type) -> Self::FunctionLet; +} + +/// DerivedTypeMethods defines all extended type APIs. +pub trait DerivedTypeMethods: BaseTypeMethods { + /// Lookup a intrinsic type by the name. + fn get_intrinsic_type(&self, name: &str) -> Self::Type; + /// Get the value pointer type. + fn value_ptr_type(&self) -> Self::Type { + self.ptr_type_to(self.get_intrinsic_type(VALUE_TYPE_NAME)) + } + /// Get the context pointer type. + fn context_ptr_type(&self) -> Self::Type { + self.ptr_type_to(self.get_intrinsic_type(CONTEXT_TYPE_NAME)) + } + /// Get the function type. + fn function_type(&self) -> Self::FunctionLet { + let value_ptr_type = self.value_ptr_type(); + let context_ptr_type = self.context_ptr_type(); + self.function_let( + &[context_ptr_type, value_ptr_type, value_ptr_type], + value_ptr_type, + ) + } +} + +/// TypeCodeGen defines all type APIs. +pub trait TypeCodeGen: DerivedTypeMethods {} diff --git a/kclvm/compiler/src/codegen/traits/value.rs b/kclvm/compiler/src/codegen/traits/value.rs new file mode 100644 index 000000000..72f808eeb --- /dev/null +++ b/kclvm/compiler/src/codegen/traits/value.rs @@ -0,0 +1,191 @@ +//! Copyright 2021 The KCL Authors. All rights reserved. + +use super::BackendTypes; + +/// ValueMethods defines all value APIs. +pub trait ValueMethods: BackendTypes { + /// Construct a 64-bit int value using i64 + fn int_value(&self, v: i64) -> Self::Value; + /// Construct a 64-bit float value using f64 + fn float_value(&self, v: f64) -> Self::Value; + /// Construct a string value using &str + fn string_value(&self, v: &str) -> Self::Value; + /// Construct a bool value + fn bool_value(&self, v: bool) -> Self::Value; + /// Construct a None value + fn none_value(&self) -> Self::Value; + /// Construct a Undefined value + fn undefined_value(&self) -> Self::Value; + /// Construct a empty list value + fn list_value(&self) -> Self::Value; + /// Construct a list value with `n` elements + fn list_values(&self, values: &[Self::Value]) -> Self::Value; + /// Construct a empty dict value. + fn dict_value(&self) -> Self::Value; + /// Construct a unit value. + fn unit_value(&self, v: f64, raw: i64, unit: &str) -> Self::Value; + /// Construct a function value using a native function value. + fn function_value(&self, function: Self::Function) -> Self::Value; + /// Construct a closure function value with the closure variable. + fn closure_value(&self, function: Self::Function, closure: Self::Value) -> Self::Value; + /// Construct a structure function value using native functions. + fn struct_function_value( + &self, + functions: &[Self::Function], + runtime_type: &str, + ) -> Self::Value; + /// Construct a builtin function value using the function name. + fn builtin_function_value(&self, function_name: &str) -> Self::Value; + /// Get a global value pointer named `name`. + fn global_value_ptr(&self, name: &str) -> Self::Value; + /// Get the global runtime context pointer. + fn global_ctx_ptr(&self) -> Self::Value; +} + +/// DerivedValueCalculationMethods defines all value base calculation APIs. +pub trait ValueCalculationMethods: BackendTypes { + // calculations + /// lhs + rhs + fn add(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs - rhs + fn sub(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs * rhs + fn mul(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs / rhs + fn div(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs // rhs + fn floor_div(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs % rhs + fn r#mod(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs ** rhs + fn pow(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs << rhs + fn bit_lshift(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs >> rhs + fn bit_rshift(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs & rhs + fn bit_and(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs | rhs + fn bit_or(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs ^ rhs + fn bit_xor(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs and rhs + fn logic_and(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs or rhs + fn logic_or(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs == rhs + fn cmp_equal_to(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs != rhs + fn cmp_not_equal_to(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs > rhs + fn cmp_greater_than(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs >= rhs + fn cmp_greater_than_or_equal(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs < rhs + fn cmp_less_than(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs <= rhs + fn cmp_less_than_or_equal(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs as rhs + fn r#as(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs is rhs + fn is(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs is not rhs + fn is_not(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs in rhs + fn r#in(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// lhs not in rhs + fn not_in(&self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; +} + +/// DerivedValueCalculationMethods based ValueCalculationMethods defines all value derived +/// operation APIs such as deep copy, union and collection operations. +pub trait DerivedValueCalculationMethods: ValueMethods + ValueCalculationMethods { + /// Value subscript a[b] + fn value_subscript(&self, value: Self::Value, item: Self::Value) -> Self::Value; + /// Value is truth function, return i1 value. + fn value_is_truthy(&self, value: Self::Value) -> Self::Value; + /// Value deep copy + fn value_deep_copy(&self, value: Self::Value) -> Self::Value; + /// value_union unions two collection elements. + fn value_union(&self, lhs: Self::Value, rhs: Self::Value); + // List get the item using the index. + fn list_get(&self, list: Self::Value, index: Self::Value) -> Self::Value; + // List set the item using the index. + fn list_set(&self, list: Self::Value, index: Self::Value, value: Self::Value); + // List slice. + fn list_slice( + &self, + list: Self::Value, + start: Self::Value, + stop: Self::Value, + step: Self::Value, + ) -> Self::Value; + /// Append a item into the list. + fn list_append(&self, list: Self::Value, item: Self::Value); + /// Append a list item and unpack it into the list. + fn list_append_unpack(&self, list: Self::Value, item: Self::Value); + /// List value pop + fn list_pop(&self, list: Self::Value) -> Self::Value; + /// List value pop first + fn list_pop_first(&self, list: Self::Value) -> Self::Value; + /// List clear value + fn list_clear(&self, list: Self::Value); + /// Return number of occurrences of the list value. + fn list_count(&self, list: Self::Value, item: Self::Value) -> Self::Value; + /// Return first index of the list value. Panic if the value is not present. + fn list_find(&self, list: Self::Value, item: Self::Value) -> Self::Value; + /// Insert object before index of the list value. + fn list_insert(&self, list: Self::Value, index: Self::Value, value: Self::Value); + /// List length. + fn list_len(&self, list: Self::Value) -> Self::Value; + /// Dict get the value of the key. + fn dict_get(&self, dict: Self::Value, key: Self::Value) -> Self::Value; + /// Dict set the value of the key. + fn dict_set(&self, dict: Self::Value, key: Self::Value, value: Self::Value); + /// Return all dict keys. + fn dict_keys(&self, dict: Self::Value) -> Self::Value; + /// Return all dict values. + fn dict_values(&self, dict: Self::Value) -> Self::Value; + /// Dict clear value. + fn dict_clear(&self, dict: Self::Value); + /// Dict pop the value of the key. + fn dict_pop(&self, dict: Self::Value, key: Self::Value) -> Self::Value; + /// Dict length. + fn dict_len(&self, dict: Self::Value) -> Self::Value; + /// Insert a dict entry including key, value, op and insert_index into the dict. + /// and the type of key is `Self::Value` + fn dict_insert_with_key_value( + &self, + dict: Self::Value, + key: Self::Value, + value: Self::Value, + op: i32, + insert_index: i32, + ); + /// Insert a dict entry including key, value, op and insert_index into the dict, + /// and the type of key is `&str` + fn dict_insert( + &self, + dict: Self::Value, + key: &str, + value: Self::Value, + op: i32, + insert_index: i32, + ) { + self.dict_insert_with_key_value(dict, self.string_value(key), value, op, insert_index); + } + /// Insert a dict entry with the override = attribute operator including key, value into the dict. + fn dict_insert_override_item(&self, dict: Self::Value, key: &str, value: Self::Value) { + self.dict_insert(dict, key, value, 1, -1); + } + /// Dict contains key. + fn dict_contains_key(&self, dict: Self::Value, key: &str) -> Self::Value { + self.r#in(self.string_value(key), dict) + } +} + +/// ValueCodeGen defines all value APIs. +pub trait ValueCodeGen: + ValueMethods + ValueCalculationMethods + DerivedValueCalculationMethods +{ +} diff --git a/kclvm/compiler/src/lib.rs b/kclvm/compiler/src/lib.rs new file mode 100644 index 000000000..949b320cd --- /dev/null +++ b/kclvm/compiler/src/lib.rs @@ -0,0 +1,9 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod codegen; +pub mod value; + +#[macro_use] +mod macros; + +extern crate kclvm_error; diff --git a/kclvm/compiler/src/macros.rs b/kclvm/compiler/src/macros.rs new file mode 100644 index 000000000..cc47ea8c4 --- /dev/null +++ b/kclvm/compiler/src/macros.rs @@ -0,0 +1,22 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +#[macro_export] +macro_rules! check_backtrack_stop { + ($ctx: expr) => { + if let Some(backtrack_meta) = $ctx.backtrack_meta.borrow_mut().as_ref() { + if backtrack_meta.stop { + return $ctx.ok_result(); + } + } + }; +} + +#[macro_export] +macro_rules! pkgpath_without_prefix { + ($pkgpath: expr) => { + match $pkgpath.strip_prefix('@') { + Some(v) => v.to_string(), + None => $pkgpath.to_string(), + } + }; +} diff --git a/kclvm/compiler/src/value/lambda.rs b/kclvm/compiler/src/value/lambda.rs new file mode 100644 index 000000000..5948071d2 --- /dev/null +++ b/kclvm/compiler/src/value/lambda.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub const LAMBDA_NAME: &str = "kclvm_lambda"; +pub const LAMBDA_CLOSURE: &str = "$lambda_closure"; diff --git a/kclvm/compiler/src/value/mod.rs b/kclvm/compiler/src/value/mod.rs new file mode 100644 index 000000000..3d10ce495 --- /dev/null +++ b/kclvm/compiler/src/value/mod.rs @@ -0,0 +1,7 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +mod lambda; +pub use self::lambda::*; + +mod schema; +pub use self::schema::*; diff --git a/kclvm/compiler/src/value/schema.rs b/kclvm/compiler/src/value/schema.rs new file mode 100644 index 000000000..ab837b45b --- /dev/null +++ b/kclvm/compiler/src/value/schema.rs @@ -0,0 +1,37 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub const SCHEMA_NAME: &str = "$kclvm_schema"; +pub const SCHEMA_ATTR_NAME: &str = "$kclvm_schema_attr"; +pub const SCHEMA_CHECK_BLOCK_NAME: &str = "$kclvm_schema_check_block"; +pub const SCHEMA_SELF_NAME: &str = "$schema_self"; +pub const SCHEMA_CONFIG_NAME: &str = "$schema_config"; +pub const SCHEMA_CONFIG_META_NAME: &str = "$schema_config_meta"; +pub const SCHEMA_CAL_MAP: &str = "$schema_cal_map"; +pub const SCHEMA_ARGS: &str = "$schema_args"; +pub const SCHEMA_KWARGS: &str = "$schema_kwargs"; +pub const SCHEMA_RUNTIME_TYPE: &str = "$schema_runtime_type"; +pub const BACKTRACK_LEVEL_MAP: &str = "$backtrack_level_map"; +pub const BACKTRACK_CACHE: &str = "$backtrack_cache"; + +/// KCL schema type +pub struct SchemaType { + pub name: String, + pub pkgpath: String, + pub runtime_type: String, + pub is_mixin: bool, + pub is_protocol: bool, + pub is_rule: bool, +} + +impl SchemaType { + pub fn new(name: &str, pkgpath: &str, runtime_type: &str, is_mixin: bool) -> SchemaType { + SchemaType { + name: name.to_string(), + pkgpath: pkgpath.to_string(), + runtime_type: runtime_type.to_string(), + is_mixin, + is_protocol: false, + is_rule: false, + } + } +} diff --git a/kclvm/config/Cargo.lock b/kclvm/config/Cargo.lock new file mode 100644 index 000000000..94e9cd237 --- /dev/null +++ b/kclvm/config/Cargo.lock @@ -0,0 +1,384 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + +[[package]] +name = "fslock" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "kclvm-config" +version = "0.1.0" +dependencies = [ + "ahash", + "chrono", + "fslock", + "glob", + "indexmap", + "kclvm-version", + "pathdiff", + "ron", + "rust-crypto", + "serde", + "serde_yaml", + "toml", +] + +[[package]] +name = "kclvm-version" +version = "0.1.0" + +[[package]] +name = "libc" +version = "0.2.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "proc-macro2" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "ron" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86018df177b1beef6c7c8ef949969c4f7cb9a9344181b92486b23c79995bdaa4" +dependencies = [ + "base64", + "bitflags", + "serde", +] + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +dependencies = [ + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_yaml" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af" +dependencies = [ + "dtoa", + "indexmap", + "serde", + "yaml-rust", +] + +[[package]] +name = "syn" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/kclvm/config/Cargo.toml b/kclvm/config/Cargo.toml new file mode 100644 index 000000000..10bab2ee0 --- /dev/null +++ b/kclvm/config/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "kclvm-config" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1", features = ["derive"] } +serde_yaml = "0.8.7" +indexmap = "1.0" +ahash = "0.7.2" +toml = "0.5.8" +ron = "0.7.0" +chrono = "0.4.19" +rust-crypto = "^0.2" +glob = "0.3.0" +fslock = "0.2.1" +pathdiff = "0.2.1" + +kclvm-version = {path = "../version", version = "0.1.0"} diff --git a/kclvm/config/src/cache.rs b/kclvm/config/src/cache.rs new file mode 100644 index 000000000..3a286789a --- /dev/null +++ b/kclvm/config/src/cache.rs @@ -0,0 +1,224 @@ +// Copyright 2021 The KCL Authors. All rights reserved. +extern crate chrono; +use super::modfile::KCL_FILE_SUFFIX; +use crypto::digest::Digest; +use crypto::md5::Md5; +use fslock::LockFile; +use serde::{de::DeserializeOwned, Serialize}; +use std::collections::HashMap; +use std::error; +use std::fs::{create_dir_all, File}; +use std::io::{Read, Write}; +use std::path::Path; + +use kclvm_version as version; + +const LOCK_SUFFIX: &str = ".lock"; +const DEFAULT_CACHE_DIR: &str = ".kclvm/cache"; +const CACHE_INFO_FILENAME: &str = "info"; +const KCL_SUFFIX_PATTERN: &str = "*.k"; + +pub type CacheInfo = String; +pub type Cache = HashMap; + +#[allow(dead_code)] +pub struct CacheOption { + cache_dir: String, +} + +impl Default for CacheOption { + fn default() -> Self { + Self { + cache_dir: DEFAULT_CACHE_DIR.to_string(), + } + } +} + +/// Load pkg cache. +pub fn load_pkg_cache(root: &str, pkgpath: &str, option: CacheOption) -> Option +where + T: DeserializeOwned + Default, +{ + if root.is_empty() || pkgpath.is_empty() { + None + } else { + let filename = get_cache_filename(root, pkgpath, Some(&option.cache_dir)); + if !Path::new(&filename).exists() { + None + } else { + // Compare the md5 using cache + let real_path = get_pkg_realpath_from_pkgpath(root, pkgpath); + if Path::new(&real_path).exists() { + let cache_info = read_info_cache(root, Some(&option.cache_dir)); + let relative_path = real_path.replacen(root, ".", 1); + match cache_info.get(&relative_path) { + Some(path_info_in_cache) => { + if get_cache_info(&real_path).ne(path_info_in_cache) { + return None; + } + } + None => return None, + }; + } + load_data_from_file(&filename) + } + } +} + +/// Save pkg cache. +pub fn save_pkg_cache(root: &str, pkgpath: &str, data: T, option: CacheOption) +where + T: Serialize, +{ + if root.is_empty() || pkgpath.is_empty() { + return; + } + let dst_filename = get_cache_filename(root, pkgpath, Some(&option.cache_dir)); + let real_path = get_pkg_realpath_from_pkgpath(root, pkgpath); + if Path::new(&real_path).exists() { + write_info_cache(root, Some(&option.cache_dir), &real_path).unwrap(); + } + let cache_dir = get_cache_dir(root, Some(&option.cache_dir)); + create_dir_all(&cache_dir).unwrap(); + let tmp_filename = temp_file(&cache_dir, pkgpath); + save_data_to_file(&dst_filename, &tmp_filename, data) +} + +#[inline] +fn get_cache_dir(root: &str, cache_dir: Option<&str>) -> String { + let cache_dir = cache_dir.or(Some(DEFAULT_CACHE_DIR)).unwrap(); + format!( + "{}/{}/{}-{}", + root, + cache_dir, + version::VERSION, + version::CHECK_SUM + ) +} + +#[inline] +#[allow(dead_code)] +fn get_cache_filename(root: &str, pkgpath: &str, cache_dir: Option<&str>) -> String { + let cache_dir = cache_dir.or(Some(DEFAULT_CACHE_DIR)).unwrap(); + format!( + "{}/{}/{}-{}/{}", + root, + cache_dir, + version::VERSION, + version::CHECK_SUM, + pkgpath + ) +} + +#[inline] +fn get_cache_info_filename(root: &str, cache_dir: Option<&str>) -> String { + let cache_dir = cache_dir.or(Some(DEFAULT_CACHE_DIR)).unwrap(); + format!( + "{}/{}/{}-{}/{}", + root, + cache_dir, + version::VERSION, + version::CHECK_SUM, + CACHE_INFO_FILENAME + ) +} + +/// Read the cache if it exists and is well formed. +/// If it is not well formed, the call to write_info_cache later should resolve the issue. +pub fn read_info_cache(root: &str, cache_dir: Option<&str>) -> Cache { + let cache_file = get_cache_info_filename(root, cache_dir); + if !Path::new(&cache_file).exists() { + return Cache::default(); + } + let file = File::open(cache_file).unwrap(); + match ron::de::from_reader(file) { + Ok(cache) => cache, + Err(_) => HashMap::new(), + } +} + +/// Update the cache info file. +pub fn write_info_cache( + root: &str, + cache_name: Option<&str>, + filepath: &str, +) -> Result<(), Box> { + let dst_filename = get_cache_info_filename(root, cache_name); + let cache_dir = get_cache_dir(root, cache_name); + let path = Path::new(&cache_dir); + create_dir_all(path).unwrap(); + let relative_path = filepath.replacen(root, ".", 1); + let cache_info = get_cache_info(filepath); + let tmp_filename = temp_file(&cache_dir, ""); + let mut lock_file = LockFile::open(&format!("{}{}", dst_filename, LOCK_SUFFIX)).unwrap(); + lock_file.lock().unwrap(); + let mut cache = read_info_cache(root, cache_name); + cache.insert(relative_path, cache_info); + let mut file = File::create(&tmp_filename).unwrap(); + file.write_all(&ron::ser::to_string(&cache).unwrap().as_bytes()).unwrap(); + std::fs::rename(&tmp_filename, &dst_filename).unwrap(); + lock_file.unlock().unwrap(); + Ok(()) +} + +/// Return the information used to check if a file or path is already changed or not. +fn get_cache_info(path_str: &str) -> CacheInfo { + let path = Path::new(path_str); + let mut md5 = Md5::new(); + if path.is_file() { + let mut file = File::open(path_str).unwrap(); + let mut buf: Vec = vec![]; + file.read_to_end(&mut buf).unwrap(); + md5.input(buf.as_slice()); + } else { + let pattern = format!("{}/{}", path_str, KCL_SUFFIX_PATTERN); + for file in glob::glob(&pattern).unwrap().flatten() { + let mut file = File::open(file).unwrap(); + let mut buf: Vec = vec![]; + file.read_to_end(&mut buf).unwrap(); + md5.input(buf.as_slice()); + } + } + md5.result_str() +} + +pub fn get_pkg_realpath_from_pkgpath(root: &str, pkgpath: &str) -> String { + let filepath = format!("{}/{}", root, pkgpath.replace('.', "/")); + let filepath_with_suffix = format!("{}{}", filepath, KCL_FILE_SUFFIX); + if Path::new(&filepath_with_suffix).is_file() { + filepath_with_suffix + } else { + filepath + } +} + +pub fn load_data_from_file(filename: &str) -> Option +where + T: DeserializeOwned + Default, +{ + let file = File::open(filename); + if let Ok(file) = file { + ron::de::from_reader(file).ok() + } else { + None + } +} + +pub fn save_data_to_file(dst_filename: &str, tmp_filename: &str, data: T) +where + T: Serialize, +{ + let mut lock_file = LockFile::open(&format!("{}{}", dst_filename, LOCK_SUFFIX)).unwrap(); + lock_file.lock().unwrap(); + let file = File::create(tmp_filename).unwrap(); + ron::ser::to_writer(file, &data).unwrap(); + std::fs::rename(tmp_filename, dst_filename).unwrap(); + lock_file.unlock().unwrap(); +} + +#[inline] +fn temp_file(cache_dir: &str, pkgpath: &str) -> String { + let timestamp = chrono::Local::now().timestamp_nanos(); + let id = std::process::id(); + format!("{}/{}.{}.{}.tmp", cache_dir, pkgpath, id, timestamp) +} diff --git a/kclvm/config/src/lib.rs b/kclvm/config/src/lib.rs new file mode 100644 index 000000000..fc6c23d66 --- /dev/null +++ b/kclvm/config/src/lib.rs @@ -0,0 +1,6 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod cache; +pub mod modfile; +pub mod settings; +pub mod vfs; diff --git a/kclvm/config/src/modfile.rs b/kclvm/config/src/modfile.rs new file mode 100644 index 000000000..246fc045c --- /dev/null +++ b/kclvm/config/src/modfile.rs @@ -0,0 +1,158 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use serde::Deserialize; +use std::io::Read; +use toml; + +pub const KCL_MOD_FILE: &str = "kcl.mod"; +pub const KCL_FILE_SUFFIX: &str = ".k"; +pub const KCL_MOD_PATH_ENV: &str = "${KCL_MOD}"; + +#[allow(dead_code)] +#[derive(Default, Deserialize)] +pub struct KCLModFile { + pub root: Option, + pub root_pkg: Option, + pub build: Option, + pub expected: Option, +} + +#[allow(dead_code)] +#[derive(Default, Deserialize)] +pub struct KCLModFileBuildSection { + pub enable_pkg_cache: Option, + pub cached_pkg_prefix: Option, + pub target: Option, +} + +#[allow(dead_code)] +#[derive(Default, Deserialize)] +pub struct KCLModFileExpectedSection { + pub min_build_time: Option, + pub max_build_time: Option, + pub kclvm_version: Option, + pub kcl_plugin_version: Option, + pub global_version: Option, +} + +pub fn get_pkg_root_from_paths(file_paths: &[String]) -> Result { + if file_paths.is_empty() { + return Err("no files".to_string()); + } + + let mut m = std::collections::HashMap::::new(); + let mut last_root = "".to_string(); + for s in file_paths { + if s.contains(KCL_MOD_PATH_ENV) { + continue; + } + if let Some(root) = get_pkg_root(s) { + m.insert(root.clone(), root.clone()); + last_root = root.clone(); + } + } + if m.is_empty() { + return Err("not found".to_string()); + } + if m.len() == 1 { + return Ok(last_root); + } + + Err(format!("conflict kcl.mod file paths: {:?}", m)) +} + +pub fn get_pkg_root(k_file_path: &str) -> Option { + if k_file_path.is_empty() { + return None; + } + // # search by kcl.mod file + if let Ok(module_path) = std::path::Path::new(k_file_path).canonicalize() { + let mut module_path = module_path; + while module_path.exists() { + let kcl_mod_path = module_path.join(KCL_MOD_FILE); + if kcl_mod_path.exists() && kcl_mod_path.is_file() { + return Some(module_path.to_str().unwrap().to_string()); + } + if let Some(path) = module_path.parent() { + module_path = path.to_path_buf(); + } else { + break; + } + } + } + if k_file_path.ends_with(KCL_FILE_SUFFIX) { + if let Ok(path) = std::path::Path::new(k_file_path).canonicalize() { + if let Some(path) = path.parent() { + return Some(path.to_str().unwrap().to_string()); + } + } + } + None +} + +pub fn load_mod_file(root: &str) -> KCLModFile { + let k_mod_file_path = std::path::Path::new(root).join(KCL_MOD_FILE); + if !k_mod_file_path.exists() { + return KCLModFile::default(); + } + let mut file = std::fs::File::open(k_mod_file_path.to_str().unwrap()).unwrap(); + let mut buffer: Vec = vec![]; + file.read_to_end(&mut buffer).unwrap(); + toml::from_slice(buffer.as_slice()).unwrap() +} + +#[cfg(test)] +mod modfile_test { + use crate::modfile::*; + + const TEST_ROOT: &str = "./src/testdata/"; + const SETTINGS_FILE: &str = "./src/testdata/kcl.mod"; + + #[test] + fn test_get_pkg_root() { + let root = get_pkg_root(SETTINGS_FILE); + assert!(root.is_some()); + let expected_root = std::path::Path::new(TEST_ROOT).canonicalize().unwrap(); + let expected = expected_root.to_str().unwrap(); + assert_eq!(root.unwrap().as_str(), expected); + } + + #[test] + fn test_load_mod_file() { + let kcl_mod = load_mod_file(TEST_ROOT); + assert_eq!( + kcl_mod.build.as_ref().unwrap().enable_pkg_cache.unwrap(), + true + ); + assert_eq!( + kcl_mod + .build + .as_ref() + .unwrap() + .cached_pkg_prefix + .as_ref() + .unwrap(), + "pkg.path" + ); + assert_eq!( + kcl_mod + .expected + .as_ref() + .unwrap() + .kclvm_version + .as_ref() + .unwrap(), + "v0.3.0" + ); + assert_eq!( + kcl_mod + .expected + .as_ref() + .unwrap() + .kcl_plugin_version + .as_ref() + .unwrap(), + "v0.2.0" + ); + } +} diff --git a/kclvm/config/src/settings.rs b/kclvm/config/src/settings.rs new file mode 100644 index 000000000..8af9b8a8a --- /dev/null +++ b/kclvm/config/src/settings.rs @@ -0,0 +1,292 @@ +// Copyright 2021 The KCL Authors. All rights reserved. +use serde::{Deserialize, Serialize}; + +const INVALID_KCL_OPTIONS_MSG: &str = "invalid kcl_options"; +const SETTINGS_FILE_PARA: &str = "-Y"; +const ARGUMENTS_PARA: &str = "-D"; +const OUTPUT_PARA: &str = "-o"; +const OVERRIDES_PARA: &str = "-O"; +const PATH_SELECTOR_PARA: &str = "-S"; +const STRICT_RANGE_CHECK_PARA: &str = "-r"; +const DISABLE_NONE_PARA: &str = "-n"; +const VERBOSE_PARA: &str = "-v"; +const DEBUG_PARA: &str = "-d"; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SettingsFile { + pub kcl_cli_configs: Option, + pub kcl_options: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Config { + pub files: Option>, + pub file: Option>, + pub output: Option, + pub overrides: Option>, + pub path_selector: Option>, + pub strict_range_check: Option, + pub disable_none: Option, + pub verbose: Option, + pub debug: Option, +} + +impl SettingsFile { + pub fn new() -> Self { + SettingsFile { + kcl_cli_configs: Some(Config { + file: Some(vec![]), + files: Some(vec![]), + output: None, + overrides: Some(vec![]), + path_selector: Some(vec![]), + strict_range_check: Some(false), + disable_none: Some(false), + verbose: Some(0), + debug: Some(false), + }), + kcl_options: Some(vec![]), + } + } +} + +impl Default for SettingsFile { + fn default() -> Self { + Self::new() + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct KeyValuePair { + key: String, + value: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct TestSettingsFile { + kcl_options: Option, +} + +pub fn load_file(filename: &str) -> SettingsFile { + let f = std::fs::File::open(filename).unwrap(); + let data: SettingsFile = serde_yaml::from_reader(f).unwrap(); + data +} + +macro_rules! set_if { + ($result: expr, $attr: ident, $setting: expr) => { + if $setting.$attr.is_some() { + $result.$attr = $setting.$attr.clone(); + } + }; +} + +pub fn merge_settings(settings: &[SettingsFile]) -> SettingsFile { + let mut result = SettingsFile::new(); + for setting in settings { + if let Some(kcl_cli_configs) = &setting.kcl_cli_configs { + let mut result_kcl_cli_configs = result.kcl_cli_configs.as_mut().unwrap(); + set_if!(result_kcl_cli_configs, files, kcl_cli_configs); + set_if!(result_kcl_cli_configs, file, kcl_cli_configs); + set_if!(result_kcl_cli_configs, output, kcl_cli_configs); + set_if!(result_kcl_cli_configs, overrides, kcl_cli_configs); + set_if!(result_kcl_cli_configs, path_selector, kcl_cli_configs); + set_if!(result_kcl_cli_configs, strict_range_check, kcl_cli_configs); + set_if!(result_kcl_cli_configs, disable_none, kcl_cli_configs); + set_if!(result_kcl_cli_configs, verbose, kcl_cli_configs); + set_if!(result_kcl_cli_configs, debug, kcl_cli_configs); + // debug: Option, + } + if let Some(kcl_options) = &setting.kcl_options { + let result_kcl_options = result.kcl_options.as_mut().unwrap(); + for option in kcl_options { + result_kcl_options.push(option.clone()); + } + } + } + result +} + +pub fn decode_test_format_settings_file(filename: &str, workdir: &str) -> SettingsFile { + let f = std::fs::File::open(filename).unwrap(); + let data: TestSettingsFile = serde_yaml::from_reader(f).unwrap(); + let mut settings_file: SettingsFile = SettingsFile::new(); + match data.kcl_options { + Some(ref arg) => { + let args: Vec<&str> = arg.split(' ').collect(); + let mut i = 0; + while i < args.len() { + let arg = <&str>::clone(args.get(i).unwrap()); + match arg { + SETTINGS_FILE_PARA => { + i += 1; + let mut settings_vec: Vec = vec![settings_file]; + while i < args.len() && !args.get(i).unwrap().starts_with('-') { + let para = args + .get(i) + .unwrap_or_else(|| panic!("{}: {}", INVALID_KCL_OPTIONS_MSG, arg)); + let settings = load_file( + std::path::Path::new(workdir).join(para).to_str().unwrap(), + ); + settings_vec.push(settings); + } + settings_file = merge_settings(&settings_vec); + } + ARGUMENTS_PARA => { + i += 1; + let para = args + .get(i) + .unwrap_or_else(|| panic!("{}: {}", INVALID_KCL_OPTIONS_MSG, arg)); + let para = String::from(*para); + let paras: Vec<&str> = para.split('=').collect(); + if paras.len() != 2 { + panic!("{}: {}", INVALID_KCL_OPTIONS_MSG, arg); + } + (*settings_file.kcl_options.as_mut().unwrap()).push(KeyValuePair { + key: String::from(*paras.get(0).unwrap()), + value: String::from(*paras.get(1).unwrap()), + }); + } + DEBUG_PARA => { + (*settings_file.kcl_cli_configs.as_mut().unwrap()).debug = Some(true) + } + STRICT_RANGE_CHECK_PARA => { + (*settings_file.kcl_cli_configs.as_mut().unwrap()).strict_range_check = + Some(true) + } + DISABLE_NONE_PARA => { + (*settings_file.kcl_cli_configs.as_mut().unwrap()).disable_none = Some(true) + } + OVERRIDES_PARA => { + i += 1; + let para = args + .get(i) + .unwrap_or_else(|| panic!("{}: {}", INVALID_KCL_OPTIONS_MSG, arg)); + (*settings_file.kcl_cli_configs.as_mut().unwrap()) + .overrides + .as_mut() + .unwrap() + .push(para.to_string()); + } + PATH_SELECTOR_PARA => { + i += 1; + let para = args + .get(i) + .unwrap_or_else(|| panic!("{}: {}", INVALID_KCL_OPTIONS_MSG, arg)); + (*settings_file.kcl_cli_configs.as_mut().unwrap()) + .path_selector + .as_mut() + .unwrap() + .push(para.to_string()); + } + VERBOSE_PARA => { + let verbose = (*settings_file.kcl_cli_configs.as_mut().unwrap()) + .verbose + .as_mut() + .unwrap(); + *verbose += 1; + } + OUTPUT_PARA => { + i += 1; + let para = args + .get(i) + .unwrap_or_else(|| panic!("{}: {}", INVALID_KCL_OPTIONS_MSG, arg)); + (*settings_file.kcl_cli_configs.as_mut().unwrap()).output = + Some(para.to_string()); + } + _ => { + if arg.ends_with(".k") { + (*settings_file + .kcl_cli_configs + .as_mut() + .unwrap() + .files + .as_mut() + .unwrap()) + .push(String::from(arg)); + } + } + } + i += 1; + } + settings_file + } + None => settings_file, + } +} + +#[cfg(test)] +mod settings_test { + use crate::settings::*; + + const SETTINGS_FILE: &str = "./src/testdata/settings.yaml"; + + #[test] + fn test_settings_load_file() { + let settings = load_file(SETTINGS_FILE); + assert!(settings.kcl_cli_configs.is_some()); + assert!(settings.kcl_options.is_some()); + if let Some(kcl_cli_configs) = settings.kcl_cli_configs { + let files = vec![ + String::from("../main.k"), + String::from("./before/base.k"), + String::from("./main.k"), + String::from("./sub/sub.k"), + ]; + assert!(kcl_cli_configs.files.is_some()); + assert!(kcl_cli_configs.disable_none.is_some()); + assert!(kcl_cli_configs.strict_range_check.is_some()); + assert!(kcl_cli_configs.debug.is_some()); + assert!(kcl_cli_configs.path_selector.is_none()); + assert!(kcl_cli_configs.overrides.is_none()); + if let Some(config_files) = kcl_cli_configs.files { + assert!(config_files == files); + } + } + if let Some(kcl_options) = settings.kcl_options { + assert!(kcl_options.len() == 2); + } + } + + #[test] + fn test_merge_settings() { + let settings1 = load_file(SETTINGS_FILE); + let settings2 = load_file(SETTINGS_FILE); + let settings = merge_settings(&vec![settings1, settings2]); + if let Some(kcl_cli_configs) = settings.kcl_cli_configs { + let files = vec![ + String::from("../main.k"), + String::from("./before/base.k"), + String::from("./main.k"), + String::from("./sub/sub.k"), + ]; + assert!(kcl_cli_configs.files.is_some()); + assert!(kcl_cli_configs.disable_none.is_some()); + assert!(kcl_cli_configs.strict_range_check.is_some()); + assert!(kcl_cli_configs.debug.is_some()); + assert!(kcl_cli_configs.path_selector.is_some()); + assert!(kcl_cli_configs.overrides.is_some()); + if let Some(config_files) = kcl_cli_configs.files { + assert!(config_files == files); + } + } + if let Some(kcl_options) = settings.kcl_options { + assert!(kcl_options.len() == 4); + } + } + + #[test] + fn test_decode_test_format_settings_file() { + let settings = decode_test_format_settings_file("./src/testdata/test_settings.yaml", ""); + assert!(settings.kcl_cli_configs.as_ref().unwrap().debug.unwrap() == true); + assert!( + settings + .kcl_cli_configs + .as_ref() + .unwrap() + .strict_range_check + .unwrap() + == true + ); + } +} diff --git a/kclvm/config/src/testdata/kcl.mod b/kclvm/config/src/testdata/kcl.mod new file mode 100644 index 000000000..3be7f7e6d --- /dev/null +++ b/kclvm/config/src/testdata/kcl.mod @@ -0,0 +1,6 @@ +[build] +enable_pkg_cache=true +cached_pkg_prefix="pkg.path" +[expected] +kclvm_version="v0.3.0" +kcl_plugin_version="v0.2.0" diff --git a/kclvm/config/src/testdata/settings.yaml b/kclvm/config/src/testdata/settings.yaml new file mode 100644 index 000000000..8b7a8a399 --- /dev/null +++ b/kclvm/config/src/testdata/settings.yaml @@ -0,0 +1,14 @@ +kcl_cli_configs: + files: + - ../main.k + - ./before/base.k + - ./main.k + - ./sub/sub.k + disable_none: false + strict_range_check: false + debug: false +kcl_options: + - key: app-name + value: kclvm + - key: image + value: kclvm:v0.0.1 diff --git a/kclvm/config/src/testdata/test_settings.yaml b/kclvm/config/src/testdata/test_settings.yaml new file mode 100644 index 000000000..e0304040d --- /dev/null +++ b/kclvm/config/src/testdata/test_settings.yaml @@ -0,0 +1 @@ +kcl_options: -r -d diff --git a/kclvm/config/src/vfs.rs b/kclvm/config/src/vfs.rs new file mode 100644 index 000000000..aec95fc82 --- /dev/null +++ b/kclvm/config/src/vfs.rs @@ -0,0 +1,119 @@ +extern crate pathdiff; + +use std::path::Path; + +pub fn is_abs_pkgpath(pkgpath: &str) -> bool { + if pkgpath.is_empty() { + return false; + } + if pkgpath.starts_with('.') { + return false; + } + if std::path::Path::new(pkgpath).is_absolute() { + return false; + } + if pkgpath.contains("..") { + return false; + } + if pkgpath.contains(char::is_whitespace) { + return false; + } + + true +} + +pub fn is_rel_pkgpath(pkgpath: &str) -> bool { + let pkgpath = pkgpath.trim(); + pkgpath.starts_with('.') +} + +pub fn fix_import_path(root: &str, filepath: &str, import_path: &str) -> String { + // relpath: import .sub + // FixImportPath(root, "path/to/app/file.k", ".sub") => path.to.app.sub + // FixImportPath(root, "path/to/app/file.k", "..sub") => path.to.sub + // FixImportPath(root, "path/to/app/file.k", "...sub") => path.sub + // FixImportPath(root, "path/to/app/file.k", "....sub") => sub + // FixImportPath(root, "path/to/app/file.k", ".....sub") => "" + // + // abspath: import path.to.sub + // FixImportPath(root, "path/to/app/file.k", "path.to.sub") => path.to.sub + + debug_assert!(!root.is_empty()); + debug_assert!(!filepath.is_empty()); + debug_assert!(!import_path.is_empty()); + + if !import_path.starts_with('.') { + return import_path.to_string(); + } + + // Filepath to pkgpath + let pkgpath = { + let base = Path::new(&root); + let dirpath = std::path::Path::new(&filepath).parent().unwrap(); + + let pkgpath = if let Some(x) = pathdiff::diff_paths(dirpath, base) { + x.to_str().unwrap().to_string() + } else { + dirpath.to_str().unwrap().to_string() + }; + + let pkgpath = pkgpath.replace('/', ".").replace('\\', "."); + pkgpath.trim_end_matches('.').to_string() + }; + + let mut leading_dot_count = import_path.len(); + for (i, c) in import_path.chars().enumerate() { + if c != '.' { + leading_dot_count = i; + break; + } + } + + // The pkgpath is the current root path + if pkgpath.is_empty() { + if leading_dot_count <= 1 { + return import_path.trim_matches('.').to_string(); + } else { + return "".to_string(); + } + } + + if leading_dot_count == 1 { + return pkgpath + import_path; + } + + let ss = pkgpath.split('.').collect::>(); + + if (leading_dot_count - 1) < ss.len() { + let prefix = ss[..(ss.len() - leading_dot_count + 1)].join("."); + let suffix = import_path[leading_dot_count..].to_string(); + + return format!("{}.{}", prefix, suffix); + } + + if leading_dot_count - 1 == ss.len() { + return import_path[leading_dot_count..].to_string(); + } + + "".to_string() +} + +#[test] +fn test_fix_import_path() { + let root = "/home/konfig"; + + let s = fix_import_path(root, "path/to/app/file.k", ".sub"); + assert_eq!(s, "path.to.app.sub"); + + let s = fix_import_path(root, "path/to/app/file.k", "..sub"); + assert_eq!(s, "path.to.sub"); + + let s = fix_import_path(root, "path/to/app/file.k", "...sub"); + assert_eq!(s, "path.sub"); + + let s = fix_import_path(root, "path/to/app/file.k", "....sub"); + assert_eq!(s, "sub"); + + let s = fix_import_path(root, "path/to/app/file.k", ".....sub"); + assert_eq!(s, ""); +} diff --git a/kclvm/docs/m1-mac-setup.md b/kclvm/docs/m1-mac-setup.md new file mode 100644 index 000000000..93595205e --- /dev/null +++ b/kclvm/docs/m1-mac-setup.md @@ -0,0 +1,75 @@ +## Env + ++ OS: MacOS Manterey 12.1 ++ CPU: Apple M1 pro, 10 cores ++ Mem: 16GB ++ Rust: cargo 1.59.0 (49d8809dc 2022-02-10) + +## Prerequirements + +1. Make sure you have arm64e-version homebrew installed @`/opt/homebrew`, otherwise install it ➡ + ``` + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + export PATH=/opt/homebrew/bin:$PATH + ``` +2. Install openssl@1.1 ➡ brew install openssl@1.1 +3. Check out openssl installed @`/opt/homebrew/opt/openssl@1.1/lib/libssl.1.1.dylib` + +## Build LLVM from Source + +1. Go to the folder where you want to build the LLVM source +2. Download LLVM 12.0.0 source from [http://releases.llvm.org/download.html](http://releases.llvm.org/download.html) and unpack source .tar.xz with tar -xJf [archive] +3. Create a directory where you want to build LLVM files ➡ mkdir build-llvm-12.0.0 +4. Move into the build folder ➡ cd build-llvm-12.0.0; that's where you create `build.sh`. + +``` +#!usr/bin/env sh + +LLVMSRC="" +. "$HOME/.cargo/env" +LLVMTARGET="" + + +cmake \ + -G "Ninja" \ + -DCMAKE_INSTALL_PREFIX="$LLVMTARGET" \ + -DCMAKE_OSX_ARCHITECTURES='arm64' \ + -DCMAKE_C_COMPILER=`which clang` \ + -DCMAKE_CXX_COMPILER=`which clang++` \ + -DCMAKE_BUILD_TYPE="Release" \ + -DLLVM_TARGETS_TO_BUILD="AArch64" \ + -DLLVM_HOST_TRIPLE='aarch64-apple-darwin' \ + -DLLVM_DEFAULT_TARGET_TRIPLE='aarch64-apple-darwin' \ + -DLLVM_ENABLE_WERROR=FALSE \ + "$LLVMSRC" + +cmake --build . +cmake --build . --target install +``` + +5. You may not have ninja installed, so if you have brew installed, you need to install it ninja with ➡ brew install `ninja`. +6. Run the script ➡ sh build.sh +7. Took about 10-15mins. Check out @``. + +## Build KCLVM + +1. Build KCLVM according to `kclvm/README.md`. +2. Took about 5mins. +3. Done! + +## Notes + +1. If you've brew-updating and github brew-submodule-download issue, you'd better use a mirror to speed up. + ``` + cd "$(brew --repo)" + git remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git + + cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core" + git remote set-url origin https://mirrors.aliyun.com/homebrew/homebrew-core.git + + cd "$(brew --repo)/Library/Taps/homebrew/homebrew-cask" +git remote set-url origin https://mirrors.aliyun.com/homebrew/homebrew-cask.git + + echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles' >> ~/.zshrc +source ~/.zshrc + ``` \ No newline at end of file diff --git a/kclvm/error/Cargo.lock b/kclvm/error/Cargo.lock new file mode 100644 index 000000000..b45dfc14a --- /dev/null +++ b/kclvm/error/Cargo.lock @@ -0,0 +1,693 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "annotate-snippets" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", + "rustc-rayon", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "kclvm-error" +version = "0.1.0" +dependencies = [ + "annotate-snippets", + "atty", + "indexmap", + "kclvm-span", + "rustc_span", + "termcolor", + "termize", + "tracing", +] + +[[package]] +name = "kclvm-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "kclvm-span" +version = "0.1.0" +dependencies = [ + "kclvm-macros", + "rustc_span", + "scoped-tls", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest", +] + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "proc-macro2" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871372391786ccec00d3c5d3d6608905b3d4db263639cfe075d3b60a736d115a" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-rayon" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" +dependencies = [ + "crossbeam-deque", + "either", + "rustc-rayon-core", +] + +[[package]] +name = "rustc-rayon-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc_data_structures" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ena", + "indexmap", + "jobserver", + "libc", + "memmap2", + "parking_lot", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "stable_deref_trait", + "stacker", + "tempfile", + "tracing", + "winapi", +] + +[[package]] +name = "rustc_span" +version = "0.0.0" +dependencies = [ + "cfg-if 0.1.10", + "md-5", + "rustc_data_structures", + "scoped-tls", + "sha-1", + "sha2", + "tracing", + "unicode-width", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termize" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tracing" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90442985ee2f57c9e1b548ee72ae842f4a9a20e3f417cc38dbc5dc684d9bb4ee" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" diff --git a/kclvm/error/Cargo.toml b/kclvm/error/Cargo.toml new file mode 100644 index 000000000..c2e9df40b --- /dev/null +++ b/kclvm/error/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "kclvm-error" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rustc_span = { path = "../3rdparty/rustc_span" } +kclvm-span = {path = "../span", version = "0.1.0"} + +tracing = "0.1" +atty = "0.2" +termcolor = "1.0" +annotate-snippets = "0.8.0" +termize = "0.1.1" +indexmap = "1.0" diff --git a/kclvm/error/src/bug.rs b/kclvm/error/src/bug.rs new file mode 100644 index 000000000..2a0a027c6 --- /dev/null +++ b/kclvm/error/src/bug.rs @@ -0,0 +1,48 @@ +use std::{error, fmt, panic}; + +/// `bug!` macro is used to report compiler internal bug. +/// You can use bug! macros directly by adding `#[macro_use]extern crate kclvm_error;` +/// in the lib.rs, and then call as follows: +/// ```no_check +/// bug!(); +/// bug!("an error msg"); +/// bug!("an error msg with string format {}", "msg"); +/// ``` +#[macro_export] +macro_rules! bug { + () => ( $crate::bug::bug("impossible case reached") ); + ($msg:expr) => ({ $crate::bug::bug(&format!($msg)) }); + ($msg:expr,) => ({ $crate::bug::bug($msg) }); + ($fmt:expr, $($arg:tt)+) => ({ + $crate::bug::bug(&format!($fmt, $($arg)+)) + }); +} + +/// Signifies that the compiler died with an explicit call to `.bug` +/// rather than a failed assertion, etc. +#[derive(Clone, Debug)] +pub struct ExplicitBug { + msg: String, +} + +impl fmt::Display for ExplicitBug { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Internal error, please report a bug to us. The error message is: {}", + self.msg + ) + } +} + +impl error::Error for ExplicitBug {} + +#[inline] +pub fn bug(msg: &str) -> ! { + panic!( + "{}", + ExplicitBug { + msg: msg.to_string() + } + ); +} diff --git a/kclvm/error/src/diagnostic.rs b/kclvm/error/src/diagnostic.rs new file mode 100644 index 000000000..f88b6cc05 --- /dev/null +++ b/kclvm/error/src/diagnostic.rs @@ -0,0 +1,187 @@ +use std::fmt; +use std::hash::Hash; + +use kclvm_span::Loc; +use rustc_span::Pos; +use termcolor::{Color, ColorSpec}; + +use crate::ErrorKind; + +/// Diagnostic structure. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Diagnostic { + pub level: Level, + pub messages: Vec, + pub code: Option, +} + +/// Position describes an arbitrary source position including the filename, +/// line, and column location. +/// +/// A Position is valid if the line number is > 0. +/// The line and column are both 1 based. +#[derive(PartialEq, Clone, Eq, Hash, Debug, Default)] +pub struct Position { + pub filename: String, + pub line: u64, + pub column: Option, +} + +impl Position { + #[inline] + pub fn dummy_pos() -> Self { + Position { + filename: "".to_string(), + line: 1, + column: None, + } + } + + #[inline] + pub fn is_valid(&self) -> bool { + self.line > 0 + } + + pub fn less(&self, other: &Position) -> bool { + if !self.is_valid() || !other.is_valid() || self.filename != other.filename { + false + } else if self.line < other.line { + true + } else if self.line == other.line { + match (self.column, other.column) { + (Some(column), Some(other_column)) => column < other_column, + _ => false, + } + } else { + false + } + } + + pub fn less_equal(&self, other: &Position) -> bool { + if !self.is_valid() || !other.is_valid() { + false + } else if self.less(other) { + true + } else { + self == other + } + } + + pub fn info(&self) -> String { + let mut info = "---> File ".to_string(); + info += &self.filename; + info += &format!(":{}", self.line); + if let Some(column) = self.column { + info += &format!(":{}", column + 1); + } + info + } +} + +impl From for Position { + fn from(loc: Loc) -> Self { + Self { + filename: format!("{}", loc.file.name.prefer_remapped()), + line: loc.line as u64, + column: if loc.col_display > 0 { + // Loc col is the (0-based) column offset. + Some(loc.col.to_usize() as u64 + 1) + } else { + None + }, + } + } +} + +impl Diagnostic { + pub fn new(level: Level, message: &str, pos: Position) -> Self { + Diagnostic::new_with_code(level, message, pos, None) + } + + /// New a diagnostic with error code. + pub fn new_with_code( + level: Level, + message: &str, + pos: Position, + code: Option, + ) -> Self { + Diagnostic { + level, + messages: vec![Message { + pos, + style: Style::LineAndColumn, + message: message.to_string(), + note: None, + }], + code, + } + } + + #[inline] + pub fn is_error(&self) -> bool { + matches!(self.level, Level::Error) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Message { + pub pos: Position, + pub style: Style, + pub message: String, + pub note: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum DiagnosticId { + Error(ErrorKind), + Warning(String), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum Level { + Error, + Warning, + Note, +} + +impl Level { + pub fn to_str(self) -> &'static str { + match self { + Level::Error => "Error", + Level::Warning => "Warning", + Level::Note => "Note", + } + } + + pub fn color(&self) -> ColorSpec { + let mut spec = ColorSpec::new(); + match self { + Level::Error => { + spec.set_fg(Some(Color::Red)).set_intense(true); + } + Level::Warning => { + spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows)); + } + Level::Note => { + spec.set_fg(Some(Color::Green)).set_intense(true); + } + } + spec + } +} + +impl fmt::Display for Level { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.to_str().fmt(f) + } +} + +/// Style indicates the style of error message: +/// - `LineAndColumn` is :: +/// - `Line` is : +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum Style { + Empty, + LineAndColumn, + Line, +} diff --git a/kclvm/error/src/emitter.rs b/kclvm/error/src/emitter.rs new file mode 100644 index 000000000..5d2930905 --- /dev/null +++ b/kclvm/error/src/emitter.rs @@ -0,0 +1,232 @@ +use crate::{ + diagnostic::{Diagnostic, Style}, + DiagnosticId, Level, +}; + +use kclvm_span::{FilePathMapping, SourceMap}; +use std::sync::Arc; +use std::{ + io::{self, Write}, + path::Path, +}; +use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; + +/// Emitter trait for emitting errors. +pub trait Emitter { + /// Emit a structured diagnostic. + fn emit_diagnostic(&mut self, diag: &Diagnostic); + /// Checks if we can use colors in the current output stream. + fn supports_color(&self) -> bool { + false + } +} + +/// Emitter writer. +pub struct EmitterWriter { + dst: Destination, + short_message: bool, + source_map: Option>, +} + +impl Default for EmitterWriter { + fn default() -> Self { + Self { + dst: Destination::from_stderr(), + short_message: false, + source_map: None, + } + } +} + +impl EmitterWriter { + pub fn from_stderr(source_map: Arc) -> Self { + Self { + dst: Destination::from_stderr(), + short_message: false, + source_map: Some(source_map), + } + } +} + +/// Emit destinations +pub enum Destination { + Terminal(StandardStream), + Buffered(BufferWriter), + // The bool denotes whether we should be emitting ansi color codes or not + Raw(Box<(dyn Write + Send)>, bool), +} + +impl Destination { + #[allow(dead_code)] + pub fn from_raw(dst: Box, colored: bool) -> Self { + Destination::Raw(dst, colored) + } + + pub fn from_stderr() -> Self { + // On Windows we'll be performing global synchronization on the entire + // system for emitting rustc errors, so there's no need to buffer + // anything. + // + // On non-Windows we rely on the atomicity of `write` to ensure errors + // don't get all jumbled up. + if !cfg!(windows) { + Destination::Terminal(StandardStream::stderr(ColorChoice::Auto)) + } else { + Destination::Buffered(BufferWriter::stderr(ColorChoice::Auto)) + } + } + + fn supports_color(&self) -> bool { + match *self { + Self::Terminal(ref stream) => stream.supports_color(), + Self::Buffered(ref buffer) => buffer.buffer().supports_color(), + Self::Raw(_, supports_color) => supports_color, + } + } + + fn apply_style(&mut self, lvl: Level, style: Style) -> io::Result<()> { + let mut spec = ColorSpec::new(); + match style { + Style::Empty | Style::LineAndColumn => { + spec.set_bold(true); + spec = lvl.color(); + } + Style::Line => { + spec.set_bold(true); + spec.set_intense(true); + if cfg!(windows) { + spec.set_fg(Some(Color::Cyan)); + } else { + spec.set_fg(Some(Color::Blue)); + } + } + } + self.set_color(&spec) + } + + fn set_color(&mut self, color: &ColorSpec) -> io::Result<()> { + match *self { + Destination::Terminal(ref mut t) => t.set_color(color), + Destination::Buffered(ref mut t) => t.buffer().set_color(color), + Destination::Raw(_, _) => Ok(()), + } + } + + fn reset(&mut self) -> io::Result<()> { + match *self { + Destination::Terminal(ref mut t) => t.reset(), + Destination::Buffered(ref mut t) => t.buffer().reset(), + Destination::Raw(..) => Ok(()), + } + } +} + +impl<'a> Write for Destination { + fn write(&mut self, bytes: &[u8]) -> io::Result { + match *self { + Destination::Terminal(ref mut t) => t.write(bytes), + Destination::Buffered(ref mut t) => t.buffer().write(bytes), + Destination::Raw(ref mut t, _) => t.write(bytes), + } + } + + fn flush(&mut self) -> io::Result<()> { + match *self { + Destination::Terminal(ref mut t) => t.flush(), + Destination::Buffered(ref mut t) => t.buffer().flush(), + Destination::Raw(ref mut t, _) => t.flush(), + } + } +} + +impl Emitter for EmitterWriter { + fn supports_color(&self) -> bool { + self.dst.supports_color() + } + + fn emit_diagnostic(&mut self, diag: &Diagnostic) { + let mut buffer: Vec = vec![]; + let mut diag_str = "KCL ".to_string(); + diag_str += diag.level.to_str(); + if let Some(code) = &diag.code { + let code_str = match code { + DiagnosticId::Error(kind) => kind.name(), + DiagnosticId::Warning(warn_msg) => warn_msg.to_string(), + }; + diag_str += &format!(" [{}]", code_str); + } + buffer.push(diag_str); + for (i, msg) in diag.messages.iter().enumerate() { + buffer.push(" ".repeat(i) + &msg.pos.info()); + let mut line_source = format!("{} |", msg.pos.line); + let line_hint_len = line_source.len(); + if let Some(sm) = &self.source_map { + if let Some(source_file) = sm.source_file_by_filename(&msg.pos.filename) { + if let Some(line) = source_file.get_line(msg.pos.line as usize - 1) { + line_source += &line.to_string(); + } + } + } else { + let sm = SourceMap::new(FilePathMapping::empty()); + if let Ok(source_file) = sm.load_file(Path::new(&msg.pos.filename)) { + if let Some(line) = source_file.get_line(msg.pos.line as usize - 1) { + line_source += &line.to_string(); + } + } + } + buffer.push(" ".repeat(i) + &line_source); + if let Style::LineAndColumn = msg.style { + if let Some(column) = msg.pos.column { + let column = column + 1; + let column_source = format!("{} ^", column); + let prefix_space = line_hint_len + column as usize - column_source.len(); + let column_source = " ".repeat(prefix_space) + &column_source; + buffer.push(" ".repeat(i) + &column_source); + } + } + buffer.push(" ".repeat(i) + &msg.message.clone()); + if !self.short_message { + if let Some(note) = &msg.note { + buffer.push(" ".repeat(i) + &format!("Note: {}", note)); + } + } + buffer.push("".to_string()); + } + if let Err(e) = emit_to_destination(&buffer, &diag.level, &mut self.dst, self.short_message) + { + panic!("failed to emit error: {}", e) + } + } +} + +fn emit_to_destination( + rendered_buffer: &[String], + lvl: &Level, + dst: &mut Destination, + short_message: bool, +) -> io::Result<()> { + // In order to prevent error message interleaving, where multiple error lines get intermixed + // when multiple compiler processes error simultaneously, we emit errors with additional + // steps. + // + // On Unix systems, we write into a buffered terminal rather than directly to a terminal. When + // the .flush() is called we take the buffer created from the buffered writes and write it at + // one shot. Because the Unix systems use ANSI for the colors, which is a text-based styling + // scheme, this buffered approach works and maintains the styling. + // + // On Windows, styling happens through calls to a terminal API. This prevents us from using the + // same buffering approach. Instead, we use a global Windows mutex, which we acquire long + // enough to output the full error message, then we release. + for (pos, line) in rendered_buffer.iter().enumerate() { + if line.starts_with("KCL") { + dst.apply_style(*lvl, Style::LineAndColumn)?; + } + write!(dst, "{}", line)?; + dst.reset()?; + if !short_message || pos != rendered_buffer.len() - 1 { + writeln!(dst)?; + } + } + dst.flush()?; + Ok(()) +} diff --git a/kclvm/error/src/error.rs b/kclvm/error/src/error.rs new file mode 100644 index 000000000..b10c73d17 --- /dev/null +++ b/kclvm/error/src/error.rs @@ -0,0 +1,88 @@ +//! This module is used to gather all error codes into one place, +//! the goal being to make their maintenance easier. + +macro_rules! register_errors { + ($($ecode:ident: $kind:expr, $message:expr,)*) => ( + pub static ERRORS: &[(&str, Error)] = &[ + $( (stringify!($ecode), Error { + code: stringify!($ecode), + kind: $kind, + message: Some($message), + }), )* + ]; + $(pub const $ecode: Error = Error { + code: stringify!($ecode), + kind: $kind, + message: Some($message), + };)* + ) +} + +// Error messages for EXXXX errors. Each message should start and end with a +// new line. +register_errors! { + E1001: ErrorKind::InvalidSyntax, include_str!("./error_codes/E1001.md"), + E2G22: ErrorKind::TypeError, include_str!("./error_codes/E2G22.md"), + E2F04: ErrorKind::CannotFindModule, include_str!("./error_codes/E2F04.md"), + E2L23: ErrorKind::CompileError, include_str!("./error_codes/E2L23.md"), + E2A31: ErrorKind::IllegalAttributeError, include_str!("./error_codes/E2A31.md"), + E2L28: ErrorKind::UniqueKeyError, include_str!("./error_codes/E2L28.md"), + E2D34: ErrorKind::IllegalInheritError, include_str!("./error_codes/E2D34.md"), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Error { + pub code: &'static str, + pub kind: ErrorKind, + pub message: Option<&'static str>, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ErrorKind { + InvalidSyntax, + TabError, + Indentation, + CannotFindModule, + RecursiveLoad, + FloatOverflow, + FloatUnderflow, + IntOverflow, + InvalidDocstring, + Deprecated, + UnKnownDecorator, + InvalidDecoratorTarget, + InvalidFormatSpec, + SchemaCheckFailure, + IndexSignatureError, + TypeError, + NameError, + ValueError, + KeyError, + AttributeError, + AssertionError, + ImmutableError, + MultiInheritError, + CycleInheritError, + IllegalInheritError, + IllegalAttributeError, + IllegalParameterError, + RecursionError, + PlanError, + CannotAddMembers, + CompileError, + EvaluationError, + UniqueKeyError, +} + +impl std::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl ErrorKind { + #[allow(dead_code)] + pub fn name(&self) -> String { + return format!("{:?}", self); + } +} diff --git a/kclvm/error/src/error_codes/E1001.md b/kclvm/error/src/error_codes/E1001.md new file mode 100644 index 000000000..30c1ae3ca --- /dev/null +++ b/kclvm/error/src/error_codes/E1001.md @@ -0,0 +1,9 @@ + +This error indicates that the compiler syntax error has occurred. + +Erroneous code example: + +```kcl,E1001 +x = f( + 7 ^ -> Expected one of ['all', 'any', 'bin_number', 'dec_number', '**', 'False', 'filter', 'float_number','hex_number', 'lambda', '{', '[', '(', 'long_string', 'not', 'map', '-', '*', 'name', 'None', '~', 'oct_number', '+',')', 'string', 'True', 'Undefined'] +``` diff --git a/kclvm/error/src/error_codes/E2A31.md b/kclvm/error/src/error_codes/E2A31.md new file mode 100644 index 000000000..6d1d8dac2 --- /dev/null +++ b/kclvm/error/src/error_codes/E2A31.md @@ -0,0 +1,11 @@ + +This error indicates that the illegal attribute error has occurred. + +Erroneous code example: + +```kcl,E2A31 +KCL Compile Error[E2A31] : Illegal attribute +1 |x = {None: None} + 6 ^ -> Failure +type 'NoneType' +``` diff --git a/kclvm/error/src/error_codes/E2D34.md b/kclvm/error/src/error_codes/E2D34.md new file mode 100644 index 000000000..6ebda5fee --- /dev/null +++ b/kclvm/error/src/error_codes/E2D34.md @@ -0,0 +1,20 @@ + +This error indicates that invalid inheritance structure has occurred. + +Erroneous code example: + +```kcl +schema FullnameMixin: + fullName = "{} {}".format(firstName, lastName) + +schema Scholar(FullnameMixin): + school: str +``` + +```kcl,E2D34 +KCL Complier Error[E2D34] : Illegal inheritance +---> File /schema/inherit/inherit_mixin_fail/main.k:8:1 +8 |schema Scholar(FullnameMixin): + 1 ^ -> Failure +mixin inheritance FullnameMixin is prohibited +``` diff --git a/kclvm/error/src/error_codes/E2F04.md b/kclvm/error/src/error_codes/E2F04.md new file mode 100644 index 000000000..29f438dfd --- /dev/null +++ b/kclvm/error/src/error_codes/E2F04.md @@ -0,0 +1,10 @@ + +This error indicates that the import module is not found. + +Erroneous code example: + +```kcl,E2F04 +1 |import not_existed_pkg + 1 ^^^^^^^^^^^^^^^^^^^^^^ -> Failure +Cannot find the module not_existed_pkg from ./not_existed_pkg +``` diff --git a/kclvm/error/src/error_codes/E2G22.md b/kclvm/error/src/error_codes/E2G22.md new file mode 100644 index 000000000..a5d79d0a0 --- /dev/null +++ b/kclvm/error/src/error_codes/E2G22.md @@ -0,0 +1,10 @@ + +This error indicates that the compiler type error has occurred. + +Erroneous code example: + +```kcl,E2G22 +1 |a: int = "1" + 1 ^ -> got str(1) +expect int, got str(1) +``` diff --git a/kclvm/error/src/error_codes/E2L23.md b/kclvm/error/src/error_codes/E2L23.md new file mode 100644 index 000000000..e61992a69 --- /dev/null +++ b/kclvm/error/src/error_codes/E2L23.md @@ -0,0 +1,10 @@ + +This error indicates that the compile time error has occurred. + +Erroneous code example: + +```kcl,E2L23 +1 |x = 1() + 5 ^ -> Failure +'int(1)' object is not callable +``` diff --git a/kclvm/error/src/error_codes/E2L28.md b/kclvm/error/src/error_codes/E2L28.md new file mode 100644 index 000000000..d0c7cc8a2 --- /dev/null +++ b/kclvm/error/src/error_codes/E2L28.md @@ -0,0 +1,21 @@ + +This error indicates that variables with the same name or duplicate definitions. + +Erroneous code example: + +```kcl +schema Person: + name: str = "kcl" + age: int = 1 + +schema Person: + aa: int +``` + +```kcl,E2L28 +KCL Complier Error[E2L28] : Unique key error +---> File /schema/same_name/main.k:5:1 +5 |schema Person: + 1 ^ -> Failure +Variable name 'Person' must be unique in package context +``` diff --git a/kclvm/error/src/lib.rs b/kclvm/error/src/lib.rs new file mode 100644 index 000000000..35279d171 --- /dev/null +++ b/kclvm/error/src/lib.rs @@ -0,0 +1,232 @@ +//! Diagnostics creation and emission for `KCLVM`. +//! This module contains the code for creating and emitting diagnostics. +//! +//! We can use `Handler` to create and emit diagnostics. +#[macro_use] +pub mod bug; +mod diagnostic; +mod emitter; +mod error; +#[cfg(test)] +mod tests; + +use std::sync::Arc; + +pub use diagnostic::{Diagnostic, DiagnosticId, Level, Message, Position, Style}; +pub use emitter::{Emitter, EmitterWriter}; +pub use error::*; +use indexmap::IndexSet; +use kclvm_span::SourceMap; + +/// A handler deals with errors and other compiler output. +/// Certain errors (error, bug) may cause immediate exit, +/// others log errors for later reporting. +/// ```no_check +/// use kclvm_error::{Handler, Position, ParseError}; +/// let mut handler = Handler::default(); +/// handler.add_parse_error( +/// ParseError::unexpected_token(&["+", "-", "*", "/"], "//"), +/// Position::dummy_pos(), +/// ); +/// handler.abort_if_errors(); +/// ``` +pub struct Handler { + /// The number of errors that have been emitted, including duplicates. + /// + /// This is not necessarily the count that's reported to the user once + /// compilation ends. + emitter: Box, + pub diagnostics: IndexSet, +} + +impl Default for Handler { + fn default() -> Self { + Self { + emitter: Box::new(EmitterWriter::default()), + diagnostics: Default::default(), + } + } +} + +impl Handler { + /// New a handler using a emitter + pub fn new(emitter: Box) -> Self { + Self { + emitter, + diagnostics: Default::default(), + } + } + + pub fn with_source_map(source_map: Arc) -> Self { + Self { + emitter: Box::new(EmitterWriter::from_stderr(source_map)), + diagnostics: Default::default(), + } + } + + /// Panic program and report a bug + #[inline] + pub fn bug(&self, msg: &str) -> ! { + bug!("{}", msg) + } + + #[inline] + pub fn has_errors(&self) -> bool { + self.diagnostics + .iter() + .any(|diag| diag.level == Level::Error) + } + + /// Emit all diagnostics and return whether has errors. + pub fn emit(&mut self) -> bool { + for diag in &self.diagnostics { + self.emitter.emit_diagnostic(diag); + } + self.has_errors() + } + + /// Emit all diagnostics and abort if has any errors. + pub fn abort_if_errors(&mut self) -> ! { + if self.emit() { + std::process::exit(1) + } else { + panic!("compiler internal error") + } + } + + /// Emit all diagnostics and abort if has any errors. + pub fn abort_if_any_errors(&mut self) { + if self.emit() { + std::process::exit(1) + } + } + + /// Construct a parse error and put it into the handler diagnostic buffer + pub fn add_syntex_error(&mut self, msg: &str, pos: Position) -> &mut Self { + let message = format!("Invalid syntax: {}", msg); + let diag = Diagnostic::new_with_code( + Level::Error, + &message, + pos, + Some(DiagnosticId::Error(E1001.kind)), + ); + self.add_diagnostic(diag); + + self + } + + /// Construct a parse error and put it into the handler diagnostic buffer + pub fn add_parse_error(&mut self, err: ParseError, pos: Position) -> &mut Self { + match err { + ParseError::UnexpectedToken { expected, got } => { + let message = format!("expect {:?} got {}", expected, got); + let diag = Diagnostic::new_with_code( + Level::Error, + &message, + pos, + Some(DiagnosticId::Error(E1001.kind)), + ); + self.add_diagnostic(diag); + } + } + self + } + + /// Construct a type error and put it into the handler diagnostic buffer + pub fn add_type_error(&mut self, msg: &str, pos: Position) -> &mut Self { + let diag = Diagnostic::new_with_code( + Level::Error, + msg, + pos, + Some(DiagnosticId::Error(E2G22.kind)), + ); + self.add_diagnostic(diag); + + self + } + + /// Construct a type error and put it into the handler diagnostic buffer + pub fn add_compile_error(&mut self, msg: &str, pos: Position) -> &mut Self { + let diag = Diagnostic::new_with_code( + Level::Error, + msg, + pos, + Some(DiagnosticId::Error(E2L23.kind)), + ); + self.add_diagnostic(diag); + + self + } + + /// Add an error into the handler + /// ``` + /// use kclvm_error::*; + /// let mut handler = Handler::default(); + /// handler.add_error(ErrorKind::InvalidSyntax, &[ + /// Message { + /// pos: Position::dummy_pos(), + /// style: Style::LineAndColumn, + /// message: "Invalid syntax: expected '+', got '-'".to_string(), + /// note: None, + /// } + /// ]); + /// ``` + pub fn add_error(&mut self, err: ErrorKind, msgs: &[Message]) -> &mut Self { + let diag = Diagnostic { + level: Level::Error, + messages: msgs.to_owned(), + code: Some(DiagnosticId::Error(err)), + }; + self.add_diagnostic(diag); + + self + } + + /// Store a diagnostics + #[inline] + fn add_diagnostic(&mut self, diagnostic: Diagnostic) -> &mut Self { + self.diagnostics.insert(diagnostic); + + self + } +} + +#[derive(Debug, Clone)] +pub enum ParseError { + UnexpectedToken { expected: Vec, got: String }, +} + +impl ParseError { + pub fn unexpected_token(expected: &[&str], got: &str) -> Self { + ParseError::UnexpectedToken { + expected: expected + .iter() + .map(|v| v.to_string()) + .collect::>(), + got: got.to_string(), + } + } +} + +/// Used as a return value to signify a fatal error occurred. (It is also +/// used as the argument to panic at the moment, but that will eventually +/// not be true.) +#[derive(Copy, Clone, Debug)] +#[must_use] +pub struct FatalError; + +pub struct FatalErrorMarker; + +impl FatalError { + pub fn raise(self) -> ! { + std::panic::panic_any(Box::new(FatalErrorMarker)) + } +} + +impl std::fmt::Display for FatalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "fatal error") + } +} + +impl std::error::Error for FatalError {} diff --git a/kclvm/error/src/tests.rs b/kclvm/error/src/tests.rs new file mode 100644 index 000000000..7fd5b76ec --- /dev/null +++ b/kclvm/error/src/tests.rs @@ -0,0 +1,17 @@ +use crate::*; + +#[test] +fn test_bug_macro() { + let result = std::panic::catch_unwind(|| { + bug!(); + }); + assert!(result.is_err()); + let result = std::panic::catch_unwind(|| { + bug!("an error msg"); + }); + assert!(result.is_err()); + let result = std::panic::catch_unwind(|| { + bug!("an error msg with string format {}", "msg"); + }); + assert!(result.is_err()); +} diff --git a/kclvm/lexer/Cargo.lock b/kclvm/lexer/Cargo.lock new file mode 100644 index 000000000..d79fe20da --- /dev/null +++ b/kclvm/lexer/Cargo.lock @@ -0,0 +1,775 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "annotate-snippets" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dissimilar" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "expect-test" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3e6b28dccda91d8742195c71fbda412112c0c77febf56bf3d895d68b19db16" +dependencies = [ + "dissimilar", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", + "rustc-rayon", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "kclvm-error" +version = "0.1.0" +dependencies = [ + "annotate-snippets", + "atty", + "indexmap", + "kclvm-span", + "rustc_span", + "termcolor", + "termize", + "tracing", +] + +[[package]] +name = "kclvm-lexer" +version = "0.1.0" +dependencies = [ + "expect-test", + "kclvm-error", + "rustc_lexer", + "unic-emoji-char", +] + +[[package]] +name = "kclvm-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "kclvm-span" +version = "0.1.0" +dependencies = [ + "kclvm-macros", + "rustc_span", + "scoped-tls", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest", +] + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "proc-macro2" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871372391786ccec00d3c5d3d6608905b3d4db263639cfe075d3b60a736d115a" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-rayon" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" +dependencies = [ + "crossbeam-deque", + "either", + "rustc-rayon-core", +] + +[[package]] +name = "rustc-rayon-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc_data_structures" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ena", + "indexmap", + "jobserver", + "libc", + "memmap2", + "parking_lot", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "stable_deref_trait", + "stacker", + "tempfile", + "tracing", + "winapi", +] + +[[package]] +name = "rustc_lexer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86aae0c77166108c01305ee1a36a1e77289d7dc6ca0a3cd91ff4992de2d16a5" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "rustc_span" +version = "0.0.0" +dependencies = [ + "cfg-if 0.1.10", + "md-5", + "rustc_data_structures", + "scoped-tls", + "sha-1", + "sha2", + "tracing", + "unicode-width", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termize" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tracing" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" diff --git a/kclvm/lexer/Cargo.toml b/kclvm/lexer/Cargo.toml new file mode 100644 index 000000000..d0ff55a9f --- /dev/null +++ b/kclvm/lexer/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "kclvm-lexer" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rustc_lexer = "0.1.0" +unic-emoji-char = "0.9.0" +kclvm-error = {path = "../error", version = "0.1.0"} + +[dev-dependencies] +expect-test = "1.0" \ No newline at end of file diff --git a/kclvm/lexer/src/cursor.rs b/kclvm/lexer/src/cursor.rs new file mode 100644 index 000000000..0bf51e0e6 --- /dev/null +++ b/kclvm/lexer/src/cursor.rs @@ -0,0 +1,94 @@ +use std::str::Chars; + +/// Peekable iterator over a char sequence. +/// +/// Next 0th characters can be peeked via `peek`(0th) and `peek1th`(1th) method, +/// and position can be shifted forward via `bump` method. +pub struct Cursor<'a> { + initial_len: usize, + /// Iterator over chars. Slightly faster than a &str. + chars: Chars<'a>, + #[cfg(debug_assertions)] + prev: char, +} + +pub const EOF_CHAR: char = '\0'; +pub const DOLLAR_CHAR: char = '$'; + +impl<'a> Cursor<'a> { + pub fn new(input: &'a str) -> Cursor<'a> { + Cursor { + initial_len: input.len(), + chars: input.chars(), + #[cfg(debug_assertions)] + prev: EOF_CHAR, + } + } + + /// Returns the last eaten symbol (or `'\0'` in release builds). + /// (For debug assertions only.) + pub fn prev(&self) -> char { + #[cfg(debug_assertions)] + { + self.prev + } + + #[cfg(not(debug_assertions))] + { + EOF_CHAR + } + } + + /// Peeks the next symbol from the input stream without consuming it. + /// If requested position doesn't exist, `EOF_CHAR` is returned. + /// However, getting `EOF_CHAR` doesn't always mean actual end of file, + /// it should be checked with `is_eof` method. + pub fn peek(&self) -> char { + // `.next()` optimizes better than `.nth(0)` + self.chars.clone().next().unwrap_or(EOF_CHAR) + } + + /// Peeks the second symbol from the input stream without consuming it. + pub fn peek1th(&self) -> char { + // `.next()` optimizes better than `.nth(1)` + let mut iter = self.chars.clone(); + iter.next(); + iter.next().unwrap_or(EOF_CHAR) + } + + /// Checks if there is nothing more to consume. + pub fn is_eof(&self) -> bool { + self.chars.as_str().is_empty() + } + + /// Returns amount of already consumed symbols. + pub fn len_consumed(&self) -> usize { + self.initial_len - self.chars.as_str().len() + } + + /// Resets the number of bytes consumed to 0. + pub(crate) fn reset_len_consumed(&mut self) { + self.initial_len = self.chars.as_str().len(); + } + + /// Moves to the next character. + pub(crate) fn bump(&mut self) -> Option { + let c = self.chars.next()?; + + #[cfg(debug_assertions)] + { + self.prev = c; + } + + Some(c) + } + + /// Eats symbols while predicate returns true or until the end of file is reached. + pub fn eat_while(&mut self, mut predicate: impl FnMut(char) -> bool) { + // It was tried making optimized version of this for eg. line comments, but + // LLVM can inline all of this and compile it down to fast iteration over bytes. + while predicate(self.peek()) && !self.is_eof() { + self.bump(); + } + } +} diff --git a/kclvm/lexer/src/kcl_cursor.rs b/kclvm/lexer/src/kcl_cursor.rs new file mode 100644 index 000000000..6d376416c --- /dev/null +++ b/kclvm/lexer/src/kcl_cursor.rs @@ -0,0 +1,146 @@ +//! KCL-specific cursor implementation, +//! including comment cursor, string cursor and identifier cursor. +//! +//! Todo: The implementation should be moved to [`parser::lexer`]. +//! To do that, we should make IABCCursor as dynamic traits +//! and enable implemente Cursor structs in different crate. + +use crate::cursor::DOLLAR_CHAR; +use crate::Cursor; +use crate::DocStyle; +use crate::ICommentCursor; +use crate::IIdentCurosr; +use crate::IStringCursor; +use crate::Literal; +use crate::LiteralKind::*; +use crate::TokenKind; +use crate::TokenKind::*; + +impl<'a> ICommentCursor for Cursor<'a> { + fn try_comment_magic(&self, c: char) -> bool { + c == '#' + } + + fn eat_comment(&mut self) -> TokenKind { + debug_assert!(self.prev() == '#'); + + self.eat_while(|c| c != '\n'); + LineComment { + doc_style: Some(DocStyle::Inner), + } + } +} + +impl<'a> IStringCursor for Cursor<'a> { + fn try_string_magic(&self, c: char) -> bool { + match c { + 'r' | 'R' => matches!(self.peek(), '\'' | '\"'), + '\'' | '\"' => true, + _ => false, + } + } + + fn eat_string(&mut self, c: char) -> TokenKind { + match c { + 'r' | 'R' => match self.peek() { + '\'' | '\"' => { + // R string + let quote = self.bump().unwrap(); + self.eat_quoted_string(quote) + } + _ => Unknown, + }, + '\'' | '\"' => self.eat_quoted_string(c), + _ => Unknown, + } + } +} + +impl<'a> Cursor<'a> { + // Eat (single | double | triple) quoted string. + // If string is not closed, mark 'terminated' as false. + // Note, it does not check whether the string content is correct in the quick scan here. + // For example, it's not checking for newlines in single-line strings. + fn eat_quoted_string(&mut self, c: char) -> TokenKind { + debug_assert!(self.prev() == '\'' || self.prev() == '\"'); + + let quote = c; + + // Check if we have a triple-quoted string, and make sure we have a triple-quote to close + let triple_quoted = if quote == self.peek() && quote == self.peek1th() { + self.bump(); + self.bump(); + true + } else { + false + }; + + while let Some(c) = self.bump() { + match c { + '\\' if self.peek() == '\\' || self.peek() == quote => { + // Skip the escaped quote + self.bump(); + } + c if c == quote => { + if triple_quoted { + if quote == self.peek() && quote == self.peek1th() { + self.bump(); + self.bump(); + + // Triple quote string closed + return Literal { + kind: Str { + terminated: true, + triple_quoted: true, + }, + suffix_start: self.len_consumed(), + }; + } + } else { + // Single or double quote string closed + return Literal { + kind: Str { + terminated: true, + triple_quoted: false, + }, + suffix_start: self.len_consumed(), + }; + } + } + _ => (), + } + } + + // Oops, we get an error here, string not closed + Literal { + kind: Str { + terminated: false, + triple_quoted: false, + }, + suffix_start: self.len_consumed(), + } + } +} + +impl<'a> IIdentCurosr for Cursor<'a> { + fn try_ident_magic(&self, c: char) -> bool { + match c { + DOLLAR_CHAR => rustc_lexer::is_id_start(self.peek()), + _ => rustc_lexer::is_id_start(c), + } + } + + fn eat_ident(&mut self) -> TokenKind { + debug_assert!(rustc_lexer::is_id_start(self.prev()) || self.prev() == DOLLAR_CHAR); + // Start is already eaten, eat the rest of identifier. + self.eat_while(rustc_lexer::is_id_continue); + // Known prefixes must have been handled earlier. So if + // we see a prefix here, it is definitely an unknown prefix. + match self.peek() { + c if !c.is_ascii() && unic_emoji_char::is_emoji(c) => { + self.fake_ident_or_unknown_prefix() + } + _ => Ident, + } + } +} diff --git a/kclvm/lexer/src/lib.rs b/kclvm/lexer/src/lib.rs new file mode 100644 index 000000000..149f4cdef --- /dev/null +++ b/kclvm/lexer/src/lib.rs @@ -0,0 +1,595 @@ +//! Low-level token stream lexer. +//! +//! The purpose of `kclvm_lexer` is similar to [`rustc_lexer`] crate, +//! which separates out pure lexing and language-specific designs. +//! +//! The difference with [`rustc_lexer`] is that here we want to define +//! a more general and wider range of tokens used by more languages. +//! +//! A language-specific lexer is needed to convert the basic token stream +//! into wide tokens consumed by the parser. +//! +//! The purpose of the lexer is to convert raw sources into a labeled sequence +//! of well-known token types. No error reporting on obvious error(e.g., string-not-closed error), +//! instead storing them as flags on the token. Checking of literal content is +//! not performed in this lexer. +//! +//! The main entity of this crate is the [`TokenKind`] enum which represents common +//! lexeme types. +//! +//! [`parser::lexer`]: ../parser/lexer/index.html +//! We want to be able to build this crate with a stable compiler, so no +//! `#![feature]` attributes should be added. + +mod cursor; +mod kcl_cursor; +mod number; + +#[cfg(test)] +mod tests; + +extern crate kclvm_error; + +use cursor::EOF_CHAR; + +use self::TokenKind::*; +pub use crate::cursor::Cursor; + +/// Parsed token. +#[derive(Debug)] +pub struct Token { + pub kind: TokenKind, + pub len: usize, +} + +impl Token { + fn new(kind: TokenKind, len: usize) -> Self { + Token { kind, len } + } +} + +/// Enum representing common lexeme types. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum TokenKind { + /// "# comment" or "// comment" + LineComment { doc_style: Option }, + + /// `/* block comment */` + /// + /// Block comments can be recursive, so the sequence like `/* /* */` + /// will not be considered terminated and will result in a parsing error. + BlockComment { + doc_style: Option, + terminated: bool, + }, + + /// "\t" + Tab, + + /// " " + Space, + + /// "\r" + CarriageReturn, + + /// "\n" + Newline, + + /// Any other whitespace characters sequence. + Whitespace, + + /// "ident" + Ident, + + /// Like the above, but containing invalid unicode codepoints. + InvalidIdent, + + /// Invalid line continue symbol `\\` without the `\n` followed. + InvalidLineContinue, + + /// Valid Line continue '\\' + LineContinue, + + /// "12_u8", "1.0e-40", "b"123"". See `LiteralKind` for more details. + Literal { + kind: LiteralKind, + suffix_start: usize, + }, + + /// ";" + Semi, + + /// "," + Comma, + + /// "." + Dot, + + /// ".." + DotDot, + + /// "..." + DotDotDot, + + /// "(" + OpenParen, + + /// ")" + CloseParen, + + /// "{" + OpenBrace, + + /// "}" + CloseBrace, + + /// "[" + OpenBracket, + + /// "]" + CloseBracket, + + /// "@" + At, + + /// "#" + Pound, + + /// "~" + Tilde, + + /// "?" + Question, + + /// ":" + Colon, + + /// "$" + Dollar, + + /// "=" + Eq, + + /// "!" + Bang, + + /// "<" + Lt, + + /// ">" + Gt, + + /// "==" + EqEq, + + /// "!=" + BangEq, + + /// ">=" + GtEq, + + /// "<=" + LtEq, + + /// "-" + Minus, + + /// "&" + And, + + /// "|" + Or, + + /// "+" + Plus, + + /// "*" + Star, + + /// "/" + Slash, + + /// "^" + Caret, + + /// "%" + Percent, + + /// "**" + StarStar, + + /// "//" + SlashSlash, + + /// "<<" + LtLt, + + /// ">>" + GtGt, + + /// "+=" + PlusEq, + + /// "-=" + MinusEq, + + /// "*=" + StarEq, + + /// "/=" + SlashEq, + + /// "%=" + PercentEq, + + /// "&=" + AndEq, + + /// "|=" + OrEq, + + /// "^=" + CaretEq, + + /// "**=" + StarStarEq, + + /// "//=" + SlashSlashEq, + + /// "<<=" + LtLtEq, + + /// ">>=" + GtGtEq, + + /// Unknown token, not expected by the lexer, e.g. "№" + Unknown, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum DocStyle { + Outer, + Inner, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum LiteralKind { + /// "12_u8", "0o100", "0b120i99" + Int { base: Base, empty_int: bool }, + /// "12.34f32", "0b100.100" + Float { base: Base, empty_exponent: bool }, + /// "'a'", "'\\'", "'''", "';" + Char { terminated: bool }, + /// "b'a'", "b'\\'", "b'''", "b';" + Byte { terminated: bool }, + /// ""abc"", ""abc" + Str { + terminated: bool, + triple_quoted: bool, + }, + /// True, False + Bool { terminated: bool }, +} + +/// Base of numeric literal encoding according to its prefix. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum Base { + /// Literal starts with "0b". + Binary, + /// Literal starts with "0o". + Octal, + /// Literal starts with "0x". + Hexadecimal, + /// Literal doesn't contain a prefix. + Decimal, +} + +/// Parses the first token from the provided input string. +pub fn first_token(input: &str) -> Token { + debug_assert!(!input.is_empty()); + Cursor::new(input).token() +} + +/// Creates an iterator that produces tokens from the input string. +pub fn tokenize(input: &str) -> impl Iterator + '_ { + let mut cursor = Cursor::new(input); + std::iter::from_fn(move || { + if cursor.is_eof() { + None + } else { + cursor.reset_len_consumed(); + Some(cursor.token()) + } + }) +} + +pub trait ICursor: ITokenCursor + ICommentCursor {} + +// Cursor trait to read one token from char stream. +pub trait ITokenCursor { + fn token(&mut self) -> Token; +} + +// Cursor trait to read comment. +// Line comment and block comment should be considered here. +pub trait ICommentCursor { + // If we encounter a comment. + // Returns true if exists, otherwise returns false. + fn try_comment_magic(&self, _c: char) -> bool { + false + } + + // Eat it if so. + // This mehod **MUST** be called after 'try_comment_magic'. + // No gurantee to ensure the correctness if no comment here, + // and return 'Unknown' if it happens. + fn eat_comment(&mut self) -> TokenKind { + Unknown + } +} + +// Cursor trait to read string. +// Simple string, raw string, unicode string, multi-lines string, +// and more string cases should be considered here. +pub trait IStringCursor { + // If we encounter a string. + // Returns true if exists, otherwise returns false. + fn try_string_magic(&self, _c: char) -> bool { + false + } + + // Eat it if so. + // This mehod **MUST** be called after 'try_string_magic'. + // No gurantee to ensure the correctness if no string here, + // and return 'Unknown' if it happens. + // For example, no identifier check if no string found. + fn eat_string(&mut self, _c: char) -> TokenKind { + Unknown + } +} + +// Cursor trait to read identifier. +// Simple identifier, raw identifier, and more identifier cases should be considered here. +pub trait IIdentCurosr { + // If we encounter a identifier. + // Returns true if exists, otherwise returns false. + fn try_ident_magic(&self, _c: char) -> bool { + false + } + + // Eat it if so. + // This mehod **MUST** be called after 'try_ident_magic'. + // No gurantee to ensure the correctness if no identifier here, + // and return 'Unknown' if it happens. + fn eat_ident(&mut self) -> TokenKind { + Unknown + } +} + +impl<'a> ITokenCursor for Cursor<'a> { + fn token(&mut self) -> Token { + let char = self.bump().unwrap(); + + let token_kind = match char { + // Comment or block comment, or a simple token + c if self.try_comment_magic(c) => self.eat_comment(), + + // Whitespace sequence. + c if rustc_lexer::is_whitespace(c) => self.whitespace(c), + + // Various of string. E.g., quoted string, raw string. + c if self.try_string_magic(c) => self.eat_string(c), + + // Identifier (this should be checked after other variant that can + // start as identifier). + c if self.try_ident_magic(c) => self.eat_ident(), + + // Numeric literal. + c @ '0'..='9' => { + let kind = self.number(c); + let suffix_start = self.len_consumed(); + self.eat_lit_suffix(); // In case we have some suffix + + TokenKind::Literal { kind, suffix_start } + } + + ';' => Semi, + ',' => Comma, + '.' => match (self.peek(), self.peek1th()) { + ('.', '.') => { + self.bump(); + self.bump(); + DotDotDot + } + _ => Dot, + }, + '(' => OpenParen, + ')' => CloseParen, + '{' => OpenBrace, + '}' => CloseBrace, + '[' => OpenBracket, + ']' => CloseBracket, + '@' => At, + '#' => Pound, + '~' => Tilde, + '?' => Question, + ':' => Colon, + '$' => Dollar, + '=' => match self.peek() { + '=' => { + self.bump(); + EqEq + } + _ => Eq, + }, + '!' => match self.peek() { + '=' => { + self.bump(); + BangEq + } + _ => Bang, + }, + '<' => match self.peek() { + '=' => { + self.bump(); + LtEq + } + '<' => { + self.bump(); + match self.peek() { + '=' => { + self.bump(); + LtEq + } + _ => LtLt, + } + } + _ => Lt, + }, + '>' => match self.peek() { + '=' => { + self.bump(); + GtEq + } + '>' => { + self.bump(); + match self.peek() { + '=' => { + self.bump(); + GtGtEq + } + _ => GtGt, + } + } + _ => Gt, + }, + '-' => match self.peek() { + '=' => { + self.bump(); + MinusEq + } + _ => Minus, + }, + '&' => match self.peek() { + '=' => { + self.bump(); + AndEq + } + _ => And, + }, + '|' => match self.peek() { + '=' => { + self.bump(); + OrEq + } + _ => Or, + }, + '+' => match self.peek() { + '=' => { + self.bump(); + PlusEq + } + _ => Plus, + }, + '*' => match self.peek() { + '*' => { + self.bump(); + match self.peek() { + '=' => { + self.bump(); + StarStarEq + } + _ => StarStar, + } + } + '=' => { + self.bump(); + StarEq + } + _ => Star, + }, + '/' => match self.peek() { + '/' => { + self.bump(); + match self.peek() { + '=' => { + self.bump(); + SlashSlashEq + } + _ => SlashSlash, + } + } + '=' => { + self.bump(); + SlashEq + } + _ => Slash, + }, + '^' => match self.peek() { + '=' => { + self.bump(); + CaretEq + } + _ => Caret, + }, + '%' => match self.peek() { + '=' => { + self.bump(); + PercentEq + } + _ => Percent, + }, + '\\' => match self.peek() { + '\n' => { + self.bump(); + LineContinue + } + EOF_CHAR => LineContinue, + _ => InvalidLineContinue, + }, + // Identifier starting with an emoji. Only lexed for graceful error recovery. + c if !c.is_ascii() && unic_emoji_char::is_emoji(c) => { + self.fake_ident_or_unknown_prefix() + } + _ => Unknown, + }; + Token::new(token_kind, self.len_consumed()) + } +} + +impl<'a> Cursor<'a> { + fn whitespace(&mut self, c: char) -> TokenKind { + debug_assert!(rustc_lexer::is_whitespace(c)); + + match c { + '\u{0009}' => Tab, + '\u{0020}' => Space, + '\u{000D}' => CarriageReturn, + '\u{000A}' => Newline, + _ => Whitespace, + } + } + + // Eats the suffix of the literal, e.g. 'Ki', 'M', etc. + fn eat_lit_suffix(&mut self) { + if !rustc_lexer::is_id_start(self.peek()) { + return; + } + self.bump(); + + self.eat_while(rustc_lexer::is_id_continue); + } + + fn fake_ident_or_unknown_prefix(&mut self) -> TokenKind { + // Start is already eaten, eat the rest of identifier. + self.eat_while(|c| { + rustc_lexer::is_id_continue(c) + || (!c.is_ascii() && unic_emoji_char::is_emoji(c)) + || c == '\u{200d}' + }); + // Known prefixes must have been handled earlier. So if + // we see a prefix here, it is definitely an unknown prefix. + InvalidIdent + } +} diff --git a/kclvm/lexer/src/number.rs b/kclvm/lexer/src/number.rs new file mode 100644 index 000000000..f9f7d9ab2 --- /dev/null +++ b/kclvm/lexer/src/number.rs @@ -0,0 +1,142 @@ +//! Numberic tokens handling of the Low-level token stream lexer. +//! Structure and functions are ported from rustc_lexer due to its +//! poor reusability and extensibility. +//! Rust specific literal suffix(e.g. _u8, 2us) is not supported. + +use crate::cursor::Cursor; +use crate::Base; +use crate::LiteralKind; +use crate::LiteralKind::*; + +impl<'a> Cursor<'a> { + pub(crate) fn number(&mut self, first_digit: char) -> LiteralKind { + debug_assert!('0' <= self.prev() && self.prev() <= '9'); + let mut base = Base::Decimal; + if first_digit == '0' { + // Attempt to parse encoding base. + let has_digits = match self.peek() { + 'b' => { + base = Base::Binary; + self.bump(); + self.eat_decimal_digits() + } + 'o' | '0'..='9' => { + // Note: we follow C/Go Octal rule here to allow 0 prefix Octal. + base = Base::Octal; + self.bump(); + self.eat_decimal_digits() + } + 'x' => { + base = Base::Hexadecimal; + self.bump(); + self.eat_hexadecimal_digits() + } + // Not a base prefix. + '_' | '.' | 'e' | 'E' => { + self.eat_decimal_digits(); + true + } + // Just a 0. + _ => { + return Int { + base, + empty_int: false, + } + } + }; + // Base prefix was provided, but there were no digits + // after it, e.g. "0x". + if !has_digits { + return Int { + base, + empty_int: true, + }; + } + } else { + // No base prefix, parse number in the usual way. + self.eat_decimal_digits(); + }; + + match self.peek() { + // Don't be greedy if this is actually an + // integer literal followed by field/method access or a range pattern + // (`0..2` and `12.foo()`) + '.' if self.peek1th() != '.' && !rustc_lexer::is_id_start(self.peek1th()) => { + // might have stuff after the ., and if it does, it needs to start + // with a number + self.bump(); + let mut empty_exponent = false; + if self.peek().is_digit(10) { + self.eat_decimal_digits(); + match self.peek() { + 'e' | 'E' => { + self.bump(); + empty_exponent = !self.eat_float_exponent(); + } + _ => (), + } + } + Float { + base, + empty_exponent, + } + } + 'e' | 'E' => { + self.bump(); + let empty_exponent = !self.eat_float_exponent(); + Float { + base, + empty_exponent, + } + } + _ => Int { + base, + empty_int: false, + }, + } + } + + fn eat_decimal_digits(&mut self) -> bool { + let mut has_digits = false; + loop { + match self.peek() { + '_' => { + self.bump(); + } + '0'..='9' => { + has_digits = true; + self.bump(); + } + _ => break, + } + } + has_digits + } + + fn eat_hexadecimal_digits(&mut self) -> bool { + let mut has_digits = false; + loop { + match self.peek() { + '_' => { + self.bump(); + } + '0'..='9' | 'a'..='f' | 'A'..='F' => { + has_digits = true; + self.bump(); + } + _ => break, + } + } + has_digits + } + + /// Eats the float exponent. Returns true if at least one digit was met, + /// and returns false otherwise. + fn eat_float_exponent(&mut self) -> bool { + debug_assert!(self.prev() == 'e' || self.prev() == 'E'); + if self.peek() == '-' || self.peek() == '+' { + self.bump(); + } + self.eat_decimal_digits() + } +} diff --git a/kclvm/lexer/src/tests.rs b/kclvm/lexer/src/tests.rs new file mode 100644 index 000000000..41eb852f3 --- /dev/null +++ b/kclvm/lexer/src/tests.rs @@ -0,0 +1,321 @@ +use super::*; +use expect_test::{expect, Expect}; + +fn check_lexing(src: &str, expect: Expect) { + let actual: String = tokenize(src) + .map(|token| format!("{:?}\n", token)) + .collect(); + expect.assert_eq(&actual) +} + +#[test] +fn smoke_test() { + check_lexing( + " lambda { println(\"kclvm\"); }\n", + expect![[r#" + Token { kind: Space, len: 1 } + Token { kind: Space, len: 1 } + Token { kind: Ident, len: 6 } + Token { kind: Space, len: 1 } + Token { kind: OpenBrace, len: 1 } + Token { kind: Space, len: 1 } + Token { kind: Ident, len: 7 } + Token { kind: OpenParen, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: false }, suffix_start: 7 }, len: 7 } + Token { kind: CloseParen, len: 1 } + Token { kind: Semi, len: 1 } + Token { kind: Space, len: 1 } + Token { kind: CloseBrace, len: 1 } + Token { kind: Newline, len: 1 } + "#]], + ) +} + +#[test] +fn comment_flavors() { + check_lexing( + r" +# +# line +", + expect![[r#" + Token { kind: Newline, len: 1 } + Token { kind: LineComment { doc_style: Some(Inner) }, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: LineComment { doc_style: Some(Inner) }, len: 6 } + Token { kind: Newline, len: 1 } +"#]], + ) +} + +#[test] +fn simple_tokens() { + check_lexing( + r####" +; +, +. +( +) +{ +} +[ +] +@ +# +~ +? +: +$ += +! +< +> +== +!= +>= +<= +- +& +| ++ +* +/ +^ +% +** +// +<< +>> +... ++= +-= +*= +/= +%= +&= +|= +^= +**= +//= +<<= +>>= +"####, + expect![[r#" + Token { kind: Newline, len: 1 } + Token { kind: Semi, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Comma, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Dot, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: OpenParen, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: CloseParen, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: OpenBrace, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: CloseBrace, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: OpenBracket, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: CloseBracket, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: At, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: LineComment { doc_style: Some(Inner) }, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Tilde, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Question, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Colon, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Dollar, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Eq, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Bang, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Lt, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Gt, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: EqEq, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: BangEq, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: GtEq, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: LtEq, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: Minus, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: And, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Or, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Plus, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Star, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Slash, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Caret, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Percent, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: StarStar, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: SlashSlash, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: LtLt, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: GtGt, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: DotDotDot, len: 3 } + Token { kind: Newline, len: 1 } + Token { kind: PlusEq, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: MinusEq, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: StarEq, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: SlashEq, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: PercentEq, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: AndEq, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: OrEq, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: CaretEq, len: 2 } + Token { kind: Newline, len: 1 } + Token { kind: StarStarEq, len: 3 } + Token { kind: Newline, len: 1 } + Token { kind: SlashSlashEq, len: 3 } + Token { kind: Newline, len: 1 } + Token { kind: LtEq, len: 3 } + Token { kind: Newline, len: 1 } + Token { kind: GtGtEq, len: 3 } + Token { kind: Newline, len: 1 } + "#]], + ) +} + +#[test] +fn nonstring_literal() { + check_lexing( + r####" +1234 +0b101 +0xABC +1.0 +1.0e10 +0777 +0077 +1Ki +"####, + expect![[r#" + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Int { base: Decimal, empty_int: false }, suffix_start: 4 }, len: 4 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Int { base: Binary, empty_int: false }, suffix_start: 5 }, len: 5 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Int { base: Hexadecimal, empty_int: false }, suffix_start: 5 }, len: 5 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Float { base: Decimal, empty_exponent: false }, suffix_start: 3 }, len: 3 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Float { base: Decimal, empty_exponent: false }, suffix_start: 6 }, len: 6 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Int { base: Octal, empty_int: false }, suffix_start: 4 }, len: 4 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Int { base: Octal, empty_int: false }, suffix_start: 4 }, len: 4 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Int { base: Decimal, empty_int: false }, suffix_start: 1 }, len: 3 } + Token { kind: Newline, len: 1 } + "#]], + ) +} + +#[test] +fn string_literal() { + check_lexing( + r####" +'a' +"a" +'''a''' +"""a""" +r'a' +r"a" +r'''a''' +r"""a""" +R'a' +R"a" +R'''a''' +R"""a""" +"####, + expect![[r#" + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: false }, suffix_start: 3 }, len: 3 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: false }, suffix_start: 3 }, len: 3 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: true }, suffix_start: 7 }, len: 7 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: true }, suffix_start: 7 }, len: 7 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: false }, suffix_start: 4 }, len: 4 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: false }, suffix_start: 4 }, len: 4 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: true }, suffix_start: 8 }, len: 8 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: true }, suffix_start: 8 }, len: 8 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: false }, suffix_start: 4 }, len: 4 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: false }, suffix_start: 4 }, len: 4 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: true }, suffix_start: 8 }, len: 8 } + Token { kind: Newline, len: 1 } + Token { kind: Literal { kind: Str { terminated: true, triple_quoted: true }, suffix_start: 8 }, len: 8 } + Token { kind: Newline, len: 1 } + "#]], + ) +} + +#[test] +fn identifier() { + check_lexing( + r####" +a +abc +$schema +"####, + expect![[r#" + Token { kind: Newline, len: 1 } + Token { kind: Ident, len: 1 } + Token { kind: Newline, len: 1 } + Token { kind: Ident, len: 3 } + Token { kind: Newline, len: 1 } + Token { kind: Ident, len: 7 } + Token { kind: Newline, len: 1 } + "#]], + ) +} + +#[test] +fn line_continue() { + check_lexing( + r####" +\ +\ +"####, + expect![[r#" + Token { kind: Newline, len: 1 } + Token { kind: LineContinue, len: 2 } + Token { kind: LineContinue, len: 2 } + "#]], + ) +} diff --git a/kclvm/macros/Cargo.lock b/kclvm/macros/Cargo.lock new file mode 100644 index 000000000..109f22417 --- /dev/null +++ b/kclvm/macros/Cargo.lock @@ -0,0 +1,60 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "kclvm-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" diff --git a/kclvm/macros/Cargo.toml b/kclvm/macros/Cargo.toml new file mode 100644 index 000000000..7945b7958 --- /dev/null +++ b/kclvm/macros/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "kclvm-macros" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +synstructure = "0.12.1" +syn = { version = "1", features = ["full"] } +proc-macro2 = "1" +quote = "1" diff --git a/kclvm/macros/src/lib.rs b/kclvm/macros/src/lib.rs new file mode 100644 index 000000000..c5c621f46 --- /dev/null +++ b/kclvm/macros/src/lib.rs @@ -0,0 +1,8 @@ +use proc_macro::TokenStream; + +mod symbols; + +#[proc_macro] +pub fn symbols(input: TokenStream) -> TokenStream { + symbols::symbols(input.into()).into() +} diff --git a/kclvm/macros/src/symbols.rs b/kclvm/macros/src/symbols.rs new file mode 100644 index 000000000..f218f9f60 --- /dev/null +++ b/kclvm/macros/src/symbols.rs @@ -0,0 +1,221 @@ +//! Proc macro which builds the Symbol table +//! +//! # Debugging +//! +//! Since this proc-macro does some non-trivial work, debugging it is important. +//! This proc-macro can be invoked as an ordinary unit test, like so: +//! +//! Reference: + +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use std::collections::HashMap; +use syn::parse::{Parse, ParseStream, Result}; +use syn::{braced, punctuated::Punctuated, Ident, LitStr, Token}; + +#[cfg(test)] +mod tests; + +mod kw { + syn::custom_keyword!(Keywords); + syn::custom_keyword!(Symbols); +} + +struct Keyword { + name: Ident, + value: LitStr, +} + +impl Parse for Keyword { + fn parse(input: ParseStream<'_>) -> Result { + let name = input.parse()?; + input.parse::()?; + let value = input.parse()?; + + Ok(Keyword { name, value }) + } +} + +struct Symbol { + name: Ident, + value: Option, +} + +impl Parse for Symbol { + fn parse(input: ParseStream<'_>) -> Result { + let name = input.parse()?; + let value = match input.parse::() { + Ok(_) => Some(input.parse()?), + Err(_) => None, + }; + + Ok(Symbol { name, value }) + } +} + +struct Input { + keywords: Punctuated, + symbols: Punctuated, +} + +impl Parse for Input { + fn parse(input: ParseStream<'_>) -> Result { + input.parse::()?; + let content; + braced!(content in input); + let keywords = Punctuated::parse_terminated(&content)?; + + input.parse::()?; + let content; + braced!(content in input); + let symbols = Punctuated::parse_terminated(&content)?; + + Ok(Input { keywords, symbols }) + } +} + +#[derive(Default)] +struct Errors { + list: Vec, +} + +impl Errors { + fn error(&mut self, span: Span, message: String) { + self.list.push(syn::Error::new(span, message)); + } +} + +pub fn symbols(input: TokenStream) -> TokenStream { + let (mut output, errors) = symbols_with_errors(input); + + // If we generated any errors, then report them as compiler_error!() macro calls. + // This lets the errors point back to the most relevant span. It also allows us + // to report as many errors as we can during a single run. + output.extend(errors.into_iter().map(|e| e.to_compile_error())); + + output +} + +fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { + let mut errors = Errors::default(); + + let input: Input = match syn::parse2(input) { + Ok(input) => input, + Err(e) => { + // This allows us to display errors at the proper span, while minimizing + // unrelated errors caused by bailing out (and not generating code). + errors.list.push(e); + Input { + keywords: Default::default(), + symbols: Default::default(), + } + } + }; + + let mut keyword_stream = quote! {}; + let mut symbols_stream = quote! {}; + let mut prefill_stream = quote! {}; + let mut counter = 0u32; + let mut keys = + HashMap::::with_capacity(input.keywords.len() + input.symbols.len() + 10); + let mut prev_key: Option<(Span, String)> = None; + + let mut check_dup = |span: Span, str: &str, errors: &mut Errors| { + if let Some(prev_span) = keys.get(str) { + errors.error(span, format!("Symbol `{}` is duplicated", str)); + errors.error(*prev_span, "location of previous definition".to_string()); + } else { + keys.insert(str.to_string(), span); + } + }; + + let mut check_order = |span: Span, str: &str, errors: &mut Errors| { + if let Some((prev_span, ref prev_str)) = prev_key { + if str < prev_str { + errors.error( + span, + format!("Symbol `{}` must precede `{}`", str, prev_str), + ); + errors.error( + prev_span, + format!("location of previous symbol `{}`", prev_str), + ); + } + } + prev_key = Some((span, str.to_string())); + }; + + // Generate the listed keywords. + for keyword in input.keywords.iter() { + let name = &keyword.name; + let value = &keyword.value; + let value_string = value.value(); + check_dup(keyword.name.span(), &value_string, &mut errors); + prefill_stream.extend(quote! { + #value, + }); + keyword_stream.extend(quote! { + pub const #name: Symbol = Symbol::new(#counter); + }); + counter += 1; + } + + // Generate the listed symbols. + for symbol in input.symbols.iter() { + let name = &symbol.name; + let value = match &symbol.value { + Some(value) => value.value(), + None => name.to_string(), + }; + check_dup(symbol.name.span(), &value, &mut errors); + check_order(symbol.name.span(), &name.to_string(), &mut errors); + + prefill_stream.extend(quote! { + #value, + }); + symbols_stream.extend(quote! { + pub const #name: Symbol = Symbol::new(#counter); + }); + counter += 1; + } + + // Generate symbols for the strings "0", "1", ..., "9". + let digits_base = counter; + counter += 10; + for n in 0..10 { + let n = n.to_string(); + check_dup(Span::call_site(), &n, &mut errors); + prefill_stream.extend(quote! { + #n, + }); + } + let _ = counter; // for future use + + let output = quote! { + const SYMBOL_DIGITS_BASE: u32 = #digits_base; + + #[doc(hidden)] + #[allow(non_upper_case_globals)] + mod kw_generated { + use super::Symbol; + #keyword_stream + } + + #[allow(non_upper_case_globals)] + #[doc(hidden)] + pub mod sym_generated { + use super::Symbol; + #symbols_stream + } + + impl Interner { + pub(crate) fn fresh() -> Self { + Interner::prefill(&[ + #prefill_stream + ]) + } + } + }; + + (output, errors.list) +} diff --git a/kclvm/macros/src/symbols/tests.rs b/kclvm/macros/src/symbols/tests.rs new file mode 100644 index 000000000..897c3fc1a --- /dev/null +++ b/kclvm/macros/src/symbols/tests.rs @@ -0,0 +1,127 @@ +use super::*; + +// This test is mainly here for interactive development. Use this test while +// you're working on the proc-macro defined in this file. +#[test] +fn test_symbols() { + // We textually include the symbol.rs file, which contains the list of all + // symbols, keywords, and common words. Then we search for the + // `symbols! { ... }` call. + + static SYMBOL_RS_FILE: &str = include_str!("../../../span/src/symbol.rs"); + + let file = syn::parse_file(SYMBOL_RS_FILE).unwrap(); + let symbols_path: syn::Path = syn::parse_quote!(symbols); + + let m: &syn::ItemMacro = file + .items + .iter() + .filter_map(|i| { + if let syn::Item::Macro(m) = i { + if m.mac.path == symbols_path { + Some(m) + } else { + None + } + } else { + None + } + }) + .next() + .expect("did not find `symbols!` macro invocation."); + + let body_tokens = m.mac.tokens.clone(); + + test_symbols_macro(body_tokens, &[]); +} + +fn test_symbols_macro(input: TokenStream, expected_errors: &[&str]) { + let (output, found_errors) = symbols_with_errors(input); + + // It should always parse. + let _parsed_file = syn::parse2::(output).unwrap(); + + assert_eq!( + found_errors.len(), + expected_errors.len(), + "Macro generated a different number of errors than expected" + ); + + for (found_error, &expected_error) in found_errors.iter().zip(expected_errors) { + let found_error_str = format!("{}", found_error); + assert_eq!(found_error_str, expected_error); + } +} + +#[test] +fn check_dup_keywords() { + let input = quote! { + Keywords { + Crate: "crate", + Crate: "crate", + } + Symbols {} + }; + test_symbols_macro( + input, + &[ + "Symbol `crate` is duplicated", + "location of previous definition", + ], + ); +} + +#[test] +fn check_dup_symbol() { + let input = quote! { + Keywords {} + Symbols { + splat, + splat, + } + }; + test_symbols_macro( + input, + &[ + "Symbol `splat` is duplicated", + "location of previous definition", + ], + ); +} + +#[test] +fn check_dup_symbol_and_keyword() { + let input = quote! { + Keywords { + Splat: "splat", + } + Symbols { + splat, + } + }; + test_symbols_macro( + input, + &[ + "Symbol `splat` is duplicated", + "location of previous definition", + ], + ); +} + +#[test] +fn check_symbol_order() { + let input = quote! { + Keywords {} + Symbols { + zebra, + aardvark, + } + }; + test_symbols_macro( + input, + &[ + "Symbol `aardvark` must precede `zebra`", + "location of previous symbol `zebra`", + ], + ); +} diff --git a/kclvm/makefile b/kclvm/makefile new file mode 100644 index 000000000..810af635c --- /dev/null +++ b/kclvm/makefile @@ -0,0 +1,42 @@ +default: run + +PWD:=$(shell pwd) +RUNTIME_NATIVE_BC_PATH:=./runtime/src/_kclvm.bc +COVER_TEST_FILE_PATH:=$(PWD)/../cover/TEST.xml +COVER_REPORT_FILE_PATH:=$(PWD)/../cover/report.html +CARGO_LIBS = $(shell find . -maxdepth 1 -type d) + +run: + cd .. && ./run.sh -a update-kclvm && cd kclvm + kcl ../samples/hello.k --target native + +check: + cargo check --release + +fmt: + cargo fmt --all + +lint: + @for i in $(CARGO_LIBS); do cd $(PWD)/$$i && cargo clippy && cd ..; done + +test: + @for i in $(CARGO_LIBS); do cd $(PWD)/$$i && cargo test || { echo 'test kclvm/' $$i 'crate failed' ; exit 1; } && cd ..; done + +test-runtime: + cd ./tests/test_units && python3 -m pip install pytest && PYTHONPATH=./../../plugin python3 -m pytest -vv || { echo 'kclvm/tests/test_units failed' ; exit 1; } + +test-grammar: install-pytest + cd tests/integration/grammar && kclvm -m pytest -v -n 5 --junitxml $(COVER_TEST_FILE_PATH) --html=$(COVER_REPORT_FILE_PATH) + +install-pytest: + kclvm -mpip install pytest-html + +release: + cargo build --release + + +gen-runtime-api: + make -C ./runtime gen-api-spec + +install-rustc-wasm: + rustup target add wasm32-unknown-unknown diff --git a/kclvm/parser/Cargo.lock b/kclvm/parser/Cargo.lock new file mode 100644 index 000000000..a51c6e185 --- /dev/null +++ b/kclvm/parser/Cargo.lock @@ -0,0 +1,1486 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "annotate-snippets" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", +] + +[[package]] +name = "dissimilar" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "enquote" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c36cb11dbde389f4096111698d8b567c0720e3452fd5ac3e6b4e47e1939932" +dependencies = [ + "thiserror", +] + +[[package]] +name = "expect-test" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3e6b28dccda91d8742195c71fbda412112c0c77febf56bf3d895d68b19db16" +dependencies = [ + "dissimilar", + "once_cell", +] + +[[package]] +name = "fancy-regex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "fixedbitset" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" + +[[package]] +name = "fslock" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", + "rustc-rayon", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "json_minimal" +version = "0.1.3" + +[[package]] +name = "kclvm-ast" +version = "0.1.0" +dependencies = [ + "kclvm-span", + "rustc_span", + "serde", + "serde_json", +] + +[[package]] +name = "kclvm-config" +version = "0.1.0" +dependencies = [ + "ahash", + "chrono", + "fslock", + "glob", + "indexmap", + "kclvm-version", + "pathdiff", + "ron", + "rust-crypto", + "serde", + "serde_yaml", + "toml", +] + +[[package]] +name = "kclvm-error" +version = "0.1.0" +dependencies = [ + "annotate-snippets", + "atty", + "indexmap", + "kclvm-span", + "rustc_span", + "termcolor", + "termize", + "tracing", +] + +[[package]] +name = "kclvm-lexer" +version = "0.1.0" +dependencies = [ + "kclvm-error", + "rustc_lexer", + "unic-emoji-char", +] + +[[package]] +name = "kclvm-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "kclvm-parser" +version = "0.1.0" +dependencies = [ + "bstr", + "either", + "enquote", + "expect-test", + "kclvm-ast", + "kclvm-config", + "kclvm-error", + "kclvm-lexer", + "kclvm-sema", + "kclvm-span", + "num-bigint", + "rustc_data_structures", + "rustc_lexer", + "rustc_span", + "serde", + "serde_json", + "tracing", + "unicode_names2", +] + +[[package]] +name = "kclvm-runtime" +version = "0.1.0" +dependencies = [ + "ahash", + "base64", + "bstr", + "chrono", + "fancy-regex", + "indexmap", + "itertools", + "json_minimal", + "kclvm_runtime_internal_macros", + "libc", + "md5", + "num-integer", + "phf", + "regex", + "serde_json", + "serde_yaml", + "sha1", + "sha2 0.9.9", + "unic-ucd-bidi", + "unic-ucd-category", + "unicode-casing", +] + +[[package]] +name = "kclvm-sema" +version = "0.1.0" +dependencies = [ + "ahash", + "bit-set", + "bitflags", + "fancy-regex", + "indexmap", + "kclvm-ast", + "kclvm-error", + "kclvm-runtime", + "kclvm-span", + "once_cell", + "petgraph", + "phf", + "unicode_names2", +] + +[[package]] +name = "kclvm-span" +version = "0.1.0" +dependencies = [ + "kclvm-macros", + "rustc_span", + "scoped-tls", +] + +[[package]] +name = "kclvm-version" +version = "0.1.0" + +[[package]] +name = "kclvm_runtime_internal_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest 0.10.3", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "petgraph" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871372391786ccec00d3c5d3d6608905b3d4db263639cfe075d3b60a736d115a" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ron" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86018df177b1beef6c7c8ef949969c4f7cb9a9344181b92486b23c79995bdaa4" +dependencies = [ + "base64", + "bitflags", + "serde", +] + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +dependencies = [ + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-rayon" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" +dependencies = [ + "crossbeam-deque", + "either", + "rustc-rayon-core", +] + +[[package]] +name = "rustc-rayon-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" + +[[package]] +name = "rustc_data_structures" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ena", + "indexmap", + "jobserver", + "libc", + "memmap2", + "parking_lot", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "stable_deref_trait", + "stacker", + "tempfile", + "tracing", + "winapi", +] + +[[package]] +name = "rustc_lexer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86aae0c77166108c01305ee1a36a1e77289d7dc6ca0a3cd91ff4992de2d16a5" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "rustc_span" +version = "0.0.0" +dependencies = [ + "cfg-if 0.1.10", + "md-5", + "rustc_data_structures", + "scoped-tls", + "sha-1", + "sha2 0.10.2", + "tracing", + "unicode-width", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termize" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-category" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8d4591f5fcfe1bd4453baaf803c40e1b1e69ff8455c47620440b46efef91c0" +dependencies = [ + "matches", + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-casing" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "623f59e6af2a98bdafeb93fa277ac8e1e40440973001ca15cf4ae1541cd16d56" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "unicode_names2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d6678d7916394abad0d4b19df4d3802e1fd84abd7d701f39b75ee71b9e8cf1" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/kclvm/parser/Cargo.toml b/kclvm/parser/Cargo.toml new file mode 100644 index 000000000..df7894a05 --- /dev/null +++ b/kclvm/parser/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "kclvm-parser" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rustc_span = { path = "../3rdparty/rustc_span" } +rustc_data_structures = { path = "../3rdparty/rustc_data_structures" } + +tracing = "0.1" +serde = { version = "1", features = ["derive"] } +serde_json = "1.0" +rustc_lexer = "0.1.0" +either = "1.1.0" +enquote = "1.1.0" +unicode_names2 = "0.4" +bstr = "0.2.16" +num-bigint = "0.4" + +kclvm-lexer = {path = "../lexer", version = "0.1.0"} +kclvm-ast = {path = "../ast", version = "0.1.0"} +kclvm-span = {path = "../span", version = "0.1.0"} +kclvm-error = {path = "../error", version = "0.1.0"} +kclvm-config = {path = "../config", version = "0.1.0"} +kclvm-sema = {path = "../sema", version = "0.1.0"} + +[dev-dependencies] +expect-test = "1.0" \ No newline at end of file diff --git a/kclvm/parser/Makefile b/kclvm/parser/Makefile new file mode 100644 index 000000000..5bc0e2252 --- /dev/null +++ b/kclvm/parser/Makefile @@ -0,0 +1,31 @@ +TARG:=hello.k + +IGNORED_KEYS:=line,column,end_line,end_column,comments,filename + +dev: + -@cargo fmt + + cargo build + ./target/debug/parse_module ${TARG} 2>&1 1> ./a.out.json + + cat ./a.out.json | jq + + -@rm ./a.out.json + +debug: + RUST_BACKTRACE=1 cargo run --bin parse_module ${TARG} + +diff: + -@ rm *.out.json + cargo run --bin load_program ${TARG} | jq -S > 1.out.json + kclvm -m kclvm.internal.kclx -f=${TARG} | jq -S > 0.out.json + code --diff 0.out.json 1.out.json + +ast: + @kclvm -m kclvm.internal.kclx -f=${TARG} | jq + +lint: + cargo clippy + +clean: + cargo clean diff --git a/kclvm/parser/src/bin/load_program.rs b/kclvm/parser/src/bin/load_program.rs new file mode 100644 index 000000000..4d50dc81f --- /dev/null +++ b/kclvm/parser/src/bin/load_program.rs @@ -0,0 +1,8 @@ +extern crate kclvm_parser; + +fn main() { + let filename = std::env::args().nth(1).expect("filename missing"); + let m = kclvm_parser::load_program(&[filename.as_str()], None); + let json = serde_json::ser::to_string(&m).unwrap(); + println!("{}", json); +} diff --git a/kclvm/parser/src/bin/parse_file.rs b/kclvm/parser/src/bin/parse_file.rs new file mode 100644 index 000000000..8c40aa5cf --- /dev/null +++ b/kclvm/parser/src/bin/parse_file.rs @@ -0,0 +1,8 @@ +extern crate kclvm_parser; + +fn main() { + let filename = std::env::args().nth(1).expect("filename missing"); + let m = kclvm_parser::parse_file(filename.as_str(), None); + let json = serde_json::ser::to_string(&m).unwrap(); + println!("{}", json); +} diff --git a/kclvm/parser/src/bin/parse_module.rs b/kclvm/parser/src/bin/parse_module.rs new file mode 100644 index 000000000..81c01dcb0 --- /dev/null +++ b/kclvm/parser/src/bin/parse_module.rs @@ -0,0 +1,8 @@ +extern crate kclvm_parser; + +fn main() { + let filename = std::env::args().nth(1).expect("filename missing"); + let m = kclvm_parser::parse_program(filename.as_str()); + let json = serde_json::ser::to_string(&m).unwrap(); + println!("{}", json); +} diff --git a/kclvm/parser/src/lexer/indent.rs b/kclvm/parser/src/lexer/indent.rs new file mode 100644 index 000000000..d5361d0f5 --- /dev/null +++ b/kclvm/parser/src/lexer/indent.rs @@ -0,0 +1,142 @@ +//! KCL indent handling. +//! See details defined in KCL Grammar ['./spec/grammar']. + +use std::cmp::Ordering; + +use crate::lexer::IndentOrDedents; +use crate::lexer::Lexer; +use kclvm_ast::token::{self, Token}; + +#[derive(Clone, Copy, PartialEq, Debug, Default)] +pub(crate) struct IndentLevel { + pub(crate) tabs: usize, + pub(crate) spaces: usize, +} + +impl IndentLevel { + pub(crate) fn cmp(&self, other: &IndentLevel) -> Result { + match self.tabs.cmp(&other.tabs) { + Ordering::Less => { + if self.spaces <= other.spaces { + Ok(Ordering::Less) + } else { + Err("inconsistent use of tabs and spaces in indentation") + } + } + Ordering::Greater => { + if self.spaces >= other.spaces { + Ok(Ordering::Greater) + } else { + Err("inconsistent use of tabs and spaces in indentation") + } + } + Ordering::Equal => Ok(self.spaces.cmp(&other.spaces)), + } + } +} + +impl<'a> Lexer<'a> { + pub(crate) fn lex_indent_context( + &mut self, + token: kclvm_lexer::TokenKind, + ) -> Option { + // process for indent context for a newline + if !self.indent_cxt.new_line_beginning { + return None; + } + + match token { + kclvm_lexer::TokenKind::LineComment { doc_style: _ } + | kclvm_lexer::TokenKind::Newline => { + // No in(de)ent in comment line and new line + self.indent_cxt.tabs = 0; + self.indent_cxt.spaces = 0; + None + } + kclvm_lexer::TokenKind::Tab => { + self.indent_cxt.tabs += 1; + None + } + kclvm_lexer::TokenKind::Space => { + self.indent_cxt.spaces += 1; + None + } + _ => { + // End of detect on unrelated token, then do lex indent. + self.indent_cxt.new_line_beginning = false; + self.lex_indent() + } + } + } + + fn lex_indent(&mut self) -> Option { + let tabs = self.indent_cxt.tabs; + let spaces = self.indent_cxt.spaces; + // reset counters + self.indent_cxt.tabs = 0; + self.indent_cxt.spaces = 0; + + // process indent at the end of the newline + let mut cur_indent = self.indent_cxt.indents.last().unwrap(); + let indet = IndentLevel { tabs, spaces }; + let mut ordering = indet.cmp(cur_indent); + + match ordering { + Ok(order) => { + Some(match order { + Ordering::Greater => { + self.indent_cxt.indents.push(indet); + + // For indent token, we ignore the length + let indent = Token::new(token::Indent, self.span(self.pos, self.pos)); + IndentOrDedents::Indent { token: indent } + } + Ordering::Less => { + let mut dedents = Vec::new(); + + loop { + match ordering { + Ok(order) => { + match order { + Ordering::Less => { + // Pop indents util we find an equal ident level + self.indent_cxt.indents.pop(); + // update pos & collect dedent + // For dedent token, we ignore the length + let dedent = Token::new( + token::Dedent, + self.span(self.pos, self.pos), + ); + dedents.push(dedent); + } + Ordering::Equal => { + // Proper indent level found. + break; + } + Ordering::Greater => self.sess.struct_span_error( + "fatal: logic error on dedenting.", + self.span(self.pos, self.pos), + ), + } + + // update cur indent and ordering + cur_indent = self.indent_cxt.indents.last().unwrap(); + ordering = indet.cmp(cur_indent); + } + Err(msg) => self + .sess + .struct_span_error(msg, self.span(self.pos, self.pos)), + } + } + + IndentOrDedents::Dedents { tokens: dedents } + } + _ => return None, + }) + } + Err(msg) => self + .sess + .struct_span_error(msg, self.span(self.pos, self.pos)), + } + } +} diff --git a/kclvm/parser/src/lexer/mod.rs b/kclvm/parser/src/lexer/mod.rs new file mode 100644 index 000000000..06c60490f --- /dev/null +++ b/kclvm/parser/src/lexer/mod.rs @@ -0,0 +1,584 @@ +//! A KCL lexer. +//! +//! The lexer is built on the low level [`kclvm_lexer`], and works +//! based on the rules defined by the KCL grammar ['./spec/grammar']. +//! +//! It's main responsibilities: +//! 1. Mapping low level [`kclvm_lexer::Token`] tokens into [`kclvm_ast::Token`] tokens, +//! and provide TokenStream to downstream [`kclvm_parser::parser`]. +//! 2. Validations on Literals(String, Int, Float). +//! 3. Validations on closure of delim tokens. +//! 4. Validations on indent and dedent. +//! +//! The main differences of tokens between ast and lexer is: +//! 1. AST Affinity, based on unary, binary and other operations. +//! 2. Has Indent and dedent. +//! 3. Don't have some tokens(such as ';', '..', '..=', '<-') + +mod indent; +mod string; + +#[cfg(test)] +mod tests; + +use kclvm_ast::ast::NumberBinarySuffix; +use kclvm_ast::token::{self, CommentKind, Token, TokenKind}; +use kclvm_ast::token_stream::TokenStream; +use kclvm_lexer::Base; +use kclvm_span::symbol::Symbol; +use kclvm_span::{self, BytePos, Span}; +use rustc_span::Pos; +pub(crate) use string::str_content_eval; + +use self::indent::IndentLevel; +use crate::session::ParseSession; + +/// EntryPoint of the lexer. +/// Parse token streams from an input raw string and a fixed start point. +/// Return an iterable token stream. +pub fn parse_token_streams(sess: &ParseSession, src: &str, start_pos: BytePos) -> TokenStream { + Lexer { + sess, + start_pos, + pos: start_pos, + end_src_index: src.len(), + src, + token: TokenWithIndents::Token { + token: Token::dummy(), + }, + indent_cxt: IndentContext { + nesting: 0, + tabs: 0, + spaces: 0, + new_line_beginning: false, + indents: vec![Default::default()], + }, + } + .into_tokens() +} + +/// A token or a token with indent. +enum TokenWithIndents { + Token { + token: Token, + }, + WithIndent { + token: Token, + indent: IndentOrDedents, + }, +} + +/// A indent or a fixed count of dedents. +enum IndentOrDedents { + Indent { token: Token }, + Dedents { tokens: Vec }, +} + +impl TokenWithIndents { + pub(crate) fn is_eof(&self) -> bool { + match self { + TokenWithIndents::Token { token } => *token == token::Eof, + TokenWithIndents::WithIndent { token, indent: _ } => *token == token::Eof, + } + } + + pub(crate) fn append_to(&self, buf: &mut TokenStreamBuilder) { + match self { + TokenWithIndents::Token { token } => { + buf.push(*token); + } + TokenWithIndents::WithIndent { token, indent } => { + match indent { + IndentOrDedents::Indent { token } => { + buf.push(*token); + } + IndentOrDedents::Dedents { tokens } => { + for dedent in tokens { + buf.push(*dedent); + } + } + } + + buf.push(*token); + } + } + } +} + +struct Lexer<'a> { + /// Initial position, read-only. + start_pos: BytePos, + + /// The absolute offset within the source_map of the current character. + pos: BytePos, + + /// Stop reading src at this index. + end_src_index: usize, + + /// Source text to tokenize. + src: &'a str, + + /// Token + token: TokenWithIndents, + + /// A on-going context to handle indent/dedents + indent_cxt: IndentContext, + + /// parse-time session + pub sess: &'a ParseSession, +} + +struct IndentContext { + /// nested level counter + nesting: usize, + + /// A new line flag + new_line_beginning: bool, + + /// tab counter + tabs: usize, + + /// space counter + spaces: usize, + + /// Indents stack + indents: Vec, +} + +impl<'a> Lexer<'a> { + fn into_tokens(mut self) -> TokenStream { + let mut buf = TokenStreamBuilder::default(); + self.token = self.token(); + + while !self.token.is_eof() { + self.token.append_to(&mut buf); + self.token = self.token(); + } + + self.eof(&mut buf); + buf.into_token_stream() + } + + fn token(&mut self) -> TokenWithIndents { + loop { + let start_src_index = self.src_index(self.pos); + let text: &str = &self.src[start_src_index..self.end_src_index]; + + if text.is_empty() { + return TokenWithIndents::Token { + token: Token::new(token::Eof, self.span(self.pos, self.pos)), + }; + } + + // fetch next token + let token = kclvm_lexer::first_token(text); + + // Detect and handle indent cases before lexing on-going token + let indent = self.lex_indent_context(token.kind); + + let start = self.pos; + // update pos after token and indent handling + self.pos = self.pos + BytePos::from_usize(token.len); + + if let Some(kind) = self.lex_token(token, start) { + let span = self.span(start, self.pos); + + match indent { + Some(iord) => { + // return the token with the leading indent/dedents + return TokenWithIndents::WithIndent { + token: Token::new(kind, span), + indent: iord, + }; + } + None => { + // return the token itself + return TokenWithIndents::Token { + token: Token::new(kind, span), + }; + } + } + } + } + } + + /// Turns `kclvm_lexer::TokenKind` into a rich `kclvm_ast::TokenKind`. + fn lex_token(&mut self, token: kclvm_lexer::Token, start: BytePos) -> Option { + Some(match token.kind { + kclvm_lexer::TokenKind::LineComment { doc_style: _ } => { + let s = self.str_from(start); + token::DocComment(CommentKind::Line(Symbol::intern(s))) + } + // Whitespace + kclvm_lexer::TokenKind::Newline => { + self.indent_cxt.new_line_beginning = true; + token::Newline + } + kclvm_lexer::TokenKind::Tab + | kclvm_lexer::TokenKind::Space + | kclvm_lexer::TokenKind::CarriageReturn + | kclvm_lexer::TokenKind::Whitespace => return None, + // Identifier + kclvm_lexer::TokenKind::Ident => { + let s = self.str_from(start); + token::Ident(Symbol::intern(s)) + } + // Literal + kclvm_lexer::TokenKind::Literal { kind, suffix_start } => { + let suffix_start = start + BytePos::from_u32(suffix_start as u32); + let (kind, symbol, suffix, raw) = self.lex_literal(start, suffix_start, kind); + token::Literal(token::Lit { + kind, + symbol, + suffix, + raw, + }) + } + // Unary op + kclvm_lexer::TokenKind::Tilde => token::UnaryOp(token::UTilde), + kclvm_lexer::TokenKind::Bang => token::UnaryOp(token::UNot), + // Binary op + kclvm_lexer::TokenKind::Plus => token::BinOp(token::Plus), + kclvm_lexer::TokenKind::Minus => { + let next_tkn = + self.str_from_to(start + BytePos::from_u32(1), start + BytePos::from_u32(2)); + if next_tkn == ">" { + // waste '>' token + self.pos = self.pos + BytePos::from_usize(1); + token::RArrow + } else { + token::BinOp(token::Minus) + } + } + kclvm_lexer::TokenKind::Star => token::BinOp(token::Star), + kclvm_lexer::TokenKind::Slash => token::BinOp(token::Slash), + kclvm_lexer::TokenKind::Percent => token::BinOp(token::Percent), + kclvm_lexer::TokenKind::StarStar => token::BinOp(token::StarStar), + kclvm_lexer::TokenKind::SlashSlash => token::BinOp(token::SlashSlash), + kclvm_lexer::TokenKind::Caret => token::BinOp(token::Caret), + kclvm_lexer::TokenKind::And => token::BinOp(token::And), + kclvm_lexer::TokenKind::Or => token::BinOp(token::Or), + kclvm_lexer::TokenKind::LtLt => token::BinOp(token::Shl), + kclvm_lexer::TokenKind::GtGt => token::BinOp(token::Shr), + // Binary op eq + kclvm_lexer::TokenKind::PlusEq => token::BinOpEq(token::Plus), + kclvm_lexer::TokenKind::MinusEq => token::BinOpEq(token::Minus), + kclvm_lexer::TokenKind::StarEq => token::BinOpEq(token::Star), + kclvm_lexer::TokenKind::SlashEq => token::BinOpEq(token::Slash), + kclvm_lexer::TokenKind::PercentEq => token::BinOpEq(token::Percent), + kclvm_lexer::TokenKind::StarStarEq => token::BinOpEq(token::StarStar), + kclvm_lexer::TokenKind::SlashSlashEq => token::BinOpEq(token::SlashSlash), + kclvm_lexer::TokenKind::CaretEq => token::BinOpEq(token::Caret), + kclvm_lexer::TokenKind::AndEq => token::BinOpEq(token::And), + kclvm_lexer::TokenKind::OrEq => token::BinOpEq(token::Or), + kclvm_lexer::TokenKind::LtLtEq => token::BinOpEq(token::Shl), + kclvm_lexer::TokenKind::GtGtEq => token::BinOpEq(token::Shr), + // Binary cmp + kclvm_lexer::TokenKind::EqEq => token::BinCmp(token::Eq), + kclvm_lexer::TokenKind::BangEq => token::BinCmp(token::NotEq), + kclvm_lexer::TokenKind::Lt => token::BinCmp(token::Lt), + kclvm_lexer::TokenKind::LtEq => token::BinCmp(token::LtEq), + kclvm_lexer::TokenKind::Gt => token::BinCmp(token::Gt), + kclvm_lexer::TokenKind::GtEq => token::BinCmp(token::GtEq), + // Structural symbols + kclvm_lexer::TokenKind::At => token::At, + kclvm_lexer::TokenKind::Dot => token::Dot, + kclvm_lexer::TokenKind::DotDotDot => token::DotDotDot, + kclvm_lexer::TokenKind::Comma => token::Comma, + kclvm_lexer::TokenKind::Colon => token::Colon, + kclvm_lexer::TokenKind::Dollar => token::Dollar, + kclvm_lexer::TokenKind::Question => token::Question, + kclvm_lexer::TokenKind::Eq => token::Assign, + // Delim tokens + kclvm_lexer::TokenKind::OpenParen => { + self.indent_cxt.nesting += 1; + token::OpenDelim(token::Paren) + } + kclvm_lexer::TokenKind::CloseParen => { + if self.indent_cxt.nesting == 0 { + self.sess.struct_span_error( + "error nesting on close paren", + self.span(start, self.pos), + ) + } + self.indent_cxt.nesting -= 1; + token::CloseDelim(token::Paren) + } + kclvm_lexer::TokenKind::OpenBrace => { + self.indent_cxt.nesting += 1; + token::OpenDelim(token::Brace) + } + kclvm_lexer::TokenKind::CloseBrace => { + if self.indent_cxt.nesting == 0 { + self.sess.struct_span_error( + "error nesting on close brace", + self.span(start, self.pos), + ) + } + self.indent_cxt.nesting -= 1; + token::CloseDelim(token::Brace) + } + kclvm_lexer::TokenKind::OpenBracket => { + self.indent_cxt.nesting += 1; + token::OpenDelim(token::Bracket) + } + kclvm_lexer::TokenKind::CloseBracket => { + if self.indent_cxt.nesting == 0 { + self.sess.struct_span_error( + "error nesting on close bracket", + self.span(start, self.pos), + ) + } + self.indent_cxt.nesting -= 1; + token::CloseDelim(token::Bracket) + } + kclvm_lexer::TokenKind::LineContinue => return None, + kclvm_lexer::TokenKind::InvalidLineContinue => self.sess.struct_span_error( + "unexpected character after line continuation character", + self.span(start, self.pos), + ), + _ => self + .sess + .struct_span_error("unknown start of token", self.span(start, self.pos)), + }) + } + + fn lex_literal( + &self, + start: BytePos, + suffix_start: BytePos, + kind: kclvm_lexer::LiteralKind, + ) -> (token::LitKind, Symbol, Option, Option) { + match kind { + kclvm_lexer::LiteralKind::Str { + terminated, + triple_quoted, + } => { + if !terminated { + self.sess + .struct_span_error("unterminated string", self.span(start, self.pos)) + } + + let start_char = self.char_from(start); + let (is_raw, quote_char) = match start_char { + 'r' | 'R' => (true, self.char_from(start + BytePos::from_u32(1))), + _ => (false, start_char), + }; + + // cut offset before validation + let offset = if triple_quoted { + if is_raw { + 4 + } else { + 3 + } + } else if is_raw { + 2 + } else { + 1 + }; + + let content_start = start + BytePos::from_u32(offset); + let mut content_end = suffix_start - BytePos::from_u32(offset); + if is_raw { + content_end = content_end + BytePos::from_u32(1); + } + let string_content = self.str_from_to(content_start, content_end); + let value = match str_content_eval( + string_content, + quote_char, + triple_quoted, + false, + is_raw, + ) { + Some(v) => v, + None => self.sess.struct_span_error( + "Invalid string syntax", + self.span(content_start, self.pos), + ), + }; + + ( + token::Str { + is_long_string: triple_quoted, + is_raw, + }, + Symbol::intern(&value), + None, + Some(self.symbol_from_to(start, suffix_start)), + ) + } + kclvm_lexer::LiteralKind::Int { base, empty_int } => { + if empty_int { + self.sess.struct_span_error( + "no valid digits found for number", + self.span(start, self.pos), + ) + } else { + self.validate_literal_int(base, start, suffix_start); + + let suffix = if suffix_start < self.pos { + let suffix_str = self.str_from(suffix_start); + // int binary suffix + if !NumberBinarySuffix::all_names().contains(&suffix_str) { + self.sess.struct_span_error( + "invalid int binary suffix", + self.span(start, self.pos), + ) + } + Some(Symbol::intern(suffix_str)) + } else { + None + }; + + ( + token::Integer, + self.symbol_from_to(start, suffix_start), + suffix, + None, + ) + } + } + + kclvm_lexer::LiteralKind::Float { + base, + empty_exponent, + } => { + self.validate_literal_float(base, start, empty_exponent); + ( + token::Float, + self.symbol_from_to(start, suffix_start), + None, + None, + ) + } + kclvm_lexer::LiteralKind::Bool { terminated: _ } => ( + token::Bool, + self.symbol_from_to(start, suffix_start), + None, + None, + ), + _ => self.sess.struct_span_error( + &format!("invalid lit kind {:?}", kind), + self.span(start, self.pos), + ), + } + } + + fn validate_literal_int(&self, base: Base, content_start: BytePos, content_end: BytePos) { + let base = match base { + Base::Binary => 2, + Base::Octal => 8, + Base::Hexadecimal => 16, + _ => return, + }; + let s = self.str_from_to(content_start + BytePos::from_u32(2), content_end); + for (idx, c) in s.char_indices() { + let idx = idx as u32; + if c != '_' && c.to_digit(base).is_none() { + let lo = content_start + BytePos::from_u32(2 + idx); + let hi = content_start + BytePos::from_u32(2 + idx + c.len_utf8() as u32); + + self.sess.struct_span_error( + &format!( + "invalid digit for a base {} literal, start: {}, stop: {}", + base, lo, hi + ), + self.span(lo, self.pos), + ) + } + } + } + + fn validate_literal_float(&self, base: Base, start: BytePos, empty_exponent: bool) { + if empty_exponent { + self.sess.struct_span_error( + "expected at least one digit in exponent", + self.span(start, self.pos), + ) + } + + match base { + kclvm_lexer::Base::Hexadecimal => self.sess.struct_span_error( + "hexadecimal float literal is not supported", + self.span(start, self.pos), + ), + kclvm_lexer::Base::Octal => self.sess.struct_span_error( + "octal float literal is not supported", + self.span(start, self.pos), + ), + kclvm_lexer::Base::Binary => self.sess.struct_span_error( + "binary float literal is not supported", + self.span(start, self.pos), + ), + _ => (), + } + } + + fn span(&self, lo: BytePos, hi: BytePos) -> Span { + Span::new(lo, hi) + } + + #[inline] + fn src_index(&self, pos: BytePos) -> usize { + (pos - self.start_pos).to_usize() + } + + /// Char at `pos` in the source + fn char_from(&self, pos: BytePos) -> char { + self.src.as_bytes()[self.src_index(pos)] as char + } + + /// Slice of the source text from `start` up to but excluding `self.pos`, + /// meaning the slice does not include the character `self.ch`. + fn str_from(&self, start: BytePos) -> &str { + self.str_from_to(start, self.pos) + } + + /// Slice of the source text spanning from `start` up to but excluding `end`. + fn str_from_to(&self, start: BytePos, end: BytePos) -> &str { + &self.src[self.src_index(start)..self.src_index(end)] + } + + fn symbol_from_to(&self, start: BytePos, end: BytePos) -> Symbol { + Symbol::intern(self.str_from_to(start, end)) + } + + fn eof(&mut self, buf: &mut TokenStreamBuilder) { + let start = self.pos; + + if self.indent_cxt.nesting > 0 { + self.sess.struct_span_error( + "Unclosed nesting at the end of the file", + self.span(start, self.pos), + ) + } + + if !self.indent_cxt.new_line_beginning { + self.indent_cxt.new_line_beginning = true; + buf.push(Token::new(token::Newline, self.span(self.pos, self.pos))); + } + + while self.indent_cxt.indents.len() > 1 { + self.indent_cxt.indents.pop(); + buf.push(Token::new(token::Dedent, self.span(self.pos, self.pos))); + } + + buf.push(Token::new(token::Eof, self.span(self.pos, self.pos))); + } +} + +#[derive(Default)] +struct TokenStreamBuilder { + buf: Vec, +} + +impl TokenStreamBuilder { + fn push(&mut self, token: Token) { + self.buf.push(token) + } + + fn into_token_stream(self) -> TokenStream { + TokenStream::new(self.buf) + } +} diff --git a/kclvm/parser/src/lexer/string.rs b/kclvm/parser/src/lexer/string.rs new file mode 100644 index 000000000..fd490c105 --- /dev/null +++ b/kclvm/parser/src/lexer/string.rs @@ -0,0 +1,132 @@ +//! This module mainly provides KCL literals into forms that can be +//! represented by Rust literals. +//! +//! Reference: +//! - https://github.com/RustPython/RustPython/blob/main/parser/src/lexer.rs +//! - https://docs.python.org/3/library/string.html +use std::str::Chars; + +/// Eval a string content a Rust string. +/// todo: use in place algorithm through pointers. +pub(crate) fn str_content_eval( + string_content: &str, + quote_char: char, + triple_quoted: bool, + is_bytes: bool, + is_raw: bool, +) -> Option { + let mut chars: std::iter::Peekable = string_content.chars().peekable(); + let mut string_content = String::new(); + loop { + match chars.next() { + Some('\\') => { + let next_char = chars.next(); + if next_char == Some(quote_char) && !is_raw { + string_content.push(quote_char); + } else if is_raw { + string_content.push('\\'); + if let Some(c) = next_char { + string_content.push(c) + } else { + return None; + } + } else { + match next_char { + Some('\\') => { + string_content.push('\\'); + } + Some('\'') => string_content.push('\''), + Some('\"') => string_content.push('\"'), + Some('\n') => { + // Ignore Unix EOL character + } + Some('a') => string_content.push('\x07'), + Some('b') => string_content.push('\x08'), + Some('f') => string_content.push('\x0c'), + Some('n') => { + string_content.push('\n'); + } + Some('r') => string_content.push('\r'), + Some('t') => { + string_content.push('\t'); + } + Some('v') => string_content.push('\x0b'), + Some(o @ '0'..='7') => string_content.push(parse_octet(o, &mut chars)), + Some('x') => string_content.push(unicode_literal(2, &mut chars)?), + Some('u') if !is_bytes => { + string_content.push(unicode_literal(4, &mut chars)?) + } + Some('U') if !is_bytes => { + string_content.push(unicode_literal(8, &mut chars)?) + } + Some('N') if !is_bytes => { + string_content.push(parse_unicode_name(&mut chars)?) + } + Some(c) => { + string_content.push('\\'); + string_content.push(c); + } + None => return None, + } + } + } + Some(c) => { + if (c == '\n' && !triple_quoted) || (is_bytes && !c.is_ascii()) { + return None; + } + string_content.push(c); + } + None => break, + } + } + Some(string_content) +} + +/// Parse unicode literal. +fn unicode_literal(literal_number: usize, chars: &mut std::iter::Peekable) -> Option { + let mut p: u32 = 0u32; + for i in 1..=literal_number { + match chars.next() { + Some(c) => match c.to_digit(16) { + Some(d) => p += d << ((literal_number - i) * 4), + None => return None, + }, + None => return None, + } + } + match p { + 0xD800..=0xDFFF => Some(std::char::REPLACEMENT_CHARACTER), + _ => std::char::from_u32(p), + } +} + +fn parse_octet(first: char, chars: &mut std::iter::Peekable) -> char { + let mut octet_content = String::new(); + octet_content.push(first); + while octet_content.len() < 3 { + let next_char = chars.next(); + if let Some('0'..='7') = next_char { + octet_content.push(next_char.unwrap()) + } else { + break; + } + } + let value = u32::from_str_radix(&octet_content, 8).unwrap(); + char::from_u32(value).unwrap() +} + +fn parse_unicode_name(chars: &mut std::iter::Peekable) -> Option { + match chars.next() { + Some('{') => {} + _ => return None, + } + let mut name = String::new(); + loop { + match chars.next() { + Some('}') => break, + Some(c) => name.push(c), + None => return None, + } + } + unicode_names2::character(&name) +} diff --git a/kclvm/parser/src/lexer/tests.rs b/kclvm/parser/src/lexer/tests.rs new file mode 100644 index 000000000..a5216d53d --- /dev/null +++ b/kclvm/parser/src/lexer/tests.rs @@ -0,0 +1,490 @@ +use super::*; +use crate::lexer::str_content_eval; +use crate::session::ParseSession; +use expect_test::{expect, Expect}; +use kclvm_span::{create_session_globals_then, BytePos, FilePathMapping, SourceMap}; +use std::path::PathBuf; +use std::sync::Arc; + +fn check_lexing(src: &str, expect: Expect) { + let sm = SourceMap::new(FilePathMapping::empty()); + sm.new_source_file(PathBuf::from("").into(), src.to_string()); + let sess = &ParseSession::with_source_map(Arc::new(sm)); + + create_session_globals_then(|| { + let actual: String = parse_token_streams(sess, src, BytePos::from_u32(0)) + .iter() + .map(|token| format!("{:?}\n", token)) + .collect(); + expect.assert_eq(&actual) + }); +} + +#[test] +fn test_str_content_eval() { + let cases = [ + // true cases + (("1", '\'', false, false, false), Some("1".to_string())), + (("1", '"', false, false, false), Some("1".to_string())), + ( + ("1\\n2", '"', false, false, false), + Some("1\n2".to_string()), + ), + ( + ("1\\n2", '"', false, false, true), + Some("1\\n2".to_string()), + ), + (("1\\2", '"', false, false, true), Some("1\\2".to_string())), + (("1", '\'', true, false, false), Some("1".to_string())), + (("1", '"', true, false, false), Some("1".to_string())), + (("1\n2", '"', true, false, false), Some("1\n2".to_string())), + ]; + for ((input, quote_char, triple_quoted, is_bytes, is_raw), expected) in cases { + assert_eq!( + str_content_eval(input, quote_char, triple_quoted, is_bytes, is_raw), + expected, + "test failed, input: {}", + input + ) + } +} + +#[test] +fn smoke_test() { + check_lexing( + "lambda { println(\"kclvm\") }\n", + expect![[r#" + Token { kind: Ident(Symbol(SymbolIndex { idx: 18 })), span: Span { base_or_index: 0, len_or_tag: 6 } } + Token { kind: OpenDelim(Brace), span: Span { base_or_index: 7, len_or_tag: 1 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 42 })), span: Span { base_or_index: 9, len_or_tag: 7 } } + Token { kind: OpenDelim(Paren), span: Span { base_or_index: 16, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: false, is_raw: false }, symbol: Symbol(SymbolIndex { idx: 43 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 44 })) }), span: Span { base_or_index: 17, len_or_tag: 7 } } + Token { kind: CloseDelim(Paren), span: Span { base_or_index: 24, len_or_tag: 1 } } + Token { kind: CloseDelim(Brace), span: Span { base_or_index: 26, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 27, len_or_tag: 1 } } + Token { kind: Eof, span: Span { base_or_index: 28, len_or_tag: 0 } } + "#]], + ) +} + +#[test] +fn comment_flavors() { + check_lexing( + r" +# line +", + expect![[r#" + Token { kind: Newline, span: Span { base_or_index: 0, len_or_tag: 1 } } + Token { kind: DocComment(Line(Symbol(SymbolIndex { idx: 42 }))), span: Span { base_or_index: 1, len_or_tag: 6 } } + Token { kind: Newline, span: Span { base_or_index: 7, len_or_tag: 1 } } + Token { kind: Eof, span: Span { base_or_index: 8, len_or_tag: 0 } } +"#]], + ) +} + +#[test] +fn simple_tokens() { + check_lexing( + r####" +, +. +( +) +{ +} +[ +] +@ +# +~ +? +: +$ += +! +< +> +== +!= +>= +<= +- +& +| ++ +* +/ +^ +% +** +// +<< +>> +... ++= +-= +*= +/= +%= +&= +|= +^= +**= +//= +<<= +>>= +-> +"####, + expect![[r#" + Token { kind: Newline, span: Span { base_or_index: 0, len_or_tag: 1 } } + Token { kind: Comma, span: Span { base_or_index: 1, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 2, len_or_tag: 1 } } + Token { kind: Dot, span: Span { base_or_index: 3, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 4, len_or_tag: 1 } } + Token { kind: OpenDelim(Paren), span: Span { base_or_index: 5, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 6, len_or_tag: 1 } } + Token { kind: CloseDelim(Paren), span: Span { base_or_index: 7, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 8, len_or_tag: 1 } } + Token { kind: OpenDelim(Brace), span: Span { base_or_index: 9, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 10, len_or_tag: 1 } } + Token { kind: CloseDelim(Brace), span: Span { base_or_index: 11, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 12, len_or_tag: 1 } } + Token { kind: OpenDelim(Bracket), span: Span { base_or_index: 13, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 14, len_or_tag: 1 } } + Token { kind: CloseDelim(Bracket), span: Span { base_or_index: 15, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 16, len_or_tag: 1 } } + Token { kind: At, span: Span { base_or_index: 17, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 18, len_or_tag: 1 } } + Token { kind: DocComment(Line(Symbol(SymbolIndex { idx: 42 }))), span: Span { base_or_index: 19, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 20, len_or_tag: 1 } } + Token { kind: UnaryOp(UTilde), span: Span { base_or_index: 21, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 22, len_or_tag: 1 } } + Token { kind: Question, span: Span { base_or_index: 23, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 24, len_or_tag: 1 } } + Token { kind: Colon, span: Span { base_or_index: 25, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 26, len_or_tag: 1 } } + Token { kind: Dollar, span: Span { base_or_index: 27, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 28, len_or_tag: 1 } } + Token { kind: Assign, span: Span { base_or_index: 29, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 30, len_or_tag: 1 } } + Token { kind: UnaryOp(UNot), span: Span { base_or_index: 31, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 32, len_or_tag: 1 } } + Token { kind: BinCmp(Lt), span: Span { base_or_index: 33, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 34, len_or_tag: 1 } } + Token { kind: BinCmp(Gt), span: Span { base_or_index: 35, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 36, len_or_tag: 1 } } + Token { kind: BinCmp(Eq), span: Span { base_or_index: 37, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 39, len_or_tag: 1 } } + Token { kind: BinCmp(NotEq), span: Span { base_or_index: 40, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 42, len_or_tag: 1 } } + Token { kind: BinCmp(GtEq), span: Span { base_or_index: 43, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 45, len_or_tag: 1 } } + Token { kind: BinCmp(LtEq), span: Span { base_or_index: 46, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 48, len_or_tag: 1 } } + Token { kind: BinOp(Minus), span: Span { base_or_index: 49, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 50, len_or_tag: 1 } } + Token { kind: BinOp(And), span: Span { base_or_index: 51, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 52, len_or_tag: 1 } } + Token { kind: BinOp(Or), span: Span { base_or_index: 53, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 54, len_or_tag: 1 } } + Token { kind: BinOp(Plus), span: Span { base_or_index: 55, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 56, len_or_tag: 1 } } + Token { kind: BinOp(Star), span: Span { base_or_index: 57, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 58, len_or_tag: 1 } } + Token { kind: BinOp(Slash), span: Span { base_or_index: 59, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 60, len_or_tag: 1 } } + Token { kind: BinOp(Caret), span: Span { base_or_index: 61, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 62, len_or_tag: 1 } } + Token { kind: BinOp(Percent), span: Span { base_or_index: 63, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 64, len_or_tag: 1 } } + Token { kind: BinOp(StarStar), span: Span { base_or_index: 65, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 67, len_or_tag: 1 } } + Token { kind: BinOp(SlashSlash), span: Span { base_or_index: 68, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 70, len_or_tag: 1 } } + Token { kind: BinOp(Shl), span: Span { base_or_index: 71, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 73, len_or_tag: 1 } } + Token { kind: BinOp(Shr), span: Span { base_or_index: 74, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 76, len_or_tag: 1 } } + Token { kind: DotDotDot, span: Span { base_or_index: 77, len_or_tag: 3 } } + Token { kind: Newline, span: Span { base_or_index: 80, len_or_tag: 1 } } + Token { kind: BinOpEq(Plus), span: Span { base_or_index: 81, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 83, len_or_tag: 1 } } + Token { kind: BinOpEq(Minus), span: Span { base_or_index: 84, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 86, len_or_tag: 1 } } + Token { kind: BinOpEq(Star), span: Span { base_or_index: 87, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 89, len_or_tag: 1 } } + Token { kind: BinOpEq(Slash), span: Span { base_or_index: 90, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 92, len_or_tag: 1 } } + Token { kind: BinOpEq(Percent), span: Span { base_or_index: 93, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 95, len_or_tag: 1 } } + Token { kind: BinOpEq(And), span: Span { base_or_index: 96, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 98, len_or_tag: 1 } } + Token { kind: BinOpEq(Or), span: Span { base_or_index: 99, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 101, len_or_tag: 1 } } + Token { kind: BinOpEq(Caret), span: Span { base_or_index: 102, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 104, len_or_tag: 1 } } + Token { kind: BinOpEq(StarStar), span: Span { base_or_index: 105, len_or_tag: 3 } } + Token { kind: Newline, span: Span { base_or_index: 108, len_or_tag: 1 } } + Token { kind: BinOpEq(SlashSlash), span: Span { base_or_index: 109, len_or_tag: 3 } } + Token { kind: Newline, span: Span { base_or_index: 112, len_or_tag: 1 } } + Token { kind: BinCmp(LtEq), span: Span { base_or_index: 113, len_or_tag: 3 } } + Token { kind: Newline, span: Span { base_or_index: 116, len_or_tag: 1 } } + Token { kind: BinOpEq(Shr), span: Span { base_or_index: 117, len_or_tag: 3 } } + Token { kind: Newline, span: Span { base_or_index: 120, len_or_tag: 1 } } + Token { kind: RArrow, span: Span { base_or_index: 121, len_or_tag: 2 } } + Token { kind: Newline, span: Span { base_or_index: 123, len_or_tag: 1 } } + Token { kind: Eof, span: Span { base_or_index: 124, len_or_tag: 0 } } + "#]], + ) +} + +#[test] +fn nonstring_literal() { + check_lexing( + r####" +1234 +0b101 +0xABC +1.0 +1.0e10 +0777 +0077 +1Ki +"####, + expect![[r#" + Token { kind: Newline, span: Span { base_or_index: 0, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Integer, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: None }), span: Span { base_or_index: 1, len_or_tag: 4 } } + Token { kind: Newline, span: Span { base_or_index: 5, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Integer, symbol: Symbol(SymbolIndex { idx: 43 }), suffix: None, raw: None }), span: Span { base_or_index: 6, len_or_tag: 5 } } + Token { kind: Newline, span: Span { base_or_index: 11, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Integer, symbol: Symbol(SymbolIndex { idx: 44 }), suffix: None, raw: None }), span: Span { base_or_index: 12, len_or_tag: 5 } } + Token { kind: Newline, span: Span { base_or_index: 17, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Float, symbol: Symbol(SymbolIndex { idx: 45 }), suffix: None, raw: None }), span: Span { base_or_index: 18, len_or_tag: 3 } } + Token { kind: Newline, span: Span { base_or_index: 21, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Float, symbol: Symbol(SymbolIndex { idx: 46 }), suffix: None, raw: None }), span: Span { base_or_index: 22, len_or_tag: 6 } } + Token { kind: Newline, span: Span { base_or_index: 28, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Integer, symbol: Symbol(SymbolIndex { idx: 47 }), suffix: None, raw: None }), span: Span { base_or_index: 29, len_or_tag: 4 } } + Token { kind: Newline, span: Span { base_or_index: 33, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Integer, symbol: Symbol(SymbolIndex { idx: 48 }), suffix: None, raw: None }), span: Span { base_or_index: 34, len_or_tag: 4 } } + Token { kind: Newline, span: Span { base_or_index: 38, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Integer, symbol: Symbol(SymbolIndex { idx: 33 }), suffix: Some(Symbol(SymbolIndex { idx: 49 })), raw: None }), span: Span { base_or_index: 39, len_or_tag: 3 } } + Token { kind: Newline, span: Span { base_or_index: 42, len_or_tag: 1 } } + Token { kind: Eof, span: Span { base_or_index: 43, len_or_tag: 0 } } + "#]], + ) +} + +#[test] +fn string_literal() { + check_lexing( + r####" +'a' +"a" +'''a''' +"""a""" +r'a' +r"a" +r'''a''' +r"""a""" +R'a' +R"a" +R'''a''' +R"""a""" +"####, + expect![[r#" + Token { kind: Newline, span: Span { base_or_index: 0, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: false, is_raw: false }, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 43 })) }), span: Span { base_or_index: 1, len_or_tag: 3 } } + Token { kind: Newline, span: Span { base_or_index: 4, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: false, is_raw: false }, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 44 })) }), span: Span { base_or_index: 5, len_or_tag: 3 } } + Token { kind: Newline, span: Span { base_or_index: 8, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: true, is_raw: false }, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 45 })) }), span: Span { base_or_index: 9, len_or_tag: 7 } } + Token { kind: Newline, span: Span { base_or_index: 16, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: true, is_raw: false }, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 46 })) }), span: Span { base_or_index: 17, len_or_tag: 7 } } + Token { kind: Newline, span: Span { base_or_index: 24, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: false, is_raw: true }, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 47 })) }), span: Span { base_or_index: 25, len_or_tag: 4 } } + Token { kind: Newline, span: Span { base_or_index: 29, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: false, is_raw: true }, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 48 })) }), span: Span { base_or_index: 30, len_or_tag: 4 } } + Token { kind: Newline, span: Span { base_or_index: 34, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: true, is_raw: true }, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 49 })) }), span: Span { base_or_index: 35, len_or_tag: 8 } } + Token { kind: Newline, span: Span { base_or_index: 43, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: true, is_raw: true }, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 50 })) }), span: Span { base_or_index: 44, len_or_tag: 8 } } + Token { kind: Newline, span: Span { base_or_index: 52, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: false, is_raw: true }, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 51 })) }), span: Span { base_or_index: 53, len_or_tag: 4 } } + Token { kind: Newline, span: Span { base_or_index: 57, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: false, is_raw: true }, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 52 })) }), span: Span { base_or_index: 58, len_or_tag: 4 } } + Token { kind: Newline, span: Span { base_or_index: 62, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: true, is_raw: true }, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 53 })) }), span: Span { base_or_index: 63, len_or_tag: 8 } } + Token { kind: Newline, span: Span { base_or_index: 71, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: true, is_raw: true }, symbol: Symbol(SymbolIndex { idx: 42 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 54 })) }), span: Span { base_or_index: 72, len_or_tag: 8 } } + Token { kind: Newline, span: Span { base_or_index: 80, len_or_tag: 1 } } + Token { kind: Eof, span: Span { base_or_index: 81, len_or_tag: 0 } } + "#]], + ) +} + +#[test] +fn indents() { + check_lexing( + r####" +if test0: + if test1: + println("true true") + else: + println("true false") +println("end") +"####, + expect![[r#" + Token { kind: Newline, span: Span { base_or_index: 0, len_or_tag: 1 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 10 })), span: Span { base_or_index: 1, len_or_tag: 2 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 42 })), span: Span { base_or_index: 4, len_or_tag: 5 } } + Token { kind: Colon, span: Span { base_or_index: 9, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 10, len_or_tag: 1 } } + Token { kind: Indent, span: Span { base_or_index: 15, len_or_tag: 0 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 10 })), span: Span { base_or_index: 15, len_or_tag: 2 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 43 })), span: Span { base_or_index: 18, len_or_tag: 5 } } + Token { kind: Colon, span: Span { base_or_index: 23, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 24, len_or_tag: 1 } } + Token { kind: Indent, span: Span { base_or_index: 33, len_or_tag: 0 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 44 })), span: Span { base_or_index: 33, len_or_tag: 7 } } + Token { kind: OpenDelim(Paren), span: Span { base_or_index: 40, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: false, is_raw: false }, symbol: Symbol(SymbolIndex { idx: 45 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 46 })) }), span: Span { base_or_index: 41, len_or_tag: 11 } } + Token { kind: CloseDelim(Paren), span: Span { base_or_index: 52, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 53, len_or_tag: 1 } } + Token { kind: Dedent, span: Span { base_or_index: 58, len_or_tag: 0 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 12 })), span: Span { base_or_index: 58, len_or_tag: 4 } } + Token { kind: Colon, span: Span { base_or_index: 62, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 63, len_or_tag: 1 } } + Token { kind: Indent, span: Span { base_or_index: 72, len_or_tag: 0 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 44 })), span: Span { base_or_index: 72, len_or_tag: 7 } } + Token { kind: OpenDelim(Paren), span: Span { base_or_index: 79, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: false, is_raw: false }, symbol: Symbol(SymbolIndex { idx: 47 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 48 })) }), span: Span { base_or_index: 80, len_or_tag: 12 } } + Token { kind: CloseDelim(Paren), span: Span { base_or_index: 92, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 93, len_or_tag: 1 } } + Token { kind: Dedent, span: Span { base_or_index: 94, len_or_tag: 0 } } + Token { kind: Dedent, span: Span { base_or_index: 94, len_or_tag: 0 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 44 })), span: Span { base_or_index: 94, len_or_tag: 7 } } + Token { kind: OpenDelim(Paren), span: Span { base_or_index: 101, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Str { is_long_string: false, is_raw: false }, symbol: Symbol(SymbolIndex { idx: 49 }), suffix: None, raw: Some(Symbol(SymbolIndex { idx: 50 })) }), span: Span { base_or_index: 102, len_or_tag: 5 } } + Token { kind: CloseDelim(Paren), span: Span { base_or_index: 107, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 108, len_or_tag: 1 } } + Token { kind: Eof, span: Span { base_or_index: 109, len_or_tag: 0 } } + "#]], + ) +} + +#[test] +fn binary_expr_0() { + check_lexing( + r####"1 + a or b"####, + expect![[r#" + Token { kind: Literal(Lit { kind: Integer, symbol: Symbol(SymbolIndex { idx: 33 }), suffix: None, raw: None }), span: Span { base_or_index: 0, len_or_tag: 1 } } + Token { kind: BinOp(Plus), span: Span { base_or_index: 2, len_or_tag: 1 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 42 })), span: Span { base_or_index: 4, len_or_tag: 1 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 13 })), span: Span { base_or_index: 6, len_or_tag: 2 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 43 })), span: Span { base_or_index: 9, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 10, len_or_tag: 0 } } + Token { kind: Eof, span: Span { base_or_index: 10, len_or_tag: 0 } } + "#]], + ); +} + +#[test] +fn schema_expr_0() { + check_lexing( + r####" +Schema (1, 2) { + k=v +} +"####, + expect![[r#" + Token { kind: Newline, span: Span { base_or_index: 0, len_or_tag: 1 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 42 })), span: Span { base_or_index: 1, len_or_tag: 6 } } + Token { kind: OpenDelim(Paren), span: Span { base_or_index: 8, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Integer, symbol: Symbol(SymbolIndex { idx: 33 }), suffix: None, raw: None }), span: Span { base_or_index: 9, len_or_tag: 1 } } + Token { kind: Comma, span: Span { base_or_index: 10, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Integer, symbol: Symbol(SymbolIndex { idx: 34 }), suffix: None, raw: None }), span: Span { base_or_index: 12, len_or_tag: 1 } } + Token { kind: CloseDelim(Paren), span: Span { base_or_index: 13, len_or_tag: 1 } } + Token { kind: OpenDelim(Brace), span: Span { base_or_index: 15, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 16, len_or_tag: 1 } } + Token { kind: Indent, span: Span { base_or_index: 21, len_or_tag: 0 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 43 })), span: Span { base_or_index: 21, len_or_tag: 1 } } + Token { kind: Assign, span: Span { base_or_index: 22, len_or_tag: 1 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 44 })), span: Span { base_or_index: 23, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 24, len_or_tag: 1 } } + Token { kind: Dedent, span: Span { base_or_index: 25, len_or_tag: 0 } } + Token { kind: CloseDelim(Brace), span: Span { base_or_index: 25, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 26, len_or_tag: 1 } } + Token { kind: Eof, span: Span { base_or_index: 27, len_or_tag: 0 } } + "#]], + ); +} + +#[test] +fn schema_expr_1() { + check_lexing( + r####"Schema (1, 2) { + k=v +}"####, + expect![[r#" + Token { kind: Ident(Symbol(SymbolIndex { idx: 42 })), span: Span { base_or_index: 0, len_or_tag: 6 } } + Token { kind: OpenDelim(Paren), span: Span { base_or_index: 7, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Integer, symbol: Symbol(SymbolIndex { idx: 33 }), suffix: None, raw: None }), span: Span { base_or_index: 8, len_or_tag: 1 } } + Token { kind: Comma, span: Span { base_or_index: 9, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Integer, symbol: Symbol(SymbolIndex { idx: 34 }), suffix: None, raw: None }), span: Span { base_or_index: 11, len_or_tag: 1 } } + Token { kind: CloseDelim(Paren), span: Span { base_or_index: 12, len_or_tag: 1 } } + Token { kind: OpenDelim(Brace), span: Span { base_or_index: 14, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 15, len_or_tag: 1 } } + Token { kind: Indent, span: Span { base_or_index: 20, len_or_tag: 0 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 43 })), span: Span { base_or_index: 20, len_or_tag: 1 } } + Token { kind: Assign, span: Span { base_or_index: 21, len_or_tag: 1 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 44 })), span: Span { base_or_index: 22, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 23, len_or_tag: 1 } } + Token { kind: Dedent, span: Span { base_or_index: 24, len_or_tag: 0 } } + Token { kind: CloseDelim(Brace), span: Span { base_or_index: 24, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 25, len_or_tag: 0 } } + Token { kind: Eof, span: Span { base_or_index: 25, len_or_tag: 0 } } + "#]], + ); +} + +#[test] +fn test_peek() { + let src = "\na=1"; + let sm = SourceMap::new(FilePathMapping::empty()); + sm.new_source_file(PathBuf::from("").into(), src.to_string()); + let mut sess = ParseSession::with_source_map(Arc::new(sm)); + + create_session_globals_then(|| { + let stream = parse_token_streams(&mut sess, src, BytePos::from_u32(0)); + let mut cursor = stream.cursor(); + + let tok0 = cursor.next(); + assert_eq!( + format!("{:?}", tok0), + "Some(Token { kind: Newline, span: Span { base_or_index: 0, len_or_tag: 1 } })" + ); + + let peek = cursor.peek(); + assert_eq!( + format!("{:?}", peek), + "Some(Token { kind: Ident(Symbol(SymbolIndex { idx: 42 })), span: Span { base_or_index: 1, len_or_tag: 1 } })" + ); + }); +} + +#[test] +fn test_assign_stmt() { + check_lexing( + r####" +a=1 +"####, + expect![[r#" + Token { kind: Newline, span: Span { base_or_index: 0, len_or_tag: 1 } } + Token { kind: Ident(Symbol(SymbolIndex { idx: 42 })), span: Span { base_or_index: 1, len_or_tag: 1 } } + Token { kind: Assign, span: Span { base_or_index: 2, len_or_tag: 1 } } + Token { kind: Literal(Lit { kind: Integer, symbol: Symbol(SymbolIndex { idx: 33 }), suffix: None, raw: None }), span: Span { base_or_index: 3, len_or_tag: 1 } } + Token { kind: Newline, span: Span { base_or_index: 4, len_or_tag: 1 } } + Token { kind: Eof, span: Span { base_or_index: 5, len_or_tag: 0 } } + "#]], + ) +} diff --git a/kclvm/parser/src/lib.rs b/kclvm/parser/src/lib.rs new file mode 100644 index 000000000..810b684f6 --- /dev/null +++ b/kclvm/parser/src/lib.rs @@ -0,0 +1,443 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +mod lexer; +mod parser; +mod session; + +#[cfg(test)] +mod tests; + +extern crate kclvm_error; + +use crate::session::ParseSession; +use kclvm_ast::ast; +use kclvm_span::{self, FilePathMapping, SourceMap}; +use lexer::parse_token_streams; +use parser::Parser; +use rustc_span::BytePos; +use rustc_span::Pos; + +use std::error::Error; +use std::fs::File; +use std::io::BufReader; +use std::path::PathBuf; +use std::sync::Arc; + +use kclvm_span::create_session_globals_then; + +/// parser mode +#[derive(Debug, Clone)] +pub enum ParseMode { + Null, + ParseComments, +} + +/// Get the AST program from json file. +pub fn parse_program_from_json_file(path: &str) -> Result> { + let file = File::open(path)?; + let reader = BufReader::new(file); + let u = serde_json::from_reader(reader)?; + Ok(u) +} + +pub fn parse_program(filename: &str) -> ast::Program { + let abspath = std::fs::canonicalize(&std::path::PathBuf::from(filename)).unwrap(); + + let mut prog = ast::Program { + root: abspath.parent().unwrap().to_str().unwrap().to_string(), + main: "__main__".to_string(), + pkgs: std::collections::HashMap::new(), + cmd_args: Vec::new(), + cmd_overrides: Vec::new(), + }; + + let mainpkg = "__main__"; + + let mut module = parse_file(abspath.to_str().unwrap(), None); + module.filename = filename.to_string(); + module.pkg = mainpkg.to_string(); + module.name = mainpkg.to_string(); + + prog.pkgs.insert(mainpkg.to_string(), vec![module]); + + prog +} + +pub fn parse_file(filename: &str, code: Option) -> ast::Module { + create_session_globals_then(move || { + let src = if let Some(s) = code { + s + } else { + std::fs::read_to_string(filename).unwrap() + }; + + let sm = kclvm_span::SourceMap::new(FilePathMapping::empty()); + let sf = sm.new_source_file(PathBuf::from(filename).into(), src.to_string()); + let sess = &ParseSession::with_source_map(std::sync::Arc::new(sm)); + + let stream = lexer::parse_token_streams(sess, src.as_str(), sf.start_pos); + let mut p = parser::Parser::new(sess, stream); + let mut m = p.parse_module(); + + m.filename = filename.to_string(); + m.pkg = kclvm_ast::MAIN_PKG.to_string(); + m.name = kclvm_ast::MAIN_PKG.to_string(); + + m + }) +} + +pub fn parse_expr(src: &str) -> ast::NodeRef { + let sm = SourceMap::new(FilePathMapping::empty()); + sm.new_source_file(PathBuf::from("").into(), src.to_string()); + let sess = &ParseSession::with_source_map(Arc::new(sm)); + + create_session_globals_then(|| { + let stream = parse_token_streams(sess, src, BytePos::from_u32(0)); + let mut parser = Parser::new(sess, stream); + parser.parse_expr() + }) +} + +#[derive(Debug, Default, Clone)] +pub struct LoadProgramOptions { + pub work_dir: String, + pub k_code_list: Vec, + + pub cmd_args: Vec, + pub cmd_overrides: Vec, + + pub _mode: Option, + pub _load_packages: bool, +} + +pub fn load_program(paths: &[&str], opts: Option) -> ast::Program { + // todo: support cache + if let Some(opts) = opts { + Loader::new(paths, Some(opts)).load_main() + } else { + Loader::new(paths, None).load_main() + } +} + +struct Loader { + paths: Vec, + opts: LoadProgramOptions, + + pkgroot: String, + + modfile: kclvm_config::modfile::KCLModFile, + pkgs: std::collections::HashMap>, + missing_pkgs: Vec, + // todo: add shared source_map all parse_file. +} + +impl Loader { + fn new(paths: &[&str], opts: Option) -> Self { + Self { + paths: paths.iter().map(|s| s.to_string()).collect(), + opts: opts.unwrap_or_default(), + + pkgroot: "".to_string(), + + modfile: Default::default(), + pkgs: Default::default(), + missing_pkgs: Default::default(), + } + } + + fn load_main(&mut self) -> ast::Program { + debug_assert!(!self.paths.is_empty()); + + if let Ok(s) = kclvm_config::modfile::get_pkg_root_from_paths(&self.paths) { + self.pkgroot = s; + } + + if !self.pkgroot.is_empty() { + debug_assert!(self.is_dir(self.pkgroot.as_str())); + debug_assert!(self.path_exist(self.pkgroot.as_str())); + + self.modfile = kclvm_config::modfile::load_mod_file(self.pkgroot.as_str()); + } + + // fix path + let mut path_list = Vec::new(); + for s in &self.paths { + let mut s = s.clone(); + if s.contains(kclvm_config::modfile::KCL_MOD_PATH_ENV) { + debug_assert!(!self.pkgroot.is_empty()); + s = s.replace( + kclvm_config::modfile::KCL_MOD_PATH_ENV, + self.pkgroot.as_str(), + ); + } + if !self.pkgroot.is_empty() && !self.is_absolute(s.as_str()) { + let p = std::path::Path::new(s.as_str()); + if let Ok(x) = std::fs::canonicalize(p) { + s = x.to_str().unwrap_or(s.as_str()).to_string(); + } + } + + path_list.push(s); + } + + // get k files + let mut k_files: Vec = Vec::new(); + for (i, path) in path_list.iter().enumerate() { + if path.ends_with(".k") { + k_files.push(path.to_string()); + continue; + } + + // read dir/*.k + if self.is_dir(path) { + if self.opts.k_code_list.len() > i { + panic!("invalid code list") + } + //k_code_list + for s in self.get_dir_kfile_list(path) { + k_files.push(s); + } + continue; + } + } + + if k_files.is_empty() { + panic!("No input KCL files"); + } + + // check all file exists + for (i, filename) in (&k_files).iter().enumerate() { + if i < self.opts.k_code_list.len() { + continue; + } + + if !self.pkgroot.is_empty() { + debug_assert!(self.is_file(filename.as_str())); + debug_assert!(self.is_absolute(filename.as_str()), "filename={}", filename); + } + + if !self.path_exist(filename.as_str()) { + panic!( + "Cannot find the kcl file, please check whether the file path {}", + filename.as_str(), + ); + } + } + + // load module + let mut pkg_files = Vec::new(); + for (i, filename) in (&k_files).iter().enumerate() { + // todo: add shared source map for all files + if i < self.opts.k_code_list.len() { + let mut m = parse_file(filename, Some(self.opts.k_code_list[i].clone())); + self.fix_rel_import_path(&mut m); + pkg_files.push(m) + } else { + let mut m = parse_file(filename, None); + self.fix_rel_import_path(&mut m); + pkg_files.push(m); + } + } + + let __kcl_main__ = kclvm_ast::MAIN_PKG; + let import_list = self.get_import_list(&pkg_files); + + self.pkgs.insert(__kcl_main__.to_string(), pkg_files); + + // load imported packages + for import_spec in import_list { + self.load_package(import_spec.path.to_string()); + } + + // Ok + ast::Program { + root: self.pkgroot.clone(), + main: __kcl_main__.to_string(), + pkgs: self.pkgs.clone(), + cmd_args: Vec::new(), + cmd_overrides: Vec::new(), + } + } + + fn fix_rel_import_path(&mut self, m: &mut ast::Module) { + for stmt in &mut m.body { + if let ast::Stmt::Import(ref mut import_spec) = &mut stmt.node { + import_spec.path = kclvm_config::vfs::fix_import_path( + &self.pkgroot, + &m.filename, + import_spec.path.as_str(), + ); + } + } + } + + fn load_package(&mut self, pkgpath: String) { + if pkgpath.is_empty() { + return; + } + + if self.pkgs.contains_key(&pkgpath) { + return; + } + if self.missing_pkgs.contains(&pkgpath) { + return; + } + + // plugin pkgs + if self.is_plugin_pkg(pkgpath.as_str()) { + return; + } + + // builtin pkgs + if self.is_builtin_pkg(pkgpath.as_str()) { + return; + } + + let k_files = self.get_pkg_kfile_list(pkgpath.as_str()); + + if k_files.is_empty() { + self.missing_pkgs.push(pkgpath); + return; + } + + let mut pkg_files = Vec::new(); + for filename in k_files { + debug_assert!(self.is_file(filename.as_str())); + debug_assert!(self.path_exist(filename.as_str())); + + let mut m = parse_file(filename.as_str(), None); + + m.pkg = pkgpath.clone(); + m.name = "".to_string(); + self.fix_rel_import_path(&mut m); + + pkg_files.push(m); + } + + let import_list = self.get_import_list(&pkg_files); + self.pkgs.insert(pkgpath, pkg_files); + + for import_spec in import_list { + self.load_package(import_spec.path.to_string()); + } + } + + fn get_import_list(&self, pkg: &[ast::Module]) -> Vec { + let mut import_list = Vec::new(); + for m in pkg { + for stmt in &m.body { + if let ast::Stmt::Import(import_spec) = &stmt.node { + let mut import_spec = import_spec.clone(); + import_spec.path = kclvm_config::vfs::fix_import_path( + &self.pkgroot, + &m.filename, + import_spec.path.as_str(), + ); + import_list.push(import_spec); + } + } + } + import_list + } + + fn get_pkg_kfile_list(&self, pkgpath: &str) -> Vec { + debug_assert!(!pkgpath.is_empty()); + + // plugin pkgs + if self.is_plugin_pkg(pkgpath) { + return Vec::new(); + } + + // builtin pkgs + if self.is_builtin_pkg(pkgpath) { + return Vec::new(); + } + + if self.pkgroot.is_empty() { + panic!("pkgroot not found") + } + + let mut pathbuf = std::path::PathBuf::new(); + pathbuf.push(&self.pkgroot); + for s in pkgpath.split('.') { + pathbuf.push(s); + } + + let pkgpath: String = pathbuf.as_path().to_str().unwrap().to_string(); + let abspath: String = std::path::Path::new(&self.pkgroot) + .join(pkgpath) + .to_str() + .unwrap() + .to_string(); + + if std::path::Path::new(abspath.as_str()).exists() { + return self.get_dir_kfile_list(abspath.as_str()); + } + + let as_k_path = abspath + ".k"; + if std::path::Path::new((&as_k_path).as_str()).exists() { + return vec![as_k_path]; + } + + Vec::new() + } + fn get_dir_kfile_list(&self, dir: &str) -> Vec { + if !std::path::Path::new(dir).exists() { + return Vec::new(); + } + + let mut list = Vec::new(); + + for path in std::fs::read_dir(dir).unwrap() { + let path = path.unwrap(); + if !path.file_name().to_str().unwrap().ends_with(".k") { + continue; + } + if path.file_name().to_str().unwrap().ends_with("_test.k") { + continue; + } + if path.file_name().to_str().unwrap().starts_with('_') { + continue; + } + + let s = format!("{}", path.path().display()); + list.push(s); + } + + list.sort(); + list + } + + fn is_builtin_pkg(&self, pkgpath: &str) -> bool { + let system_modules = kclvm_sema::builtin::system_module::STANDARD_SYSTEM_MODULES; + system_modules.contains(&pkgpath) + } + + fn is_plugin_pkg(&self, pkgpath: &str) -> bool { + pkgpath.starts_with("kcl_plugin.") + } +} + +// utils +impl Loader { + fn current_work_dir() -> String { + let p = std::env::current_dir().unwrap(); + let s = p.to_str().unwrap().to_string(); + s + } + fn is_file(&self, path: &str) -> bool { + std::path::Path::new(path).is_file() + } + fn is_dir(&self, path: &str) -> bool { + std::path::Path::new(path).is_dir() + } + + fn is_absolute(&self, path: &str) -> bool { + std::path::Path::new(path).is_absolute() + } + + fn path_exist(&self, path: &str) -> bool { + std::path::Path::new(path).exists() + } +} diff --git a/kclvm/parser/src/parser/expr.rs b/kclvm/parser/src/parser/expr.rs new file mode 100644 index 000000000..01ee984eb --- /dev/null +++ b/kclvm/parser/src/parser/expr.rs @@ -0,0 +1,2096 @@ +#![allow(dead_code)] + +extern crate enquote; + +use std::vec; + +use super::int::bytes_to_int; +use super::Parser; + +use either::{self, Either}; + +use crate::node_ref; +use crate::parser::precedence::Precedence; +use kclvm_ast::ast::*; +use kclvm_ast::token; +use kclvm_ast::token::{BinOpToken, DelimToken, TokenKind}; +use kclvm_span::symbol::kw; + +/// Parser implementation of expressions, which consists of sub-expressions, +/// operand and tokens. Like the general LL1 paser, parser constantly looking for +/// left-side derivation, priority is specified by matching code explicitly. +/// The entrances of expression parsing are `parse_exprlist` and `parse_expr`. +/// TODO: operand design is quite complex, can be simplified later. +impl<'a> Parser<'a> { + /// ~~~ Entrances + + /// Syntax: + /// expr_list: expr (COMMA expr)* + pub(crate) fn parse_exprlist(&mut self) -> Vec> { + let mut exprs = Vec::new(); + let expr = self.parse_expr(); + exprs.push(expr); + + loop { + let token = self.token; + match token.kind { + TokenKind::Comma => { + self.bump(); + let expr = self.parse_expr(); + exprs.push(expr); + } + _ => break, + } + } + + exprs + } + + /// Syntax: + /// test: if_expr | simple_expr + pub(crate) fn parse_expr(&mut self) -> NodeRef { + let operand = self.parse_simple_expr(); + + // try if expr + if self.token.is_keyword(kw::If) { + return self.parse_if_expr(operand); + } + + operand + } + + /// Syntax: + /// simple_expr: unary_expr | binary_expr | primary_expr + /// unary_expr: un_op simple_expr + /// binary_expr: simple_expr bin_op simple_expr + pub(crate) fn parse_simple_expr(&mut self) -> NodeRef { + self.do_parse_simple_expr(Precedence::Lowest) + } + + /// Syntax: + /// identifier: NAME (DOT NAME)* + pub(crate) fn parse_identifier_expr(&mut self) -> NodeRef { + let token = self.token; + Box::new(Node::node( + Expr::Identifier(self.parse_identifier().node), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + fn do_parse_simple_expr(&mut self, prec1: Precedence) -> NodeRef { + let token = self.token; + + let mut x = self.parse_unary_expr(); + + let mut cmp_expr = Compare { + left: x.clone(), + ops: Vec::new(), + comparators: Vec::new(), + }; + loop { + // try bin expr + // If current op in a op-right pair has a higher priority to prev one, + // try to merging following tokens to binary exprs. + // Otherwise, just return operand to merge to prev binary expr with no + // operation affinity processing. + + let mut use_peek_op = false; + let mut oprec = Precedence::from(self.token); + + if let Some(peek) = self.cursor.peek() { + if self.token.is_keyword(kw::Not) && peek.is_keyword(kw::In) { + oprec = Precedence::InOrNotIn; + use_peek_op = true; + } + if self.token.is_keyword(kw::Is) && peek.is_keyword(kw::Not) { + oprec = Precedence::IsOrIsNot; + use_peek_op = true; + } + } + + if oprec <= prec1 { + if !cmp_expr.ops.is_empty() { + return Box::new(Node::node( + Expr::Compare(cmp_expr), + self.sess.struct_token_loc(token, self.prev_token), + )); + } + return x; + } + + let op = if use_peek_op { + let peek = self.cursor.peek().unwrap(); + if self.token.is_keyword(kw::Not) && peek.is_keyword(kw::In) { + BinOrCmpOp::Cmp(CmpOp::NotIn) + } else if self.token.is_keyword(kw::Is) && peek.is_keyword(kw::Not) { + BinOrCmpOp::Cmp(CmpOp::IsNot) + } else { + panic!("unreachable") + } + } else { + BinOrCmpOp::try_from(self.token) + .expect("invalid binary expr: missing binary operation") + }; + + self.bump(); + if use_peek_op { + self.bump(); // bump peek + } + + let y = self.do_parse_simple_expr(oprec); + + // compare: a == b == c + if let BinOrCmpOp::Cmp(cmp_op) = op.clone() { + if cmp_expr.ops.is_empty() { + cmp_expr.left = x.clone(); + } + cmp_expr.ops.push(cmp_op); + cmp_expr.comparators.push(y); + continue; + } + + if !cmp_expr.ops.is_empty() { + x = Box::new(Node::node( + Expr::Compare(cmp_expr.clone()), + self.sess.struct_token_loc(token, self.prev_token), + )); + cmp_expr.ops = Vec::new(); + cmp_expr.comparators = Vec::new(); + } + + x = Box::new(Node::node( + Expr::Binary(BinaryExpr { + left: x, + op, + right: y, + }), + self.sess.struct_token_loc(token, self.prev_token), + )); + } + } + + /// ~~~ Sub Expressions + + /// Syntax: + /// if_expr: simple_expr IF simple_expr ELSE test + /// test: if_expr | simple_expr + fn parse_if_expr(&mut self, body: NodeRef) -> NodeRef { + let token = self.token; + if self.token.is_keyword(kw::If) { + self.bump(); + let cond = self.parse_simple_expr(); + if self.token.is_keyword(kw::Else) { + self.bump(); + let orelse = self.parse_expr(); + Box::new(Node::node( + Expr::If(IfExpr { body, cond, orelse }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } else { + self.sess + .struct_token_error(&[&kw::Else.into()], self.token) + } + } else { + self.sess.struct_token_error(&[&kw::If.into()], self.token) + } + } + + /// Syntax: + /// primary_expr: operand | primary_expr select_suffix | primary_expr call_suffix | primary_expr slice_suffix + /// Note: we need to look ahead 2 tokens to match select_suffix and slice_suffix, which actually breaks LL1 rule. + fn parse_primary_expr(&mut self) -> NodeRef { + let mut operand = self.parse_operand_expr(); + + loop { + match self.token.kind { + TokenKind::Dot => { + // select_suffix + operand = self.parse_selector_expr(operand) + } + TokenKind::Question => { + match self.cursor.peek() { + Some(token) => { + match token.kind { + TokenKind::Dot => { + // select_suffix + operand = self.parse_selector_expr(operand) + } + TokenKind::OpenDelim(DelimToken::Bracket) => { + // slice_suffix + operand = self.parse_subscript_expr(operand) + } + _ => break operand, + } + } + None => break operand, + } + } + TokenKind::OpenDelim(dt) => { + match dt { + DelimToken::Paren => { + // call_suffix + operand = self.parse_call_expr(operand) + } + DelimToken::Bracket => { + // slice_suffix + operand = self.parse_subscript_expr(operand) + } + _ => {} + } + } + _ => break operand, + } + } + } + + /// Syntax: + /// unary_expr: un_op simple_expr + fn parse_unary_expr(&mut self) -> NodeRef { + let token = self.token; + let op = if let Ok(x) = UnaryOp::try_from(self.token) { + x + } else { + return self.parse_primary_expr(); + }; + + self.bump(); + let operand = self.parse_primary_expr(); + + Box::new(Node::node( + Expr::Unary(UnaryExpr { op, operand }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + /// Syntax: + /// select_suffix: [QUESTION] DOT NAME + fn parse_selector_expr(&mut self, value: NodeRef) -> NodeRef { + let token = self.token; + let has_question = match self.token.kind { + TokenKind::Question => { + self.bump(); + true + } + _ => false, + }; + // bump . + self.bump(); + let attr = self.parse_identifier(); + Box::new(Node::node( + Expr::Selector(SelectorExpr { + value, + attr, + has_question, + ctx: ExprContext::Load, + }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + /// Syntax: + /// call_suffix: LEFT_PARENTHESES [arguments [COMMA]] RIGHT_PARENTHESES + fn parse_call_expr(&mut self, func: NodeRef) -> NodeRef { + let token = self.token; + let call_expr = self.parse_call(func); + Box::new(Node::node( + Expr::Call(call_expr), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + fn parse_call(&mut self, func: NodeRef) -> CallExpr { + // LEFT_PARENTHESES + match self.token.kind { + TokenKind::OpenDelim(DelimToken::Paren) => self.bump(), + _ => self.sess.struct_token_error( + &[&TokenKind::OpenDelim(DelimToken::Paren).into()], + self.token, + ), + } + + // arguments or empty + let (args, keywords) = if self.token.kind == TokenKind::CloseDelim(DelimToken::Paren) { + (Vec::new(), Vec::new()) + } else { + self.parse_arguments_expr() + }; + + // [COMMA] + if self.token.kind == TokenKind::Comma { + self.bump() + } + + // RIGHT_PARENTHESES + match self.token.kind { + TokenKind::CloseDelim(DelimToken::Paren) => self.bump(), + _ => self.sess.struct_token_error( + &[&TokenKind::CloseDelim(DelimToken::Paren).into()], + self.token, + ), + } + + CallExpr { + func, + args, + keywords, + } + } + + /// Syntax: + /// slice_suffix: [QUESTION] LEFT_BRACKETS (expr | [expr] COLON [expr] [COLON [expr]]) RIGHT_BRACKETS + fn parse_subscript_expr(&mut self, value: NodeRef) -> NodeRef { + let token = self.token; + let mut has_question = false; + // [QUESTION] + if self.token.kind == TokenKind::Question { + self.bump(); + has_question = true; + } + + // LEFT_BRACKETS + match self.token.kind { + TokenKind::OpenDelim(DelimToken::Bracket) => self.bump(), + _ => self.sess.struct_token_error( + &[&TokenKind::OpenDelim(DelimToken::Bracket).into()], + self.token, + ), + } + + let mut round = 0; + let mut is_slice = false; + let mut colon_counter = 0; + let mut exprs = vec![None, None, None]; + let mut expr_index = 0; + let mut exprs_consecutive = 0; + + while round <= 4 { + match self.token.kind { + TokenKind::Colon => { + self.bump(); + is_slice = true; + colon_counter += 1; + expr_index += 1; + + if colon_counter > 2 { + self.sess + .struct_token_error(&[&"expression".to_string()], self.token) + } + exprs_consecutive -= 1 + } + TokenKind::CloseDelim(DelimToken::Bracket) => break, + _ => { + if !is_slice && round == 1 { + // it just has one round for an array + self.sess + .struct_compiler_bug("an list should have only one expr") + } + + exprs[expr_index] = Some(self.parse_expr()); + exprs_consecutive += 1; + + if exprs_consecutive > 1 { + self.sess.struct_compiler_bug("consecutive exprs found.") + } + } + } + round += 1; + } + + if exprs.len() != 3 { + self.sess + .struct_compiler_bug("an slice should have three exprs.") + } + + // RIGHT_BRACKETS + match self.token.kind { + TokenKind::CloseDelim(DelimToken::Bracket) => self.bump(), + _ => self.sess.struct_token_error( + &[&TokenKind::CloseDelim(DelimToken::Bracket).into()], + self.token, + ), + } + + if is_slice { + Box::new(Node::node( + Expr::Subscript(Subscript { + value, + index: None, + lower: exprs[0].clone(), + upper: exprs[1].clone(), + step: exprs[2].clone(), + ctx: ExprContext::Load, + has_question, + }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } else { + if !(exprs[1].is_none() && exprs[2].is_none()) { + self.sess + .struct_compiler_bug("an list should have only one expr.") + } + Box::new(Node::node( + Expr::Subscript(Subscript { + value, + index: exprs[0].clone(), + lower: None, + upper: None, + step: None, + ctx: ExprContext::Load, + has_question, + }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + } + + /// ~~~ Operand + + /// Syntax: + /// operand: identifier | number | string | constant | quant_expr | list_expr | list_comp | config_expr | dict_comp | identifier call_suffix | schema_expr | lambda_expr | paren_expr + fn parse_operand_expr(&mut self) -> NodeRef { + let token = self.token; + // try primary expr + match self.token.kind { + TokenKind::Ident(_) => { + // None + if self.token.is_keyword(kw::None) { + return self.parse_constant_expr(token::None); + } + // Undefined + if self.token.is_keyword(kw::Undefined) { + return self.parse_constant_expr(token::Undefined); + } + // Bool: True/False + if self.token.is_keyword(kw::True) || self.token.is_keyword(kw::False) { + return self.parse_constant_expr(token::Bool); + } + + // lambda expression + if self.token.is_keyword(kw::Lambda) { + self.parse_lambda_expr() + // quant expression + } else if self.token.is_keyword(kw::Any) + || self.token.is_keyword(kw::All) + || self.token.is_keyword(kw::Map) + || self.token.is_keyword(kw::Filter) + { + self.parse_quant_expr() + } else { + // identifier + let mut operand = self.parse_identifier_expr(); + + // identifier call_suffix | schema_expr + match self.token.kind { + TokenKind::OpenDelim(DelimToken::Brace) => { + // schema expression without args + operand = self.parse_schema_expr(*operand, token) + } + TokenKind::OpenDelim(DelimToken::Paren) => { + let call = self.parse_call(Box::new(*operand)); + + if let TokenKind::OpenDelim(DelimToken::Brace) = self.token.kind { + // schema expression with args + operand = self.parse_schema_expr_with_args(call, token) + } else { + // identifier call_suffix + return Box::new(Node::node( + Expr::Call(call), + self.sess.struct_token_loc(token, self.prev_token), + )); + } + } + _ => (), + } + + operand + } + } + TokenKind::Literal(lk) => { + // lit expr + match lk.kind { + token::LitKind::Bool => self.parse_constant_expr(lk.kind), + token::LitKind::Integer | token::LitKind::Float => self.parse_num_expr(lk), + token::LitKind::Str { .. } => self.parse_str_expr(lk), + // Note: None and Undefined are handled in ident, skip handle them here. + _ => self.sess.struct_token_error( + &[ + &token::LitKind::Bool.into(), + &token::LitKind::Integer.into(), + &token::LitKind::Str { + is_long_string: false, + is_raw: false, + } + .into(), + ], + self.token, + ), + } + } + TokenKind::OpenDelim(dt) => { + // list expr, dict expr, paren expr + match dt { + // paren expr + DelimToken::Paren => self.parse_paren_expr(), + // list expr or list comp + DelimToken::Bracket => self.parse_list_expr(), + // dict expr or dict comp + DelimToken::Brace => self.parse_config_expr(), + _ => self.sess.struct_token_error( + &[ + &TokenKind::OpenDelim(DelimToken::Paren).into(), + &TokenKind::OpenDelim(DelimToken::Bracket).into(), + &TokenKind::OpenDelim(DelimToken::Brace).into(), + ], + self.token, + ), + } + } + _ => self.sess.struct_token_error( + &[ + &TokenKind::ident_value(), + &TokenKind::literal_value(), + &TokenKind::OpenDelim(DelimToken::NoDelim).into(), + ], + self.token, + ), + } + } + + fn match_operand_expr(&self) -> bool { + matches!( + self.token.kind, + TokenKind::Literal(_) | TokenKind::Ident(_) | TokenKind::OpenDelim(_) + ) + } + + /// Syntax: + /// quant_expr: quant_op [ identifier COMMA ] identifier IN quant_target LEFT_BRACE (expr [IF expr] + /// | NEWLINE _INDENT simple_expr [IF expr] NEWLINE _DEDENT)? RIGHT_BRACE + /// quant_target: string | identifier | list_expr | list_comp | dict_expr | dict_comp + /// quant_op: ALL | ANY | FILTER | MAP + fn parse_quant_expr(&mut self) -> NodeRef { + let token = self.token; + // quant_op + let op = if self.token.is_keyword(kw::All) { + QuantOperation::All + } else if self.token.is_keyword(kw::Any) { + QuantOperation::Any + } else if self.token.is_keyword(kw::Filter) { + QuantOperation::Filter + } else if self.token.is_keyword(kw::Map) { + QuantOperation::Map + } else { + self.sess.struct_token_error( + &[ + &QuantOperation::All.into(), + &QuantOperation::Any.into(), + &QuantOperation::Filter.into(), + &QuantOperation::Map.into(), + ], + self.token, + ) + }; + self.bump(); + + // [ identifier COMMA ] identifier + let mut variables = vec![self.parse_identifier()]; + if self.token.kind == TokenKind::Comma { + self.bump(); + variables.push(self.parse_identifier()); + } + + // IN + if self.token.is_keyword(kw::In) { + self.bump(); + } else { + self.sess.struct_token_error(&[&kw::In.into()], self.token) + } + + // quant_target + let target = self.parse_quant_target_expr(); + + // LEFT_BRACE + match self.token.kind { + TokenKind::OpenDelim(DelimToken::Brace) => { + self.bump(); + } + _ => self.sess.struct_token_error( + &[&TokenKind::OpenDelim(DelimToken::Brace).into()], + self.token, + ), + } + + // NEWLINE _INDENT + let has_newline = if self.token.kind == TokenKind::Newline { + self.skip_newlines(); + + if self.token.kind == TokenKind::Indent { + self.bump(); + } else { + self.sess + .struct_token_error(&[&TokenKind::Indent.into()], self.token) + } + + true + } else { + false + }; + + // expr + let test = self.parse_simple_expr(); + + // [IF epxr] + let if_cond = if self.token.is_keyword(kw::If) { + self.bump(); + + Some(self.parse_expr()) + } else { + None + }; + + // NEWLINE _DEDENT + if has_newline { + if self.token.kind == TokenKind::Newline { + self.skip_newlines(); + } else { + self.sess + .struct_token_error(&[&TokenKind::Newline.into()], self.token) + } + + if self.token.kind == TokenKind::Dedent { + self.bump(); + } else { + self.sess + .struct_token_error(&[&TokenKind::Dedent.into()], self.token) + } + } + + // RIGHT_BRACE + match self.token.kind { + TokenKind::CloseDelim(DelimToken::Brace) => { + self.bump(); + } + _ => self.sess.struct_token_error( + &[&TokenKind::CloseDelim(DelimToken::Brace).into()], + self.token, + ), + } + + Box::new(Node::node( + Expr::Quant(QuantExpr { + target, + variables, + op, + test, + if_cond, + ctx: ExprContext::Load, + }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + /// Syntax: + /// quant_target: string | identifier | list_expr | list_comp | dict_expr | dict_comp + fn parse_quant_target_expr(&mut self) -> NodeRef { + // try primary expr + match self.token.kind { + TokenKind::Ident(_) => { + if self.token.is_keyword(kw::None) + | self.token.is_keyword(kw::Undefined) + | self.token.is_keyword(kw::Lambda) + | self.token.is_keyword(kw::Any) + || self.token.is_keyword(kw::All) + || self.token.is_keyword(kw::Map) + || self.token.is_keyword(kw::Filter) + { + self.sess.struct_token_error( + &[ + &kw::None.into(), + &kw::Undefined.into(), + &kw::Lambda.into(), + &kw::Any.into(), + &kw::All.into(), + &kw::Map.into(), + &kw::Filter.into(), + ], + self.token, + ) + } else { + // identifier + self.parse_identifier_expr() + } + } + TokenKind::Literal(lk) => { + // lit expr + match lk.kind { + token::LitKind::Str { .. } => self.parse_str_expr(lk), + // Note: None and Undefined are handled in ident, skip handle them here. + _ => self.sess.struct_token_error( + &[&token::LitKind::Str { + is_long_string: false, + is_raw: false, + } + .into()], + self.token, + ), + } + } + TokenKind::OpenDelim(dt) => { + // list expr, dict expr, paren expr + match dt { + // list expr or list comp + DelimToken::Bracket => self.parse_list_expr(), + // dict expr or dict comp + DelimToken::Brace => self.parse_config_expr(), + _ => self.sess.struct_token_error( + &[ + &TokenKind::OpenDelim(DelimToken::Bracket).into(), + &TokenKind::OpenDelim(DelimToken::Brace).into(), + ], + self.token, + ), + } + } + _ => self.sess.struct_token_error( + &[ + &TokenKind::ident_value(), + &TokenKind::literal_value(), + &TokenKind::OpenDelim(DelimToken::NoDelim).into(), + ], + self.token, + ), + } + } + + /// Syntax: + /// list_expr: LEFT_BRACKETS [list_items | NEWLINE _INDENT list_items _DEDENT] RIGHT_BRACKETS + /// list_comp: LEFT_BRACKETS (expr comp_clause+ | NEWLINE _INDENT expr comp_clause+ _DEDENT) RIGHT_BRACKETS + fn parse_list_expr(&mut self) -> NodeRef { + let token = self.token; + // LEFT_BRACKETS + self.bump(); + + // try RIGHT_BRACKETS: empty config + if let TokenKind::CloseDelim(DelimToken::Bracket) = self.token.kind { + self.bump(); + + return Box::new(Node::node( + Expr::List(ListExpr { + elts: vec![], + ctx: ExprContext::Load, + }), + self.sess.struct_token_loc(token, self.prev_token), + )); + } + + let has_newline = if self.token.kind == TokenKind::Newline { + self.skip_newlines(); + if self.token.kind == TokenKind::Indent { + self.bump(); + } else if self.token.kind == TokenKind::CloseDelim(DelimToken::Bracket) { + self.bump(); + return Box::new(Node::node( + Expr::List(ListExpr { + elts: vec![], + ctx: ExprContext::Load, + }), + self.sess.struct_token_loc(token, self.prev_token), + )); + } else { + self.sess + .struct_token_error(&[&TokenKind::Indent.into()], self.token) + } + true + } else { + false + }; + + let items = self.parse_list_items(); + let generators = self.parse_comp_clauses(); + + // _DEDENT + if has_newline { + self.skip_newlines(); + if self.token.kind == TokenKind::Dedent { + self.bump(); + } else { + self.sess + .struct_token_error(&[&TokenKind::Dedent.into()], self.token) + } + } + + // RIGHT_BRACKETS + match self.token.kind { + TokenKind::CloseDelim(DelimToken::Bracket) => { + self.bump(); + } + _ => self.sess.struct_token_error( + &[&TokenKind::CloseDelim(DelimToken::Bracket).into()], + self.token, + ), + } + + if !generators.is_empty() { + if items.len() > 1 { + self.sess.struct_compiler_bug("multiple items found.") + } + + Box::new(Node::node( + Expr::ListComp(ListComp { + elt: items[0].clone(), + generators, + }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } else { + Box::new(Node::node( + Expr::List(ListExpr { + elts: items, + ctx: ExprContext::Load, + }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + } + + /// Syntax: + /// list_items: expr ((COMMA [NEWLINE] | NEWLINE) expr)* [COMMA] [NEWLINE] + pub(crate) fn parse_list_items(&mut self) -> Vec> { + if let TokenKind::CloseDelim(DelimToken::Bracket) = self.token.kind { + return Vec::new(); + } + + let mut items = vec![self.parse_list_item()]; + if let TokenKind::Comma = self.token.kind { + self.bump(); + } + self.skip_newlines(); + + loop { + if matches!( + self.token.kind, + TokenKind::CloseDelim(DelimToken::Bracket) | TokenKind::Dedent + ) { + break; + } + if self.token.is_keyword(kw::For) { + break; + } + + if let TokenKind::Comma = self.token.kind { + self.bump(); + } + self.skip_newlines(); + + items.push(self.parse_list_item()); + if let TokenKind::Comma = self.token.kind { + self.bump(); + } + self.skip_newlines(); + } + + items + } + + /// Syntax: + /// list_item: test | star_expr | if_item + fn parse_list_item(&mut self) -> NodeRef { + let token = self.token; + let item; + + match self.token.kind { + TokenKind::BinOp(BinOpToken::Star) => { + self.bump(); + let expr = self.parse_expr(); + let pos = self.token_span_pos(token, self.prev_token); + item = node_ref!( + Expr::Starred(StarredExpr { + value: expr, + ctx: ExprContext::Load, + }), + pos + ); + } + _ => { + if self.token.is_keyword(kw::If) { + item = self.parse_if_item_expr(); + } else { + item = self.parse_expr(); + } + } + } + + item + } + + /// Syntax: + /// if_item: + /// IF test COLON if_item_exec_block + /// (ELIF test COLON if_item_exec_block)* + /// (ELSE COLON if_item_exec_block)? + fn parse_if_item_expr(&mut self) -> NodeRef { + let token = self.token; + let mut need_skip_newlines = false; + + let mut if_item = { + self.bump_keyword(kw::If); + + let if_cond = self.parse_expr(); + self.bump_token(TokenKind::Colon); + + if let TokenKind::Newline = self.token.kind { + need_skip_newlines = true; + } + + let exprs = self.parse_if_item_exec_block(need_skip_newlines); + + ListIfItemExpr { + if_cond, + exprs, + orelse: None, + } + }; + + if let TokenKind::Newline = self.token.kind { + self.skip_newlines(); + } + + // elif ... + let mut elif_list = Vec::new(); + loop { + if !self.token.is_keyword(kw::Elif) { + break; + } + + let token = self.token; + self.bump_keyword(kw::Elif); + + let elif_cond = self.parse_expr(); + self.bump_token(TokenKind::Colon); + + if let TokenKind::Newline = self.token.kind { + need_skip_newlines = true; + } + + let exprs = self.parse_if_item_exec_block(need_skip_newlines); + let x = ListIfItemExpr { + if_cond: elif_cond, + exprs, + orelse: None, + }; + + elif_list.push(Box::new(Node::node( + x, + self.sess.struct_token_loc(token, self.prev_token), + ))); + } + + if let TokenKind::Newline = self.token.kind { + self.skip_newlines(); + } + + // else + if self.token.is_keyword(kw::Else) { + let token = self.token; + + self.bump_keyword(kw::Else); + self.bump_token(TokenKind::Colon); + + if let TokenKind::Newline = self.token.kind { + need_skip_newlines = true; + } + + let else_body = self.parse_if_item_exec_block(need_skip_newlines); + + let t = Box::new(Node::node( + Expr::List(ListExpr { + elts: else_body, + ctx: ExprContext::Load, + }), + self.sess.struct_token_loc(token, self.prev_token), + )); + + if_item.orelse = Some(t); + } + + // fix elif-list and else + while let Some(mut x) = elif_list.pop() { + x.node.orelse = if_item.orelse; + + let t = Node { + node: Expr::ListIfItem(x.node), + filename: x.filename, + line: x.line, + column: x.column, + end_line: x.end_line, + end_column: x.end_column, + }; + + if_item.orelse = Some(Box::new(t)); + } + + Box::new(Node::node( + Expr::ListIfItem(if_item), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + /// Syntax: + /// if_item_exec_block + /// : list_item [NEWLINE] + /// | NEWLINE _INDENT + /// list_item ((COMMA [NEWLINE] | NEWLINE) list_item)* + /// [COMMA] [NEWLINE] + /// _DEDENT + fn parse_if_item_exec_block(&mut self, need_skip_newlines: bool) -> Vec> { + if !need_skip_newlines { + return vec![self.parse_list_item()]; + } + + self.skip_newlines(); + self.bump_token(TokenKind::Indent); + + let mut body = Vec::new(); + + loop { + if matches!(self.token.kind, TokenKind::Dedent) { + break; + } + + body.push(self.parse_list_item()); + + if matches!(self.token.kind, TokenKind::Comma) { + self.bump(); + } + self.skip_newlines(); + } + + self.bump_token(TokenKind::Dedent); + body + } + + /// Syntax: + /// config_expr: LEFT_BRACE [config_entries | NEWLINE _INDENT config_entries _DEDENT] RIGHT_BRACE + /// config_entries: config_entry ((COMMA [NEWLINE] | NEWLINE) config_entry)* [COMMA] [NEWLINE] + /// config_comp: LEFT_BRACE (config_entry comp_clause+ | NEWLINE _INDENT config_entry comp_clause+ _DEDENT) RIGHT_BRACE + fn parse_config_expr(&mut self) -> NodeRef { + let token = self.token; + // LEFT_BRACE + self.bump(); + + // try RIGHT_BRACE: empty config + if let TokenKind::CloseDelim(DelimToken::Brace) = self.token.kind { + self.bump(); + + return Box::new(Node::node( + Expr::Config(ConfigExpr { items: vec![] }), + self.sess.struct_token_loc(token, self.prev_token), + )); + } + + let has_newline = if self.token.kind == TokenKind::Newline { + self.skip_newlines(); + if self.token.kind == TokenKind::Indent { + self.bump(); + } else if self.token.kind == TokenKind::CloseDelim(DelimToken::Brace) { + self.bump(); + return Box::new(Node::node( + Expr::Config(ConfigExpr { items: vec![] }), + self.sess.struct_token_loc(token, self.prev_token), + )); + } else { + self.sess + .struct_token_error(&[&TokenKind::Indent.into()], self.token) + } + true + } else { + false + }; + + let items = self.parse_config_entries(); + let generators = self.parse_comp_clauses(); + + // _DEDENT + if has_newline { + self.skip_newlines(); + if self.token.kind == TokenKind::Dedent { + self.bump(); + } else { + self.sess + .struct_token_error(&[&TokenKind::Dedent.into()], self.token) + } + } + + // RIGHT_BRACE + match self.token.kind { + TokenKind::CloseDelim(DelimToken::Brace) => { + self.bump(); + } + _ => self.sess.struct_token_error( + &[&TokenKind::CloseDelim(DelimToken::Brace).into()], + self.token, + ), + } + + if !generators.is_empty() { + if items.len() > 1 { + self.sess.struct_compiler_bug("multiple entries found.") + } + + Box::new(Node::node( + Expr::DictComp(DictComp { + entry: items[0].node.clone(), + generators, + }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } else { + Box::new(Node::node( + Expr::Config(ConfigExpr { items }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + } + + /// Syntax: + /// config_entries: config_entry ((COMMA [NEWLINE] | NEWLINE) config_entry)* [COMMA] [NEWLINE] + fn parse_config_entries(&mut self) -> Vec> { + let mut entries = vec![self.parse_config_entry()]; + if let TokenKind::Comma = self.token.kind { + self.bump(); + } + self.skip_newlines(); + + loop { + if matches!( + self.token.kind, + TokenKind::CloseDelim(DelimToken::Brace) | TokenKind::Dedent + ) { + break; + } + if self.token.is_keyword(kw::For) { + break; + } + + if let TokenKind::Comma = self.token.kind { + self.bump(); + } + self.skip_newlines(); + + entries.push(self.parse_config_entry()); + + if let TokenKind::Comma = self.token.kind { + self.bump(); + } + self.skip_newlines(); + } + + entries + } + + /// Syntax: + /// config_entry: expr (COLON | ASSIGN | COMP_PLUS) expr | double_star_expr | if_entry + /// entry: expr (COLON | ASSIGN | COMP_PLUS) expr | double_star_expr + /// Note: use the same ast node here for simplicity, do semantic checking later + fn parse_config_entry(&mut self) -> NodeRef { + let token = self.token; + let key; + let value; + let operation; + + match self.token.kind { + TokenKind::BinOp(BinOpToken::StarStar) => { + self.bump(); + key = None; + value = self.parse_expr(); + operation = ConfigEntryOperation::Union; + } + _ => { + if self.token.is_keyword(kw::If) { + key = None; + value = self.parse_if_entry_expr(); + operation = ConfigEntryOperation::Union; + } else { + key = Some(self.parse_expr()); + match self.token.kind { + TokenKind::Colon => { + operation = ConfigEntryOperation::Union; + } + TokenKind::Assign => { + operation = ConfigEntryOperation::Override; + } + TokenKind::BinOpEq(BinOpToken::Plus) => { + operation = ConfigEntryOperation::Insert; + } + _ => self.sess.struct_token_error( + &[ + &TokenKind::Colon.into(), + &TokenKind::Assign.into(), + &TokenKind::BinOpEq(BinOpToken::Plus).into(), + ], + self.token, + ), + } + self.bump(); + value = self.parse_expr(); + } + } + } + + Box::new(Node::node( + ConfigEntry { + key, + value, + operation, + insert_index: -1, + }, + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + /// Syntax: + /// comp_clause+ + fn parse_comp_clauses(&mut self) -> Vec> { + let mut clauses = Vec::new(); + + loop { + // comp_clause+ + if self.token.is_keyword(kw::For) { + let clause = self.parse_comp_clause(); + clauses.push(clause); + } else { + break; + } + } + + clauses + } + + /// Syntax: + /// comp_clause: FOR loop_variables [COMMA] IN simple_expr [NEWLINE] (IF expr)* + /// loop_variables: identifier (COMMA identifier)* + fn parse_comp_clause(&mut self) -> NodeRef { + let token = self.token; + self.bump(); + + let mut targets = vec![self.parse_identifier()]; + + while let TokenKind::Comma = self.token.kind { + self.bump(); + targets.push(self.parse_identifier()); + } + + // [COMMA] + if self.token.kind == TokenKind::Comma { + self.bump(); + } + + if !self.token.is_keyword(kw::In) { + self.sess.struct_token_error(&[&kw::In.into()], self.token) + } + self.bump(); + + let iter = self.parse_simple_expr(); + + // [NEWLINE] + if self.token.kind == TokenKind::Newline { + self.skip_newlines(); + } + + let mut ifs = Vec::new(); + + // (IF expr)* + loop { + if self.token.is_keyword(kw::If) { + self.bump(); + + ifs.push(self.parse_expr()); + } else { + break; + } + } + + Box::new(Node::node( + CompClause { targets, iter, ifs }, + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + /// Syntax: + /// if_entry: + /// IF expr COLON if_entry_exec_block + /// (ELIF expr COLON if_entry_exec_block)* + /// (ELSE COLON if_entry_exec_block)? + fn parse_if_entry_expr(&mut self) -> NodeRef { + let mut need_skip_newlines = false; + + let mut if_entry = { + self.bump_keyword(kw::If); + + let if_cond = self.parse_expr(); + self.bump_token(TokenKind::Colon); + + if let TokenKind::Newline = self.token.kind { + need_skip_newlines = true; + } + + let mut body = self.parse_if_entry_exec_block(need_skip_newlines); + body.node.if_cond = if_cond; + body + }; + + if let TokenKind::Newline = self.token.kind { + self.skip_newlines(); + } + + // elif ... + let mut need_skip_newlines = false; + let mut elif_list = Vec::new(); + loop { + if !self.token.is_keyword(kw::Elif) { + break; + } + + let token = self.token; + self.bump_keyword(kw::Elif); + + let elif_cond = self.parse_expr(); + self.bump_token(TokenKind::Colon); + + if let TokenKind::Newline = self.token.kind { + need_skip_newlines = true; + } + + let elif_body = self.parse_if_entry_exec_block(need_skip_newlines); + let x = ConfigIfEntryExpr { + if_cond: elif_cond, + items: elif_body.node.items, + orelse: None, + }; + + elif_list.push(Box::new(Node::node( + x, + self.sess.struct_token_loc(token, self.prev_token), + ))); + } + + if let TokenKind::Newline = self.token.kind { + self.skip_newlines(); + } + + // else + let mut need_skip_newlines = false; + if self.token.is_keyword(kw::Else) { + let token = self.token; + + self.bump_keyword(kw::Else); + self.bump_token(TokenKind::Colon); + + if let TokenKind::Newline = self.token.kind { + need_skip_newlines = true; + } + + let else_body = self.parse_if_entry_exec_block(need_skip_newlines); + + let mut orelse = ConfigExpr { items: Vec::new() }; + for item in else_body.node.items { + orelse.items.push(item); + } + + let t = Box::new(Node::node( + Expr::Config(orelse), + self.sess.struct_token_loc(token, self.prev_token), + )); + + if_entry.node.orelse = Some(t); + } + + if let TokenKind::Comma = self.token.kind { + self.bump(); + } + if let TokenKind::Newline = self.token.kind { + self.skip_newlines(); + } + + // fix elif-list and else + while let Some(mut x) = elif_list.pop() { + x.node.orelse = if_entry.node.orelse; + + let t = Node { + node: Expr::ConfigIfEntry(x.node), + filename: x.filename, + line: x.line, + column: x.column, + end_line: x.end_line, + end_column: x.end_column, + }; + + if_entry.node.orelse = Some(Box::new(t)); + } + Box::new(Node::new( + Expr::ConfigIfEntry(if_entry.node), + if_entry.filename, + if_entry.line, + if_entry.column, + if_entry.end_line, + if_entry.end_column, + )) + } + + /// Syntax: + /// if_entry_exec_block: + /// if_entry_exec_block + /// : (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry) [NEWLINE] + /// | NEWLINE _INDENT + /// (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry) + /// ((COMMA [NEWLINE] | [NEWLINE]) + /// (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry))* [COMMA] [NEWLINE] + /// _DEDENT + fn parse_if_entry_exec_block( + &mut self, + need_skip_newlines: bool, + ) -> NodeRef { + if need_skip_newlines { + self.skip_newlines(); + self.bump_token(TokenKind::Indent); + } + + let token = self.token; + + let mut body = { + let node = Node { + node: Expr::NameConstantLit(NameConstantLit { + value: NameConstant::None, // ignore + }), + filename: "".to_string(), + line: 0, + column: 0, + end_line: 0, + end_column: 0, + }; + ConfigIfEntryExpr { + if_cond: Box::new(node), + items: vec![], + orelse: None, + } + }; + + fn parse_body_item( + this: &mut Parser, + body: &mut ConfigIfEntryExpr, + need_skip_newlines: bool, + ) -> bool { + if need_skip_newlines { + if let TokenKind::Dedent = this.token.kind { + return false; + } + } else if let TokenKind::Newline = this.token.kind { + return false; + } + + if this.token.is_keyword(kw::Elif) || this.token.is_keyword(kw::Else) { + return false; + } + + // if_entry + if this.token.is_keyword(kw::If) { + let expr0 = None; + let expr1 = this.parse_if_entry_expr(); + let op = ConfigEntryOperation::Override; + let pos = expr1.pos(); + body.items.push(node_ref!( + ConfigEntry { + key: expr0, + value: expr1, + operation: op, + insert_index: -1 + }, + pos + )); + + return true; + } + + if let TokenKind::Dedent = this.token.kind { + return false; + } + if let TokenKind::Newline = this.token.kind { + return false; + } + + // double_star_expr + if let TokenKind::BinOp(BinOpToken::StarStar) = this.token.kind { + this.bump(); + + let expr0 = None; + let expr1 = this.parse_primary_expr(); + let op = ConfigEntryOperation::Override; + + let pos = expr1.pos(); + + body.items.push(node_ref!( + ConfigEntry { + key: expr0, + value: expr1, + operation: op, + insert_index: -1 + }, + pos + )); + + return true; + } + + if let TokenKind::Dedent = this.token.kind { + return false; + } + if let TokenKind::Newline = this.token.kind { + return false; + } + + // expr (COLON | ASSIGN | COMP_PLUS) expr + { + let expr0 = Some(this.parse_expr()); + let op = match this.token.kind { + TokenKind::Colon => { + this.bump(); + ConfigEntryOperation::Union + } + TokenKind::Assign => { + this.bump(); + ConfigEntryOperation::Override + } + TokenKind::BinOpEq(BinOpToken::Plus) => { + this.bump(); + ConfigEntryOperation::Insert + } + _ => { + panic!("invalid op: {:?}", this.token); + } + }; + + let expr1 = this.parse_expr(); + + let pos = expr1.pos(); + + body.items.push(node_ref!( + ConfigEntry { + key: expr0, + value: expr1, + operation: op, + insert_index: -1 + }, + pos + )); + } + + true + } + + while parse_body_item(self, &mut body, need_skip_newlines) { + if let TokenKind::Comma = self.token.kind { + self.bump(); + } + if need_skip_newlines { + self.skip_newlines(); + } + } + if let TokenKind::Newline = self.token.kind { + self.bump(); + } + + if need_skip_newlines { + self.skip_newlines(); + self.bump_token(TokenKind::Dedent); + + Box::new(Node::node( + body, + self.sess.struct_token_loc(token, self.prev_token), + )) + } else { + Box::new(Node::node( + body, + self.sess.struct_token_loc(token, self.prev_token), + )) + } + } + + /// Syntax: + /// schema_expr: identifier config_expr + pub(crate) fn parse_schema_expr( + &mut self, + identifier: Node, + lo: token::Token, + ) -> NodeRef { + let result = identifier.try_into(); + + let name = match result { + Ok(v) => v, + Err(_) => self + .sess + .struct_token_error(&[&TokenKind::ident_value()], self.token), + }; + + // config_expr + let config = self.parse_config_expr(); + Box::new(Node::node( + Expr::Schema(SchemaExpr { + name: Box::new(name), + args: Vec::new(), + kwargs: Vec::new(), + config, + }), + self.sess.struct_token_loc(lo, self.prev_token), + )) + } + + /// Syntax: + /// schema_expr: identifier LEFT_PARENTHESES [arguments] RIGHT_PARENTHESES config_expr + fn parse_schema_expr_with_args(&mut self, call: CallExpr, lo: token::Token) -> NodeRef { + let result = call.func.as_ref().clone().try_into(); + + let name = match result { + Ok(v) => v, + Err(_) => self + .sess + .struct_token_error(&[&TokenKind::ident_value()], self.token), + }; + + // config_expr + let config = self.parse_config_expr(); + Box::new(Node::node( + Expr::Schema(SchemaExpr { + name: Box::new(name), + args: call.args, + kwargs: call.keywords, + config, + }), + self.sess.struct_token_loc(lo, self.prev_token), + )) + } + + /// Syntax: + /// lambda_expr: LAMBDA [arguments] [RIGHT_ARROW type] + /// LEFT_BRACE + /// [expr_stmt | NEWLINE _INDENT schema_init_stmt+ _DEDENT] + /// RIGHT_BRACE + fn parse_lambda_expr(&mut self) -> NodeRef { + let token = self.token; + self.bump_keyword(kw::Lambda); + + let mut args = None; + let mut return_type_str = None; + let mut return_ty = None; + + // schema_arguments + if !matches!(self.token.kind, TokenKind::RArrow | TokenKind::OpenDelim(_)) { + args = self.parse_parameters( + &[], + &[TokenKind::RArrow, TokenKind::OpenDelim(DelimToken::Brace)], + false, + ); + } + + // return type + if let TokenKind::RArrow = self.token.kind { + self.bump_token(TokenKind::RArrow); + let typ = self.parse_type_annotation(); + return_type_str = Some(typ.node.to_string()); + return_ty = Some(typ); + } + + let mut stmt_list = Vec::new(); + + self.bump_token(TokenKind::OpenDelim(DelimToken::Brace)); + + // NEWLINE _INDENT + let has_newline = if self.token.kind == TokenKind::Newline { + self.skip_newlines(); + + if self.token.kind == TokenKind::Indent { + self.bump(); + } else { + self.sess + .struct_token_error(&[&TokenKind::Indent.into()], self.token) + } + true + } else { + false + }; + + loop { + if matches!( + self.token.kind, + TokenKind::CloseDelim(DelimToken::Brace) | TokenKind::Dedent + ) { + break; + } + if let Some(stmt) = self.parse_stmt() { + stmt_list.push(stmt); + self.skip_newlines(); + } + } + + // _DEDENT + if has_newline { + if self.token.kind == TokenKind::Dedent { + self.bump(); + } else { + self.sess + .struct_token_error(&[&TokenKind::Dedent.into()], self.token) + } + } + + self.bump_token(TokenKind::CloseDelim(DelimToken::Brace)); + + Box::new(Node::node( + Expr::Lambda(LambdaExpr { + args, + return_type_str, + return_ty, + body: stmt_list, + }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + /// Return type of the lambda + fn parse_lambda_type(&mut self) -> String { + self.bump(); + + if self.token.is_keyword(kw::Type) { + self.bump(); + + // rules: append strings util a left brace. panic on a '\n'. + let mut s = String::new(); + + while let TokenKind::Literal(lt) = self.token.kind { + let token_str = lt.symbol.as_str(); + if token_str == "\n" { + self.sess + .struct_span_error("cross line type is not supported.", self.token.span) + } + + s.push_str(lt.symbol.as_str()) + } + + s.to_string() + } else { + self.sess + .struct_token_error(&[&kw::Type.into()], self.token) + } + } + + /// Syntax: + /// paren_expr: LEFT_PARENTHESES expr RIGHT_PARENTHESES + fn parse_paren_expr(&mut self) -> NodeRef { + let token = self.token; + self.bump(); + + let expr = self.parse_expr(); + match self.token.kind { + TokenKind::CloseDelim(DelimToken::Paren) => { + self.bump(); + } + _ => self.sess.struct_token_error( + &[&token::TokenKind::CloseDelim(token::DelimToken::Paren).into()], + self.token, + ), + } + + Box::new(Node::node( + Expr::Paren(ParenExpr { expr }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + /// Syntax: + /// arguments: argument (COMMA argument)* + fn parse_arguments_expr(&mut self) -> (Vec>, Vec>) { + let mut args: Vec> = Vec::new(); + let mut keywords: Vec> = Vec::new(); + let mut has_keyword = false; + + loop { + match self.parse_argument_expr() { + Either::Left(expr) => { + args.push(Box::new(expr)); + if has_keyword { + self.sess.struct_span_error( + "positional argument follows keyword argument.", + self.token.span, + ) + } + } + Either::Right(keyword) => { + keywords.push(Box::new(keyword)); + has_keyword = true; + } + } + + if self.token.kind == TokenKind::Comma { + self.bump(); + } else { + break; + } + } + + (args, keywords) + } + + /// Syntax: + /// argument: expr | Identifier ASSIGN expr + fn parse_argument_expr(&mut self) -> Either, Node> { + let token = self.token; + + // Identifier + let arg_lo = self.token; + let expr = self.parse_expr(); + let arg_hi = self.prev_token; + + match self.token.kind { + TokenKind::Assign => { + self.bump(); + + let arg = match &expr.node { + Expr::Identifier(x) => x.clone(), + _ => self + .sess + .struct_token_error(&[&TokenKind::ident_value()], self.token), + }; + + // expr + let value = self.parse_expr(); + + either::Right(Node::node( + Keyword { + arg: Box::new(Node::node(arg, self.sess.struct_token_loc(arg_lo, arg_hi))), + value: Some(value), + }, + self.sess.struct_token_loc(token, self.prev_token), + )) + } + _ => either::Left(*expr), + } + } + + /// ~~~ Schema + + /// Syntax: + /// decorator_expr: identifier [call_suffix] + fn parse_decorator_expr(&mut self) -> NodeRef { + let token = self.token; + let func = self.parse_identifier_expr(); + + // LEFT_PARENTHESES + match self.token.kind { + TokenKind::OpenDelim(DelimToken::Paren) => { + self.bump(); + + self.parse_call_expr(func) + } + _ => Box::new(Node::node( + Expr::Call(CallExpr { + func, + args: Vec::new(), + keywords: Vec::new(), + }), + self.sess.struct_token_loc(token, self.prev_token), + )), + } + } + + /// Syntax: + /// check_expr: simple_expr [IF simple_expr] [COMMA primary_expr] NEWLINE + pub(crate) fn parse_check_expr(&mut self) -> NodeRef { + let token = self.token; + + // expr + let test = self.parse_simple_expr(); + // [IF expr] + let if_cond = if self.token.is_keyword(kw::If) { + self.bump(); + Some(self.parse_simple_expr()) + } else { + None + }; + + // [COMMA primary_expr] + let msg = if self.token.kind == TokenKind::Comma { + self.bump(); + + Some(self.parse_primary_expr()) + } else { + None + }; + + Box::new(Node::node( + Expr::Check(CheckExpr { test, if_cond, msg }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + /// ~~~ Id + + fn parse_identifier(&mut self) -> NodeRef { + let token = self.token; + let mut names = Vec::new(); + let ident = self.token.ident(); + match ident { + Some(id) => { + names.push(id.as_str().to_string()); + self.bump(); + } + None => self + .sess + .struct_token_error(&[&TokenKind::ident_value()], self.token), + } + + loop { + let token = self.token; + match token.kind { + TokenKind::Dot => { + self.bump(); + let ident = self.token.ident(); + match ident { + Some(id) => { + names.push(id.as_str().to_string()); + self.bump(); + } + None => self + .sess + .struct_token_error(&[&TokenKind::ident_value()], self.token), + } + } + _ => break, + } + } + + Box::new(Node::node( + Identifier { + names, + pkgpath: "".to_string(), + ctx: ExprContext::Load, + }, + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + /// ~~~ Lit + + /// Syntax: + /// number: DEC_NUMBER [multiplier] | HEX_NUMBER | BIN_NUMBER | OCT_NUMBER | FLOAT_NUMBER + fn parse_num_expr(&mut self, lk: token::Lit) -> NodeRef { + let token = self.token; + + let (binary_suffix, value) = match lk.kind { + token::LitKind::Integer => { + let value = bytes_to_int(lk.symbol.as_str().as_bytes(), 0).unwrap(); + + match lk.suffix { + Some(suffix) => (suffix.as_str().try_into().ok(), NumberLitValue::Int(value)), + None => (None, NumberLitValue::Int(value)), + } + } + token::LitKind::Float => { + let value = lk.symbol.as_str().parse().unwrap(); + (None, NumberLitValue::Float(value)) + } + _ => self.sess.struct_token_error( + &[ + &token::LitKind::Integer.into(), + &token::LitKind::Float.into(), + ], + self.token, + ), + }; + + self.bump(); + + Box::new(Node::node( + Expr::NumberLit(NumberLit { + binary_suffix, + value, + }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } + + /// Syntax: + /// string: STRING | LONG_STRING + pub(crate) fn parse_str_expr(&mut self, lk: token::Lit) -> NodeRef { + let token = self.token; + + let (is_long_string, raw_value, value) = match lk.kind { + token::LitKind::Str { is_long_string, .. } => { + let value = lk.symbol.as_str().to_string(); + let raw_value = lk + .raw + .map_or("".to_string(), |raw| raw.as_str().to_string()); + (is_long_string, raw_value, value) + } + _ => self.sess.struct_token_error( + &[&token::LitKind::Str { + is_long_string: false, + is_raw: false, + } + .into()], + self.token, + ), + }; + + self.bump(); + + let loc = self.sess.struct_token_loc(token, self.prev_token); + let lit = StringLit { + is_long_string, + value, + raw_value, + }; + + if let Some(joined_str) = self.parse_joined_string(&lit, token.span.lo()) { + Box::new(Node::node( + Expr::JoinedString(joined_str), + self.sess.struct_token_loc(token, self.prev_token), + )) + } else { + Box::new(Node::node(Expr::StringLit(lit), loc)) + } + } + + /// Syntax: + /// constant : TRUE | FALSE | NONE | UNDEFINED + fn parse_constant_expr(&mut self, lk: token::LitKind) -> NodeRef { + let token = self.token; + + let value = match lk { + token::LitKind::Bool => { + if self.token.is_keyword(kw::True) { + NameConstant::True + } else if self.token.is_keyword(kw::False) { + NameConstant::False + } else { + self.sess + .struct_token_error(&[&token::LitKind::Bool.into()], self.token) + } + } + token::LitKind::None => NameConstant::None, + token::LitKind::Undefined => NameConstant::Undefined, + _ => self.sess.struct_token_error( + &[ + &token::LitKind::Bool.into(), + &token::LitKind::None.into(), + &token::LitKind::Undefined.into(), + ], + self.token, + ), + }; + + self.bump(); + + Box::new(Node::node( + Expr::NameConstantLit(NameConstantLit { value }), + self.sess.struct_token_loc(token, self.prev_token), + )) + } +} diff --git a/kclvm/parser/src/parser/int.rs b/kclvm/parser/src/parser/int.rs new file mode 100644 index 000000000..78e2201c2 --- /dev/null +++ b/kclvm/parser/src/parser/int.rs @@ -0,0 +1,125 @@ +use bstr::ByteSlice; +use num_bigint::{BigInt, BigUint, Sign}; + +/// Convert rust bytes to BigInt with the int base. +pub fn bytes_to_int(lit: &[u8], mut base: u32) -> Option { + // split sign + let mut lit = lit.trim(); + let sign = match lit.first()? { + b'+' => Some(Sign::Plus), + b'-' => Some(Sign::Minus), + _ => None, + }; + if sign.is_some() { + lit = &lit[1..]; + } + + // split radix + let first = *lit.first()?; + let (has_radix, radix_len) = if first == b'0' { + match base { + 0 => { + if let Some(parsed) = lit.get(1) { + let (base_deteced, radix_len) = detect_base(parsed); + base = base_deteced; + (true, radix_len) + } else { + if let [_first, ref others @ .., last] = lit { + let is_zero = + others.iter().all(|&c| c == b'0' || c == b'_') && *last == b'0'; + if !is_zero { + return None; + } + } + return Some(0); + } + } + 16 => lit + .get(1) + .map_or((false, 0), |&b| (matches!(b, b'x' | b'X'), 2)), + 2 => lit + .get(1) + .map_or((false, 0), |&b| (matches!(b, b'b' | b'B'), 2)), + 8 => lit + .get(1) + .map_or((false, 0), |&b| (matches!(b, b'o' | b'O'), 2)), + _ => (false, 0), + } + } else { + if base == 0 { + base = 10; + } + (false, 0) + }; + if has_radix { + lit = &lit[radix_len as usize..]; + if lit.first()? == &b'_' { + lit = &lit[1..]; + } + } + + // remove zeroes + let mut last = *lit.first()?; + if last == b'0' { + let mut count = 0; + for &cur in &lit[1..] { + if cur == b'_' { + if last == b'_' { + return None; + } + } else if cur != b'0' { + break; + }; + count += 1; + last = cur; + } + let prefix_last = lit[count]; + lit = &lit[count + 1..]; + if lit.is_empty() && prefix_last == b'_' { + return None; + } + } + + // validate + for c in lit.iter() { + let c = *c; + if !(c.is_ascii_alphanumeric() || c == b'_') { + return None; + } + + if c == b'_' && last == b'_' { + return None; + } + + last = c; + } + if last == b'_' { + return None; + } + + // parse + if lit.is_empty() { + Some(0) + } else { + let uint = BigUint::parse_bytes(lit, base)?; + let num = BigInt::from_biguint(sign.unwrap_or(Sign::Plus), uint); + let (sign, data) = num.to_u64_digits(); + if data.len() != 1 { + None + } else { + match sign { + Sign::Minus => Some(-(data[0] as i64)), + Sign::Plus | Sign::NoSign => Some(data[0] as i64), + } + } + } +} + +fn detect_base(c: &u8) -> (u32, u32) { + match c { + b'x' | b'X' => (16, 2), + b'b' | b'B' => (2, 2), + b'o' | b'O' => (8, 2), + _ => (8, 1), + } +} diff --git a/kclvm/parser/src/parser/mod.rs b/kclvm/parser/src/parser/mod.rs new file mode 100644 index 000000000..c4517418d --- /dev/null +++ b/kclvm/parser/src/parser/mod.rs @@ -0,0 +1,245 @@ +//! A KCL Parser. +//! +//! The parser is built on top of the [`kclvm_parser::lexer`], and ordering KCL tokens +//! [`kclvm_ast::token`] to KCL ast nodes [`kclvm_ast::ast`]. +//! +//! The parser follows a LL1 parsing method, which constantly looking for +//! left-side derivation until a terminal token found. Since we hand-written the parser, +//! there is more flexibility in dealing with deduction priorities. +//! +//! KCL syntax elements can be simply divided into statements, expressions and tokens, +//! in which statement consists of expressions and tokens. In expression, operand is the most +//! complex part to enable all kinds of ident, constant, list, dict, config exprs. + +#![macro_use] + +mod expr; +mod int; +mod module; +mod precedence; +mod schema; +mod stmt; +#[cfg(test)] +mod tests; +mod ty; + +use crate::session::ParseSession; + +use kclvm_ast::ast::{Comment, NodeRef}; +use kclvm_ast::token::{CommentKind, Token, TokenKind}; +use kclvm_ast::token_stream::{Cursor, TokenStream}; +use kclvm_span::symbol::Symbol; + +#[macro_export] +macro_rules! node_ref { + ($node: expr) => { + NodeRef::new(Node::dummy_node($node)) + }; + ($node: expr, $pos:expr) => { + NodeRef::new(Node::node_with_pos($node, $pos)) + }; +} + +#[macro_export] +macro_rules! expr_as { + ($expr: expr, $expr_enum: path) => { + if let $expr_enum(x) = ($expr.node as Expr) { + Some(x) + } else { + None + } + }; +} + +#[macro_export] +macro_rules! stmt_as { + ($stmt: expr, $stmt_enum: path) => { + if let $stmt_enum(x) = ($stmt.node as Stmt) { + Some(x) + } else { + None + } + }; +} + +pub struct Parser<'a> { + /// The current token. + pub token: Token, + /// The previous token. + pub prev_token: Token, + /// Stream cursor + cursor: Cursor, + /// all comments. + comments: Vec>, + /// parse-time session + pub sess: &'a ParseSession, +} + +impl<'a> Parser<'a> { + pub fn new(sess: &'a ParseSession, stream: TokenStream) -> Self { + let (non_comment_tokens, comments) = Parser::split_token_stream(&sess, stream); + + let mut parser = Parser { + token: Token::dummy(), + prev_token: Token::dummy(), + cursor: TokenStream::new(non_comment_tokens).cursor(), + comments, + sess, + }; + + // bump to the first token + parser.bump(); + + parser + } + + pub(crate) fn token_span_pos( + &mut self, + lo_tok: Token, + hi_tok: Token, + ) -> (String, u64, u64, u64, u64) { + use rustc_span::Pos; + let lo = self.sess.source_map.lookup_char_pos(lo_tok.span.lo()); + let hi = self.sess.source_map.lookup_char_pos(hi_tok.span.hi()); + + let filename: String = format!("{}", lo.file.name.prefer_remapped()); + ( + filename, + lo.line as u64, + lo.col.to_usize() as u64, + hi.line as u64, + hi.col.to_usize() as u64, + ) + } + + pub(crate) fn bump(&mut self) { + self.prev_token = self.token; + let next = self.cursor.next(); + + if let Some(token) = next { + self.token = token + } + } + + pub(crate) fn bump_keyword(&mut self, kw: Symbol) { + if !self.token.is_keyword(kw) { + if let TokenKind::Ident(ident) = self.token.kind { + self.sess.struct_span_error( + &format!( + "bump keyword failed: expect={}, got={:?} # ident={}", + kw.to_ident_string(), + self.token, + ident + ), + self.token.span, + ); + } else { + self.sess.struct_span_error( + &format!( + "bump keyword failed: expect={}, {:?}", + kw.to_ident_string(), + self.token + ), + self.token.span, + ); + } + } + self.bump(); + } + + pub(crate) fn bump_token(&mut self, kind: TokenKind) { + if self.token.kind != kind { + if let TokenKind::Ident(ident) = self.token.kind { + self.sess.struct_span_error( + &format!( + "bump token failed: expect={:?}, got={:?} # ident={}", + kind, self.token, ident + ), + self.token.span, + ); + } else { + self.sess.struct_span_error( + &format!("bump token failed: expect={:?}, {:?}", kind, self.token), + self.token.span, + ); + } + } + self.bump(); + } + + pub(crate) fn skip_newlines(&mut self) { + while let TokenKind::Newline = self.token.kind { + self.bump(); + } + } +} + +impl<'a> Parser<'a> { + fn split_token_stream( + sess: &'a ParseSession, + stream: TokenStream, + ) -> (Vec, Vec>) { + use rustc_span::BytePos; + + let mut comments = Vec::new(); + let mut non_comment_tokens = Vec::new(); + + for (i, tok) in stream.iter().enumerate() { + let prev_token = if i == 0 { + Token { + kind: TokenKind::Dummy, + span: kclvm_span::Span::new(BytePos(0), BytePos(0)), + } + } else { + stream[i - 1] + }; + + // eof: add newline + if tok.kind == TokenKind::Eof { + // append Newline + if prev_token.kind != TokenKind::Newline { + non_comment_tokens.push(Token { + kind: TokenKind::Newline, + span: tok.span, + }); + } + non_comment_tokens.push(*tok); + break; + } + + // split comments + if matches!(tok.kind, TokenKind::DocComment(_)) { + match tok.kind { + TokenKind::DocComment(comment_kind) => match comment_kind { + CommentKind::Line(x) => { + use rustc_span::Pos; + let lo = sess.source_map.lookup_char_pos(tok.span.lo()); + let hi = sess.source_map.lookup_char_pos(tok.span.hi()); + let filename: String = format!("{}", lo.file.name.prefer_remapped()); + + let node = kclvm_ast::ast::Node { + node: Comment { + text: x.as_str().to_string(), + }, + filename: filename, + line: lo.line as u64, + column: lo.col.to_usize() as u64, + end_line: hi.line as u64, + end_column: hi.col.to_usize() as u64, + }; + + comments.push(NodeRef::new(node)); + } + }, + _ => (), + } + continue; + } + + // normal tokens + non_comment_tokens.push(*tok); + } + + (non_comment_tokens, comments) + } +} diff --git a/kclvm/parser/src/parser/module.rs b/kclvm/parser/src/parser/module.rs new file mode 100644 index 000000000..1a4172b25 --- /dev/null +++ b/kclvm/parser/src/parser/module.rs @@ -0,0 +1,44 @@ +use kclvm_ast::ast::*; +use kclvm_ast::{token::LitKind, token::TokenKind}; + +use super::Parser; + +impl<'a> Parser<'_> { + /// Syntax: + /// start: (NEWLINE | statement)* + pub fn parse_module(&mut self) -> Module { + let doc = self.parse_doc(); + let body = self.parse_body(); + Module { + filename: "".to_string(), + pkg: "".to_string(), + name: "".to_string(), + doc, + comments: self.comments.clone(), + body, + } + } + + fn parse_doc(&mut self) -> String { + if let TokenKind::Literal(lit) = self.token.kind { + if let LitKind::Str { is_long_string, .. } = lit.kind { + if is_long_string { + let doc = format!("{:?}", self.token); + self.bump(); + return doc; + } + } + } + "".to_string() + } + + fn parse_body(&mut self) -> Vec> { + let mut stmts = Vec::new(); + + while let Some(stmt) = self.parse_stmt() { + stmts.push(stmt) + } + + stmts + } +} diff --git a/kclvm/parser/src/parser/precedence.rs b/kclvm/parser/src/parser/precedence.rs new file mode 100644 index 000000000..2f31815ec --- /dev/null +++ b/kclvm/parser/src/parser/precedence.rs @@ -0,0 +1,89 @@ +use kclvm_ast::token::{Token, TokenKind}; + +use kclvm_span::symbol::kw; + +#[repr(i32)] +#[derive(PartialEq, PartialOrd, Debug, Clone)] +pub enum Precedence { + /// lowest place holder + Lowest, + /// as + As, + /// logic or || + LogicOr, + /// logic and && + LogicAnd, + /// ==, != + Equals, + /// in, not it + InOrNotIn, + /// is, is not + IsOrIsNot, + /// >, <, >=, <= + LessGreater, + /// | + BitOr, + /// ^ + BitXor, + /// & + BitAnd, + /// >>, << + Shift, + /// +, - + Sum, + /// *, /, % // + Product, + /// ** + Power, + /// +X, -X, !X + Prefix, +} + +impl From for Precedence { + fn from(tok: Token) -> Self { + if tok.is_keyword(kw::As) { + return Precedence::As; + } + match tok.kind { + TokenKind::UnaryOp(_) => Precedence::Prefix, + TokenKind::BinOp(ot) => match ot { + kclvm_ast::token::BinOpToken::Plus | kclvm_ast::token::BinOpToken::Minus => { + Precedence::Sum + } + kclvm_ast::token::BinOpToken::Star + | kclvm_ast::token::BinOpToken::Slash + | kclvm_ast::token::BinOpToken::Percent + | kclvm_ast::token::BinOpToken::SlashSlash => Precedence::Product, + kclvm_ast::token::BinOpToken::StarStar => Precedence::Power, + kclvm_ast::token::BinOpToken::Caret => Precedence::BitXor, + kclvm_ast::token::BinOpToken::And => Precedence::BitAnd, + kclvm_ast::token::BinOpToken::Or => Precedence::BitOr, + kclvm_ast::token::BinOpToken::Shl | kclvm_ast::token::BinOpToken::Shr => { + Precedence::Shift + } + }, + TokenKind::BinCmp(ct) => match ct { + kclvm_ast::token::BinCmpToken::Eq | kclvm_ast::token::BinCmpToken::NotEq => { + Precedence::Equals + } + kclvm_ast::token::BinCmpToken::Lt + | kclvm_ast::token::BinCmpToken::LtEq + | kclvm_ast::token::BinCmpToken::Gt + | kclvm_ast::token::BinCmpToken::GtEq => Precedence::LessGreater, + }, + _ => { + if tok.is_keyword(kw::Or) { + Precedence::LogicOr + } else if tok.is_keyword(kw::And) { + Precedence::LogicAnd + } else if tok.is_keyword(kw::In) { + Precedence::InOrNotIn + } else if tok.is_keyword(kw::Is) { + Precedence::IsOrIsNot + } else { + Precedence::Lowest + } + } + } + } +} diff --git a/kclvm/parser/src/parser/schema.rs b/kclvm/parser/src/parser/schema.rs new file mode 100644 index 000000000..8aaff84af --- /dev/null +++ b/kclvm/parser/src/parser/schema.rs @@ -0,0 +1,3 @@ +use super::Parser; + +impl<'a> Parser<'_> {} diff --git a/kclvm/parser/src/parser/stmt.rs b/kclvm/parser/src/parser/stmt.rs new file mode 100644 index 000000000..b10847295 --- /dev/null +++ b/kclvm/parser/src/parser/stmt.rs @@ -0,0 +1,1479 @@ +#![allow(dead_code)] +#![allow(unused_macros)] + +use core::panic; + +use kclvm_ast::ast::*; +use kclvm_ast::token::{DelimToken, LitKind, Token, TokenKind}; +use kclvm_span::symbol::kw; + +use super::Parser; + +use crate::expr_as; +use crate::node_ref; + +/// Parser implementation of statements, which consists of expressions and tokens. +/// Parser uses `parse_exprlist` and `parse_expr` in [`kclvm_parser::parser::expr`] +/// to get a expression node, and then concretize it into the specified expression node, +/// and then assemble it into the corresponding statement node. +impl<'a> Parser<'_> { + /// Syntax: + /// statement: simple_stmt | compound_stmt + /// simple_stmt: (assign_stmt | expr_stmt | assert_stmt | import_stmt | type_alias_stmt) NEWLINE + /// compound_stmt: if_stmt | schema_stmt + pub(crate) fn parse_stmt(&mut self) -> Option> { + // skip new lines + if matches!(self.token.kind, TokenKind::Newline) { + self.skip_newlines(); + } + + // eof => None + if matches!(self.token.kind, TokenKind::Eof) { + return None; + } + + // compound_stmt + if let Some(stmt) = self.parse_compound_stmt() { + return Some(stmt); + } + + // simple_stmt + if let Some(stmt) = self.parse_simple_stmt() { + return Some(stmt); + } + + None + } + + /// Syntax: + /// simple_stmt: (assign_stmt | expr_stmt | assert_stmt | import_stmt | type_alias_stmt) NEWLINE + fn parse_compound_stmt(&mut self) -> Option> { + // skip new lines + if matches!(self.token.kind, TokenKind::Newline) { + self.skip_newlines(); + } + + // eof => None + if matches!(self.token.kind, TokenKind::Eof) { + return None; + } + + // if ... + if self.token.is_keyword(kw::If) { + return self.parse_if_stmt(); + } + + // @decorators + let decorators = if matches!(self.token.kind, TokenKind::At) { + Some(self.parse_decorators()) + } else { + None + }; + + // schema/mixin/protocol/rule ... + if self.token.is_keyword(kw::Schema) { + return self.parse_schema_stmt(decorators); + } + if self.token.is_keyword(kw::Mixin) { + return self.parse_schema_stmt(decorators); + } + if self.token.is_keyword(kw::Protocol) { + return self.parse_schema_stmt(decorators); + } + if self.token.is_keyword(kw::Rule) { + return self.parse_rule_stmt(decorators); + } + + None + } + + /// Syntax: + /// simple_stmt: (assign_stmt | unification_stmt | expr_stmt | assert_stmt | import_stmt | type_alias_stmt) NEWLINE + fn parse_simple_stmt(&mut self) -> Option> { + // skip new lines + if matches!(self.token.kind, TokenKind::Newline) { + self.skip_newlines(); + } + + // eof => None + if matches!(self.token.kind, TokenKind::Eof) { + return None; + } + + // import ... + if self.token.is_keyword(kw::Import) { + return self.parse_import_stmt(); + } + + // type ... + if self.token.is_keyword(kw::Type) { + return self.parse_type_alias_stmt(); + } + // assert ... + if self.token.is_keyword(kw::Assert) { + return self.parse_assert_stmt(); + } + + // unary expr + if let TokenKind::UnaryOp(_) = self.token.kind { + return self.parse_expr_stmt(); + } + + // expr or assign + self.parse_expr_or_assign_stmt() + } + + /// Syntax: + /// Indent/Dedent + pub(crate) fn parse_block_stmt_list( + &mut self, + open_tok: TokenKind, + close_tok: TokenKind, + ) -> Vec>> { + let mut stmt_list = Vec::new(); + + self.bump_token(open_tok); + loop { + if self.token.kind == close_tok { + self.bump_token(close_tok); + break; + } + + if let Some(stmt) = self.parse_stmt() { + stmt_list.push(stmt); + } + } + + self.skip_newlines(); + stmt_list + } + + /// Syntax: + /// test: if_expr | simple_expr + fn parse_expr_stmt(&mut self) -> Option> { + let token = self.token; + let expr = vec![self.parse_expr()]; + + let stmt = node_ref!( + Stmt::Expr(ExprStmt { exprs: expr }), + self.token_span_pos(token, self.prev_token) + ); + + self.skip_newlines(); + Some(stmt) + } + + /// Syntax: + /// + /// expr_stmt: testlist_expr + /// testlist_expr: test (COMMA test)* + /// + /// assign_stmt: identifier [COLON type] (ASSIGN identifier)* ASSIGN test + /// | identifier (COMP_PLUS | COMP_MINUS | COMP_MULTIPLY | COMP_DOUBLE_STAR | COMP_DIVIDE + /// | COMP_DOUBLE_DIVIDE | COMP_MOD | COMP_AND | COMP_OR | COMP_XOR | COMP_SHIFT_LEFT + /// | COMP_SHIFT_RIGHT | L_OR | L_AND) + /// test + fn parse_expr_or_assign_stmt(&mut self) -> Option> { + let token = self.token; + let mut targets = vec![self.parse_expr()]; + + let mut value_or_target = None; + let mut type_annotation = None; + let mut ty = None; + + if let TokenKind::Colon = self.token.kind { + self.bump_token(TokenKind::Colon); + let typ = self.parse_type_annotation(); + + type_annotation = Some(node_ref!(typ.node.to_string(), typ.pos())); + // Unification statement + if let TokenKind::OpenDelim(DelimToken::Brace) = self.token.kind { + // schema expression without args + if let Type::Named(ref identifier) = typ.node { + let identifier = node_ref!(Expr::Identifier(identifier.clone()), typ.pos()); + let schema_expr = self.parse_schema_expr(*identifier, token); + let mut ident = expr_as!(targets[0].clone(), Expr::Identifier).unwrap(); + ident.ctx = ExprContext::Store; + let unification_stmt = UnificationStmt { + target: Box::new(Node::node_with_pos(ident, targets[0].pos())), + value: Box::new(schema_expr.as_ref().clone().try_into().unwrap()), + }; + self.skip_newlines(); + return Some(node_ref!( + Stmt::Unification(unification_stmt), + self.token_span_pos(token, self.prev_token) + )); + } + } + ty = Some(typ); + } else if let TokenKind::BinOpEq(x) = self.token.kind { + let op = AugOp::from(x); + self.bump_token(self.token.kind); + + let value = self.parse_expr(); + let mut ident = expr_as!(targets[0].clone(), Expr::Identifier).unwrap(); + ident.ctx = ExprContext::Store; + + let t = node_ref!( + Stmt::AugAssign(AugAssignStmt { + target: Box::new(Node::node_with_pos(ident, targets[0].pos())), + value, + op, + }), + self.token_span_pos(token, self.prev_token) + ); + + self.skip_newlines(); + + return Some(t); + } + + while let TokenKind::Assign = self.token.kind { + self.bump_token(TokenKind::Assign); + + let expr = self.parse_expr(); + if let Some(target) = value_or_target { + targets.push(target); + } + + value_or_target = Some(expr); + } + + if let TokenKind::BinOpEq(x) = self.token.kind { + if targets.len() == 1 && type_annotation.is_some() { + let aug_op = AugOp::from(x); + self.bump_token(self.token.kind); + let value = self.parse_expr(); + if let Expr::Identifier(target) = &targets[0].node { + self.skip_newlines(); + return Some(node_ref!( + Stmt::SchemaAttr(SchemaAttr { + doc: "".to_string(), + name: node_ref!(target.names.join("."), targets[0].pos()), + type_str: type_annotation.unwrap(), + ty, + op: Some(BinOrAugOp::Aug(aug_op)), + value: Some(value), + is_optional: false, + decorators: Vec::new(), + }), + self.token_span_pos(token, self.prev_token) + )); + } + } + } + + self.skip_newlines(); + + if let Some(value) = value_or_target { + let mut pos = targets[0].pos(); + pos.3 = value.end_line; + pos.4 = value.end_column; + + let targets = targets + .iter() + .map(|expr| match &expr.node { + Expr::Identifier(x) => { + let mut x = x.clone(); + x.ctx = ExprContext::Store; + Box::new(Node::node_with_pos(x, expr.pos())) + } + _ => panic!("invalid target: {:?}", expr.node), + }) + .collect(); + + Some(node_ref!( + Stmt::Assign(AssignStmt { + targets, + value, + type_annotation, + ty, + }), + self.token_span_pos(token, self.prev_token) + )) + } else { + if targets.len() == 1 && type_annotation.is_some() { + if let Expr::Identifier(target) = &targets[0].node { + return Some(node_ref!( + Stmt::SchemaAttr(SchemaAttr { + doc: "".to_string(), + name: node_ref!(target.names.join("."), targets[0].pos()), + type_str: type_annotation.unwrap(), + ty, + op: None, + value: None, + is_optional: false, + decorators: Vec::new(), + }), + self.token_span_pos(token, self.prev_token) + )); + } + } + + let mut pos = targets[0].pos(); + pos.3 = targets.last().unwrap().end_line; + pos.4 = targets.last().unwrap().end_column; + + let t = Box::new(Node::node_with_pos( + Stmt::Expr(ExprStmt { + exprs: targets.clone(), + }), + pos, + )); + + Some(t) + } + } + + /// Syntax: + /// assert_stmt: ASSERT simple_expr (IF simple_expr)? (COMMA test)? + fn parse_assert_stmt(&mut self) -> Option> { + let token = self.token; + self.bump_keyword(kw::Assert); + + let simple_expr = self.parse_simple_expr(); + let if_cond = if self.token.is_keyword(kw::If) { + self.bump_keyword(kw::If); + Some(self.parse_simple_expr()) + } else { + None + }; + + let msg = if let TokenKind::Comma = self.token.kind { + self.bump_token(TokenKind::Comma); + Some(self.parse_expr()) + } else { + None + }; + + let t = node_ref!( + Stmt::Assert(AssertStmt { + test: simple_expr, + if_cond, + msg, + }), + self.token_span_pos(token, self.prev_token) + ); + + self.skip_newlines(); + + Some(t) + } + + /// Syntax: + /// import_stmt: IMPORT dot_name (AS NAME)? + fn parse_import_stmt(&mut self) -> Option> { + let token = self.token; + self.bump_keyword(kw::Import); + + let mut leading_dot = Vec::new(); + while let TokenKind::DotDotDot = self.token.kind { + leading_dot.push("...".to_string()); + self.bump_token(TokenKind::DotDotDot); + } + while let TokenKind::Dot = self.token.kind { + leading_dot.push(".".to_string()); + self.bump_token(TokenKind::Dot); + } + + let dot_name = expr_as!(self.parse_identifier_expr(), Expr::Identifier).unwrap(); + let asname = if self.token.is_keyword(kw::As) { + self.bump_keyword(kw::As); + let ident = expr_as!(self.parse_identifier_expr(), Expr::Identifier).unwrap(); + Some(ident.names.join(".")) + } else { + None + }; + + let mut path = leading_dot.join(""); + path.push_str(dot_name.names.join(".").as_str()); + + let rawpath = path.clone(); + + let name = if let Some(as_name_value) = asname.clone() { + as_name_value + } else { + dot_name.names.last().unwrap().clone() + }; + + let t = node_ref!( + Stmt::Import(ImportStmt { + path, + rawpath, + name, + asname, + }), + self.token_span_pos(token, self.prev_token) + ); + + self.skip_newlines(); + + Some(t) + } + + /// Syntax: + /// type_alias_stmt: "type" NAME ASSIGN type + fn parse_type_alias_stmt(&mut self) -> Option> { + self.bump_keyword(kw::Type); + + let type_name_pos = self.token; + let type_name = expr_as!(self.parse_expr(), Expr::Identifier).unwrap(); + let type_name_end = self.prev_token; + + self.bump_token(TokenKind::Assign); + + let typ_pos = self.token; + let typ = self.parse_type_annotation(); + let typ_end = self.prev_token; + + self.skip_newlines(); + + Some(node_ref!( + Stmt::TypeAlias(TypeAliasStmt { + type_name: node_ref!(type_name, self.token_span_pos(type_name_pos, type_name_end)), + type_value: node_ref!(typ.node.to_string(), self.token_span_pos(typ_pos, typ_end)), + ty: Some(typ), + }), + self.token_span_pos(type_name_pos, typ_end) + )) + } + + /// Syntax: + /// if_stmt: IF test COLON execution_block (ELIF test COLON execution_block)* (ELSE COLON execution_block)? + /// execution_block: if_simple_stmt | NEWLINE _INDENT schema_init_stmt+ _DEDENT + /// if_simple_stmt: (simple_assign_stmt | unification_stmt | expr_stmt | assert_stmt) NEWLINE + /// schema_init_stmt: if_simple_stmt | if_stmt + fn parse_if_stmt(&mut self) -> Option> { + let token = self.token; + + // if + let mut if_stmt = { + self.bump_keyword(kw::If); + + let cond = self.parse_expr(); + + self.bump_token(TokenKind::Colon); + + let body = if self.token.kind != TokenKind::Newline { + vec![self.parse_expr_or_assign_stmt().expect("invalid if_stmt")] + } else { + self.skip_newlines(); + self.parse_block_stmt_list(TokenKind::Indent, TokenKind::Dedent) + }; + + IfStmt { + body, + cond, + orelse: Vec::new(), + } + }; + + if self.token.kind == TokenKind::Newline { + self.skip_newlines(); + } + + // elif ... + let mut elif_list = Vec::new(); + while self.token.is_keyword(kw::Elif) { + let token = self.token; + self.bump_keyword(kw::Elif); + + let cond = self.parse_expr(); + + self.bump_token(TokenKind::Colon); + + let body = if self.token.kind != TokenKind::Newline { + vec![self.parse_expr_or_assign_stmt().expect("invalid if_stmt")] + } else { + self.skip_newlines(); + self.parse_block_stmt_list(TokenKind::Indent, TokenKind::Dedent) + }; + + let t = node_ref!( + IfStmt { + body, + cond, + orelse: Vec::new(), + }, + self.token_span_pos(token, self.prev_token) + ); + + elif_list.push(t); + } + + if self.token.kind == TokenKind::Newline { + self.skip_newlines(); + } + + // else + if self.token.is_keyword(kw::Else) { + self.bump_keyword(kw::Else); + self.bump_token(TokenKind::Colon); + + let else_body = if self.token.kind != TokenKind::Newline { + vec![self.parse_expr_or_assign_stmt().expect("invalid if_stmt")] + } else { + self.skip_newlines(); + self.parse_block_stmt_list(TokenKind::Indent, TokenKind::Dedent) + }; + + if_stmt.orelse = else_body; + } + + // fix elif-list and else + while let Some(mut x) = elif_list.pop() { + x.node.orelse = if_stmt.orelse; + let pos = x.clone().pos(); + let t = Box::new(Node::node_with_pos(Stmt::If(x.node), pos)); + if_stmt.orelse = vec![t]; + } + + let t = node_ref!( + Stmt::If(if_stmt), + self.token_span_pos(token, self.prev_token) + ); + + self.skip_newlines(); + + Some(t) + } + + /// Syntax: + /// schema_stmt: [decorators] (SCHEMA|MIXIN|PROTOCOL) NAME + /// [LEFT_BRACKETS [schema_arguments] RIGHT_BRACKETS] + /// [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] + /// [for_host] COLON NEWLINE [schema_body] + fn parse_schema_stmt( + &mut self, + decorators: Option>>, + ) -> Option> { + let token = self.token; + + // schema decorators + let decorators = match decorators { + Some(v) => v, + None => Vec::new(), + }; + + // schema|mixin|protocol + let mut is_mixin = false; + let mut is_protocol = false; + { + if self.token.is_keyword(kw::Mixin) { + self.bump_keyword(kw::Mixin); + is_mixin = true; + } else if self.token.is_keyword(kw::Protocol) { + self.bump_keyword(kw::Protocol); + is_protocol = true; + } else { + self.bump_keyword(kw::Schema); + } + } + + // schema Name + let name_expr = self.parse_identifier_expr(); + let name_pos = name_expr.pos(); + let name = expr_as!(name_expr, Expr::Identifier).unwrap(); + let name = node_ref!(name.names.join("."), name_pos); + + if name + .node + .ends_with(kclvm_sema::resolver::global::MIXIN_SUFFIX) + { + is_mixin = true; + } else if name + .node + .ends_with(kclvm_sema::resolver::global::PROTOCOL_SUFFIX) + { + is_protocol = true; + } + + // schema Name[args...] + let args = if let TokenKind::OpenDelim(DelimToken::Bracket) = self.token.kind { + self.parse_parameters( + &[TokenKind::OpenDelim(DelimToken::Bracket)], + &[TokenKind::CloseDelim(DelimToken::Bracket)], + true, + ) + } else { + None + }; + + // schema Name [args...](Base) + let parent_name = if let TokenKind::OpenDelim(DelimToken::Paren) = self.token.kind { + self.bump_token(TokenKind::OpenDelim(DelimToken::Paren)); + let expr = self.parse_identifier_expr(); + let expr_pos = expr.pos(); + let base_schema_name = expr_as!(expr, Expr::Identifier).unwrap(); + self.bump_token(TokenKind::CloseDelim(DelimToken::Paren)); + Some(node_ref!(base_schema_name, expr_pos)) + } else { + None + }; + + // schema Name [args...](Base) for SomeProtocol + let for_host_name = if self.token.is_keyword(kw::For) { + self.bump_keyword(kw::For); + let expr = self.parse_expr(); + let expr_pos = expr.pos(); + let ident = expr_as!(expr, Expr::Identifier).unwrap(); + Some(node_ref!(ident, expr_pos)) + } else { + None + }; + + self.bump_token(TokenKind::Colon); + + self.skip_newlines(); + + if let TokenKind::Indent = self.token.kind { + let body = self.parse_schema_body(); + + let pos = self.token_span_pos(token, self.prev_token); + + Some(node_ref!( + Stmt::Schema(SchemaStmt { + doc: body.doc, + name, + parent_name, + for_host_name, + is_mixin, + is_protocol, + args, + mixins: body.mixins, + body: body.body, + decorators, + checks: body.checks, + index_signature: body.index_signature, + }), + pos + )) + } else { + let pos = self.token_span_pos(token, self.prev_token); + Some(node_ref!( + Stmt::Schema(SchemaStmt { + doc: "".to_string(), + name, + parent_name, + for_host_name, + is_mixin, + is_protocol, + args, + mixins: vec![], + body: vec![], + decorators, + checks: vec![], + index_signature: None, + }), + pos + )) + } + } + + /// Syntax: + /// decorators: (AT decorator_expr NEWLINE)+ + fn parse_decorators(&mut self) -> Vec> { + let mut decorators = Vec::new(); + while let TokenKind::At = self.token.kind { + self.bump_token(TokenKind::At); + + let expr = self.parse_expr(); + let expr_pos = expr.pos(); + match expr.node { + Expr::Identifier(x) => { + decorators.push(node_ref!( + CallExpr { + func: node_ref!(Expr::Identifier(x), expr_pos.clone()), + args: Vec::new(), + keywords: Vec::new(), + }, + expr_pos + )); + } + Expr::Call(x) => { + decorators.push(node_ref!(x, expr_pos)); + } + _ => { + panic!("invalid Decorator: {:?}", expr); + } + }; + + self.skip_newlines(); + } + + self.skip_newlines(); + decorators + } + + /// Syntax: + /// schema_arguments: schema_argument (COMMA schema_argument)* + /// schema_argument: NAME [COLON type] [ASSIGN test] + pub(crate) fn parse_parameters( + &mut self, + open_tokens: &[TokenKind], + close_tokens: &[TokenKind], + bump_close: bool, + ) -> Option> { + debug_assert!(!close_tokens.is_empty()); + let mut has_open_token = false; + + let token = self.token; + + for token in open_tokens { + if *token == self.token.kind { + self.bump_token(*token); + has_open_token = true; + break; + } + } + if !open_tokens.is_empty() && !has_open_token { + return None; + } + let mut args = Arguments { + args: Vec::new(), + defaults: Vec::new(), + type_annotation_list: Vec::new(), + ty_list: Vec::new(), + }; + + loop { + let mut has_close_token = false; + for token in close_tokens { + if *token == self.token.kind { + if bump_close { + self.bump_token(*token); + } + has_close_token = true; + break; + } + } + if has_close_token { + break; + } + + let name_pos = self.token; + let name = expr_as!(self.parse_identifier_expr(), Expr::Identifier).unwrap(); + let name_end = self.prev_token; + + let name = node_ref!(name, self.token_span_pos(name_pos, name_end)); + + let (type_annotation, type_annotation_node) = if let TokenKind::Colon = self.token.kind + { + self.bump_token(TokenKind::Colon); + let typ = self.parse_type_annotation(); + + (Some(node_ref!(typ.node.to_string(), typ.pos())), Some(typ)) + } else { + (None, None) + }; + + let default_value = if let TokenKind::Assign = self.token.kind { + self.bump_token(TokenKind::Assign); + Some(self.parse_expr()) + } else { + None + }; + + args.args.push(name); + args.type_annotation_list.push(type_annotation); + args.ty_list.push(type_annotation_node); + args.defaults.push(default_value); + // Parameter interval comma + if let TokenKind::Comma = self.token.kind { + self.bump(); + } + } + + self.skip_newlines(); + + Some(node_ref!(args, self.token_span_pos(token, self.prev_token))) + } + + /// Syntax: + /// schema_body: _INDENT (string NEWLINE)* [mixin_stmt] + /// (schema_attribute_stmt|schema_init_stmt|schema_index_signature)* + /// [check_block] _DEDENT + /// + /// schema_attribute_stmt: attribute_stmt NEWLINE + /// attribute_stmt: [decorators] NAME [QUESTION] COLON type [(ASSIGN|COMP_OR) test] + /// + /// schema_init_stmt: if_simple_stmt | if_stmt + /// if_stmt: IF test COLON execution_block (ELIF test COLON execution_block)* (ELSE COLON execution_block)? + /// execution_block: if_simple_stmt | NEWLINE _INDENT schema_init_stmt+ _DEDENT + /// if_simple_stmt: (simple_assign_stmt | unification_stmt | expr_stmt | assert_stmt) NEWLINE + /// + /// schema_index_signature: + /// LEFT_BRACKETS [NAME COLON] [ELLIPSIS] basic_type RIGHT_BRACKETS + /// COLON type [ASSIGN test] NEWLINE + fn parse_schema_body(&mut self) -> SchemaStmt { + self.bump_token(TokenKind::Indent); + + // doc string + let body_doc = match self.token.kind { + TokenKind::Literal(lit) => { + if let LitKind::Str { .. } = lit.kind { + let doc_expr = self.parse_str_expr(lit); + self.skip_newlines(); + match &doc_expr.node { + Expr::StringLit(str) => str.raw_value.clone(), + Expr::JoinedString(str) => str.raw_value.clone(), + _ => "".to_string(), + } + } else { + "".to_string() + } + } + _ => "".to_string(), + }; + + // mixin + let body_mixins = if self.token.is_keyword(kw::Mixin) { + let mixins = self.parse_mixins(); + self.skip_newlines(); + mixins + } else { + Vec::new() + }; + + // body + let mut body_body = Vec::new(); + let mut body_index_signature = None; + + loop { + if self.token.kind == TokenKind::Dedent || self.token.is_keyword(kw::Check) { + break; + } + + // assert stmt + if self.token.is_keyword(kw::Assert) { + body_body.push(self.parse_assert_stmt().unwrap()); + continue; + } + + // if stmt + if self.token.is_keyword(kw::If) { + body_body.push(self.parse_if_stmt().unwrap()); + continue; + } + + // schema_attribute_stmt + if let TokenKind::At = self.token.kind { + let token = self.token; + let attr = self.parse_schema_attribute(); + body_body.push(node_ref!( + Stmt::SchemaAttr(attr), + self.token_span_pos(token, self.prev_token) + )); + continue; + } + if let Some(peek) = self.cursor.peek() { + if let TokenKind::Question = peek.kind { + let token = self.token; + let attr = self.parse_schema_attribute(); + body_body.push(node_ref!( + Stmt::SchemaAttr(attr), + self.token_span_pos(token, self.prev_token) + )); + continue; + } + } + + // schema_index_signature or list + if let TokenKind::OpenDelim(DelimToken::Bracket) = self.token.kind { + let token = self.token; + + let (index_sig, or_list_expr) = self.parse_schema_index_signature_or_list(); + + if let Some(x) = index_sig { + body_index_signature = + Some(node_ref!(x, self.token_span_pos(token, self.prev_token))); + } else if let Some(list_expr) = or_list_expr { + let stmt = Stmt::Expr(ExprStmt { + exprs: vec![node_ref!( + Expr::List(list_expr), + self.token_span_pos(token, self.prev_token) + )], + }); + body_body.push(node_ref!(stmt, self.token_span_pos(token, self.prev_token))); + } else { + self.sess.struct_compiler_bug("unreachable"); + } + + self.skip_newlines(); + continue; + } + + // expr or attr + if let Some(x) = self.parse_expr_or_assign_stmt() { + if let Stmt::SchemaAttr(attr) = &x.node { + body_body.push(node_ref!(Stmt::SchemaAttr(attr.clone()), x.pos())); + continue; + } + + if let Stmt::Assign(assign) = x.node.clone() { + if assign.targets.len() == 1 { + let ident = assign.targets[0].clone().node; + if let Some(type_str) = assign.type_annotation { + if !type_str.node.is_empty() { + body_body.push(node_ref!( + Stmt::SchemaAttr(SchemaAttr { + doc: "".to_string(), + name: node_ref!( + ident.names.join("."), + assign.targets[0].pos() + ), + type_str, + ty: assign.ty, + op: Some(BinOrAugOp::Aug(AugOp::Assign)), + value: Some(assign.value), + is_optional: false, + decorators: Vec::new(), + }), + x.pos() + )); + continue; + } + }; + } + } + + body_body.push(x); + } + } + + // check_block + let body_checks = self.parse_schema_check_block(); + + self.bump_token(TokenKind::Dedent); + self.skip_newlines(); + + SchemaStmt { + doc: body_doc, + mixins: body_mixins, + body: body_body, + checks: body_checks, + index_signature: body_index_signature, + + name: Box::new(Node { + node: "".to_string(), + filename: "".to_string(), + line: 0, + column: 0, + end_line: 0, + end_column: 0, + }), + parent_name: None, + for_host_name: None, + is_mixin: false, + is_protocol: false, + args: None, + decorators: Vec::new(), + } + } + + /// Syntax: + /// mixin_stmt: MIXIN LEFT_BRACKETS [mixins | multiline_mixins] RIGHT_BRACKETS NEWLINE + /// multiline_mixins: NEWLINE _INDENT mixins NEWLINE _DEDENT + /// mixins: identifier (COMMA (NEWLINE mixins | identifier))* + fn parse_mixins(&mut self) -> Vec> { + self.bump_keyword(kw::Mixin); + + let mut mixins = Vec::new(); + + self.bump_token(TokenKind::OpenDelim(DelimToken::Bracket)); + + // NEWLINE _INDENT + let has_newline = if self.token.kind == TokenKind::Newline { + self.skip_newlines(); + + if self.token.kind == TokenKind::Indent { + self.bump(); + } else { + self.sess + .struct_token_error(&[&TokenKind::Indent.into()], self.token) + } + true + } else { + false + }; + + loop { + if matches!( + self.token.kind, + TokenKind::CloseDelim(DelimToken::Bracket) | TokenKind::Dedent + ) { + break; + } + let expr = self.parse_identifier_expr(); + let expr_pos = expr.pos(); + let ident = expr_as!(expr, Expr::Identifier).unwrap(); + mixins.push(node_ref!(ident, expr_pos)); + if let TokenKind::Comma = self.token.kind { + self.bump(); + } + if let TokenKind::Newline = self.token.kind { + self.skip_newlines() + } + } + + // _DEDENT + if has_newline { + if self.token.kind == TokenKind::Dedent { + self.bump(); + } else { + self.sess + .struct_token_error(&[&TokenKind::Dedent.into()], self.token) + } + } + + self.bump_token(TokenKind::CloseDelim(DelimToken::Bracket)); + + mixins + } + + /// Syntax: + /// schema_attribute_stmt: attribute_stmt NEWLINE + /// attribute_stmt: [decorators] identifier [QUESTION] COLON type [(ASSIGN|COMP_OR) test] + fn parse_schema_attribute(&mut self) -> SchemaAttr { + let doc = "".to_string(); + + // @decorators + let decorators = if matches!(self.token.kind, TokenKind::At) { + let decorators = self.parse_decorators(); + self.skip_newlines(); + decorators + } else { + Vec::new() + }; + + let name_expr = self.parse_identifier_expr(); + + let name_pos = name_expr.pos(); + let name = expr_as!(name_expr, Expr::Identifier).unwrap(); + let name = node_ref!(name.names.join("."), name_pos.clone()); + + let is_optional = if let TokenKind::Question = self.token.kind { + self.bump_token(TokenKind::Question); + true + } else { + false + }; + + self.bump_token(TokenKind::Colon); + + let typ = self.parse_type_annotation(); + let type_str = node_ref!(typ.node.to_string(), name_pos); + + let op = if self.token.kind == TokenKind::Assign { + self.bump_token(TokenKind::Assign); + Some(BinOrAugOp::Aug(AugOp::Assign)) + } else if let TokenKind::BinOpEq(x) = self.token.kind { + self.bump_token(self.token.kind); + Some(BinOrAugOp::Aug(x.into())) + } else { + None + }; + + let value = if op.is_some() { + Some(self.parse_expr()) + } else { + None + }; + self.skip_newlines(); + SchemaAttr { + doc, + name, + type_str, + ty: Some(typ), + op, + value, + is_optional, + decorators, + } + } + + /// Syntax: + /// schema_index_signature: + /// LEFT_BRACKETS [NAME COLON] [ELLIPSIS] basic_type RIGHT_BRACKETS + /// COLON type [ASSIGN test] NEWLINE + fn parse_schema_index_signature_or_list( + &mut self, + ) -> (Option, Option) { + //let mut list_elts: Vec> = Vec::new(); + let mut maybe_list_expr = true; + + self.bump_token(TokenKind::OpenDelim(DelimToken::Bracket)); + let any_other = if let TokenKind::DotDotDot = self.token.kind { + maybe_list_expr = false; + self.bump(); + true + } else { + false + }; + + self.skip_newlines(); + + let mut ident = None; + let (key_name, key_type, any_other) = if any_other { + let key_type = { + let typ = self.parse_type_annotation(); + node_ref!(typ.node.to_string(), typ.pos()) + }; + (None, key_type, any_other) + } else { + if maybe_list_expr && !matches!(self.token.kind, TokenKind::Ident(_)) { + let list_expr = ListExpr { + elts: self.parse_list_items(), + ctx: ExprContext::Load, + }; + + self.bump_token(TokenKind::CloseDelim(DelimToken::Bracket)); + return (None, Some(list_expr)); + } + + ident = Some(self.parse_identifier_expr()); + if let TokenKind::CloseDelim(DelimToken::Bracket) = self.token.kind { + let key_type = { + let pos = ident.clone().unwrap().pos(); + let ident_node = expr_as!(ident.clone().unwrap(), Expr::Identifier).unwrap(); + let typ = node_ref!(Type::Named(ident_node), pos); + node_ref!(typ.node.to_string(), typ.pos()) + }; + (None, key_type, false) + } else { + maybe_list_expr = false; + + self.bump_token(TokenKind::Colon); + let ident = expr_as!(ident.clone().unwrap(), Expr::Identifier).unwrap(); + let key_name = ident.names.join("."); + let any_other = if let TokenKind::DotDotDot = self.token.kind { + self.bump(); + true + } else { + false + }; + let key_type = { + let typ = self.parse_type_annotation(); + node_ref!(typ.node.to_string(), typ.pos()) + }; + (Some(key_name), key_type, any_other) + } + }; + + if maybe_list_expr && !matches!(self.token.kind, TokenKind::CloseDelim(DelimToken::Bracket)) + { + let mut list_expr = ListExpr { + elts: vec![ident.unwrap()], + ctx: ExprContext::Load, + }; + + if let TokenKind::Comma = self.token.kind { + self.bump(); + } + + list_expr.elts.extend(self.parse_list_items()); + + self.bump_token(TokenKind::CloseDelim(DelimToken::Bracket)); + return (None, Some(list_expr)); + } + + self.bump_token(TokenKind::CloseDelim(DelimToken::Bracket)); + + // must list + if maybe_list_expr { + if let TokenKind::Newline = self.token.kind { + let mut list_expr = ListExpr { + elts: vec![ident.unwrap()], + ctx: ExprContext::Load, + }; + list_expr.elts.extend(self.parse_list_items()); + + self.bump_token(TokenKind::CloseDelim(DelimToken::Bracket)); + return (None, Some(list_expr)); + } + } + + self.bump_token(TokenKind::Colon); + + let typ = self.parse_type_annotation(); + let value_type = node_ref!(typ.node.to_string(), typ.pos()); + + let value = if let TokenKind::Assign = self.token.kind { + self.bump(); + Some(self.parse_expr()) + } else { + None + }; + + self.skip_newlines(); + + let index_sig = SchemaIndexSignature { + key_name, + key_type, + value_type, + value_ty: Some(typ), + value, + any_other, + }; + + (Some(index_sig), None) + } + + /// Syntax: + /// check_block: CHECK COLON NEWLINE _INDENT check_expr+ _DEDENT + /// check_expr: simple_expr [IF simple_expr] [COMMA primary_expr] NEWLINE + fn parse_schema_check_block(&mut self) -> Vec> { + let mut check_expr_list = Vec::new(); + + if self.token.is_keyword(kw::Check) { + self.bump_keyword(kw::Check); + self.bump_token(TokenKind::Colon); + self.skip_newlines(); + + self.bump_token(TokenKind::Indent); + while self.token.kind != TokenKind::Dedent { + let expr = self.parse_check_expr(); + let expr_pos = expr.pos(); + let check_expr = expr_as!(expr, Expr::Check).unwrap(); + check_expr_list.push(node_ref!(check_expr, expr_pos)); + self.skip_newlines(); + } + self.bump_token(TokenKind::Dedent); + } + + check_expr_list + } + + /// Syntax: + /// rule_stmt: [decorators] RULE NAME [LEFT_BRACKETS [schema_arguments] RIGHT_BRACKETS] [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] [for_host] COLON NEWLINE [rule_body] + /// rule_body: _INDENT (string NEWLINE)* check_expr+ _DEDENT + fn parse_rule_stmt( + &mut self, + decorators: Option>>, + ) -> Option> { + let token = self.token; + self.bump_keyword(kw::Rule); + + let decorators = if let Some(x) = decorators { + x + } else { + Vec::new() + }; + + let name_expr = self.parse_identifier_expr(); + let name_pos = name_expr.pos(); + let name = expr_as!(name_expr, Expr::Identifier).unwrap(); + let name = node_ref!(name.names.join("."), name_pos); + + let args = if let TokenKind::OpenDelim(DelimToken::Bracket) = self.token.kind { + self.parse_parameters( + &[TokenKind::OpenDelim(DelimToken::Bracket)], + &[TokenKind::CloseDelim(DelimToken::Bracket)], + true, + ) + } else { + None + }; + + let mut parent_rules = vec![]; + if self.token.kind == TokenKind::OpenDelim(DelimToken::Paren) { + self.bump(); + loop { + if let TokenKind::CloseDelim(DelimToken::Paren) = self.token.kind { + self.bump(); + break; + } + let expr = self.parse_expr(); + let expr_pos = expr.pos(); + let rule_name = expr_as!(expr, Expr::Identifier).unwrap(); + parent_rules.push(node_ref!(rule_name, expr_pos)); + } + } + + let for_host_name = if self.token.is_keyword(kw::For) { + self.bump_keyword(kw::For); + let expr = self.parse_expr(); + let expr_pos = expr.pos(); + let ident = expr_as!(expr, Expr::Identifier).unwrap(); + Some(node_ref!(ident, expr_pos)) + } else { + None + }; + + self.bump_token(TokenKind::Colon); + self.skip_newlines(); + + self.bump_token(TokenKind::Indent); + + // doc string + let body_doc = match self.token.kind { + TokenKind::Literal(lit) => { + if let LitKind::Str { .. } = lit.kind { + let doc_expr = self.parse_str_expr(lit); + self.skip_newlines(); + match &doc_expr.node { + Expr::StringLit(str) => str.raw_value.clone(), + Expr::JoinedString(str) => str.raw_value.clone(), + _ => "".to_string(), + } + } else { + "".to_string() + } + } + _ => "".to_string(), + }; + + let mut check_expr_list = vec![]; + while self.token.kind != TokenKind::Dedent { + let expr = self.parse_check_expr(); + let expr_pos = expr.pos(); + let check_expr = expr_as!(expr, Expr::Check).unwrap(); + check_expr_list.push(node_ref!(check_expr, expr_pos)); + self.skip_newlines(); + } + self.bump_token(TokenKind::Dedent); + + let pos = self.token_span_pos(token, self.prev_token); + + Some(node_ref!( + Stmt::Rule(RuleStmt { + doc: body_doc, + name, + parent_rules, + decorators, + checks: check_expr_list, + args, + for_host_name, + }), + pos + )) + } + + pub(crate) fn parse_joined_string( + &mut self, + s: &StringLit, + pos: rustc_span::BytePos, + ) -> Option { + // skip raw string + if s.raw_value.starts_with(&['r', 'R']) { + return None; + } + if !s.value.contains("${") { + return None; + } + + let start_pos = if s.is_long_string { + pos + rustc_span::BytePos(3) + } else { + pos + rustc_span::BytePos(1) + }; + + let mut joined_value = JoinedString { + is_long_string: s.is_long_string, + raw_value: s.raw_value.clone(), + values: Vec::new(), + }; + + fn parse_expr( + this: &mut Parser, + src: &str, + start_pos: rustc_span::BytePos, + ) -> NodeRef { + use crate::lexer::parse_token_streams; + + debug_assert!(src.starts_with("${"), "{}", src); + debug_assert!(src.ends_with('}'), "{}", src); + + let src = &src[2..src.len() - 1]; + if src.is_empty() { + panic!("string interpolation expression can not be empty") + } + + let start_pos = start_pos + rustc_span::BytePos(2); + + let stream = parse_token_streams(this.sess, src, start_pos); + + let mut parser = Parser { + token: Token::dummy(), + prev_token: Token::dummy(), + cursor: stream.cursor(), + comments: Vec::new(), + sess: this.sess, + }; + + // bump to the first token + parser.bump(); + + let _token = parser.token; + let expr = parser.parse_expr(); + + let mut formatted_value = FormattedValue { + is_long_string: false, + value: expr, + format_spec: None, + }; + + if let TokenKind::Colon = parser.token.kind { + parser.bump(); + if let TokenKind::DocComment(_) = parser.token.kind { + let format_spec = parser + .sess + .source_map + .span_to_snippet(parser.token.span) + .unwrap(); + formatted_value.format_spec = Some(format_spec); + } else { + panic!("invalid joined string spec"); + } + } + + node_ref!(Expr::FormattedValue(formatted_value)) + } + + let data = s.value.as_str(); + let mut off: usize = 0; + loop { + if let Some(i) = data[off..].find("${") { + if let Some(j) = data[off + i..].find('}') { + let lo: usize = off + i; + let hi: usize = off + i + j + 1; + + let s0 = &data[off..lo]; + let s1 = &data[lo..hi]; + + let s0_expr = node_ref!(Expr::StringLit(StringLit { + is_long_string: false, + raw_value: s0.to_string(), + value: s0.to_string().replace("$$", "$"), + })); + + let s1_expr = parse_expr(self, s1, start_pos + rustc_span::BytePos(lo as u32)); + + joined_value.values.push(s0_expr); + joined_value.values.push(s1_expr); + + off = hi; + continue; + } else { + panic!("invalid joined string"); + } + } else { + if off >= s.value.as_str().len() { + break; + } + + // todo: fix pos + joined_value + .values + .push(node_ref!(Expr::StringLit(StringLit { + is_long_string: false, + raw_value: data[off..].to_string(), + value: data[off..].to_string().replace("$$", "$"), + }))); + break; + } + } + + Some(joined_value) + } +} diff --git a/kclvm/parser/src/parser/tests.rs b/kclvm/parser/src/parser/tests.rs new file mode 100644 index 000000000..6fca53f80 --- /dev/null +++ b/kclvm/parser/src/parser/tests.rs @@ -0,0 +1,1139 @@ +use crate::lexer::parse_token_streams; +use crate::parser::Parser; +use crate::session::ParseSession; +use expect_test::{expect, Expect}; +use kclvm_ast::ast::*; +use kclvm_span::{create_session_globals_then, BytePos, FilePathMapping, SourceMap, Symbol}; +use rustc_span::Pos; +use std::path::PathBuf; +use std::sync::Arc; + +fn check_parsing_expr(src: &str, expect: Expect) { + let sm = SourceMap::new(FilePathMapping::empty()); + sm.new_source_file(PathBuf::from("").into(), src.to_string()); + let sess = &ParseSession::with_source_map(Arc::new(sm)); + + create_session_globals_then(|| { + let stream = parse_token_streams(sess, src, BytePos::from_u32(0)); + let mut parser = Parser::new(sess, stream); + let expr = parser.parse_expr(); + let actual = format!("{:?}\n", expr); + expect.assert_eq(&actual) + }); +} + +fn check_parsing_file_ast_json(filename: &str, src: &str, expect: Expect) { + let m = crate::parse_file(filename, Some(src.into())); + let actual = serde_json::ser::to_string(&m).unwrap(); + let actual = format!("{}\n", actual); + expect.assert_eq(&actual) +} + +fn check_parsing_type(src: &str, expect: Expect) { + let sm = SourceMap::new(FilePathMapping::empty()); + sm.new_source_file(PathBuf::from("").into(), src.to_string()); + let sess = &ParseSession::with_source_map(Arc::new(sm)); + + create_session_globals_then(|| { + let stream = parse_token_streams(sess, src, BytePos::from_u32(0)); + let mut parser = Parser::new(sess, stream); + let typ = parser.parse_type_annotation(); + let actual = format!("{:?}\n", typ); + expect.assert_eq(&actual) + }); +} + +fn check_type_str(src: &str, expect: Expect) { + let sm = SourceMap::new(FilePathMapping::empty()); + sm.new_source_file(PathBuf::from("").into(), src.to_string()); + let sess = &ParseSession::with_source_map(Arc::new(sm)); + + create_session_globals_then(|| { + let stream = parse_token_streams(sess, src, BytePos::from_u32(0)); + let mut parser = Parser::new(sess, stream); + let typ = parser.parse_type_annotation(); + let actual = typ.node.to_string(); + expect.assert_eq(&actual) + }); +} + +fn check_type_stmt(src: &str, expect: Expect) { + let sm = SourceMap::new(FilePathMapping::empty()); + sm.new_source_file(PathBuf::from("").into(), src.to_string()); + let sess = &ParseSession::with_source_map(Arc::new(sm)); + + create_session_globals_then(|| { + let stream = parse_token_streams(sess, src, BytePos::from_u32(0)); + let mut parser = Parser::new(sess, stream); + let stmt = parser.parse_stmt().unwrap(); + let actual = format!("{:?}\n", stmt); + expect.assert_eq(&actual) + }); +} + +fn check_parsing_module(filename: &str, src: &str, expect: &str) { + let m = crate::parse_file(filename, Some(src.to_string())); + let actual = format!("{}\n", serde_json::ser::to_string(&m).unwrap()); + assert_eq!(actual, expect); +} + +#[test] +fn smoke_test_parsing_expr() { + check_parsing_expr( + "1\n", + expect![[r#" + Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 } + "#]], + ); + check_parsing_expr( + "\"1\"\n", + expect![[r#" + Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"1\"", value: "1" }), filename: "", line: 1, column: 0, end_line: 1, end_column: 3 } + "#]], + ); +} + +#[test] +fn named_literal_expr() { + check_parsing_expr( + r####"Undefined"####, + expect![[r#" + Node { node: NameConstantLit(NameConstantLit { value: Undefined }), filename: "", line: 1, column: 0, end_line: 1, end_column: 9 } + "#]], + ); + check_parsing_expr( + r####"None"####, + expect![[r#" + Node { node: NameConstantLit(NameConstantLit { value: None }), filename: "", line: 1, column: 0, end_line: 1, end_column: 4 } + "#]], + ); + ( + r####"True"####, + expect![[r#" + Node { node: NameConstantLit(NameConstantLit { value: True }), filename: "", line: 1, column: 1, end_line: 1, end_column: 1 } + "#]], + ); + check_parsing_expr( + r####"False"####, + expect![[r#" + Node { node: NameConstantLit(NameConstantLit { value: False }), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 } + "#]], + ); +} + +#[test] +fn nonstring_literal_expr() { + check_parsing_expr( + r####"1234"####, + expect![[r#" + Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1234) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 4 } + "#]], + ) +} + +#[test] +fn string_literal_expr_0() { + check_parsing_expr( + r####"'1234'"####, + expect![[r#" + Node { node: StringLit(StringLit { is_long_string: false, raw_value: "'1234'", value: "1234" }), filename: "", line: 1, column: 0, end_line: 1, end_column: 6 } + "#]], + ) +} + +#[test] +fn string_literal_expr_1() { + check_parsing_expr( + r####""1234""####, + expect![[r#" + Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"1234\"", value: "1234" }), filename: "", line: 1, column: 0, end_line: 1, end_column: 6 } + "#]], + ) +} + +#[test] +fn string_literal_expr_2() { + check_parsing_expr( + r####""1234\n""####, + expect![[r#" + Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"1234\\n\"", value: "1234\n" }), filename: "", line: 1, column: 0, end_line: 1, end_column: 8 } + "#]], + ) +} + +#[test] +fn number_bin_suffix_expr() { + check_parsing_expr( + r####"1234Ki"####, + expect![[r#" + Node { node: NumberLit(NumberLit { binary_suffix: Some(Ki), value: Int(1234) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 6 } + "#]], + ) +} + +#[test] +fn unary_expr() { + check_parsing_expr( + r####"+1"####, + expect![[r#" + Node { node: Unary(UnaryExpr { op: UAdd, operand: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 1, end_line: 1, end_column: 2 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 2 } + "#]], + ); +} + +#[test] +fn binary_expr_0() { + check_parsing_expr( + r####"1+2+3"####, + expect![[r#" + Node { node: Binary(BinaryExpr { left: Node { node: Binary(BinaryExpr { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, op: Bin(Add), right: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 2, end_line: 1, end_column: 3 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 3 }, op: Bin(Add), right: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(3) }), filename: "", line: 1, column: 4, end_line: 1, end_column: 5 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 } + "#]], + ); +} + +#[test] +fn binary_expr_1() { + check_parsing_expr( + r####"1+2*3-4"####, + expect![[r#" + Node { node: Binary(BinaryExpr { left: Node { node: Binary(BinaryExpr { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, op: Bin(Add), right: Node { node: Binary(BinaryExpr { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 2, end_line: 1, end_column: 3 }, op: Bin(Mul), right: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(3) }), filename: "", line: 1, column: 4, end_line: 1, end_column: 5 } }), filename: "", line: 1, column: 2, end_line: 1, end_column: 5 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 }, op: Bin(Sub), right: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(4) }), filename: "", line: 1, column: 6, end_line: 1, end_column: 7 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 7 } + "#]], + ); +} + +#[test] +fn binary_expr_2() { + check_parsing_expr( + r####"1+2*3/4"####, + expect![[r#" + Node { node: Binary(BinaryExpr { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, op: Bin(Add), right: Node { node: Binary(BinaryExpr { left: Node { node: Binary(BinaryExpr { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 2, end_line: 1, end_column: 3 }, op: Bin(Mul), right: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(3) }), filename: "", line: 1, column: 4, end_line: 1, end_column: 5 } }), filename: "", line: 1, column: 2, end_line: 1, end_column: 5 }, op: Bin(Div), right: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(4) }), filename: "", line: 1, column: 6, end_line: 1, end_column: 7 } }), filename: "", line: 1, column: 2, end_line: 1, end_column: 7 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 7 } + "#]], + ); +} + +#[test] +fn binary_expr_3() { + check_parsing_expr( + r####"a or b"####, + expect![[r#" + Node { node: Binary(BinaryExpr { left: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, op: Bin(Or), right: Node { node: Identifier(Identifier { names: ["b"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 5, end_line: 1, end_column: 6 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 6 } + "#]], + ); +} + +#[test] +fn binary_expr_4() { + check_parsing_expr( + r####"x == a or b"####, + expect![[r#" + Node { node: Binary(BinaryExpr { left: Node { node: Compare(Compare { left: Node { node: Identifier(Identifier { names: ["x"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, ops: [Eq], comparators: [Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 5, end_line: 1, end_column: 6 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 11 }, op: Bin(Or), right: Node { node: Identifier(Identifier { names: ["b"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 10, end_line: 1, end_column: 11 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 11 } + "#]], + ); + check_parsing_expr( + r####"22 > 11 and 111 < 222"####, + expect![[r#" + Node { node: Binary(BinaryExpr { left: Node { node: Compare(Compare { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(22) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 2 }, ops: [Gt], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(11) }), filename: "", line: 1, column: 5, end_line: 1, end_column: 7 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 21 }, op: Bin(And), right: Node { node: Compare(Compare { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(111) }), filename: "", line: 1, column: 12, end_line: 1, end_column: 15 }, ops: [Lt], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(222) }), filename: "", line: 1, column: 18, end_line: 1, end_column: 21 }] }), filename: "", line: 1, column: 12, end_line: 1, end_column: 21 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 21 } + "#]], + ); + check_parsing_expr( + r####"int(e.value) > 1 and i == 0"####, + expect![[r#" + Node { node: Binary(BinaryExpr { left: Node { node: Compare(Compare { left: Node { node: Call(CallExpr { func: Node { node: Identifier(Identifier { names: ["int"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 3 }, args: [Node { node: Identifier(Identifier { names: ["e", "value"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 4, end_line: 1, end_column: 11 }], keywords: [] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 12 }, ops: [Gt], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 15, end_line: 1, end_column: 16 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 27 }, op: Bin(And), right: Node { node: Compare(Compare { left: Node { node: Identifier(Identifier { names: ["i"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 21, end_line: 1, end_column: 22 }, ops: [Eq], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(0) }), filename: "", line: 1, column: 26, end_line: 1, end_column: 27 }] }), filename: "", line: 1, column: 21, end_line: 1, end_column: 27 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 27 } + "#]], + ); + check_parsing_expr( + r####"key in ['key']"####, + expect![[r#" + Node { node: Compare(Compare { left: Node { node: Identifier(Identifier { names: ["key"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 3 }, ops: [In], comparators: [Node { node: List(ListExpr { elts: [Node { node: StringLit(StringLit { is_long_string: false, raw_value: "'key'", value: "key" }), filename: "", line: 1, column: 8, end_line: 1, end_column: 13 }], ctx: Load }), filename: "", line: 1, column: 7, end_line: 1, end_column: 14 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 14 } + "#]], + ); + check_parsing_expr( + r####"key not in ['key']"####, + expect![[r#" + Node { node: Compare(Compare { left: Node { node: Identifier(Identifier { names: ["key"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 3 }, ops: [NotIn], comparators: [Node { node: List(ListExpr { elts: [Node { node: StringLit(StringLit { is_long_string: false, raw_value: "'key'", value: "key" }), filename: "", line: 1, column: 12, end_line: 1, end_column: 17 }], ctx: Load }), filename: "", line: 1, column: 11, end_line: 1, end_column: 18 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 18 } + "#]], + ); + + check_parsing_expr( + r####"1 is 1 and 11 is not 22"####, + expect![[r#" + Node { node: Binary(BinaryExpr { left: Node { node: Compare(Compare { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, ops: [Is], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 5, end_line: 1, end_column: 6 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 23 }, op: Bin(And), right: Node { node: Compare(Compare { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(11) }), filename: "", line: 1, column: 11, end_line: 1, end_column: 13 }, ops: [IsNot], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(22) }), filename: "", line: 1, column: 21, end_line: 1, end_column: 23 }] }), filename: "", line: 1, column: 11, end_line: 1, end_column: 23 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 23 } + "#]], + ); +} + +#[test] +fn binary_expr_5() { + check_parsing_expr( + r####"1 + a and b"####, + expect![[r#" + Node { node: Binary(BinaryExpr { left: Node { node: Binary(BinaryExpr { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, op: Bin(Add), right: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 4, end_line: 1, end_column: 5 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 }, op: Bin(And), right: Node { node: Identifier(Identifier { names: ["b"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 10, end_line: 1, end_column: 11 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 11 } + "#]], + ); +} + +#[test] +fn binary_expr_with_paren() { + check_parsing_expr( + r####"1*(2+3)-4"####, + expect![[r#" + Node { node: Binary(BinaryExpr { left: Node { node: Binary(BinaryExpr { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, op: Bin(Mul), right: Node { node: Paren(ParenExpr { expr: Node { node: Binary(BinaryExpr { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 3, end_line: 1, end_column: 4 }, op: Bin(Add), right: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(3) }), filename: "", line: 1, column: 5, end_line: 1, end_column: 6 } }), filename: "", line: 1, column: 3, end_line: 1, end_column: 6 } }), filename: "", line: 1, column: 2, end_line: 1, end_column: 7 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 7 }, op: Bin(Sub), right: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(4) }), filename: "", line: 1, column: 8, end_line: 1, end_column: 9 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 9 } + "#]], + ); +} + +#[test] +fn if_expr() { + check_parsing_expr( + r####"1 if true else 2"####, + expect![[r#" + Node { node: If(IfExpr { body: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, cond: Node { node: Identifier(Identifier { names: ["true"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 5, end_line: 1, end_column: 9 }, orelse: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 15, end_line: 1, end_column: 16 } }), filename: "", line: 1, column: 2, end_line: 1, end_column: 16 } + "#]], + ); +} + +#[test] +fn primary_expr_0() { + check_parsing_expr( + r####"a.b.c"####, + expect![[r#" + Node { node: Identifier(Identifier { names: ["a", "b", "c"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 } + "#]], + ); +} + +#[test] +fn primary_expr_1() { + check_parsing_expr( + r####"'{}'.format(1)"####, + expect![[r#" + Node { node: Call(CallExpr { func: Node { node: Selector(SelectorExpr { value: Node { node: StringLit(StringLit { is_long_string: false, raw_value: "'{}'", value: "{}" }), filename: "", line: 1, column: 0, end_line: 1, end_column: 4 }, attr: Node { node: Identifier { names: ["format"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 5, end_line: 1, end_column: 11 }, ctx: Load, has_question: false }), filename: "", line: 1, column: 4, end_line: 1, end_column: 11 }, args: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 12, end_line: 1, end_column: 13 }], keywords: [] }), filename: "", line: 1, column: 11, end_line: 1, end_column: 14 } + "#]], + ); +} + +#[test] +fn primary_expr_2() { + check_parsing_expr( + r####"str(1).isdigit()"####, + expect![[r#" + Node { node: Call(CallExpr { func: Node { node: Selector(SelectorExpr { value: Node { node: Call(CallExpr { func: Node { node: Identifier(Identifier { names: ["str"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 3 }, args: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 4, end_line: 1, end_column: 5 }], keywords: [] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 6 }, attr: Node { node: Identifier { names: ["isdigit"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 7, end_line: 1, end_column: 14 }, ctx: Load, has_question: false }), filename: "", line: 1, column: 6, end_line: 1, end_column: 14 }, args: [], keywords: [] }), filename: "", line: 1, column: 14, end_line: 1, end_column: 16 } + "#]], + ); +} + +#[test] +fn list_expr() { + check_parsing_expr( + r####"[1, 2, 3]"####, + expect![[r#" + Node { node: List(ListExpr { elts: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 1, end_line: 1, end_column: 2 }, Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 4, end_line: 1, end_column: 5 }, Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(3) }), filename: "", line: 1, column: 7, end_line: 1, end_column: 8 }], ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 9 } + "#]], + ); + + check_parsing_expr( + r####"[1, if True: 2, 3]"####, + expect![[r#" + Node { node: List(ListExpr { elts: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 1, end_line: 1, end_column: 2 }, Node { node: ListIfItem(ListIfItemExpr { if_cond: Node { node: NameConstantLit(NameConstantLit { value: True }), filename: "", line: 1, column: 7, end_line: 1, end_column: 11 }, exprs: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 13, end_line: 1, end_column: 14 }], orelse: None }), filename: "", line: 1, column: 4, end_line: 1, end_column: 14 }, Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(3) }), filename: "", line: 1, column: 16, end_line: 1, end_column: 17 }], ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 18 } + "#]], + ); +} + +#[test] +fn list_comp_expr_0() { + check_parsing_expr( + r####"[x ** 2 for x in [1, 2, 3]]"####, + expect![[r#" + Node { node: ListComp(ListComp { elt: Node { node: Binary(BinaryExpr { left: Node { node: Identifier(Identifier { names: ["x"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 1, end_line: 1, end_column: 2 }, op: Bin(Pow), right: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 6, end_line: 1, end_column: 7 } }), filename: "", line: 1, column: 1, end_line: 1, end_column: 7 }, generators: [Node { node: CompClause { targets: [Node { node: Identifier { names: ["x"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 12, end_line: 1, end_column: 13 }], iter: Node { node: List(ListExpr { elts: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 18, end_line: 1, end_column: 19 }, Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 21, end_line: 1, end_column: 22 }, Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(3) }), filename: "", line: 1, column: 24, end_line: 1, end_column: 25 }], ctx: Load }), filename: "", line: 1, column: 17, end_line: 1, end_column: 26 }, ifs: [] }, filename: "", line: 1, column: 8, end_line: 1, end_column: 26 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 27 } + "#]], + ); +} + +#[test] +fn list_comp_expr_1() { + check_parsing_expr( + r####"[i for i in [1, 2, 3] if i > 2]"####, + expect![[r#" + Node { node: ListComp(ListComp { elt: Node { node: Identifier(Identifier { names: ["i"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 1, end_line: 1, end_column: 2 }, generators: [Node { node: CompClause { targets: [Node { node: Identifier { names: ["i"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 7, end_line: 1, end_column: 8 }], iter: Node { node: List(ListExpr { elts: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 13, end_line: 1, end_column: 14 }, Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 16, end_line: 1, end_column: 17 }, Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(3) }), filename: "", line: 1, column: 19, end_line: 1, end_column: 20 }], ctx: Load }), filename: "", line: 1, column: 12, end_line: 1, end_column: 21 }, ifs: [Node { node: Compare(Compare { left: Node { node: Identifier(Identifier { names: ["i"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 25, end_line: 1, end_column: 26 }, ops: [Gt], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 29, end_line: 1, end_column: 30 }] }), filename: "", line: 1, column: 25, end_line: 1, end_column: 30 }] }, filename: "", line: 1, column: 3, end_line: 1, end_column: 30 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 31 } + "#]], + ); +} + +#[test] +fn dict_expr() { + check_parsing_expr( + r####"{k0=v0, k1=v1}"####, + expect![[r#" + Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k0"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 1, end_line: 1, end_column: 3 }), value: Node { node: Identifier(Identifier { names: ["v0"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 4, end_line: 1, end_column: 6 }, operation: Override, insert_index: -1 }, filename: "", line: 1, column: 1, end_line: 1, end_column: 6 }, Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k1"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 8, end_line: 1, end_column: 10 }), value: Node { node: Identifier(Identifier { names: ["v1"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 11, end_line: 1, end_column: 13 }, operation: Override, insert_index: -1 }, filename: "", line: 1, column: 8, end_line: 1, end_column: 13 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 14 } + "#]], + ); +} + +#[test] +fn dict_comp_expr() { + check_parsing_expr( + r####"{k: v + 1 for k, v in {k1 = 1, k2 = 2}}"####, + expect![[r#" + Node { node: DictComp(DictComp { entry: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 1, end_line: 1, end_column: 2 }), value: Node { node: Binary(BinaryExpr { left: Node { node: Identifier(Identifier { names: ["v"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 4, end_line: 1, end_column: 5 }, op: Bin(Add), right: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 8, end_line: 1, end_column: 9 } }), filename: "", line: 1, column: 4, end_line: 1, end_column: 9 }, operation: Union, insert_index: -1 }, generators: [Node { node: CompClause { targets: [Node { node: Identifier { names: ["k"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 14, end_line: 1, end_column: 15 }, Node { node: Identifier { names: ["v"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 17, end_line: 1, end_column: 18 }], iter: Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k1"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 23, end_line: 1, end_column: 25 }), value: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 28, end_line: 1, end_column: 29 }, operation: Override, insert_index: -1 }, filename: "", line: 1, column: 23, end_line: 1, end_column: 29 }, Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k2"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 31, end_line: 1, end_column: 33 }), value: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 36, end_line: 1, end_column: 37 }, operation: Override, insert_index: -1 }, filename: "", line: 1, column: 31, end_line: 1, end_column: 37 }] }), filename: "", line: 1, column: 22, end_line: 1, end_column: 38 }, ifs: [] }, filename: "", line: 1, column: 10, end_line: 1, end_column: 38 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 39 } + "#]], + ); +} + +#[test] +fn subscript_expr_0() { + check_parsing_expr( + r####"a[0]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(0) }), filename: "", line: 1, column: 2, end_line: 1, end_column: 3 }), lower: None, upper: None, step: None, ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 4 } + "#]], + ); +} + +#[test] +fn subscript_expr_1() { + check_parsing_expr( + r####"b["k"]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["b"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: Some(Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"k\"", value: "k" }), filename: "", line: 1, column: 2, end_line: 1, end_column: 5 }), lower: None, upper: None, step: None, ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 6 } + "#]], + ); +} + +#[test] +fn subscript_expr_2() { + check_parsing_expr( + r####"c?[1]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["c"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 3, end_line: 1, end_column: 4 }), lower: None, upper: None, step: None, ctx: Load, has_question: true }), filename: "", line: 1, column: 1, end_line: 1, end_column: 5 } + "#]], + ); +} + +#[test] +fn subscript_expr_3() { + check_parsing_expr( + r####"a[1:]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: None, lower: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 2, end_line: 1, end_column: 3 }), upper: None, step: None, ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 5 } + "#]], + ); +} + +#[test] +fn subscript_expr_4() { + check_parsing_expr( + r####"a[:-1]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: None, lower: None, upper: Some(Node { node: Unary(UnaryExpr { op: USub, operand: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 4, end_line: 1, end_column: 5 } }), filename: "", line: 1, column: 3, end_line: 1, end_column: 5 }), step: None, ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 6 } + "#]], + ); +} + +#[test] +fn subscript_expr_5() { + check_parsing_expr( + r####"a[1:len]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: None, lower: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 2, end_line: 1, end_column: 3 }), upper: Some(Node { node: Identifier(Identifier { names: ["len"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 4, end_line: 1, end_column: 7 }), step: None, ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 8 } + "#]], + ); +} + +#[test] +fn subscript_expr_6() { + check_parsing_expr( + r####"a[0:-1]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: None, lower: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(0) }), filename: "", line: 1, column: 2, end_line: 1, end_column: 3 }), upper: Some(Node { node: Unary(UnaryExpr { op: USub, operand: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 5, end_line: 1, end_column: 6 } }), filename: "", line: 1, column: 4, end_line: 1, end_column: 6 }), step: None, ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 7 } + "#]], + ); +} + +#[test] +fn subscript_expr_7() { + check_parsing_expr( + r####"a[::]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: None, lower: None, upper: None, step: None, ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 5 } + "#]], + ); +} + +#[test] +fn subscript_expr_8() { + check_parsing_expr( + r####"a[1::]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: None, lower: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 2, end_line: 1, end_column: 3 }), upper: None, step: None, ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 6 } + "#]], + ); +} + +#[test] +fn subscript_expr_9() { + check_parsing_expr( + r####"a[:0:]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: None, lower: None, upper: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(0) }), filename: "", line: 1, column: 3, end_line: 1, end_column: 4 }), step: None, ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 6 } + "#]], + ); +} + +#[test] +fn subscript_expr_10() { + check_parsing_expr( + r####"a[::-1]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: None, lower: None, upper: None, step: Some(Node { node: Unary(UnaryExpr { op: USub, operand: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 5, end_line: 1, end_column: 6 } }), filename: "", line: 1, column: 4, end_line: 1, end_column: 6 }), ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 7 } + "#]], + ); +} + +#[test] +fn subscript_expr_11() { + check_parsing_expr( + r####"a[1::2]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: None, lower: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 2, end_line: 1, end_column: 3 }), upper: None, step: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 5, end_line: 1, end_column: 6 }), ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 7 } + "#]], + ); +} + +#[test] +fn subscript_expr_12() { + check_parsing_expr( + r####"a[:2:1]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: None, lower: None, upper: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 3, end_line: 1, end_column: 4 }), step: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 5, end_line: 1, end_column: 6 }), ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 7 } + "#]], + ); +} + +#[test] +fn subscript_expr_13() { + check_parsing_expr( + r####"a[1:2:]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: None, lower: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 2, end_line: 1, end_column: 3 }), upper: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 4, end_line: 1, end_column: 5 }), step: None, ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 7 } + "#]], + ); +} + +#[test] +fn subscript_expr_14() { + check_parsing_expr( + r####"a[1:3:1]"####, + expect![[r#" + Node { node: Subscript(Subscript { value: Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, index: None, lower: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 2, end_line: 1, end_column: 3 }), upper: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(3) }), filename: "", line: 1, column: 4, end_line: 1, end_column: 5 }), step: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 6, end_line: 1, end_column: 7 }), ctx: Load, has_question: false }), filename: "", line: 1, column: 1, end_line: 1, end_column: 8 } + "#]], + ); +} + +#[test] +fn call_expr_0() { + check_parsing_expr( + r####"func0()"####, + expect![[r#" + Node { node: Call(CallExpr { func: Node { node: Identifier(Identifier { names: ["func0"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 }, args: [], keywords: [] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 7 } + "#]], + ); +} + +#[test] +fn call_expr_1() { + check_parsing_expr( + r####"func1(1)"####, + expect![[r#" + Node { node: Call(CallExpr { func: Node { node: Identifier(Identifier { names: ["func1"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 }, args: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 6, end_line: 1, end_column: 7 }], keywords: [] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 8 } + "#]], + ); +} + +#[test] +fn call_expr_2() { + check_parsing_expr( + r####"func2(x=2)"####, + expect![[r#" + Node { node: Call(CallExpr { func: Node { node: Identifier(Identifier { names: ["func2"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 }, args: [], keywords: [Node { node: Keyword { arg: Node { node: Identifier { names: ["x"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 6, end_line: 1, end_column: 7 }, value: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 8, end_line: 1, end_column: 9 }) }, filename: "", line: 1, column: 6, end_line: 1, end_column: 9 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 10 } + "#]], + ); +} + +#[test] +fn call_expr_3() { + check_parsing_expr( + r####"func3(1,x=2)"####, + expect![[r#" + Node { node: Call(CallExpr { func: Node { node: Identifier(Identifier { names: ["func3"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 }, args: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 6, end_line: 1, end_column: 7 }], keywords: [Node { node: Keyword { arg: Node { node: Identifier { names: ["x"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 8, end_line: 1, end_column: 9 }, value: Some(Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 10, end_line: 1, end_column: 11 }) }, filename: "", line: 1, column: 8, end_line: 1, end_column: 11 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 12 } + "#]], + ); +} + +#[test] +fn quant_expr_0() { + check_parsing_expr( + r####"all x in collection {x > 0}"####, + expect![[r#" + Node { node: Quant(QuantExpr { target: Node { node: Identifier(Identifier { names: ["collection"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 9, end_line: 1, end_column: 19 }, variables: [Node { node: Identifier { names: ["x"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 4, end_line: 1, end_column: 5 }], op: All, test: Node { node: Compare(Compare { left: Node { node: Identifier(Identifier { names: ["x"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 21, end_line: 1, end_column: 22 }, ops: [Gt], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(0) }), filename: "", line: 1, column: 25, end_line: 1, end_column: 26 }] }), filename: "", line: 1, column: 21, end_line: 1, end_column: 26 }, if_cond: None, ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 27 } + "#]], + ); +} + +#[test] +fn quant_expr_1() { + check_parsing_expr( + r####"any y in collection {y < 0}"####, + expect![[r#" + Node { node: Quant(QuantExpr { target: Node { node: Identifier(Identifier { names: ["collection"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 9, end_line: 1, end_column: 19 }, variables: [Node { node: Identifier { names: ["y"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 4, end_line: 1, end_column: 5 }], op: Any, test: Node { node: Compare(Compare { left: Node { node: Identifier(Identifier { names: ["y"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 21, end_line: 1, end_column: 22 }, ops: [Lt], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(0) }), filename: "", line: 1, column: 25, end_line: 1, end_column: 26 }] }), filename: "", line: 1, column: 21, end_line: 1, end_column: 26 }, if_cond: None, ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 27 } + "#]], + ); +} + +#[test] +fn quant_expr_2() { + check_parsing_expr( + r####"map x in collection {x + 1}"####, + expect![[r#" + Node { node: Quant(QuantExpr { target: Node { node: Identifier(Identifier { names: ["collection"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 9, end_line: 1, end_column: 19 }, variables: [Node { node: Identifier { names: ["x"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 4, end_line: 1, end_column: 5 }], op: Map, test: Node { node: Binary(BinaryExpr { left: Node { node: Identifier(Identifier { names: ["x"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 21, end_line: 1, end_column: 22 }, op: Bin(Add), right: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 25, end_line: 1, end_column: 26 } }), filename: "", line: 1, column: 21, end_line: 1, end_column: 26 }, if_cond: None, ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 27 } + "#]], + ); +} + +#[test] +fn quant_expr_3() { + check_parsing_expr( + r####"filter x in collection {x > 1}"####, + expect![[r#" + Node { node: Quant(QuantExpr { target: Node { node: Identifier(Identifier { names: ["collection"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 12, end_line: 1, end_column: 22 }, variables: [Node { node: Identifier { names: ["x"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 7, end_line: 1, end_column: 8 }], op: Filter, test: Node { node: Compare(Compare { left: Node { node: Identifier(Identifier { names: ["x"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 24, end_line: 1, end_column: 25 }, ops: [Gt], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 28, end_line: 1, end_column: 29 }] }), filename: "", line: 1, column: 24, end_line: 1, end_column: 29 }, if_cond: None, ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 30 } + "#]], + ); +} + +#[test] +fn quant_expr_4() { + check_parsing_expr( + r####"filter x in collection {x > 1}"####, + expect![[r#" + Node { node: Quant(QuantExpr { target: Node { node: Identifier(Identifier { names: ["collection"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 12, end_line: 1, end_column: 22 }, variables: [Node { node: Identifier { names: ["x"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 7, end_line: 1, end_column: 8 }], op: Filter, test: Node { node: Compare(Compare { left: Node { node: Identifier(Identifier { names: ["x"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 24, end_line: 1, end_column: 25 }, ops: [Gt], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 28, end_line: 1, end_column: 29 }] }), filename: "", line: 1, column: 24, end_line: 1, end_column: 29 }, if_cond: None, ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 30 } + "#]], + ); +} + +#[test] +fn quant_expr_5() { + check_parsing_expr( + r####"map i, e in [{k1 = "v1", k2 = "v2"}] { e }"####, + expect![[r#" + Node { node: Quant(QuantExpr { target: Node { node: List(ListExpr { elts: [Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k1"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 14, end_line: 1, end_column: 16 }), value: Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"v1\"", value: "v1" }), filename: "", line: 1, column: 19, end_line: 1, end_column: 23 }, operation: Override, insert_index: -1 }, filename: "", line: 1, column: 14, end_line: 1, end_column: 23 }, Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k2"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 25, end_line: 1, end_column: 27 }), value: Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"v2\"", value: "v2" }), filename: "", line: 1, column: 30, end_line: 1, end_column: 34 }, operation: Override, insert_index: -1 }, filename: "", line: 1, column: 25, end_line: 1, end_column: 34 }] }), filename: "", line: 1, column: 13, end_line: 1, end_column: 35 }], ctx: Load }), filename: "", line: 1, column: 12, end_line: 1, end_column: 36 }, variables: [Node { node: Identifier { names: ["i"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 4, end_line: 1, end_column: 5 }, Node { node: Identifier { names: ["e"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 7, end_line: 1, end_column: 8 }], op: Map, test: Node { node: Identifier(Identifier { names: ["e"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 39, end_line: 1, end_column: 40 }, if_cond: None, ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 42 } + "#]], + ); +} + +#[test] +fn quant_expr_6() { + check_parsing_expr( + r####"map i, e in [{k1 = "v1", k2 = "v2"}] { e if i > 0 }"####, + expect![[r#" + Node { node: Quant(QuantExpr { target: Node { node: List(ListExpr { elts: [Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k1"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 14, end_line: 1, end_column: 16 }), value: Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"v1\"", value: "v1" }), filename: "", line: 1, column: 19, end_line: 1, end_column: 23 }, operation: Override, insert_index: -1 }, filename: "", line: 1, column: 14, end_line: 1, end_column: 23 }, Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k2"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 25, end_line: 1, end_column: 27 }), value: Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"v2\"", value: "v2" }), filename: "", line: 1, column: 30, end_line: 1, end_column: 34 }, operation: Override, insert_index: -1 }, filename: "", line: 1, column: 25, end_line: 1, end_column: 34 }] }), filename: "", line: 1, column: 13, end_line: 1, end_column: 35 }], ctx: Load }), filename: "", line: 1, column: 12, end_line: 1, end_column: 36 }, variables: [Node { node: Identifier { names: ["i"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 4, end_line: 1, end_column: 5 }, Node { node: Identifier { names: ["e"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 7, end_line: 1, end_column: 8 }], op: Map, test: Node { node: Identifier(Identifier { names: ["e"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 39, end_line: 1, end_column: 40 }, if_cond: Some(Node { node: Compare(Compare { left: Node { node: Identifier(Identifier { names: ["i"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 44, end_line: 1, end_column: 45 }, ops: [Gt], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(0) }), filename: "", line: 1, column: 48, end_line: 1, end_column: 49 }] }), filename: "", line: 1, column: 44, end_line: 1, end_column: 49 }), ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 51 } + "#]], + ); +} + +#[test] +fn lambda_expr_0() { + check_parsing_expr( + r####"lambda {}"####, + expect![[r#" + Node { node: Lambda(LambdaExpr { args: None, return_type_str: None, body: [], return_ty: None }), filename: "", line: 1, column: 0, end_line: 1, end_column: 9 } + "#]], + ); +} + +#[test] +fn lambda_expr_1() { + check_parsing_expr( + r####"lambda x {}"####, + expect![[r#" + Node { node: Lambda(LambdaExpr { args: Some(Node { node: Arguments { args: [Node { node: Identifier { names: ["x"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 7, end_line: 1, end_column: 8 }], defaults: [None], type_annotation_list: [None], ty_list: [None] }, filename: "", line: 1, column: 7, end_line: 1, end_column: 8 }), return_type_str: None, body: [], return_ty: None }), filename: "", line: 1, column: 0, end_line: 1, end_column: 11 } + "#]], + ); +} + +#[test] +fn lambda_expr_2() { + check_parsing_expr( + r####"lambda x: int -> int {x}"####, + expect![[r#" + Node { node: Lambda(LambdaExpr { args: Some(Node { node: Arguments { args: [Node { node: Identifier { names: ["x"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 7, end_line: 1, end_column: 8 }], defaults: [None], type_annotation_list: [Some(Node { node: "int", filename: "", line: 1, column: 10, end_line: 1, end_column: 13 })], ty_list: [Some(Node { node: Basic(Int), filename: "", line: 1, column: 10, end_line: 1, end_column: 13 })] }, filename: "", line: 1, column: 7, end_line: 1, end_column: 13 }), return_type_str: Some("int"), body: [Node { node: Expr(ExprStmt { exprs: [Node { node: Identifier(Identifier { names: ["x"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 22, end_line: 1, end_column: 23 }] }), filename: "", line: 1, column: 22, end_line: 1, end_column: 23 }], return_ty: Some(Node { node: Basic(Int), filename: "", line: 1, column: 17, end_line: 1, end_column: 20 }) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 24 } + "#]], + ); +} + +#[test] +fn lambda_expr_3() { + check_parsing_expr( + r####"lambda { + if True: + _a = 1 + else: + _a = 2 + _a +}"####, + expect![[r#" + Node { node: Lambda(LambdaExpr { args: None, return_type_str: None, body: [Node { node: If(IfStmt { body: [Node { node: Assign(AssignStmt { targets: [Node { node: Identifier { names: ["_a"], pkgpath: "", ctx: Store }, filename: "", line: 3, column: 8, end_line: 3, end_column: 10 }], value: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 3, column: 13, end_line: 3, end_column: 14 }, type_annotation: None, ty: None }), filename: "", line: 3, column: 8, end_line: 4, end_column: 0 }], cond: Node { node: NameConstantLit(NameConstantLit { value: True }), filename: "", line: 2, column: 7, end_line: 2, end_column: 11 }, orelse: [Node { node: Assign(AssignStmt { targets: [Node { node: Identifier { names: ["_a"], pkgpath: "", ctx: Store }, filename: "", line: 5, column: 8, end_line: 5, end_column: 10 }], value: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 5, column: 13, end_line: 5, end_column: 14 }, type_annotation: None, ty: None }), filename: "", line: 5, column: 8, end_line: 6, end_column: 0 }] }), filename: "", line: 2, column: 4, end_line: 6, end_column: 4 }, Node { node: Expr(ExprStmt { exprs: [Node { node: Identifier(Identifier { names: ["_a"], pkgpath: "", ctx: Load }), filename: "", line: 6, column: 4, end_line: 6, end_column: 6 }] }), filename: "", line: 6, column: 4, end_line: 6, end_column: 6 }], return_ty: None }), filename: "", line: 1, column: 0, end_line: 7, end_column: 1 } + "#]], + ); +} + +#[test] +fn config_expr_0() { + check_parsing_expr( + r####"{ + "name" = { + "name": "alice" + }, + "gender" = "female" +}"####, + expect![[r#" + Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"name\"", value: "name" }), filename: "", line: 2, column: 4, end_line: 2, end_column: 10 }), value: Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"name\"", value: "name" }), filename: "", line: 3, column: 8, end_line: 3, end_column: 14 }), value: Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"alice\"", value: "alice" }), filename: "", line: 3, column: 16, end_line: 3, end_column: 23 }, operation: Union, insert_index: -1 }, filename: "", line: 3, column: 8, end_line: 3, end_column: 23 }] }), filename: "", line: 2, column: 13, end_line: 4, end_column: 5 }, operation: Override, insert_index: -1 }, filename: "", line: 2, column: 4, end_line: 4, end_column: 5 }, Node { node: ConfigEntry { key: Some(Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"gender\"", value: "gender" }), filename: "", line: 5, column: 4, end_line: 5, end_column: 12 }), value: Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"female\"", value: "female" }), filename: "", line: 5, column: 15, end_line: 5, end_column: 23 }, operation: Override, insert_index: -1 }, filename: "", line: 5, column: 4, end_line: 5, end_column: 23 }] }), filename: "", line: 1, column: 0, end_line: 6, end_column: 1 } + "#]], + ); +} + +#[test] +fn config_expr_1() { + check_parsing_expr( + r####"{ + "name" = { + "name": "alice" + } + "gender" = "female", +}"####, + expect![[r#" + Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"name\"", value: "name" }), filename: "", line: 2, column: 4, end_line: 2, end_column: 10 }), value: Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"name\"", value: "name" }), filename: "", line: 3, column: 8, end_line: 3, end_column: 14 }), value: Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"alice\"", value: "alice" }), filename: "", line: 3, column: 16, end_line: 3, end_column: 23 }, operation: Union, insert_index: -1 }, filename: "", line: 3, column: 8, end_line: 3, end_column: 23 }] }), filename: "", line: 2, column: 13, end_line: 4, end_column: 5 }, operation: Override, insert_index: -1 }, filename: "", line: 2, column: 4, end_line: 4, end_column: 5 }, Node { node: ConfigEntry { key: Some(Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"gender\"", value: "gender" }), filename: "", line: 5, column: 4, end_line: 5, end_column: 12 }), value: Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"female\"", value: "female" }), filename: "", line: 5, column: 15, end_line: 5, end_column: 23 }, operation: Override, insert_index: -1 }, filename: "", line: 5, column: 4, end_line: 5, end_column: 23 }] }), filename: "", line: 1, column: 0, end_line: 6, end_column: 1 } + "#]], + ); +} + +#[test] +fn config_expr_2() { + check_parsing_expr( + r####"{ + "name" = { + "name": "alice", + } + "gender" = "female" +}"####, + expect![[r#" + Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"name\"", value: "name" }), filename: "", line: 2, column: 4, end_line: 2, end_column: 10 }), value: Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"name\"", value: "name" }), filename: "", line: 3, column: 8, end_line: 3, end_column: 14 }), value: Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"alice\"", value: "alice" }), filename: "", line: 3, column: 16, end_line: 3, end_column: 23 }, operation: Union, insert_index: -1 }, filename: "", line: 3, column: 8, end_line: 3, end_column: 23 }] }), filename: "", line: 2, column: 13, end_line: 4, end_column: 5 }, operation: Override, insert_index: -1 }, filename: "", line: 2, column: 4, end_line: 4, end_column: 5 }, Node { node: ConfigEntry { key: Some(Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"gender\"", value: "gender" }), filename: "", line: 5, column: 4, end_line: 5, end_column: 12 }), value: Node { node: StringLit(StringLit { is_long_string: false, raw_value: "\"female\"", value: "female" }), filename: "", line: 5, column: 15, end_line: 5, end_column: 23 }, operation: Override, insert_index: -1 }, filename: "", line: 5, column: 4, end_line: 5, end_column: 23 }] }), filename: "", line: 1, column: 0, end_line: 6, end_column: 1 } + "#]], + ); +} + +#[test] +fn config_if_expr_0() { + check_parsing_expr( + r####"{ + if True: + a = 1 +}"####, + expect![[r#" + Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: None, value: Node { node: ConfigIfEntry(ConfigIfEntryExpr { if_cond: Node { node: NameConstantLit(NameConstantLit { value: True }), filename: "", line: 2, column: 7, end_line: 2, end_column: 11 }, items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 3, column: 8, end_line: 3, end_column: 9 }), value: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 3, column: 12, end_line: 3, end_column: 13 }, operation: Override, insert_index: -1 }, filename: "", line: 3, column: 12, end_line: 3, end_column: 13 }], orelse: None }), filename: "", line: 3, column: 8, end_line: 4, end_column: 0 }, operation: Union, insert_index: -1 }, filename: "", line: 2, column: 4, end_line: 4, end_column: 0 }] }), filename: "", line: 1, column: 0, end_line: 4, end_column: 1 } + "#]], + ); +} + +#[test] +fn config_if_expr_1() { + check_parsing_expr( + r####"{ + if True: + a = 1 + else: + a = 2 +}"####, + expect![[r#" + Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: None, value: Node { node: ConfigIfEntry(ConfigIfEntryExpr { if_cond: Node { node: NameConstantLit(NameConstantLit { value: True }), filename: "", line: 2, column: 7, end_line: 2, end_column: 11 }, items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 3, column: 8, end_line: 3, end_column: 9 }), value: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 3, column: 12, end_line: 3, end_column: 13 }, operation: Override, insert_index: -1 }, filename: "", line: 3, column: 12, end_line: 3, end_column: 13 }], orelse: Some(Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 5, column: 8, end_line: 5, end_column: 9 }), value: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 5, column: 12, end_line: 5, end_column: 13 }, operation: Override, insert_index: -1 }, filename: "", line: 5, column: 12, end_line: 5, end_column: 13 }] }), filename: "", line: 4, column: 4, end_line: 6, end_column: 0 }) }), filename: "", line: 3, column: 8, end_line: 4, end_column: 4 }, operation: Union, insert_index: -1 }, filename: "", line: 2, column: 4, end_line: 6, end_column: 0 }] }), filename: "", line: 1, column: 0, end_line: 6, end_column: 1 } + "#]], + ); +} + +#[test] +fn config_if_expr_2() { + check_parsing_expr( + r####"{ + if True: + a = 1 + elif x > 1: + a = 2 + else: + a = 3 +}"####, + expect![[r#" + Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: None, value: Node { node: ConfigIfEntry(ConfigIfEntryExpr { if_cond: Node { node: NameConstantLit(NameConstantLit { value: True }), filename: "", line: 2, column: 7, end_line: 2, end_column: 11 }, items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 3, column: 8, end_line: 3, end_column: 9 }), value: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 3, column: 12, end_line: 3, end_column: 13 }, operation: Override, insert_index: -1 }, filename: "", line: 3, column: 12, end_line: 3, end_column: 13 }], orelse: Some(Node { node: ConfigIfEntry(ConfigIfEntryExpr { if_cond: Node { node: Compare(Compare { left: Node { node: Identifier(Identifier { names: ["x"], pkgpath: "", ctx: Load }), filename: "", line: 4, column: 9, end_line: 4, end_column: 10 }, ops: [Gt], comparators: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 4, column: 13, end_line: 4, end_column: 14 }] }), filename: "", line: 4, column: 9, end_line: 4, end_column: 14 }, items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 5, column: 8, end_line: 5, end_column: 9 }), value: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 5, column: 12, end_line: 5, end_column: 13 }, operation: Override, insert_index: -1 }, filename: "", line: 5, column: 12, end_line: 5, end_column: 13 }], orelse: Some(Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 7, column: 8, end_line: 7, end_column: 9 }), value: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(3) }), filename: "", line: 7, column: 12, end_line: 7, end_column: 13 }, operation: Override, insert_index: -1 }, filename: "", line: 7, column: 12, end_line: 7, end_column: 13 }] }), filename: "", line: 6, column: 4, end_line: 8, end_column: 0 }) }), filename: "", line: 4, column: 4, end_line: 6, end_column: 4 }) }), filename: "", line: 3, column: 8, end_line: 4, end_column: 4 }, operation: Union, insert_index: -1 }, filename: "", line: 2, column: 4, end_line: 8, end_column: 0 }] }), filename: "", line: 1, column: 0, end_line: 8, end_column: 1 } + "#]], + ); +} + +#[test] +fn config_if_expr_3() { + check_parsing_expr( + r####"{ + if True: + if False: + a = 1 +}"####, + expect![[r#" + Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: None, value: Node { node: ConfigIfEntry(ConfigIfEntryExpr { if_cond: Node { node: NameConstantLit(NameConstantLit { value: True }), filename: "", line: 2, column: 7, end_line: 2, end_column: 11 }, items: [Node { node: ConfigEntry { key: None, value: Node { node: ConfigIfEntry(ConfigIfEntryExpr { if_cond: Node { node: NameConstantLit(NameConstantLit { value: False }), filename: "", line: 3, column: 11, end_line: 3, end_column: 16 }, items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["a"], pkgpath: "", ctx: Load }), filename: "", line: 4, column: 12, end_line: 4, end_column: 13 }), value: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 4, column: 16, end_line: 4, end_column: 17 }, operation: Override, insert_index: -1 }, filename: "", line: 4, column: 16, end_line: 4, end_column: 17 }], orelse: None }), filename: "", line: 4, column: 12, end_line: 5, end_column: 0 }, operation: Override, insert_index: -1 }, filename: "", line: 4, column: 12, end_line: 5, end_column: 0 }], orelse: None }), filename: "", line: 3, column: 8, end_line: 5, end_column: 0 }, operation: Union, insert_index: -1 }, filename: "", line: 2, column: 4, end_line: 5, end_column: 0 }] }), filename: "", line: 1, column: 0, end_line: 5, end_column: 1 } + "#]], + ); +} + +#[test] +fn schema_expr_0() { + check_parsing_expr( + r####"Schema {}"####, + expect![[r#" + Node { node: Schema(SchemaExpr { name: Node { node: Identifier { names: ["Schema"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 0, end_line: 1, end_column: 6 }, args: [], kwargs: [], config: Node { node: Config(ConfigExpr { items: [] }), filename: "", line: 1, column: 7, end_line: 1, end_column: 9 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 9 } + "#]], + ); +} + +#[test] +fn schema_expr_1() { + check_parsing_expr( + r####"Schema {k=v}"####, + expect![[r#" + Node { node: Schema(SchemaExpr { name: Node { node: Identifier { names: ["Schema"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 0, end_line: 1, end_column: 6 }, args: [], kwargs: [], config: Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 8, end_line: 1, end_column: 9 }), value: Node { node: Identifier(Identifier { names: ["v"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 10, end_line: 1, end_column: 11 }, operation: Override, insert_index: -1 }, filename: "", line: 1, column: 8, end_line: 1, end_column: 11 }] }), filename: "", line: 1, column: 7, end_line: 1, end_column: 12 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 12 } + "#]], + ); +} + +#[test] +fn schema_expr_2() { + check_parsing_expr( + r####"Schema () {k=v}"####, + expect![[r#" + Node { node: Schema(SchemaExpr { name: Node { node: Identifier { names: ["Schema"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 0, end_line: 1, end_column: 6 }, args: [], kwargs: [], config: Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 11, end_line: 1, end_column: 12 }), value: Node { node: Identifier(Identifier { names: ["v"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 13, end_line: 1, end_column: 14 }, operation: Override, insert_index: -1 }, filename: "", line: 1, column: 11, end_line: 1, end_column: 14 }] }), filename: "", line: 1, column: 10, end_line: 1, end_column: 15 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 15 } + "#]], + ); +} + +#[test] +fn schema_expr_3() { + check_parsing_expr( + r####"Schema (1, 2) {k=v}"####, + expect![[r#" + Node { node: Schema(SchemaExpr { name: Node { node: Identifier { names: ["Schema"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 0, end_line: 1, end_column: 6 }, args: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 8, end_line: 1, end_column: 9 }, Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 11, end_line: 1, end_column: 12 }], kwargs: [], config: Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 15, end_line: 1, end_column: 16 }), value: Node { node: Identifier(Identifier { names: ["v"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 17, end_line: 1, end_column: 18 }, operation: Override, insert_index: -1 }, filename: "", line: 1, column: 15, end_line: 1, end_column: 18 }] }), filename: "", line: 1, column: 14, end_line: 1, end_column: 19 } }), filename: "", line: 1, column: 0, end_line: 1, end_column: 19 } + "#]], + ); +} + +#[test] +fn schema_expr_4() { + check_parsing_expr( + r####"Schema (1, 2) { + k=v +}"####, + expect![[r#" + Node { node: Schema(SchemaExpr { name: Node { node: Identifier { names: ["Schema"], pkgpath: "", ctx: Load }, filename: "", line: 1, column: 0, end_line: 1, end_column: 6 }, args: [Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 8, end_line: 1, end_column: 9 }, Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 1, column: 11, end_line: 1, end_column: 12 }], kwargs: [], config: Node { node: Config(ConfigExpr { items: [Node { node: ConfigEntry { key: Some(Node { node: Identifier(Identifier { names: ["k"], pkgpath: "", ctx: Load }), filename: "", line: 2, column: 4, end_line: 2, end_column: 5 }), value: Node { node: Identifier(Identifier { names: ["v"], pkgpath: "", ctx: Load }), filename: "", line: 2, column: 6, end_line: 2, end_column: 7 }, operation: Override, insert_index: -1 }, filename: "", line: 2, column: 4, end_line: 2, end_column: 7 }] }), filename: "", line: 1, column: 14, end_line: 3, end_column: 1 } }), filename: "", line: 1, column: 0, end_line: 3, end_column: 1 } + "#]], + ); +} + +#[test] +fn line_continue() { + check_parsing_expr( + r####"1 + \ +2 +"####, + expect![[r#" + Node { node: Binary(BinaryExpr { left: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(1) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 1 }, op: Bin(Add), right: Node { node: NumberLit(NumberLit { binary_suffix: None, value: Int(2) }), filename: "", line: 2, column: 0, end_line: 2, end_column: 1 } }), filename: "", line: 1, column: 0, end_line: 2, end_column: 1 } + "#]], + ); +} + +#[test] +fn test_basic_type() { + check_parsing_type( + r####"bool"####, + expect![[r#" + Node { node: Basic(Bool), filename: "", line: 1, column: 0, end_line: 1, end_column: 4 } + "#]], + ); + check_parsing_type( + r####"int"####, + expect![[r#" + Node { node: Basic(Int), filename: "", line: 1, column: 0, end_line: 1, end_column: 3 } + "#]], + ); + check_parsing_type( + r####"float"####, + expect![[r#" + Node { node: Basic(Float), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 } + "#]], + ); + check_parsing_type( + r####"str"####, + expect![[r#" + Node { node: Basic(Str), filename: "", line: 1, column: 0, end_line: 1, end_column: 3 } + "#]], + ); +} + +#[test] +fn test_any_type() { + check_parsing_type( + r####"any"####, + expect![[r#" + Node { node: Any, filename: "", line: 1, column: 0, end_line: 1, end_column: 3 } + "#]], + ); +} + +#[test] +fn test_list_type() { + check_parsing_type( + r####"[]"####, + expect![[r#" + Node { node: List(ListType { inner_type: None }), filename: "", line: 1, column: 0, end_line: 1, end_column: 2 } + "#]], + ); + check_parsing_type( + r####"[int]"####, + expect![[r#" + Node { node: List(ListType { inner_type: Some(Node { node: Basic(Int), filename: "", line: 1, column: 1, end_line: 1, end_column: 4 }) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 } + "#]], + ); + check_parsing_type( + r####"[any]"####, + expect![[r#" + Node { node: List(ListType { inner_type: Some(Node { node: Any, filename: "", line: 1, column: 1, end_line: 1, end_column: 4 }) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 } + "#]], + ); + + check_parsing_type( + r####"[[]]"####, + expect![[r#" + Node { node: List(ListType { inner_type: Some(Node { node: List(ListType { inner_type: None }), filename: "", line: 1, column: 1, end_line: 1, end_column: 3 }) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 4 } + "#]], + ); + check_parsing_type( + r####"[[str]]"####, + expect![[r#" + Node { node: List(ListType { inner_type: Some(Node { node: List(ListType { inner_type: Some(Node { node: Basic(Str), filename: "", line: 1, column: 2, end_line: 1, end_column: 5 }) }), filename: "", line: 1, column: 1, end_line: 1, end_column: 6 }) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 7 } + "#]], + ); +} + +#[test] +fn test_dict_type() { + check_parsing_type( + r####"{:}"####, + expect![[r#" + Node { node: Dict(DictType { key_type: None, value_type: None }), filename: "", line: 1, column: 0, end_line: 1, end_column: 3 } + "#]], + ); + check_parsing_type( + r####"{str:}"####, + expect![[r#" + Node { node: Dict(DictType { key_type: Some(Node { node: Basic(Str), filename: "", line: 1, column: 1, end_line: 1, end_column: 4 }), value_type: None }), filename: "", line: 1, column: 0, end_line: 1, end_column: 6 } + "#]], + ); + check_parsing_type( + r####"{:[]}"####, + expect![[r#" + Node { node: Dict(DictType { key_type: None, value_type: Some(Node { node: List(ListType { inner_type: None }), filename: "", line: 1, column: 2, end_line: 1, end_column: 4 }) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 } + "#]], + ); + check_parsing_type( + r####"{str:{:float}}"####, + expect![[r#" + Node { node: Dict(DictType { key_type: Some(Node { node: Basic(Str), filename: "", line: 1, column: 1, end_line: 1, end_column: 4 }), value_type: Some(Node { node: Dict(DictType { key_type: None, value_type: Some(Node { node: Basic(Float), filename: "", line: 1, column: 7, end_line: 1, end_column: 12 }) }), filename: "", line: 1, column: 5, end_line: 1, end_column: 13 }) }), filename: "", line: 1, column: 0, end_line: 1, end_column: 14 } + "#]], + ); +} + +#[test] +fn test_union_type() { + check_parsing_type( + r####"int|str"####, + expect![[r#" + Node { node: Union(UnionType { type_elements: [Node { node: Basic(Int), filename: "", line: 1, column: 0, end_line: 1, end_column: 3 }, Node { node: Basic(Str), filename: "", line: 1, column: 4, end_line: 1, end_column: 7 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 7 } + "#]], + ); + check_parsing_type( + r####"int | str | [] | {:}"####, + expect![[r#" + Node { node: Union(UnionType { type_elements: [Node { node: Basic(Int), filename: "", line: 1, column: 0, end_line: 1, end_column: 3 }, Node { node: Basic(Str), filename: "", line: 1, column: 6, end_line: 1, end_column: 9 }, Node { node: List(ListType { inner_type: None }), filename: "", line: 1, column: 12, end_line: 1, end_column: 14 }, Node { node: Dict(DictType { key_type: None, value_type: None }), filename: "", line: 1, column: 17, end_line: 1, end_column: 20 }] }), filename: "", line: 1, column: 0, end_line: 1, end_column: 20 } + "#]], + ); +} + +#[test] +fn test_named_type() { + check_parsing_type( + r####"Person"####, + expect![[r#" + Node { node: Named(Identifier { names: ["Person"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 6 } + "#]], + ); + check_parsing_type( + r####"some.pkg.Person"####, + expect![[r#" + Node { node: Named(Identifier { names: ["some", "pkg", "Person"], pkgpath: "", ctx: Load }), filename: "", line: 1, column: 0, end_line: 1, end_column: 15 } + "#]], + ) +} + +#[test] +fn test_literal_type() { + check_parsing_type( + r####"True"####, + expect![[r#" + Node { node: Literal(Bool(true)), filename: "", line: 1, column: 0, end_line: 1, end_column: 4 } + "#]], + ); + check_parsing_type( + r####" False "####, + expect![[r#" + Node { node: Literal(Bool(false)), filename: "", line: 1, column: 1, end_line: 1, end_column: 6 } + "#]], + ); + + check_parsing_type( + r####"123"####, + expect![[r#" + Node { node: Literal(Int(123, None)), filename: "", line: 1, column: 0, end_line: 1, end_column: 3 } + "#]], + ); + + check_parsing_type( + r####"123.0"####, + expect![[r#" + Node { node: Literal(Float(123.0)), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 } + "#]], + ); + + check_parsing_type( + r####""abc""####, + expect![[r#" + Node { node: Literal(Str("abc")), filename: "", line: 1, column: 0, end_line: 1, end_column: 5 } + "#]], + ); + check_parsing_type( + r####"''"####, + expect![[r#" + Node { node: Literal(Str("")), filename: "", line: 1, column: 0, end_line: 1, end_column: 2 } + "#]], + ); +} + +#[test] +fn test_type_str() { + check_type_str(r####"int"####, expect![[r#"int"#]]); + check_type_str(r####" int "####, expect![[r#"int"#]]); + + check_type_str( + r####"bool | True | int | str|str"####, + expect![[r#"bool|True|int|str|str"#]], + ); + check_type_str( + r####"[ [{str: float}] | int]"####, + expect![[r#"[[{str:float}]|int]"#]], + ); +} + +#[test] +fn test_parse_if_stmt() { + check_parsing_file_ast_json( + "hello.k", + r####" +schema TestBool: + [] + [1 + 2, + ] + [str ]: int + "####, + expect![[r#" + {"filename":"hello.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Schema":{"doc":"","name":{"node":"TestBool","filename":"hello.k","line":2,"column":7,"end_line":2,"end_column":15},"parent_name":null,"for_host_name":null,"is_mixin":false,"is_protocol":false,"args":null,"mixins":[],"body":[{"node":{"Expr":{"exprs":[{"node":{"List":{"elts":[],"ctx":"Load"}},"filename":"hello.k","line":3,"column":4,"end_line":3,"end_column":6}]}},"filename":"hello.k","line":3,"column":4,"end_line":3,"end_column":6},{"node":{"Expr":{"exprs":[{"node":{"List":{"elts":[{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"hello.k","line":4,"column":5,"end_line":4,"end_column":6},{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":2}}},"filename":"hello.k","line":5,"column":4,"end_line":5,"end_column":5}],"ctx":"Load"}},"filename":"hello.k","line":4,"column":4,"end_line":6,"end_column":5}]}},"filename":"hello.k","line":4,"column":4,"end_line":6,"end_column":5}],"decorators":[],"checks":[],"index_signature":{"node":{"key_name":null,"key_type":{"node":"str","filename":"hello.k","line":7,"column":5,"end_line":7,"end_column":8},"value_type":{"node":"int","filename":"hello.k","line":7,"column":15,"end_line":7,"end_column":18},"value":null,"any_other":false},"filename":"hello.k","line":7,"column":4,"end_line":8,"end_column":0}}},"filename":"hello.k","line":2,"column":0,"end_line":8,"end_column":8}],"comments":[]} + "#]], + ); +} + +#[test] +fn test_parse_joined_string() { + // todo: fix joined_string + // check_type_stmt( + // r####"a='${123+200}'"####, + // expect![[r#" + // sss + // "#]], + // ); +} + +#[test] +fn test_parse_file() { + let filenames = vec![ + "testdata/assert-01.k", + "testdata/assert-02.k", + "testdata/assert-03.k", + "testdata/assert-if-0.k", + "testdata/assert-if-1.k", + "testdata/assert-if-2.k", + "testdata/assign-01.k", + "testdata/config_expr-01.k", + "testdata/config_expr-02.k", + "testdata/config_expr-03.k", + "testdata/config_expr-04.k", + "testdata/import-01.k", + "testdata/if-01.k", + "testdata/if-02.k", + "testdata/if-03.k", + "testdata/type-01.k", + ]; + for filename in filenames { + let code = std::fs::read_to_string(&filename).unwrap(); + let expect = std::fs::read_to_string(filename.to_string() + ".json").unwrap(); + check_parsing_module( + filename.trim_start_matches("testdata/"), + code.as_str(), + expect.as_str(), + ); + } +} + +// TODO: enable file tests after pos & error added. +// #[test] +fn smoke_test_parsing_stmt() { + let code = "a=1"; + let node = Some(Node::dummy_node(Stmt::Assign(AssignStmt { + targets: vec![Box::new(Node::dummy_node(Identifier { + names: vec!["a".to_string()], + pkgpath: "".to_string(), + ctx: ExprContext::Store, + }))], + value: Box::new(Node::dummy_node(Expr::NumberLit(NumberLit { + binary_suffix: None, + value: NumberLitValue::Int(1), + }))), + type_annotation: None, + ty: None, + }))); + + create_session_globals_then(move || { + let sm = SourceMap::new(FilePathMapping::empty()); + sm.new_source_file(PathBuf::from("").into(), code.to_string()); + let sess = &ParseSession::with_source_map(Arc::new(sm)); + + let stream = parse_token_streams(sess, code, BytePos::from_u32(0)); + let mut parser = Parser::new(sess, stream); + let stmt = parser.parse_stmt(); + + let expect = format!("{:?}\n", node); + let got = format!("{:?}\n", stmt); + + assert_eq!(got, expect); + }); +} diff --git a/kclvm/parser/src/parser/ty.rs b/kclvm/parser/src/parser/ty.rs new file mode 100644 index 000000000..3f67d8a9e --- /dev/null +++ b/kclvm/parser/src/parser/ty.rs @@ -0,0 +1,234 @@ +#![allow(dead_code)] + +use super::Parser; + +use crate::expr_as; + +use kclvm_ast::ast; +use kclvm_ast::ast::{Expr, Node, NodeRef, Type}; +use kclvm_ast::token; +use kclvm_ast::token::{BinOpToken, DelimToken, TokenKind}; +use kclvm_span::symbol::{kw, sym}; + +impl<'a> Parser<'_> { + /// Syntax: + /// + /// type: type_element (OR type_element)* + /// type_element: schema_type | basic_type | compound_type | literal_type + /// schema_type: identifier + /// basic_type: STRING_TYPE | INT_TYPE | FLOAT_TYPE | BOOL_TYPE | ANY_TYPE + /// compound_type: list_type | dict_type + /// list_type: LEFT_BRACKETS (type)? RIGHT_BRACKETS + /// dict_type: LEFT_BRACE (type)? COLON (type)? RIGHT_BRACE + /// literal_type: string | number | TRUE | FALSE | NONE + pub(crate) fn parse_type_annotation(&mut self) -> NodeRef { + let token = self.token; + let mut type_node_list = vec![self.parse_type_element()]; + + while let TokenKind::BinOp(BinOpToken::Or) = self.token.kind { + self.bump(); + let t = self.parse_type_element(); + type_node_list.push(t); + } + + if type_node_list.len() > 1 { + let mut union_type = ast::UnionType { + type_elements: Vec::new(), + }; + for v in type_node_list.iter_mut() { + union_type.type_elements.push(v.clone()); + } + + Box::new(Node::node( + Type::Union(union_type.clone()), + self.sess.struct_token_loc(token, self.prev_token), + )) + } else { + type_node_list[0].clone() + } + } + + fn parse_type_element(&mut self) -> NodeRef { + let token = self.token; + + // any + if self.token.is_keyword(kw::Any) { + let t = Type::Any; + self.bump_keyword(kw::Any); + return Box::new(Node::node( + t, + self.sess.struct_token_loc(token, self.prev_token), + )); + } + + // lit: true/false + if self.token.is_keyword(kw::True) { + self.bump_keyword(kw::True); + return Box::new(Node::node( + Type::Literal(ast::LiteralType::Bool(true)), + self.sess.struct_token_loc(token, self.prev_token), + )); + } + if self.token.is_keyword(kw::False) { + self.bump_keyword(kw::False); + return Box::new(Node::node( + Type::Literal(ast::LiteralType::Bool(false)), + self.sess.struct_token_loc(token, self.prev_token), + )); + } + + // basic type + if self.token.is_keyword(sym::bool) { + let t = Type::Basic(ast::BasicType::Bool); + self.bump_keyword(sym::bool); + return Box::new(Node::node( + t, + self.sess.struct_token_loc(token, self.prev_token), + )); + } + if self.token.is_keyword(sym::int) { + let t = Type::Basic(ast::BasicType::Int); + self.bump_keyword(sym::int); + return Box::new(Node::node( + t, + self.sess.struct_token_loc(token, self.prev_token), + )); + } + if self.token.is_keyword(sym::float) { + let t = Type::Basic(ast::BasicType::Float); + self.bump_keyword(sym::float); + return Box::new(Node::node( + t, + self.sess.struct_token_loc(token, self.prev_token), + )); + } + if self.token.is_keyword(sym::str) { + let t = Type::Basic(ast::BasicType::Str); + self.bump_keyword(sym::str); + return Box::new(Node::node( + t, + self.sess.struct_token_loc(token, self.prev_token), + )); + } + + // named type + if let TokenKind::Ident(_) = self.token.kind { + let ident = self.parse_identifier_expr(); + let ident = expr_as!(ident, Expr::Identifier).unwrap(); + let t = Type::Named(ident); + return Box::new(Node::node( + t, + self.sess.struct_token_loc(token, self.prev_token), + )); + } + + // lit type + if let TokenKind::Literal(lit) = self.token.kind { + let t = match lit.kind { + token::LitKind::Bool => { + if lit.symbol == kw::True { + ast::LiteralType::Bool(true) + } else if lit.symbol == kw::False { + ast::LiteralType::Bool(false) + } else { + panic!("invalid lit type: {:?}", self.token); + } + } + token::LitKind::Integer => { + let v = lit.symbol.as_str().parse::().unwrap(); + if let Some(suffix) = lit.suffix { + let x = ast::NumberBinarySuffix::try_from(suffix.as_str()); + ast::LiteralType::Int(v, Some(x.unwrap())) + } else { + ast::LiteralType::Int(v, None) + } + } + token::LitKind::Float => { + let v = lit.symbol.as_str().parse::().unwrap(); + ast::LiteralType::Float(v) + } + token::LitKind::Str { .. } => { + ast::LiteralType::Str(lit.symbol.as_str().to_string()) + } + _ => { + if self.token.is_keyword(kw::True) { + ast::LiteralType::Bool(true) + } else if self.token.is_keyword(kw::False) { + ast::LiteralType::Bool(false) + } else { + panic!("invalid lit type: {:?}", self.token); + } + } + }; + + let t = Type::Literal(t); + + self.bump(); + + return Box::new(Node::node( + t, + self.sess.struct_token_loc(token, self.prev_token), + )); + } + + // [type] + if let TokenKind::OpenDelim(DelimToken::Bracket) = self.token.kind { + self.bump_token(TokenKind::OpenDelim(DelimToken::Bracket)); + + if let TokenKind::CloseDelim(DelimToken::Bracket) = self.token.kind { + self.bump(); + let t = Type::List(ast::ListType { inner_type: None }); + + return Box::new(Node::node( + t, + self.sess.struct_token_loc(token, self.prev_token), + )); + } else { + let elem_type = self.parse_type_annotation(); + let t = Type::List(ast::ListType { + inner_type: Some(elem_type), + }); + + self.bump_token(TokenKind::CloseDelim(DelimToken::Bracket)); + + return Box::new(Node::node( + t, + self.sess.struct_token_loc(token, self.prev_token), + )); + } + } + + // {key:value} + if let TokenKind::OpenDelim(DelimToken::Brace) = self.token.kind { + self.bump_token(TokenKind::OpenDelim(DelimToken::Brace)); + + let key_type = if let TokenKind::Colon = self.token.kind { + None + } else { + Some(self.parse_type_annotation()) + }; + + self.bump_token(TokenKind::Colon); + + let value_type = if let TokenKind::CloseDelim(DelimToken::Brace) = self.token.kind { + None + } else { + Some(self.parse_type_annotation()) + }; + + let t = Type::Dict(ast::DictType { + key_type, + value_type, + }); + + self.bump_token(TokenKind::CloseDelim(DelimToken::Brace)); + + return Box::new(Node::node( + t, + self.sess.struct_token_loc(token, self.prev_token), + )); + } + + panic!("invalid type token: {:?}", self.token); + } +} diff --git a/kclvm/parser/src/session/mod.rs b/kclvm/parser/src/session/mod.rs new file mode 100644 index 000000000..49835572e --- /dev/null +++ b/kclvm/parser/src/session/mod.rs @@ -0,0 +1,57 @@ +use kclvm_ast::token::Token; +use kclvm_error::{Handler, ParseError}; +use kclvm_span::{Loc, SourceMap, Span}; +use std::cell::RefCell; +use std::sync::Arc; + +pub struct ParseSession { + pub source_map: Arc, + pub handler: RefCell, +} + +impl ParseSession { + pub fn with_source_map(source_map: Arc) -> Self { + let handler = Handler::with_source_map(source_map.clone()).into(); + Self { + handler, + source_map, + } + } + + // Struct an loc of first and last valid tokens in an expr, returns a loc tuple + pub fn struct_token_loc(&self, lot: Token, hit: Token) -> (Loc, Loc) { + ( + self.source_map.lookup_char_pos(lot.span.lo()), + self.source_map.lookup_char_pos(hit.span.hi()), + ) + } + + /// Struct and report an error based on a token and abort the compiler process. + pub fn struct_token_error(&self, expected: &[&String], got: Token) -> ! { + let pos = self.source_map.lookup_char_pos(got.span.lo()).into(); + self.handler + .borrow_mut() + .add_parse_error( + ParseError::UnexpectedToken { + expected: expected.iter().map(|tok| (*tok).into()).collect(), + got: got.into(), + }, + pos, + ) + .abort_if_errors() + } + + /// Struct and report an error based on a span and abort the compiler process. + pub fn struct_span_error(&self, msg: &str, span: Span) -> ! { + let pos = self.source_map.lookup_char_pos(span.lo()).into(); + self.handler + .borrow_mut() + .add_syntex_error(msg, pos) + .abort_if_errors() + } + + /// Report a compiler bug + pub fn struct_compiler_bug(&self, msg: &str) -> ! { + self.handler.borrow_mut().bug(msg) + } +} diff --git a/kclvm/parser/src/tests.rs b/kclvm/parser/src/tests.rs new file mode 100644 index 000000000..3c000606b --- /dev/null +++ b/kclvm/parser/src/tests.rs @@ -0,0 +1,76 @@ +use std::fmt::format; + +use crate::*; + +use expect_test::{expect, Expect}; + +fn check_parsing_file_ast_json(filename: &str, src: &str, expect: Expect) { + let m = parse_file(filename, Some(src.into())); + let actual = serde_json::ser::to_string(&m).unwrap(); + let actual = format!("{}\n", actual); + expect.assert_eq(&actual) +} + +fn check_load_program_ast_json(files: &[&str], opts: Option, expect: Expect) { + let prog = load_program(&files, opts); + let actual = serde_json::ser::to_string(&prog).unwrap(); + let actual = format!("{}\n", actual); + expect.assert_eq(&actual) +} + +#[test] +fn test_parse_file() { + check_parsing_file_ast_json( + "hello.k", + r####"a=123"####, + expect![[r#" + {"filename":"hello.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assign":{"targets":[{"node":{"names":["a"],"pkgpath":"","ctx":"Store"},"filename":"hello.k","line":1,"column":0,"end_line":1,"end_column":1}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":123}}},"filename":"hello.k","line":1,"column":2,"end_line":1,"end_column":5},"type_annotation":null}},"filename":"hello.k","line":1,"column":0,"end_line":1,"end_column":5}],"comments":[]} + "#]], + ); +} + +#[test] +fn test_parse_if_stmt() { + check_parsing_file_ast_json( + "hello.k", + r####" +a = 10 +b = 12 +_condition = 0 +if a == 11 or b == 13: _condition = 1 +elif a == 10 and b == 12: _condition = 2 +condition = _condition + "####, + expect![[r#" + {"filename":"hello.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assign":{"targets":[{"node":{"names":["a"],"pkgpath":"","ctx":"Store"},"filename":"hello.k","line":2,"column":0,"end_line":2,"end_column":1}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":10}}},"filename":"hello.k","line":2,"column":4,"end_line":2,"end_column":6},"type_annotation":null}},"filename":"hello.k","line":2,"column":0,"end_line":3,"end_column":0},{"node":{"Assign":{"targets":[{"node":{"names":["b"],"pkgpath":"","ctx":"Store"},"filename":"hello.k","line":3,"column":0,"end_line":3,"end_column":1}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":12}}},"filename":"hello.k","line":3,"column":4,"end_line":3,"end_column":6},"type_annotation":null}},"filename":"hello.k","line":3,"column":0,"end_line":4,"end_column":0},{"node":{"Assign":{"targets":[{"node":{"names":["_condition"],"pkgpath":"","ctx":"Store"},"filename":"hello.k","line":4,"column":0,"end_line":4,"end_column":10}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":0}}},"filename":"hello.k","line":4,"column":13,"end_line":4,"end_column":14},"type_annotation":null}},"filename":"hello.k","line":4,"column":0,"end_line":5,"end_column":0},{"node":{"If":{"body":[{"node":{"Assign":{"targets":[{"node":{"names":["_condition"],"pkgpath":"","ctx":"Store"},"filename":"hello.k","line":5,"column":23,"end_line":5,"end_column":33}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"hello.k","line":5,"column":36,"end_line":5,"end_column":37},"type_annotation":null}},"filename":"hello.k","line":5,"column":23,"end_line":6,"end_column":0}],"cond":{"node":{"Binary":{"left":{"node":{"Compare":{"left":{"node":{"Identifier":{"names":["a"],"pkgpath":"","ctx":"Load"}},"filename":"hello.k","line":5,"column":3,"end_line":5,"end_column":4},"ops":["Eq"],"comparators":[{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":11}}},"filename":"hello.k","line":5,"column":8,"end_line":5,"end_column":10}]}},"filename":"hello.k","line":5,"column":3,"end_line":5,"end_column":21},"op":{"Bin":"Or"},"right":{"node":{"Compare":{"left":{"node":{"Identifier":{"names":["b"],"pkgpath":"","ctx":"Load"}},"filename":"hello.k","line":5,"column":14,"end_line":5,"end_column":15},"ops":["Eq"],"comparators":[{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":13}}},"filename":"hello.k","line":5,"column":19,"end_line":5,"end_column":21}]}},"filename":"hello.k","line":5,"column":14,"end_line":5,"end_column":21}}},"filename":"hello.k","line":5,"column":3,"end_line":5,"end_column":21},"orelse":[{"node":{"If":{"body":[{"node":{"Assign":{"targets":[{"node":{"names":["_condition"],"pkgpath":"","ctx":"Store"},"filename":"hello.k","line":6,"column":26,"end_line":6,"end_column":36}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":2}}},"filename":"hello.k","line":6,"column":39,"end_line":6,"end_column":40},"type_annotation":null}},"filename":"hello.k","line":6,"column":26,"end_line":7,"end_column":0}],"cond":{"node":{"Binary":{"left":{"node":{"Compare":{"left":{"node":{"Identifier":{"names":["a"],"pkgpath":"","ctx":"Load"}},"filename":"hello.k","line":6,"column":5,"end_line":6,"end_column":6},"ops":["Eq"],"comparators":[{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":10}}},"filename":"hello.k","line":6,"column":10,"end_line":6,"end_column":12}]}},"filename":"hello.k","line":6,"column":5,"end_line":6,"end_column":24},"op":{"Bin":"And"},"right":{"node":{"Compare":{"left":{"node":{"Identifier":{"names":["b"],"pkgpath":"","ctx":"Load"}},"filename":"hello.k","line":6,"column":17,"end_line":6,"end_column":18},"ops":["Eq"],"comparators":[{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":12}}},"filename":"hello.k","line":6,"column":22,"end_line":6,"end_column":24}]}},"filename":"hello.k","line":6,"column":17,"end_line":6,"end_column":24}}},"filename":"hello.k","line":6,"column":5,"end_line":6,"end_column":24},"orelse":[]}},"filename":"hello.k","line":6,"column":0,"end_line":7,"end_column":0}]}},"filename":"hello.k","line":5,"column":0,"end_line":7,"end_column":0},{"node":{"Assign":{"targets":[{"node":{"names":["condition"],"pkgpath":"","ctx":"Store"},"filename":"hello.k","line":7,"column":0,"end_line":7,"end_column":9}],"value":{"node":{"Identifier":{"names":["_condition"],"pkgpath":"","ctx":"Load"}},"filename":"hello.k","line":7,"column":12,"end_line":7,"end_column":22},"type_annotation":null}},"filename":"hello.k","line":7,"column":0,"end_line":8,"end_column":0}],"comments":[]} + "#]], + ); + + check_parsing_file_ast_json( + "hello.k", + r####" +data2 = { + **{key = "value1"} + if a == 123: if b == 456: key = "value2" +} + "####, + expect![[r#" + {"filename":"hello.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assign":{"targets":[{"node":{"names":["data2"],"pkgpath":"","ctx":"Store"},"filename":"hello.k","line":2,"column":0,"end_line":2,"end_column":5}],"value":{"node":{"Config":{"items":[{"node":{"key":null,"value":{"node":{"Config":{"items":[{"node":{"key":{"node":{"Identifier":{"names":["key"],"pkgpath":"","ctx":"Load"}},"filename":"hello.k","line":3,"column":7,"end_line":3,"end_column":10},"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"value1\"","value":"value1"}},"filename":"hello.k","line":3,"column":13,"end_line":3,"end_column":21},"operation":"Override","insert_index":-1},"filename":"hello.k","line":3,"column":7,"end_line":3,"end_column":21}]}},"filename":"hello.k","line":3,"column":6,"end_line":3,"end_column":22},"operation":"Union","insert_index":-1},"filename":"hello.k","line":3,"column":4,"end_line":3,"end_column":22},{"node":{"key":null,"value":{"node":{"ConfigIfEntry":{"if_cond":{"node":{"Compare":{"left":{"node":{"Identifier":{"names":["a"],"pkgpath":"","ctx":"Load"}},"filename":"hello.k","line":4,"column":7,"end_line":4,"end_column":8},"ops":["Eq"],"comparators":[{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":123}}},"filename":"hello.k","line":4,"column":12,"end_line":4,"end_column":15}]}},"filename":"hello.k","line":4,"column":7,"end_line":4,"end_column":15},"items":[{"node":{"key":null,"value":{"node":{"ConfigIfEntry":{"if_cond":{"node":{"Compare":{"left":{"node":{"Identifier":{"names":["b"],"pkgpath":"","ctx":"Load"}},"filename":"hello.k","line":4,"column":20,"end_line":4,"end_column":21},"ops":["Eq"],"comparators":[{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":456}}},"filename":"hello.k","line":4,"column":25,"end_line":4,"end_column":28}]}},"filename":"hello.k","line":4,"column":20,"end_line":4,"end_column":28},"items":[{"node":{"key":{"node":{"Identifier":{"names":["key"],"pkgpath":"","ctx":"Load"}},"filename":"hello.k","line":4,"column":30,"end_line":4,"end_column":33},"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"value2\"","value":"value2"}},"filename":"hello.k","line":4,"column":36,"end_line":4,"end_column":44},"operation":"Override","insert_index":-1},"filename":"hello.k","line":4,"column":36,"end_line":4,"end_column":44}],"orelse":null}},"filename":"hello.k","line":4,"column":30,"end_line":5,"end_column":0},"operation":"Override","insert_index":-1},"filename":"hello.k","line":4,"column":30,"end_line":5,"end_column":0}],"orelse":null}},"filename":"hello.k","line":4,"column":17,"end_line":5,"end_column":0},"operation":"Union","insert_index":-1},"filename":"hello.k","line":4,"column":4,"end_line":5,"end_column":0}]}},"filename":"hello.k","line":2,"column":8,"end_line":5,"end_column":1},"type_annotation":null}},"filename":"hello.k","line":2,"column":0,"end_line":6,"end_column":0}],"comments":[]} + "#]], + ); + + check_parsing_file_ast_json( + "hello.k", + r####" +# comment1 +a = 1 +# comment22 +b = 2 +# comment333 +c = 3 # comment4444 + "####, + expect![[r###" + {"filename":"hello.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assign":{"targets":[{"node":{"names":["a"],"pkgpath":"","ctx":"Store"},"filename":"hello.k","line":3,"column":0,"end_line":3,"end_column":1}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"hello.k","line":3,"column":4,"end_line":3,"end_column":5},"type_annotation":null}},"filename":"hello.k","line":3,"column":0,"end_line":5,"end_column":0},{"node":{"Assign":{"targets":[{"node":{"names":["b"],"pkgpath":"","ctx":"Store"},"filename":"hello.k","line":5,"column":0,"end_line":5,"end_column":1}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":2}}},"filename":"hello.k","line":5,"column":4,"end_line":5,"end_column":5},"type_annotation":null}},"filename":"hello.k","line":5,"column":0,"end_line":7,"end_column":0},{"node":{"Assign":{"targets":[{"node":{"names":["c"],"pkgpath":"","ctx":"Store"},"filename":"hello.k","line":7,"column":0,"end_line":7,"end_column":1}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":3}}},"filename":"hello.k","line":7,"column":4,"end_line":7,"end_column":5},"type_annotation":null}},"filename":"hello.k","line":7,"column":0,"end_line":8,"end_column":0}],"comments":[{"node":{"text":"# comment1"},"filename":"hello.k","line":2,"column":0,"end_line":2,"end_column":10},{"node":{"text":"# comment22"},"filename":"hello.k","line":4,"column":0,"end_line":4,"end_column":11},{"node":{"text":"# comment333"},"filename":"hello.k","line":6,"column":0,"end_line":6,"end_column":12},{"node":{"text":"# comment4444"},"filename":"hello.k","line":7,"column":6,"end_line":7,"end_column":19}]} + "###]], + ); +} diff --git a/kclvm/parser/testdata/Makefile b/kclvm/parser/testdata/Makefile new file mode 100644 index 000000000..ac278ac6b --- /dev/null +++ b/kclvm/parser/testdata/Makefile @@ -0,0 +1,13 @@ + +K_FILES=$(wildcard ./*.k) +K_JSON_FILES=$(patsubst %.k,%.k.json,$(K_FILES)) + +default: clean $(K_JSON_FILES) $(K_FMT_JSON_FILES) + @echo "ok" + +clean: + -rm *.k.json + +%.k.json: %.k + cargo run --bin parse_file $< > $@ + diff --git a/kclvm/parser/testdata/a1.k b/kclvm/parser/testdata/a1.k new file mode 100644 index 000000000..e69de29bb diff --git a/kclvm/parser/testdata/a1.k.json b/kclvm/parser/testdata/a1.k.json new file mode 100644 index 000000000..3b4674ab1 --- /dev/null +++ b/kclvm/parser/testdata/a1.k.json @@ -0,0 +1 @@ +{"filename":"a1.k","pkg":"__main__","doc":"","name":"__main__","body":[],"comments":[]} diff --git a/kclvm/parser/testdata/a2.k b/kclvm/parser/testdata/a2.k new file mode 100644 index 000000000..e69de29bb diff --git a/kclvm/parser/testdata/a2.k.json b/kclvm/parser/testdata/a2.k.json new file mode 100644 index 000000000..9de81155a --- /dev/null +++ b/kclvm/parser/testdata/a2.k.json @@ -0,0 +1 @@ +{"filename":"a2.k","pkg":"__main__","doc":"","name":"__main__","body":[],"comments":[]} diff --git a/kclvm/parser/testdata/a3.k b/kclvm/parser/testdata/a3.k new file mode 100644 index 000000000..e69de29bb diff --git a/kclvm/parser/testdata/a3.k.json b/kclvm/parser/testdata/a3.k.json new file mode 100644 index 000000000..70af70275 --- /dev/null +++ b/kclvm/parser/testdata/a3.k.json @@ -0,0 +1 @@ +{"filename":"a3.k","pkg":"__main__","doc":"","name":"__main__","body":[],"comments":[]} diff --git a/kclvm/parser/testdata/assert-01.k b/kclvm/parser/testdata/assert-01.k new file mode 100644 index 000000000..9b70ec7e1 --- /dev/null +++ b/kclvm/parser/testdata/assert-01.k @@ -0,0 +1,3 @@ +assert 1 +assert 2, "msg" +assert True diff --git a/kclvm/parser/testdata/assert-01.k.json b/kclvm/parser/testdata/assert-01.k.json new file mode 100644 index 000000000..75b5a5f19 --- /dev/null +++ b/kclvm/parser/testdata/assert-01.k.json @@ -0,0 +1 @@ +{"filename":"assert-01.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assert":{"test":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assert-01.k","line":1,"column":7,"end_line":1,"end_column":8},"if_cond":null,"msg":null}},"filename":"assert-01.k","line":1,"column":0,"end_line":1,"end_column":8},{"node":{"Assert":{"test":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":2}}},"filename":"assert-01.k","line":2,"column":7,"end_line":2,"end_column":8},"if_cond":null,"msg":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"msg\"","value":"msg"}},"filename":"assert-01.k","line":2,"column":10,"end_line":2,"end_column":15}}},"filename":"assert-01.k","line":2,"column":0,"end_line":2,"end_column":15},{"node":{"Assert":{"test":{"node":{"NameConstantLit":{"value":"True"}},"filename":"assert-01.k","line":3,"column":7,"end_line":3,"end_column":11},"if_cond":null,"msg":null}},"filename":"assert-01.k","line":3,"column":0,"end_line":3,"end_column":11}],"comments":[]} diff --git a/kclvm/parser/testdata/assert-02.k b/kclvm/parser/testdata/assert-02.k new file mode 100644 index 000000000..6665b9613 --- /dev/null +++ b/kclvm/parser/testdata/assert-02.k @@ -0,0 +1,4 @@ +assert True +assert 1 == 1 +_x = "good case" +assert _x == "good case" diff --git a/kclvm/parser/testdata/assert-02.k.json b/kclvm/parser/testdata/assert-02.k.json new file mode 100644 index 000000000..4f792f2de --- /dev/null +++ b/kclvm/parser/testdata/assert-02.k.json @@ -0,0 +1 @@ +{"filename":"assert-02.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assert":{"test":{"node":{"NameConstantLit":{"value":"True"}},"filename":"assert-02.k","line":1,"column":7,"end_line":1,"end_column":11},"if_cond":null,"msg":null}},"filename":"assert-02.k","line":1,"column":0,"end_line":1,"end_column":11},{"node":{"Assert":{"test":{"node":{"Compare":{"left":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assert-02.k","line":2,"column":7,"end_line":2,"end_column":8},"ops":["Eq"],"comparators":[{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assert-02.k","line":2,"column":12,"end_line":2,"end_column":13}]}},"filename":"assert-02.k","line":2,"column":7,"end_line":2,"end_column":13},"if_cond":null,"msg":null}},"filename":"assert-02.k","line":2,"column":0,"end_line":2,"end_column":13},{"node":{"Assign":{"targets":[{"node":{"names":["_x"],"pkgpath":"","ctx":"Store"},"filename":"assert-02.k","line":3,"column":0,"end_line":3,"end_column":2}],"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"good case\"","value":"good case"}},"filename":"assert-02.k","line":3,"column":5,"end_line":3,"end_column":16},"type_annotation":null}},"filename":"assert-02.k","line":3,"column":0,"end_line":4,"end_column":0},{"node":{"Assert":{"test":{"node":{"Compare":{"left":{"node":{"Identifier":{"names":["_x"],"pkgpath":"","ctx":"Load"}},"filename":"assert-02.k","line":4,"column":7,"end_line":4,"end_column":9},"ops":["Eq"],"comparators":[{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"good case\"","value":"good case"}},"filename":"assert-02.k","line":4,"column":13,"end_line":4,"end_column":24}]}},"filename":"assert-02.k","line":4,"column":7,"end_line":4,"end_column":24},"if_cond":null,"msg":null}},"filename":"assert-02.k","line":4,"column":0,"end_line":4,"end_column":24}],"comments":[]} diff --git a/kclvm/parser/testdata/assert-03.k b/kclvm/parser/testdata/assert-03.k new file mode 100644 index 000000000..1dfe9071d --- /dev/null +++ b/kclvm/parser/testdata/assert-03.k @@ -0,0 +1,8 @@ +if True: + assert True, "Error messgae" + assert 1 == 1, "" + _x = "good case" + assert _x == "good case" +else: + assert False + diff --git a/kclvm/parser/testdata/assert-03.k.json b/kclvm/parser/testdata/assert-03.k.json new file mode 100644 index 000000000..454097999 --- /dev/null +++ b/kclvm/parser/testdata/assert-03.k.json @@ -0,0 +1 @@ +{"filename":"assert-03.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"If":{"body":[{"node":{"Assert":{"test":{"node":{"NameConstantLit":{"value":"True"}},"filename":"assert-03.k","line":2,"column":11,"end_line":2,"end_column":15},"if_cond":null,"msg":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"Error messgae\"","value":"Error messgae"}},"filename":"assert-03.k","line":2,"column":17,"end_line":2,"end_column":32}}},"filename":"assert-03.k","line":2,"column":4,"end_line":2,"end_column":32},{"node":{"Assert":{"test":{"node":{"Compare":{"left":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assert-03.k","line":3,"column":11,"end_line":3,"end_column":12},"ops":["Eq"],"comparators":[{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assert-03.k","line":3,"column":16,"end_line":3,"end_column":17}]}},"filename":"assert-03.k","line":3,"column":11,"end_line":3,"end_column":17},"if_cond":null,"msg":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"\"","value":""}},"filename":"assert-03.k","line":3,"column":19,"end_line":3,"end_column":21}}},"filename":"assert-03.k","line":3,"column":4,"end_line":3,"end_column":21},{"node":{"Assign":{"targets":[{"node":{"names":["_x"],"pkgpath":"","ctx":"Store"},"filename":"assert-03.k","line":4,"column":4,"end_line":4,"end_column":6}],"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"good case\"","value":"good case"}},"filename":"assert-03.k","line":4,"column":9,"end_line":4,"end_column":20},"type_annotation":null}},"filename":"assert-03.k","line":4,"column":4,"end_line":5,"end_column":0},{"node":{"Assert":{"test":{"node":{"Compare":{"left":{"node":{"Identifier":{"names":["_x"],"pkgpath":"","ctx":"Load"}},"filename":"assert-03.k","line":5,"column":11,"end_line":5,"end_column":13},"ops":["Eq"],"comparators":[{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"good case\"","value":"good case"}},"filename":"assert-03.k","line":5,"column":17,"end_line":5,"end_column":28}]}},"filename":"assert-03.k","line":5,"column":11,"end_line":5,"end_column":28},"if_cond":null,"msg":null}},"filename":"assert-03.k","line":5,"column":4,"end_line":5,"end_column":28}],"cond":{"node":{"NameConstantLit":{"value":"True"}},"filename":"assert-03.k","line":1,"column":3,"end_line":1,"end_column":7},"orelse":[{"node":{"Assert":{"test":{"node":{"NameConstantLit":{"value":"False"}},"filename":"assert-03.k","line":7,"column":11,"end_line":7,"end_column":16},"if_cond":null,"msg":null}},"filename":"assert-03.k","line":7,"column":4,"end_line":7,"end_column":16}]}},"filename":"assert-03.k","line":1,"column":0,"end_line":8,"end_column":1}],"comments":[]} diff --git a/kclvm/parser/testdata/assert-if-0.k b/kclvm/parser/testdata/assert-if-0.k new file mode 100644 index 000000000..90bcccbb8 --- /dev/null +++ b/kclvm/parser/testdata/assert-if-0.k @@ -0,0 +1,4 @@ +assert True +assert 1 == 1 if True +_x = "good case" +assert _x == "good case" if _x, "_x need to be 'good case'" diff --git a/kclvm/parser/testdata/assert-if-0.k.json b/kclvm/parser/testdata/assert-if-0.k.json new file mode 100644 index 000000000..e539c0e4c --- /dev/null +++ b/kclvm/parser/testdata/assert-if-0.k.json @@ -0,0 +1 @@ +{"filename":"assert-if-0.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assert":{"test":{"node":{"NameConstantLit":{"value":"True"}},"filename":"assert-if-0.k","line":1,"column":7,"end_line":1,"end_column":11},"if_cond":null,"msg":null}},"filename":"assert-if-0.k","line":1,"column":0,"end_line":1,"end_column":11},{"node":{"Assert":{"test":{"node":{"Compare":{"left":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assert-if-0.k","line":2,"column":7,"end_line":2,"end_column":8},"ops":["Eq"],"comparators":[{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assert-if-0.k","line":2,"column":12,"end_line":2,"end_column":13}]}},"filename":"assert-if-0.k","line":2,"column":7,"end_line":2,"end_column":13},"if_cond":{"node":{"NameConstantLit":{"value":"True"}},"filename":"assert-if-0.k","line":2,"column":17,"end_line":2,"end_column":21},"msg":null}},"filename":"assert-if-0.k","line":2,"column":0,"end_line":2,"end_column":21},{"node":{"Assign":{"targets":[{"node":{"names":["_x"],"pkgpath":"","ctx":"Store"},"filename":"assert-if-0.k","line":3,"column":0,"end_line":3,"end_column":2}],"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"good case\"","value":"good case"}},"filename":"assert-if-0.k","line":3,"column":5,"end_line":3,"end_column":16},"type_annotation":null}},"filename":"assert-if-0.k","line":3,"column":0,"end_line":4,"end_column":0},{"node":{"Assert":{"test":{"node":{"Compare":{"left":{"node":{"Identifier":{"names":["_x"],"pkgpath":"","ctx":"Load"}},"filename":"assert-if-0.k","line":4,"column":7,"end_line":4,"end_column":9},"ops":["Eq"],"comparators":[{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"good case\"","value":"good case"}},"filename":"assert-if-0.k","line":4,"column":13,"end_line":4,"end_column":24}]}},"filename":"assert-if-0.k","line":4,"column":7,"end_line":4,"end_column":24},"if_cond":{"node":{"Identifier":{"names":["_x"],"pkgpath":"","ctx":"Load"}},"filename":"assert-if-0.k","line":4,"column":28,"end_line":4,"end_column":30},"msg":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"_x need to be 'good case'\"","value":"_x need to be 'good case'"}},"filename":"assert-if-0.k","line":4,"column":32,"end_line":4,"end_column":59}}},"filename":"assert-if-0.k","line":4,"column":0,"end_line":4,"end_column":59}],"comments":[]} diff --git a/kclvm/parser/testdata/assert-if-1.k b/kclvm/parser/testdata/assert-if-1.k new file mode 100644 index 000000000..123910a7d --- /dev/null +++ b/kclvm/parser/testdata/assert-if-1.k @@ -0,0 +1,4 @@ +assert True if False +assert 1 == 1 if False +_x = "good case" +assert _x == "good case" if not _x, "_x need to be 'good case'" diff --git a/kclvm/parser/testdata/assert-if-1.k.json b/kclvm/parser/testdata/assert-if-1.k.json new file mode 100644 index 000000000..a93760fdc --- /dev/null +++ b/kclvm/parser/testdata/assert-if-1.k.json @@ -0,0 +1 @@ +{"filename":"assert-if-1.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assert":{"test":{"node":{"NameConstantLit":{"value":"True"}},"filename":"assert-if-1.k","line":1,"column":7,"end_line":1,"end_column":11},"if_cond":{"node":{"NameConstantLit":{"value":"False"}},"filename":"assert-if-1.k","line":1,"column":15,"end_line":1,"end_column":20},"msg":null}},"filename":"assert-if-1.k","line":1,"column":0,"end_line":1,"end_column":20},{"node":{"Assert":{"test":{"node":{"Compare":{"left":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assert-if-1.k","line":2,"column":7,"end_line":2,"end_column":8},"ops":["Eq"],"comparators":[{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assert-if-1.k","line":2,"column":12,"end_line":2,"end_column":13}]}},"filename":"assert-if-1.k","line":2,"column":7,"end_line":2,"end_column":13},"if_cond":{"node":{"NameConstantLit":{"value":"False"}},"filename":"assert-if-1.k","line":2,"column":17,"end_line":2,"end_column":22},"msg":null}},"filename":"assert-if-1.k","line":2,"column":0,"end_line":2,"end_column":22},{"node":{"Assign":{"targets":[{"node":{"names":["_x"],"pkgpath":"","ctx":"Store"},"filename":"assert-if-1.k","line":3,"column":0,"end_line":3,"end_column":2}],"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"good case\"","value":"good case"}},"filename":"assert-if-1.k","line":3,"column":5,"end_line":3,"end_column":16},"type_annotation":null}},"filename":"assert-if-1.k","line":3,"column":0,"end_line":4,"end_column":0},{"node":{"Assert":{"test":{"node":{"Compare":{"left":{"node":{"Identifier":{"names":["_x"],"pkgpath":"","ctx":"Load"}},"filename":"assert-if-1.k","line":4,"column":7,"end_line":4,"end_column":9},"ops":["Eq"],"comparators":[{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"good case\"","value":"good case"}},"filename":"assert-if-1.k","line":4,"column":13,"end_line":4,"end_column":24}]}},"filename":"assert-if-1.k","line":4,"column":7,"end_line":4,"end_column":24},"if_cond":{"node":{"Unary":{"op":"Not","operand":{"node":{"Identifier":{"names":["_x"],"pkgpath":"","ctx":"Load"}},"filename":"assert-if-1.k","line":4,"column":32,"end_line":4,"end_column":34}}},"filename":"assert-if-1.k","line":4,"column":28,"end_line":4,"end_column":34},"msg":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"_x need to be 'good case'\"","value":"_x need to be 'good case'"}},"filename":"assert-if-1.k","line":4,"column":36,"end_line":4,"end_column":63}}},"filename":"assert-if-1.k","line":4,"column":0,"end_line":4,"end_column":63}],"comments":[]} diff --git a/kclvm/parser/testdata/assert-if-2.k b/kclvm/parser/testdata/assert-if-2.k new file mode 100644 index 000000000..f80453656 --- /dev/null +++ b/kclvm/parser/testdata/assert-if-2.k @@ -0,0 +1,7 @@ +schema Data: + assert True if False + assert 1 == 1 if False + _x = "good case" + assert _x == "good case" if not _x, "_x need to be 'good case'" + +data = Data {} diff --git a/kclvm/parser/testdata/assert-if-2.k.json b/kclvm/parser/testdata/assert-if-2.k.json new file mode 100644 index 000000000..3a5c70faf --- /dev/null +++ b/kclvm/parser/testdata/assert-if-2.k.json @@ -0,0 +1 @@ +{"filename":"assert-if-2.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Schema":{"doc":"","name":{"node":"Data","filename":"assert-if-2.k","line":1,"column":7,"end_line":1,"end_column":11},"parent_name":null,"for_host_name":null,"is_mixin":false,"is_protocol":false,"args":null,"mixins":[],"body":[{"node":{"Assert":{"test":{"node":{"NameConstantLit":{"value":"True"}},"filename":"assert-if-2.k","line":2,"column":11,"end_line":2,"end_column":15},"if_cond":{"node":{"NameConstantLit":{"value":"False"}},"filename":"assert-if-2.k","line":2,"column":19,"end_line":2,"end_column":24},"msg":null}},"filename":"assert-if-2.k","line":2,"column":4,"end_line":2,"end_column":24},{"node":{"Assert":{"test":{"node":{"Compare":{"left":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assert-if-2.k","line":3,"column":11,"end_line":3,"end_column":12},"ops":["Eq"],"comparators":[{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assert-if-2.k","line":3,"column":16,"end_line":3,"end_column":17}]}},"filename":"assert-if-2.k","line":3,"column":11,"end_line":3,"end_column":17},"if_cond":{"node":{"NameConstantLit":{"value":"False"}},"filename":"assert-if-2.k","line":3,"column":21,"end_line":3,"end_column":26},"msg":null}},"filename":"assert-if-2.k","line":3,"column":4,"end_line":3,"end_column":26},{"node":{"Assign":{"targets":[{"node":{"names":["_x"],"pkgpath":"","ctx":"Store"},"filename":"assert-if-2.k","line":4,"column":4,"end_line":4,"end_column":6}],"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"good case\"","value":"good case"}},"filename":"assert-if-2.k","line":4,"column":9,"end_line":4,"end_column":20},"type_annotation":null}},"filename":"assert-if-2.k","line":4,"column":4,"end_line":5,"end_column":0},{"node":{"Assert":{"test":{"node":{"Compare":{"left":{"node":{"Identifier":{"names":["_x"],"pkgpath":"","ctx":"Load"}},"filename":"assert-if-2.k","line":5,"column":11,"end_line":5,"end_column":13},"ops":["Eq"],"comparators":[{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"good case\"","value":"good case"}},"filename":"assert-if-2.k","line":5,"column":17,"end_line":5,"end_column":28}]}},"filename":"assert-if-2.k","line":5,"column":11,"end_line":5,"end_column":28},"if_cond":{"node":{"Unary":{"op":"Not","operand":{"node":{"Identifier":{"names":["_x"],"pkgpath":"","ctx":"Load"}},"filename":"assert-if-2.k","line":5,"column":36,"end_line":5,"end_column":38}}},"filename":"assert-if-2.k","line":5,"column":32,"end_line":5,"end_column":38},"msg":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"_x need to be 'good case'\"","value":"_x need to be 'good case'"}},"filename":"assert-if-2.k","line":5,"column":40,"end_line":5,"end_column":67}}},"filename":"assert-if-2.k","line":5,"column":4,"end_line":5,"end_column":67}],"decorators":[],"checks":[],"index_signature":null}},"filename":"assert-if-2.k","line":1,"column":0,"end_line":7,"end_column":0},{"node":{"Assign":{"targets":[{"node":{"names":["data"],"pkgpath":"","ctx":"Store"},"filename":"assert-if-2.k","line":7,"column":0,"end_line":7,"end_column":4}],"value":{"node":{"Schema":{"name":{"node":{"names":["Data"],"pkgpath":"","ctx":"Load"},"filename":"assert-if-2.k","line":7,"column":7,"end_line":7,"end_column":11},"args":[],"kwargs":[],"config":{"node":{"Config":{"items":[]}},"filename":"assert-if-2.k","line":7,"column":12,"end_line":7,"end_column":14}}},"filename":"assert-if-2.k","line":7,"column":7,"end_line":7,"end_column":14},"type_annotation":null}},"filename":"assert-if-2.k","line":7,"column":0,"end_line":7,"end_column":15}],"comments":[]} diff --git a/kclvm/parser/testdata/assign-01.k b/kclvm/parser/testdata/assign-01.k new file mode 100644 index 000000000..a044863e7 --- /dev/null +++ b/kclvm/parser/testdata/assign-01.k @@ -0,0 +1,3 @@ +a=1 +b = 1 + 2 +c = 2 + 2*3 diff --git a/kclvm/parser/testdata/assign-01.k.json b/kclvm/parser/testdata/assign-01.k.json new file mode 100644 index 000000000..5ba151537 --- /dev/null +++ b/kclvm/parser/testdata/assign-01.k.json @@ -0,0 +1 @@ +{"filename":"assign-01.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assign":{"targets":[{"node":{"names":["a"],"pkgpath":"","ctx":"Store"},"filename":"assign-01.k","line":1,"column":0,"end_line":1,"end_column":1}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assign-01.k","line":1,"column":2,"end_line":1,"end_column":3},"type_annotation":null}},"filename":"assign-01.k","line":1,"column":0,"end_line":2,"end_column":0},{"node":{"Assign":{"targets":[{"node":{"names":["b"],"pkgpath":"","ctx":"Store"},"filename":"assign-01.k","line":2,"column":0,"end_line":2,"end_column":1}],"value":{"node":{"Binary":{"left":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"assign-01.k","line":2,"column":4,"end_line":2,"end_column":5},"op":{"Bin":"Add"},"right":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":2}}},"filename":"assign-01.k","line":2,"column":8,"end_line":2,"end_column":9}}},"filename":"assign-01.k","line":2,"column":4,"end_line":2,"end_column":9},"type_annotation":null}},"filename":"assign-01.k","line":2,"column":0,"end_line":3,"end_column":0},{"node":{"Assign":{"targets":[{"node":{"names":["c"],"pkgpath":"","ctx":"Store"},"filename":"assign-01.k","line":3,"column":0,"end_line":3,"end_column":1}],"value":{"node":{"Binary":{"left":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":2}}},"filename":"assign-01.k","line":3,"column":4,"end_line":3,"end_column":5},"op":{"Bin":"Add"},"right":{"node":{"Binary":{"left":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":2}}},"filename":"assign-01.k","line":3,"column":8,"end_line":3,"end_column":9},"op":{"Bin":"Mul"},"right":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":3}}},"filename":"assign-01.k","line":3,"column":10,"end_line":3,"end_column":11}}},"filename":"assign-01.k","line":3,"column":8,"end_line":3,"end_column":11}}},"filename":"assign-01.k","line":3,"column":4,"end_line":3,"end_column":11},"type_annotation":null}},"filename":"assign-01.k","line":3,"column":0,"end_line":3,"end_column":12}],"comments":[]} diff --git a/kclvm/parser/testdata/config_expr-01.k b/kclvm/parser/testdata/config_expr-01.k new file mode 100644 index 000000000..8403f7553 --- /dev/null +++ b/kclvm/parser/testdata/config_expr-01.k @@ -0,0 +1,2 @@ +config = { +} diff --git a/kclvm/parser/testdata/config_expr-01.k.json b/kclvm/parser/testdata/config_expr-01.k.json new file mode 100644 index 000000000..69b72efe8 --- /dev/null +++ b/kclvm/parser/testdata/config_expr-01.k.json @@ -0,0 +1 @@ +{"filename":"config_expr-01.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assign":{"targets":[{"node":{"names":["config"],"pkgpath":"","ctx":"Store"},"filename":"config_expr-01.k","line":1,"column":0,"end_line":1,"end_column":6}],"value":{"node":{"Config":{"items":[]}},"filename":"config_expr-01.k","line":1,"column":9,"end_line":2,"end_column":1},"type_annotation":null}},"filename":"config_expr-01.k","line":1,"column":0,"end_line":2,"end_column":2}],"comments":[]} diff --git a/kclvm/parser/testdata/config_expr-02.k b/kclvm/parser/testdata/config_expr-02.k new file mode 100644 index 000000000..f830e360a --- /dev/null +++ b/kclvm/parser/testdata/config_expr-02.k @@ -0,0 +1,4 @@ +config = { + k1 = 111 + k2 = 222 +} diff --git a/kclvm/parser/testdata/config_expr-02.k.json b/kclvm/parser/testdata/config_expr-02.k.json new file mode 100644 index 000000000..a53af3a1b --- /dev/null +++ b/kclvm/parser/testdata/config_expr-02.k.json @@ -0,0 +1 @@ +{"filename":"config_expr-02.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assign":{"targets":[{"node":{"names":["config"],"pkgpath":"","ctx":"Store"},"filename":"config_expr-02.k","line":1,"column":0,"end_line":1,"end_column":6}],"value":{"node":{"Config":{"items":[{"node":{"key":{"node":{"Identifier":{"names":["k1"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-02.k","line":2,"column":4,"end_line":2,"end_column":6},"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":111}}},"filename":"config_expr-02.k","line":2,"column":9,"end_line":2,"end_column":12},"operation":"Override","insert_index":-1},"filename":"config_expr-02.k","line":2,"column":4,"end_line":2,"end_column":12},{"node":{"key":{"node":{"Identifier":{"names":["k2"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-02.k","line":3,"column":4,"end_line":3,"end_column":6},"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":222}}},"filename":"config_expr-02.k","line":3,"column":9,"end_line":3,"end_column":12},"operation":"Override","insert_index":-1},"filename":"config_expr-02.k","line":3,"column":4,"end_line":3,"end_column":12}]}},"filename":"config_expr-02.k","line":1,"column":9,"end_line":4,"end_column":1},"type_annotation":null}},"filename":"config_expr-02.k","line":1,"column":0,"end_line":4,"end_column":2}],"comments":[]} diff --git a/kclvm/parser/testdata/config_expr-03.k b/kclvm/parser/testdata/config_expr-03.k new file mode 100644 index 000000000..d5fd10549 --- /dev/null +++ b/kclvm/parser/testdata/config_expr-03.k @@ -0,0 +1,14 @@ +# test/grammar/attr_operator/config_inside/insert/dict_0/main.k + +config = { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: { + env += [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/kclvm/parser/testdata/config_expr-03.k.json b/kclvm/parser/testdata/config_expr-03.k.json new file mode 100644 index 000000000..ae716b613 --- /dev/null +++ b/kclvm/parser/testdata/config_expr-03.k.json @@ -0,0 +1 @@ +{"filename":"config_expr-03.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assign":{"targets":[{"node":{"names":["config"],"pkgpath":"","ctx":"Store"},"filename":"config_expr-03.k","line":3,"column":0,"end_line":3,"end_column":6}],"value":{"node":{"Config":{"items":[{"node":{"key":{"node":{"Identifier":{"names":["main"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-03.k","line":4,"column":4,"end_line":4,"end_column":8},"value":{"node":{"Config":{"items":[{"node":{"key":{"node":{"Identifier":{"names":["env"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-03.k","line":5,"column":8,"end_line":5,"end_column":11},"value":{"node":{"List":{"elts":[{"node":{"Config":{"items":[{"node":{"key":{"node":{"Identifier":{"names":["name"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-03.k","line":6,"column":13,"end_line":6,"end_column":17},"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"ENV_1\"","value":"ENV_1"}},"filename":"config_expr-03.k","line":6,"column":19,"end_line":6,"end_column":26},"operation":"Union","insert_index":-1},"filename":"config_expr-03.k","line":6,"column":13,"end_line":6,"end_column":26},{"node":{"key":{"node":{"Identifier":{"names":["value"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-03.k","line":6,"column":28,"end_line":6,"end_column":33},"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"1\"","value":"1"}},"filename":"config_expr-03.k","line":6,"column":35,"end_line":6,"end_column":38},"operation":"Union","insert_index":-1},"filename":"config_expr-03.k","line":6,"column":28,"end_line":6,"end_column":38}]}},"filename":"config_expr-03.k","line":6,"column":12,"end_line":6,"end_column":39}],"ctx":"Load"}},"filename":"config_expr-03.k","line":5,"column":13,"end_line":7,"end_column":9},"operation":"Union","insert_index":-1},"filename":"config_expr-03.k","line":5,"column":8,"end_line":7,"end_column":9}]}},"filename":"config_expr-03.k","line":4,"column":10,"end_line":8,"end_column":5},"operation":"Union","insert_index":-1},"filename":"config_expr-03.k","line":4,"column":4,"end_line":8,"end_column":5},{"node":{"key":{"node":{"Identifier":{"names":["main"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-03.k","line":9,"column":4,"end_line":9,"end_column":8},"value":{"node":{"Config":{"items":[{"node":{"key":{"node":{"Identifier":{"names":["env"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-03.k","line":10,"column":8,"end_line":10,"end_column":11},"value":{"node":{"List":{"elts":[{"node":{"Config":{"items":[{"node":{"key":{"node":{"Identifier":{"names":["name"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-03.k","line":11,"column":13,"end_line":11,"end_column":17},"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"ENV_2\"","value":"ENV_2"}},"filename":"config_expr-03.k","line":11,"column":19,"end_line":11,"end_column":26},"operation":"Union","insert_index":-1},"filename":"config_expr-03.k","line":11,"column":13,"end_line":11,"end_column":26},{"node":{"key":{"node":{"Identifier":{"names":["value"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-03.k","line":11,"column":28,"end_line":11,"end_column":33},"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"2\"","value":"2"}},"filename":"config_expr-03.k","line":11,"column":35,"end_line":11,"end_column":38},"operation":"Union","insert_index":-1},"filename":"config_expr-03.k","line":11,"column":28,"end_line":11,"end_column":38}]}},"filename":"config_expr-03.k","line":11,"column":12,"end_line":11,"end_column":39}],"ctx":"Load"}},"filename":"config_expr-03.k","line":10,"column":15,"end_line":12,"end_column":9},"operation":"Insert","insert_index":-1},"filename":"config_expr-03.k","line":10,"column":8,"end_line":12,"end_column":9}]}},"filename":"config_expr-03.k","line":9,"column":10,"end_line":13,"end_column":5},"operation":"Union","insert_index":-1},"filename":"config_expr-03.k","line":9,"column":4,"end_line":13,"end_column":5}]}},"filename":"config_expr-03.k","line":3,"column":9,"end_line":14,"end_column":1},"type_annotation":null}},"filename":"config_expr-03.k","line":3,"column":0,"end_line":14,"end_column":2}],"comments":[{"node":{"text":"# test/grammar/attr_operator/config_inside/insert/dict_0/main.k"},"filename":"config_expr-03.k","line":1,"column":0,"end_line":1,"end_column":63}]} diff --git a/kclvm/parser/testdata/config_expr-04.k b/kclvm/parser/testdata/config_expr-04.k new file mode 100644 index 000000000..99335a27e --- /dev/null +++ b/kclvm/parser/testdata/config_expr-04.k @@ -0,0 +1,26 @@ +# test/grammar/attr_operator/config_inside/insert/schema_0/main.k + +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +_main = Main { + env: [ + {name: "ENV_1", value: "1"} + ] +} + +config = Config { + main: _main + main: Main { + env += [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/kclvm/parser/testdata/config_expr-04.k.json b/kclvm/parser/testdata/config_expr-04.k.json new file mode 100644 index 000000000..e4a711f5e --- /dev/null +++ b/kclvm/parser/testdata/config_expr-04.k.json @@ -0,0 +1 @@ +{"filename":"config_expr-04.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Schema":{"doc":"","name":{"node":"Env","filename":"config_expr-04.k","line":3,"column":7,"end_line":3,"end_column":10},"parent_name":null,"for_host_name":null,"is_mixin":false,"is_protocol":false,"args":null,"mixins":[],"body":[{"node":{"SchemaAttr":{"doc":"","name":{"node":"name","filename":"config_expr-04.k","line":4,"column":4,"end_line":4,"end_column":8},"type_str":{"node":"str","filename":"config_expr-04.k","line":4,"column":10,"end_line":4,"end_column":13},"op":null,"value":null,"is_optional":false,"decorators":[]}},"filename":"config_expr-04.k","line":4,"column":4,"end_line":5,"end_column":0},{"node":{"SchemaAttr":{"doc":"","name":{"node":"value","filename":"config_expr-04.k","line":5,"column":4,"end_line":5,"end_column":9},"type_str":{"node":"str","filename":"config_expr-04.k","line":5,"column":11,"end_line":5,"end_column":14},"op":null,"value":null,"is_optional":false,"decorators":[]}},"filename":"config_expr-04.k","line":5,"column":4,"end_line":7,"end_column":0}],"decorators":[],"checks":[],"index_signature":null}},"filename":"config_expr-04.k","line":3,"column":0,"end_line":7,"end_column":0},{"node":{"Schema":{"doc":"","name":{"node":"Main","filename":"config_expr-04.k","line":7,"column":7,"end_line":7,"end_column":11},"parent_name":null,"for_host_name":null,"is_mixin":false,"is_protocol":false,"args":null,"mixins":[],"body":[{"node":{"SchemaAttr":{"doc":"","name":{"node":"env","filename":"config_expr-04.k","line":8,"column":4,"end_line":8,"end_column":7},"type_str":{"node":"[Env]","filename":"config_expr-04.k","line":8,"column":9,"end_line":8,"end_column":14},"op":null,"value":null,"is_optional":false,"decorators":[]}},"filename":"config_expr-04.k","line":8,"column":4,"end_line":10,"end_column":0}],"decorators":[],"checks":[],"index_signature":null}},"filename":"config_expr-04.k","line":7,"column":0,"end_line":10,"end_column":0},{"node":{"Schema":{"doc":"","name":{"node":"Config","filename":"config_expr-04.k","line":10,"column":7,"end_line":10,"end_column":13},"parent_name":null,"for_host_name":null,"is_mixin":false,"is_protocol":false,"args":null,"mixins":[],"body":[{"node":{"SchemaAttr":{"doc":"","name":{"node":"main","filename":"config_expr-04.k","line":11,"column":4,"end_line":11,"end_column":8},"type_str":{"node":"Main","filename":"config_expr-04.k","line":11,"column":10,"end_line":11,"end_column":14},"op":null,"value":null,"is_optional":false,"decorators":[]}},"filename":"config_expr-04.k","line":11,"column":4,"end_line":13,"end_column":0}],"decorators":[],"checks":[],"index_signature":null}},"filename":"config_expr-04.k","line":10,"column":0,"end_line":13,"end_column":0},{"node":{"Assign":{"targets":[{"node":{"names":["_main"],"pkgpath":"","ctx":"Store"},"filename":"config_expr-04.k","line":13,"column":0,"end_line":13,"end_column":5}],"value":{"node":{"Schema":{"name":{"node":{"names":["Main"],"pkgpath":"","ctx":"Load"},"filename":"config_expr-04.k","line":13,"column":8,"end_line":13,"end_column":12},"args":[],"kwargs":[],"config":{"node":{"Config":{"items":[{"node":{"key":{"node":{"Identifier":{"names":["env"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-04.k","line":14,"column":4,"end_line":14,"end_column":7},"value":{"node":{"List":{"elts":[{"node":{"Config":{"items":[{"node":{"key":{"node":{"Identifier":{"names":["name"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-04.k","line":15,"column":9,"end_line":15,"end_column":13},"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"ENV_1\"","value":"ENV_1"}},"filename":"config_expr-04.k","line":15,"column":15,"end_line":15,"end_column":22},"operation":"Union","insert_index":-1},"filename":"config_expr-04.k","line":15,"column":9,"end_line":15,"end_column":22},{"node":{"key":{"node":{"Identifier":{"names":["value"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-04.k","line":15,"column":24,"end_line":15,"end_column":29},"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"1\"","value":"1"}},"filename":"config_expr-04.k","line":15,"column":31,"end_line":15,"end_column":34},"operation":"Union","insert_index":-1},"filename":"config_expr-04.k","line":15,"column":24,"end_line":15,"end_column":34}]}},"filename":"config_expr-04.k","line":15,"column":8,"end_line":15,"end_column":35}],"ctx":"Load"}},"filename":"config_expr-04.k","line":14,"column":9,"end_line":16,"end_column":5},"operation":"Union","insert_index":-1},"filename":"config_expr-04.k","line":14,"column":4,"end_line":16,"end_column":5}]}},"filename":"config_expr-04.k","line":13,"column":13,"end_line":17,"end_column":1}}},"filename":"config_expr-04.k","line":13,"column":8,"end_line":17,"end_column":1},"type_annotation":null}},"filename":"config_expr-04.k","line":13,"column":0,"end_line":19,"end_column":0},{"node":{"Assign":{"targets":[{"node":{"names":["config"],"pkgpath":"","ctx":"Store"},"filename":"config_expr-04.k","line":19,"column":0,"end_line":19,"end_column":6}],"value":{"node":{"Schema":{"name":{"node":{"names":["Config"],"pkgpath":"","ctx":"Load"},"filename":"config_expr-04.k","line":19,"column":9,"end_line":19,"end_column":15},"args":[],"kwargs":[],"config":{"node":{"Config":{"items":[{"node":{"key":{"node":{"Identifier":{"names":["main"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-04.k","line":20,"column":4,"end_line":20,"end_column":8},"value":{"node":{"Identifier":{"names":["_main"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-04.k","line":20,"column":10,"end_line":20,"end_column":15},"operation":"Union","insert_index":-1},"filename":"config_expr-04.k","line":20,"column":4,"end_line":20,"end_column":15},{"node":{"key":{"node":{"Identifier":{"names":["main"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-04.k","line":21,"column":4,"end_line":21,"end_column":8},"value":{"node":{"Schema":{"name":{"node":{"names":["Main"],"pkgpath":"","ctx":"Load"},"filename":"config_expr-04.k","line":21,"column":10,"end_line":21,"end_column":14},"args":[],"kwargs":[],"config":{"node":{"Config":{"items":[{"node":{"key":{"node":{"Identifier":{"names":["env"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-04.k","line":22,"column":8,"end_line":22,"end_column":11},"value":{"node":{"List":{"elts":[{"node":{"Config":{"items":[{"node":{"key":{"node":{"Identifier":{"names":["name"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-04.k","line":23,"column":13,"end_line":23,"end_column":17},"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"ENV_2\"","value":"ENV_2"}},"filename":"config_expr-04.k","line":23,"column":19,"end_line":23,"end_column":26},"operation":"Union","insert_index":-1},"filename":"config_expr-04.k","line":23,"column":13,"end_line":23,"end_column":26},{"node":{"key":{"node":{"Identifier":{"names":["value"],"pkgpath":"","ctx":"Load"}},"filename":"config_expr-04.k","line":23,"column":28,"end_line":23,"end_column":33},"value":{"node":{"StringLit":{"is_long_string":false,"raw_value":"\"2\"","value":"2"}},"filename":"config_expr-04.k","line":23,"column":35,"end_line":23,"end_column":38},"operation":"Union","insert_index":-1},"filename":"config_expr-04.k","line":23,"column":28,"end_line":23,"end_column":38}]}},"filename":"config_expr-04.k","line":23,"column":12,"end_line":23,"end_column":39}],"ctx":"Load"}},"filename":"config_expr-04.k","line":22,"column":15,"end_line":24,"end_column":9},"operation":"Insert","insert_index":-1},"filename":"config_expr-04.k","line":22,"column":8,"end_line":24,"end_column":9}]}},"filename":"config_expr-04.k","line":21,"column":15,"end_line":25,"end_column":5}}},"filename":"config_expr-04.k","line":21,"column":10,"end_line":25,"end_column":5},"operation":"Union","insert_index":-1},"filename":"config_expr-04.k","line":21,"column":4,"end_line":25,"end_column":5}]}},"filename":"config_expr-04.k","line":19,"column":16,"end_line":26,"end_column":1}}},"filename":"config_expr-04.k","line":19,"column":9,"end_line":26,"end_column":1},"type_annotation":null}},"filename":"config_expr-04.k","line":19,"column":0,"end_line":26,"end_column":2}],"comments":[{"node":{"text":"# test/grammar/attr_operator/config_inside/insert/schema_0/main.k"},"filename":"config_expr-04.k","line":1,"column":0,"end_line":1,"end_column":65}]} diff --git a/kclvm/parser/testdata/if-01.k b/kclvm/parser/testdata/if-01.k new file mode 100644 index 000000000..5fc5e7ea2 --- /dev/null +++ b/kclvm/parser/testdata/if-01.k @@ -0,0 +1,4 @@ +a = 1 + +if a: + bbb = 2 diff --git a/kclvm/parser/testdata/if-01.k.json b/kclvm/parser/testdata/if-01.k.json new file mode 100644 index 000000000..ea4fa48cf --- /dev/null +++ b/kclvm/parser/testdata/if-01.k.json @@ -0,0 +1 @@ +{"filename":"if-01.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assign":{"targets":[{"node":{"names":["a"],"pkgpath":"","ctx":"Store"},"filename":"if-01.k","line":1,"column":0,"end_line":1,"end_column":1}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"if-01.k","line":1,"column":4,"end_line":1,"end_column":5},"type_annotation":null}},"filename":"if-01.k","line":1,"column":0,"end_line":3,"end_column":0},{"node":{"If":{"body":[{"node":{"Assign":{"targets":[{"node":{"names":["bbb"],"pkgpath":"","ctx":"Store"},"filename":"if-01.k","line":4,"column":4,"end_line":4,"end_column":7}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":2}}},"filename":"if-01.k","line":4,"column":10,"end_line":4,"end_column":11},"type_annotation":null}},"filename":"if-01.k","line":4,"column":4,"end_line":4,"end_column":12}],"cond":{"node":{"Identifier":{"names":["a"],"pkgpath":"","ctx":"Load"}},"filename":"if-01.k","line":3,"column":3,"end_line":3,"end_column":4},"orelse":[]}},"filename":"if-01.k","line":3,"column":0,"end_line":4,"end_column":12}],"comments":[]} diff --git a/kclvm/parser/testdata/if-02.k b/kclvm/parser/testdata/if-02.k new file mode 100644 index 000000000..5132d411e --- /dev/null +++ b/kclvm/parser/testdata/if-02.k @@ -0,0 +1,10 @@ +a = 1 + +if a: + bbb = 2 +elif a + 10: + ccc = 3 +elif a + 100: + ddd = 4 +else: + eee = 5 diff --git a/kclvm/parser/testdata/if-02.k.json b/kclvm/parser/testdata/if-02.k.json new file mode 100644 index 000000000..7fdc9b69e --- /dev/null +++ b/kclvm/parser/testdata/if-02.k.json @@ -0,0 +1 @@ +{"filename":"if-02.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Assign":{"targets":[{"node":{"names":["a"],"pkgpath":"","ctx":"Store"},"filename":"if-02.k","line":1,"column":0,"end_line":1,"end_column":1}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"if-02.k","line":1,"column":4,"end_line":1,"end_column":5},"type_annotation":null}},"filename":"if-02.k","line":1,"column":0,"end_line":3,"end_column":0},{"node":{"If":{"body":[{"node":{"Assign":{"targets":[{"node":{"names":["bbb"],"pkgpath":"","ctx":"Store"},"filename":"if-02.k","line":4,"column":4,"end_line":4,"end_column":7}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":2}}},"filename":"if-02.k","line":4,"column":10,"end_line":4,"end_column":11},"type_annotation":null}},"filename":"if-02.k","line":4,"column":4,"end_line":5,"end_column":0}],"cond":{"node":{"Identifier":{"names":["a"],"pkgpath":"","ctx":"Load"}},"filename":"if-02.k","line":3,"column":3,"end_line":3,"end_column":4},"orelse":[{"node":{"If":{"body":[{"node":{"Assign":{"targets":[{"node":{"names":["ccc"],"pkgpath":"","ctx":"Store"},"filename":"if-02.k","line":6,"column":4,"end_line":6,"end_column":7}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":3}}},"filename":"if-02.k","line":6,"column":10,"end_line":6,"end_column":11},"type_annotation":null}},"filename":"if-02.k","line":6,"column":4,"end_line":7,"end_column":0}],"cond":{"node":{"Binary":{"left":{"node":{"Identifier":{"names":["a"],"pkgpath":"","ctx":"Load"}},"filename":"if-02.k","line":5,"column":5,"end_line":5,"end_column":6},"op":{"Bin":"Add"},"right":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":10}}},"filename":"if-02.k","line":5,"column":9,"end_line":5,"end_column":11}}},"filename":"if-02.k","line":5,"column":5,"end_line":5,"end_column":11},"orelse":[{"node":{"If":{"body":[{"node":{"Assign":{"targets":[{"node":{"names":["ddd"],"pkgpath":"","ctx":"Store"},"filename":"if-02.k","line":8,"column":4,"end_line":8,"end_column":7}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":4}}},"filename":"if-02.k","line":8,"column":10,"end_line":8,"end_column":11},"type_annotation":null}},"filename":"if-02.k","line":8,"column":4,"end_line":9,"end_column":0}],"cond":{"node":{"Binary":{"left":{"node":{"Identifier":{"names":["a"],"pkgpath":"","ctx":"Load"}},"filename":"if-02.k","line":7,"column":5,"end_line":7,"end_column":6},"op":{"Bin":"Add"},"right":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":100}}},"filename":"if-02.k","line":7,"column":9,"end_line":7,"end_column":12}}},"filename":"if-02.k","line":7,"column":5,"end_line":7,"end_column":12},"orelse":[{"node":{"Assign":{"targets":[{"node":{"names":["eee"],"pkgpath":"","ctx":"Store"},"filename":"if-02.k","line":10,"column":4,"end_line":10,"end_column":7}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":5}}},"filename":"if-02.k","line":10,"column":10,"end_line":10,"end_column":11},"type_annotation":null}},"filename":"if-02.k","line":10,"column":4,"end_line":10,"end_column":12}]}},"filename":"if-02.k","line":7,"column":0,"end_line":9,"end_column":0}]}},"filename":"if-02.k","line":5,"column":0,"end_line":7,"end_column":0}]}},"filename":"if-02.k","line":3,"column":0,"end_line":10,"end_column":12}],"comments":[]} diff --git a/kclvm/parser/testdata/if-03.k b/kclvm/parser/testdata/if-03.k new file mode 100644 index 000000000..e154c1488 --- /dev/null +++ b/kclvm/parser/testdata/if-03.k @@ -0,0 +1 @@ +if True: a = 1 diff --git a/kclvm/parser/testdata/if-03.k.json b/kclvm/parser/testdata/if-03.k.json new file mode 100644 index 000000000..b0e7c0339 --- /dev/null +++ b/kclvm/parser/testdata/if-03.k.json @@ -0,0 +1 @@ +{"filename":"if-03.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"If":{"body":[{"node":{"Assign":{"targets":[{"node":{"names":["a"],"pkgpath":"","ctx":"Store"},"filename":"if-03.k","line":1,"column":9,"end_line":1,"end_column":10}],"value":{"node":{"NumberLit":{"binary_suffix":null,"value":{"Int":1}}},"filename":"if-03.k","line":1,"column":13,"end_line":1,"end_column":14},"type_annotation":null}},"filename":"if-03.k","line":1,"column":9,"end_line":1,"end_column":15}],"cond":{"node":{"NameConstantLit":{"value":"True"}},"filename":"if-03.k","line":1,"column":3,"end_line":1,"end_column":7},"orelse":[]}},"filename":"if-03.k","line":1,"column":0,"end_line":1,"end_column":15}],"comments":[]} diff --git a/kclvm/parser/testdata/import-01.k b/kclvm/parser/testdata/import-01.k new file mode 100644 index 000000000..7fb94a9bc --- /dev/null +++ b/kclvm/parser/testdata/import-01.k @@ -0,0 +1,7 @@ +import a1 + +import a2 as a2_pkg + +import subpkg.b1.c1 + +import .a3 diff --git a/kclvm/parser/testdata/import-01.k.json b/kclvm/parser/testdata/import-01.k.json new file mode 100644 index 000000000..be1415934 --- /dev/null +++ b/kclvm/parser/testdata/import-01.k.json @@ -0,0 +1 @@ +{"filename":"import-01.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"Import":{"path":"a1","rawpath":"a1","name":"a1","asname":null}},"filename":"import-01.k","line":1,"column":0,"end_line":1,"end_column":9},{"node":{"Import":{"path":"a2","rawpath":"a2","name":"a2_pkg","asname":"a2_pkg"}},"filename":"import-01.k","line":3,"column":0,"end_line":3,"end_column":19},{"node":{"Import":{"path":"subpkg.b1.c1","rawpath":"subpkg.b1.c1","name":"c1","asname":null}},"filename":"import-01.k","line":5,"column":0,"end_line":5,"end_column":19},{"node":{"Import":{"path":".a3","rawpath":".a3","name":"a3","asname":null}},"filename":"import-01.k","line":7,"column":0,"end_line":7,"end_column":10}],"comments":[]} diff --git a/kclvm/parser/testdata/kcl.mod b/kclvm/parser/testdata/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/kclvm/parser/testdata/subpkg/a1.k b/kclvm/parser/testdata/subpkg/a1.k new file mode 100644 index 000000000..e69de29bb diff --git a/kclvm/parser/testdata/subpkg/a2.k b/kclvm/parser/testdata/subpkg/a2.k new file mode 100644 index 000000000..e69de29bb diff --git a/kclvm/parser/testdata/subpkg/a3.k b/kclvm/parser/testdata/subpkg/a3.k new file mode 100644 index 000000000..e69de29bb diff --git a/kclvm/parser/testdata/subpkg/b1/c1.k b/kclvm/parser/testdata/subpkg/b1/c1.k new file mode 100644 index 000000000..6fff06a20 --- /dev/null +++ b/kclvm/parser/testdata/subpkg/b1/c1.k @@ -0,0 +1,2 @@ +import ..a3 +import ...a3 diff --git a/kclvm/parser/testdata/type-01.k b/kclvm/parser/testdata/type-01.k new file mode 100644 index 000000000..fba9429c7 --- /dev/null +++ b/kclvm/parser/testdata/type-01.k @@ -0,0 +1,12 @@ +type a = any + +type b = bool +type c = int +type d = float +type e = str + +type type_list1 = [] +type type_list2 = [[]] +type type_list3 = [int] + +type b = int | str | [] | {:} diff --git a/kclvm/parser/testdata/type-01.k.json b/kclvm/parser/testdata/type-01.k.json new file mode 100644 index 000000000..5600554e2 --- /dev/null +++ b/kclvm/parser/testdata/type-01.k.json @@ -0,0 +1 @@ +{"filename":"type-01.k","pkg":"__main__","doc":"","name":"__main__","body":[{"node":{"TypeAlias":{"type_name":{"node":{"names":["a"],"pkgpath":"","ctx":"Load"},"filename":"type-01.k","line":1,"column":5,"end_line":1,"end_column":6},"type_value":{"node":"any","filename":"type-01.k","line":1,"column":9,"end_line":1,"end_column":12}}},"filename":"type-01.k","line":1,"column":5,"end_line":1,"end_column":12},{"node":{"TypeAlias":{"type_name":{"node":{"names":["b"],"pkgpath":"","ctx":"Load"},"filename":"type-01.k","line":3,"column":5,"end_line":3,"end_column":6},"type_value":{"node":"bool","filename":"type-01.k","line":3,"column":9,"end_line":3,"end_column":13}}},"filename":"type-01.k","line":3,"column":5,"end_line":3,"end_column":13},{"node":{"TypeAlias":{"type_name":{"node":{"names":["c"],"pkgpath":"","ctx":"Load"},"filename":"type-01.k","line":4,"column":5,"end_line":4,"end_column":6},"type_value":{"node":"int","filename":"type-01.k","line":4,"column":9,"end_line":4,"end_column":12}}},"filename":"type-01.k","line":4,"column":5,"end_line":4,"end_column":12},{"node":{"TypeAlias":{"type_name":{"node":{"names":["d"],"pkgpath":"","ctx":"Load"},"filename":"type-01.k","line":5,"column":5,"end_line":5,"end_column":6},"type_value":{"node":"float","filename":"type-01.k","line":5,"column":9,"end_line":5,"end_column":14}}},"filename":"type-01.k","line":5,"column":5,"end_line":5,"end_column":14},{"node":{"TypeAlias":{"type_name":{"node":{"names":["e"],"pkgpath":"","ctx":"Load"},"filename":"type-01.k","line":6,"column":5,"end_line":6,"end_column":6},"type_value":{"node":"str","filename":"type-01.k","line":6,"column":9,"end_line":6,"end_column":12}}},"filename":"type-01.k","line":6,"column":5,"end_line":6,"end_column":12},{"node":{"TypeAlias":{"type_name":{"node":{"names":["type_list1"],"pkgpath":"","ctx":"Load"},"filename":"type-01.k","line":8,"column":5,"end_line":8,"end_column":15},"type_value":{"node":"[]","filename":"type-01.k","line":8,"column":18,"end_line":8,"end_column":20}}},"filename":"type-01.k","line":8,"column":5,"end_line":8,"end_column":20},{"node":{"TypeAlias":{"type_name":{"node":{"names":["type_list2"],"pkgpath":"","ctx":"Load"},"filename":"type-01.k","line":9,"column":5,"end_line":9,"end_column":15},"type_value":{"node":"[[]]","filename":"type-01.k","line":9,"column":18,"end_line":9,"end_column":22}}},"filename":"type-01.k","line":9,"column":5,"end_line":9,"end_column":22},{"node":{"TypeAlias":{"type_name":{"node":{"names":["type_list3"],"pkgpath":"","ctx":"Load"},"filename":"type-01.k","line":10,"column":5,"end_line":10,"end_column":15},"type_value":{"node":"[int]","filename":"type-01.k","line":10,"column":18,"end_line":10,"end_column":23}}},"filename":"type-01.k","line":10,"column":5,"end_line":10,"end_column":23},{"node":{"TypeAlias":{"type_name":{"node":{"names":["b"],"pkgpath":"","ctx":"Load"},"filename":"type-01.k","line":12,"column":5,"end_line":12,"end_column":6},"type_value":{"node":"int|str|[]|{:}","filename":"type-01.k","line":12,"column":9,"end_line":12,"end_column":29}}},"filename":"type-01.k","line":12,"column":5,"end_line":12,"end_column":29}],"comments":[]} diff --git a/kclvm/plugin/.gitignore b/kclvm/plugin/.gitignore new file mode 100644 index 000000000..a4045b0f4 --- /dev/null +++ b/kclvm/plugin/.gitignore @@ -0,0 +1,4 @@ +build +*.so +*.dll +target diff --git a/kclvm/plugin/Makefile b/kclvm/plugin/Makefile new file mode 100644 index 000000000..728058188 --- /dev/null +++ b/kclvm/plugin/Makefile @@ -0,0 +1,19 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +default: + swig -c++ -python kclvm_plugin.i + python3 -m black . + + cd ../runtime && cargo fmt && cargo build --release + + kclvm setup.py build_ext --inplace + kclvm setup.py install_lib + + @echo ------------------ + cd demo && kclvm hello.py + +swig: + swig -c++ -python kclvm_plugin.i + +clean: + -rm -rf build *.so *.dll diff --git a/kclvm/plugin/kclvm_plugin.cpp b/kclvm/plugin/kclvm_plugin.cpp new file mode 100644 index 000000000..001d49651 --- /dev/null +++ b/kclvm/plugin/kclvm_plugin.cpp @@ -0,0 +1,148 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +#include "kclvm_plugin.h" + +#include +#include + +static const int32_t kDefaultBufferSize = 1024*1024*10; + +static _kclvm_plugin_AppContextBase* g_self_ = NULL; +static uint64_t g_rust_invoke_json_ptr_ = 0; + +static const char* _invoke_json_proxy( + const char* method, + const char* args_json, + const char* kwargs_json +) { + if(g_self_ == NULL) { return ""; } + + static std::string jsonResult; + jsonResult = g_self_->_call_py_method(method, args_json, kwargs_json); + return jsonResult.c_str(); +} + +_kclvm_plugin_AppContextBase::_kclvm_plugin_AppContextBase(uint64_t rust_invoke_json_ptr) { + g_rust_invoke_json_ptr_ = rust_invoke_json_ptr; + assert(g_self_ == NULL); + g_self_ = this; +} +_kclvm_plugin_AppContextBase::~_kclvm_plugin_AppContextBase() { + g_rust_invoke_json_ptr_ = 0; + g_self_ = NULL; +} + +void _kclvm_plugin_AppContextBase::_clear_options() { + this->option_keys_.clear(); + this->option_values_.clear(); +} +void _kclvm_plugin_AppContextBase::_add_option(const std::string& key, const std::string& value) { + this->option_keys_.push_back(key); + this->option_values_.push_back(value); +} + +std::string _kclvm_plugin_AppContextBase::_run_app( + uint64_t _start_fn_ptr, + uint64_t _kclvm_main_ptr, // main.k => kclvm_main + int32_t strict_range_check, + int32_t disable_none, + int32_t disable_schema_check, + int32_t list_option_mode, + int32_t debug_mode, + int32_t buffer_size +) { + typedef int32_t (*kcl_run_t)( + uint64_t _kclvm_main_ptr, // main.k => kclvm_main + int32_t option_len, + const char** option_keys, + const char** option_values, + int32_t strict_range_check, + int32_t disable_none, + int32_t disable_schema_check, + int32_t list_option_mode, + int32_t debug_mode, + int32_t result_buffer_len, + char* result_buffer, + int32_t warn_buffer_len, + char* warn_buffer + ); + + int32_t _option_len = this->option_keys_.size(); + std::vector _option_keys(_option_len); + std::vector _option_values(_option_len); + + for(size_t i = 0; i < this->option_keys_.size(); i++) { + _option_keys[i] = (char*)this->option_keys_[i].c_str(); + _option_values[i] = (char*)this->option_values_[i].c_str(); + } + + this->buffer_.clear(); + this->warn_buffer_.clear(); + + if(buffer_size > 0) { + this->buffer_.resize(buffer_size, '\0'); + } else { + this->buffer_.resize(kDefaultBufferSize, '\0'); + } + + this->warn_buffer_.resize(10*1024*1924, '\0'); + + kcl_run_t _kcl_run = (kcl_run_t)(_start_fn_ptr); + int32_t result_len = _kcl_run( + _kclvm_main_ptr, + _option_len, + (const char**)(_option_keys.data()), + (const char**)(_option_values.data()), + strict_range_check, + disable_none, + disable_schema_check, + list_option_mode, + debug_mode, + this->buffer_.size()-1, + &this->buffer_[0], + this->warn_buffer_.size()-1, + &this->warn_buffer_[0] + ); + + if(result_len > 0) { + this->buffer_.resize(result_len); + } else if (result_len == 0) { + this->buffer_ = "{}"; + } else { + this->buffer_ = "{\"error\": \"buffer size limit\"}"; + } + + this->warn_buffer_.resize(strlen(&this->warn_buffer_[0])); + + return this->buffer_; +} + +std::string _kclvm_plugin_AppContextBase::_get_warn() { + return this->warn_buffer_; +} + +uint64_t _kclvm_plugin_AppContextBase::_get_cxx_invoke_proxy_ptr() { + return uint64_t(_invoke_json_proxy); +} + +std::string _kclvm_plugin_AppContextBase::_call_rust_method( + const std::string& name, + const std::string& args_json, + const std::string& kwargs_json +) { + typedef const char* (*invoke_fn_t)( + const char* method, + const char* args_json, + const char* kwargs_json + ); + invoke_fn_t fn = (invoke_fn_t)(g_rust_invoke_json_ptr_); + return fn(name.c_str(), args_json.c_str(), kwargs_json.c_str()); +} + +std::string _kclvm_plugin_AppContextBase::_call_py_method( + const std::string& name, + const std::string& args_json, + const std::string& kwargs_json +) { + return "implemented in Python!!!"; +} diff --git a/kclvm/plugin/kclvm_plugin.h b/kclvm/plugin/kclvm_plugin.h new file mode 100644 index 000000000..bc0e19df5 --- /dev/null +++ b/kclvm/plugin/kclvm_plugin.h @@ -0,0 +1,62 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +#pragma once + +#ifndef __cplusplus +#error "please use C++" +#endif + +#include +#include +#include +#include + +#include +#include + +// ---------------------------------------------------------------------------- + +class _kclvm_plugin_AppContextBase { + std::string buffer_; + std::string warn_buffer_; + std::vector option_keys_; + std::vector option_values_; + +public: + _kclvm_plugin_AppContextBase(uint64_t rust_invoke_json_ptr); + virtual ~_kclvm_plugin_AppContextBase(); + + void _clear_options(); + void _add_option(const std::string& key, const std::string& value); + + std::string _run_app( + uint64_t _start_fn_ptr, + uint64_t _kclvm_main_ptr, // main.k => kclvm_main + int32_t strict_range_check, + int32_t disable_none, + int32_t disable_schema_check, + int32_t list_option_mode, + int32_t debug_mode, + int32_t buffer_size + ); + + std::string _get_warn(); + + uint64_t _get_cxx_invoke_proxy_ptr(); + + std::string _call_rust_method( + const std::string& name, + const std::string& args_json, + const std::string& kwargs_json + ); + + virtual std::string _call_py_method( + const std::string& name, + const std::string& args_json, + const std::string& kwargs_json + ); +}; + +// ---------------------------------------------------------------------------- +// END +// ---------------------------------------------------------------------------- diff --git a/kclvm/plugin/kclvm_plugin.i b/kclvm/plugin/kclvm_plugin.i new file mode 100644 index 000000000..56bb5c9aa --- /dev/null +++ b/kclvm/plugin/kclvm_plugin.i @@ -0,0 +1,266 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +// http://www.swig.org/Doc3.0/Python.html#Python_directors + +%module(directors="1") kclvm_plugin + +%{ +#define SWIG_FILE_WITH_INIT +#include "kclvm_plugin.h" +%} + +// ---------------------------------------------------------------------------- +// C/C++ code +// ---------------------------------------------------------------------------- + +%include "stdint.i" +%include "std_string.i" + +%feature("director") _kclvm_plugin_AppContextBase; +class _kclvm_plugin_AppContextBase { +public: + _kclvm_plugin_AppContextBase(uint64_t rust_invoke_json_ptr); + virtual ~_kclvm_plugin_AppContextBase(); + + void _clear_options(); + void _add_option(const std::string& key, const std::string& value); + + std::string _run_app( + uint64_t _start_fn_ptr, + uint64_t _kclvm_main_ptr, // main.k => kclvm_main + int32_t strict_range_check, + int32_t disable_none, + int32_t disable_schema_check, + int32_t list_option_mode, + int32_t debug_mode, + int32_t buffer_size + ); + + std::string _get_warn(); + + uint64_t _get_cxx_invoke_proxy_ptr(); + + std::string _call_rust_method( + const std::string& name, + const std::string& args_json, + const std::string& kwargs_json + ); + + virtual std::string _call_py_method( + const std::string& name, + const std::string& args_json, + const std::string& kwargs_json + ); +}; + +// ---------------------------------------------------------------------------- +// Python code +// ---------------------------------------------------------------------------- + +%pythonbegin %{ +import sys +import typing +import ctypes +import os +import importlib +import json +import inspect + +import kclvm.kcl.info as kcl_info +import kclvm.compiler.extension.plugin.plugin as kcl_plugin +import kclvm.api.object.internal.option as option +import kclvm.api.object as objpkg +%} + +%pythoncode %{ +class AppContext(_kclvm_plugin_AppContextBase): + def __init__(self, app_dll_name: str): + self._is_windows: bool = os.name == "nt" + + self._start_func_name: str = "" + self._app_dll_name = app_dll_name + self._plugin_dict: typing.Dict[str, any] = {} + + if self._is_windows: + _executable_root = os.path.dirname(sys.executable) + self._kclvm_runtime = ctypes.CDLL(f"{_executable_root}\\kclvm.dll") + self._app_lib = ctypes.CDLL(app_dll_name) + else: + self._kclvm_runtime = ctypes.CDLL(app_dll_name) + self._app_lib = ctypes.CDLL(app_dll_name) + + self._kclvm_runtime.kclvm_plugin_init.restype = None + self._kclvm_runtime.kclvm_plugin_init.argtypes = [ctypes.c_longlong] + + self._kclvm_runtime.kclvm_plugin_invoke_json.restype = ctypes.c_char_p + self._kclvm_runtime.kclvm_plugin_invoke_json.argtypes = [ + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p + ] + + rust_invoke_json_ptr = ctypes.cast(self._kclvm_runtime.kclvm_plugin_invoke_json, ctypes.c_void_p).value + super().__init__(rust_invoke_json_ptr) + + self._kclvm_runtime.kclvm_plugin_init(self._get_cxx_invoke_proxy_ptr()) + + def InitOptions(self, arguments): + self._clear_options() + for kv in arguments or []: + key, value = kv + if isinstance(value, (bool, list, dict)): + value = json.dumps(value) + elif isinstance(value, str): + value = '"{}"'.format(value.replace('"', '\\"')) + else: + value = str(value) + self._add_option(key, value) + + def RunApp(self, *, + start_func_name='_kcl_run', + strict_range_check=None, + disable_none=None, + disable_schema_check=None, + list_option_mode=None, + debug_mode=None, + buffer_size=0 + ) -> str: + self._start_func_name = start_func_name + + _start = getattr(self._kclvm_runtime, start_func_name) + _start_ptr = ctypes.cast(_start, ctypes.c_void_p).value + + if hasattr(self._app_lib, 'kclvm_main'): + _kclvm_main = getattr(self._app_lib, 'kclvm_main') + _kclvm_main_ptr = ctypes.cast(_kclvm_main, ctypes.c_void_p).value + elif hasattr(self._app_lib, 'kclvm_main_win'): + _kclvm_main = getattr(self._app_lib, 'kclvm_main_win') + _kclvm_main_ptr = ctypes.cast(_kclvm_main, ctypes.c_void_p).value + else: + _kclvm_main_ptr = 0 + + if disable_none: + disable_none = 1 + else: + disable_none = 0 + + if strict_range_check: + strict_range_check = 1 + else: + strict_range_check = 0 + + if disable_schema_check: + disable_schema_check = 1 + else: + disable_schema_check = 0 + + if list_option_mode: + list_option_mode = 1 + else: + list_option_mode = 0 + + if debug_mode: + debug_mode = 1 + else: + debug_mode = 0 + + json_result = self._run_app(_start_ptr, _kclvm_main_ptr, + strict_range_check, + disable_none, + disable_schema_check, + list_option_mode, + debug_mode, + buffer_size + ) + return json_result + + def GetWarn(self) -> str: + json_warn_result = self._get_warn() + return json_warn_result + + def CallMethod(self, name:str, args_json:str, kwargs_json:str) -> str: + return self._call_rust_method(name, args_json, kwargs_json) + + def _call_py_method(self, name:str, args_json:str, kwargs_json:str) -> str: + try: + return self._call_py_method_unsafe(name, args_json, kwargs_json) + except Exception as e: + return json.dumps({ "$__kcl_PanicInfo__$": f"{e}" }) + + def _get_plugin(self, plugin_name:str) -> typing.Optional[any]: + if plugin_name in self._plugin_dict: + return self._plugin_dict[plugin_name] + + module = kcl_plugin.get_plugin(plugin_name) + self._plugin_dict[plugin_name] = module + return module + + def _call_py_method_unsafe(self, name:str, args_json:str, kwargs_json:str) -> str: + dotIdx = name.rfind(".") + if dotIdx < 0: + return "" + + modulePath = name[:dotIdx] + mathodName = name[dotIdx+1:] + + plugin_name = modulePath[modulePath.rfind('.')+1:] + + module = self._get_plugin(plugin_name) + mathodFunc = None + + for func_name, func in inspect.getmembers(module): + if func_name == kcl_info.demangle(mathodName): + mathodFunc = func + break + + args = [] + kwargs = {} + + if args_json: + args = json.loads(args_json) + if not isinstance(args, list): + return "" + if kwargs_json: + kwargs = json.loads(kwargs_json) + if not isinstance(kwargs, dict): + return "" + + result = mathodFunc(*args, **kwargs) + return json.dumps(result) + + def __del__(self): + self._free_library() + + def _free_library(self): + if os.name == 'nt': + import ctypes.wintypes + kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) + kernel32.FreeLibrary.argtypes = [ctypes.wintypes.HMODULE] + kernel32.FreeLibrary(self._app_lib._handle) + self._app_lib = None + # kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) + # kernel32.FreeLibrary.argtypes = [ctypes.wintypes.HMODULE] + # kernel32.FreeLibrary(self._app_dll._handle) + pass + else: + # libdl = ctypes.CDLL("libdl.so") + # libdl.dlclose(self._app_dll._handle) + pass + + +def main(args: typing.List[str]): + if len(args) < 2: + print("usage: kclvm_plugin app.[dll|dylib|so]") + sys._exit(1) + + ctx = AppContext(args[1]) + ctx.RunApp(args[2:]) + + +if __name__ == "__main__": + main(sys.argv) +%} + +// ---------------------------------------------------------------------------- +// END +// ---------------------------------------------------------------------------- diff --git a/kclvm/plugin/kclvm_plugin.py b/kclvm/plugin/kclvm_plugin.py new file mode 100644 index 000000000..bb5d729fc --- /dev/null +++ b/kclvm/plugin/kclvm_plugin.py @@ -0,0 +1,423 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 3.0.12 +# +# Do not make changes to this file unless you know what you are doing--modify +# the SWIG interface file instead. + + +import sys +import typing +import ctypes +import os +import importlib +import json +import inspect + +import kclvm.kcl.info as kcl_info +import kclvm.compiler.extension.plugin.plugin as kcl_plugin +import kclvm.api.object.internal.option as option +import kclvm.api.object as objpkg + + +from sys import version_info as _swig_python_version_info + +if _swig_python_version_info >= (2, 7, 0): + + def swig_import_helper(): + import importlib + + pkg = __name__.rpartition(".")[0] + mname = ".".join((pkg, "_kclvm_plugin")).lstrip(".") + try: + return importlib.import_module(mname) + except ImportError: + return importlib.import_module("_kclvm_plugin") + + _kclvm_plugin = swig_import_helper() + del swig_import_helper +elif _swig_python_version_info >= (2, 6, 0): + + def swig_import_helper(): + from os.path import dirname + import imp + + fp = None + try: + fp, pathname, description = imp.find_module( + "_kclvm_plugin", [dirname(__file__)] + ) + except ImportError: + import _kclvm_plugin + + return _kclvm_plugin + try: + _mod = imp.load_module("_kclvm_plugin", fp, pathname, description) + finally: + if fp is not None: + fp.close() + return _mod + + _kclvm_plugin = swig_import_helper() + del swig_import_helper +else: + import _kclvm_plugin +del _swig_python_version_info + +try: + _swig_property = property +except NameError: + pass # Python < 2.2 doesn't have 'property'. + +try: + import builtins as __builtin__ +except ImportError: + import __builtin__ + + +def _swig_setattr_nondynamic(self, class_type, name, value, static=1): + if name == "thisown": + return self.this.own(value) + if name == "this": + if type(value).__name__ == "SwigPyObject": + self.__dict__[name] = value + return + method = class_type.__swig_setmethods__.get(name, None) + if method: + return method(self, value) + if not static: + if _newclass: + object.__setattr__(self, name, value) + else: + self.__dict__[name] = value + else: + raise AttributeError("You cannot add attributes to %s" % self) + + +def _swig_setattr(self, class_type, name, value): + return _swig_setattr_nondynamic(self, class_type, name, value, 0) + + +def _swig_getattr(self, class_type, name): + if name == "thisown": + return self.this.own() + method = class_type.__swig_getmethods__.get(name, None) + if method: + return method(self) + raise AttributeError( + "'%s' object has no attribute '%s'" % (class_type.__name__, name) + ) + + +def _swig_repr(self): + try: + strthis = "proxy of " + self.this.__repr__() + except __builtin__.Exception: + strthis = "" + return "<%s.%s; %s >" % ( + self.__class__.__module__, + self.__class__.__name__, + strthis, + ) + + +try: + _object = object + _newclass = 1 +except __builtin__.Exception: + + class _object: + pass + + _newclass = 0 + +try: + import weakref + + weakref_proxy = weakref.proxy +except __builtin__.Exception: + weakref_proxy = lambda x: x + + +class _kclvm_plugin_AppContextBase(_object): + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr( + self, _kclvm_plugin_AppContextBase, name, value + ) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr( + self, _kclvm_plugin_AppContextBase, name + ) + __repr__ = _swig_repr + + def __init__(self, rust_invoke_json_ptr): + if self.__class__ == _kclvm_plugin_AppContextBase: + _self = None + else: + _self = self + this = _kclvm_plugin.new__kclvm_plugin_AppContextBase( + _self, rust_invoke_json_ptr + ) + try: + self.this.append(this) + except __builtin__.Exception: + self.this = this + + __swig_destroy__ = _kclvm_plugin.delete__kclvm_plugin_AppContextBase + __del__ = lambda self: None + + def _clear_options(self): + return _kclvm_plugin._kclvm_plugin_AppContextBase__clear_options(self) + + def _add_option(self, key, value): + return _kclvm_plugin._kclvm_plugin_AppContextBase__add_option(self, key, value) + + def _run_app( + self, + _start_fn_ptr, + _kclvm_main_ptr, + strict_range_check, + disable_none, + disable_schema_check, + list_option_mode, + debug_mode, + buffer_size, + ): + return _kclvm_plugin._kclvm_plugin_AppContextBase__run_app( + self, + _start_fn_ptr, + _kclvm_main_ptr, + strict_range_check, + disable_none, + disable_schema_check, + list_option_mode, + debug_mode, + buffer_size, + ) + + def _get_warn(self): + return _kclvm_plugin._kclvm_plugin_AppContextBase__get_warn(self) + + def _get_cxx_invoke_proxy_ptr(self): + return _kclvm_plugin._kclvm_plugin_AppContextBase__get_cxx_invoke_proxy_ptr( + self + ) + + def _call_rust_method(self, name, args_json, kwargs_json): + return _kclvm_plugin._kclvm_plugin_AppContextBase__call_rust_method( + self, name, args_json, kwargs_json + ) + + def _call_py_method(self, name, args_json, kwargs_json): + return _kclvm_plugin._kclvm_plugin_AppContextBase__call_py_method( + self, name, args_json, kwargs_json + ) + + def __disown__(self): + self.this.disown() + _kclvm_plugin.disown__kclvm_plugin_AppContextBase(self) + return weakref_proxy(self) + + +_kclvm_plugin_AppContextBase_swigregister = ( + _kclvm_plugin._kclvm_plugin_AppContextBase_swigregister +) +_kclvm_plugin_AppContextBase_swigregister(_kclvm_plugin_AppContextBase) + + +class AppContext(_kclvm_plugin_AppContextBase): + def __init__(self, app_dll_name: str): + self._is_windows: bool = os.name == "nt" + + self._start_func_name: str = "" + self._app_dll_name = app_dll_name + self._plugin_dict: typing.Dict[str, any] = {} + + if self._is_windows: + _executable_root = os.path.dirname(sys.executable) + self._kclvm_runtime = ctypes.CDLL(f"{_executable_root}\\kclvm.dll") + self._app_lib = ctypes.CDLL(app_dll_name) + else: + self._kclvm_runtime = ctypes.CDLL(app_dll_name) + self._app_lib = ctypes.CDLL(app_dll_name) + + self._kclvm_runtime.kclvm_plugin_init.restype = None + self._kclvm_runtime.kclvm_plugin_init.argtypes = [ctypes.c_longlong] + + self._kclvm_runtime.kclvm_plugin_invoke_json.restype = ctypes.c_char_p + self._kclvm_runtime.kclvm_plugin_invoke_json.argtypes = [ + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p, + ] + + rust_invoke_json_ptr = ctypes.cast( + self._kclvm_runtime.kclvm_plugin_invoke_json, ctypes.c_void_p + ).value + super().__init__(rust_invoke_json_ptr) + + self._kclvm_runtime.kclvm_plugin_init(self._get_cxx_invoke_proxy_ptr()) + + def InitOptions(self, arguments): + self._clear_options() + for kv in arguments or []: + key, value = kv + if isinstance(value, (bool, list, dict)): + value = json.dumps(value) + elif isinstance(value, str): + value = '"{}"'.format(value.replace('"', '\\"')) + else: + value = str(value) + self._add_option(key, value) + + def RunApp( + self, + *, + start_func_name="_kcl_run", + strict_range_check=None, + disable_none=None, + disable_schema_check=None, + list_option_mode=None, + debug_mode=None, + buffer_size=0, + ) -> str: + self._start_func_name = start_func_name + + _start = getattr(self._kclvm_runtime, start_func_name) + _start_ptr = ctypes.cast(_start, ctypes.c_void_p).value + + if hasattr(self._app_lib, "kclvm_main"): + _kclvm_main = getattr(self._app_lib, "kclvm_main") + _kclvm_main_ptr = ctypes.cast(_kclvm_main, ctypes.c_void_p).value + elif hasattr(self._app_lib, "kclvm_main_win"): + _kclvm_main = getattr(self._app_lib, "kclvm_main_win") + _kclvm_main_ptr = ctypes.cast(_kclvm_main, ctypes.c_void_p).value + else: + _kclvm_main_ptr = 0 + + if disable_none: + disable_none = 1 + else: + disable_none = 0 + + if strict_range_check: + strict_range_check = 1 + else: + strict_range_check = 0 + + if disable_schema_check: + disable_schema_check = 1 + else: + disable_schema_check = 0 + + if list_option_mode: + list_option_mode = 1 + else: + list_option_mode = 0 + + if debug_mode: + debug_mode = 1 + else: + debug_mode = 0 + + json_result = self._run_app( + _start_ptr, + _kclvm_main_ptr, + strict_range_check, + disable_none, + disable_schema_check, + list_option_mode, + debug_mode, + buffer_size, + ) + return json_result + + def GetWarn(self) -> str: + json_warn_result = self._get_warn() + return json_warn_result + + def CallMethod(self, name: str, args_json: str, kwargs_json: str) -> str: + return self._call_rust_method(name, args_json, kwargs_json) + + def _call_py_method(self, name: str, args_json: str, kwargs_json: str) -> str: + try: + return self._call_py_method_unsafe(name, args_json, kwargs_json) + except Exception as e: + return json.dumps({"$__kcl_PanicInfo__$": f"{e}"}) + + def _get_plugin(self, plugin_name: str) -> typing.Optional[any]: + if plugin_name in self._plugin_dict: + return self._plugin_dict[plugin_name] + + module = kcl_plugin.get_plugin(plugin_name) + self._plugin_dict[plugin_name] = module + return module + + def _call_py_method_unsafe( + self, name: str, args_json: str, kwargs_json: str + ) -> str: + dotIdx = name.rfind(".") + if dotIdx < 0: + return "" + + modulePath = name[:dotIdx] + mathodName = name[dotIdx + 1 :] + + plugin_name = modulePath[modulePath.rfind(".") + 1 :] + + module = self._get_plugin(plugin_name) + mathodFunc = None + + for func_name, func in inspect.getmembers(module): + if func_name == kcl_info.demangle(mathodName): + mathodFunc = func + break + + args = [] + kwargs = {} + + if args_json: + args = json.loads(args_json) + if not isinstance(args, list): + return "" + if kwargs_json: + kwargs = json.loads(kwargs_json) + if not isinstance(kwargs, dict): + return "" + + result = mathodFunc(*args, **kwargs) + return json.dumps(result) + + def __del__(self): + self._free_library() + + def _free_library(self): + if os.name == "nt": + import ctypes.wintypes + + kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) + kernel32.FreeLibrary.argtypes = [ctypes.wintypes.HMODULE] + kernel32.FreeLibrary(self._app_lib._handle) + self._app_lib = None + # kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) + # kernel32.FreeLibrary.argtypes = [ctypes.wintypes.HMODULE] + # kernel32.FreeLibrary(self._app_dll._handle) + pass + else: + # libdl = ctypes.CDLL("libdl.so") + # libdl.dlclose(self._app_dll._handle) + pass + + +def main(args: typing.List[str]): + if len(args) < 2: + print("usage: kclvm_plugin app.[dll|dylib|so]") + sys._exit(1) + + ctx = AppContext(args[1]) + ctx.RunApp(args[2:]) + + +if __name__ == "__main__": + main(sys.argv) + +# This file is compatible with both classic and new-style classes. diff --git a/kclvm/plugin/kclvm_plugin_wrap.cxx b/kclvm/plugin/kclvm_plugin_wrap.cxx new file mode 100644 index 000000000..1b98fdf40 --- /dev/null +++ b/kclvm/plugin/kclvm_plugin_wrap.cxx @@ -0,0 +1,5290 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version 3.0.12 + * + * This file is not intended to be easily readable and contains a number of + * coding conventions designed to improve portability and efficiency. Do not make + * changes to this file unless you know what you are doing--modify the SWIG + * interface file instead. + * ----------------------------------------------------------------------------- */ + + +#ifndef SWIGPYTHON +#define SWIGPYTHON +#endif + +#define SWIG_DIRECTORS +#define SWIG_PYTHON_DIRECTOR_NO_VTABLE + + +#ifdef __cplusplus +/* SwigValueWrapper is described in swig.swg */ +template class SwigValueWrapper { + struct SwigMovePointer { + T *ptr; + SwigMovePointer(T *p) : ptr(p) { } + ~SwigMovePointer() { delete ptr; } + SwigMovePointer& operator=(SwigMovePointer& rhs) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = rhs.ptr; rhs.ptr = 0; return *this; } + } pointer; + SwigValueWrapper& operator=(const SwigValueWrapper& rhs); + SwigValueWrapper(const SwigValueWrapper& rhs); +public: + SwigValueWrapper() : pointer(0) { } + SwigValueWrapper& operator=(const T& t) { SwigMovePointer tmp(new T(t)); pointer = tmp; return *this; } + operator T&() const { return *pointer.ptr; } + T *operator&() { return pointer.ptr; } +}; + +template T SwigValueInit() { + return T(); +} +#endif + +/* ----------------------------------------------------------------------------- + * This section contains generic SWIG labels for method/variable + * declarations/attributes, and other compiler dependent labels. + * ----------------------------------------------------------------------------- */ + +/* template workaround for compilers that cannot correctly implement the C++ standard */ +#ifndef SWIGTEMPLATEDISAMBIGUATOR +# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) +# define SWIGTEMPLATEDISAMBIGUATOR template +# elif defined(__HP_aCC) +/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ +/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ +# define SWIGTEMPLATEDISAMBIGUATOR template +# else +# define SWIGTEMPLATEDISAMBIGUATOR +# endif +#endif + +/* inline attribute */ +#ifndef SWIGINLINE +# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) +# define SWIGINLINE inline +# else +# define SWIGINLINE +# endif +#endif + +/* attribute recognised by some compilers to avoid 'unused' warnings */ +#ifndef SWIGUNUSED +# if defined(__GNUC__) +# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +# elif defined(__ICC) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +#endif + +#ifndef SWIG_MSC_UNSUPPRESS_4505 +# if defined(_MSC_VER) +# pragma warning(disable : 4505) /* unreferenced local function has been removed */ +# endif +#endif + +#ifndef SWIGUNUSEDPARM +# ifdef __cplusplus +# define SWIGUNUSEDPARM(p) +# else +# define SWIGUNUSEDPARM(p) p SWIGUNUSED +# endif +#endif + +/* internal SWIG method */ +#ifndef SWIGINTERN +# define SWIGINTERN static SWIGUNUSED +#endif + +/* internal inline SWIG method */ +#ifndef SWIGINTERNINLINE +# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE +#endif + +/* exporting methods */ +#if defined(__GNUC__) +# if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# ifndef GCC_HASCLASSVISIBILITY +# define GCC_HASCLASSVISIBILITY +# endif +# endif +#endif + +#ifndef SWIGEXPORT +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# if defined(STATIC_LINKED) +# define SWIGEXPORT +# else +# define SWIGEXPORT __declspec(dllexport) +# endif +# else +# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) +# define SWIGEXPORT __attribute__ ((visibility("default"))) +# else +# define SWIGEXPORT +# endif +# endif +#endif + +/* calling conventions for Windows */ +#ifndef SWIGSTDCALL +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# define SWIGSTDCALL __stdcall +# else +# define SWIGSTDCALL +# endif +#endif + +/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ +#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE +#endif + +/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ +#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) +# define _SCL_SECURE_NO_DEPRECATE +#endif + +/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */ +#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES) +# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 +#endif + +/* Intel's compiler complains if a variable which was never initialised is + * cast to void, which is a common idiom which we use to indicate that we + * are aware a variable isn't used. So we just silence that warning. + * See: https://github.com/swig/swig/issues/192 for more discussion. + */ +#ifdef __INTEL_COMPILER +# pragma warning disable 592 +#endif + + +#if defined(_DEBUG) && defined(SWIG_PYTHON_INTERPRETER_NO_DEBUG) +/* Use debug wrappers with the Python release dll */ +# undef _DEBUG +# include +# define _DEBUG +#else +# include +#endif + +/* ----------------------------------------------------------------------------- + * swigrun.swg + * + * This file contains generic C API SWIG runtime support for pointer + * type checking. + * ----------------------------------------------------------------------------- */ + +/* This should only be incremented when either the layout of swig_type_info changes, + or for whatever reason, the runtime changes incompatibly */ +#define SWIG_RUNTIME_VERSION "4" + +/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */ +#ifdef SWIG_TYPE_TABLE +# define SWIG_QUOTE_STRING(x) #x +# define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x) +# define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE) +#else +# define SWIG_TYPE_TABLE_NAME +#endif + +/* + You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for + creating a static or dynamic library from the SWIG runtime code. + In 99.9% of the cases, SWIG just needs to declare them as 'static'. + + But only do this if strictly necessary, ie, if you have problems + with your compiler or suchlike. +*/ + +#ifndef SWIGRUNTIME +# define SWIGRUNTIME SWIGINTERN +#endif + +#ifndef SWIGRUNTIMEINLINE +# define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE +#endif + +/* Generic buffer size */ +#ifndef SWIG_BUFFER_SIZE +# define SWIG_BUFFER_SIZE 1024 +#endif + +/* Flags for pointer conversions */ +#define SWIG_POINTER_DISOWN 0x1 +#define SWIG_CAST_NEW_MEMORY 0x2 + +/* Flags for new pointer objects */ +#define SWIG_POINTER_OWN 0x1 + + +/* + Flags/methods for returning states. + + The SWIG conversion methods, as ConvertPtr, return an integer + that tells if the conversion was successful or not. And if not, + an error code can be returned (see swigerrors.swg for the codes). + + Use the following macros/flags to set or process the returning + states. + + In old versions of SWIG, code such as the following was usually written: + + if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) { + // success code + } else { + //fail code + } + + Now you can be more explicit: + + int res = SWIG_ConvertPtr(obj,vptr,ty.flags); + if (SWIG_IsOK(res)) { + // success code + } else { + // fail code + } + + which is the same really, but now you can also do + + Type *ptr; + int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags); + if (SWIG_IsOK(res)) { + // success code + if (SWIG_IsNewObj(res) { + ... + delete *ptr; + } else { + ... + } + } else { + // fail code + } + + I.e., now SWIG_ConvertPtr can return new objects and you can + identify the case and take care of the deallocation. Of course that + also requires SWIG_ConvertPtr to return new result values, such as + + int SWIG_ConvertPtr(obj, ptr,...) { + if () { + if () { + *ptr = ; + return SWIG_NEWOBJ; + } else { + *ptr = ; + return SWIG_OLDOBJ; + } + } else { + return SWIG_BADOBJ; + } + } + + Of course, returning the plain '0(success)/-1(fail)' still works, but you can be + more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the + SWIG errors code. + + Finally, if the SWIG_CASTRANK_MODE is enabled, the result code + allows to return the 'cast rank', for example, if you have this + + int food(double) + int fooi(int); + + and you call + + food(1) // cast rank '1' (1 -> 1.0) + fooi(1) // cast rank '0' + + just use the SWIG_AddCast()/SWIG_CheckState() +*/ + +#define SWIG_OK (0) +#define SWIG_ERROR (-1) +#define SWIG_IsOK(r) (r >= 0) +#define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError) + +/* The CastRankLimit says how many bits are used for the cast rank */ +#define SWIG_CASTRANKLIMIT (1 << 8) +/* The NewMask denotes the object was created (using new/malloc) */ +#define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1) +/* The TmpMask is for in/out typemaps that use temporal objects */ +#define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1) +/* Simple returning values */ +#define SWIG_BADOBJ (SWIG_ERROR) +#define SWIG_OLDOBJ (SWIG_OK) +#define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK) +#define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK) +/* Check, add and del mask methods */ +#define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r) +#define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r) +#define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK)) +#define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r) +#define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r) +#define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK)) + +/* Cast-Rank Mode */ +#if defined(SWIG_CASTRANK_MODE) +# ifndef SWIG_TypeRank +# define SWIG_TypeRank unsigned long +# endif +# ifndef SWIG_MAXCASTRANK /* Default cast allowed */ +# define SWIG_MAXCASTRANK (2) +# endif +# define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1) +# define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK) +SWIGINTERNINLINE int SWIG_AddCast(int r) { + return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r; +} +SWIGINTERNINLINE int SWIG_CheckState(int r) { + return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0; +} +#else /* no cast-rank mode */ +# define SWIG_AddCast(r) (r) +# define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0) +#endif + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *(*swig_converter_func)(void *, int *); +typedef struct swig_type_info *(*swig_dycast_func)(void **); + +/* Structure to store information on one type */ +typedef struct swig_type_info { + const char *name; /* mangled name of this type */ + const char *str; /* human readable name of this type */ + swig_dycast_func dcast; /* dynamic cast function down a hierarchy */ + struct swig_cast_info *cast; /* linked list of types that can cast into this type */ + void *clientdata; /* language specific type data */ + int owndata; /* flag if the structure owns the clientdata */ +} swig_type_info; + +/* Structure to store a type and conversion function used for casting */ +typedef struct swig_cast_info { + swig_type_info *type; /* pointer to type that is equivalent to this type */ + swig_converter_func converter; /* function to cast the void pointers */ + struct swig_cast_info *next; /* pointer to next cast in linked list */ + struct swig_cast_info *prev; /* pointer to the previous cast */ +} swig_cast_info; + +/* Structure used to store module information + * Each module generates one structure like this, and the runtime collects + * all of these structures and stores them in a circularly linked list.*/ +typedef struct swig_module_info { + swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */ + size_t size; /* Number of types in this module */ + struct swig_module_info *next; /* Pointer to next element in circularly linked list */ + swig_type_info **type_initial; /* Array of initially generated type structures */ + swig_cast_info **cast_initial; /* Array of initially generated casting structures */ + void *clientdata; /* Language specific module data */ +} swig_module_info; + +/* + Compare two type names skipping the space characters, therefore + "char*" == "char *" and "Class" == "Class", etc. + + Return 0 when the two name types are equivalent, as in + strncmp, but skipping ' '. +*/ +SWIGRUNTIME int +SWIG_TypeNameComp(const char *f1, const char *l1, + const char *f2, const char *l2) { + for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) { + while ((*f1 == ' ') && (f1 != l1)) ++f1; + while ((*f2 == ' ') && (f2 != l2)) ++f2; + if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1; + } + return (int)((l1 - f1) - (l2 - f2)); +} + +/* + Check type equivalence in a name list like ||... + Return 0 if equal, -1 if nb < tb, 1 if nb > tb +*/ +SWIGRUNTIME int +SWIG_TypeCmp(const char *nb, const char *tb) { + int equiv = 1; + const char* te = tb + strlen(tb); + const char* ne = nb; + while (equiv != 0 && *ne) { + for (nb = ne; *ne; ++ne) { + if (*ne == '|') break; + } + equiv = SWIG_TypeNameComp(nb, ne, tb, te); + if (*ne) ++ne; + } + return equiv; +} + +/* + Check type equivalence in a name list like ||... + Return 0 if not equal, 1 if equal +*/ +SWIGRUNTIME int +SWIG_TypeEquiv(const char *nb, const char *tb) { + return SWIG_TypeCmp(nb, tb) == 0 ? 1 : 0; +} + +/* + Check the typename +*/ +SWIGRUNTIME swig_cast_info * +SWIG_TypeCheck(const char *c, swig_type_info *ty) { + if (ty) { + swig_cast_info *iter = ty->cast; + while (iter) { + if (strcmp(iter->type->name, c) == 0) { + if (iter == ty->cast) + return iter; + /* Move iter to the top of the linked list */ + iter->prev->next = iter->next; + if (iter->next) + iter->next->prev = iter->prev; + iter->next = ty->cast; + iter->prev = 0; + if (ty->cast) ty->cast->prev = iter; + ty->cast = iter; + return iter; + } + iter = iter->next; + } + } + return 0; +} + +/* + Identical to SWIG_TypeCheck, except strcmp is replaced with a pointer comparison +*/ +SWIGRUNTIME swig_cast_info * +SWIG_TypeCheckStruct(swig_type_info *from, swig_type_info *ty) { + if (ty) { + swig_cast_info *iter = ty->cast; + while (iter) { + if (iter->type == from) { + if (iter == ty->cast) + return iter; + /* Move iter to the top of the linked list */ + iter->prev->next = iter->next; + if (iter->next) + iter->next->prev = iter->prev; + iter->next = ty->cast; + iter->prev = 0; + if (ty->cast) ty->cast->prev = iter; + ty->cast = iter; + return iter; + } + iter = iter->next; + } + } + return 0; +} + +/* + Cast a pointer up an inheritance hierarchy +*/ +SWIGRUNTIMEINLINE void * +SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) { + return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory); +} + +/* + Dynamic pointer casting. Down an inheritance hierarchy +*/ +SWIGRUNTIME swig_type_info * +SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) { + swig_type_info *lastty = ty; + if (!ty || !ty->dcast) return ty; + while (ty && (ty->dcast)) { + ty = (*ty->dcast)(ptr); + if (ty) lastty = ty; + } + return lastty; +} + +/* + Return the name associated with this type +*/ +SWIGRUNTIMEINLINE const char * +SWIG_TypeName(const swig_type_info *ty) { + return ty->name; +} + +/* + Return the pretty name associated with this type, + that is an unmangled type name in a form presentable to the user. +*/ +SWIGRUNTIME const char * +SWIG_TypePrettyName(const swig_type_info *type) { + /* The "str" field contains the equivalent pretty names of the + type, separated by vertical-bar characters. We choose + to print the last name, as it is often (?) the most + specific. */ + if (!type) return NULL; + if (type->str != NULL) { + const char *last_name = type->str; + const char *s; + for (s = type->str; *s; s++) + if (*s == '|') last_name = s+1; + return last_name; + } + else + return type->name; +} + +/* + Set the clientdata field for a type +*/ +SWIGRUNTIME void +SWIG_TypeClientData(swig_type_info *ti, void *clientdata) { + swig_cast_info *cast = ti->cast; + /* if (ti->clientdata == clientdata) return; */ + ti->clientdata = clientdata; + + while (cast) { + if (!cast->converter) { + swig_type_info *tc = cast->type; + if (!tc->clientdata) { + SWIG_TypeClientData(tc, clientdata); + } + } + cast = cast->next; + } +} +SWIGRUNTIME void +SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) { + SWIG_TypeClientData(ti, clientdata); + ti->owndata = 1; +} + +/* + Search for a swig_type_info structure only by mangled name + Search is a O(log #types) + + We start searching at module start, and finish searching when start == end. + Note: if start == end at the beginning of the function, we go all the way around + the circular list. +*/ +SWIGRUNTIME swig_type_info * +SWIG_MangledTypeQueryModule(swig_module_info *start, + swig_module_info *end, + const char *name) { + swig_module_info *iter = start; + do { + if (iter->size) { + size_t l = 0; + size_t r = iter->size - 1; + do { + /* since l+r >= 0, we can (>> 1) instead (/ 2) */ + size_t i = (l + r) >> 1; + const char *iname = iter->types[i]->name; + if (iname) { + int compare = strcmp(name, iname); + if (compare == 0) { + return iter->types[i]; + } else if (compare < 0) { + if (i) { + r = i - 1; + } else { + break; + } + } else if (compare > 0) { + l = i + 1; + } + } else { + break; /* should never happen */ + } + } while (l <= r); + } + iter = iter->next; + } while (iter != end); + return 0; +} + +/* + Search for a swig_type_info structure for either a mangled name or a human readable name. + It first searches the mangled names of the types, which is a O(log #types) + If a type is not found it then searches the human readable names, which is O(#types). + + We start searching at module start, and finish searching when start == end. + Note: if start == end at the beginning of the function, we go all the way around + the circular list. +*/ +SWIGRUNTIME swig_type_info * +SWIG_TypeQueryModule(swig_module_info *start, + swig_module_info *end, + const char *name) { + /* STEP 1: Search the name field using binary search */ + swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name); + if (ret) { + return ret; + } else { + /* STEP 2: If the type hasn't been found, do a complete search + of the str field (the human readable name) */ + swig_module_info *iter = start; + do { + size_t i = 0; + for (; i < iter->size; ++i) { + if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name))) + return iter->types[i]; + } + iter = iter->next; + } while (iter != end); + } + + /* neither found a match */ + return 0; +} + +/* + Pack binary data into a string +*/ +SWIGRUNTIME char * +SWIG_PackData(char *c, void *ptr, size_t sz) { + static const char hex[17] = "0123456789abcdef"; + const unsigned char *u = (unsigned char *) ptr; + const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + unsigned char uu = *u; + *(c++) = hex[(uu & 0xf0) >> 4]; + *(c++) = hex[uu & 0xf]; + } + return c; +} + +/* + Unpack binary data from a string +*/ +SWIGRUNTIME const char * +SWIG_UnpackData(const char *c, void *ptr, size_t sz) { + unsigned char *u = (unsigned char *) ptr; + const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + char d = *(c++); + unsigned char uu; + if ((d >= '0') && (d <= '9')) + uu = (unsigned char)((d - '0') << 4); + else if ((d >= 'a') && (d <= 'f')) + uu = (unsigned char)((d - ('a'-10)) << 4); + else + return (char *) 0; + d = *(c++); + if ((d >= '0') && (d <= '9')) + uu |= (unsigned char)(d - '0'); + else if ((d >= 'a') && (d <= 'f')) + uu |= (unsigned char)(d - ('a'-10)); + else + return (char *) 0; + *u = uu; + } + return c; +} + +/* + Pack 'void *' into a string buffer. +*/ +SWIGRUNTIME char * +SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) { + char *r = buff; + if ((2*sizeof(void *) + 2) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,&ptr,sizeof(void *)); + if (strlen(name) + 1 > (bsz - (r - buff))) return 0; + strcpy(r,name); + return buff; +} + +SWIGRUNTIME const char * +SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) { + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + *ptr = (void *) 0; + return name; + } else { + return 0; + } + } + return SWIG_UnpackData(++c,ptr,sizeof(void *)); +} + +SWIGRUNTIME char * +SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) { + char *r = buff; + size_t lname = (name ? strlen(name) : 0); + if ((2*sz + 2 + lname) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,ptr,sz); + if (lname) { + strncpy(r,name,lname+1); + } else { + *r = 0; + } + return buff; +} + +SWIGRUNTIME const char * +SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) { + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + memset(ptr,0,sz); + return name; + } else { + return 0; + } + } + return SWIG_UnpackData(++c,ptr,sz); +} + +#ifdef __cplusplus +} +#endif + +/* Errors in SWIG */ +#define SWIG_UnknownError -1 +#define SWIG_IOError -2 +#define SWIG_RuntimeError -3 +#define SWIG_IndexError -4 +#define SWIG_TypeError -5 +#define SWIG_DivisionByZero -6 +#define SWIG_OverflowError -7 +#define SWIG_SyntaxError -8 +#define SWIG_ValueError -9 +#define SWIG_SystemError -10 +#define SWIG_AttributeError -11 +#define SWIG_MemoryError -12 +#define SWIG_NullReferenceError -13 + + + +/* Compatibility macros for Python 3 */ +#if PY_VERSION_HEX >= 0x03000000 + +#define PyClass_Check(obj) PyObject_IsInstance(obj, (PyObject *)&PyType_Type) +#define PyInt_Check(x) PyLong_Check(x) +#define PyInt_AsLong(x) PyLong_AsLong(x) +#define PyInt_FromLong(x) PyLong_FromLong(x) +#define PyInt_FromSize_t(x) PyLong_FromSize_t(x) +#define PyString_Check(name) PyBytes_Check(name) +#define PyString_FromString(x) PyUnicode_FromString(x) +#define PyString_Format(fmt, args) PyUnicode_Format(fmt, args) +#define PyString_AsString(str) PyBytes_AsString(str) +#define PyString_Size(str) PyBytes_Size(str) +#define PyString_InternFromString(key) PyUnicode_InternFromString(key) +#define Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_BASETYPE +#define PyString_AS_STRING(x) PyUnicode_AS_STRING(x) +#define _PyLong_FromSsize_t(x) PyLong_FromSsize_t(x) + +#endif + +#ifndef Py_TYPE +# define Py_TYPE(op) ((op)->ob_type) +#endif + +/* SWIG APIs for compatibility of both Python 2 & 3 */ + +#if PY_VERSION_HEX >= 0x03000000 +# define SWIG_Python_str_FromFormat PyUnicode_FromFormat +#else +# define SWIG_Python_str_FromFormat PyString_FromFormat +#endif + + +/* Warning: This function will allocate a new string in Python 3, + * so please call SWIG_Python_str_DelForPy3(x) to free the space. + */ +SWIGINTERN char* +SWIG_Python_str_AsChar(PyObject *str) +{ +#if PY_VERSION_HEX >= 0x03000000 + char *cstr; + char *newstr; + Py_ssize_t len; + str = PyUnicode_AsUTF8String(str); + PyBytes_AsStringAndSize(str, &cstr, &len); + newstr = (char *) malloc(len+1); + memcpy(newstr, cstr, len+1); + Py_XDECREF(str); + return newstr; +#else + return PyString_AsString(str); +#endif +} + +#if PY_VERSION_HEX >= 0x03000000 +# define SWIG_Python_str_DelForPy3(x) free( (void*) (x) ) +#else +# define SWIG_Python_str_DelForPy3(x) +#endif + + +SWIGINTERN PyObject* +SWIG_Python_str_FromChar(const char *c) +{ +#if PY_VERSION_HEX >= 0x03000000 + return PyUnicode_FromString(c); +#else + return PyString_FromString(c); +#endif +} + +/* Add PyOS_snprintf for old Pythons */ +#if PY_VERSION_HEX < 0x02020000 +# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WATCOM) +# define PyOS_snprintf _snprintf +# else +# define PyOS_snprintf snprintf +# endif +#endif + +/* A crude PyString_FromFormat implementation for old Pythons */ +#if PY_VERSION_HEX < 0x02020000 + +#ifndef SWIG_PYBUFFER_SIZE +# define SWIG_PYBUFFER_SIZE 1024 +#endif + +static PyObject * +PyString_FromFormat(const char *fmt, ...) { + va_list ap; + char buf[SWIG_PYBUFFER_SIZE * 2]; + int res; + va_start(ap, fmt); + res = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + return (res < 0 || res >= (int)sizeof(buf)) ? 0 : PyString_FromString(buf); +} +#endif + +#ifndef PyObject_DEL +# define PyObject_DEL PyObject_Del +#endif + +/* A crude PyExc_StopIteration exception for old Pythons */ +#if PY_VERSION_HEX < 0x02020000 +# ifndef PyExc_StopIteration +# define PyExc_StopIteration PyExc_RuntimeError +# endif +# ifndef PyObject_GenericGetAttr +# define PyObject_GenericGetAttr 0 +# endif +#endif + +/* Py_NotImplemented is defined in 2.1 and up. */ +#if PY_VERSION_HEX < 0x02010000 +# ifndef Py_NotImplemented +# define Py_NotImplemented PyExc_RuntimeError +# endif +#endif + +/* A crude PyString_AsStringAndSize implementation for old Pythons */ +#if PY_VERSION_HEX < 0x02010000 +# ifndef PyString_AsStringAndSize +# define PyString_AsStringAndSize(obj, s, len) {*s = PyString_AsString(obj); *len = *s ? strlen(*s) : 0;} +# endif +#endif + +/* PySequence_Size for old Pythons */ +#if PY_VERSION_HEX < 0x02000000 +# ifndef PySequence_Size +# define PySequence_Size PySequence_Length +# endif +#endif + +/* PyBool_FromLong for old Pythons */ +#if PY_VERSION_HEX < 0x02030000 +static +PyObject *PyBool_FromLong(long ok) +{ + PyObject *result = ok ? Py_True : Py_False; + Py_INCREF(result); + return result; +} +#endif + +/* Py_ssize_t for old Pythons */ +/* This code is as recommended by: */ +/* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */ +#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) +typedef int Py_ssize_t; +# define PY_SSIZE_T_MAX INT_MAX +# define PY_SSIZE_T_MIN INT_MIN +typedef inquiry lenfunc; +typedef intargfunc ssizeargfunc; +typedef intintargfunc ssizessizeargfunc; +typedef intobjargproc ssizeobjargproc; +typedef intintobjargproc ssizessizeobjargproc; +typedef getreadbufferproc readbufferproc; +typedef getwritebufferproc writebufferproc; +typedef getsegcountproc segcountproc; +typedef getcharbufferproc charbufferproc; +static long PyNumber_AsSsize_t (PyObject *x, void *SWIGUNUSEDPARM(exc)) +{ + long result = 0; + PyObject *i = PyNumber_Int(x); + if (i) { + result = PyInt_AsLong(i); + Py_DECREF(i); + } + return result; +} +#endif + +#if PY_VERSION_HEX < 0x02050000 +#define PyInt_FromSize_t(x) PyInt_FromLong((long)x) +#endif + +#if PY_VERSION_HEX < 0x02040000 +#define Py_VISIT(op) \ + do { \ + if (op) { \ + int vret = visit((op), arg); \ + if (vret) \ + return vret; \ + } \ + } while (0) +#endif + +#if PY_VERSION_HEX < 0x02030000 +typedef struct { + PyTypeObject type; + PyNumberMethods as_number; + PyMappingMethods as_mapping; + PySequenceMethods as_sequence; + PyBufferProcs as_buffer; + PyObject *name, *slots; +} PyHeapTypeObject; +#endif + +#if PY_VERSION_HEX < 0x02030000 +typedef destructor freefunc; +#endif + +#if ((PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 6) || \ + (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION > 0) || \ + (PY_MAJOR_VERSION > 3)) +# define SWIGPY_USE_CAPSULE +# define SWIGPY_CAPSULE_NAME ((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION ".type_pointer_capsule" SWIG_TYPE_TABLE_NAME) +#endif + +#if PY_VERSION_HEX < 0x03020000 +#define PyDescr_TYPE(x) (((PyDescrObject *)(x))->d_type) +#define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name) +#define Py_hash_t long +#endif + +/* ----------------------------------------------------------------------------- + * error manipulation + * ----------------------------------------------------------------------------- */ + +SWIGRUNTIME PyObject* +SWIG_Python_ErrorType(int code) { + PyObject* type = 0; + switch(code) { + case SWIG_MemoryError: + type = PyExc_MemoryError; + break; + case SWIG_IOError: + type = PyExc_IOError; + break; + case SWIG_RuntimeError: + type = PyExc_RuntimeError; + break; + case SWIG_IndexError: + type = PyExc_IndexError; + break; + case SWIG_TypeError: + type = PyExc_TypeError; + break; + case SWIG_DivisionByZero: + type = PyExc_ZeroDivisionError; + break; + case SWIG_OverflowError: + type = PyExc_OverflowError; + break; + case SWIG_SyntaxError: + type = PyExc_SyntaxError; + break; + case SWIG_ValueError: + type = PyExc_ValueError; + break; + case SWIG_SystemError: + type = PyExc_SystemError; + break; + case SWIG_AttributeError: + type = PyExc_AttributeError; + break; + default: + type = PyExc_RuntimeError; + } + return type; +} + + +SWIGRUNTIME void +SWIG_Python_AddErrorMsg(const char* mesg) +{ + PyObject *type = 0; + PyObject *value = 0; + PyObject *traceback = 0; + + if (PyErr_Occurred()) PyErr_Fetch(&type, &value, &traceback); + if (value) { + char *tmp; + PyObject *old_str = PyObject_Str(value); + PyErr_Clear(); + Py_XINCREF(type); + + PyErr_Format(type, "%s %s", tmp = SWIG_Python_str_AsChar(old_str), mesg); + SWIG_Python_str_DelForPy3(tmp); + Py_DECREF(old_str); + Py_DECREF(value); + } else { + PyErr_SetString(PyExc_RuntimeError, mesg); + } +} + +#if defined(SWIG_PYTHON_NO_THREADS) +# if defined(SWIG_PYTHON_THREADS) +# undef SWIG_PYTHON_THREADS +# endif +#endif +#if defined(SWIG_PYTHON_THREADS) /* Threading support is enabled */ +# if !defined(SWIG_PYTHON_USE_GIL) && !defined(SWIG_PYTHON_NO_USE_GIL) +# if (PY_VERSION_HEX >= 0x02030000) /* For 2.3 or later, use the PyGILState calls */ +# define SWIG_PYTHON_USE_GIL +# endif +# endif +# if defined(SWIG_PYTHON_USE_GIL) /* Use PyGILState threads calls */ +# ifndef SWIG_PYTHON_INITIALIZE_THREADS +# define SWIG_PYTHON_INITIALIZE_THREADS PyEval_InitThreads() +# endif +# ifdef __cplusplus /* C++ code */ + class SWIG_Python_Thread_Block { + bool status; + PyGILState_STATE state; + public: + void end() { if (status) { PyGILState_Release(state); status = false;} } + SWIG_Python_Thread_Block() : status(true), state(PyGILState_Ensure()) {} + ~SWIG_Python_Thread_Block() { end(); } + }; + class SWIG_Python_Thread_Allow { + bool status; + PyThreadState *save; + public: + void end() { if (status) { PyEval_RestoreThread(save); status = false; }} + SWIG_Python_Thread_Allow() : status(true), save(PyEval_SaveThread()) {} + ~SWIG_Python_Thread_Allow() { end(); } + }; +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK SWIG_Python_Thread_Block _swig_thread_block +# define SWIG_PYTHON_THREAD_END_BLOCK _swig_thread_block.end() +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW SWIG_Python_Thread_Allow _swig_thread_allow +# define SWIG_PYTHON_THREAD_END_ALLOW _swig_thread_allow.end() +# else /* C code */ +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK PyGILState_STATE _swig_thread_block = PyGILState_Ensure() +# define SWIG_PYTHON_THREAD_END_BLOCK PyGILState_Release(_swig_thread_block) +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW PyThreadState *_swig_thread_allow = PyEval_SaveThread() +# define SWIG_PYTHON_THREAD_END_ALLOW PyEval_RestoreThread(_swig_thread_allow) +# endif +# else /* Old thread way, not implemented, user must provide it */ +# if !defined(SWIG_PYTHON_INITIALIZE_THREADS) +# define SWIG_PYTHON_INITIALIZE_THREADS +# endif +# if !defined(SWIG_PYTHON_THREAD_BEGIN_BLOCK) +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK +# endif +# if !defined(SWIG_PYTHON_THREAD_END_BLOCK) +# define SWIG_PYTHON_THREAD_END_BLOCK +# endif +# if !defined(SWIG_PYTHON_THREAD_BEGIN_ALLOW) +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW +# endif +# if !defined(SWIG_PYTHON_THREAD_END_ALLOW) +# define SWIG_PYTHON_THREAD_END_ALLOW +# endif +# endif +#else /* No thread support */ +# define SWIG_PYTHON_INITIALIZE_THREADS +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK +# define SWIG_PYTHON_THREAD_END_BLOCK +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW +# define SWIG_PYTHON_THREAD_END_ALLOW +#endif + +/* ----------------------------------------------------------------------------- + * Python API portion that goes into the runtime + * ----------------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------------------------------------------------------- + * Constant declarations + * ----------------------------------------------------------------------------- */ + +/* Constant Types */ +#define SWIG_PY_POINTER 4 +#define SWIG_PY_BINARY 5 + +/* Constant information structure */ +typedef struct swig_const_info { + int type; + char *name; + long lvalue; + double dvalue; + void *pvalue; + swig_type_info **ptype; +} swig_const_info; + + +/* ----------------------------------------------------------------------------- + * Wrapper of PyInstanceMethod_New() used in Python 3 + * It is exported to the generated module, used for -fastproxy + * ----------------------------------------------------------------------------- */ +#if PY_VERSION_HEX >= 0x03000000 +SWIGRUNTIME PyObject* SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func) +{ + return PyInstanceMethod_New(func); +} +#else +SWIGRUNTIME PyObject* SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *SWIGUNUSEDPARM(func)) +{ + return NULL; +} +#endif + +#ifdef __cplusplus +} +#endif + + +/* ----------------------------------------------------------------------------- + * pyrun.swg + * + * This file contains the runtime support for Python modules + * and includes code for managing global variables and pointer + * type checking. + * + * ----------------------------------------------------------------------------- */ + +/* Common SWIG API */ + +/* for raw pointers */ +#define SWIG_Python_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, 0) +#define SWIG_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtr(obj, pptr, type, flags) +#define SWIG_ConvertPtrAndOwn(obj,pptr,type,flags,own) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, own) + +#ifdef SWIGPYTHON_BUILTIN +#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(self, ptr, type, flags) +#else +#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags) +#endif + +#define SWIG_InternalNewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags) + +#define SWIG_CheckImplicit(ty) SWIG_Python_CheckImplicit(ty) +#define SWIG_AcquirePtr(ptr, src) SWIG_Python_AcquirePtr(ptr, src) +#define swig_owntype int + +/* for raw packed data */ +#define SWIG_ConvertPacked(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) +#define SWIG_NewPackedObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) + +/* for class or struct pointers */ +#define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_ConvertPtr(obj, pptr, type, flags) +#define SWIG_NewInstanceObj(ptr, type, flags) SWIG_NewPointerObj(ptr, type, flags) + +/* for C or C++ function pointers */ +#define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_Python_ConvertFunctionPtr(obj, pptr, type) +#define SWIG_NewFunctionPtrObj(ptr, type) SWIG_Python_NewPointerObj(NULL, ptr, type, 0) + +/* for C++ member pointers, ie, member methods */ +#define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) +#define SWIG_NewMemberObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) + + +/* Runtime API */ + +#define SWIG_GetModule(clientdata) SWIG_Python_GetModule(clientdata) +#define SWIG_SetModule(clientdata, pointer) SWIG_Python_SetModule(pointer) +#define SWIG_NewClientData(obj) SwigPyClientData_New(obj) + +#define SWIG_SetErrorObj SWIG_Python_SetErrorObj +#define SWIG_SetErrorMsg SWIG_Python_SetErrorMsg +#define SWIG_ErrorType(code) SWIG_Python_ErrorType(code) +#define SWIG_Error(code, msg) SWIG_Python_SetErrorMsg(SWIG_ErrorType(code), msg) +#define SWIG_fail goto fail + + +/* Runtime API implementation */ + +/* Error manipulation */ + +SWIGINTERN void +SWIG_Python_SetErrorObj(PyObject *errtype, PyObject *obj) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + PyErr_SetObject(errtype, obj); + Py_DECREF(obj); + SWIG_PYTHON_THREAD_END_BLOCK; +} + +SWIGINTERN void +SWIG_Python_SetErrorMsg(PyObject *errtype, const char *msg) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + PyErr_SetString(errtype, msg); + SWIG_PYTHON_THREAD_END_BLOCK; +} + +#define SWIG_Python_Raise(obj, type, desc) SWIG_Python_SetErrorObj(SWIG_Python_ExceptionType(desc), obj) + +/* Set a constant value */ + +#if defined(SWIGPYTHON_BUILTIN) + +SWIGINTERN void +SwigPyBuiltin_AddPublicSymbol(PyObject *seq, const char *key) { + PyObject *s = PyString_InternFromString(key); + PyList_Append(seq, s); + Py_DECREF(s); +} + +SWIGINTERN void +SWIG_Python_SetConstant(PyObject *d, PyObject *public_interface, const char *name, PyObject *obj) { +#if PY_VERSION_HEX < 0x02030000 + PyDict_SetItemString(d, (char *)name, obj); +#else + PyDict_SetItemString(d, name, obj); +#endif + Py_DECREF(obj); + if (public_interface) + SwigPyBuiltin_AddPublicSymbol(public_interface, name); +} + +#else + +SWIGINTERN void +SWIG_Python_SetConstant(PyObject *d, const char *name, PyObject *obj) { +#if PY_VERSION_HEX < 0x02030000 + PyDict_SetItemString(d, (char *)name, obj); +#else + PyDict_SetItemString(d, name, obj); +#endif + Py_DECREF(obj); +} + +#endif + +/* Append a value to the result obj */ + +SWIGINTERN PyObject* +SWIG_Python_AppendOutput(PyObject* result, PyObject* obj) { +#if !defined(SWIG_PYTHON_OUTPUT_TUPLE) + if (!result) { + result = obj; + } else if (result == Py_None) { + Py_DECREF(result); + result = obj; + } else { + if (!PyList_Check(result)) { + PyObject *o2 = result; + result = PyList_New(1); + PyList_SetItem(result, 0, o2); + } + PyList_Append(result,obj); + Py_DECREF(obj); + } + return result; +#else + PyObject* o2; + PyObject* o3; + if (!result) { + result = obj; + } else if (result == Py_None) { + Py_DECREF(result); + result = obj; + } else { + if (!PyTuple_Check(result)) { + o2 = result; + result = PyTuple_New(1); + PyTuple_SET_ITEM(result, 0, o2); + } + o3 = PyTuple_New(1); + PyTuple_SET_ITEM(o3, 0, obj); + o2 = result; + result = PySequence_Concat(o2, o3); + Py_DECREF(o2); + Py_DECREF(o3); + } + return result; +#endif +} + +/* Unpack the argument tuple */ + +SWIGINTERN Py_ssize_t +SWIG_Python_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, PyObject **objs) +{ + if (!args) { + if (!min && !max) { + return 1; + } else { + PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got none", + name, (min == max ? "" : "at least "), (int)min); + return 0; + } + } + if (!PyTuple_Check(args)) { + if (min <= 1 && max >= 1) { + Py_ssize_t i; + objs[0] = args; + for (i = 1; i < max; ++i) { + objs[i] = 0; + } + return 2; + } + PyErr_SetString(PyExc_SystemError, "UnpackTuple() argument list is not a tuple"); + return 0; + } else { + Py_ssize_t l = PyTuple_GET_SIZE(args); + if (l < min) { + PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", + name, (min == max ? "" : "at least "), (int)min, (int)l); + return 0; + } else if (l > max) { + PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", + name, (min == max ? "" : "at most "), (int)max, (int)l); + return 0; + } else { + Py_ssize_t i; + for (i = 0; i < l; ++i) { + objs[i] = PyTuple_GET_ITEM(args, i); + } + for (; l < max; ++l) { + objs[l] = 0; + } + return i + 1; + } + } +} + +/* A functor is a function object with one single object argument */ +#if PY_VERSION_HEX >= 0x02020000 +#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunctionObjArgs(functor, obj, NULL); +#else +#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunction(functor, "O", obj); +#endif + +/* + Helper for static pointer initialization for both C and C++ code, for example + static PyObject *SWIG_STATIC_POINTER(MyVar) = NewSomething(...); +*/ +#ifdef __cplusplus +#define SWIG_STATIC_POINTER(var) var +#else +#define SWIG_STATIC_POINTER(var) var = 0; if (!var) var +#endif + +/* ----------------------------------------------------------------------------- + * Pointer declarations + * ----------------------------------------------------------------------------- */ + +/* Flags for new pointer objects */ +#define SWIG_POINTER_NOSHADOW (SWIG_POINTER_OWN << 1) +#define SWIG_POINTER_NEW (SWIG_POINTER_NOSHADOW | SWIG_POINTER_OWN) + +#define SWIG_POINTER_IMPLICIT_CONV (SWIG_POINTER_DISOWN << 1) + +#define SWIG_BUILTIN_TP_INIT (SWIG_POINTER_OWN << 2) +#define SWIG_BUILTIN_INIT (SWIG_BUILTIN_TP_INIT | SWIG_POINTER_OWN) + +#ifdef __cplusplus +extern "C" { +#endif + +/* How to access Py_None */ +#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# ifndef SWIG_PYTHON_NO_BUILD_NONE +# ifndef SWIG_PYTHON_BUILD_NONE +# define SWIG_PYTHON_BUILD_NONE +# endif +# endif +#endif + +#ifdef SWIG_PYTHON_BUILD_NONE +# ifdef Py_None +# undef Py_None +# define Py_None SWIG_Py_None() +# endif +SWIGRUNTIMEINLINE PyObject * +_SWIG_Py_None(void) +{ + PyObject *none = Py_BuildValue((char*)""); + Py_DECREF(none); + return none; +} +SWIGRUNTIME PyObject * +SWIG_Py_None(void) +{ + static PyObject *SWIG_STATIC_POINTER(none) = _SWIG_Py_None(); + return none; +} +#endif + +/* The python void return value */ + +SWIGRUNTIMEINLINE PyObject * +SWIG_Py_Void(void) +{ + PyObject *none = Py_None; + Py_INCREF(none); + return none; +} + +/* SwigPyClientData */ + +typedef struct { + PyObject *klass; + PyObject *newraw; + PyObject *newargs; + PyObject *destroy; + int delargs; + int implicitconv; + PyTypeObject *pytype; +} SwigPyClientData; + +SWIGRUNTIMEINLINE int +SWIG_Python_CheckImplicit(swig_type_info *ty) +{ + SwigPyClientData *data = (SwigPyClientData *)ty->clientdata; + return data ? data->implicitconv : 0; +} + +SWIGRUNTIMEINLINE PyObject * +SWIG_Python_ExceptionType(swig_type_info *desc) { + SwigPyClientData *data = desc ? (SwigPyClientData *) desc->clientdata : 0; + PyObject *klass = data ? data->klass : 0; + return (klass ? klass : PyExc_RuntimeError); +} + + +SWIGRUNTIME SwigPyClientData * +SwigPyClientData_New(PyObject* obj) +{ + if (!obj) { + return 0; + } else { + SwigPyClientData *data = (SwigPyClientData *)malloc(sizeof(SwigPyClientData)); + /* the klass element */ + data->klass = obj; + Py_INCREF(data->klass); + /* the newraw method and newargs arguments used to create a new raw instance */ + if (PyClass_Check(obj)) { + data->newraw = 0; + data->newargs = obj; + Py_INCREF(obj); + } else { +#if (PY_VERSION_HEX < 0x02020000) + data->newraw = 0; +#else + data->newraw = PyObject_GetAttrString(data->klass, (char *)"__new__"); +#endif + if (data->newraw) { + Py_INCREF(data->newraw); + data->newargs = PyTuple_New(1); + PyTuple_SetItem(data->newargs, 0, obj); + } else { + data->newargs = obj; + } + Py_INCREF(data->newargs); + } + /* the destroy method, aka as the C++ delete method */ + data->destroy = PyObject_GetAttrString(data->klass, (char *)"__swig_destroy__"); + if (PyErr_Occurred()) { + PyErr_Clear(); + data->destroy = 0; + } + if (data->destroy) { + int flags; + Py_INCREF(data->destroy); + flags = PyCFunction_GET_FLAGS(data->destroy); +#ifdef METH_O + data->delargs = !(flags & (METH_O)); +#else + data->delargs = 0; +#endif + } else { + data->delargs = 0; + } + data->implicitconv = 0; + data->pytype = 0; + return data; + } +} + +SWIGRUNTIME void +SwigPyClientData_Del(SwigPyClientData *data) { + Py_XDECREF(data->newraw); + Py_XDECREF(data->newargs); + Py_XDECREF(data->destroy); +} + +/* =============== SwigPyObject =====================*/ + +typedef struct { + PyObject_HEAD + void *ptr; + swig_type_info *ty; + int own; + PyObject *next; +#ifdef SWIGPYTHON_BUILTIN + PyObject *dict; +#endif +} SwigPyObject; + + +#ifdef SWIGPYTHON_BUILTIN + +SWIGRUNTIME PyObject * +SwigPyObject_get___dict__(PyObject *v, PyObject *SWIGUNUSEDPARM(args)) +{ + SwigPyObject *sobj = (SwigPyObject *)v; + + if (!sobj->dict) + sobj->dict = PyDict_New(); + + Py_INCREF(sobj->dict); + return sobj->dict; +} + +#endif + +SWIGRUNTIME PyObject * +SwigPyObject_long(SwigPyObject *v) +{ + return PyLong_FromVoidPtr(v->ptr); +} + +SWIGRUNTIME PyObject * +SwigPyObject_format(const char* fmt, SwigPyObject *v) +{ + PyObject *res = NULL; + PyObject *args = PyTuple_New(1); + if (args) { + if (PyTuple_SetItem(args, 0, SwigPyObject_long(v)) == 0) { + PyObject *ofmt = SWIG_Python_str_FromChar(fmt); + if (ofmt) { +#if PY_VERSION_HEX >= 0x03000000 + res = PyUnicode_Format(ofmt,args); +#else + res = PyString_Format(ofmt,args); +#endif + Py_DECREF(ofmt); + } + Py_DECREF(args); + } + } + return res; +} + +SWIGRUNTIME PyObject * +SwigPyObject_oct(SwigPyObject *v) +{ + return SwigPyObject_format("%o",v); +} + +SWIGRUNTIME PyObject * +SwigPyObject_hex(SwigPyObject *v) +{ + return SwigPyObject_format("%x",v); +} + +SWIGRUNTIME PyObject * +#ifdef METH_NOARGS +SwigPyObject_repr(SwigPyObject *v) +#else +SwigPyObject_repr(SwigPyObject *v, PyObject *args) +#endif +{ + const char *name = SWIG_TypePrettyName(v->ty); + PyObject *repr = SWIG_Python_str_FromFormat("", (name ? name : "unknown"), (void *)v); + if (v->next) { +# ifdef METH_NOARGS + PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next); +# else + PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next, args); +# endif +# if PY_VERSION_HEX >= 0x03000000 + PyObject *joined = PyUnicode_Concat(repr, nrep); + Py_DecRef(repr); + Py_DecRef(nrep); + repr = joined; +# else + PyString_ConcatAndDel(&repr,nrep); +# endif + } + return repr; +} + +SWIGRUNTIME int +SwigPyObject_compare(SwigPyObject *v, SwigPyObject *w) +{ + void *i = v->ptr; + void *j = w->ptr; + return (i < j) ? -1 : ((i > j) ? 1 : 0); +} + +/* Added for Python 3.x, would it also be useful for Python 2.x? */ +SWIGRUNTIME PyObject* +SwigPyObject_richcompare(SwigPyObject *v, SwigPyObject *w, int op) +{ + PyObject* res; + if( op != Py_EQ && op != Py_NE ) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + res = PyBool_FromLong( (SwigPyObject_compare(v, w)==0) == (op == Py_EQ) ? 1 : 0); + return res; +} + + +SWIGRUNTIME PyTypeObject* SwigPyObject_TypeOnce(void); + +#ifdef SWIGPYTHON_BUILTIN +static swig_type_info *SwigPyObject_stype = 0; +SWIGRUNTIME PyTypeObject* +SwigPyObject_type(void) { + SwigPyClientData *cd; + assert(SwigPyObject_stype); + cd = (SwigPyClientData*) SwigPyObject_stype->clientdata; + assert(cd); + assert(cd->pytype); + return cd->pytype; +} +#else +SWIGRUNTIME PyTypeObject* +SwigPyObject_type(void) { + static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyObject_TypeOnce(); + return type; +} +#endif + +SWIGRUNTIMEINLINE int +SwigPyObject_Check(PyObject *op) { +#ifdef SWIGPYTHON_BUILTIN + PyTypeObject *target_tp = SwigPyObject_type(); + if (PyType_IsSubtype(op->ob_type, target_tp)) + return 1; + return (strcmp(op->ob_type->tp_name, "SwigPyObject") == 0); +#else + return (Py_TYPE(op) == SwigPyObject_type()) + || (strcmp(Py_TYPE(op)->tp_name,"SwigPyObject") == 0); +#endif +} + +SWIGRUNTIME PyObject * +SwigPyObject_New(void *ptr, swig_type_info *ty, int own); + +SWIGRUNTIME void +SwigPyObject_dealloc(PyObject *v) +{ + SwigPyObject *sobj = (SwigPyObject *) v; + PyObject *next = sobj->next; + if (sobj->own == SWIG_POINTER_OWN) { + swig_type_info *ty = sobj->ty; + SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; + PyObject *destroy = data ? data->destroy : 0; + if (destroy) { + /* destroy is always a VARARGS method */ + PyObject *res; + + /* PyObject_CallFunction() has the potential to silently drop + the active active exception. In cases of unnamed temporary + variable or where we just finished iterating over a generator + StopIteration will be active right now, and this needs to + remain true upon return from SwigPyObject_dealloc. So save + and restore. */ + + PyObject *val = NULL, *type = NULL, *tb = NULL; + PyErr_Fetch(&val, &type, &tb); + + if (data->delargs) { + /* we need to create a temporary object to carry the destroy operation */ + PyObject *tmp = SwigPyObject_New(sobj->ptr, ty, 0); + res = SWIG_Python_CallFunctor(destroy, tmp); + Py_DECREF(tmp); + } else { + PyCFunction meth = PyCFunction_GET_FUNCTION(destroy); + PyObject *mself = PyCFunction_GET_SELF(destroy); + res = ((*meth)(mself, v)); + } + if (!res) + PyErr_WriteUnraisable(destroy); + + PyErr_Restore(val, type, tb); + + Py_XDECREF(res); + } +#if !defined(SWIG_PYTHON_SILENT_MEMLEAK) + else { + const char *name = SWIG_TypePrettyName(ty); + printf("swig/python detected a memory leak of type '%s', no destructor found.\n", (name ? name : "unknown")); + } +#endif + } + Py_XDECREF(next); + PyObject_DEL(v); +} + +SWIGRUNTIME PyObject* +SwigPyObject_append(PyObject* v, PyObject* next) +{ + SwigPyObject *sobj = (SwigPyObject *) v; +#ifndef METH_O + PyObject *tmp = 0; + if (!PyArg_ParseTuple(next,(char *)"O:append", &tmp)) return NULL; + next = tmp; +#endif + if (!SwigPyObject_Check(next)) { + PyErr_SetString(PyExc_TypeError, "Attempt to append a non SwigPyObject"); + return NULL; + } + sobj->next = next; + Py_INCREF(next); + return SWIG_Py_Void(); +} + +SWIGRUNTIME PyObject* +#ifdef METH_NOARGS +SwigPyObject_next(PyObject* v) +#else +SwigPyObject_next(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +#endif +{ + SwigPyObject *sobj = (SwigPyObject *) v; + if (sobj->next) { + Py_INCREF(sobj->next); + return sobj->next; + } else { + return SWIG_Py_Void(); + } +} + +SWIGINTERN PyObject* +#ifdef METH_NOARGS +SwigPyObject_disown(PyObject *v) +#else +SwigPyObject_disown(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +#endif +{ + SwigPyObject *sobj = (SwigPyObject *)v; + sobj->own = 0; + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject* +#ifdef METH_NOARGS +SwigPyObject_acquire(PyObject *v) +#else +SwigPyObject_acquire(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +#endif +{ + SwigPyObject *sobj = (SwigPyObject *)v; + sobj->own = SWIG_POINTER_OWN; + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject* +SwigPyObject_own(PyObject *v, PyObject *args) +{ + PyObject *val = 0; +#if (PY_VERSION_HEX < 0x02020000) + if (!PyArg_ParseTuple(args,(char *)"|O:own",&val)) +#elif (PY_VERSION_HEX < 0x02050000) + if (!PyArg_UnpackTuple(args, (char *)"own", 0, 1, &val)) +#else + if (!PyArg_UnpackTuple(args, "own", 0, 1, &val)) +#endif + { + return NULL; + } + else + { + SwigPyObject *sobj = (SwigPyObject *)v; + PyObject *obj = PyBool_FromLong(sobj->own); + if (val) { +#ifdef METH_NOARGS + if (PyObject_IsTrue(val)) { + SwigPyObject_acquire(v); + } else { + SwigPyObject_disown(v); + } +#else + if (PyObject_IsTrue(val)) { + SwigPyObject_acquire(v,args); + } else { + SwigPyObject_disown(v,args); + } +#endif + } + return obj; + } +} + +#ifdef METH_O +static PyMethodDef +swigobject_methods[] = { + {(char *)"disown", (PyCFunction)SwigPyObject_disown, METH_NOARGS, (char *)"releases ownership of the pointer"}, + {(char *)"acquire", (PyCFunction)SwigPyObject_acquire, METH_NOARGS, (char *)"acquires ownership of the pointer"}, + {(char *)"own", (PyCFunction)SwigPyObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"}, + {(char *)"append", (PyCFunction)SwigPyObject_append, METH_O, (char *)"appends another 'this' object"}, + {(char *)"next", (PyCFunction)SwigPyObject_next, METH_NOARGS, (char *)"returns the next 'this' object"}, + {(char *)"__repr__",(PyCFunction)SwigPyObject_repr, METH_NOARGS, (char *)"returns object representation"}, + {0, 0, 0, 0} +}; +#else +static PyMethodDef +swigobject_methods[] = { + {(char *)"disown", (PyCFunction)SwigPyObject_disown, METH_VARARGS, (char *)"releases ownership of the pointer"}, + {(char *)"acquire", (PyCFunction)SwigPyObject_acquire, METH_VARARGS, (char *)"acquires ownership of the pointer"}, + {(char *)"own", (PyCFunction)SwigPyObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"}, + {(char *)"append", (PyCFunction)SwigPyObject_append, METH_VARARGS, (char *)"appends another 'this' object"}, + {(char *)"next", (PyCFunction)SwigPyObject_next, METH_VARARGS, (char *)"returns the next 'this' object"}, + {(char *)"__repr__",(PyCFunction)SwigPyObject_repr, METH_VARARGS, (char *)"returns object representation"}, + {0, 0, 0, 0} +}; +#endif + +#if PY_VERSION_HEX < 0x02020000 +SWIGINTERN PyObject * +SwigPyObject_getattr(SwigPyObject *sobj,char *name) +{ + return Py_FindMethod(swigobject_methods, (PyObject *)sobj, name); +} +#endif + +SWIGRUNTIME PyTypeObject* +SwigPyObject_TypeOnce(void) { + static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer"; + + static PyNumberMethods SwigPyObject_as_number = { + (binaryfunc)0, /*nb_add*/ + (binaryfunc)0, /*nb_subtract*/ + (binaryfunc)0, /*nb_multiply*/ + /* nb_divide removed in Python 3 */ +#if PY_VERSION_HEX < 0x03000000 + (binaryfunc)0, /*nb_divide*/ +#endif + (binaryfunc)0, /*nb_remainder*/ + (binaryfunc)0, /*nb_divmod*/ + (ternaryfunc)0,/*nb_power*/ + (unaryfunc)0, /*nb_negative*/ + (unaryfunc)0, /*nb_positive*/ + (unaryfunc)0, /*nb_absolute*/ + (inquiry)0, /*nb_nonzero*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + 0, /*nb_and*/ + 0, /*nb_xor*/ + 0, /*nb_or*/ +#if PY_VERSION_HEX < 0x03000000 + 0, /*nb_coerce*/ +#endif + (unaryfunc)SwigPyObject_long, /*nb_int*/ +#if PY_VERSION_HEX < 0x03000000 + (unaryfunc)SwigPyObject_long, /*nb_long*/ +#else + 0, /*nb_reserved*/ +#endif + (unaryfunc)0, /*nb_float*/ +#if PY_VERSION_HEX < 0x03000000 + (unaryfunc)SwigPyObject_oct, /*nb_oct*/ + (unaryfunc)SwigPyObject_hex, /*nb_hex*/ +#endif +#if PY_VERSION_HEX >= 0x03050000 /* 3.5 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_matrix_multiply */ +#elif PY_VERSION_HEX >= 0x03000000 /* 3.0 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index, nb_inplace_divide removed */ +#elif PY_VERSION_HEX >= 0x02050000 /* 2.5.0 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index */ +#elif PY_VERSION_HEX >= 0x02020000 /* 2.2.0 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_true_divide */ +#elif PY_VERSION_HEX >= 0x02000000 /* 2.0.0 */ + 0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_or */ +#endif + }; + + static PyTypeObject swigpyobject_type; + static int type_init = 0; + if (!type_init) { + const PyTypeObject tmp = { +#if PY_VERSION_HEX >= 0x03000000 + PyVarObject_HEAD_INIT(NULL, 0) +#else + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ +#endif + (char *)"SwigPyObject", /* tp_name */ + sizeof(SwigPyObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)SwigPyObject_dealloc, /* tp_dealloc */ + 0, /* tp_print */ +#if PY_VERSION_HEX < 0x02020000 + (getattrfunc)SwigPyObject_getattr, /* tp_getattr */ +#else + (getattrfunc)0, /* tp_getattr */ +#endif + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03000000 + 0, /* tp_reserved in 3.0.1, tp_compare in 3.0.0 but not used */ +#else + (cmpfunc)SwigPyObject_compare, /* tp_compare */ +#endif + (reprfunc)SwigPyObject_repr, /* tp_repr */ + &SwigPyObject_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + swigobject_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)SwigPyObject_richcompare,/* tp_richcompare */ + 0, /* tp_weaklistoffset */ +#if PY_VERSION_HEX >= 0x02020000 + 0, /* tp_iter */ + 0, /* tp_iternext */ + swigobject_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#endif +#if PY_VERSION_HEX >= 0x02030000 + 0, /* tp_del */ +#endif +#if PY_VERSION_HEX >= 0x02060000 + 0, /* tp_version_tag */ +#endif +#if PY_VERSION_HEX >= 0x03040000 + 0, /* tp_finalize */ +#endif +#ifdef COUNT_ALLOCS + 0, /* tp_allocs */ + 0, /* tp_frees */ + 0, /* tp_maxalloc */ +#if PY_VERSION_HEX >= 0x02050000 + 0, /* tp_prev */ +#endif + 0 /* tp_next */ +#endif + }; + swigpyobject_type = tmp; + type_init = 1; +#if PY_VERSION_HEX < 0x02020000 + swigpyobject_type.ob_type = &PyType_Type; +#else + if (PyType_Ready(&swigpyobject_type) < 0) + return NULL; +#endif + } + return &swigpyobject_type; +} + +SWIGRUNTIME PyObject * +SwigPyObject_New(void *ptr, swig_type_info *ty, int own) +{ + SwigPyObject *sobj = PyObject_NEW(SwigPyObject, SwigPyObject_type()); + if (sobj) { + sobj->ptr = ptr; + sobj->ty = ty; + sobj->own = own; + sobj->next = 0; + } + return (PyObject *)sobj; +} + +/* ----------------------------------------------------------------------------- + * Implements a simple Swig Packed type, and use it instead of string + * ----------------------------------------------------------------------------- */ + +typedef struct { + PyObject_HEAD + void *pack; + swig_type_info *ty; + size_t size; +} SwigPyPacked; + +SWIGRUNTIME int +SwigPyPacked_print(SwigPyPacked *v, FILE *fp, int SWIGUNUSEDPARM(flags)) +{ + char result[SWIG_BUFFER_SIZE]; + fputs("pack, v->size, 0, sizeof(result))) { + fputs("at ", fp); + fputs(result, fp); + } + fputs(v->ty->name,fp); + fputs(">", fp); + return 0; +} + +SWIGRUNTIME PyObject * +SwigPyPacked_repr(SwigPyPacked *v) +{ + char result[SWIG_BUFFER_SIZE]; + if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) { + return SWIG_Python_str_FromFormat("", result, v->ty->name); + } else { + return SWIG_Python_str_FromFormat("", v->ty->name); + } +} + +SWIGRUNTIME PyObject * +SwigPyPacked_str(SwigPyPacked *v) +{ + char result[SWIG_BUFFER_SIZE]; + if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))){ + return SWIG_Python_str_FromFormat("%s%s", result, v->ty->name); + } else { + return SWIG_Python_str_FromChar(v->ty->name); + } +} + +SWIGRUNTIME int +SwigPyPacked_compare(SwigPyPacked *v, SwigPyPacked *w) +{ + size_t i = v->size; + size_t j = w->size; + int s = (i < j) ? -1 : ((i > j) ? 1 : 0); + return s ? s : strncmp((char *)v->pack, (char *)w->pack, 2*v->size); +} + +SWIGRUNTIME PyTypeObject* SwigPyPacked_TypeOnce(void); + +SWIGRUNTIME PyTypeObject* +SwigPyPacked_type(void) { + static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyPacked_TypeOnce(); + return type; +} + +SWIGRUNTIMEINLINE int +SwigPyPacked_Check(PyObject *op) { + return ((op)->ob_type == SwigPyPacked_TypeOnce()) + || (strcmp((op)->ob_type->tp_name,"SwigPyPacked") == 0); +} + +SWIGRUNTIME void +SwigPyPacked_dealloc(PyObject *v) +{ + if (SwigPyPacked_Check(v)) { + SwigPyPacked *sobj = (SwigPyPacked *) v; + free(sobj->pack); + } + PyObject_DEL(v); +} + +SWIGRUNTIME PyTypeObject* +SwigPyPacked_TypeOnce(void) { + static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer"; + static PyTypeObject swigpypacked_type; + static int type_init = 0; + if (!type_init) { + const PyTypeObject tmp = { +#if PY_VERSION_HEX>=0x03000000 + PyVarObject_HEAD_INIT(NULL, 0) +#else + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ +#endif + (char *)"SwigPyPacked", /* tp_name */ + sizeof(SwigPyPacked), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)SwigPyPacked_dealloc, /* tp_dealloc */ + (printfunc)SwigPyPacked_print, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX>=0x03000000 + 0, /* tp_reserved in 3.0.1 */ +#else + (cmpfunc)SwigPyPacked_compare, /* tp_compare */ +#endif + (reprfunc)SwigPyPacked_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)SwigPyPacked_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + swigpacked_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ +#if PY_VERSION_HEX >= 0x02020000 + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#endif +#if PY_VERSION_HEX >= 0x02030000 + 0, /* tp_del */ +#endif +#if PY_VERSION_HEX >= 0x02060000 + 0, /* tp_version_tag */ +#endif +#if PY_VERSION_HEX >= 0x03040000 + 0, /* tp_finalize */ +#endif +#ifdef COUNT_ALLOCS + 0, /* tp_allocs */ + 0, /* tp_frees */ + 0, /* tp_maxalloc */ +#if PY_VERSION_HEX >= 0x02050000 + 0, /* tp_prev */ +#endif + 0 /* tp_next */ +#endif + }; + swigpypacked_type = tmp; + type_init = 1; +#if PY_VERSION_HEX < 0x02020000 + swigpypacked_type.ob_type = &PyType_Type; +#else + if (PyType_Ready(&swigpypacked_type) < 0) + return NULL; +#endif + } + return &swigpypacked_type; +} + +SWIGRUNTIME PyObject * +SwigPyPacked_New(void *ptr, size_t size, swig_type_info *ty) +{ + SwigPyPacked *sobj = PyObject_NEW(SwigPyPacked, SwigPyPacked_type()); + if (sobj) { + void *pack = malloc(size); + if (pack) { + memcpy(pack, ptr, size); + sobj->pack = pack; + sobj->ty = ty; + sobj->size = size; + } else { + PyObject_DEL((PyObject *) sobj); + sobj = 0; + } + } + return (PyObject *) sobj; +} + +SWIGRUNTIME swig_type_info * +SwigPyPacked_UnpackData(PyObject *obj, void *ptr, size_t size) +{ + if (SwigPyPacked_Check(obj)) { + SwigPyPacked *sobj = (SwigPyPacked *)obj; + if (sobj->size != size) return 0; + memcpy(ptr, sobj->pack, size); + return sobj->ty; + } else { + return 0; + } +} + +/* ----------------------------------------------------------------------------- + * pointers/data manipulation + * ----------------------------------------------------------------------------- */ + +SWIGRUNTIMEINLINE PyObject * +_SWIG_This(void) +{ + return SWIG_Python_str_FromChar("this"); +} + +static PyObject *swig_this = NULL; + +SWIGRUNTIME PyObject * +SWIG_This(void) +{ + if (swig_this == NULL) + swig_this = _SWIG_This(); + return swig_this; +} + +/* #define SWIG_PYTHON_SLOW_GETSET_THIS */ + +/* TODO: I don't know how to implement the fast getset in Python 3 right now */ +#if PY_VERSION_HEX>=0x03000000 +#define SWIG_PYTHON_SLOW_GETSET_THIS +#endif + +SWIGRUNTIME SwigPyObject * +SWIG_Python_GetSwigThis(PyObject *pyobj) +{ + PyObject *obj; + + if (SwigPyObject_Check(pyobj)) + return (SwigPyObject *) pyobj; + +#ifdef SWIGPYTHON_BUILTIN + (void)obj; +# ifdef PyWeakref_CheckProxy + if (PyWeakref_CheckProxy(pyobj)) { + pyobj = PyWeakref_GET_OBJECT(pyobj); + if (pyobj && SwigPyObject_Check(pyobj)) + return (SwigPyObject*) pyobj; + } +# endif + return NULL; +#else + + obj = 0; + +#if (!defined(SWIG_PYTHON_SLOW_GETSET_THIS) && (PY_VERSION_HEX >= 0x02030000)) + if (PyInstance_Check(pyobj)) { + obj = _PyInstance_Lookup(pyobj, SWIG_This()); + } else { + PyObject **dictptr = _PyObject_GetDictPtr(pyobj); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + obj = dict ? PyDict_GetItem(dict, SWIG_This()) : 0; + } else { +#ifdef PyWeakref_CheckProxy + if (PyWeakref_CheckProxy(pyobj)) { + PyObject *wobj = PyWeakref_GET_OBJECT(pyobj); + return wobj ? SWIG_Python_GetSwigThis(wobj) : 0; + } +#endif + obj = PyObject_GetAttr(pyobj,SWIG_This()); + if (obj) { + Py_DECREF(obj); + } else { + if (PyErr_Occurred()) PyErr_Clear(); + return 0; + } + } + } +#else + obj = PyObject_GetAttr(pyobj,SWIG_This()); + if (obj) { + Py_DECREF(obj); + } else { + if (PyErr_Occurred()) PyErr_Clear(); + return 0; + } +#endif + if (obj && !SwigPyObject_Check(obj)) { + /* a PyObject is called 'this', try to get the 'real this' + SwigPyObject from it */ + return SWIG_Python_GetSwigThis(obj); + } + return (SwigPyObject *)obj; +#endif +} + +/* Acquire a pointer value */ + +SWIGRUNTIME int +SWIG_Python_AcquirePtr(PyObject *obj, int own) { + if (own == SWIG_POINTER_OWN) { + SwigPyObject *sobj = SWIG_Python_GetSwigThis(obj); + if (sobj) { + int oldown = sobj->own; + sobj->own = own; + return oldown; + } + } + return 0; +} + +/* Convert a pointer value */ + +SWIGRUNTIME int +SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int flags, int *own) { + int res; + SwigPyObject *sobj; + int implicit_conv = (flags & SWIG_POINTER_IMPLICIT_CONV) != 0; + + if (!obj) + return SWIG_ERROR; + if (obj == Py_None && !implicit_conv) { + if (ptr) + *ptr = 0; + return SWIG_OK; + } + + res = SWIG_ERROR; + + sobj = SWIG_Python_GetSwigThis(obj); + if (own) + *own = 0; + while (sobj) { + void *vptr = sobj->ptr; + if (ty) { + swig_type_info *to = sobj->ty; + if (to == ty) { + /* no type cast needed */ + if (ptr) *ptr = vptr; + break; + } else { + swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); + if (!tc) { + sobj = (SwigPyObject *)sobj->next; + } else { + if (ptr) { + int newmemory = 0; + *ptr = SWIG_TypeCast(tc,vptr,&newmemory); + if (newmemory == SWIG_CAST_NEW_MEMORY) { + assert(own); /* badly formed typemap which will lead to a memory leak - it must set and use own to delete *ptr */ + if (own) + *own = *own | SWIG_CAST_NEW_MEMORY; + } + } + break; + } + } + } else { + if (ptr) *ptr = vptr; + break; + } + } + if (sobj) { + if (own) + *own = *own | sobj->own; + if (flags & SWIG_POINTER_DISOWN) { + sobj->own = 0; + } + res = SWIG_OK; + } else { + if (implicit_conv) { + SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; + if (data && !data->implicitconv) { + PyObject *klass = data->klass; + if (klass) { + PyObject *impconv; + data->implicitconv = 1; /* avoid recursion and call 'explicit' constructors*/ + impconv = SWIG_Python_CallFunctor(klass, obj); + data->implicitconv = 0; + if (PyErr_Occurred()) { + PyErr_Clear(); + impconv = 0; + } + if (impconv) { + SwigPyObject *iobj = SWIG_Python_GetSwigThis(impconv); + if (iobj) { + void *vptr; + res = SWIG_Python_ConvertPtrAndOwn((PyObject*)iobj, &vptr, ty, 0, 0); + if (SWIG_IsOK(res)) { + if (ptr) { + *ptr = vptr; + /* transfer the ownership to 'ptr' */ + iobj->own = 0; + res = SWIG_AddCast(res); + res = SWIG_AddNewMask(res); + } else { + res = SWIG_AddCast(res); + } + } + } + Py_DECREF(impconv); + } + } + } + } + if (!SWIG_IsOK(res) && obj == Py_None) { + if (ptr) + *ptr = 0; + if (PyErr_Occurred()) + PyErr_Clear(); + res = SWIG_OK; + } + } + return res; +} + +/* Convert a function ptr value */ + +SWIGRUNTIME int +SWIG_Python_ConvertFunctionPtr(PyObject *obj, void **ptr, swig_type_info *ty) { + if (!PyCFunction_Check(obj)) { + return SWIG_ConvertPtr(obj, ptr, ty, 0); + } else { + void *vptr = 0; + + /* here we get the method pointer for callbacks */ + const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc); + const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0; + if (desc) + desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0; + if (!desc) + return SWIG_ERROR; + if (ty) { + swig_cast_info *tc = SWIG_TypeCheck(desc,ty); + if (tc) { + int newmemory = 0; + *ptr = SWIG_TypeCast(tc,vptr,&newmemory); + assert(!newmemory); /* newmemory handling not yet implemented */ + } else { + return SWIG_ERROR; + } + } else { + *ptr = vptr; + } + return SWIG_OK; + } +} + +/* Convert a packed value value */ + +SWIGRUNTIME int +SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty) { + swig_type_info *to = SwigPyPacked_UnpackData(obj, ptr, sz); + if (!to) return SWIG_ERROR; + if (ty) { + if (to != ty) { + /* check type cast? */ + swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); + if (!tc) return SWIG_ERROR; + } + } + return SWIG_OK; +} + +/* ----------------------------------------------------------------------------- + * Create a new pointer object + * ----------------------------------------------------------------------------- */ + +/* + Create a new instance object, without calling __init__, and set the + 'this' attribute. +*/ + +SWIGRUNTIME PyObject* +SWIG_Python_NewShadowInstance(SwigPyClientData *data, PyObject *swig_this) +{ +#if (PY_VERSION_HEX >= 0x02020000) + PyObject *inst = 0; + PyObject *newraw = data->newraw; + if (newraw) { + inst = PyObject_Call(newraw, data->newargs, NULL); + if (inst) { +#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS) + PyObject **dictptr = _PyObject_GetDictPtr(inst); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + if (dict == NULL) { + dict = PyDict_New(); + *dictptr = dict; + PyDict_SetItem(dict, SWIG_This(), swig_this); + } + } +#else + PyObject *key = SWIG_This(); + PyObject_SetAttr(inst, key, swig_this); +#endif + } + } else { +#if PY_VERSION_HEX >= 0x03000000 + inst = ((PyTypeObject*) data->newargs)->tp_new((PyTypeObject*) data->newargs, Py_None, Py_None); + if (inst) { + PyObject_SetAttr(inst, SWIG_This(), swig_this); + Py_TYPE(inst)->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; + } +#else + PyObject *dict = PyDict_New(); + if (dict) { + PyDict_SetItem(dict, SWIG_This(), swig_this); + inst = PyInstance_NewRaw(data->newargs, dict); + Py_DECREF(dict); + } +#endif + } + return inst; +#else +#if (PY_VERSION_HEX >= 0x02010000) + PyObject *inst = 0; + PyObject *dict = PyDict_New(); + if (dict) { + PyDict_SetItem(dict, SWIG_This(), swig_this); + inst = PyInstance_NewRaw(data->newargs, dict); + Py_DECREF(dict); + } + return (PyObject *) inst; +#else + PyInstanceObject *inst = PyObject_NEW(PyInstanceObject, &PyInstance_Type); + if (inst == NULL) { + return NULL; + } + inst->in_class = (PyClassObject *)data->newargs; + Py_INCREF(inst->in_class); + inst->in_dict = PyDict_New(); + if (inst->in_dict == NULL) { + Py_DECREF(inst); + return NULL; + } +#ifdef Py_TPFLAGS_HAVE_WEAKREFS + inst->in_weakreflist = NULL; +#endif +#ifdef Py_TPFLAGS_GC + PyObject_GC_Init(inst); +#endif + PyDict_SetItem(inst->in_dict, SWIG_This(), swig_this); + return (PyObject *) inst; +#endif +#endif +} + +SWIGRUNTIME void +SWIG_Python_SetSwigThis(PyObject *inst, PyObject *swig_this) +{ + PyObject *dict; +#if (PY_VERSION_HEX >= 0x02020000) && !defined(SWIG_PYTHON_SLOW_GETSET_THIS) + PyObject **dictptr = _PyObject_GetDictPtr(inst); + if (dictptr != NULL) { + dict = *dictptr; + if (dict == NULL) { + dict = PyDict_New(); + *dictptr = dict; + } + PyDict_SetItem(dict, SWIG_This(), swig_this); + return; + } +#endif + dict = PyObject_GetAttrString(inst, (char*)"__dict__"); + PyDict_SetItem(dict, SWIG_This(), swig_this); + Py_DECREF(dict); +} + + +SWIGINTERN PyObject * +SWIG_Python_InitShadowInstance(PyObject *args) { + PyObject *obj[2]; + if (!SWIG_Python_UnpackTuple(args, "swiginit", 2, 2, obj)) { + return NULL; + } else { + SwigPyObject *sthis = SWIG_Python_GetSwigThis(obj[0]); + if (sthis) { + SwigPyObject_append((PyObject*) sthis, obj[1]); + } else { + SWIG_Python_SetSwigThis(obj[0], obj[1]); + } + return SWIG_Py_Void(); + } +} + +/* Create a new pointer object */ + +SWIGRUNTIME PyObject * +SWIG_Python_NewPointerObj(PyObject *self, void *ptr, swig_type_info *type, int flags) { + SwigPyClientData *clientdata; + PyObject * robj; + int own; + + if (!ptr) + return SWIG_Py_Void(); + + clientdata = type ? (SwigPyClientData *)(type->clientdata) : 0; + own = (flags & SWIG_POINTER_OWN) ? SWIG_POINTER_OWN : 0; + if (clientdata && clientdata->pytype) { + SwigPyObject *newobj; + if (flags & SWIG_BUILTIN_TP_INIT) { + newobj = (SwigPyObject*) self; + if (newobj->ptr) { + PyObject *next_self = clientdata->pytype->tp_alloc(clientdata->pytype, 0); + while (newobj->next) + newobj = (SwigPyObject *) newobj->next; + newobj->next = next_self; + newobj = (SwigPyObject *)next_self; +#ifdef SWIGPYTHON_BUILTIN + newobj->dict = 0; +#endif + } + } else { + newobj = PyObject_New(SwigPyObject, clientdata->pytype); +#ifdef SWIGPYTHON_BUILTIN + newobj->dict = 0; +#endif + } + if (newobj) { + newobj->ptr = ptr; + newobj->ty = type; + newobj->own = own; + newobj->next = 0; + return (PyObject*) newobj; + } + return SWIG_Py_Void(); + } + + assert(!(flags & SWIG_BUILTIN_TP_INIT)); + + robj = SwigPyObject_New(ptr, type, own); + if (robj && clientdata && !(flags & SWIG_POINTER_NOSHADOW)) { + PyObject *inst = SWIG_Python_NewShadowInstance(clientdata, robj); + Py_DECREF(robj); + robj = inst; + } + return robj; +} + +/* Create a new packed object */ + +SWIGRUNTIMEINLINE PyObject * +SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) { + return ptr ? SwigPyPacked_New((void *) ptr, sz, type) : SWIG_Py_Void(); +} + +/* -----------------------------------------------------------------------------* + * Get type list + * -----------------------------------------------------------------------------*/ + +#ifdef SWIG_LINK_RUNTIME +void *SWIG_ReturnGlobalTypeList(void *); +#endif + +SWIGRUNTIME swig_module_info * +SWIG_Python_GetModule(void *SWIGUNUSEDPARM(clientdata)) { + static void *type_pointer = (void *)0; + /* first check if module already created */ + if (!type_pointer) { +#ifdef SWIG_LINK_RUNTIME + type_pointer = SWIG_ReturnGlobalTypeList((void *)0); +#else +# ifdef SWIGPY_USE_CAPSULE + type_pointer = PyCapsule_Import(SWIGPY_CAPSULE_NAME, 0); +# else + type_pointer = PyCObject_Import((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, + (char*)"type_pointer" SWIG_TYPE_TABLE_NAME); +# endif + if (PyErr_Occurred()) { + PyErr_Clear(); + type_pointer = (void *)0; + } +#endif + } + return (swig_module_info *) type_pointer; +} + +#if PY_MAJOR_VERSION < 2 +/* PyModule_AddObject function was introduced in Python 2.0. The following function + is copied out of Python/modsupport.c in python version 2.3.4 */ +SWIGINTERN int +PyModule_AddObject(PyObject *m, char *name, PyObject *o) +{ + PyObject *dict; + if (!PyModule_Check(m)) { + PyErr_SetString(PyExc_TypeError, "PyModule_AddObject() needs module as first arg"); + return SWIG_ERROR; + } + if (!o) { + PyErr_SetString(PyExc_TypeError, "PyModule_AddObject() needs non-NULL value"); + return SWIG_ERROR; + } + + dict = PyModule_GetDict(m); + if (dict == NULL) { + /* Internal error -- modules must have a dict! */ + PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__", + PyModule_GetName(m)); + return SWIG_ERROR; + } + if (PyDict_SetItemString(dict, name, o)) + return SWIG_ERROR; + Py_DECREF(o); + return SWIG_OK; +} +#endif + +SWIGRUNTIME void +#ifdef SWIGPY_USE_CAPSULE +SWIG_Python_DestroyModule(PyObject *obj) +#else +SWIG_Python_DestroyModule(void *vptr) +#endif +{ +#ifdef SWIGPY_USE_CAPSULE + swig_module_info *swig_module = (swig_module_info *) PyCapsule_GetPointer(obj, SWIGPY_CAPSULE_NAME); +#else + swig_module_info *swig_module = (swig_module_info *) vptr; +#endif + swig_type_info **types = swig_module->types; + size_t i; + for (i =0; i < swig_module->size; ++i) { + swig_type_info *ty = types[i]; + if (ty->owndata) { + SwigPyClientData *data = (SwigPyClientData *) ty->clientdata; + if (data) SwigPyClientData_Del(data); + } + } + Py_DECREF(SWIG_This()); + swig_this = NULL; +} + +SWIGRUNTIME void +SWIG_Python_SetModule(swig_module_info *swig_module) { +#if PY_VERSION_HEX >= 0x03000000 + /* Add a dummy module object into sys.modules */ + PyObject *module = PyImport_AddModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION); +#else + static PyMethodDef swig_empty_runtime_method_table[] = { {NULL, NULL, 0, NULL} }; /* Sentinel */ + PyObject *module = Py_InitModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, swig_empty_runtime_method_table); +#endif +#ifdef SWIGPY_USE_CAPSULE + PyObject *pointer = PyCapsule_New((void *) swig_module, SWIGPY_CAPSULE_NAME, SWIG_Python_DestroyModule); + if (pointer && module) { + PyModule_AddObject(module, (char*)"type_pointer_capsule" SWIG_TYPE_TABLE_NAME, pointer); + } else { + Py_XDECREF(pointer); + } +#else + PyObject *pointer = PyCObject_FromVoidPtr((void *) swig_module, SWIG_Python_DestroyModule); + if (pointer && module) { + PyModule_AddObject(module, (char*)"type_pointer" SWIG_TYPE_TABLE_NAME, pointer); + } else { + Py_XDECREF(pointer); + } +#endif +} + +/* The python cached type query */ +SWIGRUNTIME PyObject * +SWIG_Python_TypeCache(void) { + static PyObject *SWIG_STATIC_POINTER(cache) = PyDict_New(); + return cache; +} + +SWIGRUNTIME swig_type_info * +SWIG_Python_TypeQuery(const char *type) +{ + PyObject *cache = SWIG_Python_TypeCache(); + PyObject *key = SWIG_Python_str_FromChar(type); + PyObject *obj = PyDict_GetItem(cache, key); + swig_type_info *descriptor; + if (obj) { +#ifdef SWIGPY_USE_CAPSULE + descriptor = (swig_type_info *) PyCapsule_GetPointer(obj, NULL); +#else + descriptor = (swig_type_info *) PyCObject_AsVoidPtr(obj); +#endif + } else { + swig_module_info *swig_module = SWIG_GetModule(0); + descriptor = SWIG_TypeQueryModule(swig_module, swig_module, type); + if (descriptor) { +#ifdef SWIGPY_USE_CAPSULE + obj = PyCapsule_New((void*) descriptor, NULL, NULL); +#else + obj = PyCObject_FromVoidPtr(descriptor, NULL); +#endif + PyDict_SetItem(cache, key, obj); + Py_DECREF(obj); + } + } + Py_DECREF(key); + return descriptor; +} + +/* + For backward compatibility only +*/ +#define SWIG_POINTER_EXCEPTION 0 +#define SWIG_arg_fail(arg) SWIG_Python_ArgFail(arg) +#define SWIG_MustGetPtr(p, type, argnum, flags) SWIG_Python_MustGetPtr(p, type, argnum, flags) + +SWIGRUNTIME int +SWIG_Python_AddErrMesg(const char* mesg, int infront) +{ + if (PyErr_Occurred()) { + PyObject *type = 0; + PyObject *value = 0; + PyObject *traceback = 0; + PyErr_Fetch(&type, &value, &traceback); + if (value) { + char *tmp; + PyObject *old_str = PyObject_Str(value); + Py_XINCREF(type); + PyErr_Clear(); + if (infront) { + PyErr_Format(type, "%s %s", mesg, tmp = SWIG_Python_str_AsChar(old_str)); + } else { + PyErr_Format(type, "%s %s", tmp = SWIG_Python_str_AsChar(old_str), mesg); + } + SWIG_Python_str_DelForPy3(tmp); + Py_DECREF(old_str); + } + return 1; + } else { + return 0; + } +} + +SWIGRUNTIME int +SWIG_Python_ArgFail(int argnum) +{ + if (PyErr_Occurred()) { + /* add information about failing argument */ + char mesg[256]; + PyOS_snprintf(mesg, sizeof(mesg), "argument number %d:", argnum); + return SWIG_Python_AddErrMesg(mesg, 1); + } else { + return 0; + } +} + +SWIGRUNTIMEINLINE const char * +SwigPyObject_GetDesc(PyObject *self) +{ + SwigPyObject *v = (SwigPyObject *)self; + swig_type_info *ty = v ? v->ty : 0; + return ty ? ty->str : ""; +} + +SWIGRUNTIME void +SWIG_Python_TypeError(const char *type, PyObject *obj) +{ + if (type) { +#if defined(SWIG_COBJECT_TYPES) + if (obj && SwigPyObject_Check(obj)) { + const char *otype = (const char *) SwigPyObject_GetDesc(obj); + if (otype) { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'SwigPyObject(%s)' is received", + type, otype); + return; + } + } else +#endif + { + const char *otype = (obj ? obj->ob_type->tp_name : 0); + if (otype) { + PyObject *str = PyObject_Str(obj); + const char *cstr = str ? SWIG_Python_str_AsChar(str) : 0; + if (cstr) { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received", + type, otype, cstr); + SWIG_Python_str_DelForPy3(cstr); + } else { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received", + type, otype); + } + Py_XDECREF(str); + return; + } + } + PyErr_Format(PyExc_TypeError, "a '%s' is expected", type); + } else { + PyErr_Format(PyExc_TypeError, "unexpected type is received"); + } +} + + +/* Convert a pointer value, signal an exception on a type mismatch */ +SWIGRUNTIME void * +SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int SWIGUNUSEDPARM(argnum), int flags) { + void *result; + if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) { + PyErr_Clear(); +#if SWIG_POINTER_EXCEPTION + if (flags) { + SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj); + SWIG_Python_ArgFail(argnum); + } +#endif + } + return result; +} + +#ifdef SWIGPYTHON_BUILTIN +SWIGRUNTIME int +SWIG_Python_NonDynamicSetAttr(PyObject *obj, PyObject *name, PyObject *value) { + PyTypeObject *tp = obj->ob_type; + PyObject *descr; + PyObject *encoded_name; + descrsetfunc f; + int res = -1; + +# ifdef Py_USING_UNICODE + if (PyString_Check(name)) { + name = PyUnicode_Decode(PyString_AsString(name), PyString_Size(name), NULL, NULL); + if (!name) + return -1; + } else if (!PyUnicode_Check(name)) +# else + if (!PyString_Check(name)) +# endif + { + PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", name->ob_type->tp_name); + return -1; + } else { + Py_INCREF(name); + } + + if (!tp->tp_dict) { + if (PyType_Ready(tp) < 0) + goto done; + } + + descr = _PyType_Lookup(tp, name); + f = NULL; + if (descr != NULL) + f = descr->ob_type->tp_descr_set; + if (!f) { + if (PyString_Check(name)) { + encoded_name = name; + Py_INCREF(name); + } else { + encoded_name = PyUnicode_AsUTF8String(name); + } + PyErr_Format(PyExc_AttributeError, "'%.100s' object has no attribute '%.200s'", tp->tp_name, PyString_AsString(encoded_name)); + Py_DECREF(encoded_name); + } else { + res = f(descr, obj, value); + } + + done: + Py_DECREF(name); + return res; +} +#endif + + +#ifdef __cplusplus +} +#endif + + + +#define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0) + +#define SWIG_contract_assert(expr, msg) if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } else + + +/* ----------------------------------------------------------------------------- + * director_common.swg + * + * This file contains support for director classes which is common between + * languages. + * ----------------------------------------------------------------------------- */ + +/* + Use -DSWIG_DIRECTOR_STATIC if you prefer to avoid the use of the + 'Swig' namespace. This could be useful for multi-modules projects. +*/ +#ifdef SWIG_DIRECTOR_STATIC +/* Force anonymous (static) namespace */ +#define Swig +#endif +/* ----------------------------------------------------------------------------- + * director.swg + * + * This file contains support for director classes so that Python proxy + * methods can be called from C++. + * ----------------------------------------------------------------------------- */ + +#ifndef SWIG_DIRECTOR_PYTHON_HEADER_ +#define SWIG_DIRECTOR_PYTHON_HEADER_ + +#include +#include +#include +#include +#include + + +/* + Use -DSWIG_PYTHON_DIRECTOR_NO_VTABLE if you don't want to generate a 'virtual + table', and avoid multiple GetAttr calls to retrieve the python + methods. +*/ + +#ifndef SWIG_PYTHON_DIRECTOR_NO_VTABLE +#ifndef SWIG_PYTHON_DIRECTOR_VTABLE +#define SWIG_PYTHON_DIRECTOR_VTABLE +#endif +#endif + + + +/* + Use -DSWIG_DIRECTOR_NO_UEH if you prefer to avoid the use of the + Undefined Exception Handler provided by swig. +*/ +#ifndef SWIG_DIRECTOR_NO_UEH +#ifndef SWIG_DIRECTOR_UEH +#define SWIG_DIRECTOR_UEH +#endif +#endif + + +/* + Use -DSWIG_DIRECTOR_NORTTI if you prefer to avoid the use of the + native C++ RTTI and dynamic_cast<>. But be aware that directors + could stop working when using this option. +*/ +#ifdef SWIG_DIRECTOR_NORTTI +/* + When we don't use the native C++ RTTI, we implement a minimal one + only for Directors. +*/ +# ifndef SWIG_DIRECTOR_RTDIR +# define SWIG_DIRECTOR_RTDIR + +namespace Swig { + class Director; + SWIGINTERN std::map& get_rtdir_map() { + static std::map rtdir_map; + return rtdir_map; + } + + SWIGINTERNINLINE void set_rtdir(void *vptr, Director *rtdir) { + get_rtdir_map()[vptr] = rtdir; + } + + SWIGINTERNINLINE Director *get_rtdir(void *vptr) { + std::map::const_iterator pos = get_rtdir_map().find(vptr); + Director *rtdir = (pos != get_rtdir_map().end()) ? pos->second : 0; + return rtdir; + } +} +# endif /* SWIG_DIRECTOR_RTDIR */ + +# define SWIG_DIRECTOR_CAST(ARG) Swig::get_rtdir(static_cast(ARG)) +# define SWIG_DIRECTOR_RGTR(ARG1, ARG2) Swig::set_rtdir(static_cast(ARG1), ARG2) + +#else + +# define SWIG_DIRECTOR_CAST(ARG) dynamic_cast(ARG) +# define SWIG_DIRECTOR_RGTR(ARG1, ARG2) + +#endif /* SWIG_DIRECTOR_NORTTI */ + +extern "C" { + struct swig_type_info; +} + +namespace Swig { + + /* memory handler */ + struct GCItem { + virtual ~GCItem() {} + + virtual int get_own() const { + return 0; + } + }; + + struct GCItem_var { + GCItem_var(GCItem *item = 0) : _item(item) { + } + + GCItem_var& operator=(GCItem *item) { + GCItem *tmp = _item; + _item = item; + delete tmp; + return *this; + } + + ~GCItem_var() { + delete _item; + } + + GCItem * operator->() const { + return _item; + } + + private: + GCItem *_item; + }; + + struct GCItem_Object : GCItem { + GCItem_Object(int own) : _own(own) { + } + + virtual ~GCItem_Object() { + } + + int get_own() const { + return _own; + } + + private: + int _own; + }; + + template + struct GCItem_T : GCItem { + GCItem_T(Type *ptr) : _ptr(ptr) { + } + + virtual ~GCItem_T() { + delete _ptr; + } + + private: + Type *_ptr; + }; + + template + struct GCArray_T : GCItem { + GCArray_T(Type *ptr) : _ptr(ptr) { + } + + virtual ~GCArray_T() { + delete[] _ptr; + } + + private: + Type *_ptr; + }; + + /* base class for director exceptions */ + class DirectorException : public std::exception { + protected: + std::string swig_msg; + public: + DirectorException(PyObject *error, const char *hdr ="", const char *msg ="") : swig_msg(hdr) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + if (msg[0]) { + swig_msg += " "; + swig_msg += msg; + } + if (!PyErr_Occurred()) { + PyErr_SetString(error, what()); + } + SWIG_PYTHON_THREAD_END_BLOCK; + } + + virtual ~DirectorException() throw() { + } + + /* Deprecated, use what() instead */ + const char *getMessage() const { + return what(); + } + + const char *what() const throw() { + return swig_msg.c_str(); + } + + static void raise(PyObject *error, const char *msg) { + throw DirectorException(error, msg); + } + + static void raise(const char *msg) { + raise(PyExc_RuntimeError, msg); + } + }; + + /* unknown exception handler */ + class UnknownExceptionHandler { +#ifdef SWIG_DIRECTOR_UEH + static void handler() { + try { + throw; + } catch (DirectorException& e) { + std::cerr << "SWIG Director exception caught:" << std::endl + << e.what() << std::endl; + } catch (std::exception& e) { + std::cerr << "std::exception caught: "<< e.what() << std::endl; + } catch (...) { + std::cerr << "Unknown exception caught." << std::endl; + } + + std::cerr << std::endl + << "Python interpreter traceback:" << std::endl; + PyErr_Print(); + std::cerr << std::endl; + + std::cerr << "This exception was caught by the SWIG unexpected exception handler." << std::endl + << "Try using %feature(\"director:except\") to avoid reaching this point." << std::endl + << std::endl + << "Exception is being re-thrown, program will likely abort/terminate." << std::endl; + throw; + } + + public: + + std::unexpected_handler old; + UnknownExceptionHandler(std::unexpected_handler nh = handler) { + old = std::set_unexpected(nh); + } + + ~UnknownExceptionHandler() { + std::set_unexpected(old); + } +#endif + }; + + /* type mismatch in the return value from a python method call */ + class DirectorTypeMismatchException : public DirectorException { + public: + DirectorTypeMismatchException(PyObject *error, const char *msg="") + : DirectorException(error, "SWIG director type mismatch", msg) { + } + + DirectorTypeMismatchException(const char *msg="") + : DirectorException(PyExc_TypeError, "SWIG director type mismatch", msg) { + } + + static void raise(PyObject *error, const char *msg) { + throw DirectorTypeMismatchException(error, msg); + } + + static void raise(const char *msg) { + throw DirectorTypeMismatchException(msg); + } + }; + + /* any python exception that occurs during a director method call */ + class DirectorMethodException : public DirectorException { + public: + DirectorMethodException(const char *msg = "") + : DirectorException(PyExc_RuntimeError, "SWIG director method error.", msg) { + } + + static void raise(const char *msg) { + throw DirectorMethodException(msg); + } + }; + + /* attempt to call a pure virtual method via a director method */ + class DirectorPureVirtualException : public DirectorException { + public: + DirectorPureVirtualException(const char *msg = "") + : DirectorException(PyExc_RuntimeError, "SWIG director pure virtual method called", msg) { + } + + static void raise(const char *msg) { + throw DirectorPureVirtualException(msg); + } + }; + + +#if defined(SWIG_PYTHON_THREADS) +/* __THREAD__ is the old macro to activate some thread support */ +# if !defined(__THREAD__) +# define __THREAD__ 1 +# endif +#endif + +#ifdef __THREAD__ +# include "pythread.h" + class Guard { + PyThread_type_lock &mutex_; + + public: + Guard(PyThread_type_lock & mutex) : mutex_(mutex) { + PyThread_acquire_lock(mutex_, WAIT_LOCK); + } + + ~Guard() { + PyThread_release_lock(mutex_); + } + }; +# define SWIG_GUARD(mutex) Guard _guard(mutex) +#else +# define SWIG_GUARD(mutex) +#endif + + /* director base class */ + class Director { + private: + /* pointer to the wrapped python object */ + PyObject *swig_self; + /* flag indicating whether the object is owned by python or c++ */ + mutable bool swig_disown_flag; + + /* decrement the reference count of the wrapped python object */ + void swig_decref() const { + if (swig_disown_flag) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + Py_DECREF(swig_self); + SWIG_PYTHON_THREAD_END_BLOCK; + } + } + + public: + /* wrap a python object. */ + Director(PyObject *self) : swig_self(self), swig_disown_flag(false) { + } + + /* discard our reference at destruction */ + virtual ~Director() { + swig_decref(); + } + + /* return a pointer to the wrapped python object */ + PyObject *swig_get_self() const { + return swig_self; + } + + /* acquire ownership of the wrapped python object (the sense of "disown" is from python) */ + void swig_disown() const { + if (!swig_disown_flag) { + swig_disown_flag=true; + swig_incref(); + } + } + + /* increase the reference count of the wrapped python object */ + void swig_incref() const { + if (swig_disown_flag) { + Py_INCREF(swig_self); + } + } + + /* methods to implement pseudo protected director members */ + virtual bool swig_get_inner(const char * /* swig_protected_method_name */) const { + return true; + } + + virtual void swig_set_inner(const char * /* swig_protected_method_name */, bool /* swig_val */) const { + } + + /* ownership management */ + private: + typedef std::map swig_ownership_map; + mutable swig_ownership_map swig_owner; +#ifdef __THREAD__ + static PyThread_type_lock swig_mutex_own; +#endif + + public: + template + void swig_acquire_ownership_array(Type *vptr) const { + if (vptr) { + SWIG_GUARD(swig_mutex_own); + swig_owner[vptr] = new GCArray_T(vptr); + } + } + + template + void swig_acquire_ownership(Type *vptr) const { + if (vptr) { + SWIG_GUARD(swig_mutex_own); + swig_owner[vptr] = new GCItem_T(vptr); + } + } + + void swig_acquire_ownership_obj(void *vptr, int own) const { + if (vptr && own) { + SWIG_GUARD(swig_mutex_own); + swig_owner[vptr] = new GCItem_Object(own); + } + } + + int swig_release_ownership(void *vptr) const { + int own = 0; + if (vptr) { + SWIG_GUARD(swig_mutex_own); + swig_ownership_map::iterator iter = swig_owner.find(vptr); + if (iter != swig_owner.end()) { + own = iter->second->get_own(); + swig_owner.erase(iter); + } + } + return own; + } + + template + static PyObject *swig_pyobj_disown(PyObject *pyobj, PyObject *SWIGUNUSEDPARM(args)) { + SwigPyObject *sobj = (SwigPyObject *)pyobj; + sobj->own = 0; + Director *d = SWIG_DIRECTOR_CAST(reinterpret_cast(sobj->ptr)); + if (d) + d->swig_disown(); + return PyWeakref_NewProxy(pyobj, NULL); + } + }; + +#ifdef __THREAD__ + PyThread_type_lock Director::swig_mutex_own = PyThread_allocate_lock(); +#endif +} + +#endif + +/* -------- TYPES TABLE (BEGIN) -------- */ + +#define SWIGTYPE_p__kclvm_plugin_AppContextBase swig_types[0] +#define SWIGTYPE_p_char swig_types[1] +#define SWIGTYPE_p_int swig_types[2] +#define SWIGTYPE_p_long_long swig_types[3] +#define SWIGTYPE_p_short swig_types[4] +#define SWIGTYPE_p_signed_char swig_types[5] +#define SWIGTYPE_p_unsigned_char swig_types[6] +#define SWIGTYPE_p_unsigned_int swig_types[7] +#define SWIGTYPE_p_unsigned_long_long swig_types[8] +#define SWIGTYPE_p_unsigned_short swig_types[9] +static swig_type_info *swig_types[11]; +static swig_module_info swig_module = {swig_types, 10, 0, 0, 0, 0}; +#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name) +#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name) + +/* -------- TYPES TABLE (END) -------- */ + +#if (PY_VERSION_HEX <= 0x02000000) +# if !defined(SWIG_PYTHON_CLASSIC) +# error "This python version requires swig to be run with the '-classic' option" +# endif +#endif + +/*----------------------------------------------- + @(target):= _kclvm_plugin.so + ------------------------------------------------*/ +#if PY_VERSION_HEX >= 0x03000000 +# define SWIG_init PyInit__kclvm_plugin + +#else +# define SWIG_init init_kclvm_plugin + +#endif +#define SWIG_name "_kclvm_plugin" + +#define SWIGVERSION 0x030012 +#define SWIG_VERSION SWIGVERSION + + +#define SWIG_as_voidptr(a) const_cast< void * >(static_cast< const void * >(a)) +#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),reinterpret_cast< void** >(a)) + + +#include + + +namespace swig { + class SwigPtr_PyObject { + protected: + PyObject *_obj; + + public: + SwigPtr_PyObject() :_obj(0) + { + } + + SwigPtr_PyObject(const SwigPtr_PyObject& item) : _obj(item._obj) + { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + Py_XINCREF(_obj); + SWIG_PYTHON_THREAD_END_BLOCK; + } + + SwigPtr_PyObject(PyObject *obj, bool initial_ref = true) :_obj(obj) + { + if (initial_ref) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + Py_XINCREF(_obj); + SWIG_PYTHON_THREAD_END_BLOCK; + } + } + + SwigPtr_PyObject & operator=(const SwigPtr_PyObject& item) + { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + Py_XINCREF(item._obj); + Py_XDECREF(_obj); + _obj = item._obj; + SWIG_PYTHON_THREAD_END_BLOCK; + return *this; + } + + ~SwigPtr_PyObject() + { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + Py_XDECREF(_obj); + SWIG_PYTHON_THREAD_END_BLOCK; + } + + operator PyObject *() const + { + return _obj; + } + + PyObject *operator->() const + { + return _obj; + } + }; +} + + +namespace swig { + struct SwigVar_PyObject : SwigPtr_PyObject { + SwigVar_PyObject(PyObject* obj = 0) : SwigPtr_PyObject(obj, false) { } + + SwigVar_PyObject & operator = (PyObject* obj) + { + Py_XDECREF(_obj); + _obj = obj; + return *this; + } + }; +} + + +#define SWIG_FILE_WITH_INIT +#include "kclvm_plugin.h" + + +#include // Use the C99 official header + + +#include + + +SWIGINTERN swig_type_info* +SWIG_pchar_descriptor(void) +{ + static int init = 0; + static swig_type_info* info = 0; + if (!init) { + info = SWIG_TypeQuery("_p_char"); + init = 1; + } + return info; +} + + +SWIGINTERN int +SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc) +{ +#if PY_VERSION_HEX>=0x03000000 +#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) + if (PyBytes_Check(obj)) +#else + if (PyUnicode_Check(obj)) +#endif +#else + if (PyString_Check(obj)) +#endif + { + char *cstr; Py_ssize_t len; +#if PY_VERSION_HEX>=0x03000000 +#if !defined(SWIG_PYTHON_STRICT_BYTE_CHAR) + if (!alloc && cptr) { + /* We can't allow converting without allocation, since the internal + representation of string in Python 3 is UCS-2/UCS-4 but we require + a UTF-8 representation. + TODO(bhy) More detailed explanation */ + return SWIG_RuntimeError; + } + obj = PyUnicode_AsUTF8String(obj); + if(alloc) *alloc = SWIG_NEWOBJ; +#endif + PyBytes_AsStringAndSize(obj, &cstr, &len); +#else + PyString_AsStringAndSize(obj, &cstr, &len); +#endif + if (cptr) { + if (alloc) { + /* + In python the user should not be able to modify the inner + string representation. To warranty that, if you define + SWIG_PYTHON_SAFE_CSTRINGS, a new/copy of the python string + buffer is always returned. + + The default behavior is just to return the pointer value, + so, be careful. + */ +#if defined(SWIG_PYTHON_SAFE_CSTRINGS) + if (*alloc != SWIG_OLDOBJ) +#else + if (*alloc == SWIG_NEWOBJ) +#endif + { + *cptr = reinterpret_cast< char* >(memcpy(new char[len + 1], cstr, sizeof(char)*(len + 1))); + *alloc = SWIG_NEWOBJ; + } else { + *cptr = cstr; + *alloc = SWIG_OLDOBJ; + } + } else { +#if PY_VERSION_HEX>=0x03000000 +#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) + *cptr = PyBytes_AsString(obj); +#else + assert(0); /* Should never reach here with Unicode strings in Python 3 */ +#endif +#else + *cptr = SWIG_Python_str_AsChar(obj); +#endif + } + } + if (psize) *psize = len + 1; +#if PY_VERSION_HEX>=0x03000000 && !defined(SWIG_PYTHON_STRICT_BYTE_CHAR) + Py_XDECREF(obj); +#endif + return SWIG_OK; + } else { +#if defined(SWIG_PYTHON_2_UNICODE) +#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) +#error "Cannot use both SWIG_PYTHON_2_UNICODE and SWIG_PYTHON_STRICT_BYTE_CHAR at once" +#endif +#if PY_VERSION_HEX<0x03000000 + if (PyUnicode_Check(obj)) { + char *cstr; Py_ssize_t len; + if (!alloc && cptr) { + return SWIG_RuntimeError; + } + obj = PyUnicode_AsUTF8String(obj); + if (PyString_AsStringAndSize(obj, &cstr, &len) != -1) { + if (cptr) { + if (alloc) *alloc = SWIG_NEWOBJ; + *cptr = reinterpret_cast< char* >(memcpy(new char[len + 1], cstr, sizeof(char)*(len + 1))); + } + if (psize) *psize = len + 1; + + Py_XDECREF(obj); + return SWIG_OK; + } else { + Py_XDECREF(obj); + } + } +#endif +#endif + + swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); + if (pchar_descriptor) { + void* vptr = 0; + if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) { + if (cptr) *cptr = (char *) vptr; + if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0; + if (alloc) *alloc = SWIG_OLDOBJ; + return SWIG_OK; + } + } + } + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsPtr_std_string (PyObject * obj, std::string **val) +{ + char* buf = 0 ; size_t size = 0; int alloc = SWIG_OLDOBJ; + if (SWIG_IsOK((SWIG_AsCharPtrAndSize(obj, &buf, &size, &alloc)))) { + if (buf) { + if (val) *val = new std::string(buf, size - 1); + if (alloc == SWIG_NEWOBJ) delete[] buf; + return SWIG_NEWOBJ; + } else { + if (val) *val = 0; + return SWIG_OLDOBJ; + } + } else { + static int init = 0; + static swig_type_info* descriptor = 0; + if (!init) { + descriptor = SWIG_TypeQuery("std::string" " *"); + init = 1; + } + if (descriptor) { + std::string *vptr; + int res = SWIG_ConvertPtr(obj, (void**)&vptr, descriptor, 0); + if (SWIG_IsOK(res) && val) *val = vptr; + return res; + } + } + return SWIG_ERROR; +} + + +SWIGINTERNINLINE PyObject * +SWIG_FromCharPtrAndSize(const char* carray, size_t size) +{ + if (carray) { + if (size > INT_MAX) { + swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); + return pchar_descriptor ? + SWIG_InternalNewPointerObj(const_cast< char * >(carray), pchar_descriptor, 0) : SWIG_Py_Void(); + } else { +#if PY_VERSION_HEX >= 0x03000000 +#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) + return PyBytes_FromStringAndSize(carray, static_cast< Py_ssize_t >(size)); +#else +#if PY_VERSION_HEX >= 0x03010000 + return PyUnicode_DecodeUTF8(carray, static_cast< Py_ssize_t >(size), "surrogateescape"); +#else + return PyUnicode_FromStringAndSize(carray, static_cast< Py_ssize_t >(size)); +#endif +#endif +#else + return PyString_FromStringAndSize(carray, static_cast< Py_ssize_t >(size)); +#endif + } + } else { + return SWIG_Py_Void(); + } +} + + +SWIGINTERNINLINE PyObject * +SWIG_From_std_string (const std::string& s) +{ + return SWIG_FromCharPtrAndSize(s.data(), s.size()); +} + + +SWIGINTERN int +SWIG_AsVal_double (PyObject *obj, double *val) +{ + int res = SWIG_TypeError; + if (PyFloat_Check(obj)) { + if (val) *val = PyFloat_AsDouble(obj); + return SWIG_OK; +#if PY_VERSION_HEX < 0x03000000 + } else if (PyInt_Check(obj)) { + if (val) *val = (double) PyInt_AsLong(obj); + return SWIG_OK; +#endif + } else if (PyLong_Check(obj)) { + double v = PyLong_AsDouble(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + double d = PyFloat_AsDouble(obj); + if (!PyErr_Occurred()) { + if (val) *val = d; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + long v = PyLong_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_AddCast(SWIG_OK)); + } else { + PyErr_Clear(); + } + } + } +#endif + return res; +} + + +#include + + +#include + + +SWIGINTERNINLINE int +SWIG_CanCastAsInteger(double *d, double min, double max) { + double x = *d; + if ((min <= x && x <= max)) { + double fx = floor(x); + double cx = ceil(x); + double rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */ + if ((errno == EDOM) || (errno == ERANGE)) { + errno = 0; + } else { + double summ, reps, diff; + if (rd < x) { + diff = x - rd; + } else if (rd > x) { + diff = rd - x; + } else { + return 1; + } + summ = rd + x; + reps = diff/summ; + if (reps < 8*DBL_EPSILON) { + *d = rd; + return 1; + } + } + } + return 0; +} + + +SWIGINTERN int +SWIG_AsVal_unsigned_SS_long (PyObject *obj, unsigned long *val) +{ +#if PY_VERSION_HEX < 0x03000000 + if (PyInt_Check(obj)) { + long v = PyInt_AsLong(obj); + if (v >= 0) { + if (val) *val = v; + return SWIG_OK; + } else { + return SWIG_OverflowError; + } + } else +#endif + if (PyLong_Check(obj)) { + unsigned long v = PyLong_AsUnsignedLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + return SWIG_OverflowError; + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + unsigned long v = PyLong_AsUnsignedLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + double d; + int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d)); + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ULONG_MAX)) { + if (val) *val = (unsigned long)(d); + return res; + } + } + } +#endif + return SWIG_TypeError; +} + + +#include +#if !defined(SWIG_NO_LLONG_MAX) +# if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__) +# define LLONG_MAX __LONG_LONG_MAX__ +# define LLONG_MIN (-LLONG_MAX - 1LL) +# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) +# endif +#endif + + +#if defined(LLONG_MAX) && !defined(SWIG_LONG_LONG_AVAILABLE) +# define SWIG_LONG_LONG_AVAILABLE +#endif + + +#ifdef SWIG_LONG_LONG_AVAILABLE +SWIGINTERN int +SWIG_AsVal_unsigned_SS_long_SS_long (PyObject *obj, unsigned long long *val) +{ + int res = SWIG_TypeError; + if (PyLong_Check(obj)) { + unsigned long long v = PyLong_AsUnsignedLongLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + res = SWIG_OverflowError; + } + } else { + unsigned long v; + res = SWIG_AsVal_unsigned_SS_long (obj,&v); + if (SWIG_IsOK(res)) { + if (val) *val = v; + return res; + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + const double mant_max = 1LL << DBL_MANT_DIG; + double d; + res = SWIG_AsVal_double (obj,&d); + if (SWIG_IsOK(res) && !SWIG_CanCastAsInteger(&d, 0, mant_max)) + return SWIG_OverflowError; + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, mant_max)) { + if (val) *val = (unsigned long long)(d); + return SWIG_AddCast(res); + } + res = SWIG_TypeError; + } +#endif + return res; +} +#endif + + +SWIGINTERN int +SWIG_AsVal_long (PyObject *obj, long* val) +{ +#if PY_VERSION_HEX < 0x03000000 + if (PyInt_Check(obj)) { + if (val) *val = PyInt_AsLong(obj); + return SWIG_OK; + } else +#endif + if (PyLong_Check(obj)) { + long v = PyLong_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + return SWIG_OverflowError; + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + long v = PyInt_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + double d; + int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d)); + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) { + if (val) *val = (long)(d); + return res; + } + } + } +#endif + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsVal_int (PyObject * obj, int *val) +{ + long v; + int res = SWIG_AsVal_long (obj, &v); + if (SWIG_IsOK(res)) { + if ((v < INT_MIN || v > INT_MAX)) { + return SWIG_OverflowError; + } else { + if (val) *val = static_cast< int >(v); + } + } + return res; +} + + +#ifdef SWIG_LONG_LONG_AVAILABLE +SWIGINTERNINLINE PyObject* +SWIG_From_unsigned_SS_long_SS_long (unsigned long long value) +{ + return (value > LONG_MAX) ? + PyLong_FromUnsignedLongLong(value) : PyInt_FromLong(static_cast< long >(value)); +} +#endif + + + +/* --------------------------------------------------- + * C++ director class methods + * --------------------------------------------------- */ + +#include "kclvm_plugin_wrap.h" + +SwigDirector__kclvm_plugin_AppContextBase::SwigDirector__kclvm_plugin_AppContextBase(PyObject *self, uint64_t rust_invoke_json_ptr): _kclvm_plugin_AppContextBase(rust_invoke_json_ptr), Swig::Director(self) { + SWIG_DIRECTOR_RGTR((_kclvm_plugin_AppContextBase *)this, this); +} + + + + +SwigDirector__kclvm_plugin_AppContextBase::~SwigDirector__kclvm_plugin_AppContextBase() { +} + +std::string SwigDirector__kclvm_plugin_AppContextBase::_call_py_method(std::string const &name, std::string const &args_json, std::string const &kwargs_json) { + std::string c_result; + swig::SwigVar_PyObject obj0; + obj0 = SWIG_From_std_string(static_cast< std::string >(name)); + swig::SwigVar_PyObject obj1; + obj1 = SWIG_From_std_string(static_cast< std::string >(args_json)); + swig::SwigVar_PyObject obj2; + obj2 = SWIG_From_std_string(static_cast< std::string >(kwargs_json)); + if (!swig_get_self()) { + Swig::DirectorException::raise("'self' uninitialized, maybe you forgot to call _kclvm_plugin_AppContextBase.__init__."); + } +#if defined(SWIG_PYTHON_DIRECTOR_VTABLE) + const size_t swig_method_index = 0; + const char *const swig_method_name = "_call_py_method"; + PyObject *method = swig_get_method(swig_method_index, swig_method_name); + swig::SwigVar_PyObject result = PyObject_CallFunction(method, (char *)"(OOO)" ,(PyObject *)obj0,(PyObject *)obj1,(PyObject *)obj2); +#else + swig::SwigVar_PyObject result = PyObject_CallMethod(swig_get_self(), (char *)"_call_py_method", (char *)"(OOO)" ,(PyObject *)obj0,(PyObject *)obj1,(PyObject *)obj2); +#endif + if (!result) { + PyObject *error = PyErr_Occurred(); + if (error) { + Swig::DirectorMethodException::raise("Error detected when calling '_kclvm_plugin_AppContextBase._call_py_method'"); + } + } + std::string *swig_optr = 0; + int swig_ores = SWIG_AsPtr_std_string(result, &swig_optr); + if (!SWIG_IsOK(swig_ores) || !swig_optr) { + Swig::DirectorTypeMismatchException::raise(SWIG_ErrorType(SWIG_ArgError((swig_optr ? swig_ores : SWIG_TypeError))), "in output value of type '""std::string""'"); + } + c_result = *swig_optr; + if (SWIG_IsNewObj(swig_ores)) delete swig_optr; + return (std::string) c_result; +} + + +#ifdef __cplusplus +extern "C" { +#endif +SWIGINTERN PyObject *_wrap_new__kclvm_plugin_AppContextBase(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + PyObject *arg1 = (PyObject *) 0 ; + uint64_t arg2 ; + unsigned long long val2 ; + int ecode2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + _kclvm_plugin_AppContextBase *result = 0 ; + + if (!PyArg_ParseTuple(args,(char *)"OO:new__kclvm_plugin_AppContextBase",&obj0,&obj1)) SWIG_fail; + arg1 = obj0; + ecode2 = SWIG_AsVal_unsigned_SS_long_SS_long(obj1, &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "new__kclvm_plugin_AppContextBase" "', argument " "2"" of type '" "uint64_t""'"); + } + arg2 = static_cast< uint64_t >(val2); + if ( arg1 != Py_None ) { + /* subclassed */ + result = (_kclvm_plugin_AppContextBase *)new SwigDirector__kclvm_plugin_AppContextBase(arg1,arg2); + } else { + result = (_kclvm_plugin_AppContextBase *)new _kclvm_plugin_AppContextBase(arg2); + } + + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p__kclvm_plugin_AppContextBase, SWIG_POINTER_NEW | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_delete__kclvm_plugin_AppContextBase(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + _kclvm_plugin_AppContextBase *arg1 = (_kclvm_plugin_AppContextBase *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + + if (!PyArg_ParseTuple(args,(char *)"O:delete__kclvm_plugin_AppContextBase",&obj0)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p__kclvm_plugin_AppContextBase, SWIG_POINTER_DISOWN | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete__kclvm_plugin_AppContextBase" "', argument " "1"" of type '" "_kclvm_plugin_AppContextBase *""'"); + } + arg1 = reinterpret_cast< _kclvm_plugin_AppContextBase * >(argp1); + delete arg1; + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap__kclvm_plugin_AppContextBase__clear_options(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + _kclvm_plugin_AppContextBase *arg1 = (_kclvm_plugin_AppContextBase *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + + if (!PyArg_ParseTuple(args,(char *)"O:_kclvm_plugin_AppContextBase__clear_options",&obj0)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p__kclvm_plugin_AppContextBase, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "_kclvm_plugin_AppContextBase__clear_options" "', argument " "1"" of type '" "_kclvm_plugin_AppContextBase *""'"); + } + arg1 = reinterpret_cast< _kclvm_plugin_AppContextBase * >(argp1); + (arg1)->_clear_options(); + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap__kclvm_plugin_AppContextBase__add_option(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + _kclvm_plugin_AppContextBase *arg1 = (_kclvm_plugin_AppContextBase *) 0 ; + std::string *arg2 = 0 ; + std::string *arg3 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 = SWIG_OLDOBJ ; + int res3 = SWIG_OLDOBJ ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + + if (!PyArg_ParseTuple(args,(char *)"OOO:_kclvm_plugin_AppContextBase__add_option",&obj0,&obj1,&obj2)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p__kclvm_plugin_AppContextBase, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "_kclvm_plugin_AppContextBase__add_option" "', argument " "1"" of type '" "_kclvm_plugin_AppContextBase *""'"); + } + arg1 = reinterpret_cast< _kclvm_plugin_AppContextBase * >(argp1); + { + std::string *ptr = (std::string *)0; + res2 = SWIG_AsPtr_std_string(obj1, &ptr); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "_kclvm_plugin_AppContextBase__add_option" "', argument " "2"" of type '" "std::string const &""'"); + } + if (!ptr) { + SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "_kclvm_plugin_AppContextBase__add_option" "', argument " "2"" of type '" "std::string const &""'"); + } + arg2 = ptr; + } + { + std::string *ptr = (std::string *)0; + res3 = SWIG_AsPtr_std_string(obj2, &ptr); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "_kclvm_plugin_AppContextBase__add_option" "', argument " "3"" of type '" "std::string const &""'"); + } + if (!ptr) { + SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "_kclvm_plugin_AppContextBase__add_option" "', argument " "3"" of type '" "std::string const &""'"); + } + arg3 = ptr; + } + (arg1)->_add_option((std::string const &)*arg2,(std::string const &)*arg3); + resultobj = SWIG_Py_Void(); + if (SWIG_IsNewObj(res2)) delete arg2; + if (SWIG_IsNewObj(res3)) delete arg3; + return resultobj; +fail: + if (SWIG_IsNewObj(res2)) delete arg2; + if (SWIG_IsNewObj(res3)) delete arg3; + return NULL; +} + + +SWIGINTERN PyObject *_wrap__kclvm_plugin_AppContextBase__run_app(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + _kclvm_plugin_AppContextBase *arg1 = (_kclvm_plugin_AppContextBase *) 0 ; + uint64_t arg2 ; + uint64_t arg3 ; + int32_t arg4 ; + int32_t arg5 ; + int32_t arg6 ; + int32_t arg7 ; + int32_t arg8 ; + int32_t arg9 ; + void *argp1 = 0 ; + int res1 = 0 ; + unsigned long long val2 ; + int ecode2 = 0 ; + unsigned long long val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + int val5 ; + int ecode5 = 0 ; + int val6 ; + int ecode6 = 0 ; + int val7 ; + int ecode7 = 0 ; + int val8 ; + int ecode8 = 0 ; + int val9 ; + int ecode9 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + PyObject * obj4 = 0 ; + PyObject * obj5 = 0 ; + PyObject * obj6 = 0 ; + PyObject * obj7 = 0 ; + PyObject * obj8 = 0 ; + std::string result; + + if (!PyArg_ParseTuple(args,(char *)"OOOOOOOOO:_kclvm_plugin_AppContextBase__run_app",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6,&obj7,&obj8)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p__kclvm_plugin_AppContextBase, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "_kclvm_plugin_AppContextBase__run_app" "', argument " "1"" of type '" "_kclvm_plugin_AppContextBase *""'"); + } + arg1 = reinterpret_cast< _kclvm_plugin_AppContextBase * >(argp1); + ecode2 = SWIG_AsVal_unsigned_SS_long_SS_long(obj1, &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "_kclvm_plugin_AppContextBase__run_app" "', argument " "2"" of type '" "uint64_t""'"); + } + arg2 = static_cast< uint64_t >(val2); + ecode3 = SWIG_AsVal_unsigned_SS_long_SS_long(obj2, &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "_kclvm_plugin_AppContextBase__run_app" "', argument " "3"" of type '" "uint64_t""'"); + } + arg3 = static_cast< uint64_t >(val3); + ecode4 = SWIG_AsVal_int(obj3, &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "_kclvm_plugin_AppContextBase__run_app" "', argument " "4"" of type '" "int32_t""'"); + } + arg4 = static_cast< int32_t >(val4); + ecode5 = SWIG_AsVal_int(obj4, &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "_kclvm_plugin_AppContextBase__run_app" "', argument " "5"" of type '" "int32_t""'"); + } + arg5 = static_cast< int32_t >(val5); + ecode6 = SWIG_AsVal_int(obj5, &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "_kclvm_plugin_AppContextBase__run_app" "', argument " "6"" of type '" "int32_t""'"); + } + arg6 = static_cast< int32_t >(val6); + ecode7 = SWIG_AsVal_int(obj6, &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "_kclvm_plugin_AppContextBase__run_app" "', argument " "7"" of type '" "int32_t""'"); + } + arg7 = static_cast< int32_t >(val7); + ecode8 = SWIG_AsVal_int(obj7, &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "_kclvm_plugin_AppContextBase__run_app" "', argument " "8"" of type '" "int32_t""'"); + } + arg8 = static_cast< int32_t >(val8); + ecode9 = SWIG_AsVal_int(obj8, &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "_kclvm_plugin_AppContextBase__run_app" "', argument " "9"" of type '" "int32_t""'"); + } + arg9 = static_cast< int32_t >(val9); + result = (arg1)->_run_app(arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9); + resultobj = SWIG_From_std_string(static_cast< std::string >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap__kclvm_plugin_AppContextBase__get_warn(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + _kclvm_plugin_AppContextBase *arg1 = (_kclvm_plugin_AppContextBase *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + std::string result; + + if (!PyArg_ParseTuple(args,(char *)"O:_kclvm_plugin_AppContextBase__get_warn",&obj0)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p__kclvm_plugin_AppContextBase, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "_kclvm_plugin_AppContextBase__get_warn" "', argument " "1"" of type '" "_kclvm_plugin_AppContextBase *""'"); + } + arg1 = reinterpret_cast< _kclvm_plugin_AppContextBase * >(argp1); + result = (arg1)->_get_warn(); + resultobj = SWIG_From_std_string(static_cast< std::string >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap__kclvm_plugin_AppContextBase__get_cxx_invoke_proxy_ptr(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + _kclvm_plugin_AppContextBase *arg1 = (_kclvm_plugin_AppContextBase *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + uint64_t result; + + if (!PyArg_ParseTuple(args,(char *)"O:_kclvm_plugin_AppContextBase__get_cxx_invoke_proxy_ptr",&obj0)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p__kclvm_plugin_AppContextBase, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "_kclvm_plugin_AppContextBase__get_cxx_invoke_proxy_ptr" "', argument " "1"" of type '" "_kclvm_plugin_AppContextBase *""'"); + } + arg1 = reinterpret_cast< _kclvm_plugin_AppContextBase * >(argp1); + result = (uint64_t)(arg1)->_get_cxx_invoke_proxy_ptr(); + resultobj = SWIG_From_unsigned_SS_long_SS_long(static_cast< unsigned long long >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap__kclvm_plugin_AppContextBase__call_rust_method(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + _kclvm_plugin_AppContextBase *arg1 = (_kclvm_plugin_AppContextBase *) 0 ; + std::string *arg2 = 0 ; + std::string *arg3 = 0 ; + std::string *arg4 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 = SWIG_OLDOBJ ; + int res3 = SWIG_OLDOBJ ; + int res4 = SWIG_OLDOBJ ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + std::string result; + + if (!PyArg_ParseTuple(args,(char *)"OOOO:_kclvm_plugin_AppContextBase__call_rust_method",&obj0,&obj1,&obj2,&obj3)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p__kclvm_plugin_AppContextBase, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "_kclvm_plugin_AppContextBase__call_rust_method" "', argument " "1"" of type '" "_kclvm_plugin_AppContextBase *""'"); + } + arg1 = reinterpret_cast< _kclvm_plugin_AppContextBase * >(argp1); + { + std::string *ptr = (std::string *)0; + res2 = SWIG_AsPtr_std_string(obj1, &ptr); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "_kclvm_plugin_AppContextBase__call_rust_method" "', argument " "2"" of type '" "std::string const &""'"); + } + if (!ptr) { + SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "_kclvm_plugin_AppContextBase__call_rust_method" "', argument " "2"" of type '" "std::string const &""'"); + } + arg2 = ptr; + } + { + std::string *ptr = (std::string *)0; + res3 = SWIG_AsPtr_std_string(obj2, &ptr); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "_kclvm_plugin_AppContextBase__call_rust_method" "', argument " "3"" of type '" "std::string const &""'"); + } + if (!ptr) { + SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "_kclvm_plugin_AppContextBase__call_rust_method" "', argument " "3"" of type '" "std::string const &""'"); + } + arg3 = ptr; + } + { + std::string *ptr = (std::string *)0; + res4 = SWIG_AsPtr_std_string(obj3, &ptr); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "_kclvm_plugin_AppContextBase__call_rust_method" "', argument " "4"" of type '" "std::string const &""'"); + } + if (!ptr) { + SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "_kclvm_plugin_AppContextBase__call_rust_method" "', argument " "4"" of type '" "std::string const &""'"); + } + arg4 = ptr; + } + result = (arg1)->_call_rust_method((std::string const &)*arg2,(std::string const &)*arg3,(std::string const &)*arg4); + resultobj = SWIG_From_std_string(static_cast< std::string >(result)); + if (SWIG_IsNewObj(res2)) delete arg2; + if (SWIG_IsNewObj(res3)) delete arg3; + if (SWIG_IsNewObj(res4)) delete arg4; + return resultobj; +fail: + if (SWIG_IsNewObj(res2)) delete arg2; + if (SWIG_IsNewObj(res3)) delete arg3; + if (SWIG_IsNewObj(res4)) delete arg4; + return NULL; +} + + +SWIGINTERN PyObject *_wrap__kclvm_plugin_AppContextBase__call_py_method(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + _kclvm_plugin_AppContextBase *arg1 = (_kclvm_plugin_AppContextBase *) 0 ; + std::string *arg2 = 0 ; + std::string *arg3 = 0 ; + std::string *arg4 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 = SWIG_OLDOBJ ; + int res3 = SWIG_OLDOBJ ; + int res4 = SWIG_OLDOBJ ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + Swig::Director *director = 0; + bool upcall = false; + std::string result; + + if (!PyArg_ParseTuple(args,(char *)"OOOO:_kclvm_plugin_AppContextBase__call_py_method",&obj0,&obj1,&obj2,&obj3)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p__kclvm_plugin_AppContextBase, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "_kclvm_plugin_AppContextBase__call_py_method" "', argument " "1"" of type '" "_kclvm_plugin_AppContextBase *""'"); + } + arg1 = reinterpret_cast< _kclvm_plugin_AppContextBase * >(argp1); + { + std::string *ptr = (std::string *)0; + res2 = SWIG_AsPtr_std_string(obj1, &ptr); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "_kclvm_plugin_AppContextBase__call_py_method" "', argument " "2"" of type '" "std::string const &""'"); + } + if (!ptr) { + SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "_kclvm_plugin_AppContextBase__call_py_method" "', argument " "2"" of type '" "std::string const &""'"); + } + arg2 = ptr; + } + { + std::string *ptr = (std::string *)0; + res3 = SWIG_AsPtr_std_string(obj2, &ptr); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "_kclvm_plugin_AppContextBase__call_py_method" "', argument " "3"" of type '" "std::string const &""'"); + } + if (!ptr) { + SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "_kclvm_plugin_AppContextBase__call_py_method" "', argument " "3"" of type '" "std::string const &""'"); + } + arg3 = ptr; + } + { + std::string *ptr = (std::string *)0; + res4 = SWIG_AsPtr_std_string(obj3, &ptr); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "_kclvm_plugin_AppContextBase__call_py_method" "', argument " "4"" of type '" "std::string const &""'"); + } + if (!ptr) { + SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "_kclvm_plugin_AppContextBase__call_py_method" "', argument " "4"" of type '" "std::string const &""'"); + } + arg4 = ptr; + } + director = SWIG_DIRECTOR_CAST(arg1); + upcall = (director && (director->swig_get_self()==obj0)); + try { + if (upcall) { + result = (arg1)->_kclvm_plugin_AppContextBase::_call_py_method((std::string const &)*arg2,(std::string const &)*arg3,(std::string const &)*arg4); + } else { + result = (arg1)->_call_py_method((std::string const &)*arg2,(std::string const &)*arg3,(std::string const &)*arg4); + } + } catch (Swig::DirectorException&) { + SWIG_fail; + } + resultobj = SWIG_From_std_string(static_cast< std::string >(result)); + if (SWIG_IsNewObj(res2)) delete arg2; + if (SWIG_IsNewObj(res3)) delete arg3; + if (SWIG_IsNewObj(res4)) delete arg4; + return resultobj; +fail: + if (SWIG_IsNewObj(res2)) delete arg2; + if (SWIG_IsNewObj(res3)) delete arg3; + if (SWIG_IsNewObj(res4)) delete arg4; + return NULL; +} + + +SWIGINTERN PyObject *_wrap_disown__kclvm_plugin_AppContextBase(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + _kclvm_plugin_AppContextBase *arg1 = (_kclvm_plugin_AppContextBase *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + + if (!PyArg_ParseTuple(args,(char *)"O:disown__kclvm_plugin_AppContextBase",&obj0)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p__kclvm_plugin_AppContextBase, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "disown__kclvm_plugin_AppContextBase" "', argument " "1"" of type '" "_kclvm_plugin_AppContextBase *""'"); + } + arg1 = reinterpret_cast< _kclvm_plugin_AppContextBase * >(argp1); + { + Swig::Director *director = SWIG_DIRECTOR_CAST(arg1); + if (director) director->swig_disown(); + } + + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_kclvm_plugin_AppContextBase_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *obj; + if (!PyArg_ParseTuple(args,(char *)"O:swigregister", &obj)) return NULL; + SWIG_TypeNewClientData(SWIGTYPE_p__kclvm_plugin_AppContextBase, SWIG_NewClientData(obj)); + return SWIG_Py_Void(); +} + +static PyMethodDef SwigMethods[] = { + { (char *)"SWIG_PyInstanceMethod_New", (PyCFunction)SWIG_PyInstanceMethod_New, METH_O, NULL}, + { (char *)"new__kclvm_plugin_AppContextBase", _wrap_new__kclvm_plugin_AppContextBase, METH_VARARGS, NULL}, + { (char *)"delete__kclvm_plugin_AppContextBase", _wrap_delete__kclvm_plugin_AppContextBase, METH_VARARGS, NULL}, + { (char *)"_kclvm_plugin_AppContextBase__clear_options", _wrap__kclvm_plugin_AppContextBase__clear_options, METH_VARARGS, NULL}, + { (char *)"_kclvm_plugin_AppContextBase__add_option", _wrap__kclvm_plugin_AppContextBase__add_option, METH_VARARGS, NULL}, + { (char *)"_kclvm_plugin_AppContextBase__run_app", _wrap__kclvm_plugin_AppContextBase__run_app, METH_VARARGS, NULL}, + { (char *)"_kclvm_plugin_AppContextBase__get_warn", _wrap__kclvm_plugin_AppContextBase__get_warn, METH_VARARGS, NULL}, + { (char *)"_kclvm_plugin_AppContextBase__get_cxx_invoke_proxy_ptr", _wrap__kclvm_plugin_AppContextBase__get_cxx_invoke_proxy_ptr, METH_VARARGS, NULL}, + { (char *)"_kclvm_plugin_AppContextBase__call_rust_method", _wrap__kclvm_plugin_AppContextBase__call_rust_method, METH_VARARGS, NULL}, + { (char *)"_kclvm_plugin_AppContextBase__call_py_method", _wrap__kclvm_plugin_AppContextBase__call_py_method, METH_VARARGS, NULL}, + { (char *)"disown__kclvm_plugin_AppContextBase", _wrap_disown__kclvm_plugin_AppContextBase, METH_VARARGS, NULL}, + { (char *)"_kclvm_plugin_AppContextBase_swigregister", _kclvm_plugin_AppContextBase_swigregister, METH_VARARGS, NULL}, + { NULL, NULL, 0, NULL } +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */ + +static swig_type_info _swigt__p__kclvm_plugin_AppContextBase = {"_p__kclvm_plugin_AppContextBase", "_kclvm_plugin_AppContextBase *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_int = {"_p_int", "intptr_t *|int *|int_least32_t *|int_fast32_t *|int32_t *|int_fast16_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_long_long = {"_p_long_long", "int_least64_t *|int_fast64_t *|int64_t *|long long *|intmax_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_short = {"_p_short", "short *|int_least16_t *|int16_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_signed_char = {"_p_signed_char", "signed char *|int_least8_t *|int_fast8_t *|int8_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_unsigned_char = {"_p_unsigned_char", "unsigned char *|uint_least8_t *|uint_fast8_t *|uint8_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_unsigned_int = {"_p_unsigned_int", "uintptr_t *|uint_least32_t *|uint_fast32_t *|uint32_t *|unsigned int *|uint_fast16_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_unsigned_long_long = {"_p_unsigned_long_long", "uint_least64_t *|uint_fast64_t *|uint64_t *|unsigned long long *|uintmax_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_unsigned_short = {"_p_unsigned_short", "unsigned short *|uint_least16_t *|uint16_t *", 0, 0, (void*)0, 0}; + +static swig_type_info *swig_type_initial[] = { + &_swigt__p__kclvm_plugin_AppContextBase, + &_swigt__p_char, + &_swigt__p_int, + &_swigt__p_long_long, + &_swigt__p_short, + &_swigt__p_signed_char, + &_swigt__p_unsigned_char, + &_swigt__p_unsigned_int, + &_swigt__p_unsigned_long_long, + &_swigt__p_unsigned_short, +}; + +static swig_cast_info _swigc__p__kclvm_plugin_AppContextBase[] = { {&_swigt__p__kclvm_plugin_AppContextBase, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_char[] = { {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_int[] = { {&_swigt__p_int, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_long_long[] = { {&_swigt__p_long_long, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_short[] = { {&_swigt__p_short, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_signed_char[] = { {&_swigt__p_signed_char, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_unsigned_char[] = { {&_swigt__p_unsigned_char, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_unsigned_int[] = { {&_swigt__p_unsigned_int, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_unsigned_long_long[] = { {&_swigt__p_unsigned_long_long, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_unsigned_short[] = { {&_swigt__p_unsigned_short, 0, 0, 0},{0, 0, 0, 0}}; + +static swig_cast_info *swig_cast_initial[] = { + _swigc__p__kclvm_plugin_AppContextBase, + _swigc__p_char, + _swigc__p_int, + _swigc__p_long_long, + _swigc__p_short, + _swigc__p_signed_char, + _swigc__p_unsigned_char, + _swigc__p_unsigned_int, + _swigc__p_unsigned_long_long, + _swigc__p_unsigned_short, +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */ + +static swig_const_info swig_const_table[] = { +{0, 0, 0, 0.0, 0, 0}}; + +#ifdef __cplusplus +} +#endif +/* ----------------------------------------------------------------------------- + * Type initialization: + * This problem is tough by the requirement that no dynamic + * memory is used. Also, since swig_type_info structures store pointers to + * swig_cast_info structures and swig_cast_info structures store pointers back + * to swig_type_info structures, we need some lookup code at initialization. + * The idea is that swig generates all the structures that are needed. + * The runtime then collects these partially filled structures. + * The SWIG_InitializeModule function takes these initial arrays out of + * swig_module, and does all the lookup, filling in the swig_module.types + * array with the correct data and linking the correct swig_cast_info + * structures together. + * + * The generated swig_type_info structures are assigned statically to an initial + * array. We just loop through that array, and handle each type individually. + * First we lookup if this type has been already loaded, and if so, use the + * loaded structure instead of the generated one. Then we have to fill in the + * cast linked list. The cast data is initially stored in something like a + * two-dimensional array. Each row corresponds to a type (there are the same + * number of rows as there are in the swig_type_initial array). Each entry in + * a column is one of the swig_cast_info structures for that type. + * The cast_initial array is actually an array of arrays, because each row has + * a variable number of columns. So to actually build the cast linked list, + * we find the array of casts associated with the type, and loop through it + * adding the casts to the list. The one last trick we need to do is making + * sure the type pointer in the swig_cast_info struct is correct. + * + * First off, we lookup the cast->type name to see if it is already loaded. + * There are three cases to handle: + * 1) If the cast->type has already been loaded AND the type we are adding + * casting info to has not been loaded (it is in this module), THEN we + * replace the cast->type pointer with the type pointer that has already + * been loaded. + * 2) If BOTH types (the one we are adding casting info to, and the + * cast->type) are loaded, THEN the cast info has already been loaded by + * the previous module so we just ignore it. + * 3) Finally, if cast->type has not already been loaded, then we add that + * swig_cast_info to the linked list (because the cast->type) pointer will + * be correct. + * ----------------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* c-mode */ +#endif +#endif + +#if 0 +#define SWIGRUNTIME_DEBUG +#endif + + +SWIGRUNTIME void +SWIG_InitializeModule(void *clientdata) { + size_t i; + swig_module_info *module_head, *iter; + int init; + + /* check to see if the circular list has been setup, if not, set it up */ + if (swig_module.next==0) { + /* Initialize the swig_module */ + swig_module.type_initial = swig_type_initial; + swig_module.cast_initial = swig_cast_initial; + swig_module.next = &swig_module; + init = 1; + } else { + init = 0; + } + + /* Try and load any already created modules */ + module_head = SWIG_GetModule(clientdata); + if (!module_head) { + /* This is the first module loaded for this interpreter */ + /* so set the swig module into the interpreter */ + SWIG_SetModule(clientdata, &swig_module); + } else { + /* the interpreter has loaded a SWIG module, but has it loaded this one? */ + iter=module_head; + do { + if (iter==&swig_module) { + /* Our module is already in the list, so there's nothing more to do. */ + return; + } + iter=iter->next; + } while (iter!= module_head); + + /* otherwise we must add our module into the list */ + swig_module.next = module_head->next; + module_head->next = &swig_module; + } + + /* When multiple interpreters are used, a module could have already been initialized in + a different interpreter, but not yet have a pointer in this interpreter. + In this case, we do not want to continue adding types... everything should be + set up already */ + if (init == 0) return; + + /* Now work on filling in swig_module.types */ +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: size %d\n", swig_module.size); +#endif + for (i = 0; i < swig_module.size; ++i) { + swig_type_info *type = 0; + swig_type_info *ret; + swig_cast_info *cast; + +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); +#endif + + /* if there is another module already loaded */ + if (swig_module.next != &swig_module) { + type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name); + } + if (type) { + /* Overwrite clientdata field */ +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: found type %s\n", type->name); +#endif + if (swig_module.type_initial[i]->clientdata) { + type->clientdata = swig_module.type_initial[i]->clientdata; +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name); +#endif + } + } else { + type = swig_module.type_initial[i]; + } + + /* Insert casting types */ + cast = swig_module.cast_initial[i]; + while (cast->type) { + /* Don't need to add information already in the list */ + ret = 0; +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: look cast %s\n", cast->type->name); +#endif + if (swig_module.next != &swig_module) { + ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name); +#ifdef SWIGRUNTIME_DEBUG + if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name); +#endif + } + if (ret) { + if (type == swig_module.type_initial[i]) { +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: skip old type %s\n", ret->name); +#endif + cast->type = ret; + ret = 0; + } else { + /* Check for casting already in the list */ + swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type); +#ifdef SWIGRUNTIME_DEBUG + if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name); +#endif + if (!ocast) ret = 0; + } + } + + if (!ret) { +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name); +#endif + if (type->cast) { + type->cast->prev = cast; + cast->next = type->cast; + } + type->cast = cast; + } + cast++; + } + /* Set entry in modules->types array equal to the type */ + swig_module.types[i] = type; + } + swig_module.types[i] = 0; + +#ifdef SWIGRUNTIME_DEBUG + printf("**** SWIG_InitializeModule: Cast List ******\n"); + for (i = 0; i < swig_module.size; ++i) { + int j = 0; + swig_cast_info *cast = swig_module.cast_initial[i]; + printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); + while (cast->type) { + printf("SWIG_InitializeModule: cast type %s\n", cast->type->name); + cast++; + ++j; + } + printf("---- Total casts: %d\n",j); + } + printf("**** SWIG_InitializeModule: Cast List ******\n"); +#endif +} + +/* This function will propagate the clientdata field of type to +* any new swig_type_info structures that have been added into the list +* of equivalent types. It is like calling +* SWIG_TypeClientData(type, clientdata) a second time. +*/ +SWIGRUNTIME void +SWIG_PropagateClientData(void) { + size_t i; + swig_cast_info *equiv; + static int init_run = 0; + + if (init_run) return; + init_run = 1; + + for (i = 0; i < swig_module.size; i++) { + if (swig_module.types[i]->clientdata) { + equiv = swig_module.types[i]->cast; + while (equiv) { + if (!equiv->converter) { + if (equiv->type && !equiv->type->clientdata) + SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata); + } + equiv = equiv->next; + } + } + } +} + +#ifdef __cplusplus +#if 0 +{ + /* c-mode */ +#endif +} +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + /* Python-specific SWIG API */ +#define SWIG_newvarlink() SWIG_Python_newvarlink() +#define SWIG_addvarlink(p, name, get_attr, set_attr) SWIG_Python_addvarlink(p, name, get_attr, set_attr) +#define SWIG_InstallConstants(d, constants) SWIG_Python_InstallConstants(d, constants) + + /* ----------------------------------------------------------------------------- + * global variable support code. + * ----------------------------------------------------------------------------- */ + + typedef struct swig_globalvar { + char *name; /* Name of global variable */ + PyObject *(*get_attr)(void); /* Return the current value */ + int (*set_attr)(PyObject *); /* Set the value */ + struct swig_globalvar *next; + } swig_globalvar; + + typedef struct swig_varlinkobject { + PyObject_HEAD + swig_globalvar *vars; + } swig_varlinkobject; + + SWIGINTERN PyObject * + swig_varlink_repr(swig_varlinkobject *SWIGUNUSEDPARM(v)) { +#if PY_VERSION_HEX >= 0x03000000 + return PyUnicode_InternFromString(""); +#else + return PyString_FromString(""); +#endif + } + + SWIGINTERN PyObject * + swig_varlink_str(swig_varlinkobject *v) { +#if PY_VERSION_HEX >= 0x03000000 + PyObject *str = PyUnicode_InternFromString("("); + PyObject *tail; + PyObject *joined; + swig_globalvar *var; + for (var = v->vars; var; var=var->next) { + tail = PyUnicode_FromString(var->name); + joined = PyUnicode_Concat(str, tail); + Py_DecRef(str); + Py_DecRef(tail); + str = joined; + if (var->next) { + tail = PyUnicode_InternFromString(", "); + joined = PyUnicode_Concat(str, tail); + Py_DecRef(str); + Py_DecRef(tail); + str = joined; + } + } + tail = PyUnicode_InternFromString(")"); + joined = PyUnicode_Concat(str, tail); + Py_DecRef(str); + Py_DecRef(tail); + str = joined; +#else + PyObject *str = PyString_FromString("("); + swig_globalvar *var; + for (var = v->vars; var; var=var->next) { + PyString_ConcatAndDel(&str,PyString_FromString(var->name)); + if (var->next) PyString_ConcatAndDel(&str,PyString_FromString(", ")); + } + PyString_ConcatAndDel(&str,PyString_FromString(")")); +#endif + return str; + } + + SWIGINTERN int + swig_varlink_print(swig_varlinkobject *v, FILE *fp, int SWIGUNUSEDPARM(flags)) { + char *tmp; + PyObject *str = swig_varlink_str(v); + fprintf(fp,"Swig global variables "); + fprintf(fp,"%s\n", tmp = SWIG_Python_str_AsChar(str)); + SWIG_Python_str_DelForPy3(tmp); + Py_DECREF(str); + return 0; + } + + SWIGINTERN void + swig_varlink_dealloc(swig_varlinkobject *v) { + swig_globalvar *var = v->vars; + while (var) { + swig_globalvar *n = var->next; + free(var->name); + free(var); + var = n; + } + } + + SWIGINTERN PyObject * + swig_varlink_getattr(swig_varlinkobject *v, char *n) { + PyObject *res = NULL; + swig_globalvar *var = v->vars; + while (var) { + if (strcmp(var->name,n) == 0) { + res = (*var->get_attr)(); + break; + } + var = var->next; + } + if (res == NULL && !PyErr_Occurred()) { + PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n); + } + return res; + } + + SWIGINTERN int + swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p) { + int res = 1; + swig_globalvar *var = v->vars; + while (var) { + if (strcmp(var->name,n) == 0) { + res = (*var->set_attr)(p); + break; + } + var = var->next; + } + if (res == 1 && !PyErr_Occurred()) { + PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n); + } + return res; + } + + SWIGINTERN PyTypeObject* + swig_varlink_type(void) { + static char varlink__doc__[] = "Swig var link object"; + static PyTypeObject varlink_type; + static int type_init = 0; + if (!type_init) { + const PyTypeObject tmp = { +#if PY_VERSION_HEX >= 0x03000000 + PyVarObject_HEAD_INIT(NULL, 0) +#else + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ +#endif + (char *)"swigvarlink", /* tp_name */ + sizeof(swig_varlinkobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) swig_varlink_dealloc, /* tp_dealloc */ + (printfunc) swig_varlink_print, /* tp_print */ + (getattrfunc) swig_varlink_getattr, /* tp_getattr */ + (setattrfunc) swig_varlink_setattr, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc) swig_varlink_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc) swig_varlink_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + 0, /* tp_flags */ + varlink__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ +#if PY_VERSION_HEX >= 0x02020000 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_iter -> tp_weaklist */ +#endif +#if PY_VERSION_HEX >= 0x02030000 + 0, /* tp_del */ +#endif +#if PY_VERSION_HEX >= 0x02060000 + 0, /* tp_version_tag */ +#endif +#if PY_VERSION_HEX >= 0x03040000 + 0, /* tp_finalize */ +#endif +#ifdef COUNT_ALLOCS + 0, /* tp_allocs */ + 0, /* tp_frees */ + 0, /* tp_maxalloc */ +#if PY_VERSION_HEX >= 0x02050000 + 0, /* tp_prev */ +#endif + 0 /* tp_next */ +#endif + }; + varlink_type = tmp; + type_init = 1; +#if PY_VERSION_HEX < 0x02020000 + varlink_type.ob_type = &PyType_Type; +#else + if (PyType_Ready(&varlink_type) < 0) + return NULL; +#endif + } + return &varlink_type; + } + + /* Create a variable linking object for use later */ + SWIGINTERN PyObject * + SWIG_Python_newvarlink(void) { + swig_varlinkobject *result = PyObject_NEW(swig_varlinkobject, swig_varlink_type()); + if (result) { + result->vars = 0; + } + return ((PyObject*) result); + } + + SWIGINTERN void + SWIG_Python_addvarlink(PyObject *p, char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) { + swig_varlinkobject *v = (swig_varlinkobject *) p; + swig_globalvar *gv = (swig_globalvar *) malloc(sizeof(swig_globalvar)); + if (gv) { + size_t size = strlen(name)+1; + gv->name = (char *)malloc(size); + if (gv->name) { + strncpy(gv->name,name,size); + gv->get_attr = get_attr; + gv->set_attr = set_attr; + gv->next = v->vars; + } + } + v->vars = gv; + } + + SWIGINTERN PyObject * + SWIG_globals(void) { + static PyObject *_SWIG_globals = 0; + if (!_SWIG_globals) _SWIG_globals = SWIG_newvarlink(); + return _SWIG_globals; + } + + /* ----------------------------------------------------------------------------- + * constants/methods manipulation + * ----------------------------------------------------------------------------- */ + + /* Install Constants */ + SWIGINTERN void + SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) { + PyObject *obj = 0; + size_t i; + for (i = 0; constants[i].type; ++i) { + switch(constants[i].type) { + case SWIG_PY_POINTER: + obj = SWIG_InternalNewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0); + break; + case SWIG_PY_BINARY: + obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype)); + break; + default: + obj = 0; + break; + } + if (obj) { + PyDict_SetItemString(d, constants[i].name, obj); + Py_DECREF(obj); + } + } + } + + /* -----------------------------------------------------------------------------*/ + /* Fix SwigMethods to carry the callback ptrs when needed */ + /* -----------------------------------------------------------------------------*/ + + SWIGINTERN void + SWIG_Python_FixMethods(PyMethodDef *methods, + swig_const_info *const_table, + swig_type_info **types, + swig_type_info **types_initial) { + size_t i; + for (i = 0; methods[i].ml_name; ++i) { + const char *c = methods[i].ml_doc; + if (!c) continue; + c = strstr(c, "swig_ptr: "); + if (c) { + int j; + swig_const_info *ci = 0; + const char *name = c + 10; + for (j = 0; const_table[j].type; ++j) { + if (strncmp(const_table[j].name, name, + strlen(const_table[j].name)) == 0) { + ci = &(const_table[j]); + break; + } + } + if (ci) { + void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue : 0; + if (ptr) { + size_t shift = (ci->ptype) - types; + swig_type_info *ty = types_initial[shift]; + size_t ldoc = (c - methods[i].ml_doc); + size_t lptr = strlen(ty->name)+2*sizeof(void*)+2; + char *ndoc = (char*)malloc(ldoc + lptr + 10); + if (ndoc) { + char *buff = ndoc; + strncpy(buff, methods[i].ml_doc, ldoc); + buff += ldoc; + strncpy(buff, "swig_ptr: ", 10); + buff += 10; + SWIG_PackVoidPtr(buff, ptr, ty->name, lptr); + methods[i].ml_doc = ndoc; + } + } + } + } + } + } + +#ifdef __cplusplus +} +#endif + +/* -----------------------------------------------------------------------------* + * Partial Init method + * -----------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" +#endif + +SWIGEXPORT +#if PY_VERSION_HEX >= 0x03000000 +PyObject* +#else +void +#endif +SWIG_init(void) { + PyObject *m, *d, *md; +#if PY_VERSION_HEX >= 0x03000000 + static struct PyModuleDef SWIG_module = { +# if PY_VERSION_HEX >= 0x03020000 + PyModuleDef_HEAD_INIT, +# else + { + PyObject_HEAD_INIT(NULL) + NULL, /* m_init */ + 0, /* m_index */ + NULL, /* m_copy */ + }, +# endif + (char *) SWIG_name, + NULL, + -1, + SwigMethods, + NULL, + NULL, + NULL, + NULL + }; +#endif + +#if defined(SWIGPYTHON_BUILTIN) + static SwigPyClientData SwigPyObject_clientdata = { + 0, 0, 0, 0, 0, 0, 0 + }; + static PyGetSetDef this_getset_def = { + (char *)"this", &SwigPyBuiltin_ThisClosure, NULL, NULL, NULL + }; + static SwigPyGetSet thisown_getset_closure = { + (PyCFunction) SwigPyObject_own, + (PyCFunction) SwigPyObject_own + }; + static PyGetSetDef thisown_getset_def = { + (char *)"thisown", SwigPyBuiltin_GetterClosure, SwigPyBuiltin_SetterClosure, NULL, &thisown_getset_closure + }; + PyTypeObject *builtin_pytype; + int builtin_base_count; + swig_type_info *builtin_basetype; + PyObject *tuple; + PyGetSetDescrObject *static_getset; + PyTypeObject *metatype; + PyTypeObject *swigpyobject; + SwigPyClientData *cd; + PyObject *public_interface, *public_symbol; + PyObject *this_descr; + PyObject *thisown_descr; + PyObject *self = 0; + int i; + + (void)builtin_pytype; + (void)builtin_base_count; + (void)builtin_basetype; + (void)tuple; + (void)static_getset; + (void)self; + + /* Metaclass is used to implement static member variables */ + metatype = SwigPyObjectType(); + assert(metatype); +#endif + + /* Fix SwigMethods to carry the callback ptrs when needed */ + SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_type_initial); + +#if PY_VERSION_HEX >= 0x03000000 + m = PyModule_Create(&SWIG_module); +#else + m = Py_InitModule((char *) SWIG_name, SwigMethods); +#endif + + md = d = PyModule_GetDict(m); + (void)md; + + SWIG_InitializeModule(0); + +#ifdef SWIGPYTHON_BUILTIN + swigpyobject = SwigPyObject_TypeOnce(); + + SwigPyObject_stype = SWIG_MangledTypeQuery("_p_SwigPyObject"); + assert(SwigPyObject_stype); + cd = (SwigPyClientData*) SwigPyObject_stype->clientdata; + if (!cd) { + SwigPyObject_stype->clientdata = &SwigPyObject_clientdata; + SwigPyObject_clientdata.pytype = swigpyobject; + } else if (swigpyobject->tp_basicsize != cd->pytype->tp_basicsize) { + PyErr_SetString(PyExc_RuntimeError, "Import error: attempted to load two incompatible swig-generated modules."); +# if PY_VERSION_HEX >= 0x03000000 + return NULL; +# else + return; +# endif + } + + /* All objects have a 'this' attribute */ + this_descr = PyDescr_NewGetSet(SwigPyObject_type(), &this_getset_def); + (void)this_descr; + + /* All objects have a 'thisown' attribute */ + thisown_descr = PyDescr_NewGetSet(SwigPyObject_type(), &thisown_getset_def); + (void)thisown_descr; + + public_interface = PyList_New(0); + public_symbol = 0; + (void)public_symbol; + + PyDict_SetItemString(md, "__all__", public_interface); + Py_DECREF(public_interface); + for (i = 0; SwigMethods[i].ml_name != NULL; ++i) + SwigPyBuiltin_AddPublicSymbol(public_interface, SwigMethods[i].ml_name); + for (i = 0; swig_const_table[i].name != 0; ++i) + SwigPyBuiltin_AddPublicSymbol(public_interface, swig_const_table[i].name); +#endif + + SWIG_InstallConstants(d,swig_const_table); + +#if PY_VERSION_HEX >= 0x03000000 + return m; +#else + return; +#endif +} + diff --git a/kclvm/plugin/kclvm_plugin_wrap.h b/kclvm/plugin/kclvm_plugin_wrap.h new file mode 100644 index 000000000..0548c7e4c --- /dev/null +++ b/kclvm/plugin/kclvm_plugin_wrap.h @@ -0,0 +1,60 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version 3.0.12 + * + * This file is not intended to be easily readable and contains a number of + * coding conventions designed to improve portability and efficiency. Do not make + * changes to this file unless you know what you are doing--modify the SWIG + * interface file instead. + * ----------------------------------------------------------------------------- */ + +#ifndef SWIG_kclvm_plugin_WRAP_H_ +#define SWIG_kclvm_plugin_WRAP_H_ + +#include +#include + + +class SwigDirector__kclvm_plugin_AppContextBase : public _kclvm_plugin_AppContextBase, public Swig::Director { + +public: + SwigDirector__kclvm_plugin_AppContextBase(PyObject *self, uint64_t rust_invoke_json_ptr); + virtual ~SwigDirector__kclvm_plugin_AppContextBase(); + virtual std::string _call_py_method(std::string const &name, std::string const &args_json, std::string const &kwargs_json); + +/* Internal director utilities */ +public: + bool swig_get_inner(const char *swig_protected_method_name) const { + std::map::const_iterator iv = swig_inner.find(swig_protected_method_name); + return (iv != swig_inner.end() ? iv->second : false); + } + void swig_set_inner(const char *swig_protected_method_name, bool swig_val) const { + swig_inner[swig_protected_method_name] = swig_val; + } +private: + mutable std::map swig_inner; + +#if defined(SWIG_PYTHON_DIRECTOR_VTABLE) +/* VTable implementation */ + PyObject *swig_get_method(size_t method_index, const char *method_name) const { + PyObject *method = vtable[method_index]; + if (!method) { + swig::SwigVar_PyObject name = SWIG_Python_str_FromChar(method_name); + method = PyObject_GetAttr(swig_get_self(), name); + if (!method) { + std::string msg = "Method in class _kclvm_plugin_AppContextBase doesn't exist, undefined "; + msg += method_name; + Swig::DirectorMethodException::raise(msg.c_str()); + } + vtable[method_index] = method; + } + return method; + } +private: + mutable swig::SwigVar_PyObject vtable[1]; +#endif + +}; + + +#endif diff --git a/kclvm/plugin/kclvm_runtime.py b/kclvm/plugin/kclvm_runtime.py new file mode 100644 index 000000000..3bd464a7c --- /dev/null +++ b/kclvm/plugin/kclvm_runtime.py @@ -0,0 +1,97 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import ctypes +import json +import os +import sys + + +def _find_default_dylib_path() -> str: + _executable_root = os.path.dirname(os.path.dirname(sys.executable)) + + pathList = [ + f"{_executable_root}/lib/libkclvm.dylib", + f"{_executable_root}/lib/libkclvm.so", + f"{_executable_root}/lib/kclvm.dll", + f"{os.path.dirname(__file__)}/../runtime/target/release/libkclvm.dylib", + f"{os.path.dirname(__file__)}/../runtime/target/release/libkclvm.so", + f"{os.path.dirname(__file__)}/../runtime/target/release/kclvm.dll", + f"{os.path.dirname(__file__)}/../runtime/target/debug/libkclvm.dylib", + f"{os.path.dirname(__file__)}/../runtime/target/debug/libkclvm.so", + f"{os.path.dirname(__file__)}/../runtime/target/debug/kclvm.dll", + ] + + for s in pathList: + if os.path.exists(s): + return s + return "" + + +class KclvmRuntimeDylib: + def __init__(self, dllpath: str = None): + if dllpath is None: + dllpath = _find_default_dylib_path() + if not dllpath: + raise f"kclvm runtime lib not found" + + self.dllpath = dllpath + self._app_dll = ctypes.cdll.LoadLibrary(dllpath) + self._app_lib = ctypes.CDLL(dllpath) + self.ctx = None + + # kclvm_context_t* kclvm_context_new(); + self._app_lib.kclvm_context_new.restype = ctypes.c_void_p + + # void kclvm_context_delete(kclvm_context_t* p); + self._app_lib.kclvm_context_delete.argtypes = [ + ctypes.c_void_p, + ] + + # const char* kclvm_context_invoke(kclvm_context_t* p, const char* method, const char* args, const char* kwargs); + self._app_lib.kclvm_context_invoke.restype = ctypes.c_char_p + self._app_lib.kclvm_context_invoke.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p, + ] + + def _kclvm_context_new(self) -> ctypes.c_void_p: + return self._app_lib.kclvm_context_new() + + def kclvm_context_delete(self, ctx: ctypes.c_void_p): + self._app_lib.kclvm_context_delete(ctx) + + def _kclvm_context_invoke( + self, ctx: ctypes.c_void_p, method: str, args: str, kwargs: str + ) -> any: + jsonValue = self._app_lib.kclvm_context_invoke( + ctx, method.encode(), args.encode(), kwargs.encode() + ) + return json.loads(jsonValue) + + def Path(self) -> str: + return self.dllpath + + def Invoke(self, method: str, *args, **kwargs) -> any: + if self.ctx is None: + self.ctx = self._kclvm_context_new() + + if not method.startswith("kclvm_"): + if method.startswith("str."): + # str.startswith => kclvm_builtin_str_startswith + method = f'kclvm_builtin_{method.replace(".", "_")}' + elif "." in method: + # regex.match => kclvm_regex_match + method = f'kclvm_{method.replace(".", "_")}' # json.encode => kclvm_json_encode + else: + method = f"kclvm_builtin_{method}" # print => kclvm_builtin_print + + return self._kclvm_context_invoke( + self.ctx, method, json.dumps(args), json.dumps(kwargs) + ) + + +if __name__ == "__main__": + dylib = KclvmRuntimeDylib() + dylib.Invoke(f"print", "hello kclvm") diff --git a/kclvm/plugin/readme.md b/kclvm/plugin/readme.md new file mode 100644 index 000000000..3132958d4 --- /dev/null +++ b/kclvm/plugin/readme.md @@ -0,0 +1 @@ +# KCLVM Plugin diff --git a/kclvm/plugin/setup.py b/kclvm/plugin/setup.py new file mode 100644 index 000000000..93daf026c --- /dev/null +++ b/kclvm/plugin/setup.py @@ -0,0 +1,20 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import os +import distutils.core + +PWD = os.path.abspath(os.path.dirname(__file__)) +kclvm_ROOT = os.path.abspath(f"{PWD}/..") + +distutils.core.setup( + name="kclvm-plugin", + version="1.0", + py_modules=["kclvm_plugin", "kclvm_runtime"], + ext_modules=[ + distutils.core.Extension( + "_kclvm_plugin", + [f"{PWD}/kclvm_plugin.cpp", f"{PWD}/kclvm_plugin_wrap.cxx"], + include_dirs=[f"{kclvm_ROOT}/runtime/src"], + ) + ], +) diff --git a/kclvm/runner/Cargo.lock b/kclvm/runner/Cargo.lock new file mode 100644 index 000000000..09fcdf0a5 --- /dev/null +++ b/kclvm/runner/Cargo.lock @@ -0,0 +1,1655 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "annotate-snippets" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5" + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "enquote" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c36cb11dbde389f4096111698d8b567c0720e3452fd5ac3e6b4e47e1939932" +dependencies = [ + "thiserror", +] + +[[package]] +name = "fancy-regex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "fixedbitset" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" + +[[package]] +name = "fslock" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", + "rustc-rayon", +] + +[[package]] +name = "inkwell" +version = "0.1.0" +source = "git+https://github.com/TheDan64/inkwell?branch=master#bff378bee02bcbb5bed35f47e9ed69e6642e9188" +dependencies = [ + "either", + "inkwell_internals", + "libc", + "llvm-sys", + "once_cell", + "parking_lot", +] + +[[package]] +name = "inkwell_internals" +version = "0.5.0" +source = "git+https://github.com/TheDan64/inkwell?branch=master#bff378bee02bcbb5bed35f47e9ed69e6642e9188" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "json_minimal" +version = "0.1.3" + +[[package]] +name = "kclvm-ast" +version = "0.1.0" +dependencies = [ + "kclvm-span", + "rustc_span", + "serde", + "serde_json", +] + +[[package]] +name = "kclvm-compiler" +version = "0.1.0" +dependencies = [ + "ahash", + "bit-set", + "bitflags", + "fancy-regex", + "indexmap", + "inkwell", + "kclvm-ast", + "kclvm-error", + "kclvm-runtime", + "kclvm-sema", + "once_cell", + "phf", + "time", + "unicode_names2", +] + +[[package]] +name = "kclvm-config" +version = "0.1.0" +dependencies = [ + "ahash", + "chrono", + "fslock", + "glob", + "indexmap", + "kclvm-version", + "pathdiff", + "ron", + "rust-crypto", + "serde", + "serde_yaml", + "toml", +] + +[[package]] +name = "kclvm-error" +version = "0.1.0" +dependencies = [ + "annotate-snippets", + "atty", + "indexmap", + "kclvm-span", + "rustc_span", + "termcolor", + "termize", + "tracing", +] + +[[package]] +name = "kclvm-lexer" +version = "0.1.0" +dependencies = [ + "kclvm-error", + "rustc_lexer", + "unic-emoji-char", +] + +[[package]] +name = "kclvm-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "kclvm-parser" +version = "0.1.0" +dependencies = [ + "bstr", + "either", + "enquote", + "kclvm-ast", + "kclvm-config", + "kclvm-error", + "kclvm-lexer", + "kclvm-sema", + "kclvm-span", + "num-bigint", + "rustc_data_structures", + "rustc_lexer", + "rustc_span", + "serde", + "serde_json", + "tracing", + "unicode_names2", +] + +[[package]] +name = "kclvm-runner" +version = "0.1.0" +dependencies = [ + "clap", + "fslock", + "glob", + "indexmap", + "kclvm-ast", + "kclvm-compiler", + "kclvm-config", + "kclvm-parser", + "kclvm-runtime", + "kclvm-sema", + "kclvm-version", + "libc", + "libloading", + "serde", + "serde_json", + "walkdir", +] + +[[package]] +name = "kclvm-runtime" +version = "0.1.0" +dependencies = [ + "ahash", + "base64", + "bstr", + "chrono", + "fancy-regex", + "indexmap", + "itertools", + "json_minimal", + "kclvm_runtime_internal_macros", + "libc", + "md5", + "num-integer", + "phf", + "regex", + "serde_json", + "serde_yaml", + "sha1", + "sha2 0.9.9", + "unic-ucd-bidi", + "unic-ucd-category", + "unicode-casing", +] + +[[package]] +name = "kclvm-sema" +version = "0.1.0" +dependencies = [ + "ahash", + "bit-set", + "bitflags", + "fancy-regex", + "indexmap", + "kclvm-ast", + "kclvm-error", + "kclvm-runtime", + "kclvm-span", + "once_cell", + "petgraph", + "phf", + "unicode_names2", +] + +[[package]] +name = "kclvm-span" +version = "0.1.0" +dependencies = [ + "kclvm-macros", + "rustc_span", + "scoped-tls", +] + +[[package]] +name = "kclvm-version" +version = "0.1.0" + +[[package]] +name = "kclvm_runtime_internal_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" + +[[package]] +name = "libloading" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "llvm-sys" +version = "120.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b716322964966a62377cf86e64f00ca7043505fdf27bd2ec7d41ae6682d1e7" +dependencies = [ + "cc", + "lazy_static", + "libc", + "regex", + "semver", +] + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest 0.10.3", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "petgraph" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871372391786ccec00d3c5d3d6608905b3d4db263639cfe075d3b60a736d115a" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ron" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86018df177b1beef6c7c8ef949969c4f7cb9a9344181b92486b23c79995bdaa4" +dependencies = [ + "base64", + "bitflags", + "serde", +] + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +dependencies = [ + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-rayon" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" +dependencies = [ + "crossbeam-deque", + "either", + "rustc-rayon-core", +] + +[[package]] +name = "rustc-rayon-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" + +[[package]] +name = "rustc_data_structures" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ena", + "indexmap", + "jobserver", + "libc", + "memmap2", + "parking_lot", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "stable_deref_trait", + "stacker", + "tempfile", + "tracing", + "winapi", +] + +[[package]] +name = "rustc_lexer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86aae0c77166108c01305ee1a36a1e77289d7dc6ca0a3cd91ff4992de2d16a5" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "rustc_span" +version = "0.0.0" +dependencies = [ + "cfg-if 0.1.10", + "md-5", + "rustc_data_structures", + "scoped-tls", + "sha-1", + "sha2 0.10.2", + "tracing", + "unicode-width", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termize" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-category" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8d4591f5fcfe1bd4453baaf803c40e1b1e69ff8455c47620440b46efef91c0" +dependencies = [ + "matches", + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-casing" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "623f59e6af2a98bdafeb93fa277ac8e1e40440973001ca15cf4ae1541cd16d56" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + +[[package]] +name = "unicode_names2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d6678d7916394abad0d4b19df4d3802e1fd84abd7d701f39b75ee71b9e8cf1" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/kclvm/runner/Cargo.toml b/kclvm/runner/Cargo.toml new file mode 100644 index 000000000..27190b8ab --- /dev/null +++ b/kclvm/runner/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "kclvm-runner" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = "2.33.3" +serde_json = "1.0" +serde = { version = "1", features = ["derive"] } +glob = "0.3.0" +walkdir = "2" +libc = "0.2.112" +indexmap = "1.0" +fslock = "0.2.1" +libloading = "0.7.3" + +kclvm-ast = {path = "../ast", version = "0.1.0"} +kclvm-parser = {path = "../parser", version = "0.1.0"} +kclvm-compiler = {path = "../compiler", version = "0.1.0"} +kclvm-config = {path = "../config", version = "0.1.0"} +kclvm-runtime = {path = "../runtime", version = "0.1.0"} +kclvm-sema = {path = "../sema", version = "0.1.0"} +kclvm-version = {path = "../version", version = "0.1.0"} diff --git a/kclvm/runner/src/command.rs b/kclvm/runner/src/command.rs new file mode 100644 index 000000000..489f00f37 --- /dev/null +++ b/kclvm/runner/src/command.rs @@ -0,0 +1,461 @@ +use std::env; + +use super::runner::*; +use kclvm::ValueRef; +use kclvm_config::settings::SettingsFile; + +#[derive(Debug)] +pub struct Command { + clang_path: String, + rust_libstd_dylib: String, + executable_root: String, + plugin_method_ptr: u64, +} + +impl Command { + pub fn new(plugin_method_ptr: u64) -> Self { + let executable_root = Self::get_executable_root(); + let rust_libstd_dylib = Self::get_rust_libstd_dylib(executable_root.as_str()); + let clang_path = Self::get_clang_path(); + + Self { + clang_path, + rust_libstd_dylib, + executable_root, + plugin_method_ptr, + } + } + + pub fn run_dylib(&self, dylib_path: &str) -> Result { + unsafe { + let lib = libloading::Library::new(dylib_path).unwrap(); + + // get kclvm_plugin_init + let kclvm_plugin_init: libloading::Symbol< + unsafe extern "C" fn( + fn_ptr: extern "C" fn( + method: *const i8, + args_json: *const i8, + kwargs_json: *const i8, + ) -> *const i8, + ), + > = lib.get(b"kclvm_plugin_init").unwrap(); + + // get _kcl_run + let kcl_run: libloading::Symbol< + unsafe extern "C" fn( + kclvm_main_ptr: u64, // main.k => kclvm_main + option_len: kclvm_size_t, + option_keys: *const *const kclvm_char_t, + option_values: *const *const kclvm_char_t, + strict_range_check: i32, + disable_none: i32, + disable_schema_check: i32, + list_option_mode: i32, + debug_mode: i32, + result_buffer_len: kclvm_size_t, + result_buffer: *mut kclvm_char_t, + warn_buffer_len: kclvm_size_t, + warn_buffer: *mut kclvm_char_t, + ) -> kclvm_size_t, + > = lib.get(b"_kcl_run").unwrap(); + + // get kclvm_main + let kclvm_main: libloading::Symbol = lib.get(b"kclvm_main").unwrap(); + let kclvm_main_ptr = kclvm_main.into_raw().into_raw() as u64; + + // get plugin_method + let plugin_method_ptr = self.plugin_method_ptr; + let plugin_method_ptr = (plugin_method_ptr as *const u64) as *const () + as *const extern "C" fn( + method: *const i8, + args: *const i8, + kwargs: *const i8, + ) -> *const i8; + let plugin_method: extern "C" fn( + method: *const i8, + args: *const i8, + kwargs: *const i8, + ) -> *const i8 = std::mem::transmute(plugin_method_ptr); + + // register plugin agent + kclvm_plugin_init(plugin_method); + + let option_len = 0; + let option_keys = std::ptr::null(); + let option_values = std::ptr::null(); + let strict_range_check = 0; + let disable_none = 0; + let disable_schema_check = 0; + let list_option_mode = 0; + let debug_mode = 0; + + let mut result = vec![0u8; 1024 * 1024]; + let result_buffer_len = result.len() as i32 - 1; + let result_buffer = result.as_mut_ptr() as *mut i8; + + let mut warn_buffer = vec![0u8; 1024 * 1024]; + let warn_buffer_len = warn_buffer.len() as i32 - 1; + let warn_buffer = warn_buffer.as_mut_ptr() as *mut i8; + + let n = kcl_run( + kclvm_main_ptr, + option_len, + option_keys, + option_values, + strict_range_check, + disable_none, + disable_schema_check, + list_option_mode, + debug_mode, + result_buffer_len, + result_buffer, + warn_buffer_len, + warn_buffer, + ); + + let s = std::str::from_utf8(&result[0..n as usize]).unwrap(); + Ok(s.to_string()) + } + } + + pub fn run_dylib_with_settings( + &self, + dylib_path: &str, + settings: SettingsFile, + ) -> Result { + unsafe { + let lib = libloading::Library::new(dylib_path).unwrap(); + + let kcl_run: libloading::Symbol< + unsafe extern "C" fn( + kclvm_main_ptr: u64, // main.k => kclvm_main + option_len: kclvm_size_t, + option_keys: *const *const kclvm_char_t, + option_values: *const *const kclvm_char_t, + strict_range_check: i32, + disable_none: i32, + disable_schema_check: i32, + list_option_mode: i32, + debug_mode: i32, + result_buffer_len: kclvm_size_t, + result_buffer: *mut kclvm_char_t, + warn_buffer_len: kclvm_size_t, + warn_buffer: *mut kclvm_char_t, + ) -> kclvm_size_t, + > = lib.get(b"_kcl_run").unwrap(); + + let kclvm_main: libloading::Symbol = lib.get(b"kclvm_main").unwrap(); + let kclvm_main_ptr = kclvm_main.into_raw().into_raw() as u64; + + let option_len = 0; + let option_keys = std::ptr::null(); + let option_values = std::ptr::null(); + let strict_range_check = 0; + let disable_none = settings + .kcl_cli_configs + .as_ref() + .map_or(0, |c| c.disable_none.map_or(0, |v| v as i32)); + let disable_schema_check = 0; + let list_option_mode = 0; + let debug_mode = settings + .kcl_cli_configs + .as_ref() + .map_or(0, |c| c.debug.map_or(0, |v| v as i32)); + + let mut result = vec![0u8; 1024 * 1024]; + let result_buffer_len = result.len() as i32 - 1; + let result_buffer = result.as_mut_ptr() as *mut i8; + + let mut warn_buffer = vec![0u8; 1024 * 1024]; + let warn_buffer_len = warn_buffer.len() as i32 - 1; + let warn_buffer = warn_buffer.as_mut_ptr() as *mut i8; + + let n = kcl_run( + kclvm_main_ptr, + option_len, + option_keys, + option_values, + strict_range_check, + disable_none, + disable_schema_check, + list_option_mode, + debug_mode, + result_buffer_len, + result_buffer, + warn_buffer_len, + warn_buffer, + ); + + let ctx = kclvm::Context::current_context_mut(); + ctx.cfg.debug_mode = debug_mode > 0; + ctx.cfg.disable_none = disable_none > 0; + let s = std::str::from_utf8(&result[0..n as usize]).unwrap(); + if s.is_empty() { + println!() + } else { + println!("{}", ValueRef::from_json(s).unwrap().plan_to_yaml_string()); + } + } + + Ok("".to_string()) + } + + pub fn link_dylibs(&mut self, dylibs: &[String], dylib_path: &str) -> String { + let mut dylib_path = dylib_path.to_string(); + + if dylib_path.is_empty() { + dylib_path = format!("{}{}", "_a.out", Self::get_lib_suffix()); + } + + let mut args: Vec = vec![ + "-Wno-override-module".to_string(), + "-Wno-error=unused-command-line-argument".to_string(), + "-Wno-unused-command-line-argument".to_string(), + "-shared".to_string(), + "-undefined".to_string(), + "dynamic_lookup".to_string(), + format!("-Wl,-rpath,{}/lib", self.executable_root), + format!("-L{}/lib", self.executable_root), + "-lkclvm_native_shared".to_string(), + format!("-I{}/include", self.executable_root), + ]; + let mut bc_files = dylibs.to_owned(); + args.append(&mut bc_files); + let mut more_args = vec![ + self.rust_libstd_dylib.clone(), + "-fPIC".to_string(), + "-o".to_string(), + dylib_path.to_string(), + ]; + args.append(&mut more_args); + + std::process::Command::new(self.clang_path.clone()) + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()) + .args(&args) + .output() + .expect("clang failed"); + + dylib_path + } + + pub fn run_clang(&mut self, bc_path: &str, dylib_path: &str) -> String { + let mut bc_path = bc_path.to_string(); + let mut dylib_path = dylib_path.to_string(); + + let mut bc_files = vec![]; + + for entry in glob::glob(&format!("{}*.ll", bc_path)).unwrap() { + match entry { + Ok(path) => { + if path.exists() { + bc_files.push(path); + } + } + Err(e) => println!("{:?}", e), + }; + } + let mut bc_files = bc_files + .iter() + .map(|f| f.to_str().unwrap().to_string()) + .collect::>(); + + if !Self::path_exist(bc_path.as_str()) { + let s = format!("{}.ll", bc_path); + if Self::path_exist(s.as_str()) { + bc_path = s; + } else { + let s = format!("{}.ll", bc_path); + if Self::path_exist(s.as_str()) { + bc_path = s; + } + } + } + + if dylib_path.is_empty() { + dylib_path = format!("{}{}", bc_path, Self::get_lib_suffix()); + } + + let mut args: Vec = vec![ + "-Wno-override-module".to_string(), + "-Wno-error=unused-command-line-argument".to_string(), + "-Wno-unused-command-line-argument".to_string(), + "-shared".to_string(), + "-undefined".to_string(), + "dynamic_lookup".to_string(), + format!("-Wl,-rpath,{}/lib", self.executable_root), + format!("-L{}/lib", self.executable_root), + "-lkclvm_native_shared".to_string(), + format!("-I{}/include", self.executable_root), + ]; + args.append(&mut bc_files); + let mut more_args = vec![ + self.rust_libstd_dylib.clone(), + "-fPIC".to_string(), + "-o".to_string(), + dylib_path.to_string(), + ]; + args.append(&mut more_args); + + std::process::Command::new(self.clang_path.clone()) + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()) + .args(&args) + .output() + .expect("clang failed"); + + dylib_path + } + + pub fn run_clang_single(&mut self, bc_path: &str, dylib_path: &str) -> String { + let mut bc_path = bc_path.to_string(); + let mut dylib_path = dylib_path.to_string(); + + if !Self::path_exist(bc_path.as_str()) { + let s = format!("{}.ll", bc_path); + if Self::path_exist(s.as_str()) { + bc_path = s; + } else { + let s = format!("{}.ll", bc_path); + if Self::path_exist(s.as_str()) { + bc_path = s; + } + } + } + + if dylib_path.is_empty() { + dylib_path = format!("{}{}", bc_path, Self::get_lib_suffix()); + } + + let mut args: Vec = vec![ + "-Wno-override-module".to_string(), + "-Wno-error=unused-command-line-argument".to_string(), + "-Wno-unused-command-line-argument".to_string(), + "-shared".to_string(), + "-undefined".to_string(), + "dynamic_lookup".to_string(), + format!("-Wl,-rpath,{}/lib", self.executable_root), + format!("-L{}/lib", self.executable_root), + "-lkclvm_native_shared".to_string(), + format!("-I{}/include", self.executable_root), + ]; + let mut bc_files = vec![bc_path]; + args.append(&mut bc_files); + let mut more_args = vec![ + self.rust_libstd_dylib.clone(), + "-fPIC".to_string(), + "-o".to_string(), + dylib_path.to_string(), + ]; + args.append(&mut more_args); + + std::process::Command::new(self.clang_path.clone()) + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()) + .args(&args) + .output() + .expect("clang failed"); + + dylib_path + } + + fn get_executable_root() -> String { + if Self::is_windows() { + todo!(); + } + + let kclvm_exe = if Self::is_windows() { + "kclvm.exe" + } else { + "kclvm" + }; + let p = if let Some(x) = Self::find_it(kclvm_exe) { + x + } else { + std::env::current_exe().unwrap() + }; + + let p = p.parent().unwrap().parent().unwrap(); + p.to_str().unwrap().to_string() + } + + fn get_rust_libstd_dylib(executable_root: &str) -> String { + let txt_path = std::path::Path::new(&executable_root) + .join("lib") + .join("rust-libstd-name.txt"); + let rust_libstd_name = std::fs::read_to_string(txt_path).unwrap(); + let rust_libstd_name = rust_libstd_name.trim(); + format!("{}/lib/{}", executable_root, rust_libstd_name) + } + + fn get_clang_path() -> String { + let clang_path = env::var("KCLVM_CLANG").map_or("clang".to_string(), |v| v); + if Self::is_windows() { + format!("{}.exe", clang_path) + } else { + clang_path + } + } + + pub fn get_lib_suffix() -> String { + if Self::is_windows() { + return ".dll".to_string(); + } + if Self::is_macos() { + return ".dylib".to_string(); + } + if Self::is_linux() { + return ".so".to_string(); + } + panic!("unsuport os") + } + + fn is_windows() -> bool { + cfg!(target_os = "windows") + } + fn is_macos() -> bool { + cfg!(target_os = "macos") + } + fn is_linux() -> bool { + cfg!(target_os = "linux") + } + + fn current_work_dir() -> String { + let p = std::env::current_dir().unwrap(); + let s = p.to_str().unwrap().to_string(); + s + } + fn is_file(path: &str) -> bool { + std::path::Path::new(path).is_file() + } + fn is_dir(path: &str) -> bool { + std::path::Path::new(path).is_dir() + } + + fn is_absolute(path: &str) -> bool { + std::path::Path::new(path).is_absolute() + } + + fn path_exist(path: &str) -> bool { + std::path::Path::new(path).exists() + } + + fn find_it

(exe_name: P) -> Option + where + P: AsRef, + { + std::env::var_os("PATH").and_then(|paths| { + std::env::split_paths(&paths) + .filter_map(|dir| { + let full_path = dir.join(&exe_name); + if full_path.is_file() { + Some(full_path) + } else { + None + } + }) + .next() + }) + } +} diff --git a/kclvm/runner/src/lib.rs b/kclvm/runner/src/lib.rs new file mode 100644 index 000000000..e7a51a61d --- /dev/null +++ b/kclvm/runner/src/lib.rs @@ -0,0 +1,2 @@ +pub mod command; +pub mod runner; diff --git a/kclvm/runner/src/runner.rs b/kclvm/runner/src/runner.rs new file mode 100644 index 000000000..77af158fb --- /dev/null +++ b/kclvm/runner/src/runner.rs @@ -0,0 +1,277 @@ +use serde::{Deserialize, Serialize}; + +use kclvm_ast::ast; + +#[allow(non_camel_case_types)] +pub type kclvm_char_t = i8; +#[allow(non_camel_case_types)] +pub type kclvm_size_t = i32; +#[allow(non_camel_case_types)] +pub type kclvm_context_t = std::ffi::c_void; +#[allow(non_camel_case_types)] +pub type kclvm_value_ref_t = std::ffi::c_void; + +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct ExecProgramArgs { + pub work_dir: Option, + pub k_filename_list: Vec, + pub k_code_list: Vec, + + pub args: Vec, + pub overrides: Vec, + + pub disable_yaml_result: bool, + pub print_override_ast: bool, + + // -r --strict-range-check + pub strict_range_check: bool, + + // -n --disable-none + pub disable_none: bool, + // -v --verbose + pub verbose: i32, + + // -d --debug + pub debug: i32, + + // yaml/json: sort keys + pub sort_keys: bool, + // include schema type path in JSON/YAML result + pub include_schema_type_path: bool, +} + +#[derive(Serialize, Deserialize, Debug, Default, Clone)] +pub struct ExecProgramResult { + pub json_result: String, + pub yaml_result: String, + + pub escaped_time: String, +} + +impl ExecProgramArgs { + pub fn from_str(s: &str) -> Self { + if s.trim().is_empty() { + return Default::default(); + } + serde_json::from_str::(s).expect(s) + } + pub fn to_json(&self) -> String { + serde_json::ser::to_string(self).unwrap() + } + + pub fn get_files(&self) -> Vec<&str> { + self.k_filename_list.iter().map(|s| s.as_str()).collect() + } + + pub fn get_load_program_options(&self) -> kclvm_parser::LoadProgramOptions { + kclvm_parser::LoadProgramOptions { + work_dir: self.work_dir.clone().unwrap_or("".to_string()).clone(), + k_code_list: self.k_code_list.clone(), + cmd_args: self.args.clone(), + cmd_overrides: self.overrides.clone(), + ..Default::default() + } + } +} + +#[derive(Debug, Default)] +pub struct KclvmRunnerOptions { + pub plugin_agent_ptr: u64, +} + +pub struct KclvmRunner { + dylib_path: String, + opts: KclvmRunnerOptions, + + lib: libloading::Library, +} + +impl KclvmRunner { + pub fn new(dylib_path: &str, opts: Option) -> Self { + let lib = unsafe { + libloading::Library::new(std::path::PathBuf::from(dylib_path).canonicalize().unwrap()) + .unwrap() + }; + Self { + dylib_path: dylib_path.to_string(), + opts: opts.unwrap_or_default(), + lib, + } + } + + pub fn run(&self, args: &ExecProgramArgs) -> Result { + unsafe { + Self::dylib_kclvm_plugin_init(&self.lib, self.opts.plugin_agent_ptr); + Self::dylib_kcl_run(&self.lib, &args) + } + } +} + +impl KclvmRunner { + unsafe fn dylib_kclvm_plugin_init(lib: &libloading::Library, plugin_method_ptr: u64) { + // get kclvm_plugin_init + let kclvm_plugin_init: libloading::Symbol< + unsafe extern "C" fn( + fn_ptr: extern "C" fn( + method: *const i8, + args_json: *const i8, + kwargs_json: *const i8, + ) -> *const i8, + ), + > = lib.get(b"kclvm_plugin_init").unwrap(); + + // get plugin_method + let plugin_method_ptr = plugin_method_ptr; + let plugin_method_ptr = (plugin_method_ptr as *const u64) as *const () + as *const extern "C" fn( + method: *const i8, + args: *const i8, + kwargs: *const i8, + ) -> *const i8; + let plugin_method: extern "C" fn( + method: *const i8, + args: *const i8, + kwargs: *const i8, + ) -> *const i8 = std::mem::transmute(plugin_method_ptr); + + // register plugin agent + kclvm_plugin_init(plugin_method); + } + + unsafe fn dylib_kcl_run( + lib: &libloading::Library, + args: &ExecProgramArgs, + ) -> Result { + let kcl_run: libloading::Symbol< + unsafe extern "C" fn( + kclvm_main_ptr: u64, // main.k => kclvm_main + option_len: kclvm_size_t, + option_keys: *const *const kclvm_char_t, + option_values: *const *const kclvm_char_t, + strict_range_check: i32, + disable_none: i32, + disable_schema_check: i32, + list_option_mode: i32, + debug_mode: i32, + result_buffer_len: kclvm_size_t, + result_buffer: *mut kclvm_char_t, + warn_buffer_len: kclvm_size_t, + warn_buffer: *mut kclvm_char_t, + ) -> kclvm_size_t, + > = lib.get(b"_kcl_run").unwrap(); + + let kclvm_main: libloading::Symbol = lib.get(b"kclvm_main").unwrap(); + let kclvm_main_ptr = kclvm_main.into_raw().into_raw() as u64; + + let option_len = args.args.len() as kclvm_size_t; + + let cstr_argv: Vec<_> = args + .args + .iter() + .map(|arg| std::ffi::CString::new(arg.name.as_str()).unwrap()) + .collect(); + + let mut p_argv: Vec<_> = cstr_argv + .iter() // do NOT into_iter() + .map(|arg| arg.as_ptr()) + .collect(); + p_argv.push(std::ptr::null()); + + let p: *const *const kclvm_char_t = p_argv.as_ptr(); + let option_keys = p; + + let cstr_argv: Vec<_> = args + .args + .iter() + .map(|arg| std::ffi::CString::new(arg.value.as_str()).unwrap()) + .collect(); + + let mut p_argv: Vec<_> = cstr_argv + .iter() // do NOT into_iter() + .map(|arg| arg.as_ptr()) + .collect(); + p_argv.push(std::ptr::null()); + + let p: *const *const kclvm_char_t = p_argv.as_ptr(); + let option_values = p; + + let strict_range_check = args.strict_range_check as i32; + let disable_none = args.disable_none as i32; + let disable_schema_check = 0; // todo + let list_option_mode = 0; // todo + let debug_mode = args.debug as i32; + + let mut result = vec![0u8; 1024 * 1024]; + let result_buffer_len = result.len() as i32 - 1; + let result_buffer = result.as_mut_ptr() as *mut i8; + + let mut warn_data = vec![0u8; 1024 * 1024]; + let warn_buffer_len = warn_data.len() as i32 - 1; + let warn_buffer = warn_data.as_mut_ptr() as *mut i8; + + let n = kcl_run( + kclvm_main_ptr, + option_len, + option_keys, + option_values, + strict_range_check, + disable_none, + disable_schema_check, + list_option_mode, + debug_mode, + result_buffer_len, + result_buffer, + warn_buffer_len, + warn_buffer, + ); + + if n > 0 { + let return_len = n; + let s = std::str::from_utf8(&result[0..return_len as usize]).unwrap(); + Ok(s.to_string()) + } else { + let return_len = 0 - n; + let s = std::str::from_utf8(&warn_data[0..return_len as usize]).unwrap(); + Err(s.to_string()) + } + } +} + +impl KclvmRunner { + // only for test + fn invoke_plugin_method( + plugin_method_ptr: u64, + method: &str, + args: &str, + kwargs: &str, + ) -> String { + use std::ffi::CStr; + use std::ffi::CString; + + if plugin_method_ptr == 0 { + panic!("no plugin") + } + + let plugin_method_ptr = (plugin_method_ptr as *const u64) as *const () + as *const extern "C" fn( + method: *const i8, + args: *const i8, + kwargs: *const i8, + ) -> *const i8; + let plugin_method: extern "C" fn( + method: *const i8, + args: *const i8, + kwargs: *const i8, + ) -> *const i8 = unsafe { std::mem::transmute(plugin_method_ptr) }; + + let method = CString::new(method).unwrap(); + let args = CString::new(args).unwrap(); + let kwargs = CString::new(kwargs).unwrap(); + + let _result_ptr = plugin_method(method.as_ptr(), args.as_ptr(), kwargs.as_ptr()); + let result: &CStr = unsafe { CStr::from_ptr(_result_ptr) }; + let result: String = result.to_string_lossy().into(); + + result + } +} diff --git a/kclvm/runtime/.gitignore b/kclvm/runtime/.gitignore new file mode 100644 index 000000000..6fbaabf93 --- /dev/null +++ b/kclvm/runtime/.gitignore @@ -0,0 +1,3 @@ +target +a.out* +*.so diff --git a/kclvm/runtime/Cargo.lock b/kclvm/runtime/Cargo.lock new file mode 100644 index 000000000..8fcacc574 --- /dev/null +++ b/kclvm/runtime/Cargo.lock @@ -0,0 +1,615 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "fancy-regex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "json_minimal" +version = "0.1.3" + +[[package]] +name = "kclvm-runtime" +version = "0.1.0" +dependencies = [ + "ahash", + "base64", + "bstr", + "chrono", + "fancy-regex", + "indexmap", + "itertools", + "json_minimal", + "kclvm_runtime_internal_macros", + "libc", + "md5", + "num-integer", + "phf", + "regex", + "serde_json", + "serde_yaml", + "sha1", + "sha2", + "unic-ucd-bidi", + "unic-ucd-category", + "unicode-casing", +] + +[[package]] +name = "kclvm_runtime_internal_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "phf" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" +dependencies = [ + "siphasher", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "serde" +version = "1.0.131" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1" + +[[package]] +name = "serde_json" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + +[[package]] +name = "sha2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "siphasher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "typenum" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-category" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8d4591f5fcfe1bd4453baaf803c40e1b1e69ff8455c47620440b46efef91c0" +dependencies = [ + "matches", + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-casing" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "623f59e6af2a98bdafeb93fa277ac8e1e40440973001ca15cf4ae1541cd16d56" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/kclvm/runtime/Cargo.toml b/kclvm/runtime/Cargo.toml new file mode 100644 index 000000000..44adfbaef --- /dev/null +++ b/kclvm/runtime/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "kclvm-runtime" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = [ + "staticlib", + "cdylib", + "rlib" +] +name = "kclvm" +path = "src/lib.rs" + +[dependencies] +kclvm_runtime_internal_macros = { path = "./internal_macros" } +json_minimal = {path = "./src/3rdparty/json_minimal", version = "0.1.0"} +serde_yaml = "0.8.23" + +base64 = "0.13.0" +serde_json = "1.0.69" +libc = "0.2.112" +itertools = "0.10.3" +unic-ucd-bidi = "0.9" +unic-ucd-category = "0.9" +unicode-casing = "0.1" +bstr = "0.2.16" +regex = "1.5" +md5 = "0.7.0" +sha2 = "0.9.8" +sha1 = "0.6.0" +chrono = "0.4.19" +ahash = "0.7.2" +indexmap = "1.0" +phf = { version = "0.9", features = ["macros"] } +fancy-regex = "0.7.1" +num-integer = "0.1.44" + +#pprof = { version = "0.4", features = ["flamegraph"] } + +[profile.release] +rpath = true +panic = "unwind" +opt-level = "z" # Optimize for size. +lto = true diff --git a/kclvm/runtime/Makefile b/kclvm/runtime/Makefile new file mode 100644 index 000000000..d1453760d --- /dev/null +++ b/kclvm/runtime/Makefile @@ -0,0 +1,78 @@ +default: + make gen-api-spec + cargo test + +gen-api-spec: + mkdir -p target + + cargo clean -q + + kclvm_RUNTIME_GEN_API_SPEC= cargo build > ./src/_kclvm_api_spec.rs.tmp + + echo "// Copyright 2022 The KCL Authors. All rights reserved.\n" > ./src/_kclvm_api_spec.rs + echo "// Auto generated by command, DONOT EDIT!!!\n" >> ./src/_kclvm_api_spec.rs + cat ./src/_kclvm_api_spec.rs.tmp >> ./src/_kclvm_api_spec.rs + rm ./src/_kclvm_api_spec.rs.tmp + + make -C ./tools/kclvm-runtime-gen-api + +dev: + cargo build + cargo run --bin hello-llvm + llvm-dis a.out.bc + + # Rust can't re-export from a linked C library (unless you rename) when compiled as a cdylib. + # https://marcopolo.io/code/from-c-to-rust-to-c/ + # https://github.com/rust-lang/rfcs/issues/2771 + + clang++ -stdlib=libc++ -std=c++14 -Wno-override-module -o a.out.exe a.out.bc ./target/debug/libkclvm.a + #clang++ -Wno-override-module -o a.out.exe a.out.bc ./target/debug/libkclvm.dylib + ./a.out.exe + +lib: + cargo build + #nm -gU ./target/debug/libkclvm.dylib + nm -gU ./target/debug/libkclvm.a + +ll: + # target/debug/deps/kclvm.ll + + # cargo build + # cargo rustc --lib -- --emit=llvm-ir + # cargo rustc --lib -- --emit=llvm-bc + # cp target/debug/deps/kclvm.ll target/kclvm-debug.ll + # cp target/debug/deps/kclvm.bc target/kclvm-debug.bc + + # https://stackoverflow.com/questions/69042049/rust-including-dependenies-in-llvm-bitcode + + RUSTFLAGS="--emit=llvm-bc" cargo build + llvm-link target/debug/deps/*.bc > target/kclvm-debug.bc + llvm-dis target/kclvm-debug.bc + +ll-release: + RUSTFLAGS="--emit=llvm-bc" cargo build --release + llvm-link target/release/deps/*.bc > target/kclvm-release.bc + llvm-dis target/kclvm-release.bc + +ll-wasm: + RUSTFLAGS="--emit=llvm-bc" cargo build --release --target wasm32-unknown-unknown + llvm-link target/wasm32-unknown-unknown/release/deps/*.bc > target/kclvm-wasm.bc + llvm-dis target/kclvm-wasm.bc + +fmt: + cargo fmt + +test: + cargo test + +# Only for linux, run it in docker +coverage: + cargo install cargo-tarpaulin + cargo tarpaulin -v + +lint: + cargo clippy + +clean: + -rm -rf target + -rm a.out diff --git a/kclvm/runtime/internal_macros/Cargo.lock b/kclvm/runtime/internal_macros/Cargo.lock new file mode 100644 index 000000000..a60b6e922 --- /dev/null +++ b/kclvm/runtime/internal_macros/Cargo.lock @@ -0,0 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "kclvm_runtime_internal_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" diff --git a/kclvm/runtime/internal_macros/Cargo.toml b/kclvm/runtime/internal_macros/Cargo.toml new file mode 100644 index 000000000..2b9db5b8b --- /dev/null +++ b/kclvm/runtime/internal_macros/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "kclvm_runtime_internal_macros" +version = "0.1.0" +edition = "2021" + +[dependencies] +syn = { version = "1.0", features = ["full"] } +proc-macro2 = "1.0" +quote = "1.0" + +[lib] +proc-macro = true + +[features] +default = [] diff --git a/kclvm/runtime/internal_macros/src/lib.rs b/kclvm/runtime/internal_macros/src/lib.rs new file mode 100644 index 000000000..94ee77c2f --- /dev/null +++ b/kclvm/runtime/internal_macros/src/lib.rs @@ -0,0 +1,218 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, FnArg}; + +// ---------------------------------------------------------------------------- + +#[proc_macro_attribute] +pub fn runtime_fn(_attr: TokenStream, item: TokenStream) -> TokenStream { + let parsed_fn = parse_macro_input!(item as syn::ItemFn); + + if let Ok(_) = std::env::var("kclvm_RUNTIME_GEN_API_SPEC") { + print_api_spec(&parsed_fn); + } + + let x = quote! { + #parsed_fn + }; + return x.into(); +} + +// ---------------------------------------------------------------------------- + +#[derive(Debug)] +enum TargetName { + C, + LLVM, +} + +fn print_api_spec(fn_item: &syn::ItemFn) { + let fn_name = get_fn_name(fn_item); + let fn_c_sig = get_fn_sig(fn_item, &TargetName::C); + let fn_llvm_sig = get_fn_sig(fn_item, &TargetName::LLVM); + + // skip _fn_name() + if !fn_name.starts_with("_") { + println!("// api-spec: {}", fn_name); + println!("// api-spec(c): {};", fn_c_sig); + println!("// api-spec(llvm): {};", fn_llvm_sig); + println!(); + } +} + +// ---------------------------------------------------------------------------- + +fn get_fn_name(fn_item: &syn::ItemFn) -> String { + fn_item.sig.ident.to_string() +} + +fn get_fn_sig(fn_item: &syn::ItemFn, target: &TargetName) -> String { + let fn_name = get_fn_name(fn_item); + let args_type = get_fn_args_type(fn_item, target); + let output_type = get_fn_output_type(fn_item, target); + + match target { + TargetName::C => format!("{} {}({})", output_type, fn_name, args_type), + TargetName::LLVM => format!("declare {} @{}({})", output_type, fn_name, args_type), + } +} + +fn get_fn_output_type(fn_item: &syn::ItemFn, target: &TargetName) -> String { + match target { + TargetName::C => match &fn_item.sig.output { + syn::ReturnType::Type(_, ty) => build_c_type(ty), + syn::ReturnType::Default => "void".to_string(), + }, + TargetName::LLVM => match &fn_item.sig.output { + syn::ReturnType::Type(_, ty) => build_llvm_type(ty), + syn::ReturnType::Default => "void".to_string(), + }, + } +} + +// ---------------------------------------------------------------------------- + +fn get_fn_args_type(fn_item: &syn::ItemFn, target: &TargetName) -> String { + let fn_name = get_fn_name(fn_item); + let inputs = &fn_item.sig.inputs; + + let mut result = String::new(); + for (i, arg) in inputs.iter().enumerate() { + let arg_name = get_fn_arg_name(arg, target); + let arg_typ = get_fn_arg_type(arg, target); + + if arg_name.is_empty() { + panic!("{}", format!("{}, arg{}: invalid arg type", fn_name, i)); + } + if arg_typ.is_empty() { + panic!("{}", format!("{}, arg{}: invalid arg type", fn_name, i)); + } + + if i > 0 { + result.push_str(", "); + } + result.push_str(format!("{} {}", arg_typ, arg_name).as_str()); + } + + result +} + +// ---------------------------------------------------------------------------- + +fn get_fn_arg_name(arg: &FnArg, target: &TargetName) -> String { + match arg { + syn::FnArg::Typed(ty) => match ty { + syn::PatType { pat, .. } => match &**pat { + syn::Pat::Ident(x) => match target { + TargetName::C => x.ident.to_string(), + TargetName::LLVM => format!("%{}", x.ident), + }, + _ => panic!("unsupported type: {}", quote!(#ty)), + }, + }, + _ => panic!("unsupported arg: {}", quote!(#arg)), + } +} + +fn get_fn_arg_type(arg: &FnArg, target: &TargetName) -> String { + match arg { + syn::FnArg::Typed(ty) => match ty { + syn::PatType { ty, .. } => match target { + TargetName::C => build_c_type(ty), + TargetName::LLVM => build_llvm_type(ty), + }, + }, + _ => panic!("unsupported fn arg: {}", quote!(#arg)), + } +} + +// ---------------------------------------------------------------------------- + +fn build_c_type(ty: &syn::Type) -> String { + match ty { + syn::Type::Path(ty_path) => { + let ty_name = ty_path.path.segments[0].ident.to_string(); + + match ty_name.as_str() { + "c_void" => "void".to_string(), + "c_char" => "char".to_string(), + + "bool" => "uint8_t".to_string(), + + "i8" => "int8_t".to_string(), + "u8" => "uint8_t".to_string(), + + "i16" => "int16_t".to_string(), + "u16" => "uint16_t".to_string(), + + "i32" => "int32_t".to_string(), + "u32" => "uint32_t".to_string(), + + "i64" => "int64_t".to_string(), + "u64" => "uint64_t".to_string(), + "i128" | "u128" => "".to_string(), + + "f32" => "float".to_string(), + "f64" => "double".to_string(), + + _ => ty_name, + } + } + syn::Type::Ptr(ty_ptr) => { + let base_ty = &ty_ptr.elem; + let base_constr = build_c_type(&base_ty); + format!("{}*", base_constr).to_string() + } + syn::Type::Reference(ty_ref) => { + let base_ty = &ty_ref.elem; + let base_constr = build_c_type(&base_ty); + format!("{}*", base_constr).to_string() + } + syn::Type::BareFn(_) => "void*".to_string(), + syn::Type::Never(_) => "void".to_string(), + _ => panic!("unsupported type: {}", quote!(#ty)), + } +} + +fn build_llvm_type(ty: &syn::Type) -> String { + match ty { + syn::Type::Path(ty_path) => { + let ty_name = ty_path.path.segments[0].ident.to_string(); + + match ty_name.as_str() { + "c_void" => "void".to_string(), + "c_char" => "i8".to_string(), + + "bool" => "i8".to_string(), + + "i8" | "u8" => "i8".to_string(), + "i16" | "u16" => "i16".to_string(), + "i32" | "u32" => "i32".to_string(), + "i64" | "u64" => "i64".to_string(), + "i128" | "u128" => "i128".to_string(), + + "f32" => "float".to_string(), + "f64" => "double".to_string(), + + _ => format!("%{}", ty_name), + } + } + syn::Type::Ptr(ty_ptr) => { + let base_ty = &ty_ptr.elem; + let base_constr = build_llvm_type(&base_ty); + format!("{}*", base_constr).to_string() + } + syn::Type::Reference(ty_ref) => { + let base_ty = &ty_ref.elem; + let base_constr = build_llvm_type(&base_ty); + format!("{}*", base_constr).to_string() + } + syn::Type::BareFn(_) => "i8*".to_string(), + syn::Type::Never(_) => "void".to_string(), + _ => panic!("unsupported type: {}", quote!(#ty)), + } +} + +// ---------------------------------------------------------------------------- +// END +// ---------------------------------------------------------------------------- diff --git a/kclvm/runtime/readme.md b/kclvm/runtime/readme.md new file mode 100644 index 000000000..1ff43103d --- /dev/null +++ b/kclvm/runtime/readme.md @@ -0,0 +1,7 @@ +# KCLVM - runtime + +KCLVM runtime library. + +- run test: `make` +- regenerate llvm-ir file: `make gen-api-spec` +- lint code: `cargo clippy` diff --git a/kclvm/runtime/src/3rdparty/json_minimal/.gitignore b/kclvm/runtime/src/3rdparty/json_minimal/.gitignore new file mode 100644 index 000000000..96ef6c0b9 --- /dev/null +++ b/kclvm/runtime/src/3rdparty/json_minimal/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/kclvm/runtime/src/3rdparty/json_minimal/Cargo.toml b/kclvm/runtime/src/3rdparty/json_minimal/Cargo.toml new file mode 100644 index 000000000..5fb678c94 --- /dev/null +++ b/kclvm/runtime/src/3rdparty/json_minimal/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "json_minimal" +version = "0.1.3" +authors = ["36den"] +edition = "2021" +description = "A minimal json crate." +license = "MIT OR Apache-2.0" +keywords = ["json","minimal"] +categories = ["encoding"] +repository = "https://github.com/36den/json_minimal-rust" +readme = "README.md" + +[dependencies] diff --git a/kclvm/runtime/src/3rdparty/json_minimal/LICENSE.md b/kclvm/runtime/src/3rdparty/json_minimal/LICENSE.md new file mode 100644 index 000000000..5147935e1 --- /dev/null +++ b/kclvm/runtime/src/3rdparty/json_minimal/LICENSE.md @@ -0,0 +1,19 @@ +# APACHE + +Copyright 2020 36den + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +`http://www.apache.org/licenses/LICENSE-2.0` + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +# MIT + +Copyright 2020 36den + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/kclvm/runtime/src/3rdparty/json_minimal/README.md b/kclvm/runtime/src/3rdparty/json_minimal/README.md new file mode 100644 index 000000000..f5869899a --- /dev/null +++ b/kclvm/runtime/src/3rdparty/json_minimal/README.md @@ -0,0 +1,316 @@ +# json_minimal + +A minimal json crate conforming to https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf . + +## Tutorial (creating jsons) + +In order to create a valid (i.e. generally accepted) json you should always start with: +```rust + use json_minimal::*; + + let mut json = Json::new(); + // which is equivalent to + let mut json = Json::JSON(Vec::new()); + // ... +``` + +To add an object, simply do this: +```rust + // ... + let greeting = + Json::OBJECT { + name: String::from("Greeting"), + + value: Box::new( + Json::STRING( String::from("Hello, world!") ) + ) + } + ; + + json.add(greeting); + // ... +``` +or alternatively: +```rust + // ... + json.add( + Json::OBJECT { + name: String::from("Greeting"), + + value: Box::new( + Json::STRING( String::from("Hello, world!") ) + ) + } + ); + // ... +``` + +As you can see, whilst the crate is minimal (in my opinion) it may not be the quickest to work with. This becomes clearer when adding an array to an object: +```rust + // ... + + let mut days_in_the_week = + Json::OBJECT { + name: String::from("Days of the week"), + + value: Box::new( + Json::JSON(Vec::new()) + ) + } + ; + + let mut days = Json::ARRAY(Vec::new()); + + days + .add( + Json::STRING( String::from("Monday") ) + ) + .add( + Json::STRING( String::from("Tuesday") ) + ) + .add( + Json::STRING( String::from("Wednesday") ) + ) + .add( + Json::STRING( String::from("Thursday") ) + ) + .add( + Json::STRING( String::from("Friday") ) + ) + .add( + Json::STRING( String::from("Saturday") ) + ) + .add( + Json::STRING( String::from("Sunday") ) + ) + ; + + days_in_the_week + .add( + Json::OBJECT { + name: String::from("Total number of days"), + + value: Box::new( + Json::NUMBER(7.0) // Accepts `f64` + ) + } + ) + .add( + Json::OBJECT { + name: String::from("They are called"), + + value: Box::new( + days + ) + } + ) + ; + + json.add(days_in_the_week); + // ... +``` + +In conclusion: +```rust + // ... + + let mut conclusion = + Json::OBJECT { + name: String::from("Conclusion"), + + value: Box::new( + Json::JSON(Vec::new()) + ) + } + ; + + conclusion + .add( + Json::OBJECT { + name: String::from("Minimal in my opinion"), + + value: Box::new( + Json::BOOL(true) + ) + } + ) + .add( + Json::OBJECT { + name: String::from("How much I care about your opinion"), + + value: Box::new( + Json::NULL + ) + } + ) + .add( + Json::OBJECT { + name: String::from("Comment"), + + value: Box::new( + Json::STRING( String::from(";)") ) + ) + } + ) + ; + + json.add(conclusion); + // ... +``` + +Calling: +```rust + // ... + let resulting_json = json.print(); +``` +will result in a `String` containing: +`{"Greeting":"Hello, world!","Days of the week":{"Total number of days":7,"They are called":["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]},"Conclusion":{"Minimal in my opinion":true,"How much I care about your opinion":null,"Comment":";)"}}` + +If you would like the json string in a different format you can easily make your own 'print' function. + +## Tutorial (parsing and working with jsons) + +Parsing a json value from bytes is even more minimal - at the cost of being more cumbersome. Let's see how we can parse the json we generated above: +```rust + use json_minimal::*; + + let json = match Json::parse(b"{\"Greeting\":\"Hello, world!\",\"Days of the week\":{\"Total number of days\":7,\"They are called\":[\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\",\"Sunday\"]},\"Conclusion\":{\"Minimal in my opinion\":true,\"How much I care about your opinion\":null,\"Comment\":\";)\"}}") { + Ok(json) => { + json + }, + Err( (position,message) ) => { + panic!("`{}` at position `{}`!!!"); + } + } + // ... +``` + +Let's first talk about what information is given for a parsing error. As you might expect it is minimal. `position` above is the position were everything went wrong and the `message` will be something like`"Error parsing array."` if, for example, a closing `]` is missing somewhere. Continuing where we left off: +```rust + // ... + match json.get("Greeting") { + Some(json) => { + match json { + Json::OBJECT { name: _, value } => { + match value.unbox() { + Json::STRING(val) => { + assert_eq!("Hello, world!",val); + }, + json => { + panic!("Expected Json::STRING but found {:?}",json); + } + } + }, + json => { + panic!("Expected Json::JSON but found {:?}!!!",json) + } + } + }, + None => { + panic!("Couln't find Greeting. How rude!"); + } + } + // ... +``` +Unfortunately all of this was necessary because, even though we were able to confirm that `"Greeting"` exists, we had no way of knowing what it really is. It's not over: +```rust + // ... + match json.get("Days of the week") { // Hint: You can also use `get_mut` to aid in editing/creating jsons... + Some(json) => { + match json { + Json::OBJECT { name: _, value } => { + match value.unbox() { + Json::JSON(values) => { + assert_eq!(values.len(),2); + + match &values[0] { + Json::OBJECT { name, value: _ } => { + assert_eq!("Total number of days",name); + }, + json => { + panic!("Expected Json::OBJECT but found {:?}!!!",json); + } + } + + match &values[1] { + Json::OBJECT { name, value: _ } => { + assert_eq!("They are called",name); + }, + json => { + panic!("Expected Json::OBJECT but found {:?}!!!",json); + } + } + + }, + json => { + panic!("Expected Json::JSON but found {:?}!!!",json); + } + } + }, + json => { + panic!("Expected Json::OBJECT but found {:?}!!!",json); + } + } + }, + None => { + panic!("Days of the week not found!"); + } + } + // You get the idea. +``` +The function `Json::parse(...)` can also parse 'standalone values'. Example: + +```rust + match Json::parse("\"What's up?\"") { + Ok(json) => { + match json { + Json::STRING(val) => { + assert_eq!("What's up?",val); + }, + json => { + panic!("Expected Json::STRING but found {:?}!!!",json); + } + } + }, + Err( (position,message) ) => { + panic!("`{}` at position `{}`."); + } + } + + // Another example: + + match Json::parse("[1,2,3,\"four\"]") { + Ok(json) => { + match json { + Json::ARRAY(val) => { + assert_eq!(val.len(),4); + }, + json => { + panic!("Expected Json::ARRAY but found {:?}!!!",json); + } + } + }, + Err( (position,message) ) => { + panic!("`{}` at position `{}`."); + } + } +``` +## Changes & Improvements + +* Lonami (github) has made improvements: + 1. `json_minimal` can now parse non-ASCII strings and escape sequences. (I overlooked this, I admit.) + 2. The code is cleaner thanks to the question-mark operator and using rustfmt. + 3. Some parsing stuff that didn't work now works. + + A thousand thanks to Lonami !!! + +* `json_minimal` can now also parse 'pretty' json like this (as long as only `\r`, `\n`, `\t` and whitespace were used for formatting): +``` +{ + "Array": [ "Hello" , "World" , "!" ] +} +``` +This should also have worked from the start but I did not include because it of my aversion to energy inefficiency (although it is, perhaps, unfounded). + +--- +Please let me know if something doesn't work. I can't promise i'll react immediately, though. \ No newline at end of file diff --git a/kclvm/runtime/src/3rdparty/json_minimal/src/lib.rs b/kclvm/runtime/src/3rdparty/json_minimal/src/lib.rs new file mode 100644 index 000000000..86dbdcd92 --- /dev/null +++ b/kclvm/runtime/src/3rdparty/json_minimal/src/lib.rs @@ -0,0 +1,1070 @@ +#[derive(Debug)] +pub enum Json { + OBJECT { name: String, value: Box }, + JSON(Vec), + ARRAY(Vec), + STRING(String), + NUMBER(f64), + INT(i64), + FLOAT(f64), + BOOL(bool), + NULL, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct PrintOption { + pub sort_keys: bool, + pub indent: i32, + pub sep_space: bool, + pub py_style_f64: bool, + pub append_null: bool, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct ParseOption { + pub support_int: bool, +} + +impl Json { + /// Construct a new `Json::JSON` + /// ## Example + /// ``` + /// use json_minimal::*; + /// + /// let mut json = Json::new(); + /// ``` + pub fn new() -> Json { + Json::JSON(Vec::new()) + } + + /// Add any `Json` variant to a `Json` variant of type `Json::JSON`, `Json::ARRAY` + /// or a `Json::OBJECT` (holding a `Json::JSON`,`Json::ARRAY`,`Json::OBJECT` (holding a `Json::JSON`,`Json::`...)). + /// ## Panics! + /// Will panic if the conditions stated above are not met OR if an attempt is made to add a `Json::JSON` to a `Json::JSON` + /// without wrapping it in a `Json::OBJECT` first. + /// ## Example + /// ``` + /// use json_minimal::*; + /// + /// let mut json = Json::new(); + /// + /// json + /// .add( + /// Json::OBJECT { + /// name: String::from("Greeting"), + /// + /// value: Box::new( + /// Json::STRING( String::from("Hello, world!") ) + /// ) + /// } + /// ) + /// ; + /// ``` + /// See the tutorial on github for more. + pub fn add(&mut self, value: Json) -> &mut Json { + match self { + Json::JSON(values) => match value { + Json::OBJECT { name, value } => { + values.push(Json::OBJECT { name, value }); + } + Json::JSON(_) => { + panic!("A `Json::JSON` may not be added to a `Json::JSON` if it is not within a `Json::OBJECT`."); + } + Json::ARRAY(vals) => { + values.push(Json::ARRAY(vals)); + } + Json::STRING(val) => { + values.push(Json::STRING(val)); + } + Json::NUMBER(val) => { + values.push(Json::NUMBER(val)); + } + Json::INT(val) => { + values.push(Json::INT(val)); + } + Json::FLOAT(val) => { + values.push(Json::FLOAT(val)); + } + Json::BOOL(val) => { + values.push(Json::BOOL(val)); + } + Json::NULL => { + values.push(Json::NULL); + } + }, + Json::OBJECT { + name: _, + value: obj_val, + } => match obj_val.unbox_mut() { + Json::JSON(values) => match value { + Json::OBJECT { name, value } => { + values.push(Json::OBJECT { name, value }); + } + Json::JSON(_) => { + panic!("A `Json::JSON` may not be added to a `Json::JSON` if it is not within a `Json::OBJECT`."); + } + Json::ARRAY(vals) => { + values.push(Json::ARRAY(vals)); + } + Json::STRING(val) => { + values.push(Json::STRING(val)); + } + Json::NUMBER(val) => { + values.push(Json::NUMBER(val)); + } + Json::INT(val) => { + values.push(Json::INT(val)); + } + Json::FLOAT(val) => { + values.push(Json::FLOAT(val)); + } + Json::BOOL(val) => { + values.push(Json::BOOL(val)); + } + Json::NULL => { + values.push(Json::NULL); + } + }, + Json::ARRAY(values) => match value { + Json::OBJECT { name, value } => { + values.push(Json::OBJECT { name, value }); + } + Json::JSON(vals) => { + values.push(Json::JSON(vals)); + } + Json::ARRAY(vals) => { + values.push(Json::ARRAY(vals)); + } + Json::STRING(val) => { + values.push(Json::STRING(val)); + } + Json::NUMBER(val) => { + values.push(Json::NUMBER(val)); + } + Json::INT(val) => { + values.push(Json::INT(val)); + } + Json::FLOAT(val) => { + values.push(Json::FLOAT(val)); + } + Json::BOOL(val) => { + values.push(Json::BOOL(val)); + } + Json::NULL => { + values.push(Json::NULL); + } + }, + json => { + panic!("The function `add(`&mut self`,`name: String`,`value: Json`)` may only be called on a `Json::JSON`, `Json::ARRAY` or `Json::OBJECT` holding a `Json::JSON` or `Json::ARRAY`. It was called on: {:?}",json); + } + }, + Json::ARRAY(values) => match value { + Json::OBJECT { name, value } => { + values.push(Json::OBJECT { name, value }); + } + Json::JSON(vals) => { + values.push(Json::JSON(vals)); + } + Json::ARRAY(vals) => { + values.push(Json::ARRAY(vals)); + } + Json::STRING(val) => { + values.push(Json::STRING(val)); + } + Json::NUMBER(val) => { + values.push(Json::NUMBER(val)); + } + Json::INT(val) => { + values.push(Json::INT(val)); + } + Json::FLOAT(val) => { + values.push(Json::FLOAT(val)); + } + Json::BOOL(val) => { + values.push(Json::BOOL(val)); + } + Json::NULL => { + values.push(Json::NULL); + } + }, + json => { + panic!("The function `add(`&mut self`,`name: String`,`value: Json`)` may only be called on a `Json::JSON`, `Json::ARRAY` or `Json::OBJECT` holding a `Json::JSON` or `Json::ARRAY`. It was called on: {:?}",json); + } + } + + self + } + + /// Get the `Json` with the requested name if it exists. + /// ## Panics + /// This function will panic if called on a `Json` variant other than `Json::JSON` or `Json::OBJECT`, + /// as only these two variants may hold `Json::OBJECT` (which has a `name` field). + /// ## Example + /// ``` + /// use json_minimal::*; + /// + /// let mut json = Json::new(); + /// + /// json + /// .add( + /// Json::OBJECT { + /// name: String::from("Greeting"), + /// + /// value: Box::new( + /// Json::STRING( String::from("Hello, world!") ) + /// ) + /// } + /// ) + /// ; + /// + /// match json.get("Greeting") { + /// Some(json) => { + /// match json { + /// Json::OBJECT { name, value } => { + /// match value.unbox() { // See `unbox()` below + /// Json::STRING(val) => { + /// assert_eq!("Hello, world!",val); + /// }, + /// _ => { + /// panic!("I expected this to be a `Json::STRING`!!!"); + /// } + /// } + /// }, + /// _ => { + /// panic!("This shouldn't happen!!!"); + /// } + /// } + /// }, + /// None => { + /// panic!("Not found!!!"); + /// } + /// } + /// ``` + pub fn get(&self, search: &str) -> Option<&Json> { + match self { + Json::JSON(values) => { + for n in 0..values.len() { + match &values[n] { + Json::OBJECT { name, value: _ } => { + if name == search { + return Some(&values[n]); + } + } + _ => {} + } + } + + return None; + } + Json::OBJECT { name: _, value } => match value.unbox() { + Json::JSON(values) => { + for n in 0..values.len() { + match &values[n] { + Json::OBJECT { name, value: _ } => { + if name == search { + return Some(&values[n]); + } + } + _ => {} + } + } + + return None; + } + json => { + panic!("The function `get(`&self`,`search: &str`)` may only be called on a `Json::JSON` or a `Json::OBJECT` holding a `Json::JSON`. I was called on: {:?}",json); + } + }, + json => { + panic!("The function `get(`&self`,`search: &str`)` may only be called on a `Json::JSON`. I was called on: {:?}",json); + } + } + } + + /// Same as `get` above, but the references are mutable. Use `unbox_mut()` (see below) with this one. + /// ## Panics + /// This function will panic if called on a `Json` variant other than `Json::JSON` or `Json::OBJECT`, + /// as only these two variants may hold `Json::OBJECT` which has a `name` field. + pub fn get_mut(&mut self, search: &str) -> Option<&mut Json> { + match self { + Json::JSON(values) => { + for n in 0..values.len() { + match &values[n] { + Json::OBJECT { name, value: _ } => { + if name == search { + return Some(&mut values[n]); + } + } + _ => {} + } + } + } + Json::OBJECT { name: _, value } => match value.unbox_mut() { + Json::JSON(values) => { + for n in 0..values.len() { + match &values[n] { + Json::OBJECT { name, value: _ } => { + if name == search { + return Some(&mut values[n]); + } + } + _ => {} + } + } + } + json => { + panic!("The function `get_mut(`&self`,`search: &str`)` may only be called on a `Json::JSON` or a `Json::OBJECT` holding a `Json::JSON`. I was called on: {:?}",json); + } + }, + json => { + panic!("The function `get_mut(`&self`,`search: &str`)` may only be called on a `Json::JSON` or a `Json::OBJECT` holding a `Json::JSON`. I was called on: {:?}",json); + } + } + + None + } + + /// Enables matching the contents of a `Box`. + pub fn unbox(&self) -> &Json { + self + } + + /// Idem. + pub fn unbox_mut(&mut self) -> &mut Json { + self + } + + /// Returns a `String` of the form: `{"Json":"Value",...}` but can also be called on 'standalone objects' + /// which could result in `"Object":{"Stuff":...}` or `"Json":true`. + pub fn print(&self) -> String { + return self._print(&Default::default(), 0); + } + + pub fn print_with_option(&self, opt: &PrintOption) -> String { + let s = self._print(opt, 0); + if !opt.append_null { + return s; + } + + let mut result = s.clone(); + result.push('\0'); + return result; + } + + fn _print_indent(&self, result: &mut String, opt: &PrintOption, level: i32) { + if opt.indent == 0 || level == 0 { + return; + } + for _i in 0..level { + for _j in 0..opt.indent { + result.push(' '); + } + } + } + fn _print_sep_space(&self, result: &mut String, opt: &PrintOption, is_tail: bool) { + if opt.indent > 0 { + result.push('\n'); + return; + } + if opt.sep_space && !is_tail { + result.push(' '); + return; + } + } + + fn _print(&self, opt: &PrintOption, level: i32) -> String { + let mut result = String::new(); + + match self { + Json::OBJECT { name, value } => { + if opt.sep_space || opt.indent > 0 { + result.push_str(&format!("\"{}\": {}", name, value._print(opt, level))); + } else { + result.push_str(&format!("\"{}\":{}", name, value._print(opt, level))); + } + } + Json::JSON(values) => { + if values.len() == 0 { + return "{}".to_string(); + } + + result.push('{'); + if opt.indent > 0 { + result.push('\n'); + } + + if opt.sort_keys { + panic!("todo"); + } + + for n in 0..values.len() { + self._print_indent(&mut result, opt, level + 1); + result.push_str(&values[n]._print(opt, level + 1)); + if n < values.len() - 1 { + result.push(','); + self._print_sep_space(&mut result, opt, false); + } else { + self._print_sep_space(&mut result, opt, true); + } + } + + self._print_indent(&mut result, opt, level); + result.push('}'); + } + Json::ARRAY(values) => { + if values.len() == 0 { + return "[]".to_string(); + } + + result.push('['); + if opt.indent > 0 { + result.push('\n'); + } + + for n in 0..values.len() { + self._print_indent(&mut result, opt, level + 1); + result.push_str(&values[n]._print(opt, level + 1)); + if n < values.len() - 1 { + result.push(','); + self._print_sep_space(&mut result, opt, false); + } else { + self._print_sep_space(&mut result, opt, true); + } + } + + self._print_indent(&mut result, opt, level); + result.push(']'); + } + Json::STRING(val) => { + let s = Json::encode_string_escape(val); + result.push_str(&format!("\"{}\"", s)); + } + Json::NUMBER(val) => { + if opt.py_style_f64 { + result.push_str(&float_to_string(*val)); + } else { + result.push_str(&format!("{}", val)); + } + } + Json::INT(val) => { + result.push_str(&format!("{}", val)); + } + Json::FLOAT(val) => { + if ((*val as i64) as f64) == *val { + result.push_str(&format!("{}.0", val)); + } else { + result.push_str(&float_to_string(*val)); + } + } + Json::BOOL(val) => { + if *val { + result.push_str("true"); + } else { + result.push_str("false") + } + } + Json::NULL => { + result.push_str("null"); + } + } + + result + } + + fn encode_string_escape(s: &str) -> String { + let mut result = String::new(); + + for x in s.to_string().chars() { + if x == '\\' { + result.push_str("\\\\"); + continue; + } + if x == '\"' { + result.push_str("\\\""); + continue; + } + if x == 0x08 as char { + result.push_str("\\b"); + continue; + } + if x == 0x0c as char { + result.push_str("\\f"); + continue; + } + if x == '\n' { + result.push_str("\\n"); + continue; + } + if x == '\r' { + result.push_str("\\r"); + continue; + } + if x == '\t' { + result.push_str("\\t"); + continue; + } + if is_control(x as i32) { + // \uxxxx + let s = format!("\\u{:04x}", x as i32); + result.push_str(s.as_str()); + continue; + } + result.push(x); + } + return result; + } + + /// Parses the given bytes if a json structure is found. It even works with `\"Hello\":\"World\"` + /// (doesn't have to be like `{...}`), i.e. it can return any of the variants in the `Json` enum. + /// The error is returned in the form `(last position, what went wrong)`. Unfortunately the error + /// description are minimal (basically "Error parsing ...type..."). + /// ## Example + /// ``` + /// use json_minimal::*; + /// + /// match Json::parse(b"{\"Greeting\":\"Hello, world!\"}") { + /// Ok(json) => { + /// + /// match json.get("Greeting") { + /// Some(json) => { + /// match json { + /// Json::OBJECT { name, value } => { + /// match value.unbox() { + /// Json::STRING(val) => { + /// assert_eq!(val,"Hello, world!"); + /// }, + /// json => { + /// panic!("Expected Json::STRING but found {:?}!!!",json); + /// } + /// } + /// } + /// json => { + /// panic!("Expected Json::OBJECT but found {:?}!!!",json); + /// } + /// } + /// }, + /// None => { + /// panic!("Greeting was not found!!!"); + /// } + /// } + /// }, + /// Err( (pos,msg) ) => { + /// panic!("`{}` at position `{}`!!!",msg,pos); + /// } + /// } + /// ``` + /// See the tutorial on github for more. + pub fn parse(input: &[u8]) -> Result { + return Json::parse_with_option(input, &ParseOption { support_int: false }); + } + + pub fn parse_with_option( + input: &[u8], + opt: &ParseOption, + ) -> Result { + let mut incr: usize = 0; + + match input[incr] as char { + '{' => Self::parse_json(input, &mut incr, opt), + '\"' => Self::parse_string(input, &mut incr, opt), + '[' => Self::parse_array(input, &mut incr, opt), + 't' | 'f' => Self::parse_bool(input, &mut incr, opt), + 'n' => Self::parse_null(input, &mut incr, opt), + + // NaN, Infinity, -Infinity + '-' | '0'..='9' | 'N' | 'I' => Self::parse_number(input, &mut incr, opt), + _ => Err((incr, "Not a valid json format")), + } + } + + // This must exclusively be used by `parse_string` to make any sense. + fn parse_object( + input: &[u8], + incr: &mut usize, + name: String, + opt: &ParseOption, + ) -> Result { + // if input[*incr] as char != ':' { + // return Err((*incr, "Error parsing object.")); + // } + + *incr += 1; + + if *incr >= input.len() { + return Err((*incr, "Error parsing object.")); + } + + loop { + match input[*incr] as char { + '\r' | '\n' | '\t' | ' ' => { + *incr += 1; + + if *incr >= input.len() { + return Err((*incr, "Error parsing object.")); + } + } + _ => { + break; + } + } + } + + let value = match input[*incr] as char { + '{' => Self::parse_json(input, incr, opt)?, + '[' => Self::parse_array(input, incr, opt)?, + '\"' => Self::parse_string(input, incr, opt)?, + 't' | 'f' => Self::parse_bool(input, incr, opt)?, + 'n' => Self::parse_null(input, incr, opt)?, + + // NaN, Infinity, -Infinity + '-' | '0'..='9' | 'N' | 'I' => Self::parse_number(input, incr, opt)?, + _ => { + return Err((*incr, "Error parsing object.")); + } + }; + + Ok(Json::OBJECT { + name, + + value: Box::new(value), + }) + } + + // Parse if you thik it's something like `{...}` + fn parse_json( + input: &[u8], + incr: &mut usize, + opt: &ParseOption, + ) -> Result { + let mut result: Vec = Vec::new(); + + // if input[*incr] as char != '{' { + // return Err((*incr, "Error parsing json.")); + // } + + *incr += 1; + + if *incr >= input.len() { + return Err((*incr, "Error parsing json.")); + } + + loop { + let json = match input[*incr] as char { + ',' => { + *incr += 1; + continue; + } + '\"' => Self::parse_string(input, incr, opt)?, + '[' => Self::parse_array(input, incr, opt)?, + 't' | 'f' => Self::parse_bool(input, incr, opt)?, + 'n' => Self::parse_null(input, incr, opt)?, + + // NaN, Infinity, -Infinity + '-' | '0'..='9' | 'N' | 'I' => Self::parse_number(input, incr, opt)?, + '}' => { + *incr += 1; + + return Ok(Json::JSON(result)); + } + '{' => Self::parse_json(input, incr, opt)?, + // '\x0c' => '\f' + '\r' | '\n' | '\t' | '\x0c' | ' ' => { + *incr += 1; + + if *incr >= input.len() { + return Err((*incr, "Error parsing json.")); + } + + continue; + } + _ => { + return Err((*incr, "Error parsing json.")); + } + }; + + result.push(json); + } + } + + // Parse a &str if you're sure it resembles `[...` + fn parse_array( + input: &[u8], + incr: &mut usize, + opt: &ParseOption, + ) -> Result { + let mut result: Vec = Vec::new(); + + // if input[*incr] as char != '[' { + // return Err((*incr, "Error parsing array.")); + // } + + *incr += 1; + + if *incr >= input.len() { + return Err((*incr, "Error parsing array.")); + } + + loop { + let json = match input[*incr] as char { + ',' => { + *incr += 1; + continue; + } + '\"' => Self::parse_string(input, incr, opt)?, + '[' => Self::parse_array(input, incr, opt)?, + '{' => Self::parse_json(input, incr, opt)?, + 't' | 'f' => Self::parse_bool(input, incr, opt)?, + 'n' => Self::parse_null(input, incr, opt)?, + + // NaN, Infinity, -Infinity + '-' | '0'..='9' | 'N' | 'I' => Self::parse_number(input, incr, opt)?, + ']' => { + *incr += 1; + + return Ok(Json::ARRAY(result)); + } + // '\x0c' => '\f' + '\r' | '\n' | '\t' | '\x0c' | ' ' => { + *incr += 1; + + if *incr >= input.len() { + return Err((*incr, "Error parsing array.")); + } + + continue; + } + _ => { + return Err((*incr, "Error parsing array.")); + } + }; + + result.push(json); + } + } + + // Parse a &str if you know that it corresponds to/starts with a json String. + fn parse_string( + input: &[u8], + incr: &mut usize, + opt: &ParseOption, + ) -> Result { + let mut result: Vec = Vec::new(); + + // if input[*incr] as char != '\"' { + // return Err((*incr, "Error parsing string.")); + // } + + *incr += 1; + + if *incr >= input.len() { + return Err((*incr, "Error parsing string.")); + } + + loop { + match input[*incr] { + b'\"' => { + *incr += 1; + + let result = String::from_utf8(result) + .map_err(|_| (*incr, "Error parsing non-utf8 string."))?; + + if *incr < input.len() { + if input[*incr] as char == ':' { + return Self::parse_object(input, incr, result, opt); + } else { + return Ok(Json::STRING(result)); + } + } else { + return Ok(Json::STRING(result)); + } + } + b'\\' => { + Self::parse_string_escape_sequence(input, incr, &mut result, opt)?; + } + c => { + result.push(c); + + *incr += 1; + + if *incr >= input.len() { + return Err((*incr, "Error parsing string.")); + } + } + } + } + } + + // Parse an escape sequence inside a string + fn parse_string_escape_sequence( + input: &[u8], + incr: &mut usize, + result: &mut Vec, + _opt: &ParseOption, + ) -> Result<(), (usize, &'static str)> { + // if input[*incr] as char != '\\' { + // return Err((*incr, "Error parsing string escape sequence.")); + // } + + *incr += 1; + + if *incr >= input.len() { + return Err((*incr, "Error parsing string escape sequence.")); + } + match input[*incr] as char { + '\"' | '\\' | '/' => { + result.push(input[*incr]); + } + 'b' => { + result.push(b'\x08'); + } + // '\x0c' => '\f' + 'f' => { + result.push(b'\x0c'); + } + 'n' => { + result.push(b'\n'); + } + 'r' => { + result.push(b'\r'); + } + 't' => { + result.push(b'\t'); + } + 'u' => { + const BAD_UNICODE: &str = "Error parsing unicode string escape sequence."; + + if *incr + 4 >= input.len() { + return Err((*incr, BAD_UNICODE)); + } + + let hex = (&input[*incr + 1..*incr + 5]).to_vec(); + let hex = String::from_utf8(hex).map_err(|_| (*incr, BAD_UNICODE))?; + let mut value = u32::from_str_radix(&hex, 16).map_err(|_| (*incr, BAD_UNICODE))?; + + //high surrogate + if value >= 0xD800 && value <= 0xDBFF { + // /u xxxx /u xxx x + // 1234 56 789 10 + if *incr + 10 >= input.len() { + return Err((*incr, BAD_UNICODE)); + } + // /u xxxx /uxxxx + // 1234 56 + if &input[*incr + 5..*incr + 7] != "\\u".as_bytes() { + return Err((*incr, BAD_UNICODE)); + } + *incr += 6; + let low_hex = (&input[*incr + 1..*incr + 5]).to_vec(); + let low_hex = String::from_utf8(low_hex).map_err(|_| (*incr, BAD_UNICODE))?; + let low_value = + u32::from_str_radix(&low_hex, 16).map_err(|_| (*incr, BAD_UNICODE))?; + //low surrogate + if low_value >= 0xDC00 && low_value <= 0xDFFF { + value = ((value - 0xD800) << 10) + (low_value - 0xDC00) + 0x10000; + } else { + return Err((*incr, "Error parsing invalid string escape sequence.")); + } + } + let value = std::char::from_u32(value).ok_or((*incr, BAD_UNICODE))?; + + let mut buffer = [0; 4]; + result.extend(value.encode_utf8(&mut buffer).as_bytes()); + *incr += 4; + } + _ => { + return Err((*incr, "Error parsing invalid string escape sequence.")); + } + } + + *incr += 1; + + if *incr >= input.len() { + return Err((*incr, "Error parsing string escape sequence.")); + } + + Ok(()) + } + + fn parse_number( + input: &[u8], + incr: &mut usize, + opt: &ParseOption, + ) -> Result { + let mut result = String::new(); + + loop { + match input[*incr] as char { + // '\x0c' => '\f' + ',' | ']' | '}' | '\r' | '\n' | '\t' | '\x0c' | ' ' => { + break; + } + c => { + result.push(c); + + *incr += 1; + + if *incr >= input.len() { + match result.parse::() { + Ok(num) => { + if opt.support_int { + if result.contains(".") { + return Ok(Json::NUMBER(num)); + } else { + return Ok(Json::INT(num as i64)); + } + } else { + return Ok(Json::NUMBER(num)); + } + } + Err(_) => { + return Err((*incr, "Error parsing number.")); + } + } + } + } + } + } + + match result.parse::() { + Ok(num) => { + if opt.support_int { + if result.contains(".") { + return Ok(Json::NUMBER(num)); + } else if (num as i64) as f64 == num { + return Ok(Json::INT(num as i64)); + } else { + return Ok(Json::NUMBER(num)); + } + } else { + return Ok(Json::NUMBER(num)); + } + } + Err(_) => { + return Err((*incr, "Error parsing number.")); + } + } + } + + fn parse_bool( + input: &[u8], + incr: &mut usize, + _opt: &ParseOption, + ) -> Result { + let mut result = String::new(); + + loop { + match input[*incr] as char { + // '\x0c' => '\f' + ',' | ']' | '}' | '\r' | '\n' | '\t' | '\x0c' | ' ' => { + break; + } + c => { + result.push(c); + + *incr += 1; + + if *incr >= input.len() { + if result == "true" { + return Ok(Json::BOOL(true)); + } + + if result == "false" { + return Ok(Json::BOOL(false)); + } + + return Err((*incr, "Error parsing bool.")); + } + } + } + } + + if result == "true" { + return Ok(Json::BOOL(true)); + } + + if result == "false" { + return Ok(Json::BOOL(false)); + } + + return Err((*incr, "Error parsing bool.")); + } + + fn parse_null( + input: &[u8], + incr: &mut usize, + _opt: &ParseOption, + ) -> Result { + let mut result = String::new(); + + loop { + match input[*incr] as char { + // '\x0c' => '\f' + ',' | ']' | '}' | '\r' | '\n' | '\t' | '\x0c' | ' ' => { + break; + } + c => { + result.push(c); + + *incr += 1; + + if *incr >= input.len() { + if result == "null" { + return Ok(Json::NULL); + } else { + return Err((*incr, "Error parsing null.")); + } + } + } + } + } + + if result == "null" { + return Ok(Json::NULL); + } else { + return Err((*incr, "Error parsing null.")); + } + } +} + +pub fn float_to_string(value: f64) -> String { + if value.is_nan() { + return "NaN".to_string(); + } + if value.is_infinite() { + if value.is_sign_negative() { + return "-Infinity".to_string(); + } else { + return "Infinity".to_string(); + } + } + + let lit = format!("{:e}", value); + if let Some(position) = lit.find('e') { + let significand = &lit[..position]; + let exponent = &lit[position + 1..]; + let exponent = exponent.parse::().unwrap(); + if exponent < 16 && exponent > -5 { + if is_integer(value) { + format!("{:.1?}", value) + } else { + value.to_string() + } + } else { + format!("{}e{:+#03}", significand, exponent) + } + } else { + value.to_string() + } +} + +pub fn is_integer(v: f64) -> bool { + (v - v.round()).abs() < std::f64::EPSILON +} + +fn is_control(b: i32) -> bool { + const DEL: i32 = 127; + b < 32 || b == DEL +} + +#[cfg(test)] +mod tests; diff --git a/kclvm/runtime/src/3rdparty/json_minimal/src/tests.rs b/kclvm/runtime/src/3rdparty/json_minimal/src/tests.rs new file mode 100644 index 000000000..311f1513a --- /dev/null +++ b/kclvm/runtime/src/3rdparty/json_minimal/src/tests.rs @@ -0,0 +1,651 @@ +use super::*; + +#[test] +fn test_make_json() { + let mut json = Json::new(); + + let greeting = Json::OBJECT { + name: String::from("Greeting"), + + value: Box::new(Json::STRING(String::from("Hello, world!"))), + }; + + json.add(greeting); + + let mut days_in_the_week = Json::OBJECT { + name: String::from("Days in the week"), + + value: Box::new(Json::JSON(Vec::new())), + }; + + let mut days = Json::ARRAY(Vec::new()); + + days.add(Json::STRING(String::from("Monday"))) + .add(Json::STRING(String::from("Tuesday"))) + .add(Json::STRING(String::from("Wednesday"))) + .add(Json::STRING(String::from("Thursday"))) + .add(Json::STRING(String::from("Friday"))) + .add(Json::STRING(String::from("Saturday"))) + .add(Json::STRING(String::from("Sunday"))); + + days_in_the_week + .add(Json::OBJECT { + name: String::from("Total number of days"), + + value: Box::new(Json::NUMBER(7.0)), + }) + .add(Json::OBJECT { + name: String::from("They are called"), + + value: Box::new(days), + }); + + json.add(days_in_the_week); + + let mut conclusion = Json::OBJECT { + name: String::from("Conclusion"), + + value: Box::new(Json::JSON(Vec::new())), + }; + + conclusion + .add(Json::OBJECT { + name: String::from("Minimal in my opinion"), + + value: Box::new(Json::BOOL(true)), + }) + .add(Json::OBJECT { + name: String::from("How much I care about your opinion"), + + value: Box::new(Json::NULL), + }) + .add(Json::OBJECT { + name: String::from("Comment"), + + value: Box::new(Json::STRING(String::from(";)"))), + }); + + json.add(conclusion); + + assert_eq!( + "{\"Greeting\":\"Hello, world!\",\"Days in the week\":{\"Total number of days\":7,\"They are called\":[\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\",\"Sunday\"]},\"Conclusion\":{\"Minimal in my opinion\":true,\"How much I care about your opinion\":null,\"Comment\":\";)\"}}", + &json.print() + ) +} + +#[test] +fn test_get_mut() { + let mut json = Json::new(); + + json.add(Json::OBJECT { + name: String::from("Greeting"), + + value: Box::new(Json::STRING(String::from("Hello, world!"))), + }); + + match json.get_mut("Greeting") { + Some(json) => match json { + Json::OBJECT { name: _, value } => match value.unbox_mut() { + Json::STRING(val) => { + assert_eq!("Hello, world!", val); + + val.push_str(" How are you?"); + + assert_eq!("Hello, world! How are you?", val); + } + _ => { + panic!("Expected `Json::STRING`!!!"); + } + }, + _ => { + panic!("Expected `Json::OBJECT`!!!"); + } + }, + None => { + panic!("Not found!!!"); + } + } +} + +#[test] +fn test_parse_number() { + let mut incr: usize = 0; + + match Json::parse_number(b"36.36", &mut incr, &ParseOption::default()) { + Ok(json) => match json { + Json::NUMBER(val) => { + assert_eq!(val, 36.36); + } + json => { + panic!("Expected Json::NUMBER but found {:?}", json); + } + }, + Err(e) => { + parse_error(e); + } + } +} + +#[test] +fn test_parse_bool() { + let mut incr: usize = 0; + + match Json::parse_bool(b"true", &mut incr, &ParseOption::default()) { + Ok(json) => match json { + Json::BOOL(val) => { + assert_eq!(val, true); + } + json => { + panic!("Expected Json::BOOL but found {:?}", json); + } + }, + Err(e) => { + parse_error(e); + } + } + + incr = 0; + + match Json::parse_bool(b"false", &mut incr, &ParseOption::default()) { + Ok(json) => match json { + Json::BOOL(val) => { + assert_eq!(val, false); + } + json => { + panic!("Expected Json::BOOL but found {:?}", json); + } + }, + Err(e) => { + parse_error(e); + } + } +} + +#[test] +fn test_parse_null() { + let mut incr: usize = 0; + + match Json::parse_null(b"null", &mut incr, &ParseOption::default()) { + Ok(json) => match json { + Json::NULL => {} + json => { + panic!("Expected Json::NULL but found {:?}", json); + } + }, + Err(e) => { + parse_error(e); + } + } +} + +#[test] +fn test_parse_array() { + let mut incr: usize = 0; + + match Json::parse_array( + b"[1,\"two\",true,[\"array\",[\"another one\",[\"another one\",1.5]]]]", + &mut incr, + &ParseOption::default(), + ) { + Ok(json) => match json { + Json::ARRAY(vals) => { + assert_eq!(vals.len(), 4); + } + json => { + panic!("Expected Json::ARRAY but found {:?}", json); + } + }, + Err(e) => { + parse_error(e); + } + } +} + +#[test] +fn test_parse_json() { + let mut incr: usize = 0; + + match Json::parse_json(b"{\"on\",\"off\"}", &mut incr, &ParseOption::default()) { + Ok(json) => match json { + Json::JSON(vals) => { + assert_eq!(vals.len(), 2); + } + json => { + panic!("Expected Json::ARRAY but found {:?}", json); + } + }, + Err(e) => { + parse_error(e); + } + } +} + +#[test] +fn test_parse_json_2() { + let mut incr: usize = 0; + + match Json::parse_json( + b"{\"on\",\"off\",\"OBJECT\":{\"ARRAY\":[\"on\",\"off\"]},\"on or off?\"}", + &mut incr, + &ParseOption::default(), + ) { + Ok(json) => match json { + Json::JSON(vals) => { + assert_eq!(vals.len(), 4); + } + json => { + panic!("Expected Json::ARRAY but found {:?}", json); + } + }, + Err(e) => { + parse_error(e); + } + } +} + +#[test] +fn test_parse_object() { + let mut incr: usize = 0; + + match Json::parse_string(b"\"String\":\"Value\"", &mut incr, &ParseOption::default()) { + Ok(json) => match json { + Json::OBJECT { name, value } => { + assert_eq!(name, "String"); + + match value.unbox() { + Json::STRING(val) => { + assert_eq!(val, "Value"); + } + json => { + panic!("Expected Json::STRING but found {:?}", json); + } + } + } + json => { + panic!("Expected Json::OBJECT but found {:?}", json); + } + }, + Err(e) => { + parse_error(e); + } + } +} + +#[test] +fn test_parse() { + match Json::parse(b"{\"Greeting\":\"Hello, world!\",\"Days in the week\":{\"Total number of days\":7,\"They are called\":[\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\",\"Sunday\"]},\"Minimal in my opinion\":true,\"How much I care about your opinion\":null}") { + Ok(json) => { + match json { + Json::JSON(values) => { + assert_eq!(values.len(),4); + + match &values[0] { + Json::OBJECT { name, value } => { + assert_eq!("Greeting",name); + + match value.unbox() { + Json::STRING(val) => { + assert_eq!("Hello, world!",val); + }, + json => { + panic!("Expected Json::STRING but found {:?}",json); + } + } + }, + json => { + panic!("Expected Json::OBJECT but found {:?}",json); + } + } + + match &values[1] { + Json::OBJECT { name, value } => { + assert_eq!("Days in the week",name); + + match value.unbox() { + Json::JSON(values) => { + assert_eq!(values.len(),2); + + match &values[0] { + Json::OBJECT { name, value } => { + assert_eq!("Total number of days",name); + + match value.unbox() { + Json::NUMBER(num) => { + assert_eq!(*num,7.0); + }, + json => { + panic!("Expected Json::NUMBER but found {:?}",json); + } + } + }, + json => { + panic!("Expected Json::OBJECT but found {:?}",json); + } + } + + match &values[1] { + Json::OBJECT { name, value } => { + assert_eq!("They are called",name); + + match value.unbox() { + Json::ARRAY(vals) => { + assert_eq!(vals.len(),7); + + for n in 0..7 { + match &vals[n] { + Json::STRING(val) => { + match val.as_bytes() { + b"Monday" => { + + }, + b"Tuesday" => { + + }, + b"Wednesday" => { + + }, + b"Thursday" => { + + }, + b"Friday" => { + + }, + b"Saturday" => { + + }, + b"Sunday" => { + + }, + d => { + panic!("\"{:?}\" is not a day of the week!!",d); + } + } + }, + json => { + panic!("Expected Json::STRING but found {:?}",json); + } + } + } + }, + json => { + panic!("Expected Json::ARRAY but found {:?}",json); + } + } + }, + json => { + panic!("Expected Json::OBJECT but found {:?}",json); + } + } + }, + json => { + panic!("Expected Json::JSON but found {:?}",json); + } + } + }, + json => { + panic!("Expected Json::OBJECT but found {:?}",json); + } + } + }, + json => { + panic!("Expected Json::JSON but found {:?}",json); + } + } + }, + Err(e) => { + parse_error(e); + } + } +} + +#[test] +fn test_parse_2() { + #[allow(unused_assignments)] + + let json = match Json::parse(b"{\"Greeting\":\"Hello, world!\",\"Days of the week\":{\"Total number of days\":7,\"They are called\":[\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\",\"Sunday\"]},\"Conclusion\":{\"Minimal in my opinion\":true,\"How much I care about your opinion\":null,\"Comment\":\";)\"}}") { + Ok(json) => { + json + }, + Err( (position,message) ) => { + panic!("`{}` at position `{}`!!!",message,position); + } + }; + + match json.get("Greeting") { + Some(json) => match json { + Json::OBJECT { name: _, value } => match value.unbox() { + Json::STRING(val) => { + assert_eq!("Hello, world!", val); + } + json => { + panic!("Expected Json::STRING but found {:?}", json); + } + }, + json => panic!("Expected Json::JSON but found {:?}!!!", json), + }, + None => { + panic!("Couln't find Greeting. How rude!"); + } + } + + match json.get("Days of the week") { + // Hint: You can also use `get_mut` to aid in editing/creating jsons... + Some(json) => match json { + Json::OBJECT { name: _, value } => match value.unbox() { + Json::JSON(values) => { + assert_eq!(values.len(), 2); + + match &values[0] { + Json::OBJECT { name, value: _ } => { + assert_eq!("Total number of days", name); + } + json => { + panic!("Expected Json::OBJECT but found {:?}!!!", json); + } + } + + match &values[1] { + Json::OBJECT { name, value: _ } => { + assert_eq!("They are called", name); + } + json => { + panic!("Expected Json::OBJECT but found {:?}!!!", json); + } + } + } + json => { + panic!("Expected Json::JSON but found {:?}!!!", json); + } + }, + json => { + panic!("Expected Json::OBJECT but found {:?}!!!", json); + } + }, + None => { + panic!("Days of the week not found!"); + } + } +} + +#[test] +fn parse_strange() { + let json = match Json::parse(b"[0,{\"hello\":\"world\",\"what's\":\"up?\"}]") { + Ok(json) => json, + Err((pos, msg)) => { + panic!("`{}` at position {}", msg, pos); + } + }; + + match json { + Json::ARRAY(vals) => { + assert_eq!(vals.len(), 2); + + match &vals[0] { + Json::NUMBER(n) => { + assert_eq!(*n, 0.0); + } + json => { + panic!("Expected Json::NUMBER but found {:?}!!!", json); + } + } + + match &vals[1] { + Json::JSON(vals) => { + assert_eq!(2, vals.len()); + + match &vals[0] { + Json::OBJECT { name, value: _ } => { + assert_eq!("hello", name); + } + json => { + panic!("Expected Json::ARRAY but found {:?}!!!", json); + } + } + + match &vals[1] { + Json::OBJECT { name, value: _ } => { + assert_eq!("what's", name); + } + json => { + panic!("Expected Json::ARRAY but found {:?}!!!", json); + } + } + } + json => { + panic!("Expected Json::JSON but found {:?}!!!", json); + } + } + } + json => { + panic!("Expected Json::ARRAY but found {:?}!!!", json); + } + } +} + +#[test] +fn parse_escape_sequence() { + let json = match Json::parse(br#""a \" \/ \b \f \n \r \t \u2764 z""#) { + Ok(json) => json, + Err((pos, msg)) => { + panic!("`{}` at position {}", msg, pos); + } + }; + + match json { + Json::STRING(string) => { + assert_eq!(string, "a \" / \u{8} \u{c} \n \r \t ❤ z"); + } + json => { + panic!("Expected Json::STRING but found {:?}!!!", json); + } + } +} + +#[test] +fn parse_escape_sequence_in_array() { + let json = match Json::parse(br#"["\"foo"]"#) { + Ok(json) => json, + Err((pos, msg)) => { + panic!("`{}` at position {}", msg, pos); + } + }; + + match json { + Json::ARRAY(vals) => { + assert_eq!(vals.len(), 1); + + match &vals[0] { + Json::STRING(n) => { + assert_eq!(*n, "\"foo"); + } + json => { + panic!("Expected Json::STRING but found {:?}!!!", json); + } + } + } + json => { + panic!("Expected Json::ARRAY but found {:?}!!!", json); + } + } +} + +#[test] +fn parse_non_ascii() { + let json = match Json::parse(r#""a ❤ z""#.as_bytes()) { + Ok(json) => json, + Err((pos, msg)) => { + panic!("`{}` at position {}", msg, pos); + } + }; + + match json { + Json::STRING(string) => { + assert_eq!(string, "a ❤ z"); + } + json => { + panic!("Expected Json::STRING but found {:?}!!!", json); + } + } +} + +#[test] +fn parse_pretty() { + let json = match Json::parse(b"{\r\n\t\"Array\": [\r\n\t\t\"First\" ,\r\n\r\n\t\t2 ,\r\n\r\n\t\t[\"Three\"] ,\r\n\r\n\t\t3.6\r\n\t],\r\n\t{\r\n\r\n\t\t\"Sub-Object\": \"Hello, world!\"\r\n\t}\r\n}") { + Ok(json) => json, + Err((pos, msg)) => { + panic!("`{}` at position {}", msg, pos); + } + }; + + match json { + Json::JSON(values) => { + match values[0].unbox() { + Json::OBJECT { name, value } => { + assert_eq!(name, "Array"); + + match value.unbox() { + Json::ARRAY(values) => { + assert_eq!(values.len(), 4); + } + json => { + panic!("Expected Json::ARRAY but found {:?}!!!", json); + } + } + } + json => { + panic!("Expected Json::OBJECT but found {:?}!!!", json); + } + } + + match values[1].unbox() { + Json::JSON(values) => match values[0].unbox() { + Json::OBJECT { name, value } => { + assert_eq!(name, "Sub-Object"); + + match value.unbox() { + Json::STRING(value) => { + assert_eq!(value, "Hello, world!"); + } + json => { + panic!("Expected Json::STRING but found {:?}!!!", json); + } + } + } + json => { + panic!("Expected Json::OBJECT but found {:?}!!!", json); + } + }, + json => { + panic!("Expected Json::Json but found {:?}!!!", json); + } + } + } + json => { + panic!("Expected Json::JSON but found {:?}!!!", json); + } + } +} + +fn parse_error((pos, msg): (usize, &str)) { + panic!("`{}` at position `{}`!!!", msg, pos); +} diff --git a/kclvm/runtime/src/_kcl_run.rs b/kclvm/runtime/src/_kcl_run.rs new file mode 100644 index 000000000..e1654cb8f --- /dev/null +++ b/kclvm/runtime/src/_kcl_run.rs @@ -0,0 +1,175 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[allow(dead_code, non_camel_case_types)] +pub type kclvm_buffer_t = Buffer; + +#[allow(dead_code, non_camel_case_types)] +pub type kclvm_context_t = Context; + +#[allow(dead_code, non_camel_case_types)] +pub type kclvm_kind_t = Kind; + +#[allow(dead_code, non_camel_case_types)] +pub type kclvm_type_t = Type; + +#[allow(dead_code, non_camel_case_types)] +pub type kclvm_value_ref_t = ValueRef; + +#[allow(dead_code, non_camel_case_types)] +pub type kclvm_iterator_t = ValueIterator; + +#[allow(dead_code, non_camel_case_types)] +pub type kclvm_char_t = i8; + +#[allow(dead_code, non_camel_case_types)] +pub type kclvm_size_t = i32; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_bool_t = i8; + +#[allow(dead_code, non_camel_case_types)] +pub type kclvm_int_t = i64; + +#[allow(dead_code, non_camel_case_types)] +pub type kclvm_float_t = f64; + +// const SHOULD_PROFILE: bool = false; + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn _kcl_run( + kclvm_main_ptr: u64, // main.k => kclvm_main + option_len: kclvm_size_t, + option_keys: *const *const kclvm_char_t, + option_values: *const *const kclvm_char_t, + strict_range_check: i32, + disable_none: i32, + disable_schema_check: i32, + list_option_mode: i32, + debug_mode: i32, + result_buffer_len: kclvm_size_t, + result_buffer: *mut kclvm_char_t, + warn_buffer_len: kclvm_size_t, + warn_buffer: *mut kclvm_char_t, +) -> kclvm_size_t { + let ctx = kclvm_context_new(); + + std::panic::set_hook(Box::new(|info: &std::panic::PanicInfo| { + let ctx = Context::current_context_mut(); + ctx.set_panic_info(info); + let _ = ctx; + })); + + let result = std::panic::catch_unwind(|| { + _kcl_run_in_closure( + kclvm_main_ptr, + option_len, + option_keys, + option_values, + strict_range_check, + disable_none, + disable_schema_check, + list_option_mode, + debug_mode, + result_buffer_len, + result_buffer, + ) + }); + match result { + Ok(n) => { + let json_panic_info = Context::current_context().get_panic_info_json_string(); + + let c_str_ptr = json_panic_info.as_ptr() as *const i8; + let c_str_len = json_panic_info.len() as i32; + + unsafe { + if c_str_len <= warn_buffer_len { + std::ptr::copy(c_str_ptr, warn_buffer, c_str_len as usize); + } + } + + kclvm_context_delete(ctx); + n + } + Err(_) => { + let json_panic_info = Context::current_context().get_panic_info_json_string(); + + let c_str_ptr = json_panic_info.as_ptr() as *const i8; + let c_str_len = json_panic_info.len() as i32; + + let mut return_len = c_str_len; + + unsafe { + if return_len <= result_buffer_len { + std::ptr::copy(c_str_ptr, result_buffer, return_len as usize); + } else { + *result_buffer = '\0' as kclvm_char_t; + return_len = 0 - return_len; + } + } + + kclvm_context_delete(ctx); + return_len + } + } +} + +#[allow(clippy::too_many_arguments)] +fn _kcl_run_in_closure( + kclvm_main_ptr: u64, // main.k => kclvm_main + option_len: kclvm_size_t, + option_keys: *const *const kclvm_char_t, + option_values: *const *const kclvm_char_t, + strict_range_check: i32, + disable_none: i32, + disable_schema_check: i32, + list_option_mode: i32, + debug_mode: i32, + result_buffer_len: kclvm_size_t, + result_buffer: *mut kclvm_char_t, +) -> kclvm_size_t { + let ctx = kclvm_context_current(); + + let kclvm_main = (&kclvm_main_ptr as *const u64) as *const () + as *const extern "C" fn(ctx: *mut kclvm_context_t) -> *mut kclvm_value_ref_t; + + kclvm_context_set_strict_range_check(ctx, strict_range_check as kclvm_bool_t); + kclvm_context_set_disable_none(ctx, disable_none as kclvm_bool_t); + kclvm_context_set_disable_schema_check(ctx, disable_schema_check as kclvm_bool_t); + kclvm_context_set_list_option_mode(ctx, list_option_mode as kclvm_bool_t); + kclvm_context_set_debug_mode(ctx, debug_mode as kclvm_bool_t); + + unsafe { + let option_keys = std::slice::from_raw_parts(option_keys, option_len as usize); + let option_values = std::slice::from_raw_parts(option_values, option_len as usize); + + for i in 0..(option_len as usize) { + kclvm_builtin_option_init(ctx, option_keys[i], option_values[i]); + } + + let value = if kclvm_main.is_null() { + kclvm_value_Str(b"{}\0" as *const u8 as *const kclvm_char_t) + } else { + kclvm_context_main_begin_hook(ctx); + let x = (*kclvm_main)(ctx); + kclvm_context_main_end_hook(ctx, x) + }; + + let c_str_ptr = kclvm_value_Str_ptr(value); + let c_str_len = kclvm_value_len(value); + + let mut return_len = c_str_len; + + if return_len <= result_buffer_len { + std::ptr::copy(c_str_ptr, result_buffer, return_len as usize); + } else { + *result_buffer = '\0' as kclvm_char_t; + return_len = 0 - return_len; + } + + kclvm_value_delete(value); + return_len + } +} diff --git a/kclvm/runtime/src/_kclvm.bc b/kclvm/runtime/src/_kclvm.bc new file mode 100644 index 000000000..99aaa00ab Binary files /dev/null and b/kclvm/runtime/src/_kclvm.bc differ diff --git a/kclvm/runtime/src/_kclvm.h b/kclvm/runtime/src/_kclvm.h new file mode 100644 index 000000000..17611eef6 --- /dev/null +++ b/kclvm/runtime/src/_kclvm.h @@ -0,0 +1,793 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +// Auto generated, DONOT EDIT!!! + +#pragma once + +#ifndef _kclvm_h_ +#define _kclvm_h_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// please keep same as 'kclvm/runtime/src/kind/mod.rs#Kind' + +enum kclvm_kind_t { + Invalid = 0, + + // only for value + + Undefined = 1, + None = 2, + + // for value & type + + Bool = 3, + Int = 4, + Float = 5, + Str = 6, + List = 7, + Dict = 8, + + Schema = 9, + Error = 10, + + // only for type + + Any = 11, + Union = 12, + + BoolLit = 13, + IntLit = 14, + FloatLit = 15, + StrLit = 16, + + Func = 17, + + // max num + + Max = 18, +}; + +typedef int8_t kclvm_bool_t; + +typedef struct kclvm_buffer_t kclvm_buffer_t; + +typedef char kclvm_char_t; + +typedef struct kclvm_context_t kclvm_context_t; + +typedef struct kclvm_decorator_value_t kclvm_decorator_value_t; + +typedef double kclvm_float_t; + +typedef int64_t kclvm_int_t; + +typedef struct kclvm_iterator_t kclvm_iterator_t; + +typedef enum kclvm_kind_t kclvm_kind_t; + +typedef int32_t kclvm_size_t; + +typedef struct kclvm_type_t kclvm_type_t; + +typedef struct kclvm_value_ref_t kclvm_value_ref_t; + +typedef struct kclvm_value_t kclvm_value_t; + +void kclvm_assert(kclvm_value_ref_t* value, kclvm_value_ref_t* msg); + +kclvm_value_ref_t* kclvm_base64_decode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_base64_encode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_char_t* kclvm_buffer_data(kclvm_buffer_t* p); + +void kclvm_buffer_delete(kclvm_buffer_t* p); + +kclvm_buffer_t* kclvm_buffer_new(kclvm_size_t size); + +kclvm_size_t kclvm_buffer_size(kclvm_buffer_t* p); + +kclvm_value_ref_t* kclvm_builtin_abs(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_all_true(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_any_true(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_bin(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_bool(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_dict(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_float(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_hex(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_int(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_isunique(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_len(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_list(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_max(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_min(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_multiplyof(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_oct(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_option(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +void kclvm_builtin_option_init(kclvm_context_t* ctx, int8_t* key, int8_t* value); + +kclvm_value_ref_t* kclvm_builtin_option_reset(kclvm_context_t* ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_ord(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_pow(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_print(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_range(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_round(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_sorted(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_str(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_capitalize(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_count(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_endswith(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_find(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_format(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_index(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_isalnum(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_isalpha(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_isdigit(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_islower(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_isspace(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_istitle(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_isupper(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_join(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_lower(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_lstrip(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_replace(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_rfind(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_rindex(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_rsplit(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_rstrip(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_split(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_splitlines(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_startswith(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_strip(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_title(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_str_upper(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_builtin_sum(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_typeof(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_builtin_zip(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +void kclvm_config_attr_map(kclvm_value_ref_t* value, kclvm_char_t* name, kclvm_char_t* type_str); + +void kclvm_context_args_clear(kclvm_context_t* p); + +kclvm_char_t* kclvm_context_args_get(kclvm_context_t* _p, kclvm_char_t* _key); + +void kclvm_context_args_set(kclvm_context_t* _p, kclvm_char_t* _key, kclvm_char_t* _value); + +void kclvm_context_clear_all_types(kclvm_context_t* p); + +kclvm_context_t* kclvm_context_current(); + +void kclvm_context_delete(kclvm_context_t* p); + +char* kclvm_context_invoke(kclvm_context_t* p, char* method, char* args, char* kwargs); + +void kclvm_context_main_begin_hook(kclvm_context_t* p); + +kclvm_value_ref_t* kclvm_context_main_end_hook(kclvm_context_t* p, kclvm_value_ref_t* return_value); + +kclvm_context_t* kclvm_context_new(); + +kclvm_bool_t kclvm_context_pkgpath_is_imported(kclvm_char_t* pkgpath); + +void kclvm_context_put_type(kclvm_context_t* p, kclvm_type_t* typ); + +void kclvm_context_set_debug_mode(kclvm_context_t* p, kclvm_bool_t v); + +void kclvm_context_set_disable_none(kclvm_context_t* p, kclvm_bool_t v); + +void kclvm_context_set_disable_schema_check(kclvm_context_t* p, kclvm_bool_t v); + +void kclvm_context_set_import_names(kclvm_context_t* p, kclvm_value_ref_t* import_names); + +void kclvm_context_set_kcl_filename(int8_t* filename); + +void kclvm_context_set_kcl_line_col(int32_t line, int32_t col); + +void kclvm_context_set_kcl_location(kclvm_context_t* p, int8_t* filename, int32_t line, int32_t col); + +void kclvm_context_set_kcl_pkgpath(kclvm_context_t* p, int8_t* pkgpath); + +void kclvm_context_set_list_option_mode(kclvm_context_t* p, kclvm_bool_t v); + +void kclvm_context_set_strict_range_check(kclvm_context_t* p, kclvm_bool_t v); + +void kclvm_context_symbol_init(kclvm_context_t* p, kclvm_size_t n, kclvm_char_t** symbol_names); + +kclvm_char_t* kclvm_context_symbol_name(kclvm_context_t* p, kclvm_size_t i); + +kclvm_size_t kclvm_context_symbol_num(kclvm_context_t* p); + +kclvm_value_t* kclvm_context_symbol_value(kclvm_context_t* p, kclvm_size_t i); + +kclvm_value_ref_t* kclvm_convert_collection_value(kclvm_value_ref_t* value, kclvm_char_t* tpe); + +kclvm_value_ref_t* kclvm_crypto_md5(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_crypto_sha1(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_crypto_sha224(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_crypto_sha256(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_crypto_sha384(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_crypto_sha512(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_datetime_date(kclvm_context_t* _ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_datetime_now(kclvm_context_t* _ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_datetime_ticks(kclvm_context_t* _ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_datetime_today(kclvm_context_t* _ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); + +void kclvm_debug_hello(); + +void kclvm_debug_invoke_func(void* fn_ptr); + +void kclvm_debug_print(int8_t* cs); + +void kclvm_debug_print_str_list(int32_t len, int8_t** ss); + +void kclvm_debug_print_type(kclvm_type_t* p); + +void kclvm_debug_print_value(kclvm_value_ref_t* p); + +void kclvm_debug_print_value_json_string(kclvm_value_ref_t* p); + +void kclvm_default_collection_insert_int_pointer(kclvm_value_ref_t* p, kclvm_char_t* key, uint64_t* ptr); + +void kclvm_default_collection_insert_value(kclvm_value_ref_t* p, kclvm_char_t* key, kclvm_value_ref_t* value); + +void kclvm_dict_clear(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_dict_get(kclvm_value_ref_t* p, kclvm_value_ref_t* key); + +kclvm_value_ref_t* kclvm_dict_get_entry(kclvm_value_ref_t* p, kclvm_char_t* key); + +kclvm_value_ref_t* kclvm_dict_get_value(kclvm_value_ref_t* p, kclvm_char_t* key); + +kclvm_value_ref_t* kclvm_dict_get_value_by_path(kclvm_value_ref_t* p, kclvm_char_t* path); + +kclvm_bool_t kclvm_dict_has_value(kclvm_value_ref_t* p, kclvm_char_t* key); + +void kclvm_dict_insert(kclvm_value_ref_t* p, kclvm_char_t* key, kclvm_value_ref_t* v, kclvm_size_t op, kclvm_size_t insert_index); + +void kclvm_dict_insert_unpack(kclvm_value_ref_t* p, kclvm_value_ref_t* v); + +void kclvm_dict_insert_value(kclvm_value_ref_t* p, kclvm_value_ref_t* key, kclvm_value_ref_t* v, kclvm_size_t op, kclvm_size_t insert_index); + +kclvm_value_ref_t* kclvm_dict_keys(kclvm_value_ref_t* p); + +kclvm_size_t kclvm_dict_len(kclvm_value_ref_t* p); + +void kclvm_dict_merge(kclvm_value_ref_t* p, kclvm_char_t* key, kclvm_value_ref_t* v, kclvm_size_t op, kclvm_size_t insert_index); + +void kclvm_dict_remove(kclvm_value_ref_t* p, kclvm_char_t* key); + +void kclvm_dict_safe_insert(kclvm_value_ref_t* p, kclvm_char_t* key, kclvm_value_ref_t* v, kclvm_size_t op, kclvm_size_t insert_index); + +void kclvm_dict_set_value(kclvm_value_ref_t* p, kclvm_char_t* key, kclvm_value_ref_t* val); + +void kclvm_dict_update(kclvm_value_ref_t* p, kclvm_value_ref_t* v); + +void kclvm_dict_update_key_value(kclvm_value_ref_t* p, kclvm_value_ref_t* key, kclvm_value_ref_t* v); + +kclvm_value_ref_t* kclvm_dict_values(kclvm_value_ref_t* p); + +void kclvm_free(uint8_t* ptr); + +kclvm_value_ref_t* kclvm_iterator_cur_key(kclvm_iterator_t* p); + +kclvm_value_ref_t* kclvm_iterator_cur_value(kclvm_iterator_t* p); + +void kclvm_iterator_delete(kclvm_iterator_t* p); + +kclvm_bool_t kclvm_iterator_is_end(kclvm_iterator_t* p); + +kclvm_value_ref_t* kclvm_iterator_next_value(kclvm_iterator_t* p, kclvm_value_ref_t* host); + +kclvm_value_ref_t* kclvm_json_decode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_json_dump_to_file(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_json_encode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +void kclvm_list_append(kclvm_value_ref_t* p, kclvm_value_ref_t* v); + +void kclvm_list_append_bool(kclvm_value_ref_t* p, kclvm_bool_t v); + +void kclvm_list_append_float(kclvm_value_ref_t* p, kclvm_float_t v); + +void kclvm_list_append_int(kclvm_value_ref_t* p, kclvm_int_t v); + +void kclvm_list_append_str(kclvm_value_ref_t* p, kclvm_char_t* v); + +void kclvm_list_append_unpack(kclvm_value_ref_t* p, kclvm_value_ref_t* v); + +void kclvm_list_clear(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_list_count(kclvm_value_ref_t* p, kclvm_value_ref_t* item); + +kclvm_value_ref_t* kclvm_list_find(kclvm_value_ref_t* p, kclvm_value_ref_t* item); + +kclvm_value_ref_t* kclvm_list_get(kclvm_value_ref_t* p, kclvm_size_t i); + +kclvm_value_ref_t* kclvm_list_get_option(kclvm_value_ref_t* p, kclvm_size_t i); + +void kclvm_list_insert(kclvm_value_ref_t* p, kclvm_value_ref_t* index, kclvm_value_ref_t* value); + +kclvm_size_t kclvm_list_len(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_list_pop(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_list_pop_first(kclvm_value_ref_t* p); + +void kclvm_list_remove_at(kclvm_value_ref_t* p, kclvm_size_t i); + +void kclvm_list_resize(kclvm_value_ref_t* p, kclvm_size_t newsize); + +void kclvm_list_set(kclvm_value_ref_t* p, kclvm_size_t i, kclvm_value_ref_t* v); + +uint8_t* kclvm_malloc(int32_t n); + +kclvm_value_ref_t* kclvm_math_ceil(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_exp(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_expm1(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_factorial(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_floor(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_gcd(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_isfinite(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_isinf(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_isnan(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_log(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_log10(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_log1p(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_log2(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_modf(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_pow(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_math_sqrt(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_IP_string(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_fqdn(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_is_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_is_IPv4(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_is_global_unicast_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_is_interface_local_multicast_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_is_link_local_multicast_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_is_link_local_unicast_IP(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_net_is_loopback_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_is_multicast_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_is_unspecified_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_join_host_port(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_parse_IP(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_net_split_host_port(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_to_IP16(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_net_to_IP4(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +void kclvm_plugin_init(void* fn_ptr); + +kclvm_value_ref_t* kclvm_plugin_invoke(int8_t* method, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +char* kclvm_plugin_invoke_json(int8_t* method, char* args, char* kwargs); + +kclvm_value_ref_t* kclvm_regex_compile(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_regex_findall(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_regex_match(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_regex_replace(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_regex_search(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_regex_split(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +void kclvm_schema_assert(kclvm_value_ref_t* value, kclvm_value_ref_t* msg, kclvm_value_ref_t* config_meta); + +void kclvm_schema_backtrack_cache(kclvm_value_ref_t* schema, kclvm_value_ref_t* cache, kclvm_value_ref_t* cal_map, kclvm_char_t* name, kclvm_value_ref_t* runtime_type); + +void kclvm_schema_default_settings(kclvm_value_ref_t* schema_value, kclvm_value_ref_t* config_value, kclvm_char_t* runtime_type); + +void kclvm_schema_do_check_with_index_sign_attr(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs, uint64_t* check_fn_ptr, kclvm_char_t* attr_name); + +kclvm_value_ref_t* kclvm_schema_get_value(kclvm_value_ref_t* p, kclvm_char_t* key, kclvm_value_ref_t* config, kclvm_value_ref_t* config_meta, kclvm_value_ref_t* cal_map, kclvm_char_t* target_attr, kclvm_value_ref_t* backtrack_level_map, kclvm_value_ref_t* backtrack_cache, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_schema_instances(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_schema_optional_check(kclvm_value_ref_t* p, kclvm_value_ref_t* v, kclvm_char_t* schema_name, kclvm_value_ref_t* config_meta); + +void kclvm_schema_value_check(kclvm_value_ref_t* schema_value, kclvm_value_ref_t* schema_config, kclvm_value_ref_t* _config_meta, kclvm_char_t* schema_name, kclvm_value_ref_t* index_sign_value, kclvm_char_t* _key_name, kclvm_char_t* key_type, kclvm_char_t* _value_type, kclvm_bool_t _any_other, kclvm_bool_t is_relaxed); + +kclvm_value_ref_t* kclvm_schema_value_new(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs, kclvm_value_ref_t* schema_value_or_func, kclvm_value_ref_t* config, kclvm_value_ref_t* config_meta, kclvm_char_t* pkgpath); + +kclvm_size_t kclvm_strlen(uint8_t* ptr); + +void kclvm_testing_arguments(kclvm_context_t* _ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); + +void kclvm_testing_setting_file(kclvm_context_t* _ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); + +kclvm_type_t* kclvm_type_Any(); + +kclvm_type_t* kclvm_type_Bool(); + +kclvm_type_t* kclvm_type_BoolLit(kclvm_bool_t v); + +kclvm_bool_t kclvm_type_BoolLit_value(kclvm_type_t* p); + +kclvm_type_t* kclvm_type_Dict(kclvm_type_t* key_type, kclvm_type_t* elem_type); + +kclvm_type_t* kclvm_type_Float(); + +kclvm_type_t* kclvm_type_FloatLit(double v); + +double kclvm_type_FloatLit_value(kclvm_type_t* p); + +kclvm_type_t* kclvm_type_Func(kclvm_size_t args_len, kclvm_type_t** args_types, kclvm_type_t* return_type); + +kclvm_type_t* kclvm_type_Int(); + +kclvm_type_t* kclvm_type_IntLit(int64_t v); + +int64_t kclvm_type_IntLit_value(kclvm_type_t* p); + +kclvm_type_t* kclvm_type_List(kclvm_type_t* elem_type); + +kclvm_type_t* kclvm_type_Schema(kclvm_char_t* name, kclvm_char_t* parent_name, kclvm_bool_t _is_relaxed, kclvm_size_t field_num, kclvm_char_t** field_names, kclvm_type_t** field_types); + +kclvm_type_t* kclvm_type_Str(); + +kclvm_type_t* kclvm_type_StrLit(kclvm_char_t* s); + +kclvm_char_t* kclvm_type_StrLit_value(kclvm_type_t* p); + +kclvm_type_t* kclvm_type_Union(kclvm_size_t n, kclvm_type_t** elem_types); + +kclvm_size_t kclvm_type_arg_num(kclvm_type_t* p); + +kclvm_type_t* kclvm_type_arg_type(kclvm_type_t* p, kclvm_size_t i); + +void kclvm_type_delete(kclvm_type_t* p); + +kclvm_type_t* kclvm_type_elem_type(kclvm_type_t* p); + +kclvm_type_t* kclvm_type_key_type(kclvm_type_t* p); + +kclvm_kind_t kclvm_type_kind(kclvm_type_t* p); + +kclvm_type_t* kclvm_type_return_type(kclvm_type_t* p); + +kclvm_char_t* kclvm_type_schema_field_name(kclvm_type_t* p, kclvm_size_t i); + +kclvm_size_t kclvm_type_schema_field_num(kclvm_type_t* p); + +kclvm_type_t* kclvm_type_schema_field_type(kclvm_type_t* p, kclvm_size_t i); + +kclvm_char_t* kclvm_type_schema_name(kclvm_type_t* p); + +kclvm_char_t* kclvm_type_schema_parent_name(kclvm_type_t* p); + +kclvm_bool_t kclvm_type_schema_relaxed(kclvm_type_t* p); + +kclvm_kind_t kclvm_type_str(kclvm_type_t* p); + +kclvm_value_ref_t* kclvm_units_to_G(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_units_to_Gi(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_units_to_K(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_units_to_Ki(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_units_to_M(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_units_to_Mi(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_units_to_P(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_units_to_Pi(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_units_to_T(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_units_to_Ti(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_units_to_m(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_units_to_n(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_units_to_u(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_value_Bool(kclvm_bool_t v); + +kclvm_bool_t* kclvm_value_Bool_ptr(kclvm_value_ref_t* p); + +kclvm_decorator_value_t* kclvm_value_Decorator(kclvm_char_t* name, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs, kclvm_value_ref_t* config_meta, kclvm_char_t* attr_name, kclvm_value_ref_t* config_value, kclvm_value_ref_t* is_schema_target); + +kclvm_value_ref_t* kclvm_value_Dict(); + +kclvm_value_ref_t* kclvm_value_False(); + +kclvm_value_ref_t* kclvm_value_Float(kclvm_float_t v); + +kclvm_float_t* kclvm_value_Float_ptr(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_Function(uint64_t* fn_ptr, kclvm_value_ref_t* closure, kclvm_char_t* external_name); + +kclvm_value_ref_t* kclvm_value_Function_using_ptr(uint64_t* fn_ptr); + +kclvm_value_ref_t* kclvm_value_Int(kclvm_int_t v); + +kclvm_int_t* kclvm_value_Int_ptr(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_List(); + +kclvm_value_ref_t* kclvm_value_List10(kclvm_value_ref_t* v1, kclvm_value_ref_t* v2, kclvm_value_ref_t* v3, kclvm_value_ref_t* v4, kclvm_value_ref_t* v5, kclvm_value_ref_t* v6, kclvm_value_ref_t* v7, kclvm_value_ref_t* v8, kclvm_value_ref_t* v9, kclvm_value_ref_t* v10); + +kclvm_value_ref_t* kclvm_value_List6(kclvm_value_ref_t* v1, kclvm_value_ref_t* v2, kclvm_value_ref_t* v3, kclvm_value_ref_t* v4, kclvm_value_ref_t* v5, kclvm_value_ref_t* v6); + +kclvm_value_ref_t* kclvm_value_ListN(kclvm_int_t n, kclvm_value_ref_t** elem_values); + +kclvm_value_ref_t* kclvm_value_None(); + +kclvm_value_ref_t* kclvm_value_Schema(); + +kclvm_value_ref_t* kclvm_value_Str(kclvm_char_t* v); + +kclvm_size_t kclvm_value_Str_len(kclvm_value_ref_t* p); + +kclvm_char_t* kclvm_value_Str_ptr(kclvm_value_ref_t* p); + +void kclvm_value_Str_resize(kclvm_value_ref_t* p, kclvm_size_t n); + +kclvm_value_ref_t* kclvm_value_True(); + +kclvm_value_ref_t* kclvm_value_Undefined(); + +kclvm_value_ref_t* kclvm_value_Unit(kclvm_float_t v, kclvm_int_t raw, kclvm_char_t* unit); + +kclvm_value_ref_t* kclvm_value_as(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +uint64_t* kclvm_value_check_function_ptr(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_cmp_equal_to(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_cmp_greater_than(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_cmp_greater_than_or_equal(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_cmp_less_than(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_cmp_less_than_or_equal(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_cmp_not_equal_to(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_deep_copy(kclvm_value_ref_t* p); + +void kclvm_value_delete(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_from_json(kclvm_char_t* s); + +kclvm_value_ref_t* kclvm_value_function_external_invoke(kclvm_value_ref_t* p, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +kclvm_value_ref_t* kclvm_value_function_get_closure(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_function_invoke(kclvm_value_ref_t* p, kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs, kclvm_char_t* pkgpath); + +kclvm_bool_t kclvm_value_function_is_external(kclvm_value_ref_t* p); + +uint64_t* kclvm_value_function_ptr(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_in(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_is(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_is_not(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_bool_t kclvm_value_is_truthy(kclvm_value_ref_t* p); + +kclvm_iterator_t* kclvm_value_iter(kclvm_value_ref_t* p); + +kclvm_kind_t kclvm_value_kind(kclvm_value_ref_t* p); + +kclvm_size_t kclvm_value_len(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_load_attr(kclvm_value_ref_t* obj, kclvm_char_t* key); + +kclvm_value_ref_t* kclvm_value_load_attr_option(kclvm_value_ref_t* p, kclvm_char_t* key); + +kclvm_value_ref_t* kclvm_value_logic_and(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_logic_or(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_not_in(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_add(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_aug_add(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_aug_bit_and(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_aug_bit_lshift(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_aug_bit_or(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_aug_bit_rshift(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_aug_bit_xor(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_aug_div(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_aug_floor_div(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_aug_mod(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_aug_mul(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_aug_pow(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_aug_sub(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_bit_and(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_bit_lshift(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_bit_or(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_bit_rshift(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_bit_xor(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_div(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_floor_div(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_mod(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_mul(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_pow(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_op_sub(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_plan_to_json(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_plan_to_yaml(kclvm_value_ref_t* p); + +void kclvm_value_remove_item(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_schema_function(uint64_t* fn_ptr, uint64_t* check_fn_ptr, kclvm_char_t* tpe); + +kclvm_value_ref_t* kclvm_value_schema_with_config(kclvm_value_ref_t* schema_dict, kclvm_value_ref_t* config, kclvm_char_t* name, kclvm_char_t* pkgpath, kclvm_value_ref_t* is_sub_schema, kclvm_value_ref_t* record_instance, kclvm_value_ref_t* instance_pkgpath); + +kclvm_value_ref_t* kclvm_value_slice(kclvm_value_ref_t* x, kclvm_value_ref_t* a, kclvm_value_ref_t* b, kclvm_value_ref_t* step); + +kclvm_value_ref_t* kclvm_value_slice_option(kclvm_value_ref_t* x, kclvm_value_ref_t* a, kclvm_value_ref_t* b, kclvm_value_ref_t* step); + +kclvm_value_ref_t* kclvm_value_subscr(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_subscr_option(kclvm_value_ref_t* a, kclvm_value_ref_t* b); + +kclvm_buffer_t* kclvm_value_to_json(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_to_json_value(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_to_json_value_with_null(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_to_str_value(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_to_yaml_value(kclvm_value_ref_t* p); + +kclvm_value_ref_t* kclvm_value_unary_l_not(kclvm_value_ref_t* a); + +kclvm_value_ref_t* kclvm_value_unary_minus(kclvm_value_ref_t* a); + +kclvm_value_ref_t* kclvm_value_unary_not(kclvm_value_ref_t* a); + +kclvm_value_ref_t* kclvm_value_unary_plus(kclvm_value_ref_t* a); + +kclvm_value_ref_t* kclvm_value_union(kclvm_value_ref_t* schema, kclvm_value_ref_t* b); + +kclvm_value_ref_t* kclvm_value_union_all(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_yaml_decode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_yaml_dump_to_file(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_yaml_encode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _kclvm_h_ diff --git a/kclvm/runtime/src/_kclvm.ll b/kclvm/runtime/src/_kclvm.ll new file mode 100644 index 000000000..d1a22957b --- /dev/null +++ b/kclvm/runtime/src/_kclvm.ll @@ -0,0 +1,740 @@ +; Copyright 2021 The KCL Authors. All rights reserved. + +; Auto generated, DONOT EDIT!!! + +%"kclvm_bool_t" = type i8 + +%"kclvm_buffer_t" = type { i8* } + +%"kclvm_char_t" = type i8 + +%"kclvm_context_t" = type { i8* } + +%"kclvm_decorator_value_t" = type opaque + +%"kclvm_float_t" = type double + +%"kclvm_int_t" = type i64 + +%"kclvm_iterator_t" = type { i8* } + +%"kclvm_kind_t" = type i32 + +%"kclvm_size_t" = type i32 + +%"kclvm_type_t" = type { i8* } + +%"kclvm_value_ref_t" = type { i8* } + +%"kclvm_value_t" = type { i8* } + +declare void @kclvm_assert(%kclvm_value_ref_t* %value, %kclvm_value_ref_t* %msg); + +declare %kclvm_value_ref_t* @kclvm_base64_decode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_base64_encode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_char_t* @kclvm_buffer_data(%kclvm_buffer_t* %p); + +declare void @kclvm_buffer_delete(%kclvm_buffer_t* %p); + +declare %kclvm_buffer_t* @kclvm_buffer_new(%kclvm_size_t %size); + +declare %kclvm_size_t @kclvm_buffer_size(%kclvm_buffer_t* %p); + +declare %kclvm_value_ref_t* @kclvm_builtin_abs(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_all_true(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_any_true(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_bin(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_bool(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_dict(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_float(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_hex(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_int(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_isunique(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_len(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_list(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_max(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_min(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_multiplyof(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_oct(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_option(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare void @kclvm_builtin_option_init(%kclvm_context_t* %ctx, i8* %key, i8* %value); + +declare %kclvm_value_ref_t* @kclvm_builtin_option_reset(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_ord(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_pow(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_print(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_range(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_round(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_sorted(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_capitalize(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_count(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_endswith(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_find(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_format(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_index(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_isalnum(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_isalpha(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_isdigit(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_islower(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_isspace(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_istitle(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_isupper(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_join(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_lower(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_lstrip(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_replace(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_rfind(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_rindex(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_rsplit(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_rstrip(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_split(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_splitlines(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_startswith(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_strip(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_title(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_str_upper(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_sum(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_typeof(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_builtin_zip(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare void @kclvm_config_attr_map(%kclvm_value_ref_t* %value, %kclvm_char_t* %name, %kclvm_char_t* %type_str); + +declare void @kclvm_context_args_clear(%kclvm_context_t* %p); + +declare %kclvm_char_t* @kclvm_context_args_get(%kclvm_context_t* %_p, %kclvm_char_t* %_key); + +declare void @kclvm_context_args_set(%kclvm_context_t* %_p, %kclvm_char_t* %_key, %kclvm_char_t* %_value); + +declare void @kclvm_context_clear_all_types(%kclvm_context_t* %p); + +declare %kclvm_context_t* @kclvm_context_current(); + +declare void @kclvm_context_delete(%kclvm_context_t* %p); + +declare i8* @kclvm_context_invoke(%kclvm_context_t* %p, i8* %method, i8* %args, i8* %kwargs); + +declare void @kclvm_context_main_begin_hook(%kclvm_context_t* %p); + +declare %kclvm_value_ref_t* @kclvm_context_main_end_hook(%kclvm_context_t* %p, %kclvm_value_ref_t* %return_value); + +declare %kclvm_context_t* @kclvm_context_new(); + +declare %kclvm_bool_t @kclvm_context_pkgpath_is_imported(%kclvm_char_t* %pkgpath); + +declare void @kclvm_context_put_type(%kclvm_context_t* %p, %kclvm_type_t* %typ); + +declare void @kclvm_context_set_debug_mode(%kclvm_context_t* %p, %kclvm_bool_t %v); + +declare void @kclvm_context_set_disable_none(%kclvm_context_t* %p, %kclvm_bool_t %v); + +declare void @kclvm_context_set_disable_schema_check(%kclvm_context_t* %p, %kclvm_bool_t %v); + +declare void @kclvm_context_set_import_names(%kclvm_context_t* %p, %kclvm_value_ref_t* %import_names); + +declare void @kclvm_context_set_kcl_filename(i8* %filename); + +declare void @kclvm_context_set_kcl_line_col(i32 %line, i32 %col); + +declare void @kclvm_context_set_kcl_location(%kclvm_context_t* %p, i8* %filename, i32 %line, i32 %col); + +declare void @kclvm_context_set_kcl_pkgpath(%kclvm_context_t* %p, i8* %pkgpath); + +declare void @kclvm_context_set_list_option_mode(%kclvm_context_t* %p, %kclvm_bool_t %v); + +declare void @kclvm_context_set_strict_range_check(%kclvm_context_t* %p, %kclvm_bool_t %v); + +declare void @kclvm_context_symbol_init(%kclvm_context_t* %p, %kclvm_size_t %n, %kclvm_char_t** %symbol_names); + +declare %kclvm_char_t* @kclvm_context_symbol_name(%kclvm_context_t* %p, %kclvm_size_t %i); + +declare %kclvm_size_t @kclvm_context_symbol_num(%kclvm_context_t* %p); + +declare %kclvm_value_t* @kclvm_context_symbol_value(%kclvm_context_t* %p, %kclvm_size_t %i); + +declare %kclvm_value_ref_t* @kclvm_convert_collection_value(%kclvm_value_ref_t* %value, %kclvm_char_t* %tpe); + +declare %kclvm_value_ref_t* @kclvm_crypto_md5(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_crypto_sha1(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_crypto_sha224(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_crypto_sha256(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_crypto_sha384(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_crypto_sha512(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_datetime_date(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_datetime_now(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_datetime_ticks(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_datetime_today(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +declare void @kclvm_debug_hello(); + +declare void @kclvm_debug_invoke_func(i8* %fn_ptr); + +declare void @kclvm_debug_print(i8* %cs); + +declare void @kclvm_debug_print_str_list(i32 %len, i8** %ss); + +declare void @kclvm_debug_print_type(%kclvm_type_t* %p); + +declare void @kclvm_debug_print_value(%kclvm_value_ref_t* %p); + +declare void @kclvm_debug_print_value_json_string(%kclvm_value_ref_t* %p); + +declare void @kclvm_default_collection_insert_int_pointer(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, i64* %ptr); + +declare void @kclvm_default_collection_insert_value(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, %kclvm_value_ref_t* %value); + +declare void @kclvm_dict_clear(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_dict_get(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %key); + +declare %kclvm_value_ref_t* @kclvm_dict_get_entry(%kclvm_value_ref_t* %p, %kclvm_char_t* %key); + +declare %kclvm_value_ref_t* @kclvm_dict_get_value(%kclvm_value_ref_t* %p, %kclvm_char_t* %key); + +declare %kclvm_value_ref_t* @kclvm_dict_get_value_by_path(%kclvm_value_ref_t* %p, %kclvm_char_t* %path); + +declare %kclvm_bool_t @kclvm_dict_has_value(%kclvm_value_ref_t* %p, %kclvm_char_t* %key); + +declare void @kclvm_dict_insert(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, %kclvm_value_ref_t* %v, %kclvm_size_t %op, %kclvm_size_t %insert_index); + +declare void @kclvm_dict_insert_unpack(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %v); + +declare void @kclvm_dict_insert_value(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %key, %kclvm_value_ref_t* %v, %kclvm_size_t %op, %kclvm_size_t %insert_index); + +declare %kclvm_value_ref_t* @kclvm_dict_keys(%kclvm_value_ref_t* %p); + +declare %kclvm_size_t @kclvm_dict_len(%kclvm_value_ref_t* %p); + +declare void @kclvm_dict_merge(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, %kclvm_value_ref_t* %v, %kclvm_size_t %op, %kclvm_size_t %insert_index); + +declare void @kclvm_dict_remove(%kclvm_value_ref_t* %p, %kclvm_char_t* %key); + +declare void @kclvm_dict_safe_insert(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, %kclvm_value_ref_t* %v, %kclvm_size_t %op, %kclvm_size_t %insert_index); + +declare void @kclvm_dict_set_value(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, %kclvm_value_ref_t* %val); + +declare void @kclvm_dict_update(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %v); + +declare void @kclvm_dict_update_key_value(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %key, %kclvm_value_ref_t* %v); + +declare %kclvm_value_ref_t* @kclvm_dict_values(%kclvm_value_ref_t* %p); + +declare void @kclvm_free(i8* %ptr); + +declare %kclvm_value_ref_t* @kclvm_iterator_cur_key(%kclvm_iterator_t* %p); + +declare %kclvm_value_ref_t* @kclvm_iterator_cur_value(%kclvm_iterator_t* %p); + +declare void @kclvm_iterator_delete(%kclvm_iterator_t* %p); + +declare %kclvm_bool_t @kclvm_iterator_is_end(%kclvm_iterator_t* %p); + +declare %kclvm_value_ref_t* @kclvm_iterator_next_value(%kclvm_iterator_t* %p, %kclvm_value_ref_t* %host); + +declare %kclvm_value_ref_t* @kclvm_json_decode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_json_dump_to_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_json_encode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare void @kclvm_list_append(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %v); + +declare void @kclvm_list_append_bool(%kclvm_value_ref_t* %p, %kclvm_bool_t %v); + +declare void @kclvm_list_append_float(%kclvm_value_ref_t* %p, %kclvm_float_t %v); + +declare void @kclvm_list_append_int(%kclvm_value_ref_t* %p, %kclvm_int_t %v); + +declare void @kclvm_list_append_str(%kclvm_value_ref_t* %p, %kclvm_char_t* %v); + +declare void @kclvm_list_append_unpack(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %v); + +declare void @kclvm_list_clear(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_list_count(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %item); + +declare %kclvm_value_ref_t* @kclvm_list_find(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %item); + +declare %kclvm_value_ref_t* @kclvm_list_get(%kclvm_value_ref_t* %p, %kclvm_size_t %i); + +declare %kclvm_value_ref_t* @kclvm_list_get_option(%kclvm_value_ref_t* %p, %kclvm_size_t %i); + +declare void @kclvm_list_insert(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %index, %kclvm_value_ref_t* %value); + +declare %kclvm_size_t @kclvm_list_len(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_list_pop(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_list_pop_first(%kclvm_value_ref_t* %p); + +declare void @kclvm_list_remove_at(%kclvm_value_ref_t* %p, %kclvm_size_t %i); + +declare void @kclvm_list_resize(%kclvm_value_ref_t* %p, %kclvm_size_t %newsize); + +declare void @kclvm_list_set(%kclvm_value_ref_t* %p, %kclvm_size_t %i, %kclvm_value_ref_t* %v); + +declare i8* @kclvm_malloc(i32 %n); + +declare %kclvm_value_ref_t* @kclvm_math_ceil(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_exp(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_expm1(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_factorial(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_floor(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_gcd(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_isfinite(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_isinf(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_isnan(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_log(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_log10(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_log1p(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_log2(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_modf(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_pow(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_math_sqrt(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_IP_string(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_fqdn(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_is_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_is_IPv4(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_is_global_unicast_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_is_interface_local_multicast_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_is_link_local_multicast_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_is_link_local_unicast_IP(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_is_loopback_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_is_multicast_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_is_unspecified_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_join_host_port(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_parse_IP(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_split_host_port(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_to_IP16(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_net_to_IP4(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare void @kclvm_plugin_init(i8* %fn_ptr); + +declare %kclvm_value_ref_t* @kclvm_plugin_invoke(i8* %method, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare i8* @kclvm_plugin_invoke_json(i8* %method, i8* %args, i8* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_regex_compile(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_regex_findall(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_regex_match(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_regex_replace(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_regex_search(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_regex_split(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare void @kclvm_schema_assert(%kclvm_value_ref_t* %value, %kclvm_value_ref_t* %msg, %kclvm_value_ref_t* %config_meta); + +declare void @kclvm_schema_backtrack_cache(%kclvm_value_ref_t* %schema, %kclvm_value_ref_t* %cache, %kclvm_value_ref_t* %cal_map, %kclvm_char_t* %name, %kclvm_value_ref_t* %runtime_type); + +declare void @kclvm_schema_default_settings(%kclvm_value_ref_t* %schema_value, %kclvm_value_ref_t* %config_value, %kclvm_char_t* %runtime_type); + +declare void @kclvm_schema_do_check_with_index_sign_attr(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs, i64* %check_fn_ptr, %kclvm_char_t* %attr_name); + +declare %kclvm_value_ref_t* @kclvm_schema_get_value(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, %kclvm_value_ref_t* %config, %kclvm_value_ref_t* %config_meta, %kclvm_value_ref_t* %cal_map, %kclvm_char_t* %target_attr, %kclvm_value_ref_t* %backtrack_level_map, %kclvm_value_ref_t* %backtrack_cache, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_schema_instances(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_schema_optional_check(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %v, %kclvm_char_t* %schema_name, %kclvm_value_ref_t* %config_meta); + +declare void @kclvm_schema_value_check(%kclvm_value_ref_t* %schema_value, %kclvm_value_ref_t* %schema_config, %kclvm_value_ref_t* %_config_meta, %kclvm_char_t* %schema_name, %kclvm_value_ref_t* %index_sign_value, %kclvm_char_t* %_key_name, %kclvm_char_t* %key_type, %kclvm_char_t* %_value_type, %kclvm_bool_t %_any_other, %kclvm_bool_t %is_relaxed); + +declare %kclvm_value_ref_t* @kclvm_schema_value_new(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs, %kclvm_value_ref_t* %schema_value_or_func, %kclvm_value_ref_t* %config, %kclvm_value_ref_t* %config_meta, %kclvm_char_t* %pkgpath); + +declare %kclvm_size_t @kclvm_strlen(i8* %ptr); + +declare void @kclvm_testing_arguments(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +declare void @kclvm_testing_setting_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_type_t* @kclvm_type_Any(); + +declare %kclvm_type_t* @kclvm_type_Bool(); + +declare %kclvm_type_t* @kclvm_type_BoolLit(%kclvm_bool_t %v); + +declare %kclvm_bool_t @kclvm_type_BoolLit_value(%kclvm_type_t* %p); + +declare %kclvm_type_t* @kclvm_type_Dict(%kclvm_type_t* %key_type, %kclvm_type_t* %elem_type); + +declare %kclvm_type_t* @kclvm_type_Float(); + +declare %kclvm_type_t* @kclvm_type_FloatLit(double %v); + +declare double @kclvm_type_FloatLit_value(%kclvm_type_t* %p); + +declare %kclvm_type_t* @kclvm_type_Func(%kclvm_size_t %args_len, %kclvm_type_t** %args_types, %kclvm_type_t* %return_type); + +declare %kclvm_type_t* @kclvm_type_Int(); + +declare %kclvm_type_t* @kclvm_type_IntLit(i64 %v); + +declare i64 @kclvm_type_IntLit_value(%kclvm_type_t* %p); + +declare %kclvm_type_t* @kclvm_type_List(%kclvm_type_t* %elem_type); + +declare %kclvm_type_t* @kclvm_type_Schema(%kclvm_char_t* %name, %kclvm_char_t* %parent_name, %kclvm_bool_t %_is_relaxed, %kclvm_size_t %field_num, %kclvm_char_t** %field_names, %kclvm_type_t** %field_types); + +declare %kclvm_type_t* @kclvm_type_Str(); + +declare %kclvm_type_t* @kclvm_type_StrLit(%kclvm_char_t* %s); + +declare %kclvm_char_t* @kclvm_type_StrLit_value(%kclvm_type_t* %p); + +declare %kclvm_type_t* @kclvm_type_Union(%kclvm_size_t %n, %kclvm_type_t** %elem_types); + +declare %kclvm_size_t @kclvm_type_arg_num(%kclvm_type_t* %p); + +declare %kclvm_type_t* @kclvm_type_arg_type(%kclvm_type_t* %p, %kclvm_size_t %i); + +declare void @kclvm_type_delete(%kclvm_type_t* %p); + +declare %kclvm_type_t* @kclvm_type_elem_type(%kclvm_type_t* %p); + +declare %kclvm_type_t* @kclvm_type_key_type(%kclvm_type_t* %p); + +declare %kclvm_kind_t @kclvm_type_kind(%kclvm_type_t* %p); + +declare %kclvm_type_t* @kclvm_type_return_type(%kclvm_type_t* %p); + +declare %kclvm_char_t* @kclvm_type_schema_field_name(%kclvm_type_t* %p, %kclvm_size_t %i); + +declare %kclvm_size_t @kclvm_type_schema_field_num(%kclvm_type_t* %p); + +declare %kclvm_type_t* @kclvm_type_schema_field_type(%kclvm_type_t* %p, %kclvm_size_t %i); + +declare %kclvm_char_t* @kclvm_type_schema_name(%kclvm_type_t* %p); + +declare %kclvm_char_t* @kclvm_type_schema_parent_name(%kclvm_type_t* %p); + +declare %kclvm_bool_t @kclvm_type_schema_relaxed(%kclvm_type_t* %p); + +declare %kclvm_kind_t @kclvm_type_str(%kclvm_type_t* %p); + +declare %kclvm_value_ref_t* @kclvm_units_to_G(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_units_to_Gi(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_units_to_K(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_units_to_Ki(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_units_to_M(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_units_to_Mi(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_units_to_P(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_units_to_Pi(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_units_to_T(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_units_to_Ti(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_units_to_m(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_units_to_n(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_units_to_u(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_value_Bool(%kclvm_bool_t %v); + +declare %kclvm_bool_t* @kclvm_value_Bool_ptr(%kclvm_value_ref_t* %p); + +declare %kclvm_decorator_value_t* @kclvm_value_Decorator(%kclvm_char_t* %name, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs, %kclvm_value_ref_t* %config_meta, %kclvm_char_t* %attr_name, %kclvm_value_ref_t* %config_value, %kclvm_value_ref_t* %is_schema_target); + +declare %kclvm_value_ref_t* @kclvm_value_Dict(); + +declare %kclvm_value_ref_t* @kclvm_value_False(); + +declare %kclvm_value_ref_t* @kclvm_value_Float(%kclvm_float_t %v); + +declare %kclvm_float_t* @kclvm_value_Float_ptr(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_Function(i64* %fn_ptr, %kclvm_value_ref_t* %closure, %kclvm_char_t* %external_name); + +declare %kclvm_value_ref_t* @kclvm_value_Function_using_ptr(i64* %fn_ptr); + +declare %kclvm_value_ref_t* @kclvm_value_Int(%kclvm_int_t %v); + +declare %kclvm_int_t* @kclvm_value_Int_ptr(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_List(); + +declare %kclvm_value_ref_t* @kclvm_value_List10(%kclvm_value_ref_t* %v1, %kclvm_value_ref_t* %v2, %kclvm_value_ref_t* %v3, %kclvm_value_ref_t* %v4, %kclvm_value_ref_t* %v5, %kclvm_value_ref_t* %v6, %kclvm_value_ref_t* %v7, %kclvm_value_ref_t* %v8, %kclvm_value_ref_t* %v9, %kclvm_value_ref_t* %v10); + +declare %kclvm_value_ref_t* @kclvm_value_List6(%kclvm_value_ref_t* %v1, %kclvm_value_ref_t* %v2, %kclvm_value_ref_t* %v3, %kclvm_value_ref_t* %v4, %kclvm_value_ref_t* %v5, %kclvm_value_ref_t* %v6); + +declare %kclvm_value_ref_t* @kclvm_value_ListN(%kclvm_int_t %n, %kclvm_value_ref_t** %elem_values); + +declare %kclvm_value_ref_t* @kclvm_value_None(); + +declare %kclvm_value_ref_t* @kclvm_value_Schema(); + +declare %kclvm_value_ref_t* @kclvm_value_Str(%kclvm_char_t* %v); + +declare %kclvm_size_t @kclvm_value_Str_len(%kclvm_value_ref_t* %p); + +declare %kclvm_char_t* @kclvm_value_Str_ptr(%kclvm_value_ref_t* %p); + +declare void @kclvm_value_Str_resize(%kclvm_value_ref_t* %p, %kclvm_size_t %n); + +declare %kclvm_value_ref_t* @kclvm_value_True(); + +declare %kclvm_value_ref_t* @kclvm_value_Undefined(); + +declare %kclvm_value_ref_t* @kclvm_value_Unit(%kclvm_float_t %v, %kclvm_int_t %raw, %kclvm_char_t* %unit); + +declare %kclvm_value_ref_t* @kclvm_value_as(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare i64* @kclvm_value_check_function_ptr(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_cmp_equal_to(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_cmp_greater_than(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_cmp_greater_than_or_equal(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_cmp_less_than(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_cmp_less_than_or_equal(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_cmp_not_equal_to(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_deep_copy(%kclvm_value_ref_t* %p); + +declare void @kclvm_value_delete(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_from_json(%kclvm_char_t* %s); + +declare %kclvm_value_ref_t* @kclvm_value_function_external_invoke(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +declare %kclvm_value_ref_t* @kclvm_value_function_get_closure(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_function_invoke(%kclvm_value_ref_t* %p, %kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs, %kclvm_char_t* %pkgpath); + +declare %kclvm_bool_t @kclvm_value_function_is_external(%kclvm_value_ref_t* %p); + +declare i64* @kclvm_value_function_ptr(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_in(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_is(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_is_not(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_bool_t @kclvm_value_is_truthy(%kclvm_value_ref_t* %p); + +declare %kclvm_iterator_t* @kclvm_value_iter(%kclvm_value_ref_t* %p); + +declare %kclvm_kind_t @kclvm_value_kind(%kclvm_value_ref_t* %p); + +declare %kclvm_size_t @kclvm_value_len(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_load_attr(%kclvm_value_ref_t* %obj, %kclvm_char_t* %key); + +declare %kclvm_value_ref_t* @kclvm_value_load_attr_option(%kclvm_value_ref_t* %p, %kclvm_char_t* %key); + +declare %kclvm_value_ref_t* @kclvm_value_logic_and(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_logic_or(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_not_in(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_add(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_aug_add(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_aug_bit_and(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_aug_bit_lshift(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_aug_bit_or(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_aug_bit_rshift(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_aug_bit_xor(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_aug_div(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_aug_floor_div(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_aug_mod(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_aug_mul(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_aug_pow(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_aug_sub(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_bit_and(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_bit_lshift(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_bit_or(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_bit_rshift(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_bit_xor(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_div(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_floor_div(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_mod(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_mul(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_pow(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_op_sub(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_plan_to_json(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_plan_to_yaml(%kclvm_value_ref_t* %p); + +declare void @kclvm_value_remove_item(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_schema_function(i64* %fn_ptr, i64* %check_fn_ptr, %kclvm_char_t* %tpe); + +declare %kclvm_value_ref_t* @kclvm_value_schema_with_config(%kclvm_value_ref_t* %schema_dict, %kclvm_value_ref_t* %config, %kclvm_char_t* %name, %kclvm_char_t* %pkgpath, %kclvm_value_ref_t* %is_sub_schema, %kclvm_value_ref_t* %record_instance, %kclvm_value_ref_t* %instance_pkgpath); + +declare %kclvm_value_ref_t* @kclvm_value_slice(%kclvm_value_ref_t* %x, %kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b, %kclvm_value_ref_t* %step); + +declare %kclvm_value_ref_t* @kclvm_value_slice_option(%kclvm_value_ref_t* %x, %kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b, %kclvm_value_ref_t* %step); + +declare %kclvm_value_ref_t* @kclvm_value_subscr(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_subscr_option(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +declare %kclvm_buffer_t* @kclvm_value_to_json(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_to_json_value(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_to_json_value_with_null(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_to_str_value(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_to_yaml_value(%kclvm_value_ref_t* %p); + +declare %kclvm_value_ref_t* @kclvm_value_unary_l_not(%kclvm_value_ref_t* %a); + +declare %kclvm_value_ref_t* @kclvm_value_unary_minus(%kclvm_value_ref_t* %a); + +declare %kclvm_value_ref_t* @kclvm_value_unary_not(%kclvm_value_ref_t* %a); + +declare %kclvm_value_ref_t* @kclvm_value_unary_plus(%kclvm_value_ref_t* %a); + +declare %kclvm_value_ref_t* @kclvm_value_union(%kclvm_value_ref_t* %schema, %kclvm_value_ref_t* %b); + +declare %kclvm_value_ref_t* @kclvm_value_union_all(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_yaml_decode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_yaml_dump_to_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +declare %kclvm_value_ref_t* @kclvm_yaml_encode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +define void @__kcl_keep_link_runtime(%kclvm_value_ref_t* %_a, %kclvm_context_t* %_b) { + call %kclvm_value_ref_t*() @kclvm_value_None() + ret void +} diff --git a/kclvm/runtime/src/_kclvm.rs b/kclvm/runtime/src/_kclvm.rs new file mode 100644 index 000000000..17da328eb --- /dev/null +++ b/kclvm/runtime/src/_kclvm.rs @@ -0,0 +1,395 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +// Auto generated, DONOT EDIT!!! + +#[allow(dead_code, non_camel_case_types)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum ApiType { + Value, +} + +impl std::fmt::Display for ApiType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + ApiType::Value => write!(f, "{:?}", "api::kclvm::Value"), + } + } +} + +impl ApiType { + #[allow(dead_code)] + pub fn name(&self) -> String { + return format!("{:?}", self); + } +} + +#[allow(dead_code, non_camel_case_types)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum ApiFunc { + kclvm_assert, + kclvm_base64_decode, + kclvm_base64_encode, + kclvm_buffer_data, + kclvm_buffer_delete, + kclvm_buffer_new, + kclvm_buffer_size, + kclvm_builtin_abs, + kclvm_builtin_all_true, + kclvm_builtin_any_true, + kclvm_builtin_bin, + kclvm_builtin_bool, + kclvm_builtin_dict, + kclvm_builtin_float, + kclvm_builtin_hex, + kclvm_builtin_int, + kclvm_builtin_isunique, + kclvm_builtin_len, + kclvm_builtin_list, + kclvm_builtin_max, + kclvm_builtin_min, + kclvm_builtin_multiplyof, + kclvm_builtin_oct, + kclvm_builtin_option, + kclvm_builtin_option_init, + kclvm_builtin_option_reset, + kclvm_builtin_ord, + kclvm_builtin_pow, + kclvm_builtin_print, + kclvm_builtin_range, + kclvm_builtin_round, + kclvm_builtin_sorted, + kclvm_builtin_str, + kclvm_builtin_str_capitalize, + kclvm_builtin_str_count, + kclvm_builtin_str_endswith, + kclvm_builtin_str_find, + kclvm_builtin_str_format, + kclvm_builtin_str_index, + kclvm_builtin_str_isalnum, + kclvm_builtin_str_isalpha, + kclvm_builtin_str_isdigit, + kclvm_builtin_str_islower, + kclvm_builtin_str_isspace, + kclvm_builtin_str_istitle, + kclvm_builtin_str_isupper, + kclvm_builtin_str_join, + kclvm_builtin_str_lower, + kclvm_builtin_str_lstrip, + kclvm_builtin_str_replace, + kclvm_builtin_str_rfind, + kclvm_builtin_str_rindex, + kclvm_builtin_str_rsplit, + kclvm_builtin_str_rstrip, + kclvm_builtin_str_split, + kclvm_builtin_str_splitlines, + kclvm_builtin_str_startswith, + kclvm_builtin_str_strip, + kclvm_builtin_str_title, + kclvm_builtin_str_upper, + kclvm_builtin_sum, + kclvm_builtin_typeof, + kclvm_builtin_zip, + kclvm_config_attr_map, + kclvm_context_args_clear, + kclvm_context_args_get, + kclvm_context_args_set, + kclvm_context_clear_all_types, + kclvm_context_current, + kclvm_context_delete, + kclvm_context_invoke, + kclvm_context_main_begin_hook, + kclvm_context_main_end_hook, + kclvm_context_new, + kclvm_context_pkgpath_is_imported, + kclvm_context_put_type, + kclvm_context_set_debug_mode, + kclvm_context_set_disable_none, + kclvm_context_set_disable_schema_check, + kclvm_context_set_import_names, + kclvm_context_set_kcl_filename, + kclvm_context_set_kcl_line_col, + kclvm_context_set_kcl_location, + kclvm_context_set_kcl_pkgpath, + kclvm_context_set_list_option_mode, + kclvm_context_set_strict_range_check, + kclvm_context_symbol_init, + kclvm_context_symbol_name, + kclvm_context_symbol_num, + kclvm_context_symbol_value, + kclvm_convert_collection_value, + kclvm_crypto_md5, + kclvm_crypto_sha1, + kclvm_crypto_sha224, + kclvm_crypto_sha256, + kclvm_crypto_sha384, + kclvm_crypto_sha512, + kclvm_datetime_date, + kclvm_datetime_now, + kclvm_datetime_ticks, + kclvm_datetime_today, + kclvm_debug_hello, + kclvm_debug_invoke_func, + kclvm_debug_print, + kclvm_debug_print_str_list, + kclvm_debug_print_type, + kclvm_debug_print_value, + kclvm_debug_print_value_json_string, + kclvm_default_collection_insert_int_pointer, + kclvm_default_collection_insert_value, + kclvm_dict_clear, + kclvm_dict_get, + kclvm_dict_get_entry, + kclvm_dict_get_value, + kclvm_dict_get_value_by_path, + kclvm_dict_has_value, + kclvm_dict_insert, + kclvm_dict_insert_unpack, + kclvm_dict_insert_value, + kclvm_dict_keys, + kclvm_dict_len, + kclvm_dict_merge, + kclvm_dict_remove, + kclvm_dict_safe_insert, + kclvm_dict_set_value, + kclvm_dict_update, + kclvm_dict_update_key_value, + kclvm_dict_values, + kclvm_free, + kclvm_iterator_cur_key, + kclvm_iterator_cur_value, + kclvm_iterator_delete, + kclvm_iterator_is_end, + kclvm_iterator_next_value, + kclvm_json_decode, + kclvm_json_dump_to_file, + kclvm_json_encode, + kclvm_list_append, + kclvm_list_append_bool, + kclvm_list_append_float, + kclvm_list_append_int, + kclvm_list_append_str, + kclvm_list_append_unpack, + kclvm_list_clear, + kclvm_list_count, + kclvm_list_find, + kclvm_list_get, + kclvm_list_get_option, + kclvm_list_insert, + kclvm_list_len, + kclvm_list_pop, + kclvm_list_pop_first, + kclvm_list_remove_at, + kclvm_list_resize, + kclvm_list_set, + kclvm_malloc, + kclvm_math_ceil, + kclvm_math_exp, + kclvm_math_expm1, + kclvm_math_factorial, + kclvm_math_floor, + kclvm_math_gcd, + kclvm_math_isfinite, + kclvm_math_isinf, + kclvm_math_isnan, + kclvm_math_log, + kclvm_math_log10, + kclvm_math_log1p, + kclvm_math_log2, + kclvm_math_modf, + kclvm_math_pow, + kclvm_math_sqrt, + kclvm_net_IP_string, + kclvm_net_fqdn, + kclvm_net_is_IP, + kclvm_net_is_IPv4, + kclvm_net_is_global_unicast_IP, + kclvm_net_is_interface_local_multicast_IP, + kclvm_net_is_link_local_multicast_IP, + kclvm_net_is_link_local_unicast_IP, + kclvm_net_is_loopback_IP, + kclvm_net_is_multicast_IP, + kclvm_net_is_unspecified_IP, + kclvm_net_join_host_port, + kclvm_net_parse_IP, + kclvm_net_split_host_port, + kclvm_net_to_IP16, + kclvm_net_to_IP4, + kclvm_plugin_init, + kclvm_plugin_invoke, + kclvm_plugin_invoke_json, + kclvm_regex_compile, + kclvm_regex_findall, + kclvm_regex_match, + kclvm_regex_replace, + kclvm_regex_search, + kclvm_regex_split, + kclvm_schema_assert, + kclvm_schema_backtrack_cache, + kclvm_schema_default_settings, + kclvm_schema_do_check_with_index_sign_attr, + kclvm_schema_get_value, + kclvm_schema_instances, + kclvm_schema_optional_check, + kclvm_schema_value_check, + kclvm_schema_value_new, + kclvm_strlen, + kclvm_testing_arguments, + kclvm_testing_setting_file, + kclvm_type_Any, + kclvm_type_Bool, + kclvm_type_BoolLit, + kclvm_type_BoolLit_value, + kclvm_type_Dict, + kclvm_type_Float, + kclvm_type_FloatLit, + kclvm_type_FloatLit_value, + kclvm_type_Func, + kclvm_type_Int, + kclvm_type_IntLit, + kclvm_type_IntLit_value, + kclvm_type_List, + kclvm_type_Schema, + kclvm_type_Str, + kclvm_type_StrLit, + kclvm_type_StrLit_value, + kclvm_type_Union, + kclvm_type_arg_num, + kclvm_type_arg_type, + kclvm_type_delete, + kclvm_type_elem_type, + kclvm_type_key_type, + kclvm_type_kind, + kclvm_type_return_type, + kclvm_type_schema_field_name, + kclvm_type_schema_field_num, + kclvm_type_schema_field_type, + kclvm_type_schema_name, + kclvm_type_schema_parent_name, + kclvm_type_schema_relaxed, + kclvm_type_str, + kclvm_units_to_G, + kclvm_units_to_Gi, + kclvm_units_to_K, + kclvm_units_to_Ki, + kclvm_units_to_M, + kclvm_units_to_Mi, + kclvm_units_to_P, + kclvm_units_to_Pi, + kclvm_units_to_T, + kclvm_units_to_Ti, + kclvm_units_to_m, + kclvm_units_to_n, + kclvm_units_to_u, + kclvm_value_Bool, + kclvm_value_Bool_ptr, + kclvm_value_Decorator, + kclvm_value_Dict, + kclvm_value_False, + kclvm_value_Float, + kclvm_value_Float_ptr, + kclvm_value_Function, + kclvm_value_Function_using_ptr, + kclvm_value_Int, + kclvm_value_Int_ptr, + kclvm_value_List, + kclvm_value_List10, + kclvm_value_List6, + kclvm_value_ListN, + kclvm_value_None, + kclvm_value_Schema, + kclvm_value_Str, + kclvm_value_Str_len, + kclvm_value_Str_ptr, + kclvm_value_Str_resize, + kclvm_value_True, + kclvm_value_Undefined, + kclvm_value_Unit, + kclvm_value_as, + kclvm_value_check_function_ptr, + kclvm_value_cmp_equal_to, + kclvm_value_cmp_greater_than, + kclvm_value_cmp_greater_than_or_equal, + kclvm_value_cmp_less_than, + kclvm_value_cmp_less_than_or_equal, + kclvm_value_cmp_not_equal_to, + kclvm_value_deep_copy, + kclvm_value_delete, + kclvm_value_from_json, + kclvm_value_function_external_invoke, + kclvm_value_function_get_closure, + kclvm_value_function_invoke, + kclvm_value_function_is_external, + kclvm_value_function_ptr, + kclvm_value_in, + kclvm_value_is, + kclvm_value_is_not, + kclvm_value_is_truthy, + kclvm_value_iter, + kclvm_value_kind, + kclvm_value_len, + kclvm_value_load_attr, + kclvm_value_load_attr_option, + kclvm_value_logic_and, + kclvm_value_logic_or, + kclvm_value_not_in, + kclvm_value_op_add, + kclvm_value_op_aug_add, + kclvm_value_op_aug_bit_and, + kclvm_value_op_aug_bit_lshift, + kclvm_value_op_aug_bit_or, + kclvm_value_op_aug_bit_rshift, + kclvm_value_op_aug_bit_xor, + kclvm_value_op_aug_div, + kclvm_value_op_aug_floor_div, + kclvm_value_op_aug_mod, + kclvm_value_op_aug_mul, + kclvm_value_op_aug_pow, + kclvm_value_op_aug_sub, + kclvm_value_op_bit_and, + kclvm_value_op_bit_lshift, + kclvm_value_op_bit_or, + kclvm_value_op_bit_rshift, + kclvm_value_op_bit_xor, + kclvm_value_op_div, + kclvm_value_op_floor_div, + kclvm_value_op_mod, + kclvm_value_op_mul, + kclvm_value_op_pow, + kclvm_value_op_sub, + kclvm_value_plan_to_json, + kclvm_value_plan_to_yaml, + kclvm_value_remove_item, + kclvm_value_schema_function, + kclvm_value_schema_with_config, + kclvm_value_slice, + kclvm_value_slice_option, + kclvm_value_subscr, + kclvm_value_subscr_option, + kclvm_value_to_json, + kclvm_value_to_json_value, + kclvm_value_to_json_value_with_null, + kclvm_value_to_str_value, + kclvm_value_to_yaml_value, + kclvm_value_unary_l_not, + kclvm_value_unary_minus, + kclvm_value_unary_not, + kclvm_value_unary_plus, + kclvm_value_union, + kclvm_value_union_all, + kclvm_yaml_decode, + kclvm_yaml_dump_to_file, + kclvm_yaml_encode, +} + +impl std::fmt::Display for ApiFunc { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl ApiFunc { + #[allow(dead_code)] + pub fn name(&self) -> String { + return format!("{:?}", self); + } +} diff --git a/kclvm/runtime/src/_kclvm_addr.rs b/kclvm/runtime/src/_kclvm_addr.rs new file mode 100644 index 000000000..b0687ed84 --- /dev/null +++ b/kclvm/runtime/src/_kclvm_addr.rs @@ -0,0 +1,417 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +// Auto generated, DONOT EDIT!!! + +#[allow(dead_code)] +pub fn _kclvm_get_fn_ptr_by_name(name: &str) -> u64 { + match name { + "kclvm_assert" => crate::kclvm_assert as *const () as u64, + "kclvm_base64_decode" => crate::kclvm_base64_decode as *const () as u64, + "kclvm_base64_encode" => crate::kclvm_base64_encode as *const () as u64, + "kclvm_buffer_data" => crate::kclvm_buffer_data as *const () as u64, + "kclvm_buffer_delete" => crate::kclvm_buffer_delete as *const () as u64, + "kclvm_buffer_new" => crate::kclvm_buffer_new as *const () as u64, + "kclvm_buffer_size" => crate::kclvm_buffer_size as *const () as u64, + "kclvm_builtin_abs" => crate::kclvm_builtin_abs as *const () as u64, + "kclvm_builtin_all_true" => crate::kclvm_builtin_all_true as *const () as u64, + "kclvm_builtin_any_true" => crate::kclvm_builtin_any_true as *const () as u64, + "kclvm_builtin_bin" => crate::kclvm_builtin_bin as *const () as u64, + "kclvm_builtin_bool" => crate::kclvm_builtin_bool as *const () as u64, + "kclvm_builtin_dict" => crate::kclvm_builtin_dict as *const () as u64, + "kclvm_builtin_float" => crate::kclvm_builtin_float as *const () as u64, + "kclvm_builtin_hex" => crate::kclvm_builtin_hex as *const () as u64, + "kclvm_builtin_int" => crate::kclvm_builtin_int as *const () as u64, + "kclvm_builtin_isunique" => crate::kclvm_builtin_isunique as *const () as u64, + "kclvm_builtin_len" => crate::kclvm_builtin_len as *const () as u64, + "kclvm_builtin_list" => crate::kclvm_builtin_list as *const () as u64, + "kclvm_builtin_max" => crate::kclvm_builtin_max as *const () as u64, + "kclvm_builtin_min" => crate::kclvm_builtin_min as *const () as u64, + "kclvm_builtin_multiplyof" => crate::kclvm_builtin_multiplyof as *const () as u64, + "kclvm_builtin_oct" => crate::kclvm_builtin_oct as *const () as u64, + "kclvm_builtin_option" => crate::kclvm_builtin_option as *const () as u64, + "kclvm_builtin_option_init" => crate::kclvm_builtin_option_init as *const () as u64, + "kclvm_builtin_option_reset" => crate::kclvm_builtin_option_reset as *const () as u64, + "kclvm_builtin_ord" => crate::kclvm_builtin_ord as *const () as u64, + "kclvm_builtin_pow" => crate::kclvm_builtin_pow as *const () as u64, + "kclvm_builtin_print" => crate::kclvm_builtin_print as *const () as u64, + "kclvm_builtin_range" => crate::kclvm_builtin_range as *const () as u64, + "kclvm_builtin_round" => crate::kclvm_builtin_round as *const () as u64, + "kclvm_builtin_sorted" => crate::kclvm_builtin_sorted as *const () as u64, + "kclvm_builtin_str" => crate::kclvm_builtin_str as *const () as u64, + "kclvm_builtin_str_capitalize" => crate::kclvm_builtin_str_capitalize as *const () as u64, + "kclvm_builtin_str_count" => crate::kclvm_builtin_str_count as *const () as u64, + "kclvm_builtin_str_endswith" => crate::kclvm_builtin_str_endswith as *const () as u64, + "kclvm_builtin_str_find" => crate::kclvm_builtin_str_find as *const () as u64, + "kclvm_builtin_str_format" => crate::kclvm_builtin_str_format as *const () as u64, + "kclvm_builtin_str_index" => crate::kclvm_builtin_str_index as *const () as u64, + "kclvm_builtin_str_isalnum" => crate::kclvm_builtin_str_isalnum as *const () as u64, + "kclvm_builtin_str_isalpha" => crate::kclvm_builtin_str_isalpha as *const () as u64, + "kclvm_builtin_str_isdigit" => crate::kclvm_builtin_str_isdigit as *const () as u64, + "kclvm_builtin_str_islower" => crate::kclvm_builtin_str_islower as *const () as u64, + "kclvm_builtin_str_isspace" => crate::kclvm_builtin_str_isspace as *const () as u64, + "kclvm_builtin_str_istitle" => crate::kclvm_builtin_str_istitle as *const () as u64, + "kclvm_builtin_str_isupper" => crate::kclvm_builtin_str_isupper as *const () as u64, + "kclvm_builtin_str_join" => crate::kclvm_builtin_str_join as *const () as u64, + "kclvm_builtin_str_lower" => crate::kclvm_builtin_str_lower as *const () as u64, + "kclvm_builtin_str_lstrip" => crate::kclvm_builtin_str_lstrip as *const () as u64, + "kclvm_builtin_str_replace" => crate::kclvm_builtin_str_replace as *const () as u64, + "kclvm_builtin_str_rfind" => crate::kclvm_builtin_str_rfind as *const () as u64, + "kclvm_builtin_str_rindex" => crate::kclvm_builtin_str_rindex as *const () as u64, + "kclvm_builtin_str_rsplit" => crate::kclvm_builtin_str_rsplit as *const () as u64, + "kclvm_builtin_str_rstrip" => crate::kclvm_builtin_str_rstrip as *const () as u64, + "kclvm_builtin_str_split" => crate::kclvm_builtin_str_split as *const () as u64, + "kclvm_builtin_str_splitlines" => crate::kclvm_builtin_str_splitlines as *const () as u64, + "kclvm_builtin_str_startswith" => crate::kclvm_builtin_str_startswith as *const () as u64, + "kclvm_builtin_str_strip" => crate::kclvm_builtin_str_strip as *const () as u64, + "kclvm_builtin_str_title" => crate::kclvm_builtin_str_title as *const () as u64, + "kclvm_builtin_str_upper" => crate::kclvm_builtin_str_upper as *const () as u64, + "kclvm_builtin_sum" => crate::kclvm_builtin_sum as *const () as u64, + "kclvm_builtin_typeof" => crate::kclvm_builtin_typeof as *const () as u64, + "kclvm_builtin_zip" => crate::kclvm_builtin_zip as *const () as u64, + "kclvm_config_attr_map" => crate::kclvm_config_attr_map as *const () as u64, + "kclvm_context_args_clear" => crate::kclvm_context_args_clear as *const () as u64, + "kclvm_context_args_get" => crate::kclvm_context_args_get as *const () as u64, + "kclvm_context_args_set" => crate::kclvm_context_args_set as *const () as u64, + "kclvm_context_clear_all_types" => crate::kclvm_context_clear_all_types as *const () as u64, + "kclvm_context_current" => crate::kclvm_context_current as *const () as u64, + "kclvm_context_delete" => crate::kclvm_context_delete as *const () as u64, + "kclvm_context_invoke" => crate::kclvm_context_invoke as *const () as u64, + "kclvm_context_main_begin_hook" => crate::kclvm_context_main_begin_hook as *const () as u64, + "kclvm_context_main_end_hook" => crate::kclvm_context_main_end_hook as *const () as u64, + "kclvm_context_new" => crate::kclvm_context_new as *const () as u64, + "kclvm_context_pkgpath_is_imported" => { + crate::kclvm_context_pkgpath_is_imported as *const () as u64 + } + "kclvm_context_put_type" => crate::kclvm_context_put_type as *const () as u64, + "kclvm_context_set_debug_mode" => crate::kclvm_context_set_debug_mode as *const () as u64, + "kclvm_context_set_disable_none" => { + crate::kclvm_context_set_disable_none as *const () as u64 + } + "kclvm_context_set_disable_schema_check" => { + crate::kclvm_context_set_disable_schema_check as *const () as u64 + } + "kclvm_context_set_import_names" => { + crate::kclvm_context_set_import_names as *const () as u64 + } + "kclvm_context_set_kcl_filename" => { + crate::kclvm_context_set_kcl_filename as *const () as u64 + } + "kclvm_context_set_kcl_line_col" => { + crate::kclvm_context_set_kcl_line_col as *const () as u64 + } + "kclvm_context_set_kcl_location" => { + crate::kclvm_context_set_kcl_location as *const () as u64 + } + "kclvm_context_set_kcl_pkgpath" => crate::kclvm_context_set_kcl_pkgpath as *const () as u64, + "kclvm_context_set_list_option_mode" => { + crate::kclvm_context_set_list_option_mode as *const () as u64 + } + "kclvm_context_set_strict_range_check" => { + crate::kclvm_context_set_strict_range_check as *const () as u64 + } + "kclvm_context_symbol_init" => crate::kclvm_context_symbol_init as *const () as u64, + "kclvm_context_symbol_name" => crate::kclvm_context_symbol_name as *const () as u64, + "kclvm_context_symbol_num" => crate::kclvm_context_symbol_num as *const () as u64, + "kclvm_context_symbol_value" => crate::kclvm_context_symbol_value as *const () as u64, + "kclvm_convert_collection_value" => { + crate::kclvm_convert_collection_value as *const () as u64 + } + "kclvm_crypto_md5" => crate::kclvm_crypto_md5 as *const () as u64, + "kclvm_crypto_sha1" => crate::kclvm_crypto_sha1 as *const () as u64, + "kclvm_crypto_sha224" => crate::kclvm_crypto_sha224 as *const () as u64, + "kclvm_crypto_sha256" => crate::kclvm_crypto_sha256 as *const () as u64, + "kclvm_crypto_sha384" => crate::kclvm_crypto_sha384 as *const () as u64, + "kclvm_crypto_sha512" => crate::kclvm_crypto_sha512 as *const () as u64, + "kclvm_datetime_date" => crate::kclvm_datetime_date as *const () as u64, + "kclvm_datetime_now" => crate::kclvm_datetime_now as *const () as u64, + "kclvm_datetime_ticks" => crate::kclvm_datetime_ticks as *const () as u64, + "kclvm_datetime_today" => crate::kclvm_datetime_today as *const () as u64, + "kclvm_debug_hello" => crate::kclvm_debug_hello as *const () as u64, + "kclvm_debug_invoke_func" => crate::kclvm_debug_invoke_func as *const () as u64, + "kclvm_debug_print" => crate::kclvm_debug_print as *const () as u64, + "kclvm_debug_print_str_list" => crate::kclvm_debug_print_str_list as *const () as u64, + "kclvm_debug_print_type" => crate::kclvm_debug_print_type as *const () as u64, + "kclvm_debug_print_value" => crate::kclvm_debug_print_value as *const () as u64, + "kclvm_debug_print_value_json_string" => { + crate::kclvm_debug_print_value_json_string as *const () as u64 + } + "kclvm_default_collection_insert_int_pointer" => { + crate::kclvm_default_collection_insert_int_pointer as *const () as u64 + } + "kclvm_default_collection_insert_value" => { + crate::kclvm_default_collection_insert_value as *const () as u64 + } + "kclvm_dict_clear" => crate::kclvm_dict_clear as *const () as u64, + "kclvm_dict_get" => crate::kclvm_dict_get as *const () as u64, + "kclvm_dict_get_entry" => crate::kclvm_dict_get_entry as *const () as u64, + "kclvm_dict_get_value" => crate::kclvm_dict_get_value as *const () as u64, + "kclvm_dict_get_value_by_path" => crate::kclvm_dict_get_value_by_path as *const () as u64, + "kclvm_dict_has_value" => crate::kclvm_dict_has_value as *const () as u64, + "kclvm_dict_insert" => crate::kclvm_dict_insert as *const () as u64, + "kclvm_dict_insert_unpack" => crate::kclvm_dict_insert_unpack as *const () as u64, + "kclvm_dict_insert_value" => crate::kclvm_dict_insert_value as *const () as u64, + "kclvm_dict_keys" => crate::kclvm_dict_keys as *const () as u64, + "kclvm_dict_len" => crate::kclvm_dict_len as *const () as u64, + "kclvm_dict_merge" => crate::kclvm_dict_merge as *const () as u64, + "kclvm_dict_remove" => crate::kclvm_dict_remove as *const () as u64, + "kclvm_dict_safe_insert" => crate::kclvm_dict_safe_insert as *const () as u64, + "kclvm_dict_set_value" => crate::kclvm_dict_set_value as *const () as u64, + "kclvm_dict_update" => crate::kclvm_dict_update as *const () as u64, + "kclvm_dict_update_key_value" => crate::kclvm_dict_update_key_value as *const () as u64, + "kclvm_dict_values" => crate::kclvm_dict_values as *const () as u64, + "kclvm_free" => crate::kclvm_free as *const () as u64, + "kclvm_iterator_cur_key" => crate::kclvm_iterator_cur_key as *const () as u64, + "kclvm_iterator_cur_value" => crate::kclvm_iterator_cur_value as *const () as u64, + "kclvm_iterator_delete" => crate::kclvm_iterator_delete as *const () as u64, + "kclvm_iterator_is_end" => crate::kclvm_iterator_is_end as *const () as u64, + "kclvm_iterator_next_value" => crate::kclvm_iterator_next_value as *const () as u64, + "kclvm_json_decode" => crate::kclvm_json_decode as *const () as u64, + "kclvm_json_dump_to_file" => crate::kclvm_json_dump_to_file as *const () as u64, + "kclvm_json_encode" => crate::kclvm_json_encode as *const () as u64, + "kclvm_list_append" => crate::kclvm_list_append as *const () as u64, + "kclvm_list_append_bool" => crate::kclvm_list_append_bool as *const () as u64, + "kclvm_list_append_float" => crate::kclvm_list_append_float as *const () as u64, + "kclvm_list_append_int" => crate::kclvm_list_append_int as *const () as u64, + "kclvm_list_append_str" => crate::kclvm_list_append_str as *const () as u64, + "kclvm_list_append_unpack" => crate::kclvm_list_append_unpack as *const () as u64, + "kclvm_list_clear" => crate::kclvm_list_clear as *const () as u64, + "kclvm_list_count" => crate::kclvm_list_count as *const () as u64, + "kclvm_list_find" => crate::kclvm_list_find as *const () as u64, + "kclvm_list_get" => crate::kclvm_list_get as *const () as u64, + "kclvm_list_get_option" => crate::kclvm_list_get_option as *const () as u64, + "kclvm_list_insert" => crate::kclvm_list_insert as *const () as u64, + "kclvm_list_len" => crate::kclvm_list_len as *const () as u64, + "kclvm_list_pop" => crate::kclvm_list_pop as *const () as u64, + "kclvm_list_pop_first" => crate::kclvm_list_pop_first as *const () as u64, + "kclvm_list_remove_at" => crate::kclvm_list_remove_at as *const () as u64, + "kclvm_list_resize" => crate::kclvm_list_resize as *const () as u64, + "kclvm_list_set" => crate::kclvm_list_set as *const () as u64, + "kclvm_malloc" => crate::kclvm_malloc as *const () as u64, + "kclvm_math_ceil" => crate::kclvm_math_ceil as *const () as u64, + "kclvm_math_exp" => crate::kclvm_math_exp as *const () as u64, + "kclvm_math_expm1" => crate::kclvm_math_expm1 as *const () as u64, + "kclvm_math_factorial" => crate::kclvm_math_factorial as *const () as u64, + "kclvm_math_floor" => crate::kclvm_math_floor as *const () as u64, + "kclvm_math_gcd" => crate::kclvm_math_gcd as *const () as u64, + "kclvm_math_isfinite" => crate::kclvm_math_isfinite as *const () as u64, + "kclvm_math_isinf" => crate::kclvm_math_isinf as *const () as u64, + "kclvm_math_isnan" => crate::kclvm_math_isnan as *const () as u64, + "kclvm_math_log" => crate::kclvm_math_log as *const () as u64, + "kclvm_math_log10" => crate::kclvm_math_log10 as *const () as u64, + "kclvm_math_log1p" => crate::kclvm_math_log1p as *const () as u64, + "kclvm_math_log2" => crate::kclvm_math_log2 as *const () as u64, + "kclvm_math_modf" => crate::kclvm_math_modf as *const () as u64, + "kclvm_math_pow" => crate::kclvm_math_pow as *const () as u64, + "kclvm_math_sqrt" => crate::kclvm_math_sqrt as *const () as u64, + "kclvm_net_IP_string" => crate::kclvm_net_IP_string as *const () as u64, + "kclvm_net_fqdn" => crate::kclvm_net_fqdn as *const () as u64, + "kclvm_net_is_IP" => crate::kclvm_net_is_IP as *const () as u64, + "kclvm_net_is_IPv4" => crate::kclvm_net_is_IPv4 as *const () as u64, + "kclvm_net_is_global_unicast_IP" => { + crate::kclvm_net_is_global_unicast_IP as *const () as u64 + } + "kclvm_net_is_interface_local_multicast_IP" => { + crate::kclvm_net_is_interface_local_multicast_IP as *const () as u64 + } + "kclvm_net_is_link_local_multicast_IP" => { + crate::kclvm_net_is_link_local_multicast_IP as *const () as u64 + } + "kclvm_net_is_link_local_unicast_IP" => { + crate::kclvm_net_is_link_local_unicast_IP as *const () as u64 + } + "kclvm_net_is_loopback_IP" => crate::kclvm_net_is_loopback_IP as *const () as u64, + "kclvm_net_is_multicast_IP" => crate::kclvm_net_is_multicast_IP as *const () as u64, + "kclvm_net_is_unspecified_IP" => crate::kclvm_net_is_unspecified_IP as *const () as u64, + "kclvm_net_join_host_port" => crate::kclvm_net_join_host_port as *const () as u64, + "kclvm_net_parse_IP" => crate::kclvm_net_parse_IP as *const () as u64, + "kclvm_net_split_host_port" => crate::kclvm_net_split_host_port as *const () as u64, + "kclvm_net_to_IP16" => crate::kclvm_net_to_IP16 as *const () as u64, + "kclvm_net_to_IP4" => crate::kclvm_net_to_IP4 as *const () as u64, + "kclvm_plugin_init" => crate::kclvm_plugin_init as *const () as u64, + "kclvm_plugin_invoke" => crate::kclvm_plugin_invoke as *const () as u64, + "kclvm_plugin_invoke_json" => crate::kclvm_plugin_invoke_json as *const () as u64, + "kclvm_regex_compile" => crate::kclvm_regex_compile as *const () as u64, + "kclvm_regex_findall" => crate::kclvm_regex_findall as *const () as u64, + "kclvm_regex_match" => crate::kclvm_regex_match as *const () as u64, + "kclvm_regex_replace" => crate::kclvm_regex_replace as *const () as u64, + "kclvm_regex_search" => crate::kclvm_regex_search as *const () as u64, + "kclvm_regex_split" => crate::kclvm_regex_split as *const () as u64, + "kclvm_schema_assert" => crate::kclvm_schema_assert as *const () as u64, + "kclvm_schema_backtrack_cache" => crate::kclvm_schema_backtrack_cache as *const () as u64, + "kclvm_schema_default_settings" => crate::kclvm_schema_default_settings as *const () as u64, + "kclvm_schema_do_check_with_index_sign_attr" => { + crate::kclvm_schema_do_check_with_index_sign_attr as *const () as u64 + } + "kclvm_schema_get_value" => crate::kclvm_schema_get_value as *const () as u64, + "kclvm_schema_instances" => crate::kclvm_schema_instances as *const () as u64, + "kclvm_schema_optional_check" => crate::kclvm_schema_optional_check as *const () as u64, + "kclvm_schema_value_check" => crate::kclvm_schema_value_check as *const () as u64, + "kclvm_schema_value_new" => crate::kclvm_schema_value_new as *const () as u64, + "kclvm_strlen" => crate::kclvm_strlen as *const () as u64, + "kclvm_testing_arguments" => crate::kclvm_testing_arguments as *const () as u64, + "kclvm_testing_setting_file" => crate::kclvm_testing_setting_file as *const () as u64, + "kclvm_type_Any" => crate::kclvm_type_Any as *const () as u64, + "kclvm_type_Bool" => crate::kclvm_type_Bool as *const () as u64, + "kclvm_type_BoolLit" => crate::kclvm_type_BoolLit as *const () as u64, + "kclvm_type_BoolLit_value" => crate::kclvm_type_BoolLit_value as *const () as u64, + "kclvm_type_Dict" => crate::kclvm_type_Dict as *const () as u64, + "kclvm_type_Float" => crate::kclvm_type_Float as *const () as u64, + "kclvm_type_FloatLit" => crate::kclvm_type_FloatLit as *const () as u64, + "kclvm_type_FloatLit_value" => crate::kclvm_type_FloatLit_value as *const () as u64, + "kclvm_type_Func" => crate::kclvm_type_Func as *const () as u64, + "kclvm_type_Int" => crate::kclvm_type_Int as *const () as u64, + "kclvm_type_IntLit" => crate::kclvm_type_IntLit as *const () as u64, + "kclvm_type_IntLit_value" => crate::kclvm_type_IntLit_value as *const () as u64, + "kclvm_type_List" => crate::kclvm_type_List as *const () as u64, + "kclvm_type_Schema" => crate::kclvm_type_Schema as *const () as u64, + "kclvm_type_Str" => crate::kclvm_type_Str as *const () as u64, + "kclvm_type_StrLit" => crate::kclvm_type_StrLit as *const () as u64, + "kclvm_type_StrLit_value" => crate::kclvm_type_StrLit_value as *const () as u64, + "kclvm_type_Union" => crate::kclvm_type_Union as *const () as u64, + "kclvm_type_arg_num" => crate::kclvm_type_arg_num as *const () as u64, + "kclvm_type_arg_type" => crate::kclvm_type_arg_type as *const () as u64, + "kclvm_type_delete" => crate::kclvm_type_delete as *const () as u64, + "kclvm_type_elem_type" => crate::kclvm_type_elem_type as *const () as u64, + "kclvm_type_key_type" => crate::kclvm_type_key_type as *const () as u64, + "kclvm_type_kind" => crate::kclvm_type_kind as *const () as u64, + "kclvm_type_return_type" => crate::kclvm_type_return_type as *const () as u64, + "kclvm_type_schema_field_name" => crate::kclvm_type_schema_field_name as *const () as u64, + "kclvm_type_schema_field_num" => crate::kclvm_type_schema_field_num as *const () as u64, + "kclvm_type_schema_field_type" => crate::kclvm_type_schema_field_type as *const () as u64, + "kclvm_type_schema_name" => crate::kclvm_type_schema_name as *const () as u64, + "kclvm_type_schema_parent_name" => crate::kclvm_type_schema_parent_name as *const () as u64, + "kclvm_type_schema_relaxed" => crate::kclvm_type_schema_relaxed as *const () as u64, + "kclvm_type_str" => crate::kclvm_type_str as *const () as u64, + "kclvm_units_to_G" => crate::kclvm_units_to_G as *const () as u64, + "kclvm_units_to_Gi" => crate::kclvm_units_to_Gi as *const () as u64, + "kclvm_units_to_K" => crate::kclvm_units_to_K as *const () as u64, + "kclvm_units_to_Ki" => crate::kclvm_units_to_Ki as *const () as u64, + "kclvm_units_to_M" => crate::kclvm_units_to_M as *const () as u64, + "kclvm_units_to_Mi" => crate::kclvm_units_to_Mi as *const () as u64, + "kclvm_units_to_P" => crate::kclvm_units_to_P as *const () as u64, + "kclvm_units_to_Pi" => crate::kclvm_units_to_Pi as *const () as u64, + "kclvm_units_to_T" => crate::kclvm_units_to_T as *const () as u64, + "kclvm_units_to_Ti" => crate::kclvm_units_to_Ti as *const () as u64, + "kclvm_units_to_m" => crate::kclvm_units_to_m as *const () as u64, + "kclvm_units_to_n" => crate::kclvm_units_to_n as *const () as u64, + "kclvm_units_to_u" => crate::kclvm_units_to_u as *const () as u64, + "kclvm_value_Bool" => crate::kclvm_value_Bool as *const () as u64, + "kclvm_value_Bool_ptr" => crate::kclvm_value_Bool_ptr as *const () as u64, + "kclvm_value_Decorator" => crate::kclvm_value_Decorator as *const () as u64, + "kclvm_value_Dict" => crate::kclvm_value_Dict as *const () as u64, + "kclvm_value_False" => crate::kclvm_value_False as *const () as u64, + "kclvm_value_Float" => crate::kclvm_value_Float as *const () as u64, + "kclvm_value_Float_ptr" => crate::kclvm_value_Float_ptr as *const () as u64, + "kclvm_value_Function" => crate::kclvm_value_Function as *const () as u64, + "kclvm_value_Function_using_ptr" => { + crate::kclvm_value_Function_using_ptr as *const () as u64 + } + "kclvm_value_Int" => crate::kclvm_value_Int as *const () as u64, + "kclvm_value_Int_ptr" => crate::kclvm_value_Int_ptr as *const () as u64, + "kclvm_value_List" => crate::kclvm_value_List as *const () as u64, + "kclvm_value_List10" => crate::kclvm_value_List10 as *const () as u64, + "kclvm_value_List6" => crate::kclvm_value_List6 as *const () as u64, + "kclvm_value_ListN" => crate::kclvm_value_ListN as *const () as u64, + "kclvm_value_None" => crate::kclvm_value_None as *const () as u64, + "kclvm_value_Schema" => crate::kclvm_value_Schema as *const () as u64, + "kclvm_value_Str" => crate::kclvm_value_Str as *const () as u64, + "kclvm_value_Str_len" => crate::kclvm_value_Str_len as *const () as u64, + "kclvm_value_Str_ptr" => crate::kclvm_value_Str_ptr as *const () as u64, + "kclvm_value_Str_resize" => crate::kclvm_value_Str_resize as *const () as u64, + "kclvm_value_True" => crate::kclvm_value_True as *const () as u64, + "kclvm_value_Undefined" => crate::kclvm_value_Undefined as *const () as u64, + "kclvm_value_Unit" => crate::kclvm_value_Unit as *const () as u64, + "kclvm_value_as" => crate::kclvm_value_as as *const () as u64, + "kclvm_value_check_function_ptr" => { + crate::kclvm_value_check_function_ptr as *const () as u64 + } + "kclvm_value_cmp_equal_to" => crate::kclvm_value_cmp_equal_to as *const () as u64, + "kclvm_value_cmp_greater_than" => crate::kclvm_value_cmp_greater_than as *const () as u64, + "kclvm_value_cmp_greater_than_or_equal" => { + crate::kclvm_value_cmp_greater_than_or_equal as *const () as u64 + } + "kclvm_value_cmp_less_than" => crate::kclvm_value_cmp_less_than as *const () as u64, + "kclvm_value_cmp_less_than_or_equal" => { + crate::kclvm_value_cmp_less_than_or_equal as *const () as u64 + } + "kclvm_value_cmp_not_equal_to" => crate::kclvm_value_cmp_not_equal_to as *const () as u64, + "kclvm_value_deep_copy" => crate::kclvm_value_deep_copy as *const () as u64, + "kclvm_value_delete" => crate::kclvm_value_delete as *const () as u64, + "kclvm_value_from_json" => crate::kclvm_value_from_json as *const () as u64, + "kclvm_value_function_external_invoke" => { + crate::kclvm_value_function_external_invoke as *const () as u64 + } + "kclvm_value_function_get_closure" => { + crate::kclvm_value_function_get_closure as *const () as u64 + } + "kclvm_value_function_invoke" => crate::kclvm_value_function_invoke as *const () as u64, + "kclvm_value_function_is_external" => { + crate::kclvm_value_function_is_external as *const () as u64 + } + "kclvm_value_function_ptr" => crate::kclvm_value_function_ptr as *const () as u64, + "kclvm_value_in" => crate::kclvm_value_in as *const () as u64, + "kclvm_value_is" => crate::kclvm_value_is as *const () as u64, + "kclvm_value_is_not" => crate::kclvm_value_is_not as *const () as u64, + "kclvm_value_is_truthy" => crate::kclvm_value_is_truthy as *const () as u64, + "kclvm_value_iter" => crate::kclvm_value_iter as *const () as u64, + "kclvm_value_kind" => crate::kclvm_value_kind as *const () as u64, + "kclvm_value_len" => crate::kclvm_value_len as *const () as u64, + "kclvm_value_load_attr" => crate::kclvm_value_load_attr as *const () as u64, + "kclvm_value_load_attr_option" => crate::kclvm_value_load_attr_option as *const () as u64, + "kclvm_value_logic_and" => crate::kclvm_value_logic_and as *const () as u64, + "kclvm_value_logic_or" => crate::kclvm_value_logic_or as *const () as u64, + "kclvm_value_not_in" => crate::kclvm_value_not_in as *const () as u64, + "kclvm_value_op_add" => crate::kclvm_value_op_add as *const () as u64, + "kclvm_value_op_aug_add" => crate::kclvm_value_op_aug_add as *const () as u64, + "kclvm_value_op_aug_bit_and" => crate::kclvm_value_op_aug_bit_and as *const () as u64, + "kclvm_value_op_aug_bit_lshift" => crate::kclvm_value_op_aug_bit_lshift as *const () as u64, + "kclvm_value_op_aug_bit_or" => crate::kclvm_value_op_aug_bit_or as *const () as u64, + "kclvm_value_op_aug_bit_rshift" => crate::kclvm_value_op_aug_bit_rshift as *const () as u64, + "kclvm_value_op_aug_bit_xor" => crate::kclvm_value_op_aug_bit_xor as *const () as u64, + "kclvm_value_op_aug_div" => crate::kclvm_value_op_aug_div as *const () as u64, + "kclvm_value_op_aug_floor_div" => crate::kclvm_value_op_aug_floor_div as *const () as u64, + "kclvm_value_op_aug_mod" => crate::kclvm_value_op_aug_mod as *const () as u64, + "kclvm_value_op_aug_mul" => crate::kclvm_value_op_aug_mul as *const () as u64, + "kclvm_value_op_aug_pow" => crate::kclvm_value_op_aug_pow as *const () as u64, + "kclvm_value_op_aug_sub" => crate::kclvm_value_op_aug_sub as *const () as u64, + "kclvm_value_op_bit_and" => crate::kclvm_value_op_bit_and as *const () as u64, + "kclvm_value_op_bit_lshift" => crate::kclvm_value_op_bit_lshift as *const () as u64, + "kclvm_value_op_bit_or" => crate::kclvm_value_op_bit_or as *const () as u64, + "kclvm_value_op_bit_rshift" => crate::kclvm_value_op_bit_rshift as *const () as u64, + "kclvm_value_op_bit_xor" => crate::kclvm_value_op_bit_xor as *const () as u64, + "kclvm_value_op_div" => crate::kclvm_value_op_div as *const () as u64, + "kclvm_value_op_floor_div" => crate::kclvm_value_op_floor_div as *const () as u64, + "kclvm_value_op_mod" => crate::kclvm_value_op_mod as *const () as u64, + "kclvm_value_op_mul" => crate::kclvm_value_op_mul as *const () as u64, + "kclvm_value_op_pow" => crate::kclvm_value_op_pow as *const () as u64, + "kclvm_value_op_sub" => crate::kclvm_value_op_sub as *const () as u64, + "kclvm_value_plan_to_json" => crate::kclvm_value_plan_to_json as *const () as u64, + "kclvm_value_plan_to_yaml" => crate::kclvm_value_plan_to_yaml as *const () as u64, + "kclvm_value_remove_item" => crate::kclvm_value_remove_item as *const () as u64, + "kclvm_value_schema_function" => crate::kclvm_value_schema_function as *const () as u64, + "kclvm_value_schema_with_config" => { + crate::kclvm_value_schema_with_config as *const () as u64 + } + "kclvm_value_slice" => crate::kclvm_value_slice as *const () as u64, + "kclvm_value_slice_option" => crate::kclvm_value_slice_option as *const () as u64, + "kclvm_value_subscr" => crate::kclvm_value_subscr as *const () as u64, + "kclvm_value_subscr_option" => crate::kclvm_value_subscr_option as *const () as u64, + "kclvm_value_to_json" => crate::kclvm_value_to_json as *const () as u64, + "kclvm_value_to_json_value" => crate::kclvm_value_to_json_value as *const () as u64, + "kclvm_value_to_json_value_with_null" => { + crate::kclvm_value_to_json_value_with_null as *const () as u64 + } + "kclvm_value_to_str_value" => crate::kclvm_value_to_str_value as *const () as u64, + "kclvm_value_to_yaml_value" => crate::kclvm_value_to_yaml_value as *const () as u64, + "kclvm_value_unary_l_not" => crate::kclvm_value_unary_l_not as *const () as u64, + "kclvm_value_unary_minus" => crate::kclvm_value_unary_minus as *const () as u64, + "kclvm_value_unary_not" => crate::kclvm_value_unary_not as *const () as u64, + "kclvm_value_unary_plus" => crate::kclvm_value_unary_plus as *const () as u64, + "kclvm_value_union" => crate::kclvm_value_union as *const () as u64, + "kclvm_value_union_all" => crate::kclvm_value_union_all as *const () as u64, + "kclvm_yaml_decode" => crate::kclvm_yaml_decode as *const () as u64, + "kclvm_yaml_dump_to_file" => crate::kclvm_yaml_dump_to_file as *const () as u64, + "kclvm_yaml_encode" => crate::kclvm_yaml_encode as *const () as u64, + _ => panic!("unknown {}", name), + } +} diff --git a/kclvm/runtime/src/_kclvm_api_spec.rs b/kclvm/runtime/src/_kclvm_api_spec.rs new file mode 100644 index 000000000..87f041ab9 --- /dev/null +++ b/kclvm/runtime/src/_kclvm_api_spec.rs @@ -0,0 +1,1416 @@ +// Copyright 2022 The KCL Authors. All rights reserved. + +// Auto generated by command, DONOT EDIT!!! + +// api-spec: kclvm_buffer_new +// api-spec(c): kclvm_buffer_t* kclvm_buffer_new(kclvm_size_t size); +// api-spec(llvm): declare %kclvm_buffer_t* @kclvm_buffer_new(%kclvm_size_t %size); + +// api-spec: kclvm_buffer_delete +// api-spec(c): void kclvm_buffer_delete(kclvm_buffer_t* p); +// api-spec(llvm): declare void @kclvm_buffer_delete(%kclvm_buffer_t* %p); + +// api-spec: kclvm_buffer_size +// api-spec(c): kclvm_size_t kclvm_buffer_size(kclvm_buffer_t* p); +// api-spec(llvm): declare %kclvm_size_t @kclvm_buffer_size(%kclvm_buffer_t* %p); + +// api-spec: kclvm_buffer_data +// api-spec(c): kclvm_char_t* kclvm_buffer_data(kclvm_buffer_t* p); +// api-spec(llvm): declare %kclvm_char_t* @kclvm_buffer_data(%kclvm_buffer_t* %p); + +// api-spec: kclvm_malloc +// api-spec(c): uint8_t* kclvm_malloc(int32_t n); +// api-spec(llvm): declare i8* @kclvm_malloc(i32 %n); + +// api-spec: kclvm_free +// api-spec(c): void kclvm_free(uint8_t* ptr); +// api-spec(llvm): declare void @kclvm_free(i8* %ptr); + +// api-spec: kclvm_strlen +// api-spec(c): kclvm_size_t kclvm_strlen(uint8_t* ptr); +// api-spec(llvm): declare %kclvm_size_t @kclvm_strlen(i8* %ptr); + +// api-spec: kclvm_context_current +// api-spec(c): kclvm_context_t* kclvm_context_current(); +// api-spec(llvm): declare %kclvm_context_t* @kclvm_context_current(); + +// api-spec: kclvm_context_new +// api-spec(c): kclvm_context_t* kclvm_context_new(); +// api-spec(llvm): declare %kclvm_context_t* @kclvm_context_new(); + +// api-spec: kclvm_context_delete +// api-spec(c): void kclvm_context_delete(kclvm_context_t* p); +// api-spec(llvm): declare void @kclvm_context_delete(%kclvm_context_t* %p); + +// api-spec: kclvm_context_main_begin_hook +// api-spec(c): void kclvm_context_main_begin_hook(kclvm_context_t* p); +// api-spec(llvm): declare void @kclvm_context_main_begin_hook(%kclvm_context_t* %p); + +// api-spec: kclvm_context_main_end_hook +// api-spec(c): kclvm_value_ref_t* kclvm_context_main_end_hook(kclvm_context_t* p, kclvm_value_ref_t* return_value); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_context_main_end_hook(%kclvm_context_t* %p, %kclvm_value_ref_t* %return_value); + +// api-spec: kclvm_context_set_kcl_location +// api-spec(c): void kclvm_context_set_kcl_location(kclvm_context_t* p, int8_t* filename, int32_t line, int32_t col); +// api-spec(llvm): declare void @kclvm_context_set_kcl_location(%kclvm_context_t* %p, i8* %filename, i32 %line, i32 %col); + +// api-spec: kclvm_context_set_kcl_pkgpath +// api-spec(c): void kclvm_context_set_kcl_pkgpath(kclvm_context_t* p, int8_t* pkgpath); +// api-spec(llvm): declare void @kclvm_context_set_kcl_pkgpath(%kclvm_context_t* %p, i8* %pkgpath); + +// api-spec: kclvm_context_set_kcl_filename +// api-spec(c): void kclvm_context_set_kcl_filename(int8_t* filename); +// api-spec(llvm): declare void @kclvm_context_set_kcl_filename(i8* %filename); + +// api-spec: kclvm_context_set_kcl_line_col +// api-spec(c): void kclvm_context_set_kcl_line_col(int32_t line, int32_t col); +// api-spec(llvm): declare void @kclvm_context_set_kcl_line_col(i32 %line, i32 %col); + +// api-spec: kclvm_context_put_type +// api-spec(c): void kclvm_context_put_type(kclvm_context_t* p, kclvm_type_t* typ); +// api-spec(llvm): declare void @kclvm_context_put_type(%kclvm_context_t* %p, %kclvm_type_t* %typ); + +// api-spec: kclvm_context_clear_all_types +// api-spec(c): void kclvm_context_clear_all_types(kclvm_context_t* p); +// api-spec(llvm): declare void @kclvm_context_clear_all_types(%kclvm_context_t* %p); + +// api-spec: kclvm_context_symbol_init +// api-spec(c): void kclvm_context_symbol_init(kclvm_context_t* p, kclvm_size_t n, kclvm_char_t** symbol_names); +// api-spec(llvm): declare void @kclvm_context_symbol_init(%kclvm_context_t* %p, %kclvm_size_t %n, %kclvm_char_t** %symbol_names); + +// api-spec: kclvm_context_symbol_num +// api-spec(c): kclvm_size_t kclvm_context_symbol_num(kclvm_context_t* p); +// api-spec(llvm): declare %kclvm_size_t @kclvm_context_symbol_num(%kclvm_context_t* %p); + +// api-spec: kclvm_context_symbol_name +// api-spec(c): kclvm_char_t* kclvm_context_symbol_name(kclvm_context_t* p, kclvm_size_t i); +// api-spec(llvm): declare %kclvm_char_t* @kclvm_context_symbol_name(%kclvm_context_t* %p, %kclvm_size_t %i); + +// api-spec: kclvm_context_symbol_value +// api-spec(c): kclvm_value_t* kclvm_context_symbol_value(kclvm_context_t* p, kclvm_size_t i); +// api-spec(llvm): declare %kclvm_value_t* @kclvm_context_symbol_value(%kclvm_context_t* %p, %kclvm_size_t %i); + +// api-spec: kclvm_context_args_get +// api-spec(c): kclvm_char_t* kclvm_context_args_get(kclvm_context_t* _p, kclvm_char_t* _key); +// api-spec(llvm): declare %kclvm_char_t* @kclvm_context_args_get(%kclvm_context_t* %_p, %kclvm_char_t* %_key); + +// api-spec: kclvm_context_args_set +// api-spec(c): void kclvm_context_args_set(kclvm_context_t* _p, kclvm_char_t* _key, kclvm_char_t* _value); +// api-spec(llvm): declare void @kclvm_context_args_set(%kclvm_context_t* %_p, %kclvm_char_t* %_key, %kclvm_char_t* %_value); + +// api-spec: kclvm_context_args_clear +// api-spec(c): void kclvm_context_args_clear(kclvm_context_t* p); +// api-spec(llvm): declare void @kclvm_context_args_clear(%kclvm_context_t* %p); + +// api-spec: kclvm_context_set_debug_mode +// api-spec(c): void kclvm_context_set_debug_mode(kclvm_context_t* p, kclvm_bool_t v); +// api-spec(llvm): declare void @kclvm_context_set_debug_mode(%kclvm_context_t* %p, %kclvm_bool_t %v); + +// api-spec: kclvm_context_set_strict_range_check +// api-spec(c): void kclvm_context_set_strict_range_check(kclvm_context_t* p, kclvm_bool_t v); +// api-spec(llvm): declare void @kclvm_context_set_strict_range_check(%kclvm_context_t* %p, %kclvm_bool_t %v); + +// api-spec: kclvm_context_set_disable_none +// api-spec(c): void kclvm_context_set_disable_none(kclvm_context_t* p, kclvm_bool_t v); +// api-spec(llvm): declare void @kclvm_context_set_disable_none(%kclvm_context_t* %p, %kclvm_bool_t %v); + +// api-spec: kclvm_context_set_disable_schema_check +// api-spec(c): void kclvm_context_set_disable_schema_check(kclvm_context_t* p, kclvm_bool_t v); +// api-spec(llvm): declare void @kclvm_context_set_disable_schema_check(%kclvm_context_t* %p, %kclvm_bool_t %v); + +// api-spec: kclvm_context_set_list_option_mode +// api-spec(c): void kclvm_context_set_list_option_mode(kclvm_context_t* p, kclvm_bool_t v); +// api-spec(llvm): declare void @kclvm_context_set_list_option_mode(%kclvm_context_t* %p, %kclvm_bool_t %v); + +// api-spec: kclvm_context_invoke +// api-spec(c): char* kclvm_context_invoke(kclvm_context_t* p, char* method, char* args, char* kwargs); +// api-spec(llvm): declare i8* @kclvm_context_invoke(%kclvm_context_t* %p, i8* %method, i8* %args, i8* %kwargs); + +// api-spec: kclvm_context_pkgpath_is_imported +// api-spec(c): kclvm_bool_t kclvm_context_pkgpath_is_imported(kclvm_char_t* pkgpath); +// api-spec(llvm): declare %kclvm_bool_t @kclvm_context_pkgpath_is_imported(%kclvm_char_t* %pkgpath); + +// api-spec: kclvm_type_Any +// api-spec(c): kclvm_type_t* kclvm_type_Any(); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_Any(); + +// api-spec: kclvm_type_Bool +// api-spec(c): kclvm_type_t* kclvm_type_Bool(); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_Bool(); + +// api-spec: kclvm_type_BoolLit +// api-spec(c): kclvm_type_t* kclvm_type_BoolLit(kclvm_bool_t v); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_BoolLit(%kclvm_bool_t %v); + +// api-spec: kclvm_type_Int +// api-spec(c): kclvm_type_t* kclvm_type_Int(); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_Int(); + +// api-spec: kclvm_type_IntLit +// api-spec(c): kclvm_type_t* kclvm_type_IntLit(int64_t v); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_IntLit(i64 %v); + +// api-spec: kclvm_type_Float +// api-spec(c): kclvm_type_t* kclvm_type_Float(); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_Float(); + +// api-spec: kclvm_type_FloatLit +// api-spec(c): kclvm_type_t* kclvm_type_FloatLit(double v); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_FloatLit(double %v); + +// api-spec: kclvm_type_Str +// api-spec(c): kclvm_type_t* kclvm_type_Str(); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_Str(); + +// api-spec: kclvm_type_StrLit +// api-spec(c): kclvm_type_t* kclvm_type_StrLit(kclvm_char_t* s); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_StrLit(%kclvm_char_t* %s); + +// api-spec: kclvm_type_List +// api-spec(c): kclvm_type_t* kclvm_type_List(kclvm_type_t* elem_type); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_List(%kclvm_type_t* %elem_type); + +// api-spec: kclvm_type_Dict +// api-spec(c): kclvm_type_t* kclvm_type_Dict(kclvm_type_t* key_type, kclvm_type_t* elem_type); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_Dict(%kclvm_type_t* %key_type, %kclvm_type_t* %elem_type); + +// api-spec: kclvm_type_Union +// api-spec(c): kclvm_type_t* kclvm_type_Union(kclvm_size_t n, kclvm_type_t** elem_types); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_Union(%kclvm_size_t %n, %kclvm_type_t** %elem_types); + +// api-spec: kclvm_type_Schema +// api-spec(c): kclvm_type_t* kclvm_type_Schema(kclvm_char_t* name, kclvm_char_t* parent_name, kclvm_bool_t _is_relaxed, kclvm_size_t field_num, kclvm_char_t** field_names, kclvm_type_t** field_types); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_Schema(%kclvm_char_t* %name, %kclvm_char_t* %parent_name, %kclvm_bool_t %_is_relaxed, %kclvm_size_t %field_num, %kclvm_char_t** %field_names, %kclvm_type_t** %field_types); + +// api-spec: kclvm_type_Func +// api-spec(c): kclvm_type_t* kclvm_type_Func(kclvm_size_t args_len, kclvm_type_t** args_types, kclvm_type_t* return_type); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_Func(%kclvm_size_t %args_len, %kclvm_type_t** %args_types, %kclvm_type_t* %return_type); + +// api-spec: kclvm_type_delete +// api-spec(c): void kclvm_type_delete(kclvm_type_t* p); +// api-spec(llvm): declare void @kclvm_type_delete(%kclvm_type_t* %p); + +// api-spec: kclvm_type_kind +// api-spec(c): kclvm_kind_t kclvm_type_kind(kclvm_type_t* p); +// api-spec(llvm): declare %kclvm_kind_t @kclvm_type_kind(%kclvm_type_t* %p); + +// api-spec: kclvm_type_str +// api-spec(c): kclvm_kind_t kclvm_type_str(kclvm_type_t* p); +// api-spec(llvm): declare %kclvm_kind_t @kclvm_type_str(%kclvm_type_t* %p); + +// api-spec: kclvm_type_BoolLit_value +// api-spec(c): kclvm_bool_t kclvm_type_BoolLit_value(kclvm_type_t* p); +// api-spec(llvm): declare %kclvm_bool_t @kclvm_type_BoolLit_value(%kclvm_type_t* %p); + +// api-spec: kclvm_type_IntLit_value +// api-spec(c): int64_t kclvm_type_IntLit_value(kclvm_type_t* p); +// api-spec(llvm): declare i64 @kclvm_type_IntLit_value(%kclvm_type_t* %p); + +// api-spec: kclvm_type_FloatLit_value +// api-spec(c): double kclvm_type_FloatLit_value(kclvm_type_t* p); +// api-spec(llvm): declare double @kclvm_type_FloatLit_value(%kclvm_type_t* %p); + +// api-spec: kclvm_type_StrLit_value +// api-spec(c): kclvm_char_t* kclvm_type_StrLit_value(kclvm_type_t* p); +// api-spec(llvm): declare %kclvm_char_t* @kclvm_type_StrLit_value(%kclvm_type_t* %p); + +// api-spec: kclvm_type_key_type +// api-spec(c): kclvm_type_t* kclvm_type_key_type(kclvm_type_t* p); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_key_type(%kclvm_type_t* %p); + +// api-spec: kclvm_type_elem_type +// api-spec(c): kclvm_type_t* kclvm_type_elem_type(kclvm_type_t* p); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_elem_type(%kclvm_type_t* %p); + +// api-spec: kclvm_type_schema_name +// api-spec(c): kclvm_char_t* kclvm_type_schema_name(kclvm_type_t* p); +// api-spec(llvm): declare %kclvm_char_t* @kclvm_type_schema_name(%kclvm_type_t* %p); + +// api-spec: kclvm_type_schema_parent_name +// api-spec(c): kclvm_char_t* kclvm_type_schema_parent_name(kclvm_type_t* p); +// api-spec(llvm): declare %kclvm_char_t* @kclvm_type_schema_parent_name(%kclvm_type_t* %p); + +// api-spec: kclvm_type_schema_relaxed +// api-spec(c): kclvm_bool_t kclvm_type_schema_relaxed(kclvm_type_t* p); +// api-spec(llvm): declare %kclvm_bool_t @kclvm_type_schema_relaxed(%kclvm_type_t* %p); + +// api-spec: kclvm_type_schema_field_num +// api-spec(c): kclvm_size_t kclvm_type_schema_field_num(kclvm_type_t* p); +// api-spec(llvm): declare %kclvm_size_t @kclvm_type_schema_field_num(%kclvm_type_t* %p); + +// api-spec: kclvm_type_schema_field_name +// api-spec(c): kclvm_char_t* kclvm_type_schema_field_name(kclvm_type_t* p, kclvm_size_t i); +// api-spec(llvm): declare %kclvm_char_t* @kclvm_type_schema_field_name(%kclvm_type_t* %p, %kclvm_size_t %i); + +// api-spec: kclvm_type_schema_field_type +// api-spec(c): kclvm_type_t* kclvm_type_schema_field_type(kclvm_type_t* p, kclvm_size_t i); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_schema_field_type(%kclvm_type_t* %p, %kclvm_size_t %i); + +// api-spec: kclvm_type_arg_num +// api-spec(c): kclvm_size_t kclvm_type_arg_num(kclvm_type_t* p); +// api-spec(llvm): declare %kclvm_size_t @kclvm_type_arg_num(%kclvm_type_t* %p); + +// api-spec: kclvm_type_arg_type +// api-spec(c): kclvm_type_t* kclvm_type_arg_type(kclvm_type_t* p, kclvm_size_t i); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_arg_type(%kclvm_type_t* %p, %kclvm_size_t %i); + +// api-spec: kclvm_type_return_type +// api-spec(c): kclvm_type_t* kclvm_type_return_type(kclvm_type_t* p); +// api-spec(llvm): declare %kclvm_type_t* @kclvm_type_return_type(%kclvm_type_t* %p); + +// api-spec: kclvm_context_set_import_names +// api-spec(c): void kclvm_context_set_import_names(kclvm_context_t* p, kclvm_value_ref_t* import_names); +// api-spec(llvm): declare void @kclvm_context_set_import_names(%kclvm_context_t* %p, %kclvm_value_ref_t* %import_names); + +// api-spec: kclvm_value_Undefined +// api-spec(c): kclvm_value_ref_t* kclvm_value_Undefined(); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Undefined(); + +// api-spec: kclvm_value_None +// api-spec(c): kclvm_value_ref_t* kclvm_value_None(); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_None(); + +// api-spec: kclvm_value_True +// api-spec(c): kclvm_value_ref_t* kclvm_value_True(); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_True(); + +// api-spec: kclvm_value_False +// api-spec(c): kclvm_value_ref_t* kclvm_value_False(); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_False(); + +// api-spec: kclvm_value_Bool +// api-spec(c): kclvm_value_ref_t* kclvm_value_Bool(kclvm_bool_t v); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Bool(%kclvm_bool_t %v); + +// api-spec: kclvm_value_Int +// api-spec(c): kclvm_value_ref_t* kclvm_value_Int(kclvm_int_t v); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Int(%kclvm_int_t %v); + +// api-spec: kclvm_value_Float +// api-spec(c): kclvm_value_ref_t* kclvm_value_Float(kclvm_float_t v); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Float(%kclvm_float_t %v); + +// api-spec: kclvm_value_Unit +// api-spec(c): kclvm_value_ref_t* kclvm_value_Unit(kclvm_float_t v, kclvm_int_t raw, kclvm_char_t* unit); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Unit(%kclvm_float_t %v, %kclvm_int_t %raw, %kclvm_char_t* %unit); + +// api-spec: kclvm_value_Str +// api-spec(c): kclvm_value_ref_t* kclvm_value_Str(kclvm_char_t* v); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Str(%kclvm_char_t* %v); + +// api-spec: kclvm_value_List +// api-spec(c): kclvm_value_ref_t* kclvm_value_List(); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_List(); + +// api-spec: kclvm_value_List6 +// api-spec(c): kclvm_value_ref_t* kclvm_value_List6(kclvm_value_ref_t* v1, kclvm_value_ref_t* v2, kclvm_value_ref_t* v3, kclvm_value_ref_t* v4, kclvm_value_ref_t* v5, kclvm_value_ref_t* v6); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_List6(%kclvm_value_ref_t* %v1, %kclvm_value_ref_t* %v2, %kclvm_value_ref_t* %v3, %kclvm_value_ref_t* %v4, %kclvm_value_ref_t* %v5, %kclvm_value_ref_t* %v6); + +// api-spec: kclvm_value_List10 +// api-spec(c): kclvm_value_ref_t* kclvm_value_List10(kclvm_value_ref_t* v1, kclvm_value_ref_t* v2, kclvm_value_ref_t* v3, kclvm_value_ref_t* v4, kclvm_value_ref_t* v5, kclvm_value_ref_t* v6, kclvm_value_ref_t* v7, kclvm_value_ref_t* v8, kclvm_value_ref_t* v9, kclvm_value_ref_t* v10); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_List10(%kclvm_value_ref_t* %v1, %kclvm_value_ref_t* %v2, %kclvm_value_ref_t* %v3, %kclvm_value_ref_t* %v4, %kclvm_value_ref_t* %v5, %kclvm_value_ref_t* %v6, %kclvm_value_ref_t* %v7, %kclvm_value_ref_t* %v8, %kclvm_value_ref_t* %v9, %kclvm_value_ref_t* %v10); + +// api-spec: kclvm_value_ListN +// api-spec(c): kclvm_value_ref_t* kclvm_value_ListN(kclvm_int_t n, kclvm_value_ref_t** elem_values); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_ListN(%kclvm_int_t %n, %kclvm_value_ref_t** %elem_values); + +// api-spec: kclvm_value_Dict +// api-spec(c): kclvm_value_ref_t* kclvm_value_Dict(); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Dict(); + +// api-spec: kclvm_value_Schema +// api-spec(c): kclvm_value_ref_t* kclvm_value_Schema(); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Schema(); + +// api-spec: kclvm_value_schema_with_config +// api-spec(c): kclvm_value_ref_t* kclvm_value_schema_with_config(kclvm_value_ref_t* schema_dict, kclvm_value_ref_t* config, kclvm_char_t* name, kclvm_char_t* pkgpath, kclvm_value_ref_t* is_sub_schema, kclvm_value_ref_t* record_instance, kclvm_value_ref_t* instance_pkgpath); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_schema_with_config(%kclvm_value_ref_t* %schema_dict, %kclvm_value_ref_t* %config, %kclvm_char_t* %name, %kclvm_char_t* %pkgpath, %kclvm_value_ref_t* %is_sub_schema, %kclvm_value_ref_t* %record_instance, %kclvm_value_ref_t* %instance_pkgpath); + +// api-spec: kclvm_value_Function +// api-spec(c): kclvm_value_ref_t* kclvm_value_Function(uint64_t* fn_ptr, kclvm_value_ref_t* closure, kclvm_char_t* external_name); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Function(i64* %fn_ptr, %kclvm_value_ref_t* %closure, %kclvm_char_t* %external_name); + +// api-spec: kclvm_value_Function_using_ptr +// api-spec(c): kclvm_value_ref_t* kclvm_value_Function_using_ptr(uint64_t* fn_ptr); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Function_using_ptr(i64* %fn_ptr); + +// api-spec: kclvm_value_schema_function +// api-spec(c): kclvm_value_ref_t* kclvm_value_schema_function(uint64_t* fn_ptr, uint64_t* check_fn_ptr, kclvm_char_t* tpe); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_schema_function(i64* %fn_ptr, i64* %check_fn_ptr, %kclvm_char_t* %tpe); + +// api-spec: kclvm_value_from_json +// api-spec(c): kclvm_value_ref_t* kclvm_value_from_json(kclvm_char_t* s); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_from_json(%kclvm_char_t* %s); + +// api-spec: kclvm_value_to_json +// api-spec(c): kclvm_buffer_t* kclvm_value_to_json(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_buffer_t* @kclvm_value_to_json(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_to_json_value +// api-spec(c): kclvm_value_ref_t* kclvm_value_to_json_value(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_to_json_value(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_to_json_value_with_null +// api-spec(c): kclvm_value_ref_t* kclvm_value_to_json_value_with_null(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_to_json_value_with_null(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_plan_to_json +// api-spec(c): kclvm_value_ref_t* kclvm_value_plan_to_json(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_plan_to_json(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_plan_to_yaml +// api-spec(c): kclvm_value_ref_t* kclvm_value_plan_to_yaml(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_plan_to_yaml(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_to_yaml_value +// api-spec(c): kclvm_value_ref_t* kclvm_value_to_yaml_value(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_to_yaml_value(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_to_str_value +// api-spec(c): kclvm_value_ref_t* kclvm_value_to_str_value(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_to_str_value(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_Bool_ptr +// api-spec(c): kclvm_bool_t* kclvm_value_Bool_ptr(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_bool_t* @kclvm_value_Bool_ptr(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_Int_ptr +// api-spec(c): kclvm_int_t* kclvm_value_Int_ptr(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_int_t* @kclvm_value_Int_ptr(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_Float_ptr +// api-spec(c): kclvm_float_t* kclvm_value_Float_ptr(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_float_t* @kclvm_value_Float_ptr(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_Str_ptr +// api-spec(c): kclvm_char_t* kclvm_value_Str_ptr(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_char_t* @kclvm_value_Str_ptr(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_Str_len +// api-spec(c): kclvm_size_t kclvm_value_Str_len(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_size_t @kclvm_value_Str_len(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_Str_resize +// api-spec(c): void kclvm_value_Str_resize(kclvm_value_ref_t* p, kclvm_size_t n); +// api-spec(llvm): declare void @kclvm_value_Str_resize(%kclvm_value_ref_t* %p, %kclvm_size_t %n); + +// api-spec: kclvm_value_function_ptr +// api-spec(c): uint64_t* kclvm_value_function_ptr(kclvm_value_ref_t* p); +// api-spec(llvm): declare i64* @kclvm_value_function_ptr(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_check_function_ptr +// api-spec(c): uint64_t* kclvm_value_check_function_ptr(kclvm_value_ref_t* p); +// api-spec(llvm): declare i64* @kclvm_value_check_function_ptr(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_function_is_external +// api-spec(c): kclvm_bool_t kclvm_value_function_is_external(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_bool_t @kclvm_value_function_is_external(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_function_external_invoke +// api-spec(c): kclvm_value_ref_t* kclvm_value_function_external_invoke(kclvm_value_ref_t* p, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_function_external_invoke(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_value_function_invoke +// api-spec(c): kclvm_value_ref_t* kclvm_value_function_invoke(kclvm_value_ref_t* p, kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs, kclvm_char_t* pkgpath); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_function_invoke(%kclvm_value_ref_t* %p, %kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs, %kclvm_char_t* %pkgpath); + +// api-spec: kclvm_value_function_get_closure +// api-spec(c): kclvm_value_ref_t* kclvm_value_function_get_closure(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_function_get_closure(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_kind +// api-spec(c): kclvm_kind_t kclvm_value_kind(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_kind_t @kclvm_value_kind(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_deep_copy +// api-spec(c): kclvm_value_ref_t* kclvm_value_deep_copy(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_deep_copy(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_delete +// api-spec(c): void kclvm_value_delete(kclvm_value_ref_t* p); +// api-spec(llvm): declare void @kclvm_value_delete(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_iter +// api-spec(c): kclvm_iterator_t* kclvm_value_iter(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_iterator_t* @kclvm_value_iter(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_iterator_delete +// api-spec(c): void kclvm_iterator_delete(kclvm_iterator_t* p); +// api-spec(llvm): declare void @kclvm_iterator_delete(%kclvm_iterator_t* %p); + +// api-spec: kclvm_iterator_is_end +// api-spec(c): kclvm_bool_t kclvm_iterator_is_end(kclvm_iterator_t* p); +// api-spec(llvm): declare %kclvm_bool_t @kclvm_iterator_is_end(%kclvm_iterator_t* %p); + +// api-spec: kclvm_iterator_cur_key +// api-spec(c): kclvm_value_ref_t* kclvm_iterator_cur_key(kclvm_iterator_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_iterator_cur_key(%kclvm_iterator_t* %p); + +// api-spec: kclvm_iterator_cur_value +// api-spec(c): kclvm_value_ref_t* kclvm_iterator_cur_value(kclvm_iterator_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_iterator_cur_value(%kclvm_iterator_t* %p); + +// api-spec: kclvm_iterator_next_value +// api-spec(c): kclvm_value_ref_t* kclvm_iterator_next_value(kclvm_iterator_t* p, kclvm_value_ref_t* host); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_iterator_next_value(%kclvm_iterator_t* %p, %kclvm_value_ref_t* %host); + +// api-spec: kclvm_list_len +// api-spec(c): kclvm_size_t kclvm_list_len(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_size_t @kclvm_list_len(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_list_resize +// api-spec(c): void kclvm_list_resize(kclvm_value_ref_t* p, kclvm_size_t newsize); +// api-spec(llvm): declare void @kclvm_list_resize(%kclvm_value_ref_t* %p, %kclvm_size_t %newsize); + +// api-spec: kclvm_list_clear +// api-spec(c): void kclvm_list_clear(kclvm_value_ref_t* p); +// api-spec(llvm): declare void @kclvm_list_clear(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_list_count +// api-spec(c): kclvm_value_ref_t* kclvm_list_count(kclvm_value_ref_t* p, kclvm_value_ref_t* item); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_list_count(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %item); + +// api-spec: kclvm_list_find +// api-spec(c): kclvm_value_ref_t* kclvm_list_find(kclvm_value_ref_t* p, kclvm_value_ref_t* item); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_list_find(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %item); + +// api-spec: kclvm_list_insert +// api-spec(c): void kclvm_list_insert(kclvm_value_ref_t* p, kclvm_value_ref_t* index, kclvm_value_ref_t* value); +// api-spec(llvm): declare void @kclvm_list_insert(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %index, %kclvm_value_ref_t* %value); + +// api-spec: kclvm_list_get +// api-spec(c): kclvm_value_ref_t* kclvm_list_get(kclvm_value_ref_t* p, kclvm_size_t i); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_list_get(%kclvm_value_ref_t* %p, %kclvm_size_t %i); + +// api-spec: kclvm_list_get_option +// api-spec(c): kclvm_value_ref_t* kclvm_list_get_option(kclvm_value_ref_t* p, kclvm_size_t i); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_list_get_option(%kclvm_value_ref_t* %p, %kclvm_size_t %i); + +// api-spec: kclvm_list_set +// api-spec(c): void kclvm_list_set(kclvm_value_ref_t* p, kclvm_size_t i, kclvm_value_ref_t* v); +// api-spec(llvm): declare void @kclvm_list_set(%kclvm_value_ref_t* %p, %kclvm_size_t %i, %kclvm_value_ref_t* %v); + +// api-spec: kclvm_list_pop +// api-spec(c): kclvm_value_ref_t* kclvm_list_pop(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_list_pop(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_list_pop_first +// api-spec(c): kclvm_value_ref_t* kclvm_list_pop_first(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_list_pop_first(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_list_append +// api-spec(c): void kclvm_list_append(kclvm_value_ref_t* p, kclvm_value_ref_t* v); +// api-spec(llvm): declare void @kclvm_list_append(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %v); + +// api-spec: kclvm_list_append_bool +// api-spec(c): void kclvm_list_append_bool(kclvm_value_ref_t* p, kclvm_bool_t v); +// api-spec(llvm): declare void @kclvm_list_append_bool(%kclvm_value_ref_t* %p, %kclvm_bool_t %v); + +// api-spec: kclvm_list_append_int +// api-spec(c): void kclvm_list_append_int(kclvm_value_ref_t* p, kclvm_int_t v); +// api-spec(llvm): declare void @kclvm_list_append_int(%kclvm_value_ref_t* %p, %kclvm_int_t %v); + +// api-spec: kclvm_list_append_float +// api-spec(c): void kclvm_list_append_float(kclvm_value_ref_t* p, kclvm_float_t v); +// api-spec(llvm): declare void @kclvm_list_append_float(%kclvm_value_ref_t* %p, %kclvm_float_t %v); + +// api-spec: kclvm_list_append_str +// api-spec(c): void kclvm_list_append_str(kclvm_value_ref_t* p, kclvm_char_t* v); +// api-spec(llvm): declare void @kclvm_list_append_str(%kclvm_value_ref_t* %p, %kclvm_char_t* %v); + +// api-spec: kclvm_list_append_unpack +// api-spec(c): void kclvm_list_append_unpack(kclvm_value_ref_t* p, kclvm_value_ref_t* v); +// api-spec(llvm): declare void @kclvm_list_append_unpack(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %v); + +// api-spec: kclvm_list_remove_at +// api-spec(c): void kclvm_list_remove_at(kclvm_value_ref_t* p, kclvm_size_t i); +// api-spec(llvm): declare void @kclvm_list_remove_at(%kclvm_value_ref_t* %p, %kclvm_size_t %i); + +// api-spec: kclvm_dict_len +// api-spec(c): kclvm_size_t kclvm_dict_len(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_size_t @kclvm_dict_len(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_dict_clear +// api-spec(c): void kclvm_dict_clear(kclvm_value_ref_t* p); +// api-spec(llvm): declare void @kclvm_dict_clear(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_dict_get +// api-spec(c): kclvm_value_ref_t* kclvm_dict_get(kclvm_value_ref_t* p, kclvm_value_ref_t* key); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_dict_get(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %key); + +// api-spec: kclvm_dict_has_value +// api-spec(c): kclvm_bool_t kclvm_dict_has_value(kclvm_value_ref_t* p, kclvm_char_t* key); +// api-spec(llvm): declare %kclvm_bool_t @kclvm_dict_has_value(%kclvm_value_ref_t* %p, %kclvm_char_t* %key); + +// api-spec: kclvm_dict_get_value +// api-spec(c): kclvm_value_ref_t* kclvm_dict_get_value(kclvm_value_ref_t* p, kclvm_char_t* key); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_dict_get_value(%kclvm_value_ref_t* %p, %kclvm_char_t* %key); + +// api-spec: kclvm_dict_get_entry +// api-spec(c): kclvm_value_ref_t* kclvm_dict_get_entry(kclvm_value_ref_t* p, kclvm_char_t* key); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_dict_get_entry(%kclvm_value_ref_t* %p, %kclvm_char_t* %key); + +// api-spec: kclvm_dict_get_value_by_path +// api-spec(c): kclvm_value_ref_t* kclvm_dict_get_value_by_path(kclvm_value_ref_t* p, kclvm_char_t* path); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_dict_get_value_by_path(%kclvm_value_ref_t* %p, %kclvm_char_t* %path); + +// api-spec: kclvm_dict_set_value +// api-spec(c): void kclvm_dict_set_value(kclvm_value_ref_t* p, kclvm_char_t* key, kclvm_value_ref_t* val); +// api-spec(llvm): declare void @kclvm_dict_set_value(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, %kclvm_value_ref_t* %val); + +// api-spec: kclvm_dict_keys +// api-spec(c): kclvm_value_ref_t* kclvm_dict_keys(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_dict_keys(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_dict_values +// api-spec(c): kclvm_value_ref_t* kclvm_dict_values(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_dict_values(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_dict_insert +// api-spec(c): void kclvm_dict_insert(kclvm_value_ref_t* p, kclvm_char_t* key, kclvm_value_ref_t* v, kclvm_size_t op, kclvm_size_t insert_index); +// api-spec(llvm): declare void @kclvm_dict_insert(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, %kclvm_value_ref_t* %v, %kclvm_size_t %op, %kclvm_size_t %insert_index); + +// api-spec: kclvm_dict_merge +// api-spec(c): void kclvm_dict_merge(kclvm_value_ref_t* p, kclvm_char_t* key, kclvm_value_ref_t* v, kclvm_size_t op, kclvm_size_t insert_index); +// api-spec(llvm): declare void @kclvm_dict_merge(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, %kclvm_value_ref_t* %v, %kclvm_size_t %op, %kclvm_size_t %insert_index); + +// api-spec: kclvm_dict_insert_value +// api-spec(c): void kclvm_dict_insert_value(kclvm_value_ref_t* p, kclvm_value_ref_t* key, kclvm_value_ref_t* v, kclvm_size_t op, kclvm_size_t insert_index); +// api-spec(llvm): declare void @kclvm_dict_insert_value(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %key, %kclvm_value_ref_t* %v, %kclvm_size_t %op, %kclvm_size_t %insert_index); + +// api-spec: kclvm_dict_update_key_value +// api-spec(c): void kclvm_dict_update_key_value(kclvm_value_ref_t* p, kclvm_value_ref_t* key, kclvm_value_ref_t* v); +// api-spec(llvm): declare void @kclvm_dict_update_key_value(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %key, %kclvm_value_ref_t* %v); + +// api-spec: kclvm_dict_safe_insert +// api-spec(c): void kclvm_dict_safe_insert(kclvm_value_ref_t* p, kclvm_char_t* key, kclvm_value_ref_t* v, kclvm_size_t op, kclvm_size_t insert_index); +// api-spec(llvm): declare void @kclvm_dict_safe_insert(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, %kclvm_value_ref_t* %v, %kclvm_size_t %op, %kclvm_size_t %insert_index); + +// api-spec: kclvm_dict_insert_unpack +// api-spec(c): void kclvm_dict_insert_unpack(kclvm_value_ref_t* p, kclvm_value_ref_t* v); +// api-spec(llvm): declare void @kclvm_dict_insert_unpack(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %v); + +// api-spec: kclvm_default_collection_insert_int_pointer +// api-spec(c): void kclvm_default_collection_insert_int_pointer(kclvm_value_ref_t* p, kclvm_char_t* key, uint64_t* ptr); +// api-spec(llvm): declare void @kclvm_default_collection_insert_int_pointer(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, i64* %ptr); + +// api-spec: kclvm_default_collection_insert_value +// api-spec(c): void kclvm_default_collection_insert_value(kclvm_value_ref_t* p, kclvm_char_t* key, kclvm_value_ref_t* value); +// api-spec(llvm): declare void @kclvm_default_collection_insert_value(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, %kclvm_value_ref_t* %value); + +// api-spec: kclvm_dict_remove +// api-spec(c): void kclvm_dict_remove(kclvm_value_ref_t* p, kclvm_char_t* key); +// api-spec(llvm): declare void @kclvm_dict_remove(%kclvm_value_ref_t* %p, %kclvm_char_t* %key); + +// api-spec: kclvm_dict_update +// api-spec(c): void kclvm_dict_update(kclvm_value_ref_t* p, kclvm_value_ref_t* v); +// api-spec(llvm): declare void @kclvm_dict_update(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %v); + +// api-spec: kclvm_value_is_truthy +// api-spec(c): kclvm_bool_t kclvm_value_is_truthy(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_bool_t @kclvm_value_is_truthy(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_len +// api-spec(c): kclvm_size_t kclvm_value_len(kclvm_value_ref_t* p); +// api-spec(llvm): declare %kclvm_size_t @kclvm_value_len(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_value_cmp_equal_to +// api-spec(c): kclvm_value_ref_t* kclvm_value_cmp_equal_to(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_cmp_equal_to(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_cmp_not_equal_to +// api-spec(c): kclvm_value_ref_t* kclvm_value_cmp_not_equal_to(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_cmp_not_equal_to(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_cmp_less_than +// api-spec(c): kclvm_value_ref_t* kclvm_value_cmp_less_than(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_cmp_less_than(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_cmp_less_than_or_equal +// api-spec(c): kclvm_value_ref_t* kclvm_value_cmp_less_than_or_equal(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_cmp_less_than_or_equal(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_cmp_greater_than +// api-spec(c): kclvm_value_ref_t* kclvm_value_cmp_greater_than(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_cmp_greater_than(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_cmp_greater_than_or_equal +// api-spec(c): kclvm_value_ref_t* kclvm_value_cmp_greater_than_or_equal(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_cmp_greater_than_or_equal(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_is +// api-spec(c): kclvm_value_ref_t* kclvm_value_is(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_is(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_is_not +// api-spec(c): kclvm_value_ref_t* kclvm_value_is_not(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_is_not(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_in +// api-spec(c): kclvm_value_ref_t* kclvm_value_in(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_in(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_not_in +// api-spec(c): kclvm_value_ref_t* kclvm_value_not_in(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_not_in(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_as +// api-spec(c): kclvm_value_ref_t* kclvm_value_as(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_as(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_unary_plus +// api-spec(c): kclvm_value_ref_t* kclvm_value_unary_plus(kclvm_value_ref_t* a); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_unary_plus(%kclvm_value_ref_t* %a); + +// api-spec: kclvm_value_unary_minus +// api-spec(c): kclvm_value_ref_t* kclvm_value_unary_minus(kclvm_value_ref_t* a); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_unary_minus(%kclvm_value_ref_t* %a); + +// api-spec: kclvm_value_unary_not +// api-spec(c): kclvm_value_ref_t* kclvm_value_unary_not(kclvm_value_ref_t* a); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_unary_not(%kclvm_value_ref_t* %a); + +// api-spec: kclvm_value_unary_l_not +// api-spec(c): kclvm_value_ref_t* kclvm_value_unary_l_not(kclvm_value_ref_t* a); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_unary_l_not(%kclvm_value_ref_t* %a); + +// api-spec: kclvm_value_op_add +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_add(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_add(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_sub +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_sub(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_sub(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_mul +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_mul(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_mul(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_div +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_div(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_div(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_mod +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_mod(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_mod(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_pow +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_pow(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_pow(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_floor_div +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_floor_div(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_floor_div(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_bit_lshift +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_bit_lshift(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_bit_lshift(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_bit_rshift +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_bit_rshift(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_bit_rshift(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_bit_and +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_bit_and(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_bit_and(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_bit_xor +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_bit_xor(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_bit_xor(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_bit_or +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_bit_or(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_bit_or(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_aug_add +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_aug_add(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_aug_add(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_aug_sub +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_aug_sub(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_aug_sub(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_aug_mul +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_aug_mul(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_aug_mul(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_aug_div +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_aug_div(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_aug_div(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_aug_mod +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_aug_mod(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_aug_mod(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_aug_pow +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_aug_pow(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_aug_pow(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_aug_floor_div +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_aug_floor_div(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_aug_floor_div(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_aug_bit_lshift +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_aug_bit_lshift(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_aug_bit_lshift(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_aug_bit_rshift +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_aug_bit_rshift(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_aug_bit_rshift(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_aug_bit_and +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_aug_bit_and(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_aug_bit_and(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_aug_bit_xor +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_aug_bit_xor(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_aug_bit_xor(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_op_aug_bit_or +// api-spec(c): kclvm_value_ref_t* kclvm_value_op_aug_bit_or(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_op_aug_bit_or(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_union +// api-spec(c): kclvm_value_ref_t* kclvm_value_union(kclvm_value_ref_t* schema, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_union(%kclvm_value_ref_t* %schema, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_logic_and +// api-spec(c): kclvm_value_ref_t* kclvm_value_logic_and(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_logic_and(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_logic_or +// api-spec(c): kclvm_value_ref_t* kclvm_value_logic_or(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_logic_or(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_subscr +// api-spec(c): kclvm_value_ref_t* kclvm_value_subscr(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_subscr(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_subscr_option +// api-spec(c): kclvm_value_ref_t* kclvm_value_subscr_option(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_subscr_option(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_load_attr +// api-spec(c): kclvm_value_ref_t* kclvm_value_load_attr(kclvm_value_ref_t* obj, kclvm_char_t* key); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_load_attr(%kclvm_value_ref_t* %obj, %kclvm_char_t* %key); + +// api-spec: kclvm_value_load_attr_option +// api-spec(c): kclvm_value_ref_t* kclvm_value_load_attr_option(kclvm_value_ref_t* p, kclvm_char_t* key); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_load_attr_option(%kclvm_value_ref_t* %p, %kclvm_char_t* %key); + +// api-spec: kclvm_value_remove_item +// api-spec(c): void kclvm_value_remove_item(kclvm_value_ref_t* a, kclvm_value_ref_t* b); +// api-spec(llvm): declare void @kclvm_value_remove_item(%kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b); + +// api-spec: kclvm_value_slice +// api-spec(c): kclvm_value_ref_t* kclvm_value_slice(kclvm_value_ref_t* x, kclvm_value_ref_t* a, kclvm_value_ref_t* b, kclvm_value_ref_t* step); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_slice(%kclvm_value_ref_t* %x, %kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b, %kclvm_value_ref_t* %step); + +// api-spec: kclvm_value_slice_option +// api-spec(c): kclvm_value_ref_t* kclvm_value_slice_option(kclvm_value_ref_t* x, kclvm_value_ref_t* a, kclvm_value_ref_t* b, kclvm_value_ref_t* step); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_slice_option(%kclvm_value_ref_t* %x, %kclvm_value_ref_t* %a, %kclvm_value_ref_t* %b, %kclvm_value_ref_t* %step); + +// api-spec: kclvm_schema_backtrack_cache +// api-spec(c): void kclvm_schema_backtrack_cache(kclvm_value_ref_t* schema, kclvm_value_ref_t* cache, kclvm_value_ref_t* cal_map, kclvm_char_t* name, kclvm_value_ref_t* runtime_type); +// api-spec(llvm): declare void @kclvm_schema_backtrack_cache(%kclvm_value_ref_t* %schema, %kclvm_value_ref_t* %cache, %kclvm_value_ref_t* %cal_map, %kclvm_char_t* %name, %kclvm_value_ref_t* %runtime_type); + +// api-spec: kclvm_schema_instances +// api-spec(c): kclvm_value_ref_t* kclvm_schema_instances(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_schema_instances(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_schema_value_check +// api-spec(c): void kclvm_schema_value_check(kclvm_value_ref_t* schema_value, kclvm_value_ref_t* schema_config, kclvm_value_ref_t* _config_meta, kclvm_char_t* schema_name, kclvm_value_ref_t* index_sign_value, kclvm_char_t* _key_name, kclvm_char_t* key_type, kclvm_char_t* _value_type, kclvm_bool_t _any_other, kclvm_bool_t is_relaxed); +// api-spec(llvm): declare void @kclvm_schema_value_check(%kclvm_value_ref_t* %schema_value, %kclvm_value_ref_t* %schema_config, %kclvm_value_ref_t* %_config_meta, %kclvm_char_t* %schema_name, %kclvm_value_ref_t* %index_sign_value, %kclvm_char_t* %_key_name, %kclvm_char_t* %key_type, %kclvm_char_t* %_value_type, %kclvm_bool_t %_any_other, %kclvm_bool_t %is_relaxed); + +// api-spec: kclvm_schema_do_check_with_index_sign_attr +// api-spec(c): void kclvm_schema_do_check_with_index_sign_attr(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs, uint64_t* check_fn_ptr, kclvm_char_t* attr_name); +// api-spec(llvm): declare void @kclvm_schema_do_check_with_index_sign_attr(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs, i64* %check_fn_ptr, %kclvm_char_t* %attr_name); + +// api-spec: kclvm_schema_optional_check +// api-spec(c): kclvm_value_ref_t* kclvm_schema_optional_check(kclvm_value_ref_t* p, kclvm_value_ref_t* v, kclvm_char_t* schema_name, kclvm_value_ref_t* config_meta); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_schema_optional_check(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %v, %kclvm_char_t* %schema_name, %kclvm_value_ref_t* %config_meta); + +// api-spec: kclvm_schema_default_settings +// api-spec(c): void kclvm_schema_default_settings(kclvm_value_ref_t* schema_value, kclvm_value_ref_t* config_value, kclvm_char_t* runtime_type); +// api-spec(llvm): declare void @kclvm_schema_default_settings(%kclvm_value_ref_t* %schema_value, %kclvm_value_ref_t* %config_value, %kclvm_char_t* %runtime_type); + +// api-spec: kclvm_schema_assert +// api-spec(c): void kclvm_schema_assert(kclvm_value_ref_t* value, kclvm_value_ref_t* msg, kclvm_value_ref_t* config_meta); +// api-spec(llvm): declare void @kclvm_schema_assert(%kclvm_value_ref_t* %value, %kclvm_value_ref_t* %msg, %kclvm_value_ref_t* %config_meta); + +// api-spec: kclvm_schema_value_new +// api-spec(c): kclvm_value_ref_t* kclvm_schema_value_new(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs, kclvm_value_ref_t* schema_value_or_func, kclvm_value_ref_t* config, kclvm_value_ref_t* config_meta, kclvm_char_t* pkgpath); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_schema_value_new(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs, %kclvm_value_ref_t* %schema_value_or_func, %kclvm_value_ref_t* %config, %kclvm_value_ref_t* %config_meta, %kclvm_char_t* %pkgpath); + +// api-spec: kclvm_convert_collection_value +// api-spec(c): kclvm_value_ref_t* kclvm_convert_collection_value(kclvm_value_ref_t* value, kclvm_char_t* tpe); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_convert_collection_value(%kclvm_value_ref_t* %value, %kclvm_char_t* %tpe); + +// api-spec: kclvm_schema_get_value +// api-spec(c): kclvm_value_ref_t* kclvm_schema_get_value(kclvm_value_ref_t* p, kclvm_char_t* key, kclvm_value_ref_t* config, kclvm_value_ref_t* config_meta, kclvm_value_ref_t* cal_map, kclvm_char_t* target_attr, kclvm_value_ref_t* backtrack_level_map, kclvm_value_ref_t* backtrack_cache, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_schema_get_value(%kclvm_value_ref_t* %p, %kclvm_char_t* %key, %kclvm_value_ref_t* %config, %kclvm_value_ref_t* %config_meta, %kclvm_value_ref_t* %cal_map, %kclvm_char_t* %target_attr, %kclvm_value_ref_t* %backtrack_level_map, %kclvm_value_ref_t* %backtrack_cache, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_config_attr_map +// api-spec(c): void kclvm_config_attr_map(kclvm_value_ref_t* value, kclvm_char_t* name, kclvm_char_t* type_str); +// api-spec(llvm): declare void @kclvm_config_attr_map(%kclvm_value_ref_t* %value, %kclvm_char_t* %name, %kclvm_char_t* %type_str); + +// api-spec: kclvm_value_Decorator +// api-spec(c): kclvm_decorator_value_t* kclvm_value_Decorator(kclvm_char_t* name, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs, kclvm_value_ref_t* config_meta, kclvm_char_t* attr_name, kclvm_value_ref_t* config_value, kclvm_value_ref_t* is_schema_target); +// api-spec(llvm): declare %kclvm_decorator_value_t* @kclvm_value_Decorator(%kclvm_char_t* %name, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs, %kclvm_value_ref_t* %config_meta, %kclvm_char_t* %attr_name, %kclvm_value_ref_t* %config_value, %kclvm_value_ref_t* %is_schema_target); + +// api-spec: kclvm_builtin_str_lower +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_lower(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_lower(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_upper +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_upper(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_upper(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_capitalize +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_capitalize(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_capitalize(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_count +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_count(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_count(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_endswith +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_endswith(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_endswith(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_find +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_find(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_find(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_format +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_format(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_format(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_str_index +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_index(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_index(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_isalnum +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_isalnum(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_isalnum(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_isalpha +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_isalpha(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_isalpha(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_isdigit +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_isdigit(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_isdigit(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_islower +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_islower(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_islower(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_isspace +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_isspace(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_isspace(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_istitle +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_istitle(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_istitle(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_isupper +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_isupper(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_isupper(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_join +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_join(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_join(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_lstrip +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_lstrip(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_lstrip(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_rstrip +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_rstrip(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_rstrip(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_replace +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_replace(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_replace(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_rfind +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_rfind(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_rfind(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_rindex +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_rindex(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_rindex(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_rsplit +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_rsplit(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_rsplit(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_str_split +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_split(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_split(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_str_splitlines +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_splitlines(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_splitlines(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_str_startswith +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_startswith(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_startswith(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_strip +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_strip(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_strip(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str_title +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str_title(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str_title(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_base64_encode +// api-spec(c): kclvm_value_ref_t* kclvm_base64_encode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_base64_encode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_base64_decode +// api-spec(c): kclvm_value_ref_t* kclvm_base64_decode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_base64_decode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_value_union_all +// api-spec(c): kclvm_value_ref_t* kclvm_value_union_all(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_union_all(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_crypto_md5 +// api-spec(c): kclvm_value_ref_t* kclvm_crypto_md5(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_crypto_md5(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_crypto_sha1 +// api-spec(c): kclvm_value_ref_t* kclvm_crypto_sha1(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_crypto_sha1(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_crypto_sha224 +// api-spec(c): kclvm_value_ref_t* kclvm_crypto_sha224(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_crypto_sha224(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_crypto_sha256 +// api-spec(c): kclvm_value_ref_t* kclvm_crypto_sha256(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_crypto_sha256(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_crypto_sha384 +// api-spec(c): kclvm_value_ref_t* kclvm_crypto_sha384(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_crypto_sha384(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_crypto_sha512 +// api-spec(c): kclvm_value_ref_t* kclvm_crypto_sha512(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_crypto_sha512(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_datetime_today +// api-spec(c): kclvm_value_ref_t* kclvm_datetime_today(kclvm_context_t* _ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_datetime_today(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_datetime_now +// api-spec(c): kclvm_value_ref_t* kclvm_datetime_now(kclvm_context_t* _ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_datetime_now(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_datetime_ticks +// api-spec(c): kclvm_value_ref_t* kclvm_datetime_ticks(kclvm_context_t* _ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_datetime_ticks(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_datetime_date +// api-spec(c): kclvm_value_ref_t* kclvm_datetime_date(kclvm_context_t* _ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_datetime_date(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_json_encode +// api-spec(c): kclvm_value_ref_t* kclvm_json_encode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_json_encode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_json_decode +// api-spec(c): kclvm_value_ref_t* kclvm_json_decode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_json_decode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_json_dump_to_file +// api-spec(c): kclvm_value_ref_t* kclvm_json_dump_to_file(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_json_dump_to_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_ceil +// api-spec(c): kclvm_value_ref_t* kclvm_math_ceil(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_ceil(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_factorial +// api-spec(c): kclvm_value_ref_t* kclvm_math_factorial(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_factorial(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_floor +// api-spec(c): kclvm_value_ref_t* kclvm_math_floor(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_floor(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_gcd +// api-spec(c): kclvm_value_ref_t* kclvm_math_gcd(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_gcd(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_isfinite +// api-spec(c): kclvm_value_ref_t* kclvm_math_isfinite(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_isfinite(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_isinf +// api-spec(c): kclvm_value_ref_t* kclvm_math_isinf(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_isinf(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_isnan +// api-spec(c): kclvm_value_ref_t* kclvm_math_isnan(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_isnan(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_modf +// api-spec(c): kclvm_value_ref_t* kclvm_math_modf(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_modf(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_exp +// api-spec(c): kclvm_value_ref_t* kclvm_math_exp(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_exp(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_expm1 +// api-spec(c): kclvm_value_ref_t* kclvm_math_expm1(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_expm1(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_log +// api-spec(c): kclvm_value_ref_t* kclvm_math_log(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_log(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_log1p +// api-spec(c): kclvm_value_ref_t* kclvm_math_log1p(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_log1p(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_log2 +// api-spec(c): kclvm_value_ref_t* kclvm_math_log2(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_log2(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_log10 +// api-spec(c): kclvm_value_ref_t* kclvm_math_log10(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_log10(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_pow +// api-spec(c): kclvm_value_ref_t* kclvm_math_pow(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_pow(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_math_sqrt +// api-spec(c): kclvm_value_ref_t* kclvm_math_sqrt(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_math_sqrt(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_net_split_host_port +// api-spec(c): kclvm_value_ref_t* kclvm_net_split_host_port(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_split_host_port(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_net_join_host_port +// api-spec(c): kclvm_value_ref_t* kclvm_net_join_host_port(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_join_host_port(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_net_fqdn +// api-spec(c): kclvm_value_ref_t* kclvm_net_fqdn(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_fqdn(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_net_parse_IP +// api-spec(c): kclvm_value_ref_t* kclvm_net_parse_IP(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_parse_IP(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_net_to_IP4 +// api-spec(c): kclvm_value_ref_t* kclvm_net_to_IP4(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_to_IP4(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_net_to_IP16 +// api-spec(c): kclvm_value_ref_t* kclvm_net_to_IP16(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_to_IP16(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_net_IP_string +// api-spec(c): kclvm_value_ref_t* kclvm_net_IP_string(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_IP_string(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_net_is_IPv4 +// api-spec(c): kclvm_value_ref_t* kclvm_net_is_IPv4(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_is_IPv4(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_net_is_IP +// api-spec(c): kclvm_value_ref_t* kclvm_net_is_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_is_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_net_is_loopback_IP +// api-spec(c): kclvm_value_ref_t* kclvm_net_is_loopback_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_is_loopback_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_net_is_multicast_IP +// api-spec(c): kclvm_value_ref_t* kclvm_net_is_multicast_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_is_multicast_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_net_is_interface_local_multicast_IP +// api-spec(c): kclvm_value_ref_t* kclvm_net_is_interface_local_multicast_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_is_interface_local_multicast_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_net_is_link_local_multicast_IP +// api-spec(c): kclvm_value_ref_t* kclvm_net_is_link_local_multicast_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_is_link_local_multicast_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_net_is_link_local_unicast_IP +// api-spec(c): kclvm_value_ref_t* kclvm_net_is_link_local_unicast_IP(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_is_link_local_unicast_IP(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_net_is_global_unicast_IP +// api-spec(c): kclvm_value_ref_t* kclvm_net_is_global_unicast_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_is_global_unicast_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_net_is_unspecified_IP +// api-spec(c): kclvm_value_ref_t* kclvm_net_is_unspecified_IP(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_net_is_unspecified_IP(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_regex_match +// api-spec(c): kclvm_value_ref_t* kclvm_regex_match(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_regex_match(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_regex_replace +// api-spec(c): kclvm_value_ref_t* kclvm_regex_replace(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_regex_replace(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_regex_compile +// api-spec(c): kclvm_value_ref_t* kclvm_regex_compile(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_regex_compile(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_regex_findall +// api-spec(c): kclvm_value_ref_t* kclvm_regex_findall(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_regex_findall(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_regex_search +// api-spec(c): kclvm_value_ref_t* kclvm_regex_search(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_regex_search(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_regex_split +// api-spec(c): kclvm_value_ref_t* kclvm_regex_split(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_regex_split(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_assert +// api-spec(c): void kclvm_assert(kclvm_value_ref_t* value, kclvm_value_ref_t* msg); +// api-spec(llvm): declare void @kclvm_assert(%kclvm_value_ref_t* %value, %kclvm_value_ref_t* %msg); + +// api-spec: kclvm_debug_hello +// api-spec(c): void kclvm_debug_hello(); +// api-spec(llvm): declare void @kclvm_debug_hello(); + +// api-spec: kclvm_debug_print +// api-spec(c): void kclvm_debug_print(int8_t* cs); +// api-spec(llvm): declare void @kclvm_debug_print(i8* %cs); + +// api-spec: kclvm_debug_print_str_list +// api-spec(c): void kclvm_debug_print_str_list(int32_t len, int8_t** ss); +// api-spec(llvm): declare void @kclvm_debug_print_str_list(i32 %len, i8** %ss); + +// api-spec: kclvm_debug_print_type +// api-spec(c): void kclvm_debug_print_type(kclvm_type_t* p); +// api-spec(llvm): declare void @kclvm_debug_print_type(%kclvm_type_t* %p); + +// api-spec: kclvm_debug_print_value +// api-spec(c): void kclvm_debug_print_value(kclvm_value_ref_t* p); +// api-spec(llvm): declare void @kclvm_debug_print_value(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_debug_print_value_json_string +// api-spec(c): void kclvm_debug_print_value_json_string(kclvm_value_ref_t* p); +// api-spec(llvm): declare void @kclvm_debug_print_value_json_string(%kclvm_value_ref_t* %p); + +// api-spec: kclvm_debug_invoke_func +// api-spec(c): void kclvm_debug_invoke_func(void* fn_ptr); +// api-spec(llvm): declare void @kclvm_debug_invoke_func(i8* %fn_ptr); + +// api-spec: kclvm_builtin_option_init +// api-spec(c): void kclvm_builtin_option_init(kclvm_context_t* ctx, int8_t* key, int8_t* value); +// api-spec(llvm): declare void @kclvm_builtin_option_init(%kclvm_context_t* %ctx, i8* %key, i8* %value); + +// api-spec: kclvm_builtin_option_reset +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_option_reset(kclvm_context_t* ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_option_reset(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_option +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_option(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_option(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_print +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_print(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_print(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_len +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_len(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_len(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_any_true +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_any_true(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_any_true(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_isunique +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_isunique(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_isunique(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_sorted +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_sorted(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_sorted(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_int +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_int(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_int(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_float +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_float(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_float(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_bool +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_bool(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_bool(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_str +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_str(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_str(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_builtin_max +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_max(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_max(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_min +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_min(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_min(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_multiplyof +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_multiplyof(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_multiplyof(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_abs +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_abs(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_abs(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_all_true +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_all_true(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_all_true(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_hex +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_hex(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_hex(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_sum +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_sum(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_sum(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_pow +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_pow(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_pow(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_round +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_round(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_round(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_zip +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_zip(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_zip(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_list +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_list(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_list(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_dict +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_dict(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_dict(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_typeof +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_typeof(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_typeof(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_bin +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_bin(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_bin(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_oct +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_oct(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_oct(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_ord +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_ord(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_ord(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_builtin_range +// api-spec(c): kclvm_value_ref_t* kclvm_builtin_range(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_builtin_range(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_plugin_init +// api-spec(c): void kclvm_plugin_init(void* fn_ptr); +// api-spec(llvm): declare void @kclvm_plugin_init(i8* %fn_ptr); + +// api-spec: kclvm_plugin_invoke +// api-spec(c): kclvm_value_ref_t* kclvm_plugin_invoke(int8_t* method, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_plugin_invoke(i8* %method, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_plugin_invoke_json +// api-spec(c): char* kclvm_plugin_invoke_json(int8_t* method, char* args, char* kwargs); +// api-spec(llvm): declare i8* @kclvm_plugin_invoke_json(i8* %method, i8* %args, i8* %kwargs); + +// api-spec: kclvm_testing_arguments +// api-spec(c): void kclvm_testing_arguments(kclvm_context_t* _ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare void @kclvm_testing_arguments(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_testing_setting_file +// api-spec(c): void kclvm_testing_setting_file(kclvm_context_t* _ctx, kclvm_value_ref_t* _args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare void @kclvm_testing_setting_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_units_to_n +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_n(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_n(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_units_to_u +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_u(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_u(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_units_to_m +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_m(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_m(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_units_to_K +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_K(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_K(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_units_to_M +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_M(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_M(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_units_to_G +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_G(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_G(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_units_to_T +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_T(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_T(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_units_to_P +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_P(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_P(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_units_to_Ki +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_Ki(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_Ki(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_units_to_Mi +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_Mi(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_Mi(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_units_to_Gi +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_Gi(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_Gi(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_units_to_Ti +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_Ti(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_Ti(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_units_to_Pi +// api-spec(c): kclvm_value_ref_t* kclvm_units_to_Pi(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_units_to_Pi(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_yaml_encode +// api-spec(c): kclvm_value_ref_t* kclvm_yaml_encode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_yaml_encode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs); + +// api-spec: kclvm_yaml_decode +// api-spec(c): kclvm_value_ref_t* kclvm_yaml_decode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_yaml_decode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + +// api-spec: kclvm_yaml_dump_to_file +// api-spec(c): kclvm_value_ref_t* kclvm_yaml_dump_to_file(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_yaml_dump_to_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs); + diff --git a/kclvm/runtime/src/_kclvm_main_win.c b/kclvm/runtime/src/_kclvm_main_win.c new file mode 100644 index 000000000..917efac53 --- /dev/null +++ b/kclvm/runtime/src/_kclvm_main_win.c @@ -0,0 +1,18 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +extern void* kclvm_main(void* ctx); + +extern void kclvm_debug_hello(); +extern void kclvm_debug_print(const char* s); + +__declspec(dllexport) void* kclvm_main_win(void* ctx) { + return kclvm_main(ctx); +} + +__declspec(dllexport) void kclvm_debug_hello_win() { + kclvm_debug_hello(); +} + +__declspec(dllexport) void kclvm_debug_print_win(const char* s) { + kclvm_debug_print(s); +} diff --git a/kclvm/runtime/src/_kclvm_undefined_wasm.txt b/kclvm/runtime/src/_kclvm_undefined_wasm.txt new file mode 100644 index 000000000..7524e7d15 --- /dev/null +++ b/kclvm/runtime/src/_kclvm_undefined_wasm.txt @@ -0,0 +1 @@ +kclvm_plugin_invoke_json_wasm \ No newline at end of file diff --git a/kclvm/runtime/src/api/buf.rs b/kclvm/runtime/src/api/buf.rs new file mode 100644 index 000000000..110a9a220 --- /dev/null +++ b/kclvm/runtime/src/api/buf.rs @@ -0,0 +1,95 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_bool_t = i8; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_char_t = i8; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_size_t = i32; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_buffer_t = Buffer; + +#[allow(dead_code, non_camel_case_types)] +pub type kclvm_value_t = Value; + +#[repr(C)] +#[derive(Debug, Clone, Default)] +pub struct Buffer { + buf: Vec, +} + +impl Buffer { + #[allow(dead_code)] + pub fn new_with_buf(buf: &[u8]) -> Self { + Buffer { buf: buf.to_vec() } + } + + #[allow(dead_code)] + pub fn into_raw(self) -> *mut Self { + Box::into_raw(Box::new(self)) + } + + #[allow(dead_code)] + pub fn malloc(size: usize) -> *mut u8 { + let p = Box::into_raw(Box::new(Buffer { + buf: vec![0u8; size + 8], + })); + + unsafe { + let data_ptr = (*p).buf.as_ptr() as *mut u8; + let u64bytes = (p as u64).to_le_bytes(); + (*p).buf[..8].clone_from_slice(&u64bytes[..8]); + data_ptr.add(8) + } + } + + #[allow(dead_code)] + pub fn free(_data_ptr: *mut u8) { + unsafe { + let p = u64::from_le_bytes( + std::slice::from_raw_parts(((_data_ptr as u64) - 8) as *const u8, 8) + .try_into() + .unwrap(), + ) as *mut Self; + + Box::from_raw(p); + } + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_buffer_new(size: kclvm_size_t) -> *mut kclvm_buffer_t { + let mut p = Buffer { buf: Vec::new() }; + p.buf.resize(size as usize, 0); + new_mut_ptr(p) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_buffer_delete(p: *mut kclvm_buffer_t) { + free_mut_ptr(p) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_buffer_size(p: *const kclvm_buffer_t) -> kclvm_size_t { + let p = ptr_as_ref(p); + p.buf.len() as kclvm_size_t +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_buffer_data(p: *const kclvm_buffer_t) -> *const kclvm_char_t { + let p = ptr_as_ref(p); + if !p.buf.is_empty() { + p.buf.as_ptr() as *const kclvm_char_t + } else { + std::ptr::null() + } +} diff --git a/kclvm/runtime/src/api/err_type.rs b/kclvm/runtime/src/api/err_type.rs new file mode 100644 index 000000000..b36faf3e0 --- /dev/null +++ b/kclvm/runtime/src/api/err_type.rs @@ -0,0 +1,54 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +// Auto generated, DONOT EDIT!!! + +// python: kclvm.kcl.error.ErrType + +#[derive(Copy, Clone)] +#[allow(non_camel_case_types)] +pub enum ErrType { + InvalidSyntax_TYPE = 0, + TabError_TYPE = 1, + IndentationError_TYPE = 2, + CannotFindModule_TYPE = 3, + FailedLoadModule_TYPE = 4, + CompileError_TYPE = 5, + EvaluationError_TYPE = 6, + RecursiveLoad_TYPE = 7, + FloatOverflow_TYPE = 8, + FloatUnderflow_TYPE = 9, + IntOverflow_TYPE = 10, + InvalidDocstring_TYPE = 11, + Deprecated_TYPE = 12, + UnKnownDecorator_TYPE = 13, + InvalidDecoratorTarget_TYPE = 14, + InvalidFormatSpec_TYPE = 15, + SelectorError_TYPE = 16, + SchemaCheckFailure_TYPE = 17, + MixinNamingError_TYPE = 18, + MixinStructureIllegal_TYPE = 19, + IndexSignatureError_TYPE = 20, + TypeError_Runtime_TYPE = 21, + TypeError_Compile_TYPE = 22, + NameError_TYPE = 23, + ValueError_TYPE = 24, + KeyError_TYPE = 25, + UniqueKeyError_TYPE = 26, + AttributeError_TYPE = 27, + AttributeError_Runtime_TYPE = 28, + AssertionError_TYPE = 29, + ImmutableCompileError_TYPE = 30, + ImmutableRuntimeError_TYPE = 31, + MultiInheritError_TYPE = 32, + CycleInheritError_TYPE = 33, + IllegalInheritError_TYPE = 34, + IllegalAttributeError_TYPE = 35, + IllegalArgumentError_TYPE = 36, + IllegalArgumentError_Complie_TYPE = 37, + IllegalArgumentError_Syntax_TYPE = 38, + RecursionError_TYPE = 39, + PlanError_TYPE = 40, + Deprecated_Warning_TYPE = 41, + CannotAddMembers_TYPE = 42, + CannotAddMembers_Runtime_TYPE = 43, +} diff --git a/kclvm/runtime/src/api/kclvm.rs b/kclvm/runtime/src/api/kclvm.rs new file mode 100644 index 000000000..efeb17b15 --- /dev/null +++ b/kclvm/runtime/src/api/kclvm.rs @@ -0,0 +1,429 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = crate::ValueRef; + +use crate::IndexMap; +use std::collections::{HashMap, HashSet}; +use std::{ + cell::RefCell, + cmp::Ordering, + hash::{Hash, Hasher}, + rc::Rc, +}; + +#[allow(non_upper_case_globals)] +pub const UNDEFINED: Value = Value::undefined; + +#[allow(non_upper_case_globals)] +pub const NONE: Value = Value::none; + +#[allow(non_upper_case_globals)] +pub const TRUE: Value = Value::bool_value(true); + +#[allow(non_upper_case_globals)] +pub const FALSE: Value = Value::bool_value(false); + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct KclError { + pub err_code: i32, + pub err_text: String, + pub filename: String, + pub source_code: String, + pub line: i32, + pub column: i32, +} + +#[allow(non_camel_case_types)] +#[derive(Clone, PartialEq, Debug)] +pub enum Type { + any_type, + bool_type, + bool_lit_type(bool), + int_type, + int_lit_type(i64), + float_type, + float_lit_type(f64), + str_type, + str_lit_type(String), + list_type(ListType), + dict_type(DictType), + union_type(UnionType), + schema_type(SchemaType), + func_type(FuncType), +} + +impl Default for Type { + fn default() -> Self { + Type::any_type + } +} + +impl Type { + #[allow(dead_code)] + pub fn into_raw(self) -> *mut Self { + Box::into_raw(Box::new(self)) + } +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct ListType { + pub elem_type: Box, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct DictType { + pub key_type: Box, + pub elem_type: Box, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct UnionType { + pub elem_types: Vec, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct SchemaType { + pub name: String, + pub parent_name: String, + pub field_names: Vec, + pub field_types: Vec, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct FuncType { + pub args_types: Vec, + pub return_type: Box, +} + +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Clone, Debug)] +pub struct ValueRef { + pub rc: Rc, +} + +impl Eq for ValueRef {} + +impl PartialEq for ValueRef { + fn eq(&self, other: &Self) -> bool { + self.cmp_equal(other) + } +} + +impl Ord for ValueRef { + fn cmp(&self, other: &ValueRef) -> Ordering { + let ord = match *self.rc { + Value::int_value(a) => match *other.rc { + Value::int_value(b) => a.partial_cmp(&b), + Value::float_value(b) => (a as f64).partial_cmp(&b), + _ => None, + }, + Value::float_value(a) => match *other.rc { + Value::int_value(b) => a.partial_cmp(&(b as f64)), + Value::float_value(b) => a.partial_cmp(&b), + _ => None, + }, + Value::str_value(ref a) => match &*other.rc { + Value::str_value(ref b) => a.partial_cmp(b), + _ => None, + }, + _ => None, + }; + match ord { + Some(ord) => ord, + _ => panic!( + "cannot compare {} and {}", + self.type_str(), + other.type_str() + ), + } + } +} + +impl PartialOrd for ValueRef { + fn partial_cmp(&self, other: &ValueRef) -> Option { + Some(self.cmp(other)) + } +} + +impl Hash for ValueRef { + fn hash(&self, state: &mut H) { + match &*self.rc { + Value::undefined => panic!("unsupport hash for undefined"), + Value::none => panic!("unsupport hash for none"), + Value::int_value(v) => (*v as f64).to_bits().hash(state), + Value::unit_value(_real, raw, unit) => { + raw.hash(state); + unit.hash(state); + } + Value::float_value(v) => v.to_bits().hash(state), + Value::bool_value(v) => v.hash(state), + Value::str_value(ref v) => (*v).hash(state), + Value::list_value(ref v) => { + for i in 0..v.values.len() { + v.values[i].hash(state); + } + } + Value::dict_value(ref v) => { + for (k, v) in v.values.iter() { + (*k).hash(state); + v.hash(state); + } + } + Value::schema_value(ref v) => { + for (k, v) in v.config.values.iter() { + (*k).hash(state); + v.hash(state); + } + } + Value::func_value(ref v) => { + v.fn_ptr.hash(state); + } + } + } +} + +impl Default for ValueRef { + fn default() -> Self { + Self { + rc: Rc::new(Value::undefined), + } + } +} + +impl ValueRef { + pub fn into_raw(self) -> *mut Self { + Box::into_raw(Box::new(self)) + } +} + +#[allow(non_camel_case_types)] +#[derive(Clone, PartialEq, Debug)] +pub enum Value { + undefined, + none, + bool_value(bool), + int_value(i64), + float_value(f64), + str_value(String), + list_value(ListValue), + dict_value(DictValue), + schema_value(SchemaValue), + func_value(FuncValue), + unit_value(f64, i64, String), // (Real value, raw value, unit string) +} + +impl Default for Value { + fn default() -> Self { + Self::undefined + } +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct ListValue { + pub values: Vec, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct DictValue { + pub values: IndexMap, + pub ops: IndexMap, + pub insert_indexs: IndexMap, + pub attr_map: IndexMap, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct SchemaValue { + pub name: String, + pub pkgpath: String, + pub config: Rc, + pub config_keys: Vec, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct DecoratorValue { + pub name: String, + pub args: ValueRef, + pub kwargs: ValueRef, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct FuncValue { + // TODO (refactor): SchemaFuncValue + pub fn_ptr: u64, + pub check_fn_ptr: u64, + pub closure: ValueRef, + pub external_name: String, + pub runtime_type: String, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct ErrorValue { + pub errors: Vec, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct OptionHelp { + pub name: String, + pub typ: String, + pub required: bool, + pub default_value: Option, + pub help: String, +} + +#[allow(non_snake_case)] +#[derive(PartialEq, Clone, Default, Debug)] +pub struct PanicInfo { + pub __kcl_PanicInfo__: bool, // "$__kcl_PanicInfo__$" + + pub rust_file: String, + pub rust_line: i32, + pub rust_col: i32, + + pub kcl_pkgpath: String, + pub kcl_file: String, + pub kcl_line: i32, + pub kcl_col: i32, + pub kcl_arg_msg: String, + + // only for schema check + pub kcl_config_meta_file: String, + pub kcl_config_meta_line: i32, + pub kcl_config_meta_col: i32, + pub kcl_config_meta_arg_msg: String, + + pub message: String, + pub err_type_code: i32, + pub is_warning: bool, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct ContextConfig { + pub debug_mode: bool, + + pub strict_range_check: bool, + pub disable_none: bool, + pub disable_schema_check: bool, + + pub list_option_mode: bool, +} + +#[derive(PartialEq, Clone, Debug)] +pub struct ContextBuffer { + pub kclvm_context_invoke_result: String, +} + +impl Default for ContextBuffer { + fn default() -> Self { + Self { + kclvm_context_invoke_result: "\0".to_string(), + } + } +} + +#[derive(PartialEq, Clone, Debug)] +pub struct ContextOutput { + pub stdout: String, + pub stderr: String, + + pub return_value: *mut kclvm_value_ref_t, // *mut kclvm_value_ref_t +} + +impl Default for ContextOutput { + fn default() -> Self { + Self { + stdout: "".to_string(), + stderr: "".to_string(), + return_value: std::ptr::null_mut(), + } + } +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct Context { + pub cfg: ContextConfig, + pub output: ContextOutput, + pub panic_info: PanicInfo, + + pub main_pkg_path: String, + pub main_pkg_files: Vec, + + pub imported_pkgpath: HashSet, + pub app_args: HashMap, + pub instances: RefCell>>, + pub all_types: Vec, + pub all_schemas: RefCell>, + pub import_names: IndexMap>, + pub symbol_names: Vec, + pub symbol_values: Vec, + pub func_handlers: Vec, + + pub option_helps: Vec, + pub buffer: ContextBuffer, +} + +impl Context { + pub fn new() -> Self { + Context { + instances: RefCell::new(HashMap::new()), + ..Default::default() + } + } +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct FuncHandler { + pub namespace: String, + pub fn_pointer: u64, +} + +#[repr(C)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum Kind { + Invalid = 0, + Undefined = 1, + None = 2, + Bool = 3, + Int = 4, + Float = 5, + Str = 6, + List = 7, + Dict = 8, + Schema = 9, + Error = 10, + Any = 11, + Union = 12, + BoolLit = 13, + IntLit = 14, + FloatLit = 15, + StrLit = 16, + Unit = 17, + Func = 18, +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum ConfigEntryOperationKind { + Union = 0, + Override = 1, + Insert = 2, +} + +impl Default for ConfigEntryOperationKind { + fn default() -> Self { + ConfigEntryOperationKind::Union + } +} + +impl ConfigEntryOperationKind { + pub fn from_i32(v: i32) -> Self { + match v { + x if x == ConfigEntryOperationKind::Union as i32 => ConfigEntryOperationKind::Union, + x if x == ConfigEntryOperationKind::Override as i32 => { + ConfigEntryOperationKind::Override + } + x if x == ConfigEntryOperationKind::Insert as i32 => ConfigEntryOperationKind::Insert, + _ => panic!("Invalid AttrOpKind integer {}, expected 0, 1 or 2", v), + } + } +} diff --git a/kclvm/runtime/src/api/malloc.rs b/kclvm/runtime/src/api/malloc.rs new file mode 100644 index 000000000..958d97777 --- /dev/null +++ b/kclvm/runtime/src/api/malloc.rs @@ -0,0 +1,27 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_malloc(n: i32) -> *mut u8 { + Buffer::malloc(n as usize) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_free(ptr: *mut u8) { + Buffer::free(ptr); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_strlen(ptr: *mut u8) -> kclvm_size_t { + unsafe { + let mut p = ptr; + while *p != b'\0' { + p = p.add(1); + } + (p as i32) - (ptr as i32) + } +} diff --git a/kclvm/runtime/src/api/mod.rs b/kclvm/runtime/src/api/mod.rs new file mode 100644 index 000000000..45eacd222 --- /dev/null +++ b/kclvm/runtime/src/api/mod.rs @@ -0,0 +1,16 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod kclvm; +pub use self::kclvm::*; + +pub mod buf; +pub use self::buf::*; + +pub mod malloc; +pub use self::malloc::*; + +pub mod utils; +pub use self::utils::*; + +pub mod err_type; +pub use self::err_type::*; diff --git a/kclvm/runtime/src/api/utils.rs b/kclvm/runtime/src/api/utils.rs new file mode 100644 index 000000000..0d4a5ea08 --- /dev/null +++ b/kclvm/runtime/src/api/utils.rs @@ -0,0 +1,76 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +/// New a mutable raw pointer. +pub fn new_mut_ptr(x: T) -> *mut T { + Box::into_raw(Box::new(x)) +} + +/// Free a mutable raw pointer. +pub fn free_mut_ptr(p: *mut T) { + if !p.is_null() { + unsafe { + Box::from_raw(p); + } + } +} + +/// Convert a const raw pointer to a immutable borrow. +pub fn ptr_as_ref<'a, T>(p: *const T) -> &'a T { + assert!(!p.is_null()); + unsafe { &*p } +} + +/// Convert a mutable raw pointer to a mutable borrow. +pub fn mut_ptr_as_ref<'a, T>(p: *mut T) -> &'a mut T { + assert!(!p.is_null()); + unsafe { &mut *p } +} + +/// Convert a C str pointer to a Rust &str. +pub fn c2str<'a>(s: *const i8) -> &'a str { + let s = unsafe { std::ffi::CStr::from_ptr(s) }.to_str().unwrap(); + s +} + +/// Convert a immutable borrow to a mutable borrow unsafely to enable rapid data changes. +/// Please use it with caution. +#[inline] +pub fn get_ref_mut(val: &T) -> &mut T { + unsafe { &mut *(val as *const T as *mut T) } +} + +/// Convert a raw double pinter to a Rust Vec. +pub fn convert_double_pointer_to_vec(data: &mut &mut i8, len: usize) -> Vec { + unsafe { + match std::slice::from_raw_parts(data, len) + .iter() + .map(|arg| { + std::ffi::CStr::from_ptr(*arg) + .to_str() + .map(ToString::to_string) + }) + .collect() + { + Err(_error) => Vec::::new(), + Ok(x) => x, + } + } +} + +pub fn assert_panic () + std::panic::UnwindSafe>(msg: &str, func: F) { + match std::panic::catch_unwind(func) { + Ok(_v) => { + panic!("not panic, expect={}", msg); + } + Err(e) => match e.downcast::() { + Ok(_v) => panic!("unreachable"), + Err(e) => match e.downcast::<&str>() { + Ok(v) => { + let got = v.to_string(); + assert!(got.contains(msg), "expect={}, got={}", msg, got); + } + _ => unreachable!(), + }, + }, + }; +} diff --git a/kclvm/runtime/src/base64/base64.rs b/kclvm/runtime/src/base64/base64.rs new file mode 100644 index 000000000..8329aaac0 --- /dev/null +++ b/kclvm/runtime/src/base64/base64.rs @@ -0,0 +1,57 @@ +// Copyright 2021 The KCL Authors. All rights reserved. +extern crate base64; +use base64::{decode, encode}; + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_base64_encode( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + let p = args.arg_0().unwrap(); + match &*p.rc { + Value::str_value(x) => { + let s = encode(x.clone()); + return ValueRef::str(s.as_str()).into_raw(); + } + _ => { + let ctx = Context::current_context_mut(); + ctx.set_err_type(&ErrType::TypeError_Runtime_TYPE); + + panic!("a bytes-like object is required, not '{}'", p.as_str()); + } + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_base64_decode( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let args = ptr_as_ref(args); + let p = args.arg_0().unwrap(); + match &*p.rc { + Value::str_value(x) => { + let de_str = decode(x.clone()).unwrap(); + return ValueRef::str(std::str::from_utf8(&de_str).unwrap()).into_raw(); + } + _ => { + let ctx = Context::current_context_mut(); + ctx.set_err_type(&ErrType::TypeError_Runtime_TYPE); + + panic!( + "argument should be a bytes-like object or ASCII string, not '{}'", + p.as_str() + ); + } + } +} diff --git a/kclvm/runtime/src/base64/mod.rs b/kclvm/runtime/src/base64/mod.rs new file mode 100644 index 000000000..e56a77ea1 --- /dev/null +++ b/kclvm/runtime/src/base64/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod base64; +pub use self::base64::*; diff --git a/kclvm/runtime/src/collection/collection.rs b/kclvm/runtime/src/collection/collection.rs new file mode 100644 index 000000000..b7e1dad98 --- /dev/null +++ b/kclvm/runtime/src/collection/collection.rs @@ -0,0 +1,35 @@ +//! KCL collection system module +//! +//! Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_union_all( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(arg) = args.arg_0() { + if !arg.is_truthy() || !arg.is_list() { + return ValueRef::dict(None).into_raw(); + } + let value = arg.as_list_ref(); + if value.values.is_empty() { + return ValueRef::dict(None).into_raw(); + } + let mut result = value.values[0].deep_copy(); + for (i, v) in value.values.iter().enumerate() { + if i > 0 { + result.bin_aug_union_with(v); + } + } + return result.into_raw(); + } + panic!("union_all() takes at least 1 argument (0 given)") +} diff --git a/kclvm/runtime/src/collection/mod.rs b/kclvm/runtime/src/collection/mod.rs new file mode 100644 index 000000000..552ad5847 --- /dev/null +++ b/kclvm/runtime/src/collection/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod collection; +pub use self::collection::*; diff --git a/kclvm/runtime/src/context/api.rs b/kclvm/runtime/src/context/api.rs new file mode 100644 index 000000000..398065010 --- /dev/null +++ b/kclvm/runtime/src/context/api.rs @@ -0,0 +1,358 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; +use std::os::raw::c_char; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_context_t = Context; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_error_t = KclError; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_kind_t = Kind; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_type_t = Type; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_value_t = Value; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_char_t = i8; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_size_t = i32; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_bool_t = i8; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_int_t = i64; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_float_t = f64; + +// ---------------------------------------------------------------------------- +// new/delete +// ---------------------------------------------------------------------------- + +// singleton + +#[allow(non_camel_case_types, non_upper_case_globals)] +static mut _kclvm_context_current: u64 = 0; + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_current() -> *mut kclvm_context_t { + unsafe { + if _kclvm_context_current == 0 { + _kclvm_context_current = kclvm_context_new() as u64; + } + _kclvm_context_current as *mut kclvm_context_t + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_new() -> *mut kclvm_context_t { + let p = new_mut_ptr(Context::new()); + unsafe { + _kclvm_context_current = p as u64; + } + p +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_delete(p: *mut kclvm_context_t) { + free_mut_ptr(p) +} + +// ---------------------------------------------------------------------------- +// main begin/end +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_main_begin_hook(p: *mut kclvm_context_t) { + let p = mut_ptr_as_ref(p); + p.main_begin_hook(); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_main_end_hook( + p: *mut kclvm_context_t, + return_value: *mut kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let p = mut_ptr_as_ref(p); + p.main_end_hook(return_value) +} + +// ---------------------------------------------------------------------------- +// panic_info +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_set_kcl_location( + p: *mut kclvm_context_t, + filename: *const i8, + line: i32, + col: i32, +) { + let p = mut_ptr_as_ref(p); + if !filename.is_null() { + p.set_kcl_location_info(None, Some(c2str(filename)), Some(line), Some(col)); + } else { + p.set_kcl_location_info(None, None, Some(line), Some(col)); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_set_kcl_pkgpath(p: *mut kclvm_context_t, pkgpath: *const i8) { + let p = mut_ptr_as_ref(p); + if !pkgpath.is_null() { + p.set_kcl_pkgpath(c2str(pkgpath)); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_set_kcl_filename(filename: *const i8) { + let p = Context::current_context_mut(); + if !filename.is_null() { + p.set_kcl_filename(c2str(filename)); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_set_kcl_line_col(line: i32, col: i32) { + let p = Context::current_context_mut(); + p.set_kcl_line_col(line, col); +} + +// ---------------------------------------------------------------------------- +// manage types +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_put_type(p: *mut kclvm_context_t, typ: *const kclvm_type_t) { + let p = mut_ptr_as_ref(p); + let typ = ptr_as_ref(typ); + + p.all_types.push(typ.clone()); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_clear_all_types(p: *mut kclvm_context_t) { + let p = mut_ptr_as_ref(p); + p.all_types.clear(); +} + +// ---------------------------------------------------------------------------- +// symbol +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_symbol_init( + p: *mut kclvm_context_t, + n: kclvm_size_t, + symbol_names: *const *const kclvm_char_t, +) { + let p = mut_ptr_as_ref(p); + + unsafe { + p.symbol_names.clear(); + p.symbol_values.clear(); + + let _ = std::slice::from_raw_parts(symbol_names, n as usize) + .iter() + .map(|arg| { + p.symbol_names.push(c2str(*arg).to_string()); + p.symbol_values.push(Value::default()); + }); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_symbol_num(p: *const kclvm_context_t) -> kclvm_size_t { + let p = ptr_as_ref(p); + + p.symbol_names.len() as kclvm_size_t +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_symbol_name( + p: *const kclvm_context_t, + i: kclvm_size_t, +) -> *const kclvm_char_t { + let p = ptr_as_ref(p); + return match p.symbol_names.get(i as usize) { + Some(value) => value.as_bytes().as_ptr() as *const kclvm_char_t, + None => std::ptr::null(), + }; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_symbol_value( + p: *const kclvm_context_t, + i: kclvm_size_t, +) -> *const kclvm_value_t { + let p = ptr_as_ref(p); + match p.symbol_values.get(i as usize) { + Some(v) => v as *const kclvm_value_t, + None => std::ptr::null(), + } +} + +// ---------------------------------------------------------------------------- +// args +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_args_get( + _p: *const kclvm_context_t, + _key: *const kclvm_char_t, +) -> *const kclvm_char_t { + //let p = ptr_as_ref(p); + //match p.app_args.get(c2str(key)) { + // Some(value) => (*value).as_bytes().as_ptr() as *const kclvm_char_t, + // None => std::ptr::null(), + //}; + std::ptr::null() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_args_set( + _p: *mut kclvm_context_t, + _key: *const kclvm_char_t, + _value: *const kclvm_char_t, +) { + //let p = mut_ptr_as_ref(p); + //p.app_args + // .insert(c2str(key).to_string(), c2str(value).to_string()); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_args_clear(p: *mut kclvm_context_t) { + let p = mut_ptr_as_ref(p); + p.app_args.clear(); +} + +// ---------------------------------------------------------------------------- +// CLI config +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_set_debug_mode(p: *mut kclvm_context_t, v: kclvm_bool_t) { + let p = mut_ptr_as_ref(p); + p.cfg.debug_mode = v != 0; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_set_strict_range_check(p: *mut kclvm_context_t, v: kclvm_bool_t) { + let p = mut_ptr_as_ref(p); + p.cfg.strict_range_check = v != 0; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_set_disable_none(p: *mut kclvm_context_t, v: kclvm_bool_t) { + let p = mut_ptr_as_ref(p); + p.cfg.disable_none = v != 0; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_set_disable_schema_check(p: *mut kclvm_context_t, v: kclvm_bool_t) { + let p = mut_ptr_as_ref(p); + p.cfg.disable_schema_check = v != 0; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_set_list_option_mode(p: *mut kclvm_context_t, v: kclvm_bool_t) { + let p = mut_ptr_as_ref(p); + p.cfg.list_option_mode = v != 0; +} + +// ---------------------------------------------------------------------------- +// invoke +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_invoke( + p: *mut kclvm_context_t, + method: *const c_char, + args: *const c_char, + kwargs: *const c_char, +) -> *const c_char { + let p = mut_ptr_as_ref(p); + let method = c2str(method); + + let args = kclvm_value_from_json(args); + let kwargs = kclvm_value_from_json(kwargs); + let result = _kclvm_context_invoke(p, method, args, kwargs); + + p.buffer.kclvm_context_invoke_result = ptr_as_ref(result).to_json_string_with_null(); + let result_json = p.buffer.kclvm_context_invoke_result.as_ptr() as *const i8; + + kclvm_value_delete(args); + kclvm_value_delete(kwargs); + kclvm_value_delete(result); + + result_json +} + +fn _kclvm_context_invoke( + ctx: *mut kclvm_context_t, + method: &str, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let ctx = mut_ptr_as_ref(ctx); + + let fn_addr = _kclvm_get_fn_ptr_by_name(method); + if fn_addr == 0 { + panic!("null fn ptr"); + } + + let ptr = (&fn_addr as *const u64) as *const () + as *const extern "C" fn( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, + ) -> *mut kclvm_value_ref_t; + + unsafe { (*ptr)(ctx, args, kwargs) } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_pkgpath_is_imported(pkgpath: *const kclvm_char_t) -> kclvm_bool_t { + let pkgpath = c2str(pkgpath); + let ctx = Context::current_context_mut(); + let result = ctx.imported_pkgpath.contains(pkgpath); + ctx.imported_pkgpath.insert(pkgpath.to_string()); + result as kclvm_bool_t +} + +// ---------------------------------------------------------------------------- +// END +// ---------------------------------------------------------------------------- diff --git a/kclvm/runtime/src/context/mod.rs b/kclvm/runtime/src/context/mod.rs new file mode 100644 index 000000000..20c384d9a --- /dev/null +++ b/kclvm/runtime/src/context/mod.rs @@ -0,0 +1,322 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod api; +pub use api::*; +use json_minimal::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = crate::ValueRef; + +impl crate::PanicInfo { + pub fn to_string(&self) -> String { + return format!("{:?}", self); + } + + pub fn to_json_string(&self) -> String { + let mut json = Json::new(); + + // PanicInfo + json.add(Json::OBJECT { + name: "$__kcl_PanicInfo__$".to_string(), + value: Box::new(Json::BOOL(self.__kcl_PanicInfo__)), + }); + + // is warnning? + json.add(Json::OBJECT { + name: "is_warning".to_string(), + value: Box::new(Json::BOOL(self.is_warning)), + }); + + // message + json.add(Json::OBJECT { + name: "message".to_string(), + value: Box::new(Json::STRING(self.message.to_string())), + }); + + // err_type + json.add(Json::OBJECT { + name: "err_type_code".to_string(), + value: Box::new(Json::NUMBER(self.err_type_code as f64)), + }); + + // kcl info + if !self.kcl_arg_msg.is_empty() { + json.add(Json::OBJECT { + name: "kcl_arg_msg".to_string(), + value: Box::new(Json::STRING(self.kcl_arg_msg.to_string())), + }); + } + json.add(Json::OBJECT { + name: "kcl_file".to_string(), + value: Box::new(Json::STRING(self.kcl_file.to_string())), + }); + json.add(Json::OBJECT { + name: "kcl_line".to_string(), + value: Box::new(Json::NUMBER(self.kcl_line as f64)), + }); + json.add(Json::OBJECT { + name: "kcl_col".to_string(), + value: Box::new(Json::NUMBER(self.kcl_col as f64)), + }); + + if !self.kcl_config_meta_arg_msg.is_empty() { + json.add(Json::OBJECT { + name: "kcl_config_meta_arg_msg".to_string(), + value: Box::new(Json::STRING(self.kcl_config_meta_arg_msg.to_string())), + }); + json.add(Json::OBJECT { + name: "kcl_config_meta_file".to_string(), + value: Box::new(Json::STRING(self.kcl_config_meta_file.to_string())), + }); + json.add(Json::OBJECT { + name: "kcl_config_meta_line".to_string(), + value: Box::new(Json::NUMBER(self.kcl_config_meta_line as f64)), + }); + json.add(Json::OBJECT { + name: "kcl_config_meta_col".to_string(), + value: Box::new(Json::NUMBER(self.kcl_config_meta_col as f64)), + }); + } + + // rust info + json.add(Json::OBJECT { + name: "rust_file".to_string(), + value: Box::new(Json::STRING(self.rust_file.to_string())), + }); + json.add(Json::OBJECT { + name: "rust_line".to_string(), + value: Box::new(Json::NUMBER(self.rust_line as f64)), + }); + json.add(Json::OBJECT { + name: "rust_col".to_string(), + value: Box::new(Json::NUMBER(self.rust_col as f64)), + }); + + json.print() + } +} + +impl crate::Context { + pub fn into_raw(self) -> *mut Self { + Box::into_raw(Box::new(self)) + } + + pub fn current_context() -> &'static crate::Context { + unsafe { + let ctx = kclvm_context_current(); + &*ctx + } + } + + pub fn current_context_mut() -> &'static mut crate::Context { + unsafe { + let ctx = kclvm_context_current(); + &mut *ctx + } + } + + pub fn main_begin_hook(&mut self) { + // Nothing to do + } + + pub fn main_end_hook( + &mut self, + return_value: *mut kclvm_value_ref_t, + ) -> *mut kclvm_value_ref_t { + self.output.return_value = return_value; + + if self.cfg.list_option_mode { + self.output.return_value = + crate::ValueRef::str(self.list_option_help().as_str()).into_raw(); + } + + self.output.return_value + } + + pub fn get_panic_info_json_string(&self) -> String { + self.panic_info.to_json_string() + } + + pub fn set_kcl_pkgpath(&mut self, pkgpath: &str) { + self.panic_info.kcl_pkgpath = pkgpath.to_string(); + } + + pub fn set_kcl_filename(&mut self, file: &str) { + if !file.is_empty() { + self.panic_info.kcl_file = file.to_string(); + } + } + + pub fn set_kcl_line_col(&mut self, line: i32, col: i32) { + self.panic_info.kcl_line = line; + self.panic_info.kcl_col = col; + } + + pub fn set_kcl_location_info( + &mut self, + arg_msg: Option<&str>, + file: Option<&str>, + line: Option, + col: Option, + ) { + if let Some(s) = arg_msg { + self.panic_info.kcl_arg_msg = s.to_string(); + } + if let Some(s) = file { + self.panic_info.kcl_file = s.to_string(); + } + if let Some(line) = line { + self.panic_info.kcl_line = line; + } + if let Some(col) = col { + self.panic_info.kcl_col = col; + } + } + + pub fn set_kcl_config_meta_location_info( + &mut self, + arg_msg: Option<&str>, + file: Option<&str>, + line: Option, + col: Option, + ) { + if let Some(s) = arg_msg { + self.panic_info.kcl_config_meta_arg_msg = s.to_string(); + } + if let Some(s) = file { + self.panic_info.kcl_config_meta_file = s.to_string(); + } + if let Some(line) = line { + self.panic_info.kcl_config_meta_line = line; + } + if let Some(col) = col { + self.panic_info.kcl_config_meta_col = col; + } + } + + pub fn set_err_type(&mut self, err_type: &crate::ErrType) { + self.panic_info.__kcl_PanicInfo__ = true; + self.panic_info.err_type_code = *err_type as i32; + } + pub fn set_warnning_message(&mut self, msg: &str) { + self.panic_info.__kcl_PanicInfo__ = true; + self.panic_info.message = msg.to_string(); + self.panic_info.is_warning = true; + } + + pub fn set_panic_info(&mut self, info: &std::panic::PanicInfo) { + self.panic_info.__kcl_PanicInfo__ = true; + + if let Some(s) = info.payload().downcast_ref::<&str>() { + self.panic_info.message = s.to_string(); + } else if let Some(s) = info.payload().downcast_ref::<&String>() { + self.panic_info.message = (*s).clone(); + } else if let Some(s) = info.payload().downcast_ref::() { + self.panic_info.message = (*s).clone(); + } else { + self.panic_info.message = "".to_string(); + } + + if let Some(location) = info.location() { + self.panic_info.rust_file = location.file().to_string(); + self.panic_info.rust_line = location.line() as i32; + self.panic_info.rust_col = location.column() as i32; + } else { + self.panic_info.rust_file = "".to_string(); + self.panic_info.rust_line = 0; + self.panic_info.rust_col = 0; + } + } +} + +impl crate::Context { + pub fn define_option( + &mut self, + name: &str, + typ: &str, + required: bool, + default_value: Option, + help: &str, + ) { + // check dup + for i in 0..self.option_helps.len() { + if self.option_helps[i].name == name { + if typ.is_empty() && !required && default_value == None && help.is_empty() { + return; + } + + if self.option_helps[i].typ.is_empty() { + self.option_helps[i].typ = typ.to_string(); + } + + if !self.option_helps[i].required { + self.option_helps[i].required = required; + } + if self.option_helps[i].default_value == None { + self.option_helps[i].default_value = default_value; + } + if self.option_helps[i].help.is_empty() { + self.option_helps[i].help = help.to_string(); + } + + return; + } + } + + self.option_helps.push(crate::OptionHelp { + name: name.to_string(), + typ: typ.to_string(), + required, + default_value, + help: help.to_string(), + }); + } + + pub fn list_option_help(&self) -> String { + let mut msg: String = "".to_string(); + + // name=? (required) set name value + // name=? (str,required) set name value + // a=42 set a value + // b=? set b value + // obj=? + // obj2=? + + msg.push_str("option list:\n"); + for opt in &self.option_helps { + let name = opt.name.clone(); + + let mut default_value: String = "?".to_string(); + if let Some(ref v) = opt.default_value { + default_value = (*v).clone(); + } + + let s = format!(" -D {}={}", name, default_value); + msg.push_str(s.as_str()); + + // (required) + // (str,required) + if !opt.typ.is_empty() || opt.required { + if opt.required && !opt.typ.is_empty() { + let s = format!(" ({},{})", opt.typ, "required"); + msg.push_str(s.as_str()); + } else if !opt.typ.is_empty() { + let s = format!(" ({})", opt.typ); + msg.push_str(s.as_str()); + } else { + msg.push_str(" (required)"); + } + } + + if !opt.help.is_empty() { + msg.push(' '); + msg.push_str(opt.help.as_str()); + } + + msg.push('\n'); + } + + msg = msg.as_str().trim_end_matches('\n').to_string(); + msg + } +} diff --git a/kclvm/runtime/src/crypto/crypto.rs b/kclvm/runtime/src/crypto/crypto.rs new file mode 100644 index 000000000..9c6341555 --- /dev/null +++ b/kclvm/runtime/src/crypto/crypto.rs @@ -0,0 +1,162 @@ +//! KCL crypto system module +//! +//! Copyright 2021 The KCL Authors. All rights reserved. + +extern crate md5; +extern crate sha1; +extern crate sha2; + +use sha2::{Digest, Sha224, Sha256, Sha384, Sha512}; + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +// md5(value: str, encoding: str = "utf-8") -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_crypto_md5( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(s) = args.arg_i_str(0, None) { + let hex = format!("{:x}", md5::compute(&s)); + return ValueRef::str(hex.as_ref()).into_raw(); + } + panic!("md5() missing 1 required positional argument: 'value'"); +} + +// sha1(value: str, encoding: str = "utf-8") -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_crypto_sha1( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(s) = args.arg_i_str(0, None) { + let hex = sha1::Sha1::from(s).digest().to_string(); + return ValueRef::str(hex.as_ref()).into_raw(); + } + panic!("sha1() missing 1 required positional argument: 'value'"); +} + +// sha224(value: str, encoding: str = "utf-8") -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_crypto_sha224( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(s) = args.arg_i_str(0, None) { + let mut hasher = Sha224::new(); + hasher.update(&s); + let result = hasher.finalize(); + + let mut hex = String::with_capacity(2 * Sha256::output_size()); + use std::fmt::Write; + + for byte in result { + let _ = write!(&mut hex, "{:02x}", byte); + } + + return ValueRef::str(hex.as_ref()).into_raw(); + } + panic!("sha224() missing 1 required positional argument: 'value'"); +} + +// sha256(value: str, encoding: str = "utf-8") -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_crypto_sha256( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(s) = args.arg_i_str(0, None) { + let mut hasher = Sha256::new(); + hasher.update(&s); + let result = hasher.finalize(); + + let mut hex = String::with_capacity(2 * Sha256::output_size()); + use std::fmt::Write; + + for byte in result { + let _ = write!(&mut hex, "{:02x}", byte); + } + + return ValueRef::str(hex.as_ref()).into_raw(); + } + panic!("sha256() missing 1 required positional argument: 'value'"); +} + +// sha384(value: str, encoding: str = "utf-8") -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_crypto_sha384( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(s) = args.arg_i_str(0, None) { + let mut hasher = Sha384::new(); + hasher.update(&s); + let result = hasher.finalize(); + + let mut hex = String::with_capacity(2 * Sha256::output_size()); + use std::fmt::Write; + + for byte in result { + let _ = write!(&mut hex, "{:02x}", byte); + } + + return ValueRef::str(hex.as_ref()).into_raw(); + } + panic!("sha384() missing 1 required positional argument: 'value'"); +} + +// sha512(value: str, encoding: str = "utf-8") -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_crypto_sha512( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(s) = args.arg_i_str(0, None) { + let mut hasher = Sha512::new(); + hasher.update(&s); + let result = hasher.finalize(); + + let mut hex = String::with_capacity(2 * Sha256::output_size()); + use std::fmt::Write; + + for byte in result { + let _ = write!(&mut hex, "{:02x}", byte); + } + + return ValueRef::str(hex.as_ref()).into_raw(); + } + panic!("sha512() missing 1 required positional argument: 'value'"); +} diff --git a/kclvm/runtime/src/crypto/mod.rs b/kclvm/runtime/src/crypto/mod.rs new file mode 100644 index 000000000..4105751af --- /dev/null +++ b/kclvm/runtime/src/crypto/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod crypto; +pub use self::crypto::*; diff --git a/kclvm/runtime/src/datetime/datetime.rs b/kclvm/runtime/src/datetime/datetime.rs new file mode 100644 index 000000000..79161b43d --- /dev/null +++ b/kclvm/runtime/src/datetime/datetime.rs @@ -0,0 +1,64 @@ +//! KCL datetime system module +//! +//! Copyright 2021 The KCL Authors. All rights reserved. + +extern crate chrono; + +use chrono::prelude::Local; + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +// def KMANGLED_today() -> str: + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_datetime_today( + _ctx: *mut kclvm_context_t, + _args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let s = Local::today().to_string(); + return ValueRef::str(s.as_ref()).into_raw(); +} + +// def KMANGLED_now() -> str: + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_datetime_now( + _ctx: *mut kclvm_context_t, + _args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let s = Local::now().to_string(); + return ValueRef::str(s.as_ref()).into_raw(); +} + +// def KMANGLED_ticks() -> float: + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_datetime_ticks( + _ctx: *mut kclvm_context_t, + _args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let x = Local::now().timestamp(); + ValueRef::float(x as f64).into_raw() +} + +// def KMANGLED_date() -> str: + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_datetime_date( + _ctx: *mut kclvm_context_t, + _args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let s = Local::today().to_string(); + return ValueRef::str(s.as_ref()).into_raw(); +} diff --git a/kclvm/runtime/src/datetime/mod.rs b/kclvm/runtime/src/datetime/mod.rs new file mode 100644 index 000000000..6aa3369cc --- /dev/null +++ b/kclvm/runtime/src/datetime/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod datetime; +pub use self::datetime::*; diff --git a/kclvm/runtime/src/json/json.rs b/kclvm/runtime/src/json/json.rs new file mode 100644 index 000000000..b8c5e6fa0 --- /dev/null +++ b/kclvm/runtime/src/json/json.rs @@ -0,0 +1,78 @@ +//! KCL json system module +//! +//! Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +// data, sort_keys=False, indent=None, ignore_private=False, ignore_none=False + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_json_encode( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + + let mut opt = JsonEncodeOptions::default(); + if let Some(sort_keys) = kwargs.kwarg_bool("sort_keys", None) { + opt.sort_keys = sort_keys; + } + if let Some(indent) = kwargs.kwarg_int("indent", None) { + opt.indent = indent; + } + if let Some(ignore_private) = kwargs.kwarg_bool("ignore_private", None) { + opt.ignore_private = ignore_private; + } + if let Some(ignore_none) = kwargs.kwarg_bool("ignore_none", None) { + opt.ignore_none = ignore_none; + } + + if let Some(arg0) = args.arg_i(0) { + let s = ValueRef::str(arg0.to_json_string_with_option(&opt).as_ref()); + return s.into_raw(); + } + panic!("encode() missing 1 required positional argument: 'value'") +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_json_decode( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(arg0) = args.arg_i(0) { + if let Some(x) = ValueRef::from_json(arg0.as_str().as_ref()) { + return x.into_raw(); + } + } + panic!("decode() missing 1 required positional argument: 'value'") +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_json_dump_to_file( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(data) = args.arg_i(0) { + if let Some(filename) = args.arg_i(0) { + let yaml = data.to_json_string(); + let filename = filename.as_str(); + + std::fs::write(filename, yaml).expect("Unable to write file"); + } + } + panic!("dump_to_file() missing 2 required positional arguments: 'data' and 'filename'") +} diff --git a/kclvm/runtime/src/json/mod.rs b/kclvm/runtime/src/json/mod.rs new file mode 100644 index 000000000..4859bd9cb --- /dev/null +++ b/kclvm/runtime/src/json/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod json; +pub use self::json::*; diff --git a/kclvm/runtime/src/lib.rs b/kclvm/runtime/src/lib.rs new file mode 100644 index 000000000..fad7f33eb --- /dev/null +++ b/kclvm/runtime/src/lib.rs @@ -0,0 +1,116 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use kclvm_runtime_internal_macros::runtime_fn; + +// api-spec: kclvm_context_t +// api-spec(c): typedef struct kclvm_context_t kclvm_context_t; +// api-spec(llvm): %"kclvm_context_t" = type { i8* } + +// api-spec: kclvm_type_t +// api-spec(c): typedef struct kclvm_type_t kclvm_type_t; +// api-spec(llvm): %"kclvm_type_t" = type { i8* } + +// api-spec: kclvm_value_t +// api-spec(c): typedef struct kclvm_value_t kclvm_value_t; +// api-spec(llvm): %"kclvm_value_t" = type { i8* } + +// api-spec: kclvm_value_ref_t +// api-spec(c): typedef struct kclvm_value_ref_t kclvm_value_ref_t; +// api-spec(llvm): %"kclvm_value_ref_t" = type { i8* } + +// api-spec: kclvm_iterator_t +// api-spec(c): typedef struct kclvm_iterator_t kclvm_iterator_t; +// api-spec(llvm): %"kclvm_iterator_t" = type { i8* } + +// api-spec: kclvm_buffer_t +// api-spec(c): typedef struct kclvm_buffer_t kclvm_buffer_t; +// api-spec(llvm): %"kclvm_buffer_t" = type { i8* } + +// api-spec: kclvm_kind_t +// api-spec(c): typedef enum kclvm_kind_t kclvm_kind_t; +// api-spec(llvm): %"kclvm_kind_t" = type i32 + +// api-spec: kclvm_size_t +// api-spec(c): typedef int32_t kclvm_size_t; +// api-spec(llvm): %"kclvm_size_t" = type i32 + +// api-spec: kclvm_char_t +// api-spec(c): typedef char kclvm_char_t; +// api-spec(llvm): %"kclvm_char_t" = type i8 + +// api-spec: kclvm_bool_t +// api-spec(c): typedef int8_t kclvm_bool_t; +// api-spec(llvm): %"kclvm_bool_t" = type i8 + +// api-spec: kclvm_int_t +// api-spec(c): typedef int64_t kclvm_int_t; +// api-spec(llvm): %"kclvm_int_t" = type i64 + +// api-spec: kclvm_float_t +// api-spec(c): typedef double kclvm_float_t; +// api-spec(llvm): %"kclvm_float_t" = type double + +// api-spec: kclvm_decorator_value_t +// api-spec(c): typedef struct kclvm_decorator_value_t kclvm_decorator_value_t; +// api-spec(llvm): %"kclvm_decorator_value_t" = type opaque + +pub mod api; +pub use self::api::*; + +pub mod context; +pub use self::context::*; + +pub mod types; +pub use self::types::*; + +pub mod unification; + +pub mod value; +pub use self::value::*; + +pub mod base64; +pub use self::base64::*; + +pub mod collection; +pub use self::collection::*; + +pub mod crypto; +pub use self::crypto::*; + +pub mod datetime; +pub use self::datetime::*; + +pub mod json; +pub use self::json::*; + +pub mod math; +pub use self::math::*; + +pub mod net; +pub use self::net::*; + +pub mod regex; +pub use self::regex::*; + +pub mod stdlib; +pub use self::stdlib::*; + +pub mod testing; +pub use self::testing::*; + +pub mod units; +pub use self::units::*; + +pub mod yaml; +pub use self::yaml::*; + +pub mod _kcl_run; +pub use self::_kcl_run::*; + +pub mod _kclvm; +pub use self::_kclvm::*; + +pub mod _kclvm_addr; +pub use self::_kclvm_addr::*; + +type IndexMap = indexmap::IndexMap; diff --git a/kclvm/runtime/src/math/math.rs b/kclvm/runtime/src/math/math.rs new file mode 100644 index 000000000..93f667066 --- /dev/null +++ b/kclvm/runtime/src/math/math.rs @@ -0,0 +1,360 @@ +//! KCL math system module +//! +//! Copyright 2021 The KCL Authors. All rights reserved. + +extern crate num_integer; + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +// https://docs.python.org/3/library/math.html +// https://doc.rust-lang.org/std/primitive.f64.html +// https://github.com/RustPython/RustPython/blob/main/stdlib/src/math.rs + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_ceil( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(x) = args.arg_i_int(0, None) { + return kclvm_value_Int(x); + } + if let Some(x) = args.arg_i_float(0, None) { + return kclvm_value_Int(x.ceil() as i64); + } + + panic!("ceil() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_factorial( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + fn factorial(num: i64) -> i64 { + if num >= 21 { + // overflow: 21! = 51090942171709440000 + // MaxInt64: 9223372036854775807 + panic!("factorial() result overflow"); + } + match num { + 0 => 1, + 1 => 1, + _ => factorial(num - 1) * num, + } + } + + let args = ptr_as_ref(args); + + if let Some(x) = args.arg_i_int(0, None) { + if x >= 0 { + return kclvm_value_Int(factorial(x)); + } + } + if let Some(x) = args.arg_i_float(0, None) { + if x >= 0.0 && (x as i64 as f64) == x { + return kclvm_value_Float(factorial(x as i64) as f64); + } + } + if args.args_len() > 0 { + panic!("factorial() only accepts integral values") + } + panic!("factorial() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_floor( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(x) = args.arg_i_int(0, None) { + return kclvm_value_Int(x); + } + if let Some(x) = args.arg_i_float(0, None) { + return kclvm_value_Float(x.floor()); + } + + panic!("floor() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_gcd( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(a) = args.arg_i_int(0, None) { + if let Some(b) = args.arg_i_int(1, None) { + return kclvm_value_Int(num_integer::gcd(a, b)); + } + } + + panic!( + "gcd() takes exactly two arguments ({} given)", + args.args_len() + ); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_isfinite( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(_x) = args.arg_i_int(0, None) { + return kclvm_value_Bool(true as i8); + } + if let Some(x) = args.arg_i_float(0, None) { + return kclvm_value_Bool(x.is_finite() as i8); + } + + panic!("isfinite() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_isinf( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(_x) = args.arg_i_int(0, None) { + return kclvm_value_Bool(false as i8); + } + if let Some(x) = args.arg_i_float(0, None) { + return kclvm_value_Bool(x.is_infinite() as i8); + } + + panic!("isinf() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_isnan( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(_x) = args.arg_i_int(0, None) { + return kclvm_value_Bool(false as i8); + } + if let Some(x) = args.arg_i_float(0, None) { + return kclvm_value_Bool(x.is_nan() as i8); + } + + panic!("isnan() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_modf( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(x) = args.arg_i_int(0, None) { + let list = ValueRef::list_float(&[0.0, x as f64]); + return list.into_raw(); + } + if let Some(x) = args.arg_i_float(0, None) { + if !x.is_finite() { + if x.is_infinite() { + let list = ValueRef::list_float(&[0.0_f64.copysign(x), x]); + return list.into_raw(); + } else if x.is_nan() { + let list = ValueRef::list_float(&[x, x]); + return list.into_raw(); + } + } + let list = ValueRef::list_float(&[x.fract(), x.trunc()]); + return list.into_raw(); + } + + panic!("modf() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_exp( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(x) = args.arg_i_int(0, None) { + return kclvm_value_Float((x as f64).exp()); + } + if let Some(x) = args.arg_i_float(0, None) { + return kclvm_value_Float(x.exp()); + } + panic!("exp() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_expm1( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(x) = args.arg_i_int(0, None) { + return kclvm_value_Float((x as f64).exp_m1()); + } + if let Some(x) = args.arg_i_float(0, None) { + return kclvm_value_Float(x.exp_m1()); + } + panic!("expm1() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_log( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(x) = args.arg_i_int(0, None) { + if let Some(base) = args.arg_i_float(1, Some(std::f64::consts::E)) { + return kclvm_value_Int((x as f64).log(base) as i64); + } + } + if let Some(x) = args.arg_i_float(0, None) { + if let Some(base) = args.arg_i_float(1, Some(std::f64::consts::E)) { + return kclvm_value_Float(x.log(base)); + } + } + panic!("log() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_log1p( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(x) = args.arg_i_int(0, None) { + return kclvm_value_Float(((x + 1) as f64).ln_1p()); + } + if let Some(x) = args.arg_i_float(0, None) { + return kclvm_value_Float((x + 1.0).ln_1p()); + } + panic!("log1p() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_log2( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(x) = args.arg_i_int(0, None) { + return kclvm_value_Int((x as f64).log2() as i64); + } + if let Some(x) = args.arg_i_float(0, None) { + return kclvm_value_Float(x.log2()); + } + panic!("log2() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_log10( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(x) = args.arg_i_int(0, None) { + return kclvm_value_Float((x as f64).log10()); + } + if let Some(x) = args.arg_i_float(0, None) { + return kclvm_value_Float(x.log10()); + } + panic!("log10() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_pow( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(x) = args.arg_i_int(0, None) { + if let Some(n) = args.arg_i_int(1, None) { + if n < 0 { + return kclvm_value_Float((x as f64).powf(n as f64)); + } else { + return kclvm_value_Int(x.pow(n as u32)); + } + } + if let Some(n) = args.arg_i_float(1, None) { + return kclvm_value_Float((x as f64).powf(n)); + } + } + if let Some(x) = args.arg_i_float(0, None) { + if let Some(n) = args.arg_i_int(1, None) { + return kclvm_value_Float(x.powi(n as i32)); + } + if let Some(n) = args.arg_i_float(1, None) { + return kclvm_value_Float(x.powf(n)); + } + } + panic!("pow() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_math_sqrt( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(x) = args.arg_i_int(0, None) { + return kclvm_value_Float((x as f64).sqrt()); + } + if let Some(x) = args.arg_i_float(0, None) { + return kclvm_value_Float(x.sqrt()); + } + panic!("sqrt() takes exactly one argument (0 given)"); +} diff --git a/kclvm/runtime/src/math/mod.rs b/kclvm/runtime/src/math/mod.rs new file mode 100644 index 000000000..fb00f12d7 --- /dev/null +++ b/kclvm/runtime/src/math/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod math; +pub use self::math::*; diff --git a/kclvm/runtime/src/net/mod.rs b/kclvm/runtime/src/net/mod.rs new file mode 100644 index 000000000..84dc29eb9 --- /dev/null +++ b/kclvm/runtime/src/net/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod net; +pub use self::net::*; diff --git a/kclvm/runtime/src/net/net.rs b/kclvm/runtime/src/net/net.rs new file mode 100644 index 000000000..6c6da329e --- /dev/null +++ b/kclvm/runtime/src/net/net.rs @@ -0,0 +1,418 @@ +//! KCL net system module +//! +//! Copyright 2021 The KCL Authors. All rights reserved. + +use std::net::Ipv4Addr; +use std::net::Ipv6Addr; +use std::str::FromStr; + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +// split_host_port(ip_end_point: str) -> List[str] + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_split_host_port( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(string) = args.arg_i_str(0, None) { + let mut list = ValueRef::list(None); + for s in string.split(':') { + list.list_append(&ValueRef::str(s)); + } + return list.into_raw(); + } + + panic!("split_host_port() missing 1 required positional argument: 'ip_end_point'"); +} + +// join_host_port(host, port) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_join_host_port( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(host) = args.arg_i_str(0, None) { + if let Some(port) = args.arg_i_int(1, None) { + let s = format!("{}:{}", host, port); + return ValueRef::str(s.as_ref()).into_raw(); + } + if let Some(port) = args.arg_i_str(1, None) { + let s = format!("{}:{}", host, port); + return ValueRef::str(s.as_ref()).into_raw(); + } + } + panic!("join_host_port() missing 2 required positional arguments: 'host' and 'port'"); +} + +// fqdn(name: str = '') -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_fqdn( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(_name) = args.arg_i_str(0, Some("".to_string())) { + todo!("todo"); + } + panic!("fqdn() missing 1 required positional argument: 'name'"); +} + +// parse_IP(ip) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_parse_IP( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + kclvm_net_IP_string(ctx, args, kwargs) +} + +// to_IP4(ip) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_to_IP4( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + kclvm_net_IP_string(ctx, args, kwargs) +} + +// to_IP16(ip) -> int + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_to_IP16( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + kclvm_net_IP_string(ctx, args, kwargs) +} + +// IP_string(ip: str) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_IP_string( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(ip) = args.arg_i_str(0, None) { + if let Ok(addr) = Ipv4Addr::from_str(ip.as_ref()) { + let s = format!("{}", addr); + return ValueRef::str(s.as_ref()).into_raw(); + } + if let Ok(addr) = Ipv6Addr::from_str(ip.as_ref()) { + let s = format!("{}", addr); + return ValueRef::str(s.as_ref()).into_raw(); + } + + return kclvm_value_False(); + } + + panic!("IP_string() missing 1 required positional argument: 'ip'"); +} + +// is_IPv4(ip: str) -> bool + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_is_IPv4( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(ip) = args.arg_i_str(0, None) { + if let Ok(_addr) = Ipv4Addr::from_str(ip.as_ref()) { + return kclvm_value_True(); + } + if let Ok(_addr) = Ipv6Addr::from_str(ip.as_ref()) { + return kclvm_value_False(); + } + + return kclvm_value_False(); + } + + panic!("is_IPv4() missing 1 required positional argument: 'ip'"); +} + +// is_IP(ip: str) -> bool + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_is_IP( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(ip) = args.arg_i_str(0, None) { + if let Ok(..) = Ipv4Addr::from_str(ip.as_ref()) { + return kclvm_value_True(); + } + if let Ok(..) = Ipv6Addr::from_str(ip.as_ref()) { + return kclvm_value_True(); + } + + return kclvm_value_False(); + } + + panic!("is_IP() missing 1 required positional argument: 'ip'"); +} + +// is_loopback_IP(ip: str) -> bool + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_is_loopback_IP( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(ip) = args.arg_i_str(0, None) { + if let Ok(addr) = Ipv4Addr::from_str(ip.as_ref()) { + let x = addr.is_loopback(); + return kclvm_value_Bool(x as i8); + } + if let Ok(addr) = Ipv6Addr::from_str(ip.as_ref()) { + let x = addr.is_loopback(); + return kclvm_value_Bool(x as i8); + } + + return kclvm_value_False(); + } + + panic!("is_loopback_IP() missing 1 required positional argument: 'ip'"); +} + +// is_multicast_IP(ip: str) -> bool + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_is_multicast_IP( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(ip) = args.arg_i_str(0, None) { + if let Ok(addr) = Ipv4Addr::from_str(ip.as_ref()) { + let x = addr.is_multicast(); + return kclvm_value_Bool(x as i8); + } + if let Ok(addr) = Ipv6Addr::from_str(ip.as_ref()) { + let x = addr.is_multicast(); + return kclvm_value_Bool(x as i8); + } + + return kclvm_value_False(); + } + + panic!("kclvm_net_is_multicast_IP() missing 1 required positional argument: 'ip'"); +} + +// is_interface_local_multicast_IP(ip: str) -> bool + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_is_interface_local_multicast_IP( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(ip) = args.arg_i_str(0, None) { + if let Ok(addr) = Ipv4Addr::from_str(ip.as_ref()) { + let is_site_local = false; // TODO + let x = is_site_local && addr.is_multicast(); + return kclvm_value_Bool(x as i8); + } + if let Ok(_addr) = Ipv6Addr::from_str(ip.as_ref()) { + todo!("todo"); + } + + return kclvm_value_False(); + } + + panic!("is_interface_local_multicast_IP() missing 1 required positional argument: 'ip'"); +} + +// is_link_local_multicast_IP(ip: str) -> bool + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_is_link_local_multicast_IP( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(ip) = args.arg_i_str(0, None) { + if let Ok(addr) = Ipv4Addr::from_str(ip.as_ref()) { + let x = addr.is_link_local() && addr.is_multicast(); + return kclvm_value_Bool(x as i8); + } + if let Ok(addr) = Ipv6Addr::from_str(ip.as_ref()) { + let is_link_local = false; // TODO + let x = is_link_local && addr.is_multicast(); + return kclvm_value_Bool(x as i8); + } + + return kclvm_value_False(); + } + + panic!("is_link_local_multicast_IP() missing 1 required positional argument: 'ip'"); +} + +// is_link_local_unicast_IP(ip: str) -> bool + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_is_link_local_unicast_IP( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(ip) = args.arg_i_str(0, None) { + if let Ok(addr) = Ipv4Addr::from_str(ip.as_ref()) { + let x = addr.is_link_local() && (!addr.is_multicast()); + return kclvm_value_Bool(x as i8); + } + if let Ok(_addr) = Ipv6Addr::from_str(ip.as_ref()) { + let x = Ipv6Addr_is_unicast_link_local(&_addr) && (!_addr.is_multicast()); + return kclvm_value_Bool(x as i8); + } + return kclvm_value_False(); + } + + panic!("is_link_local_unicast_IP() missing 1 required positional argument: 'ip'"); +} + +#[allow(non_camel_case_types, non_snake_case)] +pub const fn Ipv6Addr_is_unicast_link_local(_self: &Ipv6Addr) -> bool { + (_self.segments()[0] & 0xffc0) == 0xfe80 +} + +// is_global_unicast_IP(ip: str) -> bool + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_is_global_unicast_IP( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(ip) = args.arg_i_str(0, None) { + if let Ok(addr) = Ipv4Addr::from_str(ip.as_ref()) { + let x = Ipv4Addr_is_global(&addr) && (!addr.is_multicast()); + return kclvm_value_Bool(x as i8); + } + if let Ok(addr) = Ipv6Addr::from_str(ip.as_ref()) { + return kclvm_value_Bool(addr.is_multicast() as i8); + } + + return kclvm_value_False(); + } + + panic!("is_global_unicast_IP() missing 1 required positional argument: 'ip'"); +} + +#[allow(non_camel_case_types, non_snake_case)] +fn Ipv4Addr_is_global(_self: &std::net::Ipv4Addr) -> bool { + // check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two + // globally routable addresses in the 192.0.0.0/24 range. + if u32::from_be_bytes(_self.octets()) == 0xc0000009 + || u32::from_be_bytes(_self.octets()) == 0xc000000a + { + return true; + } + !_self.is_private() + && !_self.is_loopback() + && !_self.is_link_local() + && !_self.is_broadcast() + && !_self.is_documentation() + && !Ipv4Addr_is_shared(_self) // _self.is_shared() + && !Ipv4Addr_is_ietf_protocol_assignment(_self) // _self.is_ietf_protocol_assignment() + && !Ipv4Addr_is_reserved(_self) // _self.is_reserved() + && !Ipv4Addr_is_benchmarking(_self) // _self.is_benchmarking() + // Make sure the address is not in 0.0.0.0/8 + && _self.octets()[0] != 0 +} + +#[allow(non_camel_case_types, non_snake_case)] +const fn Ipv4Addr_is_shared(_self: &std::net::Ipv4Addr) -> bool { + _self.octets()[0] == 100 && (_self.octets()[1] & 0b1100_0000 == 0b0100_0000) +} +#[allow(non_camel_case_types, non_snake_case)] +const fn Ipv4Addr_is_ietf_protocol_assignment(_self: &std::net::Ipv4Addr) -> bool { + _self.octets()[0] == 192 && _self.octets()[1] == 0 && _self.octets()[2] == 0 +} +#[allow(non_camel_case_types, non_snake_case)] +const fn Ipv4Addr_is_reserved(_self: &std::net::Ipv4Addr) -> bool { + _self.octets()[0] & 240 == 240 && !_self.is_broadcast() +} +#[allow(non_camel_case_types, non_snake_case)] +const fn Ipv4Addr_is_benchmarking(_self: &std::net::Ipv4Addr) -> bool { + _self.octets()[0] == 198 && (_self.octets()[1] & 0xfe) == 18 +} + +// is_unspecified_IP(ip: str) -> bool + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_is_unspecified_IP( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(ip) = args.arg_i_str(0, None) { + if let Ok(addr) = Ipv4Addr::from_str(ip.as_ref()) { + return kclvm_value_Bool(addr.is_unspecified() as i8); + } + if let Ok(addr) = Ipv6Addr::from_str(ip.as_ref()) { + return kclvm_value_Bool(addr.is_unspecified() as i8); + } + return kclvm_value_False(); + } + panic!("is_unspecified_IP() missing 1 required positional argument: 'ip'"); +} diff --git a/kclvm/runtime/src/regex/mod.rs b/kclvm/runtime/src/regex/mod.rs new file mode 100644 index 000000000..71be7d77c --- /dev/null +++ b/kclvm/runtime/src/regex/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod regex; +pub use self::regex::*; diff --git a/kclvm/runtime/src/regex/regex.rs b/kclvm/runtime/src/regex/regex.rs new file mode 100644 index 000000000..766005f9b --- /dev/null +++ b/kclvm/runtime/src/regex/regex.rs @@ -0,0 +1,200 @@ +//! KCL regex system module +//! regex.match(string: str, pattern: str) -> bool +//! regex.replace(string: str, pattern: str, replace: str, count: int = 0) -> str +//! regex.compile(pattern: str) -> bool +//! regex.findall(string: str, pattern: str) -> [str] +//! regex.search(string: str, pattern: str) -> bool +//! regex.split(string: str, pattern: str, maxsplit: int = 0) -> [str] +//! +//! Copyright 2021 The KCL Authors. All rights reserved. + +extern crate fancy_regex; + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +// def KMANGLED_match(string: str, pattern: str) -> bool: + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_regex_match( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(string) = args.arg_i_str(0, None) { + if let Some(pattern) = args.arg_i_str(1, None) { + let re = fancy_regex::Regex::new(pattern.as_ref()).unwrap(); + match re.is_match(string.as_ref()) { + Ok(ok) => { + if ok { + return kclvm_value_Bool(1); + } else { + return kclvm_value_Bool(0); + } + } + _ => return kclvm_value_Bool(0), + } + } + } + + panic!("match() missing 2 required positional arguments: 'string' and 'pattern'") +} + +// def KMANGLED_replace(string: str, pattern: str, replace: str, count: int = 0): + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_regex_replace( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(string) = args.arg_i_str(0, None) { + if let Some(pattern) = args.arg_i_str(1, None) { + if let Some(replace) = args.arg_i_str(2, None) { + if let Some(count) = args.arg_i_int(3, Some(0)) { + let re = fancy_regex::Regex::new(pattern.as_ref()).unwrap(); + let s = re.replacen(string.as_ref(), count as usize, replace.as_ref() as &str); + return ValueRef::str(&s).into_raw(); + } + } + } + } + panic!("replace() missing 3 required positional arguments: 'string', 'pattern', and 'replace"); +} + +// def KMANGLED_compile(pattern: str) -> bool: + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_regex_compile( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(pattern) = args.arg_i_str(0, None) { + match fancy_regex::Regex::new(pattern.as_ref()) { + Ok(_) => return kclvm_value_Bool(1), + _ => return kclvm_value_Bool(0), + } + } + panic!("compile() missing 2 required positional arguments: 'string' and 'pattern'") +} + +// def KMANGLED_findall(string: str, pattern: str) -> [str]: + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_regex_findall( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(string) = args.arg_i_str(0, None) { + if let Some(pattern) = args.arg_i_str(1, None) { + let mut list = ValueRef::list(None); + + for x in fancy_regex::Regex::new(pattern.as_ref()) + .unwrap() + .captures_iter(string.as_ref()) + .flatten() + { + let len = x.len(); + if len < 3 { + list.list_append(&ValueRef::str(x.get(0).unwrap().as_str())); + } else { + let mut sub_list = ValueRef::list(None); + for i in 1..len { + sub_list.list_append(&ValueRef::str(x.get(i).unwrap().as_str())); + } + list.list_append(&sub_list) + } + } + + return list.into_raw(); + } + } + + panic!("findall() missing 2 required positional arguments: 'string' and 'pattern'") +} + +// def KMANGLED_search(string: str, pattern: str): + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_regex_search( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(string) = args.arg_i_str(0, None) { + if let Some(pattern) = args.arg_i_str(1, None) { + let re = fancy_regex::Regex::new(pattern.as_ref()).unwrap(); + + if let Ok(Some(..)) = re.find(string.as_ref()) { + return kclvm_value_Bool(1); + } + return kclvm_value_Bool(0); + } + } + panic!("search() missing 2 required positional arguments: 'string' and 'pattern'"); +} + +// def KMANGLED_split(string: str, pattern: str, maxsplit: int = 0): + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_regex_split( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(string) = args.arg_i_str(0, None) { + if let Some(pattern) = args.arg_i_str(1, None) { + if let Some(maxsplit) = args.arg_i_int(2, Some(0)) { + let mut list = ValueRef::list(None); + + let re = fancy_regex::Regex::new(pattern.as_ref()).unwrap(); + + let mut fields: Vec = Vec::new(); + let mut current_pos = 0; + loop { + let capture = re + .captures_from_pos(string.as_ref(), current_pos) + .map_or(None, |c| c); + if let Some(Some(cap)) = capture.map(|c| c.get(0)) { + fields.push(string[current_pos..cap.start()].to_string()); + if maxsplit > 0 && fields.len() >= (maxsplit as usize) { + break; + } + current_pos = cap.end(); + } else { + fields.push(string[current_pos..].to_string()); + break; + } + } + + for s in fields { + list.list_append(&ValueRef::str(s.as_ref())); + } + return list.into_raw(); + } + } + } + panic!("split() missing 2 required positional arguments: 'string' and 'pattern'"); +} diff --git a/kclvm/runtime/src/stdlib/assert_api.rs b/kclvm/runtime/src/stdlib/assert_api.rs new file mode 100644 index 000000000..478a3a254 --- /dev/null +++ b/kclvm/runtime/src/stdlib/assert_api.rs @@ -0,0 +1,21 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_assert(value: *const kclvm_value_ref_t, msg: *const kclvm_value_ref_t) { + let value = ptr_as_ref(value); + let msg = ptr_as_ref(msg); + + if !value.is_truthy() { + let ctx = Context::current_context_mut(); + ctx.set_err_type(&ErrType::AssertionError_TYPE); + + let msg = msg.as_str(); + panic!("{}", msg); + } +} diff --git a/kclvm/runtime/src/stdlib/builtin.rs b/kclvm/runtime/src/stdlib/builtin.rs new file mode 100644 index 000000000..be3464dea --- /dev/null +++ b/kclvm/runtime/src/stdlib/builtin.rs @@ -0,0 +1,916 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use std::{collections::HashSet, ops::Index}; + +use crate::*; + +impl Context { + pub fn builtin_option_init(&mut self, key: &str, value: &str) { + if let Some(x) = ValueRef::from_json(value) { + self.app_args.insert(key.to_string(), x.into_raw() as u64); + return; + } + self.app_args + .insert(key.to_string(), ValueRef::str(value).into_raw() as u64); + } + + pub fn builtin_option_reset(&mut self) { + for (_, x) in self.app_args.iter() { + if (*x) != 0 { + kclvm_value_delete((*x) as *mut ValueRef); + } + } + self.app_args.clear(); + } +} + +impl ValueRef { + pub fn any_true(&self) -> bool { + match &*self.rc { + Value::list_value(ref list) => { + for x in list.values.iter() { + if x.is_truthy() { + return true; + } + } + false + } + Value::dict_value(ref dict) => { + for (_k, x) in dict.values.iter() { + if x.is_truthy() { + return true; + } + } + false + } + Value::schema_value(ref schema) => { + for (_k, x) in schema.config.values.iter() { + if x.is_truthy() { + return true; + } + } + false + } + _ => false, + } + } + + pub fn all_true(&self) -> bool { + match &*self.rc { + Value::list_value(ref list) => { + for x in list.values.iter() { + if !x.is_truthy() { + return false; + } + } + true + } + Value::dict_value(ref dict) => { + for (_k, x) in dict.values.iter() { + if !x.is_truthy() { + return false; + } + } + true + } + Value::schema_value(ref schema) => { + for (_k, x) in schema.config.values.iter() { + if x.is_truthy() { + return false; + } + } + true + } + _ => false, + } + } + + pub fn isunique(&self) -> bool { + match &*self.rc { + Value::list_value(ref list) => { + let mut set: HashSet<&ValueRef> = HashSet::new(); + for x in list.values.iter() { + if set.contains(x) { + return false; + } + set.insert(x); + } + true + } + _ => false, + } + } + + pub fn sorted(&self, reverse: Option<&ValueRef>) -> ValueRef { + let reverse = if let Some(v) = reverse { + v.as_bool() + } else { + false + }; + match &*self.rc { + Value::str_value(s) => { + let mut list = ListValue::default(); + for c in s.chars() { + list.values + .push(Self::from(Value::str_value(String::from(c)))); + } + let values = &mut list.values; + if reverse { + values.sort_by(|a, b| b.partial_cmp(a).unwrap()); + } else { + values.sort(); + } + Self::from(Value::list_value(list)) + } + Value::list_value(_) => { + let mut list = self.deep_copy(); + let list_ref = list.as_list_mut_ref(); + let values = &mut list_ref.values; + if reverse { + values.sort_by(|a, b| b.partial_cmp(a).unwrap()); + } else { + values.sort(); + } + list + } + Value::dict_value(dict) => { + let mut list = ListValue::default(); + for (k, _v) in dict.values.iter() { + list.values + .push(Self::from(Value::str_value(k.to_string()))); + } + let values = &mut list.values; + if reverse { + values.sort_by(|a, b| b.partial_cmp(a).unwrap()); + } else { + values.sort(); + } + Self::from(Value::list_value(list)) + } + _ => panic!("sorted only for str|list|dict type"), + } + } + + pub fn convert_to_int(&self, base: Option<&ValueRef>) -> ValueRef { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_i32 = ctx.cfg.strict_range_check; + let strict_range_check_i64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match &*self.rc { + Value::int_value(ref v) => ValueRef::int(*v), + Value::float_value(ref v) => ValueRef::int(*v as i64), + Value::unit_value(ref v, raw, unit) => { + let v_i128 = crate::real_uint_value(*raw, unit); + let int_32_overflow = strict_range_check_i32 && v_i128 != ((v_i128 as i32) as i128); + let int_64_overflow = strict_range_check_i64 && v_i128 != ((v_i128 as i64) as i128); + + if ctx.cfg.debug_mode { + if int_32_overflow { + let ctx = Context::current_context_mut(); + ctx.set_err_type(&ErrType::IntOverflow_TYPE); + + panic!("{}: A 32 bit integer overflow", v_i128); + } + if int_64_overflow { + let ctx = Context::current_context_mut(); + ctx.set_err_type(&ErrType::IntOverflow_TYPE); + + panic!("{}: A 64 bit integer overflow", v_i128); + } + } + + ValueRef::int(*v as i64) + } + Value::bool_value(ref v) => ValueRef::int(*v as i64), + Value::str_value(ref v) => { + let base = if let Some(v) = base { v.as_int() } else { 10 }; + let number_str = to_quantity(v.as_str()).to_string(); + let v: i64 = + i64::from_str_radix(number_str.as_str(), base as u32).unwrap_or_else(|_| { + panic!( + "invalid literal for int() with base {}: '{}'", + base, + self.to_string() + ) + }); + ValueRef::int(v) + } + _ => panic!( + "int() argument must be a string, a bytes-like object or a number, not '{}'", + self.type_str() + ), + } + } + + pub fn convert_to_float(&self) -> ValueRef { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_i32 = ctx.cfg.strict_range_check; + let strict_range_check_i64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match &*self.rc { + Value::int_value(ref v) => ValueRef::float(*v as f64), + Value::float_value(ref v) => { + let float32_overflow = strict_range_check_i32 && (*v as f32).is_infinite(); + let float64_overflow = strict_range_check_i64 && (*v).is_infinite(); + + if float32_overflow { + ctx.set_err_type(&ErrType::FloatOverflow_TYPE); + + panic!("inf: A 32-bit floating point number overflow"); + } + if float64_overflow { + ctx.set_err_type(&ErrType::FloatOverflow_TYPE); + + panic!("inf: A 64-bit floating point number overflow"); + } + ValueRef::float(*v) + } + Value::unit_value(ref v, _, _) => ValueRef::float(*v), + Value::bool_value(ref v) => ValueRef::float((*v as i64) as f64), + Value::str_value(ref v) => { + let v: f64 = v.parse().unwrap_or_else(|_| { + panic!( + "invalid literal for float() with base 10: '{}'", + self.to_string() + ) + }); + let float32_overflow = strict_range_check_i32 && (v as f32).is_infinite(); + let float64_overflow = strict_range_check_i64 && (v).is_infinite(); + if float32_overflow { + ctx.set_err_type(&ErrType::FloatOverflow_TYPE); + + panic!("inf: A 32-bit floating point number overflow"); + } + if float64_overflow { + ctx.set_err_type(&ErrType::FloatOverflow_TYPE); + + panic!("inf: A 64-bit floating point number overflow"); + } + ValueRef::float(v) + } + _ => panic!( + "invalid literal for float() with base 10: '{}'", + self.to_string() + ), + } + } + + pub fn max_value(&self) -> ValueRef { + self.filter(|x, y| x.cmp_greater_than(y)) + } + + pub fn min_value(&self) -> ValueRef { + self.filter(|x, y| x.cmp_less_than(y)) + } + + pub fn filter(&self, filter: fn(&ValueRef, &ValueRef) -> bool) -> ValueRef { + match &*self.rc { + Value::str_value(ref s) => { + if s.is_empty() { + panic!("arg is an empty str"); + } + let mut result = s.chars().next().unwrap(); + for ch in s.chars() { + if filter( + &ValueRef::str(&ch.to_string()), + &ValueRef::str(&result.to_string()), + ) { + result = ch; + } + } + ValueRef::str(&result.to_string()) + } + Value::list_value(ref list) => { + if list.values.is_empty() { + panic!("arg is an empty list"); + } + let mut result = list.values.first().unwrap(); + for val in list.values.iter() { + if filter(val, result) { + result = val; + } + } + result.clone() + } + Value::dict_value(ref dict) => { + if dict.values.is_empty() { + panic!("arg is an empty dict"); + } + let keys: Vec = dict.values.keys().map(|s| (*s).clone()).collect(); + let mut result = keys.first().unwrap(); + for key in keys.iter() { + if filter( + &ValueRef::str(&key.to_string()), + &ValueRef::str(&result.to_string()), + ) { + result = key; + } + } + ValueRef::str(&result.to_string()) + } + Value::schema_value(ref schema) => { + if schema.config.values.is_empty() { + panic!("arg is an empty dict"); + } + let keys: Vec = schema.config.values.keys().map(|s| (*s).clone()).collect(); + let mut result = keys.first().unwrap(); + for key in keys.iter() { + if filter( + &ValueRef::str(&key.to_string()), + &ValueRef::str(&result.to_string()), + ) { + result = key; + } + } + ValueRef::str(&result.to_string()) + } + _ => panic!("{} object is not iterable", self.type_str()), + } + } + + pub fn hex(&self) -> ValueRef { + match &*self.rc { + Value::int_value(val) => { + if *val == i64::MIN { + ValueRef::str("-0x8000000000000000") + } else if *val >= 0 { + ValueRef::str(format!("0x{:X}", val.abs()).to_lowercase().as_str()) + } else { + ValueRef::str(format!("-0x{:X}", val.abs()).to_lowercase().as_str()) + } + } + _ => ValueRef::undefined(), + } + } + + pub fn oct(&self) -> ValueRef { + match &*self.rc { + Value::int_value(val) => { + if *val == i64::MIN { + ValueRef::str("-01000000000000000000000") + } else if *val >= 0 { + ValueRef::str(format!("0o{:o}", val.abs()).as_str()) + } else { + ValueRef::str(format!("-0o{:o}", val.abs()).as_str()) + } + } + _ => ValueRef::undefined(), + } + } + + pub fn bin(&self) -> ValueRef { + match &*self.rc { + Value::int_value(val) => { + if *val == i64::MIN { + ValueRef::str( + "-0b1000000000000000000000000000000000000000000000000000000000000000", + ) + } else if *val >= 0 { + ValueRef::str(format!("0b{:b}", val.abs()).as_str()) + } else { + ValueRef::str(format!("-0b{:b}", val.abs()).as_str()) + } + } + _ => ValueRef::undefined(), + } + } + + pub fn sum(&self, init_value: &ValueRef) -> ValueRef { + match &*self.rc { + Value::list_value(list) => { + let mut result = match &*init_value.rc { + Value::str_value(_str) => panic!("sum() can't sum strings"), + _ => init_value.clone(), + }; + for val in list.values.iter() { + //xx_bin_aug_add() might modify the value of init_value + result = result.bin_add(val) + } + result + } + _ => ValueRef::undefined(), + } + } + + pub fn abs(&self) -> ValueRef { + match &*self.rc { + Value::int_value(val) => ValueRef::int(val.abs()), + Value::float_value(val) => ValueRef::float(val.abs()), + _ => ValueRef::undefined(), + } + } + + pub fn ord(&self) -> ValueRef { + match &*self.rc { + Value::str_value(str) => { + let string_len = str.chars().count(); + if string_len != 1 { + panic!( + " ord() expected string of length 1, but string of length {} found", + str.len() + ) + } + let ch = str.chars().next().unwrap(); + ValueRef::int(ch as i64) + } + _ => ValueRef::undefined(), + } + } + + pub fn zip(&self) -> ValueRef { + match &*self.rc { + Value::list_value(list) => { + let mut iters = Vec::new(); + for val in list.values.iter() { + iters.push(val.iter()); + } + let mut zip_lists = Vec::new(); + let mut pre_len = 0; + loop { + for (i, iter) in iters.iter_mut().enumerate() { + if iter.is_end() { + if i != 0 { + zip_lists.pop(); + } + let mut result = ValueRef::list(None); + for val in zip_lists.iter() { + result.list_append(val); + } + return result; + } + if zip_lists.len() <= pre_len { + zip_lists.push(ValueRef::list(None)); + } + zip_lists[pre_len].list_append(iter.next(list.values.index(i)).unwrap()); + } + pre_len += 1; + } + } + _ => ValueRef::undefined(), + } + } +} + +pub fn list(iterable: Option<&ValueRef>) -> ValueRef { + match iterable { + Some(val) => { + let mut iter = val.iter(); + let mut result = ValueRef::list(None); + while !iter.is_end() { + result.list_append(iter.next(val).unwrap()); + } + result + } + _ => ValueRef::list(None), + } +} + +pub fn dict(iterable: Option<&ValueRef>) -> ValueRef { + match iterable { + Some(val) => { + let mut iter = val.iter(); + let mut result = ValueRef::dict(None); + while !iter.is_end() { + iter.next(val); + let elem = &iter.cur_val.clone(); + let k = &iter.cur_key.clone(); + match &*k.rc { + Value::str_value(str) => { + result.dict_insert(str.as_str(), elem, Default::default(), 0); + } + _ => { + let mut elem_iter = elem.iter(); + if elem_iter.len != 2 { + panic!("dictionary update sequence element #{} has length {}; 2 is required",iter.pos-1,elem_iter.len); + } + let k = elem_iter.next(val).unwrap().to_string(); + let v = elem_iter.next(val).unwrap(); + result.dict_insert(k.as_str(), v, Default::default(), 0); + } + } + } + result + } + _ => ValueRef::dict(None), + } +} + +pub fn range(start: &ValueRef, stop: &ValueRef, step: &ValueRef) -> ValueRef { + match (&*start.rc, &*stop.rc, &*step.rc) { + (Value::int_value(start), Value::int_value(stop), Value::int_value(step)) => { + if *step == 0 { + panic!("range() step argument must not be zero"); + } + let mut cur = *start; + let mut list = ValueRef::list(None); + let cmp = if *step > 0 { + |x, y| x < y + } else { + |x, y| x > y + }; + while cmp(cur, *stop) { + list.list_append(&ValueRef::int(cur)); + cur += step; + } + list + } + _ => ValueRef::undefined(), + } +} + +/// Check if the modular result of a and b is 0 +pub fn multiplyof(a: &ValueRef, b: &ValueRef) -> ValueRef { + match (&*a.rc, &*b.rc) { + (Value::int_value(a), Value::int_value(b)) => ValueRef::bool(a % b == 0), + _ => ValueRef::undefined(), + } +} + +pub fn pow(x: &ValueRef, y: &ValueRef, z: &ValueRef) -> ValueRef { + match &*z.rc { + Value::int_value(z) => { + if *z == 0 { + panic!("pow() 3rd argument cannot be 0") + } + match (&*x.rc, &*y.rc) { + (Value::int_value(x), Value::int_value(y)) => match (*y).cmp(&0) { + std::cmp::Ordering::Equal => ValueRef::int(1), + std::cmp::Ordering::Greater => { + let mut ans = 1_i128; + let mut base = *x as i128; + let m = *z as i128; + let mut exp = *y as i128; + while exp > 0 { + if exp % 2 == 1 { + ans = (ans * base) % m; + } + base = (base * base) % m; + exp /= 2; + } + ValueRef::int(ans as i64) + } + std::cmp::Ordering::Less => { + panic!("pow() 2nd argument cannot be negative when 3rd argument specified") + } + }, + _ => panic!("pow() 3rd argument not allowed unless all arguments are integers"), + } + } + _ => match (&*x.rc, &*y.rc) { + (Value::int_value(x), Value::int_value(y)) => { + if *y >= 0 { + ValueRef::int((*x as f64).powf(*y as f64) as i64) + } else { + ValueRef::float((*x as f64).powf(*y as f64)) + } + } + (Value::int_value(x), Value::float_value(y)) => ValueRef::float((*x as f64).powf(*y)), + (Value::float_value(x), Value::int_value(y)) => ValueRef::float((*x).powf(*y as f64)), + (Value::float_value(x), Value::float_value(y)) => ValueRef::float((*x).powf(*y)), + _ => ValueRef::undefined(), + }, + } +} + +pub fn round(number: &ValueRef, ndigits: &ValueRef) -> ValueRef { + match &*ndigits.rc { + Value::int_value(ndigits) => match &*number.rc { + Value::int_value(number) => ValueRef::float(*number as f64), + Value::float_value(number) => { + if *ndigits == 0 { + ValueRef::float(number.round()) + } else { + let (y, pow1, pow2) = if *ndigits >= 0 { + // according to cpython: pow1 and pow2 are each safe from overflow, but + // pow1*pow2 ~= pow(10.0, ndigits) might overflow + let (pow1, pow2) = if *ndigits > 22 { + ((10.0_f64).powf((*ndigits - 22) as f64), 1e22) + } else { + ((10.0_f64).powf(*ndigits as f64), 1.0) + }; + let y = ((*number) * pow1) * pow2; + if !y.is_finite() { + return ValueRef::float(*number); + } + (y, pow1, Some(pow2)) + } else { + let pow1 = (10.0_f64).powf((-ndigits) as f64); + ((*number) / pow1, pow1, None) + }; + let z = y.round(); + #[allow(clippy::float_cmp)] + let z = if (y - z).abs() == 0.5 { + 2.0 * (y / 2.0).round() + } else { + z + }; + let z = if let Some(pow2) = pow2 { + // ndigits >= 0 + (z / pow2) / pow1 + } else { + z * pow1 + }; + if !z.is_finite() { + // overflow + ValueRef::undefined() + } else { + ValueRef::float(z) + } + } + } + _ => ValueRef::undefined(), + }, + _ => match &*number.rc { + Value::int_value(number) => ValueRef::int(*number), + Value::float_value(number) => ValueRef::int(number.round() as i64), + _ => ValueRef::undefined(), + }, + } +} + +pub fn type_of(x: &ValueRef, full_name: &ValueRef) -> ValueRef { + if x.is_schema() { + if full_name.is_truthy() { + let schema = x.as_schema(); + let full_type_str = if let Some(v) = schema.pkgpath.strip_prefix('@') { + v + } else { + schema.pkgpath.as_str() + }; + let mut result = String::new(); + if full_type_str != MAIN_PKG_PATH { + result += &full_type_str.to_string(); + result += "."; + } + result += &x.type_str(); + return ValueRef::str(result.as_str()); + } + } else if x.is_none() { + return ValueRef::str("None"); + } + return ValueRef::str(x.type_str().as_str()); +} + +#[cfg(test)] +mod test_builtin { + + use crate::*; + + #[test] + fn test_sorted() { + assert_panic("sorted only for str|list|dict type", || { + let _ = ValueRef::int(1).sorted(None); + }); + } + + #[test] + fn test_pow() { + assert_eq!( + 8, + builtin::pow(&ValueRef::int(2), &ValueRef::int(3), &ValueRef::none()).as_int() + ); + assert_eq!( + 3, + builtin::pow(&ValueRef::int(2), &ValueRef::int(3), &ValueRef::int(5)).as_int() + ); + assert_eq!( + (2.0 as f64).powf(0.5), + builtin::pow(&ValueRef::int(2), &ValueRef::float(0.5), &ValueRef::none()).as_float() + ); + assert_eq!( + 182523810312617, + builtin::pow( + &ValueRef::int(141592653589793), + &ValueRef::int(238462643383279), + &ValueRef::int(502884197169399) + ) + .as_int() + ) + } + + #[test] + fn test_zip() { + let mut list1 = ValueRef::list(Some(&[ + &ValueRef::int(1), + &ValueRef::int(2), + &ValueRef::int(3), + ])); + let mut list2 = ValueRef::list(Some(&[ + &ValueRef::int(6), + &ValueRef::int(7), + &ValueRef::int(8), + ])); + + let mut lists = ValueRef::list(Some(&[&list1, &list2])); + + let zip_list1 = ValueRef::list(Some(&[&ValueRef::int(1), &ValueRef::int(6)])); + + let zip_list2 = ValueRef::list(Some(&[&ValueRef::int(2), &ValueRef::int(7)])); + + let zip_list3 = ValueRef::list(Some(&[&ValueRef::int(3), &ValueRef::int(8)])); + + let zip_lists = ValueRef::list(Some(&[&zip_list1, &zip_list2, &zip_list3])); + + assert!(zip_lists.cmp_equal(&lists.zip())); + + list1 = ValueRef::list(Some(&[ + &ValueRef::int(1), + &ValueRef::int(2), + &ValueRef::int(3), + &ValueRef::int(4), + ])); + + lists = ValueRef::list(Some(&[&list1, &list2])); + + assert!(zip_lists.cmp_equal(&lists.zip())); + + list1 = ValueRef::list(Some(&[ + &ValueRef::int(1), + &ValueRef::int(2), + &ValueRef::int(3), + ])); + + list2 = ValueRef::list(Some(&[ + &ValueRef::int(6), + &ValueRef::int(7), + &ValueRef::int(8), + &ValueRef::int(9), + ])); + + lists = ValueRef::list(Some(&[&list1, &list2])); + + assert!(zip_lists.cmp_equal(&lists.zip())); + } + + #[test] + fn test_round() { + assert_eq!( + 1, + builtin::round(&ValueRef::int(1), &ValueRef::none()).as_int() + ); + assert_eq!( + 1, + builtin::round(&ValueRef::float(1.4), &ValueRef::none()).as_int() + ); + assert_eq!( + 2, + builtin::round(&ValueRef::float(1.5), &ValueRef::none()).as_int() + ); + + assert_eq!( + 1.6, + builtin::round(&ValueRef::float(1.5555), &ValueRef::int(1)).as_float() + ); + assert_eq!( + 1.56, + builtin::round(&ValueRef::float(1.5555), &ValueRef::int(2)).as_float() + ); + + assert_eq!( + 2, + builtin::round(&ValueRef::float(1.5555), &ValueRef::none()).as_int() + ); + assert_eq!( + 2.0, + builtin::round(&ValueRef::float(1.5555), &ValueRef::int(0)).as_float() + ); + } + + #[test] + fn test_ord() { + assert_eq!(65, ValueRef::str("A").ord().as_int()); + assert_eq!(66, ValueRef::str("B").ord().as_int()); + assert_eq!(67, ValueRef::str("C").ord().as_int()); + } + + #[test] + fn test_hex() { + assert_eq!("0x12", ValueRef::int(18).hex().to_string()); + assert_eq!("-0x12", ValueRef::int(-18).hex().to_string()); + assert_eq!( + "-0x8000000000000000", + ValueRef::int(i64::MIN).hex().to_string() + ); + } + + #[test] + fn test_bin() { + assert_eq!("0b1000", ValueRef::int(8).bin().to_string()); + assert_eq!("-0b1000", ValueRef::int(-8).bin().to_string()); + assert_eq!( + "-0b1000000000000000000000000000000000000000000000000000000000000000", + ValueRef::int(i64::MIN).bin().to_string() + ); + } + + #[test] + fn test_multiplyof() { + assert!(builtin::multiplyof(&ValueRef::int(25), &ValueRef::int(5)) + .cmp_equal(&ValueRef::bool(true))); + assert!(builtin::multiplyof(&ValueRef::int(25), &ValueRef::int(7)) + .cmp_equal(&ValueRef::bool(false))); + } + + #[test] + fn test_isunique() { + //list=[] + let mut list = ValueRef::list(None); + assert!(list.isunique()); + + //list=[1] + list = ValueRef::list(Some(&[&ValueRef::int(1)])); + assert!(list.isunique()); + + //list=[1, 2] + list = ValueRef::list(Some(&[&ValueRef::int(1), &ValueRef::int(2)])); + assert!(list.isunique()); + + //list=[1, 1] + list = ValueRef::list(Some(&[&ValueRef::int(1), &ValueRef::int(1)])); + assert!(!list.isunique()); + + //list=[1, 1.0] + list = ValueRef::list(Some(&[&ValueRef::int(1), &ValueRef::float(1.0)])); + assert!(!list.isunique()); + + //list=[1.1, 1.1] + list = ValueRef::list(Some(&[&ValueRef::float(1.1), &ValueRef::float(1.1)])); + assert!(!list.isunique()); + + //list=["abc","abc"] + list = ValueRef::list(Some(&[&ValueRef::str("abc"), &ValueRef::str("abc")])); + assert!(!list.isunique()); + + //list=["abc","a${'bc'}"] + list = ValueRef::list(Some(&[&ValueRef::str("abc"), &ValueRef::str("a${'bc'}")])); + assert!(list.isunique()); + } + + #[test] + fn test_range() { + let mut list = range(&ValueRef::int(1), &ValueRef::int(5), &ValueRef::int(1)); + let mut expect_list = ValueRef::list(Some(&[ + &ValueRef::int(1), + &ValueRef::int(2), + &ValueRef::int(3), + &ValueRef::int(4), + ])); + assert!(expect_list.cmp_equal(&list)); + list = range(&ValueRef::int(1), &ValueRef::int(5), &ValueRef::int(2)); + expect_list = ValueRef::list(Some(&[&ValueRef::int(1), &ValueRef::int(3)])); + assert!(expect_list.cmp_equal(&list)); + + list = range(&ValueRef::int(5), &ValueRef::int(1), &ValueRef::int(-1)); + expect_list = ValueRef::list(Some(&[ + &ValueRef::int(5), + &ValueRef::int(4), + &ValueRef::int(3), + &ValueRef::int(2), + ])); + assert!(expect_list.cmp_equal(&list)); + } + + #[test] + fn test_max() { + let list = ValueRef::list(Some(&[ + &ValueRef::int(1), + &ValueRef::int(8), + &ValueRef::int(5), + &ValueRef::int(12), + &ValueRef::int(3), + ])); + assert!(list.max_value().cmp_equal(&ValueRef::int(12))); + } + + #[test] + fn test_min_value() { + let list = ValueRef::list(Some(&[ + &ValueRef::int(1), + &ValueRef::int(8), + &ValueRef::int(5), + &ValueRef::int(12), + &ValueRef::int(3), + ])); + assert!(list.min_value().cmp_equal(&ValueRef::int(1))); + } + + #[test] + fn test_sorted_normal() { + //list=[] + let mut list = ValueRef::list(None); + let mut sorted = ValueRef::list(None); + for i in 1..6 { + list.list_append(&ValueRef::int(6 - i)); + sorted.list_append(&ValueRef::int(i)); + } + + assert!(sorted.cmp_equal(&list.sorted(None))); + } + + #[test] + #[should_panic] + fn test_sorted_panic() { + let list = ValueRef::list(Some(&[&ValueRef::str("abc"), &ValueRef::int(1)])); + list.sorted(None); + } +} diff --git a/kclvm/runtime/src/stdlib/builtin_api.rs b/kclvm/runtime/src/stdlib/builtin_api.rs new file mode 100644 index 000000000..212c415e2 --- /dev/null +++ b/kclvm/runtime/src/stdlib/builtin_api.rs @@ -0,0 +1,715 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_option_init( + ctx: *mut kclvm_context_t, + key: *const i8, + value: *const i8, +) { + let ctx = mut_ptr_as_ref(ctx); + ctx.builtin_option_init(c2str(key), c2str(value)); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_option_reset( + ctx: *mut kclvm_context_t, + _args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let ctx = mut_ptr_as_ref(ctx); + + ctx.builtin_option_reset(); + kclvm_value_Undefined() +} + +// def kcl_option(name: str, *, type="", required=False, default=None, help="", file="", line=0) -> typing.Any: + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_option( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + + let mut list_option_mode = false; + + if ctx.cfg.list_option_mode { + list_option_mode = true; + + let name = args.arg_i_str(0, Some("?".to_string())).unwrap(); + let typ = kwargs.kwarg_str("type", Some("".to_string())).unwrap(); + let required = kwargs.kwarg_bool("required", Some(false)).unwrap(); + let help = kwargs.kwarg_str("help", Some("".to_string())).unwrap(); + + let mut default_value: Option = None; + if let Some(x) = kwargs.kwarg("default") { + default_value = Some(x.to_string()); + } + + ctx.define_option( + name.as_str(), + typ.as_str(), + required, + default_value, + help.as_str(), + ); + } + + fn _value_to_type(this: &ValueRef, typ: String, list_option_mode: bool) -> ValueRef { + if typ.is_empty() { + return this.clone(); + } + if typ == "bool" { + match *this.rc { + Value::bool_value(ref v) => { + return ValueRef::bool(*v); + } + Value::int_value(ref v) => { + return ValueRef::bool(*v != 0); + } + Value::float_value(ref v) => { + return ValueRef::bool(*v != 0.0); + } + Value::str_value(ref v) => { + return ValueRef::bool(v == "True" || v == "true"); + } + _ => { + return ValueRef::undefined(); + } + } + } + if typ == "int" { + match *this.rc { + Value::bool_value(ref v) => { + if *v { + return ValueRef::int(1); + } else { + return ValueRef::int(0); + } + } + Value::int_value(ref v) => { + return ValueRef::int(*v); + } + Value::float_value(ref v) => { + return ValueRef::int(*v as i64); + } + Value::str_value(ref v) => { + match v.parse::() { + Ok(n) => return ValueRef::int(n), + _ => panic!("cannot use '{}' as type '{}'", v, typ), + }; + } + _ => { + if list_option_mode { + return ValueRef::none(); + } + let err_msg = format!("cannot use '{}' as type '{}'", this.to_string(), typ); + panic!("{}", err_msg); + } + } + } + if typ == "float" { + match *this.rc { + Value::bool_value(ref v) => { + if *v { + return ValueRef::float(1.0); + } else { + return ValueRef::float(0.0); + } + } + Value::int_value(ref v) => { + return ValueRef::float(*v as f64); + } + Value::float_value(ref v) => { + return ValueRef::float(*v); + } + Value::str_value(ref v) => { + match v.parse::() { + Ok(n) => return ValueRef::float(n), + _ => return ValueRef::float(0.0), + }; + } + _ => { + if list_option_mode { + return ValueRef::none(); + } + let err_msg = format!("cannot use '{}' as type '{}'", this.to_string(), typ); + panic!("{}", err_msg); + } + } + } + if typ == "str" { + match *this.rc { + Value::bool_value(ref v) => { + let s = format!("{}", *v); + return ValueRef::str(s.as_ref()); + } + Value::int_value(ref v) => { + let s = format!("{}", *v); + return ValueRef::str(s.as_ref()); + } + Value::float_value(ref v) => { + let s = format!("{}", *v); + return ValueRef::str(s.as_ref()); + } + Value::str_value(ref v) => { + return ValueRef::str(v.as_ref()); + } + _ => { + if list_option_mode { + return ValueRef::none(); + } + let err_msg = format!("cannot use '{}' as type '{}'", this.to_string(), typ); + panic!("{}", err_msg); + } + } + } + if typ == "list" { + match *this.rc { + Value::list_value(_) => { + return this.clone(); + } + _ => { + if list_option_mode { + return ValueRef::none(); + } + let err_msg = format!("cannot use '{}' as type '{}'", this.to_string(), typ); + panic!("{}", err_msg); + } + } + } + if typ == "dict" { + match *this.rc { + Value::dict_value(_) => { + return this.clone(); + } + _ => { + if list_option_mode { + return ValueRef::none(); + } + let err_msg = format!("cannot use '{}' as type '{}'", this.to_string(), typ); + panic!("{}", err_msg); + } + } + } + + if list_option_mode { + return ValueRef::none(); + } + + panic!("unknonwn type '{}'", typ); + } + + if let Some(arg0) = args.arg_0() { + if let Some(x) = ctx.app_args.get(&arg0.as_str()) { + if *x == 0 { + return kclvm_value_Undefined(); + } + + let opt_value = mut_ptr_as_ref((*x) as *mut kclvm_value_ref_t); + + if let Some(kwarg_type) = kwargs.kwarg_str("type", None) { + return _value_to_type(opt_value, kwarg_type, ctx.cfg.list_option_mode).into_raw(); + } + + return (*x) as *mut kclvm_value_ref_t; + } else if let Some(kwarg_default) = kwargs.kwarg("default") { + if let Some(kwarg_type) = kwargs.kwarg_str("type", None) { + return _value_to_type(kwarg_default, kwarg_type, ctx.cfg.list_option_mode) + .into_raw(); + } + + return kwarg_default.clone().into_raw(); + } + } + + if list_option_mode { + return kclvm_value_None(); + } + + let required = kwargs.kwarg_bool("required", Some(false)).unwrap(); + if required { + let name = args.arg_i_str(0, Some("?".to_string())).unwrap(); + panic!( + "option('{}') must be initialized, try '-D {}=?' argument", + name, name + ); + } + + kclvm_value_None() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_print( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + // args + let list = args.as_list_ref(); + let values: Vec = list.values.iter().map(|v| v.to_string()).collect(); + print!("{}", values.join(" ")); + let dict = kwargs.as_dict_ref(); + // kwargs: end + if let Some(c) = dict.values.get("end") { + print!("{}", c.to_string()); + use std::io::Write; + let _ = std::io::stdout().flush(); + } else { + println!(); + } + kclvm_value_None() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_len( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(arg) = args.arg_0() { + return kclvm_value_Int(arg.len() as i64); + } + panic!("len() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_any_true( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(arg0) = args.arg_0() { + return kclvm_value_Bool(arg0.any_true() as i8); + } + kclvm_value_Bool(0) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_isunique( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(arg0) = args.arg_0() { + return kclvm_value_Bool(arg0.isunique() as i8); + } + kclvm_value_Bool(0) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_sorted( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + + if let Some(arg0) = args.arg_0() { + let reverse = kwargs.kwarg("reverse"); + return arg0.sorted(reverse).into_raw(); + } + panic!("sorted() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_int( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + + if let Some(arg0) = args.arg_0() { + let base = args.arg_i(1).or_else(|| kwargs.kwarg("base")); + return arg0.convert_to_int(base).into_raw(); + } + panic!("int() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_float( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(arg0) = args.arg_0() { + return arg0.convert_to_float().into_raw(); + } + panic!("float() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_bool( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(arg0) = args.arg_0() { + return ValueRef::bool(arg0.is_truthy()).into_raw(); + } + panic!("bool() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(arg0) = args.arg_0() { + return ValueRef::str(&arg0.to_string()).into_raw(); + } + ValueRef::str("").into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_max( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + if args.args_len() > 1 { + return args.max_value().into_raw(); + } + if let Some(arg0) = args.arg_0() { + return arg0.max_value().into_raw(); + } + panic!("max() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_min( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + if args.args_len() > 1 { + return args.min_value().into_raw(); + } + if let Some(arg0) = args.arg_0() { + return arg0.min_value().into_raw(); + } + panic!("min() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_multiplyof( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + if let (Some(arg0), Some(arg1)) = (args.arg_i(0), args.arg_i(1)) { + return builtin::multiplyof(arg0, arg1).into_raw(); + } + panic!( + "multiplyof() takes exactly two argument ({} given)", + args.args_len() + ); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_abs( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + if let Some(arg0) = args.arg_0() { + return arg0.abs().into_raw(); + } + panic!("abs() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_all_true( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + if let Some(arg0) = args.arg_0() { + return kclvm_value_Bool(arg0.all_true() as i8); + } + kclvm_value_Bool(0) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_hex( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + if let Some(arg0) = args.arg_0() { + return arg0.hex().into_raw(); + } + panic!("hex() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_sum( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + match args.arg_i(0) { + Some(arg0) => match args.arg_i(1) { + Some(arg1) => arg0.sum(arg1).into_raw(), + _ => arg0.sum(&ValueRef::int(0)).into_raw(), + }, + _ => kclvm_value_Undefined(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_pow( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + match (args.arg_i(0), args.arg_i(1)) { + (Some(arg0), Some(arg1)) => match args.arg_i(2) { + Some(arg2) => builtin::pow(arg0, arg1, arg2).into_raw(), + _ => builtin::pow(arg0, arg1, &ValueRef::none()).into_raw(), + }, + _ => kclvm_value_Undefined(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_round( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + match args.arg_i(0) { + Some(arg0) => match args.arg_i(1) { + Some(arg1) => builtin::round(arg0, arg1).into_raw(), + _ => builtin::round(arg0, &ValueRef::none()).into_raw(), + }, + _ => kclvm_value_Undefined(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_zip( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + args.zip().into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_list( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + if args.args_len() > 0 { + if let Some(arg0) = args.arg_0() { + return builtin::list(Some(arg0)).into_raw(); + } + panic!("invalid arguments in list() function"); + } else { + builtin::list(None).into_raw() + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_dict( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + let mut dict = ValueRef::dict(None); + if let Some(arg0) = args.arg_0() { + dict.dict_insert_unpack(&builtin::dict(Some(arg0))); + } + dict.dict_insert_unpack(&builtin::dict(Some(kwargs))); + dict.into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_typeof( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + if let Some(arg0) = args.arg_0() { + if let Some(full_name) = kwargs.kwarg("full_name") { + return builtin::type_of(arg0, full_name).into_raw(); + } + return builtin::type_of(arg0, &ValueRef::bool(false)).into_raw(); + } + + panic!("typeof() missing 1 required positional argument: 'x'"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_bin( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + if let Some(arg0) = args.arg_0() { + return arg0.bin().into_raw(); + } + panic!("bin() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_oct( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + if let Some(arg0) = args.arg_0() { + return arg0.oct().into_raw(); + } + panic!("oct() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_ord( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + if let Some(arg0) = args.arg_0() { + return arg0.ord().into_raw(); + } + panic!("ord() takes exactly one argument (0 given)"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_range( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + match args.arg_i(0) { + Some(arg0) => match args.arg_i(1) { + Some(arg1) => match args.arg_i(2) { + Some(arg2) => builtin::range(arg0, arg1, arg2).into_raw(), + _ => builtin::range(arg0, arg1, &ValueRef::int(1)).into_raw(), + }, + _ => builtin::range(&ValueRef::int(0), arg0, &ValueRef::int(1)).into_raw(), + }, + _ => kclvm_value_Undefined(), + } +} diff --git a/kclvm/runtime/src/stdlib/debug_api.rs b/kclvm/runtime/src/stdlib/debug_api.rs new file mode 100644 index 000000000..3ff5dc36c --- /dev/null +++ b/kclvm/runtime/src/stdlib/debug_api.rs @@ -0,0 +1,58 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_type_t = Type; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_debug_hello() { + println!("kclvm_debug_hello: hello kclvm") +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_debug_print(cs: *const i8) { + let msg = unsafe { std::ffi::CStr::from_ptr(cs) }.to_str().unwrap(); + println!("kclvm_debug_print: {}", msg) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_debug_print_str_list(len: i32, ss: &mut &mut i8) { + let x = crate::convert_double_pointer_to_vec(ss, len as usize); + println!("kclvm_debug_print_str_list: {:?}", x); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_debug_print_type(p: *const kclvm_type_t) { + let p = ptr_as_ref(p); + println!("kclvm_debug_print_type: {:?}", p) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_debug_print_value(p: *const kclvm_value_ref_t) { + let p = ptr_as_ref(p); + println!("kclvm_debug_print_value: {:?}", p); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_debug_print_value_json_string(p: *const kclvm_value_ref_t) { + let p = ptr_as_ref(p); + println!("kclvm_debug_print_value: {}", p.to_json_string()); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_debug_invoke_func(fn_ptr: extern "C" fn()) { + println!("kclvm_debug_invoke_func begin"); + fn_ptr(); + println!("kclvm_debug_invoke_func end"); +} diff --git a/kclvm/runtime/src/stdlib/mod.rs b/kclvm/runtime/src/stdlib/mod.rs new file mode 100644 index 000000000..6a1f5ba42 --- /dev/null +++ b/kclvm/runtime/src/stdlib/mod.rs @@ -0,0 +1,16 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod assert_api; +pub use assert_api::*; + +pub mod debug_api; +pub use debug_api::*; + +pub mod builtin_api; +pub use builtin_api::*; + +pub mod builtin; +pub use builtin::*; + +pub mod plugin; +pub use plugin::*; diff --git a/kclvm/runtime/src/stdlib/plugin.rs b/kclvm/runtime/src/stdlib/plugin.rs new file mode 100644 index 000000000..e5f3ef371 --- /dev/null +++ b/kclvm/runtime/src/stdlib/plugin.rs @@ -0,0 +1,104 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +use std::os::raw::c_char; + +#[allow(non_upper_case_globals)] +static mut _plugin_handler_fn_ptr: u64 = 0; + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_plugin_init( + fn_ptr: extern "C" fn( + method: *const i8, + args_json: *const c_char, + kwargs_json: *const c_char, + ) -> *const c_char, +) { + unsafe { + _plugin_handler_fn_ptr = fn_ptr as usize as u64; + } +} + +// import kcl_plugin.hello +// hello.SayHello() +// +// => return kclvm_plugin_invoke("kcl_plugin.hello.SayHello", args, kwarge) + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_plugin_invoke( + method: *const i8, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args_s = kclvm_value_to_json_value_with_null(args); + let kwargs_s = kclvm_value_to_json_value_with_null(kwargs); + + let args_json = kclvm_value_Str_ptr(args_s); + let kwargs_json = kclvm_value_Str_ptr(kwargs_s); + + let result_json = kclvm_plugin_invoke_json(method, args_json, kwargs_json); + + kclvm_value_delete(args_s); + kclvm_value_delete(kwargs_s); + + let ptr = kclvm_value_from_json(result_json); + { + if let Some(msg) = ptr_as_ref(ptr).dict_get_value("$__kcl_PanicInfo__$") { + let ctx = Context::current_context_mut(); + ctx.set_err_type(&ErrType::EvaluationError_TYPE); + + panic!("{}", msg.as_str()); + } + } + + ptr +} + +#[cfg(not(target_arch = "wasm32"))] +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_plugin_invoke_json( + method: *const i8, + args: *const c_char, + kwargs: *const c_char, +) -> *const c_char { + unsafe { + if _plugin_handler_fn_ptr == 0 { + panic!("plugin is nil, should call kclvm_plugin_init at first"); + } + + let ptr = (&_plugin_handler_fn_ptr as *const u64) as *const () + as *const extern "C" fn( + method: *const i8, + args: *const c_char, + kwargs: *const c_char, + ) -> *const c_char; + + (*ptr)(method, args, kwargs) + } +} + +#[cfg(target_arch = "wasm32")] +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_plugin_invoke_json( + method: *const i8, + args: *const c_char, + kwargs: *const c_char, +) -> *const c_char { + unsafe { + return kclvm_plugin_invoke_json_wasm(method, args, kwargs); + } +} + +#[cfg(target_arch = "wasm32")] +extern "C" { + pub fn kclvm_plugin_invoke_json_wasm( + method: *const i8, + args: *const c_char, + kwargs: *const c_char, + ) -> *const c_char; +} diff --git a/kclvm/runtime/src/testing/mod.rs b/kclvm/runtime/src/testing/mod.rs new file mode 100644 index 000000000..c4034afa8 --- /dev/null +++ b/kclvm/runtime/src/testing/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod testing; +pub use self::testing::*; diff --git a/kclvm/runtime/src/testing/testing.rs b/kclvm/runtime/src/testing/testing.rs new file mode 100644 index 000000000..3a16ad538 --- /dev/null +++ b/kclvm/runtime/src/testing/testing.rs @@ -0,0 +1,28 @@ +//! KCL testing system module +//! +//! Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_testing_arguments( + _ctx: *mut kclvm_context_t, + _args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) { + // Nothing to do +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_testing_setting_file( + _ctx: *mut kclvm_context_t, + _args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) { + // Nothing to do +} diff --git a/kclvm/runtime/src/types/api.rs b/kclvm/runtime/src/types/api.rs new file mode 100644 index 000000000..1359dccc0 --- /dev/null +++ b/kclvm/runtime/src/types/api.rs @@ -0,0 +1,387 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_context_t = Context; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_kind_t = Kind; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_type_t = Type; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_value_t = Value; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_char_t = i8; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_size_t = i32; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_bool_t = i8; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_int_t = i64; + +#[allow(dead_code, non_camel_case_types)] +type kclvm_float_t = f64; + +// ---------------------------------------------------------------------------- +// new +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_Any() -> *mut kclvm_type_t { + new_mut_ptr(Type::any()) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_Bool() -> *mut kclvm_type_t { + new_mut_ptr(Type::bool()) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_BoolLit(v: kclvm_bool_t) -> *mut kclvm_type_t { + new_mut_ptr(Type::bool_lit(v != 0)) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_Int() -> *mut kclvm_type_t { + new_mut_ptr(Type::int()) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_IntLit(v: i64) -> *mut kclvm_type_t { + new_mut_ptr(Type::int_lit(v)) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_Float() -> *mut kclvm_type_t { + new_mut_ptr(Type::float()) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_FloatLit(v: f64) -> *mut kclvm_type_t { + new_mut_ptr(Type::float_lit(v)) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_Str() -> *mut kclvm_type_t { + new_mut_ptr(Type::str()) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_StrLit(s: *const kclvm_char_t) -> *mut kclvm_type_t { + return new_mut_ptr(Type::str_lit(c2str(s))); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_List(elem_type: *const kclvm_type_t) -> *mut kclvm_type_t { + return new_mut_ptr(Type::list(ptr_as_ref(elem_type))); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_Dict( + key_type: *const kclvm_type_t, + elem_type: *const kclvm_type_t, +) -> *mut kclvm_type_t { + return new_mut_ptr(Type::dict(ptr_as_ref(key_type), ptr_as_ref(elem_type))); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_Union( + n: kclvm_size_t, + elem_types: *const *const kclvm_type_t, +) -> *mut kclvm_type_t { + unsafe { + let mut ut: UnionType = Default::default(); + + let _ = std::slice::from_raw_parts(elem_types, n as usize) + .iter() + .map(|arg| ut.elem_types.push(ptr_as_ref(*arg).clone())); + + new_mut_ptr(Type::union_type(ut)) + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_Schema( + name: *const kclvm_char_t, + parent_name: *const kclvm_char_t, + _is_relaxed: kclvm_bool_t, + field_num: kclvm_size_t, + field_names: *const *const kclvm_char_t, + field_types: *const *const kclvm_type_t, +) -> *mut kclvm_type_t { + unsafe { + let mut st: SchemaType = SchemaType { + name: c2str(name).to_string(), + parent_name: c2str(parent_name).to_string(), + ..Default::default() + }; + + let _ = std::slice::from_raw_parts(field_names, field_num as usize) + .iter() + .map(|arg| st.field_names.push(c2str(*arg).to_string())); + let _ = std::slice::from_raw_parts(field_types, field_num as usize) + .iter() + .map(|arg| st.field_types.push(ptr_as_ref(*arg).clone())); + + new_mut_ptr(Type::schema_type(st)) + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_Func( + args_len: kclvm_size_t, + args_types: *const *const kclvm_type_t, + return_type: *const kclvm_type_t, +) -> *mut kclvm_type_t { + unsafe { + let mut ft: FuncType = FuncType { + return_type: Box::new(ptr_as_ref(return_type).clone()), + ..Default::default() + }; + let _ = std::slice::from_raw_parts(args_types, args_len as usize) + .iter() + .map(|arg| ft.args_types.push(ptr_as_ref(*arg).clone())); + + new_mut_ptr(Type::func_type(ft)) + } +} + +// ---------------------------------------------------------------------------- +// delete +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_delete(p: *mut kclvm_type_t) { + free_mut_ptr(p); +} + +// ---------------------------------------------------------------------------- +// kind +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_kind(p: *const kclvm_type_t) -> kclvm_kind_t { + let p = ptr_as_ref(p); + + p.kind() +} + +// ---------------------------------------------------------------------------- +// type_str +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_str(p: *const kclvm_type_t) -> kclvm_kind_t { + let p = ptr_as_ref(p); + + p.kind() +} + +// ---------------------------------------------------------------------------- +// lit value +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_BoolLit_value(p: *const kclvm_type_t) -> kclvm_bool_t { + match ptr_as_ref(p) { + Type::bool_lit_type(ref v) => *v as kclvm_bool_t, + _ => 0, + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_IntLit_value(p: *const kclvm_type_t) -> i64 { + let p = ptr_as_ref(p); + match p { + Type::int_lit_type(ref v) => *v, + _ => 0, + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_FloatLit_value(p: *const kclvm_type_t) -> f64 { + let p = ptr_as_ref(p); + match p { + Type::float_lit_type(ref v) => *v, + _ => 0.0, + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_StrLit_value(p: *const kclvm_type_t) -> *const kclvm_char_t { + let p = ptr_as_ref(p); + match p { + Type::str_lit_type(ref v) => v.as_ptr() as *const kclvm_char_t, + _ => std::ptr::null(), + } +} + +// ---------------------------------------------------------------------------- +// list/dict type +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_key_type(p: *const kclvm_type_t) -> *const kclvm_type_t { + let p = ptr_as_ref(p); + match p { + Type::dict_type(ref v) => { + return v.key_type.as_ref() as *const Type; + } + _ => std::ptr::null(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_elem_type(p: *const kclvm_type_t) -> *const kclvm_type_t { + let p = ptr_as_ref(p); + match p { + Type::list_type(ref v) => { + return v.elem_type.as_ref() as *const Type; + } + Type::dict_type(ref v) => { + return v.elem_type.as_ref() as *const Type; + } + _ => std::ptr::null(), + } +} + +// ---------------------------------------------------------------------------- +// schema +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_schema_name(p: *const kclvm_type_t) -> *const kclvm_char_t { + let p = ptr_as_ref(p); + match p { + Type::schema_type(ref v) => v.name.as_ptr() as *const kclvm_char_t, + _ => std::ptr::null(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_schema_parent_name(p: *const kclvm_type_t) -> *const kclvm_char_t { + let p = ptr_as_ref(p); + match p { + Type::schema_type(ref v) => v.parent_name.as_ptr() as *const kclvm_char_t, + _ => std::ptr::null(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_schema_relaxed(p: *const kclvm_type_t) -> kclvm_bool_t { + let p = ptr_as_ref(p); + match p { + Type::schema_type(..) => false as kclvm_bool_t, + _ => 0, + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_schema_field_num(p: *const kclvm_type_t) -> kclvm_size_t { + let p = ptr_as_ref(p); + match p { + Type::schema_type(ref v) => v.field_names.len() as kclvm_size_t, + _ => 0, + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_schema_field_name( + p: *const kclvm_type_t, + i: kclvm_size_t, +) -> *const kclvm_char_t { + let p = ptr_as_ref(p); + match p { + Type::schema_type(ref v) => v.field_names[i as usize].as_ptr() as *const kclvm_char_t, + _ => std::ptr::null(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_schema_field_type( + p: *const kclvm_type_t, + i: kclvm_size_t, +) -> *const kclvm_type_t { + let p = ptr_as_ref(p); + match p { + Type::schema_type(ref v) => &v.field_types[i as usize] as *const kclvm_type_t, + _ => std::ptr::null(), + } +} + +// ---------------------------------------------------------------------------- +// func (for plugin) +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_arg_num(p: *const kclvm_type_t) -> kclvm_size_t { + let p = ptr_as_ref(p); + match p { + Type::func_type(ref v) => v.args_types.len() as kclvm_size_t, + _ => 0, + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_arg_type( + p: *const kclvm_type_t, + i: kclvm_size_t, +) -> *const kclvm_type_t { + let p = ptr_as_ref(p); + match p { + Type::func_type(ref v) => &v.args_types[i as usize] as *const kclvm_type_t, + _ => std::ptr::null(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_type_return_type(p: *const kclvm_type_t) -> *const kclvm_type_t { + let p = ptr_as_ref(p); + match p { + Type::func_type(ref v) => v.return_type.as_ref() as *const kclvm_type_t, + _ => std::ptr::null(), + } +} + +// ---------------------------------------------------------------------------- +// END +// ---------------------------------------------------------------------------- diff --git a/kclvm/runtime/src/types/mod.rs b/kclvm/runtime/src/types/mod.rs new file mode 100644 index 000000000..7f1db4c8b --- /dev/null +++ b/kclvm/runtime/src/types/mod.rs @@ -0,0 +1,13 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod api; +pub use api::*; + +pub mod typ_kind; +pub use typ_kind::*; + +pub mod typ_new; +pub use typ_new::*; + +pub mod typ_type_str; +pub use typ_type_str::*; diff --git a/kclvm/runtime/src/types/typ_kind.rs b/kclvm/runtime/src/types/typ_kind.rs new file mode 100644 index 000000000..3c11d8fa2 --- /dev/null +++ b/kclvm/runtime/src/types/typ_kind.rs @@ -0,0 +1,24 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl Type { + pub fn kind(&self) -> Kind { + match self { + Type::any_type => Kind::Any, + Type::bool_type => Kind::Bool, + Type::bool_lit_type(..) => Kind::BoolLit, + Type::int_type => Kind::Int, + Type::int_lit_type(..) => Kind::IntLit, + Type::float_type => Kind::Float, + Type::float_lit_type(..) => Kind::FloatLit, + Type::str_type => Kind::Str, + Type::str_lit_type(..) => Kind::StrLit, + Type::list_type(..) => Kind::List, + Type::dict_type(..) => Kind::Dict, + Type::union_type(..) => Kind::Union, + Type::schema_type(..) => Kind::Schema, + Type::func_type(..) => Kind::Func, + } + } +} diff --git a/kclvm/runtime/src/types/typ_new.rs b/kclvm/runtime/src/types/typ_new.rs new file mode 100644 index 000000000..d679698b2 --- /dev/null +++ b/kclvm/runtime/src/types/typ_new.rs @@ -0,0 +1,88 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl Type { + pub fn any() -> Self { + Type::any_type + } + + pub fn func() -> Self { + Type::func_type(Default::default()) + } + + pub fn bool() -> Self { + Type::bool_type + } + + pub fn bool_lit(v: bool) -> Self { + Type::bool_lit_type(v) + } + + pub fn int() -> Self { + Type::int_type + } + + pub fn int_lit(v: i64) -> Self { + Type::int_lit_type(v) + } + + pub fn float() -> Self { + Type::float_type + } + + pub fn float_lit(v: f64) -> Self { + Type::float_lit_type(v) + } + + pub fn str() -> Self { + Type::str_type + } + + pub fn str_lit(s: &str) -> Self { + Type::str_lit_type(s.to_string()) + } + + pub fn list(elem_type: &Self) -> Self { + Type::list_type(ListType { + elem_type: Box::new(elem_type.clone()), + }) + } + + pub fn dict(key_type: &Self, elem_type: &Self) -> Self { + Type::dict_type(DictType { + key_type: Box::new(key_type.clone()), + elem_type: Box::new(elem_type.clone()), + }) + } + + pub fn union(elem_types: &[&Self]) -> Self { + let mut ut: UnionType = Default::default(); + for typ in elem_types { + ut.elem_types.push((*typ).clone()); + } + Type::union_type(ut) + } + + pub fn schema( + name: &str, + parent_name: &str, + field_names: &[&str], + field_types: &[&Self], + ) -> Self { + let mut st = SchemaType { + name: name.to_string(), + parent_name: parent_name.to_string(), + ..Default::default() + }; + + for name in field_names { + st.field_names.push((*name).to_string()); + } + for typ in field_types { + st.field_types.push((*typ).clone()); + } + + Type::schema_type(st) + } +} diff --git a/kclvm/runtime/src/types/typ_type_str.rs b/kclvm/runtime/src/types/typ_type_str.rs new file mode 100644 index 000000000..50fa8756b --- /dev/null +++ b/kclvm/runtime/src/types/typ_type_str.rs @@ -0,0 +1,34 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl Type { + pub fn type_str(&self) -> String { + match self { + Type::any_type => KCL_TYPE_ANY.to_string(), + Type::bool_type => BUILTIN_TYPE_BOOL.to_string(), + Type::bool_lit_type(ref v) => format!("{}({})", BUILTIN_TYPE_BOOL, v), + Type::int_type => BUILTIN_TYPE_INT.to_string(), + Type::int_lit_type(ref v) => format!("{}({})", BUILTIN_TYPE_INT, v), + Type::float_type => BUILTIN_TYPE_FLOAT.to_string(), + Type::float_lit_type(ref v) => format!("{}({})", BUILTIN_TYPE_FLOAT, v), + Type::str_type => BUILTIN_TYPE_STR.to_string(), + Type::str_lit_type(ref v) => format!("{}({})", BUILTIN_TYPE_STR, v), + Type::list_type(ref v) => format!("[{}]", v.elem_type.type_str()), + Type::dict_type(ref v) => { + format!("{{{}:{}}}", v.key_type.type_str(), v.elem_type.type_str()) + } + Type::union_type(ref v) => match v.elem_types.len() { + 0 => String::new(), + 1 => v.elem_types[0].type_str(), + _ => { + let mut types = Vec::new(); + let _ = v.elem_types.iter().map(|e| types.push(e.type_str())); + types.join("|") + } + }, + Type::schema_type(ref v) => v.name.to_string(), + Type::func_type(ref _v) => "func".to_string(), + } + } +} diff --git a/kclvm/runtime/src/unification/mod.rs b/kclvm/runtime/src/unification/mod.rs new file mode 100644 index 000000000..ff06983fc --- /dev/null +++ b/kclvm/runtime/src/unification/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod subsume; +pub use subsume::*; diff --git a/kclvm/runtime/src/unification/subsume.rs b/kclvm/runtime/src/unification/subsume.rs new file mode 100644 index 000000000..5f4295d2c --- /dev/null +++ b/kclvm/runtime/src/unification/subsume.rs @@ -0,0 +1,67 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +/// Calculate the partial order relationship between `KCL value objects` +/// and judge whether the value1 object ∈ the value2 object. +/// +/// Please note that The type and value of KCL are defined and used separately, +/// so the partial order relationship calculation is also divided into two types, +/// type and value, and there is no partial order relationship between type +/// objects and value objects. +pub fn value_subsume(value1: &ValueRef, value2: &ValueRef, should_recursive_check: bool) -> bool { + if value1.is_none_or_undefined() || value2.is_none_or_undefined() { + return true; + } + if value1 == value2 { + return true; + } + if value1.is_int() && value2.is_int() { + return value1.as_int() == value2.as_int(); + } + if value1.is_float() && value2.is_float() { + return value1.as_float() == value2.as_float(); + } + if value1.is_bool() && value2.is_bool() { + return value1.as_bool() == value2.as_bool(); + } + if value1.is_str() && value2.is_str() { + return value1.as_str() == value2.as_str(); + } + match (&*value1.rc, &*value2.rc) { + (Value::list_value(value1), Value::list_value(value2)) => { + return value1.values.len() == value2.values.len() + && value1 + .values + .iter() + .zip(value2.values.iter()) + .all(|(item1, item2)| value_subsume(item1, item2, should_recursive_check)); + } + ( + Value::dict_value(_) | Value::schema_value(_), + Value::dict_value(_) | Value::schema_value(_), + ) => { + let value1_dict = &value1.as_dict_ref().values; + let value2_dict = &value2.as_dict_ref().values; + if value1_dict.is_empty() { + return true; + } + if value1_dict.keys().all(|key| !value2_dict.contains_key(key)) { + return true; + } + if should_recursive_check { + for (key1, value1) in value1_dict { + if !value2_dict.contains_key(key1) { + continue; + } + let value2 = value2_dict.get(key1).unwrap(); + if !value_subsume(value1, value2, should_recursive_check) { + return false; + } + } + } + true + } + _ => false, + } +} diff --git a/kclvm/runtime/src/units/mod.rs b/kclvm/runtime/src/units/mod.rs new file mode 100644 index 000000000..c0a05d119 --- /dev/null +++ b/kclvm/runtime/src/units/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod units; +pub use self::units::*; diff --git a/kclvm/runtime/src/units/units.rs b/kclvm/runtime/src/units/units.rs new file mode 100644 index 000000000..856b97a5e --- /dev/null +++ b/kclvm/runtime/src/units/units.rs @@ -0,0 +1,439 @@ +//! KCL units system module +//! +//! Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +#[derive(Debug)] +#[allow(dead_code, non_camel_case_types)] +enum to_unit_suffix { + n, + u, + m, + k, + K, + M, + G, + T, + P, + Ki, + Mi, + Gi, + Ti, + Pi, +} + +use phf::{phf_map, Map}; + +pub const IEC_SUFFIX: &str = "i"; +pub const EXPONENTS: Map<&str, i8> = phf_map! { + "n" => -3, + "u" => -2, + "m" => -1, + "k" => 1, + "K" => 1, + "M" => 2, + "G" => 3, + "T" => 4, + "P" => 5, +}; +pub const INVALID_UNITS: [&str; 4] = ["ni", "ui", "mi", "ki"]; + +// to_n(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_n( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + let num = args.arg_0().unwrap().convert_to_float().as_float(); + let s = to_unit(num, to_unit_suffix::n); + return ValueRef::str(s.as_ref()).into_raw(); +} + +// to_u(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_u( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + let num = args.arg_0().unwrap().convert_to_float().as_float(); + let s = to_unit(num, to_unit_suffix::u); + return ValueRef::str(s.as_ref()).into_raw(); +} + +// to_m(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_m( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + let num = args.arg_0().unwrap().convert_to_float().as_float(); + let s = to_unit(num, to_unit_suffix::m); + return ValueRef::str(s.as_ref()).into_raw(); +} + +// to_K(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_K( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(num) = args.arg_i_num(0, None) { + let s = to_unit(num, to_unit_suffix::K); + return ValueRef::str(s.as_ref()).into_raw(); + } + panic!("to_K() missing 1 required positional argument: 'num'"); +} + +// to_M(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_M( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(num) = args.arg_i_num(0, None) { + let s = to_unit(num, to_unit_suffix::M); + return ValueRef::str(s.as_ref()).into_raw(); + } + panic!("to_M() missing 1 required positional argument: 'num'"); +} + +// to_G(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_G( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(num) = args.arg_i_num(0, None) { + let s = to_unit(num, to_unit_suffix::G); + return ValueRef::str(s.as_ref()).into_raw(); + } + panic!("to_G() missing 1 required positional argument: 'num'"); +} + +// to_T(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_T( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(num) = args.arg_i_num(0, None) { + let s = to_unit(num, to_unit_suffix::T); + return ValueRef::str(s.as_ref()).into_raw(); + } + panic!("to_T() missing 1 required positional argument: 'num'"); +} + +// to_P(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_P( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(num) = args.arg_i_num(0, None) { + let s = to_unit(num, to_unit_suffix::P); + return ValueRef::str(s.as_ref()).into_raw(); + } + panic!("to_P() missing 1 required positional argument: 'num'"); +} + +// to_Ki(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_Ki( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(num) = args.arg_i_num(0, None) { + let s = to_unit(num, to_unit_suffix::Ki); + return ValueRef::str(s.as_ref()).into_raw(); + } + panic!("to_Ki() missing 1 required positional argument: 'num'"); +} + +// to_Mi(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_Mi( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(num) = args.arg_i_num(0, None) { + let s = to_unit(num, to_unit_suffix::Mi); + return ValueRef::str(s.as_ref()).into_raw(); + } + panic!("to_Mi() missing 1 required positional argument: 'num'"); +} + +// to_Gi(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_Gi( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(num) = args.arg_i_num(0, None) { + let s = to_unit(num, to_unit_suffix::Gi); + return ValueRef::str(s.as_ref()).into_raw(); + } + panic!("to_Gi() missing 1 required positional argument: 'num'"); +} + +// to_Ti(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_Ti( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(num) = args.arg_i_num(0, None) { + let s = to_unit(num, to_unit_suffix::Ti); + return ValueRef::str(s.as_ref()).into_raw(); + } + panic!("to_Ti() missing 1 required positional argument: 'num'"); +} + +// to_Pi(num: int) -> str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_units_to_Pi( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let _ctx = mut_ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let _kwargs = ptr_as_ref(kwargs); + + if let Some(num) = args.arg_i_num(0, None) { + let s = to_unit(num, to_unit_suffix::Pi); + return ValueRef::str(s.as_ref()).into_raw(); + } + panic!("to_Pi() missing 1 required positional argument: 'num'"); +} + +fn to_unit(num: f64, suffix: to_unit_suffix) -> String { + match suffix { + to_unit_suffix::n => format!("{}{:?}", (num / 1e-09) as i64, suffix), + to_unit_suffix::u => format!("{}{:?}", (num / 1e-06) as i64, suffix), + to_unit_suffix::m => format!("{}{:?}", (num / 0.001) as i64, suffix), + to_unit_suffix::k => format!("{}{:?}", (num / 1_000.0) as i64, suffix), + to_unit_suffix::K => format!("{}{:?}", (num / 1_000.0) as i64, suffix), + to_unit_suffix::M => format!("{}{:?}", (num / 1_000_000.0) as i64, suffix), + to_unit_suffix::G => format!("{}{:?}", (num / 1_000_000_000.0) as i64, suffix), + to_unit_suffix::T => format!("{}{:?}", (num / 1_000_000_000_000.0) as i64, suffix), + to_unit_suffix::P => format!("{}{:?}", (num / 1_000_000_000_000_000.0) as i64, suffix), + to_unit_suffix::Ki => format!("{}{:?}", (num / 1024.0) as i64, suffix), + to_unit_suffix::Mi => format!("{}{:?}", (num / (1024.0 * 1024.0)) as i64, suffix), + to_unit_suffix::Gi => format!("{}{:?}", (num / (1024.0 * 1024.0 * 1024.0)) as i64, suffix), + to_unit_suffix::Ti => format!( + "{}{:?}", + (num / (1024.0 * 1024.0 * 1024.0 * 1024.0)) as i64, + suffix + ), + to_unit_suffix::Pi => format!( + "{}{:?}", + (num / (1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)) as i64, + suffix + ), + } +} + +/// Parse and return number based on input quantity. +/// +/// Supported suffixes: +/// SI: n | u | m | k | K | M | G | T | P +/// IEC: Ki | Mi | Gi | Ti | Pi +/// +/// Input: +/// quantity: &str. +/// +/// Returns: +/// result: i64 +pub fn to_quantity(quantity: &str) -> i64 { + let quantity_len = quantity.len(); + let mut number = quantity; + let mut suffix: Option<&str> = None; + if quantity_len >= 2 && &quantity[quantity_len - 1..] == IEC_SUFFIX { + if EXPONENTS.contains_key(&quantity[quantity_len - 2..quantity_len - 1]) { + number = &quantity[..quantity_len - 2]; + suffix = Some(&quantity[quantity_len - 2..]); + } + } else if quantity_len >= 1 && EXPONENTS.contains_key(&quantity[quantity_len - 1..]) { + number = &quantity[..quantity_len - 1]; + suffix = Some(&quantity[quantity_len - 1..]); + } + if number.is_empty() { + panic!("number can't be empty") + } + let number: i64 = number.parse().unwrap(); + if suffix.is_none() { + return number; + } + let suffix = suffix.unwrap(); + validate_unit(&suffix[0..1]); + let base: i64 = if suffix.ends_with(IEC_SUFFIX) { + 1024 + } else { + 1000 + }; + let exponent = EXPONENTS.get(&suffix[0..1]).unwrap(); + number * (base.pow(*exponent as u32)) as i64 +} + +/// Calculate number based on value and binary suffix. +/// +/// Supported suffixes: +/// SI: n | u | m | k | K | M | G | T | P +/// IEC: Ki | Mi | Gi | Ti | Pi +/// +/// Input: +/// value: int. +/// suffix: str. +/// +/// Returns: +/// int +/// +/// Raises: +/// ValueError on invalid or unknown suffix +pub fn cal_num(value: i64, unit: &str) -> f64 { + validate_unit(unit); + let mut base: f64 = 1000.0; + let mut unit = unit; + if unit.len() > 1 && &unit[1..2] == IEC_SUFFIX { + base = 1024.0; + unit = &unit[0..1]; + } + let exponent = EXPONENTS + .get(unit) + .unwrap_or_else(|| panic!("invalid unit {}", unit)); + value as f64 * base.powf(*exponent as f64) +} + +#[inline] +pub fn real_uint_value(raw: i64, unit: &str) -> i128 { + (raw as i128) * (u64_unit_value(unit) as i128) +} + +/// Validate the unit is valid +pub fn validate_unit(unit: &str) { + if unit.is_empty() || unit.len() > 2 { + panic!("Invalid suffix {}", unit); + } + if INVALID_UNITS.contains(&unit) { + panic!("Invalid suffix {}", unit); + } + if !EXPONENTS.contains_key(&unit[..1]) { + panic!("Invalid suffix {}", unit); + } +} + +pub fn f64_unit_value(unit: &str) -> f64 { + match unit { + "n" => 1e-09, + "u" => 1e-06, + "m" => 0.001, + _ => 1_f64, + } +} + +pub fn u64_unit_value(unit: &str) -> u64 { + match unit { + "k" => 1_000, + "K" => 1_000, + "M" => 1_000_000, + "G" => 1_000_000_000, + "T" => 1_000_000_000_000, + "P" => 1_000_000_000_000_000, + "Ki" => 1024, + "Mi" => 1048576, + "Gi" => 1073741824, + "Ti" => 1099511627776, + "Pi" => 1125899906842624, + _ => 1_u64, + } +} diff --git a/kclvm/runtime/src/value/api.rs b/kclvm/runtime/src/value/api.rs new file mode 100644 index 000000000..95f45f335 --- /dev/null +++ b/kclvm/runtime/src/value/api.rs @@ -0,0 +1,2930 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use std::mem::transmute_copy; + +use crate::*; + +#[allow(non_camel_case_types)] +pub type kclvm_buffer_t = Buffer; + +#[allow(non_camel_case_types)] +pub type kclvm_context_t = Context; + +#[allow(non_camel_case_types)] +pub type kclvm_decorator_value_t = DecoratorValue; + +#[allow(non_camel_case_types)] +pub type kclvm_kind_t = Kind; + +#[allow(non_camel_case_types)] +pub type kclvm_type_t = Type; + +#[allow(non_camel_case_types)] +pub type kclvm_value_ref_t = ValueRef; + +#[allow(non_camel_case_types)] +pub type kclvm_iterator_t = ValueIterator; + +#[allow(non_camel_case_types)] +pub type kclvm_char_t = i8; + +#[allow(non_camel_case_types)] +pub type kclvm_size_t = i32; + +#[allow(non_camel_case_types)] +type kclvm_bool_t = i8; + +#[allow(non_camel_case_types)] +pub type kclvm_int_t = i64; + +#[allow(non_camel_case_types)] +pub type kclvm_float_t = f64; + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_context_set_import_names( + p: *mut kclvm_context_t, + import_names: *const kclvm_value_ref_t, +) { + let p = mut_ptr_as_ref(p); + let import_names = ptr_as_ref(import_names); + + let import_names_dict = import_names.as_dict_ref(); + for (k, v) in &import_names_dict.values { + let mut map = IndexMap::default(); + let v_dict = v.as_dict_ref(); + for (pkgname, pkgpath) in &v_dict.values { + map.insert(pkgname.to_string(), pkgpath.as_str()); + } + p.import_names.insert(k.to_string(), map); + } +} + +// ---------------------------------------------------------------------------- +// values: new +// ---------------------------------------------------------------------------- + +// singleton + +#[allow(non_camel_case_types, non_upper_case_globals)] +static mut kclvm_value_Undefined_obj: usize = 0; + +#[allow(non_camel_case_types, non_upper_case_globals)] +static mut kclvm_value_None_obj: usize = 0; + +#[allow(non_camel_case_types, non_upper_case_globals)] +static mut kclvm_value_Bool_true_obj: usize = 0; + +#[allow(non_camel_case_types, non_upper_case_globals)] +static mut kclvm_value_Bool_false_obj: usize = 0; + +#[allow(non_camel_case_types, non_upper_case_globals)] +static mut kclvm_value_Int_0_obj: usize = 0; + +#[allow(non_camel_case_types, non_upper_case_globals)] +static mut kclvm_value_Float_0_obj: usize = 0; + +// Undefine/None + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Undefined() -> *mut kclvm_value_ref_t { + ValueRef::undefined().into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_None() -> *mut kclvm_value_ref_t { + ValueRef::none().into_raw() +} + +// bool/int/float/str + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_True() -> *mut kclvm_value_ref_t { + kclvm_value_Bool(1) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_False() -> *mut kclvm_value_ref_t { + kclvm_value_Bool(0) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Bool(v: kclvm_bool_t) -> *mut kclvm_value_ref_t { + unsafe { + if v != 0 { + if kclvm_value_Bool_true_obj == 0 { + kclvm_value_Bool_true_obj = new_mut_ptr(ValueRef::bool(true)) as usize; + } + kclvm_value_Bool_true_obj as *mut kclvm_value_ref_t + } else { + if kclvm_value_Bool_false_obj == 0 { + kclvm_value_Bool_false_obj = new_mut_ptr(ValueRef::bool(false)) as usize; + } + kclvm_value_Bool_false_obj as *mut kclvm_value_ref_t + } + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Int(v: kclvm_int_t) -> *mut kclvm_value_ref_t { + if v == 0 { + unsafe { + if kclvm_value_Int_0_obj == 0 { + kclvm_value_Int_0_obj = new_mut_ptr(ValueRef::int(0)) as usize; + } + return kclvm_value_Int_0_obj as *mut kclvm_value_ref_t; + } + } + new_mut_ptr(ValueRef::int(v)) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Float(v: kclvm_float_t) -> *mut kclvm_value_ref_t { + if v == 0.0 { + unsafe { + if kclvm_value_Float_0_obj == 0 { + kclvm_value_Float_0_obj = new_mut_ptr(ValueRef::float(0.0)) as usize; + } + return kclvm_value_Float_0_obj as *mut kclvm_value_ref_t; + } + } + new_mut_ptr(ValueRef::float(v)) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Unit( + v: kclvm_float_t, + raw: kclvm_int_t, + unit: *const kclvm_char_t, +) -> *mut kclvm_value_ref_t { + let unit = c2str(unit); + new_mut_ptr(ValueRef::unit(v, raw, unit)) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Str(v: *const kclvm_char_t) -> *mut kclvm_value_ref_t { + unsafe { + if v.is_null() || *v == '\0' as i8 { + return new_mut_ptr(ValueRef::str("")); + } + } + return new_mut_ptr(ValueRef::str(c2str(v))); +} + +// list/dict/schema + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_List() -> *mut kclvm_value_ref_t { + new_mut_ptr(ValueRef::list(None)) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_List6( + v1: *const kclvm_value_ref_t, + v2: *const kclvm_value_ref_t, + v3: *const kclvm_value_ref_t, + v4: *const kclvm_value_ref_t, + v5: *const kclvm_value_ref_t, + v6: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let values: Vec<&ValueRef> = vec![v1, v2, v3, v4, v5, v6] + .into_iter() + .map(ptr_as_ref) + .collect(); + new_mut_ptr(ValueRef::list(Some(values.as_slice()))) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_List10( + v1: *const kclvm_value_ref_t, + v2: *const kclvm_value_ref_t, + v3: *const kclvm_value_ref_t, + v4: *const kclvm_value_ref_t, + v5: *const kclvm_value_ref_t, + v6: *const kclvm_value_ref_t, + v7: *const kclvm_value_ref_t, + v8: *const kclvm_value_ref_t, + v9: *const kclvm_value_ref_t, + v10: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let values: Vec<&ValueRef> = vec![v1, v2, v3, v4, v5, v6, v7, v8, v9, v10] + .into_iter() + .map(ptr_as_ref) + .collect(); + new_mut_ptr(ValueRef::list(Some(values.as_slice()))) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_ListN( + n: kclvm_int_t, + elem_values: *const *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let mut list = ListValue::default(); + + unsafe { + for xi in std::slice::from_raw_parts(elem_values, n as usize).iter() { + let v: &ValueRef = ptr_as_ref(*xi); + list.values.push(v.clone()); + } + + ValueRef::from(Value::list_value(list)).into_raw() + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Dict() -> *mut kclvm_value_ref_t { + new_mut_ptr(ValueRef::dict(None)) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Schema() -> *mut kclvm_value_ref_t { + new_mut_ptr(ValueRef::schema()) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_schema_with_config( + schema_dict: *const kclvm_value_ref_t, + config: *const kclvm_value_ref_t, + name: *const kclvm_char_t, + pkgpath: *const kclvm_char_t, + is_sub_schema: *const kclvm_value_ref_t, + record_instance: *const kclvm_value_ref_t, + instance_pkgpath: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let schema_dict = ptr_as_ref(schema_dict); + // Config dict + let config = ptr_as_ref(config); + let config_keys: Vec = config + .as_dict_ref() + .values + .keys() + .into_iter() + .cloned() + .collect(); + // Schema meta + let name = c2str(name); + let pkgpath = c2str(pkgpath); + let runtime_type = schema_runtime_type(name, pkgpath); + let is_sub_schema = ptr_as_ref(is_sub_schema); + let record_instance = ptr_as_ref(record_instance); + let instance_pkgpath = ptr_as_ref(instance_pkgpath); + let instance_pkgpath = instance_pkgpath.as_str(); + let schema = schema_dict.dict_to_schema(name, pkgpath, &config_keys); + // Runtime context + let ctx = Context::current_context(); + if record_instance.is_truthy() + && (instance_pkgpath.is_empty() || instance_pkgpath == MAIN_PKG_PATH) + { + // Record schema instance in the context + let mut instance_map = ctx.instances.borrow_mut(); + if !instance_map.contains_key(&runtime_type) { + instance_map.insert(runtime_type.clone(), vec![]); + } + instance_map + .get_mut(&runtime_type) + .unwrap() + .push(schema_dict.clone()); + } + // Dict to schema + if is_sub_schema.is_truthy() { + schema.into_raw() + } else { + schema_dict.clone().into_raw() + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Function( + fn_ptr: *const u64, + closure: *const kclvm_value_ref_t, + external_name: *const kclvm_char_t, +) -> *mut kclvm_value_ref_t { + let closure = ptr_as_ref(closure); + let name = c2str(external_name); + new_mut_ptr(ValueRef::func(fn_ptr as u64, 0, closure.clone(), name, "")) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Function_using_ptr(fn_ptr: *const u64) -> *mut kclvm_value_ref_t { + new_mut_ptr(ValueRef::func(fn_ptr as u64, 0, ValueRef::none(), "", "")) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_schema_function( + fn_ptr: *const u64, + check_fn_ptr: *const u64, + tpe: *const kclvm_char_t, +) -> *mut kclvm_value_ref_t { + // Schema function closures + let is_sub_schema = ValueRef::bool(false); + let config_meta = ValueRef::dict(None); + let config = ValueRef::dict(None); + let schema = ValueRef::dict(None); + let optional_mapping = ValueRef::dict(None); + let cal_map = ValueRef::dict(None); + let backtrack_level_map = ValueRef::dict(None); + let backtrack_cache = ValueRef::dict(None); + let record_instance = ValueRef::bool(false); + let instance_pkgpath = ValueRef::str(MAIN_PKG_PATH); + + let mut schema_args = ValueRef::list(None); + let schema_args_ref = schema_args.as_list_mut_ref(); + schema_args_ref.values.push(is_sub_schema); + schema_args_ref.values.push(config_meta); + schema_args_ref.values.push(config); + schema_args_ref.values.push(schema); + schema_args_ref.values.push(optional_mapping); + schema_args_ref.values.push(cal_map); + schema_args_ref.values.push(backtrack_level_map); + schema_args_ref.values.push(backtrack_cache); + schema_args_ref.values.push(record_instance); + schema_args_ref.values.push(instance_pkgpath); + + let runtime_type = c2str(tpe); + let schema_func = ValueRef::func( + fn_ptr as u64, + check_fn_ptr as u64, + schema_args, + "", + runtime_type, + ); + let ctx = Context::current_context_mut(); + let mut all_schemas = ctx.all_schemas.borrow_mut(); + all_schemas.insert(runtime_type.to_string(), schema_func.clone()); + new_mut_ptr(schema_func) +} + +// ---------------------------------------------------------------------------- +// values: json +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_from_json(s: *const kclvm_char_t) -> *mut kclvm_value_ref_t { + if s.is_null() { + return kclvm_value_Undefined(); + } + match ValueRef::from_json(c2str(s)) { + Some(x) => x.into_raw(), + _ => kclvm_value_Undefined(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_to_json(p: *const kclvm_value_ref_t) -> *mut kclvm_buffer_t { + let p = ptr_as_ref(p); + let x = p.to_json(); + let buf = Buffer::new_with_buf(&x); + buf.into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_to_json_value(p: *const kclvm_value_ref_t) -> *mut kclvm_value_ref_t { + if p.is_null() { + return kclvm_value_Str(std::ptr::null()); + } + + let p = ptr_as_ref(p); + let s = p.to_json_string(); + + return new_mut_ptr(ValueRef::str(s.as_ref())); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_to_json_value_with_null( + p: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + if p.is_null() { + return kclvm_value_Str(std::ptr::null()); + } + + let p = ptr_as_ref(p); + let s = p.to_json_string_with_null(); + + return new_mut_ptr(ValueRef::str(s.as_ref())); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_plan_to_json(p: *const kclvm_value_ref_t) -> *mut kclvm_value_ref_t { + let p = ptr_as_ref(p); + let s = p.plan_to_json_string(); + + return new_mut_ptr(ValueRef::str(s.as_ref())); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_plan_to_yaml(p: *const kclvm_value_ref_t) -> *mut kclvm_value_ref_t { + let p = ptr_as_ref(p); + let s = p.plan_to_yaml_string(); + + return new_mut_ptr(ValueRef::str(s.as_ref())); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_to_yaml_value(p: *const kclvm_value_ref_t) -> *mut kclvm_value_ref_t { + if p.is_null() { + return kclvm_value_Str(std::ptr::null()); + } + + let p = ptr_as_ref(p); + let s = p.to_yaml_string(); + + return new_mut_ptr(ValueRef::str(s.as_ref())); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_to_str_value(p: *const kclvm_value_ref_t) -> *mut kclvm_value_ref_t { + if p.is_null() { + return kclvm_value_Str(std::ptr::null()); + } + + let p = ptr_as_ref(p); + let s = p.to_string(); + + return new_mut_ptr(ValueRef::str(s.as_ref())); +} + +// ---------------------------------------------------------------------------- +// values: value pointer +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Bool_ptr(p: *const kclvm_value_ref_t) -> *const kclvm_bool_t { + let p = ptr_as_ref(p); + match &*p.rc { + Value::bool_value(ref v) => v as *const bool as *const kclvm_bool_t, // sizeof(bool) == sizeof(i8) + _ => std::ptr::null(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Int_ptr(p: *const kclvm_value_ref_t) -> *const kclvm_int_t { + let p = ptr_as_ref(p); + match &*p.rc { + Value::int_value(ref v) => v as *const kclvm_int_t, + _ => std::ptr::null(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Float_ptr(p: *const kclvm_value_ref_t) -> *const kclvm_float_t { + let p = ptr_as_ref(p); + match &*p.rc { + Value::float_value(ref v) => v as *const kclvm_float_t, + _ => std::ptr::null(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Str_ptr(p: *const kclvm_value_ref_t) -> *const kclvm_char_t { + let p = ptr_as_ref(p); + match &*p.rc { + Value::str_value(ref v) => v.as_ptr() as *const i8, + _ => std::ptr::null(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Str_len(p: *const kclvm_value_ref_t) -> kclvm_size_t { + let p = ptr_as_ref(p); + p.str_len() as kclvm_size_t +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Str_resize(p: *mut kclvm_value_ref_t, n: kclvm_size_t) { + let p = mut_ptr_as_ref(p); + p.str_resize(n as usize) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_function_ptr(p: *const kclvm_value_ref_t) -> *const u64 { + let p = ptr_as_ref(p); + match &*p.rc { + Value::func_value(ref v) => v.fn_ptr as *const u64, + _ => std::ptr::null::(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_check_function_ptr(p: *const kclvm_value_ref_t) -> *const u64 { + let p = ptr_as_ref(p); + match &*p.rc { + Value::func_value(ref v) => v.check_fn_ptr as *const u64, + _ => std::ptr::null::(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_function_is_external(p: *const kclvm_value_ref_t) -> kclvm_bool_t { + let p = ptr_as_ref(p); + match &*p.rc { + Value::func_value(ref v) => !v.external_name.is_empty() as kclvm_bool_t, + _ => false as kclvm_bool_t, + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_function_external_invoke( + p: *const kclvm_value_ref_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + match &*p.rc { + Value::func_value(ref v) => { + let name = format!("{}\0", v.external_name); + kclvm_plugin_invoke(name.as_ptr() as *const i8, args, kwargs) + } + _ => kclvm_value_None(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_function_invoke( + p: *const kclvm_value_ref_t, + ctx: *mut kclvm_context_t, + args: *mut kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, + pkgpath: *const kclvm_char_t, +) -> *const kclvm_value_ref_t { + let func = ptr_as_ref(p); + let args_ref = mut_ptr_as_ref(args); + if func.is_func() { + let func = func.as_function(); + let fn_ptr = func.fn_ptr; + let closure = &func.closure; + let is_schema = !func.runtime_type.is_empty(); + let is_external = !func.external_name.is_empty(); + let ctx_ref = mut_ptr_as_ref(ctx); + let now_meta_info = ctx_ref.panic_info.clone(); + unsafe { + let call_fn: SchemaTypeFunc = transmute_copy(&fn_ptr); + // Call schema constructor twice + let value = if is_schema { + let pkgpath = c2str(pkgpath); + // Schema function closure + let mut args_new = args_ref.deep_copy(); + let mut closure_new = closure.deep_copy(); + let config_meta_index: isize = 1; + let cal_map_index: isize = 5; + let record_instance_index = closure.len() - 2; + let instance_pkgpath_index = closure.len() - 1; + args_ref.list_append_unpack(closure); + let args = args_ref.clone().into_raw(); + call_fn(ctx, args, kwargs); + let cal_map = closure.list_get(cal_map_index).unwrap(); + // is sub schema + closure_new.list_set(0, &ValueRef::bool(true)); + // record instance + closure_new.list_set(record_instance_index, &ValueRef::bool(true)); + // instance pkgpath + closure_new.list_set(instance_pkgpath_index, &ValueRef::str(pkgpath)); + // cal map + closure_new.list_set(cal_map_index as usize, cal_map); + // config meta + let config_meta = schema_config_meta( + &ctx_ref.panic_info.kcl_file, + ctx_ref.panic_info.kcl_line as u64, + ctx_ref.panic_info.kcl_col as u64, + ); + closure_new.list_set(config_meta_index as usize, &config_meta); + args_new.list_append_unpack(&closure_new); + call_fn(ctx, args_new.into_raw(), kwargs) + // Normal kcl function, call directly + } else if is_external { + let name = format!("{}\0", func.external_name); + kclvm_plugin_invoke(name.as_ptr() as *const i8, args, kwargs) + } else { + args_ref.list_append_unpack_first(closure); + let args = args_ref.clone().into_raw(); + call_fn(ctx, args, kwargs) + }; + ctx_ref.panic_info = now_meta_info; + return value; + }; + } + kclvm_value_None() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_function_get_closure( + p: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let p = ptr_as_ref(p); + match &*p.rc { + Value::func_value(ref v) => v.closure.deep_copy().into_raw(), + Value::none | Value::undefined => kclvm_value_None(), + _ => panic!("invalid value of function self value function"), + } +} + +// ---------------------------------------------------------------------------- +// values: method +// ---------------------------------------------------------------------------- + +// kind + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_kind(p: *const kclvm_value_ref_t) -> kclvm_kind_t { + let p = ptr_as_ref(p); + p.kind() +} + +// clone + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_deep_copy(p: *const kclvm_value_ref_t) -> *mut kclvm_value_ref_t { + let p = ptr_as_ref(p); + p.deep_copy().into_raw() +} + +// delete + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_delete(p: *mut kclvm_value_ref_t) { + if p.is_null() { + return; + } + unsafe { + if p as usize == kclvm_value_Undefined_obj { + return; + } + if p as usize == kclvm_value_None_obj { + return; + } + if p as usize == kclvm_value_Bool_true_obj { + return; + } + if p as usize == kclvm_value_Bool_false_obj { + return; + } + + if p as usize == kclvm_value_Int_0_obj { + return; + } + if p as usize == kclvm_value_Float_0_obj { + return; + } + } + free_mut_ptr(p); +} + +// ---------------------------------------------------------------------------- +// values: iterator +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_iter(p: *const kclvm_value_ref_t) -> *mut kclvm_iterator_t { + let p = ptr_as_ref(p); + let iter = ValueIterator::from_value(p); + new_mut_ptr(iter) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_iterator_delete(p: *mut kclvm_iterator_t) { + free_mut_ptr(p); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_iterator_is_end(p: *mut kclvm_iterator_t) -> kclvm_bool_t { + let p = ptr_as_ref(p); + p.is_end() as kclvm_bool_t +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_iterator_cur_key(p: *mut kclvm_iterator_t) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + match p.key() { + Some(x) => x, + None => std::ptr::null(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_iterator_cur_value(p: *mut kclvm_iterator_t) -> *const kclvm_value_ref_t { + let p = mut_ptr_as_ref(p); + match p.value() { + Some(x) => x, + None => std::ptr::null(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_iterator_next_value( + p: *mut kclvm_iterator_t, + host: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let p = mut_ptr_as_ref(p); + let host = ptr_as_ref(host); + + match p.next(host) { + Some(x) => x, + None => std::ptr::null(), + } +} + +// ---------------------------------------------------------------------------- +// values: list +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_len(p: *const kclvm_value_ref_t) -> kclvm_size_t { + let p = ptr_as_ref(p); + p.len() as kclvm_size_t +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_resize(p: *mut kclvm_value_ref_t, newsize: kclvm_size_t) { + let p = mut_ptr_as_ref(p); + p.list_resize(newsize as usize); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_clear(p: *mut kclvm_value_ref_t) { + let p = mut_ptr_as_ref(p); + p.list_clear(); +} + +/// Return number of occurrences of the list value. +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_count( + p: *const kclvm_value_ref_t, + item: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + let item = ptr_as_ref(item); + let count = p.list_count(item); + let count_value = ValueRef::int(count as i64); + count_value.into_raw() +} + +/// Return first index of the list value. Panic if the value is not present. +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_find( + p: *const kclvm_value_ref_t, + item: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + let item = ptr_as_ref(item); + let index = p.list_find(item); + let index_value = ValueRef::int(index as i64); + index_value.into_raw() +} + +/// Insert object before index of the list value. +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_insert( + p: *mut kclvm_value_ref_t, + index: *const kclvm_value_ref_t, + value: *const kclvm_value_ref_t, +) { + let p = mut_ptr_as_ref(p); + let index = ptr_as_ref(index); + let value = ptr_as_ref(value); + p.list_insert_at(index.as_int() as usize, value); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_get( + p: *const kclvm_value_ref_t, + i: kclvm_size_t, +) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + match p.list_get(i as isize) { + Some(x) => x, + _ => panic!("list index out of range"), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_get_option( + p: *const kclvm_value_ref_t, + i: kclvm_size_t, +) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + match p.list_get_option(i as isize) { + Some(x) => x.clone().into_raw(), + _ => kclvm_value_Undefined(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_set( + p: *mut kclvm_value_ref_t, + i: kclvm_size_t, + v: *const kclvm_value_ref_t, +) { + let p = mut_ptr_as_ref(p); + let v = ptr_as_ref(v); + p.list_set(i as usize, v); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_pop(p: *mut kclvm_value_ref_t) -> *const kclvm_value_ref_t { + let p = mut_ptr_as_ref(p); + match p.list_pop() { + Some(x) => x, + _ => kclvm_value_Undefined(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_pop_first(p: *mut kclvm_value_ref_t) -> *const kclvm_value_ref_t { + let p = mut_ptr_as_ref(p); + match p.list_pop_first() { + Some(x) => x.into_raw(), + _ => kclvm_value_Undefined(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_append(p: *mut kclvm_value_ref_t, v: *const kclvm_value_ref_t) { + let p = mut_ptr_as_ref(p); + let v = ptr_as_ref(v); + p.list_append(v); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_append_bool(p: *mut kclvm_value_ref_t, v: kclvm_bool_t) { + let p = mut_ptr_as_ref(p); + p.list_append(&ValueRef::bool(v != 0)); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_append_int(p: *mut kclvm_value_ref_t, v: kclvm_int_t) { + let p = mut_ptr_as_ref(p); + p.list_append(&ValueRef::int(v)); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_append_float(p: *mut kclvm_value_ref_t, v: kclvm_float_t) { + let p = mut_ptr_as_ref(p); + p.list_append(&ValueRef::float(v)); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_append_str(p: *mut kclvm_value_ref_t, v: *const kclvm_char_t) { + let p = mut_ptr_as_ref(p); + p.list_append(&ValueRef::str(c2str(v))); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_append_unpack(p: *mut kclvm_value_ref_t, v: *const kclvm_value_ref_t) { + let p = mut_ptr_as_ref(p); + let v = ptr_as_ref(v); + + if let Value::list_value(ref _list) = &*p.rc { + p.list_append_unpack(v); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_list_remove_at(p: *mut kclvm_value_ref_t, i: kclvm_size_t) { + let p = mut_ptr_as_ref(p); + p.list_remove_at(i as usize); +} + +// ---------------------------------------------------------------------------- +// values: dict +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_len(p: *const kclvm_value_ref_t) -> kclvm_size_t { + let p = ptr_as_ref(p); + match &*p.rc { + Value::dict_value(ref dict) => dict.values.len() as kclvm_size_t, + _ => 0, + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_clear(p: *mut kclvm_value_ref_t) { + let p = mut_ptr_as_ref(p); + p.dict_clear(); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_get( + p: *const kclvm_value_ref_t, + key: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + let key = ptr_as_ref(key); + + match p.dict_get(key) { + Some(x) => x.clone().into_raw(), + None => kclvm_value_Undefined(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_has_value( + p: *const kclvm_value_ref_t, + key: *const kclvm_char_t, +) -> kclvm_bool_t { + let p = ptr_as_ref(p); + let key = c2str(key); + match p.dict_get_value(key) { + Some(_) => true as kclvm_bool_t, + None => false as kclvm_bool_t, + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_get_value( + p: *const kclvm_value_ref_t, + key: *const kclvm_char_t, +) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + let key = c2str(key); + match p.dict_get_value(key) { + Some(x) => x.clone().into_raw(), + None => kclvm_value_Undefined(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_get_entry( + p: *const kclvm_value_ref_t, + key: *const kclvm_char_t, +) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + let key = c2str(key); + match p.dict_get_entry(key) { + Some(x) => x.into_raw(), + None => kclvm_value_Undefined(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_get_value_by_path( + p: *const kclvm_value_ref_t, + path: *const kclvm_char_t, +) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + let path = c2str(path); + match p.get_by_path(path) { + Some(x) => x.clone().into_raw(), + None => kclvm_value_Undefined(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_set_value( + p: *mut kclvm_value_ref_t, + key: *const kclvm_char_t, + val: *const kclvm_value_ref_t, +) { + let p = mut_ptr_as_ref(p); + let key = c2str(key); + let val = ptr_as_ref(val); + if p.is_config() { + p.dict_update_key_value(key, val.clone()); + } + if p.is_schema() { + let schema_value = p.as_schema(); + let mut config_keys = schema_value.config_keys.clone(); + config_keys.push(key.to_string()); + let schema = resolve_schema(p, &config_keys); + p.schema_update_with_schema(&schema); + } + /*panic*/ +} + +#[no_mangle] +#[runtime_fn] +/// Return all dict keys. +pub extern "C" fn kclvm_dict_keys(p: *const kclvm_value_ref_t) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + let r = p.dict_keys(); + r.into_raw() +} + +#[no_mangle] +#[runtime_fn] +/// Return all dict values. +pub extern "C" fn kclvm_dict_values(p: *const kclvm_value_ref_t) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + let r = p.dict_values(); + r.into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_insert( + p: *mut kclvm_value_ref_t, + key: *const kclvm_char_t, + v: *const kclvm_value_ref_t, + op: kclvm_size_t, + insert_index: kclvm_size_t, +) { + let p = mut_ptr_as_ref(p); + let v = ptr_as_ref(v); + p.dict_insert( + c2str(key), + v, + ConfigEntryOperationKind::from_i32(op), + insert_index, + ); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_merge( + p: *mut kclvm_value_ref_t, + key: *const kclvm_char_t, + v: *const kclvm_value_ref_t, + op: kclvm_size_t, + insert_index: kclvm_size_t, +) { + let p = mut_ptr_as_ref(p); + let v = ptr_as_ref(v); + let key = c2str(key); + let attr_map = { + match &*p.rc { + Value::dict_value(dict) => &dict.attr_map, + Value::schema_value(schema) => &schema.config.attr_map, + _ => panic!("invalid object '{}' in attr_map", p.type_str()), + } + }; + if attr_map.contains_key(key) { + let v = type_pack_and_check(v, vec![attr_map.get(key).unwrap()]); + p.dict_merge( + key, + &v, + ConfigEntryOperationKind::from_i32(op), + insert_index, + ); + } else { + p.dict_merge(key, v, ConfigEntryOperationKind::from_i32(op), insert_index); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_insert_value( + p: *mut kclvm_value_ref_t, + key: *const kclvm_value_ref_t, + v: *const kclvm_value_ref_t, + op: kclvm_size_t, + insert_index: kclvm_size_t, +) { + let p = mut_ptr_as_ref(p); + let v = ptr_as_ref(v); + let key = ptr_as_ref(key); + let key = key.attr_str(); + p.dict_insert( + key.as_str(), + v, + ConfigEntryOperationKind::from_i32(op), + insert_index, + ); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_update_key_value( + p: *mut kclvm_value_ref_t, + key: *const kclvm_value_ref_t, + v: *const kclvm_value_ref_t, +) { + let p = mut_ptr_as_ref(p); + let v = ptr_as_ref(v); + let key = ptr_as_ref(key); + let key = key.attr_str(); + p.dict_update_key_value(key.as_str(), v.clone()); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_safe_insert( + p: *mut kclvm_value_ref_t, + key: *const kclvm_char_t, + v: *const kclvm_value_ref_t, + op: kclvm_size_t, + insert_index: kclvm_size_t, +) { + if p.is_null() || key.is_null() || v.is_null() { + return; + } + kclvm_dict_insert(p, key, v, op, insert_index); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_insert_unpack(p: *mut kclvm_value_ref_t, v: *const kclvm_value_ref_t) { + let p = mut_ptr_as_ref(p); + let v = ptr_as_ref(v); + p.dict_insert_unpack(v); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_default_collection_insert_int_pointer( + p: *mut kclvm_value_ref_t, + key: *const kclvm_char_t, + ptr: *const u64, +) { + let p = mut_ptr_as_ref(p); + let key = c2str(key); + let ptr = ptr as i64; + if p.is_dict() { + let dict_ref_mut = p.as_dict_mut_ref(); + if !dict_ref_mut.values.contains_key(key) { + let value = ValueRef::list(None); + dict_ref_mut.values.insert(key.to_string(), value); + } + let values = dict_ref_mut.values.get_mut(key).unwrap(); + let value = ValueRef::int(ptr); + if !value.r#in(values) { + values.list_append(&value); + } + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_default_collection_insert_value( + p: *mut kclvm_value_ref_t, + key: *const kclvm_char_t, + value: *const kclvm_value_ref_t, +) { + let p = mut_ptr_as_ref(p); + let key = c2str(key); + let value = ptr_as_ref(value); + if p.is_dict() { + let dict_ref_mut = p.as_dict_mut_ref(); + if !dict_ref_mut.values.contains_key(key) { + let value = ValueRef::list(None); + dict_ref_mut.values.insert(key.to_string(), value); + } + let values = dict_ref_mut.values.get_mut(key).unwrap(); + if !value.r#in(values) { + values.list_append(value); + } + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_remove(p: *mut kclvm_value_ref_t, key: *const kclvm_char_t) { + let p = mut_ptr_as_ref(p); + p.dict_remove(c2str(key)); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_dict_update(p: *mut kclvm_value_ref_t, v: *const kclvm_value_ref_t) { + let p = mut_ptr_as_ref(p); + let v = ptr_as_ref(v); + p.dict_update(v); +} + +// ---------------------------------------------------------------------------- +// values: arith +// ---------------------------------------------------------------------------- + +// is true + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_is_truthy(p: *const kclvm_value_ref_t) -> kclvm_bool_t { + let p = ptr_as_ref(p); + p.is_truthy() as kclvm_bool_t +} + +// len + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_len(p: *const kclvm_value_ref_t) -> kclvm_size_t { + let p = ptr_as_ref(p); + p.len() as kclvm_size_t +} + +// compare + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_cmp_equal_to( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + if a == b { + return kclvm_value_Bool(1); + } + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + ValueRef::bool(a.cmp_equal(b)).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_cmp_not_equal_to( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + if a == b { + return kclvm_value_Bool(0); + } + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + ValueRef::bool(!a.cmp_equal(b)).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_cmp_less_than( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + if a == b { + return kclvm_value_Bool(0); + } + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + ValueRef::bool(a.cmp_less_than(b)).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_cmp_less_than_or_equal( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + ValueRef::bool(a.cmp_less_than_or_equal(b)).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_cmp_greater_than( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + if a == b { + return kclvm_value_Bool(0); + } + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + ValueRef::bool(a.cmp_greater_than(b)).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_cmp_greater_than_or_equal( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + ValueRef::bool(a.cmp_greater_than_or_equal(b)).into_raw() +} + +// is/in + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_is( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + if a == b { + return kclvm_value_Bool(1); + } + kclvm_value_Bool(0) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_is_not( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + if a == b { + return kclvm_value_Bool(0); + } + kclvm_value_Bool(1) +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_in( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + ValueRef::bool(a.r#in(b)).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_not_in( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + ValueRef::bool(a.not_in(b)).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_as( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + let ty_str = b.as_str(); + let value = type_pack_and_check(a, vec![ty_str.as_str()]); + value.into_raw() +} + +// unary-xxx + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_unary_plus(a: *const kclvm_value_ref_t) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + a.unary_plus().into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_unary_minus(a: *const kclvm_value_ref_t) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + a.unary_minus().into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_unary_not(a: *const kclvm_value_ref_t) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + a.unary_not().into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_unary_l_not(a: *const kclvm_value_ref_t) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + a.unary_l_not().into_raw() +} + +// op-xxx + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_add( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_add(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_sub( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_sub(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_mul( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_mul(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_div( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_div(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_mod( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_mod(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_pow( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_pow(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_floor_div( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_floor_div(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_bit_lshift( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_bit_lshift(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_bit_rshift( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_bit_rshift(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_bit_and( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_bit_and(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_bit_xor( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_bit_xor(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_bit_or( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_bit_or(b).into_raw() +} + +// op-aug-xxx + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_aug_add( + a: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + return a.bin_aug_add(b) as *const kclvm_value_ref_t; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_aug_sub( + a: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + return a.bin_aug_sub(b) as *const kclvm_value_ref_t; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_aug_mul( + a: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + return a.bin_aug_mul(b) as *const kclvm_value_ref_t; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_aug_div( + a: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + return a.bin_aug_div(b) as *const kclvm_value_ref_t; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_aug_mod( + a: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + return a.bin_aug_mod(b) as *const kclvm_value_ref_t; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_aug_pow( + a: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + return a.bin_aug_pow(b) as *const kclvm_value_ref_t; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_aug_floor_div( + a: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + return a.bin_aug_floor_div(b) as *const kclvm_value_ref_t; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_aug_bit_lshift( + a: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_aug_bit_lshift(b) as *const kclvm_value_ref_t +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_aug_bit_rshift( + a: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_aug_bit_rshift(b) as *const kclvm_value_ref_t +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_aug_bit_and( + a: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_aug_bit_and(b) as *const kclvm_value_ref_t +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_aug_bit_xor( + a: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_aug_bit_xor(b) as *const kclvm_value_ref_t +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_op_aug_bit_or( + a: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_aug_bit_or(b) as *const kclvm_value_ref_t +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_union( + schema: *mut kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let a = mut_ptr_as_ref(schema); + let b = ptr_as_ref(b); + let attr_map = { + match &*a.rc { + Value::dict_value(dict) => &dict.attr_map, + Value::schema_value(schema) => &schema.config.attr_map, + _ => panic!("invalid object '{}' in attr_map", a.type_str()), + } + }; + if b.is_config() { + let dict = b.as_dict_ref(); + let mut result = schema; + for (k, v) in &dict.values { + if attr_map.contains_key(k) { + let v = type_pack_and_check(v, vec![attr_map.get(k).unwrap()]); + let mut entry = b.dict_get_entry(k).unwrap().deep_copy(); + entry.dict_update_key_value(k, v); + result = a + .union(&entry, true, false, false, false) + .clone() + .into_raw(); + } else { + let entry = b.dict_get_entry(k).unwrap(); + result = a + .union(&entry, true, false, false, false) + .clone() + .into_raw(); + } + } + result + } else { + a.union(b, true, false, false, false).into_raw() + } +} + +// logic: && || + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_logic_and( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + ValueRef::bool(a.logic_and(b)).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_logic_or( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + ValueRef::bool(a.logic_or(b)).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_subscr( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_subscr(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_subscr_option( + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, +) -> *mut kclvm_value_ref_t { + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + a.bin_subscr_option(b).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_load_attr( + obj: *const kclvm_value_ref_t, + key: *const kclvm_char_t, +) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(obj); + let key = c2str(key); + // load_attr including str/dict/schema. + if p.is_dict() { + match p.dict_get_value(key) { + Some(x) => { + return x.clone().into_raw(); + } + None => { + return kclvm_value_Undefined(); + } + } + } else if p.is_schema() { + let dict = p.schema_to_dict(); + match dict.dict_get_value(key) { + Some(x) => { + return x.clone().into_raw(); + } + None => panic!("schema '{}' attribute '{}' not found", p.type_str(), key), + } + } else if p.is_str() { + let function = match key { + "lower" => kclvm_builtin_str_lower, + "upper" => kclvm_builtin_str_upper, + "capitalize" => kclvm_builtin_str_capitalize, + "count" => kclvm_builtin_str_count, + "endswith" => kclvm_builtin_str_endswith, + "find" => kclvm_builtin_str_find, + "format" => kclvm_builtin_str_format, + "index" => kclvm_builtin_str_index, + "isalnum" => kclvm_builtin_str_isalnum, + "isalpha" => kclvm_builtin_str_isalpha, + "isdigit" => kclvm_builtin_str_isdigit, + "islower" => kclvm_builtin_str_islower, + "isspace" => kclvm_builtin_str_isspace, + "istitle" => kclvm_builtin_str_istitle, + "isupper" => kclvm_builtin_str_isupper, + "join" => kclvm_builtin_str_join, + "lstrip" => kclvm_builtin_str_lstrip, + "rstrip" => kclvm_builtin_str_rstrip, + "replace" => kclvm_builtin_str_replace, + "rfind" => kclvm_builtin_str_rfind, + "rindex" => kclvm_builtin_str_rindex, + "rsplit" => kclvm_builtin_str_rsplit, + "split" => kclvm_builtin_str_split, + "splitlines" => kclvm_builtin_str_splitlines, + "startswith" => kclvm_builtin_str_startswith, + "strip" => kclvm_builtin_str_strip, + "title" => kclvm_builtin_str_title, + _ => panic!("str object attr '{}' not found", key), + }; + let closure = ValueRef::list(Some(&[p])); + return new_mut_ptr(ValueRef::func(function as usize as u64, 0, closure, "", "")); + } + // schema instance + else if p.is_func() { + let function = match key { + "instances" => kclvm_schema_instances, + _ => panic!("schema object attr '{}' not found", key), + }; + let closure = ValueRef::list(Some(&[p])); + return new_mut_ptr(ValueRef::func(function as usize as u64, 0, closure, "", "")); + } + panic!( + "invalid value '{}' to load attribute '{}'", + p.type_str(), + key + ); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_load_attr_option( + p: *const kclvm_value_ref_t, + key: *const kclvm_char_t, +) -> *const kclvm_value_ref_t { + let p_ref = ptr_as_ref(p); + if p_ref.is_truthy() { + kclvm_value_load_attr(p, key) + } else { + kclvm_value_None() + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_remove_item(a: *mut kclvm_value_ref_t, b: *const kclvm_value_ref_t) { + let a = mut_ptr_as_ref(a); + let b = ptr_as_ref(b); + if a.is_dict() { + a.dict_remove(&b.as_str()); + } else if a.is_list() { + a.list_remove(b); + } else { + panic!("only list, dict and schema can be removed item"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_slice( + x: *const kclvm_value_ref_t, + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, + step: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let x = ptr_as_ref(x); + let a = ptr_as_ref(a); + let b = ptr_as_ref(b); + let step = ptr_as_ref(step); + x.list_slice(a, b, step).into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_slice_option( + x: *const kclvm_value_ref_t, + a: *const kclvm_value_ref_t, + b: *const kclvm_value_ref_t, + step: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let value = ptr_as_ref(x); + if value.is_truthy() { + kclvm_value_slice(x, a, b, step) + } else { + kclvm_value_None() + } +} + +// ---------------------------------------------------------------------------- +// values: schema +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_schema_backtrack_cache( + schema: *const kclvm_value_ref_t, + cache: *mut kclvm_value_ref_t, + cal_map: *const kclvm_value_ref_t, + name: *const kclvm_char_t, + runtime_type: *const kclvm_value_ref_t, +) { + let schema = ptr_as_ref(schema); + let cache = mut_ptr_as_ref(cache); + let cal_map = ptr_as_ref(cal_map); + let name = c2str(name); + if let Some(v) = cal_map.dict_get_value(name) { + if v.len() == 1 { + if let Some(value) = schema.dict_get_value(name) { + cache.dict_update_key_value(name, value.clone()); + } + } else { + match ( + cal_map.dict_get_value(&format!("{}_{}", name, CAL_MAP_RUNTIME_TYPE)), + cal_map.dict_get_value(&format!("{}_{}", name, CAL_MAP_META_LINE)), + ) { + (Some(cal_map_runtime_type_list), Some(cal_map_meta_line_list)) => { + match ( + cal_map_runtime_type_list.list_get(-1), + cal_map_meta_line_list.list_get(-1), + ) { + (Some(cal_map_runtime_type), Some(cal_map_meta_line)) => { + let runtime_type = ptr_as_ref(runtime_type); + let line = Context::current_context().panic_info.kcl_line as i64; + let cal_map_meta_line = cal_map_meta_line.as_int(); + if runtime_type == cal_map_runtime_type && line >= cal_map_meta_line { + if let Some(value) = schema.dict_get_value(name) { + cache.dict_update_key_value(name, value.clone()); + } + } + } + _ => {} + } + } + _ => {} + } + } + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_schema_instances( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let ctx = ptr_as_ref(ctx); + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + if let Some(val) = args.pop_arg_first() { + let function = val.as_function(); + let main_pkg = args.arg_0().or_else(|| kwargs.kwarg("main_pkg")); + let main_pkg = if let Some(v) = main_pkg { + v.is_truthy() + } else { + true + }; + let runtime_type = &function.runtime_type; + let instance_map = ctx.instances.borrow_mut(); + if instance_map.contains_key(runtime_type) { + let mut list = ValueRef::list(None); + for v in instance_map.get(runtime_type).unwrap() { + if v.is_schema() { + let schema = v.as_schema(); + if main_pkg { + if schema.pkgpath == MAIN_PKG_PATH { + list.list_append(v) + } + } else { + list.list_append(v) + } + } else if v.is_dict() { + let runtime_type_attr_path = + format!("{}.{}", SCHEMA_SETTINGS_ATTR_NAME, SETTINGS_SCHEMA_TYPE_KEY); + let runtime_type = + if let Some(runtime_type) = v.get_by_path(&runtime_type_attr_path) { + runtime_type.as_str() + } else { + runtime_type.to_string() + }; + let names: Vec<&str> = runtime_type.rsplit('.').collect(); + let name = names[0]; + let pkgpath = names[1]; + let v = v.dict_to_schema(name, pkgpath, &[]); + list.list_append(&v); + } + } + list.into_raw() + } else { + kclvm_value_List() + } + } else { + kclvm_value_None() + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_schema_value_check( + schema_value: *mut kclvm_value_ref_t, + schema_config: *const kclvm_value_ref_t, + _config_meta: *const kclvm_value_ref_t, + schema_name: *const kclvm_char_t, + index_sign_value: *const kclvm_value_ref_t, + _key_name: *const kclvm_char_t, + key_type: *const kclvm_char_t, + _value_type: *const kclvm_char_t, + _any_other: kclvm_bool_t, + is_relaxed: kclvm_bool_t, +) { + let schema_value = mut_ptr_as_ref(schema_value); + let schema_config = ptr_as_ref(schema_config); + let index_sign_value = ptr_as_ref(index_sign_value); + let key_type = c2str(key_type); + let has_index_signature = !key_type.is_empty(); + let should_add_attr = is_relaxed != 0 || has_index_signature; + + let ctx = Context::current_context_mut(); + if ctx.cfg.disable_schema_check { + return; + } + + let config = schema_config.as_dict_ref(); + for (key, value) in &config.values { + let is_not_in_schema = schema_value.dict_get_value(key).is_none(); + if should_add_attr && is_not_in_schema { + let value = index_sign_value + .deep_copy() + .union(value, true, false, false, true); + let op = config + .ops + .get(key) + .or_else(|| Some(&ConfigEntryOperationKind::Union)) + .unwrap(); + schema_value.dict_update_entry(key.as_str(), &value.clone(), op, &-1); + } else if !should_add_attr && is_not_in_schema { + let schema_name = c2str(schema_name); + panic!("{}: No such member in the schema '{}'", key, schema_name); + } + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_schema_do_check_with_index_sign_attr( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, + check_fn_ptr: *const u64, + attr_name: *const kclvm_char_t, +) { + let check_fn_ptr = check_fn_ptr as u64; + let args_value = ptr_as_ref(args); + let attr_name = c2str(attr_name); + unsafe { + let check_fn: SchemaTypeFunc = transmute_copy(&check_fn_ptr); + // args_0: config_meta, args_1: config, args_2: schema, args_3: cal_map + // Schema check function closure + let config_meta = args_value.arg_i(0).unwrap(); + let config = args_value.arg_i(1).unwrap(); + let schema = args_value.arg_i(2).unwrap(); + let cal_map = args_value.arg_i(3).unwrap(); + let backtrack_level_map = args_value.arg_i(4).unwrap(); + let backtrack_cache = args_value.arg_i(5).unwrap(); + let schema = get_ref_mut(schema); + for (k, _) in &config.as_dict_ref().values { + // relaxed keys + if schema.attr_map_get(k).is_none() { + let value = ValueRef::str(k); + schema.dict_update_key_value(attr_name, value); + let args = &mut ValueRef::list(None); + // Schema check function closure + args.list_append(config_meta); + args.list_append(config); + args.list_append(schema); + args.list_append(cal_map); + args.list_append(backtrack_level_map); + args.list_append(backtrack_cache); + let args = args.clone().into_raw(); + check_fn(ctx, args, kwargs); + } + } + schema.dict_remove(attr_name); + }; +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_schema_optional_check( + p: *const kclvm_value_ref_t, + v: *const kclvm_value_ref_t, + schema_name: *const kclvm_char_t, + config_meta: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let p = ptr_as_ref(p); + let v = ptr_as_ref(v); + let schema_name = c2str(schema_name); + let config_meta = ptr_as_ref(config_meta); + + let ctx = Context::current_context_mut(); + if ctx.cfg.disable_schema_check { + return kclvm_value_None(); + } + + p.schema_check_attr_optional(v, schema_name, config_meta); + kclvm_value_None() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_schema_default_settings( + schema_value: *mut kclvm_value_ref_t, + config_value: *const kclvm_value_ref_t, + runtime_type: *const kclvm_char_t, +) { + let schema_value = mut_ptr_as_ref(schema_value); + let config_value = ptr_as_ref(config_value); + let runtime_type = c2str(runtime_type); + schema_value.schema_default_settings(config_value, runtime_type); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_schema_assert( + value: *const kclvm_value_ref_t, + msg: *const kclvm_value_ref_t, + config_meta: *const kclvm_value_ref_t, +) { + let value = ptr_as_ref(value); + let msg = ptr_as_ref(msg); + let config_meta = ptr_as_ref(config_meta); + if !value.is_truthy() { + let ctx = Context::current_context_mut(); + ctx.set_err_type(&ErrType::SchemaCheckFailure_TYPE); + if let Some(config_meta_file) = config_meta.get_by_key(CONFIG_META_FILENAME) { + let config_meta_line = config_meta.get_by_key(CONFIG_META_LINE).unwrap(); + let config_meta_column = config_meta.get_by_key(CONFIG_META_COLUMN).unwrap(); + ctx.set_kcl_config_meta_location_info( + Some("Instance check failed"), + Some(config_meta_file.as_str().as_str()), + Some(config_meta_line.as_int() as i32), + Some(config_meta_column.as_int() as i32), + ); + } + + ctx.set_kcl_location_info(Some("Check failed on the condition"), None, None, None); + + panic!("{}", msg.as_str()); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_schema_value_new( + ctx: *mut kclvm_context_t, + args: *mut kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, + schema_value_or_func: *const kclvm_value_ref_t, + config: *const kclvm_value_ref_t, + config_meta: *const kclvm_value_ref_t, + pkgpath: *const kclvm_char_t, +) -> *const kclvm_value_ref_t { + let schema_value_or_func = ptr_as_ref(schema_value_or_func); + if schema_value_or_func.is_func() { + let schema_func = schema_value_or_func.as_function(); + let schema_fn_ptr = schema_func.fn_ptr; + let value = unsafe { + let org_args = ptr_as_ref(args).deep_copy(); + let schema_fn: SchemaTypeFunc = transmute_copy(&schema_fn_ptr); + let cal_map = kclvm_value_Dict(); + let instance_pkgpath = kclvm_value_Str(pkgpath); + // Schema function closures + let values = [ + // is_sub_schema + kclvm_value_Bool(0), + // Config meta + config_meta, + // Config value + config, + // Schema value + kclvm_value_Dict(), + // optional_mapping + kclvm_value_Dict(), + // cal order map + cal_map, + // backtrack level map + kclvm_value_Dict(), + // backtrack cache + kclvm_value_Dict(), + // record instance + kclvm_value_Bool(0), + // instance pkgpath + instance_pkgpath, + ]; + for value in values { + kclvm_list_append(args, value); + } + schema_fn(ctx, args, kwargs); + // schema args + let args = org_args.into_raw(); + let values = [ + // is_sub_schema + kclvm_value_Bool(1), + // Config meta + config_meta, + // Config value + config, + // Schema value + kclvm_value_Dict(), + // optional_mapping + kclvm_value_Dict(), + // cal order map + cal_map, + // backtrack level map + kclvm_value_Dict(), + // backtrack cache + kclvm_value_Dict(), + // record instance + kclvm_value_Bool(1), + // instance pkgpath + instance_pkgpath, + ]; + for value in values { + kclvm_list_append(args, value); + } + schema_fn(ctx, args, kwargs) + }; + value + } else { + let config = ptr_as_ref(config); + let result = schema_value_or_func + .deep_copy() + .union(config, true, false, true, true); + result.into_raw() + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_convert_collection_value( + value: *const kclvm_value_ref_t, + tpe: *const kclvm_char_t, +) -> *const kclvm_value_ref_t { + let value = ptr_as_ref(value); + let tpe = c2str(tpe); + let value = convert_collection_value(value, tpe); + value.into_raw() +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_schema_get_value( + p: *const kclvm_value_ref_t, + key: *const kclvm_char_t, + config: *const kclvm_value_ref_t, + config_meta: *const kclvm_value_ref_t, + cal_map: *const kclvm_value_ref_t, + target_attr: *const kclvm_char_t, + backtrack_level_map: *mut kclvm_value_ref_t, + backtrack_cache: *mut kclvm_value_ref_t, + args: *mut kclvm_value_ref_t, + kwargs: *mut kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let schema = ptr_as_ref(p); + let key = c2str(key); + let cal_map = ptr_as_ref(cal_map); + let target_attr = c2str(target_attr); + let backtrack_level_map = mut_ptr_as_ref(backtrack_level_map); + let backtrack_cache = mut_ptr_as_ref(backtrack_cache); + let args_org = mut_ptr_as_ref(args); + let kwargs = mut_ptr_as_ref(kwargs); + let default_level = ValueRef::int(0); + let level = backtrack_level_map + .dict_get_value(key) + .or(Some(&default_level)) + .unwrap(); + let level = level.as_int(); + let is_backtracking = level > 0; + // Deal in-place modify and return it self immediately + if key == target_attr && !is_backtracking { + match schema.dict_get_value(key) { + Some(x) => return x.clone().into_raw(), + None => return kclvm_value_Undefined(), + } + } + if let Some(v) = backtrack_cache.dict_get_value(key) { + return v.clone().into_raw(); + } + if let Some(attr_code) = cal_map.dict_get_value(key) { + let now_level = level + 1; + backtrack_level_map.dict_update_key_value(key, ValueRef::int(now_level)); + let attr_code = attr_code.as_list_ref(); + let n = attr_code.values.len(); + let index = n - now_level as usize; + if index >= n { + let value = match schema.dict_get_value(key) { + Some(x) => x.clone(), + None => ValueRef::undefined(), + }; + return value.into_raw(); + } + let fn_ptr = &attr_code.values[index]; + let fn_ptr = fn_ptr.as_int(); + unsafe { + let attr_fn: SchemaTypeFunc = transmute_copy(&fn_ptr); + // args_0: config_meta, args_1: config, args_2: schema, args_3: cal_map + let config_meta = ptr_as_ref(config_meta); + let config = ptr_as_ref(config); + let schema = get_ref_mut(schema); + let mut args = ValueRef::list(None); + let args_org = args_org.as_list_ref(); + for value in &args_org.values { + args.list_append(value); + } + // Schema attr function closure + args.list_append(config_meta); + args.list_append(config); + args.list_append(schema); + args.list_append(cal_map); + args.list_append(backtrack_level_map); + args.list_append(backtrack_cache); + let args = args.into_raw(); + let kwargs = kwargs.clone().into_raw(); + let ctx = kclvm_context_current(); + attr_fn(ctx, args, kwargs); + }; + backtrack_level_map.dict_update_key_value(key, ValueRef::int(level)); + let value = match schema.dict_get_value(key) { + Some(x) => x.clone(), + None => ValueRef::undefined(), + }; + backtrack_cache.dict_update_key_value(key, value); + } + match schema.dict_get_value(key) { + Some(x) => x.clone().into_raw(), + None => kclvm_value_Undefined(), + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_config_attr_map( + value: *mut kclvm_value_ref_t, + name: *const kclvm_char_t, + type_str: *const kclvm_char_t, +) { + let value = mut_ptr_as_ref(value); + let name = c2str(name); + let type_str = c2str(type_str); + value.update_attr_map(name, type_str); +} + +// ---------------------------------------------------------------------------- +// values: decorators +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_value_Decorator( + name: *const kclvm_char_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, + config_meta: *const kclvm_value_ref_t, + attr_name: *const kclvm_char_t, + config_value: *const kclvm_value_ref_t, + is_schema_target: *const kclvm_value_ref_t, +) -> *const kclvm_decorator_value_t { + let name = c2str(name); + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + let config_meta = ptr_as_ref(config_meta); + let attr_name = c2str(attr_name); + let config_value = ptr_as_ref(config_value); + let is_schema_target = ptr_as_ref(is_schema_target); + let decorator = DecoratorValue::new(name, args, kwargs); + decorator.run( + attr_name, + is_schema_target.as_bool(), + config_value, + config_meta, + ); + decorator.into_raw() +} + +// ---------------------------------------------------------------------------- +// values: string member functions +// ---------------------------------------------------------------------------- + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_lower( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + val.str_lower().into_raw() + } else { + panic!("invalid self value in str_lower"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_upper( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + val.str_upper().into_raw() + } else { + panic!("invalid self value in str_upper"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_capitalize( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + val.str_capitalize().into_raw() + } else { + panic!("invalid self value in str_capitalize"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_count( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + if let Some(sub) = args.arg_0() { + let start = args.arg_i(1); + let end = args.arg_i(2); + val.str_count(sub, start, end).into_raw() + } else { + panic!("count() takes at least 1 argument (0 given)"); + } + } else { + panic!("invalid self value in str_count"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_endswith( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + if let Some(suffix) = args.arg_0() { + let start = args.arg_i(1); + let end = args.arg_i(2); + val.str_endswith(suffix, start, end).into_raw() + } else { + panic!("endswith() takes at least 1 argument (0 given)"); + } + } else { + panic!("invalid self value in str_endswith"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_find( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + if let Some(sub) = args.arg_0() { + let start = args.arg_i(1); + let end = args.arg_i(2); + val.str_find(sub, start, end).into_raw() + } else { + panic!("find() takes at least 1 argument (0 given)"); + } + } else { + panic!("invalid self value in str_find"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_format( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + if let Some(val) = args.pop_arg_first() { + val.str_format(args, kwargs).into_raw() + } else { + panic!("invalid self value in str_format"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_index( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + if let Some(sub) = args.arg_0() { + let start = args.arg_i(1); + let end = args.arg_i(2); + val.str_index(sub, start, end).into_raw() + } else { + panic!("index() takes at least 1 argument (0 given)"); + } + } else { + panic!("invalid self value in str_index"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_isalnum( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + val.str_isalnum().into_raw() + } else { + panic!("invalid self value in str_isalnum"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_isalpha( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + val.str_isalpha().into_raw() + } else { + panic!("invalid self value in str_isalpha"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_isdigit( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + val.str_isdigit().into_raw() + } else { + panic!("invalid self value in str_isdigit"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_islower( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + val.str_islower().into_raw() + } else { + panic!("invalid self value in str_islower"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_isspace( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + val.str_isspace().into_raw() + } else { + panic!("invalid self value in str_isspace"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_istitle( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + val.str_istitle().into_raw() + } else { + panic!("invalid self value in str_istitle"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_isupper( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + val.str_isupper().into_raw() + } else { + panic!("invalid self value in str_isupper"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_join( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + let iter = args.arg_i(0).unwrap(); + val.str_join(iter).into_raw() + } else { + panic!("invalid self value in str_join"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_lstrip( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + let chars = args.arg_i(0); + val.str_lstrip(chars).into_raw() + } else { + panic!("invalid self value in str_lstrip"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_rstrip( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + let chars = args.arg_i(0); + val.str_rstrip(chars).into_raw() + } else { + panic!("invalid self value in str_rstrip"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_replace( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + let old = args.arg_i(0).unwrap(); + let new = args.arg_i(1).unwrap(); + let count = args.arg_i(2); + val.str_replace(old, new, count).into_raw() + } else { + panic!("invalid self value in str_replace"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_rfind( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + if let Some(sub) = args.arg_0() { + let start = args.arg_i(1); + let end = args.arg_i(2); + val.str_rfind(sub, start, end).into_raw() + } else { + panic!("rfind() takes at least 1 argument (0 given)"); + } + } else { + panic!("invalid self value in str_rfind"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_rindex( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + if let Some(sub) = args.arg_0() { + let start = args.arg_i(1); + let end = args.arg_i(2); + val.str_rindex(sub, start, end).into_raw() + } else { + panic!("rindex() takes at least 1 argument (0 given)"); + } + } else { + panic!("invalid self value in str_rindex"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_rsplit( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + if let Some(val) = args.pop_arg_first() { + let sep = if let Some(sep) = args.arg_i(0) { + Some(sep) + } else { + kwargs.kwarg("sep") + }; + let maxsplit = if let Some(maxsplit) = args.arg_i(1) { + Some(maxsplit) + } else { + kwargs.kwarg("maxsplit") + }; + val.str_rsplit(sep, maxsplit).into_raw() + } else { + panic!("invalid self value in str_rsplit"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_split( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + if let Some(val) = args.pop_arg_first() { + let sep = if let Some(sep) = args.arg_i(0) { + Some(sep) + } else { + kwargs.kwarg("sep") + }; + let maxsplit = if let Some(maxsplit) = args.arg_i(1) { + Some(maxsplit) + } else { + kwargs.kwarg("maxsplit") + }; + let x = val.str_split(sep, maxsplit); + x.into_raw() + } else { + panic!("invalid self value in str_split"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_splitlines( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + if let Some(val) = args.pop_arg_first() { + if let Some(keepends) = args.arg_i(0) { + val.str_splitlines(Some(keepends)).into_raw() + } else if let Some(keepends) = kwargs.kwarg("keepends") { + val.str_splitlines(Some(keepends)).into_raw() + } else { + val.str_splitlines(None).into_raw() + } + } else { + panic!("invalid self value in str_splitlines"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_startswith( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + if let Some(suffix) = args.arg_0() { + let start = args.arg_i(1); + let end = args.arg_i(2); + val.str_startswith(suffix, start, end).into_raw() + } else { + panic!("startswith() takes at least 1 argument (0 given)"); + } + } else { + panic!("invalid self value in str_startswith"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_strip( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + let chars = args.arg_i(0); + val.str_strip(chars).into_raw() + } else { + panic!("invalid self value in str_strip"); + } +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_builtin_str_title( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + if let Some(val) = args.pop_arg_first() { + val.str_title().into_raw() + } else { + panic!("invalid self value in str_title"); + } +} + +// ---------------------------------------------------------------------------- +// END +// ---------------------------------------------------------------------------- diff --git a/kclvm/runtime/src/value/iter.rs b/kclvm/runtime/src/value/iter.rs new file mode 100644 index 000000000..cbe61a3fa --- /dev/null +++ b/kclvm/runtime/src/value/iter.rs @@ -0,0 +1,240 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct ValueIterator { + pub len: usize, + pub cur_key: ValueRef, + pub cur_val: ValueRef, + pub end_val: *const ValueRef, + pub keys: Vec, + pub pos: i32, +} + +impl Default for ValueIterator { + fn default() -> Self { + Self { + len: 0, + cur_key: Default::default(), + cur_val: Default::default(), + end_val: std::ptr::null(), + keys: Vec::new(), + pos: 0, + } + } +} + +impl ValueIterator { + pub fn from_value(p: &ValueRef) -> Self { + if p.len() == 0 { + return Default::default(); + } + match *p.rc { + Value::str_value(ref s) => { + ValueIterator { + len: s.len(), + cur_key: Default::default(), + cur_val: Default::default(), + end_val: 1 as *const ValueRef, // just as bool flag + keys: Vec::new(), + pos: 0, + } + } + Value::list_value(ref list) => { + ValueIterator { + len: list.values.len(), + cur_key: Default::default(), + cur_val: Default::default(), + end_val: 1 as *const ValueRef, // just as bool flag + keys: Vec::new(), + pos: 0, + } + } + Value::dict_value(ref dict) => { + let keys: Vec = dict.values.keys().map(|s| (*s).clone()).collect(); + ValueIterator { + len: dict.values.len(), + cur_key: Default::default(), + cur_val: Default::default(), + end_val: 1 as *const ValueRef, // just as bool flag + keys, + pos: 0, + } + } + + Value::schema_value(ref schema) => { + let keys: Vec = schema.config.values.keys().map(|s| (*s).clone()).collect(); + ValueIterator { + len: schema.config.values.len(), + cur_key: Default::default(), + cur_val: Default::default(), + end_val: 1 as *const ValueRef, // just as bool flag + keys, + pos: 0, + } + } + + _ => Default::default(), + } + } + + pub fn is_end(&self) -> bool { + self.pos >= self.len as i32 + } + + pub fn key(&self) -> Option<&ValueRef> { + if self.pos == 0 || self.pos > self.len as i32 { + return Option::None; + } + if !self.end_val.is_null() { + Some(&self.cur_key) + } else { + Option::None + } + } + + pub fn value<'a>(&'a mut self) -> Option<&'a ValueRef> { + if self.pos == 0 { + return Option::None; + } + if !self.end_val.is_null() { + Some(&self.cur_val) + } else { + Option::None + } + } + + pub fn next<'a>(&'a mut self, host: &'a ValueRef) -> Option<&'a ValueRef> { + if host.len() == 0 { + return None; + } + if self.pos >= host.len() as i32 { + self.end_val = std::ptr::null(); + return None; + } + match *host.rc { + Value::str_value(ref s) => { + let ch = s.chars().nth(self.pos as usize).unwrap(); + self.cur_key = ValueRef::int(self.pos as i64); + self.cur_val = ValueRef::str(&ch.to_string()); + self.end_val = &self.cur_val; + self.pos += 1; + Some(&self.cur_val) + } + Value::list_value(ref list) => { + self.cur_key = ValueRef::int(self.pos as i64); + self.cur_val = list.values[self.pos as usize].clone(); + self.end_val = &self.cur_val; + self.pos += 1; + Some(&self.cur_val) + } + Value::dict_value(ref dict) => { + let key = &self.keys[self.pos as usize]; + self.cur_key = ValueRef::str(key); + self.cur_val = dict.values[key].clone(); + self.end_val = &self.cur_val; + self.pos += 1; + Some(&self.cur_key) + } + Value::schema_value(ref schema) => { + let key = &self.keys[self.pos as usize]; + self.cur_key = ValueRef::str(key); + self.cur_val = schema.config.values[key].clone(); + self.end_val = &self.cur_val; + self.pos += 1; + Some(&self.cur_key) + } + _ => panic!("{} object is not iterable", host.type_str()), + } + } +} + +impl ValueRef { + #[inline] + pub fn iter(&self) -> ValueIterator { + ValueIterator::from_value(self) + } +} + +#[cfg(test)] +mod test_value_iter { + use crate::*; + + #[test] + fn test_str_iter() { + let s = ValueRef::str("abc"); + + let mut it = s.iter(); + assert_eq!(it.is_end(), false); + + let _ = it.next(&s); + assert_eq!(it.key().unwrap().as_int(), 0); + assert_eq!(it.value().unwrap().as_str(), "a"); + + let _ = it.next(&s); + assert_eq!(it.is_end(), false); + assert_eq!(it.key().unwrap().as_int(), 1); + assert_eq!(it.value().unwrap().as_str(), "b"); + + let v = it.next(&s); + assert_eq!(v.unwrap().as_str(), "c"); + assert_eq!(it.key().unwrap().as_int(), 2); + assert_eq!(it.value().unwrap().as_str(), "c"); + assert_eq!(it.is_end(), true); + + let _ = it.next(&s); + assert_eq!(it.is_end(), true); + } + + #[test] + fn test_list_iter() { + let value = ValueRef::list_int(&[1, 2, 3]); + + let mut it = value.iter(); + assert_eq!(it.is_end(), false); + + let _ = it.next(&value); + assert_eq!(it.key().unwrap().as_int(), 0); + assert_eq!(it.value().unwrap().as_int(), 1); + + let _ = it.next(&value); + assert_eq!(it.is_end(), false); + assert_eq!(it.key().unwrap().as_int(), 1); + assert_eq!(it.value().unwrap().as_int(), 2); + + let _ = it.next(&value); + assert_eq!(it.key().unwrap().as_int(), 2); + assert_eq!(it.value().unwrap().as_int(), 3); + assert_eq!(it.is_end(), true); + + let _ = it.next(&value); + assert_eq!(it.is_end(), true); + } + + #[test] + fn test_dict_iter() { + let value = ValueRef::dict_int(&[("a", 1), ("b", 2), ("c", 3)]); + + let mut it = value.iter(); + assert_eq!(it.is_end(), false); + + let _ = it.next(&value); + assert_eq!(it.key().unwrap().as_str(), "a"); + assert_eq!(it.value().unwrap().as_int(), 1); + + let _ = it.next(&value); + assert_eq!(it.is_end(), false); + assert_eq!(it.key().unwrap().as_str(), "b"); + assert_eq!(it.value().unwrap().as_int(), 2); + + let _ = it.next(&value); + assert_eq!(it.key().unwrap().as_str(), "c"); + assert_eq!(it.value().unwrap().as_int(), 3); + assert_eq!(it.is_end(), true); + + let _ = it.next(&value); + assert_eq!(it.is_end(), true); + } +} diff --git a/kclvm/runtime/src/value/mod.rs b/kclvm/runtime/src/value/mod.rs new file mode 100644 index 000000000..3ff12728d --- /dev/null +++ b/kclvm/runtime/src/value/mod.rs @@ -0,0 +1,88 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod val_panic; +pub use val_panic::*; + +pub mod val_overflow; +pub use val_overflow::*; + +pub mod api; +pub use api::*; + +pub mod iter; +pub use iter::*; + +pub mod val; +pub use val::*; + +pub mod val_len; +pub use val_len::*; + +pub mod val_args; +pub use val_args::*; + +pub mod val_logic; +pub use val_logic::*; + +pub mod val_as_val; +pub use val_as_val::*; + +pub mod val_kind; +pub use val_kind::*; + +pub mod val_clone; +pub use val_clone::*; + +pub mod val_cmp; +pub use val_cmp::*; + +pub mod val_decorator; +pub use val_decorator::*; + +pub mod val_is_in; +pub use val_is_in::*; + +pub mod val_list; +pub use val_list::*; + +pub mod val_dict; +pub use val_dict::*; + +pub mod val_fmt; +pub use val_fmt::*; + +pub mod val_from; +pub use val_from::*; + +pub mod val_get_set; +pub use val_get_set::*; + +pub mod val_schema; +pub use val_schema::*; + +pub mod val_json; +pub use val_json::*; + +pub mod val_bin_aug; +pub use val_bin_aug::*; + +pub mod val_unary; +pub use val_unary::*; + +pub mod val_bin; +pub use val_bin::*; + +pub mod val_plan; +pub use val_plan::*; + +pub mod val_str; +pub use val_str::*; + +pub mod val_type; +pub use val_type::*; + +pub mod val_union; +pub use val_union::*; + +pub mod val_yaml; +pub use val_yaml::*; diff --git a/kclvm/runtime/src/value/val.rs b/kclvm/runtime/src/value/val.rs new file mode 100644 index 000000000..215466f50 --- /dev/null +++ b/kclvm/runtime/src/value/val.rs @@ -0,0 +1,84 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl ValueRef { + pub fn undefined() -> Self { + Self::from(UNDEFINED) + } + + pub fn none() -> Self { + Self::from(NONE) + } + + pub fn bool(v: bool) -> Self { + Self::from(if v { TRUE } else { FALSE }) + } + + pub fn int(v: i64) -> Self { + Self::from(Value::int_value(v)) + } + + pub fn float(v: f64) -> Self { + Self::from(Value::float_value(v)) + } + + pub fn unit(v: f64, raw: i64, unit: &str) -> Self { + Self::from(Value::unit_value(v, raw, unit.to_string())) + } + + pub fn str(v: &str) -> Self { + Self::from(Value::str_value(v.to_string())) + } + + pub fn list(values: Option<&[&Self]>) -> Self { + let mut list: ListValue = Default::default(); + if let Some(values) = values { + for x in values { + list.values.push((**x).clone()); + } + } + Self::from(Value::list_value(list)) + } + + pub fn list_value(values: Option<&[Self]>) -> Self { + let mut list: ListValue = Default::default(); + if let Some(values) = values { + for x in values { + list.values.push((*x).clone()); + } + } + Self::from(Value::list_value(list)) + } + + pub fn dict(values: Option<&[(&str, &Self)]>) -> Self { + let mut dict: DictValue = Default::default(); + if let Some(values) = values { + for x in values { + dict.values.insert(x.0.to_string(), (*x.1).clone()); + } + } + Self::from(Value::dict_value(dict)) + } + + pub fn schema() -> Self { + let s: SchemaValue = Default::default(); + Self::from(Value::schema_value(s)) + } + + pub fn func( + fn_ptr: u64, + check_fn_ptr: u64, + closure: ValueRef, + name: &str, + runtime_type: &str, + ) -> Self { + Self::from(Value::func_value(FuncValue { + fn_ptr, + check_fn_ptr, + closure, + external_name: name.to_string(), + runtime_type: runtime_type.to_string(), + })) + } +} diff --git a/kclvm/runtime/src/value/val_args.rs b/kclvm/runtime/src/value/val_args.rs new file mode 100644 index 000000000..905e309e7 --- /dev/null +++ b/kclvm/runtime/src/value/val_args.rs @@ -0,0 +1,252 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +/// Adjust ValueRef when the value is used with Rust option. +/// If the value is KCL None or Undefined, mapping it to Rust None, +/// else mapping it to Some(value). +#[inline] +pub fn adjust_parameter(value: Option<&ValueRef>) -> Option<&ValueRef> { + value.and_then(|v| { + if v.is_none_or_undefined() { + None + } else { + Some(v) + } + }) +} + +impl ValueRef { + pub fn arg_0(&self) -> Option<&Self> { + self.arg_i(0) + } + + pub fn arg_last(&self) -> Option<&Self> { + match *self.rc { + Value::list_value(ref list) => Some(&list.values[list.values.len() - 1]), + _ => None, + } + } + + pub fn pop_arg_last(&self) -> Option { + match *self.rc { + Value::list_value(ref list) => { + let list = get_ref_mut(list); + list.values.pop() + } + _ => None, + } + } + + pub fn pop_arg_first(&self) -> Option { + match *self.rc { + Value::list_value(ref list) => { + let list = get_ref_mut(list); + if !list.values.is_empty() { + Some(list.values.remove(0)) + } else { + None + } + } + _ => None, + } + } + + pub fn args_len(&self) -> usize { + match *self.rc { + Value::list_value(ref list) => list.values.len(), + _ => 1, + } + } + + pub fn arg_i(&self, i: usize) -> Option<&Self> { + match *self.rc { + Value::list_value(ref list) => { + if i < list.values.len() { + return Some(&list.values[i]); + } + None + } + _ => None, + } + } + + pub fn arg_i_bool(&self, i: usize, default: Option) -> Option { + if let Some(x) = self.arg_i(i) { + match *x.rc { + Value::bool_value(v) => return Some(v), + Value::none => return default, + _ => return None, + } + } + default + } + + pub fn arg_i_int(&self, i: usize, default: Option) -> Option { + if let Some(x) = self.arg_i(i) { + match *x.rc { + Value::int_value(v) => return Some(v), + Value::none => return default, + _ => return None, + } + } + default + } + + pub fn arg_i_float(&self, i: usize, default: Option) -> Option { + if let Some(x) = self.arg_i(i) { + match *x.rc { + Value::float_value(v) => return Some(v), + Value::none => return default, + _ => return None, + } + } + default + } + + pub fn arg_i_num(&self, i: usize, default: Option) -> Option { + if let Some(x) = self.arg_i(i) { + match *x.rc { + Value::float_value(v) => return Some(v), + Value::int_value(v) => return Some(v as f64), + Value::none => return default, + _ => return None, + } + } + default + } + + pub fn arg_i_str(&self, i: usize, default: Option) -> Option { + if let Some(x) = self.arg_i(i) { + match &*x.rc { + Value::str_value(s) => return Some(s.to_string()), + Value::none => return default, + _ => return None, + } + } + default + } + + pub fn arg_i_list(&self, i: usize) -> Option<&Self> { + if let Some(x) = self.arg_i(i) { + match *x.rc { + Value::list_value(_) => return Some(x), + _ => return None, + } + } + None + } + + pub fn arg_i_dict(&self, i: usize) -> Option<&Self> { + if let Some(x) = self.arg_i(i) { + match *x.rc { + Value::dict_value(_) => return Some(x), + _ => return None, + } + } + None + } + + pub fn kwarg(&self, name: &str) -> Option<&Self> { + match *self.rc { + Value::dict_value(ref dict) => dict.values.get(&name.to_string()), + _ => None, + } + } + + pub fn kwarg_bool(&self, name: &str, default: Option) -> Option { + if let Some(x) = self.kwarg(name) { + match *x.rc { + Value::bool_value(v) => return Some(v), + Value::none => return default, + _ => return None, + } + } + default + } + + pub fn kwarg_int(&self, name: &str, default: Option) -> Option { + if let Some(x) = self.kwarg(name) { + match *x.rc { + Value::int_value(v) => return Some(v), + Value::none => return default, + _ => return None, + } + } + default + } + + pub fn kwarg_float(&self, name: &str, default: Option) -> Option { + if let Some(x) = self.kwarg(name) { + match *x.rc { + Value::float_value(v) => return Some(v), + Value::none => return default, + _ => return None, + } + } + default + } + + pub fn kwarg_str(&self, name: &str, default: Option) -> Option { + if let Some(x) = self.kwarg(name) { + match &*x.rc { + Value::str_value(s) => return Some(s.to_string()), + Value::none => return default, + _ => return None, + } + } + default + } + + pub fn kwarg_list(&self, name: &str) -> Option<&Self> { + if let Some(x) = self.kwarg(name) { + match *x.rc { + Value::list_value(_) => return Some(x), + _ => return None, + } + } + None + } + + pub fn kwarg_dict(&self, name: &str) -> Option<&Self> { + if let Some(x) = self.kwarg(name) { + match *x.rc { + Value::dict_value(_) => return Some(x), + _ => return None, + } + } + None + } +} + +#[cfg(test)] +mod test_value_args { + use crate::*; + + #[test] + fn test_value_args() { + let args = ValueRef::list(Some(&[ + &ValueRef::int(0), + &ValueRef::float(1.0), + &ValueRef::str("ss"), + ])); + let arg_first = args.pop_arg_first().unwrap(); + let arg_last = args.pop_arg_last().unwrap(); + assert_eq!(arg_first, ValueRef::int(0)); + assert_eq!(arg_last, ValueRef::str("ss")); + assert_eq!(args.arg_0().unwrap().clone(), ValueRef::float(1.0)); + assert_eq!(args.arg_i_float(0, Some(2.0)).unwrap(), 1.0); + } + + #[test] + fn test_value_kwargs() { + let mut kwargs = ValueRef::dict(None); + kwargs.dict_update_key_value("key1", ValueRef::int(1)); + kwargs.dict_update_key_value("key2", ValueRef::str("2")); + assert_eq!(kwargs.kwarg_int("key1", Some(2)).unwrap(), 1); + assert_eq!( + kwargs.kwarg_str("key2", Some("ss".to_string())).unwrap(), + "2" + ); + } +} diff --git a/kclvm/runtime/src/value/val_as_val.rs b/kclvm/runtime/src/value/val_as_val.rs new file mode 100644 index 000000000..e870cd720 --- /dev/null +++ b/kclvm/runtime/src/value/val_as_val.rs @@ -0,0 +1,171 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl ValueRef { + #[inline] + pub fn as_bool(&self) -> bool { + self.is_truthy() + } + + #[inline] + pub fn as_int(&self) -> i64 { + match *self.rc { + Value::int_value(ref v) => *v, + Value::float_value(ref v) => *v as i64, + Value::unit_value(ref v, _, _) => *v as i64, + _ => 0, + } + } + + #[inline] + pub fn as_float(&self) -> f64 { + match *self.rc { + Value::int_value(ref v) => *v as f64, + Value::float_value(ref v) => *v, + Value::unit_value(ref v, _, _) => *v, + _ => 0.0, + } + } + + #[inline] + pub fn as_str(&self) -> String { + match *self.rc { + Value::str_value(ref v) => v.clone(), + _ => "".to_string(), + } + } + + #[inline] + pub fn as_list_ref(&self) -> &ListValue { + match *self.rc { + Value::list_value(ref v) => v, + _ => panic!("invalid list value"), + } + } + + #[inline] + pub fn as_list_mut_ref(&mut self) -> &mut ListValue { + match *self.rc { + Value::list_value(ref v) => get_ref_mut(v), + _ => panic!("invalid list value"), + } + } + + #[inline] + pub fn as_dict_ref(&self) -> &DictValue { + match *self.rc { + Value::dict_value(ref v) => v, + Value::schema_value(ref v) => v.config.as_ref(), + _ => panic!("invalid dict value"), + } + } + + #[inline] + pub fn as_dict_mut_ref(&mut self) -> &mut DictValue { + match *self.rc { + Value::dict_value(ref v) => get_ref_mut(v), + Value::schema_value(ref v) => get_ref_mut(v.config.as_ref()), + _ => panic!("invalid dict value"), + } + } + + #[inline] + pub fn as_schema(&self) -> &SchemaValue { + match *self.rc { + Value::schema_value(ref v) => v, + _ => panic!("invalid schema value"), + } + } + + #[inline] + pub fn as_function(&self) -> &FuncValue { + match *self.rc { + Value::func_value(ref v) => v, + _ => panic!("invalid function value"), + } + } + + #[inline] + pub fn as_unit(&self) -> (f64, i64, String) { + match &*self.rc { + Value::unit_value(v, raw, unit) => (*v, *raw, unit.clone()), + _ => panic!("invalid unit value"), + } + } +} + +#[cfg(test)] +mod test_value_as { + use crate::*; + + #[test] + fn test_as_bool() { + let cases = [ + (ValueRef::undefined(), false), + (ValueRef::none(), false), + (ValueRef::bool(false), false), + (ValueRef::bool(true), true), + (ValueRef::int(0), false), + (ValueRef::int(1), true), + (ValueRef::int(-1), true), + (ValueRef::int(2), true), + (ValueRef::int(123), true), + (ValueRef::float(0.0), false), + (ValueRef::float(0.1), true), + (ValueRef::float(1234.5), true), + (ValueRef::str(""), false), + (ValueRef::str("false"), true), + (ValueRef::str("1"), true), + (ValueRef::list_int(&[0]), true), + (ValueRef::list(None), false), + (ValueRef::dict_int(&[("k", 0)]), true), + (ValueRef::dict(None), false), + (ValueRef::schema(), false), + ]; + for (value, expected) in cases { + assert_eq!(value.as_bool(), expected); + } + } + + #[test] + fn test_as_int() { + let cases = [ + (ValueRef::int(0), 0), + (ValueRef::int(1), 1), + (ValueRef::int(-1), -1), + (ValueRef::int(256), 256), + (ValueRef::float(0.0), 0), + (ValueRef::float(0.1), 0), + (ValueRef::float(1234.5), 1234), + (ValueRef::unit(1024.0, 1, "Ki"), 1024), + ]; + for (value, expected) in cases { + assert_eq!(value.as_int(), expected); + } + } + + #[test] + fn test_as_float() { + let cases = [ + (ValueRef::int(0), 0.0), + (ValueRef::float(256.0), 256.0), + (ValueRef::unit(1024.0, 1, "Ki"), 1024.0), + ]; + for (value, expected) in cases { + assert_eq!(value.as_float(), expected); + } + } + + #[test] + fn test_as_str() { + let cases = [ + (ValueRef::int(0), ""), + (ValueRef::float(1234.5), ""), + (ValueRef::str("ss"), "ss"), + ]; + for (value, expected) in cases { + assert_eq!(value.as_str(), expected); + } + } +} diff --git a/kclvm/runtime/src/value/val_bin.rs b/kclvm/runtime/src/value/val_bin.rs new file mode 100644 index 000000000..bb256130f --- /dev/null +++ b/kclvm/runtime/src/value/val_bin.rs @@ -0,0 +1,475 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl ValueRef { + pub fn bin_add(&self, x: &Self) -> Self { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_32 = ctx.cfg.strict_range_check; + let strict_range_check_64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_i32_overflow_add(*a, *b) { + panic_i32_overflow!(*a as i128 + *b as i128); + } + } + if strict_range_check_64 { + if is_i64_overflow_add(*a, *b) { + panic_i64_overflow!(*a as i128 + *b as i128); + } + } + + Self::int(*a + *b) + } + (Value::float_value(a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_add(*a, *b) { + panic_f32_overflow!(*a + *b); + } + } + Self::float(*a + *b) + } + (Value::int_value(a), Value::float_value(b)) => { + if is_f32_overflow_add(*a as f64, *b) { + panic_f32_overflow!(*a as f64 + *b); + } + Self::float(*a as f64 + *b) + } + (Value::float_value(a), Value::int_value(b)) => { + if is_f32_overflow_add(*a, *b as f64) { + panic_f32_overflow!(*a + *b as f64); + } + Self::float(*a + *b as f64) + } + + (Value::str_value(a), Value::str_value(b)) => { + Self::str(format!("{}{}", *a, *b).as_ref()) + } + (Value::list_value(a), _) => { + if x.is_list() { + let mut list = a.clone(); + let b = x.as_list_ref(); + for x in b.values.iter() { + list.values.push(x.clone()); + } + Self::from(Value::list_value(list)) + } else { + let msg = format!( + "can only concatenate list (not \"{}\") to list", + x.type_str() + ); + panic!("{}", msg); + } + } + _ => panic_unsupported_bin_op!("+", self.type_str(), x.type_str()), + } + } + + pub fn bin_sub(&self, x: &Self) -> Self { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_32 = ctx.cfg.strict_range_check; + let strict_range_check_64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_i32_overflow_sub(*a, *b) { + panic_i32_overflow!(*a as i128 - *b as i128); + } + } + if strict_range_check_64 { + if is_i64_overflow_sub(*a, *b) { + panic_i32_overflow!(*a as i128 - *b as i128); + } + } + Self::int(*a - *b) + } + (Value::float_value(a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_sub(*a, *b) { + panic_f32_overflow!(*a - *b); + } + } + Self::float(*a - *b) + } + (Value::int_value(a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_sub(*a as f64, *b) { + panic_f32_overflow!(*a as f64 - *b); + } + } + Self::float(*a as f64 - *b) + } + (Value::float_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_sub(*a, *b as f64) { + panic_f32_overflow!(*a - *b as f64); + } + } + Self::float(*a - *b as f64) + } + _ => panic_unsupported_bin_op!("-", self.type_str(), x.type_str()), + } + } + + pub fn bin_mul(&self, x: &Self) -> Self { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_32 = ctx.cfg.strict_range_check; + let strict_range_check_64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_i32_overflow_mul(*a, *b) { + panic_i32_overflow!(*a as i128 * *b as i128); + } + } + if strict_range_check_64 { + if is_i64_overflow_mul(*a, *b) { + panic_i64_overflow!(*a as i128 * *b as i128); + } + } + Self::int(*a * *b) + } + (Value::float_value(a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_mul(*a, *b) { + panic_f32_overflow!(*a * *b); + } + } + Self::float(*a * *b) + } + (Value::int_value(a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_mul(*a as f64, *b) { + panic_f32_overflow!(*a as f64 * *b); + } + } + Self::float(*a as f64 * *b) + } + (Value::float_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_mul(*a, *b as f64) { + panic_f32_overflow!(*a * *b as f64); + } + } + Self::float(*a * *b as f64) + } + + (Value::str_value(a), Value::int_value(b)) => Self::str(a.repeat(*b as usize).as_ref()), + (Value::int_value(b), Value::str_value(a)) => Self::str(a.repeat(*b as usize).as_ref()), + (Value::list_value(a), Value::int_value(b)) => { + let mut list = ListValue::default(); + for _ in 0..(*b as usize) { + for x in a.values.iter() { + list.values.push(x.clone()); + } + } + Self::from(Value::list_value(list)) + } + (Value::int_value(b), Value::list_value(a)) => { + let mut list = ListValue::default(); + for _ in 0..(*b as usize) { + for x in a.values.iter() { + list.values.push(x.clone()); + } + } + Self::from(Value::list_value(list)) + } + _ => panic_unsupported_bin_op!("*", self.type_str(), x.type_str()), + } + } + + pub fn bin_div(&self, x: &Self) -> Self { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => Self::float((*a as f64) / (*b as f64)), + (Value::float_value(a), Value::float_value(b)) => Self::float(*a / *b), + (Value::int_value(a), Value::float_value(b)) => Self::float(*a as f64 / *b), + (Value::float_value(a), Value::int_value(b)) => Self::float(*a / *b as f64), + _ => panic_unsupported_bin_op!("/", self.type_str(), x.type_str()), + } + } + + pub fn bin_mod(&self, x: &Self) -> Self { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + let x = *a; + let y = *b; + if -13 / 5 == -2 && (x < 0) != (y < 0) && x % y != 0 { + Self::int(x % y + y) + } else { + Self::int(x % y) + } + } + (Value::float_value(a), Value::float_value(b)) => Self::float(*a % *b), + (Value::int_value(a), Value::float_value(b)) => Self::float(*a as f64 % *b), + (Value::float_value(a), Value::int_value(b)) => Self::float(*a % *b as f64), + _ => panic_unsupported_bin_op!("%", self.type_str(), x.type_str()), + } + } + + pub fn bin_pow(&self, x: &Self) -> Self { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_32 = ctx.cfg.strict_range_check; + let strict_range_check_64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match (&*self.rc, &*x.rc) { + (Value::int_value(ref a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_i32_overflow_pow(*a, *b) { + panic_i32_overflow!((*a as i128).pow(*b as u32)); + } + } + if strict_range_check_64 { + if is_i64_overflow_pow(*a, *b) { + panic_i64_overflow!((*a as i128).pow(*b as u32)); + } + } + Self::int(a.pow(*b as u32)) + } + (Value::float_value(a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_pow(*a, *b) { + panic_f32_overflow!(a.powf(*b)); + } + } + Self::float(a.powf(*b)) + } + (Value::int_value(a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_pow(*a as f64, *b) { + panic_f32_overflow!((*a as f64).powf(*b)); + } + } + Self::float((*a as f64).powf(*b)) + } + (Value::float_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_pow(*a, *b as f64) { + panic_f32_overflow!(a.powf(*b as f64)); + } + } + Self::float(a.powf(*b as f64)) + } + _ => panic_unsupported_bin_op!("**", self.type_str(), x.type_str()), + } + } + + pub fn bin_floor_div(&self, x: &Self) -> Self { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + let x = *a; + let y = *b; + if -13 / 5 == -2 && (x < 0) != (y < 0) && x % y != 0 { + Self::int(x / y - 1) + } else { + Self::int(x / y) + } + } + (Value::float_value(a), Value::float_value(b)) => Self::float((*a / *b).floor()), + (Value::int_value(a), Value::float_value(b)) => Self::float((*a as f64 / *b).floor()), + (Value::float_value(a), Value::int_value(b)) => Self::float((*a / *b as f64).floor()), + _ => panic_unsupported_bin_op!("//", self.type_str(), x.type_str()), + } + } + + pub fn bin_bit_lshift(&self, x: &Self) -> Self { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_32 = ctx.cfg.strict_range_check; + let strict_range_check_64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_i32_overflow_shl(*a, *b) { + panic_i32_overflow!((*a as i128) << (*b as u32)); + } + } + if strict_range_check_64 { + if is_i64_overflow_shl(*a, *b) { + panic_i64_overflow!((*a as i128) << (*b as u32)); + } + } + Self::int(*a << *b) + } + _ => panic_unsupported_bin_op!("<<", self.type_str(), x.type_str()), + } + } + + pub fn bin_bit_rshift(&self, x: &Self) -> Self { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_32 = ctx.cfg.strict_range_check; + let strict_range_check_64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_i32_overflow_shr(*a, *b) { + panic_i32_overflow!((*a as i128) >> (*b as u32)); + } + } + if strict_range_check_64 { + if is_i64_overflow_shr(*a, *b) { + panic_i64_overflow!((*a as i128) >> (*b as u32)); + } + } + Self::int(*a >> *b) + } + _ => panic_unsupported_bin_op!(">>", self.type_str(), x.type_str()), + } + } + + pub fn bin_bit_and(&self, x: &Self) -> Self { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => Self::int(*a & *b), + _ => panic_unsupported_bin_op!("&", self.type_str(), x.type_str()), + } + } + + pub fn bin_bit_xor(&self, x: &Self) -> Self { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => Self::int(*a ^ *b), + _ => panic_unsupported_bin_op!("^", self.type_str(), x.type_str()), + } + } + + pub fn bin_bit_or(&self, x: &Self) -> Self { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => Self::int(*a | *b), + _ => self.deep_copy().union(x, true, false, true, true), + } + } + + pub fn bin_subscr(&self, x: &Self) -> Self { + match (&*self.rc, &*x.rc) { + (Value::str_value(a), Value::int_value(b)) => { + let str_len = a.chars().count(); + let index = *b; + let index = if index < 0 { + (index + str_len as i64) as usize + } else { + index as usize + }; + if index < a.len() { + let ch = a.chars().nth(index).unwrap(); + Self::str(ch.to_string().as_ref()) + } else { + panic!("string index out of range"); + } + } + (Value::list_value(a), Value::int_value(b)) => { + let index = *b; + let index = if index < 0 { + (index + a.values.len() as i64) as usize + } else { + index as usize + }; + if index < a.values.len() { + a.values[index].clone() + } else { + panic!("list index out of range"); + } + } + (Value::dict_value(a), Value::str_value(b)) => match a.values.get(b) { + Some(x) => (*x).clone(), + _ => Self::undefined(), + }, + (Value::schema_value(a), Value::str_value(b)) => match a.config.values.get(b) { + Some(x) => (*x).clone(), + _ => Self::undefined(), + }, + _ => Self::undefined(), + } + } + + pub fn bin_subscr_option(&self, x: &Self) -> Self { + if self.is_truthy() { + self.bin_subscr(x) + } else { + Self::none() + } + } +} + +#[cfg(test)] +mod test_value_bin { + + use crate::*; + + #[test] + fn test_int_bin() { + let cases = [ + (0, 0, "+", 0), + (1, 1, "-", 0), + (-1, 2, "*", -2), + (4, 2, "/", 2), + (-2, 4, "//", -1), + (-2, 5, "%", 3), + (3, 2, "**", 9), + (2, 1, ">>", 1), + (3, 2, "<<", 12), + (5, 9, "&", 1), + (5, 10, "|", 15), + (7, 11, "^", 12), + ]; + for (left, right, op, expected) in cases { + let left = ValueRef::int(left); + let right = ValueRef::int(right); + let result = match op { + "+" => left.bin_add(&right), + "-" => left.bin_sub(&right), + "*" => left.bin_mul(&right), + "/" => left.bin_div(&right), + "//" => left.bin_floor_div(&right), + "%" => left.bin_mod(&right), + "**" => left.bin_pow(&right), + "<<" => left.bin_bit_lshift(&right), + ">>" => left.bin_bit_rshift(&right), + "&" => left.bin_bit_and(&right), + "|" => left.bin_bit_or(&right), + "^" => left.bin_bit_xor(&right), + _ => panic!("invalid op {}", op), + }; + assert_eq!(result.as_int(), expected as i64) + } + } + + #[test] + fn test_str_subscr() { + let data = ValueRef::str("Hello world"); + let cases = [(0, "H"), (1, "e"), (-1, "d"), (-2, "l")]; + for (index, expected) in cases { + let index = ValueRef::int(index as i64); + let result = data.bin_subscr(&index).as_str(); + assert_eq!(result, expected); + } + } + + #[test] + fn test_list_subscr() { + let data = ValueRef::list_int(&[1, 2, 3, 4]); + let cases = [(0, 1), (1, 2), (-1, 4), (-2, 3)]; + for (index, expected) in cases { + let index = ValueRef::int(index as i64); + let result = data.bin_subscr(&index).as_int(); + assert_eq!(result, expected); + } + } + + #[test] + fn test_dict_subscr() { + let data = ValueRef::dict_int(&[("k1", 1), ("k2", 2)]); + assert_eq!( + data.bin_subscr(&ValueRef::str("err_key")), + ValueRef::undefined() + ); + let cases = [("k1", 1), ("k2", 2)]; + for (key, expected) in cases { + let key = ValueRef::str(key); + let result = data.bin_subscr(&key).as_int(); + assert_eq!(result, expected); + } + } +} diff --git a/kclvm/runtime/src/value/val_bin_aug.rs b/kclvm/runtime/src/value/val_bin_aug.rs new file mode 100644 index 000000000..b3682ff6e --- /dev/null +++ b/kclvm/runtime/src/value/val_bin_aug.rs @@ -0,0 +1,522 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl ValueRef { + pub fn bin_aug_add(&mut self, x: &Self) -> &mut Self { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_32 = ctx.cfg.strict_range_check; + let strict_range_check_64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_i32_overflow_add(*a, *b) { + panic_i32_overflow!(*a as i128 + *b as i128); + } + } + if strict_range_check_64 { + if is_i64_overflow_add(*a, *b) { + panic_i64_overflow!(*a as i128 + *b as i128); + } + } + let a: &mut i64 = get_ref_mut(a); + *a += *b; + self + } + (Value::float_value(a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_add(*a, *b) { + panic_f32_overflow!(*a + *b); + } + } + let a: &mut f64 = get_ref_mut(a); + *a += *b; + self + } + (Value::int_value(a), Value::float_value(b)) => { + if is_f32_overflow_add(*a as f64, *b) { + panic_f32_overflow!(*a as f64 + *b); + } + let a: &mut i64 = get_ref_mut(a); + *a += *b as i64; + self + } + (Value::float_value(a), Value::int_value(b)) => { + if is_f32_overflow_add(*a, *b as f64) { + panic_f32_overflow!(*a + *b as f64); + } + let a: &mut f64 = get_ref_mut(a); + *a += *b as f64; + self + } + (Value::str_value(a), Value::str_value(b)) => { + let a: &mut String = get_ref_mut(a); + *a = format!("{}{}", *a, *b); + self + } + (Value::list_value(a), _) => match &*x.rc { + Value::list_value(ref b) => { + let list: &mut ListValue = get_ref_mut(a); + for x in b.values.iter() { + list.values.push(x.clone()); + } + self + } + _ => panic_unsupported_bin_op!("+", self.type_str(), x.type_str()), + }, + _ => panic_unsupported_bin_op!("+", self.type_str(), x.type_str()), + } + } + + pub fn bin_aug_sub(&mut self, x: &Self) -> &mut Self { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_32 = ctx.cfg.strict_range_check; + let strict_range_check_64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_i32_overflow_sub(*a, *b) { + panic_i32_overflow!(*a as i128 - *b as i128); + } + } + if strict_range_check_64 { + if is_i64_overflow_sub(*a, *b) { + panic_i32_overflow!(*a as i128 - *b as i128); + } + } + let a: &mut i64 = get_ref_mut(a); + *a -= *b; + self + } + (Value::float_value(a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_sub(*a, *b) { + panic_f32_overflow!(*a - *b); + } + } + let a: &mut f64 = get_ref_mut(a); + *a -= *b; + self + } + (Value::int_value(a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_sub(*a as f64, *b) { + panic_f32_overflow!(*a as f64 - *b); + } + } + let a: &mut i64 = get_ref_mut(a); + *a -= *b as i64; + self + } + (Value::float_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_sub(*a, *b as f64) { + panic_f32_overflow!(*a - *b as f64); + } + } + let a: &mut f64 = get_ref_mut(a); + *a -= *b as f64; + self + } + _ => panic_unsupported_bin_op!("-", self.type_str(), x.type_str()), + } + } + + pub fn bin_aug_mul(&mut self, x: &Self) -> &mut Self { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_32 = ctx.cfg.strict_range_check; + let strict_range_check_64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_i32_overflow_mul(*a, *b) { + panic_i32_overflow!(*a as i128 * *b as i128); + } + } + if strict_range_check_64 { + if is_i64_overflow_mul(*a, *b) { + panic_i64_overflow!(*a as i128 * *b as i128); + } + } + let a: &mut i64 = get_ref_mut(a); + *a *= *b; + self + } + (Value::float_value(a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_mul(*a, *b) { + panic_f32_overflow!(*a * *b); + } + } + let a: &mut f64 = get_ref_mut(a); + *a *= *b; + self + } + (Value::int_value(a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_mul(*a as f64, *b) { + panic_f32_overflow!(*a as f64 * *b); + } + } + let a: &mut i64 = get_ref_mut(a); + *a *= *b as i64; + self + } + (Value::float_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_mul(*a, *b as f64) { + panic_f32_overflow!(*a * *b as f64); + } + } + let a: &mut f64 = get_ref_mut(a); + *a *= *b as f64; + self + } + (Value::str_value(a), Value::int_value(b)) => { + let a: &mut String = get_ref_mut(a); + *a = a.repeat(*b as usize); + self + } + (Value::list_value(ref list), _) => match &*x.rc { + Value::int_value(ref b) => { + let list: &mut ListValue = get_ref_mut(list); + let n = list.values.len(); + for _ in 1..(*b as usize) { + for i in 0..n { + list.values.push(list.values[i].clone()); + } + } + self + } + _ => panic_unsupported_bin_op!("*", self.type_str(), x.type_str()), + }, + _ => panic_unsupported_bin_op!("*", self.type_str(), x.type_str()), + } + } + + pub fn bin_aug_div(&mut self, x: &Self) -> &mut Self { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + let a: &mut i64 = get_ref_mut(a); + *a /= *b; + self + } + (Value::int_value(a), Value::float_value(b)) => { + let a: &mut i64 = get_ref_mut(a); + *a /= *b as i64; + self + } + (Value::float_value(a), Value::int_value(b)) => { + let a: &mut f64 = get_ref_mut(a); + *a /= *b as f64; + self + } + (Value::float_value(a), Value::float_value(b)) => { + let a: &mut f64 = get_ref_mut(a); + *a /= *b; + self + } + _ => panic_unsupported_bin_op!("/", self.type_str(), x.type_str()), + } + } + + pub fn bin_aug_mod(&mut self, x: &Self) -> &mut Self { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + let a: &mut i64 = get_ref_mut(a); + let x = *a; + let y = *b; + if -13 / 5 == -2 && (x < 0) != (y < 0) && x % y != 0 { + *a = *a % *b + *b; + } else { + *a = *a % *b + } + self + } + (Value::int_value(a), Value::float_value(b)) => { + let a: &mut i64 = get_ref_mut(a); + *a %= *b as i64; + self + } + (Value::float_value(a), Value::int_value(b)) => { + let a: &mut f64 = get_ref_mut(a); + *a %= *b as f64; + self + } + (Value::float_value(a), Value::float_value(b)) => { + let a: &mut f64 = get_ref_mut(a); + *a %= *b; + self + } + _ => panic_unsupported_bin_op!("%", self.type_str(), x.type_str()), + } + } + + pub fn bin_aug_pow(&mut self, x: &Self) -> &mut Self { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_32 = ctx.cfg.strict_range_check; + let strict_range_check_64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match (&*self.rc, &*x.rc) { + (Value::int_value(ref a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_i32_overflow_pow(*a, *b) { + panic_i32_overflow!((*a as i128).pow(*b as u32)); + } + } + if strict_range_check_64 { + if is_i64_overflow_pow(*a, *b) { + panic_i64_overflow!((*a as i128).pow(*b as u32)); + } + } + let a: &mut i64 = get_ref_mut(a); + *a = a.pow(*b as u32); + self + } + (Value::float_value(ref a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_pow(*a, *b) { + panic_f32_overflow!(a.powf(*b)); + } + } + let a: &mut f64 = get_ref_mut(a); + *a = a.powf(*b as f64); + self + } + (Value::int_value(ref a), Value::float_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_pow(*a as f64, *b) { + panic_f32_overflow!((*a as f64).powf(*b)); + } + } + let a: &mut i64 = get_ref_mut(a); + *a = a.pow(*b as u32); + self + } + (Value::float_value(ref a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_f32_overflow_pow(*a, *b as f64) { + panic_f32_overflow!(a.powf(*b as f64)); + } + } + let a: &mut f64 = get_ref_mut(a); + *a = a.powf(*b as f64); + self + } + _ => panic_unsupported_bin_op!("**", self.type_str(), x.type_str()), + } + } + + pub fn bin_aug_floor_div(&mut self, x: &Self) -> &mut Self { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + let a: &mut i64 = get_ref_mut(a); + let x = *a; + let y = *b; + if -13 / 5 == -2 && (x < 0) != (y < 0) && x % y != 0 { + *a = *a / *b - 1 + } else { + *a /= *b + } + self + } + (Value::int_value(a), Value::float_value(b)) => { + let a: &mut i64 = get_ref_mut(a); + *a = (*a as f64 / *b) as i64; + self + } + (Value::float_value(a), Value::int_value(b)) => { + let a: &mut f64 = get_ref_mut(a); + *a /= *b as f64; + self + } + (Value::float_value(a), Value::float_value(b)) => { + let a: &mut f64 = get_ref_mut(a); + *a /= *b; + self + } + _ => panic_unsupported_bin_op!("//", self.type_str(), x.type_str()), + } + } + + pub fn bin_aug_bit_lshift(&mut self, x: &Self) -> &mut Self { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_32 = ctx.cfg.strict_range_check; + let strict_range_check_64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_i32_overflow_shl(*a, *b) { + panic_i32_overflow!((*a as i128) << (*b as u32)); + } + } + if strict_range_check_64 { + if is_i64_overflow_shl(*a, *b) { + panic_i64_overflow!((*a as i128) << (*b as u32)); + } + } + let a: &mut i64 = get_ref_mut(a); + *a <<= *b as usize; + self + } + _ => panic_unsupported_bin_op!("<<", self.type_str(), x.type_str()), + } + } + + pub fn bin_aug_bit_rshift(&mut self, x: &Self) -> &mut Self { + let ctx = crate::Context::current_context_mut(); + let strict_range_check_32 = ctx.cfg.strict_range_check; + let strict_range_check_64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + if strict_range_check_32 { + if is_i32_overflow_shr(*a, *b) { + panic_i32_overflow!((*a as i128) >> (*b as u32)); + } + } + if strict_range_check_64 { + if is_i64_overflow_shr(*a, *b) { + panic_i64_overflow!((*a as i128) >> (*b as u32)); + } + } + let a: &mut i64 = get_ref_mut(a); + *a >>= *b as usize; + self + } + _ => panic_unsupported_bin_op!(">>", self.type_str(), x.type_str()), + } + } + + pub fn bin_aug_bit_and(&mut self, x: &Self) -> &mut Self { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + let a: &mut i64 = get_ref_mut(a); + *a &= *b as i64; + self + } + _ => panic_unsupported_bin_op!("&", self.type_str(), x.type_str()), + } + } + + pub fn bin_aug_bit_xor(&mut self, x: &Self) -> &mut Self { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + let a: &mut i64 = get_ref_mut(a); + *a ^= *b as i64; + self + } + _ => panic_unsupported_bin_op!("^", self.type_str(), x.type_str()), + } + } + + pub fn bin_aug_bit_or(&mut self, x: &Self) -> &mut Self { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => { + let a: &mut i64 = get_ref_mut(a); + *a |= *b as i64; + self + } + _ => { + if self.is_list_or_config() || x.is_list_or_config() { + self.union(x, true, false, true, true); + return self; + } + panic_unsupported_bin_op!("|", self.type_str(), x.type_str()); + } + } + } + + /// Binary aug union a | b + pub fn bin_aug_union_with(&mut self, x: &Self) -> &mut Self { + self.bin_aug_bit_or(x) + } +} + +#[cfg(test)] +mod test_value_bin_aug { + use crate::*; + + #[test] + fn test_int_bin() { + let cases = [ + (0, 0, "+", 0), + (1, 1, "-", 0), + (-1, 2, "*", -2), + (4, 2, "/", 2), + (-2, 4, "//", -1), + (-2, 5, "%", 3), + (3, 2, "**", 9), + (2, 1, ">>", 1), + (3, 2, "<<", 12), + (5, 9, "&", 1), + (5, 10, "|", 15), + (7, 11, "^", 12), + ]; + for (left, right, op, expected) in cases { + let mut left = ValueRef::int(left); + let right = ValueRef::int(right); + let result = match op { + "+" => left.bin_aug_add(&right), + "-" => left.bin_aug_sub(&right), + "*" => left.bin_aug_mul(&right), + "/" => left.bin_aug_div(&right), + "//" => left.bin_aug_floor_div(&right), + "%" => left.bin_aug_mod(&right), + "**" => left.bin_aug_pow(&right), + "<<" => left.bin_aug_bit_lshift(&right), + ">>" => left.bin_aug_bit_rshift(&right), + "&" => left.bin_aug_bit_and(&right), + "|" => left.bin_aug_bit_or(&right), + "^" => left.bin_aug_bit_xor(&right), + _ => panic!("invalid op {}", op), + }; + assert_eq!(result.as_int(), expected as i64) + } + } + + #[test] + fn test_aug_add() { + // int + assert_eq!( + ValueRef::int(1).bin_aug_add(&ValueRef::int(2)).as_int(), + 1 + 2 + ); + + // float + assert_eq!( + ValueRef::float(1.5) + .bin_aug_add(&ValueRef::float(2.0)) + .as_float(), + 3.5 + ); + + // str + + // list + + // int + float => int + assert_eq!( + ValueRef::int(1).bin_aug_add(&ValueRef::float(2.5)).as_int(), + 1 + 2 + ); + + // float + int => float + assert_eq!( + ValueRef::float(1.5) + .bin_aug_add(&ValueRef::int(2)) + .as_float(), + 1.5 + 2.0 + ); + } + + #[test] + fn test_aug_sub() { + // int + // float + } +} diff --git a/kclvm/runtime/src/value/val_clone.rs b/kclvm/runtime/src/value/val_clone.rs new file mode 100644 index 000000000..538dfd4da --- /dev/null +++ b/kclvm/runtime/src/value/val_clone.rs @@ -0,0 +1,115 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use std::rc::Rc; + +use crate::*; + +impl ValueRef { + pub fn deep_copy(&self) -> ValueRef { + match &*self.rc { + Value::undefined => ValueRef { + rc: Rc::new(Value::undefined), + }, + Value::none => ValueRef { + rc: Rc::new(Value::none), + }, + Value::func_value(ref v) => ValueRef { + rc: Rc::new(Value::func_value(FuncValue { + fn_ptr: v.fn_ptr, + check_fn_ptr: v.check_fn_ptr, + closure: v.closure.deep_copy(), + external_name: v.external_name.clone(), + runtime_type: v.runtime_type.clone(), + })), + }, + Value::bool_value(ref v) => ValueRef { + rc: Rc::new(Value::bool_value(*v)), + }, + Value::int_value(ref v) => ValueRef { + rc: Rc::new(Value::int_value(*v)), + }, + Value::float_value(ref v) => ValueRef { + rc: Rc::new(Value::float_value(*v)), + }, + Value::unit_value(ref v, ref raw, ref unit) => ValueRef { + rc: Rc::new(Value::unit_value(*v, *raw, unit.clone())), + }, + Value::str_value(ref v) => ValueRef { + rc: Rc::new(Value::str_value(v.to_string())), + }, + Value::list_value(ref v) => ValueRef { + rc: Rc::new(Value::list_value(ListValue { + values: v.values.iter().map(|x| x.deep_copy()).collect(), + })), + }, + Value::dict_value(ref v) => { + let mut dict = DictValue::new(&[]); + for (key, val) in &v.values { + let op = v + .ops + .get(key) + .or(Some(&ConfigEntryOperationKind::Union)) + .unwrap(); + let index = v.insert_indexs.get(key).or(Some(&-1)).unwrap(); + dict.dict_update_entry( + key.as_str(), + &val.deep_copy(), + &op.clone(), + &index.clone(), + ); + } + dict + } + Value::schema_value(ref v) => { + let mut dict = DictValue::new(&[]); + for (key, val) in &v.config.values { + let op = v + .config + .ops + .get(key) + .or(Some(&ConfigEntryOperationKind::Union)) + .unwrap(); + let index = v.config.insert_indexs.get(key).or(Some(&-1)).unwrap(); + dict.dict_update_entry( + key.as_str(), + &val.deep_copy(), + &op.clone(), + &index.clone(), + ); + if let Some(type_str) = v.config.attr_map.get(key) { + dict.update_attr_map(key, type_str); + } + } + ValueRef { + rc: Rc::new(Value::schema_value(SchemaValue { + name: v.name.clone(), + pkgpath: v.pkgpath.clone(), + config: Rc::new(dict.as_dict_ref().clone()), + config_keys: v.config_keys.clone(), + })), + } + } + } + } +} + +#[cfg(test)] +mod test_value_deep_copy { + use crate::*; + + #[test] + fn test_deep_copy() { + let values = [ + ValueRef::int(123), + ValueRef::float(123.0), + ValueRef::str("abc"), + ValueRef::bool(true), + ValueRef::list_int(&[1, 2, 3]), + ValueRef::dict_int(&[("k1", 1), ("k2", 2)]), + ]; + for value in values { + let value_deep_copy = value.deep_copy(); + assert_eq!(value_deep_copy, value); + } + } +} diff --git a/kclvm/runtime/src/value/val_cmp.rs b/kclvm/runtime/src/value/val_cmp.rs new file mode 100644 index 000000000..c2dde9fa6 --- /dev/null +++ b/kclvm/runtime/src/value/val_cmp.rs @@ -0,0 +1,464 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +// cmp +impl ValueRef { + pub fn cmp_equal(&self, x: &Self) -> bool { + match *self.rc { + Value::int_value(a) => match *x.rc { + Value::int_value(b) => a == b, + Value::float_value(b) => a as f64 == b, + _ => false, + }, + Value::float_value(a) => match *x.rc { + Value::int_value(b) => a == b as f64, + Value::float_value(b) => a == b, + _ => false, + }, + _ => match (&*self.rc, &*x.rc) { + (Value::undefined, Value::undefined) => true, + (Value::none, Value::none) => true, + (Value::bool_value(a), Value::bool_value(b)) => *a == *b, + (Value::str_value(a), Value::str_value(b)) => *a == *b, + (Value::list_value(a), Value::list_value(b)) => { + if a.values.len() != b.values.len() { + return false; + } + for i in 0..a.values.len() { + if !a.values[i].cmp_equal(&b.values[i]) { + return false; + } + } + true + } + (Value::dict_value(a), Value::dict_value(b)) => { + if a.values.len() != b.values.len() { + return false; + } + for (k, v) in a.values.iter() { + if !b.values.contains_key(k) { + return false; + } + if !v.cmp_equal(&b.values[k]) { + return false; + } + } + true + } + (Value::schema_value(a), Value::schema_value(b)) => { + if a.config.values.len() != b.config.values.len() { + return false; + } + for (k, v) in a.config.values.iter() { + if !b.config.values.contains_key(k) { + return false; + } + if !v.cmp_equal(&b.config.values[k]) { + return false; + } + } + true + } + (Value::func_value(a), Value::func_value(b)) => a.fn_ptr == b.fn_ptr, + _ => false, + }, + } + } + + pub fn cmp_not_equal(&self, x: &Self) -> bool { + !self.cmp_equal(x) + } + + pub fn cmp_less_than(&self, x: &Self) -> bool { + match &*self.rc { + Value::int_value(a) => match &*x.rc { + Value::int_value(b) => *a < *b, + Value::float_value(b) => (*a as f64) < *b, + Value::bool_value(b) => *a < (*b as i64), + _ => panic!( + "'<' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::float_value(a) => match &*x.rc { + Value::int_value(b) => *a < *b as f64, + Value::float_value(b) => *a < *b, + Value::bool_value(b) => *a < ((*b as i64) as f64), + _ => panic!( + "'<' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::bool_value(a) => match &*x.rc { + Value::int_value(b) => (*a as i64) < *b, + Value::float_value(b) => ((*a as i64) as f64) < *b, + Value::bool_value(b) => !(*a) & *b, + _ => panic!( + "'<' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::str_value(a) => match &*x.rc { + Value::str_value(b) => *a < *b, + _ => panic!( + "'<' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::list_value(a) => match &*x.rc { + Value::list_value(b) => { + let len_a = a.values.len(); + let len_b = b.values.len(); + let len_min = if len_a >= len_b { len_b } else { len_a }; + for i in 0..len_min { + let value1 = &a.values[i]; + let value2 = &b.values[i]; + if !value1.cmp_equal(value2) { + return value1.cmp_less_than(value2); + } + } + len_a < len_b + } + _ => panic!( + "'<' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + _ => panic!( + "'<' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + } + } + + pub fn cmp_less_than_or_equal(&self, x: &Self) -> bool { + match &*self.rc { + Value::int_value(a) => match &*x.rc { + Value::int_value(b) => *a <= *b, + Value::float_value(b) => (*a as f64) <= *b, + Value::bool_value(b) => *a <= (*b as i64), + _ => panic!( + "'<=' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::float_value(a) => match &*x.rc { + Value::int_value(b) => *a <= *b as f64, + Value::float_value(b) => *a <= *b, + Value::bool_value(b) => *a <= ((*b as i64) as f64), + _ => panic!( + "'<=' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::bool_value(a) => match &*x.rc { + Value::int_value(b) => (*a as i64) <= *b, + Value::float_value(b) => ((*a as i64) as f64) <= *b, + Value::bool_value(b) => *a <= *b, + _ => panic!( + "'<=' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::str_value(a) => match &*x.rc { + Value::str_value(b) => *a <= *b, + _ => panic!( + "'<=' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::list_value(a) => match &*x.rc { + Value::list_value(b) => { + let len_a = a.values.len(); + let len_b = b.values.len(); + let len_min = if len_a >= len_b { len_b } else { len_a }; + for i in 0..len_min { + let value1 = &a.values[i]; + let value2 = &b.values[i]; + if !value1.cmp_equal(value2) { + return value1.cmp_less_than_or_equal(value2); + } + } + len_a <= len_b + } + _ => panic!( + "'<=' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + _ => panic!( + "'<=' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + } + } + + pub fn cmp_greater_than(&self, x: &Self) -> bool { + match &*self.rc { + Value::int_value(a) => match &*x.rc { + Value::int_value(b) => *a > *b, + Value::float_value(b) => (*a as f64) > *b, + Value::bool_value(b) => *a > (*b as i64), + _ => panic!( + "'>' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::float_value(a) => match &*x.rc { + Value::int_value(b) => *a > *b as f64, + Value::float_value(b) => *a > *b, + Value::bool_value(b) => *a > ((*b as i64) as f64), + _ => panic!( + "'>' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::bool_value(a) => match &*x.rc { + Value::int_value(b) => (*a as i64) > *b, + Value::float_value(b) => ((*a as i64) as f64) > *b, + Value::bool_value(b) => *a & !(*b), + _ => panic!( + "'>' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::str_value(a) => match &*x.rc { + Value::str_value(b) => *a > *b, + _ => panic!( + "'>' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::list_value(a) => match &*x.rc { + Value::list_value(b) => { + let len_a = a.values.len(); + let len_b = b.values.len(); + let len_min = if len_a >= len_b { len_b } else { len_a }; + for i in 0..len_min { + let value1 = &a.values[i]; + let value2 = &b.values[i]; + if !value1.cmp_equal(value2) { + return value1.cmp_greater_than(value2); + } + } + len_a > len_b + } + _ => panic!( + "'>' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + _ => panic!( + "'>' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + } + } + + pub fn cmp_greater_than_or_equal(&self, x: &Self) -> bool { + match &*self.rc { + Value::int_value(a) => match &*x.rc { + Value::int_value(b) => *a >= *b, + Value::float_value(b) => (*a as f64) >= *b, + Value::bool_value(b) => *a >= (*b as i64), + _ => panic!( + "'>=' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::float_value(a) => match &*x.rc { + Value::int_value(b) => *a >= *b as f64, + Value::float_value(b) => *a >= *b, + Value::bool_value(b) => *a >= ((*b as i64) as f64), + _ => panic!( + "'>=' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::bool_value(a) => match &*x.rc { + Value::int_value(b) => (*a as i64) >= *b, + Value::float_value(b) => ((*a as i64) as f64) >= *b, + Value::bool_value(b) => *a >= *b, + _ => panic!( + "'>=' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::str_value(a) => match &*x.rc { + Value::str_value(b) => *a >= *b, + _ => panic!( + "'>=' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + Value::list_value(a) => match &*x.rc { + Value::list_value(b) => { + let len_a = a.values.len(); + let len_b = b.values.len(); + let len_min = if len_a >= len_b { len_b } else { len_a }; + for i in 0..len_min { + let value1 = &a.values[i]; + let value2 = &b.values[i]; + if !value1.cmp_equal(value2) { + return value1.cmp_greater_than_or_equal(value2); + } + } + len_a >= len_b + } + _ => panic!( + "'>=' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + }, + _ => panic!( + "'>=' not supported between instances of '{}' and '{}'", + self.type_str(), + x.type_str() + ), + } + } +} + +#[cfg(test)] +mod test_value_cmp { + use crate::*; + + #[test] + fn test_eq() { + let cases = [ + // true cases + (ValueRef::int(123), ValueRef::int(123), true), + (ValueRef::int(123), ValueRef::float(123.0), true), + (ValueRef::str("abc"), ValueRef::str("abc"), true), + (ValueRef::bool(true), ValueRef::bool(true), true), + ( + ValueRef::list_int(&[1, 2, 3]), + ValueRef::list_int(&[1, 2, 3]), + true, + ), + ( + ValueRef::dict_int(&[("k1", 1), ("k2", 2)]), + ValueRef::dict_int(&[("k1", 1), ("k2", 2)]), + true, + ), + // false cases + (ValueRef::int(123), ValueRef::int(1234), false), + (ValueRef::int(123), ValueRef::float(1234.0), false), + (ValueRef::str("abc"), ValueRef::str("abcd"), false), + (ValueRef::bool(true), ValueRef::bool(false), false), + ( + ValueRef::list_int(&[1, 2, 3]), + ValueRef::list_int(&[2, 3, 4]), + false, + ), + ( + ValueRef::dict_int(&[("k1", 1), ("k2", 2)]), + ValueRef::dict_int(&[("1", 1), ("2", 2)]), + false, + ), + ]; + for (left, right, expected) in cases { + assert_eq!(left.cmp_equal(&right), expected); + } + } + + #[test] + fn test_ne() { + let cases = [ + // true cases + (ValueRef::int(123), ValueRef::int(1234), true), + (ValueRef::int(123), ValueRef::float(1234.0), true), + (ValueRef::str("abc"), ValueRef::str("abcd"), true), + // false cases + (ValueRef::int(123), ValueRef::int(123), false), + (ValueRef::int(123), ValueRef::float(123.0), false), + (ValueRef::str("abc"), ValueRef::str("abc"), false), + ]; + for (left, right, expected) in cases { + assert_eq!(left.cmp_not_equal(&right), expected); + } + } + + #[test] + fn test_cmp() { + let cases = [ + // > + (ValueRef::int(123), ValueRef::int(12), ">", true), + (ValueRef::int(1234), ValueRef::float(123.0), ">", true), + (ValueRef::str("abc"), ValueRef::str("ab"), ">", true), + (ValueRef::bool(true), ValueRef::bool(false), ">", true), + ( + ValueRef::list_int(&[1, 2, 3]), + ValueRef::list_int(&[1, 1, 3]), + ">", + true, + ), + // >= + (ValueRef::int(123), ValueRef::int(12), ">=", true), + (ValueRef::int(1234), ValueRef::float(123.0), ">=", true), + (ValueRef::str("abc"), ValueRef::str("ab"), ">=", true), + (ValueRef::bool(true), ValueRef::bool(false), ">=", true), + ( + ValueRef::list_int(&[1, 2]), + ValueRef::list_int(&[1]), + ">=", + true, + ), + // < + (ValueRef::int(123), ValueRef::int(12), "<", false), + (ValueRef::int(1234), ValueRef::float(123.0), "<", false), + (ValueRef::str("abc"), ValueRef::str("ab"), "<", false), + (ValueRef::bool(true), ValueRef::bool(false), "<", false), + ( + ValueRef::list_int(&[1, 2, 3]), + ValueRef::list_int(&[1, 1, 3]), + "<", + false, + ), + // <= + (ValueRef::int(123), ValueRef::int(12), "<=", false), + (ValueRef::int(1234), ValueRef::float(123.0), "<=", false), + (ValueRef::str("abc"), ValueRef::str("ab"), "<=", false), + (ValueRef::bool(true), ValueRef::bool(false), "<=", false), + ( + ValueRef::list_int(&[1, 2, 3]), + ValueRef::list_int(&[1, 1, 3]), + "<=", + false, + ), + ]; + for (left, right, op, expected) in cases { + match op { + ">" => assert_eq!(left.cmp_greater_than(&right), expected), + ">=" => assert_eq!(left.cmp_greater_than_or_equal(&right), expected), + "<" => assert_eq!(left.cmp_less_than(&right), expected), + "<=" => assert_eq!(left.cmp_less_than_or_equal(&right), expected), + _ => panic!("invalid op {}", op), + } + } + } +} diff --git a/kclvm/runtime/src/value/val_decorator.rs b/kclvm/runtime/src/value/val_decorator.rs new file mode 100644 index 000000000..69bc026bd --- /dev/null +++ b/kclvm/runtime/src/value/val_decorator.rs @@ -0,0 +1,134 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +pub const DEPRECATED_DECORATOR: &str = "deprecated"; +pub const DEPRECATED_INFO: &str = "info"; + +impl DecoratorValue { + pub fn new(name: &str, args: &ValueRef, kwargs: &ValueRef) -> DecoratorValue { + DecoratorValue { + name: name.to_string(), + args: args.clone(), + kwargs: kwargs.clone(), + } + } + + pub fn run( + &self, + attr_name: &str, + is_schema_target: bool, + config_value: &ValueRef, + config_meta: &ValueRef, + ) { + let filename = config_meta.get_by_key(CONFIG_META_FILENAME); + let line = config_meta.get_by_key(CONFIG_META_LINE); + match self.name.as_str() { + DEPRECATED_DECORATOR => { + let version = self.kwargs.kwarg("version"); + let reason = self.kwargs.kwarg("reason"); + let strict = self.kwargs.kwarg("strict"); + let version = if let Some(v) = version { + v.as_str() + } else { + "".to_string() + }; + let reason = if let Some(v) = reason { + v.as_str() + } else { + "".to_string() + }; + let strict = if let Some(v) = strict { + v.as_bool() + } else { + true + }; + let mut msg = String::new(); + if !version.is_empty() { + let version = format!("since version {}", version); + msg.push_str(&version); + } + if !reason.is_empty() { + let reason = format!(", {}", reason); + msg.push_str(&reason); + } + if strict { + if is_schema_target || config_value.get_by_key(attr_name).is_some() { + let mut err_msg = format!("{} was deprecated ", attr_name); + if !msg.is_empty() { + err_msg.push_str(&msg); + } + let ctx = Context::current_context_mut(); + if let (Some(filename), Some(line)) = (filename, line) { + ctx.set_kcl_filename(&filename.as_str()); + ctx.panic_info.kcl_line = line.as_int() as i32; + } + ctx.set_err_type(&ErrType::Deprecated_TYPE); + + panic!("{}", err_msg) + } + } else if is_schema_target || config_value.get_by_key(attr_name).is_some() { + let mut err_msg = format!("{} was deprecated ", attr_name); + if !msg.is_empty() { + err_msg.push_str(&msg); + } + + let ctx = Context::current_context_mut(); + ctx.set_err_type(&ErrType::Deprecated_Warning_TYPE); + + ctx.set_warnning_message(err_msg.as_str()); + } else { + let ctx = Context::current_context_mut(); + let err_msg = format!("{} was deprecated ", attr_name); + ctx.set_err_type(&ErrType::Deprecated_Warning_TYPE); + ctx.set_warnning_message(err_msg.as_str()); + } + } + DEPRECATED_INFO => { /* Nothing to do on Info decorator */ } + _ => { + let msg = format!("Unknown decorator {}", self.name); + panic!("{}", msg); + } + }; + } + + pub fn into_raw(self) -> *mut Self { + Box::into_raw(Box::new(self)) + } +} + +#[cfg(test)] +mod test_value_decorator { + use crate::*; + + fn assert_panic () + std::panic::UnwindSafe>(func: F) { + let result = std::panic::catch_unwind(func); + assert!(result.is_err()) + } + + #[test] + fn test_decorator() { + let args = ValueRef::list(None); + let mut kwargs = ValueRef::dict(None); + let test_deprecated_decorator = DecoratorValue::new(DEPRECATED_DECORATOR, &args, &kwargs); + kwargs.dict_update_key_value("strict", ValueRef::bool(false)); + let schema_name = "Data"; + let config_meta = ValueRef::dict(None); + let config_value = ValueRef::dict_str(&[("key1", "value1")]); + test_deprecated_decorator.run(schema_name, true, &config_value, &config_meta); + } + + #[test] + fn test_decorator_invalid() { + assert_panic(|| { + let args = ValueRef::list(None); + let kwargs = ValueRef::dict(None); + let test_deprecated_decorator = + DecoratorValue::new(DEPRECATED_DECORATOR, &args, &kwargs); + let schema_name = "Data"; + let config_meta = ValueRef::dict(None); + let config_value = ValueRef::dict_str(&[("key1", "value1")]); + test_deprecated_decorator.run(schema_name, true, &config_value, &config_meta); + }); + } +} diff --git a/kclvm/runtime/src/value/val_dict.rs b/kclvm/runtime/src/value/val_dict.rs new file mode 100644 index 000000000..80c65544c --- /dev/null +++ b/kclvm/runtime/src/value/val_dict.rs @@ -0,0 +1,433 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl DictValue { + pub fn new(values: &[(&str, &ValueRef)]) -> ValueRef { + let mut dict = DictValue::default(); + for x in values { + dict.values.insert(x.0.to_string(), x.1.clone()); + } + ValueRef::from(Value::dict_value(dict)) + } + + pub fn get(&self, key: &ValueRef) -> Option<&ValueRef> { + match &*key.rc { + Value::str_value(ref s) => self.values.get(s), + _ => None, + } + } + + pub fn insert(&mut self, key: &ValueRef, value: &ValueRef) { + if let Value::str_value(ref s) = &*key.rc { + self.values.insert(s.to_string(), value.clone()); + } + } + + pub fn insert_unpack(&mut self, v: &ValueRef) { + if let Value::dict_value(ref b) = &*v.rc { + for (k, v) in b.values.iter() { + self.values.insert(k.clone(), v.clone()); + } + } + } +} + +impl ValueRef { + fn dict_config(&self) -> &DictValue { + match &*self.rc { + Value::dict_value(ref dict) => dict, + Value::schema_value(ref schema) => schema.config.as_ref(), + _ => panic!("invalid dict config value type {}", self.type_str()), + } + } + pub fn dict_int(values: &[(&str, i64)]) -> Self { + let mut dict = DictValue::default(); + for x in values { + dict.values.insert(x.0.to_string(), Self::int(x.1)); + } + Self::from(Value::dict_value(dict)) + } + + pub fn dict_float(values: &[(&str, f64)]) -> Self { + let mut dict = DictValue::default(); + for x in values { + dict.values.insert(x.0.to_string(), Self::float(x.1)); + } + Self::from(Value::dict_value(dict)) + } + + pub fn dict_bool(values: &[(&str, bool)]) -> Self { + let mut dict = DictValue::default(); + for x in values { + dict.values.insert(x.0.to_string(), Self::bool(x.1)); + } + Self::from(Value::dict_value(dict)) + } + + pub fn dict_str(values: &[(&str, &str)]) -> Self { + let mut dict = DictValue::default(); + for x in values { + dict.values.insert(x.0.to_string(), Self::str(x.1)); + } + Self::from(Value::dict_value(dict)) + } + + /// Dict clear + pub fn dict_clear(&mut self) { + let dict = match &*self.rc { + Value::dict_value(ref dict) => get_ref_mut(dict), + Value::schema_value(ref schema) => get_ref_mut(schema.config.as_ref()), + _ => panic!("invalid config value in dict_clear"), + }; + dict.values.clear() + } + + /// Dict get keys. + pub fn dict_keys(&self) -> ValueRef { + let dict = self.dict_config(); + let keys: Vec = dict.values.keys().cloned().collect(); + ValueRef::list_str(&keys) + } + + /// Dict get values + pub fn dict_values(&self) -> ValueRef { + let dict = self.dict_config(); + let values: Vec<&ValueRef> = dict.values.values().map(|k| k).collect(); + ValueRef::list(Some(&values)) + } + + /// Dict get e.g., {k1: v1, k2, v2}.get(ValueRef::str(k1)) == v1 + pub fn dict_get(&self, key: &ValueRef) -> Option<&ValueRef> { + match &*self.rc { + Value::dict_value(ref dict) => dict.get(key), + Value::schema_value(ref schema) => schema.config.get(key), + _ => panic!("invalid config value in dict_get"), + } + } + + /// Dict get value e.g., {k1: v1, k2, v2}.get_value(k1) == v1 + pub fn dict_get_value(&self, key: &str) -> Option<&ValueRef> { + match &*self.rc { + Value::dict_value(ref dict) => dict.values.get(key), + Value::schema_value(ref schema) => schema.config.values.get(key), + _ => None, + } + } + + /// Dict get entry e.g., {k1: v1, k2, v2}.get_entry(k1) == {k1: v1} + pub fn dict_get_entry(&self, key: &str) -> Option { + match &*self.rc { + Value::dict_value(ref dict) => { + if dict.values.contains_key(key) { + let mut d = ValueRef::dict(None); + let value = dict.values.get(key).unwrap(); + let op = if let Some(op) = dict.ops.get(key) { + op + } else { + &ConfigEntryOperationKind::Union + }; + let index = if let Some(idx) = dict.insert_indexs.get(key) { + *idx + } else { + -1 + }; + d.dict_update_entry(key, value, op, &index); + Some(d) + } else { + None + } + } + Value::schema_value(ref schema) => { + if schema.config.values.contains_key(key) { + let mut d = ValueRef::dict(None); + let value = schema.config.values.get(key).unwrap(); + let op = if let Some(op) = schema.config.ops.get(key) { + op + } else { + &ConfigEntryOperationKind::Union + }; + let index = if let Some(idx) = schema.config.insert_indexs.get(key) { + *idx + } else { + -1 + }; + d.dict_update_entry(key, value, op, &index); + Some(d) + } else { + None + } + } + // Panic + _ => panic!("invalid config value in dict_get_entry"), + } + } + + /// Dict get entries e.g., {k1: v1, k2, v2}.get_entries([k1, k2]) == {k1: v1, k1: v2} + pub fn dict_get_entries(&self, keys: Vec<&str>) -> ValueRef { + match &*self.rc { + Value::dict_value(ref dict) => { + let mut d = ValueRef::dict(None); + for key in keys { + if dict.values.contains_key(key) { + let value = dict.values.get(key).unwrap(); + let op = dict + .ops + .get(key) + .or(Some(&ConfigEntryOperationKind::Union)) + .unwrap(); + let index = dict.insert_indexs.get(key).or(Some(&-1)).unwrap(); + d.dict_update_entry(key, value, op, index); + } + } + d + } + Value::schema_value(ref schema) => { + let mut d = ValueRef::dict(None); + for key in keys { + if schema.config.values.contains_key(key) { + let value = schema.config.values.get(key).unwrap(); + let op = schema + .config + .ops + .get(key) + .or(Some(&ConfigEntryOperationKind::Union)) + .unwrap(); + let index = schema.config.insert_indexs.get(key).or(Some(&-1)).unwrap(); + d.dict_update_entry(key, value, op, index); + } + } + d + } + // Panic + _ => panic!("invalid config value in dict_get_entries"), + } + } + + /// Update dict value without attribute operator check, only update + pub fn dict_update(&mut self, v: &ValueRef) { + let dict = match &*self.rc { + Value::dict_value(ref v) => get_ref_mut(v), + Value::schema_value(ref v) => { + let schema = get_ref_mut(v); + get_ref_mut(schema.config.as_ref()) + } + _ => panic!("invalid dict update value: {}", self.type_str()), + }; + if v.is_config() { + let v = v.as_dict_ref(); + for (k, v) in v.values.iter() { + dict.values.insert(k.clone(), v.clone()); + } + } + } + + /// Update key value pair without attribute operator check, only update + pub fn dict_update_key_value(&mut self, key: &str, val: ValueRef) { + match &*self.rc { + Value::dict_value(ref v) => { + let dict = get_ref_mut(v); + dict.values.insert(key.to_string(), val); + } + Value::schema_value(ref v) => { + let schema = get_ref_mut(v); + let dict = get_ref_mut(schema.config.as_ref()); + dict.values.insert(key.to_string(), val); + } + _ => panic!("invalid dict update value: {}", self.type_str()), + } + } + + /// Update entry without attribute operator check, only update + pub fn dict_update_entry( + &mut self, + key: &str, + val: &ValueRef, + op: &ConfigEntryOperationKind, + index: &i32, + ) { + let dict = match &*self.rc { + Value::dict_value(ref v) => get_ref_mut(v), + Value::schema_value(ref v) => { + let schema = get_ref_mut(v); + get_ref_mut(schema.config.as_ref()) + } + _ => panic!("invalid dict update value: {}", self.type_str()), + }; + dict.values.insert(key.to_string(), val.clone()); + dict.ops.insert(key.to_string(), op.clone()); + dict.insert_indexs.insert(key.to_string(), *index); + } + + /// Insert key value pair with the idempotent check + pub fn dict_insert( + &mut self, + key: &str, + v: &ValueRef, + op: ConfigEntryOperationKind, + insert_index: i32, + ) { + self.dict_merge_key_value_pair(key, v, op, insert_index, true); + } + + /// Merge key value pair without the idempotent check + pub fn dict_merge( + &mut self, + key: &str, + v: &ValueRef, + op: ConfigEntryOperationKind, + insert_index: i32, + ) { + self.dict_merge_key_value_pair(key, v, op, insert_index, false); + } + + /// Private dict merge key value pair with the idempotent check option + fn dict_merge_key_value_pair( + &mut self, + key: &str, + v: &ValueRef, + op: ConfigEntryOperationKind, + insert_index: i32, + should_idempotent_check: bool, + ) { + let ctx = crate::Context::current_context_mut(); + + if ctx.cfg.debug_mode { + if let Value::int_value(ref x) = *v.rc { + let strict_range_check_i32 = ctx.cfg.strict_range_check; + let strict_range_check_i64 = ctx.cfg.debug_mode || !ctx.cfg.strict_range_check; + let v_i128 = *x as i128; + + if strict_range_check_i32 { + if v_i128 != ((v_i128 as i32) as i128) { + let ctx = Context::current_context_mut(); + ctx.set_err_type(&ErrType::IntOverflow_TYPE); + + panic!("{}: A 32 bit integer overflow", v_i128); + } + } else if strict_range_check_i64 && v_i128 != ((v_i128 as i64) as i128) { + let ctx = Context::current_context_mut(); + ctx.set_err_type(&ErrType::IntOverflow_TYPE); + + panic!("{}: A 64 bit integer overflow", v_i128); + } + } + } + + match &*self.rc { + Value::dict_value(_) | Value::schema_value(_) => { + let mut dict: DictValue = Default::default(); + dict.values.insert(key.to_string(), v.clone()); + dict.ops.insert(key.to_string(), op); + dict.insert_indexs.insert(key.to_string(), insert_index); + self.union( + &ValueRef::from(Value::dict_value(dict)), + true, + false, + should_idempotent_check, + false, + ); + } + _ => panic!("invalid dict insert value: {}", self.type_str()), + } + } + + /// Dict insert unpack value e.g., data = {**v} + pub fn dict_insert_unpack(&mut self, v: &ValueRef) { + match (&*self.rc, &*v.rc) { + ( + Value::dict_value(_) | Value::schema_value(_), + Value::dict_value(_) | Value::schema_value(_), + ) => { + self.bin_aug_bit_or(&v.schema_to_dict().deep_copy()); + } + (Value::dict_value(_) | Value::schema_value(_), Value::none) => { /*Do nothing on unpacking None/Undefined*/ + } + (Value::dict_value(_) | Value::schema_value(_), Value::undefined) => { /*Do nothing on unpacking None/Undefined*/ + } + _ => panic!("only list, dict and schema object can be used with unpack operators * and **, got {}", v.to_string()), + } + } + + /// Dict remove the key-value pair equivalent to key + pub fn dict_remove(&mut self, key: &str) { + match &*self.rc { + Value::dict_value(ref dict) => { + let dict: &mut DictValue = get_ref_mut(dict); + dict.values.remove(key); + } + Value::schema_value(ref schema) => { + let dict: &mut DictValue = get_ref_mut(schema.config.as_ref()); + dict.values.remove(key); + } + _ => panic!("invalid dict remove value: {}", self.type_str()), + } + } +} + +#[cfg(test)] +mod test_value_dict { + + use crate::*; + + #[test] + fn test_dict_get() { + let entries = [("key1", 1), ("key2", 2)]; + let test_dict = ValueRef::dict_int(&entries); + for (key, val) in entries { + assert_eq!( + test_dict.dict_get(&ValueRef::str(key)).unwrap().clone(), + ValueRef::int(val) + ); + assert_eq!(test_dict.dict_get_value(key).unwrap().clone().as_int(), val); + assert_eq!( + test_dict.dict_get_entry(key).unwrap().clone(), + ValueRef::dict_int(&[(key, val)]) + ); + assert_eq!( + test_dict.dict_get_entries(vec![key]), + ValueRef::dict_int(&[(key, val)]) + ); + } + } + + #[test] + fn test_dict_update() { + let entries = [("key1", "value1"), ("key2", "value2")]; + let mut test_dict = ValueRef::dict_str(&entries); + let update_entries = [("key1", "override_value1"), ("key2", "override_value2")]; + let update_dict = ValueRef::dict_str(&update_entries); + test_dict.dict_update(&update_dict); + for (key, val) in update_entries { + assert_eq!( + test_dict.dict_get(&ValueRef::str(key)).unwrap().clone(), + ValueRef::str(val) + ); + } + let mut test_dict = ValueRef::dict_str(&entries); + for (key, val) in update_entries { + test_dict.dict_update_key_value(key, ValueRef::str(val)); + } + for (key, val) in update_entries { + assert_eq!( + test_dict.dict_get(&ValueRef::str(key)).unwrap().clone(), + ValueRef::str(val) + ); + } + let mut test_dict = ValueRef::dict_str(&entries); + for (key, val) in update_entries { + test_dict.dict_update_entry( + key, + &ValueRef::str(val), + &ConfigEntryOperationKind::Union, + &-1, + ); + } + for (key, val) in update_entries { + assert_eq!( + test_dict.dict_get(&ValueRef::str(key)).unwrap().clone(), + ValueRef::str(val) + ); + } + } +} diff --git a/kclvm/runtime/src/value/val_fmt.rs b/kclvm/runtime/src/value/val_fmt.rs new file mode 100644 index 000000000..220e8f3d6 --- /dev/null +++ b/kclvm/runtime/src/value/val_fmt.rs @@ -0,0 +1,1061 @@ +//! Ref: https://github.com/RustPython/RustPython/blob/main/vm/src/format.rs +//! +//! Copyright 2021 The KCL Authors. All rights reserved. + +use itertools::{Itertools, PeekingNext}; +use std::cmp; +use std::str::FromStr; + +use crate::*; + +#[derive(Debug)] +pub enum Case { + Lower, + Upper, +} + +/// If s represents a floating point value, trailing zeros and a possibly trailing +/// decimal point will be removed. +/// This function does NOT work with decimal commas. +fn remove_trailing_redundant_chars(s: String) -> String { + if s.contains('.') { + // only truncate floating point values + let s = remove_trailing_zeros(s); + remove_trailing_decimal_point(s) + } else { + s + } +} + +fn remove_trailing_zeros(s: String) -> String { + let mut s = s; + while s.ends_with('0') { + s.pop(); + } + s +} + +fn remove_trailing_decimal_point(s: String) -> String { + let mut s = s; + if s.ends_with('.') { + s.pop(); + } + s +} + +fn format_nan(case: Case) -> String { + let nan = match case { + Case::Lower => "nan", + Case::Upper => "NAN", + }; + + nan.to_string() +} + +fn format_inf(case: Case) -> String { + let inf = match case { + Case::Lower => "inf", + Case::Upper => "INF", + }; + + inf.to_string() +} + +pub fn format_fixed(precision: usize, magnitude: f64, case: Case) -> String { + match magnitude { + magnitude if magnitude.is_finite() => format!("{:.*}", precision, magnitude), + magnitude if magnitude.is_nan() => format_nan(case), + magnitude if magnitude.is_infinite() => format_inf(case), + _ => "".to_string(), + } +} + +pub fn is_integer(v: f64) -> bool { + (v - v.round()).abs() < std::f64::EPSILON +} + +pub fn float_to_string(value: f64) -> String { + let lit = format!("{:e}", value); + if let Some(position) = lit.find('e') { + let significand = &lit[..position]; + let exponent = &lit[position + 1..]; + let exponent = exponent.parse::().unwrap(); + if exponent < 16 && exponent > -5 { + if is_integer(value) { + format!("{:.1?}", value) + } else { + value.to_string() + } + } else { + format!("{}e{:+#03}", significand, exponent) + } + } else { + value.to_string() + } +} + +pub fn format_general(precision: usize, magnitude: f64, case: Case) -> String { + match magnitude { + magnitude if magnitude.is_finite() => { + let r_exp = format!("{:.*e}", precision.saturating_sub(1), magnitude); + let mut parts = r_exp.splitn(2, 'e'); + let base = parts.next().unwrap(); + let exponent = parts.next().unwrap().parse::().unwrap(); + if exponent < -4 || exponent >= (precision as i64) { + let e = match case { + Case::Lower => 'e', + Case::Upper => 'E', + }; + + let base = remove_trailing_redundant_chars(format!("{:.*}", precision + 1, base)); + format!("{}{}{:+#03}", base, e, exponent) + } else { + let precision = (precision as i64) - 1 - exponent; + let precision = precision as usize; + remove_trailing_redundant_chars(format!("{:.*}", precision, magnitude)) + } + } + magnitude if magnitude.is_nan() => format_nan(case), + magnitude if magnitude.is_infinite() => format_inf(case), + _ => "".to_string(), + } +} + +// Formats floats into Python style exponent notation, by first formatting in Rust style +// exponent notation (`1.0000e0`), then convert to Python style (`1.0000e+00`). +pub fn format_exponent(precision: usize, magnitude: f64, case: Case) -> String { + match magnitude { + magnitude if magnitude.is_finite() => { + let r_exp = format!("{:.*e}", precision, magnitude); + let mut parts = r_exp.splitn(2, 'e'); + let base = parts.next().unwrap(); + let exponent = parts.next().unwrap().parse::().unwrap(); + let e = match case { + Case::Lower => 'e', + Case::Upper => 'E', + }; + format!("{}{}{:+#03}", base, e, exponent) + } + magnitude if magnitude.is_nan() => format_nan(case), + magnitude if magnitude.is_infinite() => format_inf(case), + _ => "".to_string(), + } +} + +#[derive(Debug, Copy, Clone, PartialEq)] +enum FormatPreconversor { + Str, + Repr, + Ascii, + Bytes, +} + +impl FormatPreconversor { + fn from_char(c: char) -> Option { + match c { + 's' => Some(FormatPreconversor::Str), + 'r' => Some(FormatPreconversor::Repr), + 'a' => Some(FormatPreconversor::Ascii), + 'b' => Some(FormatPreconversor::Bytes), + _ => None, + } + } + + fn from_string(text: &str) -> Option { + let mut chars = text.chars(); + if chars.next() != Some('!') { + return None; + } + + FormatPreconversor::from_char(chars.next()?) + } + + fn parse_and_consume(text: &str) -> (Option, &str) { + let preconversor = FormatPreconversor::from_string(text); + match preconversor { + None => (None, text), + Some(_) => { + let mut chars = text.chars(); + chars.next(); // Consume the bang + chars.next(); // Consume one r,s,a char + (preconversor, chars.as_str()) + } + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq)] +enum FormatAlign { + Left, + Right, + AfterSign, + Center, +} + +impl FormatAlign { + fn from_char(c: char) -> Option { + match c { + '<' => Some(FormatAlign::Left), + '>' => Some(FormatAlign::Right), + '=' => Some(FormatAlign::AfterSign), + '^' => Some(FormatAlign::Center), + _ => None, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq)] +enum FormatSign { + Plus, + Minus, + MinusOrSpace, +} + +#[derive(Debug, PartialEq)] +enum FormatGrouping { + Comma, + Underscore, +} + +#[derive(Debug, PartialEq)] +enum FormatType { + String, + Binary, + Character, + Decimal, + Octal, + HexLower, + HexUpper, + Number, + ExponentLower, + ExponentUpper, + GeneralFormatLower, + GeneralFormatUpper, + FixedPointLower, + FixedPointUpper, + Percentage, +} + +#[derive(Debug, PartialEq)] +pub(crate) struct FormatSpec { + preconversor: Option, + fill: Option, + align: Option, + sign: Option, + alternate_form: bool, + width: Option, + grouping_option: Option, + precision: Option, + format_type: Option, +} + +pub(crate) fn get_num_digits(text: &str) -> usize { + for (index, character) in text.char_indices() { + if !character.is_digit(10) { + return index; + } + } + text.len() +} + +fn parse_preconversor(text: &str) -> (Option, &str) { + FormatPreconversor::parse_and_consume(text) +} + +fn parse_align(text: &str) -> (Option, &str) { + let mut chars = text.chars(); + let maybe_align = chars.next().and_then(FormatAlign::from_char); + if maybe_align.is_some() { + (maybe_align, chars.as_str()) + } else { + (None, text) + } +} + +fn parse_fill_and_align(text: &str) -> (Option, Option, &str) { + let char_indices: Vec<(usize, char)> = text.char_indices().take(3).collect(); + if char_indices.is_empty() { + (None, None, text) + } else if char_indices.len() == 1 { + let (maybe_align, remaining) = parse_align(text); + (None, maybe_align, remaining) + } else { + let (maybe_align, remaining) = parse_align(&text[char_indices[1].0..]); + if maybe_align.is_some() { + (Some(char_indices[0].1), maybe_align, remaining) + } else { + let (only_align, only_align_remaining) = parse_align(text); + (None, only_align, only_align_remaining) + } + } +} + +fn parse_number(text: &str) -> Result<(Option, &str), &'static str> { + let num_digits: usize = get_num_digits(text); + if num_digits == 0 { + return Ok((None, text)); + } + if let Ok(num) = text[..num_digits].parse::() { + Ok((Some(num), &text[num_digits..])) + } else { + // NOTE: this condition is different from CPython + Err("Too many decimal digits in format string") + } +} + +fn parse_sign(text: &str) -> (Option, &str) { + let mut chars = text.chars(); + match chars.next() { + Some('-') => (Some(FormatSign::Minus), chars.as_str()), + Some('+') => (Some(FormatSign::Plus), chars.as_str()), + Some(' ') => (Some(FormatSign::MinusOrSpace), chars.as_str()), + _ => (None, text), + } +} + +fn parse_alternate_form(text: &str) -> (bool, &str) { + let mut chars = text.chars(); + match chars.next() { + Some('#') => (true, chars.as_str()), + _ => (false, text), + } +} + +fn parse_zero(text: &str) -> (bool, &str) { + let mut chars = text.chars(); + match chars.next() { + Some('0') => (true, chars.as_str()), + _ => (false, text), + } +} + +fn parse_precision(text: &str) -> Result<(Option, &str), &'static str> { + let mut chars = text.chars(); + Ok(match chars.next() { + Some('.') => { + let (size, remaining) = parse_number(chars.as_str())?; + if size.is_some() { + (size, remaining) + } else { + (None, text) + } + } + _ => (None, text), + }) +} + +fn parse_grouping_option(text: &str) -> (Option, &str) { + let mut chars = text.chars(); + match chars.next() { + Some('_') => (Some(FormatGrouping::Underscore), chars.as_str()), + Some(',') => (Some(FormatGrouping::Comma), chars.as_str()), + _ => (None, text), + } +} + +fn parse_format_type(text: &str) -> (Option, &str) { + let mut chars = text.chars(); + match chars.next() { + Some('s') => (Some(FormatType::String), chars.as_str()), + Some('b') => (Some(FormatType::Binary), chars.as_str()), + Some('c') => (Some(FormatType::Character), chars.as_str()), + Some('d') => (Some(FormatType::Decimal), chars.as_str()), + Some('o') => (Some(FormatType::Octal), chars.as_str()), + Some('x') => (Some(FormatType::HexLower), chars.as_str()), + Some('X') => (Some(FormatType::HexUpper), chars.as_str()), + Some('e') => (Some(FormatType::ExponentLower), chars.as_str()), + Some('E') => (Some(FormatType::ExponentUpper), chars.as_str()), + Some('f') => (Some(FormatType::FixedPointLower), chars.as_str()), + Some('F') => (Some(FormatType::FixedPointUpper), chars.as_str()), + Some('g') => (Some(FormatType::GeneralFormatLower), chars.as_str()), + Some('G') => (Some(FormatType::GeneralFormatUpper), chars.as_str()), + Some('n') => (Some(FormatType::Number), chars.as_str()), + Some('%') => (Some(FormatType::Percentage), chars.as_str()), + _ => (None, text), + } +} + +fn parse_format_spec(text: &str) -> Result { + // get_integer in CPython + let (preconversor, after_preconversor) = parse_preconversor(text); + let (mut fill, mut align, after_align) = parse_fill_and_align(after_preconversor); + let (sign, after_sign) = parse_sign(after_align); + let (alternate_form, after_alternate_form) = parse_alternate_form(after_sign); + let (zero, after_zero) = parse_zero(after_alternate_form); + let (width, after_width) = parse_number(after_zero)?; + let (grouping_option, after_grouping_option) = parse_grouping_option(after_width); + let (precision, after_precision) = parse_precision(after_grouping_option)?; + let (format_type, after_format_type) = parse_format_type(after_precision); + if !after_format_type.is_empty() { + return Err("Invalid format specifier"); + } + + if zero && fill.is_none() { + fill.replace('0'); + align = align.or(Some(FormatAlign::AfterSign)); + } + + Ok(FormatSpec { + preconversor, + fill, + align, + sign, + alternate_form, + width, + grouping_option, + precision, + format_type, + }) +} + +impl FormatSpec { + pub(crate) fn parse(text: &str) -> Result { + parse_format_spec(text) + } + + fn compute_fill_string(fill_char: char, fill_chars_needed: i32) -> String { + (0..fill_chars_needed) + .map(|_| fill_char) + .collect::() + } + + #[allow(dead_code)] + fn add_magnitude_separators_for_char( + magnitude_string: String, + interval: usize, + separator: char, + ) -> String { + let mut result = String::new(); + + // Don't add separators to the floating decimal point of numbers + let mut parts = magnitude_string.splitn(2, '.'); + let magnitude_integer_string = parts.next().unwrap(); + let mut remaining: usize = magnitude_integer_string.len(); + for c in magnitude_integer_string.chars() { + result.push(c); + remaining -= 1; + if remaining % interval == 0 && remaining > 0 { + result.push(separator); + } + } + if let Some(part) = parts.next() { + result.push('.'); + result.push_str(part); + } + result + } + + #[allow(dead_code)] + fn get_separator_interval(&self) -> usize { + match self.format_type { + Some(FormatType::Binary) => 4, + Some(FormatType::Decimal) => 3, + Some(FormatType::Octal) => 4, + Some(FormatType::HexLower) => 4, + Some(FormatType::HexUpper) => 4, + Some(FormatType::Number) => 3, + Some(FormatType::FixedPointLower) | Some(FormatType::FixedPointUpper) => 3, + None => 3, + _ => panic!("Separators only valid for numbers!"), + } + } + + #[allow(dead_code)] + fn add_magnitude_separators(&self, magnitude_string: String) -> String { + match self.grouping_option { + Some(FormatGrouping::Comma) => FormatSpec::add_magnitude_separators_for_char( + magnitude_string, + self.get_separator_interval(), + ',', + ), + Some(FormatGrouping::Underscore) => FormatSpec::add_magnitude_separators_for_char( + magnitude_string, + self.get_separator_interval(), + '_', + ), + None => magnitude_string, + } + } + + pub(crate) fn format_float(&self, num: f64) -> Result { + let precision = self.precision.unwrap_or(6); + let magnitude = num.abs(); + let raw_magnitude_string_result: Result = match self.format_type { + Some(FormatType::FixedPointUpper) => { + Ok(format_fixed(precision, magnitude, Case::Upper)) + } + Some(FormatType::FixedPointLower) => { + Ok(format_fixed(precision, magnitude, Case::Lower)) + } + Some(FormatType::Decimal) => Err("Unknown format code 'd' for object of type 'float'"), + Some(FormatType::Binary) => Err("Unknown format code 'b' for object of type 'float'"), + Some(FormatType::Octal) => Err("Unknown format code 'o' for object of type 'float'"), + Some(FormatType::HexLower) => Err("Unknown format code 'x' for object of type 'float'"), + Some(FormatType::HexUpper) => Err("Unknown format code 'X' for object of type 'float'"), + Some(FormatType::String) => Err("Unknown format code 's' for object of type 'float'"), + Some(FormatType::Character) => { + Err("Unknown format code 'c' for object of type 'float'") + } + Some(FormatType::Number) => { + Err("Format code 'n' for object of type 'float' not implemented yet") + } + Some(FormatType::GeneralFormatUpper) => { + let precision = if precision == 0 { 1 } else { precision }; + Ok(format_general(precision, magnitude, Case::Upper)) + } + Some(FormatType::GeneralFormatLower) => { + let precision = if precision == 0 { 1 } else { precision }; + Ok(format_general(precision, magnitude, Case::Lower)) + } + Some(FormatType::ExponentUpper) => { + Ok(format_exponent(precision, magnitude, Case::Upper)) + } + Some(FormatType::ExponentLower) => { + Ok(format_exponent(precision, magnitude, Case::Lower)) + } + Some(FormatType::Percentage) => match magnitude { + magnitude if magnitude.is_nan() => Ok("nan%".to_owned()), + magnitude if magnitude.is_infinite() => Ok("inf%".to_owned()), + _ => Ok(format!("{:.*}%", precision, magnitude * 100.0)), + }, + None => match magnitude { + magnitude if magnitude.is_nan() => Ok("nan".to_owned()), + magnitude if magnitude.is_infinite() => Ok("inf".to_owned()), + _ => Ok(float_to_string(magnitude)), + }, + }; + + if raw_magnitude_string_result.is_err() { + return raw_magnitude_string_result; + } + + let magnitude_string = self.add_magnitude_separators(raw_magnitude_string_result.unwrap()); + let format_sign = self.sign.unwrap_or(FormatSign::Minus); + let sign_str = if num.is_sign_negative() && !num.is_nan() { + "-" + } else { + match format_sign { + FormatSign::Plus => "+", + FormatSign::Minus => "", + FormatSign::MinusOrSpace => " ", + } + }; + + self.format_sign_and_align(&magnitude_string, sign_str) + } + + pub(crate) fn format_int(&self, num: &i64) -> Result { + let magnitude = num.abs(); + let prefix = if self.alternate_form { + match self.format_type { + Some(FormatType::Binary) => "0b", + Some(FormatType::Octal) => "0o", + Some(FormatType::HexLower) => "0x", + Some(FormatType::HexUpper) => "0x", + _ => "", + } + } else { + "" + }; + let raw_magnitude_string_result: Result = match self.format_type { + Some(FormatType::Binary) => Ok(format!("{:b}", magnitude)), + Some(FormatType::Decimal) => Ok(format!("{}", magnitude)), + Some(FormatType::Octal) => Ok(format!("{:o}", magnitude)), + Some(FormatType::HexLower) => Ok(format!("{:x}", magnitude)), + Some(FormatType::HexUpper) => { + let mut result = format!("{:x}", magnitude); + result.make_ascii_uppercase(); + Ok(result) + } + Some(FormatType::Number) => Ok(format!("{}", magnitude)), + Some(FormatType::String) => Err("Unknown format code 's' for object of type 'int'"), + Some(FormatType::Character) => Err("Unknown format code 'c' for object of type 'int'"), + Some(FormatType::GeneralFormatUpper) => { + Err("Unknown format code 'G' for object of type 'int'") + } + Some(FormatType::GeneralFormatLower) => { + Err("Unknown format code 'g' for object of type 'int'") + } + Some(FormatType::FixedPointUpper) + | Some(FormatType::FixedPointLower) + | Some(FormatType::ExponentUpper) + | Some(FormatType::ExponentLower) + | Some(FormatType::Percentage) => self.format_float(*num as f64), + None => Ok(magnitude.to_string()), + }; + if raw_magnitude_string_result.is_err() { + return raw_magnitude_string_result; + } + let magnitude_string = format!( + "{}{}", + prefix, + self.add_magnitude_separators(raw_magnitude_string_result.unwrap()) + ); + + let format_sign = self.sign.unwrap_or(FormatSign::Minus); + let sign_str = match num.signum() { + -1 => "-", + _ => match format_sign { + FormatSign::Plus => "+", + FormatSign::Minus => "", + FormatSign::MinusOrSpace => " ", + }, + }; + + self.format_sign_and_align(&magnitude_string, sign_str) + } + + #[allow(dead_code)] + pub(crate) fn format_string(&self, s: &str) -> Result { + match self.format_type { + Some(FormatType::String) | None => self.format_sign_and_align(s, ""), + _ => Err("Unknown format code for object of type 'str'"), + } + } + + #[allow(dead_code)] + fn format_sign_and_align( + &self, + magnitude_string: &str, + sign_str: &str, + ) -> Result { + let align = self.align.unwrap_or(FormatAlign::Right); + + // Use the byte length as the string length since we're in ascii + let num_chars = magnitude_string.len(); + let fill_char = self.fill.unwrap_or(' '); + let fill_chars_needed: i32 = self.width.map_or(0, |w| { + cmp::max(0, (w as i32) - (num_chars as i32) - (sign_str.len() as i32)) + }); + Ok(match align { + FormatAlign::Left => format!( + "{}{}{}", + sign_str, + magnitude_string, + FormatSpec::compute_fill_string(fill_char, fill_chars_needed) + ), + FormatAlign::Right => format!( + "{}{}{}", + FormatSpec::compute_fill_string(fill_char, fill_chars_needed), + sign_str, + magnitude_string + ), + FormatAlign::AfterSign => format!( + "{}{}{}", + sign_str, + FormatSpec::compute_fill_string(fill_char, fill_chars_needed), + magnitude_string + ), + FormatAlign::Center => { + let left_fill_chars_needed = fill_chars_needed / 2; + let right_fill_chars_needed = fill_chars_needed - left_fill_chars_needed; + let left_fill_string = + FormatSpec::compute_fill_string(fill_char, left_fill_chars_needed); + let right_fill_string = + FormatSpec::compute_fill_string(fill_char, right_fill_chars_needed); + format!( + "{}{}{}{}", + left_fill_string, sign_str, magnitude_string, right_fill_string + ) + } + }) + } +} + +#[derive(Debug, PartialEq)] +pub(crate) enum FormatParseError { + UnmatchedBracket, + MissingStartBracket, + UnescapedStartBracketInLiteral, + InvalidFormatSpecifier, + EmptyAttribute, + MissingRightBracket, + InvalidCharacterAfterRightBracket, +} + +impl FromStr for FormatSpec { + type Err = &'static str; + fn from_str(s: &str) -> Result { + FormatSpec::parse(s) + } +} + +#[derive(Debug, PartialEq)] +pub(crate) enum FieldNamePart { + Attribute(String), + Index(usize), + StringIndex(String), +} + +impl FieldNamePart { + fn parse_part( + chars: &mut impl PeekingNext, + ) -> Result, FormatParseError> { + chars + .next() + .map(|ch| match ch { + '.' => { + let mut attribute = String::new(); + for ch in chars.peeking_take_while(|ch| *ch != '.' && *ch != '[') { + attribute.push(ch); + } + if attribute.is_empty() { + Err(FormatParseError::EmptyAttribute) + } else { + Ok(FieldNamePart::Attribute(attribute)) + } + } + '[' => { + let mut index = String::new(); + for ch in chars { + if ch == ']' { + return if index.is_empty() { + Err(FormatParseError::EmptyAttribute) + } else if let Ok(index) = index.parse::() { + Ok(FieldNamePart::Index(index)) + } else { + Ok(FieldNamePart::StringIndex(index)) + }; + } + index.push(ch); + } + Err(FormatParseError::MissingRightBracket) + } + _ => Err(FormatParseError::InvalidCharacterAfterRightBracket), + }) + .transpose() + } +} + +#[derive(Debug, PartialEq)] +pub(crate) enum FieldType { + Auto, + Index(usize), + Keyword(String), +} + +#[derive(Debug, PartialEq)] +pub(crate) struct FieldName { + pub field_type: FieldType, + pub parts: Vec, +} + +impl FieldName { + pub(crate) fn parse(text: &str) -> Result { + let mut chars = text.chars().peekable(); + let mut first = String::new(); + for ch in chars.peeking_take_while(|ch| *ch != '.' && *ch != '[') { + first.push(ch); + } + + let field_type = if first.is_empty() { + FieldType::Auto + } else if let Ok(index) = first.parse::() { + FieldType::Index(index) + } else { + FieldType::Keyword(first) + }; + + let mut parts = Vec::new(); + while let Some(part) = FieldNamePart::parse_part(&mut chars)? { + parts.push(part) + } + + Ok(FieldName { field_type, parts }) + } +} + +#[derive(Debug, PartialEq)] +pub(crate) enum FormatPart { + Field { + field_name: String, + format_spec: String, + }, + Literal(String), +} + +#[derive(Debug, PartialEq)] +pub(crate) struct FormatString { + pub format_parts: Vec, +} + +impl FormatString { + fn parse_literal_single(text: &str) -> Result<(char, &str), FormatParseError> { + let mut chars = text.chars(); + // This should never be called with an empty str + let first_char = chars.next().unwrap(); + // isn't this detectable only with bytes operation? + if first_char == '{' || first_char == '}' { + let maybe_next_char = chars.next(); + // if we see a bracket, it has to be escaped by doubling up to be in a literal + return if maybe_next_char.is_none() || maybe_next_char.unwrap() != first_char { + Err(FormatParseError::UnescapedStartBracketInLiteral) + } else { + Ok((first_char, chars.as_str())) + }; + } + Ok((first_char, chars.as_str())) + } + + fn parse_literal(text: &str) -> Result<(FormatPart, &str), FormatParseError> { + let mut cur_text = text; + let mut result_string = String::new(); + while !cur_text.is_empty() { + match FormatString::parse_literal_single(cur_text) { + Ok((next_char, remaining)) => { + result_string.push(next_char); + cur_text = remaining; + } + Err(err) => { + return if !result_string.is_empty() { + Ok((FormatPart::Literal(result_string), cur_text)) + } else { + Err(err) + }; + } + } + } + Ok((FormatPart::Literal(result_string), "")) + } + + fn parse_part_in_brackets(text: &str) -> Result { + let parts: Vec<&str> = text.splitn(2, ':').collect(); + // before the comma is a keyword or arg index, after the comma is maybe a spec. + let arg_part = parts[0]; + + let format_spec = if parts.len() > 1 { + parts[1].to_owned() + } else { + String::new() + }; + + // On parts[0] can still be the preconversor (!r, !s, !a) + let parts: Vec<&str> = arg_part.splitn(2, '!').collect(); + // before the bang is a keyword or arg index, after the comma is maybe a conversor spec. + let arg_part = parts[0]; + + Ok(FormatPart::Field { + field_name: arg_part.to_owned(), + format_spec, + }) + } + + fn parse_spec(text: &str) -> Result<(FormatPart, &str), FormatParseError> { + let mut nested = false; + let mut end_bracket_pos = None; + let mut left = String::new(); + + // There may be one layer nesting brackets in spec + for (idx, c) in text.chars().enumerate() { + if idx == 0 { + if c != '{' { + return Err(FormatParseError::MissingStartBracket); + } + } else if c == '{' { + if nested { + return Err(FormatParseError::InvalidFormatSpecifier); + } else { + nested = true; + left.push(c); + continue; + } + } else if c == '}' { + if nested { + nested = false; + left.push(c); + continue; + } else { + end_bracket_pos = Some(idx); + break; + } + } else { + left.push(c); + } + } + if let Some(pos) = end_bracket_pos { + let (_, right) = text.split_at(pos); + let format_part = FormatString::parse_part_in_brackets(&left)?; + Ok((format_part, &right[1..])) + } else { + Err(FormatParseError::UnmatchedBracket) + } + } + + fn format_internal(&self, args: &ValueRef, kwargs: &ValueRef) -> String { + let mut final_string = String::new(); + let mut auto_argument_index = 0; + for part in &self.format_parts { + let result_string = match part { + FormatPart::Field { + field_name, + format_spec, + } => { + let FieldName { + field_type, parts, .. + } = FieldName::parse(field_name.as_str()).unwrap(); + let mut argument = match field_type { + FieldType::Auto => { + auto_argument_index += 1; + args.arg_i(auto_argument_index - 1) + .expect("argument tuple index out of range") + } + FieldType::Index(index) => { + if auto_argument_index != 0 { + panic!("cannot switch from automatic field numbering to manual field specification"); + } + args.arg_i(index) + .expect("argument tuple index out of range") + } + FieldType::Keyword(keyword) => kwargs + .dict_get_value(keyword.as_str()) + .expect("keyword argument not found"), + }; + for name_part in parts { + match name_part { + // Load attr + FieldNamePart::Attribute(attr) => { + argument = args.dict_get_value(attr.as_str()).unwrap(); + } + // List subscript + FieldNamePart::Index(index) => { + argument = args.list_get(index as isize).unwrap(); + } + // Dict subscript + FieldNamePart::StringIndex(value) => { + argument = args.dict_get_value(value.as_str()).unwrap(); + } + } + } + argument.to_string_with_spec(format_spec) + } + FormatPart::Literal(literal) => literal.clone(), + }; + final_string.push_str(result_string.as_str()); + } + final_string + } + + pub(crate) fn format(&self, args: &ValueRef, kwargs: &ValueRef) -> String { + self.format_internal(args, kwargs) + } +} + +pub(crate) trait FromTemplate<'a>: Sized { + type Err; + fn from_str(s: &'a str) -> Result; +} + +impl<'a> FromTemplate<'a> for FormatString { + type Err = FormatParseError; + + fn from_str(text: &'a str) -> Result { + let mut cur_text: &str = text; + let mut parts: Vec = Vec::new(); + while !cur_text.is_empty() { + // Try to parse both literals and bracketed format parts util we + // run out of text + cur_text = FormatString::parse_literal(cur_text) + .or_else(|_| FormatString::parse_spec(cur_text)) + .map(|(part, new_text)| { + parts.push(part); + new_text + })?; + } + Ok(FormatString { + format_parts: parts, + }) + } +} + +/// Convert a runtime value to a quoted string e.g., abc -> 'abc' +pub fn value_to_quoted_string(value: &ValueRef) -> String { + if value.is_str() { + let value = value.as_str(); + quoted_string(&value) + } else { + value.to_string() + } +} + +/// Convert a Rust string to a quoted string e.g., abc -> 'abc' +pub fn quoted_string(value: &str) -> String { + let has_double_quote = value.contains('\''); + let has_single_quote = value.contains('\"'); + if !has_single_quote { + format!("'{}'", value) + } else if !has_double_quote { + format!("\"{}\"", value) + } else { + format!("\"{}\"", value.replace("\"", "\\\"")) + } +} + +impl ValueRef { + /// to_string_with_spec e.g., "{:.0f}".format(1.0) + pub fn to_string_with_spec(&self, spec: &str) -> String { + match &*self.rc { + Value::int_value(ref v) => { + match FormatSpec::parse(spec).and_then(|format_spec| format_spec.format_int(v)) { + Ok(string) => string, + Err(err) => panic!("{}", err), + } + } + Value::float_value(ref v) => { + match FormatSpec::parse(spec).and_then(|format_spec| format_spec.format_float(*v)) { + Ok(string) => string, + Err(err) => panic!("{}", err), + } + } + _ => self.to_string(), + } + } + + /// to_string e.g., "{}".format(1.0) + pub fn to_string(&self) -> String { + match &*self.rc { + Value::undefined => String::from("Undefined"), + Value::none => String::from("None"), + Value::bool_value(ref v) => { + if *v { + String::from("True") + } else { + String::from("False") + } + } + Value::int_value(ref v) => v.to_string(), + Value::float_value(ref v) => { + let mut float_str = v.to_string(); + if !float_str.contains('.') { + float_str.push_str(".0"); + } + float_str + } + Value::unit_value(_, raw, unit) => { + format!("{}{}", raw, unit) + } + Value::str_value(ref v) => v.clone(), + Value::list_value(ref v) => { + let values: Vec = v.values.iter().map(|v| v.to_string()).collect(); + format!("[{}]", values.join(", ")) + } + Value::dict_value(ref v) => { + let values: Vec = v + .values + .iter() + .map(|(k, v)| format!("{}: {}", quoted_string(k), value_to_quoted_string(v))) + .collect(); + format!("{{{}}}", values.join(", ")) + } + Value::schema_value(ref v) => { + let values: Vec = v + .config + .values + .iter() + .map(|(k, v)| format!("{}: {}", quoted_string(k), value_to_quoted_string(v))) + .collect(); + format!("{{{}}}", values.join(", ")) + } + Value::func_value(_) => String::from("function"), + } + } +} diff --git a/kclvm/runtime/src/value/val_from.rs b/kclvm/runtime/src/value/val_from.rs new file mode 100644 index 000000000..5bad2c568 --- /dev/null +++ b/kclvm/runtime/src/value/val_from.rs @@ -0,0 +1,259 @@ +// Copyright 2022 The KCL Authors. All rights reserved. + +use crate::*; +use std::convert::{From, TryFrom}; +use std::iter::FromIterator; +use std::rc::Rc; + +impl ValueRef { + pub fn into(&self) -> Self { + self.clone() + } +} + +// basic type + +macro_rules! define_value_from_trait { + ($from_type: ty, $kcl_type :ident) => { + impl From<$from_type> for ValueRef { + fn from(v: $from_type) -> Self { + ValueRef::$kcl_type(v) + } + } + }; +} + +macro_rules! define_value_try_from_trait { + ($for_type: ty, $kcl_type_value :ident) => { + impl TryFrom for $for_type { + type Error = (); + + fn try_from(v: ValueRef) -> Result { + match &*v.rc { + Value::$kcl_type_value(v) => Ok(v.clone()), + _ => Err(()), + } + } + } + impl TryFrom<&ValueRef> for $for_type { + type Error = (); + + fn try_from(v: &ValueRef) -> Result { + match &*v.rc { + Value::$kcl_type_value(v) => Ok(v.clone()), + _ => Err(()), + } + } + } + }; +} + +macro_rules! define_value_try_into_method { + ($try_into_type: ident, $type: ty) => { + impl ValueRef { + pub fn $try_into_type(&self) -> Result<$type, ()> { + use std::convert::TryInto; + self.try_into() + } + } + }; +} + +define_value_from_trait!(bool, bool); +define_value_from_trait!(i64, int); +define_value_from_trait!(f64, float); +define_value_from_trait!(&str, str); + +define_value_try_into_method!(try_into_bool, bool); +define_value_try_into_method!(try_into_int, i64); +define_value_try_into_method!(try_into_float, f64); +define_value_try_into_method!(try_into_str, String); + +define_value_try_from_trait!(bool, bool_value); +define_value_try_from_trait!(i64, int_value); +define_value_try_from_trait!(f64, float_value); +define_value_try_from_trait!(String, str_value); + +// value + +impl From for ValueRef { + fn from(v: Value) -> Self { + Self { rc: Rc::new(v) } + } +} + +// list + +macro_rules! define_value_list_from_iter_trait { + ($elem_type: ty) => { + impl FromIterator<$elem_type> for ValueRef { + fn from_iter>(iter: I) -> Self { + let mut list: ListValue = Default::default(); + for i in iter { + list.values.push(i.into()); + } + Self::from(Value::list_value(list)) + } + } + }; + ($_ref_: ident, $elem_type: ty) => { + impl<'a> FromIterator<&'a $elem_type> for ValueRef { + fn from_iter>(iter: I) -> Self { + let mut list: ListValue = Default::default(); + for i in iter { + list.values.push(i.into()); + } + Self::from(Value::list_value(list)) + } + } + }; +} + +define_value_list_from_iter_trait!(bool); +define_value_list_from_iter_trait!(i64); +define_value_list_from_iter_trait!(f64); + +define_value_list_from_iter_trait!(ref, str); +define_value_list_from_iter_trait!(ref, ValueRef); + +define_value_try_from_trait!(ListValue, list_value); + +define_value_try_into_method!(try_into_list, ListValue); + +// dict + +macro_rules! define_value_dict_from_iter_trait { + ($elem_type: ty) => { + impl<'a> FromIterator<(&'a str, $elem_type)> for ValueRef { + fn from_iter>(iter: I) -> Self { + let mut dict: DictValue = Default::default(); + for (k, v) in iter { + dict.values.insert(k.to_string(), v.into()); + } + Self::from(Value::dict_value(dict)) + } + } + }; + ($_ref_: ident, $elem_type: ty) => { + impl<'a> FromIterator<(&'a str, &'a $elem_type)> for ValueRef { + fn from_iter>(iter: I) -> Self { + let mut dict: DictValue = Default::default(); + for (k, v) in iter { + dict.values.insert(k.to_string(), v.into()); + } + Self::from(Value::dict_value(dict)) + } + } + }; +} + +define_value_dict_from_iter_trait!(bool); +define_value_dict_from_iter_trait!(i64); +define_value_dict_from_iter_trait!(f64); +define_value_dict_from_iter_trait!(ref, str); +define_value_dict_from_iter_trait!(ref, ValueRef); + +define_value_try_from_trait!(DictValue, dict_value); + +define_value_try_into_method!(try_into_dict, DictValue); + +// schema + +define_value_try_from_trait!(SchemaValue, schema_value); + +#[cfg(test)] +mod tests_from { + use super::*; + use std::convert::TryInto; + + #[test] + fn test_sample() { + use std::convert::From; + + assert_eq!(ValueRef::undefined(), ValueRef::from(UNDEFINED)); + assert_eq!(ValueRef::none(), ValueRef::from(NONE)); + assert_eq!(ValueRef::bool(true), ValueRef::from(TRUE)); + assert_eq!(ValueRef::bool(false), ValueRef::from(FALSE)); + + assert_eq!(true, ValueRef::from(true).try_into_bool().unwrap()); + assert_eq!(123, ValueRef::from(123).try_into_int().unwrap()); + assert_eq!(1.5, ValueRef::from(1.5).try_into_float().unwrap()); + assert_eq!("abc", ValueRef::from("abc").try_into_str().unwrap()); + + assert_eq!(true, bool::try_from(ValueRef::from(true)).unwrap()); + assert_eq!(123, i64::try_from(ValueRef::from(123)).unwrap()); + assert_eq!(1.5, f64::try_from(ValueRef::from(1.5)).unwrap()); + assert_eq!("abc", String::try_from(ValueRef::from("abc")).unwrap()); + } + + macro_rules! test_x_type { + ($test_fn_name: ident, $kcl_type :ident, $tests: expr) => { + #[test] + fn $test_fn_name() { + let tests = $tests; + for v in tests { + let expect = ValueRef::$kcl_type(v); + let got: ValueRef = v.into(); + assert_eq!(expect, got); + } + } + }; + } + + test_x_type!(test_bool, bool, vec![true, false]); + test_x_type!(test_int, int, vec![-1 as i64, 0, 1, 123, 0xFFFFFFFF + 1]); + test_x_type!( + test_float, + float, + vec![0.0 as f64, 1.5, 123.0, 0xFFFFFFFFi64 as f64 + 1.0] + ); + + test_x_type!(test_str, str, vec!["", "abc", "123"]); + + #[test] + fn test_list() { + let list = vec![true, false]; + + let list_value: ValueRef = ValueRef::from_iter(list.clone().into_iter()); + assert_eq!(list.len(), list_value.len()); + + for (i, v) in list.iter().enumerate() { + let x: bool = list_value.list_get(i as isize).unwrap().as_bool(); + assert_eq!(*v, x); + } + } + + fn test_list2() { + let list = vec![1, 2, 4, 3]; + + let list_value: ValueRef = ValueRef::from_iter(list.clone().into_iter()); + let list_value: ListValue = list_value.try_into().unwrap(); + + assert_eq!( + list_value + .values + .iter() + .map(|x| i64::try_from(x).unwrap()) + .collect::>(), + list, + ); + } + + macro_rules! test_try_into { + ($test_fn_name: ident, $type: ty, $tests: expr) => { + fn $test_fn_name() { + for v in $tests { + let v0 = v; + let v1: ValueRef = v0.into(); + let v2: $type = v1.try_into().unwrap(); + assert_eq!(v0, v2); + } + } + }; + } + + test_try_into!(test_try_into_bool, bool, vec![true, false, true, true]); + test_try_into!(test_try_into_i64, i64, vec![1, 2, 3, -1]); + test_try_into!(test_try_into_f64, f64, vec![1.5, 2.0]); + test_try_into!(test_try_into_str, String, vec!["", "abc"]); +} diff --git a/kclvm/runtime/src/value/val_get_set.rs b/kclvm/runtime/src/value/val_get_set.rs new file mode 100644 index 000000000..4c8da5e19 --- /dev/null +++ b/kclvm/runtime/src/value/val_get_set.rs @@ -0,0 +1,104 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl ValueRef { + pub fn get_by_key(&self, key: &str) -> Option<&Self> { + match &*self.rc { + Value::list_value(ref list) => match key.parse::() { + Ok(i) => list.values.as_slice().get(i), + Err(_) => None, + }, + Value::dict_value(ref dict) => dict.values.get(key), + Value::schema_value(ref schema) => schema.config.values.get(key), + _ => None, + } + } + + pub fn get_mut_by_key(&mut self, key: &str) -> Option<&mut Self> { + match &*self.rc { + Value::list_value(ref list) => match key.parse::() { + Ok(i) => { + let list: &mut ListValue = get_ref_mut(list); + return list.values.as_mut_slice().get_mut(i); + } + Err(_) => None, + }, + Value::dict_value(ref dict) => { + let dict: &mut DictValue = get_ref_mut(dict); + dict.values.get_mut(key) + } + Value::schema_value(ref schema) => { + let schema: &mut SchemaValue = get_ref_mut(schema); + let dict: &mut DictValue = get_ref_mut(schema.config.as_ref()); + dict.values.get_mut(key) + } + _ => None, + } + } + + pub fn get_by_path(&self, path: &str) -> Option<&Self> { + let mut val: &Self = self; + for key in path.split('.') { + match val.get_by_key(key) { + Some(x) => { + val = x; + } + None => { + return None; + } + } + } + Some(val) + } + + pub fn get_mut(&mut self, path: &str) -> Option<&Self> { + let mut val: &mut Self = self; + for key in path.split('.') { + match val.get_mut_by_key(key) { + Some(x) => { + val = x; + } + None => return None, + } + } + Some(val) + } +} + +#[cfg(test)] +mod test_value_get { + use crate::*; + + #[test] + fn test_get() { + let mut list_int = ValueRef::list_int(&vec![10 as i64, 20, 30]); + + let mut dict = ValueRef::dict(None); + dict.dict_insert("a", &ValueRef::str("a-value"), Default::default(), 0); + dict.dict_insert("b", &ValueRef::str("b-value"), Default::default(), 0); + + list_int.list_set(1, &dict); + list_int.list_set(2, &ValueRef::list_int(&vec![100 as i64, 200, 300])); + + assert_eq!(list_int.get_by_path("1.a").unwrap().as_str(), "a-value"); + + assert_eq!(list_int.get_by_path("2.2").unwrap().as_int(), 300); + + let dict = ValueRef::dict(Some(&vec![ + ("aaa", &ValueRef::int(111)), + ( + "bbb", + &ValueRef::list(Some(&vec![ + &ValueRef::str("a"), + &ValueRef::str("b"), + &ValueRef::dict(Some(&vec![("key0", &ValueRef::int(12345))])), + ])), + ), + ])); + + assert_eq!(dict.get_by_path("aaa").unwrap().as_int(), 111); + assert_eq!(dict.get_by_path("bbb.1").unwrap().as_str(), "b"); + assert_eq!(dict.get_by_path("bbb.2.key0").unwrap().as_int(), 12345); + } +} diff --git a/kclvm/runtime/src/value/val_is_in.rs b/kclvm/runtime/src/value/val_is_in.rs new file mode 100644 index 000000000..2bebdd95e --- /dev/null +++ b/kclvm/runtime/src/value/val_is_in.rs @@ -0,0 +1,186 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +// is + +impl ValueRef { + #[inline] + pub fn is_undefined(&self) -> bool { + matches!(&*self.rc, Value::undefined) + } + + #[inline] + pub fn is_none(&self) -> bool { + matches!(&*self.rc, Value::none) + } + + #[inline] + pub fn is_bool(&self) -> bool { + self.kind() == Kind::Bool + } + + #[inline] + pub fn is_int(&self) -> bool { + self.kind() == Kind::Int + } + + #[inline] + pub fn is_float(&self) -> bool { + self.kind() == Kind::Float + } + + #[inline] + pub fn is_str(&self) -> bool { + self.kind() == Kind::Str + } + + #[inline] + pub fn is_list(&self) -> bool { + self.kind() == Kind::List + } + + #[inline] + pub fn is_dict(&self) -> bool { + self.kind() == Kind::Dict + } + + #[inline] + pub fn is_schema(&self) -> bool { + self.kind() == Kind::Schema + } + + #[inline] + pub fn is_number(&self) -> bool { + matches!(&*self.rc, Value::int_value(_) | Value::float_value(_)) + } + + #[inline] + pub fn is_config(&self) -> bool { + matches!(&*self.rc, Value::schema_value(_) | Value::dict_value(_)) + } + + #[inline] + pub fn is_list_or_config(&self) -> bool { + matches!( + &*self.rc, + Value::list_value(_) | Value::schema_value(_) | Value::dict_value(_) + ) + } + + #[inline] + pub fn is_func(&self) -> bool { + self.kind() == Kind::Func + } + + #[inline] + pub fn is_none_or_undefined(&self) -> bool { + matches!(&*self.rc, Value::none | Value::undefined) + } + + #[inline] + pub fn is_unit(&self) -> bool { + matches!(&*self.rc, Value::unit_value(..)) + } +} + +// in + +impl ValueRef { + pub fn r#in(&self, x: &Self) -> bool { + match &*x.rc { + // "a" in "abc" + Value::str_value(ref b) => match &*self.rc { + Value::str_value(ref a) => b.contains(a), + _ => false, + }, + // x in [1, 2, 3] + Value::list_value(ref list) => { + for v in list.values.as_slice().iter() { + if self.cmp_equal(v) { + return true; + } + } + false + } + // k in {k:v} + Value::dict_value(ref dict) => { + let key = self.as_str(); + dict.values.contains_key(&key) + } + // k in schema{} + Value::schema_value(ref schema) => { + let key = self.as_str(); + schema.config.values.contains_key(&key) + } + _ => { + let msg = format!( + "TypeError: argument of type '{}' is not iterable", + x.type_str() + ); + panic!("{}", msg); + } + } + } + + pub fn not_in(&self, x: &Self) -> bool { + !self.r#in(x) + } +} + +impl ValueRef { + pub fn has_key(&self, key: &str) -> bool { + match &*self.rc { + Value::dict_value(ref dict) => dict.values.contains_key(key), + Value::schema_value(ref schema) => schema.config.values.contains_key(key), + _ => false, + } + } + + pub fn has_value(&self, x: &Self) -> bool { + x.r#in(self) + } +} + +#[cfg(test)] +mod test_value_in { + use crate::*; + + #[test] + fn test_in() { + assert_eq!(ValueRef::str("a").r#in(&ValueRef::str("abc")), true); + assert_eq!(ValueRef::str("ab").r#in(&ValueRef::str("abc")), true); + assert_eq!(ValueRef::str("abcd").r#in(&ValueRef::str("abc")), false); + + assert_eq!( + ValueRef::str("a").r#in(&ValueRef::list_str(&[ + "a".to_string(), + "b".to_string(), + "c".to_string() + ])), + true + ); + assert_eq!( + ValueRef::str("d").r#in(&ValueRef::list_str(&[ + "a".to_string(), + "b".to_string(), + "c".to_string() + ])), + false + ); + assert_eq!( + ValueRef::str("key1").r#in(&ValueRef::dict_str(&[ + ("key1", "value1"), + ("key2", "value1"), + ])), + true + ); + assert_eq!( + ValueRef::str("err_key").r#in(&ValueRef::dict_str(&[ + ("key1", "value1"), + ("key2", "value1"), + ])), + false + ); + } +} diff --git a/kclvm/runtime/src/value/val_json.rs b/kclvm/runtime/src/value/val_json.rs new file mode 100644 index 000000000..10f77ea15 --- /dev/null +++ b/kclvm/runtime/src/value/val_json.rs @@ -0,0 +1,267 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; +use json_minimal::*; + +#[derive(Debug, Default)] +pub struct JsonEncodeOptions { + pub sort_keys: bool, + pub indent: i64, + pub ignore_private: bool, + pub ignore_none: bool, +} + +impl ValueRef { + pub fn from_json(s: &str) -> Option { + match Json::parse_with_option( + s.as_bytes(), + &json_minimal::ParseOption { support_int: true }, + ) { + Ok(json) => Some(Self::parse_json(&json)), + _ => None, + } + } + fn parse_json(json: &Json) -> Self { + match json { + Json::OBJECT { name, value } => { + let _ = name; + let _ = value; + panic!("unreachable"); + } + Json::JSON(values) => { + let mut dict = Self::dict(None); + for value in values { + match value { + Json::OBJECT { name, value } => dict.dict_insert( + name.as_ref(), + &Self::parse_json(value), + ConfigEntryOperationKind::Union, + 0, + ), + _ => panic!("unreachable"), + } + } + dict + } + Json::ARRAY(values) => { + let mut list = Self::list(None); + for value in values { + list.list_append(&Self::parse_json(value)); + } + list + } + Json::STRING(val) => Self::str((*val).as_ref()), + Json::NUMBER(val) => Self::float(*val), + Json::INT(val) => Self::int(*val), + Json::FLOAT(val) => Self::float(*val), + Json::BOOL(val) => Self::bool(*val), + Json::NULL => Self::none(), + } + } + + pub fn to_json(&self) -> Vec { + let json = self.build_json(&Default::default()); + + let opt = json_minimal::PrintOption { + sep_space: true, + py_style_f64: true, + ..Default::default() + }; + + json.print_with_option(&opt).into_bytes() + } + + pub fn to_json_string(&self) -> String { + let json = self.build_json(&Default::default()); + + let opt = json_minimal::PrintOption { + sep_space: true, + py_style_f64: true, + ..Default::default() + }; + + json.print_with_option(&opt) + } + + pub fn to_json_string_with_option(&self, opt: &JsonEncodeOptions) -> String { + let json = self.build_json(opt); + + let opt = json_minimal::PrintOption { + sort_keys: opt.sort_keys, + indent: opt.indent as i32, + sep_space: true, + py_style_f64: true, + ..Default::default() + }; + + json.print_with_option(&opt) + } + + pub fn to_json_string_with_null(&self) -> String { + let json = self.build_json(&Default::default()); + + let opt = json_minimal::PrintOption { + sep_space: true, + py_style_f64: true, + append_null: true, + ..Default::default() + }; + + json.print_with_option(&opt) + } + + fn build_json(&self, opt: &JsonEncodeOptions) -> Json { + match &*self.rc { + Value::undefined => Json::NULL, + Value::none => Json::NULL, + + Value::bool_value(ref v) => Json::BOOL(*v), + Value::int_value(ref v) => Json::INT(*v), + Value::float_value(ref v) => Json::FLOAT(*v), + Value::unit_value(..) => Json::STRING(self.to_string()), + Value::str_value(ref v) => Json::STRING(v.clone()), + + Value::list_value(ref v) => { + let mut list = Json::ARRAY(Vec::new()); + for x in v.values.iter() { + match *x.rc { + Value::undefined => { + continue; + } + Value::none => { + if !opt.ignore_none { + list.add(x.build_json(opt)); + } + } + Value::func_value(_) => { + // ignore func + } + _ => { + list.add(x.build_json(opt)); + } + } + } + list + } + Value::dict_value(ref v) => { + let mut json = Json::new(); + for (key, val) in v.values.iter() { + if opt.ignore_private && (*key).starts_with(KCL_PRIVATE_VAR_PREFIX) { + continue; + } + match *val.rc { + Value::undefined => { + continue; + } + Value::none => { + if !opt.ignore_none { + json.add(Json::OBJECT { + name: key.clone(), + value: Box::new(val.build_json(opt)), + }); + } + } + Value::func_value(_) => { + // ignore func + } + _ => { + json.add(Json::OBJECT { + name: key.clone(), + value: Box::new(val.build_json(opt)), + }); + } + } + } + json + } + + Value::schema_value(ref v) => { + let mut json = Json::new(); + for (key, val) in v.config.values.iter() { + if opt.ignore_private && (*key).starts_with(KCL_PRIVATE_VAR_PREFIX) { + continue; + } + match *val.rc { + Value::undefined => { + continue; + } + Value::none => { + if !opt.ignore_none { + json.add(Json::OBJECT { + name: key.clone(), + value: Box::new(val.build_json(opt)), + }); + } + } + Value::func_value(_) => { + // ignore func + } + _ => { + json.add(Json::OBJECT { + name: key.clone(), + value: Box::new(val.build_json(opt)), + }); + } + } + } + json + } + Value::func_value(ref v) => Json::NUMBER(v.fn_ptr as f64), + } + } +} + +#[cfg(test)] +mod test_value_json { + use crate::*; + + #[test] + fn test_value_from_json() { + let cases = [ + ( + "{\"a\": 1}\n", + ValueRef::dict(Some(&[("a", &ValueRef::int(1))])), + ), + ( + "{\"a\": 1,\n\"b\": 2}\n", + ValueRef::dict(Some(&[("a", &ValueRef::int(1)), ("b", &ValueRef::int(2))])), + ), + ( + "{\"a\": [1, 2, 3],\n\"b\": \"s\"}\n", + ValueRef::dict(Some(&[ + ("a", &ValueRef::list_int(&[1, 2, 3])), + ("b", &ValueRef::str("s")), + ])), + ), + ]; + for (json_str, expected) in cases { + let result = ValueRef::from_json(json_str); + assert_eq!(result, Some(expected)); + } + } + + #[test] + fn test_value_to_json_string() { + let cases = [ + ( + ValueRef::dict(Some(&[("a", &ValueRef::int(1))])), + "{\"a\": 1}", + ), + ( + ValueRef::dict(Some(&[("a", &ValueRef::int(1)), ("b", &ValueRef::int(2))])), + "{\"a\": 1, \"b\": 2}", + ), + ( + ValueRef::dict(Some(&[ + ("a", &ValueRef::list_int(&[1, 2, 3])), + ("b", &ValueRef::str("s")), + ])), + "{\"a\": [1, 2, 3], \"b\": \"s\"}", + ), + ]; + for (value, expected) in cases { + let result = ValueRef::to_json_string(&value); + assert_eq!(result, expected); + } + } +} diff --git a/kclvm/runtime/src/value/val_kind.rs b/kclvm/runtime/src/value/val_kind.rs new file mode 100644 index 000000000..73ce8fb94 --- /dev/null +++ b/kclvm/runtime/src/value/val_kind.rs @@ -0,0 +1,22 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +// common +impl ValueRef { + pub fn kind(&self) -> Kind { + match *self.rc { + Value::undefined => Kind::Undefined, + Value::none => Kind::None, + Value::bool_value(_) => Kind::Bool, + Value::int_value(_) => Kind::Int, + Value::float_value(_) => Kind::Float, + Value::str_value(_) => Kind::Str, + Value::list_value(_) => Kind::List, + Value::dict_value(_) => Kind::Dict, + Value::schema_value(_) => Kind::Schema, + Value::func_value(_) => Kind::Func, + Value::unit_value(..) => Kind::Unit, + } + } +} diff --git a/kclvm/runtime/src/value/val_len.rs b/kclvm/runtime/src/value/val_len.rs new file mode 100644 index 000000000..047c3e420 --- /dev/null +++ b/kclvm/runtime/src/value/val_len.rs @@ -0,0 +1,55 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl ValueRef { + pub fn len(&self) -> usize { + match *self.rc { + Value::str_value(ref s) => s.len(), + Value::list_value(ref v) => v.values.len(), + Value::dict_value(ref v) => v.values.len(), + Value::schema_value(ref v) => v.config.values.len(), + _ => panic!("object of type '{}' has no len()", self.type_str()), + } + } +} + +#[cfg(test)] +mod test_value_len { + use crate::*; + + fn assert_panic () + std::panic::UnwindSafe>(func: F) { + let result = std::panic::catch_unwind(func); + assert!(result.is_err()) + } + + #[test] + fn test_len() { + assert_eq!(ValueRef::str("abc").len(), 3); + assert_eq!( + ValueRef::str("abc").bin_aug_mul(&ValueRef::int(10)).len(), + 3 * 10 + ); + assert_eq!(ValueRef::list_n(10, &ValueRef::undefined()).len(), 10); + assert_eq!(ValueRef::list_int(&vec![1 as i64, 2, 3]).len(), 3); + } + + #[test] + fn test_len_invalid() { + assert_panic(|| { + ValueRef::undefined().len(); + }); + assert_panic(|| { + ValueRef::none().len(); + }); + assert_panic(|| { + ValueRef::bool(false).len(); + }); + assert_panic(|| { + ValueRef::int(1).len(); + }); + assert_panic(|| { + ValueRef::float(2.0).len(); + }); + } +} diff --git a/kclvm/runtime/src/value/val_list.rs b/kclvm/runtime/src/value/val_list.rs new file mode 100644 index 000000000..6fc5fa23a --- /dev/null +++ b/kclvm/runtime/src/value/val_list.rs @@ -0,0 +1,612 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl ValueRef { + pub fn list_n(size: usize, val: &Self) -> Self { + let mut list = ListValue::default(); + for _i in 0..size { + list.values.push(val.clone()); + } + Self::from(Value::list_value(list)) + } + + pub fn list_bool(x: &[bool]) -> Self { + let mut list = ListValue::default(); + for x in x.iter() { + list.values.push(Self::bool(*x)); + } + Self::from(Value::list_value(list)) + } + + pub fn list_int(x: &[i64]) -> Self { + let mut list = ListValue::default(); + for x in x.iter() { + list.values.push(Self::int(*x)); + } + Self::from(Value::list_value(list)) + } + + pub fn list_float(x: &[f64]) -> Self { + let mut list = ListValue::default(); + for x in x.iter() { + list.values.push(Self::float(*x)); + } + Self::from(Value::list_value(list)) + } + + pub fn list_str(x: &[String]) -> Self { + let mut list = ListValue::default(); + for x in x.iter() { + list.values.push(Self::str((*x).as_ref())); + } + Self::from(Value::list_value(list)) + } + + pub fn list_resize(&mut self, newsize: usize) { + match &*self.rc { + Value::list_value(ref list) => { + let list: &mut ListValue = get_ref_mut(list); + if list.values.len() > newsize { + list.values.truncate(newsize); + } else { + while list.values.len() < newsize { + list.values.push(Default::default()); + } + } + } + _ => panic!("Invalid list object in list_resize"), + } + } + + pub fn list_clear(&mut self) { + match &*self.rc { + Value::list_value(ref list) => { + let list: &mut ListValue = get_ref_mut(list); + list.values.clear(); + } + _ => panic!("Invalid list object in list_clear"), + } + } + + pub fn list_get(&self, i: isize) -> Option<&Self> { + match &*self.rc { + Value::list_value(ref list) => { + let index = if i < 0 { + (i + list.values.len() as isize) as usize + } else { + i as usize + }; + if !list.values.is_empty() { + Some(&list.values.as_slice()[index]) + } else { + None + } + } + _ => panic!("Invalid list object in list_get"), + } + } + + pub fn list_get_option(&self, i: isize) -> Option<&Self> { + match &*self.rc { + Value::list_value(ref list) => { + let index = if i < 0 { + (i + list.values.len() as isize) as usize + } else { + i as usize + }; + if !list.values.is_empty() && index < list.values.len() { + Some(&list.values.as_slice()[index]) + } else { + None + } + } + _ => panic!("Invalid list object in list_get_option"), + } + } + + pub fn list_set(&mut self, i: usize, v: &Self) { + match &*self.rc { + Value::list_value(ref list) => { + let list: &mut ListValue = get_ref_mut(list); + if i < list.values.len() { + list.values.as_mut_slice()[i] = v.clone(); + } + } + _ => panic!("Invalid list object in list_set"), + } + } + + pub fn list_pop(&mut self) -> Option<&Self> { + match &*self.rc { + Value::list_value(ref list) => { + let list: &mut ListValue = get_ref_mut(list); + let last_value = self.list_get((list.values.len() - 1) as isize); + list.values.pop(); + last_value + } + _ => panic!("Invalid list object in list_pop"), + } + } + + pub fn list_pop_first(&mut self) -> Option { + match &*self.rc { + Value::list_value(ref list) => { + let list: &mut ListValue = get_ref_mut(list); + if !list.values.is_empty() { + Some(list.values.remove(0)) + } else { + None + } + } + _ => panic!("Invalid list object in list_pop"), + } + } + + pub fn list_append(&mut self, v: &Self) { + match &*self.rc { + Value::list_value(ref list) => { + let list: &mut ListValue = get_ref_mut(list); + list.values.push(v.clone()); + } + _ => panic!( + "Invalid list object in list_append {} {:?}", + self.to_json_string(), + v + ), + } + } + + pub fn list_append_unpack(&mut self, x_or_list: &Self) { + match &*self.rc { + Value::list_value(ref list) => match &*x_or_list.rc { + Value::list_value(ref list_b) => { + let list: &mut ListValue = get_ref_mut(list); + for x in list_b.values.iter() { + list.values.push(x.clone()); + } + } + Value::dict_value(ref dict_b) => { + let list: &mut ListValue = get_ref_mut(list); + for (x, _) in dict_b.values.iter() { + list.values.push(Self::str(x.as_str())); + } + } + Value::schema_value(ref schema_b) => { + let list: &mut ListValue = get_ref_mut(list); + for (x, _) in schema_b.config.values.iter() { + list.values.push(Self::str(x.as_str())); + } + } + Value::none | Value::undefined => { /*Do nothing on unpacking None/Undefined*/ } + _ => panic!("only list, dict and schema object can be used with unpack operators * and **, got {}", x_or_list.to_string()), + }, + _ => panic!("Invalid list object in list_append_unpack"), + } + } + + pub fn list_append_unpack_first(&mut self, x_or_list: &Self) { + match &*self.rc { + Value::list_value(ref list) => match &*x_or_list.rc { + Value::list_value(ref list_b) => { + let list: &mut ListValue = get_ref_mut(list); + for (i, x) in list_b.values.iter().enumerate() { + list.values.insert(i, x.clone()); + } + } + Value::dict_value(ref dict_b) => { + let list: &mut ListValue = get_ref_mut(list); + for (i, x) in dict_b.values.iter().enumerate() { + list.values.insert(i, Self::str(x.0.as_str())); + } + } + Value::schema_value(ref schema_b) => { + let list: &mut ListValue = get_ref_mut(list); + for (i, x) in schema_b.config.values.iter().enumerate() { + list.values.insert(i, Self::str(x.0.as_str())); + } + } + Value::none | Value::undefined => { /*Do nothing on unpacking None/Undefined*/ } + _ => { + let list: &mut ListValue = get_ref_mut(list); + + // Panic + list.values.insert(0, x_or_list.clone()); + } + }, + _ => panic!("Invalid list object in list_append_unpack_first"), + } + } + + pub fn list_count(&self, item: &Self) -> usize { + let mut count: usize = 0; + match &*self.rc { + Value::list_value(ref list) => { + for v in &list.values { + if v == item { + count += 1; + } + } + } + _ => panic!("Invalid list object in list_find"), + } + return count; + } + + pub fn list_find(&self, item: &Self) -> isize { + match &*self.rc { + Value::list_value(ref list) => { + for (i, v) in list.values.iter().enumerate() { + if v == item { + return i as isize; + } + } + } + _ => panic!("Invalid list object in list_find"), + } + return -1; + } + + pub fn list_insert_at(&mut self, i: usize, v: &Self) { + match &*self.rc { + Value::list_value(ref list) => { + let list: &mut ListValue = get_ref_mut(list); + list.values.insert(i, v.clone()); + } + _ => panic!("Invalid list object in list_insert_at"), + } + } + + pub fn list_remove_at(&mut self, i: usize) { + match &*self.rc { + Value::list_value(ref list) => { + let list: &mut ListValue = get_ref_mut(list); + list.values.remove(i); + } + _ => panic!("Invalid list object in list_remove_at"), + } + } + + pub fn list_remove(&mut self, item: &ValueRef) { + match &*self.rc { + Value::list_value(ref list) => { + let list: &mut ListValue = get_ref_mut(list); + let mut index: Option = None; + for (i, v) in list.values.iter().enumerate() { + if v == item { + index = Some(i); + } + } + if let Some(index) = index { + list.values.remove(index); + } + } + _ => panic!("Invalid list object in list_remove_at"), + } + } + + pub fn slice_unpack(start: &ValueRef, stop: &ValueRef, step: &ValueRef) -> (i64, i64, i64) { + let start_val; + let step_val; + let stop_val; + match &*step.rc { + Value::int_value(ref step) => { + step_val = *step; + if step_val == 0 { + panic!("slice step cannot be zero"); + } + } + _ => { + step_val = 1; + } + } + match &*start.rc { + Value::int_value(ref start) => start_val = *start, + _ => { + if step_val < 0 { + start_val = i64::MAX; + } else { + start_val = 0; + } + } + } + match &*stop.rc { + Value::int_value(ref stop) => stop_val = *stop, + _ => { + if step_val < 0 { + stop_val = i64::MIN; + } else { + stop_val = i64::MAX; + } + } + } + (start_val, stop_val, step_val) + } + + pub fn slice_adjust_indices( + len: i64, + mut start: i64, + mut stop: i64, + step: i64, + ) -> (i64, i64, i64) { + assert!(step != 0); + if start < 0 { + start += len; + if start < 0 { + if step < 0 { + start = -1; + } else { + start = 0; + } + } + } else if start >= len { + if step < 0 { + start = len - 1; + } else { + start = len; + } + } + + if stop < 0 { + stop += len; + if stop < 0 { + if step < 0 { + stop = -1; + } else { + stop = 0; + } + } + } else if stop >= len { + if step < 0 { + stop = len - 1; + } else { + stop = len; + } + } + let mut slice_len = 0; + + if step < 0 { + if stop < start { + slice_len = (start - stop - 1) / (-step) + 1; + } + } else if start < stop { + slice_len = (stop - start - 1) / step + 1; + } + + (start, stop, slice_len) + } + + pub fn list_slice(&self, start: &ValueRef, stop: &ValueRef, step: &ValueRef) -> ValueRef { + match &*self.rc { + Value::list_value(ref list) => { + let (start, stop, step) = ValueRef::slice_unpack(start, stop, step); + let (start, _stop, slice_len) = + ValueRef::slice_adjust_indices(list.values.len() as i64, start, stop, step); + let mut slice = ValueRef::list(None); + let mut cur = start; + for _i in 1..(slice_len + 1) { + slice.list_append(&list.values.as_slice()[cur as usize].clone()); + cur += step; + } + slice + } + Value::str_value(ref str) => { + let (start, stop, step) = ValueRef::slice_unpack(start, stop, step); + let (start, _stop, slice_len) = + ValueRef::slice_adjust_indices(str.chars().count() as i64, start, stop, step); + let mut slice = String::new(); + let mut cur = start; + for _i in 1..(slice_len + 1) { + let char = str.chars().nth(cur as usize).unwrap(); + slice.push_str(&char.to_string()); + cur += step; + } + ValueRef::str(&slice) + } + _ => panic!("invalid slice object {}", self.type_str()), + } + } +} + +#[cfg(test)] +mod test_value_list { + + use crate::*; + + #[test] + fn test_slice_unpack() { + let cases = [ + (1, 1, 1, (1, 1, 1)), + (1, 5, 1, (1, 5, 1)), + (5, 1, -1, (5, 1, -1)), + (-1, -5, -1, (-1, -5, -1)), + ]; + for (start, stop, step, expected) in cases { + let start = ValueRef::int(start); + let stop = ValueRef::int(stop); + let step = ValueRef::int(step); + let result = ValueRef::slice_unpack(&start, &stop, &step); + assert_eq!(result, expected); + } + } + + #[test] + fn test_slice_adjust_indices() { + let cases = [ + (1, 1, 1, 1, (1, 1, 0)), + (3, 1, 2, 1, (1, 2, 1)), + (5, 1, 3, 2, (1, 3, 1)), + (5, 1, -1, 1, (1, 4, 3)), + (5, -1, -3, -1, (4, 2, 2)), + ]; + for (len, start, stop, step, expected) in cases { + assert_eq!( + ValueRef::slice_adjust_indices(len, start, stop, step), + expected + ) + } + } + + #[test] + fn test_list_slice() { + let mut list = ValueRef::list(None); + for i in 1..11 { + list.list_append(&ValueRef::int(i)); + } + + /* + slice=list[1:9:2] + expect_slice=[2,4,6,8] + */ + let mut slice = list.list_slice(&ValueRef::int(1), &ValueRef::int(9), &ValueRef::int(2)); + let mut expect_slice = ValueRef::list(Some(&[ + &ValueRef::int(2), + &ValueRef::int(4), + &ValueRef::int(6), + &ValueRef::int(8), + ])); + assert!(expect_slice.cmp_equal(&slice)); + + /* + slice=list[1:5] + expect_slice=[2,3,4,5] + */ + slice = list.list_slice(&ValueRef::int(1), &ValueRef::int(5), &ValueRef::undefined()); + expect_slice = ValueRef::list(Some(&[ + &ValueRef::int(2), + &ValueRef::int(3), + &ValueRef::int(4), + &ValueRef::int(5), + ])); + assert!(expect_slice.cmp_equal(&slice)); + + /* + slice=list[:3] + expect_slice=[1,2,3] + */ + slice = list.list_slice( + &ValueRef::undefined(), + &ValueRef::int(3), + &ValueRef::undefined(), + ); + expect_slice = ValueRef::list(Some(&[ + &ValueRef::int(1), + &ValueRef::int(2), + &ValueRef::int(3), + ])); + assert!(expect_slice.cmp_equal(&slice)); + + /* + slice=list[7:] + expect_slice=[8,9,10] + */ + slice = list.list_slice( + &ValueRef::int(7), + &ValueRef::undefined(), + &ValueRef::undefined(), + ); + expect_slice = ValueRef::list(Some(&[ + &ValueRef::int(8), + &ValueRef::int(9), + &ValueRef::int(10), + ])); + assert!(expect_slice.cmp_equal(&slice)); + + /* + slice=list[7::-2] + expect_slice=[8,6,4,2] + */ + slice = list.list_slice( + &ValueRef::int(7), + &ValueRef::undefined(), + &ValueRef::int(-2), + ); + expect_slice = ValueRef::list(Some(&[ + &ValueRef::int(8), + &ValueRef::int(6), + &ValueRef::int(4), + &ValueRef::int(2), + ])); + assert!(expect_slice.cmp_equal(&slice)); + + /* + slice=list[::-2] + expect_slice=[10,8,6,4,2] + */ + slice = list.list_slice( + &ValueRef::undefined(), + &ValueRef::undefined(), + &ValueRef::int(-2), + ); + expect_slice = ValueRef::list(Some(&[ + &ValueRef::int(10), + &ValueRef::int(8), + &ValueRef::int(6), + &ValueRef::int(4), + &ValueRef::int(2), + ])); + assert!(expect_slice.cmp_equal(&slice)); + + /* + slice=list[-8:-3:2] + expect_slice=[3,5,7] + */ + slice = list.list_slice(&ValueRef::int(-8), &ValueRef::int(-3), &ValueRef::int(2)); + expect_slice = ValueRef::list(Some(&[ + &ValueRef::int(3), + &ValueRef::int(5), + &ValueRef::int(7), + ])); + assert!(expect_slice.cmp_equal(&slice)); + + /* + slice=list[-3:-8:-2] + expect_slice=[3,5,7] + */ + slice = list.list_slice(&ValueRef::int(-8), &ValueRef::int(-3), &ValueRef::int(2)); + expect_slice = ValueRef::list(Some(&[ + &ValueRef::int(3), + &ValueRef::int(5), + &ValueRef::int(7), + ])); + assert!(expect_slice.cmp_equal(&slice)); + + /* + slice=list[-100:200] + expect_slice=list + */ + slice = list.list_slice( + &ValueRef::int(-100), + &ValueRef::int(200), + &ValueRef::undefined(), + ); + + assert!(list.cmp_equal(&slice)); + + /* + slice=list[200:-100:-2] + expect_slice=[10,8,6,4,2] + */ + slice = list.list_slice( + &ValueRef::int(200), + &ValueRef::int(-100), + &ValueRef::int(-2), + ); + expect_slice = ValueRef::list(Some(&[ + &ValueRef::int(10), + &ValueRef::int(8), + &ValueRef::int(6), + &ValueRef::int(4), + &ValueRef::int(2), + ])); + assert!(expect_slice.cmp_equal(&slice)); + + /* + slice=list[8:2] + expect_slice=[] + */ + slice = list.list_slice(&ValueRef::int(8), &ValueRef::int(2), &ValueRef::undefined()); + expect_slice = ValueRef::list(None); + assert!(expect_slice.cmp_equal(&slice)); + } +} diff --git a/kclvm/runtime/src/value/val_logic.rs b/kclvm/runtime/src/value/val_logic.rs new file mode 100644 index 000000000..9c3f87717 --- /dev/null +++ b/kclvm/runtime/src/value/val_logic.rs @@ -0,0 +1,95 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl ValueRef { + #[inline] + pub fn is_truthy(&self) -> bool { + match *self.rc { + Value::undefined => false, + Value::none => false, + Value::bool_value(ref v) => *v, + Value::int_value(ref v) => *v != 0, + Value::float_value(ref v) => *v != 0.0, + Value::str_value(ref v) => !v.is_empty(), + Value::list_value(ref v) => !v.values.is_empty(), + Value::dict_value(ref v) => !v.values.is_empty(), + Value::schema_value(ref v) => !v.config.values.is_empty(), + Value::func_value(_) => true, + Value::unit_value(ref v, _, _) => *v != 0.0, + } + } + + #[inline] + pub fn logic_and(&self, x: &ValueRef) -> bool { + self.is_truthy() && x.is_truthy() + } + + #[inline] + pub fn logic_or(&self, x: &ValueRef) -> bool { + self.is_truthy() || x.is_truthy() + } +} + +#[cfg(test)] +mod test_value_logic { + use crate::*; + + #[test] + fn test_is_truthy() { + let cases = [ + // false cases + (ValueRef::int(1), true), + (ValueRef::float(2.0f64), true), + (ValueRef::bool(true), true), + (ValueRef::str("s"), true), + (ValueRef::list_int(&[0]), true), + (ValueRef::dict_str(&[("key", "value")]), true), + // true cases + (ValueRef::undefined(), false), + (ValueRef::none(), false), + (ValueRef::int(0), false), + (ValueRef::float(0.0f64), false), + (ValueRef::bool(false), false), + (ValueRef::str(""), false), + (ValueRef::list(Some(&[])), false), + (ValueRef::dict(Some(&[])), false), + ]; + for (value, expected) in cases { + let result = value.is_truthy(); + assert_eq!(result, expected); + } + } + + #[test] + fn test_logic_and() { + let cases = [ + // true cases + (ValueRef::int(1), ValueRef::int(1), true), + // false cases + (ValueRef::int(0), ValueRef::int(0), false), + (ValueRef::int(0), ValueRef::int(1), false), + (ValueRef::int(1), ValueRef::int(0), false), + ]; + for (left, right, expected) in cases { + let result = left.logic_and(&right); + assert_eq!(result, expected); + } + } + + #[test] + fn test_logic_or() { + let cases = [ + // true cases + (ValueRef::int(1), ValueRef::int(1), true), + (ValueRef::int(0), ValueRef::int(1), true), + (ValueRef::int(1), ValueRef::int(0), true), + // false cases + (ValueRef::int(0), ValueRef::int(0), false), + ]; + for (left, right, expected) in cases { + let result = left.logic_or(&right); + assert_eq!(result, expected); + } + } +} diff --git a/kclvm/runtime/src/value/val_overflow.rs b/kclvm/runtime/src/value/val_overflow.rs new file mode 100644 index 000000000..bb391f122 --- /dev/null +++ b/kclvm/runtime/src/value/val_overflow.rs @@ -0,0 +1,88 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub fn is_i32_overflow(v: i64) -> bool { + v > i32::MAX as i64 || v < i32::MIN as i64 +} +pub fn is_i32_overflow2(v1: i64, v2: i64) -> bool { + is_i32_overflow(v1) || is_i32_overflow(v2) +} + +pub fn is_f32_overflow(v: f64) -> bool { + v > f32::MAX as f64 || v < f32::MIN as f64 +} +pub fn is_f32_overflow2(v1: f64, v2: f64) -> bool { + is_f32_overflow(v1) || is_f32_overflow(v2) +} + +pub fn is_i32_overflow_add(v1: i64, v2: i64) -> bool { + is_i32_overflow2(v1, v2) || (v1 as i32).checked_add(v2 as i32).is_none() +} +pub fn is_i32_overflow_sub(v1: i64, v2: i64) -> bool { + is_i32_overflow2(v1, v2) || (v1 as i32).checked_sub(v2 as i32).is_none() +} +pub fn is_i32_overflow_mul(v1: i64, v2: i64) -> bool { + is_i32_overflow2(v1, v2) || (v1 as i32).checked_mul(v2 as i32).is_none() +} +pub fn is_i32_overflow_div(v1: i64, v2: i64) -> bool { + is_i32_overflow2(v1, v2) || (v1 as i32).checked_div(v2 as i32).is_none() +} +pub fn is_i32_overflow_pow(v1: i64, v2: i64) -> bool { + is_i32_overflow2(v1, v2) || (v1 as i32).checked_pow(v2 as u32).is_none() +} +pub fn is_i32_overflow_shl(v1: i64, v2: i64) -> bool { + is_i32_overflow2(v1, v2) || (v1 as i32).checked_shl(v2 as u32).is_none() +} +pub fn is_i32_overflow_shr(v1: i64, v2: i64) -> bool { + is_i32_overflow2(v1, v2) || (v1 as i32).checked_shr(v2 as u32).is_none() +} + +pub fn is_i64_overflow_add(v1: i64, v2: i64) -> bool { + v1.checked_add(v2).is_none() +} +pub fn is_i64_overflow_sub(v1: i64, v2: i64) -> bool { + v1.checked_sub(v2).is_none() +} +pub fn is_i64_overflow_mul(v1: i64, v2: i64) -> bool { + v1.checked_mul(v2).is_none() +} +pub fn is_i64_overflow_div(v1: i64, v2: i64) -> bool { + v1.checked_div(v2).is_none() +} +pub fn is_i64_overflow_pow(v1: i64, v2: i64) -> bool { + is_i32_overflow(v2) || v1.checked_pow(v2 as u32).is_none() +} +pub fn is_i64_overflow_shl(v1: i64, v2: i64) -> bool { + is_i32_overflow(v2) || v1.checked_shl(v2 as u32).is_none() +} +pub fn is_i64_overflow_shr(v1: i64, v2: i64) -> bool { + is_i32_overflow(v2) || v1.checked_shr(v2 as u32).is_none() +} + +pub fn is_f32_overflow_add(v1: f64, v2: f64) -> bool { + is_f32_overflow2(v1, v2) || is_f32_overflow(v1 + v2) +} +pub fn is_f32_overflow_sub(v1: f64, v2: f64) -> bool { + is_f32_overflow2(v1, v2) || is_f32_overflow(v1 - v2) +} +pub fn is_f32_overflow_mul(v1: f64, v2: f64) -> bool { + is_f32_overflow2(v1, v2) || is_f32_overflow(v1 * v2) +} +pub fn is_f32_overflow_div(v1: f64, v2: f64) -> bool { + is_f32_overflow2(v1, v2) || is_f32_overflow(v1 / v2) +} +pub fn is_f32_overflow_pow(v1: f64, v2: f64) -> bool { + is_f32_overflow2(v1, v2) || is_i32_overflow(v2 as i64) || is_f32_overflow(v1.powf(v2)) +} + +#[cfg(test)] +mod test_is_int_overflow { + use crate::*; + + #[test] + fn test_is_i32_overflow() { + assert_eq!(is_i32_overflow(2147483647), false); + assert_eq!(is_i32_overflow(2147483647 + 1), true); + + assert!(is_i32_overflow2(i32::MAX as i64 + 1, i32::MAX as i64 + 2)); + } +} diff --git a/kclvm/runtime/src/value/val_panic.rs b/kclvm/runtime/src/value/val_panic.rs new file mode 100644 index 000000000..468a57aba --- /dev/null +++ b/kclvm/runtime/src/value/val_panic.rs @@ -0,0 +1,46 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +#[macro_export] +macro_rules! panic_i32_overflow { + ($v: expr) => { + let v = $v as i128; + let ctx = crate::Context::current_context_mut(); + ctx.set_err_type(&ErrType::IntOverflow_TYPE); + panic!("{}: A 32 bit integer overflow", v) + }; +} + +#[macro_export] +macro_rules! panic_i64_overflow { + ($v: expr) => { + let v = $v as i128; + let ctx = crate::Context::current_context_mut(); + ctx.set_err_type(&ErrType::IntOverflow_TYPE); + panic!("{}: A 64 bit integer overflow", v) + }; +} +#[macro_export] +macro_rules! panic_f32_overflow { + ($v: expr) => { + let v = $v as f64; + + let ctx = crate::Context::current_context_mut(); + ctx.set_err_type(&ErrType::FloatOverflow_TYPE); + + let mut s = format!("{:e}: A 32-bit floating point number overflow", v); + if !s.contains("e-") { + s = s.replacen("e", "e+", 1); + } + panic!("{}", s) + }; +} + +#[macro_export] +macro_rules! panic_unsupported_bin_op { + ($op:expr, $left_type: expr, $right_type: expr) => { + panic!( + "unsupported operand type(s) for {}: '{}' and '{}'", + $op, $left_type, $right_type + ) + }; +} diff --git a/kclvm/runtime/src/value/val_plan.rs b/kclvm/runtime/src/value/val_plan.rs new file mode 100644 index 000000000..b02f0e355 --- /dev/null +++ b/kclvm/runtime/src/value/val_plan.rs @@ -0,0 +1,256 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use std::rc::Rc; + +use crate::*; + +pub const KCL_PRIVATE_VAR_PREFIX: &str = "_"; +const LIST_DICT_TEMP_KEY: &str = "$"; + +#[allow(dead_code)] +fn filter_results(key_values: &ValueRef) -> Vec { + let mut results: Vec = vec![]; + if !key_values.is_config() { + return results; + } + let ctx = Context::current_context(); + // index 0 for in-line keyvalues output, index 1: for standalone keyvalues outputs + let result = ValueRef::dict(None); + results.push(result); + let key_values = key_values.as_dict_ref(); + for (key, value) in &key_values.values { + if value.is_none() && ctx.cfg.disable_none { + continue; + } + if key.starts_with(KCL_PRIVATE_VAR_PREFIX) || value.is_undefined() || value.is_func() { + continue; + } else if value.is_schema() { + let (filtered, standalone) = handle_schema(value); + if !filtered.is_empty() { + if standalone { + // if the instance is marked as 'STANDALONE', treat it as a separate one and + // extend it and derived STANDALONE instances to results. + for v in filtered { + results.push(v); + } + } else { + // else put it as the value of the key of results + let result = results.get_mut(0).unwrap(); + result.dict_update_key_value(key.as_str(), filtered[0].clone()); + // if the value has derived 'STANDALONE' instances, extend them + if filtered.len() > 1 { + for v in &filtered[1..] { + results.push(v.clone()); + } + } + } + } + } else if value.is_dict() { + let filtered = filter_results(value); + let result = results.get_mut(0).unwrap(); + result.dict_update_key_value(key.as_str(), filtered[0].clone()); + // if the value has derived 'STANDALONE' instances, extend them + if filtered.len() > 1 { + for v in &filtered[1..] { + results.push(v.clone()); + } + } + } else if value.is_list() { + let mut filtered_list: Vec = vec![]; + let mut standalone_list: Vec = vec![]; + let mut ignore_schema_count = 0; + let list_value = value.as_list_ref(); + for v in &list_value.values { + if v.is_schema() { + let (filtered, standalone) = handle_schema(value); + if filtered.is_empty() { + ignore_schema_count += 1; + continue; + } else if standalone { + for v in filtered { + standalone_list.push(v); + } + } else { + for v in filtered { + filtered_list.push(v); + } + } + } else if v.is_dict() { + let filtered = filter_results(v); + for v in filtered { + filtered_list.push(v); + } + } else if v.is_none() && ctx.cfg.disable_none { + continue; + } else if !v.is_undefined() { + let list_dict = ValueRef::dict(Some(&[(LIST_DICT_TEMP_KEY, v)])); + let filtered = filter_results(&list_dict); + if !filtered.is_empty() { + if let Some(v) = filtered[0].get_by_key(key) { + filtered_list.push(v.clone()); + } + } + if filtered.len() > 1 { + for v in &filtered[1..] { + results.push(v.clone()); + } + } + } + } + let schema_in_list_count = ignore_schema_count + standalone_list.len(); + let value = &value.as_list_ref().values; + if schema_in_list_count < value.len() { + let result = results.get_mut(0).unwrap(); + let filtered_list: Vec<&ValueRef> = filtered_list.iter().collect(); + let filtered_list = filtered_list.as_slice(); + let filtered_list = ValueRef::list(Some(filtered_list)); + result.dict_update_key_value(key.as_str(), filtered_list); + } + for v in standalone_list { + results.push(v); + } + } else { + let result = results.get_mut(0).unwrap(); + result.dict_update_key_value(key.as_str(), value.clone()); + } + } + results +} + +#[allow(dead_code)] +fn handle_schema(value: &ValueRef) -> (Vec, bool) { + let filtered = filter_results(value); + if filtered.is_empty() { + return (filtered, false); + } + let settings = SCHEMA_SETTINGS_ATTR_NAME; + let output_type = SETTINGS_OUTPUT_KEY; + let path = format!("{}.{}", settings, output_type); + let output_type_option = value.get_by_path(&path); + if let Some(output_type) = output_type_option { + if output_type.str_equal(SETTINGS_OUTPUT_IGNORE) { + if filtered.is_empty() { + return (filtered, false); + } else { + return (filtered[1..].to_vec(), true); + } + } + } + let mut standalone = false; + if let Some(output_type) = output_type_option { + if output_type.str_equal(SETTINGS_OUTPUT_STANDALONE) { + standalone = true; + } + } + (filtered, standalone) +} + +impl ValueRef { + fn is_planned_empty(&self) -> bool { + self.is_dict() && !self.is_truthy() + } + + pub fn plan_to_json_string(&self) -> String { + let result = self.filter_results(); + if result.is_planned_empty() { + return "".to_string(); + } + result.to_json_string() + } + + pub fn plan_to_yaml_string(&self) -> String { + let result = self.filter_results(); + result.to_yaml_string() + } + + fn filter_results(&self) -> ValueRef { + let ctx = Context::current_context(); + match &*self.rc { + Value::undefined => ValueRef { + rc: Rc::new(Value::undefined), + }, + Value::none => ValueRef { + rc: Rc::new(Value::none), + }, + Value::func_value(ref v) => ValueRef { + rc: Rc::new(Value::func_value(v.clone())), + }, + Value::bool_value(ref v) => ValueRef { + rc: Rc::new(Value::bool_value(*v)), + }, + Value::int_value(ref v) => ValueRef { + rc: Rc::new(Value::int_value(*v)), + }, + Value::float_value(ref v) => ValueRef { + rc: Rc::new(Value::float_value(*v)), + }, + Value::unit_value(ref v, _, _) => ValueRef { + rc: Rc::new(Value::float_value(*v)), + }, + Value::str_value(ref v) => ValueRef { + rc: Rc::new(Value::str_value(v.to_string())), + }, + Value::list_value(ref v) => { + let mut list = ValueRef { + rc: Rc::new(Value::list_value(ListValue { values: vec![] })), + }; + for x in v.values.iter() { + if !(x.is_undefined() || x.is_func() || ctx.cfg.disable_none && x.is_none()) { + list.list_append(&x.filter_results()); + } + } + list + } + Value::dict_value(ref v) => { + let mut dict = ValueRef { + rc: Rc::new(Value::dict_value(DictValue { + values: IndexMap::default(), + ops: IndexMap::default(), + insert_indexs: IndexMap::default(), + attr_map: IndexMap::default(), + })), + }; + for (key, val) in v.values.iter() { + if !(val.is_undefined() + || val.is_func() + || ctx.cfg.disable_none && val.is_none()) + { + dict.dict_insert( + key, + &val.filter_results(), + ConfigEntryOperationKind::Override, + 0, + ); + } + } + dict + } + Value::schema_value(ref v) => { + let mut schema = ValueRef { + rc: Rc::new(Value::schema_value(SchemaValue { + name: v.name.clone(), + pkgpath: v.pkgpath.clone(), + config: Rc::new(DictValue { + values: IndexMap::default(), + ops: IndexMap::default(), + insert_indexs: IndexMap::default(), + attr_map: IndexMap::default(), + }), + config_keys: vec![], + })), + }; + for (key, val) in v.config.values.iter() { + if !val.is_undefined() && !val.is_func() { + schema.dict_insert( + key, + &val.filter_results(), + ConfigEntryOperationKind::Union, + 0, + ); + } + } + schema + } + } + } +} diff --git a/kclvm/runtime/src/value/val_schema.rs b/kclvm/runtime/src/value/val_schema.rs new file mode 100644 index 000000000..c1aec1492 --- /dev/null +++ b/kclvm/runtime/src/value/val_schema.rs @@ -0,0 +1,286 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use std::rc::Rc; + +use crate::*; + +pub const SETTINGS_OUTPUT_KEY: &str = "output_type"; +pub const SETTINGS_SCHEMA_TYPE_KEY: &str = "__schema_type__"; +pub const SETTINGS_OUTPUT_STANDALONE: &str = "STANDALONE"; +pub const SETTINGS_OUTPUT_INLINE: &str = "INLINE"; +pub const SETTINGS_OUTPUT_IGNORE: &str = "IGNORE"; +pub const SCHEMA_SETTINGS_ATTR_NAME: &str = "__settings__"; +pub const CONFIG_META_FILENAME: &str = "$filename"; +pub const CONFIG_META_LINE: &str = "$lineno"; +pub const CONFIG_META_COLUMN: &str = "$columnno"; +pub const CONFIG_ITEM_META_FILENAME: &str = "filename"; +pub const CONFIG_ITEM_META_LINE: &str = "lineno"; +pub const CONFIG_ITEM_META_COLUMN: &str = "columnno"; +pub const CONFIG_ITEM_META: &str = "$config_meta"; +pub const MAIN_PKG_PATH: &str = "__main__"; +pub const PKG_PATH_PREFIX: char = '@'; +pub const CAL_MAP_RUNTIME_TYPE: &str = "cal_map_runtime_type"; +pub const CAL_MAP_META_LINE: &str = "cal_map_meta_line"; + +/// Get the schema runtime type use the schema name and pkgpath +pub fn schema_runtime_type(name: &str, pkgpath: &str) -> String { + format!("{}.{}", pkgpath, name) +} + +/// Construct a schema config meta dict using filename, line and column +#[inline] +pub fn schema_config_meta(filename: &str, line: u64, column: u64) -> ValueRef { + ValueRef::dict(Some(&[ + (CONFIG_META_FILENAME, &ValueRef::str(filename)), + (CONFIG_META_LINE, &ValueRef::int(line as i64)), + (CONFIG_META_COLUMN, &ValueRef::int(column as i64)), + ])) +} + +impl ValueRef { + pub fn dict_to_schema(&self, name: &str, pkgpath: &str, config_keys: &[String]) -> Self { + if self.is_dict() { + Self::from(Value::schema_value(SchemaValue { + name: name.to_string(), + pkgpath: pkgpath.to_string(), + config: Rc::new(self.as_dict_ref().clone()), + config_keys: config_keys.to_owned(), + })) + } else if self.is_schema() { + self.clone() + } else { + panic!("invalid dict object to schema") + } + } + + pub fn schema_to_dict(&self) -> Self { + match &*self.rc { + Value::schema_value(ref schema) => { + Self::from(Value::dict_value(schema.config.as_ref().clone())) + } + Value::dict_value(_) => self.clone(), + _ => panic!("invalid schema object to dict"), + } + } + + pub fn schema_check_attr_optional( + &self, + optional_mapping: &ValueRef, + schema_name: &str, + config_meta: &ValueRef, + ) { + let attr_map = match &*self.rc { + Value::schema_value(ref schema) => { + let schema: &mut SchemaValue = get_ref_mut(schema); + let schema = get_ref_mut(schema); + &schema.config.values + } + Value::dict_value(ref schema) => { + let schema: &mut DictValue = get_ref_mut(schema); + &schema.values + } + _ => panic!("Invalid schema/dict value, got {}", self.type_str()), + }; + match &*optional_mapping.rc { + Value::dict_value(ref optional_mapping) => { + let optional_mapping = get_ref_mut(optional_mapping); + for (attr, is_optional) in &optional_mapping.values { + let is_required = !is_optional.as_bool(); + let undefined = ValueRef::undefined(); + let value = attr_map.get(attr).or(Some(&undefined)).unwrap(); + if is_required && value.is_none_or_undefined() { + let filename = config_meta.get_by_key(CONFIG_META_FILENAME); + let line = config_meta.get_by_key(CONFIG_META_LINE); + let ctx = Context::current_context_mut(); + if let Some(filename) = filename { + ctx.set_kcl_filename(&filename.as_str()); + } + if let Some(line) = line { + ctx.panic_info.kcl_line = line.as_int() as i32; + } + panic!( + "attribute '{}' of {} is required and can't be None or Undefined", + attr, schema_name + ); + } + } + } + _ => panic!( + "Invalid optional mapping, got {}", + optional_mapping.type_str() + ), + } + } + + pub fn schema_default_settings(&mut self, config: &ValueRef, runtime_type: &str) { + let settings = self.dict_get_value(SCHEMA_SETTINGS_ATTR_NAME); + if settings.is_none() || (settings.is_some() && !settings.unwrap().is_config()) { + let mut default_settings = ValueRef::dict(None); + default_settings + .dict_update_key_value(SETTINGS_OUTPUT_KEY, ValueRef::str(SETTINGS_OUTPUT_INLINE)); + default_settings + .dict_update_key_value(SETTINGS_SCHEMA_TYPE_KEY, ValueRef::str(runtime_type)); + self.dict_update_key_value(SCHEMA_SETTINGS_ATTR_NAME, default_settings); + } else { + let settings = get_ref_mut(settings.unwrap()); + settings.dict_update_key_value(SETTINGS_SCHEMA_TYPE_KEY, ValueRef::str(runtime_type)); + } + if let Some(v) = config.dict_get_value(SCHEMA_SETTINGS_ATTR_NAME) { + self.dict_update_key_value(SCHEMA_SETTINGS_ATTR_NAME, v.clone()); + } + } + + pub fn attr_str(&self) -> String { + match &*self.rc { + Value::int_value(v) => v.to_string(), + Value::float_value(v) => v.to_string(), + Value::str_value(v) => v.clone(), + _ => panic!("invalid attribute {}", self.type_str()), + } + } + + pub fn update_attr_map(&mut self, name: &str, type_str: &str) { + match &*self.rc { + Value::dict_value(dict) => { + let attr_map = get_ref_mut(&dict.attr_map); + attr_map.insert(name.to_string(), type_str.to_string()); + } + Value::schema_value(schema) => { + let attr_map = get_ref_mut(&schema.config.attr_map); + attr_map.insert(name.to_string(), type_str.to_string()); + } + _ => panic!("invalid object '{}' in update_attr_map", self.type_str()), + } + } + + pub fn attr_map_get(&mut self, name: &str) -> Option<&String> { + match &*self.rc { + Value::dict_value(dict) => { + let attr_map = get_ref_mut(&dict.attr_map); + attr_map.get(name) + } + Value::schema_value(schema) => { + let attr_map = get_ref_mut(&schema.config.attr_map); + attr_map.get(name) + } + _ => panic!("invalid object '{}' in attr_map_get", self.type_str()), + } + } + + pub fn schema_update_with_schema(&mut self, value: &ValueRef) { + if let (Value::schema_value(schema), Value::schema_value(value)) = (&*self.rc, &*value.rc) { + let values = get_ref_mut(&schema.config.values); + let ops = get_ref_mut(&schema.config.ops); + let insert_indexs = get_ref_mut(&schema.config.insert_indexs); + for (k, v) in &value.config.values { + let op = value + .config + .ops + .get(k) + .or(Some(&ConfigEntryOperationKind::Union)) + .unwrap(); + let index = value.config.insert_indexs.get(k).or(Some(&-1)).unwrap(); + values.insert(k.clone(), v.clone()); + ops.insert(k.clone(), op.clone()); + insert_indexs.insert(k.clone(), *index); + } + } + } +} + +#[cfg(test)] +mod test_value_schema { + use crate::*; + + const TEST_SCHEMA_NAME: &str = "Data"; + + fn get_test_schema_value() -> ValueRef { + let config = ValueRef::dict(None); + let mut schema = ValueRef::dict(None).dict_to_schema(TEST_SCHEMA_NAME, MAIN_PKG_PATH, &[]); + schema.schema_default_settings( + &config, + &schema_runtime_type(TEST_SCHEMA_NAME, MAIN_PKG_PATH), + ); + schema + } + + #[test] + fn test_dict_schema_convention() { + let dict = ValueRef::dict(None); + let dict = dict.schema_to_dict(); + assert_eq!(dict.is_dict(), true); + let schema = dict.dict_to_schema(TEST_SCHEMA_NAME, MAIN_PKG_PATH, &[]); + assert_eq!(schema.is_schema(), true); + let schema = schema.dict_to_schema(TEST_SCHEMA_NAME, MAIN_PKG_PATH, &[]); + assert_eq!(schema.is_schema(), true); + let dict = schema.schema_to_dict(); + assert_eq!(dict.is_dict(), true); + } + + #[test] + fn test_schema_check_attr_optional() { + let dict = ValueRef::dict_str(&[("key", "value")]); + let schema = dict.dict_to_schema(TEST_SCHEMA_NAME, MAIN_PKG_PATH, &[]); + let config_meta = ValueRef::dict(None); + let optional_mapping = ValueRef::dict_bool(&[("key", true)]); + schema.schema_check_attr_optional(&optional_mapping, TEST_SCHEMA_NAME, &config_meta); + let optional_mapping = ValueRef::dict_bool(&[("key", false)]); + schema.schema_check_attr_optional(&optional_mapping, TEST_SCHEMA_NAME, &config_meta); + let optional_mapping = ValueRef::dict_bool(&[("another_key", true)]); + schema.schema_check_attr_optional(&optional_mapping, TEST_SCHEMA_NAME, &config_meta); + } + + #[test] + fn test_schema_check_attr_optional_invalid() { + let err = std::panic::catch_unwind(|| { + let dict = ValueRef::dict_str(&[("key", "value")]); + let schema = dict.dict_to_schema(TEST_SCHEMA_NAME, MAIN_PKG_PATH, &[]); + let config_meta = ValueRef::dict(None); + let optional_mapping = ValueRef::dict_bool(&[("another_key", false)]); + schema.schema_check_attr_optional(&optional_mapping, TEST_SCHEMA_NAME, &config_meta); + }); + assert!(err.is_err()) + } + + #[test] + fn test_schema_default_settings() { + let schema = get_test_schema_value(); + let schema_settings = schema.get_by_key(SCHEMA_SETTINGS_ATTR_NAME).unwrap(); + let output_type = schema_settings + .get_by_key(SETTINGS_OUTPUT_KEY) + .unwrap() + .as_str(); + assert_eq!(output_type, SETTINGS_OUTPUT_INLINE); + } + + #[test] + fn test_schema_attr_map() { + let mut schema = get_test_schema_value(); + let entries = [("key1", "str"), ("key2", "int"), ("key3", "str|int")]; + for (attr, type_str) in entries { + schema.update_attr_map(attr, type_str); + } + for (attr, type_str) in entries { + let result = schema.attr_map_get(attr).unwrap().clone(); + assert_eq!(result, type_str); + } + } + + #[test] + fn test_schema_update_with_schema() { + let mut schema1 = get_test_schema_value(); + let mut schema2 = get_test_schema_value(); + let entries = [("key1", "value1"), ("key2", "value2")]; + for (key, val) in entries { + schema2.dict_update_entry( + key, + &ValueRef::str(val), + &ConfigEntryOperationKind::Union, + &-1, + ); + } + assert_ne!(schema1, schema2); + schema1.schema_update_with_schema(&schema2); + assert_eq!(schema1, schema2); + } +} diff --git a/kclvm/runtime/src/value/val_str.rs b/kclvm/runtime/src/value/val_str.rs new file mode 100644 index 000000000..517ef5cb8 --- /dev/null +++ b/kclvm/runtime/src/value/val_str.rs @@ -0,0 +1,713 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; +use bstr::ByteSlice; +use unic_ucd_bidi::BidiClass; +use unic_ucd_category::GeneralCategory; +use unicode_casing::CharExt; + +const ASCII_WHITESPACES: [u8; 6] = [0x20, 0x09, 0x0a, 0x0c, 0x0d, 0x0b]; + +fn adjust_indices( + start: Option<&ValueRef>, + end: Option<&ValueRef>, + len: usize, +) -> std::ops::Range { + let mut start = start.map_or(0, |v| { + if v.is_none_or_undefined() { + 0 + } else { + v.as_int() as isize + } + }); + let mut end = end.map_or(len as isize, |v| { + if v.is_none_or_undefined() { + len as isize + } else { + v.as_int() as isize + } + }); + if end > len as isize { + end = len as isize; + } else if end < 0 { + end += len as isize; + if end < 0 { + end = 0; + } + } + if start < 0 { + start += len as isize; + if start < 0 { + start = 0; + } + } + start as usize..end as usize +} + +fn is_case(value: &str, is_case: F, is_opposite: G) -> bool +where + F: Fn(char) -> bool, + G: Fn(char) -> bool, +{ + let mut cased = false; + for c in value.chars() { + if is_opposite(c) { + return false; + } else if !cased && is_case(c) { + cased = true + } + } + cased +} + +trait RangeNormal { + fn is_normal(&self) -> bool; +} + +impl RangeNormal for std::ops::Range { + fn is_normal(&self) -> bool { + self.start <= self.end + } +} + +impl ValueRef { + pub fn str_len(&self) -> usize { + match &*self.rc { + Value::str_value(ref v) => v.len(), + _ => panic!("Invalid str object in str len"), + } + } + pub fn str_resize(&mut self, n: usize) { + match &*self.rc { + Value::str_value(_) => { + self.rc = std::rc::Rc::new(Value::str_value("?".repeat(n))); + } + _ => panic!("Invalid str object in str resize"), + } + } + + pub fn str_lower(&self) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => ValueRef::str(&v.to_lowercase()), + _ => panic!("Invalid str object in lower"), + } + } + + pub fn str_upper(&self) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => ValueRef::str(&v.to_uppercase()), + _ => panic!("Invalid str object in upper"), + } + } + + pub fn str_capitalize(&self) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => { + let mut chars = v.chars(); + let value = if let Some(first_char) = chars.next() { + format!( + "{}{}", + first_char.to_uppercase(), + &chars.as_str().to_lowercase(), + ) + } else { + "".to_owned() + }; + ValueRef::str(value.as_str()) + } + _ => panic!("Invalid str object in str_capitalize"), + } + } + + pub fn str_count( + &self, + sub: &ValueRef, + start: Option<&ValueRef>, + end: Option<&ValueRef>, + ) -> ValueRef { + let start = adjust_parameter(start); + let end = adjust_parameter(end); + + match (&*self.rc, &*sub.rc) { + (Value::str_value(ref v), Value::str_value(ref sub_str)) => { + let range = adjust_indices(start, end, v.len()); + let count = if range.is_normal() { + v.get(range).unwrap().matches(sub_str).count() + } else { + 0 + }; + ValueRef::int(count as i64) + } + _ => panic!("Invalid str object in str_count"), + } + } + + pub fn str_startswith( + &self, + prefix: &ValueRef, + start: Option<&ValueRef>, + end: Option<&ValueRef>, + ) -> ValueRef { + let start = adjust_parameter(start); + let end = adjust_parameter(end); + + match (&*self.rc, &*prefix.rc) { + (Value::str_value(ref v), Value::str_value(ref prefix)) => { + let range = adjust_indices(start, end, v.len()); + let result = if range.is_normal() { + v.get(range).unwrap().starts_with(prefix) + } else { + false + }; + ValueRef::bool(result) + } + _ => panic!("Invalid str object in str_startswith"), + } + } + + pub fn str_endswith( + &self, + suffix: &ValueRef, + start: Option<&ValueRef>, + end: Option<&ValueRef>, + ) -> ValueRef { + let start = adjust_parameter(start); + let end = adjust_parameter(end); + + match (&*self.rc, &*suffix.rc) { + (Value::str_value(ref v), Value::str_value(ref suffix)) => { + let range = adjust_indices(start, end, v.len()); + let result = if range.is_normal() { + v.get(range).unwrap().ends_with(suffix) + } else { + false + }; + ValueRef::bool(result) + } + _ => panic!("Invalid str object in str_endswith"), + } + } + + pub fn str_format(&self, args: &ValueRef, kwargs: &ValueRef) -> ValueRef { + match (&*self.rc, &*args.rc) { + (Value::str_value(ref v), Value::list_value(_)) => { + match FormatString::from_str(v.as_str()) { + Ok(format_string) => { + let result = format_string.format(args, kwargs); + ValueRef::str(result.as_str()) + } + Err(_) => panic!("format error"), + } + } + _ => panic!("Invalid str object in str_format"), + } + } + + pub fn str_find( + &self, + sub: &ValueRef, + start: Option<&ValueRef>, + end: Option<&ValueRef>, + ) -> ValueRef { + let start = adjust_parameter(start); + let end = adjust_parameter(end); + + match (&*self.rc, &*sub.rc) { + (Value::str_value(ref v), Value::str_value(ref sub)) => { + let range = adjust_indices(start, end, v.len()); + let range_start = range.start; + let result: i64 = if range.is_normal() { + match v.get(range).unwrap().find(sub) { + Some(index) => (index + range_start) as i64, + None => -1, + } + } else { + -1 + }; + ValueRef::int(result) + } + _ => panic!("Invalid str object in str_find"), + } + } + + pub fn str_rfind( + &self, + sub: &ValueRef, + start: Option<&ValueRef>, + end: Option<&ValueRef>, + ) -> ValueRef { + let start = adjust_parameter(start); + let end = adjust_parameter(end); + + match (&*self.rc, &*sub.rc) { + (Value::str_value(ref v), Value::str_value(ref sub)) => { + let range = adjust_indices(start, end, v.len()); + let range_start = range.start; + let result: i64 = if range.is_normal() { + match v.get(range).unwrap().rfind(sub) { + Some(index) => (index + range_start) as i64, + None => -1, + } + } else { + -1 + }; + ValueRef::int(result) + } + _ => panic!("Invalid str object in str_rfind"), + } + } + + pub fn str_index( + &self, + sub: &ValueRef, + start: Option<&ValueRef>, + end: Option<&ValueRef>, + ) -> ValueRef { + let start = adjust_parameter(start); + let end = adjust_parameter(end); + + match (&*self.rc, &*sub.rc) { + (Value::str_value(ref v), Value::str_value(ref sub)) => { + let range = adjust_indices(start, end, v.len()); + let range_start = range.start; + let result: i64 = if range.is_normal() { + match v.get(range).unwrap().find(sub) { + Some(index) => (index + range_start) as i64, + None => panic!("substring not found"), + } + } else { + panic!("substring not found"); + }; + ValueRef::int(result) + } + _ => panic!("Invalid str object in str_index"), + } + } + + pub fn str_rindex( + &self, + sub: &ValueRef, + start: Option<&ValueRef>, + end: Option<&ValueRef>, + ) -> ValueRef { + let start = adjust_parameter(start); + let end = adjust_parameter(end); + + match (&*self.rc, &*sub.rc) { + (Value::str_value(ref v), Value::str_value(ref sub)) => { + let range = adjust_indices(start, end, v.len()); + let range_start = range.start; + let result: i64 = if range.is_normal() { + match v.get(range).unwrap().rfind(sub) { + Some(index) => (index + range_start) as i64, + None => panic!("substring not found"), + } + } else { + panic!("substring not found"); + }; + ValueRef::int(result) + } + _ => panic!("Invalid str object in str_rindex"), + } + } + + pub fn str_isalnum(&self) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => { + let result = !v.is_empty() && v.chars().all(char::is_alphanumeric); + ValueRef::bool(result) + } + _ => panic!("Invalid str object in str_isalnum"), + } + } + + pub fn str_isalpha(&self) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => { + let result = !v.is_empty() && v.chars().all(char::is_alphabetic); + ValueRef::bool(result) + } + _ => panic!("Invalid str object in str_isalpha"), + } + } + + pub fn str_isdigit(&self) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => { + let valid_unicodes: [u16; 10] = [ + 0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, + ]; + + let result = !v.is_empty() + && v.chars() + .filter(|c| !c.is_digit(10)) + .all(|c| valid_unicodes.contains(&(c as u16))); + ValueRef::bool(result) + } + _ => panic!("Invalid str object in str_isdigit"), + } + } + + pub fn str_islower(&self) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => { + let result = is_case(v, char::is_lowercase, char::is_uppercase); + ValueRef::bool(result) + } + _ => panic!("Invalid str object in str_islower"), + } + } + + pub fn str_isspace(&self) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => { + if v.is_empty() { + return ValueRef::bool(false); + } + use unic_ucd_bidi::bidi_class::abbr_names::*; + let result = v.chars().all(|c| { + GeneralCategory::of(c) == GeneralCategory::SpaceSeparator + || matches!(BidiClass::of(c), WS | B | S) + }); + ValueRef::bool(result) + } + _ => panic!("Invalid str object in str_isspace"), + } + } + + pub fn str_istitle(&self) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => { + if v.is_empty() { + return ValueRef::bool(false); + } + + let mut cased = false; + let mut previous_is_cased = false; + for c in v.chars() { + if c.is_uppercase() || c.is_titlecase() { + if previous_is_cased { + return ValueRef::bool(false); + } + previous_is_cased = true; + cased = true; + } else if c.is_lowercase() { + if !previous_is_cased { + return ValueRef::bool(false); + } + previous_is_cased = true; + cased = true; + } else { + previous_is_cased = false; + } + } + ValueRef::bool(cased) + } + _ => panic!("Invalid str object in str_istitle"), + } + } + + pub fn str_isupper(&self) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => { + let result = is_case(v, char::is_uppercase, char::is_lowercase); + ValueRef::bool(result) + } + _ => panic!("Invalid str object in str_isupper"), + } + } + + pub fn str_isnumeric(&self) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => { + let result = !v.is_empty() && v.chars().all(char::is_numeric); + ValueRef::bool(result) + } + _ => panic!("Invalid str object in str_isnumeric"), + } + } + + pub fn str_join(&self, value: &ValueRef) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => { + let mut joined = String::new(); + let mut iter = value.iter(); + while !iter.is_end() { + let iter_value = iter.next(value).unwrap(); + joined.push_str(iter_value.to_string().as_str()); + if !iter.is_end() { + joined.push_str(v); + } + } + ValueRef::str(joined.as_str()) + } + _ => panic!("Invalid str object in str_joined"), + } + } + + pub fn str_lstrip(&self, value: Option<&ValueRef>) -> ValueRef { + let value = adjust_parameter(value); + + match &*self.rc { + Value::str_value(ref v) => { + let value = match value { + Some(chars) => { + let chars = chars.as_str(); + let chars = chars.as_str(); + v.trim_start_matches(|c| chars.contains(c)) + } + None => v.trim_start(), + }; + ValueRef::str(value) + } + _ => panic!("Invalid str object in str_lstrip"), + } + } + + pub fn str_rstrip(&self, value: Option<&ValueRef>) -> ValueRef { + let value = adjust_parameter(value); + + match &*self.rc { + Value::str_value(ref v) => { + let value = match value { + Some(chars) => { + let chars = chars.as_str(); + let chars = chars.as_str(); + v.trim_end_matches(|c| chars.contains(c)) + } + None => v.trim_end(), + }; + ValueRef::str(value) + } + _ => panic!("Invalid str object in str_rstrip"), + } + } + + pub fn str_replace( + &self, + old: &ValueRef, + new: &ValueRef, + count: Option<&ValueRef>, + ) -> ValueRef { + let count = adjust_parameter(count); + + match &*self.rc { + Value::str_value(ref v) => { + let old = old.as_str(); + let new = new.as_str(); + let result = match count { + Some(count) => { + let maxcount = count.as_int(); + if maxcount >= 0 { + v.replacen(old.as_str(), new.as_str(), maxcount as usize) + } else { + v.replace(old.as_str(), new.as_str()) + } + } + None => v.replace(old.as_str(), new.as_str()), + }; + ValueRef::str(result.as_str()) + } + _ => panic!("Invalid str object in str_replace"), + } + } + + pub fn str_split(&self, sep: Option<&ValueRef>, maxsplit: Option<&ValueRef>) -> ValueRef { + let sep = adjust_parameter(sep); + let maxsplit = adjust_parameter(maxsplit); + + match &*self.rc { + Value::str_value(ref v) => { + let convert = ValueRef::str; + let maxsplit = match maxsplit { + Some(v) => v.as_int(), + None => -1, + }; + let splited = if let Some(pattern) = sep { + let s = pattern.as_str(); + if maxsplit < 0 { + v.split(s.as_str()).map(convert).collect() + } else { + v.splitn(maxsplit as usize + 1, s.as_str()) + .map(convert) + .collect() + } + } else { + // Split whitespace + let mut splited = Vec::new(); + let mut last_offset = 0; + let mut count = maxsplit; + for (offset, _) in + v.match_indices(|c: char| c.is_ascii_whitespace() || c == '\x0b') + { + if last_offset == offset { + last_offset += 1; + continue; + } + if count == 0 { + break; + } + splited.push(convert(&v[last_offset..offset])); + last_offset = offset + 1; + count -= 1; + } + if last_offset != v.len() { + splited.push(convert(&v[last_offset..])); + } + splited + }; + ValueRef::list_value(Some(splited.as_slice())) + } + _ => panic!("Invalid str object in str_split"), + } + } + + pub fn str_rsplit(&self, sep: Option<&ValueRef>, maxsplit: Option<&ValueRef>) -> ValueRef { + let sep = adjust_parameter(sep); + let maxsplit = adjust_parameter(maxsplit); + + match &*self.rc { + Value::str_value(ref v) => { + let convert = ValueRef::str; + let maxsplit = match maxsplit { + Some(v) => v.as_int(), + None => -1, + }; + let mut splited = if let Some(pattern) = sep { + let s = pattern.as_str(); + if maxsplit < 0 { + v.rsplit(s.as_str()).map(convert).collect() + } else { + v.rsplitn(maxsplit as usize + 1, s.as_str()) + .map(convert) + .collect() + } + } else { + // Split whitespace + let mut splited = Vec::new(); + let mut count = maxsplit; + let mut haystack = v.as_bytes(); + while let Some(offset) = haystack.rfind_byteset(ASCII_WHITESPACES) { + if offset + 1 != haystack.len() { + if count == 0 { + break; + } + splited.push(convert( + std::str::from_utf8(&haystack[offset + 1..]).unwrap(), + )); + count -= 1; + } + haystack = &haystack[..offset]; + } + if !haystack.is_empty() { + splited.push(convert(std::str::from_utf8(haystack).unwrap())); + } + splited + }; + splited.reverse(); + ValueRef::list_value(Some(splited.as_slice())) + } + _ => panic!("Invalid str object in str_rsplit"), + } + } + + pub fn str_splitlines(&self, keepends: Option<&ValueRef>) -> ValueRef { + let keepends = adjust_parameter(keepends); + + match &*self.rc { + Value::str_value(ref v) => { + let convert = ValueRef::str; + let keepends = match keepends { + Some(v) => v.as_bool(), + None => false, + }; + let keep = keepends as usize; + let mut elements = Vec::new(); + let mut last_i = 0; + let mut enumerated = v.as_bytes().iter().enumerate().peekable(); + while let Some((i, ch)) = enumerated.next() { + let (end_len, i_diff) = match *ch { + b'\n' => (keep, 1), + b'\r' => { + let is_rn = enumerated.peek().map_or(false, |(_, ch)| **ch == b'\n'); + if is_rn { + let _ = enumerated.next(); + (keep + keep, 2) + } else { + (keep, 1) + } + } + _ => { + continue; + } + }; + let range = last_i..i + end_len; + last_i = i + i_diff; + elements.push(convert(&v[range])); + } + if last_i != v.len() { + elements.push(convert(&v[last_i..v.len()])); + } + ValueRef::list_value(Some(elements.as_slice())) + } + _ => panic!("Invalid str object in str_splitlines"), + } + } + + pub fn str_strip(&self, value: Option<&ValueRef>) -> ValueRef { + let value = adjust_parameter(value); + + match &*self.rc { + Value::str_value(ref v) => { + let value = match value { + Some(chars) => { + let chars = chars.as_str(); + let chars = chars.as_str(); + v.trim_matches(|c| chars.contains(c)) + } + None => v.trim(), + }; + ValueRef::str(value) + } + _ => panic!("Invalid str object in str_strip"), + } + } + + pub fn str_title(&self) -> ValueRef { + match &*self.rc { + Value::str_value(ref v) => { + let mut title = String::with_capacity(v.len()); + let mut previous_is_cased = false; + for c in v.chars() { + if c.is_lowercase() { + if !previous_is_cased { + title.extend(c.to_titlecase()); + } else { + title.push(c); + } + previous_is_cased = true; + } else if c.is_uppercase() || c.is_titlecase() { + if previous_is_cased { + title.extend(c.to_lowercase()); + } else { + title.push(c); + } + previous_is_cased = true; + } else { + previous_is_cased = false; + title.push(c); + } + } + ValueRef::str(title.as_str()) + } + _ => panic!("Invalid str object in title"), + } + } + + pub fn str_equal(&self, value: &str) -> bool { + match &*self.rc { + Value::str_value(ref v) => *v == *value, + _ => false, + } + } +} diff --git a/kclvm/runtime/src/value/val_type.rs b/kclvm/runtime/src/value/val_type.rs new file mode 100644 index 000000000..5a6156424 --- /dev/null +++ b/kclvm/runtime/src/value/val_type.rs @@ -0,0 +1,966 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +extern crate fancy_regex; + +use crate::*; +use std::mem::transmute_copy; + +pub const BUILTIN_TYPE_INT: &str = "int"; +pub const BUILTIN_TYPE_FLOAT: &str = "float"; +pub const BUILTIN_TYPE_STR: &str = "str"; +pub const BUILTIN_TYPE_BOOL: &str = "bool"; +pub const BUILTIN_TYPES: [&str; 4] = [ + BUILTIN_TYPE_STR, + BUILTIN_TYPE_BOOL, + BUILTIN_TYPE_INT, + BUILTIN_TYPE_FLOAT, +]; +pub const KCL_TYPE_NONE: &str = "NoneType"; +pub const KCL_TYPE_UNDEFINED: &str = "UndefinedType"; +pub const KCL_TYPE_ANY: &str = "any"; +pub const KCL_TYPE_LIST: &str = "list"; +pub const KCL_TYPE_DICT: &str = "dict"; +pub const KCL_TYPE_FUNCTION: &str = "function"; +pub const KCL_TYPE_NUMBER_MULTIPLY: &str = "number_multiplier"; +pub const KCL_NAME_CONSTANT_NONE: &str = "None"; +pub const KCL_NAME_CONSTANT_UNDEFINED: &str = "Undefined"; +pub const KCL_NAME_CONSTANT_TRUE: &str = "True"; +pub const KCL_NAME_CONSTANT_FALSE: &str = "False"; +pub const KCL_NAME_CONSTANTS: [&str; 4] = [ + KCL_NAME_CONSTANT_NONE, + KCL_NAME_CONSTANT_UNDEFINED, + KCL_NAME_CONSTANT_TRUE, + KCL_NAME_CONSTANT_FALSE, +]; +pub const NUMBER_MULTIPLIER_TYPE: &str = "units.NumberMultiplier"; +pub const NUMBER_MULTIPLIER_REGEX: &str = + r"^([1-9][0-9]{0,63})(E|P|T|G|M|K|k|m|u|n|Ei|Pi|Ti|Gi|Mi|Ki)$"; + +pub type SchemaTypeFunc = unsafe extern "C" fn( + *mut kclvm_context_t, + *const kclvm_value_ref_t, + *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t; + +// common +impl ValueRef { + pub fn type_str(&self) -> String { + match &*self.rc { + Value::undefined => String::from(KCL_TYPE_UNDEFINED), + Value::none => String::from(KCL_TYPE_NONE), + Value::bool_value(..) => String::from(BUILTIN_TYPE_BOOL), + Value::int_value(..) => String::from(BUILTIN_TYPE_INT), + Value::float_value(..) => String::from(BUILTIN_TYPE_FLOAT), + Value::unit_value(_, raw, suffix) => { + format!("{}({}{})", KCL_TYPE_NUMBER_MULTIPLY, raw, suffix) + } + Value::str_value(..) => String::from(BUILTIN_TYPE_STR), + Value::list_value(..) => String::from(KCL_TYPE_LIST), + Value::dict_value(..) => String::from(KCL_TYPE_DICT), + Value::schema_value(ref v) => v.name.clone(), + Value::func_value(..) => String::from(KCL_TYPE_FUNCTION), + } + } +} + +/// Use the schema instance to build a new schema instance using the schema construct function +pub fn resolve_schema(schema: &ValueRef, keys: &[String]) -> ValueRef { + if !schema.is_schema() { + return schema.clone(); + } + let schema_value = schema.as_schema(); + let schema_type_name = schema_runtime_type(&schema_value.name, &schema_value.pkgpath); + let ctx = Context::current_context_mut(); + let now_meta_info = ctx.panic_info.clone(); + let has_schema_type = { + let all_schemas = ctx.all_schemas.borrow_mut(); + all_schemas.contains_key(&schema_type_name) + }; + if has_schema_type { + let schema_type = { + let all_schemas = ctx.all_schemas.borrow_mut(); + all_schemas.get(&schema_type_name).unwrap().clone() + }; + let schema_type = schema_type.as_function(); + let schema_fn_ptr = schema_type.fn_ptr; + let keys = keys.iter().map(|v| v.as_str()).collect(); + let config = schema.dict_get_entries(keys); + let config_new = config.clone(); + let config_meta = schema_config_meta( + &ctx.panic_info.kcl_file, + ctx.panic_info.kcl_line as u64, + ctx.panic_info.kcl_col as u64, + ); + let config_meta_new = config_meta.clone(); + let value = unsafe { + let schema_fn: SchemaTypeFunc = transmute_copy(&schema_fn_ptr); + let ctx = kclvm_context_current(); + let cal_map = kclvm_value_Dict(); + let list = kclvm_value_List(); + // Schema function closures + // is sub schema + kclvm_list_append(list, ValueRef::bool(false).into_raw()); + // config meta + kclvm_list_append(list, config_meta.into_raw()); + // schema + kclvm_list_append(list, config.into_raw()); + // config + kclvm_list_append(list, kclvm_value_Dict()); + // optional mapping + kclvm_list_append(list, kclvm_value_Dict()); + // cal order map + kclvm_list_append(list, cal_map); + // backtrack level map + kclvm_list_append(list, kclvm_value_Dict()); + // backtrack cache + kclvm_list_append(list, kclvm_value_Dict()); + // record instance + kclvm_list_append(list, ValueRef::bool(false).into_raw()); + // instance pkgpath + kclvm_list_append(list, ValueRef::str(&now_meta_info.kcl_pkgpath).into_raw()); + let dict = kclvm_value_Dict(); + schema_fn(ctx, list, dict); + let list = kclvm_value_List(); + // Schema function closures + // is sub schema + kclvm_list_append(list, ValueRef::bool(true).into_raw()); + // config meta + kclvm_list_append(list, config_meta_new.into_raw()); + // schema + kclvm_list_append(list, config_new.into_raw()); + // config + kclvm_list_append(list, kclvm_value_Dict()); + // optional mapping + kclvm_list_append(list, kclvm_value_Dict()); + // cal order map + kclvm_list_append(list, cal_map); + // backtrack level map + kclvm_list_append(list, kclvm_value_Dict()); + // backtrack cache + kclvm_list_append(list, kclvm_value_Dict()); + // record instance + kclvm_list_append(list, ValueRef::bool(true).into_raw()); + // instance pkgpath + kclvm_list_append(list, ValueRef::str(&now_meta_info.kcl_pkgpath).into_raw()); + let value = schema_fn(ctx, list, dict); + ptr_as_ref(value) + }; + ctx.panic_info = now_meta_info; + return value.clone(); + } + ctx.panic_info = now_meta_info; + schema.clone() +} + +/// Type pack and check ValueRef with the expected type vector +pub fn type_pack_and_check(value: &ValueRef, expected_types: Vec<&str>) -> ValueRef { + if value.is_none_or_undefined() || expected_types.is_empty() { + return value.clone(); + } + let is_schema = value.is_schema(); + let value_tpe = value.type_str(); + let mut checked = false; + let mut convertted_value = value.clone(); + let expected_type = &expected_types.join(" | ").replace("@", ""); + for tpe in expected_types { + let tpe = if !tpe.contains('.') { + let ctx = Context::current_context_mut(); + match ctx.import_names.get(tpe) { + Some(mapping) => mapping.keys().next().unwrap(), + None => tpe, + } + } else { + tpe + }; + if !is_schema { + convertted_value = convert_collection_value(value, tpe); + } + // Runtime type check + checked = check_type(&convertted_value, tpe); + if checked { + break; + } + } + if !checked { + panic!("expect {}, got {}", expected_type, value_tpe); + } + convertted_value +} + +/// Convert collection value including dict/list to the potential schema +pub fn convert_collection_value(value: &ValueRef, tpe: &str) -> ValueRef { + // May be a type alias. + let tpe = if !tpe.contains('.') { + let ctx = Context::current_context_mut(); + match ctx.import_names.get(tpe) { + Some(mapping) => mapping.keys().next().unwrap(), + None => tpe, + } + } else { + tpe + }; + if tpe.is_empty() || tpe == KCL_TYPE_ANY { + return value.clone(); + } + let is_collection = value.is_list() || value.is_dict(); + let invalid_match_dict = is_dict_type(tpe) && !value.is_dict(); + let invalid_match_list = is_list_type(tpe) && !value.is_list(); + let invalid_match = invalid_match_dict || invalid_match_list; + if !is_collection || invalid_match || is_type_union(tpe) { + return value.clone(); + } + if is_dict_type(tpe) { + //let (key_tpe, value_tpe) = separate_kv(tpe); + let (_, value_tpe) = separate_kv(&dereference_type(tpe)); + let mut expected_dict = ValueRef::dict(None); + let dict_ref = value.as_dict_ref(); + for (k, v) in &dict_ref.values { + let expected_value = convert_collection_value(v, &value_tpe); + let op = dict_ref + .ops + .get(k) + .or(Some(&ConfigEntryOperationKind::Union)) + .unwrap(); + let index = dict_ref.insert_indexs.get(k).or(Some(&-1)).unwrap(); + expected_dict.dict_update_entry(k, &expected_value, op, index) + } + expected_dict + } else if is_list_type(tpe) { + let expected_type = dereference_type(tpe); + let mut expected_list = ValueRef::list(None); + let list_ref = value.as_list_ref(); + for v in &list_ref.values { + let expected_value = convert_collection_value(v, &expected_type); + expected_list.list_append(&expected_value) + } + expected_list + } else if BUILTIN_TYPES.contains(&tpe) { + value.clone() + } else { + let ctx = Context::current_context_mut(); + let now_meta_info = ctx.panic_info.clone(); + let mut schema_type_name = if tpe.contains('.') { + tpe.to_string() + } else { + format!( + "{}.{}", + if now_meta_info.kcl_pkgpath.is_empty() { + MAIN_PKG_PATH + } else { + now_meta_info.kcl_pkgpath.as_str() + }, + tpe + ) + }; + + if schema_type_name.contains('.') { + let splits: Vec<&str> = schema_type_name.rsplitn(2, '.').collect(); + let pkgname = splits[1]; + let name = splits[0]; + match ctx.import_names.get(&now_meta_info.kcl_file) { + Some(mapping) => match mapping.get(pkgname) { + Some(pkgpath) => { + schema_type_name = format!("{}.{}", pkgpath, name); + } + None => {} + }, + None => { + for (_, mapping) in &ctx.import_names { + match mapping.get(pkgname) { + Some(pkgpath) => { + schema_type_name = format!("{}.{}", pkgpath, name); + break; + } + None => {} + } + } + } + } + } + let has_schema_type = { + let all_schemas = ctx.all_schemas.borrow_mut(); + all_schemas.contains_key(&schema_type_name) + }; + if has_schema_type { + let schema_type = { + let all_schemas = ctx.all_schemas.borrow_mut(); + all_schemas.get(&schema_type_name).unwrap().clone() + }; + let schema_type = schema_type.as_function(); + let schema_fn_ptr = schema_type.fn_ptr; + let value = unsafe { + let schema_fn: SchemaTypeFunc = transmute_copy(&schema_fn_ptr); + let ctx = kclvm_context_current(); + let cal_order = kclvm_value_Dict(); + let list = kclvm_value_List(); + // Schema function closures + // is_sub_schema + kclvm_list_append(list, ValueRef::bool(false).into_raw()); + // config meta + kclvm_list_append(list, kclvm_value_Dict()); + // config + kclvm_list_append(list, value.clone().into_raw()); + // schema + kclvm_list_append(list, kclvm_value_Dict()); + // optional mapping + kclvm_list_append(list, kclvm_value_Dict()); + // cal order map + kclvm_list_append(list, cal_order); + // backtrack level map + kclvm_list_append(list, kclvm_value_Dict()); + // backtrack cache + kclvm_list_append(list, kclvm_value_Dict()); + // record instance + kclvm_list_append(list, ValueRef::bool(false).into_raw()); + // instance pkgpath + kclvm_list_append(list, ValueRef::str(&now_meta_info.kcl_pkgpath).into_raw()); + let dict = kclvm_value_Dict(); + schema_fn(ctx, list, dict); + let list = kclvm_value_List(); + // Schema function closures + // is_sub_schema + kclvm_list_append(list, ValueRef::bool(true).into_raw()); + // config meta + kclvm_list_append(list, kclvm_value_Dict()); + // config + kclvm_list_append(list, value.clone().into_raw()); + // schema + kclvm_list_append(list, kclvm_value_Dict()); + // optional mapping + kclvm_list_append(list, kclvm_value_Dict()); + // cal order map + kclvm_list_append(list, cal_order); + // backtrack level map + kclvm_list_append(list, kclvm_value_Dict()); + // backtrack cache + kclvm_list_append(list, kclvm_value_Dict()); + // record instance + kclvm_list_append(list, ValueRef::bool(true).into_raw()); + // instance pkgpath + kclvm_list_append(list, ValueRef::str(&now_meta_info.kcl_pkgpath).into_raw()); + let value = schema_fn(ctx, list, dict); + ptr_as_ref(value) + }; + ctx.panic_info = now_meta_info; + return value.clone(); + } + ctx.panic_info = now_meta_info; + value.clone() + } +} + +/// check_type returns the value wether match the given the type string +pub fn check_type(value: &ValueRef, tpe: &str) -> bool { + if tpe.is_empty() || tpe == KCL_TYPE_ANY { + return true; + } + if value.is_none_or_undefined() { + return true; + } + if is_type_union(tpe) { + return check_type_union(value, tpe); + } else if check_type_literal(value, tpe) { + return true; + } else if check_number_multiplier_type(value, tpe) { + return true; + } + // if value type is a dict type e.g. {"k": "v"} + else if value.is_dict() { + return check_type_dict(value, tpe); + } + // if value type is a list type e.g. [1, 2, 3] + else if value.is_list() { + return check_type_list(value, tpe); + } else if !value.is_none_or_undefined() { + // if value type is a built-in type e.g. str, int, float, bool + if match_builtin_type(value, tpe) { + return true; + } + if value.is_schema() { + // not list/dict, not built-in type, treat as user defined schema, + // do not check user schema type because it has been checked at compile time + return is_schema_type(tpe); + } + // Type error + return false; + } + // Type error + false +} + +/// check_type_union returns the value wether match the given the union type string +pub fn check_type_union(value: &ValueRef, tpe: &str) -> bool { + let expected_types = split_type_union(tpe); + if expected_types.len() <= 1 { + return false; + } + return expected_types.iter().any(|tpe| check_type(value, tpe)); +} + +/// check_type_literal returns the value wether match the given the literal type string +pub fn check_type_literal(value: &ValueRef, tpe: &str) -> bool { + if !is_literal_type(tpe) { + return false; + } + if value.is_none() { + return tpe == KCL_NAME_CONSTANT_NONE; + } + if value.is_undefined() { + return tpe == KCL_NAME_CONSTANT_UNDEFINED; + } + if value.is_bool() { + let value = value.as_bool(); + if !value { + return tpe == KCL_NAME_CONSTANT_FALSE; + } + if value { + return tpe == KCL_NAME_CONSTANT_TRUE; + } + } + if value.is_int() || value.is_float() { + return value.to_string() == *tpe; + } + if value.is_str() { + let value = format!("{:?}", value.as_str()); + return value == *tpe; + } + false +} + +/// check_number_multiplier_type returns the value wether match the given the type string +pub fn check_number_multiplier_type(value: &ValueRef, tpe: &str) -> bool { + if value.is_unit() { + if is_number_multiplier_literal_type(tpe) { + let (_, raw, suffix) = value.as_unit(); + return format!("{}{}", raw, suffix) == tpe; + } + return tpe == NUMBER_MULTIPLIER_TYPE; + } + false +} + +/// check_type_dict returns the value wether match the given the dict type string +pub fn check_type_dict(value: &ValueRef, tpe: &str) -> bool { + if tpe.is_empty() { + return true; + } + if !is_dict_type(tpe) || !value.is_dict() { + return false; + } + let expected_type = dereference_type(tpe); + let (_, expected_value_type) = separate_kv(&expected_type); + let dict_ref = value.as_dict_ref(); + for (_, v) in &dict_ref.values { + if !check_type(v, &expected_value_type) { + return false; + } + } + true +} + +/// check_type_list returns the value wether match the given the list type string +pub fn check_type_list(value: &ValueRef, tpe: &str) -> bool { + if tpe.is_empty() { + return true; + } + if !is_list_type(tpe) || !value.is_list() { + return false; + } + let expected_type = dereference_type(tpe); + let list_ref = value.as_list_ref(); + for v in &list_ref.values { + if !check_type(v, &expected_type) { + return false; + } + } + true +} + +/// match_builtin_type returns the value wether match the given the type string +#[inline] +pub fn match_builtin_type(value: &ValueRef, tpe: &str) -> bool { + value.type_str() == *tpe || (value.type_str() == BUILTIN_TYPE_INT && tpe == BUILTIN_TYPE_FLOAT) +} + +/// is_literal_type returns the type string whether is a literal type +pub fn is_literal_type(tpe: &str) -> bool { + if KCL_NAME_CONSTANTS.contains(&tpe) { + return true; + } + if tpe.starts_with('\"') { + return tpe.ends_with('\"'); + } + if tpe.starts_with('\'') { + return tpe.ends_with('\''); + } + if ValueRef::str(tpe).str_isdigit().is_truthy() { + return true; + } + if ValueRef::str(tpe.replacen(".", "", 1).as_str()) + .str_isdigit() + .is_truthy() + && tpe.matches('.').count() < 2 + { + return true; + } + false +} + +/// is_dict_type returns the type string whether is a dict type +#[inline] +pub fn is_dict_type(tpe: &str) -> bool { + tpe.len() >= 2 && &tpe[0..1] == "{" && &tpe[tpe.len() - 1..] == "}" +} + +/// is_list_type returns the type string whether is a list type +#[inline] +pub fn is_list_type(tpe: &str) -> bool { + tpe.len() >= 2 && &tpe[0..1] == "[" && &tpe[tpe.len() - 1..] == "]" +} + +#[inline] +pub fn is_builtin_type(tpe: &str) -> bool { + BUILTIN_TYPES.contains(&tpe) +} + +/// is schema expected type +pub fn is_schema_type(expected_type: &str) -> bool { + if expected_type.is_empty() { + return true; + } + !is_list_type(expected_type) + && !is_dict_type(expected_type) + && !is_builtin_type(expected_type) + && !is_literal_type(expected_type) +} + +/// is union type +pub fn is_type_union(tpe: &str) -> bool { + let mut stack = String::new(); + let mut i = 0; + while i < tpe.chars().count() { + let c = tpe.chars().nth(i).unwrap(); + if c == '|' && stack.is_empty() { + return true; + } else if c == '[' || c == '{' { + stack.push(c); + } else if c == ']' || c == '}' { + stack.pop(); + } else if c == '\"' { + let t = &tpe[i..]; + let re = fancy_regex::Regex::new(r#""(?!"").*?(? bool { + let re = fancy_regex::Regex::new(NUMBER_MULTIPLIER_REGEX).unwrap(); + match re.is_match(tpe) { + Ok(ok) => ok, + _ => false, + } +} + +/// separate_kv split the union type and do not split '|' in dict and list +/// e.g., "int|str" -> vec!["int", "str"] +pub fn split_type_union(tpe: &str) -> Vec<&str> { + let mut i = 0; + let mut s_index = 0; + let mut stack = String::new(); + let mut types: Vec<&str> = vec![]; + while i < tpe.chars().count() { + let c = tpe.chars().nth(i).unwrap(); + if c == '|' && stack.is_empty() { + types.push(&tpe[s_index..i]); + s_index = i + 1; + } + // List/Dict type + else if c == '[' || c == '{' { + stack.push(c); + } + // List/Dict type + else if c == ']' || c == '}' { + stack.pop(); + } + // String literal type + else if c == '\"' { + let t = &tpe[i..]; + let re = fancy_regex::Regex::new(r#""(?!"").*?(? ("str", "str") +pub fn separate_kv(expected_type: &str) -> (String, String) { + let mut stack = String::new(); + for (n, c) in expected_type.chars().enumerate() { + if c == '[' || c == '{' { + stack.push(c) + } else if c == ']' { + if &stack[stack.len() - 1..] != "[" { + panic!("invalid type string {}", expected_type); + } + stack.pop(); + } else if c == '}' { + if &stack[stack.len() - 1..] != "{" { + panic!("invalid type string {}", expected_type); + } + stack.pop(); + } else if c == ':' { + if !stack.is_empty() { + panic!("invalid type string {}", expected_type); + } + return ( + expected_type[..n].to_string(), + expected_type[n + 1..].to_string(), + ); + } + } + ("".to_string(), "".to_string()) +} + +/// dereference_type function removes the first and last [] {} in the type string +/// e.g., "\[int\]" -> "int" +pub fn dereference_type(tpe: &str) -> String { + if tpe.len() > 1 + && ((&tpe[0..1] == "[" && &tpe[tpe.len() - 1..] == "]") + || (&tpe[0..1] == "{" && &tpe[tpe.len() - 1..] == "}")) + { + return tpe[1..tpe.len() - 1].to_string(); + } + tpe.to_string() +} + +#[cfg(test)] +mod test_value_type { + + use crate::*; + + #[test] + fn test_check_type() { + let cases = [ + // true cases + (ValueRef::int(0), "int", true), + (ValueRef::float(0.0), "float", true), + (ValueRef::bool(true), "bool", true), + (ValueRef::str("123"), "str", true), + (ValueRef::list_int(&[1, 2, 3]), "[int]", true), + (ValueRef::dict_str(&[("key", "value")]), "{str:}", true), + // false cases + (ValueRef::int(0), "str", false), + (ValueRef::str("0"), "int", false), + ]; + for (value, tpe, expected) in cases { + assert_eq!(check_type(&value, tpe), expected); + } + } + + #[test] + fn test_check_type_union() { + let cases = [ + // true cases + (ValueRef::int(0), "int|str", true), + (ValueRef::str("0"), "int|str", true), + (ValueRef::int(0), "int|[int|str]", true), + (ValueRef::list_int(&[1, 2, 3]), "int|[int|str]", true), + ( + ValueRef::list_str(&[1.to_string(), 2.to_string(), 3.to_string()]), + "int|[int|str]", + true, + ), + // false cases + (ValueRef::bool(true), "int|str", false), + ]; + for (value, tpe, expected) in cases { + assert_eq!(check_type_union(&value, tpe), expected); + } + } + + #[test] + fn test_check_type_literal() { + let cases = [ + // true cases + (ValueRef::int(0), "0", true), + (ValueRef::float(0.0), "0.0", true), + (ValueRef::str("123"), "\"123\"", true), + (ValueRef::bool(true), "True", true), + // false cases + (ValueRef::str("123"), "\"234\"", false), + (ValueRef::str("True"), "True", false), + ]; + for (value, tpe, expected) in cases { + assert_eq!(check_type_literal(&value, tpe), expected); + } + } + + #[test] + fn test_check_number_multiplier_type() { + let cases = [ + // true cases + (ValueRef::unit(1024.0, 1, "Ki"), "1Ki", true), + ( + ValueRef::unit(1024.0, 1, "Ki"), + NUMBER_MULTIPLIER_TYPE, + true, + ), + // false cases + (ValueRef::unit(1024.0, 1, "Ki"), "1Mi", false), + ]; + for (value, tpe, expected) in cases { + assert_eq!(check_number_multiplier_type(&value, tpe), expected); + } + } + + #[test] + fn test_check_type_dict() { + let cases = [ + // true cases + (ValueRef::dict_str(&[("key", "value")]), "{str:}", true), + (ValueRef::dict_int(&[("key", 1)]), "{str:int}", true), + // false cases + (ValueRef::int(0), "int", false), + (ValueRef::float(0.0), "float", false), + (ValueRef::dict_str(&[("key", "value")]), "{str:int}", false), + (ValueRef::dict_int(&[("key", 1)]), "{str:str}", false), + ]; + for (value, tpe, expected) in cases { + assert_eq!(check_type_dict(&value, tpe), expected); + } + } + + #[test] + fn test_check_type_list() { + let cases = [ + // true cases + (ValueRef::list_int(&[1, 2, 3]), "[int]", true), + (ValueRef::list_int(&[1, 2, 3]), "[]", true), + (ValueRef::list_int(&[1, 2, 3]), "[any]", true), + (ValueRef::list_int(&[1, 2, 3]), "[int|str]", true), + ( + ValueRef::list_str(&[1.to_string(), 2.to_string(), 3.to_string()]), + "[str]", + true, + ), + ( + ValueRef::list_str(&[1.to_string(), 2.to_string(), 3.to_string()]), + "[]", + true, + ), + ( + ValueRef::list_str(&[1.to_string(), 2.to_string(), 3.to_string()]), + "[any]", + true, + ), + ( + ValueRef::list_str(&[1.to_string(), 2.to_string(), 3.to_string()]), + "[int|str]", + true, + ), + // false cases + (ValueRef::int(0), "int", false), + (ValueRef::float(0.0), "float", false), + (ValueRef::list_int(&[1, 2, 3]), "[str]", false), + (ValueRef::list_int(&[1, 2, 3]), "[bool]", false), + ]; + for (value, tpe, expected) in cases { + assert_eq!(check_type_list(&value, tpe), expected); + } + } + + #[test] + fn test_match_builtin_type() { + let cases = [ + // true cases + (ValueRef::int(0), "int", true), + (ValueRef::int(1), "int", true), + (ValueRef::float(0.0), "float", true), + (ValueRef::float(1.0), "float", true), + (ValueRef::bool(true), "bool", true), + (ValueRef::bool(false), "bool", true), + (ValueRef::str("test"), "str", true), + (ValueRef::str("''\"''"), "str", true), + // false cases + (ValueRef::float(0.0), "int", false), + (ValueRef::bool(true), "float", false), + (ValueRef::str("test"), "bool", false), + (ValueRef::int(1), "str", false), + ]; + for (value, tpe, expected) in cases { + assert_eq!(match_builtin_type(&value, tpe), expected); + } + } + + #[test] + fn test_is_literal_type() { + let cases = [ + // true cases + ("123", true), + ("\"123\"", true), + ("True", true), + ("False", true), + ("1.0", true), + // false cases + ("int", false), + ("float", false), + ("str", false), + ("bool", false), + ("", false), + ("int8", false), + ("string", false), + ("any", false), + ("[", false), + ("]", false), + ]; + for (value, expected) in cases { + assert_eq!(is_literal_type(value), expected); + } + } + + #[test] + fn test_is_list_type() { + let cases = [ + // true cases + ("[]", true), + ("[int]", true), + ("[str]", true), + ("[[str]]", true), + ("[str|int]", true), + ("[pkg.Schema]", true), + // false cases + ("int", false), + ("float", false), + ("str", false), + ("bool", false), + ("", false), + ("int8", false), + ("string", false), + ("[", false), + ("]", false), + ]; + for (value, expected) in cases { + assert_eq!(is_list_type(value), expected); + } + } + + #[test] + fn test_is_dict_type() { + let cases = [ + // true cases + ("{}", true), + ("{:}", true), + ("{str:}", true), + ("{str:str}", true), + ("{str: str}", true), + ("{str:int}", true), + ("{str: int}", true), + ("{str:{str:}}", true), + ("{str:{str:str}}", true), + ("{str:str|int}", true), + ("{str:pkg.Schema}", true), + // false cases + ("int", false), + ("float", false), + ("str", false), + ("bool", false), + ("", false), + ("int8", false), + ("string", false), + ("{", false), + ("}", false), + ]; + for (value, expected) in cases { + assert_eq!(is_dict_type(value), expected); + } + } + + #[test] + fn test_is_type_union() { + let cases = [ + ("A|B|C", true), + ("'123'|'456'|'789'", true), + ("'|'|'||'|'|||'", true), + ("\"aa\\\"ab|\"|\"aa\\\"abccc\"", true), + ("[\"|\"]|\"\"", true), + ("{str:\"|\"}|\"|\"", true), + ("\"aa\\\"ab|\"", false), + ("\"|aa\\\"ab|\"", false), + ]; + for (value, expected) in cases { + assert_eq!(is_type_union(value), expected); + } + } + + #[test] + fn test_split_type_union() { + let cases = [ + ("", vec![""]), + ("str|int", vec!["str", "int"]), + ("str|int|bool", vec!["str", "int", "bool"]), + ("str|[str]", vec!["str", "[str]"]), + ("str|{str:int}", vec!["str", "{str:int}"]), + ("A|B|C", vec!["A", "B", "C"]), + ("'123'|'456'|'789'", vec!["'123'", "'456'", "'789'"]), + ("'|'|'||'|'|||'", vec!["'|'", "'||'", "'|||'"]), + ("{str:\"|\"}|\"|\"", vec!["{str:\"|\"}", "\"|\""]), + ]; + for (value, expected) in cases { + assert_eq!(split_type_union(value), expected); + } + } + + #[test] + fn test_separate_kv() { + let cases = [ + ("", ("", "")), + ("str:str", ("str", "str")), + ("str:[str]", ("str", "[str]")), + ("str:[str]", ("str", "[str]")), + ("str:{str:int}", ("str", "{str:int}")), + ]; + for (value, expected) in cases { + let expected = (expected.0.to_string(), expected.1.to_string()); + assert_eq!(separate_kv(value), expected); + } + } + + #[test] + fn test_dereference_type() { + let cases = [ + ("", ""), + ("[]", ""), + ("{}", ""), + ("[int]", "int"), + ("{str:str}", "str:str"), + ]; + for (value, expected) in cases { + assert_eq!(dereference_type(value), expected); + } + } +} diff --git a/kclvm/runtime/src/value/val_unary.rs b/kclvm/runtime/src/value/val_unary.rs new file mode 100644 index 000000000..06bf047a0 --- /dev/null +++ b/kclvm/runtime/src/value/val_unary.rs @@ -0,0 +1,88 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +impl ValueRef { + // +x + pub fn unary_plus(&self) -> Self { + match &*self.rc { + Value::int_value(ref a) => Self::int(*a), + Value::float_value(ref a) => Self::float(*a), + _ => panic!("bad operand type for unary +: '{}'", self.type_str()), + } + } + + // -x + pub fn unary_minus(&self) -> Self { + match &*self.rc { + Value::int_value(ref a) => Self::int(0 - *a), + Value::float_value(ref a) => Self::float(0.0 - *a), + _ => panic!("bad operand type for unary -: '{}'", self.type_str()), + } + } + + // ~ x + pub fn unary_not(&self) -> Self { + Self::int(!self.as_int()) + } + + // not x + pub fn unary_l_not(&self) -> Self { + Self::bool(!self.is_truthy()) + } +} + +#[cfg(test)] +mod test_value_unary { + use crate::*; + + #[test] + fn test_unary_plus() { + let cases = [(0, 0), (2, 2), (-2, -2)]; + for (value, expected) in cases { + assert_eq!(ValueRef::int(value).unary_plus().as_int(), expected); + } + } + + #[test] + fn test_unary_minus_not() { + let cases = [(0, 0), (2, -2), (-2, 2)]; + for (value, expected) in cases { + assert_eq!(ValueRef::int(value).unary_minus().as_int(), expected); + } + } + + #[test] + fn test_unary_not() { + let cases = [(0, -1), (-1, 0), (2, -3), (-3, 2), (0xFF, -256)]; + for (value, expected) in cases { + assert_eq!(ValueRef::int(value).unary_not().as_int(), expected); + } + } + + #[test] + fn test_unary_l_not() { + let cases = [ + // true cases + (ValueRef::undefined(), true), + (ValueRef::none(), true), + (ValueRef::int(0), true), + (ValueRef::float(0.0f64), true), + (ValueRef::bool(false), true), + (ValueRef::str(""), true), + (ValueRef::list(Some(&[])), true), + (ValueRef::dict(Some(&[])), true), + // false cases + (ValueRef::int(1), false), + (ValueRef::float(2.0f64), false), + (ValueRef::bool(true), false), + (ValueRef::str("s"), false), + (ValueRef::list_int(&[0]), false), + (ValueRef::dict_str(&[("key", "value")]), false), + ]; + for (value, expected) in cases { + let result = value.unary_l_not().as_bool(); + assert_eq!(result, expected); + } + } +} diff --git a/kclvm/runtime/src/value/val_union.rs b/kclvm/runtime/src/value/val_union.rs new file mode 100644 index 000000000..ffd8ce145 --- /dev/null +++ b/kclvm/runtime/src/value/val_union.rs @@ -0,0 +1,386 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +use crate::unification::value_subsume; +use crate::*; + +impl ValueRef { + fn do_union( + &self, + x: &Self, + should_list_override: bool, + should_idempotent_check: bool, + should_config_resolve: bool, + ) -> Self { + let union_fn = |obj: &DictValue, delta: &DictValue| { + let result_dict = get_ref_mut(obj); + // Update attribute map + for (k, v) in &delta.ops { + result_dict.ops.insert(k.clone(), v.clone()); + } + // Update index map + for (k, v) in &delta.insert_indexs { + result_dict.insert_indexs.insert(k.clone(), *v); + } + for (k, v) in &delta.values { + let operation = if let Some(op) = delta.ops.get(k) { + op + } else { + &ConfigEntryOperationKind::Union + }; + let index = if let Some(idx) = delta.insert_indexs.get(k) { + *idx + } else { + -1 + }; + if !result_dict.values.contains_key(k) { + result_dict.values.insert(k.clone(), v.clone()); + } else { + match operation { + ConfigEntryOperationKind::Union => { + if should_idempotent_check + && obj.values.contains_key(k) + && !value_subsume(v, obj.values.get(k).unwrap(), false) + { + panic!("conflicting values on the attribute '{}' between {:?} and {:?}", k, self.to_string(), x.to_string()); + } + let value = obj.values.get(k).unwrap().union( + v, + false, + should_list_override, + should_idempotent_check, + should_config_resolve, + ); + result_dict.values.insert(k.clone(), value); + } + ConfigEntryOperationKind::Override => { + if index < 0 { + result_dict.values.insert(k.clone(), v.clone()); + } else { + let origin_value = result_dict.values.get_mut(k).unwrap(); + if !origin_value.is_list() { + panic!("only list attribute can be inserted value"); + } + if v.is_none_or_undefined() { + origin_value.list_remove_at(index as usize); + } else { + origin_value.list_set(index as usize, v); + } + } + } + ConfigEntryOperationKind::Insert => { + let value = v.deep_copy(); + let origin_value = result_dict.values.get_mut(k).unwrap(); + if origin_value.is_none_or_undefined() { + let list = ValueRef::list(None); + result_dict.values.insert(k.to_string(), list); + } + let origin_value = result_dict.values.get_mut(k).unwrap(); + match (&*origin_value.rc, &*value.rc) { + (Value::list_value(origin_value), Value::list_value(value)) => { + // As RefMut + let origin_value: &mut ListValue = unsafe { + &mut *(origin_value as *const ListValue as *mut ListValue) + }; + // As RefMut + let value: &mut ListValue = unsafe { + &mut *(value as *const ListValue as *mut ListValue) + }; + if index == -1 { + origin_value.values.append(&mut value.clone().values); + } else if index >= 0 { + let mut insert_index = index; + for v in &value.values { + origin_value + .values + .insert(insert_index as usize, v.clone()); + insert_index += 1; + } + } + } + _ => panic!("only list attribute can be inserted value"), + } + } + } + } + } + self.clone() + }; + match (&*self.rc, &*x.rc) { + (Value::list_value(obj), Value::list_value(delta)) => { + // Clone reference + let mut result_list = self.clone(); + if should_list_override { + return result_list; + } + let length = if obj.values.len() > delta.values.len() { + obj.values.len() + } else { + delta.values.len() + }; + let obj_len = obj.values.len(); + let delta_len = delta.values.len(); + for idx in 0..length { + if idx >= obj_len { + result_list.list_append(&delta.values[idx]); + } else if idx < delta_len { + let value = obj.values[idx].union( + &delta.values[idx], + false, + should_list_override, + should_idempotent_check, + should_config_resolve, + ); + result_list.list_set(idx, &value); + } + } + result_list + } + (Value::dict_value(obj), Value::dict_value(delta)) => union_fn(obj, delta), + (Value::schema_value(obj), Value::dict_value(delta)) => { + let name = &obj.name; + let pkgpath = &obj.pkgpath; + let obj_value = obj.config.as_ref(); + let result = union_fn(obj_value, delta); + let mut common_keys = obj.config_keys.clone(); + let mut other_keys: Vec = delta.values.keys().cloned().collect(); + common_keys.append(&mut other_keys); + let schema = result.dict_to_schema(name.as_str(), pkgpath.as_str(), &common_keys); + if should_config_resolve { + resolve_schema(&schema, &common_keys) + } else { + schema + } + } + (Value::schema_value(obj), Value::schema_value(delta)) => { + let name = &obj.name; + let pkgpath = &obj.pkgpath; + let obj_value = obj.config.as_ref(); + let delta_value = delta.config.as_ref(); + let result = union_fn(obj_value, delta_value); + let mut common_keys = obj.config_keys.clone(); + let mut other_keys = delta.config_keys.clone(); + common_keys.append(&mut other_keys); + let schema = result.dict_to_schema(name.as_str(), pkgpath.as_str(), &common_keys); + if should_config_resolve { + resolve_schema(&schema, &common_keys) + } else { + schema + } + } + (Value::dict_value(obj), Value::schema_value(delta)) => { + let name = &delta.name; + let pkgpath = &delta.pkgpath; + let delta_value = delta.config.as_ref(); + let result = union_fn(obj, delta_value); + let mut common_keys = delta.config_keys.clone(); + let mut other_keys: Vec = obj.values.keys().cloned().collect(); + common_keys.append(&mut other_keys); + let schema = + result.dict_to_schema(name.as_str(), pkgpath.as_str(), &delta.config_keys); + if should_config_resolve { + resolve_schema(&schema, &common_keys) + } else { + schema + } + } + _ => { + panic!( + "union failure, expect {:?}, got {:?}", + self.type_str(), + x.type_str() + ); + } + } + } + + pub fn union( + &self, + x: &Self, + or_mode: bool, + should_list_override: bool, + should_idempotent_check: bool, + should_config_resolve: bool, + ) -> Self { + if self.is_none_or_undefined() { + return x.clone(); + } + if x.is_none_or_undefined() { + return self.clone(); + } + match (&*self.rc, &*x.rc) { + ( + Value::list_value(_) | Value::dict_value(_) | Value::schema_value(_), + Value::list_value(_) | Value::dict_value(_) | Value::schema_value(_), + ) => self.do_union( + x, + should_list_override, + should_idempotent_check, + should_config_resolve, + ), + _ => { + if or_mode { + match (&*self.rc, &*x.rc) { + (Value::int_value(a), Value::int_value(b)) => Self::int(*a | *b), + _ => { + panic!( + "unsupported operand type(s) for |: '{:?}' and '{:?}'", + self.type_str(), + x.type_str() + ); + } + } + } else if x.is_none_or_undefined() { + self.clone() + } else { + x.clone() + } + } + } + } +} + +#[cfg(test)] +mod test_value_union { + + use crate::*; + + #[test] + fn test_list_union() { + let cases = [ + ("[0]", "[1, 2]", "[1, 2]"), + ("[1, 2]", "[2]", "[2, 2]"), + ("[0, 0]", "[1, 2]", "[1, 2]"), + ]; + for (left, right, expected) in cases { + let left_value = ValueRef::from_json(left).unwrap(); + let right_value = ValueRef::from_json(right).unwrap(); + let value = left_value.bin_bit_or(&right_value); + assert_eq!(value.to_json_string(), expected); + } + } + + #[test] + fn test_dict_union() { + let cases = [ + ( + vec![("key", "value", ConfigEntryOperationKind::Union, -1)], + vec![("key", "value", ConfigEntryOperationKind::Union, -1)], + vec![("key", "value", ConfigEntryOperationKind::Union, -1)], + ), + ( + vec![("key", "value", ConfigEntryOperationKind::Override, -1)], + vec![("key", "value", ConfigEntryOperationKind::Override, -1)], + vec![("key", "value", ConfigEntryOperationKind::Override, -1)], + ), + ( + vec![("key", "value1", ConfigEntryOperationKind::Union, -1)], + vec![("key", "value2", ConfigEntryOperationKind::Override, -1)], + vec![("key", "value2", ConfigEntryOperationKind::Override, -1)], + ), + ( + vec![ + ("key1", "value1", ConfigEntryOperationKind::Union, -1), + ("key2", "value2", ConfigEntryOperationKind::Union, -1), + ], + vec![ + ( + "key1", + "override_value1", + ConfigEntryOperationKind::Override, + -1, + ), + ( + "key2", + "override_value2", + ConfigEntryOperationKind::Override, + -1, + ), + ], + vec![ + ( + "key1", + "override_value1", + ConfigEntryOperationKind::Override, + -1, + ), + ( + "key2", + "override_value2", + ConfigEntryOperationKind::Override, + -1, + ), + ], + ), + ]; + for (left_entries, right_entries, expected) in cases { + let mut left_value = ValueRef::dict(None); + let mut right_value = ValueRef::dict(None); + for (key, val, op, index) in left_entries { + left_value.dict_update_entry(key, &ValueRef::str(val), &op, &index); + } + for (key, val, op, index) in right_entries { + right_value.dict_update_entry(key, &ValueRef::str(val), &op, &index); + } + let result = left_value.bin_bit_or(&right_value); + for (key, val, op, index) in expected { + let result_dict = result.as_dict_ref(); + let result_val = result_dict.values.get(key).unwrap().as_str(); + let result_op = result_dict.ops.get(key).unwrap(); + let result_index = result_dict.insert_indexs.get(key).unwrap(); + assert_eq!(result_val, val); + assert_eq!(*result_op, op); + assert_eq!(*result_index, index); + } + } + } + #[test] + fn test_dict_union_insert() { + let cases = [ + ( + vec![("key", vec![0, 1], ConfigEntryOperationKind::Override, -1)], + vec![("key", vec![2, 3], ConfigEntryOperationKind::Insert, -1)], + vec![( + "key", + vec![0, 1, 2, 3], + ConfigEntryOperationKind::Insert, + -1, + )], + ), + ( + vec![("key", vec![0, 1], ConfigEntryOperationKind::Override, -1)], + vec![("key", vec![2, 3], ConfigEntryOperationKind::Insert, 0)], + vec![("key", vec![2, 3, 0, 1], ConfigEntryOperationKind::Insert, 0)], + ), + ( + vec![("key", vec![0, 1], ConfigEntryOperationKind::Override, -1)], + vec![("key", vec![2, 3], ConfigEntryOperationKind::Insert, 1)], + vec![("key", vec![0, 2, 3, 1], ConfigEntryOperationKind::Insert, 1)], + ), + ]; + for (left_entries, right_entries, expected) in cases { + let mut left_value = ValueRef::dict(None); + let mut right_value = ValueRef::dict(None); + for (key, val, op, index) in left_entries { + left_value.dict_update_entry(key, &ValueRef::list_int(val.as_slice()), &op, &index); + } + for (key, val, op, index) in right_entries { + right_value.dict_update_entry( + key, + &ValueRef::list_int(val.as_slice()), + &op, + &index, + ); + } + let result = left_value.bin_bit_or(&right_value); + for (key, val, op, index) in expected { + let result_dict = result.as_dict_ref(); + let result_val = result_dict.values.get(key).unwrap(); + let result_op = result_dict.ops.get(key).unwrap(); + let result_index = result_dict.insert_indexs.get(key).unwrap(); + assert_eq!(result_val.clone(), ValueRef::list_int(val.as_slice())); + assert_eq!(*result_op, op); + assert_eq!(*result_index, index); + } + } + } +} diff --git a/kclvm/runtime/src/value/val_yaml.rs b/kclvm/runtime/src/value/val_yaml.rs new file mode 100644 index 000000000..eaca4e9fc --- /dev/null +++ b/kclvm/runtime/src/value/val_yaml.rs @@ -0,0 +1,205 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +extern crate serde_json; +extern crate serde_yaml; + +use crate::*; + +#[derive(Debug, Default)] +pub struct YamlEncodeOptions { + pub sort_keys: bool, + pub ignore_private: bool, + pub ignore_none: bool, +} + +impl ValueRef { + pub fn from_yaml(s: &str) -> Option { + let json_value: serde_json::Value = serde_yaml::from_str(s).unwrap(); + match serde_json::to_string(&json_value) { + Ok(s) => Self::from_json(s.as_ref()), + _ => None, + } + } + + pub fn to_yaml(&self) -> Vec { + let json = self.to_json_string(); + let yaml_value: serde_yaml::Value = serde_json::from_str(json.as_ref()).unwrap(); + match serde_yaml::to_string(&yaml_value) { + Ok(s) => s.into_bytes(), + _ => Vec::new(), + } + } + + pub fn to_yaml_string(&self) -> String { + let json = self.to_json_string(); + let yaml_value: serde_yaml::Value = serde_json::from_str(json.as_ref()).unwrap(); + match serde_yaml::to_string(&yaml_value) { + Ok(s) => { + let s = s.strip_prefix("---\n").unwrap_or_else(|| s.as_ref()); + s.to_string() + } + Err(err) => panic!("{}", err), + } + } + + pub fn to_yaml_string_with_options(&self, opt: &YamlEncodeOptions) -> String { + let x = self.yaml_clone_with_filter(opt); + x.to_yaml_string() + } + + fn yaml_clone_with_filter(&self, opt: &YamlEncodeOptions) -> Self { + match &*self.rc { + Value::undefined => ValueRef::undefined(), + Value::none => ValueRef::none(), + + Value::bool_value(ref v) => ValueRef::bool(*v), + Value::int_value(ref v) => ValueRef::int(*v), + Value::float_value(ref v) => ValueRef::float(*v), + Value::str_value(ref v) => ValueRef::str(v.as_ref()), + Value::unit_value(ref v, ref raw, ref unit) => ValueRef::unit(*v, *raw, unit), + Value::list_value(ref v) => { + let mut list = ValueRef::list(None); + for x in v.values.iter() { + match *x.rc { + Value::undefined => { + continue; + } + Value::none => { + if !opt.ignore_none { + list.list_append(&x.yaml_clone_with_filter(opt)); + } + } + Value::func_value(_) => { + // ignore func + } + _ => { + list.list_append(&x.yaml_clone_with_filter(opt)); + } + } + } + list + } + Value::dict_value(ref v) => { + let mut dict = ValueRef::dict(None); + for (key, val) in v.values.iter() { + if opt.ignore_private && (*key).starts_with(KCL_PRIVATE_VAR_PREFIX) { + continue; + } + match *val.rc { + Value::undefined => { + continue; + } + Value::none => { + if !opt.ignore_none { + dict.dict_insert( + key, + &val.yaml_clone_with_filter(opt), + Default::default(), + 0, + ); + } + } + Value::func_value(_) => { + // ignore func + } + _ => { + dict.dict_insert( + key, + &val.yaml_clone_with_filter(opt), + Default::default(), + 0, + ); + } + } + } + dict + } + + Value::schema_value(ref v) => { + let mut dict = ValueRef::dict(None); + for (key, val) in v.config.values.iter() { + if opt.ignore_private && (*key).starts_with(KCL_PRIVATE_VAR_PREFIX) { + continue; + } + match *val.rc { + Value::undefined => { + continue; + } + Value::none => { + if !opt.ignore_none { + dict.dict_insert( + key, + &val.yaml_clone_with_filter(opt), + Default::default(), + 0, + ); + } + } + Value::func_value(_) => { + // ignore func + } + _ => { + dict.dict_insert( + key, + &val.yaml_clone_with_filter(opt), + Default::default(), + 0, + ); + } + } + } + dict + } + Value::func_value(_) => ValueRef::undefined(), + } + } +} + +#[cfg(test)] +mod test_value_yaml { + use crate::*; + + #[test] + fn test_value_from_yaml() { + let cases = [ + ("a: 1\n", ValueRef::dict(Some(&[("a", &ValueRef::int(1))]))), + ( + "a: 1\nb: 2\n", + ValueRef::dict(Some(&[("a", &ValueRef::int(1)), ("b", &ValueRef::int(2))])), + ), + ( + "a: [1, 2, 3]\nb: \"s\"\n", + ValueRef::dict(Some(&[ + ("a", &ValueRef::list_int(&[1, 2, 3])), + ("b", &ValueRef::str("s")), + ])), + ), + ]; + for (yaml_str, expected) in cases { + let result = ValueRef::from_yaml(yaml_str); + assert_eq!(result, Some(expected)); + } + } + + #[test] + fn test_value_to_yaml_string() { + let cases = [ + (ValueRef::dict(Some(&[("a", &ValueRef::int(1))])), "a: 1\n"), + ( + ValueRef::dict(Some(&[("a", &ValueRef::int(1)), ("b", &ValueRef::int(2))])), + "a: 1\nb: 2\n", + ), + ( + ValueRef::dict(Some(&[ + ("a", &ValueRef::list_int(&[1, 2, 3])), + ("b", &ValueRef::str("s")), + ])), + "a:\n - 1\n - 2\n - 3\nb: s\n", + ), + ]; + for (value, expected) in cases { + let result = ValueRef::to_yaml_string(&value); + assert_eq!(result, expected); + } + } +} diff --git a/kclvm/runtime/src/yaml/mod.rs b/kclvm/runtime/src/yaml/mod.rs new file mode 100644 index 000000000..a6dcac071 --- /dev/null +++ b/kclvm/runtime/src/yaml/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub mod yaml; +pub use self::yaml::*; diff --git a/kclvm/runtime/src/yaml/yaml.rs b/kclvm/runtime/src/yaml/yaml.rs new file mode 100644 index 000000000..435fed98f --- /dev/null +++ b/kclvm/runtime/src/yaml/yaml.rs @@ -0,0 +1,75 @@ +//! KCL yaml system module +//! +//! Copyright 2021 The KCL Authors. All rights reserved. + +use crate::*; + +#[allow(non_camel_case_types)] +type kclvm_value_ref_t = ValueRef; + +// def KMANGLED_encode(data, sort_keys=False, ignore_private=False, ignore_none=False): + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_yaml_encode( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + + let mut opt = YamlEncodeOptions::default(); + if let Some(sort_keys) = kwargs.kwarg_bool("sort_keys", None) { + opt.sort_keys = sort_keys; + } + if let Some(ignore_private) = kwargs.kwarg_bool("ignore_private", None) { + opt.ignore_private = ignore_private; + } + if let Some(ignore_none) = kwargs.kwarg_bool("ignore_none", None) { + opt.ignore_none = ignore_none; + } + + if let Some(arg0) = args.arg_i(0) { + let s = ValueRef::str(arg0.to_yaml_string_with_options(&opt).as_ref()); + return s.into_raw(); + } + panic!("encode() missing 1 required positional argument: 'value'") +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_yaml_decode( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(arg0) = args.arg_i(0) { + if let Some(x) = ValueRef::from_yaml(arg0.as_str().as_ref()) { + return x.into_raw(); + } + } + panic!("decode() missing 1 required positional argument: 'value'") +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_yaml_dump_to_file( + _ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + _kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + + if let Some(data) = args.arg_i(0) { + if let Some(filename) = args.arg_i(0) { + let yaml = data.to_yaml_string(); + let filename = filename.as_str(); + + std::fs::write(filename, yaml).expect("Unable to write file"); + } + } + panic!("dump_to_file() missing 2 required positional arguments: 'data' and 'filename'") +} diff --git a/kclvm/runtime/tools/kclvm-runtime-gen-api/Makefile b/kclvm/runtime/tools/kclvm-runtime-gen-api/Makefile new file mode 100644 index 000000000..e5dd18fe0 --- /dev/null +++ b/kclvm/runtime/tools/kclvm-runtime-gen-api/Makefile @@ -0,0 +1,11 @@ +default: + go run main.go \ + -root=../../src \ + -c-api=../../src/_kclvm.h \ + -ll-api=../../src/_kclvm.ll \ + -rust-api-enum=../../src/_kclvm.rs \ + -rust-api-addr=../../src/_kclvm_addr.rs + + cargo fmt + + llvm-as ../../src/_kclvm.ll diff --git a/kclvm/runtime/tools/kclvm-runtime-gen-api/main.go b/kclvm/runtime/tools/kclvm-runtime-gen-api/main.go new file mode 100644 index 000000000..34f1e221c --- /dev/null +++ b/kclvm/runtime/tools/kclvm-runtime-gen-api/main.go @@ -0,0 +1,374 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +package main + +import ( + "bytes" + "flag" + "fmt" + "os" + "path/filepath" + "regexp" + "sort" + "strings" + "text/template" +) + +var ( + flagRoot = flag.String("root", ".", "set kclvm-runtime root") + flagGenCApi = flag.String("c-api", "", "set c header file") + flagGenLLApi = flag.String("ll-api", "", "set llvm-ll file") + flagGenRustApiEnum = flag.String("rust-api-enum", "", "set rust-api-enum file") + flagGenRustApiAddr = flag.String("rust-api-addr", "", "set rust-api-addr file") +) + +func main() { + flag.Parse() + + specList := LoadAllApiSpec(*flagRoot) + if filename := *flagGenCApi; filename != "" { + src := genCApi(specList) + if err := os.WriteFile(filename, []byte(src), 0666); err != nil { + panic(err) + } + } + if filename := *flagGenLLApi; filename != "" { + src := genLLApi(specList) + if err := os.WriteFile(filename, []byte(src), 0666); err != nil { + panic(err) + } + } + if filename := *flagGenRustApiEnum; filename != "" { + src := genRustApiEnum(specList) + if err := os.WriteFile(filename, []byte(src), 0666); err != nil { + panic(err) + } + } + if filename := *flagGenRustApiAddr; filename != "" { + src := genRustApiAddr(specList) + if err := os.WriteFile(filename, []byte(src), 0666); err != nil { + panic(err) + } + } +} + +func genCApi(specs []ApiSpec) string { + tmpl, err := template.New("c-api").Parse(tmplCApi) + if err != nil { + panic(err) + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, specs) + if err != nil { + panic(err) + } + return fmtCode(buf.String()) +} + +func genLLApi(specs []ApiSpec) string { + tmpl, err := template.New("ll-api").Parse(tmplLLApi) + if err != nil { + panic(err) + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, specs) + if err != nil { + panic(err) + } + return fmtCode(buf.String()) +} + +func genRustApiEnum(specs []ApiSpec) string { + tmpl, err := template.New("rust-api-enum").Parse(tmplRustEnum) + if err != nil { + panic(err) + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, specs) + if err != nil { + panic(err) + } + return fmtCode(buf.String()) +} + +func genRustApiAddr(specs []ApiSpec) string { + tmpl, err := template.New("rust-api-addr").Parse(tmplRustAddr) + if err != nil { + panic(err) + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, specs) + if err != nil { + panic(err) + } + return fmtCode(buf.String()) +} + +func fmtCode(s string) string { + for { + if !strings.Contains(s, "\n\n\n") { + s = strings.TrimSpace(s) + "\n" + return s + } + s = strings.ReplaceAll(s, "\n\n\n", "\n\n") + } +} + +// ---------------------------------------------------------------------------- + +const ( + apiSpecPrefix_Name = "// api-spec:" + apiSpecPrefix_CApi = "// api-spec(c):" + apiSpecPrefix_LLApi = "// api-spec(llvm):" +) + +type ApiSpec struct { + File string + Line int + Name string // api-spec: kclvm_context_new + SpecC string // api-spec(c): i32 kclvm_context_new(); + SpecLL string // api-spec(llvm): declare i32* @kclvm_context_new() + IsType bool +} + +func LoadAllApiSpec(root string) []ApiSpec { + m := make(map[string]ApiSpec) + filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + panic(err) + } + if info.IsDir() { + return nil + } + + if !strings.HasSuffix(path, ".rs") { + return nil + } + + data, err := os.ReadFile(path) + if err != nil { + panic(err) + } + + var spec ApiSpec + for i, line := range strings.Split(string(data), "\n") { + line := strings.TrimSpace(line) + switch { + case strings.HasPrefix(line, apiSpecPrefix_Name): + spec.File = path + spec.Line = i + 1 + spec.Name = strings.TrimSpace(strings.TrimPrefix(line, apiSpecPrefix_Name)) + spec.IsType = strings.HasSuffix(spec.Name, "_t") + case strings.HasPrefix(line, apiSpecPrefix_CApi): + if spec.SpecC != "" { + spec.SpecC += " " + } + spec.SpecC += strings.TrimSpace(strings.TrimPrefix(line, apiSpecPrefix_CApi)) + case strings.HasPrefix(line, apiSpecPrefix_LLApi): + if spec.SpecLL != "" { + spec.SpecLL += " " + } + spec.SpecLL += strings.TrimSpace(strings.TrimPrefix(line, apiSpecPrefix_LLApi)) + default: + if matched, _ := regexp.MatchString(`//\s*api-spec`, line); matched { + panic(fmt.Errorf("%s:%d invalid 'api-spec'", path, i+1)) + } + if spec.Name != "" { + if x, ok := m[spec.Name]; ok { + fmt.Printf("WARN: %s:%d %s api-spec exits (%s:%d)\n", path, i+1, spec.Name, x.File, x.Line) + } + m[spec.Name] = spec + } + spec = ApiSpec{} + } + } + + return nil + }) + + var specs []ApiSpec + for _, x := range m { + specs = append(specs, x) + } + sort.Slice(specs, func(i, j int) bool { + return specs[i].Name < specs[j].Name + }) + return specs +} + +// ---------------------------------------------------------------------------- + +const tmplCApi = ` +{{$specList := .}} + +// Copyright 2021 The KCL Authors. All rights reserved. + +// Auto generated, DONOT EDIT!!! + +#pragma once + +#ifndef _kclvm_h_ +#define _kclvm_h_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// please keep same as 'kclvm/runtime/src/kind/mod.rs#Kind' + +enum kclvm_kind_t { + Invalid = 0, + + // only for value + + Undefined = 1, + None = 2, + + // for value & type + + Bool = 3, + Int = 4, + Float = 5, + Str = 6, + List = 7, + Dict = 8, + + Schema = 9, + Error = 10, + + // only for type + + Any = 11, + Union = 12, + + BoolLit = 13, + IntLit = 14, + FloatLit = 15, + StrLit = 16, + + Func = 17, + + // max num + + Max = 18, +}; + +{{range $_, $spec := $specList}} +{{if ($spec.IsType)}}{{$spec.SpecC}}{{end}} +{{end}} + +{{range $_, $spec := $specList}} +{{if (not $spec.IsType)}}{{$spec.SpecC}}{{end}} +{{end}} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _kclvm_h_ +` + +// ---------------------------------------------------------------------------- + +const tmplLLApi = ` +{{$specList := .}} + +; Copyright 2021 The KCL Authors. All rights reserved. + +; Auto generated, DONOT EDIT!!! + +{{range $_, $spec := $specList}} +{{if ($spec.IsType)}}{{$spec.SpecLL}}{{end}} +{{end}} + +{{range $_, $spec := $specList}} +{{if (not $spec.IsType)}}{{$spec.SpecLL}}{{end}} +{{end}} + +define void @__kcl_keep_link_runtime(%kclvm_value_ref_t* %_a, %kclvm_context_t* %_b) { + call %kclvm_value_ref_t*() @kclvm_value_None() + ret void +} +` + +// ---------------------------------------------------------------------------- + +const tmplRustEnum = ` +{{$specList := .}} + +// Copyright 2021 The KCL Authors. All rights reserved. + +// Auto generated, DONOT EDIT!!! + +#[allow(dead_code, non_camel_case_types)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum ApiType { + Value, +} + +impl std::fmt::Display for ApiType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + ApiType::Value => write!(f, "{:?}", "api::kclvm::Value"), + } + } +} + +impl ApiType { + #[allow(dead_code)] + pub fn name(&self) -> String { + return format!("{:?}", self); + } +} + +#[allow(dead_code, non_camel_case_types)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum ApiFunc { + {{range $_, $spec := $specList}}{{if (not $spec.IsType)}} + {{- $spec.Name}}, + {{end}}{{end}} +} + +impl std::fmt::Display for ApiFunc { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl ApiFunc { + #[allow(dead_code)] + pub fn name(&self) -> String { + return format!("{:?}", self); + } +} +` + +// ---------------------------------------------------------------------------- + +const tmplRustAddr = ` +{{$specList := .}} + +// Copyright 2021 The KCL Authors. All rights reserved. + +// Auto generated, DONOT EDIT!!! + +#[allow(dead_code)] +pub fn _kclvm_get_fn_ptr_by_name(name: &str) -> u64 { + match name { + {{- range $_, $spec := $specList -}}{{if (not $spec.IsType)}} + "{{$spec.Name}}" => crate::{{$spec.Name}} as *const () as u64, + {{- end}}{{end}} + _ => panic!("unknown {}", name), + } +} +` + +// ---------------------------------------------------------------------------- diff --git a/kclvm/runtime/tools/kclvm-runtime-gen-err-type/Makefile b/kclvm/runtime/tools/kclvm-runtime-gen-err-type/Makefile new file mode 100644 index 000000000..84bc7fbe3 --- /dev/null +++ b/kclvm/runtime/tools/kclvm-runtime-gen-err-type/Makefile @@ -0,0 +1,6 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +default: + kclvm ./main.py > ../../src/api/err_type.rs + +clean: diff --git a/kclvm/runtime/tools/kclvm-runtime-gen-err-type/main.py b/kclvm/runtime/tools/kclvm-runtime-gen-err-type/main.py new file mode 100644 index 000000000..c527cdbf0 --- /dev/null +++ b/kclvm/runtime/tools/kclvm-runtime-gen-err-type/main.py @@ -0,0 +1,24 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import kclvm.kcl.error as kcl_error + +# enum -> code +# kcl_error.ErrType.EvaluationError_TYPE.value[0] + +# code -> type +# x = kcl_error.ErrType((6,)) + +print( + """// Copyright 2021 The KCL Authors. All rights reserved. + +// Auto generated, DONOT EDIT!!! + +// python: kclvm.kcl.error.ErrType + +#[derive(Copy, Clone)] +#[allow(non_camel_case_types)] +pub enum ErrType {""" +) +for x in kcl_error.ErrType: + print(f" {x.name} = {x.value[0]},") +print("}") diff --git a/kclvm/sema/Cargo.lock b/kclvm/sema/Cargo.lock new file mode 100644 index 000000000..950d1f97d --- /dev/null +++ b/kclvm/sema/Cargo.lock @@ -0,0 +1,1773 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "annotate-snippets" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "cast" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa 0.4.8", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "enquote" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c36cb11dbde389f4096111698d8b567c0720e3452fd5ac3e6b4e47e1939932" +dependencies = [ + "thiserror", +] + +[[package]] +name = "fancy-regex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "fixedbitset" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" + +[[package]] +name = "fslock" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", + "rustc-rayon", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json_minimal" +version = "0.1.3" + +[[package]] +name = "kclvm-ast" +version = "0.1.0" +dependencies = [ + "kclvm-span", + "rustc_span", + "serde", + "serde_json", +] + +[[package]] +name = "kclvm-config" +version = "0.1.0" +dependencies = [ + "ahash", + "chrono", + "fslock", + "glob", + "indexmap", + "kclvm-version", + "pathdiff", + "ron", + "rust-crypto", + "serde", + "serde_yaml", + "toml", +] + +[[package]] +name = "kclvm-error" +version = "0.1.0" +dependencies = [ + "annotate-snippets", + "atty", + "indexmap", + "kclvm-span", + "rustc_span", + "termcolor", + "termize", + "tracing", +] + +[[package]] +name = "kclvm-lexer" +version = "0.1.0" +dependencies = [ + "kclvm-error", + "rustc_lexer", + "unic-emoji-char", +] + +[[package]] +name = "kclvm-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "kclvm-parser" +version = "0.1.0" +dependencies = [ + "bstr", + "either", + "enquote", + "kclvm-ast", + "kclvm-config", + "kclvm-error", + "kclvm-lexer", + "kclvm-sema", + "kclvm-span", + "num-bigint", + "rustc_data_structures", + "rustc_lexer", + "rustc_span", + "serde", + "serde_json", + "tracing", + "unicode_names2", +] + +[[package]] +name = "kclvm-runtime" +version = "0.1.0" +dependencies = [ + "ahash", + "base64", + "bstr", + "chrono", + "fancy-regex", + "indexmap", + "itertools", + "json_minimal", + "kclvm_runtime_internal_macros", + "libc", + "md5", + "num-integer", + "phf", + "regex", + "serde_json", + "serde_yaml", + "sha1", + "sha2 0.9.9", + "unic-ucd-bidi", + "unic-ucd-category", + "unicode-casing", +] + +[[package]] +name = "kclvm-sema" +version = "0.1.0" +dependencies = [ + "ahash", + "bit-set", + "bitflags", + "criterion", + "fancy-regex", + "indexmap", + "kclvm-ast", + "kclvm-error", + "kclvm-parser", + "kclvm-runtime", + "kclvm-span", + "once_cell", + "petgraph", + "phf", + "unicode_names2", +] + +[[package]] +name = "kclvm-span" +version = "0.1.0" +dependencies = [ + "kclvm-macros", + "rustc_span", + "scoped-tls", +] + +[[package]] +name = "kclvm-version" +version = "0.1.0" + +[[package]] +name = "kclvm_runtime_internal_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest 0.10.3", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "petgraph" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "plotters" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" + +[[package]] +name = "plotters-svg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871372391786ccec00d3c5d3d6608905b3d4db263639cfe075d3b60a736d115a" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ron" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86018df177b1beef6c7c8ef949969c4f7cb9a9344181b92486b23c79995bdaa4" +dependencies = [ + "base64", + "bitflags", + "serde", +] + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +dependencies = [ + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-rayon" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" +dependencies = [ + "crossbeam-deque", + "either", + "rustc-rayon-core", +] + +[[package]] +name = "rustc-rayon-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" + +[[package]] +name = "rustc_data_structures" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ena", + "indexmap", + "jobserver", + "libc", + "memmap2", + "parking_lot", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "stable_deref_trait", + "stacker", + "tempfile", + "tracing", + "winapi", +] + +[[package]] +name = "rustc_lexer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86aae0c77166108c01305ee1a36a1e77289d7dc6ca0a3cd91ff4992de2d16a5" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "rustc_span" +version = "0.0.0" +dependencies = [ + "cfg-if 0.1.10", + "md-5", + "rustc_data_structures", + "scoped-tls", + "sha-1", + "sha2 0.10.2", + "tracing", + "unicode-width", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termize" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80b9fa4360528139bc96100c160b7ae879f5567f49f1782b0b02035b0358ebf3" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dfce9f3241b150f36e8e54bb561a742d5daa1a47b5dd9a5ce369fd4a4db2210" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-category" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8d4591f5fcfe1bd4453baaf803c40e1b1e69ff8455c47620440b46efef91c0" +dependencies = [ + "matches", + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-casing" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "623f59e6af2a98bdafeb93fa277ac8e1e40440973001ca15cf4ae1541cd16d56" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "unicode_names2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d6678d7916394abad0d4b19df4d3802e1fd84abd7d701f39b75ee71b9e8cf1" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" + +[[package]] +name = "web-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/kclvm/sema/Cargo.toml b/kclvm/sema/Cargo.toml new file mode 100644 index 000000000..4eb251eba --- /dev/null +++ b/kclvm/sema/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "kclvm-sema" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +phf = { version = "0.9", features = ["macros"] } +ahash = "0.7.2" +indexmap = "1.0" +bit-set = "0.5.0" +bitflags = "1.2.1" +once_cell = "1.5.2" +fancy-regex = "0.7.1" +unicode_names2 = "0.4" +petgraph = "0.6.0" +kclvm-ast = {path = "../ast", version = "0.1.0"} +kclvm-runtime = {path = "../runtime", version = "0.1.0"} +kclvm-error = {path = "../error", version = "0.1.0"} +kclvm-span = {path = "../span", version = "0.1.0"} + +[dev-dependencies] +kclvm-parser = {path = "../parser", version = "0.1.0"} +criterion = "0.3" + +[[bench]] +name = "my_benchmark" +harness = false + diff --git a/kclvm/sema/benches/my_benchmark.rs b/kclvm/sema/benches/my_benchmark.rs new file mode 100644 index 000000000..5b0bc6fbf --- /dev/null +++ b/kclvm/sema/benches/my_benchmark.rs @@ -0,0 +1,19 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use kclvm_sema::ty::*; + +pub fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("sup", |b| { + b.iter(|| { + let types = vec![ + Type::int_lit(1), + Type::INT, + Type::union(&[Type::STR, Type::dict(Type::STR, Type::STR)]), + Type::dict(Type::ANY, Type::ANY), + ]; + sup(&types); + }) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/kclvm/sema/src/builtin/decorator.rs b/kclvm/sema/src/builtin/decorator.rs new file mode 100644 index 000000000..446c36e4e --- /dev/null +++ b/kclvm/sema/src/builtin/decorator.rs @@ -0,0 +1,71 @@ +use std::rc::Rc; + +use indexmap::IndexMap; +use once_cell::sync::Lazy; + +use crate::ty::{Parameter, Type}; + +macro_rules! register_decorator { + ($($name:ident => $ty:expr)*) => ( + // Builtin decorator map. + pub const BUILTIN_DECORATORS: Lazy> = Lazy::new(|| { + let mut builtin_mapping = IndexMap::default(); + $( builtin_mapping.insert(stringify!($name).to_string(), $ty); )* + builtin_mapping + }); + pub static DECORATOR_NAMES: &[&str] = &[ + $( stringify!($name), )* + ]; + ) +} + +register_decorator! { + deprecated => Type::function( + None, + Rc::new(Type::ANY), + &[ + Parameter { + name: "version".to_string(), + ty: Rc::new(Type::STR), + has_default: true, + }, + Parameter { + name: "reason".to_string(), + ty: Rc::new(Type::STR), + has_default: true, + }, + Parameter { + name: "strict".to_string(), + ty: Rc::new(Type::BOOL), + has_default: true, + }, + ], + r#"This decorator is used to get the deprecation message according to the wrapped key-value pair. + + Examples + -------- + @deprecated(version="v1.16", reason="The age attribute was deprecated", strict=True) + schema Person: + name: str + age: int + "#, + false, + None, + ) + info => Type::function( + None, + Rc::new(Type::ANY), + &[], + r#"Info decorator is used to mark some compile-time information for external API queries + + Examples + -------- + @info(message="User message") + schema Person: + name: str + age: int + "#, + true, + Some(0), + ) +} diff --git a/kclvm/sema/src/builtin/mod.rs b/kclvm/sema/src/builtin/mod.rs new file mode 100644 index 000000000..f932c4fa5 --- /dev/null +++ b/kclvm/sema/src/builtin/mod.rs @@ -0,0 +1,507 @@ +//! This package mainly contains the type definitions of built-in system libraries, +//! functions, decorators and member methods. +pub mod decorator; +pub mod string; +pub mod system_module; + +use std::rc::Rc; + +use indexmap::IndexMap; +use once_cell::sync::Lazy; + +use crate::ty::{Parameter, Type}; +pub use decorator::BUILTIN_DECORATORS; +pub use string::STRING_MEMBER_FUNCTIONS; +pub use system_module::*; + +pub const KCL_BUILTIN_FUNCTION_MANGLE_PREFIX: &str = "kclvm_builtin"; +pub const KCL_SYSTEM_MODULE_MANGLE_PREFIX: &str = "kclvm_"; +pub const BUILTIN_FUNCTION_PREFIX: &str = "$builtin"; + +macro_rules! register_builtin { + ($($name:ident => $ty:expr)*) => ( + // Builtin function map. + pub const BUILTIN_FUNCTIONS: Lazy> = Lazy::new(|| { + let mut builtin_mapping = IndexMap::default(); + $( builtin_mapping.insert(stringify!($name).to_string(), $ty); )* + builtin_mapping + }); + pub static BUILTIN_FUNCTION_NAMES: &[&str] = &[ + $( stringify!($name), )* + ]; + ) +} + +register_builtin! { + option => Type::function( + None, + Rc::new(Type::ANY), + &[ + Parameter { + name: "key".to_string(), + ty: Rc::new(Type::STR), + has_default: false, + }, + Parameter { + name: "type".to_string(), + ty: Rc::new(Type::STR), + has_default: true, + }, + Parameter { + name: "required".to_string(), + ty: Rc::new(Type::BOOL), + has_default: true, + }, + Parameter { + name: "default".to_string(), + ty: Rc::new(Type::ANY), + has_default: true, + }, + Parameter { + name: "help".to_string(), + ty: Rc::new(Type::STR), + has_default: true, + }, + Parameter { + name: "file".to_string(), + ty: Rc::new(Type::STR), + has_default: true, + }, + Parameter { + name: "line".to_string(), + ty: Rc::new(Type::INT), + has_default: true, + }, + ], + "Return the top level argument by the key", + false, + Some(1), + ) + print => Type::function( + None, + Rc::new(Type::ANY), + &[], + r#"Prints the values to a stream, or to sys.stdout by default. + Optional keyword arguments: + sep: string inserted between values, default a space. + end: string appended after the last value, default a newline. + "#, + true, + Some(0), + ) + multiplyof => Type::function( + None, + Rc::new(Type::BOOL), + &[ + Parameter { + name: "a".to_string(), + ty: Rc::new(Type::INT), + has_default: false, + }, + Parameter { + name: "b".to_string(), + ty: Rc::new(Type::INT), + has_default: false, + }, + ], + "Check if the modular result of a and b is 0.", + true, + Some(0), + ) + isunique => Type::function( + None, + Rc::new(Type::BOOL), + &[ + Parameter { + name: "inval".to_string(), + ty: Type::list_ref(Rc::new(Type::ANY)), + has_default: false, + }, + ], + "Check if a list has duplicated elements", + false, + None, + ) + len => Type::function( + None, + Rc::new(Type::INT), + &[ + Parameter { + name: "inval".to_string(), + ty: Type::iterable(), + has_default: false, + }, + ], + "Return the length of a value.", + false, + None, + ) + abs => Type::function( + None, + Rc::new(Type::ANY), + &[ + Parameter { + name: "inval".to_string(), + ty: Rc::new(Type::ANY), + has_default: false, + }, + ], + "Return the absolute value of the argument.", + false, + None, + ) + all_true => Type::function( + None, + Rc::new(Type::BOOL), + &[ + Parameter { + name: "inval".to_string(), + ty: Type::list_ref(Rc::new(Type::ANY)), + has_default: false, + }, + ], + r#"Return True if bool(x) is True for all values x in the iterable. + + If the iterable is empty, return True."#, + false, + None, + ) + any_true => Type::function( + None, + Rc::new(Type::BOOL), + &[ + Parameter { + name: "inval".to_string(), + ty: Type::list_ref(Rc::new(Type::ANY)), + has_default: false, + }, + ], + r#"Return True if bool(x) is True for any x in the iterable. + + If the iterable is empty, return False."#, + false, + None, + ) + hex => Type::function( + None, + Rc::new(Type::STR), + &[ + Parameter { + name: "number".to_string(), + ty: Rc::new(Type::INT), + has_default: false, + }, + ], + "Return the hexadecimal representation of an integer.", + false, + None, + ) + bin => Type::function( + None, + Rc::new(Type::STR), + &[ + Parameter { + name: "number".to_string(), + ty: Rc::new(Type::INT), + has_default: false, + }, + ], + "Return the binary representation of an integer.", + false, + None, + ) + oct => Type::function( + None, + Rc::new(Type::STR), + &[ + Parameter { + name: "number".to_string(), + ty: Rc::new(Type::INT), + has_default: false, + }, + ], + "Return the octal representation of an integer.", + false, + None, + ) + ord => Type::function( + None, + Rc::new(Type::INT), + &[ + Parameter { + name: "c".to_string(), + ty: Rc::new(Type::STR), + has_default: false, + }, + ], + "Return the Unicode code point for a one-character string.", + false, + None, + ) + sorted => Type::function( + None, + Type::list_ref(Rc::new(Type::ANY)), + &[ + Parameter { + name: "inval".to_string(), + ty: Type::iterable(), + has_default: false, + }, + Parameter { + name: "reverse".to_string(), + ty: Rc::new(Type::BOOL), + has_default: true, + }, + ], + r#"Return a new list containing all items from the iterable in ascending order. + + A custom key function can be supplied to customize the sort order, and the reverse + flag can be set to request the result in descending order."#, + false, + Some(1), + ) + range => Type::function( + None, + Type::list_ref(Rc::new(Type::INT)), + &[ + Parameter { + name: "start".to_string(), + ty: Rc::new(Type::INT), + has_default: true, + }, + Parameter { + name: "stop".to_string(), + ty: Rc::new(Type::INT), + has_default: true, + }, + Parameter { + name: "step".to_string(), + ty: Rc::new(Type::INT), + has_default: true, + }, + ], + r#"Return the range of a value."#, + false, + None, + ) + max => Type::function( + None, + Rc::new(Type::ANY), + &[], + r#"With a single iterable argument, return its biggest item. + The default keyword-only argument specifies an object to return + if the provided iterable is empty. With two or more arguments, + return the largest argument."#, + true, + None, + ) + min => Type::function( + None, + Rc::new(Type::ANY), + &[], + r#"With a single iterable argument, return its smallest item. + The default keyword-only argument specifies an object to return + if the provided iterable is empty. With two or more arguments, + return the smallest argument."#, + true, + None, + ) + sum => Type::function( + None, + Rc::new(Type::ANY), + &[ + Parameter { + name: "iterable".to_string(), + ty: Type::list_ref(Rc::new(Type::ANY)), + has_default: false, + }, + Parameter { + name: "start".to_string(), + ty: Rc::new(Type::ANY), + has_default: true, + }, + ], + r#"When the iterable is empty, return the start value. This function is + intended specifically for use with numeric values and may reject + non-numeric types."#, + false, + None, + ) + pow => Type::function( + None, + Type::number(), + &[ + Parameter { + name: "x".to_string(), + ty: Type::number(), + has_default: false, + }, + Parameter { + name: "y".to_string(), + ty: Type::number(), + has_default: false, + }, + Parameter { + name: "z".to_string(), + ty: Type::number(), + has_default: true, + }, + ], + r#"Equivalent to x**y (with two arguments) or x**y % z (with three arguments) + + Some types, such as ints, are able to use a more efficient algorithm when + invoked using the three argument form."#, + false, + None, + ) + round => Type::function( + None, + Type::number(), + &[ + Parameter { + name: "number".to_string(), + ty: Type::number(), + has_default: false, + }, + Parameter { + name: "ndigits".to_string(), + ty: Rc::new(Type::INT), + has_default: true, + }, + ], + r#"Round a number to a given precision in decimal digits. + + The return value is an integer if ndigits is omitted or None. + Otherwise the return value has the same type as the number. + ndigits may be negative."#, + false, + None, + ) + zip => Type::function( + None, + Type::list_ref(Rc::new(Type::ANY)), + &[], + r#"Return a zip object whose next method returns + a tuple where the i-th element comes from the i-th iterable + argument."#, + true, + None, + ) + int => Type::function( + None, + Rc::new(Type::INT), + &[ + Parameter { + name: "number".to_string(), + ty: Rc::new(Type::ANY), + has_default: false, + }, + Parameter { + name: "base".to_string(), + ty: Rc::new(Type::INT), + has_default: true, + }, + ], + r#"Convert a number or string to an integer, or return 0 if no arguments + are given. For floating point numbers, this truncates towards zero."#, + false, + None, + ) + float => Type::function( + None, + Rc::new(Type::FLOAT), + &[ + Parameter { + name: "number".to_string(), + ty: Rc::new(Type::ANY), + has_default: false, + }, + ], + r#"Convert a string or number to a floating point number, if possible."#, + false, + None, + ) + bool => Type::function( + None, + Rc::new(Type::ANY), + &[ + Parameter { + name: "x".to_string(), + ty: Rc::new(Type::ANY), + has_default: true, + }, + ], + r#"Returns True when the argument x is true, False otherwise. + The builtins True and False are the only two instances of the class bool. + The class bool is a subclass of the class int, and cannot be subclassed."#, + false, + None, + ) + str => Type::function( + None, + Rc::new(Type::ANY), + &[ + Parameter { + name: "x".to_string(), + ty: Rc::new(Type::ANY), + has_default: true, + }, + ], + r#"Create a new string object from the given object. + If encoding or errors is specified, then the object must + expose a data buffer that will be decoded using the + given encoding and error handler."#, + false, + None, + ) + list => Type::function( + None, + Type::list_ref(Rc::new(Type::ANY)), + &[ + Parameter { + name: "x".to_string(), + ty: Rc::new(Type::ANY), + has_default: true, + }, + ], + r#"Built-in mutable sequence. + + If no argument is given, the constructor creates a new empty list. + The argument must be an iterable if specified."#, + false, + None, + ) + dict => Type::function( + None, + Type::dict_ref(Rc::new(Type::ANY), Rc::new(Type::ANY)), + &[ + Parameter { + name: "x".to_string(), + ty: Rc::new(Type::ANY), + has_default: true, + }, + ], + r#"Built-in mutable dict."#, + true, + None, + ) + typeof => Type::function( + None, + Rc::new(Type::STR), + &[ + Parameter { + name: "x".to_string(), + ty: Rc::new(Type::ANY), + has_default: false, + }, + Parameter { + name: "full_name".to_string(), + ty: Rc::new(Type::BOOL), + has_default: true, + }, + ], + r#"Return the type of the object"#, + true, + None, + ) +} diff --git a/kclvm/sema/src/builtin/string.rs b/kclvm/sema/src/builtin/string.rs new file mode 100644 index 000000000..448b540bc --- /dev/null +++ b/kclvm/sema/src/builtin/string.rs @@ -0,0 +1,235 @@ +use indexmap::IndexMap; +use once_cell::sync::Lazy; +use std::rc::Rc; + +use crate::ty::Type; + +macro_rules! register_string_member { + ($($name:ident => $ty:expr)*) => ( + // Builtin string member function map. + pub const STRING_MEMBER_FUNCTIONS: Lazy> = Lazy::new(|| { + let mut builtin_mapping = IndexMap::default(); + $( builtin_mapping.insert(stringify!($name).to_string(), $ty); )* + builtin_mapping + }); + ) +} + +register_string_member! { + capitalize => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::ANY), + &[], + r#""#, + false, + None, + ) + count => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::INT), + &[], + r#""#, + false, + None, + ) + endswith => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::BOOL), + &[], + r#""#, + false, + None, + ) + find => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::INT), + &[], + r#""#, + false, + None, + ) + format => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::STR), + &[], + r#""#, + true, + None, + ) + index => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::INT), + &[], + r#""#, + false, + None, + ) + isalpha => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::BOOL), + &[], + r#""#, + false, + None, + ) + isalnum => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::BOOL), + &[], + r#""#, + false, + None, + ) + isdigit => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::BOOL), + &[], + r#""#, + false, + None, + ) + islower => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::BOOL), + &[], + r#""#, + false, + None, + ) + isspace => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::BOOL), + &[], + r#""#, + false, + None, + ) + istitle => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::BOOL), + &[], + r#""#, + false, + None, + ) + isupper => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::BOOL), + &[], + r#""#, + false, + None, + ) + join => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::STR), + &[], + r#""#, + true, + None, + ) + lower => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::STR), + &[], + r#""#, + true, + None, + ) + upper => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::STR), + &[], + r#""#, + true, + None, + ) + lstrip => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::STR), + &[], + r#""#, + true, + None, + ) + rstrip => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::STR), + &[], + r#""#, + true, + None, + ) + replace => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::STR), + &[], + r#""#, + true, + None, + ) + rfind => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::INT), + &[], + r#""#, + true, + None, + ) + rindex => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::INT), + &[], + r#""#, + true, + None, + ) + rsplit => Type::function( + Some(Rc::new(Type::STR)), + Type::list_ref(Rc::new(Type::STR)), + &[], + r#""#, + true, + None, + ) + split => Type::function( + Some(Rc::new(Type::STR)), + Type::list_ref(Rc::new(Type::STR)), + &[], + r#""#, + true, + None, + ) + splitlines => Type::function( + Some(Rc::new(Type::STR)), + Type::list_ref(Rc::new(Type::STR)), + &[], + r#""#, + true, + None, + ) + startswith => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::BOOL), + &[], + r#""#, + false, + None, + ) + strip => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::STR), + &[], + r#""#, + false, + None, + ) + title => Type::function( + Some(Rc::new(Type::STR)), + Rc::new(Type::STR), + &[], + r#""#, + false, + None, + ) +} diff --git a/kclvm/sema/src/builtin/system_module.rs b/kclvm/sema/src/builtin/system_module.rs new file mode 100644 index 000000000..ed49c8df5 --- /dev/null +++ b/kclvm/sema/src/builtin/system_module.rs @@ -0,0 +1,131 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub const BASE64: &str = "base64"; +pub const BASE64_FUNCTION_NAMES: [&str; 2] = ["encode", "decode"]; + +pub const NET: &str = "net"; +pub const NET_FUNCTION_NAMES: [&str; 16] = [ + "split_host_port", + "join_host_port", + "fqdn", + "parse_IP", + "to_IP4", + "to_IP16", + "IP_string", + "is_IPv4", + "is_IP", + "is_loopback_IP", + "is_multicast_IP", + "is_interface_local_multicast_IP", + "is_link_local_multicast_IP", + "is_link_local_unicast_IP", + "is_global_unicast_IP", + "is_unspecified_IP", +]; + +pub const MATH: &str = "math"; +pub const MATH_FUNCTION_NAMES: [&str; 16] = [ + "ceil", + "factorial", + "floor", + "gcd", + "isfinite", + "isinf", + "isnan", + "modf", + "exp", + "expm1", + "log", + "log1p", + "log2", + "log10", + "pow", + "sqrt", +]; + +pub const DATETIME: &str = "datetime"; +pub const DATETIME_FUNCTION_NAMES: [&str; 4] = ["today", "now", "ticks", "date"]; + +pub const REGEX: &str = "regex"; +pub const REGEX_FUNCTION_NAMES: [&str; 6] = + ["replace", "match", "compile", "findall", "search", "split"]; + +pub const YAML: &str = "yaml"; +pub const YAML_FUNCTION_NAMES: [&str; 3] = ["encode", "decode", "dump_to_file"]; + +pub const JSON: &str = "json"; +pub const JSON_FUNCTION_NAMES: [&str; 3] = ["encode", "decode", "dump_to_file"]; + +pub const CRYPTO: &str = "crypto"; +pub const CRYPTO_FUNCTION_NAMES: [&str; 6] = + ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"]; + +pub const TESTING: &str = "testing"; +pub const TESTING_FUNCTION_NAMES: [&str; 2] = ["arguments", "setting_file"]; + +pub const UNITS: &str = "units"; +pub const UNITS_FUNCTION_NAMES: [&str; 13] = [ + "to_n", "to_u", "to_m", "to_K", "to_M", "to_G", "to_T", "to_P", "to_Ki", "to_Mi", "to_Gi", + "to_Ti", "to_Pi", +]; +pub const UNITS_NUMBER_MULTIPLIER: &str = "NumberMultiplier"; +pub const UNITS_FIELD_NAMES: [&str; 15] = [ + "n", + "u", + "m", + "k", + "K", + "M", + "G", + "T", + "P", + "Ki", + "Mi", + "Gi", + "Ti", + "Pi", + UNITS_NUMBER_MULTIPLIER, +]; + +pub const COLLECTION: &str = "collection"; +pub const COLLECTION_FUNCTION_NAMES: [&str; 1] = ["union_all"]; + +pub const STANDARD_SYSTEM_MODULES: [&str; 11] = [ + COLLECTION, NET, MATH, DATETIME, REGEX, YAML, JSON, CRYPTO, BASE64, TESTING, UNITS, +]; + +pub const STANDARD_SYSTEM_MODULE_NAMES_WITH_AT: [&str; 11] = [ + "@collection", + "@net", + "@math", + "@datetime", + "@regex", + "@yaml", + "@json", + "@crypto", + "@base64", + "@testing", + "@units", +]; + +/// Get the system module members +pub fn get_system_module_members(name: &str) -> Vec<&str> { + match name { + BASE64 => BASE64_FUNCTION_NAMES.to_vec(), + NET => NET_FUNCTION_NAMES.to_vec(), + MATH => MATH_FUNCTION_NAMES.to_vec(), + DATETIME => DATETIME_FUNCTION_NAMES.to_vec(), + REGEX => REGEX_FUNCTION_NAMES.to_vec(), + YAML => YAML_FUNCTION_NAMES.to_vec(), + JSON => JSON_FUNCTION_NAMES.to_vec(), + CRYPTO => CRYPTO_FUNCTION_NAMES.to_vec(), + TESTING => TESTING_FUNCTION_NAMES.to_vec(), + UNITS => { + let mut members = UNITS_FUNCTION_NAMES.to_vec(); + members.append(&mut UNITS_FIELD_NAMES.to_vec()); + members + } + COLLECTION => COLLECTION_FUNCTION_NAMES.to_vec(), + _ => bug!("invalid system module name '{}'", name), + } +} diff --git a/kclvm/sema/src/eval/mod.rs b/kclvm/sema/src/eval/mod.rs new file mode 100644 index 000000000..20cfa589f --- /dev/null +++ b/kclvm/sema/src/eval/mod.rs @@ -0,0 +1,149 @@ +use std::str::Chars; + +/// Eval a string starts with a quote ' or " to a Rust string. +pub fn str_literal_eval(string_lit: &str, is_bytes: bool, is_raw: bool) -> Option { + let mut chars: std::iter::Peekable = string_lit.chars().peekable(); + + let quote_char = match chars.next() { + Some(c) => c, + None => return None, + }; + let mut string_content = String::new(); + // If the next two characters are also the quote character, then we have a triple-quoted + // string; consume those two characters and ensure that we require a triple-quote to close + let triple_quoted = if chars.peek() == Some("e_char) && chars.next() == Some(quote_char) { + chars.next(); + true + } else { + false + }; + loop { + let chr = chars.next(); + match chr { + Some('\\') => { + let next_char = chars.next(); + if next_char == Some(quote_char) && !is_raw { + string_content.push(quote_char); + } else if is_raw { + string_content.push('\\'); + if let Some(c) = next_char { + string_content.push(c) + } else { + return None; + } + } else { + match next_char { + Some('\\') => { + string_content.push('\\'); + } + Some('\'') => string_content.push('\''), + Some('\"') => string_content.push('\"'), + Some('\n') => { + // Ignore Unix EOL character + } + Some('a') => string_content.push('\x07'), + Some('b') => string_content.push('\x08'), + Some('f') => string_content.push('\x0c'), + Some('n') => { + string_content.push('\n'); + } + Some('r') => string_content.push('\r'), + Some('t') => { + string_content.push('\t'); + } + Some('v') => string_content.push('\x0b'), + Some(o @ '0'..='7') => string_content.push(parse_octet(o, &mut chars)), + Some('x') => string_content.push(unicode_literal(2, &mut chars)?), + Some('u') if !is_bytes => { + string_content.push(unicode_literal(4, &mut chars)?) + } + Some('U') if !is_bytes => { + string_content.push(unicode_literal(8, &mut chars)?) + } + Some('N') if !is_bytes => { + string_content.push(parse_unicode_name(&mut chars)?) + } + Some(c) => { + string_content.push('\\'); + string_content.push(c); + } + None => return None, + } + } + } + Some(c) => { + if c == quote_char { + if triple_quoted { + // Look ahead at the next two characters; if we have two more + // quote_chars, it's the end of the string; consume the remaining + // closing quotes and break the loop + if chars.peek() == Some("e_char) && chars.nth(1) == Some(quote_char) { + chars.next(); + chars.next(); + break; + } + string_content.push(c); + } else { + break; + } + } else { + if (c == '\n' && !triple_quoted) || (is_bytes && !c.is_ascii()) { + return None; + } + string_content.push(c); + } + } + None => break, + } + } + Some(string_content) +} + +/// Parse unicode literal. +fn unicode_literal(literal_number: usize, chars: &mut std::iter::Peekable) -> Option { + let mut p: u32 = 0u32; + for i in 1..=literal_number { + match chars.next() { + Some(c) => match c.to_digit(16) { + Some(d) => p += d << ((literal_number - i) * 4), + None => return None, + }, + None => return None, + } + } + match p { + 0xD800..=0xDFFF => Some(std::char::REPLACEMENT_CHARACTER), + _ => std::char::from_u32(p), + } +} + +fn parse_octet(first: char, chars: &mut std::iter::Peekable) -> char { + let mut octet_content = String::new(); + octet_content.push(first); + while octet_content.len() < 3 { + let next_char = chars.next(); + if let Some('0'..='7') = next_char { + octet_content.push(next_char.unwrap()) + } else { + break; + } + } + let value = u32::from_str_radix(&octet_content, 8).unwrap(); + char::from_u32(value).unwrap() +} + +fn parse_unicode_name(chars: &mut std::iter::Peekable) -> Option { + match chars.next() { + Some('{') => {} + _ => return None, + } + let mut name = String::new(); + loop { + match chars.next() { + Some('}') => break, + Some(c) => name.push(c), + None => return None, + } + } + unicode_names2::character(&name) +} diff --git a/kclvm/sema/src/info/mod.rs b/kclvm/sema/src/info/mod.rs new file mode 100644 index 000000000..f025de680 --- /dev/null +++ b/kclvm/sema/src/info/mod.rs @@ -0,0 +1,4 @@ +#[inline] +pub fn is_private_field(name: &str) -> bool { + name.starts_with('_') +} diff --git a/kclvm/sema/src/lib.rs b/kclvm/sema/src/lib.rs new file mode 100644 index 000000000..35120ecc4 --- /dev/null +++ b/kclvm/sema/src/lib.rs @@ -0,0 +1,10 @@ +pub mod builtin; +pub mod eval; +pub mod info; +pub mod plugin; +pub mod pre_process; +pub mod resolver; +pub mod ty; + +#[macro_use] +extern crate kclvm_error; diff --git a/kclvm/sema/src/plugin/mod.rs b/kclvm/sema/src/plugin/mod.rs new file mode 100644 index 000000000..30db08e34 --- /dev/null +++ b/kclvm/sema/src/plugin/mod.rs @@ -0,0 +1,2 @@ +pub const PLUGIN_MODULE_PREFIX: &str = "kcl_plugin."; +pub const PLUGIN_PREFIX_WITH_AT: &str = "@kcl_plugin"; diff --git a/kclvm/sema/src/pre_process/config.rs b/kclvm/sema/src/pre_process/config.rs new file mode 100644 index 000000000..d711ec357 --- /dev/null +++ b/kclvm/sema/src/pre_process/config.rs @@ -0,0 +1,377 @@ +use indexmap::{IndexMap, IndexSet}; +use kclvm_ast::walker::MutSelfMutWalker; +use kclvm_ast::{ast, walk_if_mut}; + +const NAME_NONE_BUCKET_KEY: &str = "$name_none"; + +#[derive(Debug, Default)] +struct ConfigNestAttrTransformer; + +impl ConfigNestAttrTransformer { + pub fn walk_config_entry(&mut self, config_entry: &mut Box>) { + if let Some(key) = config_entry.node.key.as_mut() { + if let ast::Expr::Identifier(identifier) = &mut key.node { + if identifier.names.len() > 1 { + let mut names = identifier.names.clone(); + let names = &mut names[1..]; + names.reverse(); + identifier.names = vec![identifier.names[0].clone()]; + + let mut value = config_entry.node.value.clone(); + for (i, name) in names.iter().enumerate() { + let is_last_item = i == 0; + let name_node = ast::Identifier { + names: vec![name.clone()], + pkgpath: "".to_string(), + ctx: ast::ExprContext::Load, + }; + let entry_value = ast::ConfigEntry { + key: Some(Box::new(ast::Node::new( + ast::Expr::Identifier(name_node), + key.filename.clone(), + key.line, + key.column, + key.end_line, + key.end_column, + ))), + value: value.clone(), + operation: if is_last_item { + config_entry.node.operation.clone() + } else { + ast::ConfigEntryOperation::Union + }, + insert_index: -1, + }; + let config_expr = ast::ConfigExpr { + items: vec![Box::new(ast::Node::new( + entry_value, + key.filename.clone(), + key.line, + key.column, + key.end_line, + key.end_column, + ))], + }; + value = Box::new(ast::Node::new( + ast::Expr::Config(config_expr), + value.filename.clone(), + value.line, + value.column, + value.end_line, + value.end_column, + )) + } + config_entry.node.value = value; + config_entry.node.operation = ast::ConfigEntryOperation::Union; + } + } + } + } +} + +impl<'ctx> MutSelfMutWalker<'ctx> for ConfigNestAttrTransformer { + fn walk_config_expr(&mut self, config_expr: &'ctx mut ast::ConfigExpr) { + for config_entry in config_expr.items.iter_mut() { + self.walk_config_entry(config_entry); + self.walk_expr(&mut config_entry.node.value.node); + } + } + fn walk_config_if_entry_expr( + &mut self, + config_if_entry_expr: &'ctx mut ast::ConfigIfEntryExpr, + ) { + for config_entry in config_if_entry_expr.items.iter_mut() { + self.walk_config_entry(config_entry); + self.walk_expr(&mut config_entry.node.value.node); + } + walk_if_mut!(self, walk_expr, config_if_entry_expr.orelse); + } +} + +#[derive(Debug)] +struct ConfigMergeTransformer {} + +#[derive(Debug)] +enum ConfigMergeKind { + Override, + Union, +} + +impl ConfigMergeTransformer { + pub fn merge(&mut self, program: &mut ast::Program) { + // {name: (filename, index, kind)} + let mut name_declaration_mapping: IndexMap> = + IndexMap::default(); + // 1. Collect merged config + if let Some(modules) = program.pkgs.get_mut(kclvm_ast::MAIN_PKG) { + for module in modules { + for (i, stmt) in module.body.iter_mut().enumerate() { + match &mut stmt.node { + ast::Stmt::Unification(unification_stmt) => { + let name = &unification_stmt.target.node.names[0]; + match name_declaration_mapping.get_mut(name) { + Some(declarations) => declarations.push(( + module.filename.to_string(), + i, + ConfigMergeKind::Union, + )), + None => { + name_declaration_mapping.insert( + name.to_string(), + vec![( + module.filename.to_string(), + i, + ConfigMergeKind::Union, + )], + ); + } + } + } + ast::Stmt::Assign(assign_stmt) => { + if let ast::Expr::Schema(_) = assign_stmt.value.node { + for target in &assign_stmt.targets { + if target.node.names.len() == 1 { + let name = &target.node.names[0]; + match name_declaration_mapping.get_mut(name) { + Some(declarations) => declarations.push(( + module.filename.to_string(), + i, + ConfigMergeKind::Override, + )), + None => { + name_declaration_mapping.insert( + name.to_string(), + vec![( + module.filename.to_string(), + i, + ConfigMergeKind::Override, + )], + ); + } + } + } + } + } + } + _ => {} + } + } + } + } + // 2. Merge config + for (_, index_list) in &name_declaration_mapping { + let index_len = index_list.len(); + if index_len > 1 { + let (filename, merged_index, merged_kind) = index_list.last().unwrap(); + let mut items: Vec> = vec![]; + for (merged_filename, index, kind) in index_list { + if let Some(modules) = program.pkgs.get_mut(kclvm_ast::MAIN_PKG) { + for module in modules { + if &module.filename == merged_filename { + let stmt = module.body.get_mut(*index).unwrap(); + match &mut stmt.node { + ast::Stmt::Unification(unification_stmt) + if matches!(kind, ConfigMergeKind::Union) => + { + if let ast::Expr::Config(config_expr) = + &mut unification_stmt.value.node.config.node + { + let mut config_items = config_expr.items.clone(); + items.append(&mut config_items); + } + } + ast::Stmt::Assign(assign_stmt) + if matches!(kind, ConfigMergeKind::Override) => + { + if let ast::Expr::Schema(schema_expr) = + &mut assign_stmt.value.node + { + if let ast::Expr::Config(config_expr) = + &mut schema_expr.config.node + { + let mut config_items = config_expr.items.clone(); + items.append(&mut config_items); + } + } + } + _ => { + bug!("mismatch ast node and config merge kind: {:?}", kind) + } + } + } + } + } + } + if let Some(modules) = program.pkgs.get_mut(kclvm_ast::MAIN_PKG) { + for module in modules { + if &module.filename == filename { + if let Some(stmt) = module.body.get_mut(*merged_index) { + match &mut stmt.node { + ast::Stmt::Unification(unification_stmt) + if matches!(merged_kind, ConfigMergeKind::Union) => + { + if let ast::Expr::Config(config_expr) = + &mut unification_stmt.value.node.config.node + { + config_expr.items = unify_config_entries(&items); + } + } + ast::Stmt::Assign(assign_stmt) + if matches!(merged_kind, ConfigMergeKind::Override) => + { + if let ast::Expr::Schema(schema_expr) = + &mut assign_stmt.value.node + { + if let ast::Expr::Config(config_expr) = + &mut schema_expr.config.node + { + config_expr.items = unify_config_entries(&items); + } + } + } + _ => bug!( + "mismatch ast node and config merge kind: {:?}", + merged_kind + ), + } + } + break; + } + } + } + } + } + // 3. Delete redundant config. + if let Some(modules) = program.pkgs.get_mut(kclvm_ast::MAIN_PKG) { + for module in modules { + let mut delete_index_set: IndexSet = IndexSet::default(); + for (_, index_list) in &name_declaration_mapping { + let index_len = index_list.len(); + if index_len > 1 { + for (filename, index, _) in &index_list[..index_len - 1] { + if &module.filename == filename { + delete_index_set.insert(*index); + } + } + } + } + let mut body: Vec<(usize, &ast::NodeRef)> = + module.body.iter().enumerate().collect(); + body.retain(|(idx, _)| !delete_index_set.contains(idx)); + module.body = body + .iter() + .map(|(_, stmt)| (*stmt).clone()) + .collect::>>(); + } + } + } +} + +/// Unify config entries. +fn unify_config_entries( + entries: &[ast::NodeRef], +) -> Vec> { + // Using bucket map to check unique/merge option and store values + let mut bucket: IndexMap>> = IndexMap::new(); + for entry in entries { + let name = match &entry.node.key { + Some(key) => { + if let ast::Expr::Identifier(identifier) = &key.node { + identifier.get_name() + } else { + NAME_NONE_BUCKET_KEY.to_string() + } + } + None => NAME_NONE_BUCKET_KEY.to_string(), + }; + match bucket.get_mut(&name) { + Some(values) => values.push(entry.clone()), + None => { + let values = vec![entry.clone()]; + bucket.insert(name, values); + } + } + } + let mut entries = vec![]; + for (key, items) in bucket.iter_mut() { + if key == NAME_NONE_BUCKET_KEY { + entries.append(items); + } else { + let mut schema_index = None; + for (i, item) in items.iter().enumerate() { + if let ast::Expr::Schema(_) = item.node.value.node { + schema_index = Some(i); + break; + } + } + match schema_index { + Some(index) => { + let mut merged_schema = items[index].as_ref().clone(); + for (i, item) in items.iter().enumerate() { + match &item.node.value.node { + ast::Expr::Schema(item_schema_expr) => { + if let ast::Expr::Schema(schema_expr) = + &mut merged_schema.node.value.node + { + if let ast::Expr::Config(schema_config) = + &mut schema_expr.config.node + { + if let ast::Expr::Config(config_expr) = + &item_schema_expr.config.node + { + if i < index { + let mut items = config_expr.items.clone(); + items.append(&mut schema_config.items); + schema_config.items = items; + } else if i > index { + let mut items = config_expr.items.clone(); + schema_config.items.append(&mut items); + } + } + } + } + } + ast::Expr::Config(item_config_expr) => { + if let ast::Expr::Schema(schema_expr) = + &mut merged_schema.node.value.node + { + if let ast::Expr::Config(schema_config) = + &mut schema_expr.config.node + { + if i < index { + let mut items = item_config_expr.items.clone(); + items.append(&mut schema_config.items); + schema_config.items = items; + } else if i > index { + let mut items = item_config_expr.items.clone(); + schema_config.items.append(&mut items); + } + } + } + } + _ => entries.push(item.clone()), + } + } + entries.push(Box::new(merged_schema)); + } + None => entries.append(items), + }; + } + } + entries +} + +/// Merge program +pub fn merge_program(program: &mut ast::Program) { + let mut merger = ConfigMergeTransformer {}; + merger.merge(program); +} + +/// Fix AST config expr nest attribute declarations. +/// +/// Examples +/// -------- +/// {a.b.c = 1} -> {a: {b: {c = 1}}} +pub fn fix_config_expr_nest_attr(module: &mut ast::Module) { + ConfigNestAttrTransformer::default().walk_module(module); +} diff --git a/kclvm/sema/src/pre_process/identifier.rs b/kclvm/sema/src/pre_process/identifier.rs new file mode 100644 index 000000000..2a6fc9d84 --- /dev/null +++ b/kclvm/sema/src/pre_process/identifier.rs @@ -0,0 +1,295 @@ +use crate::info::is_private_field; +use crate::resolver::pos::GetPos; +use indexmap::{IndexMap, IndexSet}; +use kclvm_ast::walker::MutSelfMutWalker; +use kclvm_ast::{ast, walk_if_mut, walk_list_mut}; +use kclvm_error::*; + +pub const RAW_IDENTIFIER_PREFIX: &str = "$"; + +#[derive(Default)] +struct QualifiedIdentifierTransformer { + pub import_names: IndexMap, + pub global_names: IndexMap, + pub local_vars: IndexSet, + pub scope_level: usize, + pub handler: Handler, +} + +impl<'ctx> MutSelfMutWalker<'ctx> for QualifiedIdentifierTransformer { + fn walk_rule_stmt(&mut self, rule_stmt: &'ctx mut ast::RuleStmt) { + let name = &rule_stmt.name.node; + if !self.global_names.contains_key(name) { + if self.scope_level == 0 { + self.global_names + .insert(name.to_string(), rule_stmt.name.get_pos()); + } + } else { + self.handler.add_error( + ErrorKind::UniqueKeyError, + &[ + Message { + pos: rule_stmt.name.get_pos(), + style: Style::LineAndColumn, + message: format!("Unique key error name '{}'", name), + note: None, + }, + Message { + pos: self.global_names.get(name).unwrap().clone(), + style: Style::LineAndColumn, + message: format!("The variable '{}' is declared here firstly", name), + note: None, + }, + ], + ); + } + walk_list_mut!(self, walk_identifier, rule_stmt.parent_rules); + walk_list_mut!(self, walk_call_expr, rule_stmt.decorators); + walk_if_mut!(self, walk_arguments, rule_stmt.args); + walk_if_mut!(self, walk_identifier, rule_stmt.for_host_name); + self.scope_level += 1; + walk_list_mut!(self, walk_check_expr, rule_stmt.checks); + self.scope_level -= 1; + } + fn walk_schema_stmt(&mut self, schema_stmt: &'ctx mut ast::SchemaStmt) { + let name = &schema_stmt.name.node; + if !self.global_names.contains_key(name) { + if self.scope_level == 0 { + self.global_names + .insert(name.to_string(), schema_stmt.name.get_pos()); + } + } else { + self.handler.add_error( + ErrorKind::UniqueKeyError, + &[ + Message { + pos: schema_stmt.name.get_pos(), + style: Style::LineAndColumn, + message: format!("Unique key error name '{}'", name), + note: None, + }, + Message { + pos: self.global_names.get(name).unwrap().clone(), + style: Style::LineAndColumn, + message: format!("The variable '{}' is declared here firstly", name), + note: None, + }, + ], + ); + } + walk_if_mut!(self, walk_identifier, schema_stmt.parent_name); + walk_if_mut!(self, walk_identifier, schema_stmt.for_host_name); + walk_if_mut!(self, walk_arguments, schema_stmt.args); + self.scope_level += 1; + if let Some(schema_index_signature) = schema_stmt.index_signature.as_deref_mut() { + let value = &mut schema_index_signature.node.value; + walk_if_mut!(self, walk_expr, value); + } + walk_list_mut!(self, walk_identifier, schema_stmt.mixins); + walk_list_mut!(self, walk_call_expr, schema_stmt.decorators); + walk_list_mut!(self, walk_stmt, schema_stmt.body); + walk_list_mut!(self, walk_check_expr, schema_stmt.checks); + self.scope_level -= 1; + } + fn walk_assign_stmt(&mut self, assign_stmt: &'ctx mut ast::AssignStmt) { + let is_config = matches!(assign_stmt.value.node, ast::Expr::Schema(_)); + for target in &assign_stmt.targets { + let name = &target.node.names[0]; + if is_private_field(name) || !self.global_names.contains_key(name) || is_config { + if self.scope_level == 0 { + self.global_names.insert(name.to_string(), target.get_pos()); + } + } else { + self.handler.add_error( + ErrorKind::UniqueKeyError, + &[ + Message { + pos: target.get_pos(), + style: Style::LineAndColumn, + message: format!("Unique key error name '{}'", name), + note: None, + }, + Message { + pos: self.global_names.get(name).unwrap().clone(), + style: Style::LineAndColumn, + message: format!("The variable '{}' is declared here firstly", name), + note: None, + }, + ], + ); + } + } + self.walk_expr(&mut assign_stmt.value.node); + } + fn walk_aug_assign_stmt(&mut self, aug_assign_stmt: &'ctx mut ast::AugAssignStmt) { + let is_config = matches!(aug_assign_stmt.value.node, ast::Expr::Schema(_)); + let name = &aug_assign_stmt.target.node.names[0]; + if is_private_field(name) || !self.global_names.contains_key(name) || is_config { + if self.scope_level == 0 { + self.global_names + .insert(name.to_string(), aug_assign_stmt.target.get_pos()); + } + } else { + self.handler.add_error( + ErrorKind::ImmutableError, + &[ + Message { + pos: aug_assign_stmt.target.get_pos(), + style: Style::LineAndColumn, + message: format!( + "Immutable variable '{}' is modified during compiling", + name + ), + note: None, + }, + Message { + pos: self.global_names.get(name).unwrap().clone(), + style: Style::LineAndColumn, + message: format!("The variable '{}' is declared here firstly", name), + note: None, + }, + ], + ); + } + + self.walk_expr(&mut aug_assign_stmt.value.node); + } + fn walk_schema_expr(&mut self, schema_expr: &'ctx mut ast::SchemaExpr) { + self.walk_identifier(&mut schema_expr.name.node); + walk_list_mut!(self, walk_expr, schema_expr.args); + walk_list_mut!(self, walk_keyword, schema_expr.kwargs); + self.walk_expr(&mut schema_expr.config.node); + } + fn walk_import_stmt(&mut self, _: &'ctx mut ast::ImportStmt) {} + fn walk_lambda_expr(&mut self, lambda_expr: &'ctx mut ast::LambdaExpr) { + walk_if_mut!(self, walk_arguments, lambda_expr.args); + self.scope_level += 1; + walk_list_mut!(self, walk_stmt, lambda_expr.body); + self.scope_level -= 1; + } + fn walk_list_comp(&mut self, list_comp: &'ctx mut ast::ListComp) { + for gen in &mut list_comp.generators { + for target in &gen.node.targets { + self.local_vars.insert(target.node.names[0].to_string()); + } + } + self.walk_expr(&mut list_comp.elt.node); + walk_list_mut!(self, walk_comp_clause, list_comp.generators); + self.local_vars.clear(); + } + fn walk_dict_comp(&mut self, dict_comp: &'ctx mut ast::DictComp) { + for gen in &dict_comp.generators { + for target in &gen.node.targets { + self.local_vars.insert(target.node.names[0].to_string()); + } + } + if let Some(key) = dict_comp.entry.key.as_deref_mut() { + self.walk_expr(&mut key.node); + } + self.walk_expr(&mut dict_comp.entry.value.node); + walk_list_mut!(self, walk_comp_clause, dict_comp.generators); + self.local_vars.clear(); + } + fn walk_quant_expr(&mut self, quant_expr: &'ctx mut ast::QuantExpr) { + for target in &quant_expr.variables { + self.local_vars.insert(target.node.names[0].to_string()); + } + self.walk_expr(&mut quant_expr.target.node); + self.walk_expr(&mut quant_expr.test.node); + walk_if_mut!(self, walk_expr, quant_expr.if_cond); + self.local_vars.clear(); + } + fn walk_identifier(&mut self, identifier: &'ctx mut ast::Identifier) { + if identifier.names.len() >= 2 { + // skip global name and generator local variables in list/dict comp and quant expression + let name = &identifier.names[0]; + if !self.global_names.contains_key(name) && !self.local_vars.contains(name) { + if let Some(pkgpath) = self.import_names.get(name) { + identifier.pkgpath = pkgpath.clone() + } + } + } + } +} + +#[inline] +fn remove_raw_ident_prefix(name: &str) -> String { + match name.strip_prefix(RAW_IDENTIFIER_PREFIX) { + Some(name_without_prefix) => name_without_prefix.to_string(), + None => name.to_string(), + } +} + +#[derive(Debug, Default)] +struct RawIdentifierTransformer; + +impl<'ctx> MutSelfMutWalker<'ctx> for RawIdentifierTransformer { + fn walk_identifier(&mut self, identifier: &'ctx mut ast::Identifier) { + identifier.names = identifier + .names + .iter() + .map(|n| remove_raw_ident_prefix(n)) + .collect::>(); + } + fn walk_schema_attr(&mut self, schema_attr: &'ctx mut ast::SchemaAttr) { + schema_attr.name.node = remove_raw_ident_prefix(&schema_attr.name.node); + walk_list_mut!(self, walk_call_expr, schema_attr.decorators); + walk_if_mut!(self, walk_expr, schema_attr.value); + } + fn walk_schema_stmt(&mut self, schema_stmt: &'ctx mut ast::SchemaStmt) { + schema_stmt.name.node = remove_raw_ident_prefix(&schema_stmt.name.node); + walk_if_mut!(self, walk_identifier, schema_stmt.parent_name); + walk_if_mut!(self, walk_identifier, schema_stmt.for_host_name); + walk_if_mut!(self, walk_arguments, schema_stmt.args); + if let Some(schema_index_signature) = schema_stmt.index_signature.as_deref_mut() { + let value = &mut schema_index_signature.node.value; + walk_if_mut!(self, walk_expr, value); + } + walk_list_mut!(self, walk_identifier, schema_stmt.mixins); + walk_list_mut!(self, walk_call_expr, schema_stmt.decorators); + walk_list_mut!(self, walk_check_expr, schema_stmt.checks); + walk_list_mut!(self, walk_stmt, schema_stmt.body); + } + fn walk_rule_stmt(&mut self, rule_stmt: &'ctx mut ast::RuleStmt) { + rule_stmt.name.node = remove_raw_ident_prefix(&rule_stmt.name.node); + walk_list_mut!(self, walk_identifier, rule_stmt.parent_rules); + walk_list_mut!(self, walk_call_expr, rule_stmt.decorators); + walk_list_mut!(self, walk_check_expr, rule_stmt.checks); + walk_if_mut!(self, walk_arguments, rule_stmt.args); + walk_if_mut!(self, walk_identifier, rule_stmt.for_host_name); + } + fn walk_import_stmt(&mut self, import_stmt: &'ctx mut ast::ImportStmt) { + if let Some(name) = import_stmt.asname.as_mut() { + *name = remove_raw_ident_prefix(name); + } + import_stmt.name = remove_raw_ident_prefix(&import_stmt.name); + import_stmt.path = remove_raw_ident_prefix(&import_stmt.path); + } +} + +/// import path.to.pkg as pkgname +/// +/// x = pkgname.Name +pub fn fix_qualified_identifier<'ctx>( + module: &'ctx mut ast::Module, + import_names: &mut IndexMap, +) { + // 0. init import names. + for stmt in &module.body { + if let ast::Stmt::Import(import_stmt) = &stmt.node { + import_names.insert(import_stmt.name.clone(), import_stmt.path.clone()); + } + } + // 1. fix_global_ident + let mut global_names_walker = QualifiedIdentifierTransformer { + import_names: import_names.clone(), + ..Default::default() + }; + global_names_walker.walk_module(module); + global_names_walker.handler.abort_if_any_errors(); +} + +/// Fix AST raw identifier prefix `$`, e.g., $filter -> filter +pub fn fix_raw_identifier_prefix(module: &'_ mut ast::Module) { + RawIdentifierTransformer::default().walk_module(module); +} diff --git a/kclvm/sema/src/pre_process/mod.rs b/kclvm/sema/src/pre_process/mod.rs new file mode 100644 index 000000000..e84b94dd3 --- /dev/null +++ b/kclvm/sema/src/pre_process/mod.rs @@ -0,0 +1,27 @@ +mod config; +mod identifier; + +use indexmap::IndexMap; +use kclvm_ast::ast; + +#[cfg(test)] +mod tests; + +pub use config::{fix_config_expr_nest_attr, merge_program}; +pub use identifier::{fix_qualified_identifier, fix_raw_identifier_prefix}; + +/// Pre-process AST program. +pub fn pre_process_program(program: &mut ast::Program) { + for (pkgpath, modules) in program.pkgs.iter_mut() { + let mut import_names = IndexMap::default(); + for module in modules.iter_mut() { + if pkgpath != kclvm_ast::MAIN_PKG { + import_names.clear(); + } + fix_qualified_identifier(module, &mut import_names); + fix_raw_identifier_prefix(module); + fix_config_expr_nest_attr(module); + } + } + merge_program(program); +} diff --git a/kclvm/sema/src/pre_process/test_data/qualified_identifier.k b/kclvm/sema/src/pre_process/test_data/qualified_identifier.k new file mode 100644 index 000000000..15d5dfe6b --- /dev/null +++ b/kclvm/sema/src/pre_process/test_data/qualified_identifier.k @@ -0,0 +1,3 @@ +import pkg + +x = pkg.a diff --git a/kclvm/sema/src/pre_process/test_data/raw_identifier.k b/kclvm/sema/src/pre_process/test_data/raw_identifier.k new file mode 100644 index 000000000..d86374741 --- /dev/null +++ b/kclvm/sema/src/pre_process/test_data/raw_identifier.k @@ -0,0 +1 @@ +$schema = 1 diff --git a/kclvm/sema/src/pre_process/tests.rs b/kclvm/sema/src/pre_process/tests.rs new file mode 100644 index 000000000..97559923e --- /dev/null +++ b/kclvm/sema/src/pre_process/tests.rs @@ -0,0 +1,34 @@ +use super::*; +use indexmap::IndexMap; +use kclvm_parser::parse_file; + +#[test] +fn test_fix_qualified_identifier() { + let mut module = parse_file("./src/pre_process/test_data/qualified_identifier.k", None); + fix_qualified_identifier(&mut module, &mut IndexMap::default()); + if let ast::Stmt::Assign(assign_stmt) = &module.body[1].node { + if let ast::Expr::Identifier(identifier) = &assign_stmt.value.node { + assert_eq!(identifier.pkgpath, "pkg") + } else { + panic!("invalid assign statement value") + } + } else { + panic!("invalid assign statement") + } +} + +#[test] +fn test_fix_raw_identifier_prefix() { + let mut module = parse_file("./src/pre_process/test_data/raw_identifier.k", None); + if let ast::Stmt::Assign(assign_stmt) = &module.body[0].node { + assert_eq!(assign_stmt.targets[0].node.names[0], "$schema") + } else { + panic!("invalid assign statement") + } + fix_raw_identifier_prefix(&mut module); + if let ast::Stmt::Assign(assign_stmt) = &module.body[0].node { + assert_eq!(assign_stmt.targets[0].node.names[0], "schema") + } else { + panic!("invalid assign statement") + } +} diff --git a/kclvm/sema/src/resolver/arg.rs b/kclvm/sema/src/resolver/arg.rs new file mode 100644 index 000000000..20b661070 --- /dev/null +++ b/kclvm/sema/src/resolver/arg.rs @@ -0,0 +1,67 @@ +use crate::resolver::Resolver; +use crate::ty::{Parameter, Type}; +use indexmap::IndexSet; +use kclvm_ast::ast; +use std::rc::Rc; + +use crate::resolver::pos::GetPos; + +impl<'ctx> Resolver<'ctx> { + /// Do schema/function/decorator argument type check. + pub fn do_arguments_type_check( + &mut self, + args: &'ctx [ast::NodeRef], + kwargs: &'ctx [ast::NodeRef], + params: &[Parameter], + ) { + let arg_types = self.exprs(args); + let mut kwarg_types: Vec<(String, Rc)> = vec![]; + let mut check_table: IndexSet = IndexSet::default(); + for kw in kwargs { + let arg_name = &kw.node.arg.node.names[0]; + if check_table.contains(arg_name) { + self.handler.add_compile_error( + &format!("duplicated keyword argument {}", arg_name), + kw.get_pos(), + ); + } + check_table.insert(arg_name.to_string()); + let arg_value_type = self.expr_or_any_type(&kw.node.value); + kwarg_types.push((arg_name.to_string(), arg_value_type.clone())); + } + if !params.is_empty() { + for (i, ty) in arg_types.iter().enumerate() { + let expected_ty = params[i].ty.clone(); + self.must_assignable_to(ty.clone(), expected_ty, args[i].get_pos(), None) + } + for (i, (arg_name, kwarg_ty)) in kwarg_types.iter().enumerate() { + if !params + .iter() + .map(|p| p.name.clone()) + .any(|x| x == *arg_name) + { + self.handler.add_compile_error( + &format!( + "arguments got an unexpected keyword argument '{}'", + arg_name + ), + kwargs[i].get_pos(), + ); + } + let expected_types: Vec> = params + .iter() + .filter(|p| p.name == *arg_name) + .map(|p| p.ty.clone()) + .collect(); + if !expected_types.is_empty() { + self.must_assignable_to( + kwarg_ty.clone(), + expected_types[0].clone(), + kwargs[i].get_pos(), + None, + ); + }; + } + } + } +} diff --git a/kclvm/sema/src/resolver/attr.rs b/kclvm/sema/src/resolver/attr.rs new file mode 100644 index 000000000..2a08ce2ac --- /dev/null +++ b/kclvm/sema/src/resolver/attr.rs @@ -0,0 +1,102 @@ +use std::rc::Rc; + +use crate::builtin::system_module::{get_system_module_members, UNITS, UNITS_NUMBER_MULTIPLIER}; +use crate::builtin::STRING_MEMBER_FUNCTIONS; +use crate::resolver::Resolver; +use crate::ty::{ModuleKind, Type, TypeKind}; +use kclvm_error::*; + +use super::node::ResolvedResult; + +impl<'ctx> Resolver<'ctx> { + pub fn check_attr_ty(&mut self, attr_ty: &Type, pos: Position) { + if !attr_ty.is_any() && !attr_ty.is_key() { + self.handler.add_error( + ErrorKind::IllegalAttributeError, + &[Message { + pos, + style: Style::LineAndColumn, + message: format!("type '{}'", attr_ty.ty_str()), + note: None, + }], + ); + } + } + + pub fn load_attr(&mut self, obj: Rc, attr: &str, pos: Position) -> ResolvedResult { + let (result, return_ty) = match &obj.kind { + TypeKind::Any => (true, self.any_ty()), + TypeKind::None + | TypeKind::Bool + | TypeKind::BoolLit(_) + | TypeKind::Int + | TypeKind::IntLit(_) + | TypeKind::Float + | TypeKind::FloatLit(_) + | TypeKind::List(_) + | TypeKind::NumberMultiplier(_) + | TypeKind::Function(_) + | TypeKind::Named(_) + | TypeKind::Void => (false, self.any_ty()), + TypeKind::Str | TypeKind::StrLit(_) => match STRING_MEMBER_FUNCTIONS.get(attr) { + Some(ty) => (true, Rc::new(ty.clone())), + None => (false, self.any_ty()), + }, + TypeKind::Dict(_, val_ty) => (true, Rc::new(val_ty.as_ref().clone())), + // union type load attr based the type guard. e.g, a: str|int; if a is str: xxx; if a is int: xxx; + // return sup([self.load_attr_type(t, attr, filename, line, column) for t in obj.types]) + TypeKind::Union(_) => (true, self.any_ty()), + TypeKind::Schema(schema_ty) => { + let (result, schema_attr_ty) = self.schema_load_attr(schema_ty, attr); + if result { + (result, schema_attr_ty) + } else if schema_ty.is_member_functions(attr) { + ( + true, + Rc::new(Type::function( + Some(obj.clone()), + Type::list_ref(self.any_ty()), + &[], + "", + false, + None, + )), + ) + } else { + (false, self.any_ty()) + } + } + TypeKind::Module(module_ty) => { + match &module_ty.kind { + crate::ty::ModuleKind::User => match self.scope_map.get(&module_ty.pkgpath) { + Some(scope) => match scope.borrow().elems.get(attr) { + Some(v) => { + if v.borrow().ty.is_module() { + self.handler + .add_compile_error(&format!("can not import the attribute '{}' from the module '{}'", attr, module_ty.pkgpath), pos.clone()); + } + (true, v.borrow().ty.clone()) + } + None => (false, self.any_ty()), + }, + None => (false, self.any_ty()), + }, + ModuleKind::System => { + if module_ty.pkgpath == UNITS && attr == UNITS_NUMBER_MULTIPLIER { + (true, Rc::new(Type::number_multiplier_non_lit_ty())) + } else { + let members = get_system_module_members(&module_ty.pkgpath); + (members.contains(&attr), self.any_ty()) + } + } + ModuleKind::Plugin => (true, self.any_ty()), + } + } + }; + if !result { + self.handler + .add_type_error(&format!("{} has no attribute {}", obj.ty_str(), attr), pos); + } + return_ty + } +} diff --git a/kclvm/sema/src/resolver/calculation.rs b/kclvm/sema/src/resolver/calculation.rs new file mode 100644 index 000000000..bbe2e1644 --- /dev/null +++ b/kclvm/sema/src/resolver/calculation.rs @@ -0,0 +1,320 @@ +use std::rc::Rc; + +use crate::resolver::Resolver; +use crate::ty::{has_any_type, is_upper_bound, sup, Type, TypeInferMethods, ZERO_LIT_TYPES}; +use kclvm_ast::ast; +use kclvm_error::Position; + +const DIV_OR_MOD_ZERO_MSG: &str = "integer division or modulo by zero"; + +impl<'ctx> Resolver<'ctx> { + /// Binary operator calculation table. + /// + /// Arithmetic (int or float; result has type float unless both operands have type int) + /// number + number # addition + /// number - number # subtraction + /// number * number # multiplication + /// number / number # real division (result is always a float) + /// number // number # floored division + /// number % number # remainder of floored division + /// number ^ number # bitwise XOR + /// number << number # bitwise left shift + /// number >> number # bitwise right shift + /// + /// Concatenation + /// string + string + /// list + list + /// + /// Repetition (string/list) + /// int * sequence + /// sequence * int + /// + /// Union + /// int | int + /// list | list + /// dict | dict + /// schema | schema + /// schema | dict + /// + /// Add: number + number, str + str, list + list + /// Sub: number - number + /// Mul: number * number, int * list, list * int, int * str, str * int + /// Div: number / number + /// FloorDiv: number // number + /// Mod: number % number + /// Pow: number ** number + /// LShift: int >> int + /// RShift: int << int + /// BitOr: int | int, list | list, dict | dict, schema | schema, schema | dict + /// BitXOr: int ^ int + /// BitAdd int & int + /// + /// And: any_type and any_type -> bool + /// Or: any_type1 or any_type1 -> sup([any_type1, any_type2]) + pub fn binary( + &mut self, + left: Rc, + right: Rc, + op: &ast::BinOp, + pos: Position, + ) -> Rc { + let t1 = self + .ctx + .ty_ctx + .literal_union_type_to_variable_type(left.clone()); + let t2 = self + .ctx + .ty_ctx + .literal_union_type_to_variable_type(right.clone()); + if has_any_type(&[t1.clone(), t2.clone()]) { + return self.any_ty(); + } + let number_binary = |left: &Rc, right: &Rc| { + if left.is_float() || right.is_float() { + Rc::new(Type::FLOAT) + } else { + Rc::new(Type::INT) + } + }; + let (result, return_ty) = match op { + ast::BinOp::Add => { + if t1.is_number() && t2.is_number() { + (true, number_binary(&t1, &t2)) + } else if t1.is_str() && t2.is_str() { + (true, self.str_ty()) + } else if t1.is_list() && t2.is_list() { + ( + true, + Type::list_ref(sup(&[t1.list_item_ty(), t2.list_item_ty()])), + ) + } else { + (false, self.any_ty()) + } + } + ast::BinOp::Sub | ast::BinOp::Pow => { + if t1.is_number() && t2.is_number() { + (true, number_binary(&t1, &t2)) + } else { + (false, self.any_ty()) + } + } + ast::BinOp::Mul => { + if t1.is_number() && t2.is_number() { + (true, number_binary(&t1, &t2)) + } else if t1.is_int() + && self + .ctx + .ty_ctx + .is_mul_val_type_or_mul_val_union_type(t2.clone()) + { + (true, t2) + } else if self + .ctx + .ty_ctx + .is_mul_val_type_or_mul_val_union_type(t1.clone()) + && t2.is_int() + { + (true, t1) + } else { + (false, self.any_ty()) + } + } + ast::BinOp::Div | ast::BinOp::FloorDiv => { + if t1.is_number() && t2.is_number() { + if ZERO_LIT_TYPES.contains(&t2) { + self.handler + .add_type_error(DIV_OR_MOD_ZERO_MSG, pos.clone()); + } + (true, number_binary(&t1, &t2)) + } else { + (false, self.any_ty()) + } + } + ast::BinOp::Mod => { + if t1.is_number() && t2.is_number() { + if ZERO_LIT_TYPES.contains(&t2) { + self.handler + .add_type_error(DIV_OR_MOD_ZERO_MSG, pos.clone()); + } + (true, self.int_ty()) + } else { + (false, self.any_ty()) + } + } + ast::BinOp::LShift | ast::BinOp::RShift | ast::BinOp::BitXor | ast::BinOp::BitAnd => { + if t1.is_int() && t2.is_int() { + (true, self.int_ty()) + } else { + (false, self.any_ty()) + } + } + ast::BinOp::BitOr => { + if t1.is_int() && t2.is_int() { + (true, self.int_ty()) + } else if t1.is_none() { + (true, t2) + } else if t2.is_none() { + (true, t1) + } else if t1.is_list() && t2.is_list() { + ( + true, + Type::list_ref(sup(&[t1.list_item_ty(), t2.list_item_ty()])), + ) + } else if t1.is_dict() && t2.is_dict() { + let (t1_key_ty, t1_val_ty) = t1.dict_entry_ty(); + let (t2_key_ty, t2_val_ty) = t2.dict_entry_ty(); + ( + true, + Type::dict_ref(sup(&[t1_key_ty, t2_key_ty]), sup(&[t1_val_ty, t2_val_ty])), + ) + } else if t1.is_schema() && (t2.is_schema() || t2.is_dict()) { + (true, t1) + } else { + (false, self.any_ty()) + } + } + ast::BinOp::And => (true, self.bool_ty()), + ast::BinOp::Or => (true, sup(&[t1, t2])), + ast::BinOp::As => { + if !is_upper_bound( + self.ctx.ty_ctx.infer_to_variable_type(t1.clone()), + t2.clone(), + ) { + self.handler.add_type_error( + &format!( + "Conversion of type '{}' to type '{}' may be a mistake because neither type sufficiently overlaps with the other", + t1.ty_str(), + t2.ty_str() + ), + pos.clone(), + ); + } + (true, t2) + } + }; + + if !result { + self.handler.add_type_error( + &format!( + "unsupported operand type(s) for {}: '{}' and '{}'", + op.symbol(), + left.ty_str(), + right.ty_str() + ), + pos, + ); + } + return_ty + } + + /// Unary operator calculation table + /// + /// + number unary positive (int, float) + /// - number unary negation (int, float) + /// ~ number unary bitwise inversion (int) + /// not x logical negation (any type) + pub fn unary(&mut self, ty: Rc, op: &ast::UnaryOp, pos: Position) -> Rc { + if has_any_type(&[ty.clone()]) { + return self.any_ty(); + } + let var_ty = self + .ctx + .ty_ctx + .literal_union_type_to_variable_type(ty.clone()); + let result = match op { + ast::UnaryOp::UAdd | ast::UnaryOp::USub => var_ty.is_number(), + ast::UnaryOp::Invert => var_ty.is_int() || var_ty.is_bool(), + ast::UnaryOp::Not => true, + }; + if result { + var_ty + } else { + self.handler.add_type_error( + &format!( + "bad operand type for unary {}: '{}'", + op.symbol(), + ty.ty_str(), + ), + pos, + ); + self.any_ty() + } + } + + /// Compare operator calculation table + /// + /// bool # False < True False < True + /// int # mathematical 1 < 2 + /// float # as defined by IEEE 754 1.0 < 2.0 + /// string # lexicographical "1" < 2 + /// list # lexicographical [1] == [2] + /// iterable # 1 in [1, 2, 3], "s" in "ss", "key" in Schema + pub fn compare( + &mut self, + left: Rc, + right: Rc, + op: &ast::CmpOp, + pos: Position, + ) -> Rc { + let t1 = self.ctx.ty_ctx.literal_union_type_to_variable_type(left); + let t2 = self.ctx.ty_ctx.literal_union_type_to_variable_type(right); + if has_any_type(&[t1.clone(), t2.clone()]) { + return self.any_ty(); + } + if self + .ctx + .ty_ctx + .is_number_type_or_number_union_type(t1.clone()) + && self + .ctx + .ty_ctx + .is_number_type_or_number_union_type(t2.clone()) + && !matches!(op, ast::CmpOp::In | ast::CmpOp::NotIn) + { + return self.bool_ty(); + } + if self + .ctx + .ty_ctx + .is_primitive_type_or_primitive_union_type(t1.clone()) + && self + .ctx + .ty_ctx + .is_primitive_type_or_primitive_union_type(t2.clone()) + && matches!(op, ast::CmpOp::Eq | ast::CmpOp::NotEq) + { + return self.bool_ty(); + } + if t1.is_list() && t2.is_list() { + return self.bool_ty(); + } + if t1.is_dict_or_schema() && t2.is_dict_or_schema() { + return self.bool_ty(); + } + if matches!(op, ast::CmpOp::In | ast::CmpOp::NotIn) && t2.is_iterable() { + return self.bool_ty(); + } + if (t1.is_none() || t2.is_none()) + && matches!( + op, + ast::CmpOp::Eq + | ast::CmpOp::NotEq + | ast::CmpOp::Is + | ast::CmpOp::IsNot + | ast::CmpOp::Not + ) + { + return self.bool_ty(); + } + self.handler.add_type_error( + &format!( + "unsupported operand type(s) for {}: '{}' and '{}'", + op.symbol(), + t1.ty_str(), + t2.ty_str(), + ), + pos, + ); + self.any_ty() + } +} diff --git a/kclvm/sema/src/resolver/config.rs b/kclvm/sema/src/resolver/config.rs new file mode 100644 index 000000000..78cb72c6d --- /dev/null +++ b/kclvm/sema/src/resolver/config.rs @@ -0,0 +1,374 @@ +use std::rc::Rc; + +use super::{ + scope::{ScopeObject, ScopeObjectKind}, + Resolver, +}; +use crate::resolver::pos::GetPos; +use crate::ty::SchemaType; +use crate::ty::{Type, TypeKind}; +use kclvm_ast::ast; +use kclvm_error::Position; + +/// Config Expr type check state. +/// +/// e.g. +/// ```no_check +/// schema Person: +/// name: str +/// +/// person = Person { +/// name: 1 # Type error, expect str, got int(1) +/// } +/// ``` +pub enum SwitchConfigContextState { + KeepConfigUnchanged = 0, + SwitchConfigOnce = 1, +} + +impl<'ctx> Resolver<'ctx> { + #[inline] + pub(crate) fn new_config_expr_context_item( + &mut self, + name: &str, + ty: Rc, + start: Position, + end: Position, + ) -> ScopeObject { + ScopeObject { + name: name.to_string(), + start, + end, + ty, + kind: ScopeObjectKind::Attribute, + } + } + + /// Finds the items needed to switch the context by name 'key_name' + /// + /// At present, only when the top item of the stack is 'KCLSchemaTypeObject' or 'KCLDictTypeObject', + /// it will return the next item (the attribute named 'key_name' in 'KCLSchemaTypeObject' + /// or the value of 'key_name' in 'KCLDictTypeObject') needed to be pushed. + /// If the top item of the stack is not 'KCLSchemaTypeObject' or 'KCLDictTypeObject', + /// it will return 'None'. + /// + /// Args: + /// key_name: The name of the item needed to be pushed onto the 'config_expr_context' stack + /// + /// Returns: + /// The item needed to be pushed onto the 'config_expr_context' stack + pub(crate) fn find_schema_attr_obj_from_schema_expr_stack( + &mut self, + key_name: &str, + ) -> Option { + if key_name.is_empty() { + None + } else { + match self.ctx.config_expr_context.last() { + Some(obj) => { + let obj = obj.clone(); + match obj { + Some(obj) => match &obj.ty.kind { + TypeKind::Dict(_, val_ty) => Some(self.new_config_expr_context_item( + key_name, + val_ty.clone(), + obj.start.clone(), + obj.end.clone(), + )), + TypeKind::Schema(schema_ty) => { + match schema_ty.get_obj_of_attr(key_name) { + Some(attr_ty_obj) => Some(self.new_config_expr_context_item( + key_name, + attr_ty_obj.ty.clone(), + attr_ty_obj.pos.clone(), + attr_ty_obj.pos.clone(), + )), + None => match &schema_ty.index_signature { + Some(index_signature) => { + Some(self.new_config_expr_context_item( + key_name, + index_signature.val_ty.clone(), + obj.start.clone(), + obj.end.clone(), + )) + } + None => None, + }, + } + } + _ => None, + }, + None => None, + } + } + None => None, + } + } + } + + /// Switch the context in 'config_expr_context' stack by AST nodes 'Identifier', 'Subscript' or 'Literal' + /// + /// Args: + /// key: AST nodes 'Identifier', 'Subscript' or 'Literal' + /// + /// Returns: + /// push stack times + pub(crate) fn switch_config_expr_context_by_key( + &mut self, + key: &'ctx Option>, + ) -> usize { + match key { + Some(key) => { + let names: Vec = match &key.node { + ast::Expr::Identifier(identifier) => identifier.names.clone(), + ast::Expr::Subscript(subscript) => { + if let ast::Expr::Identifier(identifier) = &subscript.value.node { + if let Some(index) = &subscript.index { + if matches!(index.node, ast::Expr::NumberLit(_)) { + identifier.names.clone() + } else { + return SwitchConfigContextState::KeepConfigUnchanged as usize; + } + } else { + return SwitchConfigContextState::KeepConfigUnchanged as usize; + } + } else { + return SwitchConfigContextState::KeepConfigUnchanged as usize; + } + } + ast::Expr::StringLit(string_lit) => vec![string_lit.value.clone()], + _ => return SwitchConfigContextState::KeepConfigUnchanged as usize, + }; + self.switch_config_expr_context_by_names(&names) + } + None => SwitchConfigContextState::KeepConfigUnchanged as usize, + } + } + + /// Switch the context in 'config_expr_context' stack by name + /// + /// find the next item that needs to be pushed into the stack, + /// according to name and the top context of the stack, and push the item into the stack. + /// + /// Args: + /// name: the name of item to be pushed + /// + /// Returns: + /// push stack times + pub(crate) fn switch_config_exprr_context_by_name(&mut self, name: &str) -> usize { + let ctx_obj = self.find_schema_attr_obj_from_schema_expr_stack(name); + self.switch_config_expr_context(ctx_obj) as usize + } + + /// Push method for the 'config_expr_context' stack + /// + /// Args: + /// config_ctx_obj: the item needed to be pushed + /// + /// Returns: + /// push stack times + pub(crate) fn switch_config_expr_context( + &mut self, + config_ctx_obj: Option, + ) -> SwitchConfigContextState { + self.ctx.config_expr_context.push(config_ctx_obj); + SwitchConfigContextState::SwitchConfigOnce + } + + /// Pop method for the 'config_expr_context' stack + /// + /// Returns: + /// the item poped from stack. + #[inline] + pub(crate) fn restore_config_expr_context(&mut self) -> Option { + match self.ctx.config_expr_context.pop() { + Some(obj) => obj, + None => None, + } + } + + /// Pop all method for the 'config_expr_context' stack + /// + /// Args: + /// stack_depth: 'stack_depth' is the number of stacks that need to be popped + /// clear_all: 'clear_all' is True to clear all the items of the stack + /// + pub(crate) fn clear_config_expr_context(&mut self, stack_depth: usize, clear_all: bool) { + if clear_all { + self.ctx.config_expr_context.clear() + } else { + for _ in 0..stack_depth { + self.restore_config_expr_context(); + } + } + } + + /// Switch the context in 'config_expr_context' stack by names + /// + /// Traverse all name in 'names', find the next item that needs to be pushed into the stack, + /// according to name and the top context of the stack, and push the item into the stack. + /// + /// Args: + /// names: A list of string containing the names of items to be pushed + /// + /// Returns: + /// push stack times + pub(crate) fn switch_config_expr_context_by_names(&mut self, names: &[String]) -> usize { + let mut stack_depth = 0; + for name in names { + stack_depth += self.switch_config_exprr_context_by_name(name); + } + stack_depth + } + + /// Check whether the key of config expr meets the constraints of schema attributes such as final, defined. + /// + /// Args: + /// name: the name of key + /// key: the ast node of key + /// check_rules: the constraints, such as 'check_defined' + pub(crate) fn check_config_expr_by_key_name( + &mut self, + name: &str, + key: &'ctx ast::NodeRef, + ) { + if !name.is_empty() { + if let Some(Some(obj)) = self.ctx.config_expr_context.last() { + let obj = obj.clone(); + if let TypeKind::Schema(schema_ty) = &obj.ty.kind { + self.check_config_attr(name, &key.get_pos(), schema_ty); + } + } + } + } + + /// Check the key-value in 'ConfigExpr', such as check_defined and check_type + /// + /// Notes: + /// If the top item of the 'config_expr_context' stack is 'None', the check will be skipped. + /// + /// Args: + /// key: the key of 'ConfigExpr'. + /// value: the value of 'ConfigExpr'. + /// check_rules: Some checks on the key individually,such as check_defined. + pub(crate) fn check_config_entry( + &mut self, + key: &'ctx Option>, + value: &'ctx ast::NodeRef, + ) { + if let Some(key) = key { + if let Some(Some(_)) = self.ctx.config_expr_context.last() { + let mut has_index = false; + let names: Vec = match &key.node { + ast::Expr::Identifier(identifier) => identifier.names.clone(), + ast::Expr::Subscript(subscript) => { + if let ast::Expr::Identifier(identifier) = &subscript.value.node { + if let Some(index) = &subscript.index { + if matches!(index.node, ast::Expr::NumberLit(_)) { + has_index = true; + identifier.names.clone() + } else { + return; + } + } else { + return; + } + } else { + return; + } + } + ast::Expr::StringLit(string_lit) => vec![string_lit.value.clone()], + _ => return, + }; + let mut stack_depth = 0; + for name in &names { + self.check_config_expr_by_key_name(name, key); + stack_depth += self.switch_config_exprr_context_by_name(name); + } + let mut val_ty = self.expr(value); + for _ in 0..names.len() - 1 { + val_ty = Type::dict_ref(self.str_ty(), val_ty); + } + if has_index { + val_ty = Type::list_ref(val_ty); + } + if let Some(Some(obj_last)) = self.ctx.config_expr_context.last() { + let ty = obj_last.ty.clone(); + let pos = obj_last.start.clone(); + self.must_assignable_to(val_ty, ty, key.get_pos(), Some(pos)); + } + self.clear_config_expr_context(stack_depth, false); + } + } + } + + /// Check config attr has been defined. + pub(crate) fn check_config_attr(&mut self, attr: &str, pos: &Position, schema_ty: &SchemaType) { + let runtime_type = kclvm::schema_runtime_type(&schema_ty.name, &schema_ty.pkgpath); + match self.ctx.schema_mapping.get(&runtime_type) { + Some(schema_mapping_ty) => { + let schema_ty = schema_mapping_ty.borrow(); + if schema_ty.get_obj_of_attr(attr).is_none() + && !schema_ty.is_mixin + && schema_ty.index_signature.is_none() + { + self.handler.add_compile_error( + &format!( + "Cannot add member '{}' to schema '{}'", + attr, schema_ty.name + ), + pos.clone(), + ); + } + } + None => { + if schema_ty.get_obj_of_attr(attr).is_none() + && !schema_ty.is_mixin + && schema_ty.index_signature.is_none() + { + self.handler.add_compile_error( + &format!( + "Cannot add member '{}' to schema '{}'", + attr, schema_ty.name + ), + pos.clone(), + ); + } + } + }; + } + + /// Schema load atr + pub(crate) fn schema_load_attr( + &mut self, + schema_ty: &SchemaType, + attr: &str, + ) -> (bool, Rc) { + let runtime_type = kclvm::schema_runtime_type(&schema_ty.name, &schema_ty.pkgpath); + match self.ctx.schema_mapping.get(&runtime_type) { + Some(schema_mapping_ty) => { + let schema_ty = schema_mapping_ty.borrow(); + match schema_ty.get_type_of_attr(attr) { + Some(ty) => (true, ty), + None => { + if schema_ty.is_mixin || schema_ty.index_signature.is_some() { + (true, self.any_ty()) + } else { + (false, self.any_ty()) + } + } + } + } + None => match schema_ty.get_type_of_attr(attr) { + Some(ty) => (true, ty), + None => { + if schema_ty.is_mixin || schema_ty.index_signature.is_some() { + (true, self.any_ty()) + } else { + (false, self.any_ty()) + } + } + }, + } + } +} diff --git a/kclvm/sema/src/resolver/format.rs b/kclvm/sema/src/resolver/format.rs new file mode 100644 index 000000000..047a547a3 --- /dev/null +++ b/kclvm/sema/src/resolver/format.rs @@ -0,0 +1 @@ +pub const VALID_FORMAT_SPEC_SET: [&str; 2] = ["#json", "#yaml"]; diff --git a/kclvm/sema/src/resolver/global.rs b/kclvm/sema/src/resolver/global.rs new file mode 100644 index 000000000..b5d7a267c --- /dev/null +++ b/kclvm/sema/src/resolver/global.rs @@ -0,0 +1,847 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use crate::info::is_private_field; +use crate::resolver::Resolver; +use crate::ty::{ + is_upper_bound, DecoratorTarget, FunctionType, Parameter, SchemaAttr, SchemaIndexSignature, + SchemaType, Type, TypeKind, RESERVED_TYPE_IDENTIFIERS, +}; +use indexmap::IndexMap; +use kclvm_ast::ast; +use kclvm_ast::walker::MutSelfTypedResultWalker; +use kclvm_error::*; + +use super::scope::{ScopeObject, ScopeObjectKind}; +use crate::resolver::pos::GetPos; + +const MAX_SCOPE_SCAN_COUNT: usize = 3; +pub const MIXIN_SUFFIX: &str = "Mixin"; +pub const PROTOCOL_SUFFIX: &str = "Protocol"; + +impl<'ctx> Resolver<'ctx> { + /// Init global types including top-level global variable types and + /// schema types. Because the schema allows backward references, + /// we scan multiple times. + pub(crate) fn init_global_types(&mut self) { + // 1. Scan all schema and rule type symbols + let pkgpath = &self.ctx.pkgpath; + let modules = match self.program.pkgs.get(pkgpath) { + Some(modules) => modules, + None => bug!("empty modules on pkgpath {}", pkgpath), + }; + // 1. Scan all schema and rule type symbol + for module in modules { + let pkgpath = &self.ctx.pkgpath.clone(); + let filename = &module.filename; + self.change_package_context(pkgpath, filename); + for stmt in &module.body { + let (start, end) = stmt.get_span_pos(); + let (name, doc, is_mixin, is_protocol, is_rule) = match &stmt.node { + ast::Stmt::Schema(schema_stmt) => ( + &schema_stmt.name.node, + &schema_stmt.doc, + schema_stmt.is_mixin, + schema_stmt.is_protocol, + false, + ), + ast::Stmt::Rule(rule_stmt) => { + (&rule_stmt.name.node, &rule_stmt.doc, false, false, true) + } + _ => continue, + }; + if self.contains_object(name) { + self.handler.add_error( + ErrorKind::UniqueKeyError, + &[Message { + pos: start.clone(), + style: Style::LineAndColumn, + message: format!("unique key error name '{}'", name), + note: None, + }], + ); + continue; + } + let schema_ty = SchemaType { + name: name.to_string(), + pkgpath: self.ctx.pkgpath.clone(), + filename: self.ctx.filename.clone(), + doc: doc.to_string(), + is_instance: false, + is_mixin, + is_protocol, + is_rule, + base: None, + protocol: None, + mixins: vec![], + attrs: IndexMap::default(), + func: Box::new(FunctionType { + doc: doc.to_string(), + params: vec![], + self_ty: None, + return_ty: Rc::new(Type::VOID), + is_variadic: false, + kw_only_index: None, + }), + index_signature: None, + decorators: vec![], + }; + self.insert_object( + name, + ScopeObject { + name: name.to_string(), + start, + end, + ty: Rc::new(Type::schema(schema_ty)), + kind: ScopeObjectKind::Definition, + }, + ) + } + } + // 2. Scan all variable type symbol + self.init_global_var_types(true); + // 3. Build all schema types + for i in 0..MAX_SCOPE_SCAN_COUNT { + for module in modules { + let pkgpath = &self.ctx.pkgpath.clone(); + let filename = &module.filename; + self.change_package_context(pkgpath, filename); + for stmt in &module.body { + let (start, end) = stmt.get_span_pos(); + let schema_ty = match &stmt.node { + ast::Stmt::Schema(schema_stmt) => { + let parent_ty = self.build_schema_parent_type(schema_stmt); + let protocol_ty = self.build_schema_protocol_type(schema_stmt); + self.build_schema_type( + schema_stmt, + parent_ty, + protocol_ty, + i == MAX_SCOPE_SCAN_COUNT - 1, + ) + } + ast::Stmt::Rule(rule_stmt) => { + let protocol_ty = self.build_rule_protocol_type(rule_stmt); + self.build_rule_type( + rule_stmt, + protocol_ty, + i == MAX_SCOPE_SCAN_COUNT - 1, + ) + } + _ => continue, + }; + self.insert_object( + &schema_ty.name.clone(), + ScopeObject { + name: schema_ty.name.to_string(), + start, + end, + ty: Rc::new(Type::schema(schema_ty)), + kind: ScopeObjectKind::Definition, + }, + ) + } + } + } + // 2. Build all variable types + self.init_global_var_types(false); + } + + /// Init global var types. + pub(crate) fn init_global_var_types(&mut self, unique_check: bool) { + let pkgpath = &self.ctx.pkgpath; + let modules = match self.program.pkgs.get(pkgpath) { + Some(modules) => modules, + None => bug!("empty modules on pkgpath {}", pkgpath), + }; + // 1. Scan all schema and rule type symbol + for module in modules { + for stmt in &module.body { + if matches!(stmt.node, ast::Stmt::TypeAlias(_)) { + self.stmt(stmt); + } + } + self.init_scope_with_stmts(&module.body, unique_check); + } + } + + fn init_scope_with_stmts( + &mut self, + stmts: &'ctx [ast::NodeRef], + unique_check: bool, + ) { + for stmt in stmts { + match &stmt.node { + ast::Stmt::Assign(assign_stmt) => { + self.init_scope_with_assign_stmt(assign_stmt, unique_check) + } + ast::Stmt::Unification(unification_stmt) => { + self.init_scope_with_unification_stmt(unification_stmt, unique_check) + } + ast::Stmt::If(if_stmt) => { + self.init_scope_with_stmts(&if_stmt.body, unique_check); + self.init_scope_with_stmts(&if_stmt.orelse, unique_check); + } + _ => {} + } + } + } + + fn init_scope_with_assign_stmt( + &mut self, + assign_stmt: &'ctx ast::AssignStmt, + unique_check: bool, + ) { + for target in &assign_stmt.targets { + let name = &target.node.names[0]; + let (start, end) = target.get_span_pos(); + if self.contains_object(name) && !is_private_field(name) && unique_check { + self.handler.add_error( + ErrorKind::ImmutableError, + &[ + Message { + pos: start.clone(), + style: Style::LineAndColumn, + message: format!( + "Can not change the value of '{}', because it was declared immutable", + name + ), + note: None, + }, + Message { + pos: self + .scope + .borrow() + .elems + .get(name) + .unwrap() + .borrow() + .start + .clone(), + style: Style::LineAndColumn, + message: format!("The variable '{}' is declared here firstly", name), + note: Some(format!("change the variable name to '_{}'", name)), + }, + ], + ); + continue; + } + let ty = if let Some(ty_annotation) = &assign_stmt.ty { + let ty = &ty_annotation.node; + let ty = self.parse_ty_with_scope(ty, ty_annotation.get_pos()); + if let Some(obj) = self.scope.borrow().elems.get(name) { + let obj = obj.borrow(); + if !is_upper_bound(obj.ty.clone(), ty.clone()) { + self.handler.add_error( + ErrorKind::TypeError, + &[ + Message { + pos: obj.start.clone(), + style: Style::LineAndColumn, + message: format!("expect {}", obj.ty.ty_str()), + note: None, + }, + Message { + pos: start.clone(), + style: Style::LineAndColumn, + message: format!("can not change the type of '{}'", name), + note: Some(format!("got {}", ty.ty_str())), + }, + ], + ); + } + } + ty + } else if let Some(obj) = self.scope.borrow().elems.get(name) { + obj.borrow().ty.clone() + } else { + self.any_ty() + }; + self.insert_object( + name, + ScopeObject { + name: name.to_string(), + start, + end, + ty, + kind: ScopeObjectKind::Variable, + }, + ); + } + } + + fn init_scope_with_unification_stmt( + &mut self, + unification_stmt: &'ctx ast::UnificationStmt, + unique_check: bool, + ) { + let target = &unification_stmt.target; + let name = &target.node.names[0]; + let (start, end) = target.get_span_pos(); + if self.contains_object(name) && !is_private_field(name) && unique_check { + self.handler.add_error( + ErrorKind::ImmutableError, + &[ + Message { + pos: start, + style: Style::LineAndColumn, + message: format!( + "Can not change the value of '{}', because it was declared immutable", + name + ), + note: None, + }, + Message { + pos: self + .scope + .borrow() + .elems + .get(name) + .unwrap() + .borrow() + .start + .clone(), + style: Style::LineAndColumn, + message: format!("The variable '{}' is declared here firstly", name), + note: Some(format!("Change the variable name to '_{}'", name)), + }, + ], + ); + return; + } + let ty = self.walk_identifier(&unification_stmt.value.node.name.node); + self.insert_object( + name, + ScopeObject { + name: name.to_string(), + start, + end, + ty, + kind: ScopeObjectKind::Variable, + }, + ); + } + + pub(crate) fn build_rule_protocol_type( + &mut self, + rule_stmt: &'ctx ast::RuleStmt, + ) -> Option> { + if let Some(host_name) = &rule_stmt.for_host_name { + let ty = self.walk_identifier(&host_name.node); + match &ty.kind { + TypeKind::Schema(schema_ty) if schema_ty.is_protocol && !schema_ty.is_instance => { + Some(Box::new(schema_ty.clone())) + } + _ => { + self.handler.add_error( + ErrorKind::IllegalInheritError, + &[Message { + pos: host_name.get_pos(), + style: Style::LineAndColumn, + message: format!( + "invalid schema inherit object type, expect protocol, got '{}'", + ty.ty_str() + ), + note: None, + }], + ); + None + } + } + } else { + None + } + } + + pub(crate) fn build_schema_protocol_type( + &mut self, + schema_stmt: &'ctx ast::SchemaStmt, + ) -> Option> { + if let Some(host_name) = &schema_stmt.for_host_name { + if !schema_stmt.is_mixin { + self.handler.add_error( + ErrorKind::IllegalInheritError, + &[Message { + pos: host_name.get_pos(), + style: Style::LineAndColumn, + message: "only schema mixin can inherit from protocol".to_string(), + note: None, + }], + ); + return None; + } + // Mixin type check with protocol + let ty = self.walk_identifier(&host_name.node); + match &ty.kind { + TypeKind::Schema(schema_ty) if schema_ty.is_protocol && !schema_ty.is_instance => { + Some(Box::new(schema_ty.clone())) + } + _ => { + self.handler.add_error( + ErrorKind::IllegalInheritError, + &[Message { + pos: host_name.get_pos(), + style: Style::LineAndColumn, + message: format!( + "invalid schema inherit object type, expect protocol, got '{}'", + ty.ty_str() + ), + note: None, + }], + ); + None + } + } + } else { + None + } + } + + pub(crate) fn build_schema_parent_type( + &mut self, + schema_stmt: &'ctx ast::SchemaStmt, + ) -> Option> { + if let Some(parent_name) = &schema_stmt.parent_name { + let ty = self.walk_identifier(&parent_name.node); + match &ty.kind { + TypeKind::Schema(schema_ty) + if !schema_ty.is_protocol && !schema_ty.is_mixin && !schema_ty.is_instance => + { + Some(Box::new(schema_ty.clone())) + } + _ => { + self.handler.add_error( + ErrorKind::IllegalInheritError, + &[Message { + pos: parent_name.get_pos(), + style: Style::LineAndColumn, + message: format!( + "invalid schema inherit object type, expect protocol, got '{}'", + ty.ty_str() + ), + note: None, + }], + ); + None + } + } + } else { + None + } + } + + pub(crate) fn build_schema_type( + &mut self, + schema_stmt: &'ctx ast::SchemaStmt, + parent_ty: Option>, + protocol_ty: Option>, + should_add_schema_ref: bool, + ) -> SchemaType { + let name = &schema_stmt.name.node; + let pos = schema_stmt.name.get_end_pos(); + if RESERVED_TYPE_IDENTIFIERS.contains(&name.as_str()) { + self.handler.add_compile_error( + &format!( + "schema name '{}' cannot be the same as the built-in types ({:?})", + name, RESERVED_TYPE_IDENTIFIERS + ), + pos.clone(), + ); + } + if schema_stmt.is_protocol && !name.ends_with(PROTOCOL_SUFFIX) { + self.handler.add_error( + ErrorKind::CompileError, + &[Message { + pos: pos.clone(), + style: Style::LineAndColumn, + message: format!("schema protocol name must end with '{}'", PROTOCOL_SUFFIX), + note: None, + }], + ); + } + if schema_stmt.is_protocol && !schema_stmt.has_only_attribute_definitions() { + self.handler.add_compile_error( + "a protocol is only allowed to define attributes in it", + pos.clone(), + ); + } + let parent_name = parent_ty + .as_ref() + .map_or("".to_string(), |ty| ty.name.clone()); + if parent_name.ends_with(MIXIN_SUFFIX) { + self.handler.add_error( + ErrorKind::IllegalInheritError, + &[Message { + pos: pos.clone(), + style: Style::LineAndColumn, + message: format!("mixin inheritance {} is prohibited", parent_name), + note: None, + }], + ); + } + let schema_attr_names = schema_stmt.get_left_identifier_list(); + let schema_attr_names: Vec = schema_attr_names + .iter() + .map(|attr| attr.2.clone()) + .collect(); + let index_signature = if let Some(index_signature) = &schema_stmt.index_signature { + if let Some(index_sign_name) = &index_signature.node.key_name { + if schema_attr_names.contains(index_sign_name) { + self.handler.add_error( + ErrorKind::IndexSignatureError, + &[Message { + pos: index_signature.get_pos(), + style: Style::LineAndColumn, + message: format!("index signature attribute name '{}' cannot have the same name as schema attributes", index_sign_name), + note: None, + }], + ); + } + } + let key_ty = self.parse_ty_str_with_scope( + &index_signature.node.key_type.node, + index_signature.node.key_type.get_pos(), + ); + let val_ty = self.parse_ty_str_with_scope( + &index_signature.node.value_type.node, + index_signature.node.value_type.get_pos(), + ); + if !self + .ctx + .ty_ctx + .is_str_type_or_str_union_type(key_ty.clone()) + { + self.handler.add_error( + ErrorKind::IndexSignatureError, + &[Message { + pos: pos.clone(), + style: Style::LineAndColumn, + message: format!("invalid index signature key type: '{}'", key_ty.ty_str()), + note: None, + }], + ); + } + Some(Box::new(SchemaIndexSignature { + key_name: index_signature.node.key_name.clone(), + key_ty, + val_ty, + any_other: index_signature.node.any_other, + })) + } else { + None + }; + // Schema attributes + let mut attr_obj_map: IndexMap = IndexMap::default(); + attr_obj_map.insert( + kclvm::SCHEMA_SETTINGS_ATTR_NAME.to_string(), + SchemaAttr { + is_optional: true, + has_default: false, + ty: Type::dict_ref(self.str_ty(), self.any_ty()), + pos: Position { + filename: self.ctx.filename.clone(), + line: pos.line, + column: pos.column, + }, + }, + ); + for stmt in &schema_stmt.body { + let pos = stmt.get_pos(); + let (name, ty, is_optional, has_default) = match &stmt.node { + ast::Stmt::Unification(unification_stmt) => { + let name = unification_stmt.value.node.name.node.names[0].clone(); + let ty = self.parse_ty_str_with_scope(&name, pos.clone()); + let is_optional = true; + let has_default = true; + (name, ty, is_optional, has_default) + } + ast::Stmt::SchemaAttr(schema_attr) => { + let name = schema_attr.name.node.clone(); + let ty = self.parse_ty_with_scope( + &schema_attr + .ty + .as_ref() + .map_or(ast::Type::Any, |ty| ty.node.clone()), + schema_attr + .ty + .as_ref() + .map_or(pos.clone(), |ty| ty.get_pos()), + ); + let is_optional = schema_attr.is_optional; + let has_default = schema_attr.value.is_some(); + (name, ty, is_optional, has_default) + } + _ => continue, + }; + let base_attr_ty = match parent_ty { + Some(ref ty) => ty.get_type_of_attr(&name).map_or(self.any_ty(), |ty| ty), + None => self.any_ty(), + }; + if !attr_obj_map.contains_key(&name) { + let existed_attr = parent_ty.as_ref().and_then(|ty| ty.get_obj_of_attr(&name)); + attr_obj_map.insert( + name.clone(), + SchemaAttr { + is_optional: existed_attr.map_or(is_optional, |attr| attr.is_optional), + has_default, + ty: ty.clone(), + pos: pos.clone(), + }, + ); + } + if !is_upper_bound(attr_obj_map.get(&name).unwrap().ty.clone(), ty.clone()) + || !is_upper_bound(base_attr_ty.clone(), ty.clone()) + { + self.handler.add_type_error( + &format!( + "can't change schema field type of '{}' from {} to {}", + name, + attr_obj_map.get(&name).unwrap().ty.clone().ty_str(), + ty.ty_str() + ), + pos.clone(), + ); + } + if is_optional && !attr_obj_map.get(&name).unwrap().is_optional { + self.handler.add_type_error( + &format!( + "can't change the required schema attribute of '{}' to optional", + name + ), + pos.clone(), + ); + } + if let Some(ref index_signature_obj) = index_signature { + if !index_signature_obj.any_other + && !is_upper_bound(index_signature_obj.val_ty.clone(), ty.clone()) + { + self.handler.add_error( + ErrorKind::IndexSignatureError, + &[Message { + pos: pos.clone(), + style: Style::LineAndColumn, + message: format!("the type '{}' of schema attribute '{}' does not meet the index signature definition {}", ty.ty_str(), name, index_signature_obj.ty_str()), + note: None, + }], + ); + } + } + } + // Mixin types + let mut mixin_types: Vec = vec![]; + for mixin in &schema_stmt.mixins { + let mixin_names = &mixin.node.names; + if !mixin_names[mixin_names.len() - 1].ends_with(MIXIN_SUFFIX) { + self.handler.add_error( + ErrorKind::NameError, + &[Message { + pos: pos.clone(), + style: Style::LineAndColumn, + message: format!( + "a valid mixin name should end with 'Mixin', got '{}'", + mixin_names[mixin_names.len() - 1] + ), + note: None, + }], + ); + } + let ty = self.walk_identifier(&mixin.node); + let mixin_ty = match &ty.kind { + TypeKind::Schema(schema_ty) + if !schema_ty.is_protocol && schema_ty.is_mixin && !schema_ty.is_instance => + { + Some(schema_ty.clone()) + } + _ => { + self.handler.add_error( + ErrorKind::IllegalInheritError, + &[Message { + pos: mixin.get_pos(), + style: Style::LineAndColumn, + message: format!("illegal schema mixin object type '{}'", ty.ty_str()), + note: None, + }], + ); + None + } + }; + + if let Some(mixin_ty) = mixin_ty { + for (name, attr) in &mixin_ty.attrs { + if !attr_obj_map.contains_key(name) { + attr_obj_map.insert(name.to_string(), attr.clone()); + } + } + mixin_types.push(mixin_ty); + } + } + // Params + let mut params: Vec = vec![]; + if let Some(args) = &schema_stmt.args { + for (i, para) in args.node.args.iter().enumerate() { + let name = para.node.get_name(); + let pos = para.get_pos(); + if schema_attr_names.contains(&name) { + self.handler.add_compile_error( + &format!( + "Unexpected parameter name '{}' with the same name as the schema attribute", + name + ), + pos.clone(), + ); + } + let ty = args.node.get_arg_type(i); + let ty = self.parse_ty_with_scope(&ty, pos); + params.push(Parameter { + name, + ty: ty.clone(), + has_default: args.node.defaults.get(i).is_some(), + }); + } + } + let schema_runtime_ty = kclvm::schema_runtime_type(name, &self.ctx.pkgpath); + if should_add_schema_ref { + let idx1 = self + .ctx + .ty_ctx + .dep_graph + .add_node(schema_runtime_ty.clone()); + if let Some(ref parent_ty) = parent_ty { + let parent_schema_runtime_ty = + kclvm::schema_runtime_type(&parent_ty.name, &parent_ty.pkgpath); + let idx2 = self.ctx.ty_ctx.dep_graph.add_node(parent_schema_runtime_ty); + self.ctx.ty_ctx.dep_graph.add_edge(idx1, idx2, ()); + } + } + let decorators = self.resolve_decorators( + &schema_stmt.decorators, + DecoratorTarget::Schema, + &schema_stmt.name.node, + ); + let schema_ty = SchemaType { + name: schema_stmt.name.node.clone(), + pkgpath: self.ctx.pkgpath.clone(), + filename: self.ctx.filename.clone(), + doc: schema_stmt.doc.clone(), + is_instance: false, + is_mixin: schema_stmt.is_mixin, + is_protocol: schema_stmt.is_protocol, + is_rule: false, + base: parent_ty, + protocol: protocol_ty, + mixins: mixin_types, + attrs: attr_obj_map, + func: Box::new(FunctionType { + doc: schema_stmt.doc.clone(), + params, + self_ty: None, + return_ty: Rc::new(Type::ANY), + is_variadic: false, + kw_only_index: None, + }), + index_signature, + decorators, + }; + self.ctx + .schema_mapping + .insert(schema_runtime_ty, Rc::new(RefCell::new(schema_ty.clone()))); + schema_ty + } + + pub(crate) fn build_rule_type( + &mut self, + rule_stmt: &'ctx ast::RuleStmt, + protocol_ty: Option>, + should_add_schema_ref: bool, + ) -> SchemaType { + let name = &rule_stmt.name.node; + let pos = rule_stmt.name.get_end_pos(); + if RESERVED_TYPE_IDENTIFIERS.contains(&name.as_str()) { + self.handler.add_compile_error( + &format!( + "rule name '{}' cannot be the same as the built-in types ({:?})", + name, RESERVED_TYPE_IDENTIFIERS + ), + pos, + ); + } + // Parent types + let mut parent_types: Vec = vec![]; + for rule in &rule_stmt.parent_rules { + let ty = self.walk_identifier(&rule.node); + let parent_ty = match &ty.kind { + TypeKind::Schema(schema_ty) if !schema_ty.is_rule && !schema_ty.is_instance => { + Some(schema_ty.clone()) + } + _ => { + self.handler.add_error( + ErrorKind::IllegalInheritError, + &[Message { + pos: rule.get_pos(), + style: Style::LineAndColumn, + message: format!("illegal schema mixin object type '{}'", ty.ty_str()), + note: None, + }], + ); + None + } + }; + if let Some(parent_ty) = parent_ty { + parent_types.push(parent_ty); + } + } + // Params + let mut params: Vec = vec![]; + if let Some(args) = &rule_stmt.args { + for (i, para) in args.node.args.iter().enumerate() { + let name = para.node.get_name(); + let pos = para.get_pos(); + let ty = args.node.get_arg_type(i); + let ty = self.parse_ty_with_scope(&ty, pos); + params.push(Parameter { + name, + ty: ty.clone(), + has_default: args.node.defaults.get(i).is_some(), + }); + } + } + if should_add_schema_ref { + let schema_runtime_ty = kclvm::schema_runtime_type(name, &self.ctx.pkgpath); + let idx1 = self.ctx.ty_ctx.dep_graph.add_node(schema_runtime_ty); + for parent_ty in &parent_types { + let parent_schema_runtime_ty = + kclvm::schema_runtime_type(&parent_ty.name, &parent_ty.pkgpath); + let idx2 = self.ctx.ty_ctx.dep_graph.add_node(parent_schema_runtime_ty); + self.ctx.ty_ctx.dep_graph.add_edge(idx1, idx2, ()); + } + } + let decorators = self.resolve_decorators( + &rule_stmt.decorators, + DecoratorTarget::Schema, + &rule_stmt.name.node, + ); + SchemaType { + name: rule_stmt.name.node.clone(), + pkgpath: self.ctx.pkgpath.clone(), + filename: self.ctx.filename.clone(), + doc: rule_stmt.doc.clone(), + is_instance: false, + is_mixin: false, + is_protocol: false, + is_rule: true, + base: None, + protocol: protocol_ty, + mixins: parent_types, + attrs: IndexMap::default(), + func: Box::new(FunctionType { + doc: rule_stmt.doc.clone(), + params, + self_ty: None, + return_ty: Rc::new(Type::ANY), + is_variadic: false, + kw_only_index: None, + }), + index_signature: None, + decorators, + } + } +} diff --git a/kclvm/sema/src/resolver/import.rs b/kclvm/sema/src/resolver/import.rs new file mode 100644 index 000000000..2f7792d39 --- /dev/null +++ b/kclvm/sema/src/resolver/import.rs @@ -0,0 +1,213 @@ +use crate::plugin::PLUGIN_MODULE_PREFIX; +use crate::resolver::Resolver; +use crate::ty::ModuleKind; +use crate::{ + builtin::system_module::STANDARD_SYSTEM_MODULES, + ty::{Type, TypeKind}, +}; +use indexmap::IndexMap; +use kclvm_ast::ast; +use kclvm_error::*; +use std::{cell::RefCell, path::Path, rc::Rc}; + +use super::scope::{Scope, ScopeKind, ScopeObject, ScopeObjectKind}; +use crate::resolver::pos::GetPos; + +impl<'ctx> Resolver<'ctx> { + /// Check import error + pub fn resolve_import(&mut self) { + let main_files = self.program.get_main_files(); + for modules in self.program.pkgs.values() { + for m in modules { + for stmt in &m.body { + if let ast::Stmt::Import(import_stmt) = &stmt.node { + let pkgpath = &import_stmt.path; + // System module. + if STANDARD_SYSTEM_MODULES.contains(&pkgpath.as_str()) { + continue; + } + if pkgpath.starts_with(PLUGIN_MODULE_PREFIX) { + continue; + } + let real_path = + Path::new(&self.program.root).join(pkgpath.replace('.', "/")); + if !self.program.pkgs.contains_key(pkgpath) { + self.handler.add_error( + ErrorKind::CannotFindModule, + &[Message { + pos: Position { + filename: self.ctx.filename.clone(), + line: stmt.line, + column: Some(1), + }, + style: Style::Line, + message: format!( + "Cannot find the module {} from {}", + import_stmt.rawpath, + real_path.to_str().unwrap() + ), + note: None, + }], + ); + } else { + let file = real_path.to_str().unwrap().to_string(); + if real_path.is_file() && main_files.contains(&file) { + self.handler.add_error( + ErrorKind::CompileError, + &[Message { + pos: Position { + filename: self.ctx.filename.clone(), + line: stmt.line, + column: Some(1), + }, + style: Style::Line, + message: format!( + "Cannot import {} in the main package", + file + ), + note: None, + }], + ); + } + } + } + } + } + } + } + + /// The import check function. + pub(crate) fn check_import(&mut self, pkgpath: &str) { + self.ctx.pkgpath = pkgpath.to_string(); + let filename = self.ctx.filename.clone(); + self.change_package_context(pkgpath, &filename); + self.init_import_list(); + } + + /// Init import list and store the module scope object into the scope map. + fn init_import_list(&mut self) { + let modules = self.program.pkgs.get(&self.ctx.pkgpath); + match modules { + Some(modules) => { + for module in modules { + self.ctx.filename = module.filename.clone(); + self.ctx.pkgpath = module.pkg.clone(); + for stmt in &module.body { + if let ast::Stmt::Import(import_stmt) = &stmt.node { + { + match self.ctx.import_names.get_mut(&self.ctx.filename) { + Some(mapping) => { + mapping.insert( + import_stmt.name.to_string(), + import_stmt.path.to_string(), + ); + } + None => { + let mut mapping = IndexMap::default(); + mapping.insert( + import_stmt.name.to_string(), + import_stmt.path.to_string(), + ); + self.ctx + .import_names + .insert(self.ctx.filename.clone(), mapping); + } + } + let mut scope = self.scope.borrow_mut(); + let is_user_module = match scope.elems.get(&import_stmt.path) { + Some(scope_obj) => { + let mut obj = scope_obj.borrow_mut(); + match &obj.ty.kind { + TypeKind::Module(module_ty) => { + let mut module_ty = module_ty.clone(); + module_ty + .imported + .push(self.ctx.filename.to_string()); + obj.ty = Rc::new(Type::module( + &module_ty.pkgpath, + &module_ty.imported, + module_ty.kind.clone(), + )); + matches!(module_ty.kind, ModuleKind::User) + } + _ => bug!( + "invalid module type in the import check function {}", + scope_obj.borrow().ty.ty_str() + ), + } + } + None => { + let kind = + if import_stmt.path.starts_with(PLUGIN_MODULE_PREFIX) { + ModuleKind::Plugin + } else if STANDARD_SYSTEM_MODULES + .contains(&import_stmt.path.as_str()) + { + ModuleKind::System + } else { + ModuleKind::User + }; + let ty = Type::module( + &import_stmt.path, + &[self.ctx.filename.clone()], + kind.clone(), + ); + let (start, end) = stmt.get_span_pos(); + scope.elems.insert( + import_stmt.path.to_string(), + Rc::new(RefCell::new(ScopeObject { + name: import_stmt.path.to_string(), + start, + end, + ty: Rc::new(ty), + kind: ScopeObjectKind::Module, + })), + ); + matches!(kind, ModuleKind::User) + } + }; + if !is_user_module { + continue; + } + } + let current_pkgpath = self.ctx.pkgpath.clone(); + let current_filename = self.ctx.filename.clone(); + let idx1 = self.ctx.ty_ctx.dep_graph.add_node(self.ctx.pkgpath.clone()); + let idx2 = self.ctx.ty_ctx.dep_graph.add_node(import_stmt.path.clone()); + self.ctx.ty_ctx.dep_graph.add_edge(idx1, idx2, ()); + // Switch pkgpath context + if !self.scope_map.contains_key(&import_stmt.path) { + self.check(&import_stmt.path); + } + // Restore the current context + self.change_package_context(¤t_pkgpath, ¤t_filename); + } + } + } + } + None => bug!("pkgpath {} not found in the program", self.ctx.pkgpath), + } + } + + pub(crate) fn change_package_context(&mut self, pkgpath: &str, filename: &str) { + if pkgpath.is_empty() { + return; + } + if !self.scope_map.contains_key(pkgpath) { + let scope = Rc::new(RefCell::new(Scope { + parent: Some(Rc::downgrade(&self.builtin_scope)), + children: vec![], + elems: IndexMap::default(), + start: Position::dummy_pos(), + end: Position::dummy_pos(), + kind: ScopeKind::Package, + })); + self.scope_map + .insert(pkgpath.to_string(), Rc::clone(&scope)); + self.scope = scope; + } + self.ctx.pkgpath = pkgpath.to_string(); + self.ctx.filename = filename.to_string(); + self.scope = self.scope_map.get(pkgpath).unwrap().clone(); + } +} diff --git a/kclvm/sema/src/resolver/loop.rs b/kclvm/sema/src/resolver/loop.rs new file mode 100644 index 000000000..b4a17b5cc --- /dev/null +++ b/kclvm/sema/src/resolver/loop.rs @@ -0,0 +1,117 @@ +use std::rc::Rc; + +use crate::resolver::Resolver; +use crate::ty::{sup, Type, TypeKind}; +use kclvm_ast::ast; +use kclvm_error::Position; + +use crate::resolver::pos::GetPos; + +impl<'ctx> Resolver<'ctx> { + /// Do loop type check including quant and comp for expression. + pub(crate) fn do_loop_type_check( + &mut self, + target_node: &'ctx ast::NodeRef, + first_var_name: Option, + second_var_name: Option, + iter_ty: Rc, + iter_pos: Position, + ) { + let types = match &iter_ty.kind { + TypeKind::Union(types) => types.clone(), + _ => vec![iter_ty.clone()], + }; + let mut first_var_ty = self.void_ty(); + let mut second_var_ty = self.void_ty(); + for iter_ty in &types { + if !(iter_ty.is_iterable() || iter_ty.is_any()) { + self.handler.add_compile_error( + &format!("'{}' object is not iterable", iter_ty.ty_str()), + iter_pos.clone(), + ); + } + match &iter_ty.kind { + TypeKind::List(item_ty) => { + if second_var_name.is_some() { + first_var_ty = sup(&[self.int_ty(), first_var_ty.clone()]); + second_var_ty = sup(&[item_ty.clone(), second_var_ty.clone()]); + self.set_type_to_scope( + first_var_name.as_ref().unwrap(), + first_var_ty.clone(), + target_node.get_pos(), + ); + self.set_type_to_scope( + second_var_name.as_ref().unwrap(), + second_var_ty.clone(), + target_node.get_pos(), + ); + } else { + first_var_ty = sup(&[item_ty.clone(), first_var_ty.clone()]); + self.set_type_to_scope( + first_var_name.as_ref().unwrap(), + first_var_ty.clone(), + target_node.get_pos(), + ); + } + } + TypeKind::Dict(key_ty, val_ty) => { + first_var_ty = sup(&[key_ty.clone(), first_var_ty.clone()]); + self.set_type_to_scope( + first_var_name.as_ref().unwrap(), + first_var_ty.clone(), + target_node.get_pos(), + ); + if second_var_name.is_some() { + second_var_ty = sup(&[val_ty.clone(), second_var_ty.clone()]); + self.set_type_to_scope( + second_var_name.as_ref().unwrap(), + second_var_ty.clone(), + target_node.get_pos(), + ); + } + } + TypeKind::Schema(schema_ty) => { + let (key_ty, val_ty) = (schema_ty.key_ty(), schema_ty.val_ty()); + first_var_ty = sup(&[key_ty, first_var_ty.clone()]); + self.set_type_to_scope( + first_var_name.as_ref().unwrap(), + first_var_ty.clone(), + target_node.get_pos(), + ); + if second_var_name.is_some() { + second_var_ty = sup(&[val_ty, second_var_ty.clone()]); + self.set_type_to_scope( + second_var_name.as_ref().unwrap(), + second_var_ty.clone(), + target_node.get_pos(), + ); + } + } + TypeKind::Str | TypeKind::StrLit(_) => { + if second_var_name.is_some() { + first_var_ty = sup(&[self.int_ty(), first_var_ty.clone()]); + second_var_ty = sup(&[self.str_ty(), second_var_ty.clone()]); + self.set_type_to_scope( + first_var_name.as_ref().unwrap(), + first_var_ty.clone(), + target_node.get_pos(), + ); + self.set_type_to_scope( + second_var_name.as_ref().unwrap(), + second_var_ty.clone(), + target_node.get_pos(), + ); + } else { + first_var_ty = sup(&[self.str_ty(), first_var_ty.clone()]); + self.set_type_to_scope( + first_var_name.as_ref().unwrap(), + first_var_ty.clone(), + target_node.get_pos(), + ); + } + } + _ => {} + } + } + } +} diff --git a/kclvm/sema/src/resolver/mod.rs b/kclvm/sema/src/resolver/mod.rs new file mode 100644 index 000000000..20200891c --- /dev/null +++ b/kclvm/sema/src/resolver/mod.rs @@ -0,0 +1,138 @@ +mod arg; +mod attr; +mod calculation; +mod config; +mod format; +pub mod global; +mod import; +mod r#loop; +mod node; +mod para; +pub mod pos; +mod schema; +pub mod scope; +mod ty; +mod ty_alias; +mod var; + +#[cfg(test)] +mod tests; + +use indexmap::IndexMap; +use std::{cell::RefCell, rc::Rc}; + +use crate::pre_process::pre_process_program; +use crate::resolver::scope::ScopeObject; +use crate::resolver::ty_alias::process_program_type_alias; +use crate::{resolver::scope::Scope, ty::SchemaType}; +use kclvm_ast::ast::Program; +use kclvm_ast::walker::MutSelfTypedResultWalker; +use kclvm_error::{Handler, Position}; + +use crate::ty::TypeContext; + +use self::scope::{builtin_scope, ProgramScope}; + +/// Resolver is responsible for program semantic checking, mainly +/// including type checking and contract model checking. +pub struct Resolver<'ctx> { + pub program: &'ctx Program, + pub scope_map: IndexMap>>, + pub scope: Rc>, + pub builtin_scope: Rc>, + pub ctx: Context, + pub options: Options, + pub handler: Handler, +} + +impl<'ctx> Resolver<'ctx> { + pub fn new(program: &'ctx Program, options: Options) -> Self { + let builtin_scope = Rc::new(RefCell::new(builtin_scope())); + let scope = Rc::clone(&builtin_scope); + Resolver { + program, + scope_map: IndexMap::default(), + builtin_scope, + scope, + ctx: Context::default(), + options, + handler: Handler::default(), + } + } + + /// The check main function. + pub(crate) fn check(&mut self, pkgpath: &str) -> ProgramScope { + self.check_import(pkgpath); + self.init_global_types(); + match self.program.pkgs.get(pkgpath) { + Some(modules) => { + for module in modules { + self.ctx.filename = module.filename.to_string(); + for stmt in &module.body { + self.walk_stmt(&stmt.node); + } + } + } + None => bug!("pkgpath {} not found in the program", pkgpath), + } + ProgramScope { + scope_map: self.scope_map.clone(), + import_names: self.ctx.import_names.clone(), + } + } +} + +/// Resolve context +#[derive(Debug, Default)] +pub struct Context { + /// What source file are we in. + pub filename: String, + /// What package path are we in. + pub pkgpath: String, + /// What schema are we in. + pub schema: Option>>, + /// What schema are we in. + pub schema_mapping: IndexMap>>, + /// For loop local vars. + pub local_vars: Vec, + /// Import pkgpath and name + pub import_names: IndexMap>, + /// Are we resolving the left value. + pub l_value: bool, + /// Are we resolving the statement start position. + pub start_pos: Position, + /// Are we resolving the statement end position. + pub end_pos: Position, + /// Is in lambda expression. + pub in_lambda_expr: Vec, + /// Current schema expr type stack + pub config_expr_context: Vec>, + /// Type context. + pub ty_ctx: TypeContext, + /// Type alias mapping + pub type_alias_mapping: IndexMap>, +} + +/// Resolve options +#[derive(Clone, Debug, Default)] +pub struct Options { + pub raise_err: bool, + pub config_auto_fix: bool, +} + +/// Resolve program +pub fn resolve_program(program: &mut Program) -> ProgramScope { + pre_process_program(program); + let mut resolver = Resolver::new( + program, + Options { + raise_err: true, + config_auto_fix: false, + }, + ); + let scope = resolver.check(kclvm_ast::MAIN_PKG); + resolver.handler.abort_if_any_errors(); + let type_alias_mapping = resolver.ctx.type_alias_mapping.clone(); + process_program_type_alias(program, type_alias_mapping); + scope +} diff --git a/kclvm/sema/src/resolver/node.rs b/kclvm/sema/src/resolver/node.rs new file mode 100644 index 000000000..e8539b6b8 --- /dev/null +++ b/kclvm/sema/src/resolver/node.rs @@ -0,0 +1,1139 @@ +use std::rc::Rc; + +use crate::resolver::pos::GetPos; +use indexmap::IndexMap; +use kclvm_ast::ast; +use kclvm_ast::walker::MutSelfTypedResultWalker; +use kclvm_error::*; + +use crate::ty::{ + sup, DecoratorTarget, Parameter, Type, TypeInferMethods, TypeKind, RESERVED_TYPE_IDENTIFIERS, +}; + +use super::format::VALID_FORMAT_SPEC_SET; +use super::scope::{ScopeKind, ScopeObject, ScopeObjectKind}; +use super::ty::ty_str_replace_pkgpath; +use super::Resolver; + +pub type TypeRef = Rc; +/// ResolvedResult denotes the result, when the result is error, +/// put the message string into the diagnostic vector. +pub type ResolvedResult = TypeRef; + +impl<'ctx> MutSelfTypedResultWalker<'ctx> for Resolver<'ctx> { + type Result = ResolvedResult; + + fn walk_module(&mut self, module: &'ctx ast::Module) -> Self::Result { + self.stmts(&module.body) + } + + fn walk_expr_stmt(&mut self, expr_stmt: &'ctx ast::ExprStmt) -> Self::Result { + let expr_types = self.exprs(&expr_stmt.exprs); + if !expr_types.is_empty() { + let ty = expr_types[expr_types.len() - 1].clone(); + if expr_types.len() > 1 { + self.handler.add_compile_error( + "expression statement can only have one expression", + expr_stmt.exprs[1].get_pos(), + ); + } + ty + } else { + bug!("invalid expr statement exprs"); + } + } + + fn walk_unification_stmt( + &mut self, + unification_stmt: &'ctx ast::UnificationStmt, + ) -> Self::Result { + let names = &unification_stmt.target.node.names; + if names.len() > 1 { + self.handler.add_compile_error( + "unification identifier can not be selected", + unification_stmt.target.get_pos(), + ); + } + self.ctx.l_value = true; + let expected_ty = self.walk_identifier_expr(&unification_stmt.target); + self.ctx.l_value = false; + let (start, end) = unification_stmt.value.get_span_pos(); + let obj = self.new_config_expr_context_item(&names[0], expected_ty.clone(), start, end); + let init_stack_depth = self.switch_config_expr_context(Some(obj)); + let ty = self.walk_schema_expr(&unification_stmt.value.node); + self.clear_config_expr_context(init_stack_depth as usize, false); + self.must_assignable_to( + ty.clone(), + expected_ty.clone(), + unification_stmt.target.get_pos(), + None, + ); + if !ty.is_any() && expected_ty.is_any() { + self.set_type_to_scope(&names[0], ty, unification_stmt.target.get_pos()); + } + expected_ty + } + + fn walk_type_alias_stmt(&mut self, type_alias_stmt: &'ctx ast::TypeAliasStmt) -> Self::Result { + let (start, end) = type_alias_stmt.type_name.get_span_pos(); + let mut ty = self + .parse_ty_str_with_scope(&type_alias_stmt.type_value.node, start.clone()) + .as_ref() + .clone(); + if let TypeKind::Schema(schema_ty) = &mut ty.kind { + schema_ty.is_instance = false; + } + ty.is_type_alias = true; + let ty = Rc::new(ty); + let ty_str = ty.into_type_annotation_str(); + let name = type_alias_stmt.type_name.node.get_name(); + let mut mapping = IndexMap::default(); + mapping.insert(ty_str.clone(), "".to_string()); + self.ctx.import_names.insert(name.to_string(), mapping); + self.add_type_alias(&name, &ty_str); + if RESERVED_TYPE_IDENTIFIERS.contains(&name.as_str()) { + self.handler.add_type_error( + &format!( + "type alias '{}' cannot be the same as the built-in types ({:?})", + name, RESERVED_TYPE_IDENTIFIERS + ), + start.clone(), + ); + } + self.insert_object( + &name, + ScopeObject { + name: name.clone(), + start, + end, + ty: ty.clone(), + kind: ScopeObjectKind::TypeAlias, + }, + ); + ty + } + + fn walk_assign_stmt(&mut self, assign_stmt: &'ctx ast::AssignStmt) -> Self::Result { + self.ctx.local_vars.clear(); + let mut value_ty = self.any_ty(); + let start = assign_stmt.targets[0].get_pos(); + let end = assign_stmt.value.get_pos(); + for target in &assign_stmt.targets { + let name = &target.node.names[0]; + if target.node.names.len() == 1 { + self.ctx.l_value = true; + let expected_ty = self.walk_identifier_expr(target); + self.ctx.l_value = false; + if let TypeKind::Schema(ty) = &expected_ty.kind { + let obj = self.new_config_expr_context_item( + &ty.name, + expected_ty.clone(), + start.clone(), + end.clone(), + ); + let init_stack_depth = self.switch_config_expr_context(Some(obj)); + value_ty = self.expr(&assign_stmt.value); + self.clear_config_expr_context(init_stack_depth as usize, false) + } else { + value_ty = self.expr(&assign_stmt.value); + self.must_assignable_to( + value_ty.clone(), + expected_ty.clone(), + target.get_pos(), + None, + ) + } + if !value_ty.is_any() + && expected_ty.is_any() + && assign_stmt.type_annotation.is_none() + { + self.set_type_to_scope(name, value_ty.clone(), target.get_pos()); + if let Some(schema_ty) = &self.ctx.schema { + let mut schema_ty = schema_ty.borrow_mut(); + schema_ty.set_type_of_attr( + name, + self.ctx.ty_ctx.infer_to_variable_type(value_ty.clone()), + ); + } + } + } else { + self.lookup_type_from_scope(name, target.get_pos()); + self.ctx.l_value = true; + let expected_ty = self.walk_identifier_expr(target); + self.ctx.l_value = false; + value_ty = self.expr(&assign_stmt.value); + self.must_assignable_to(value_ty.clone(), expected_ty, target.get_pos(), None) + } + } + value_ty + } + + fn walk_aug_assign_stmt(&mut self, aug_assign_stmt: &'ctx ast::AugAssignStmt) -> Self::Result { + self.ctx.l_value = false; + let left_ty = self.walk_identifier_expr(&aug_assign_stmt.target); + let right_ty = self.expr(&aug_assign_stmt.value); + let op = match aug_assign_stmt.op.clone().try_into() { + Ok(op) => op, + Err(msg) => bug!("{}", msg), + }; + let new_target_ty = self.binary(left_ty, right_ty, &op, aug_assign_stmt.target.get_pos()); + self.ctx.l_value = true; + let expected_ty = self.walk_identifier_expr(&aug_assign_stmt.target); + self.must_assignable_to( + new_target_ty.clone(), + expected_ty, + aug_assign_stmt.target.get_pos(), + None, + ); + self.ctx.l_value = false; + new_target_ty + } + + fn walk_assert_stmt(&mut self, assert_stmt: &'ctx ast::AssertStmt) -> Self::Result { + self.expr(&assert_stmt.test); + self.expr_or_any_type(&assert_stmt.if_cond); + if let Some(msg) = &assert_stmt.msg { + self.must_be_type(msg, self.str_ty()); + } + self.any_ty() + } + + fn walk_if_stmt(&mut self, if_stmt: &'ctx ast::IfStmt) -> Self::Result { + self.expr(&if_stmt.cond); + self.stmts(&if_stmt.body); + self.stmts(&if_stmt.orelse); + self.any_ty() + } + + fn walk_import_stmt(&mut self, _import_stmt: &'ctx ast::ImportStmt) -> Self::Result { + // Nothing to do. + self.any_ty() + } + + fn walk_schema_stmt(&mut self, schema_stmt: &'ctx ast::SchemaStmt) -> Self::Result { + self.resolve_schema_stmt(schema_stmt) + } + + fn walk_rule_stmt(&mut self, rule_stmt: &'ctx ast::RuleStmt) -> Self::Result { + self.resolve_rule_stmt(rule_stmt) + } + + fn walk_quant_expr(&mut self, quant_expr: &'ctx ast::QuantExpr) -> Self::Result { + let iter_ty = self.expr(&quant_expr.target); + if iter_ty.is_any() { + iter_ty + } else { + let (start, end) = (self.ctx.start_pos.clone(), self.ctx.end_pos.clone()); + self.enter_scope(start, end, ScopeKind::Loop); + let (mut key_name, mut val_name) = (None, None); + let mut target_node = None; + for (i, target) in quant_expr.variables.iter().enumerate() { + target_node = Some(target); + let name = &target.node.names[0]; + if i == 0 { + key_name = Some(name.to_string()); + } + if i == 1 { + val_name = Some(name.to_string()) + } + self.ctx.local_vars.push(name.to_string()); + let (start, end) = target.get_span_pos(); + self.insert_object( + name, + ScopeObject { + name: name.to_string(), + start, + end, + ty: self.any_ty(), + kind: ScopeObjectKind::Variable, + }, + ); + } + self.do_loop_type_check( + target_node.unwrap(), + key_name, + val_name, + iter_ty, + quant_expr.target.get_pos(), + ); + self.expr_or_any_type(&quant_expr.if_cond); + let item_ty = self.expr(&quant_expr.test); + self.leave_scope(); + match &quant_expr.op { + ast::QuantOperation::All | ast::QuantOperation::Any => self.bool_ty(), + ast::QuantOperation::Filter => item_ty, + ast::QuantOperation::Map => Rc::new(Type::list(item_ty)), + } + } + } + + fn walk_schema_attr(&mut self, schema_attr: &'ctx ast::SchemaAttr) -> Self::Result { + self.ctx.local_vars.clear(); + let (start, end) = schema_attr.name.get_span_pos(); + let name = if schema_attr.name.node.contains('.') { + self.handler + .add_compile_error("schema attribute can not be selected", start.clone()); + schema_attr.name.node.split('.').collect::>()[0] + } else { + &schema_attr.name.node + }; + let schema = self.ctx.schema.as_ref().unwrap(); + let expected_ty = schema + .borrow() + .get_type_of_attr(name) + .map_or(self.any_ty(), |ty| ty); + // Schema attribute decorators + self.resolve_decorators(&schema_attr.decorators, DecoratorTarget::Attribute, name); + self.insert_object( + name, + ScopeObject { + name: name.to_string(), + start, + end, + ty: expected_ty.clone(), + kind: ScopeObjectKind::Variable, + }, + ); + if let Some(value) = &schema_attr.value { + let value_ty = if let TypeKind::Schema(ty) = &expected_ty.kind { + let (start, end) = value.get_span_pos(); + let obj = + self.new_config_expr_context_item(&ty.name, expected_ty.clone(), start, end); + let init_stack_depth = self.switch_config_expr_context(Some(obj)); + let value_ty = self.expr(value); + self.clear_config_expr_context(init_stack_depth as usize, false); + value_ty + } else { + self.expr(value) + }; + let pos = schema_attr.name.get_pos(); + match &schema_attr.op { + Some(bin_or_aug) => match bin_or_aug { + // Union + ast::BinOrAugOp::Aug(ast::AugOp::BitOr) => { + let op = ast::BinOp::BitOr; + let value_ty = self.binary(value_ty, expected_ty.clone(), &op, pos.clone()); + self.must_assignable_to(value_ty, expected_ty, pos, None); + } + // Assign + _ => self.must_assignable_to(value_ty, expected_ty, pos, None), + }, + None => bug!("invalid ast schema attr op kind"), + } + } + self.any_ty() + } + + /// if else -> sup([body, orelse]) + fn walk_if_expr(&mut self, if_expr: &'ctx ast::IfExpr) -> Self::Result { + self.expr(&if_expr.cond); + let body_ty = self.expr(&if_expr.body); + let orelse_ty = self.expr(&if_expr.orelse); + sup(&[body_ty, orelse_ty]) + } + + fn walk_unary_expr(&mut self, unary_expr: &'ctx ast::UnaryExpr) -> Self::Result { + let operand_ty = self.expr(&unary_expr.operand); + self.unary(operand_ty, &unary_expr.op, unary_expr.operand.get_pos()) + } + + fn walk_binary_expr(&mut self, binary_expr: &'ctx ast::BinaryExpr) -> Self::Result { + let left_ty = self.expr(&binary_expr.left); + let mut right_ty = self.expr(&binary_expr.right); + let pos = binary_expr.left.get_pos(); + match &binary_expr.op { + ast::BinOrCmpOp::Bin(bin_op) => match bin_op { + ast::BinOp::As => { + if let ast::Expr::Identifier(identifier) = &binary_expr.right.node { + right_ty = self.parse_ty_str_with_scope( + &identifier.get_name(), + binary_expr.right.get_pos(), + ); + if right_ty.is_schema() { + let mut schema_ty = right_ty.into_schema_type(); + schema_ty.is_instance = true; + right_ty = Rc::new(Type::schema(schema_ty)); + } + let ty_annotation_str = right_ty.into_type_annotation_str(); + self.add_type_alias( + &identifier.get_name(), + &ty_str_replace_pkgpath(&ty_annotation_str, &self.ctx.pkgpath), + ); + } else { + self.handler + .add_compile_error("keyword 'as' right operand must be a type", pos); + return left_ty; + } + self.binary(left_ty, right_ty, bin_op, pos) + } + _ => self.binary(left_ty, right_ty, bin_op, pos), + }, + ast::BinOrCmpOp::Cmp(cmp_op) => self.compare(left_ty, right_ty, cmp_op, pos), + } + } + + fn walk_selector_expr(&mut self, selector_expr: &'ctx ast::SelectorExpr) -> Self::Result { + let mut value_ty = self.expr(&selector_expr.value); + let pos = selector_expr.attr.get_pos(); + for name in &selector_expr.attr.node.names { + value_ty = self.load_attr(value_ty.clone(), name, pos.clone()); + } + value_ty + } + + fn walk_call_expr(&mut self, call_expr: &'ctx ast::CallExpr) -> Self::Result { + let call_ty = self.expr(&call_expr.func); + let pos = call_expr.func.get_pos(); + if call_ty.is_any() { + self.do_arguments_type_check(&call_expr.args, &call_expr.keywords, &[]); + self.any_ty() + } else if let TypeKind::Function(func_ty) = &call_ty.kind { + self.do_arguments_type_check(&call_expr.args, &call_expr.keywords, &func_ty.params); + func_ty.return_ty.clone() + } else if let TypeKind::Schema(schema_ty) = &call_ty.kind { + if schema_ty.is_instance { + self.handler.add_compile_error( + &format!("schema '{}' instance is not callable", call_ty.ty_str()), + pos, + ); + self.any_ty() + } else { + self.do_arguments_type_check( + &call_expr.args, + &call_expr.keywords, + &schema_ty.func.params, + ); + let mut return_ty = schema_ty.clone(); + return_ty.is_instance = true; + Rc::new(Type::schema(return_ty)) + } + } else { + self.handler.add_compile_error( + &format!("'{}' object is not callable", call_ty.ty_str()), + pos, + ); + self.any_ty() + } + } + + fn walk_subscript(&mut self, subscript: &'ctx ast::Subscript) -> Self::Result { + let value_ty = self.expr(&subscript.value); + let pos = subscript.value.get_pos(); + if value_ty.is_any() { + value_ty + } else { + match &value_ty.kind { + TypeKind::Str | TypeKind::StrLit(_) | TypeKind::List(_) => { + if let Some(index) = &subscript.index { + self.must_be_type(index, self.any_ty()); + if value_ty.is_list() { + value_ty.list_item_ty() + } else { + self.str_ty() + } + } else { + for expr in [&subscript.lower, &subscript.upper, &subscript.step] + .iter() + .copied() + .flatten() + { + self.must_be_type(expr, self.int_ty()); + } + if value_ty.is_list() { + value_ty + } else { + self.str_ty() + } + } + } + TypeKind::Dict(_, val_ty) => { + if let Some(index) = &subscript.index { + let index_key_ty = self.expr(index); + if index_key_ty.is_none_or_any() { + val_ty.clone() + } else if !index_key_ty.is_key() { + self.handler.add_compile_error( + &format!( + "invalid dict/schema key type: '{}'", + index_key_ty.ty_str() + ), + pos, + ); + self.any_ty() + } else if let ast::Expr::StringLit(string_lit) = &subscript.value.node { + self.load_attr(value_ty, &string_lit.value, pos) + } else { + val_ty.clone() + } + } else { + self.handler + .add_compile_error("unhashable type: 'slice'", pos); + self.any_ty() + } + } + TypeKind::Schema(schema_ty) => { + if let Some(index) = &subscript.index { + let index_key_ty = self.expr(index); + if index_key_ty.is_none_or_any() { + schema_ty.val_ty() + } else if !index_key_ty.is_key() { + self.handler.add_compile_error( + &format!( + "invalid dict/schema key type: '{}'", + index_key_ty.ty_str() + ), + pos, + ); + self.any_ty() + } else if let ast::Expr::StringLit(string_lit) = &subscript.value.node { + self.load_attr(value_ty, &string_lit.value, pos) + } else { + schema_ty.val_ty() + } + } else { + self.handler + .add_compile_error("unhashable type: 'slice'", pos); + self.any_ty() + } + } + _ => { + self.handler.add_compile_error( + &format!("'{}' object is not subscriptable", value_ty.ty_str()), + subscript.value.get_pos(), + ); + self.any_ty() + } + } + } + } + + fn walk_paren_expr(&mut self, paren_expr: &'ctx ast::ParenExpr) -> Self::Result { + self.expr(&paren_expr.expr) + } + + fn walk_list_expr(&mut self, list_expr: &'ctx ast::ListExpr) -> Self::Result { + let item_type = sup(&self.exprs(&list_expr.elts).to_vec()); + Type::list_ref(item_type) + } + + fn walk_list_comp(&mut self, list_comp: &'ctx ast::ListComp) -> Self::Result { + let start = list_comp.elt.get_pos(); + let end = match list_comp.generators.last() { + Some(last) => last.get_end_pos(), + None => list_comp.elt.get_end_pos(), + }; + self.enter_scope(start.clone(), end, ScopeKind::Loop); + for comp_clause in &list_comp.generators { + self.walk_comp_clause(&comp_clause.node); + } + if let ast::Expr::Starred(_) = list_comp.elt.node { + self.handler + .add_compile_error("list unpacking cannot be used in list comprehension", start); + } + let item_ty = self.expr(&list_comp.elt); + self.leave_scope(); + Type::list_ref(item_ty) + } + + fn walk_dict_comp(&mut self, dict_comp: &'ctx ast::DictComp) -> Self::Result { + let key = dict_comp.entry.key.as_ref().unwrap(); + let start = key.get_pos(); + let end = match dict_comp.generators.last() { + Some(last) => last.get_end_pos(), + None => dict_comp.entry.value.get_end_pos(), + }; + self.enter_scope(start, end, ScopeKind::Loop); + for comp_clause in &dict_comp.generators { + self.walk_comp_clause(&comp_clause.node); + } + let key_ty = self.expr(key); + let val_ty = self.expr(&dict_comp.entry.value); + self.leave_scope(); + Type::dict_ref(key_ty, val_ty) + } + + fn walk_list_if_item_expr( + &mut self, + list_if_item_expr: &'ctx ast::ListIfItemExpr, + ) -> Self::Result { + self.expr(&list_if_item_expr.if_cond); + let or_else_ty = self.expr_or_any_type(&list_if_item_expr.orelse); + let exprs_ty = sup(&self.exprs(&list_if_item_expr.exprs).to_vec()); + sup(&[or_else_ty, exprs_ty]) + } + + fn walk_starred_expr(&mut self, starred_expr: &'ctx ast::StarredExpr) -> Self::Result { + let value_ty = self.expr(&starred_expr.value); + fn starred_ty_walk_fn(ty: &TypeRef) -> (TypeRef, bool) { + match &ty.kind { + TypeKind::None | TypeKind::Any => (ty.clone(), true), + TypeKind::List(item_ty) => (item_ty.clone(), true), + TypeKind::Dict(key_ty, _) => (key_ty.clone(), true), + TypeKind::Schema(schema_ty) => (schema_ty.key_ty(), true), + TypeKind::Union(types) => { + let results = types + .iter() + .map(starred_ty_walk_fn) + .collect::>(); + ( + sup(&results + .iter() + .map(|(ty, _)| ty) + .cloned() + .collect::>()), + results.iter().all(|(_, r)| *r), + ) + } + _ => (Rc::new(Type::ANY), false), + } + } + let (ty, result) = starred_ty_walk_fn(&value_ty); + if !result { + self.handler.add_compile_error( + &format!( + "only list, dict, schema object can be used * unpacked, got {}", + ty.ty_str() + ), + starred_expr.value.get_pos(), + ); + } + ty + } + + fn walk_config_if_entry_expr( + &mut self, + config_if_entry_expr: &'ctx ast::ConfigIfEntryExpr, + ) -> Self::Result { + self.expr(&config_if_entry_expr.if_cond); + let mut key_types: Vec = vec![]; + let mut val_types: Vec = vec![]; + for entry in &config_if_entry_expr.items { + let key = &entry.node.key; + let value = &entry.node.value; + let mut stack_depth = 0; + self.check_config_entry(key, value); + stack_depth += self.switch_config_expr_context_by_key(key) as usize; + let mut entry_key_ty = self.any_ty(); + let mut entry_val_ty = self.expr(value).clone(); + match key { + Some(key) => { + if let ast::Expr::Identifier(identifier) = &key.node { + entry_key_ty = self.str_ty(); + for _ in 0..identifier.names.len() - 1 { + entry_val_ty = Type::dict_ref(self.str_ty(), entry_val_ty.clone()) + } + } + } + None => match &entry_val_ty.kind { + TypeKind::None | TypeKind::Any => {} + TypeKind::Dict(key_ty, val_ty) => { + entry_key_ty = key_ty.clone(); + entry_val_ty = val_ty.clone(); + } + TypeKind::Schema(schema_ty) => { + entry_key_ty = schema_ty.key_ty(); + entry_val_ty = schema_ty.val_ty(); + } + TypeKind::Union(types) + if self + .ctx + .ty_ctx + .is_config_type_or_config_union_type(entry_val_ty.clone()) => + { + entry_key_ty = sup(&types + .iter() + .map(|ty| ty.config_key_ty()) + .collect::>()); + entry_val_ty = sup(&types + .iter() + .map(|ty| ty.config_val_ty()) + .collect::>()); + } + _ => { + self.handler.add_compile_error( + &format!( + "only dict and schema can be used ** unpack, got '{}'", + entry_val_ty.ty_str() + ), + value.get_pos(), + ); + } + }, + } + key_types.push(entry_key_ty); + val_types.push(entry_val_ty); + self.clear_config_expr_context(stack_depth, false); + } + let dict_ty = Type::dict_ref(sup(&key_types), sup(&val_types)); + let or_else_ty = self.expr_or_any_type(&config_if_entry_expr.orelse); + sup(&[dict_ty, or_else_ty]) + } + + fn walk_comp_clause(&mut self, comp_clause: &'ctx ast::CompClause) -> Self::Result { + let iter_ty = self.expr(&comp_clause.iter); + let (mut key_name, mut val_name) = (None, None); + let mut target_node = None; + for (i, target) in comp_clause.targets.iter().enumerate() { + target_node = Some(target); + let name = &target.node.names[0]; + if i == 0 { + key_name = Some(name.to_string()); + } + if i == 1 { + val_name = Some(name.to_string()) + } + self.ctx.local_vars.push(name.to_string()); + let (start, end) = target.get_span_pos(); + self.insert_object( + name, + ScopeObject { + name: name.to_string(), + start, + end, + ty: self.any_ty(), + kind: ScopeObjectKind::Variable, + }, + ); + } + if iter_ty.is_any() { + iter_ty + } else { + self.do_loop_type_check( + target_node.unwrap(), + key_name, + val_name, + iter_ty, + comp_clause.iter.get_pos(), + ); + self.exprs(&comp_clause.ifs); + self.any_ty() + } + } + + fn walk_schema_expr(&mut self, schema_expr: &'ctx ast::SchemaExpr) -> Self::Result { + let def_ty = self.walk_identifier_expr(&schema_expr.name); + if !matches!(&schema_expr.config.node, ast::Expr::Config(_)) { + self.handler.add_compile_error( + "Invalid schema config expr, expect config entries, e.g., {k1 = v1, k2 = v2}", + schema_expr.config.get_pos(), + ); + } + let mut pos = schema_expr.name.get_pos(); + let ret_ty = match &def_ty.kind { + TypeKind::Dict(_, _) => { + let obj = self.new_config_expr_context_item( + "", + def_ty.clone(), + Position::dummy_pos(), + Position::dummy_pos(), + ); + let init_stack_depth = self.switch_config_expr_context(Some(obj)); + let config_ty = self.expr(&schema_expr.config); + self.clear_config_expr_context(init_stack_depth as usize, false); + self.binary(def_ty.clone(), config_ty, &ast::BinOp::BitOr, pos) + } + TypeKind::Schema(schema_ty) => { + if !schema_ty.is_instance { + let ty_annotation_str = ty_str_replace_pkgpath( + &def_ty.into_type_annotation_str(), + &self.ctx.pkgpath, + ); + let name = schema_expr.name.node.get_name(); + self.add_type_alias(&name, &ty_annotation_str); + } + let obj = self.new_config_expr_context_item( + &schema_ty.name, + def_ty.clone(), + Position::dummy_pos(), + Position::dummy_pos(), + ); + let init_stack_depth = self.switch_config_expr_context(Some(obj)); + self.expr(&schema_expr.config); + self.clear_config_expr_context(init_stack_depth as usize, false); + if schema_ty.is_instance { + if !schema_expr.args.is_empty() || !schema_expr.kwargs.is_empty() { + self.handler.add_compile_error( + "Arguments cannot be used in the schema modification expression", + pos, + ); + } + } else { + self.do_arguments_type_check( + &schema_expr.args, + &schema_expr.kwargs, + &schema_ty.func.params, + ); + } + self.any_ty() + } + _ => { + pos.filename = self.ctx.filename.clone(); + self.handler + .add_compile_error(&format!("Invalid schema type '{}'", def_ty.ty_str()), pos); + return self.any_ty(); + } + }; + let mut def_ty_clone = def_ty.as_ref().clone(); + if let TypeKind::Schema(schema_ty) = &mut def_ty_clone.kind { + schema_ty.is_instance = true; + } + if def_ty_clone.is_schema() { + Rc::new(def_ty_clone) + } else { + ret_ty + } + } + + fn walk_config_expr(&mut self, config_expr: &'ctx ast::ConfigExpr) -> Self::Result { + let mut key_types: Vec = vec![]; + let mut val_types: Vec = vec![]; + for item in &config_expr.items { + let key = &item.node.key; + let value = &item.node.value; + let op = &item.node.operation; + let mut stack_depth: usize = 0; + self.check_config_entry(key, value); + stack_depth += self.switch_config_expr_context_by_key(key) as usize; + let mut has_insert_index = false; + let val_ty = match key { + Some(key) => match &key.node { + ast::Expr::Identifier(identifier) => { + let key_ty = if identifier.names.len() == 1 { + let name = &identifier.names[0]; + let key_ty = if self.ctx.local_vars.contains(name) { + self.expr(key) + } else { + self.str_ty() + }; + self.check_attr_ty(&key_ty, key.get_pos()); + key_ty + } else { + self.str_ty() + }; + let mut val_ty = self.expr(value); + for _ in 0..identifier.names.len() - 1 { + val_ty = Type::dict_ref(self.str_ty(), val_ty.clone()); + } + key_types.push(key_ty); + val_types.push(val_ty.clone()); + val_ty + } + ast::Expr::Subscript(subscript) + if matches!(subscript.value.node, ast::Expr::Identifier(_)) => + { + has_insert_index = true; + let val_ty = self.expr(value); + key_types.push(self.str_ty()); + val_types.push(Type::list_ref(val_ty.clone())); + val_ty + } + _ => { + let key_ty = self.expr(key); + let val_ty = self.expr(value); + self.check_attr_ty(&key_ty, key.get_pos()); + key_types.push(key_ty); + val_types.push(val_ty.clone()); + val_ty + } + }, + None => { + let val_ty = self.expr(value); + match &val_ty.kind { + TypeKind::None | TypeKind::Any => { + val_types.push(val_ty.clone()); + } + TypeKind::Dict(key_ty, val_ty) => { + key_types.push(key_ty.clone()); + val_types.push(val_ty.clone()); + } + TypeKind::Schema(schema_ty) => { + key_types.push(schema_ty.key_ty()); + val_types.push(schema_ty.val_ty()); + } + TypeKind::Union(types) + if self + .ctx + .ty_ctx + .is_config_type_or_config_union_type(val_ty.clone()) => + { + key_types.push(sup(&types + .iter() + .map(|ty| ty.config_key_ty()) + .collect::>())); + val_types.push(sup(&types + .iter() + .map(|ty| ty.config_val_ty()) + .collect::>())); + } + _ => { + self.handler.add_compile_error( + &format!( + "only dict and schema can be used ** unpack, got '{}'", + val_ty.ty_str() + ), + value.get_pos(), + ); + } + } + val_ty + } + }; + if matches!(op, ast::ConfigEntryOperation::Insert) + && !has_insert_index + && !val_ty.is_any() + && !val_ty.is_list() + { + self.handler.add_error( + ErrorKind::IllegalAttributeError, + &[Message { + pos: value.get_pos(), + style: Style::LineAndColumn, + message: format!( + "only list type can in inserted, got '{}'", + val_ty.ty_str() + ), + note: None, + }], + ); + } + self.clear_config_expr_context(stack_depth, false); + } + self.any_ty() + } + + fn walk_check_expr(&mut self, check_expr: &'ctx ast::CheckExpr) -> Self::Result { + if let Some(msg) = &check_expr.msg { + self.must_be_type(msg, self.str_ty()); + } + // Check type in if_cond expression + self.expr_or_any_type(&check_expr.if_cond); + self.expr(&check_expr.test) + } + + fn walk_lambda_expr(&mut self, lambda_expr: &'ctx ast::LambdaExpr) -> Self::Result { + let mut ret_ty = self.any_ty(); + let mut params = vec![]; + self.do_parameters_check(&lambda_expr.args); + if let Some(args) = &lambda_expr.args { + for (i, arg) in args.node.args.iter().enumerate() { + let name = arg.node.get_name(); + let arg_ty = args.node.get_arg_type(i); + let ty = self.parse_ty_with_scope(&arg_ty, arg.get_pos()); + params.push(Parameter { + name, + ty: ty.clone(), + has_default: false, + }); + let value = &args.node.defaults[i]; + self.expr_or_any_type(value); + } + } + let (start, end) = (self.ctx.start_pos.clone(), self.ctx.end_pos.clone()); + if let Some(ret_annotation_ty) = &lambda_expr.return_ty { + ret_ty = self.parse_ty_with_scope(&ret_annotation_ty.node, start.clone()); + } + self.enter_scope(start.clone(), end.clone(), ScopeKind::Lambda); + self.ctx.in_lambda_expr.push(true); + // Lambda parameters + for param in ¶ms { + self.insert_object( + ¶m.name, + ScopeObject { + name: param.name.clone(), + start: start.clone(), + end: end.clone(), + ty: param.ty.clone(), + kind: ScopeObjectKind::Parameter, + }, + ) + } + if let Some(stmt) = lambda_expr.body.last() { + if !matches!( + stmt.node, + ast::Stmt::Expr(_) | ast::Stmt::Assign(_) | ast::Stmt::AugAssign(_) + ) { + self.handler.add_compile_error( + "The last statement of the lambda body must be a expression e.g., x, 1, etc.", + stmt.get_pos(), + ); + } + } + let real_ret_ty = self.stmts(&lambda_expr.body); + self.leave_scope(); + self.ctx.in_lambda_expr.pop(); + self.must_assignable_to(real_ret_ty.clone(), ret_ty.clone(), end, None); + if !real_ret_ty.is_any() && ret_ty.is_any() && lambda_expr.return_type_str.is_none() { + ret_ty = real_ret_ty; + } + Rc::new(Type::function(None, ret_ty, ¶ms, "", false, None)) + } + + fn walk_keyword(&mut self, keyword: &'ctx ast::Keyword) -> Self::Result { + self.walk_identifier_expr(&keyword.arg); + self.expr_or_any_type(&keyword.value) + } + + fn walk_arguments(&mut self, arguments: &'ctx ast::Arguments) -> Self::Result { + for (i, arg) in arguments.args.iter().enumerate() { + let ty = arguments.get_arg_type(i); + self.parse_ty_with_scope(&ty, arg.get_pos()); + let value = &arguments.defaults[i]; + self.expr_or_any_type(value); + } + self.any_ty() + } + + fn walk_compare(&mut self, compare: &'ctx ast::Compare) -> Self::Result { + let t1 = self.expr(&compare.left); + let t2 = self.expr(&compare.comparators[0]); + self.compare( + t1.clone(), + t2.clone(), + &compare.ops[0], + compare.comparators[0].get_pos(), + ); + for i in 1..compare.comparators.len() - 1 { + let op = &compare.ops[i + 1]; + self.compare(t1.clone(), t2.clone(), op, compare.comparators[i].get_pos()); + } + self.bool_ty() + } + + fn walk_identifier(&mut self, identifier: &'ctx ast::Identifier) -> Self::Result { + self.resolve_var( + &identifier.names, + &identifier.pkgpath, + self.ctx.start_pos.clone(), + ) + } + + fn walk_number_lit(&mut self, number_lit: &'ctx ast::NumberLit) -> Self::Result { + match &number_lit.binary_suffix { + Some(binary_suffix) => { + let raw_value = match number_lit.value { + ast::NumberLitValue::Int(int_val) => int_val, + ast::NumberLitValue::Float(float_val) => { + self.handler.add_compile_error( + "float literal can not be followed the unit suffix", + self.ctx.start_pos.clone(), + ); + float_val as i64 + } + }; + let binary_suffix_str: String = binary_suffix.value(); + let value = kclvm::units::cal_num(raw_value, &binary_suffix_str); + Rc::new(Type::number_multiplier( + value, + raw_value, + &binary_suffix_str, + )) + } + None => match number_lit.value { + ast::NumberLitValue::Int(int_val) => Rc::new(Type::int_lit(int_val)), + ast::NumberLitValue::Float(float_val) => Rc::new(Type::float_lit(float_val)), + }, + } + } + + fn walk_string_lit(&mut self, string_lit: &'ctx ast::StringLit) -> Self::Result { + Rc::new(Type::str_lit(&string_lit.value)) + } + + fn walk_name_constant_lit( + &mut self, + name_constant_lit: &'ctx ast::NameConstantLit, + ) -> Self::Result { + match &name_constant_lit.value { + ast::NameConstant::True => Rc::new(Type::bool_lit(true)), + ast::NameConstant::False => Rc::new(Type::bool_lit(false)), + ast::NameConstant::None | ast::NameConstant::Undefined => self.none_ty(), + } + } + + fn walk_joined_string(&mut self, joined_string: &'ctx ast::JoinedString) -> Self::Result { + self.ctx.l_value = false; + self.exprs(&joined_string.values); + self.str_ty() + } + + fn walk_formatted_value(&mut self, formatted_value: &'ctx ast::FormattedValue) -> Self::Result { + if let Some(spec) = &formatted_value.format_spec { + let spec_lower = spec.to_lowercase(); + if !VALID_FORMAT_SPEC_SET.contains(&spec_lower.as_str()) { + self.handler.add_compile_error( + &format!("{} is a invalid format spec", spec), + formatted_value.value.get_pos(), + ); + } + } + self.expr(&formatted_value.value) + } + + fn walk_comment(&mut self, _comment: &'ctx ast::Comment) -> Self::Result { + // Nothing to do. + self.any_ty() + } +} + +impl<'ctx> Resolver<'ctx> { + #[inline] + pub fn stmts(&mut self, stmts: &'ctx [ast::NodeRef]) -> ResolvedResult { + let stmt_types: Vec = stmts + .iter() + .map(|stmt| self.walk_stmt(&stmt.node)) + .collect(); + match stmt_types.last() { + Some(ty) => ty.clone(), + _ => self.any_ty(), + } + } + + #[inline] + pub fn exprs(&mut self, exprs: &'ctx [ast::NodeRef]) -> Vec { + exprs + .iter() + .map(|expr| self.walk_expr(&expr.node)) + .collect() + } + + #[inline] + pub fn expr(&mut self, expr: &'ctx ast::NodeRef) -> ResolvedResult { + if let ast::Expr::Identifier(_) = &expr.node { + let (start, end) = expr.get_span_pos(); + self.ctx.start_pos = start; + self.ctx.end_pos = end; + } + self.walk_expr(&expr.node) + } + + #[inline] + pub fn stmt(&mut self, stmt: &'ctx ast::NodeRef) -> ResolvedResult { + let (start, end) = stmt.get_span_pos(); + self.ctx.start_pos = start; + self.ctx.end_pos = end; + self.walk_stmt(&stmt.node) + } + + #[inline] + pub fn expr_or_any_type( + &mut self, + expr: &'ctx Option>, + ) -> ResolvedResult { + match expr { + Some(expr) => self.walk_expr(&expr.node), + None => self.any_ty(), + } + } + + #[inline] + pub fn walk_identifier_expr( + &mut self, + identifier: &'ctx ast::NodeRef, + ) -> ResolvedResult { + self.resolve_var( + &identifier.node.names, + &identifier.node.pkgpath, + identifier.get_pos(), + ) + } +} diff --git a/kclvm/sema/src/resolver/para.rs b/kclvm/sema/src/resolver/para.rs new file mode 100644 index 000000000..0e79b472b --- /dev/null +++ b/kclvm/sema/src/resolver/para.rs @@ -0,0 +1,34 @@ +use crate::resolver::pos::GetPos; +use crate::resolver::Resolver; +use kclvm_ast::ast; +use kclvm_error::*; + +impl<'ctx> Resolver<'ctx> { + /// Do parameter type check. + pub fn do_parameters_check(&mut self, args: &'ctx Option>) { + if let Some(args) = args { + let mut mark = false; + let len = args.node.defaults.len(); + for i in 0..len { + let j = len - i - 1; + match &args.node.defaults[j] { + Some(default) => { + if mark { + self.handler.add_error( + ErrorKind::IllegalParameterError, + &[Message { + pos: default.get_pos(), + style: Style::LineAndColumn, + message: "non-default argument follows default argument" + .to_string(), + note: Some("A default argument".to_string()), + }], + ); + } + } + None => mark = true, + } + } + } + } +} diff --git a/kclvm/sema/src/resolver/pos.rs b/kclvm/sema/src/resolver/pos.rs new file mode 100644 index 000000000..8ab6723fa --- /dev/null +++ b/kclvm/sema/src/resolver/pos.rs @@ -0,0 +1,81 @@ +use kclvm_ast::ast; +use kclvm_error::Position; + +pub trait ContainsPos { + /// Check if current scope or node contains a position. + fn contains_pos(&self, pos: &Position) -> bool; +} + +pub trait GetPos { + /// Get start and end position from node. + fn get_span_pos(&self) -> (Position, Position) { + (self.get_pos(), self.get_end_pos()) + } + /// Get start pos from node. + fn get_pos(&self) -> Position; + /// Get end pos from node. + fn get_end_pos(&self) -> Position; +} + +impl ContainsPos for ast::Node { + fn contains_pos(&self, pos: &Position) -> bool { + let (start_pos, end_pos) = self.get_span_pos(); + start_pos.less_equal(pos) && pos.less_equal(&end_pos) + } +} + +impl GetPos for ast::Node { + fn get_pos(&self) -> Position { + Position { + filename: self.filename.clone(), + line: self.line, + column: Some(self.column), + } + } + + fn get_end_pos(&self) -> Position { + Position { + filename: self.filename.clone(), + line: self.end_line, + column: Some(self.end_column), + } + } +} + +impl GetPos for ast::SchemaStmt { + fn get_pos(&self) -> Position { + match self.decorators.first() { + Some(decorator) => decorator.get_pos(), + None => self.name.get_pos(), + } + } + + fn get_end_pos(&self) -> Position { + match self.checks.last() { + Some(check_expr) => check_expr.get_end_pos(), + None => match self.body.last() { + Some(body_stmt) => body_stmt.get_end_pos(), + None => match self.mixins.last() { + Some(mixin) => mixin.get_end_pos(), + None => self.name.get_end_pos(), + }, + }, + } + } +} + +impl GetPos for ast::RuleStmt { + fn get_pos(&self) -> Position { + match self.decorators.first() { + Some(decorator) => decorator.get_pos(), + None => self.name.get_pos(), + } + } + + fn get_end_pos(&self) -> Position { + match self.checks.last() { + Some(check_expr) => check_expr.get_end_pos(), + None => self.name.get_end_pos(), + } + } +} diff --git a/kclvm/sema/src/resolver/schema.rs b/kclvm/sema/src/resolver/schema.rs new file mode 100644 index 000000000..1d55fbb83 --- /dev/null +++ b/kclvm/sema/src/resolver/schema.rs @@ -0,0 +1,173 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use crate::builtin::BUILTIN_DECORATORS; +use crate::resolver::pos::GetPos; +use crate::resolver::Resolver; +use crate::ty::{Decorator, DecoratorTarget, TypeKind}; +use kclvm_ast::ast; +use kclvm_ast::walker::MutSelfTypedResultWalker; +use kclvm_error::Position; + +use super::node::ResolvedResult; +use super::scope::{ScopeKind, ScopeObject, ScopeObjectKind}; + +impl<'ctx> Resolver<'ctx> { + pub(crate) fn resolve_schema_stmt( + &mut self, + schema_stmt: &'ctx ast::SchemaStmt, + ) -> ResolvedResult { + let ty = self.lookup_type_from_scope(&schema_stmt.name.node, schema_stmt.name.get_pos()); + let scope_ty = ty.into_schema_type(); + self.ctx.schema = Some(Rc::new(RefCell::new(scope_ty.clone()))); + let (start, end) = schema_stmt.get_span_pos(); + self.do_parameters_check(&schema_stmt.args); + self.enter_scope( + start.clone(), + end.clone(), + ScopeKind::Schema(schema_stmt.name.node.to_string()), + ); + for param in &scope_ty.func.params { + self.insert_object( + ¶m.name, + ScopeObject { + name: param.name.clone(), + start: start.clone(), + end: end.clone(), + ty: param.ty.clone(), + kind: ScopeObjectKind::Parameter, + }, + ) + } + // Schema index signature + if let (Some(index_signature), Some(index_signature_node)) = + (scope_ty.index_signature, &schema_stmt.index_signature) + { + if let Some(key_name) = index_signature.key_name { + let (start, end) = index_signature_node.get_span_pos(); + self.insert_object( + &key_name, + ScopeObject { + name: key_name.clone(), + start, + end, + ty: index_signature.key_ty.clone(), + kind: ScopeObjectKind::Variable, + }, + ) + } + } + let schema_attr_names = schema_stmt.get_left_identifier_list(); + for (line, column, name) in schema_attr_names { + if !self.contains_object(&name) { + self.insert_object( + &name, + ScopeObject { + name: name.clone(), + start: Position { + filename: self.ctx.filename.clone(), + line, + column: Some(column), + }, + end: Position::dummy_pos(), + ty: self.any_ty(), + kind: ScopeObjectKind::Variable, + }, + ); + } + } + // Schema body. + self.stmts(&schema_stmt.body); + // Schema check blocks. + for check_expr in &schema_stmt.checks { + self.walk_check_expr(&check_expr.node); + } + self.leave_scope(); + self.ctx.schema = None; + ty + } + + pub(crate) fn resolve_rule_stmt(&mut self, rule_stmt: &'ctx ast::RuleStmt) -> ResolvedResult { + let ty = self.lookup_type_from_scope(&rule_stmt.name.node, rule_stmt.name.get_pos()); + let scope_ty = ty.into_schema_type(); + self.ctx.schema = Some(Rc::new(RefCell::new(scope_ty.clone()))); + let (start, end) = rule_stmt.get_span_pos(); + self.do_parameters_check(&rule_stmt.args); + self.enter_scope( + start.clone(), + end.clone(), + ScopeKind::Schema(rule_stmt.name.node.to_string()), + ); + for param in &scope_ty.func.params { + self.insert_object( + ¶m.name, + ScopeObject { + name: param.name.clone(), + start: start.clone(), + end: end.clone(), + ty: param.ty.clone(), + kind: ScopeObjectKind::Parameter, + }, + ) + } + // Rule check blocks. + for check_expr in &rule_stmt.checks { + self.walk_check_expr(&check_expr.node); + } + self.leave_scope(); + self.ctx.schema = None; + ty + } + + pub(crate) fn resolve_decorators( + &mut self, + decorators: &'ctx [ast::NodeRef], + target: DecoratorTarget, + key: &str, + ) -> Vec { + let mut decorator_objs = vec![]; + for decorator in decorators { + let name = if let ast::Expr::Identifier(identifier) = &decorator.node.func.node { + if identifier.names.len() == 1 { + Some(identifier.names[0].clone()) + } else { + None + } + } else { + None + }; + match name { + Some(name) => match BUILTIN_DECORATORS.get(&name) { + Some(ty) => match &ty.kind { + TypeKind::Function(func_ty) => { + self.do_arguments_type_check( + &decorator.node.args, + &decorator.node.keywords, + &func_ty.params, + ); + decorator_objs.push(Decorator { + target: target.clone(), + name, + key: key.to_string(), + }) + } + _ => bug!("invalid builtin decorator function type"), + }, + None => { + self.handler.add_compile_error( + &format!("UnKnown decorator {}", name), + decorator.get_pos(), + ); + } + }, + None => { + self.handler.add_type_error( + "decorator name must be a single identifier", + decorator.get_pos(), + ); + } + } + } + decorator_objs + } +} diff --git a/kclvm/sema/src/resolver/scope.rs b/kclvm/sema/src/resolver/scope.rs new file mode 100644 index 000000000..4c835c0cd --- /dev/null +++ b/kclvm/sema/src/resolver/scope.rs @@ -0,0 +1,320 @@ +use indexmap::IndexMap; +use kclvm_ast::{ast, MAIN_PKG}; +use std::{ + cell::RefCell, + rc::{Rc, Weak}, +}; + +use crate::resolver::pos::ContainsPos; +use crate::resolver::Resolver; +use crate::ty::Type; +use crate::{builtin::BUILTIN_FUNCTIONS, ty::TypeInferMethods}; +use kclvm_error::Position; + +/// The object stored in the scope. +#[derive(PartialEq, Clone, Debug)] +pub struct ScopeObject { + /// The scope object name. + pub name: String, + /// The scope object start position. + pub start: Position, + /// The scope object end position. + pub end: Position, + /// The type of the scope object. + pub ty: Rc, + /// The scope object kind. + pub kind: ScopeObjectKind, +} + +impl ScopeObject { + /// Positions of the scope object are valid. + #[inline] + pub fn pos_is_valid(&self) -> bool { + self.start.is_valid() && self.end.is_valid() + } +} + +#[derive(PartialEq, Clone, Debug)] +pub enum ScopeObjectKind { + Variable, + Attribute, + Definition, + Parameter, + TypeAlias, + Module, +} + +/// A Scope maintains a set of objects and links to its containing +/// (parent) and contained (children) scopes. Objects may be inserted +/// and looked up by name. The zero value for Scope is a ready-to-use +/// empty scope. +#[derive(Clone, Debug)] +pub struct Scope { + /// The parent scope. + pub parent: Option>>, + /// The child scope list. + pub children: Vec>>, + /// The scope object mapping with its name. + pub elems: IndexMap>>, + /// The scope start position. + pub start: Position, + /// The scope end position. + pub end: Position, + /// The scope kind. + pub kind: ScopeKind, +} + +impl Scope { + /// Lookup the scope object recursively with the name. + pub fn lookup(&self, name: &str) -> Option>> { + match self.elems.get(name) { + Some(obj) => Some(obj.clone()), + None => match &self.parent { + Some(parent) => match parent.upgrade() { + Some(parent) => { + let parent = parent.borrow(); + parent.lookup(name) + } + None => None, + }, + None => None, + }, + } + } + + /// Set a type by name to existed object, return true if found. + pub fn set_ty(&mut self, name: &str, ty: Rc) -> bool { + match self.elems.get_mut(name) { + Some(obj) => { + let mut obj = obj.borrow_mut(); + obj.ty = ty; + true + } + None => false, + } + } +} + +impl ContainsPos for Scope { + /// Check if current scope contains a position + fn contains_pos(&self, pos: &Position) -> bool { + self.start.less_equal(pos) && pos.less_equal(&self.end) + } +} + +#[derive(Clone, Debug)] +pub enum ScopeKind { + /// Package scope. + Package, + /// Builtin scope. + Builtin, + /// Schema name string. + Schema(String), + /// Loop scope. + Loop, + /// Condition statement. + CondStmt, + /// Lambda expression. + Lambda, +} + +impl Scope { + /// Check if current scope contains a position on the AST statement. + pub fn contains_pos_on_stmt(&self, pos: &Position, stmt: &ast::Node) -> bool { + match &stmt.node { + ast::Stmt::Schema(schema) => { + schema.body.iter().any(|n| n.contains_pos(pos)) + || schema.checks.iter().any(|n| n.contains_pos(pos)) + || schema + .index_signature + .as_ref() + .map(|n| n.contains_pos(pos)) + .is_some() + } + ast::Stmt::Rule(rule) => rule.checks.iter().any(|n| n.contains_pos(pos)), + _ => self.contains_pos(pos), + } + } + /// Returns the inner most scope on the position. + pub fn inner_most(&self, pos: &Position) -> Option { + // Builtin scope + if self.parent.is_none() { + for child in self.children.iter() { + let child = child.borrow(); + if child.contains_pos(pos) { + return child.inner_most(pos); + } + } + return None; + } + // self is not BUILTIN_SCOPE + if self.contains_pos(pos) { + for child in self.children.iter() { + let child = child.borrow(); + if child.contains_pos(pos) { + return child.inner_most(pos); + } + } + return Some(self.clone()); + } + None + } + + /// Get the enclosing scope + #[inline] + pub fn get_enclosing_scope(&self) -> Option>> { + self.parent.as_ref().map(|scope| scope.upgrade().unwrap()) + } + + /// Search child scope by the scope name. + pub fn search_child_scope_by_name(&self, name: &str) -> Option>> { + match self.elems.get(name) { + Some(_) => { + for child in self.children.iter() { + let child_ref = child.borrow(); + if let ScopeKind::Schema(schema_name) = &child_ref.kind { + if name == schema_name { + return Some(Rc::clone(child)); + } + } + } + None + } + None => None, + } + } +} + +/// Program scope is scope contains a multiple scopes related to the +/// package path. +#[derive(Clone, Debug)] +pub struct ProgramScope { + pub scope_map: IndexMap>>, + pub import_names: IndexMap>, +} + +impl ProgramScope { + #[inline] + pub fn pkgpaths(&self) -> Vec { + self.scope_map.keys().cloned().collect::>() + } + + #[inline] + pub fn main_scope(&self) -> Option<&Rc>> { + self.scope_map.get(MAIN_PKG) + } +} + +/// Construct a builtin scope +pub(crate) fn builtin_scope() -> Scope { + let mut elems = IndexMap::default(); + for (name, builtin_func) in BUILTIN_FUNCTIONS.iter() { + elems.insert( + name.to_string(), + Rc::new(RefCell::new(ScopeObject { + name: name.to_string(), + start: Position::dummy_pos(), + end: Position::dummy_pos(), + ty: Rc::new(builtin_func.clone()), + kind: ScopeObjectKind::Definition, + })), + ); + } + Scope { + parent: None, + children: vec![], + elems, + start: Position::dummy_pos(), + end: Position::dummy_pos(), + kind: ScopeKind::Builtin, + } +} + +impl<'ctx> Resolver<'ctx> { + /// Enter scope such as schema statement, for loop expressions. + pub fn enter_scope(&mut self, start: Position, end: Position, kind: ScopeKind) { + let scope = Scope { + parent: Some(Rc::downgrade(&self.scope)), + children: vec![], + elems: IndexMap::default(), + start, + end, + kind, + }; + let scope = Rc::new(RefCell::new(scope)); + { + // Borrow self.scope + let mut scope_ref = self.scope.borrow_mut(); + let children = &mut scope_ref.children; + children.push(Rc::clone(&scope)); + // Deref self.scope + } + self.scope = Rc::clone(&scope); + } + + /// Leave scope. + pub fn leave_scope(&mut self) { + self.ctx.local_vars.clear(); + let parent = match &self.scope.borrow().parent { + Some(parent) => parent.upgrade().unwrap(), + None => bug!("the scope parent is empty, can't leave the scope"), + }; + self.scope = Rc::clone(&parent); + } + + /// Find scope object type by name. + #[inline] + pub fn find_type_in_scope(&mut self, name: &str) -> Option> { + self.scope + .borrow() + .lookup(name) + .map(|obj| obj.borrow().ty.clone()) + } + + /// Lookup type from the scope by name, if not found, emit a compile error and + /// return the any type. + pub fn lookup_type_from_scope(&mut self, name: &str, pos: Position) -> Rc { + match self.find_type_in_scope(name) { + Some(ty) => ty, + None => { + self.handler.add_compile_error( + &format!("name '{}' is not defined", name.replace('@', "")), + pos, + ); + self.any_ty() + } + } + } + + /// Set type to the scope exited object, if not found, emit a compile error. + pub fn set_type_to_scope(&mut self, name: &str, ty: Rc, pos: Position) { + let mut scope = self.scope.borrow_mut(); + match scope.elems.get_mut(name) { + Some(obj) => { + let mut obj = obj.borrow_mut(); + obj.ty = self.ctx.ty_ctx.infer_to_variable_type(ty); + } + None => { + self.handler.add_compile_error( + &format!("name '{}' is not defined", name.replace('@', "")), + pos, + ); + } + } + } + + /// Insert object into the current scope. + #[inline] + pub fn insert_object(&mut self, name: &str, obj: ScopeObject) { + let mut scope = self.scope.borrow_mut(); + scope + .elems + .insert(name.to_string(), Rc::new(RefCell::new(obj))); + } + + /// Contains object into the current scope. + #[inline] + pub fn contains_object(&mut self, name: &str) -> bool { + self.scope.borrow().elems.contains_key(name) + } +} diff --git a/kclvm/sema/src/resolver/test_data/assign.k b/kclvm/sema/src/resolver/test_data/assign.k new file mode 100644 index 000000000..223ca50d9 --- /dev/null +++ b/kclvm/sema/src/resolver/test_data/assign.k @@ -0,0 +1,2 @@ +a = 1 +b = 2 diff --git a/kclvm/sema/src/resolver/tests.rs b/kclvm/sema/src/resolver/tests.rs new file mode 100644 index 000000000..074a9beb9 --- /dev/null +++ b/kclvm/sema/src/resolver/tests.rs @@ -0,0 +1,36 @@ +use crate::builtin::BUILTIN_FUNCTION_NAMES; +use crate::resolver::resolve_program; +use crate::resolver::scope::*; +use crate::ty::Type; +use kclvm_parser::parse_program; +use std::rc::Rc; + +#[test] +fn test_scope() { + let mut scope = builtin_scope(); + for name in BUILTIN_FUNCTION_NAMES { + let obj = scope.lookup(name).unwrap(); + let obj_ref = obj.borrow_mut(); + assert!(obj_ref.ty.is_func()); + } + for name in BUILTIN_FUNCTION_NAMES { + scope.set_ty(name, Rc::new(Type::ANY)); + } + for name in BUILTIN_FUNCTION_NAMES { + let obj = scope.lookup(name).unwrap(); + let obj_ref = obj.borrow_mut(); + assert!(obj_ref.ty.is_any()); + } +} + +#[test] +fn test_resolve_program() { + let mut program = parse_program("./src/resolver/test_data/assign.k"); + let scope = resolve_program(&mut program); + assert_eq!(scope.pkgpaths(), vec!["__main__".to_string()]); + let main_scope = scope.main_scope().unwrap(); + let main_scope = main_scope.borrow_mut(); + assert!(main_scope.lookup("a").is_some()); + assert!(main_scope.lookup("b").is_some()); + assert!(main_scope.lookup("print").is_none()); +} diff --git a/kclvm/sema/src/resolver/ty.rs b/kclvm/sema/src/resolver/ty.rs new file mode 100644 index 000000000..1a249dc00 --- /dev/null +++ b/kclvm/sema/src/resolver/ty.rs @@ -0,0 +1,236 @@ +use std::rc::Rc; + +use crate::resolver::pos::GetPos; +use crate::resolver::Resolver; +use crate::ty::parser::parse_type_str; +use crate::ty::{assignable_to, SchemaType, Type, TypeKind}; +use indexmap::IndexMap; +use kclvm_ast::ast; +use kclvm_error::*; + +use super::node::ResolvedResult; + +fn ty_str_to_pkgpath(ty_str: &str) -> &str { + let splits: Vec<&str> = ty_str.rsplitn(2, '.').collect(); + let len = splits.len(); + splits[len - 1] +} + +pub fn ty_str_replace_pkgpath(ty_str: &str, pkgpath: &str) -> String { + let pkgpath = format!("@{}", pkgpath); + if ty_str.contains('.') && ty_str_to_pkgpath(ty_str) == pkgpath { + ty_str.replacen(&format!("{}.", pkgpath), "", 1) + } else { + ty_str.to_string() + } +} + +impl<'ctx> Resolver<'ctx> { + #[inline] + pub fn any_ty(&self) -> Rc { + self.ctx.ty_ctx.builtin_types.any.clone() + } + #[inline] + pub fn int_ty(&self) -> Rc { + self.ctx.ty_ctx.builtin_types.int.clone() + } + #[inline] + pub fn float_ty(&self) -> Rc { + self.ctx.ty_ctx.builtin_types.float.clone() + } + #[inline] + pub fn bool_ty(&self) -> Rc { + self.ctx.ty_ctx.builtin_types.bool.clone() + } + #[inline] + pub fn str_ty(&self) -> Rc { + self.ctx.ty_ctx.builtin_types.str.clone() + } + #[inline] + pub fn none_ty(&self) -> Rc { + self.ctx.ty_ctx.builtin_types.none.clone() + } + #[inline] + pub fn void_ty(&self) -> Rc { + self.ctx.ty_ctx.builtin_types.void.clone() + } + /// Parse the type string with the scope, if parse_ty returns a Named type(schema type or type alias), + /// found it from the scope. + pub fn parse_ty_with_scope(&mut self, ty: &ast::Type, pos: Position) -> ResolvedResult { + let ty: Rc = Rc::new(ty.clone().into()); + // If a named type, find it from scope to get the specific type + let ret_ty = self.upgrade_named_ty_with_scope(ty.clone(), &pos); + self.add_type_alias( + &ty.into_type_annotation_str(), + &ret_ty.into_type_annotation_str(), + ); + ret_ty + } + + pub fn parse_ty_str_with_scope(&mut self, ty_str: &str, pos: Position) -> ResolvedResult { + let ty: Rc = parse_type_str(ty_str); + // If a named type, find it from scope to get the specific type + let ret_ty = self.upgrade_named_ty_with_scope(ty, &pos); + self.add_type_alias(ty_str, &ret_ty.into_type_annotation_str()); + ret_ty + } + + /// The given expression must be the expected type. + #[inline] + pub fn must_be_type(&mut self, expr: &'ctx ast::NodeRef, expected_ty: Rc) { + let ty = self.expr(expr); + self.must_assignable_to(ty, expected_ty, expr.get_pos(), None); + } + + /// Must assignable to the expected type. + #[inline] + pub fn must_assignable_to( + &mut self, + ty: Rc, + expected_ty: Rc, + pos: Position, + expected_pos: Option, + ) { + if !self.check_type(ty.clone(), expected_ty.clone(), &pos) { + let mut msgs = vec![Message { + pos, + style: Style::LineAndColumn, + message: format!("expect {}, got {}", expected_ty.ty_str(), ty.ty_str(),), + note: None, + }]; + + if let Some(expected_pos) = expected_pos { + msgs.push(Message { + pos: expected_pos, + style: Style::LineAndColumn, + message: format!( + "variable is defined here, its type is {}, but got {}", + expected_ty.ty_str(), + ty.ty_str(), + ), + note: None, + }); + } + self.handler.add_error(ErrorKind::TypeError, &msgs); + } + } + + /// The check type main function, returns a boolean result. + #[inline] + pub fn check_type(&mut self, ty: Rc, expected_ty: Rc, pos: &Position) -> bool { + match (&ty.kind, &expected_ty.kind) { + (TypeKind::List(item_ty), TypeKind::List(expected_item_ty)) => { + self.check_type(item_ty.clone(), expected_item_ty.clone(), pos) + } + (TypeKind::Dict(key_ty, val_ty), TypeKind::Dict(expected_key_ty, expected_val_ty)) => { + self.check_type(key_ty.clone(), expected_key_ty.clone(), pos) + && self.check_type(val_ty.clone(), expected_val_ty.clone(), pos) + } + (TypeKind::Dict(key_ty, val_ty), TypeKind::Schema(schema_ty)) => { + self.dict_assignable_to_schema(key_ty.clone(), val_ty.clone(), schema_ty, pos) + } + (TypeKind::Union(types), _) => types + .iter() + .all(|ty| self.check_type(ty.clone(), expected_ty.clone(), pos)), + (_, TypeKind::Union(types)) => types + .iter() + .any(|expected_ty| self.check_type(ty.clone(), expected_ty.clone(), pos)), + _ => assignable_to(ty, expected_ty), + } + } + + /// Judge a dict can be converted to schema in compile time + /// Do relaxed schema check key and value type check. + pub fn dict_assignable_to_schema( + &mut self, + key_ty: Rc, + val_ty: Rc, + schema_ty: &SchemaType, + pos: &Position, + ) -> bool { + if let Some(index_signature) = &schema_ty.index_signature { + if !assignable_to(val_ty.clone(), index_signature.val_ty.clone()) { + self.handler.add_type_error( + &format!( + "expected schema index signature value type {}, got {}", + index_signature.val_ty.ty_str(), + val_ty.ty_str() + ), + pos.clone(), + ); + } + if index_signature.any_other { + return assignable_to(key_ty, index_signature.key_ty.clone()) + && assignable_to(val_ty, index_signature.val_ty.clone()); + } + true + } else { + true + } + } + + fn upgrade_named_ty_with_scope(&mut self, ty: Rc, pos: &Position) -> ResolvedResult { + match &ty.kind { + TypeKind::List(item_ty) => { + Type::list_ref(self.upgrade_named_ty_with_scope(item_ty.clone(), pos)) + } + TypeKind::Dict(key_ty, val_ty) => Type::dict_ref( + self.upgrade_named_ty_with_scope(key_ty.clone(), pos), + self.upgrade_named_ty_with_scope(val_ty.clone(), pos), + ), + TypeKind::Union(types) => Type::union_ref( + &types + .iter() + .map(|ty| self.upgrade_named_ty_with_scope(ty.clone(), pos)) + .collect::>>(), + ), + TypeKind::Named(ty_str) => { + let ty_str = ty_str_replace_pkgpath(ty_str, &self.ctx.pkgpath); + let names: Vec<&str> = if ty_str.starts_with('@') { + let names: Vec<&str> = ty_str.rsplitn(2, '.').collect(); + names.iter().rev().cloned().collect() + } else { + ty_str.split('.').collect() + }; + let mut pkgpath = "".to_string(); + let name = names[0]; + if names.len() > 1 && !self.ctx.local_vars.contains(&name.to_string()) { + if let Some(mapping) = self.ctx.import_names.get(&self.ctx.filename) { + pkgpath = mapping + .get(name) + .map_or("".to_string(), |pkgpath| pkgpath.to_string()); + } + } + self.ctx.l_value = false; + self.resolve_var( + &names.iter().map(|n| n.to_string()).collect::>(), + &pkgpath, + pos.clone(), + ) + } + _ => ty.clone(), + } + } + + pub fn add_type_alias(&mut self, name: &str, alias: &str) { + if alias.starts_with('@') { + if name == &alias[1..] { + return; + } + } else if name == alias { + return; + } + match self.ctx.type_alias_mapping.get_mut(&self.ctx.pkgpath) { + Some(mapping) => { + mapping.insert(name.to_string(), alias.to_string()); + } + None => { + let mut mapping = IndexMap::default(); + mapping.insert(name.to_string(), alias.to_string()); + self.ctx + .type_alias_mapping + .insert(self.ctx.pkgpath.clone(), mapping); + } + } + } +} diff --git a/kclvm/sema/src/resolver/ty_alias.rs b/kclvm/sema/src/resolver/ty_alias.rs new file mode 100644 index 000000000..5d4f76a2d --- /dev/null +++ b/kclvm/sema/src/resolver/ty_alias.rs @@ -0,0 +1,119 @@ +use indexmap::IndexMap; +use kclvm_ast::walker::MutSelfMutWalker; +use kclvm_ast::{ast, walk_if_mut, walk_list_mut}; + +#[derive(Default)] +struct TypeAliasTransformer { + pub type_alias_mapping: IndexMap, +} + +impl<'ctx> MutSelfMutWalker<'ctx> for TypeAliasTransformer { + fn walk_rule_stmt(&mut self, rule_stmt: &'ctx mut ast::RuleStmt) { + // walk_list_mut!(self, walk_identifier, rule_stmt.parent_rules); + // walk_list_mut!(self, walk_call_expr, rule_stmt.decorators); + walk_if_mut!(self, walk_arguments, rule_stmt.args); + walk_if_mut!(self, walk_identifier, rule_stmt.for_host_name); + walk_list_mut!(self, walk_check_expr, rule_stmt.checks); + } + fn walk_schema_stmt(&mut self, schema_stmt: &'ctx mut ast::SchemaStmt) { + // walk_if_mut!(self, walk_identifier, schema_stmt.parent_name); + // walk_if_mut!(self, walk_identifier, schema_stmt.for_host_name); + walk_if_mut!(self, walk_arguments, schema_stmt.args); + if let Some(schema_index_signature) = schema_stmt.index_signature.as_deref_mut() { + let value = &mut schema_index_signature.node.value; + if let Some(type_alias) = self + .type_alias_mapping + .get(&schema_index_signature.node.key_type.node) + { + schema_index_signature.node.key_type.node = type_alias.clone(); + } + if let Some(type_alias) = self + .type_alias_mapping + .get(&schema_index_signature.node.value_type.node) + { + schema_index_signature.node.value_type.node = type_alias.clone(); + } + walk_if_mut!(self, walk_expr, value); + } + walk_list_mut!(self, walk_identifier, schema_stmt.mixins); + // walk_list_mut!(self, walk_call_expr, schema_stmt.decorators); + walk_list_mut!(self, walk_stmt, schema_stmt.body); + walk_list_mut!(self, walk_check_expr, schema_stmt.checks); + } + fn walk_schema_attr(&mut self, schema_attr: &'ctx mut ast::SchemaAttr) { + // walk_list_mut!(self, walk_call_expr, schema_attr.decorators); + if let Some(type_alias) = self.type_alias_mapping.get(&schema_attr.type_str.node) { + schema_attr.type_str.node = type_alias.clone(); + } + walk_if_mut!(self, walk_expr, schema_attr.value); + } + fn walk_assign_stmt(&mut self, assign_stmt: &'ctx mut ast::AssignStmt) { + if let Some(ty_str) = &mut assign_stmt.type_annotation { + if let Some(type_alias) = self.type_alias_mapping.get(&ty_str.node) { + ty_str.node = type_alias.clone(); + } + } + self.walk_expr(&mut assign_stmt.value.node); + } + fn walk_unification_stmt(&mut self, unification_stmt: &'ctx mut ast::UnificationStmt) { + self.walk_schema_expr(&mut unification_stmt.value.node); + } + fn walk_import_stmt(&mut self, _: &'ctx mut ast::ImportStmt) {} + fn walk_lambda_expr(&mut self, lambda_expr: &'ctx mut ast::LambdaExpr) { + walk_if_mut!(self, walk_arguments, lambda_expr.args); + walk_list_mut!(self, walk_stmt, lambda_expr.body); + if let Some(ty_str) = &mut lambda_expr.return_type_str { + if let Some(type_alias) = self.type_alias_mapping.get(ty_str) { + *ty_str = type_alias.clone(); + } + } + } + fn walk_arguments(&mut self, arguments: &'ctx mut ast::Arguments) { + walk_list_mut!(self, walk_identifier, arguments.args); + for type_annotation in (&mut arguments.type_annotation_list.iter_mut()).flatten() { + if let Some(type_alias) = self.type_alias_mapping.get(&type_annotation.node) { + type_annotation.node = type_alias.clone(); + } + } + for default in arguments.defaults.iter_mut() { + if let Some(d) = default.as_deref_mut() { + self.walk_expr(&mut d.node) + } + } + } + fn walk_identifier(&mut self, identifier: &'ctx mut ast::Identifier) { + if let Some(type_alias) = self.type_alias_mapping.get(&identifier.get_name()) { + if type_alias.starts_with('@') { + let splits: Vec<&str> = type_alias.rsplitn(2, '.').collect(); + identifier.pkgpath = splits[1].to_string(); + identifier.names = vec![splits[1].to_string(), splits[0].to_string()]; + } else { + let names = type_alias.split('.').collect::>(); + identifier.names = names.iter().map(|n| n.to_string()).collect(); + } + } + } +} + +/// Replace type alias. +fn fix_type_alias_identifier<'ctx>( + module: &'ctx mut ast::Module, + type_alias_mapping: IndexMap, +) { + let mut type_alias_transformer = TypeAliasTransformer { type_alias_mapping }; + type_alias_transformer.walk_module(module); +} + +/// Process type alias. +pub fn process_program_type_alias( + program: &mut ast::Program, + type_alias_mapping: IndexMap>, +) { + for (pkgpath, modules) in program.pkgs.iter_mut() { + for module in modules.iter_mut() { + if let Some(type_alias_mapping) = type_alias_mapping.get(pkgpath) { + fix_type_alias_identifier(module, type_alias_mapping.clone()); + } + } + } +} diff --git a/kclvm/sema/src/resolver/var.rs b/kclvm/sema/src/resolver/var.rs new file mode 100644 index 000000000..968c16d86 --- /dev/null +++ b/kclvm/sema/src/resolver/var.rs @@ -0,0 +1,107 @@ +use crate::resolver::Resolver; +use crate::ty::TypeKind; +use kclvm_error::*; + +use super::node::ResolvedResult; +use super::scope::{ScopeObject, ScopeObjectKind}; + +impl<'ctx> Resolver<'ctx> { + /// Resolve variables. + pub fn resolve_var( + &mut self, + names: &[String], + pkgpath: &str, + pos: Position, + ) -> ResolvedResult { + if !pkgpath.is_empty() && self.ctx.l_value { + self.handler.add_compile_error( + "only schema and dict object can be updated attribute", + pos.clone(), + ); + } + if names.len() == 1 { + let name = &names[0]; + let scope_schema_ty = self.ctx.schema.clone(); + if let Some(schema_ty) = &scope_schema_ty { + let mut schema_ty = schema_ty.borrow_mut(); + let ty = schema_ty.get_type_of_attr(name); + // Load from schema if in schema + if !self.ctx.l_value { + let scope_ty = self.find_type_in_scope(name); + if self.ctx.local_vars.contains(name) { + return scope_ty.map_or(self.any_ty(), |ty| ty); + } else if let Some(ref ty) = ty { + if !ty.is_any() { + return ty.clone(); + } + } + scope_ty.map_or(self.any_ty(), |ty| ty) + } + // Store + else { + if !self.contains_object(name) || ty.is_none() { + self.insert_object( + name, + ScopeObject { + name: name.to_string(), + start: pos.clone(), + end: pos.clone(), + ty: self.any_ty(), + kind: ScopeObjectKind::Variable, + }, + ); + if ty.is_none() { + schema_ty.set_type_of_attr(name, self.any_ty()) + } + return self.any_ty(); + } + // FIXME: self.check_config_attr(name, &pos, &schema_ty); + ty.map_or(self.lookup_type_from_scope(name, pos.clone()), |ty| ty) + } + } else { + // Load from schema if in schema + if !self.ctx.l_value { + self.lookup_type_from_scope(name, pos) + } + // Store + else { + if !self.contains_object(name) && self.ctx.schema.is_none() { + self.insert_object( + name, + ScopeObject { + name: name.to_string(), + start: pos.clone(), + end: pos.clone(), + ty: self.any_ty(), + kind: ScopeObjectKind::Variable, + }, + ); + return self.any_ty(); + } + self.lookup_type_from_scope(name, pos) + } + } + } else { + // Load type + let mut ty = self.resolve_var( + &[if !pkgpath.is_empty() { + pkgpath.to_string() + } else { + names[0].clone() + }], + pkgpath, + pos.clone(), + ); + for name in &names[1..] { + // Store and config attr check + if self.ctx.l_value { + if let TypeKind::Schema(schema_ty) = &ty.kind { + self.check_config_attr(name, &pos, schema_ty); + } + } + ty = self.load_attr(ty, name, pos.clone()) + } + ty + } + } +} diff --git a/kclvm/sema/src/ty/constants.rs b/kclvm/sema/src/ty/constants.rs new file mode 100644 index 000000000..01ead742e --- /dev/null +++ b/kclvm/sema/src/ty/constants.rs @@ -0,0 +1,135 @@ +use std::rc::Rc; + +use super::{Type, TypeFlags, TypeKind}; + +use indexmap::IndexMap; +use once_cell::sync::Lazy; + +/* Type string constants */ + +pub const INT_TYPE_STR: &str = "int"; +pub const FLOAT_TYPE_STR: &str = "float"; +pub const STR_TYPE_STR: &str = "str"; +pub const BOOL_TYPE_STR: &str = "bool"; +pub const ANY_TYPE_STR: &str = "any"; +pub const NONE_TYPE_STR: &str = "NoneType"; +pub const UNDEFINED_TYPE_STR: &str = "UndefinedType"; +pub const FUNCTION_TYPE_STR: &str = "function"; +pub const LIST_TYPE_STR: &str = "list"; +pub const DICT_TYPE_STR: &str = "dict"; +pub const SCHEMA_TYPE_STR: &str = "schema"; +pub const NUMBER_MULTIPLIER_TYPE_STR: &str = "number_multiplier"; +pub const NUMBER_MULTIPLIER_PKG_TYPE_STR: &str = "units.NumberMultiplier"; +pub const NUMBER_MULTIPLIER_REGEX: &str = + r"^([1-9][0-9]{0,63})(E|P|T|G|M|K|k|m|u|n|Ei|Pi|Ti|Gi|Mi|Ki)$"; + +pub const ITERABLE_TYPE_STR: &str = "str|{:}|[]"; +pub const NUMBER_TYPE_STR: &str = "int|float|bool"; +pub const NUM_OR_STR_TYPE_STR: &str = "int|float|bool|str"; +pub const RESERVED_TYPE_IDENTIFIERS: [&str; 5] = [ + ANY_TYPE_STR, + INT_TYPE_STR, + FLOAT_TYPE_STR, + STR_TYPE_STR, + BOOL_TYPE_STR, +]; +pub const BUILTIN_TYPES: [&str; 4] = [INT_TYPE_STR, FLOAT_TYPE_STR, STR_TYPE_STR, BOOL_TYPE_STR]; + +pub const MODULE_TYPE_STR: &str = "module"; +pub const NAMED_TYPE_STR: &str = "named"; +pub const VOID_TYPE_STR: &str = "void"; + +pub const NAME_CONSTANT_TRUE: &str = "True"; +pub const NAME_CONSTANT_FALSE: &str = "False"; +pub const NAME_CONSTANT_NONE: &str = "None"; +pub const NAME_CONSTANT_UNDEFINED: &str = "Undefined"; +pub const NAME_CONSTANTS: [&str; 4] = [ + NAME_CONSTANT_NONE, + NAME_CONSTANT_UNDEFINED, + NAME_CONSTANT_TRUE, + NAME_CONSTANT_FALSE, +]; + +pub const TYPES_MAPPING: Lazy> = Lazy::new(|| { + let mut mapping = IndexMap::default(); + mapping.insert(INT_TYPE_STR.to_string(), Type::INT); + mapping.insert(FLOAT_TYPE_STR.to_string(), Type::FLOAT); + mapping.insert(STR_TYPE_STR.to_string(), Type::STR); + mapping.insert(BOOL_TYPE_STR.to_string(), Type::BOOL); + mapping.insert(ANY_TYPE_STR.to_string(), Type::ANY); + mapping.insert("[]".to_string(), Type::list(Rc::new(Type::ANY))); + mapping.insert("[any]".to_string(), Type::list(Rc::new(Type::ANY))); + mapping.insert("[str]".to_string(), Type::list(Rc::new(Type::STR))); + mapping.insert( + "{:}".to_string(), + Type::dict(Rc::new(Type::ANY), Rc::new(Type::ANY)), + ); + mapping.insert( + "{str:}".to_string(), + Type::dict(Rc::new(Type::STR), Rc::new(Type::ANY)), + ); + mapping.insert( + "{str:any}".to_string(), + Type::dict(Rc::new(Type::STR), Rc::new(Type::ANY)), + ); + mapping.insert( + "{str:str}".to_string(), + Type::dict(Rc::new(Type::STR), Rc::new(Type::STR)), + ); + mapping +}); +pub const ZERO_LIT_TYPES: Lazy> = Lazy::new(|| { + vec![ + Type::int_lit(0), + Type::float_lit(0.0), + Type::bool_lit(false), + ] +}); +pub static SCHEMA_MEMBER_FUNCTIONS: Lazy> = Lazy::new(|| vec!["instances"]); + +impl Type { + /* Type constant definitions */ + + /// Type constant `void`. + pub const VOID: Type = Type { + kind: TypeKind::Void, + flags: TypeFlags::VOID, + is_type_alias: false, + }; + /// Type constant `int`. + pub const INT: Type = Type { + kind: TypeKind::Int, + flags: TypeFlags::INT, + is_type_alias: false, + }; + /// Type constant `float`. + pub const FLOAT: Type = Type { + kind: TypeKind::Float, + flags: TypeFlags::FLOAT, + is_type_alias: false, + }; + /// Type constant `str`. + pub const STR: Type = Type { + kind: TypeKind::Str, + flags: TypeFlags::STR, + is_type_alias: false, + }; + /// Type constant `bool`. + pub const BOOL: Type = Type { + kind: TypeKind::Bool, + flags: TypeFlags::BOOL, + is_type_alias: false, + }; + /// Type constant `any`. + pub const ANY: Type = Type { + kind: TypeKind::Any, + flags: TypeFlags::ANY, + is_type_alias: false, + }; + /// Type constant `NoneType` including the name constants `None` and `Undefined`. + pub const NONE: Type = Type { + kind: TypeKind::None, + flags: TypeFlags::NONE, + is_type_alias: false, + }; +} diff --git a/kclvm/sema/src/ty/constructor.rs b/kclvm/sema/src/ty/constructor.rs new file mode 100644 index 000000000..caa773690 --- /dev/null +++ b/kclvm/sema/src/ty/constructor.rs @@ -0,0 +1,321 @@ +use super::*; + +impl Type { + /// Construct a union type + #[inline] + pub fn union(types: &[Rc]) -> Type { + Type { + kind: TypeKind::Union(types.to_owned()), + flags: TypeFlags::UNION, + is_type_alias: false, + } + } + /// Construct a union type ref + #[inline] + pub fn union_ref(types: &[Rc]) -> Rc { + Rc::new(Self::union(types)) + } + /// Construct a list type + #[inline] + pub fn list(item_ty: Rc) -> Type { + Type { + kind: TypeKind::List(item_ty), + flags: TypeFlags::LIST, + is_type_alias: false, + } + } + /// Construct a list type ref + #[inline] + pub fn list_ref(item_ty: Rc) -> Rc { + Rc::new(Self::list(item_ty)) + } + /// Construct a dict type + #[inline] + pub fn dict(key_ty: Rc, val_ty: Rc) -> Type { + Type { + kind: TypeKind::Dict(key_ty, val_ty), + flags: TypeFlags::DICT, + is_type_alias: false, + } + } + /// Construct a dict type ref + #[inline] + pub fn dict_ref(key_ty: Rc, val_ty: Rc) -> Rc { + Rc::new(Self::dict(key_ty, val_ty)) + } + /// Construct a bool literal type. + #[inline] + pub fn bool_lit(val: bool) -> Type { + Type { + kind: TypeKind::BoolLit(val), + flags: TypeFlags::BOOL | TypeFlags::LITERAL, + is_type_alias: false, + } + } + /// Construct a int literal type. + #[inline] + pub fn int_lit(num: i64) -> Type { + Type { + kind: TypeKind::IntLit(num), + flags: TypeFlags::INT | TypeFlags::LITERAL, + is_type_alias: false, + } + } + /// Construct a float literal type. + #[inline] + pub fn float_lit(num: f64) -> Type { + Type { + kind: TypeKind::FloatLit(num), + flags: TypeFlags::FLOAT | TypeFlags::LITERAL, + is_type_alias: false, + } + } + /// Construct a float literal type. + #[inline] + pub fn str_lit(val: &str) -> Type { + Type { + kind: TypeKind::StrLit(val.to_string()), + flags: TypeFlags::STR | TypeFlags::LITERAL, + is_type_alias: false, + } + } + /// Construct a named type. + #[inline] + pub fn named(val: &str) -> Type { + Type { + kind: TypeKind::Named(val.to_string()), + flags: TypeFlags::NAMED, + is_type_alias: false, + } + } + /// Construct a number multiplier type. + #[inline] + pub fn number_multiplier(value: f64, raw_value: i64, binary_suffix: &str) -> Type { + Type { + kind: TypeKind::NumberMultiplier(NumberMultiplierType { + value, + raw_value, + binary_suffix: binary_suffix.to_string(), + is_literal: true, + }), + flags: TypeFlags::NUMBER_MULTIPLIER, + is_type_alias: false, + } + } + #[inline] + pub fn number_multiplier_non_lit_ty() -> Type { + Type { + kind: TypeKind::NumberMultiplier(NumberMultiplierType { + value: 0.0, + raw_value: 0, + binary_suffix: "".to_string(), + is_literal: false, + }), + flags: TypeFlags::NUMBER_MULTIPLIER, + is_type_alias: false, + } + } + /// Construct a function type. + #[inline] + pub fn function( + self_ty: Option>, + return_ty: Rc, + params: &[Parameter], + doc: &str, + is_variadic: bool, + kw_only_index: Option, + ) -> Type { + Type { + kind: TypeKind::Function(FunctionType { + doc: doc.to_string(), + params: params.to_owned(), + self_ty, + return_ty, + is_variadic, + kw_only_index, + }), + flags: TypeFlags::FUNCTION, + is_type_alias: false, + } + } + /// Construct a module type. + pub fn module(pkgpath: &str, imported: &[String], kind: ModuleKind) -> Type { + Type { + kind: TypeKind::Module(ModuleType { + pkgpath: pkgpath.to_string(), + imported: imported.to_owned(), + kind, + }), + flags: TypeFlags::MODULE, + is_type_alias: false, + } + } + /// Construct a schema type. + pub fn schema(schema_ty: SchemaType) -> Type { + Type { + kind: TypeKind::Schema(schema_ty), + flags: TypeFlags::SCHEMA, + is_type_alias: false, + } + } + /// Construct a iterable type + pub fn iterable() -> Rc { + Rc::new(Type::union(&[ + Rc::new(Type::STR), + Rc::new(Type::dict(Rc::new(Type::ANY), Rc::new(Type::ANY))), + Rc::new(Type::list(Rc::new(Type::ANY))), + ])) + } + /// Construct a number type + pub fn number() -> Rc { + Rc::new(Type::union(&[ + Rc::new(Type::INT), + Rc::new(Type::FLOAT), + Rc::new(Type::STR), + ])) + } + /// Whether is a any type. + #[inline] + pub fn is_any(&self) -> bool { + self.flags.contains(TypeFlags::ANY) + } + /// Whether is a int type. + #[inline] + pub fn is_int(&self) -> bool { + self.flags.contains(TypeFlags::INT) + } + /// Whether is a float type. + #[inline] + pub fn is_float(&self) -> bool { + self.flags.contains(TypeFlags::FLOAT) + } + /// Whether is a bool type. + #[inline] + pub fn is_bool(&self) -> bool { + self.flags.contains(TypeFlags::BOOL) + } + /// Whether is a string type. + #[inline] + pub fn is_str(&self) -> bool { + self.flags.contains(TypeFlags::STR) + } + /// Whether is a literal type. + #[inline] + pub fn is_literal(&self) -> bool { + self.flags.contains(TypeFlags::LITERAL) + } + /// Whether is a key type. + #[inline] + pub fn is_key(&self) -> bool { + match &self.kind { + TypeKind::Str | TypeKind::StrLit(_) => true, + TypeKind::Union(types) => types.iter().all(|ty| ty.is_key()), + _ => false, + } + } + /// Whether is a primitive type. + #[inline] + pub fn is_primitive(&self) -> bool { + matches!( + &self.kind, + TypeKind::Bool | TypeKind::Int | TypeKind::Float | TypeKind::Str + ) + } + /// Whether is a None type. + #[inline] + pub fn is_none(&self) -> bool { + self.flags.contains(TypeFlags::NONE) + } + /// Whether is a None or any type. + #[inline] + pub fn is_none_or_any(&self) -> bool { + self.is_none() || self.is_any() + } + /// Whether is a number type. + #[inline] + pub fn is_number(&self) -> bool { + self.flags.contains(TypeFlags::INT) + || self.flags.contains(TypeFlags::FLOAT) + || self.flags.contains(TypeFlags::BOOL) + } + /// Whether is a void type. + #[inline] + pub fn is_void(&self) -> bool { + self.flags.contains(TypeFlags::VOID) + } + /// Whether is a list type. + #[inline] + pub fn is_list(&self) -> bool { + self.flags.contains(TypeFlags::LIST) + } + /// Whether is a dict type. + #[inline] + pub fn is_dict(&self) -> bool { + self.flags.contains(TypeFlags::DICT) + } + /// Whether is a schema type. + #[inline] + pub fn is_schema(&self) -> bool { + self.flags.contains(TypeFlags::SCHEMA) + } + #[inline] + pub fn is_schema_def(&self) -> bool { + match &self.kind { + TypeKind::Schema(schema_ty) => !schema_ty.is_instance, + _ => false, + } + } + /// Whether is a schema type. + #[inline] + pub fn is_dict_or_schema(&self) -> bool { + self.is_dict() || self.is_schema() + } + /// Whether is a union type. + #[inline] + pub fn is_union(&self) -> bool { + self.flags.contains(TypeFlags::UNION) + } + /// Whether is a iterable type. + #[inline] + pub fn is_iterable(&self) -> bool { + self.is_str() || self.is_list() || self.is_dict() || self.is_schema() + } + /// Whether is a function type. + #[inline] + pub fn is_func(&self) -> bool { + self.flags.contains(TypeFlags::FUNCTION) + } + /// Whether is a number multiplier type. + #[inline] + pub fn is_number_multiplier(&self) -> bool { + self.flags.contains(TypeFlags::NUMBER_MULTIPLIER) + } + /// Whether is a module type. + #[inline] + pub fn is_module(&self) -> bool { + self.flags.contains(TypeFlags::MODULE) + } + /// Whether is an assignable type. + #[inline] + pub fn is_assignable_type(&self) -> bool { + match &self.kind { + TypeKind::None + | TypeKind::Any + | TypeKind::Bool + | TypeKind::BoolLit(_) + | TypeKind::Int + | TypeKind::IntLit(_) + | TypeKind::Float + | TypeKind::FloatLit(_) + | TypeKind::Str + | TypeKind::StrLit(_) + | TypeKind::List(_) + | TypeKind::Dict(_, _) + | TypeKind::Union(_) + | TypeKind::Schema(_) + | TypeKind::NumberMultiplier(_) + | TypeKind::Function(_) => true, + TypeKind::Void | TypeKind::Module(_) | TypeKind::Named(_) => false, + } + } +} diff --git a/kclvm/sema/src/ty/context.rs b/kclvm/sema/src/ty/context.rs new file mode 100644 index 000000000..5af42fc0d --- /dev/null +++ b/kclvm/sema/src/ty/context.rs @@ -0,0 +1,158 @@ +use std::rc::Rc; + +use super::{sup, Type, TypeFlags, TypeKind}; +use petgraph::algo::is_cyclic_directed; +use petgraph::graph::DiGraph; + +/// TypeContext responsible for type generation, calculation, +/// and equality and subtype judgment between types. +#[derive(Debug)] +pub struct TypeContext { + pub dep_graph: DiGraph, + pub builtin_types: BuiltinTypes, +} + +#[derive(Debug)] +pub struct BuiltinTypes { + pub any: Rc, + pub bool: Rc, + pub int: Rc, + pub float: Rc, + pub str: Rc, + pub void: Rc, + pub none: Rc, +} + +impl Default for TypeContext { + fn default() -> Self { + Self::new() + } +} + +impl TypeContext { + /// New a type context. + pub fn new() -> Self { + TypeContext { + dep_graph: DiGraph::new(), + builtin_types: BuiltinTypes { + any: Rc::new(Type::ANY), + bool: Rc::new(Type::BOOL), + int: Rc::new(Type::INT), + float: Rc::new(Type::FLOAT), + str: Rc::new(Type::STR), + void: Rc::new(Type::VOID), + none: Rc::new(Type::NONE), + }, + } + } + + /// Return true if the dep graph contains a cycle. + #[inline] + pub fn is_cyclic(&self) -> bool { + is_cyclic_directed(&self.dep_graph) + } + + /// Convert the literal union type to its variable type + /// e.g., 1|2 -> int, 's'|'ss' -> str. + pub fn literal_union_type_to_variable_type(&self, ty: Rc) -> Rc { + if ty.is_union() { + self.infer_to_variable_type(ty) + } else { + ty + } + } + + /// Judge a type kind in the type kind list or the union + /// type kinds are all in the type kind. + pub fn is_kind_type_or_kind_union_type(&self, ty: Rc, flags: &[TypeFlags]) -> bool { + match &ty.kind { + TypeKind::Union(types) => types + .iter() + .all(|ty| flags.iter().any(|flag| ty.contains_flags(*flag))), + _ => flags.iter().any(|flag| ty.contains_flags(*flag)), + } + } + + #[inline] + pub fn is_number_type_or_number_union_type(&self, ty: Rc) -> bool { + self.is_kind_type_or_kind_union_type( + ty, + &[TypeFlags::INT, TypeFlags::FLOAT, TypeFlags::BOOL], + ) + } + + #[inline] + pub fn is_config_type_or_config_union_type(&self, ty: Rc) -> bool { + self.is_kind_type_or_kind_union_type(ty, &[TypeFlags::DICT, TypeFlags::SCHEMA]) + } + + #[inline] + pub fn is_str_type_or_str_union_type(&self, ty: Rc) -> bool { + self.is_kind_type_or_kind_union_type(ty, &[TypeFlags::STR]) + } + + #[inline] + pub fn is_primitive_type_or_primitive_union_type(&self, ty: Rc) -> bool { + self.is_kind_type_or_kind_union_type( + ty, + &[ + TypeFlags::INT, + TypeFlags::FLOAT, + TypeFlags::BOOL, + TypeFlags::STR, + ], + ) + } + + #[inline] + pub fn is_mul_val_type_or_mul_val_union_type(&self, ty: Rc) -> bool { + self.is_kind_type_or_kind_union_type( + ty, + &[ + TypeFlags::INT, + TypeFlags::FLOAT, + TypeFlags::BOOL, + TypeFlags::STR, + TypeFlags::LIST, + ], + ) + } + + /// Convert type to the real type annotation + #[inline] + pub fn into_type_annotation_str(&self, ty: Rc) -> String { + ty.into_type_annotation_str() + } +} + +pub trait TypeInferMethods { + /// Infer the value type to the variable type" + fn infer_to_variable_type(&self, ty: Rc) -> Rc; +} + +impl TypeInferMethods for TypeContext { + /// Infer the value type to the variable type" + fn infer_to_variable_type(&self, ty: Rc) -> Rc { + match &ty.kind { + // None/Undefined type to any type e.g., None -> any + TypeKind::None => self.builtin_types.any.clone(), + // Literal type to its named type e.g., 1 -> int, "s" -> str + TypeKind::BoolLit(_) => self.builtin_types.bool.clone(), + TypeKind::IntLit(_) => self.builtin_types.int.clone(), + TypeKind::FloatLit(_) => self.builtin_types.float.clone(), + TypeKind::StrLit(_) => self.builtin_types.str.clone(), + TypeKind::List(item_ty) => Type::list_ref(self.infer_to_variable_type(item_ty.clone())), + // Dict type e.g., {str:1|2} -> {str:int} + TypeKind::Dict(key_ty, val_ty) => Type::dict_ref( + self.infer_to_variable_type(key_ty.clone()), + self.infer_to_variable_type(val_ty.clone()), + ), + // Union type e.g., 1|2|"s" -> int|str + TypeKind::Union(types) => sup(&types + .iter() + .map(|ty| self.infer_to_variable_type(ty.clone())) + .collect::>>()), + _ => ty.clone(), + } + } +} diff --git a/kclvm/sema/src/ty/into.rs b/kclvm/sema/src/ty/into.rs new file mode 100644 index 000000000..ac22c021b --- /dev/null +++ b/kclvm/sema/src/ty/into.rs @@ -0,0 +1,161 @@ +use super::*; + +impl Type { + /// Downcast ty into the list type. + #[inline] + pub fn list_item_ty(&self) -> Rc { + match &self.kind { + TypeKind::List(item_ty) => item_ty.clone(), + _ => bug!("invalid list type {}", self.ty_str()), + } + } + /// Downcast ty into the dict entry type. + #[inline] + pub fn dict_entry_ty(&self) -> (Rc, Rc) { + match &self.kind { + TypeKind::Dict(key_ty, val_ty) => (key_ty.clone(), val_ty.clone()), + _ => bug!("invalid dict type {}", self.ty_str()), + } + } + /// Downcast ty into the config key type. + #[inline] + pub fn config_key_ty(&self) -> Rc { + match &self.kind { + TypeKind::Dict(key_ty, _) => key_ty.clone(), + TypeKind::Schema(schema_ty) => schema_ty.key_ty(), + _ => bug!("invalid config type {}", self.ty_str()), + } + } + /// Downcast ty into the config value type. + #[inline] + pub fn config_val_ty(&self) -> Rc { + match &self.kind { + TypeKind::Dict(_, val_ty) => val_ty.clone(), + TypeKind::Schema(schema_ty) => schema_ty.val_ty(), + _ => bug!("invalid config type {}", self.ty_str()), + } + } + /// Get types from the union type. + #[inline] + pub fn union_types(&self) -> Vec> { + match &self.kind { + TypeKind::Union(types) => types.clone(), + _ => bug!("invalid {} into union type", self.ty_str()), + } + } + /// Into schema type. + #[inline] + pub fn into_schema_type(&self) -> SchemaType { + match &self.kind { + TypeKind::Schema(schema_ty) => schema_ty.clone(), + _ => bug!("invalid type {} into schema type", self.ty_str()), + } + } + /// Into number multiplier type. + #[inline] + pub fn into_number_multiplier(&self) -> NumberMultiplierType { + match &self.kind { + TypeKind::NumberMultiplier(number_multiplier) => number_multiplier.clone(), + _ => bug!("invalid type {} into number multiplier type", self.ty_str()), + } + } + /// Get the type string. + pub fn into_type_annotation_str(&self) -> String { + match &self.kind { + TypeKind::None => NAME_CONSTANT_NONE.to_string(), + TypeKind::BoolLit(v) => (if *v { + NAME_CONSTANT_TRUE + } else { + NAME_CONSTANT_FALSE + }) + .to_string(), + TypeKind::IntLit(v) => v.to_string(), + TypeKind::FloatLit(v) => { + let mut float_str = v.to_string(); + if !float_str.contains('.') { + float_str.push_str(".0"); + } + float_str + } + TypeKind::StrLit(v) => (format!("\"{}\"", v.replace('"', "\\\""))), + TypeKind::List(item_ty) => format!("[{}]", item_ty.into_type_annotation_str()), + TypeKind::Dict(key_ty, val_ty) => { + format!( + "{{{}:{}}}", + key_ty.into_type_annotation_str(), + val_ty.into_type_annotation_str() + ) + } + TypeKind::Union(types) => types + .iter() + .map(|ty| ty.into_type_annotation_str()) + .collect::>() + .join("|"), + TypeKind::Schema(schema_ty) => schema_ty.ty_str_with_pkgpath(), + TypeKind::NumberMultiplier(number_multiplier) => { + if number_multiplier.is_literal { + format!( + "{}({}{})", + NUMBER_MULTIPLIER_TYPE_STR, + number_multiplier.raw_value, + number_multiplier.binary_suffix + ) + } else { + NUMBER_MULTIPLIER_PKG_TYPE_STR.to_string() + } + } + _ => self.ty_str(), + } + } +} + +impl From for Type { + fn from(ty: ast::Type) -> Type { + match ty { + ast::Type::Any => Type::ANY, + ast::Type::Basic(basic_ty) => match basic_ty { + ast::BasicType::Bool => Type::BOOL, + ast::BasicType::Int => Type::INT, + ast::BasicType::Float => Type::FLOAT, + ast::BasicType::Str => Type::STR, + }, + ast::Type::Named(identifier) => Type::named(&identifier.get_name()), + ast::Type::List(list_ty) => Type::list( + list_ty + .inner_type + .as_ref() + .map_or(Rc::new(Type::ANY), |ty| Rc::new(ty.node.clone().into())), + ), + ast::Type::Dict(dict_ty) => Type::dict( + dict_ty + .key_type + .as_ref() + .map_or(Rc::new(Type::ANY), |ty| Rc::new(ty.node.clone().into())), + dict_ty + .value_type + .as_ref() + .map_or(Rc::new(Type::ANY), |ty| Rc::new(ty.node.clone().into())), + ), + ast::Type::Union(union_ty) => Type::union( + &union_ty + .type_elements + .iter() + .map(|ty| Rc::new(ty.node.clone().into())) + .collect::>>(), + ), + ast::Type::Literal(literal_ty) => match literal_ty { + ast::LiteralType::Bool(v) => Type::bool_lit(v), + ast::LiteralType::Int(v, suffix_option) => match suffix_option { + Some(suffix) => Type::number_multiplier( + kclvm::cal_num(v, &suffix.value()), + v, + &suffix.value(), + ), + None => Type::int_lit(v), + }, + ast::LiteralType::Float(v) => Type::float_lit(v), + ast::LiteralType::Str(v) => Type::str_lit(&v), + }, + } + } +} diff --git a/kclvm/sema/src/ty/mod.rs b/kclvm/sema/src/ty/mod.rs new file mode 100644 index 000000000..e18300ac3 --- /dev/null +++ b/kclvm/sema/src/ty/mod.rs @@ -0,0 +1,334 @@ +mod constants; +mod constructor; +mod context; +mod into; +pub mod parser; +mod unify; +mod walker; + +use std::rc::Rc; + +pub use constants::*; +pub use context::{TypeContext, TypeInferMethods}; +use kclvm_ast::ast; +use kclvm_ast::MAIN_PKG; +use kclvm_error::Position; +pub use unify::*; +pub use walker::walk_type; + +use indexmap::IndexMap; + +#[cfg(test)] +mod tests; + +#[derive(Debug, Clone, PartialEq)] +pub struct Type { + // The type kind. + pub kind: TypeKind, + // Is a type alias. + pub is_type_alias: bool, + /// This field provides fast access to information that is + /// also contained in `kind`. + flags: TypeFlags, +} + +impl Type { + /// Whether the type contains the flag. + #[inline] + pub fn contains_flags(&self, flag: TypeFlags) -> bool { + self.flags.contains(flag) + } + /// Returns the type string used for error handler. + pub fn ty_str(&self) -> String { + match &self.kind { + TypeKind::None => NONE_TYPE_STR.to_string(), + TypeKind::Any => ANY_TYPE_STR.to_string(), + TypeKind::Bool => BOOL_TYPE_STR.to_string(), + TypeKind::BoolLit(v) => format!("{}({})", BOOL_TYPE_STR, v), + TypeKind::Int => INT_TYPE_STR.to_string(), + TypeKind::IntLit(v) => format!("{}({})", INT_TYPE_STR, v), + TypeKind::Float => FLOAT_TYPE_STR.to_string(), + TypeKind::FloatLit(v) => format!("{}({})", FLOAT_TYPE_STR, v), + TypeKind::Str => STR_TYPE_STR.to_string(), + TypeKind::StrLit(v) => format!("{}({})", STR_TYPE_STR, v), + TypeKind::List(item_ty) => format!("[{}]", item_ty.ty_str()), + TypeKind::Dict(key_ty, val_ty) => { + format!("{{{}:{}}}", key_ty.ty_str(), val_ty.ty_str()) + } + TypeKind::Union(types) => types + .iter() + .map(|ty| ty.ty_str()) + .collect::>() + .join("|"), + TypeKind::Schema(schema_ty) => schema_ty.name.to_string(), + TypeKind::NumberMultiplier(number_multiplier) => format!( + "{}({}{})", + NUMBER_MULTIPLIER_TYPE_STR, + number_multiplier.raw_value, + number_multiplier.binary_suffix + ), + TypeKind::Function(_) => FUNCTION_TYPE_STR.to_string(), + TypeKind::Void => VOID_TYPE_STR.to_string(), + TypeKind::Module(module_ty) => format!("{} '{}'", MODULE_TYPE_STR, module_ty.pkgpath), + TypeKind::Named(name) => name.to_string(), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum TypeKind { + /// A primitive None name constant. + None, + /// The any type. + Any, + /// The primitive boolean type. Written as `bool`. + Bool, + BoolLit(bool), + /// A primitive integer type. Written as `int`. + Int, + /// A primitive integer literal type. + IntLit(i64), + /// A primitive float type. Written as `float`. + Float, + /// A primitive float literal type. + FloatLit(f64), + /// The primitive string type; holds a Unicode scalar value + /// (a non-surrogate code point). Written as `str`. + Str, + /// A primitive string literal type. + StrLit(String), + /// The pointer of an array slice. Written as `[T]`. + List(Rc), + /// A map type. Written as `{kT, vT}`. + Dict(Rc, Rc), + /// A union type. Written as ty1 | ty2 | ... | tyn + Union(Vec>), + /// A schema type. + Schema(SchemaType), + /// A number multiplier type. + NumberMultiplier(NumberMultiplierType), + /// The function type. + Function(FunctionType), + /// The bottom never type. + Void, + /// The module type. + Module(ModuleType), + /// A named type alias. + Named(String), +} + +bitflags::bitflags! { + /// TypeFlags provides fast access to information that is also contained + /// in `kind`. + pub struct TypeFlags: u16 { + const VOID = 1 << 0; + const INT = 1 << 1; + const FLOAT = 1 << 2; + const STR = 1 << 3; + const BOOL = 1 << 4; + const ANY = 1 << 5; + const NONE = 1 << 6; + const LIST = 1 << 7; + const DICT = 1 << 8; + const SCHEMA = 1 << 9; + const UNION = 1 << 10; + const LITERAL = 1 << 11; + const NUMBER_MULTIPLIER = 1 << 12; + const FUNCTION = 1 << 13; + const MODULE = 1 << 14; + const NAMED = 1 << 15; + } +} + +/// The schema type. +#[derive(Debug, Clone, PartialEq)] +pub struct SchemaType { + /// The schema name. + pub name: String, + /// The schema definition package path + pub pkgpath: String, + /// The schema definition file path. + pub filename: String, + /// The schema definition document string. + pub doc: String, + /// Mark whether the schema is a type of a instance or + /// a type value (Schema.instances()). + pub is_instance: bool, + /// Mark is a schema mixin. + pub is_mixin: bool, + /// Mark is a schema protocol. + pub is_protocol: bool, + /// Mark is a rule. + pub is_rule: bool, + /// Base schema. + pub base: Option>, + /// Protocol schema. + pub protocol: Option>, + /// Schema Mixins. + pub mixins: Vec, + /// Schema attributes. + pub attrs: IndexMap, + /// Schema function type. + pub func: Box, + /// Schema index signature. + pub index_signature: Option>, + /// Schema decorators including self and attribute decorators. + pub decorators: Vec, +} + +impl SchemaType { + /// Get the object type string with pkgpath + pub fn ty_str_with_pkgpath(&self) -> String { + if self.pkgpath.is_empty() || self.pkgpath == MAIN_PKG { + self.name.clone() + } else { + format!("@{}.{}", self.pkgpath, self.name) + } + } + /// Is `name` a schema member function + pub fn is_member_functions(&self, name: &str) -> bool { + !self.is_instance && SCHEMA_MEMBER_FUNCTIONS.contains(&name) + } + + pub fn set_type_of_attr(&mut self, attr: &str, ty: Rc) { + match self.attrs.get_mut(attr) { + Some(attr) => attr.ty = ty, + None => { + let schema_attr = SchemaAttr { + is_optional: true, + has_default: false, + ty, + pos: Position::dummy_pos(), + }; + self.attrs.insert(attr.to_string(), schema_attr); + } + } + } + + #[inline] + pub fn get_type_of_attr(&self, attr: &str) -> Option> { + self.get_obj_of_attr(attr).map(|attr| attr.ty.clone()) + } + + #[inline] + pub fn get_obj_of_attr(&self, attr: &str) -> Option<&SchemaAttr> { + match self.attrs.get(attr) { + Some(attr) => Some(attr), + None => self.base.as_ref().map_or( + self.protocol + .as_ref() + .and_then(|protocol| protocol.get_obj_of_attr(attr)), + |base| base.get_obj_of_attr(attr), + ), + } + } + + pub fn key_ty(&self) -> Rc { + Rc::new(Type::STR) + } + + pub fn val_ty(&self) -> Rc { + if let Some(index_signature) = &self.index_signature { + index_signature.val_ty.clone() + } else { + Rc::new(Type::ANY) + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct SchemaAttr { + pub is_optional: bool, + pub has_default: bool, + pub ty: Rc, + pub pos: Position, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct SchemaIndexSignature { + pub key_name: Option, + pub key_ty: Rc, + pub val_ty: Rc, + pub any_other: bool, +} + +impl SchemaIndexSignature { + pub fn ty_str(&self) -> String { + let key_name_str = match &self.key_name { + Some(name) => format!("[{}: ", name), + None => "[".to_string(), + }; + let any_other_str = if self.any_other { "..." } else { "" }; + key_name_str + + any_other_str + + &format!("{}]: {}", self.key_ty.ty_str(), self.val_ty.ty_str()) + } +} + +/// The module type. +#[derive(Debug, Clone, PartialEq)] +pub struct ModuleType { + pub pkgpath: String, + pub imported: Vec, + pub kind: ModuleKind, +} + +/// The module kind. +#[derive(Debug, Clone, PartialEq)] +pub enum ModuleKind { + User, + System, + Plugin, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Decorator { + pub target: DecoratorTarget, + /// The decorator name. + pub name: String, + /// The schema or attribute name of decorator dimension + pub key: String, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum DecoratorTarget { + Schema, + Attribute, +} + +/// The number multiplier type. +#[derive(Debug, Clone, PartialEq)] +pub struct NumberMultiplierType { + pub value: f64, + pub raw_value: i64, + pub binary_suffix: String, + pub is_literal: bool, +} + +impl NumberMultiplierType { + pub fn ty_str(&self) -> String { + format!( + "{}({}{})", + NUMBER_MULTIPLIER_TYPE_STR, self.raw_value, self.binary_suffix + ) + } +} + +/// The function type. +#[derive(Debug, Clone, PartialEq)] +pub struct FunctionType { + pub doc: String, + pub params: Vec, + pub self_ty: Option>, + pub return_ty: Rc, + pub is_variadic: bool, + pub kw_only_index: Option, +} + +/// The function parameter. +#[derive(Debug, Clone, PartialEq)] +pub struct Parameter { + pub name: String, + pub ty: Rc, + pub has_default: bool, +} diff --git a/kclvm/sema/src/ty/parser.rs b/kclvm/sema/src/ty/parser.rs new file mode 100644 index 000000000..a998c6f8d --- /dev/null +++ b/kclvm/sema/src/ty/parser.rs @@ -0,0 +1,264 @@ +use crate::eval::str_literal_eval; + +use super::*; + +/// Parse type string +pub fn parse_type_str(ty_str: &str) -> Rc { + if ty_str.is_empty() { + return Rc::new(Type::ANY); + } + let ty_str = ty_str_strip(ty_str); + match TYPES_MAPPING.get(ty_str) { + Some(ty) => Rc::new(ty.clone()), + None => { + if is_union_type_str(ty_str) { + parse_union_type_str(ty_str) + } else if is_literal_type_str(ty_str) { + parse_lit_type_str(ty_str) + } else if is_number_multiplier_literal_type_str(ty_str) { + parse_number_multiplier_literal_type_str(ty_str) + } else if is_dict_type_str(ty_str) { + let (key_ty_str, val_ty_str) = separate_kv(&dereference_type(ty_str)); + Rc::new(Type::dict( + parse_type_str(&key_ty_str), + parse_type_str(&val_ty_str), + )) + } else if is_list_type_str(ty_str) { + Rc::new(Type::list(parse_type_str(&dereference_type(ty_str)))) + } else { + parse_named_type_str(ty_str) + } + } + } +} + +/// is_literal_type returns the type string whether is a literal type +pub fn is_literal_type_str(ty_str: &str) -> bool { + if NAME_CONSTANTS.contains(&ty_str) { + return true; + } + if ty_str.starts_with('\"') { + return ty_str.ends_with('\"'); + } + if ty_str.starts_with('\'') { + return ty_str.ends_with('\''); + } + matches!(ty_str.parse::(), Ok(_)) +} + +/// is_dict_type returns the type string whether is a dict type +#[inline] +pub fn is_dict_type_str(ty: &str) -> bool { + ty.len() >= 2 && &ty[0..1] == "{" && &ty[ty.len() - 1..] == "}" +} + +/// is_list_type returns the type string whether is a list type +#[inline] +pub fn is_list_type_str(ty: &str) -> bool { + ty.len() >= 2 && &ty[0..1] == "[" && &ty[ty.len() - 1..] == "]" +} + +#[inline] +pub fn is_builtin_type_str(ty: &str) -> bool { + BUILTIN_TYPES.contains(&ty) +} + +/// is schema expected type +pub fn is_schema_type_str(expected_type: &str) -> bool { + if expected_type.is_empty() { + return true; + } + !is_list_type_str(expected_type) + && !is_dict_type_str(expected_type) + && !is_builtin_type_str(expected_type) + && !is_literal_type_str(expected_type) +} + +/// is union type +pub fn is_union_type_str(ty: &str) -> bool { + let mut stack = String::new(); + let mut i = 0; + while i < ty.chars().count() { + let c = ty.chars().nth(i).unwrap(); + if c == '|' && stack.is_empty() { + return true; + } else if c == '[' || c == '{' { + stack.push(c); + } else if c == ']' || c == '}' { + stack.pop(); + } else if c == '\"' { + let t = &ty[i..]; + let re = fancy_regex::Regex::new(r#""(?!"").*?(? bool { + let re = fancy_regex::Regex::new(NUMBER_MULTIPLIER_REGEX).unwrap(); + match re.is_match(ty_str) { + Ok(ok) => ok, + _ => false, + } +} + +/// separate_kv split the union type and do not split '|' in dict and list +/// e.g., "int|str" -> vec!["int", "str"] +pub fn split_type_union(ty_str: &str) -> Vec<&str> { + let mut i = 0; + let mut s_index = 0; + let mut stack = String::new(); + let mut types: Vec<&str> = vec![]; + while i < ty_str.chars().count() { + let c = ty_str.chars().nth(i).unwrap(); + if c == '|' && stack.is_empty() { + types.push(&ty_str[s_index..i]); + s_index = i + 1; + } + // List/Dict type + else if c == '[' || c == '{' { + stack.push(c); + } + // List/Dict type + else if c == ']' || c == '}' { + stack.pop(); + } + // String literal type + else if c == '\"' { + let t = &ty_str[i..]; + let re = fancy_regex::Regex::new(r#""(?!"").*?(? Rc { + let types = split_type_union(ty_str) + .iter() + .map(|ty_str| parse_type_str(ty_str)) + .collect::>>(); + sup(&types) +} + +/// Parse literal type string. +pub fn parse_lit_type_str(ty_str: &str) -> Rc { + // Bool literal type. + if ty_str == NAME_CONSTANT_TRUE { + return Rc::new(Type::bool_lit(true)); + } else if ty_str == NAME_CONSTANT_FALSE { + return Rc::new(Type::bool_lit(false)); + } + match ty_str.parse::() { + // Float literal type. + Ok(v) => Rc::new(Type::int_lit(v)), + Err(_) => match ty_str.parse::() { + // Int literal type. + Ok(v) => Rc::new(Type::float_lit(v)), + // Maybe string literal type + Err(_) => match str_literal_eval(ty_str, false, false) { + Some(v) => Rc::new(Type::str_lit(&v)), + None => bug!("invalid literal type string {}", ty_str), + }, + }, + } +} + +/// Parse number multiplier literal type. +pub fn parse_number_multiplier_literal_type_str(ty_str: &str) -> Rc { + let suffix_index = if &ty_str[ty_str.len() - 1..] == kclvm::IEC_SUFFIX { + ty_str.len() - 2 + } else { + ty_str.len() - 1 + }; + let (value, suffix) = ( + match ty_str[..suffix_index].parse::() { + Ok(v) => v, + Err(_) => bug!("invalid number multiplier literal type str {}", ty_str), + }, + &ty_str[suffix_index..], + ); + Rc::new(Type::number_multiplier( + kclvm::cal_num(value, suffix), + value, + suffix, + )) +} + +/// Please note Named type to find it in the scope (e.g. schema type, type alias). +#[inline] +pub fn parse_named_type_str(ty_str: &str) -> Rc { + Rc::new(Type::named(ty_str)) +} + +/// separate_kv function separates key_type and value_type in the dictionary type strings, +/// e.g., "str:str" -> ("str", "str") +pub fn separate_kv(expected_type: &str) -> (String, String) { + let mut stack = String::new(); + for (n, c) in expected_type.chars().enumerate() { + if c == '[' || c == '{' { + stack.push(c) + } else if c == ']' { + if &stack[stack.len() - 1..] != "[" { + panic!("invalid type string {}", expected_type); + } + stack.pop(); + } else if c == '}' { + if &stack[stack.len() - 1..] != "{" { + panic!("invalid type string {}", expected_type); + } + stack.pop(); + } else if c == ':' { + if !stack.is_empty() { + panic!("invalid type string {}", expected_type); + } + return ( + expected_type[..n].to_string(), + expected_type[n + 1..].to_string(), + ); + } + } + ("".to_string(), "".to_string()) +} + +/// dereference_type function removes the first and last [] {} in the type string +/// e.g., "\[int\]" -> "int" +pub fn dereference_type(tpe: &str) -> String { + if tpe.len() > 1 + && ((&tpe[0..1] == "[" && &tpe[tpe.len() - 1..] == "]") + || (&tpe[0..1] == "{" && &tpe[tpe.len() - 1..] == "}")) + { + return tpe[1..tpe.len() - 1].to_string(); + } + tpe.to_string() +} + +#[inline] +fn ty_str_strip(ty_str: &str) -> &str { + let chars = " \r\n"; + ty_str.trim_matches(|c| chars.contains(c)) +} diff --git a/kclvm/sema/src/ty/tests.rs b/kclvm/sema/src/ty/tests.rs new file mode 100644 index 000000000..c23ebc5f9 --- /dev/null +++ b/kclvm/sema/src/ty/tests.rs @@ -0,0 +1,117 @@ +use super::*; + +#[test] +fn test_sup() { + let cases = vec![ + (vec![], Rc::new(Type::ANY)), + (vec![Rc::new(Type::ANY)], Rc::new(Type::ANY)), + (vec![Rc::new(Type::STR)], Rc::new(Type::STR)), + ( + vec![Rc::new(Type::STR), Rc::new(Type::INT)], + Type::union_ref(&[Rc::new(Type::STR), Rc::new(Type::INT)]), + ), + ( + vec![Rc::new(Type::BOOL), Rc::new(Type::bool_lit(true))], + Rc::new(Type::BOOL), + ), + ( + vec![ + Rc::new(Type::str_lit("Blue")), + Rc::new(Type::str_lit("Yellow")), + Rc::new(Type::str_lit("Red")), + ], + Type::union_ref(&[ + Rc::new(Type::str_lit("Blue")), + Rc::new(Type::str_lit("Yellow")), + Rc::new(Type::str_lit("Red")), + ]), + ), + ( + vec![ + Type::list_ref(Type::union_ref(&[ + Rc::new(Type::int_lit(1)), + Rc::new(Type::int_lit(2)), + ])), + Type::list_ref(Type::union_ref(&[ + Rc::new(Type::int_lit(3)), + Rc::new(Type::int_lit(4)), + ])), + ], + Type::union_ref(&[ + Type::list_ref(Type::union_ref(&[ + Rc::new(Type::int_lit(1)), + Rc::new(Type::int_lit(2)), + ])), + Type::list_ref(Type::union_ref(&[ + Rc::new(Type::int_lit(3)), + Rc::new(Type::int_lit(4)), + ])), + ]), + ), + ( + vec![ + Type::union_ref(&[ + Rc::new(Type::STR), + Type::dict_ref(Rc::new(Type::STR), Rc::new(Type::STR)), + ]), + Type::dict_ref(Rc::new(Type::ANY), Rc::new(Type::ANY)), + ], + Type::union_ref(&[ + Rc::new(Type::STR), + Type::dict_ref(Rc::new(Type::ANY), Rc::new(Type::ANY)), + ]), + ), + ]; + for (types, expected) in &cases { + let got = sup(types); + assert_eq!(got, *expected); + } +} + +#[test] +fn test_type_walker() { + fn walk_fn(ty: &Type) -> Rc { + if ty.is_int() { + Rc::new(Type::STR) + } else { + Rc::new(ty.clone()) + } + } + let cases = [ + (Rc::new(Type::ANY), Rc::new(Type::ANY)), + (Rc::new(Type::INT), Rc::new(Type::STR)), + (Rc::new(Type::STR), Rc::new(Type::STR)), + ( + Type::list_ref(Rc::new(Type::INT)), + Type::list_ref(Rc::new(Type::STR)), + ), + ( + Type::union_ref(&[Rc::new(Type::INT), Rc::new(Type::STR)]), + Type::union_ref(&[Rc::new(Type::STR), Rc::new(Type::STR)]), + ), + ( + Type::union_ref(&[ + Rc::new(Type::INT), + Rc::new(Type::STR), + Type::union_ref(&[Rc::new(Type::INT), Rc::new(Type::STR)]), + ]), + Type::union_ref(&[ + Rc::new(Type::STR), + Rc::new(Type::STR), + Type::union_ref(&[Rc::new(Type::STR), Rc::new(Type::STR)]), + ]), + ), + ( + Type::dict_ref(Rc::new(Type::INT), Rc::new(Type::INT)), + Type::dict_ref(Rc::new(Type::STR), Rc::new(Type::STR)), + ), + ]; + for (ty, expected) in cases { + assert_eq!( + walker::walk_type(&ty, walk_fn), + expected, + "Type test failed: {}", + ty.ty_str() + ); + } +} diff --git a/kclvm/sema/src/ty/unify.rs b/kclvm/sema/src/ty/unify.rs new file mode 100644 index 000000000..331fb0d26 --- /dev/null +++ b/kclvm/sema/src/ty/unify.rs @@ -0,0 +1,170 @@ +use std::{collections::HashSet, rc::Rc}; + +use super::{SchemaType, Type, TypeKind}; + +/// The type can be assigned to the expected type. +/// +/// For security and performance considerations, dynamic dispatch of +/// types is not supported at this stage. +pub fn subsume(ty_lhs: Rc, ty_rhs: Rc, check_left_any: bool) -> bool { + if (check_left_any && ty_lhs.is_any()) || (ty_rhs.is_any() || ty_lhs.is_none()) { + true + } else if ty_lhs.is_union() { + let types = ty_lhs.union_types(); + types + .iter() + .all(|ty| subsume(ty.clone(), ty_rhs.clone(), false)) + } else if ty_rhs.is_union() { + let types = ty_rhs.union_types(); + types + .iter() + .any(|ty| subsume(ty_lhs.clone(), ty.clone(), false)) + } else if ty_lhs.is_schema() { + match &ty_rhs.kind { + TypeKind::Schema(ty_rhs_schema) => { + is_sub_schema_of(&ty_lhs.into_schema_type(), ty_rhs_schema) + } + _ => false, + } + } else if ty_lhs.is_int() && ty_rhs.is_float() { + true + } else if ty_lhs.is_number_multiplier() && ty_rhs.is_number_multiplier() { + let ty_lhs = ty_lhs.into_number_multiplier(); + let ty_rhs = ty_rhs.into_number_multiplier(); + if ty_lhs.is_literal && ty_rhs.is_literal { + ty_lhs.raw_value == ty_rhs.raw_value && ty_lhs.binary_suffix == ty_rhs.binary_suffix + } else if ty_lhs.is_literal && !ty_rhs.is_literal { + true + } else { + ty_lhs.is_literal || !ty_rhs.is_literal + } + } else if ty_lhs.is_primitive() && ty_rhs.is_primitive() { + ty_lhs.kind == ty_rhs.kind + } else if ty_lhs.is_literal() { + if ty_rhs.is_literal() { + ty_lhs.kind == ty_rhs.kind + } else if ty_rhs.is_primitive() { + // float_lit -> float + // int_lit -> int + // bool_lit -> bool + // str_lit -> str + // int_lit/bool_lit -> float + if ty_rhs.is_float() && !ty_lhs.is_str() { + true + } else { + ty_lhs.ty_str().contains(&ty_rhs.ty_str()) + } + } else { + false + } + } else if ty_lhs.is_list() && ty_rhs.is_list() { + subsume(ty_lhs.list_item_ty(), ty_rhs.list_item_ty(), check_left_any) + } else if ty_lhs.is_dict() && ty_rhs.is_dict() { + let (ty_lhs_key, ty_lhs_val) = ty_lhs.dict_entry_ty(); + let (ty_rhs_key, ty_rhs_val) = ty_rhs.dict_entry_ty(); + subsume(ty_lhs_key, ty_rhs_key, check_left_any) + && subsume(ty_lhs_val, ty_rhs_val, check_left_any) + } else { + equal(ty_lhs, ty_rhs) + } +} + +/// Are the two types exactly equal. +#[inline] +pub fn equal(ty_lhs: Rc, ty_rhs: Rc) -> bool { + ty_lhs.kind == ty_rhs.kind +} + +/// Whether the schema is sub schema of another schema. +pub fn is_sub_schema_of(schema_ty_lhs: &SchemaType, schema_ty_rhs: &SchemaType) -> bool { + if schema_ty_lhs.ty_str_with_pkgpath() == schema_ty_rhs.ty_str_with_pkgpath() { + true + } else { + match &schema_ty_lhs.base { + Some(base) => is_sub_schema_of(base, schema_ty_rhs), + None => false, + } + } +} + +/// The type can be assigned to the expected type. +#[inline] +pub fn assignable_to(ty: Rc, expected_ty: Rc) -> bool { + if !ty.is_assignable_type() { + return false; + } + subsume(ty, expected_ty, true) +} + +/// Whether `lhs_ty` is the upper bound of the `rhs_ty` +#[inline] +pub fn is_upper_bound(lhs_ty: Rc, rhs_ty: Rc) -> bool { + subsume(rhs_ty, lhs_ty, false) +} + +/// Whether the type list contains the `any` type. +#[inline] +pub fn has_any_type(types: &[Rc]) -> bool { + types.iter().any(|ty| ty.is_any()) +} + +/// The sup function returns the minimum supremum of all types in an array of types. +#[inline] +pub fn sup(types: &[Rc]) -> Rc { + r#typeof(types, true) +} + +/// Typeof types +pub fn r#typeof(types: &[Rc], should_remove_sub_types: bool) -> Rc { + // 1. Initialize an ordered set to store the type array + let mut type_set: Vec> = vec![]; + // 2. Add the type array to the ordered set for sorting by the type id and de-duplication. + add_types_to_type_set(&mut type_set, types); + // 3. Remove sub types according to partial order relation rules e.g. sub schema types. + if should_remove_sub_types { + let mut remove_index_set = HashSet::new(); + for (i, source) in type_set.iter().enumerate() { + for (j, target) in type_set.iter().enumerate() { + if i != j && subsume(source.clone(), target.clone(), false) { + remove_index_set.insert(i); + } + } + } + let types: Vec<(usize, &Rc)> = type_set + .iter() + .enumerate() + .filter(|(i, _)| !remove_index_set.contains(i)) + .collect(); + type_set = types + .iter() + .map(|(_, ty)| <&Rc>::clone(ty).clone()) + .collect(); + } + if type_set.is_empty() { + Rc::new(Type::ANY) + } else if type_set.len() == 1 { + type_set[0].clone() + } else { + Rc::new(Type::union(&type_set)) + } +} + +fn add_types_to_type_set(type_set: &mut Vec>, types: &[Rc]) { + for ty in types { + add_type_to_type_set(type_set, ty.clone()); + } +} + +fn add_type_to_type_set(type_set: &mut Vec>, ty: Rc) { + match &ty.kind { + TypeKind::Union(types) => { + add_types_to_type_set(type_set, types); + } + _ => { + // Remove the bottom type. + if !ty.is_void() && !type_set.contains(&ty) { + type_set.push(ty.clone()) + } + } + } +} diff --git a/kclvm/sema/src/ty/walker.rs b/kclvm/sema/src/ty/walker.rs new file mode 100644 index 000000000..3f996ff6c --- /dev/null +++ b/kclvm/sema/src/ty/walker.rs @@ -0,0 +1,22 @@ +use std::rc::Rc; + +use super::Type; + +/// Walk one type recursively and deal the type using the `walk_fn` +pub fn walk_type(ty: &Type, walk_fn: impl Fn(&Type) -> Rc + Copy) -> Rc { + let ty = walk_fn(ty); + match &ty.kind { + super::TypeKind::List(item_ty) => Rc::new(Type::list(walk_type(item_ty, walk_fn))), + super::TypeKind::Dict(key_ty, val_ty) => Rc::new(Type::dict( + walk_type(key_ty, walk_fn), + walk_type(val_ty, walk_fn), + )), + super::TypeKind::Union(types) => Rc::new(Type::union( + &types + .iter() + .map(|ty| walk_type(ty, walk_fn)) + .collect::>>(), + )), + _ => ty, + } +} diff --git a/kclvm/span/Cargo.lock b/kclvm/span/Cargo.lock new file mode 100644 index 000000000..910a9ab65 --- /dev/null +++ b/kclvm/span/Cargo.lock @@ -0,0 +1,634 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", + "rustc-rayon", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "kclvm-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "kclvm-span" +version = "0.1.0" +dependencies = [ + "kclvm-macros", + "rustc_span", + "scoped-tls", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest", +] + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871372391786ccec00d3c5d3d6608905b3d4db263639cfe075d3b60a736d115a" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-rayon" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" +dependencies = [ + "crossbeam-deque", + "either", + "rustc-rayon-core", +] + +[[package]] +name = "rustc-rayon-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc_data_structures" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ena", + "indexmap", + "jobserver", + "libc", + "memmap2", + "parking_lot", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "stable_deref_trait", + "stacker", + "tempfile", + "tracing", + "winapi", +] + +[[package]] +name = "rustc_span" +version = "0.0.0" +dependencies = [ + "cfg-if 0.1.10", + "md-5", + "rustc_data_structures", + "scoped-tls", + "sha-1", + "sha2", + "tracing", + "unicode-width", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "tracing" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" diff --git a/kclvm/span/Cargo.toml b/kclvm/span/Cargo.toml new file mode 100644 index 000000000..3e486eed9 --- /dev/null +++ b/kclvm/span/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "kclvm-span" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rustc_span = { path = "../3rdparty/rustc_span" } + +kclvm-macros = { path = "../macros" } +scoped-tls = "1.0" \ No newline at end of file diff --git a/kclvm/span/LICENSE b/kclvm/span/LICENSE new file mode 100644 index 000000000..dc9abf84b --- /dev/null +++ b/kclvm/span/LICENSE @@ -0,0 +1,231 @@ +Short version for non-lawyers: + +The Rust Project is dual-licensed under Apache 2.0 and MIT +terms. + + +Longer version: + +Copyrights in the Rust project are retained by their contributors. No +copyright assignment is required to contribute to the Rust project. + +Some files include explicit copyright notices and/or license notices. +For full authorship information, see the version control history or +https://thanks.rust-lang.org + +Except as otherwise noted (below and/or in individual files), Rust is +licensed under the Apache License, Version 2.0 or + or the MIT license + or , at your option. + + +The Rust Project includes packages written by third parties. +The following third party packages are included, and carry +their own copyright notices and license terms: + +* LLVM. Code for this package is found in src/llvm-project. + + Copyright (c) 2003-2013 University of Illinois at + Urbana-Champaign. All rights reserved. + + Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + + Permission is hereby granted, free of charge, to any + person obtaining a copy of this software and associated + documentation files (the "Software"), to deal with the + Software without restriction, including without + limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following + conditions: + + * Redistributions of source code must retain the + above copyright notice, this list of conditions + and the following disclaimers. + + * Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimers in the documentation + and/or other materials provided with the + distribution. + + * Neither the names of the LLVM Team, University of + Illinois at Urbana-Champaign, nor the names of its + contributors may be used to endorse or promote + products derived from this Software without + specific prior written permission. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE + FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT + OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS WITH THE SOFTWARE. + +* Additional libraries included in LLVM carry separate + BSD-compatible licenses. See src/llvm-project/llvm/LICENSE.TXT + for details. + +* compiler-rt, in src/compiler-rt is dual licensed under + LLVM's license and MIT: + + Copyright (c) 2009-2014 by the contributors listed in + CREDITS.TXT + + All rights reserved. + + Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + + Permission is hereby granted, free of charge, to any + person obtaining a copy of this software and associated + documentation files (the "Software"), to deal with the + Software without restriction, including without + limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following + conditions: + + * Redistributions of source code must retain the + above copyright notice, this list of conditions + and the following disclaimers. + + * Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimers in the documentation + and/or other materials provided with the + distribution. + + * Neither the names of the LLVM Team, University of + Illinois at Urbana-Champaign, nor the names of its + contributors may be used to endorse or promote + products derived from this Software without + specific prior written permission. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE + FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT + OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS WITH THE SOFTWARE. + + ======================================================== + + Copyright (c) 2009-2014 by the contributors listed in + CREDITS.TXT + + Permission is hereby granted, free of charge, to any + person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the + Software without restriction, including without + limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice + shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +* Portions of the FFI code for interacting with the native ABI + is derived from the Clay programming language, which carries + the following license. + + Copyright (C) 2008-2010 Tachyon Technologies. + All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, are permitted provided that the + following conditions are met: + + 1. Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + + 2. Redistributions in binary form must reproduce the + above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or + other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH DAMAGE. + +* libbacktrace, under src/libbacktrace: + + Copyright (C) 2012-2014 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + + Redistribution and use in source and binary forms, with + or without modification, are permitted provided that the + following conditions are met: + + (1) Redistributions of source code must retain the + above copyright notice, this list of conditions and + the following disclaimer. + + (2) Redistributions in binary form must reproduce + the above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the + distribution. + + (3) The name of the author may not be used to + endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH DAMAGE. */ diff --git a/kclvm/span/src/lib.rs b/kclvm/span/src/lib.rs new file mode 100644 index 000000000..77c2d81f4 --- /dev/null +++ b/kclvm/span/src/lib.rs @@ -0,0 +1,28 @@ +//! Source positions and related helper functions. +//! +//! Important concepts in this module include: +//! +//! - the *span*, represented by [`Span`] and related types; +//! - interned strings, represented by [`Symbol`]s, with some common symbols available statically in the [`sym`] module. +//! +//! Reference: https://github.com/rust-lang/rust/blob/master/compiler/rustc_span/src/lib.rs + +mod session_globals; +pub mod span; +pub mod symbol; + +#[cfg(test)] +mod tests; + +pub use session_globals::create_session_globals_then; +use session_globals::with_session_globals; +pub use span::{BytePos, Span, DUMMY_SP}; +pub use symbol::{Ident, Symbol}; + +pub type SourceMap = rustc_span::SourceMap; +pub type SourceFile = rustc_span::SourceFile; +pub type FilePathMapping = rustc_span::source_map::FilePathMapping; +pub type Loc = rustc_span::Loc; + +#[macro_use] +extern crate kclvm_macros; diff --git a/kclvm/span/src/session_globals.rs b/kclvm/span/src/session_globals.rs new file mode 100644 index 000000000..1791a5d7d --- /dev/null +++ b/kclvm/span/src/session_globals.rs @@ -0,0 +1,105 @@ +use std::{cell::RefCell, collections::HashMap}; + +use crate::symbol::Symbol; + +/// Per-session global variables: this struct is stored in thread-local storage +/// in such a way that it is accessible without any kind of handle to all +/// threads within the compilation session, but is not accessible outside the +/// session. +/// +/// The `kclvm_span::Symbol` uses `SessionGlobals` to implement a fast global +/// string cache. +#[derive(Debug)] +pub struct SessionGlobals { + pub symbol_interner: Interner, +} + +impl SessionGlobals { + pub fn new() -> SessionGlobals { + SessionGlobals { + symbol_interner: Interner::fresh(), + } + } +} + +impl Default for SessionGlobals { + fn default() -> Self { + Self::new() + } +} + +/// Create thread local global session globals +#[inline] +pub fn create_session_globals_then(f: impl FnOnce() -> R) -> R { + assert!( + !SESSION_GLOBALS.is_set(), + "SESSION_GLOBALS should never be overwritten! \ + Use another thread if you need another SessionGlobals" + ); + let session_globals = SessionGlobals::new(); + SESSION_GLOBALS.set(&session_globals, f) +} + +#[inline] +pub fn with_session_globals(f: F) -> R +where + F: FnOnce(&SessionGlobals) -> R, +{ + SESSION_GLOBALS.with(f) +} + +// If this ever becomes non thread-local, `decode_syntax_context` +// and `decode_expn_id` will need to be updated to handle concurrent +// deserialization. +scoped_tls::scoped_thread_local!(static SESSION_GLOBALS: SessionGlobals); + +#[derive(Debug)] +pub struct Interner(RefCell); + +// This type is private to prevent accidentally constructing more than one +// `Interner` on the same thread, which makes it easy to mixup `Symbol`s +// between `Interner`s. +#[derive(Default, Debug)] +struct InternerInner { + names: HashMap<&'static str, Symbol>, + strings: Vec<&'static str>, +} + +impl Default for Interner { + fn default() -> Self { + Interner(RefCell::new(InternerInner::default())) + } +} + +impl Interner { + pub fn prefill(init: &[&'static str]) -> Self { + Interner(RefCell::new(InternerInner { + strings: init.into(), + names: init.iter().copied().zip((0..).map(Symbol::new)).collect(), + })) + } + + #[inline] + pub fn intern(&self, string: &str) -> Symbol { + let mut inner = self.0.borrow_mut(); + if let Some(&name) = inner.names.get(string) { + return name; + } + + let name = Symbol::new(inner.strings.len() as u32); + + // SAFETY: we can extend the arena allocation to `'static` because we + // only access these while the arena is still alive. + let string: &'static str = Box::leak(Box::new(string.to_string())); + inner.strings.push(string); + + inner.names.insert(string, name); + name + } + + // Get the symbol as a string. `Symbol::as_str()` should be used in + // preference to this function. + pub fn get(&self, symbol: Symbol) -> &str { + self.0.borrow().strings[symbol.0.idx as usize] + } +} diff --git a/kclvm/span/src/span.rs b/kclvm/span/src/span.rs new file mode 100644 index 000000000..7a9a160c1 --- /dev/null +++ b/kclvm/span/src/span.rs @@ -0,0 +1,5 @@ +use rustc_span; + +pub type BytePos = rustc_span::BytePos; +pub type Span = rustc_span::Span; +pub const DUMMY_SP: Span = rustc_span::DUMMY_SP; diff --git a/kclvm/span/src/symbol.rs b/kclvm/span/src/symbol.rs new file mode 100644 index 000000000..d6e4be7c4 --- /dev/null +++ b/kclvm/span/src/symbol.rs @@ -0,0 +1,242 @@ +use std::{ + fmt, + hash::{Hash, Hasher}, +}; + +use crate::{ + span::{Span, DUMMY_SP}, + with_session_globals, +}; + +use crate::session_globals::Interner; + +// The proc macro code for this is in `kclvm_macros/src/symbols.rs`. +symbols! { + // After modifying this list adjust `is_special`, `is_used_keyword`/`is_unused_keyword`, + // this should be rarely necessary though if the keywords are kept in alphabetic order. + Keywords { + // Special reserved identifiers used internally for elided lifetimes, + // unnamed method parameters, crate root module, error recovery etc. + Empty: "", + As: "as", + Import: "import", + Rule: "rule", + Schema: "schema", + Mixin: "mixin", + Protocol: "protocol", + Check: "check", + For: "for", + Assert: "assert", + If: "if", + Elif: "elif", + Else: "else", + Or: "or", + And: "and", + Not: "not", + In: "in", + Is: "is", + Lambda: "lambda", + All: "all", + Any: "any", + Filter: "filter", + Map: "map", + Type: "type", + True: "True", + False: "False", + None: "None", + Undefined: "Undefined", + } + // Pre-interned symbols that can be referred to with `kclvm_span::sym::*`. + Symbols { + bool, + float, + int, + str, + } +} + +/// Ident denotes a identifier with a symbol name and span +/// +/// ``` +/// use kclvm_span::*; +/// use rustc_span::BytePos; +/// +/// create_session_globals_then(||{ +/// let ident = Ident::new( +/// Symbol::intern("identifier"), +/// Span::new(BytePos(0), BytePos(10)), +/// ); +/// }) +/// ``` +#[derive(Debug, Copy, Clone, Eq)] +pub struct Ident { + pub name: Symbol, + pub span: Span, +} + +impl std::str::FromStr for Ident { + type Err = String; + /// Maps a string to an identifier with a dummy span. + fn from_str(s: &str) -> Result { + Ok(Ident::with_dummy_span(Symbol::intern(s))) + } +} + +impl Ident { + #[inline] + /// Constructs a new identifier from a symbol and a span. + pub const fn new(name: Symbol, span: Span) -> Ident { + Ident { name, span } + } + + /// Constructs a new identifier with a dummy span. + #[inline] + pub const fn with_dummy_span(name: Symbol) -> Ident { + Ident::new(name, DUMMY_SP) + } + + /// Maps a string and a span to an identifier. + pub fn from_str_and_span(string: &str, span: Span) -> Ident { + Ident::new(Symbol::intern(string), span) + } + + pub fn without_first_quote(self) -> Ident { + Ident::new( + Symbol::intern(self.as_str().trim_start_matches('\'')), + self.span, + ) + } + + /// Access the underlying string. This is a slowish operation because it + /// requires locking the symbol interner. + /// + /// Note that the lifetime of the return value is a lie. See + /// `Symbol::as_str()` for details. + pub fn as_str(&self) -> &str { + self.name.as_str() + } +} + +impl PartialEq for Ident { + fn eq(&self, rhs: &Self) -> bool { + self.name == rhs.name + } +} + +impl Hash for Ident { + fn hash(&self, state: &mut H) { + self.name.hash(state); + self.span.hash(state); + } +} + +/// An interned string. +/// +/// Internally, a `Symbol` is implemented as an index, and all operations +/// (including hashing, equality, and ordering) operate on that index. +/// +/// ``` +/// use kclvm_span::*; +/// create_session_globals_then(||{ +/// let sym = Symbol::intern("name"); +/// }); +/// ``` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Symbol(pub(crate) SymbolIndex); + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct SymbolIndex { + pub(crate) idx: u32, +} + +impl Symbol { + pub(crate) const fn new(n: u32) -> Self { + Symbol(SymbolIndex { idx: n }) + } + + /// Maps a string to its interned representation. + pub fn intern(string: &str) -> Self { + with_session_globals(|session_globals| session_globals.symbol_interner.intern(string)) + } + + /// Access the underlying string. This is a slowish operation because it + /// requires locking the symbol interner. + /// + /// Note that the lifetime of the return value is a lie. It's not the same + /// as `&self`, but actually tied to the lifetime of the underlying + /// interner. Interners are long-lived, and there are very few of them, and + /// this function is typically used for short-lived things, so in practice + /// it works out ok. + pub fn as_str(&self) -> &str { + with_session_globals(|session_globals| unsafe { + std::mem::transmute::<&str, &str>(session_globals.symbol_interner.get(*self)) + }) + } + + pub fn as_u32(self) -> u32 { + self.0.idx + } + + /// This method is supposed to be used in error messages, so it's expected to be + /// identical to printing the original identifier token written in source code + /// (`token_to_string`, `Ident::to_string`), except that symbols don't keep the rawness flag + /// or edition, so we have to guess the rawness using the global edition. + pub fn to_ident_string(self) -> String { + format!("{:?}", Ident::with_dummy_span(self)) + } +} + +impl fmt::Display for Symbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_str(), f) + } +} + +impl Into for Symbol { + fn into(self) -> String { + self.as_str().to_string() + } +} + +// This module has a very short name because it's used a lot. +/// This module contains all the defined keyword `Symbol`s. +/// +/// Given that `kw` is imported, use them like `kw::keyword_name`. +/// For example `kw::Loop` or `kw::Break`. +pub mod kw { + pub use super::kw_generated::*; +} + +// This module has a very short name because it's used a lot. +/// This module contains all the defined non-keyword `Symbol`s. +/// +/// Given that `sym` is imported, use them like `sym::symbol_name`. +/// For example `sym::rustfmt` or `sym::u8`. +pub mod sym { + use super::Symbol; + use std::convert::TryInto; + + #[doc(inline)] + pub use super::sym_generated::*; + + /// Get the symbol for an integer. + /// + /// The first few non-negative integers each have a static symbol and therefore + /// are fast. + pub fn integer + Copy + ToString>(n: N) -> Symbol { + if let Result::Ok(idx) = n.try_into() { + if idx < 10 { + return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32); + } + } + Symbol::intern(&n.to_string()) + } +} + +/// Special symbols related to KCL keywords. +impl Symbol { + /// Returns `true` if the symbol is `true` or `false`. + pub fn is_bool_lit(self) -> bool { + self == kw::True || self == kw::False + } +} diff --git a/kclvm/span/src/tests.rs b/kclvm/span/src/tests.rs new file mode 100644 index 000000000..113d2f6ec --- /dev/null +++ b/kclvm/span/src/tests.rs @@ -0,0 +1,27 @@ +use super::session_globals::*; +use super::*; + +#[test] +fn interner_tests() { + let i = Interner::default(); + // first one is zero: + assert_eq!(i.intern("dog"), Symbol::new(0)); + // re-use gets the same entry: + assert_eq!(i.intern("dog"), Symbol::new(0)); + // different string gets a different #: + assert_eq!(i.intern("cat"), Symbol::new(1)); + assert_eq!(i.intern("cat"), Symbol::new(1)); + // dog is still at zero + assert_eq!(i.intern("dog"), Symbol::new(0)); +} + +#[test] +fn interner_symbols() { + create_session_globals_then(|| { + let symbol1 = Symbol::intern("test_str_1"); + let symbol2 = Symbol::intern("test_str_2"); + assert_eq!(symbol1.as_str(), "test_str_1"); + assert_eq!(symbol2.as_str(), "test_str_2"); + assert_eq!(symbol2.as_u32(), symbol1.as_u32() + 1); + }); +} diff --git a/kclvm/src/lib.rs b/kclvm/src/lib.rs new file mode 100644 index 000000000..21ec384dc --- /dev/null +++ b/kclvm/src/lib.rs @@ -0,0 +1,178 @@ +extern crate serde; + +use std::collections::HashMap; +use std::path::{Path, PathBuf}; +use std::sync::mpsc::channel; +use threadpool::ThreadPool; + +use indexmap::IndexMap; +use kclvm_ast::ast; +use kclvm_compiler::codegen::{llvm::emit_code, EmitOptions}; +use kclvm_config::cache::*; +use kclvm_parser::load_program; +use kclvm_sema::resolver::resolve_program; + +use kclvm_runner::command::Command; +use kclvm_runner::runner::*; +use kclvm_tools::query::apply_overrides; + +#[no_mangle] +pub extern "C" fn kclvm_cli_run(args: *const i8, plugin_agent: *const i8) -> *const i8 { + let args = ExecProgramArgs::from_str(kclvm::c2str(args)); + let plugin_agent = plugin_agent as u64; + + let files = args.get_files(); + let opts = args.get_load_program_options(); + + // load ast + let mut program = load_program(&files, Some(opts)); + apply_overrides(&mut program, &args.overrides, &[]); + let scope = resolve_program(&mut program); + + // gen bc or ll file + let ll_file = "_a.out"; + let path = std::path::Path::new(ll_file); + if path.exists() { + std::fs::remove_file(path).unwrap(); + } + for entry in glob::glob(&format!("{}*.ll", ll_file)).unwrap() { + match entry { + Ok(path) => { + if path.exists() { + std::fs::remove_file(path).unwrap(); + } + } + Err(e) => println!("{:?}", e), + }; + } + + let cache_dir = Path::new(&program.root) + .join(".kclvm") + .join("cache") + .join(kclvm_version::get_full_version()); + if !cache_dir.exists() { + std::fs::create_dir_all(&cache_dir).unwrap(); + } + let mut compile_progs: IndexMap< + String, + ( + ast::Program, + IndexMap>, + PathBuf, + ), + > = IndexMap::default(); + for (pkgpath, modules) in program.pkgs { + let mut pkgs = HashMap::new(); + pkgs.insert(pkgpath.clone(), modules); + let compile_prog = ast::Program { + root: program.root.clone(), + main: program.main.clone(), + pkgs, + cmd_args: vec![], + cmd_overrides: vec![], + }; + compile_progs.insert( + pkgpath, + (compile_prog, scope.import_names.clone(), cache_dir.clone()), + ); + } + let pool = ThreadPool::new(4); + let (tx, rx) = channel(); + let prog_count = compile_progs.len(); + for (pkgpath, (compile_prog, import_names, cache_dir)) in compile_progs { + let tx = tx.clone(); + pool.execute(move || { + let root = &compile_prog.root; + let is_main_pkg = pkgpath == kclvm_ast::MAIN_PKG; + let file = if is_main_pkg { + PathBuf::from(&pkgpath) + } else { + cache_dir.join(&pkgpath) + }; + let ll_file = file.to_str().unwrap(); + let ll_path = format!("{}.ll", ll_file); + let dylib_path = format!("{}{}", ll_file, Command::get_lib_suffix()); + let mut ll_path_lock = fslock::LockFile::open(&format!("{}.lock", ll_path)).unwrap(); + ll_path_lock.lock().unwrap(); + if Path::new(&ll_path).exists() { + std::fs::remove_file(&ll_path).unwrap(); + } + let dylib_path = if is_main_pkg { + emit_code( + &compile_prog, + import_names, + &EmitOptions { + from_path: None, + emit_path: Some(&ll_file), + no_link: true, + }, + ) + .expect("Compile KCL to LLVM error"); + let mut cmd = Command::new(plugin_agent); + cmd.run_clang_single(&ll_path, &dylib_path) + } else { + // If AST module has been modified, ignore the dylib cache + let dylib_relative_path: Option = + load_pkg_cache(root, &pkgpath, CacheOption::default()); + match dylib_relative_path { + Some(dylib_relative_path) => { + if dylib_relative_path.starts_with('.') { + dylib_relative_path.replacen(".", root, 1) + } else { + dylib_relative_path + } + } + None => { + emit_code( + &compile_prog, + import_names, + &EmitOptions { + from_path: None, + emit_path: Some(&ll_file), + no_link: true, + }, + ) + .expect("Compile KCL to LLVM error"); + let mut cmd = Command::new(plugin_agent); + let dylib_path = cmd.run_clang_single(&ll_path, &dylib_path); + let dylib_relative_path = dylib_path.replacen(root, ".", 1); + + save_pkg_cache(root, &pkgpath, dylib_relative_path, CacheOption::default()); + dylib_path + } + } + }; + if Path::new(&ll_path).exists() { + std::fs::remove_file(&ll_path).unwrap(); + } + ll_path_lock.unlock().unwrap(); + tx.send(dylib_path).expect("channel will be there waiting for the pool"); + }); + } + let dylib_paths = rx.iter().take(prog_count).collect::>(); + let mut cmd = Command::new(plugin_agent); + // link all dylibs + let dylib_path = cmd.link_dylibs(&dylib_paths, ""); + + // Config uild + // run dylib + let runner = KclvmRunner::new( + dylib_path.as_str(), + Some(KclvmRunnerOptions { + plugin_agent_ptr: plugin_agent, + }), + ); + match runner.run(&args) { + Ok(result) => { + let c_string = std::ffi::CString::new(result.as_str()).expect("CString::new failed"); + let ptr = c_string.into_raw(); + ptr as *const i8 + } + Err(result) => { + let result = format!("ERROR:{}", result); + let c_string = std::ffi::CString::new(result.as_str()).expect("CString::new failed"); + let ptr = c_string.into_raw(); + ptr as *const i8 + } + } +} diff --git a/kclvm/src/main.rs b/kclvm/src/main.rs new file mode 100644 index 000000000..6efbd39fa --- /dev/null +++ b/kclvm/src/main.rs @@ -0,0 +1,223 @@ +//! The `kclvm` command-line interface. + +#[macro_use] +extern crate clap; + +use indexmap::IndexMap; +use std::path::PathBuf; +use std::thread; +use std::{collections::HashMap, path::Path}; + +use clap::ArgMatches; +use kclvm_ast::ast; +use kclvm_compiler::codegen::{llvm::emit_code, EmitOptions}; +use kclvm_config::cache::*; +use kclvm_config::settings::{load_file, merge_settings, SettingsFile}; +use kclvm_parser::{load_program, parse_file}; +use kclvm_runner::command::Command; +use kclvm_sema::resolver::resolve_program; + +fn main() { + let matches = clap_app!(kclx => + (@subcommand run => + (@arg INPUT: ... "Sets the input file to use") + (@arg OUTPUT: -o --output +takes_value "Sets the LLVM IR/BC output file path") + (@arg SETTING: ... -Y --setting "Sets the input file to use") + (@arg EMIT_TYPE: --emit +takes_value "Sets the emit type, expect (ast)") + (@arg BC_PATH: --bc +takes_value "Sets the linked LLVM bitcode file path") + (@arg verbose: -v --verbose "Print test information verbosely") + (@arg disable_none: -n --disable-none "Disable dumping None values") + (@arg debug: -d --debug "Run in debug mode (for developers only)") + (@arg sort_key: -k --sort "Sort result keys") + (@arg ARGUMENT: ... -D --argument "Specify the top-level argument") + ) + ) + .get_matches(); + if let Some(matches) = matches.subcommand_matches("run") { + if let Some(files) = matches.values_of("INPUT") { + let files: Vec<&str> = files.into_iter().collect::>(); + if let Some(emit_ty) = matches.value_of("EMIT_TYPE") { + if emit_ty == "ast" { + let module = parse_file(files[0], None); + println!("{}", serde_json::to_string(&module).unwrap()) + } + } else { + // load ast + let mut program = load_program(&files, None); + let scope = resolve_program(&mut program); + + // gen bc or ll file + let ll_file = "_a.out"; + let path = std::path::Path::new(ll_file); + if path.exists() { + std::fs::remove_file(path).unwrap(); + } + for entry in glob::glob(&format!("{}*.ll", ll_file)).unwrap() { + match entry { + Ok(path) => { + if path.exists() { + std::fs::remove_file(path).unwrap(); + } + } + Err(e) => println!("{:?}", e), + }; + } + + let cache_dir = Path::new(&program.root) + .join(".kclvm") + .join("cache") + .join(kclvm_version::get_full_version()); + if !cache_dir.exists() { + std::fs::create_dir_all(&cache_dir).unwrap(); + } + let mut compile_progs: IndexMap< + String, + ( + ast::Program, + IndexMap>, + PathBuf, + ), + > = IndexMap::default(); + + for (pkgpath, modules) in program.pkgs { + let mut pkgs = HashMap::new(); + pkgs.insert(pkgpath.clone(), modules); + let compile_prog = ast::Program { + root: program.root.clone(), + main: program.main.clone(), + pkgs, + cmd_args: vec![], + cmd_overrides: vec![], + }; + compile_progs.insert( + pkgpath, + (compile_prog, scope.import_names.clone(), cache_dir.clone()), + ); + } + let mut theads = vec![]; + for (pkgpath, (compile_prog, import_names, cache_dir)) in compile_progs { + let t = thread::spawn(move || { + let root = &compile_prog.root; + let is_main_pkg = pkgpath == kclvm_ast::MAIN_PKG; + let file = if is_main_pkg { + let main_file = + format!("{}{}", pkgpath, chrono::Local::now().timestamp_nanos()); + cache_dir.join(&main_file) + } else { + cache_dir.join(&pkgpath) + }; + let lock_file = + format!("{}.lock", cache_dir.join(&pkgpath).to_str().unwrap()); + let ll_file = file.to_str().unwrap(); + let ll_path = format!("{}.ll", ll_file); + let dylib_path = format!("{}{}", ll_file, Command::get_lib_suffix()); + let mut ll_path_lock = fslock::LockFile::open(&lock_file).unwrap(); + ll_path_lock.lock().unwrap(); + if Path::new(&ll_path).exists() { + std::fs::remove_file(&ll_path).unwrap(); + } + let dylib_path = if is_main_pkg { + emit_code( + &compile_prog, + import_names, + &EmitOptions { + from_path: None, + emit_path: Some(&ll_file), + no_link: true, + }, + ) + .expect("Compile KCL to LLVM error"); + let mut cmd = Command::new(0); + cmd.run_clang_single(&ll_path, &dylib_path) + } else { + // If AST module has been modified, ignore the dylib cache + let dylib_relative_path: Option = + load_pkg_cache(root, &pkgpath, CacheOption::default()); + match dylib_relative_path { + Some(dylib_relative_path) => { + if dylib_relative_path.starts_with('.') { + dylib_relative_path.replacen(".", root, 1) + } else { + dylib_relative_path + } + } + None => { + emit_code( + &compile_prog, + import_names, + &EmitOptions { + from_path: None, + emit_path: Some(&ll_file), + no_link: true, + }, + ) + .expect("Compile KCL to LLVM error"); + let mut cmd = Command::new(0); + let dylib_path = cmd.run_clang_single(&ll_path, &dylib_path); + let dylib_relative_path = dylib_path.replacen(root, ".", 1); + + save_pkg_cache( + root, + &pkgpath, + dylib_relative_path, + CacheOption::default(), + ); + dylib_path + } + } + }; + if Path::new(&ll_path).exists() { + std::fs::remove_file(&ll_path).unwrap(); + } + ll_path_lock.unlock().unwrap(); + dylib_path + }); + theads.push(t); + } + let mut dylib_paths = vec![]; + for t in theads { + let dylib_path = t.join().unwrap(); + dylib_paths.push(dylib_path); + } + let mut cmd = Command::new(0); + // link all dylibs + let dylib_path = cmd.link_dylibs(&dylib_paths, ""); + // Config build + let settings = build_settings(&matches); + cmd.run_dylib_with_settings(&dylib_path, settings).unwrap(); + for dylib_path in dylib_paths { + if dylib_path.contains(kclvm_ast::MAIN_PKG) && Path::new(&dylib_path).exists() { + std::fs::remove_file(&dylib_path).unwrap(); + } + } + } + } else { + println!("{}", matches.usage()); + } + } else { + println!("{}", matches.usage()); + } +} + +/// Build settings from arg matches. +fn build_settings(matches: &ArgMatches) -> SettingsFile { + let debug_mode = matches.occurrences_of("debug") > 0; + let disable_none = matches.occurrences_of("disable_none") > 0; + + let mut settings = if let Some(files) = matches.values_of("SETTING") { + let files: Vec<&str> = files.into_iter().collect::>(); + merge_settings( + &files + .iter() + .map(|f| load_file(f)) + .collect::>(), + ) + } else { + SettingsFile::new() + }; + if let Some(config) = &mut settings.kcl_cli_configs { + config.debug = Some(debug_mode); + config.disable_none = Some(disable_none); + } + settings +} diff --git a/kclvm/tests/integration/grammar/test_grammar.py b/kclvm/tests/integration/grammar/test_grammar.py new file mode 100644 index 000000000..bdbe5f4c9 --- /dev/null +++ b/kclvm/tests/integration/grammar/test_grammar.py @@ -0,0 +1,154 @@ +"""This is a scripts to run KCL grammar test cases with the native target""" +import pytest +import os +import subprocess +import re +import yaml +import pathlib +from ruamel.yaml import YAML + +TEST_FILE = "main.k" +STDOUT_GOLDEN = "stdout.golden" +STDERR_GOLDEN = "stderr.golden" +STDOUT_GOLDEN_PY = "stdout.golden.py" +STDERR_GOLDEN_PY = "stderr.golden.py" +SETTINGS_FILE = "settings.yaml" +TEST_PATH = "test/grammar" + +# Ruamel YAML instance +ruamel_yaml = YAML(typ="unsafe", pure=True) +# Convert None to null +ruamel_yaml.representer.add_representer( + type(None), + lambda dumper, data: dumper.represent_scalar(u"tag:yaml.org,2002:null", u"null"), +) + + +def find_test_dirs(path, category): + result = [] + for root, dirs, files in os.walk(path + category): + for name in files: + if name == "main.k": + result.append(root) + return result + + +def compare_strings(result_strings: list, golden_strings: list): + result = "\n".join(result_strings) + golden_result = "\n".join(golden_strings) + result_yaml_list = [r for r in list(ruamel_yaml.load_all(result)) if r] + golden_yaml_list = [r for r in list(ruamel_yaml.load_all(golden_result)) if r] + assert result_yaml_list == golden_yaml_list + + +def compare_results(result, golden_result): + """Convert bytestring (result) and list of strings (golden_lines) both to + list of strings with line ending stripped, then compare.""" + + result_strings = result.decode().split("\n") + golden_strings = golden_result.decode().split("\n") + assert result_strings == golden_strings + + +def compare_results_with_lines(result, golden_lines): + """Convert bytestring (result) and list of strings (golden_lines) both to + list of strings with line ending stripped, then compare. + """ + + result_strings = result.decode().split("\n") + golden_strings = [] + for line in golden_lines: + clean_line = re.sub("\n$", "", line) + golden_strings.append(clean_line) + # List generated by split() has an ending empty string, when the '\n' is + # the last character + assert result_strings[-1] == "", "The result string does not end with a NEWLINE" + golden_strings.append("") + compare_strings(result_strings, golden_strings) + + +def generate_golden_file(py_file_name): + if os.path.isfile(py_file_name): + try: + process = subprocess.Popen( + ["kclvm", py_file_name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=dict(os.environ), + ) + stdout, stderr = process.communicate() + assert ( + process.returncode == 0 + ), "Error executing file {}, exit code = {}".format( + py_file_name, process.returncode + ) + except Exception: + raise + return stdout + return None + + +def read_settings_file(settings_file_name): + if os.path.isfile(settings_file_name): + try: + with open(settings_file_name, "r") as stream: + settings = yaml.safe_load(stream) + except Exception: + raise + return settings + return None + + +print("##### K Language Grammar Test Suite #####") +test_path = pathlib.Path(__file__).parent.parent.parent.parent.parent.joinpath( + TEST_PATH +) +test_dirs = find_test_dirs(str(test_path), "") + + +@pytest.mark.parametrize("test_dir", test_dirs) +def test_grammar(test_dir): + print("Testing {}".format(test_dir)) + test_settings = read_settings_file(os.path.join(test_dir, SETTINGS_FILE)) + kcl_command = ["kcl", "--target", "native", TEST_FILE] + if test_settings and test_settings["kcl_options"]: + kcl_command.extend(test_settings["kcl_options"].split()) + process = subprocess.Popen( + kcl_command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=os.path.abspath(test_dir), + env=dict(os.environ), + ) + stdout, stderr = process.communicate() + print("STDOUT:\n{}".format(stdout.decode())) + print("STDERR:\n{}".format(stderr.decode())) + RETURN_CODE = 0 + KCLVM_OUTPUT = 1 + GOLDEN_FILE = 2 + GOLDEN_FILE_SCRIPT = 3 + settings = { + "stdout": (None, stdout, STDOUT_GOLDEN, STDOUT_GOLDEN_PY), + "stderr": (1, stderr, STDERR_GOLDEN, STDERR_GOLDEN_PY), + } + for _, setting in settings.items(): + # Attempt to generate a golden stdout. + golden_file_result = generate_golden_file( + os.path.join(test_dir, setting[GOLDEN_FILE_SCRIPT]) + ) + if golden_file_result: + compare_results(setting[KCLVM_OUTPUT], golden_file_result) + else: + # Attempt to use existing golden stdout. + try: + with open( + os.path.join(test_dir, setting[GOLDEN_FILE]), "r" + ) as golden_file: + compare_results_with_lines(setting[KCLVM_OUTPUT], golden_file) + if setting[RETURN_CODE] is not None: + assert process.returncode == setting[RETURN_CODE] + except OSError: + # Ignore when a golden file does not exist. + pass + except Exception: + raise diff --git a/kclvm/tests/test_units/runtime/base64/test_base64.py b/kclvm/tests/test_units/runtime/base64/test_base64.py new file mode 100644 index 000000000..afd1a0fc5 --- /dev/null +++ b/kclvm/tests/test_units/runtime/base64/test_base64.py @@ -0,0 +1,107 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing +import unittest + +import kclvm_runtime + +# https://github.com/python/cpython/blob/main/Lib/test/test_base64.py + +# encode(value: str, encoding: str = "utf-8") -> str +# decode(value: str, encoding: str = "utf-8") -> str + +# kclvm_base64_encode +# kclvm_base64_decode + +_Dylib = kclvm_runtime.KclvmRuntimeDylib() + + +class kclx_Base64: + def __init__(self, dylib_=None): + self.dylib = dylib_ if dylib_ else _Dylib + + def encodebytes(self, value: bytes) -> str: + return self.encode(str(value, "utf-8")) + + def decodebytes(self, value: bytes) -> str: + return self.decode(str(value, "utf-8")) + + def encode(self, value: str) -> str: + return self.dylib.Invoke(f"base64.encode", value) + "\n" + + def decode(self, value: str) -> str: + return self.dylib.Invoke(f"base64.decode", value) + + +base64 = kclx_Base64(_Dylib) + + +class BaseTest(unittest.TestCase): + def _strip(delf, s: str) -> str: + s = s.replace("\t", "") + s = s.replace("\n", "") + s = s.replace(" ", "") + s = s.strip() + + def checkequal(self, expect, got): + if isinstance(expect, (bytes, bytearray)): + expect = str(expect, "utf8") + if isinstance(got, (bytes, bytearray)): + got = str(got, "utf8") + + expect = expect.strip() + got = got.strip() + + expect = self._strip(expect) + got = self._strip(got) + + self.assertEqual(expect, got) + + def test_encodebytes(self): + eq = self.checkequal + eq(base64.encodebytes(b"www.python.org"), b"d3d3LnB5dGhvbi5vcmc=\n") + eq(base64.encodebytes(b"a"), b"YQ==\n") + eq(base64.encodebytes(b"ab"), b"YWI=\n") + eq(base64.encodebytes(b"abc"), b"YWJj\n") + eq(base64.encodebytes(b""), b"") + eq( + base64.encodebytes( + b"abcdefghijklmnopqrstuvwxyz" + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + b"0123456789!@#0^&*();:<>,. []{}" + ), + b"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNE" + b"RUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0\nNT" + b"Y3ODkhQCMwXiYqKCk7Ojw+LC4gW117fQ==\n", + ) + # Non-bytes + eq(base64.encodebytes(bytearray(b"abc")), b"YWJj\n") + eq(base64.encodebytes(memoryview(b"abc")), b"YWJj\n") + # eq(base64.encodebytes(array("B", b"abc")), b"YWJj\n") + + def test_decodebytes(self): + eq = self.checkequal + eq(base64.decodebytes(b"d3d3LnB5dGhvbi5vcmc="), b"www.python.org") + eq(base64.decodebytes(b"YQ=="), b"a") + eq(base64.decodebytes(b"YWI="), b"ab") + eq(base64.decodebytes(b"YWJj"), b"abc") + eq( + base64.decodebytes( + b"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNE" + b"RUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NT" + b"Y3ODkhQCMwXiYqKCk7Ojw+LC4gW117fQ==" + ), + b"abcdefghijklmnopqrstuvwxyz" + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + b"0123456789!@#0^&*();:<>,. []{}", + ) + eq(base64.decodebytes(b""), b"") + # Non-bytes + eq(base64.decodebytes(bytearray(b"YWJj")), b"abc") + # eq(base64.decodebytes(memoryview(b"YWJj\n")), b"abc") + # eq(base64.decodebytes(array('B', b'YWJj\n')), b'abc') + # self.check_type_errors(base64.decodebytes) + + +if __name__ == "__main__": + unittest.main() diff --git a/kclvm/tests/test_units/runtime/crypto/test_crypto.py b/kclvm/tests/test_units/runtime/crypto/test_crypto.py new file mode 100644 index 000000000..bf467a410 --- /dev/null +++ b/kclvm/tests/test_units/runtime/crypto/test_crypto.py @@ -0,0 +1,83 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing +import unittest + +import kclvm_runtime + +# md5(value: str, encoding: str = "utf-8") -> str +# sha1(value: str, encoding: str = "utf-8") -> str +# sha224(value: str, encoding: str = "utf-8") -> str +# sha256(value: str, encoding: str = "utf-8") -> str +# sha384(value: str, encoding: str = "utf-8") -> str +# sha512(value: str, encoding: str = "utf-8") -> str + +_Dylib = kclvm_runtime.KclvmRuntimeDylib() + + +class BaseTest(unittest.TestCase): + def __init__(self, methodName="runTest"): + super().__init__(methodName) + self.dylib = _Dylib + + def md5(self, value: str) -> str: + return self.dylib.Invoke(f"crypto.md5", value) + + def sha1(self, value: str) -> str: + return self.dylib.Invoke(f"crypto.sha1", value) + + def sha224(self, value: str) -> str: + return self.dylib.Invoke(f"crypto.sha224", value) + + def sha256(self, value: str) -> str: + return self.dylib.Invoke(f"crypto.sha256", value) + + def sha384(self, value: str) -> str: + return self.dylib.Invoke(f"crypto.sha384", value) + + def sha512(self, value: str) -> str: + return self.dylib.Invoke(f"crypto.sha512", value) + + def test_md5(self): + self.assertEqual( + self.md5("The quick brown fox jumps over the lazy dog"), + "9e107d9d372bb6826bd81d3542a419d6", + ) + self.assertEqual( + self.md5("The quick brown fox jumps over the lazy cog"), + "1055d3e698d289f2af8663725127bd4b", + ) + self.assertEqual( + self.md5(""), + "d41d8cd98f00b204e9800998ecf8427e", + ) + + def test_sha1(self): + self.assertEqual(self.sha1(""), "da39a3ee5e6b4b0d3255bfef95601890afd80709") + + def test_sha224(self): + self.assertEqual( + self.sha224(""), "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" + ) + + def test_sha256(self): + self.assertEqual( + self.sha256(""), + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + ) + + def test_sha384(self): + self.assertEqual( + self.sha384(""), + "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", + ) + + def test_sha512(self): + self.assertEqual( + self.sha512(""), + "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/kclvm/tests/test_units/runtime/datetime/test_datetime.py b/kclvm/tests/test_units/runtime/datetime/test_datetime.py new file mode 100644 index 000000000..d63c92d29 --- /dev/null +++ b/kclvm/tests/test_units/runtime/datetime/test_datetime.py @@ -0,0 +1,24 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing +import unittest + +import kclvm_runtime + +# https://github.com/python/cpython/blob/main/Lib/test + +_Dylib = kclvm_runtime.KclvmRuntimeDylib() + + +class kclx_Crypto: + def __init__(self, dylib_=None): + self.dylib = dylib_ if dylib_ else _Dylib + + +class BaseTest(unittest.TestCase): + def test_foo(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/kclvm/tests/test_units/runtime/json/test_json.py b/kclvm/tests/test_units/runtime/json/test_json.py new file mode 100644 index 000000000..e847cac7c --- /dev/null +++ b/kclvm/tests/test_units/runtime/json/test_json.py @@ -0,0 +1,24 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing +import unittest + +import kclvm_runtime + +# https://github.com/python/cpython/blob/main/Lib/test + +_Dylib = kclvm_runtime.KclvmRuntimeDylib() + + +class kclx_Json: + def __init__(self, dylib_=None): + self.dylib = dylib_ if dylib_ else _Dylib + + +class BaseTest(unittest.TestCase): + def test_foo(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/kclvm/tests/test_units/runtime/math/test_math.py b/kclvm/tests/test_units/runtime/math/test_math.py new file mode 100644 index 000000000..344ee51e9 --- /dev/null +++ b/kclvm/tests/test_units/runtime/math/test_math.py @@ -0,0 +1,527 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import sys +import typing +import unittest +import math as pymath +import struct + +import kclvm_runtime + +# https://github.com/python/cpython/blob/main/Lib/test/test_math.py + + +_Dylib = kclvm_runtime.KclvmRuntimeDylib() + + +eps = 1e-05 +NAN = float("nan") +INF = float("inf") +NINF = float("-inf") +FLOAT_MAX = sys.float_info.max +FLOAT_MIN = sys.float_info.min + + +class kclx_Math: + def __init__(self, dylib_=None): + self.dylib = dylib_ if dylib_ else _Dylib + + def ceil(self, x) -> int: + return self.dylib.Invoke(f"math.ceil", x) + + def factorial(self, x) -> int: + return self.dylib.Invoke(f"math.factorial", x) + + def floor(self, x) -> int: + return self.dylib.Invoke(f"math.floor", x) + + def gcd(self, a: int, b: int) -> int: + return self.dylib.Invoke(f"math.gcd", a, b) + + def isfinite(self, x) -> bool: + return self.dylib.Invoke(f"math.isfinite", x) + + def isinf(self, x) -> bool: + return self.dylib.Invoke(f"math.isinf", x) + + def isnan(self, x) -> bool: + return self.dylib.Invoke(f"math.isnan", x) + + def modf(self, x) -> typing.Tuple[float, float]: + return self.dylib.Invoke(f"math.modf", x) + + def exp(self, x) -> float: + return self.dylib.Invoke(f"math.exp", x) + + def expm1(self, x) -> float: + return self.dylib.Invoke(f"math.expm1", x) + + def log(self, x) -> float: + return self.dylib.Invoke(f"math.log", x) + + def log1p(self, x) -> float: + return self.dylib.Invoke(f"math.log1p", x) + + def log2(self, x) -> float: + return self.dylib.Invoke(f"math.log2", x) + + def log10(self, x) -> float: + return self.dylib.Invoke(f"math.log10", x) + + def pow(self, x, y) -> float: + return self.dylib.Invoke(f"math.pow", x, y) + + def sqrt(self, x) -> float: + return self.dylib.Invoke(f"math.sqrt", x) + + +math = kclx_Math() + + +def to_ulps(x): + """Convert a non-NaN float x to an integer, in such a way that + adjacent floats are converted to adjacent integers. Then + abs(ulps(x) - ulps(y)) gives the difference in ulps between two + floats. + The results from this function will only make sense on platforms + where native doubles are represented in IEEE 754 binary64 format. + Note: 0.0 and -0.0 are converted to 0 and -1, respectively. + """ + n = struct.unpack("> 1 + if not numfactors: + return 1 + elif numfactors == 1: + return start + else: + mid = (start + numfactors) | 1 + return partial_product(start, mid) * partial_product(mid, stop) + + +def py_factorial(n): + """Factorial of nonnegative integer n, via "Binary Split Factorial Formula" + described at http://www.luschny.de/math/factorial/binarysplitfact.html + """ + inner = outer = 1 + for i in reversed(range(n.bit_length())): + inner *= partial_product((n >> i + 1) + 1 | 1, (n >> i) + 1 | 1) + outer *= inner + return outer << (n - count_set_bits(n)) + + +def result_check(expected, got, ulp_tol=5, abs_tol=0.0): + # Common logic of MathTests.(ftest, test_testcases, test_mtestcases) + """Compare arguments expected and got, as floats, if either + is a float, using a tolerance expressed in multiples of + ulp(expected) or absolutely (if given and greater). + As a convenience, when neither argument is a float, and for + non-finite floats, exact equality is demanded. Also, nan==nan + as far as this function is concerned. + Returns None on success and an error message on failure. + """ + + # Check exactly equal (applies also to strings representing exceptions) + if got == expected: + return None + + failure = "not equal" + + # Turn mixed float and int comparison (e.g. floor()) to all-float + if isinstance(expected, float) and isinstance(got, int): + got = float(got) + elif isinstance(got, float) and isinstance(expected, int): + expected = float(expected) + + if isinstance(expected, float) and isinstance(got, float): + if math.isnan(expected) and math.isnan(got): + # Pass, since both nan + failure = None + elif math.isinf(expected) or math.isinf(got): + # We already know they're not equal, drop through to failure + pass + else: + # Both are finite floats (now). Are they close enough? + failure = ulp_abs_check(expected, got, ulp_tol, abs_tol) + + # arguments are not equal, and if numeric, are too far apart + if failure is not None: + fail_fmt = "expected {!r}, got {!r}" + fail_msg = fail_fmt.format(expected, got) + fail_msg += " ({})".format(failure) + return fail_msg + else: + return None + + +def ulp_abs_check(expected, got, ulp_tol, abs_tol): + """Given finite floats `expected` and `got`, check that they're + approximately equal to within the given number of ulps or the + given absolute tolerance, whichever is bigger. + Returns None on success and an error message on failure. + """ + ulp_error = abs(to_ulps(expected) - to_ulps(got)) + abs_error = abs(expected - got) + + # Succeed if either abs_error <= abs_tol or ulp_error <= ulp_tol. + if abs_error <= abs_tol or ulp_error <= ulp_tol: + return None + else: + fmt = "error = {:.3g} ({:d} ulps); " "permitted error = {:.3g} or {:d} ulps" + return fmt.format(abs_error, ulp_error, abs_tol, ulp_tol) + + +def result_check(expected, got, ulp_tol=5, abs_tol=0.0): + # Common logic of MathTests.(ftest, test_testcases, test_mtestcases) + """Compare arguments expected and got, as floats, if either + is a float, using a tolerance expressed in multiples of + ulp(expected) or absolutely (if given and greater). + As a convenience, when neither argument is a float, and for + non-finite floats, exact equality is demanded. Also, nan==nan + as far as this function is concerned. + Returns None on success and an error message on failure. + """ + + # Check exactly equal (applies also to strings representing exceptions) + if got == expected: + return None + + failure = "not equal" + + # Turn mixed float and int comparison (e.g. floor()) to all-float + if isinstance(expected, float) and isinstance(got, int): + got = float(got) + elif isinstance(got, float) and isinstance(expected, int): + expected = float(expected) + + if isinstance(expected, float) and isinstance(got, float): + if math.isnan(expected) and math.isnan(got): + # Pass, since both nan + failure = None + elif math.isinf(expected) or math.isinf(got): + # We already know they're not equal, drop through to failure + pass + else: + # Both are finite floats (now). Are they close enough? + failure = ulp_abs_check(expected, got, ulp_tol, abs_tol) + + # arguments are not equal, and if numeric, are too far apart + if failure is not None: + fail_fmt = "expected {!r}, got {!r}" + fail_msg = fail_fmt.format(expected, got) + fail_msg += " ({})".format(failure) + return fail_msg + else: + return None + + +class BaseTest(unittest.TestCase): + def ftest(self, name, got, expected, ulp_tol=5, abs_tol=0.0): + """Compare arguments expected and got, as floats, if either + is a float, using a tolerance expressed in multiples of + ulp(expected) or absolutely, whichever is greater. + As a convenience, when neither argument is a float, and for + non-finite floats, exact equality is demanded. Also, nan==nan + in this function. + """ + failure = result_check(expected, got, ulp_tol, abs_tol) + if failure is not None: + self.fail("{}: {}".format(name, failure)) + + def testCeil(self): + # self.assertRaises(TypeError, math.ceil) + self.assertEqual(int, type(math.ceil(0.5))) + self.assertEqual(math.ceil(0.5), 1) + self.assertEqual(math.ceil(1.0), 1) + self.assertEqual(math.ceil(1.5), 2) + self.assertEqual(math.ceil(-0.5), 0) + self.assertEqual(math.ceil(-1.0), -1) + self.assertEqual(math.ceil(-1.5), -1) + self.assertEqual(math.ceil(0.0), 0) + self.assertEqual(math.ceil(-0.0), 0) + + # self.assertEqual(math.ceil(INF), INF) + # self.assertEqual(math.ceil(NINF), NINF) + # self.assertTrue(math.isnan(math.ceil(NAN))) + + def testExp(self): + # self.assertRaises(TypeError, math.exp) + self.ftest("exp(-1)", math.exp(-1), 1 / pymath.e) + self.ftest("exp(0)", math.exp(0), 1) + self.ftest("exp(1)", math.exp(1), pymath.e) + # self.assertEqual(math.exp(INF), INF) + # self.assertEqual(math.exp(NINF), 0.0) + # self.assertTrue(math.isnan(math.exp(NAN))) + # self.assertRaises(OverflowError, math.exp, 1000000) + + def testFactorial(self): + self.assertEqual(math.factorial(0), 1) + total = 1 + for i in range(1, 20): + total *= i + self.assertEqual(math.factorial(i), total) + self.assertEqual(math.factorial(i), py_factorial(i)) + # self.assertRaises(ValueError, math.factorial, -1) + # self.assertRaises(ValueError, math.factorial, -10**100) + + def testFloor(self): + self.assertEqual(math.floor(0.5), 0) + self.assertEqual(math.floor(1.0), 1) + self.assertEqual(math.floor(1.5), 1) + self.assertEqual(math.floor(-0.5), -1) + self.assertEqual(math.floor(-1.0), -1) + self.assertEqual(math.floor(-1.5), -2) + # self.assertEqual(math.ceil(INF), INF) + # self.assertEqual(math.ceil(NINF), NINF) + # self.assertTrue(math.isnan(math.floor(NAN))) + + def testGcd(self): + gcd = math.gcd + self.assertEqual(gcd(0, 0), 0) + self.assertEqual(gcd(1, 0), 1) + self.assertEqual(gcd(-1, 0), 1) + self.assertEqual(gcd(0, 1), 1) + self.assertEqual(gcd(0, -1), 1) + self.assertEqual(gcd(7, 1), 1) + self.assertEqual(gcd(7, -1), 1) + self.assertEqual(gcd(-23, 15), 1) + self.assertEqual(gcd(120, 84), 12) + self.assertEqual(gcd(84, -120), 12) + + def testLog(self): + # self.assertRaises(TypeError, math.log) + self.ftest("log(1/e)", math.log(1 / pymath.e), -1) + self.ftest("log(1)", math.log(1), 0) + self.ftest("log(e)", math.log(pymath.e), 1) + # self.ftest("log(32,2)", math.log(32, 2), 5) + # self.ftest("log(10**40, 10)", math.log(10 ** 40, 10), 40) + # self.ftest("log(10**40, 10**20)", math.log(10 ** 40, 10 ** 20), 2) + # self.ftest("log(10**1000)", math.log(10 ** 1000), 2302.5850929940457) + # self.assertRaises(ValueError, math.log, -1.5) + # self.assertRaises(ValueError, math.log, -10**1000) + # self.assertRaises(ValueError, math.log, NINF) + # self.assertEqual(math.log(INF), INF) + # self.assertTrue(math.isnan(math.log(NAN))) + + def testLog2(self): + # self.assertRaises(TypeError, math.log2) + + # Check some integer values + self.assertEqual(math.log2(1), 0.0) + self.assertEqual(math.log2(2), 1.0) + self.assertEqual(math.log2(4), 2.0) + + # Large integer values + # self.assertEqual(math.log2(2**1023), 1023.0) + # self.assertEqual(math.log2(2**1024), 1024.0) + # self.assertEqual(math.log2(2**2000), 2000.0) + + # self.assertRaises(ValueError, math.log2, -1.5) + # self.assertRaises(ValueError, math.log2, NINF) + # self.assertTrue(math.isnan(math.log2(NAN))) + + def testPow(self): + # self.assertRaises(TypeError, math.pow) + self.ftest("pow(0,1)", math.pow(0, 1), 0) + self.ftest("pow(1,0)", math.pow(1, 0), 1) + self.ftest("pow(2,1)", math.pow(2, 1), 2) + self.ftest("pow(2,-1)", math.pow(2, -1), 0.5) + # self.assertEqual(math.pow(INF, 1), INF) + # self.assertEqual(math.pow(NINF, 1), NINF) + # self.assertEqual((math.pow(1, INF)), 1.0) + # self.assertEqual((math.pow(1, NINF)), 1.0) + # self.assertTrue(math.isnan(math.pow(NAN, 1))) + # self.assertTrue(math.isnan(math.pow(2, NAN))) + # self.assertTrue(math.isnan(math.pow(0, NAN))) + # self.assertEqual(math.pow(1, NAN), 1) + + # pow(0., x) + # self.assertEqual(math.pow(0.0, INF), 0.0) + self.assertEqual(math.pow(0.0, 3.0), 0.0) + self.assertEqual(math.pow(0.0, 2.3), 0.0) + self.assertEqual(math.pow(0.0, 2.0), 0.0) + self.assertEqual(math.pow(0.0, 0.0), 1.0) + self.assertEqual(math.pow(0.0, -0.0), 1.0) + # self.assertRaises(ValueError, math.pow, 0.0, -2.0) + # self.assertRaises(ValueError, math.pow, 0.0, -2.3) + # self.assertRaises(ValueError, math.pow, 0.0, -3.0) + # self.assertEqual(math.pow(0.0, NINF), INF) + # self.assertTrue(math.isnan(math.pow(0.0, NAN))) + + # pow(INF, x) + # self.assertEqual(math.pow(INF, INF), INF) + # self.assertEqual(math.pow(INF, 3.0), INF) + # self.assertEqual(math.pow(INF, 2.3), INF) + # self.assertEqual(math.pow(INF, 2.0), INF) + # self.assertEqual(math.pow(INF, 0.0), 1.0) + # self.assertEqual(math.pow(INF, -0.0), 1.0) + # self.assertEqual(math.pow(INF, -2.0), 0.0) + # self.assertEqual(math.pow(INF, -2.3), 0.0) + # self.assertEqual(math.pow(INF, -3.0), 0.0) + # self.assertEqual(math.pow(INF, NINF), 0.0) + # self.assertTrue(math.isnan(math.pow(INF, NAN))) + + # pow(-0., x) + # self.assertEqual(math.pow(-0.0, INF), 0.0) + self.assertEqual(math.pow(-0.0, 3.0), -0.0) + self.assertEqual(math.pow(-0.0, 2.3), 0.0) + self.assertEqual(math.pow(-0.0, 2.0), 0.0) + self.assertEqual(math.pow(-0.0, 0.0), 1.0) + self.assertEqual(math.pow(-0.0, -0.0), 1.0) + # self.assertRaises(ValueError, math.pow, -0.0, -2.0) + # self.assertRaises(ValueError, math.pow, -0.0, -2.3) + # self.assertRaises(ValueError, math.pow, -0.0, -3.0) + # self.assertEqual(math.pow(-0.0, NINF), INF) + # self.assertTrue(math.isnan(math.pow(-0.0, NAN))) + + # pow(NINF, x) + # self.assertEqual(math.pow(NINF, INF), INF) + # self.assertEqual(math.pow(NINF, 3.0), NINF) + # self.assertEqual(math.pow(NINF, 2.3), INF) + # self.assertEqual(math.pow(NINF, 2.0), INF) + # self.assertEqual(math.pow(NINF, 0.0), 1.0) + # self.assertEqual(math.pow(NINF, -0.0), 1.0) + # self.assertEqual(math.pow(NINF, -2.0), 0.0) + # self.assertEqual(math.pow(NINF, -2.3), 0.0) + # self.assertEqual(math.pow(NINF, -3.0), -0.0) + # self.assertEqual(math.pow(NINF, NINF), 0.0) + # self.assertTrue(math.isnan(math.pow(NINF, NAN))) + + # pow(-1, x) + # self.assertEqual(math.pow(-1.0, INF), 1.0) + self.assertEqual(math.pow(-1.0, 3.0), -1.0) + # self.assertRaises(ValueError, math.pow, -1.0, 2.3) + self.assertEqual(math.pow(-1.0, 2.0), 1.0) + self.assertEqual(math.pow(-1.0, 0.0), 1.0) + self.assertEqual(math.pow(-1.0, -0.0), 1.0) + self.assertEqual(math.pow(-1.0, -2.0), 1.0) + # self.assertRaises(ValueError, math.pow, -1.0, -2.3) + self.assertEqual(math.pow(-1.0, -3.0), -1.0) + # self.assertEqual(math.pow(-1.0, NINF), 1.0) + # self.assertTrue(math.isnan(math.pow(-1.0, NAN))) + + # pow(1, x) + # self.assertEqual(math.pow(1.0, INF), 1.0) + self.assertEqual(math.pow(1.0, 3.0), 1.0) + self.assertEqual(math.pow(1.0, 2.3), 1.0) + self.assertEqual(math.pow(1.0, 2.0), 1.0) + self.assertEqual(math.pow(1.0, 0.0), 1.0) + self.assertEqual(math.pow(1.0, -0.0), 1.0) + self.assertEqual(math.pow(1.0, -2.0), 1.0) + self.assertEqual(math.pow(1.0, -2.3), 1.0) + self.assertEqual(math.pow(1.0, -3.0), 1.0) + # self.assertEqual(math.pow(1.0, NINF), 1.0) + # self.assertEqual(math.pow(1.0, NAN), 1.0) + + # pow(x, 0) should be 1 for any x + self.assertEqual(math.pow(2.3, 0.0), 1.0) + self.assertEqual(math.pow(-2.3, 0.0), 1.0) + # self.assertEqual(math.pow(NAN, 0.0), 1.0) + self.assertEqual(math.pow(2.3, -0.0), 1.0) + self.assertEqual(math.pow(-2.3, -0.0), 1.0) + # self.assertEqual(math.pow(NAN, -0.0), 1.0) + + # pow(x, y) is invalid if x is negative and y is not integral + # self.assertRaises(ValueError, math.pow, -1.0, 2.3) + # self.assertRaises(ValueError, math.pow, -15.0, -3.1) + + # pow(x, NINF) + # self.assertEqual(math.pow(1.9, NINF), 0.0) + # self.assertEqual(math.pow(1.1, NINF), 0.0) + # self.assertEqual(math.pow(0.9, NINF), INF) + # self.assertEqual(math.pow(0.1, NINF), INF) + # self.assertEqual(math.pow(-0.1, NINF), INF) + # self.assertEqual(math.pow(-0.9, NINF), INF) + # self.assertEqual(math.pow(-1.1, NINF), 0.0) + # self.assertEqual(math.pow(-1.9, NINF), 0.0) + + # pow(x, INF) + # self.assertEqual(math.pow(1.9, INF), INF) + # self.assertEqual(math.pow(1.1, INF), INF) + # self.assertEqual(math.pow(0.9, INF), 0.0) + # self.assertEqual(math.pow(0.1, INF), 0.0) + # self.assertEqual(math.pow(-0.1, INF), 0.0) + # self.assertEqual(math.pow(-0.9, INF), 0.0) + # self.assertEqual(math.pow(-1.1, INF), INF) + # self.assertEqual(math.pow(-1.9, INF), INF) + + # pow(x, y) should work for x negative, y an integer + self.ftest("(-2.)**3.", math.pow(-2.0, 3.0), -8.0) + self.ftest("(-2.)**2.", math.pow(-2.0, 2.0), 4.0) + self.ftest("(-2.)**1.", math.pow(-2.0, 1.0), -2.0) + self.ftest("(-2.)**0.", math.pow(-2.0, 0.0), 1.0) + self.ftest("(-2.)**-0.", math.pow(-2.0, -0.0), 1.0) + self.ftest("(-2.)**-1.", math.pow(-2.0, -1.0), -0.5) + self.ftest("(-2.)**-2.", math.pow(-2.0, -2.0), 0.25) + self.ftest("(-2.)**-3.", math.pow(-2.0, -3.0), -0.125) + # self.assertRaises(ValueError, math.pow, -2.0, -0.5) + # self.assertRaises(ValueError, math.pow, -2.0, 0.5) + + # the following tests have been commented out since they don't + # really belong here: the implementation of ** for floats is + # independent of the implementation of math.pow + # self.assertEqual(1**NAN, 1) + # self.assertEqual(1**INF, 1) + # self.assertEqual(1**NINF, 1) + # self.assertEqual(1**0, 1) + # self.assertEqual(1.**NAN, 1) + # self.assertEqual(1.**INF, 1) + # self.assertEqual(1.**NINF, 1) + # self.assertEqual(1.**0, 1) + + def testSqrt(self): + # self.assertRaises(TypeError, math.sqrt) + self.ftest("sqrt(0)", math.sqrt(0), 0) + self.ftest("sqrt(0)", math.sqrt(0.0), 0.0) + self.ftest("sqrt(2.5)", math.sqrt(2.5), 1.5811388300841898) + self.ftest("sqrt(0.25)", math.sqrt(0.25), 0.5) + self.ftest("sqrt(25.25)", math.sqrt(25.25), 5.024937810560445) + self.ftest("sqrt(1)", math.sqrt(1), 1) + self.ftest("sqrt(4)", math.sqrt(4), 2) + # self.assertEqual(math.sqrt(INF), INF) + # self.assertRaises(ValueError, math.sqrt, -1) + # self.assertRaises(ValueError, math.sqrt, NINF) + # self.assertTrue(math.isnan(math.sqrt(NAN))) + + def testIsfinite(self): + self.assertTrue(math.isfinite(0.0)) + self.assertTrue(math.isfinite(-0.0)) + self.assertTrue(math.isfinite(1.0)) + self.assertTrue(math.isfinite(-1.0)) + self.assertFalse(math.isfinite(float("nan"))) + self.assertFalse(math.isfinite(float("inf"))) + # self.assertFalse(math.isfinite(float("-inf"))) + + def testIsnan(self): + # self.assertTrue(math.isnan(float("nan"))) + # self.assertTrue(math.isnan(float("-nan"))) + # self.assertTrue(math.isnan(float("inf") * 0.0)) + # self.assertFalse(math.isnan(float("inf"))) + self.assertFalse(math.isnan(0.0)) + self.assertFalse(math.isnan(1.0)) + + def testIsinf(self): + # self.assertTrue(math.isinf(float("inf"))) + # self.assertTrue(math.isinf(float("-inf"))) + # self.assertTrue(math.isinf(1e400)) + # self.assertTrue(math.isinf(-1e400)) + self.assertFalse(math.isinf(float("nan"))) + self.assertFalse(math.isinf(0.0)) + self.assertFalse(math.isinf(1.0)) + + +if __name__ == "__main__": + unittest.main() diff --git a/kclvm/tests/test_units/runtime/net/test_net.py b/kclvm/tests/test_units/runtime/net/test_net.py new file mode 100644 index 000000000..46c3b6b6c --- /dev/null +++ b/kclvm/tests/test_units/runtime/net/test_net.py @@ -0,0 +1,24 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing +import unittest + +import kclvm_runtime + +# https://github.com/python/cpython/blob/main/Lib/test + +_Dylib = kclvm_runtime.KclvmRuntimeDylib() + + +class kclx_Net: + def __init__(self, dylib_=None): + self.dylib = dylib_ if dylib_ else _Dylib + + +class BaseTest(unittest.TestCase): + def test_foo(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/kclvm/tests/test_units/runtime/regex/test_regex.py b/kclvm/tests/test_units/runtime/regex/test_regex.py new file mode 100644 index 000000000..0ad72bd7c --- /dev/null +++ b/kclvm/tests/test_units/runtime/regex/test_regex.py @@ -0,0 +1,306 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing +import unittest + +import kclvm_runtime + +# https://github.com/python/cpython/blob/main/Lib/test/test_re.py + +# kclvm_regex_compile, +# kclvm_regex_findall, +# kclvm_regex_match, +# kclvm_regex_replace, +# kclvm_regex_search, +# kclvm_regex_split, + +_Dylib = kclvm_runtime.KclvmRuntimeDylib() + + +class kclx_Regex: + def __init__(self, dylib_=None): + self.dylib = dylib_ if dylib_ else _Dylib + + def compile(self, pattern: str) -> bool: + return self._kcl_compile(f"{pattern}") + + def match(self, pattern: str, string: str) -> bool: + return self._kcl_match(f"{string}", f"{pattern}") + + def compile(self, pattern: str) -> bool: + return self._kcl_compile(f"{pattern}") + + def findall(self, pattern: str, string: str) -> typing.List[str]: + return self._kcl_findall(f"{string}", f"{pattern}") + + def search(self, pattern: str, string: str) -> bool: + return self._kcl_search(f"{string}", f"{pattern}") + + def split(self, pattern: str, string: str) -> typing.List[str]: + return self._kcl_split(f"{string}", f"{pattern}") + + def _kcl_match(self, string: str, pattern: str) -> bool: + return self.dylib.Invoke(f"regex.match", string, pattern) + + def _kcl_replace( + self, string: str, pattern: str, replace: str = None, count: int = 0 + ) -> str: + return self.dylib.Invoke(f"regex.replace", string, pattern, replace, count) + + def _kcl_compile(self, pattern: str) -> bool: + return self.dylib.Invoke(f"regex.compile", pattern) + + def _kcl_findall(self, string, pattern: str) -> typing.List[str]: + return self.dylib.Invoke(f"regex.findall", string, pattern) + + def _kcl_search(self, string: str, pattern: str) -> bool: + return self.dylib.Invoke(f"regex.search", string, pattern) + + def _kcl_split( + self, string: str, pattern: str, maxsplit: int = 0 + ) -> typing.List[str]: + return self.dylib.Invoke(f"regex.split", string, pattern, maxsplit) + + +re = kclx_Regex(_Dylib) + + +class S(str): + def __getitem__(self, index): + return S(super().__getitem__(index)) + + +class B(bytes): + def __getitem__(self, index): + return B(super().__getitem__(index)) + + +class BaseTest(unittest.TestCase): + def test_match_konfig_case(self): + # error: look-around, including look-ahead and look-behind, is not supported + self.assertTrue(re.match(r"^(?!-)[a-z0-9-]{1,63}(?")) + self.assertTrue(re.match(r"\N{SNAKE}", "\U0001f40d")) + self.assertTrue( + re.match( + r"\N{ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH " + r"HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM}", + "\ufbf9", + ) + ) + self.assertTrue(re.match(r"[\N{LESS-THAN SIGN}-\N{GREATER-THAN SIGN}]", "=")) + self.assertFalse(re.match(r"[\N{LESS-THAN SIGN}-\N{GREATER-THAN SIGN}]", ";")) + + def test_string_boundaries(self): + + # There's a word boundary at the start of a string. + self.assertTrue(re.match(r"\b", "abc")) + # A non-empty string includes a non-boundary zero-length match. + self.assertTrue(re.search(r"\B", "abc")) + # There is no non-boundary match at the start of a string. + # self.assertFalse(re.match(r"\B", "abc")) + # However, an empty string contains no word boundaries, and also no + # non-boundaries. + # self.assertFalse(re.search(r"\B", "")) + # This one is questionable and different from the perlre behaviour, + # but describes current behavior. + self.assertFalse(re.search(r"\b", "")) + + def test_big_codesize(self): + # Issue #1160 + r = "|".join(("%d" % x for x in range(10000))) + self.assertTrue(re.match(r, "1000")) + self.assertTrue(re.match(r, "9999")) + + def _test_lookahead(self): + + # Group reference. + self.assertTrue(re.match(r"(a)b(?=\1)a", "aba")) + self.assertFalse(re.match(r"(a)b(?=\1)c", "abac")) + # Conditional group reference. + self.assertTrue(re.match(r"(?:(a)|(x))b(?=(?(2)x|c))c", "abc")) + self.assertFalse(re.match(r"(?:(a)|(x))b(?=(?(2)c|x))c", "abc")) + self.assertTrue(re.match(r"(?:(a)|(x))b(?=(?(2)x|c))c", "abc")) + self.assertFalse(re.match(r"(?:(a)|(x))b(?=(?(1)b|x))c", "abc")) + self.assertTrue(re.match(r"(?:(a)|(x))b(?=(?(1)c|x))c", "abc")) + # Group used before defined. + self.assertTrue(re.match(r"(a)b(?=(?(2)x|c))(c)", "abc")) + self.assertFalse(re.match(r"(a)b(?=(?(2)b|x))(c)", "abc")) + self.assertTrue(re.match(r"(a)b(?=(?(1)c|x))(c)", "abc")) + + def _test_lookbehind(self): + self.assertTrue(re.match(r"ab(?<=b)c", "abc")) + self.assertFalse(re.match(r"ab(?<=c)c", "abc")) + self.assertFalse(re.match(r"ab(? 1) + EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**") + EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**", sys.maxsize) + EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**", -1) + EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**", 4) + EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**", 3) + EQ("Th** ** a tissue", "This is a tissue", "replace", "is", "**", 2) + EQ("Th** is a tissue", "This is a tissue", "replace", "is", "**", 1) + EQ("This is a tissue", "This is a tissue", "replace", "is", "**", 0) + EQ("cobob", "bobob", "replace", "bob", "cob") + EQ("cobobXcobocob", "bobobXbobobob", "replace", "bob", "cob") + EQ("bobob", "bobob", "replace", "bot", "bot") + + # replace single character (len(from)==1, len(to)>1) + EQ("ReyKKjaviKK", "Reykjavik", "replace", "k", "KK") + EQ("ReyKKjaviKK", "Reykjavik", "replace", "k", "KK", -1) + EQ("ReyKKjaviKK", "Reykjavik", "replace", "k", "KK", sys.maxsize) + EQ("ReyKKjaviKK", "Reykjavik", "replace", "k", "KK", 2) + EQ("ReyKKjavik", "Reykjavik", "replace", "k", "KK", 1) + EQ("Reykjavik", "Reykjavik", "replace", "k", "KK", 0) + EQ("A----B----C----", "A.B.C.", "replace", ".", "----") + # issue #15534 + EQ("...\u043c......<", "...\u043c......<", "replace", "<", "<") + + EQ("Reykjavik", "Reykjavik", "replace", "q", "KK") + + # replace substring (len(from)>1, len(to)!=len(from)) + EQ( + "ham, ham, eggs and ham", + "spam, spam, eggs and spam", + "replace", + "spam", + "ham", + ) + EQ( + "ham, ham, eggs and ham", + "spam, spam, eggs and spam", + "replace", + "spam", + "ham", + sys.maxsize, + ) + EQ( + "ham, ham, eggs and ham", + "spam, spam, eggs and spam", + "replace", + "spam", + "ham", + -1, + ) + EQ( + "ham, ham, eggs and ham", + "spam, spam, eggs and spam", + "replace", + "spam", + "ham", + 4, + ) + EQ( + "ham, ham, eggs and ham", + "spam, spam, eggs and spam", + "replace", + "spam", + "ham", + 3, + ) + EQ( + "ham, ham, eggs and spam", + "spam, spam, eggs and spam", + "replace", + "spam", + "ham", + 2, + ) + EQ( + "ham, spam, eggs and spam", + "spam, spam, eggs and spam", + "replace", + "spam", + "ham", + 1, + ) + EQ( + "spam, spam, eggs and spam", + "spam, spam, eggs and spam", + "replace", + "spam", + "ham", + 0, + ) + + EQ("bobob", "bobobob", "replace", "bobob", "bob") + EQ("bobobXbobob", "bobobobXbobobob", "replace", "bobob", "bob") + EQ("BOBOBOB", "BOBOBOB", "replace", "bob", "bobby") + + self.checkequal("one@two!three!", "one!two!three!", "replace", "!", "@", 1) + self.checkequal("onetwothree", "one!two!three!", "replace", "!", "") + self.checkequal("one@two@three!", "one!two!three!", "replace", "!", "@", 2) + self.checkequal("one@two@three@", "one!two!three!", "replace", "!", "@", 3) + self.checkequal("one@two@three@", "one!two!three!", "replace", "!", "@", 4) + self.checkequal("one!two!three!", "one!two!three!", "replace", "!", "@", 0) + self.checkequal("one@two@three@", "one!two!three!", "replace", "!", "@") + self.checkequal("one!two!three!", "one!two!three!", "replace", "x", "@") + self.checkequal("one!two!three!", "one!two!three!", "replace", "x", "@", 2) + self.checkequal("-a-b-c-", "abc", "replace", "", "-") + self.checkequal("-a-b-c", "abc", "replace", "", "-", 3) + self.checkequal("abc", "abc", "replace", "", "-", 0) + self.checkequal("", "", "replace", "", "") + self.checkequal("abc", "abc", "replace", "ab", "--", 0) + self.checkequal("abc", "abc", "replace", "xy", "--") + # Next three for SF bug 422088: [OSF1 alpha] string.replace(); died with + # MemoryError due to empty result (platform malloc issue when requesting + # 0 bytes). + self.checkequal("", "123", "replace", "123", "") + self.checkequal("", "123123", "replace", "123", "") + self.checkequal("x", "123x123", "replace", "123", "") + + # self.checkraises(TypeError, 'hello', 'replace') + # self.checkraises(TypeError, 'hello', 'replace', 42) + # self.checkraises(TypeError, 'hello', 'replace', 42, 'h') + # self.checkraises(TypeError, 'hello', 'replace', 'h', 42) + + def test_capitalize(self): + self.checkequal(" hello ", " hello ", "capitalize") + self.checkequal("Hello ", "Hello ", "capitalize") + self.checkequal("Hello ", "hello ", "capitalize") + self.checkequal("Aaaa", "aaaa", "capitalize") + self.checkequal("Aaaa", "AaAa", "capitalize") + + # self.checkraises(TypeError, 'hello', 'capitalize', 42) + + def test_additional_split(self): + self.checkequal( + ["this", "is", "the", "split", "function"], + "this is the split function", + "split", + ) + + # by whitespace + self.checkequal(["a", "b", "c", "d"], "a b c d ", "split") + self.checkequal(["a", "b c d"], "a b c d", "split", None, 1) + self.checkequal(["a", "b", "c d"], "a b c d", "split", None, 2) + self.checkequal(["a", "b", "c", "d"], "a b c d", "split", None, 3) + self.checkequal(["a", "b", "c", "d"], "a b c d", "split", None, 4) + self.checkequal(["a", "b", "c", "d"], "a b c d", "split", None, sys.maxsize - 1) + self.checkequal(["a b c d"], "a b c d", "split", None, 0) + self.checkequal(["a b c d"], " a b c d", "split", None, 0) + self.checkequal(["a", "b", "c d"], "a b c d", "split", None, 2) + + self.checkequal([], " ", "split") + self.checkequal(["a"], " a ", "split") + self.checkequal(["a", "b"], " a b ", "split") + self.checkequal(["a", "b "], " a b ", "split", None, 1) + self.checkequal(["a b c "], " a b c ", "split", None, 0) + self.checkequal(["a", "b c "], " a b c ", "split", None, 1) + self.checkequal(["a", "b", "c "], " a b c ", "split", None, 2) + self.checkequal(["a", "b", "c"], " a b c ", "split", None, 3) + self.checkequal(["a", "b"], "\n\ta \t\r b \v ", "split") + aaa = " a " * 20 + self.checkequal(["a"] * 20, aaa, "split") + self.checkequal(["a"] + [aaa[4:]], aaa, "split", None, 1) + self.checkequal(["a"] * 19 + ["a "], aaa, "split", None, 19) + + for b in ("arf\tbarf", "arf\nbarf", "arf\rbarf", "arf\fbarf", "arf\vbarf"): + self.checkequal(["arf", "barf"], b, "split") + self.checkequal(["arf", "barf"], b, "split", None) + self.checkequal(["arf", "barf"], b, "split", None, 2) + + def test_additional_rsplit(self): + self.checkequal( + ["this", "is", "the", "rsplit", "function"], + "this is the rsplit function", + "rsplit", + ) + + # by whitespace + self.checkequal(["a", "b", "c", "d"], "a b c d ", "rsplit") + self.checkequal(["a b c", "d"], "a b c d", "rsplit", None, 1) + self.checkequal(["a b", "c", "d"], "a b c d", "rsplit", None, 2) + self.checkequal(["a", "b", "c", "d"], "a b c d", "rsplit", None, 3) + self.checkequal(["a", "b", "c", "d"], "a b c d", "rsplit", None, 4) + self.checkequal( + ["a", "b", "c", "d"], "a b c d", "rsplit", None, sys.maxsize - 20 + ) + self.checkequal(["a b c d"], "a b c d", "rsplit", None, 0) + self.checkequal(["a b c d"], "a b c d ", "rsplit", None, 0) + self.checkequal(["a b", "c", "d"], "a b c d", "rsplit", None, 2) + + self.checkequal([], " ", "rsplit") + self.checkequal(["a"], " a ", "rsplit") + self.checkequal(["a", "b"], " a b ", "rsplit") + self.checkequal([" a", "b"], " a b ", "rsplit", None, 1) + self.checkequal([" a b c"], " a b c ", "rsplit", None, 0) + self.checkequal([" a b", "c"], " a b c ", "rsplit", None, 1) + self.checkequal([" a", "b", "c"], " a b c ", "rsplit", None, 2) + self.checkequal(["a", "b", "c"], " a b c ", "rsplit", None, 3) + self.checkequal(["a", "b"], "\n\ta \t\r b \v ", "rsplit", None, 88) + aaa = " a " * 20 + self.checkequal(["a"] * 20, aaa, "rsplit") + self.checkequal([aaa[:-4]] + ["a"], aaa, "rsplit", None, 1) + self.checkequal([" a a"] + ["a"] * 18, aaa, "rsplit", None, 18) + + for b in ("arf\tbarf", "arf\nbarf", "arf\rbarf", "arf\fbarf", "arf\vbarf"): + self.checkequal(["arf", "barf"], b, "rsplit") + self.checkequal(["arf", "barf"], b, "rsplit", None) + self.checkequal(["arf", "barf"], b, "rsplit", None, 2) + + def test_strip_whitespace(self): + self.checkequal("hello", " hello ", "strip") + self.checkequal("hello ", " hello ", "lstrip") + self.checkequal(" hello", " hello ", "rstrip") + self.checkequal("hello", "hello", "strip") + + b = " \t\n\r\f\vabc \t\n\r\f\v" + self.checkequal("abc", b, "strip") + self.checkequal("abc \t\n\r\f\v", b, "lstrip") + self.checkequal(" \t\n\r\f\vabc", b, "rstrip") + + # strip/lstrip/rstrip with None arg + self.checkequal("hello", " hello ", "strip", None) + self.checkequal("hello ", " hello ", "lstrip", None) + self.checkequal(" hello", " hello ", "rstrip", None) + self.checkequal("hello", "hello", "strip", None) + + def test_strip(self): + # strip/lstrip/rstrip with str arg + self.checkequal("hello", "xyzzyhelloxyzzy", "strip", "xyz") + self.checkequal("helloxyzzy", "xyzzyhelloxyzzy", "lstrip", "xyz") + self.checkequal("xyzzyhello", "xyzzyhelloxyzzy", "rstrip", "xyz") + self.checkequal("hello", "hello", "strip", "xyz") + self.checkequal("", "mississippi", "strip", "mississippi") + + # only trim the start and end; does not strip internal characters + self.checkequal("mississipp", "mississippi", "strip", "i") + + # self.checkraises(TypeError, 'hello', 'strip', 42, 42) + # self.checkraises(TypeError, 'hello', 'lstrip', 42, 42) + # self.checkraises(TypeError, 'hello', 'rstrip', 42, 42) + + def test_islower(self): + self.checkequal(False, "", "islower") + self.checkequal(True, "a", "islower") + self.checkequal(False, "A", "islower") + self.checkequal(False, "\n", "islower") + self.checkequal(True, "abc", "islower") + self.checkequal(False, "aBc", "islower") + self.checkequal(True, "abc\n", "islower") + # self.checkraises(TypeError, 'abc', 'islower', 42) + + def test_isupper(self): + self.checkequal(False, "", "isupper") + self.checkequal(False, "a", "isupper") + self.checkequal(True, "A", "isupper") + self.checkequal(False, "\n", "isupper") + self.checkequal(True, "ABC", "isupper") + self.checkequal(False, "AbC", "isupper") + self.checkequal(True, "ABC\n", "isupper") + # self.checkraises(TypeError, 'abc', 'isupper', 42) + + def test_istitle(self): + self.checkequal(False, "", "istitle") + self.checkequal(False, "a", "istitle") + self.checkequal(True, "A", "istitle") + self.checkequal(False, "\n", "istitle") + self.checkequal(True, "A Titlecased Line", "istitle") + self.checkequal(True, "A\nTitlecased Line", "istitle") + self.checkequal(True, "A Titlecased, Line", "istitle") + self.checkequal(False, "Not a capitalized String", "istitle") + self.checkequal(False, "Not\ta Titlecase String", "istitle") + self.checkequal(False, "Not--a Titlecase String", "istitle") + self.checkequal(False, "NOT", "istitle") + # self.checkraises(TypeError, 'abc', 'istitle', 42) + + def test_isspace(self): + self.checkequal(False, "", "isspace") + self.checkequal(False, "a", "isspace") + self.checkequal(True, " ", "isspace") + self.checkequal(True, "\t", "isspace") + self.checkequal(True, "\r", "isspace") + self.checkequal(True, "\n", "isspace") + self.checkequal(True, " \t\r\n", "isspace") + self.checkequal(False, " \t\r\na", "isspace") + # self.checkraises(TypeError, 'abc', 'isspace', 42) + + def test_isalpha(self): + self.checkequal(False, "", "isalpha") + self.checkequal(True, "a", "isalpha") + self.checkequal(True, "A", "isalpha") + self.checkequal(False, "\n", "isalpha") + self.checkequal(True, "abc", "isalpha") + self.checkequal(False, "aBc123", "isalpha") + self.checkequal(False, "abc\n", "isalpha") + # self.checkraises(TypeError, 'abc', 'isalpha', 42) + + def test_isalnum(self): + self.checkequal(False, "", "isalnum") + self.checkequal(True, "a", "isalnum") + self.checkequal(True, "A", "isalnum") + self.checkequal(False, "\n", "isalnum") + self.checkequal(True, "123abc456", "isalnum") + self.checkequal(True, "a1b3c", "isalnum") + self.checkequal(False, "aBc000 ", "isalnum") + self.checkequal(False, "abc\n", "isalnum") + # self.checkraises(TypeError, 'abc', 'isalnum', 42) + + def test_isdigit(self): + self.checkequal(False, "", "isdigit") + self.checkequal(False, "a", "isdigit") + self.checkequal(True, "0", "isdigit") + self.checkequal(True, "0123456789", "isdigit") + self.checkequal(False, "0123456789a", "isdigit") + + # self.checkraises(TypeError, 'abc', 'isdigit', 42) + + def test_title(self): + self.checkequal(" Hello ", " hello ", "title") + self.checkequal("Hello ", "hello ", "title") + self.checkequal("Hello ", "Hello ", "title") + self.checkequal( + "Format This As Title String", "fOrMaT thIs aS titLe String", "title" + ) + self.checkequal( + "Format,This-As*Title;String", + "fOrMaT,thIs-aS*titLe;String", + "title", + ) + self.checkequal("Getint", "getInt", "title") + # self.checkraises(TypeError, 'hello', 'title', 42) + + def test_splitlines(self): + self.checkequal(["abc", "def", "", "ghi"], "abc\ndef\n\rghi", "splitlines") + self.checkequal(["abc", "def", "", "ghi"], "abc\ndef\n\r\nghi", "splitlines") + self.checkequal(["abc", "def", "ghi"], "abc\ndef\r\nghi", "splitlines") + self.checkequal(["abc", "def", "ghi"], "abc\ndef\r\nghi\n", "splitlines") + self.checkequal(["abc", "def", "ghi", ""], "abc\ndef\r\nghi\n\r", "splitlines") + self.checkequal( + ["", "abc", "def", "ghi", ""], "\nabc\ndef\r\nghi\n\r", "splitlines" + ) + self.checkequal( + ["", "abc", "def", "ghi", ""], "\nabc\ndef\r\nghi\n\r", "splitlines", False + ) + self.checkequal( + ["\n", "abc\n", "def\r\n", "ghi\n", "\r"], + "\nabc\ndef\r\nghi\n\r", + "splitlines", + True, + ) + self.checkequal( + ["", "abc", "def", "ghi", ""], + "\nabc\ndef\r\nghi\n\r", + "splitlines", + keepends=False, + ) + self.checkequal( + ["\n", "abc\n", "def\r\n", "ghi\n", "\r"], + "\nabc\ndef\r\nghi\n\r", + "splitlines", + keepends=True, + ) + + # self.checkraises(TypeError, 'abc', 'splitlines', 42, 42) + + +class CommonTest(BaseTest): + def test_capitalize_nonascii(self): + # check that titlecased chars are lowered correctly + # \u1ffc is the titlecased char + # self.checkequal( + # "\u1ffc\u1ff3\u1ff3\u1ff3", "\u1ff3\u1ff3\u1ffc\u1ffc", "capitalize" + # ) + # check with cased non-letter chars + self.checkequal( + "\u24c5\u24e8\u24e3\u24d7\u24de\u24dd", + "\u24c5\u24ce\u24c9\u24bd\u24c4\u24c3", + "capitalize", + ) + self.checkequal( + "\u24c5\u24e8\u24e3\u24d7\u24de\u24dd", + "\u24df\u24e8\u24e3\u24d7\u24de\u24dd", + "capitalize", + ) + self.checkequal("\u2160\u2171\u2172", "\u2160\u2161\u2162", "capitalize") + self.checkequal("\u2160\u2171\u2172", "\u2170\u2171\u2172", "capitalize") + # check with Ll chars with no upper - nothing changes here + # self.checkequal( + # "\u019b\u1d00\u1d86\u0221\u1fb7", + # "\u019b\u1d00\u1d86\u0221\u1fb7", + # "capitalize", + # ) + + +class MixinStrUnicodeUserStringTest(BaseTest): + def test_startswith(self): + self.checkequal(True, "hello", "startswith", "he") + self.checkequal(True, "hello", "startswith", "hello") + self.checkequal(False, "hello", "startswith", "hello world") + self.checkequal(True, "hello", "startswith", "") + self.checkequal(False, "hello", "startswith", "ello") + self.checkequal(True, "hello", "startswith", "ello", 1) + self.checkequal(True, "hello", "startswith", "o", 4) + self.checkequal(False, "hello", "startswith", "o", 5) + self.checkequal(True, "hello", "startswith", "", 5) + self.checkequal(False, "hello", "startswith", "lo", 6) + self.checkequal(True, "helloworld", "startswith", "lowo", 3) + self.checkequal(True, "helloworld", "startswith", "lowo", 3, 7) + self.checkequal(False, "helloworld", "startswith", "lowo", 3, 6) + self.checkequal(True, "", "startswith", "", 0, 1) + self.checkequal(True, "", "startswith", "", 0, 0) + self.checkequal(False, "", "startswith", "", 1, 0) + + # test negative indices + self.checkequal(True, "hello", "startswith", "he", 0, -1) + self.checkequal(True, "hello", "startswith", "he", -53, -1) + self.checkequal(False, "hello", "startswith", "hello", 0, -1) + self.checkequal(False, "hello", "startswith", "hello world", -1, -10) + self.checkequal(False, "hello", "startswith", "ello", -5) + self.checkequal(True, "hello", "startswith", "ello", -4) + self.checkequal(False, "hello", "startswith", "o", -2) + self.checkequal(True, "hello", "startswith", "o", -1) + self.checkequal(True, "hello", "startswith", "", -3, -3) + self.checkequal(False, "hello", "startswith", "lo", -9) + + # self.checkraises(TypeError, 'hello', 'startswith') + # self.checkraises(TypeError, 'hello', 'startswith', 42) + + # test tuple arguments + # self.checkequal(True, "hello", "startswith", ("he", "ha")) + # self.checkequal(False, "hello", "startswith", ("lo", "llo")) + # self.checkequal(True, "hello", "startswith", ("hellox", "hello")) + # self.checkequal(False, "hello", "startswith", ()) + # self.checkequal(True, "helloworld", "startswith", ("hellowo", "rld", "lowo"), 3) + # self.checkequal( + # False, "helloworld", "startswith", ("hellowo", "ello", "rld"), 3 + # ) + # self.checkequal(True, "hello", "startswith", ("lo", "he"), 0, -1) + # self.checkequal(False, "hello", "startswith", ("he", "hel"), 0, 1) + # self.checkequal(True, "hello", "startswith", ("he", "hel"), 0, 2) + + # self.checkraises(TypeError, 'hello', 'startswith', (42,)) + + def test_endswith(self): + self.checkequal(True, "hello", "endswith", "lo") + self.checkequal(False, "hello", "endswith", "he") + self.checkequal(True, "hello", "endswith", "") + self.checkequal(False, "hello", "endswith", "hello world") + self.checkequal(False, "helloworld", "endswith", "worl") + self.checkequal(True, "helloworld", "endswith", "worl", 3, 9) + self.checkequal(True, "helloworld", "endswith", "world", 3, 12) + self.checkequal(True, "helloworld", "endswith", "lowo", 1, 7) + self.checkequal(True, "helloworld", "endswith", "lowo", 2, 7) + self.checkequal(True, "helloworld", "endswith", "lowo", 3, 7) + self.checkequal(False, "helloworld", "endswith", "lowo", 4, 7) + self.checkequal(False, "helloworld", "endswith", "lowo", 3, 8) + self.checkequal(False, "ab", "endswith", "ab", 0, 1) + self.checkequal(False, "ab", "endswith", "ab", 0, 0) + self.checkequal(True, "", "endswith", "", 0, 1) + self.checkequal(True, "", "endswith", "", 0, 0) + self.checkequal(False, "", "endswith", "", 1, 0) + + # test negative indices + self.checkequal(True, "hello", "endswith", "lo", -2) + self.checkequal(False, "hello", "endswith", "he", -2) + self.checkequal(True, "hello", "endswith", "", -3, -3) + self.checkequal(False, "hello", "endswith", "hello world", -10, -2) + self.checkequal(False, "helloworld", "endswith", "worl", -6) + self.checkequal(True, "helloworld", "endswith", "worl", -5, -1) + self.checkequal(True, "helloworld", "endswith", "worl", -5, 9) + self.checkequal(True, "helloworld", "endswith", "world", -7, 12) + self.checkequal(True, "helloworld", "endswith", "lowo", -99, -3) + self.checkequal(True, "helloworld", "endswith", "lowo", -8, -3) + self.checkequal(True, "helloworld", "endswith", "lowo", -7, -3) + self.checkequal(False, "helloworld", "endswith", "lowo", 3, -4) + self.checkequal(False, "helloworld", "endswith", "lowo", -8, -2) + + # self.checkraises(TypeError, 'hello', 'endswith') + # self.checkraises(TypeError, 'hello', 'endswith', 42) + + # test tuple arguments + # self.checkequal(False, "hello", "endswith", ("he", "ha")) + # self.checkequal(True, "hello", "endswith", ("lo", "llo")) + # self.checkequal(True, "hello", "endswith", ("hellox", "hello")) + # self.checkequal(False, "hello", "endswith", ()) + # self.checkequal(True, "helloworld", "endswith", ("hellowo", "rld", "lowo"), 3) + # self.checkequal( + # False, "helloworld", "endswith", ("hellowo", "ello", "rld"), 3, -1 + # ) + # self.checkequal(True, "hello", "endswith", ("hell", "ell"), 0, -1) + # self.checkequal(False, "hello", "endswith", ("he", "hel"), 0, 1) + # self.checkequal(True, "hello", "endswith", ("he", "hell"), 0, 4) + + # self.checkraises(TypeError, 'hello', 'endswith', (42,)) + + def test_join(self): + # join now works with any sequence type + # moved here, because the argument order is + # different in string.join + self.checkequal("a b c d", " ", "join", ["a", "b", "c", "d"]) + self.checkequal("abcd", "", "join", ("a", "b", "c", "d")) + self.checkequal("bd", "", "join", ("", "b", "", "d")) + self.checkequal("ac", "", "join", ("a", "", "c", "")) + # self.checkequal('w x y z', ' ', 'join', Sequence()) + self.checkequal("abc", "a", "join", ("abc",)) + # self.checkequal('z', 'a', 'join', UserList(['z'])) + self.checkequal("a.b.c", ".", "join", ["a", "b", "c"]) + self.assertRaises(TypeError, ".".join, ["a", "b", 3]) + for i in [5, 25, 125]: + self.checkequal(((("a" * i) + "-") * i)[:-1], "-", "join", ["a" * i] * i) + self.checkequal(((("a" * i) + "-") * i)[:-1], "-", "join", ("a" * i,) * i) + + # self.checkequal(str(BadSeq1()), ' ', 'join', BadSeq1()) + # self.checkequal('a b c', ' ', 'join', BadSeq2()) + + def _test_inplace_rewrites(self): + # Check that strings don't copy and modify cached single-character strings + self.checkequal("a", "A", "lower") + self.checkequal(True, "A", "isupper") + self.checkequal("A", "a", "upper") + self.checkequal(True, "a", "islower") + + self.checkequal("a", "A", "replace", "A", "a") + self.checkequal(True, "A", "isupper") + + self.checkequal("A", "a", "capitalize") + self.checkequal(True, "a", "islower") + + self.checkequal("A", "a", "swapcase") + self.checkequal(True, "a", "islower") + + self.checkequal("A", "a", "title") + self.checkequal(True, "a", "islower") + + def test_none_arguments(self): + # issue 11828 + s = "hello" + self.checkequal(2, s, "find", "l", None) + self.checkequal(3, s, "find", "l", -2, None) + self.checkequal(2, s, "find", "l", None, -2) + self.checkequal(0, s, "find", "h", None, None) + + self.checkequal(3, s, "rfind", "l", None) + self.checkequal(3, s, "rfind", "l", -2, None) + self.checkequal(2, s, "rfind", "l", None, -2) + self.checkequal(0, s, "rfind", "h", None, None) + + self.checkequal(2, s, "index", "l", None) + self.checkequal(3, s, "index", "l", -2, None) + self.checkequal(2, s, "index", "l", None, -2) + self.checkequal(0, s, "index", "h", None, None) + + self.checkequal(3, s, "rindex", "l", None) + self.checkequal(3, s, "rindex", "l", -2, None) + self.checkequal(2, s, "rindex", "l", None, -2) + self.checkequal(0, s, "rindex", "h", None, None) + + self.checkequal(2, s, "count", "l", None) + self.checkequal(1, s, "count", "l", -2, None) + self.checkequal(1, s, "count", "l", None, -2) + self.checkequal(0, s, "count", "x", None, None) + + self.checkequal(True, s, "endswith", "o", None) + self.checkequal(True, s, "endswith", "lo", -2, None) + self.checkequal(True, s, "endswith", "l", None, -2) + self.checkequal(False, s, "endswith", "x", None, None) + + self.checkequal(True, s, "startswith", "h", None) + self.checkequal(True, s, "startswith", "l", -2, None) + self.checkequal(True, s, "startswith", "h", None, -2) + self.checkequal(False, s, "startswith", "x", None, None) + + +if __name__ == "__main__": + unittest.main() diff --git a/kclvm/tests/test_units/runtime/units/test_units.py b/kclvm/tests/test_units/runtime/units/test_units.py new file mode 100644 index 000000000..744904021 --- /dev/null +++ b/kclvm/tests/test_units/runtime/units/test_units.py @@ -0,0 +1,24 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing +import unittest + +import kclvm_runtime + +# https://github.com/python/cpython/blob/main/Lib/test + +_Dylib = kclvm_runtime.KclvmRuntimeDylib() + + +class kclx_Units: + def __init__(self, dylib_=None): + self.dylib = dylib_ if dylib_ else _Dylib + + +class BaseTest(unittest.TestCase): + def test_foo(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/kclvm/tests/test_units/runtime/yaml/test_yaml.py b/kclvm/tests/test_units/runtime/yaml/test_yaml.py new file mode 100644 index 000000000..00093e905 --- /dev/null +++ b/kclvm/tests/test_units/runtime/yaml/test_yaml.py @@ -0,0 +1,24 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import typing +import unittest + +import kclvm_runtime + +# https://github.com/python/cpython/blob/main/Lib/test + +_Dylib = kclvm_runtime.KclvmRuntimeDylib() + + +class kclx_Yaml: + def __init__(self, dylib_=None): + self.dylib = dylib_ if dylib_ else _Dylib + + +class BaseTest(unittest.TestCase): + def test_foo(self): + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/kclvm/tools/Cargo.lock b/kclvm/tools/Cargo.lock new file mode 100644 index 000000000..7395adf08 --- /dev/null +++ b/kclvm/tools/Cargo.lock @@ -0,0 +1,1527 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "annotate-snippets" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5" + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctor" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "diff" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "enquote" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c36cb11dbde389f4096111698d8b567c0720e3452fd5ac3e6b4e47e1939932" +dependencies = [ + "thiserror", +] + +[[package]] +name = "fancy-regex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "fixedbitset" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" + +[[package]] +name = "fslock" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", + "rustc-rayon", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "json_minimal" +version = "0.1.3" + +[[package]] +name = "kclvm-ast" +version = "0.1.0" +dependencies = [ + "kclvm-span", + "rustc_span", + "serde", + "serde_json", +] + +[[package]] +name = "kclvm-config" +version = "0.1.0" +dependencies = [ + "ahash", + "chrono", + "fslock", + "glob", + "indexmap", + "kclvm-version", + "pathdiff", + "ron", + "rust-crypto", + "serde", + "serde_yaml", + "toml", +] + +[[package]] +name = "kclvm-error" +version = "0.1.0" +dependencies = [ + "annotate-snippets", + "atty", + "indexmap", + "kclvm-span", + "rustc_span", + "termcolor", + "termize", + "tracing", +] + +[[package]] +name = "kclvm-lexer" +version = "0.1.0" +dependencies = [ + "kclvm-error", + "rustc_lexer", + "unic-emoji-char", +] + +[[package]] +name = "kclvm-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "kclvm-parser" +version = "0.1.0" +dependencies = [ + "bstr", + "either", + "enquote", + "kclvm-ast", + "kclvm-config", + "kclvm-error", + "kclvm-lexer", + "kclvm-sema", + "kclvm-span", + "num-bigint", + "rustc_data_structures", + "rustc_lexer", + "rustc_span", + "serde", + "serde_json", + "tracing", + "unicode_names2", +] + +[[package]] +name = "kclvm-runtime" +version = "0.1.0" +dependencies = [ + "ahash", + "base64", + "bstr", + "chrono", + "fancy-regex", + "indexmap", + "itertools", + "json_minimal", + "kclvm_runtime_internal_macros", + "libc", + "md5", + "num-integer", + "phf", + "regex", + "serde_json", + "serde_yaml", + "sha1", + "sha2 0.9.9", + "unic-ucd-bidi", + "unic-ucd-category", + "unicode-casing", +] + +[[package]] +name = "kclvm-sema" +version = "0.1.0" +dependencies = [ + "ahash", + "bit-set", + "bitflags", + "fancy-regex", + "indexmap", + "kclvm-ast", + "kclvm-error", + "kclvm-runtime", + "kclvm-span", + "once_cell", + "petgraph", + "phf", + "unicode_names2", +] + +[[package]] +name = "kclvm-span" +version = "0.1.0" +dependencies = [ + "kclvm-macros", + "rustc_span", + "scoped-tls", +] + +[[package]] +name = "kclvm-tools" +version = "0.1.0" +dependencies = [ + "fancy-regex", + "indexmap", + "kclvm-ast", + "kclvm-error", + "kclvm-parser", + "pretty_assertions", +] + +[[package]] +name = "kclvm-version" +version = "0.1.0" + +[[package]] +name = "kclvm_runtime_internal_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest 0.10.3", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "output_vt100" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" +dependencies = [ + "winapi", +] + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "petgraph" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "pretty_assertions" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563" +dependencies = [ + "ansi_term", + "ctor", + "diff", + "output_vt100", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "psm" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871372391786ccec00d3c5d3d6608905b3d4db263639cfe075d3b60a736d115a" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ron" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b861ecaade43ac97886a512b360d01d66be9f41f3c61088b42cedf92e03d678" +dependencies = [ + "base64", + "bitflags", + "serde", +] + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +dependencies = [ + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-rayon" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" +dependencies = [ + "crossbeam-deque", + "either", + "rustc-rayon-core", +] + +[[package]] +name = "rustc-rayon-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" + +[[package]] +name = "rustc_data_structures" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ena", + "indexmap", + "jobserver", + "libc", + "memmap2", + "parking_lot", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "stable_deref_trait", + "stacker", + "tempfile", + "tracing", + "winapi", +] + +[[package]] +name = "rustc_lexer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86aae0c77166108c01305ee1a36a1e77289d7dc6ca0a3cd91ff4992de2d16a5" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "rustc_span" +version = "0.0.0" +dependencies = [ + "cfg-if 0.1.10", + "md-5", + "rustc_data_structures", + "scoped-tls", + "sha-1", + "sha2 0.10.2", + "tracing", + "unicode-width", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04066589568b72ec65f42d65a1a52436e954b168773148893c020269563decf2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termize" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-category" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8d4591f5fcfe1bd4453baaf803c40e1b1e69ff8455c47620440b46efef91c0" +dependencies = [ + "matches", + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-casing" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "623f59e6af2a98bdafeb93fa277ac8e1e40440973001ca15cf4ae1541cd16d56" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + +[[package]] +name = "unicode_names2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d6678d7916394abad0d4b19df4d3802e1fd84abd7d701f39b75ee71b9e8cf1" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/kclvm/tools/Cargo.toml b/kclvm/tools/Cargo.toml new file mode 100644 index 000000000..c1b9d3a0b --- /dev/null +++ b/kclvm/tools/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "kclvm-tools" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +indexmap = "1.0" +fancy-regex = "0.7.1" + +kclvm-ast = {path = "../ast", version = "0.1.0"} +kclvm-error = {path = "../error", version = "0.1.0"} +kclvm-parser = {path = "../parser", version = "0.1.0"} + +[dev-dependencies] +pretty_assertions = "1.2.1" diff --git a/kclvm/tools/src/lib.rs b/kclvm/tools/src/lib.rs new file mode 100644 index 000000000..066865e5b --- /dev/null +++ b/kclvm/tools/src/lib.rs @@ -0,0 +1,5 @@ +pub mod printer; +pub mod query; + +#[macro_use] +extern crate kclvm_error; diff --git a/kclvm/tools/src/printer/mod.rs b/kclvm/tools/src/printer/mod.rs new file mode 100644 index 000000000..2fd1aaab6 --- /dev/null +++ b/kclvm/tools/src/printer/mod.rs @@ -0,0 +1,258 @@ +use indexmap::IndexMap; +use std::collections::VecDeque; + +use kclvm_ast::{ast, token::TokenKind, walker::MutSelfTypedResultWalker}; + +mod node; + +#[cfg(test)] +mod tests; + +pub const WHITESPACE: &str = " "; +pub const TAB: &str = "\t"; +pub const NEWLINE: &str = "\n"; + +#[derive(Debug, Clone)] +pub enum Indentation { + Indent = 0, + Dedent = 1, + Newline = 2, + IndentWithNewline = 3, + DedentWithNewline = 4, + Fill = 5, +} + +/// Printer config +#[derive(Debug)] +pub struct Config { + pub tab_len: usize, + pub indent_len: usize, + pub use_spaces: bool, + pub write_comments: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { + tab_len: 4, + indent_len: 4, + use_spaces: true, + write_comments: true, + } + } +} + +#[derive(Copy, Clone)] +pub struct NoHook; + +impl PrinterHook for NoHook {} + +pub enum ASTNode<'p> { + Stmt(&'p ast::NodeRef), + Expr(&'p ast::NodeRef), +} + +pub trait PrinterHook { + fn pre(&self, _printer: &mut Printer<'_>, _node: ASTNode<'_>) {} + fn post(&self, _printer: &mut Printer<'_>, _node: ASTNode<'_>) {} +} + +pub struct Printer<'p> { + /// Output string buffer. + pub out: String, + pub indent: usize, + pub cfg: Config, + /// Print comments, + pub last_ast_line: u64, + pub comments: VecDeque>, + pub import_spec: IndexMap, + pub hook: &'p (dyn PrinterHook + 'p), +} + +impl Default for Printer<'_> { + fn default() -> Self { + Self { + out: Default::default(), + indent: Default::default(), + cfg: Default::default(), + last_ast_line: Default::default(), + comments: Default::default(), + import_spec: Default::default(), + hook: &NoHook, + } + } +} + +impl<'p> Printer<'p> { + pub fn new(cfg: Config, hook: &'p (dyn PrinterHook + 'p)) -> Self { + Self { + out: "".to_string(), + indent: 0, + cfg, + last_ast_line: 0, + comments: VecDeque::default(), + import_spec: IndexMap::default(), + hook, + } + } + + // -------------------------- + // Write functions + // -------------------------- + + /// Write a string + #[inline] + pub fn write(&mut self, text: &str) { + self.write_string(text); + } + + /// Write a string with newline. + #[inline] + pub fn writeln(&mut self, text: &str) { + self.write_string(text); + self.write_string(NEWLINE); + self.fill(""); + } + + /// Write a space. + #[inline] + pub fn write_space(&mut self) { + self.write_string(WHITESPACE); + } + + /// Fill a indent + pub fn fill(&mut self, text: &str) { + if self.cfg.use_spaces { + self.write(&format!( + "{}{}", + WHITESPACE.repeat(self.indent * self.cfg.indent_len), + text + )); + } else { + self.write(&format!("{}{}", TAB.repeat(self.indent), text)); + } + } + + /// Print string + #[inline] + pub fn write_string(&mut self, string: &str) { + self.out.push_str(string); + } + + pub fn write_indentation(&mut self, indentation: Indentation) { + match indentation { + Indentation::Indent => self.enter(), + Indentation::Dedent => self.leave(), + Indentation::Newline => self.write_newline(), + Indentation::IndentWithNewline => { + self.enter(); + self.write_newline() + } + Indentation::DedentWithNewline => { + self.leave(); + self.write_newline(); + } + Indentation::Fill => self.fill(""), + } + } + + #[inline] + pub fn write_newline(&mut self) { + self.writeln("") + } + + #[inline] + pub fn write_newline_without_fill(&mut self) { + self.write_string(NEWLINE); + } + + /// Print value + #[inline] + pub fn write_value(&mut self, value: T) { + self.write(&format!("{}", value)); + } + + /// Print ast token + #[inline] + pub fn write_token(&mut self, tok: TokenKind) { + let tok_str: String = tok.into(); + self.write_string(&tok_str); + } + + /// Print ast node + #[inline] + pub fn write_node(&mut self, node: ASTNode<'_>) { + match node { + ASTNode::Stmt(stmt) => self.stmt(stmt), + ASTNode::Expr(expr) => self.expr(expr), + } + } + + /// Print ast module. + #[inline] + pub fn write_module(&mut self, module: &ast::Module) { + self.walk_module(module); + while let Some(comment) = self.comments.pop_front() { + self.writeln(&comment.node.text); + self.fill(""); + } + } + + /// Print ast comments. + pub fn write_ast_comments(&mut self, node: &ast::NodeRef) { + if !self.cfg.write_comments { + return; + } + if node.line > self.last_ast_line { + self.last_ast_line = node.line; + let mut index = None; + for (i, comment) in self.comments.iter().enumerate() { + if comment.line <= node.line { + index = Some(i); + } else { + break; + } + } + if let Some(index) = index { + let mut count = index; + while count > 0 { + match self.comments.pop_front() { + Some(comment) => { + self.writeln(&comment.node.text); + } + None => break, + } + count -= 1; + } + } + } + } + + // -------------------------- + // Indent and scope functions + // -------------------------- + + /// Enter with a indent + pub fn enter(&mut self) { + self.indent += 1; + } + + /// Leave with a dedent + pub fn leave(&mut self) { + self.indent -= 1; + } +} + +/// Print AST to string +pub fn print_ast_module(module: &ast::Module) -> String { + let mut printer = Printer::default(); + printer.walk_module(module); + printer.out +} + +/// Print AST to string +pub fn print_ast_node(node: ASTNode) -> String { + let mut printer = Printer::default(); + printer.write_node(node); + printer.out +} diff --git a/kclvm/tools/src/printer/node.rs b/kclvm/tools/src/printer/node.rs new file mode 100644 index 000000000..193845e79 --- /dev/null +++ b/kclvm/tools/src/printer/node.rs @@ -0,0 +1,847 @@ +use std::collections::HashSet; + +use kclvm_ast::ast::{self, CallExpr}; +use kclvm_ast::token::{DelimToken, TokenKind}; +use kclvm_ast::walker::MutSelfTypedResultWalker; + +use super::{Indentation, Printer}; + +type ParameterType<'a> = ( + ( + &'a ast::NodeRef, + &'a Option>, + ), + &'a Option>, +); + +const COMMA_WHITESPACE: &str = ", "; +const IDENTIFIER_REGEX: &str = r#"^\$?[a-zA-Z_]\w*$"#; + +macro_rules! interleave { + ($inter: expr, $f: expr, $seq: expr) => { + if !$seq.is_empty() { + $f(&$seq[0]); + for s in &$seq[1..] { + $inter(); + $f(s); + } + } + }; +} + +impl<'p, 'ctx> MutSelfTypedResultWalker<'ctx> for Printer<'p> { + type Result = (); + + fn walk_module(&mut self, module: &'ctx ast::Module) -> Self::Result { + for comment in &module.comments { + self.comments.push_back(comment.clone()); + } + self.stmts(&module.body); + } + + fn walk_expr_stmt(&mut self, expr_stmt: &'ctx ast::ExprStmt) -> Self::Result { + interleave!( + || self.write(COMMA_WHITESPACE), + |expr| self.expr(expr), + expr_stmt.exprs + ); + self.write_newline_without_fill(); + } + + fn walk_unification_stmt( + &mut self, + unification_stmt: &'ctx ast::UnificationStmt, + ) -> Self::Result { + self.walk_identifier(&unification_stmt.target.node); + self.write(": "); + self.walk_schema_expr(&unification_stmt.value.node); + self.write_newline_without_fill(); + } + + fn walk_type_alias_stmt(&mut self, type_alias_stmt: &'ctx ast::TypeAliasStmt) -> Self::Result { + self.write("type"); + self.write_space(); + self.walk_identifier(&type_alias_stmt.type_name.node); + self.write(" = "); + self.write(&type_alias_stmt.type_value.node); + self.write_newline_without_fill(); + } + + fn walk_assign_stmt(&mut self, assign_stmt: &'ctx ast::AssignStmt) -> Self::Result { + for (i, target) in assign_stmt.targets.iter().enumerate() { + self.walk_identifier(&target.node); + if i == 0 { + if let Some(ty) = &assign_stmt.ty { + self.write(": "); + self.write(&ty.node.to_string()); + } + } + self.write(" = "); + } + self.expr(&assign_stmt.value); + self.write_newline_without_fill(); + if matches!(assign_stmt.value.node, ast::Expr::Schema(_)) { + self.write_newline_without_fill(); + } + } + + fn walk_aug_assign_stmt(&mut self, aug_assign_stmt: &'ctx ast::AugAssignStmt) -> Self::Result { + self.walk_identifier(&aug_assign_stmt.target.node); + self.write_space(); + self.write(aug_assign_stmt.op.symbol()); + self.write_space(); + self.expr(&aug_assign_stmt.value); + self.write_newline_without_fill(); + } + + fn walk_assert_stmt(&mut self, assert_stmt: &'ctx ast::AssertStmt) -> Self::Result { + self.write("assert "); + self.expr(&assert_stmt.test); + if let Some(if_cond) = &assert_stmt.if_cond { + self.write(" if "); + self.expr(if_cond); + } + if let Some(msg) = &assert_stmt.msg { + self.write(COMMA_WHITESPACE); + self.expr(msg); + } + self.write_newline_without_fill(); + } + + fn walk_if_stmt(&mut self, if_stmt: &'ctx ast::IfStmt) -> Self::Result { + self.write("if "); + self.expr(&if_stmt.cond); + self.write_token(TokenKind::Colon); + self.write_newline_without_fill(); + self.write_indentation(Indentation::Indent); + self.stmts(&if_stmt.body); + self.write_indentation(Indentation::Dedent); + if !if_stmt.orelse.is_empty() { + if let ast::Stmt::If(elif_stmt) = &if_stmt.orelse[0].node { + self.write("el"); + self.walk_if_stmt(elif_stmt); + } else { + self.write("else:"); + self.write_newline(); + self.write_indentation(Indentation::Indent); + self.stmts(&if_stmt.orelse); + self.write_indentation(Indentation::Dedent); + self.write_newline_without_fill(); + } + } else { + self.write_newline_without_fill(); + } + } + + fn walk_import_stmt(&mut self, import_stmt: &'ctx ast::ImportStmt) -> Self::Result { + self.write("import "); + self.write(&import_stmt.path); + if let Some(as_name) = &import_stmt.asname { + self.write(" as "); + self.write(as_name); + } + self.write_newline_without_fill(); + } + + fn walk_schema_stmt(&mut self, schema_stmt: &'ctx ast::SchemaStmt) -> Self::Result { + interleave!( + || self.write_newline(), + |expr: &ast::NodeRef| {self.write("@"); self.walk_call_expr(&expr.node);}, + schema_stmt.decorators + ); + if !schema_stmt.decorators.is_empty() { + self.write_newline(); + } + if schema_stmt.is_mixin { + self.write("mixin "); + } else if schema_stmt.is_protocol { + self.write("protocol "); + } else { + self.write("schema "); + } + self.write(&schema_stmt.name.node); + if let Some(args) = &schema_stmt.args { + self.write("["); + self.walk_arguments(&args.node); + self.write("]"); + } + if let Some(parent_name) = &schema_stmt.parent_name { + self.write("("); + self.walk_identifier(&parent_name.node); + self.write(")"); + } + if let Some(host_name) = &schema_stmt.for_host_name { + self.write(" for "); + self.walk_identifier(&host_name.node); + } + self.write_token(TokenKind::Colon); + self.write_newline_without_fill(); + self.write_indentation(Indentation::Indent); + if !schema_stmt.doc.is_empty() { + self.fill(""); + self.write(&schema_stmt.doc); + self.write_newline_without_fill(); + } + if !schema_stmt.mixins.is_empty() { + self.fill(""); + self.write("mixin ["); + self.write_indentation(Indentation::IndentWithNewline); + interleave!( + || { + self.write(","); + self.write_newline(); + }, + |mixin_name: &ast::NodeRef| self.walk_identifier(&mixin_name.node), + schema_stmt.mixins + ); + self.write_indentation(Indentation::Dedent); + self.write_newline(); + self.write("]"); + self.write_newline_without_fill(); + } + if let Some(index_signature) = &schema_stmt.index_signature { + self.fill(""); + self.write_token(TokenKind::OpenDelim(DelimToken::Bracket)); + if index_signature.node.any_other { + self.write_token(TokenKind::DotDotDot); + } + if let Some(key_name) = &index_signature.node.key_name { + self.write(&format!("{}: ", key_name)); + } + self.write(&index_signature.node.key_type.node); + self.write_token(TokenKind::CloseDelim(DelimToken::Bracket)); + self.write_token(TokenKind::Colon); + self.write_space(); + self.write(&index_signature.node.value_type.node); + if let Some(value) = &index_signature.node.value { + self.write(" = "); + self.expr(value); + } + self.write_newline_without_fill(); + } + self.stmts(&schema_stmt.body); + self.write_newline_without_fill(); + if !schema_stmt.checks.is_empty() { + self.fill("check:"); + // Schema check indent + self.write_indentation(Indentation::IndentWithNewline); + interleave!( + || self.write_newline(), + |check_expr: &ast::NodeRef| self.walk_check_expr(&check_expr.node), + schema_stmt.checks + ); + self.write_newline_without_fill(); + // Schema check dedent + self.write_indentation(Indentation::Dedent); + self.write_newline_without_fill(); + } + // Schema Stmt dedent + self.write_indentation(Indentation::Dedent); + } + + fn walk_rule_stmt(&mut self, rule_stmt: &'ctx ast::RuleStmt) -> Self::Result { + interleave!( + || self.write_newline(), + |expr: &ast::NodeRef| {self.write("@"); self.walk_call_expr(&expr.node);}, + rule_stmt.decorators + ); + if !rule_stmt.decorators.is_empty() { + self.write_newline(); + } + self.write("rule "); + self.write(&rule_stmt.name.node); + if let Some(args) = &rule_stmt.args { + self.write("["); + self.walk_arguments(&args.node); + self.write("]"); + } + if !rule_stmt.parent_rules.is_empty() { + self.write("("); + interleave!( + || self.write(COMMA_WHITESPACE), + |identifier: &ast::NodeRef| self.walk_identifier(&identifier.node), + rule_stmt.parent_rules + ); + self.write(")"); + } + if let Some(host_name) = &rule_stmt.for_host_name { + self.write(" for "); + self.walk_identifier(&host_name.node); + } + self.write_token(TokenKind::Colon); + // Rule Stmt indent + self.write_indentation(Indentation::IndentWithNewline); + if !rule_stmt.doc.is_empty() { + self.write(&rule_stmt.doc); + self.write_newline(); + } + if !rule_stmt.checks.is_empty() { + interleave!( + || self.write_newline(), + |check_expr: &ast::NodeRef| self.walk_check_expr(&check_expr.node), + rule_stmt.checks + ); + self.write_newline_without_fill(); + } + // Rule Stmt dedent + self.write_indentation(Indentation::Dedent); + } + + fn walk_quant_expr(&mut self, quant_expr: &'ctx ast::QuantExpr) -> Self::Result { + let in_one_line = false; + let quant_op_string: String = quant_expr.op.clone().into(); + self.write(&quant_op_string); + self.write_space(); + interleave!( + || self.write(COMMA_WHITESPACE), + |identifier: &ast::NodeRef| self.walk_identifier(&identifier.node), + quant_expr.variables + ); + self.write(" in "); + self.expr(&quant_expr.target); + self.write(" {"); + if !in_one_line { + self.write_indentation(Indentation::IndentWithNewline); + } + self.expr(&quant_expr.test); + if let Some(if_cond) = &quant_expr.if_cond { + self.write(" if "); + self.expr(if_cond); + } + if !in_one_line { + self.write_indentation(Indentation::DedentWithNewline) + } + self.write("}") + } + + fn walk_schema_attr(&mut self, schema_attr: &'ctx ast::SchemaAttr) -> Self::Result { + interleave!( + || self.write_newline(), + |expr: &ast::NodeRef| {self.write("@"); self.walk_call_expr(&expr.node)}, + schema_attr.decorators + ); + if !schema_attr.decorators.is_empty() { + self.write_newline(); + } + self.write(&schema_attr.name.node); + if schema_attr.is_optional { + self.write("?"); + } + self.write(": "); + self.write(&schema_attr.type_str.node); + if let Some(op) = &schema_attr.op { + let symbol = match op { + ast::BinOrAugOp::Bin(bin_op) => bin_op.symbol(), + ast::BinOrAugOp::Aug(aug_op) => aug_op.symbol(), + }; + self.write_space(); + self.write(symbol); + self.write_space(); + } + if let Some(value) = &schema_attr.value { + self.expr(&value); + } + self.write_newline_without_fill(); + } + + fn walk_if_expr(&mut self, if_expr: &'ctx ast::IfExpr) -> Self::Result { + self.expr(&if_expr.body); + self.write(" if "); + self.expr(&if_expr.cond); + self.write(" else "); + self.expr(&if_expr.orelse); + } + + fn walk_unary_expr(&mut self, unary_expr: &'ctx ast::UnaryExpr) -> Self::Result { + self.write(unary_expr.op.symbol()); + self.expr(&unary_expr.operand); + } + + fn walk_binary_expr(&mut self, binary_expr: &'ctx ast::BinaryExpr) -> Self::Result { + let symbol = match &binary_expr.op { + ast::BinOrCmpOp::Bin(bin_op) => bin_op.symbol(), + ast::BinOrCmpOp::Cmp(cmp_op) => cmp_op.symbol(), + }; + self.expr(&binary_expr.left); + self.write_space(); + self.write(symbol); + self.write_space(); + self.expr(&binary_expr.right); + } + + fn walk_selector_expr(&mut self, selector_expr: &'ctx ast::SelectorExpr) -> Self::Result { + self.expr(&selector_expr.value); + self.write(if selector_expr.has_question { + "?." + } else { + "." + }); + self.walk_identifier(&selector_expr.attr.node); + } + + fn walk_call_expr(&mut self, call_expr: &'ctx ast::CallExpr) -> Self::Result { + self.expr(&call_expr.func); + self.write("("); + self.write_args_and_kwargs(&call_expr.args, &call_expr.keywords); + self.write(")"); + } + + fn walk_subscript(&mut self, subscript: &'ctx ast::Subscript) -> Self::Result { + self.expr(&subscript.value); + if subscript.has_question { + self.write("?"); + } + self.write("["); + if let Some(index) = &subscript.index { + self.expr(index); + } else { + if let Some(lower) = &subscript.lower { + self.expr(lower); + } + self.write_token(TokenKind::Colon); + if let Some(upper) = &subscript.upper { + self.expr(upper); + } + self.write_token(TokenKind::Colon); + if let Some(step) = &subscript.step { + self.expr(step); + } + } + self.write("]"); + } + + fn walk_paren_expr(&mut self, paren_expr: &'ctx ast::ParenExpr) -> Self::Result { + self.write_token(TokenKind::OpenDelim(DelimToken::Paren)); + self.expr(&paren_expr.expr); + self.write_token(TokenKind::CloseDelim(DelimToken::Paren)); + } + + fn walk_list_expr(&mut self, list_expr: &'ctx ast::ListExpr) -> Self::Result { + let line_set = list_expr + .elts + .iter() + .map(|e| e.line) + .collect::>(); + let mut in_one_line = line_set.len() <= 1; + if let Some(elt) = list_expr.elts.first() { + if let ast::Expr::ListIfItem(_) = &elt.node { + in_one_line = false; + } + } + self.write_token(TokenKind::OpenDelim(DelimToken::Bracket)); + if !in_one_line { + self.write_indentation(Indentation::IndentWithNewline); + } + interleave!( + || if in_one_line { + self.write(COMMA_WHITESPACE); + } else { + self.write_newline(); + }, + |elt| self.expr(elt), + list_expr.elts + ); + if !in_one_line { + self.write_indentation(Indentation::DedentWithNewline); + } + self.write_token(TokenKind::CloseDelim(DelimToken::Bracket)); + } + + fn walk_list_comp(&mut self, list_comp: &'ctx ast::ListComp) -> Self::Result { + self.write_token(TokenKind::OpenDelim(DelimToken::Bracket)); + self.expr(&list_comp.elt); + for gen in &list_comp.generators { + self.walk_comp_clause(&gen.node); + } + self.write_token(TokenKind::CloseDelim(DelimToken::Bracket)); + } + + fn walk_list_if_item_expr( + &mut self, + list_if_item_expr: &'ctx ast::ListIfItemExpr, + ) -> Self::Result { + self.write("if "); + self.expr(&list_if_item_expr.if_cond); + self.write(":"); + self.write_indentation(Indentation::IndentWithNewline); + interleave!( + || self.write_newline(), + |expr| self.expr(expr), + list_if_item_expr.exprs + ); + self.write_indentation(Indentation::DedentWithNewline); + if let Some(orelse) = &list_if_item_expr.orelse { + match &orelse.node { + ast::Expr::List(list_expr) => { + self.write("else:"); + self.write_indentation(Indentation::IndentWithNewline); + interleave!( + || self.write_newline(), + |expr| self.expr(expr), + list_expr.elts + ); + self.write_indentation(Indentation::Dedent); + } + ast::Expr::ListIfItem(_) => { + self.write("el"); + self.expr(orelse); + } + _ => bug!("Invalid list if expr orelse node {:?}", orelse.node), + } + } + } + + fn walk_starred_expr(&mut self, starred_expr: &'ctx ast::StarredExpr) -> Self::Result { + self.write("*"); + self.expr(&starred_expr.value) + } + + fn walk_dict_comp(&mut self, dict_comp: &'ctx ast::DictComp) -> Self::Result { + self.write_token(TokenKind::OpenDelim(DelimToken::Brace)); + self.expr(match &dict_comp.entry.key { + Some(key) => key, + None => bug!("Invalid dict comp key"), + }); + if !matches!(dict_comp.entry.operation, ast::ConfigEntryOperation::Union) { + self.write_space(); + } + self.write(dict_comp.entry.operation.symbol()); + self.write_space(); + for gen in &dict_comp.generators { + self.walk_comp_clause(&gen.node); + } + self.write_token(TokenKind::CloseDelim(DelimToken::Brace)); + } + + fn walk_config_if_entry_expr( + &mut self, + config_if_entry_expr: &'ctx ast::ConfigIfEntryExpr, + ) -> Self::Result { + self.write("if "); + self.expr(&config_if_entry_expr.if_cond); + self.write_token(TokenKind::Colon); + self.write_indentation(Indentation::IndentWithNewline); + interleave!( + || self.write_newline(), + |entry: &ast::NodeRef| self.write_entry(entry), + config_if_entry_expr.items + ); + self.write_indentation(Indentation::DedentWithNewline); + if let Some(orelse) = &config_if_entry_expr.orelse { + match &orelse.node { + ast::Expr::Config(config_expr) => { + self.write("else:"); + self.write_indentation(Indentation::IndentWithNewline); + interleave!( + || self.write_newline(), + |entry: &ast::NodeRef| self.write_entry(entry), + config_expr.items + ); + self.write_indentation(Indentation::Dedent); + } + ast::Expr::ConfigIfEntry(_) => { + self.write("el"); + self.expr(orelse); + } + _ => bug!("Invalid config if expr orelse node {:?}", orelse.node), + } + } + } + + fn walk_comp_clause(&mut self, comp_clause: &'ctx ast::CompClause) -> Self::Result { + self.write(" for "); + interleave!( + || self.write(COMMA_WHITESPACE), + |target: &ast::NodeRef| self.walk_identifier(&target.node), + comp_clause.targets + ); + self.write(" in "); + self.expr(&comp_clause.iter); + for if_clause in &comp_clause.ifs { + self.write(" if "); + self.expr(if_clause); + } + } + + fn walk_schema_expr(&mut self, schema_expr: &'ctx ast::SchemaExpr) -> Self::Result { + self.walk_identifier(&schema_expr.name.node); + if !schema_expr.args.is_empty() || !schema_expr.kwargs.is_empty() { + self.write_token(TokenKind::OpenDelim(DelimToken::Paren)); + self.write_args_and_kwargs(&schema_expr.args, &schema_expr.kwargs); + self.write_token(TokenKind::CloseDelim(DelimToken::Paren)); + } + self.write_space(); + self.expr(&schema_expr.config) + } + + fn walk_config_expr(&mut self, config_expr: &'ctx ast::ConfigExpr) -> Self::Result { + let line_set: HashSet = config_expr.items.iter().map(|item| item.line).collect(); + let mut in_one_line = line_set.len() <= 1; + if let Some(item) = config_expr.items.first() { + if let ast::Expr::ConfigIfEntry(_) = &item.node.value.node { + in_one_line = false; + } + } + self.write_token(TokenKind::OpenDelim(DelimToken::Brace)); + if !config_expr.items.is_empty() { + if !in_one_line { + self.write_indentation(Indentation::IndentWithNewline); + } + interleave!( + || if in_one_line { + self.write(COMMA_WHITESPACE); + } else { + self.write_newline(); + }, + |entry: &ast::NodeRef| self.write_entry(entry), + config_expr.items + ); + if !in_one_line { + self.write_indentation(Indentation::DedentWithNewline); + } + } + self.write_token(TokenKind::CloseDelim(DelimToken::Brace)); + } + + fn walk_check_expr(&mut self, check_expr: &'ctx ast::CheckExpr) -> Self::Result { + self.expr(&check_expr.test); + if let Some(if_cond) = &check_expr.if_cond { + self.write(" if "); + self.expr(if_cond); + } + if let Some(msg) = &check_expr.msg { + self.write(COMMA_WHITESPACE); + self.expr(msg); + } + } + + fn walk_lambda_expr(&mut self, lambda_expr: &'ctx ast::LambdaExpr) -> Self::Result { + self.write("lambda"); + if let Some(args) = &lambda_expr.args { + self.write_space(); + self.walk_arguments(&args.node); + } + if let Some(ty_str) = &lambda_expr.return_type_str { + self.write_space(); + self.write_token(TokenKind::RArrow); + self.write_space(); + self.write(ty_str); + } + self.write_space(); + self.write_token(TokenKind::OpenDelim(DelimToken::Brace)); + self.write_newline(); + self.write_indentation(Indentation::Indent); + + // lambda body + self.stmts(&lambda_expr.body); + + self.write_indentation(Indentation::Dedent); + self.write_newline(); + self.write_token(TokenKind::CloseDelim(DelimToken::Brace)); + } + + fn walk_keyword(&mut self, keyword: &'ctx ast::Keyword) -> Self::Result { + self.walk_identifier(&keyword.arg.node); + if let Some(value) = &keyword.value { + self.write("="); + self.expr(value); + } + } + + fn walk_arguments(&mut self, arguments: &'ctx ast::Arguments) -> Self::Result { + let parameter_zip_list: Vec> = arguments + .args + .iter() + .zip(arguments.type_annotation_list.iter()) + .zip(arguments.defaults.iter()) + .collect(); + interleave!( + || self.write(COMMA_WHITESPACE), + |para: &ParameterType<'_>| { + let ((arg, ty_str), default) = para; + self.walk_identifier(&arg.node); + if let Some(ty_str) = ty_str { + self.write(&format!(": {}", ty_str.node)); + } + if let Some(default) = default { + self.write(" = "); + self.expr(default); + } + }, + parameter_zip_list + ); + } + + fn walk_compare(&mut self, compare: &'ctx ast::Compare) -> Self::Result { + self.expr(&compare.left); + for (op, expr) in compare.ops.iter().zip(compare.comparators.iter()) { + self.write_space(); + self.write(op.symbol()); + self.write_space(); + self.expr(expr); + } + } + + #[inline] + fn walk_identifier(&mut self, identifier: &'ctx ast::Identifier) -> Self::Result { + self.write(&identifier.get_name()); + } + + fn walk_number_lit(&mut self, number_lit: &'ctx ast::NumberLit) -> Self::Result { + match number_lit.value { + ast::NumberLitValue::Int(int_val) => self.write(&int_val.to_string()), + ast::NumberLitValue::Float(float_val) => self.write(&float_val.to_string()), + } + } + + fn walk_string_lit(&mut self, string_lit: &'ctx ast::StringLit) -> Self::Result { + if !string_lit.raw_value.is_empty() { + self.write(&string_lit.raw_value) + } else { + self.write(&format!("\"{}\"", string_lit.value.replace('\"', "\\\""))); + } + } + + #[inline] + fn walk_name_constant_lit( + &mut self, + name_constant_lit: &'ctx ast::NameConstantLit, + ) -> Self::Result { + self.write(name_constant_lit.value.symbol()); + } + + fn walk_joined_string(&mut self, joined_string: &'ctx ast::JoinedString) -> Self::Result { + self.write("\""); + for value in &joined_string.values { + match &value.node { + ast::Expr::StringLit(string_lit) => { + self.write(&string_lit.value.replace('\"', "\\\"")); + } + _ => self.expr(value), + } + } + self.write("\""); + } + + fn walk_formatted_value(&mut self, formatted_value: &'ctx ast::FormattedValue) -> Self::Result { + self.write("${"); + self.expr(&formatted_value.value); + if let Some(spec) = &formatted_value.format_spec { + self.write(&format!(": {}", spec)); + } + self.write("}"); + } + + fn walk_comment(&mut self, comment: &'ctx ast::Comment) -> Self::Result { + self.writeln(&comment.text); + self.fill(""); + } +} + +impl<'p> Printer<'p> { + pub fn write_args_and_kwargs( + &mut self, + args: &[ast::NodeRef], + kwargs: &[ast::NodeRef], + ) { + interleave!(|| self.write(COMMA_WHITESPACE), |arg| self.expr(arg), args); + if !args.is_empty() && !kwargs.is_empty() { + self.write(COMMA_WHITESPACE); + } + interleave!( + || self.write(COMMA_WHITESPACE), + |kwarg: &ast::NodeRef| self.walk_keyword(&kwarg.node), + kwargs + ); + } + + pub fn write_entry(&mut self, item: &ast::NodeRef) { + match &item.node.key { + Some(key) => { + let print_right_brace_count = self.write_config_key(key); + if item.node.insert_index >= 0 { + self.write(&format!("[{}]", item.node.insert_index)); + } + if !matches!(item.node.operation, ast::ConfigEntryOperation::Union) { + self.write_space(); + } + self.write(item.node.operation.symbol()); + self.write_space(); + self.expr(&item.node.value); + self.write(&"}".repeat(print_right_brace_count)); + } + None => { + if !matches!(&item.node.value.node, ast::Expr::ConfigIfEntry(_)) { + self.write("**"); + } + self.expr(&item.node.value) + } + }; + } + + fn write_config_key(&mut self, key: &ast::NodeRef) -> usize { + match &key.node { + ast::Expr::Identifier(identifier) => { + self.hook.pre(self, super::ASTNode::Expr(key)); + self.write_ast_comments(key); + // Judge contains string identifier, e.g., "x-y-z" + let names = &identifier.names; + + let re = fancy_regex::Regex::new(IDENTIFIER_REGEX).unwrap(); + let need_right_brace = !names.iter().all(|n| re.is_match(n).unwrap_or(false)); + let count = if need_right_brace { + self.write( + &names + .iter() + .map(|n| n.replace('\"', "\\\"")) + .collect::>() + .join(": {"), + ); + names.len() - 1 + } else { + self.expr(key); + 0 + }; + self.hook.post(self, super::ASTNode::Expr(key)); + count + } + _ => { + self.expr(key); + 0 + } + } + } +} + +impl<'p> Printer<'p> { + // ------------------------------ + // Expr and Stmt walker functions + // ------------------------------ + + pub fn expr(&mut self, expr: &ast::NodeRef) { + self.hook.pre(self, super::ASTNode::Expr(expr)); + self.write_ast_comments(expr); + self.walk_expr(&expr.node); + self.hook.post(self, super::ASTNode::Expr(expr)); + } + + pub fn stmt(&mut self, stmt: &ast::NodeRef) { + self.hook.pre(self, super::ASTNode::Stmt(stmt)); + self.fill(""); + self.write_ast_comments(stmt); + self.walk_stmt(&stmt.node); + self.hook.post(self, super::ASTNode::Stmt(stmt)); + } + + pub fn exprs(&mut self, exprs: &[ast::NodeRef]) { + for expr in exprs { + self.expr(expr); + } + } + + pub fn stmts(&mut self, stmts: &[ast::NodeRef]) { + for stmt in stmts { + self.stmt(stmt); + } + } +} diff --git a/kclvm/tools/src/printer/test_data/arguments.input b/kclvm/tools/src/printer/test_data/arguments.input new file mode 100644 index 000000000..d356f83fb --- /dev/null +++ b/kclvm/tools/src/printer/test_data/arguments.input @@ -0,0 +1,6 @@ +schema Config[nameVar, textVar: str, idVar: int = 1]: + name: str = nameVar + text: str = textVar + id?: int = idVar + +config = Config("test", "text") diff --git a/kclvm/tools/src/printer/test_data/arguments.output b/kclvm/tools/src/printer/test_data/arguments.output new file mode 100644 index 000000000..d356f83fb --- /dev/null +++ b/kclvm/tools/src/printer/test_data/arguments.output @@ -0,0 +1,6 @@ +schema Config[nameVar, textVar: str, idVar: int = 1]: + name: str = nameVar + text: str = textVar + id?: int = idVar + +config = Config("test", "text") diff --git a/kclvm/tools/src/printer/test_data/codelayout.input b/kclvm/tools/src/printer/test_data/codelayout.input new file mode 100644 index 000000000..b8eeb768d --- /dev/null +++ b/kclvm/tools/src/printer/test_data/codelayout.input @@ -0,0 +1,76 @@ + + + +import math as alias_math +schema Person ( Base): + name:str + age:int + check : + age>0 if age , "age must > 0" +person = Person{ + name:"Alice" + age:18 +} +if True: + a = 1 +elif True: + b = 2 +else: + c = 3 +d = 1 + 2 +e = ( 1 + 2 ) +f=[ 1, 2, 3 ] +g = { "key" : "value" } +print (1) +dct={"key": "value"} +lst=[1,2,3] +h = dct [ 'key' ] +i = lst [ 1 ] +x = 1 +y = 2 +long_variable = 3 +i = i+1 +submitted+=1 +x = x*2 - 1 +hypot2 = x*x + y*y +_c = (a+b) * (a-b) +_b=2 +_c= 3 +_d =4 + +_value = (1 + 2 * 3) +_value = (1+2*3) +_value =1+ - 2 * ~ 3 +_list = [1, 2, 3] +_list = [*_list, [4, 5 ,6]] +_list = [* _list, [4, 5 ,6]] + +_dict = {** {"k": "v"}, ** {"k": "v"}} +a = [1,2,3] +b = [ + 1,2,3, + 4,5,6, +] +_dict={ + "k1":"v1" + "k2" :"v2" + "k3": "v3" + "k4" : "v4" + "k5" : "v5" +} +foo=1 +if foo is not None: + _a = 1 + _dict|={} + hello = "world{}" . format( 1 )[2 : 4] . lower( ) + range_int = [ i for i in range( 10 ) ] +op = 1+2 - - 3 + (3 - 1) // 3 +op += 1 +op -= 12 + 23 +print( " " , end= '') +log = math. log(12) +aa = 1 +assert aa == 1,"message" +assert aa == 1 if aa,"message" +aaaa = (1 + 2 / 2) if _a == 2 + + 134.3 else ("a"*3) +bbbb = "{}". format(a) \ No newline at end of file diff --git a/kclvm/tools/src/printer/test_data/codelayout.output b/kclvm/tools/src/printer/test_data/codelayout.output new file mode 100644 index 000000000..61df4a554 --- /dev/null +++ b/kclvm/tools/src/printer/test_data/codelayout.output @@ -0,0 +1,80 @@ +import math as alias_math +schema Person(Base): + name: str + age: int + + check: + age > 0 if age, "age must > 0" + +person = Person { + name: "Alice" + age: 18 +} + +if True: + a = 1 +elif True: + b = 2 +else: + c = 3 + +d = 1 + 2 +e = (1 + 2) +f = [1, 2, 3] +g = {"key": "value"} +print(1) +dct = {"key": "value"} +lst = [1, 2, 3] +h = dct['key'] +i = lst[1] +x = 1 +y = 2 +long_variable = 3 +i = i + 1 +submitted += 1 +x = x * 2 - 1 +hypot2 = x * x + y * y +_c = (a + b) * (a - b) +_b = 2 +_c = 3 +_d = 4 +_value = (1 + 2 * 3) +_value = (1 + 2 * 3) +_value = 1 + -2 * ~3 +_list = [1, 2, 3] +_list = [*_list, [4, 5, 6]] +_list = [*_list, [4, 5, 6]] +_dict = {**{"k": "v"}, **{"k": "v"}} +a = [1, 2, 3] +b = [ + 1 + 2 + 3 + 4 + 5 + 6 +] +_dict = { + "k1": "v1" + "k2": "v2" + "k3": "v3" + "k4": "v4" + "k5": "v5" +} +foo = 1 +if foo is not None: + _a = 1 + _dict |= {} + hello = "world{}".format(1)[2:4:].lower() + range_int = [i for i in range(10)] + +op = 1 + 2 - -3 + (3 - 1) // 3 +op += 1 +op -= 12 + 23 +print(" ", end='') +log = math.log(12) +aa = 1 +assert aa == 1, "message" +assert aa == 1 if aa, "message" +aaaa = (1 + 2 / 2) if _a == 2 + +134.3 else ("a" * 3) +bbbb = "{}".format(a) diff --git a/kclvm/tools/src/printer/test_data/collection_if.input b/kclvm/tools/src/printer/test_data/collection_if.input new file mode 100644 index 000000000..080538c1c --- /dev/null +++ b/kclvm/tools/src/printer/test_data/collection_if.input @@ -0,0 +1,51 @@ +schema Config: + name: str + env: str + data: [int] +env = "env" +data1 = Config { + if env == "env": + name: env + env: env + data += [0] + else: + name = "name" + env = "name" + data += [1] +} +data1 = Config { + if env == "env": + name: env + env: env + else: + name: "name" + env: "name" +} +data2 = Config { + if env != "env": + name: env + env: env + else: + name: "name" + env: "name" +} +data3 = { + if True: + key1: "value1" + elif True: + key2: "value2" + elif True: + key3: "value3" + else: + key4: "value4" +} +data4 = [ + if True: + "value1" + elif True: + "value2" + elif True: + "value3" + else: + "value4" +] diff --git a/kclvm/tools/src/printer/test_data/collection_if.output b/kclvm/tools/src/printer/test_data/collection_if.output new file mode 100644 index 000000000..009f8947a --- /dev/null +++ b/kclvm/tools/src/printer/test_data/collection_if.output @@ -0,0 +1,55 @@ +schema Config: + name: str + env: str + data: [int] + +env = "env" +data1 = Config { + if env == "env": + name: env + env: env + data += [0] + else: + name = "name" + env = "name" + data += [1] +} + +data1 = Config { + if env == "env": + name: env + env: env + else: + name: "name" + env: "name" +} + +data2 = Config { + if env != "env": + name: env + env: env + else: + name: "name" + env: "name" +} + +data3 = { + if True: + key1: "value1" + elif True: + key2: "value2" + elif True: + key3: "value3" + else: + key4: "value4" +} +data4 = [ + if True: + "value1" + elif True: + "value2" + elif True: + "value3" + else: + "value4" +] diff --git a/kclvm/tools/src/printer/test_data/comment.input b/kclvm/tools/src/printer/test_data/comment.input new file mode 100644 index 000000000..681a69c31 --- /dev/null +++ b/kclvm/tools/src/printer/test_data/comment.input @@ -0,0 +1,38 @@ +# Comment One +schema Main: + name?: str + env?: [{str:}] + +# Comment Two +schema AppConfiguration: + appName: str + image: str + overQuota: bool = False + resource: {str:} + mainContainer?: Main + labels: {str:} + +# Comment Three +appConfiguration = AppConfiguration { + # Comment Four + appName: "kusion" + image: "test-image:v1" # Comment Five + resource: { + cpu: "4" + disk: "50Gi" + memory: "12Gi" + } + labels: { + key: { + key: 12 + } + } + # Comment Six + mainContainer: Main { + name: "kusion_override" + }# Comment Seven + + # Comment Eight + overQuota: True +} +# Comment Nine diff --git a/kclvm/tools/src/printer/test_data/comment.output b/kclvm/tools/src/printer/test_data/comment.output new file mode 100644 index 000000000..4689cf1af --- /dev/null +++ b/kclvm/tools/src/printer/test_data/comment.output @@ -0,0 +1,40 @@ +# Comment One +schema Main: + name?: str + env?: [{str:}] + +# Comment Two +schema AppConfiguration: + appName: str + image: str + overQuota: bool = False + resource: {str:} + mainContainer?: Main + labels: {str:} + +# Comment Three +appConfiguration = AppConfiguration { + # Comment Four + appName: "kusion" + # Comment Five + image: "test-image:v1" + resource: { + cpu: "4" + disk: "50Gi" + memory: "12Gi" + } + labels: { + key: { + key: 12 + } + } + # Comment Six + mainContainer: Main { + name: "kusion_override" + } + # Comment Seven + # Comment Eight + overQuota: True +} + +# Comment Nine diff --git a/kclvm/tools/src/printer/test_data/empty.input b/kclvm/tools/src/printer/test_data/empty.input new file mode 100644 index 000000000..fd40910d9 --- /dev/null +++ b/kclvm/tools/src/printer/test_data/empty.input @@ -0,0 +1,4 @@ + + + + diff --git a/kclvm/tools/src/printer/test_data/empty.output b/kclvm/tools/src/printer/test_data/empty.output new file mode 100644 index 000000000..e69de29bb diff --git a/kclvm/tools/src/printer/test_data/index_sign.input b/kclvm/tools/src/printer/test_data/index_sign.input new file mode 100644 index 000000000..54d611873 --- /dev/null +++ b/kclvm/tools/src/printer/test_data/index_sign.input @@ -0,0 +1,12 @@ +schema Data: + [str]: any + +schema DataOther: + [...str]: str + id?: int + +schema DataMap: + [name: str]: str + check: + name in ["A", "B", "C"] + \ No newline at end of file diff --git a/kclvm/tools/src/printer/test_data/index_sign.output b/kclvm/tools/src/printer/test_data/index_sign.output new file mode 100644 index 000000000..7da6687ac --- /dev/null +++ b/kclvm/tools/src/printer/test_data/index_sign.output @@ -0,0 +1,13 @@ +schema Data: + [str]: any + +schema DataOther: + [...str]: str + id?: int + +schema DataMap: + [name: str]: str + + check: + name in ["A", "B", "C"] + diff --git a/kclvm/tools/src/printer/test_data/joined_str.input b/kclvm/tools/src/printer/test_data/joined_str.input new file mode 100644 index 000000000..8af336d4a --- /dev/null +++ b/kclvm/tools/src/printer/test_data/joined_str.input @@ -0,0 +1,3 @@ +a = 1 +b = "${a}" +c = "a.${1}" diff --git a/kclvm/tools/src/printer/test_data/joined_str.output b/kclvm/tools/src/printer/test_data/joined_str.output new file mode 100644 index 000000000..8af336d4a --- /dev/null +++ b/kclvm/tools/src/printer/test_data/joined_str.output @@ -0,0 +1,3 @@ +a = 1 +b = "${a}" +c = "a.${1}" diff --git a/kclvm/tools/src/printer/test_data/lambda.input b/kclvm/tools/src/printer/test_data/lambda.input new file mode 100644 index 000000000..32142fc2e --- /dev/null +++ b/kclvm/tools/src/printer/test_data/lambda.input @@ -0,0 +1,21 @@ +sumFunc1 = lambda x, y { + z = x + y + z + x + +} +sumFunc2 = lambda x, y = 1 { + x + y + +} +sumFunc3 = lambda x = 1, y = 1 { + x + y + +} +sumFunc4 = lambda x: int = 1, y: int = 1 -> int { + x + y + +} +x0 = sumFunc1(1, 2) +x1 = sumFunc1(2, 3) +x2 = sumFunc1(3, 4) +x3 = sumFunc1(4, 5) diff --git a/kclvm/tools/src/printer/test_data/lambda.output b/kclvm/tools/src/printer/test_data/lambda.output new file mode 100644 index 000000000..169ae8a1c --- /dev/null +++ b/kclvm/tools/src/printer/test_data/lambda.output @@ -0,0 +1,21 @@ +sumFunc1 = lambda x, y { + z = x + y + z + x + +} +sumFunc2 = lambda x, y = 1 { + x + y + +} +sumFunc3 = lambda x = 1, y = 1 { + x + y + +} +sumFunc4 = lambda x: int = 1, y: int = 1 -> int { + x + y + +} +x0 = sumFunc1(1, 2) +x1 = sumFunc1(2, 3) +x2 = sumFunc1(3, 4) +x3 = sumFunc1(4, 5) diff --git a/kclvm/tools/src/printer/test_data/quant.input b/kclvm/tools/src/printer/test_data/quant.input new file mode 100644 index 000000000..23022c694 --- /dev/null +++ b/kclvm/tools/src/printer/test_data/quant.input @@ -0,0 +1,8 @@ +a = all x in [-1, 0, 1, 2, 3] { + x >= 1 if x > 0 +} +b = any x in {k1 = "v1", k2 = "v2"} {x in ["k1", "v2"]} +c = map x in {k1 = "v1", k2 = "v2"} {x} +d = filter x in [1, 2, 3] { + x > 1 +} \ No newline at end of file diff --git a/kclvm/tools/src/printer/test_data/quant.output b/kclvm/tools/src/printer/test_data/quant.output new file mode 100644 index 000000000..4d35a3ba6 --- /dev/null +++ b/kclvm/tools/src/printer/test_data/quant.output @@ -0,0 +1,12 @@ +a = all x in [-1, 0, 1, 2, 3] { + x >= 1 if x > 0 +} +b = any x in {k1 = "v1", k2 = "v2"} { + x in ["k1", "v2"] +} +c = map x in {k1 = "v1", k2 = "v2"} { + x +} +d = filter x in [1, 2, 3] { + x > 1 +} diff --git a/kclvm/tools/src/printer/test_data/rule.input b/kclvm/tools/src/printer/test_data/rule.input new file mode 100644 index 000000000..662e8eec3 --- /dev/null +++ b/kclvm/tools/src/printer/test_data/rule.input @@ -0,0 +1,19 @@ +age = 1 + +schema MainProtocol: + """Protocol doc""" + var: int + +schema MainMixin for MainProtocol: + var: int + +@deprecated +rule Base: + """Rule doc""" + age > 0 + age < 10 + +rule Main[var](Base) for MainProtocol: + var + +Main(1) diff --git a/kclvm/tools/src/printer/test_data/rule.output b/kclvm/tools/src/printer/test_data/rule.output new file mode 100644 index 000000000..6d4543302 --- /dev/null +++ b/kclvm/tools/src/printer/test_data/rule.output @@ -0,0 +1,16 @@ +age = 1 +protocol MainProtocol: + """Protocol doc""" + var: int + +mixin MainMixin for MainProtocol: + var: int + +@deprecated() +rule Base: + """Rule doc""" + age > 0 + age < 10 +rule Main[var](Base) for MainProtocol: + var +Main(1) diff --git a/kclvm/tools/src/printer/test_data/type_alias.input b/kclvm/tools/src/printer/test_data/type_alias.input new file mode 100644 index 000000000..9924443d4 --- /dev/null +++ b/kclvm/tools/src/printer/test_data/type_alias.input @@ -0,0 +1,18 @@ +type Color = "Red" | "Yellow" | "Blue" + +colorRed: Color = "Red" +colorYellow: Color = "Yellow" +colorBlue: Color = "Blue" + +schema Data: + color: Color + +dataColorRed = Data { + color = "Red" +} +dataColorYellow = Data { + color = "Yellow" +} +dataColorBlue = Data { + color = "Blue" +} diff --git a/kclvm/tools/src/printer/test_data/type_alias.output b/kclvm/tools/src/printer/test_data/type_alias.output new file mode 100644 index 000000000..d80ac1c1b --- /dev/null +++ b/kclvm/tools/src/printer/test_data/type_alias.output @@ -0,0 +1,13 @@ +type Color = "Red"|"Yellow"|"Blue" +colorRed: Color = "Red" +colorYellow: Color = "Yellow" +colorBlue: Color = "Blue" +schema Data: + color: Color + +dataColorRed = Data {color = "Red"} + +dataColorYellow = Data {color = "Yellow"} + +dataColorBlue = Data {color = "Blue"} + diff --git a/kclvm/tools/src/printer/test_data/unification.input b/kclvm/tools/src/printer/test_data/unification.input new file mode 100644 index 000000000..22d05d0bb --- /dev/null +++ b/kclvm/tools/src/printer/test_data/unification.input @@ -0,0 +1,5 @@ +schema Config: + name: str +config: Config { + name = "name" +} \ No newline at end of file diff --git a/kclvm/tools/src/printer/test_data/unification.output b/kclvm/tools/src/printer/test_data/unification.output new file mode 100644 index 000000000..4db7bd2d5 --- /dev/null +++ b/kclvm/tools/src/printer/test_data/unification.output @@ -0,0 +1,4 @@ +schema Config: + name: str + +config: Config {name = "name"} diff --git a/kclvm/tools/src/printer/tests.rs b/kclvm/tools/src/printer/tests.rs new file mode 100644 index 000000000..f5651afa2 --- /dev/null +++ b/kclvm/tools/src/printer/tests.rs @@ -0,0 +1,44 @@ +use super::*; +use kclvm_parser::parse_file; +use pretty_assertions::assert_eq; + +const FILE_INPUT_SUFFIX: &str = ".input"; +const FILE_OUTPUT_SUFFIX: &str = ".output"; +const TEST_CASES: &[&'static str; 10] = &[ + "arguments", + "empty", + "codelayout", + "collection_if", + // "comment", + "index_sign", + "joined_str", + // "lambda", + "quant", + "rule", + "type_alias", + "unification", +]; + +fn read_data(data_name: &str) -> (String, String) { + let module = parse_file( + &format!("./src/printer/test_data/{}{}", data_name, FILE_INPUT_SUFFIX), + None, + ); + + ( + print_ast_module(&module), + std::fs::read_to_string(&format!( + "./src/printer/test_data/{}{}", + data_name, FILE_OUTPUT_SUFFIX + )) + .unwrap(), + ) +} + +#[test] +fn test_ast_printer() { + for case in TEST_CASES { + let (data_input, data_output) = read_data(case); + assert_eq!(data_input, data_output, "Test failed on {}", case); + } +} diff --git a/kclvm/tools/src/query/mod.rs b/kclvm/tools/src/query/mod.rs new file mode 100644 index 000000000..54781f51d --- /dev/null +++ b/kclvm/tools/src/query/mod.rs @@ -0,0 +1,3 @@ +pub mod r#override; + +pub use r#override::apply_overrides; diff --git a/kclvm/tools/src/query/override.rs b/kclvm/tools/src/query/override.rs new file mode 100644 index 000000000..e512d0f58 --- /dev/null +++ b/kclvm/tools/src/query/override.rs @@ -0,0 +1,181 @@ +use kclvm_ast::walker::MutSelfMutWalker; +use kclvm_ast::{ast, walk_if_mut}; +use kclvm_parser::parse_expr; + +pub struct OverrideInfo { + pub pkgpath: String, + pub filename: String, + pub module: ast::Module, +} + +pub fn apply_overrides( + prog: &mut ast::Program, + overrides: &[ast::CmdOverrideSpec], + _import_paths: &[String], +) { + for o in overrides { + let pkgpath = if o.pkgpath.is_empty() { + &prog.main + } else { + &o.pkgpath + }; + match prog.pkgs.get_mut(pkgpath) { + Some(modules) => { + for m in modules.iter_mut() { + if fix_module_override(m, o) {} + // module_add_import_paths(m, import_paths) + } + } + None => {} + } + } +} + +pub fn fix_module_override(m: &mut ast::Module, o: &ast::CmdOverrideSpec) -> bool { + let ss = o.field_path.split(".").collect::>(); + if ss.len() <= 1 { + false + } else { + let target_id = ss[0]; + let field = ss[1..].join("."); + let value = &o.field_value; + let key = ast::Identifier { + names: field.split(".").map(|s| s.to_string()).collect(), + ctx: ast::ExprContext::Store, + pkgpath: "".to_string(), + }; + let val = build_node_from_string(value); + let mut transformer = OverrideTransformer { + target_id: target_id.to_string(), + field_path: field, + override_key: key, + override_value: val, + override_target_count: 0, + has_override: false, + action: o.action.clone(), + }; + transformer.walk_module(m); + transformer.has_override + } +} + +pub fn build_node_from_string(value: &str) -> ast::NodeRef { + let expr = parse_expr(value); + expr +} + +struct OverrideTransformer { + pub target_id: String, + pub field_path: String, + pub override_key: ast::Identifier, + pub override_value: ast::NodeRef, + pub override_target_count: usize, + pub has_override: bool, + pub action: ast::OverrideAction, +} + +impl<'ctx> MutSelfMutWalker<'ctx> for OverrideTransformer { + fn walk_schema_stmt(&mut self, _: &'ctx mut ast::SchemaStmt) { + // Do not override AssignStmt in SchemaStmt + } + + fn walk_unification_stmt(&mut self, unification_stmt: &'ctx mut ast::UnificationStmt) { + if unification_stmt.target.node.names[0] != self.target_id { + return; + } + self.override_target_count = 1; + self.has_override = true; + self.walk_schema_expr(&mut unification_stmt.value.node); + } + + fn walk_assign_stmt(&mut self, assign_stmt: &'ctx mut ast::AssignStmt) { + if let ast::Expr::Schema(_) = &assign_stmt.value.node { + self.override_target_count = 0; + for target in &assign_stmt.targets { + if target.node.names.len() != 1 { + continue; + } + if target.node.names[0] != self.target_id { + continue; + } + self.override_target_count += 1; + } + if self.override_target_count == 0 { + return; + } + self.has_override = true; + self.walk_expr(&mut assign_stmt.value.node); + } + } + + fn walk_schema_expr(&mut self, schema_expr: &'ctx mut ast::SchemaExpr) { + if self.override_target_count == 0 { + return; + } + if true { + // Not exist and append an override value when the action is CREATE_OR_UPDATE + if let ast::OverrideAction::CreateOrUpdate = self.action { + if let ast::Expr::Config(config_expr) = &mut schema_expr.config.node { + config_expr + .items + .push(Box::new(ast::Node::dummy_node(ast::ConfigEntry { + key: Some(Box::new(ast::Node::dummy_node(ast::Expr::Identifier( + self.override_key.clone(), + )))), + value: self.override_value.clone(), + operation: ast::ConfigEntryOperation::Override, + insert_index: -1, + }))); + } + } + } + self.override_target_count = 0; + } + + fn walk_config_expr(&mut self, config_expr: &'ctx mut ast::ConfigExpr) { + for config_entry in config_expr.items.iter_mut() { + walk_if_mut!(self, walk_expr, config_entry.node.key); + self.walk_expr(&mut config_entry.node.value.node); + } + } +} + +impl OverrideTransformer { + pub(crate) fn _get_schema_config_field_paths( + &mut self, + schema_expr: &mut ast::SchemaExpr, + ) -> (Vec, Vec) { + if let ast::Expr::Config(config_expr) = &mut schema_expr.config.node { + self._get_config_field_paths(config_expr) + } else { + (vec![], vec![]) + } + } + pub(crate) fn _get_config_field_paths( + &mut self, + config: &mut ast::ConfigExpr, + ) -> (Vec, Vec) { + let mut paths = vec![]; + let mut paths_with_id = vec![]; + for entry in config.items.iter_mut() { + let (mut _paths, mut _paths_with_id) = self._get_key_value_paths(&mut entry.node); + paths.append(&mut _paths); + paths_with_id.append(&mut &mut _paths_with_id); + } + (paths, paths_with_id) + } + pub(crate) fn _get_key_value_paths( + &mut self, + _entry: &mut ast::ConfigEntry, + ) -> (Vec, Vec) { + (vec![], vec![]) + } + pub(crate) fn _find_schema_config_and_repalce( + &mut self, + _schema_config: &mut ast::SchemaExpr, + _field_path: &str, + _value: &ast::NodeRef, + ) -> bool { + false + } +} diff --git a/kclvm/version/Cargo.lock b/kclvm/version/Cargo.lock new file mode 100644 index 000000000..4c730005b --- /dev/null +++ b/kclvm/version/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "kclvm-version" +version = "0.1.0" diff --git a/kclvm/version/Cargo.toml b/kclvm/version/Cargo.toml new file mode 100644 index 000000000..ed73f8905 --- /dev/null +++ b/kclvm/version/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "kclvm-version" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/kclvm/version/src/lib.rs b/kclvm/version/src/lib.rs new file mode 100644 index 000000000..1067fdf32 --- /dev/null +++ b/kclvm/version/src/lib.rs @@ -0,0 +1,8 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +pub const VERSION: &str = "0.4.1"; +pub const CHECK_SUM: &str = "e07ed7af0d9bd1e86a3131714e4bd20c"; + +pub fn get_full_version() -> String { + format!("{}-{}", VERSION, CHECK_SUM) +} diff --git a/run.sh b/run.sh new file mode 100755 index 000000000..38bbbf578 --- /dev/null +++ b/run.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +# Environment +if [ -f "/etc/os-release" ]; then + source /etc/os-release + os=$ID +else + os=$(uname) +fi +topdir=$PWD + +# Options +help_message=$(cat <<-END + Usage: + run.sh -h + Print this help message + run.sh -a [action] + Perform an action + run.sh + Perform an action interactively + Available actions: + build + Build everything + build-cpython + Configure and build CPython + build-kclvm + Package CPython and the KCLVM extension into KCLVM + update-kclvm + Quickly update KCLVM without packaging CPython and site-packages + test + Perform testing + release + Create a package for releasing +END +) +action= +while getopts "a:h:s:" opt; do + case $opt in + a) + action="$OPTARG" + ;; + h) + echo "$help_message" + exit 1 + ;; + s) + sslpath="$OPTARG" + ;; + \?) echo "Invalid option -$OPTARG" + ;; + esac +done + +if [ "$action" == "" ]; then + PS3='Please select the action: ' + options=("build" "build-cpython" "build-kclvm" "update-kclvm" "test" "release") + select action in "${options[@]}" + do + case $action in + "build") + break + ;; + "build-cpython") + break + ;; + "build-kclvm") + break + ;; + "update-kclvm") + break + ;; + "test") + break + ;; + "release") + break + ;; + *) echo "Invalid action $REPLY:$action" + exit 1 + break + ;; + esac + done +fi + +os=$os topdir=$topdir sslpath=$sslpath $topdir/internal/kclvm_py/scripts/$action.sh diff --git a/samples/fib.k b/samples/fib.k new file mode 100644 index 000000000..fcb164f58 --- /dev/null +++ b/samples/fib.k @@ -0,0 +1,14 @@ +schema Fib: + n1: int = n - 1 + n2: int = n1 - 1 + n: int + value: int + + if n <= 1: + value = 1 + elif n == 2: + value = 1 + else: + value = Fib {n: n1}.value + Fib {n: n2}.value + +fib8 = Fib {n: 8}.value diff --git a/samples/hello.k b/samples/hello.k new file mode 100644 index 000000000..00df92b0c --- /dev/null +++ b/samples/hello.k @@ -0,0 +1,11 @@ +name = "kcl" +age = 1 + +schema Person: + name: str = "kcl" + age: int = 1 + +x0 = Person {} +x1 = Person { + age = 101 +} diff --git a/scripts/build-windows/.gitignore b/scripts/build-windows/.gitignore new file mode 100644 index 000000000..3d0dd25cd --- /dev/null +++ b/scripts/build-windows/.gitignore @@ -0,0 +1,4 @@ +python-*.zip +_output* +build +*.exe diff --git a/scripts/build-windows/Makefile b/scripts/build-windows/Makefile new file mode 100644 index 000000000..0360c624b --- /dev/null +++ b/scripts/build-windows/Makefile @@ -0,0 +1,60 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +# Only on windows platform: use mingw32-make to run. + +default: + go run download-file.go + go run unzip.go + + go run gen_pth.go + + # install pip + _output/kclvm-windows/python.exe get-pip.py + + # pip install -r ../requirements.txt + _output/kclvm-windows/python.exe -m pip install -r ../requirements.release.txt --target=_output/kclvm-windows/Lib/site-packages + + # install kclvm + go run gen-kclvm-py.go + + # renname + go run rename.go -old="_output/kclvm-windows/python.exe" -new="_output/kclvm-windows/kclvm.exe" + + # install python39 include and libs + go run ./copy-dir.go ./py39-libs ./_output/kclvm-windows + + # install kclvm-runtime + cd ../../kclvm/runtime && cargo build --release + go run ./copy-file.go -src=../../kclvm/runtime/target/release/kclvm.dll -dst=./_output/kclvm-windows/libs/kclvm.dll + go run ./copy-file.go -src=../../kclvm/runtime/target/release/kclvm.dll.lib -dst=./_output/kclvm-windows/libs/kclvm.dll.lib + go run ./copy-file.go -src=../../kclvm/runtime/src/_kclvm.ll -dst=./_output/kclvm-windows/libs/_kclvm.ll + go run ./copy-file.go -src=../../kclvm/runtime/src/_kclvm.bc -dst=./_output/kclvm-windows/libs/_kclvm.bc + go run ./copy-file.go -src=../../kclvm/runtime/src/_kclvm.h -dst=./_output/kclvm-windows/libs/_kclvm.h + go run ./copy-file.go -src=../../kclvm/runtime/src/_kclvm_main_win.c -dst=./_output/kclvm-windows/libs/_kclvm_main_win.c + + # install kclvm-plugin + ./_output/kclvm-windows/kclvm.exe ../../kclvm/plugin/setup.py install_lib + + # install kclvm-cli + cd ../../kclvm/runtime && cargo build --release + + # install hello.k + go run ./copy-file.go -src=../../hello.k -dst=./_output/kclvm-windows/hello.k + + # install tools + go build -o ./_output/kclvm-windows/kcl.exe kcl.go + go build -o ./_output/kclvm-windows/kcl-doc.exe kcl-doc.go + go build -o ./_output/kclvm-windows/kcl-lint.exe kcl-lint.go + go build -o ./_output/kclvm-windows/kcl-fmt.exe kcl-fmt.go + go build -o ./_output/kclvm-windows/kcl-plugin.exe kcl-plugin.go + go build -o ./_output/kclvm-windows/kcl-vet.exe kcl-vet.go + go build -o ./_output/kclvm-windows/kcl-fmt.exe kcl-fmt.go + + # run hello.k + _output/kclvm-windows/kclvm.exe -m kclvm ../../hello.k + _output/kclvm-windows/kcl-go.exe run ../../hello.k + _output/kclvm-windows/kcl.exe ../../hello.k + + +clean: + diff --git a/scripts/build-windows/build-win-installer.nsi b/scripts/build-windows/build-win-installer.nsi new file mode 100644 index 000000000..138069229 --- /dev/null +++ b/scripts/build-windows/build-win-installer.nsi @@ -0,0 +1,50 @@ +; Copyright 2021 The KCL Authors. All rights reserved. + +; makensis.exe build-win-installer.nsi + +!include LogicLib.nsh + +;-------------------------------- + +; The name of the installer +Name "KCLVM" + +; The file to write +OutFile "kclvm-installer.exe" + +; Request application privileges for Windows Vista +RequestExecutionLevel user + +; Build Unicode installer +Unicode True + +; The default installation directory + +InstallDir $PROFILE\.kusion\kclvm + +;-------------------------------- + +; Pages + +Page directory +Page instfiles + +;-------------------------------- + +; The stuff to install +Section "" ;No components page, name is not important + + ; Set output path to the installation directory. + SetOutPath $INSTDIR + + ; Put file there + File /r "_output\kclvm-windows\" + + ; update %path% + ReadRegStr $R0 HKCU "Environment" PATH + StrCpy $R1 "$R0;$INSTDIR" + WriteRegExpandStr HKCU "Environment" "Path" "$R1" + +SectionEnd + +;-------------------------------- diff --git a/scripts/build-windows/build.bat b/scripts/build-windows/build.bat new file mode 100644 index 000000000..ff93e7efe --- /dev/null +++ b/scripts/build-windows/build.bat @@ -0,0 +1,83 @@ +:: Copyright 2021 The KCL Authors. All rights reserved. + +setlocal + +cd %~dp0 + +go run download-file.go +go run unzip.go + +go run gen_pth.go + +:: renname +go run rename.go -old="_output\kclvm-windows\python.exe" -new="_output\kclvm-windows\kclvm.exe" + +:: install pip +_output\kclvm-windows\kclvm.exe get-pip.py + +:: pip install -r ..\requirements.txt +_output\kclvm-windows\kclvm.exe -m pip install ^ + -r .\requirements.release.txt ^ + --target=_output\kclvm-windows\Lib\site-packages ^ + -i http://mirrors.aliyun.com/pypi/simple/ ^ + --trusted-host mirrors.aliyun.com + +:: install kclvm +go run gen-kclvm-py.go + +:: install python39 include and libs +go run .\copy-dir.go .\py39-libs .\_output\kclvm-windows + +:: install kclvm-runtime +cd ..\..\kclvm\runtime +cargo build --release +cd %~dp0 + +go run .\copy-file.go --src=..\..\kclvm\runtime\target\release\kclvm.dll --dst=.\_output\kclvm-windows\kclvm.dll +go run .\copy-file.go --src=..\..\kclvm\runtime\target\release\kclvm.dll.lib --dst=.\_output\kclvm-windows\libs\kclvm.dll.lib +go run .\copy-file.go --src=..\..\kclvm\runtime\src\_kclvm.ll --dst=.\_output\kclvm-windows\libs\_kclvm.ll +go run .\copy-file.go --src=..\..\kclvm\runtime\src\_kclvm.bc --dst=.\_output\kclvm-windows\libs\_kclvm.bc +go run .\copy-file.go --src=..\..\kclvm\runtime\src\_kclvm.h --dst=.\_output\kclvm-windows\libs\_kclvm.h +go run .\copy-file.go --src=..\..\kclvm\runtime\src\_kclvm_main_win.c --dst=.\_output\kclvm-windows\libs\_kclvm_main_win.c + +:: install kclvm-runtime (wasm) +cd ..\..\kclvm\runtime +cargo build --release --target=wasm32-unknown-unknown-wasm +cd %~dp0 + +go run .\copy-file.go --src=..\..\kclvm\runtime\target\wasm32-unknown-unknown\release\libkclvm.a --dst=.\_output\kclvm-windows\libs\libkclvm_wasm32.a +go run .\copy-file.go --src=..\..\kclvm\runtime\src\_kclvm_undefined_wasm.txt --dst=.\_output\kclvm-windows\libs\_kclvm_undefined_wasm.txt + +:: install kclvm-plugin +.\_output\kclvm-windows\kclvm.exe ..\..\kclvm\plugin\setup.py install_lib +go run .\copy-file.go --src=..\..\kclvm\plugin\kclvm_plugin.py --dst=.\_output\kclvm-windows\Lib\site-packages\kclvm_plugin.py +go run .\copy-file.go --src=..\..\kclvm\plugin\kclvm_runtime.py --dst=.\_output\kclvm-windows\Lib\site-packages\kclvm_runtime.py + +:: install kclvm-cli +cd ..\..\kclvm +cargo build --release +cd %~dp0 + +go run .\copy-file.go --src=..\..\kclvm\target\release\kclvm.exe --dst=.\_output\kclvm-windows\kclvm-cli.exe + +:: install clang +go run .\copy-file.go ^ + --src=%LLVM_SYS_120_PREFIX%\bin\clang.exe ^ + --dst=.\_output\kclvm-windows\tools\clang\bin\clang.exe + +:: install hello.k +go run .\copy-file.go --src=..\..\hello.k --dst=.\_output\kclvm-windows\hello.k + +:: install tools +go build -o .\_output\kclvm-windows\kcl.exe kcl.go +go build -o .\_output\kclvm-windows\kcl-doc.exe kcl-doc.go +go build -o .\_output\kclvm-windows\kcl-lint.exe kcl-lint.go +go build -o .\_output\kclvm-windows\kcl-fmt.exe kcl-fmt.go +go build -o .\_output\kclvm-windows\kcl-plugin.exe kcl-plugin.go +go build -o .\_output\kclvm-windows\kcl-vet.exe kcl-vet.go + +:: run hello.k +_output\kclvm-windows\kclvm.exe -m kclvm ..\..\hello.k +_output\kclvm-windows\kcl-go.exe run ..\..\hello.k +_output\kclvm-windows\kcl.exe ..\..\hello.k + diff --git a/scripts/build-windows/clean.bat b/scripts/build-windows/clean.bat new file mode 100644 index 000000000..b41703d59 --- /dev/null +++ b/scripts/build-windows/clean.bat @@ -0,0 +1,9 @@ +:: Copyright 2021 The KCL Authors. All rights reserved. + +setlocal + +cd %~dp0 + +rmdir _output + +del *.zip diff --git a/scripts/build-windows/copy-dir.go b/scripts/build-windows/copy-dir.go new file mode 100644 index 000000000..690e8ae0d --- /dev/null +++ b/scripts/build-windows/copy-dir.go @@ -0,0 +1,95 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ingore +// +build ingore + +// +// Copy dir, support regexp. +// +// Example: +// cpdir src dst +// cpdir src dst "\.go$" +// +// Help: +// cpdir -h +// +package main + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "regexp" +) + +const usage = ` +Usage: cpdir src dst [filter] + cpdir -h + +Example: + cpdir src dst + cpdir src dst "\.go$" +` + +func main() { + if len(os.Args) < 3 { + fmt.Fprintln(os.Stderr, usage[1:len(usage)-1]) + os.Exit(0) + } + filter := ".*" + if len(os.Args) > 3 { + filter = os.Args[3] + } + total := cpDir(os.Args[2], os.Args[1], filter) + fmt.Printf("total %d\n", total) +} + +func cpDir(dst, src, filter string) (total int) { + entryList, err := ioutil.ReadDir(src) + if err != nil && !os.IsExist(err) { + log.Fatal("cpDir: ", err) + } + for _, entry := range entryList { + if entry.IsDir() { + cpDir(dst+"/"+entry.Name(), src+"/"+entry.Name(), filter) + } else { + mathed, err := regexp.MatchString(filter, entry.Name()) + if err != nil { + log.Fatal("regexp.MatchString: ", err) + } + if mathed { + srcFname := filepath.Clean(src + "/" + entry.Name()) + dstFname := filepath.Clean(dst + "/" + entry.Name()) + fmt.Printf("copy %s\n", srcFname) + + cpFile(dstFname, srcFname) + total++ + } + } + } + return +} + +func cpFile(dst, src string) { + err := os.MkdirAll(filepath.Dir(dst), 0777) + if err != nil && !os.IsExist(err) { + log.Fatal("cpFile: ", err) + } + fsrc, err := os.Open(src) + if err != nil { + log.Fatal("cpFile: ", err) + } + defer fsrc.Close() + + fdst, err := os.Create(dst) + if err != nil { + log.Fatal("cpFile: ", err) + } + defer fdst.Close() + if _, err = io.Copy(fdst, fsrc); err != nil { + log.Fatal("cpFile: ", err) + } +} diff --git a/scripts/build-windows/copy-file.go b/scripts/build-windows/copy-file.go new file mode 100644 index 000000000..a3ef4e5e5 --- /dev/null +++ b/scripts/build-windows/copy-file.go @@ -0,0 +1,57 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ingore +// +build ingore + +package main + +import ( + "flag" + "io" + "log" + "os" + "path/filepath" +) + +var ( + flagDst = flag.String("dst", "", "set dst path") + flagSrc = flag.String("src", "", "set src path") +) + +func init() { + log.SetFlags(log.Lshortfile) +} + +func main() { + flag.Parse() + + if *flagDst == "" { + log.Fatal("dst path missing") + } + if *flagSrc == "" { + log.Fatal("src path missing") + } + + cpFile(*flagDst, *flagSrc) +} + +func cpFile(dst, src string) { + err := os.MkdirAll(filepath.Dir(dst), 0777) + if err != nil && !os.IsExist(err) { + log.Fatal("cpFile: ", err) + } + fsrc, err := os.Open(src) + if err != nil { + log.Fatal("cpFile: ", err) + } + defer fsrc.Close() + + fdst, err := os.Create(dst) + if err != nil { + log.Fatal("cpFile: ", err) + } + defer fdst.Close() + if _, err = io.Copy(fdst, fsrc); err != nil { + log.Fatal("cpFile: ", err) + } +} diff --git a/scripts/build-windows/download-file.go b/scripts/build-windows/download-file.go new file mode 100644 index 000000000..3878cd3b8 --- /dev/null +++ b/scripts/build-windows/download-file.go @@ -0,0 +1,94 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ingore +// +build ingore + +package main + +import ( + "archive/zip" + "crypto/tls" + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + "strings" +) + +const ( + // https://www.python.org/ftp/python/3.9.6/python-3.9.6-embed-amd64.zip + // https://npm.taobao.org/mirrors/python/3.9.6/python-3.9.6-embed-amd64.zip + + baseUrl = "https://www.python.org/ftp/python" + baseUrl_taobao = "https://npm.taobao.org/mirrors/python" +) + +var ( + flagDownloadUrl = flag.String("url", baseUrl_taobao+"/3.9.6/python-3.9.6-embed-amd64.zip", "set python-x.y.z-embed-amd64.zip") + flagOutputFile = flag.String("output", "python-3.9.6-embed-amd64.zip", "set output file") +) + +func main() { + flag.Parse() + + if s := *flagOutputFile; fileExists(s) { + fmt.Printf("File %s exists\n", s) + return + } + + var err error + if err = DownloadFile(*flagDownloadUrl, *flagOutputFile); err != nil { + log.Fatal(err) + } + fmt.Printf("Download %s ok\n", *flagDownloadUrl) +} + +func DownloadFile(url, filename string) (errRet error) { + f, err := os.Create(filename) + if err != nil { + return fmt.Errorf("failed to create %s: %v", filename, err) + } + defer f.Close() + defer func() { + if errRet != nil { + os.Remove(filename) + } + }() + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{Transport: tr} + + resp, err := client.Get(url) + if err != nil { + return fmt.Errorf("failed to download %s: %v", url, err) + } + defer resp.Body.Close() + + _, err = io.Copy(f, resp.Body) + if err != nil { + return fmt.Errorf("failed to write %s: %v", filename, err) + } + return nil +} + +func fileExists(name string) bool { + if strings.HasSuffix(name, "zip") { + archive, err := zip.OpenReader(name) + if err != nil { + return false + } + defer archive.Close() + return true + } else { + f, err := os.Open(name) + if err != nil { + return false + } + defer f.Close() + return true + } +} diff --git a/scripts/build-windows/gen_pth.go b/scripts/build-windows/gen_pth.go new file mode 100644 index 000000000..9dca8456f --- /dev/null +++ b/scripts/build-windows/gen_pth.go @@ -0,0 +1,32 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ingore +// +build ingore + +package main + +import ( + "flag" + "os" +) + +var ( + flagFile = flag.String("file", "./_output/kclvm-windows/python39._pth", "set output file") +) + +func main() { + flag.Parse() + if err := os.WriteFile(*flagFile, []byte(code), 0666); err != nil { + panic(err) + } +} + +const code = ` +python39.zip +Lib +Lib\site-packages +. + +# Uncomment to run site.main() automatically +#import site +` diff --git a/scripts/build-windows/get-pip.py b/scripts/build-windows/get-pip.py new file mode 100644 index 000000000..446e14264 --- /dev/null +++ b/scripts/build-windows/get-pip.py @@ -0,0 +1,24556 @@ +#!/usr/bin/env python +# +# Hi There! +# +# You may be wondering what this giant blob of binary data here is, you might +# even be worried that we're up to something nefarious (good for you for being +# paranoid!). This is a base85 encoding of a zip file, this zip file contains +# an entire copy of pip (version 21.2.3). +# +# Pip is a thing that installs packages, pip itself is a package that someone +# might want to install, especially if they're looking to run this get-pip.py +# script. Pip has a lot of code to deal with the security of installing +# packages, various edge cases on various platforms, and other such sort of +# "tribal knowledge" that has been encoded in its code base. Because of this +# we basically include an entire copy of pip inside this blob. We do this +# because the alternatives are attempt to implement a "minipip" that probably +# doesn't do things correctly and has weird edge cases, or compress pip itself +# down into a single file. +# +# If you're wondering how this is created, it is generated using +# `scripts/generate.py` in https://github.com/pypa/get-pip. + +import sys + +this_python = sys.version_info[:2] +min_version = (3, 6) +if this_python < min_version: + message_parts = [ + "This script does not work on Python {}.{}".format(*this_python), + "The minimum supported Python version is {}.{}.".format(*min_version), + "Please use https://bootstrap.pypa.io/pip/{}.{}/get-pip.py instead.".format(*this_python), + ] + print("ERROR: " + " ".join(message_parts)) + sys.exit(1) + + +import os.path +import pkgutil +import shutil +import tempfile +from base64 import b85decode + + +def determine_pip_install_arguments(): + implicit_pip = True + implicit_setuptools = True + implicit_wheel = True + + # Check if the user has requested us not to install setuptools + if "--no-setuptools" in sys.argv or os.environ.get("PIP_NO_SETUPTOOLS"): + args = [x for x in sys.argv[1:] if x != "--no-setuptools"] + implicit_setuptools = False + else: + args = sys.argv[1:] + + # Check if the user has requested us not to install wheel + if "--no-wheel" in args or os.environ.get("PIP_NO_WHEEL"): + args = [x for x in args if x != "--no-wheel"] + implicit_wheel = False + + # We only want to implicitly install setuptools and wheel if they don't + # already exist on the target platform. + if implicit_setuptools: + try: + import setuptools # noqa + implicit_setuptools = False + except ImportError: + pass + if implicit_wheel: + try: + import wheel # noqa + implicit_wheel = False + except ImportError: + pass + + # Add any implicit installations to the end of our args + if implicit_pip: + args += ["pip"] + if implicit_setuptools: + args += ["setuptools"] + if implicit_wheel: + args += ["wheel"] + + return ["install", "--upgrade", "--force-reinstall"] + args + + +def monkeypatch_for_cert(tmpdir): + """Patches `pip install` to provide default certificate with the lowest priority. + + This ensures that the bundled certificates are used unless the user specifies a + custom cert via any of pip's option passing mechanisms (config, env-var, CLI). + + A monkeypatch is the easiest way to achieve this, without messing too much with + the rest of pip's internals. + """ + from pip._internal.commands.install import InstallCommand + + # We want to be using the internal certificates. + cert_path = os.path.join(tmpdir, "cacert.pem") + with open(cert_path, "wb") as cert: + cert.write(pkgutil.get_data("pip._vendor.certifi", "cacert.pem")) + + install_parse_args = InstallCommand.parse_args + + def cert_parse_args(self, args): + if not self.parser.get_default_values().cert: + # There are no user provided cert -- force use of bundled cert + self.parser.defaults["cert"] = cert_path # calculated above + return install_parse_args(self, args) + + InstallCommand.parse_args = cert_parse_args + + +def bootstrap(tmpdir): + monkeypatch_for_cert(tmpdir) + + # Execute the included pip and use it to install the latest pip and + # setuptools from PyPI + from pip._internal.cli.main import main as pip_entry_point + args = determine_pip_install_arguments() + sys.exit(pip_entry_point(args)) + + +def main(): + tmpdir = None + try: + # Create a temporary working directory + tmpdir = tempfile.mkdtemp() + + # Unpack the zipfile into the temporary directory + pip_zip = os.path.join(tmpdir, "pip.zip") + with open(pip_zip, "wb") as fp: + fp.write(b85decode(DATA.replace(b"\n", b""))) + + # Add the zipfile to sys.path so that we can import it + sys.path.insert(0, pip_zip) + + # Run the bootstrap + bootstrap(tmpdir=tmpdir) + finally: + # Clean up our temporary working directory + if tmpdir: + shutil.rmtree(tmpdir, ignore_errors=True) + + +DATA = b""" +P)h>@6aWAK2mnY{22-I%P<8+T0043U000jF003}la4%n9X>MtBUtcb8c|DLpOT;h`h3|#_hoKi;SW{d +DwV>cd5JWwATb7V*r|m$Sgqf*J|GlZpHsp|bnfLg3Er$_wj^sO_F$PxnAki&+MWx`~kY{;j?Ju&V(OP +(hVs~qH=B`+N@~puYMo0eIaVOPk+b=15Q9c}!c!JUu|J&ZeL$6aCu!*!EW0y47~@)KM>9#PT;yZY=;%gfC0PovO_VThjlP +CnU00nk|4=(v#%d1SxZ-+EQx%4e2-M4H7y=8s{(AeLh0{u(=wiLvz!Q$D0j2RN+6Q`=%L%(oIv_YYtA1k@8xM2$Jjg;FR@j-u7+AwgQjRu+lk +zBM5h!!Jg1hLtrJiO|H+aMPx#|X`zD0q03ocqfR2cl?c3Hait(9(_~Veln|ef>{4`Op_^C~DYBwfAw_B;_lu*HF+G)UJQSZ}O(&fubeL?3bsz`8)6LCw3dfg~ZHMJOO +IjEW?&vfHIH5Pqfhq8nCszMoWOE&f|P)h>@6aWAK2mnY{22%nz$56!p001HZ000aC003}la4&FqE_8W +tWn?9eu};M>3`O@!{fE1;JoO(K7}(x`#B5sAT1xCFaSHu=oHANEzULGvkOsnPIg@TkqKr~)uap;j9+D +X1gOOYkXIAG8qIh0@m_9I`nguXVbdn_cijQxrgSYArT-K3$bEr9bi*+e4F&9HsDdF|`J}&qXk{`Mjd| +X9zpC{4S$>d@>0;1}aE$s9ktS76mt_;(3-625@3t{h6DGa>e?YHpi^VF48tp~qhJxF(Ma0@Mug!|pE! +govM#MHPg^YjN$O9KQH0000807zB_Qvk|?QCk5308jz|02u%P0B~t=FJEbHbY*gGVQep7UukY>bYEXC +aCvo+F;B!W42Abf{SVf$+)0owNWe)jG9g&l5ITj{ZH>|-*enUk918+fh_x>$_Pgh@<$5t?mTdnNitC*^j~r#PsItsxSI~Ts)fXLK=$22~Z?0e}k +c{H!#hj@Pg3#5U{hVPmJK>vhRQ<_?mSC>YbIy>rK)57^xj$^f_Y`@OpGgJxs|or_{{>#nn}DbH8m1N? +mnmv!3GG^B`MTAG3HW(L9Or^YaF-;XXy&z}bzV>$a}IzSU~=IFh|iATtW`yRjQNq|iZ|qNIQzEmKwj9 +gS}C3y38W%3~ROWQ?t`9P~(C0V?IKsras=0}6Dg-f}zzL4;8?%eH9-(s6!qZJL_AWZziu4T;Ps&BgiE +rTd0F6#p&9VN_mnvhL5KKDb26M;zPs%<;RukILfv1W-!@1QY-O00;m`Rt8fX^hNw&3;+PHC;$K&0001 +RX>c!JX>N37a&BR4FJg6RY-C?$ZgwtkdF5K&bK|xVf4`agA5h%`$t%fenrYL~L}~6rmw0-$J!fCqRHI +=i60%WKBnwcobvORsyNeGJ0BQR&d5m5xfxC;ve*AW^^lUbJy=PTPmi2-0rry!IBYQzhvTZnFqN(zZH8 +m;soYhxEluUGFgBRtLXD*Ti)zoqpG3>Dkg{~IO8Sdw0z5oP7{GVP-x4p49OMkGTc;Xg6`PTp8t~P1>NMu%_Xc31bApe +@$>mId-LP+%|A{~e~8IRM|r-fsQ%V9JSOkKIx#tGQM0^?$pwXtHqN{rKKMZS+@*XG`=uge|llp1vWgcZ28a&HLE^s-knnH$xEuMkDIh-^tDwY +Z`Rt@rufTpE&rWYrfj6~r$2m%hGB33^*-LgAcX0Rgk(w9saFA4~WV@&WdJk4%cT?&~jTQ`*E&z}+aOvncgdZ(R7!5#3k-{oAbz +LSe67BdYVFHFj%aQgRhEzGhy-+?P5q-sz9XkiO^3(Glp+r(fq%Rwf_D2B9=*#e;d5k~ADh@DtQsK)`g3NbxXau~17Cgpr_a1iRD +W62va&A?IT8IoRZa26L%c3=9xv*2?|B60HJJaMpGrSbJJR+@Y4jsA)%MD2=5I{QUrgss6glYw#90N}9 +RgH1B98y1Xv)3XGV6<3nju112l5@mkv+Q1*@FAUT;0Oc*UQL`H&{ni@x5UekUt5DJt5)ER_!311jQHt +8DD0a>x<9G0qM!uNHJScd_q#GC*Wv8o~)s_Fr-uo^apB^Q8dElgGgV&z-fYp=eDX3q<%g9B}I4u3-%T +Ozh!-2t3yqgssxy2h>RF^S(bi6VS|91+nirvkbkz)0*(g-)zlBbiET +r^TafVUnEcH?3>-J4*rJv44~)jEG92sDv9#IpSIp^*r*iO7< +;TX=I2*cvk7M}XTECq`Ls=MIqPUBxnji(k%z6$fo5H!9i&4rN^_A(fC;0>cc)|GLF-mobU<6#WLZ6yp +V>kNpF#=zu3InaB$S)R +mwa#S5g9EdA|R?79L0#Ux;Sss5K%uiRV#Eun>w+Ll#%T!6D6{6K#+t%-SAyr1yBeYi0CRm2GOG`>iUE +#uGWJQvEP$G?bBP8;t6`xOBnz);}jE&mSF{yp=+M>kXL&uAPD`Sc6Chi7CNsoRD7niMpG#bT +BnL%bkw_M&Tc>=PvdvIm0E39AGmRX}q_(iRdLvl-$TeqJ|VY+AacEglpCIxl3;1#u1F0&Y~2b;lzttI +DStK9#`a9>xR%dbNO}9wJ>NO)KT0%dOrqjAFahbh7&1_aq9?Iq_J`1xRNHkuC>@hu(ohMd-@({RqUn8 +!Q_hnFp5GSi1?}(raxB}2Pcj#(Sdpr_5DBR`5vx0qOt@at;69y8q3&26n3NhGCR7>$W!?x8RUkk59A{ej< +4)D8UL<1{Zz{EMDa-nVvJSl=hM)VtVZ=cY29B=(c0(etx{o-_-2cyY&9Wu%HFH#&E->DgAwmEpX% +i&&M5OnouoI_Sj5CR%+WDb$2v^K;{}E87&-uOD0F?BzGY-|DhOXFgu^zu$2bXb`i@A;2_`4T46r^NU( +hR1$R#v^@6j(0$O(sz5;sb>SOcNRSaH9wf5gP7yKt*T6uF%S#rx{7Y&A4u<XNVMxV8Y?)cx3dd`!*8n2RyMoHa_1fh;;6!fB=<;}*ZKigzCxx0}GVIedorr +vl)kDrzETiLPTPG9UiKP@AyAz?`Bj|wAOhO8uz!ED0+NKTr4JVQZCn%3fSZQv&y!CtAo-3|tag*6o0X +;y1B{69lGdz{eur!jKlCCajw^HBWL5!E>^;(WmC86aVxbEnIz@M>*3hw|3jy<*&Tq6*zZO!ILPMR$V^ +-YQb{@>;RVPz!sB^~ZBe0e)2#DlVs&(Ur8VfaQ(h^^r0AyYEFQz@kMrkbG#&SPW*Ng0?=-7iK$@dL{s +*qR}SnrEVt$Tt8B)CDm)!=IdO_Lv;VC8u)}Y`Pyt@&`(vwiFh#N4D3d1GCw;N^2J;%(y1R&CJ9iHnZ- +jiGXA^;gA`7Ey#BE*&OM9vG{DuyPu84JdjCWCjDvga5$U9^hqc~w6tJbM)?EVTfrvn8;XZ% +3te)8{sF6ZP;QwxSuXw@l^k00Lu-5+Dwo5jYfHeBevUBGw{^wrqgLT+#JJ#0Qmv#1@Uu}Z1D>1MgWBT +Gel4p^|#7y3HbY~ty!fP8(wUwHG5m9K=QpWO$lOSv4M`!f5Xx;tdi$>v~{>s2#xG{5D=(i&SYp%hq4HFSX85h&bo`4<&(PawE5RUNx_clgljh{= +)B6UjTZX*_m5XF8X&& +^~r9$}BYaStw*pY^cbyPjjn(K}k~JN1&?Y!AD(ulCUeB*Cl;{z*|$2)J?JnL{dM@do}|?atJaK1 +T44EI40_CT4I9M-tzI$C?t-0sfmroa=XPh{s;P=9%u!q^zHDb^X>BW#~-}NN2{1gXpkK2+aC5r>hdd7 +fH##H3gdMz=SMjguB)T@>8Gh;)~^BcuH&H!hSZfBd%Hvv8tMxmsF(!gj~)Y{U<<%V!yuD}5B#f{^9kZ +WPXhr%X&D+V;k2KFd5u96+((gnrIfFjvpuiyH2M_7n&XRMdCJuKAgdr@pzA=p`qO$n(USr>&ofDe#<% +Fdny)87%-bW8{E1{sfDN{^B-$HJfT9~37^jlU50Q%1H>jKOE&R!F?TOS>*bt5`Ubj +SAsJeJ||Q_?kUg0vtX|E%4MgBA+O+uUXdZjhy%Rut~Jlfnl}0AVmG3-cTbhDwJgWdLbnGRK7}i*EEtp +V0iya2GJCW9a5YmVQ~%*{Wegc?|7XQS7=LwW!G5u%2% +p|v8&a>`Q^O*J8jNd{|#X1FBp`8>*FhE-;$=4T~s1le2D|{9U#V!&)PHo=2w7iF2rx2?oCMfJ&bn +zV;7Kg^}hX0ru*GpmUzwtg|+Tv*H%(PS}r+nIL=4@4^T@31QY-O00;m`Rt8fj)c!JX>N37a&BR4FJob2Xk{*NdEHuVkK4Er{=UHe2Ufvh`t70-8qz2g<%ioK}p`B@Z2=V#|RTvb-9vZL~vMqjzXhnAPJy6YBQ!|#XY9+0PQGBG$in-q~+v9G +g2>R!w6{DagYw@QQI@HM>6?qpS%2go#fP-@NXF0(3wF;pk|iO_7-IZV10_L|1xjC7u53!Ha;R)`JD(C +2`m+{VRXd49&=^8{T=ELn_hbYrYe7<}C>*weq^4>t)s_h(@nT`>-Wd|e7=cKq41=S*#22q~i5>0?Shj +!O(Mg)W^I&Av8q0c3&y>`olb>Vs~;xs0f}=Aa6*ilZb-ShS252@{oRw8*&0rKFh)M^HNk+OSF8{;^3# +m4j*Gh!#YPMY#;xdi$&FzYkIP=3mmtd;1mrn*DouFGYOU^NAq==K#%|G?O2P=Z7%RBtjI4w +nV;phTewHZA-mOHNFWCnmp_Q_zBMZOjRXSM_I`0x&C&kQ8M>Z&OV@ByWLdiaSih=t3$|K~_6Up%`6+YKQzipHw5GK*2Gy($F(;F)u*Q+)hDqQ&e;1e*wR2ef0sh7<+c(BSF!bj +6q+|uIcaU~B=szIjtLX{*DA<8l7Q<~TDgl{FfM+vEyg1Ytc!C`p-9`o^wuBuZX{hSLb}M~)pO7Cdp5j +Ujh=2Zk4eaF{pCzy>w>ty{_rNi{hMEE80n&a&lQ)`g9f%kRVwm+C<``#uts0bG89z9IGpA=VHt-)UN! +M6cGE@gF%DN3a)60#UMnwZ_36qArA03uPgQM3 +9bCL%FCt@KGI7pFDv-f;vcj~Z}Grc70XpEoP)`-e7Y8BO-PmnxUF$cxo#w+Ppp`mjnKdi +J2(t6&?!U>unKdya<;FJV4|wuABG@r7^sGIS|3sKZN_h)a8$|IL!5iKB1z=Bt#d)5s69l7T+340BST1 +-`V@B7)@FHmxdow`bqcdD>toF?0j69^V<3rN{^i+UU(7h$O*Dp+WS8-vpI}d{Z3Uk!Z^vDgA6UL}qGD +tML5f#0i31E6qZeh#TojV>`Va(9WCkj0$+b_=h`iCL9e@W=9V`}EFgDWr?nMG7wQd8B)ls7ZS!H<%9M +~G(#bAZNPj-!AP*sg1EEXwy18Mah*9GP=t?l;w4#Eu(9wLIkQwB>SJVA+3lAOaAWDq|GV32_;u6zjCt +9x-W@l}ALTCN5tWE7+VcnM~>M-K)sNB`q6RuR@wwNI{uS!F=rfXrd)aygG2w;{L#0UOK&g1DPc?)7_5 +tS+tHIQedt&u)`e)|+R`=flJE<%{9r#q##n7r8~2L~N#5K`;XH6MmUWhUO-7-H+>VPL?kM)k6(~zRjy +)Kr4gn>FOd#6NhOI6YB^;C_SAr&(ZAElA4@uWD#WFfSFOkZ54GBXm3CgGtdS5k!*w;M&ix$eTuza8Dl +U)nE@gS8UEk}E7;qNz4Ls& +{?19IS7RYIr`9uWIgrYYNYX<+q$?@Fcau&Ot;pDB^fPFl3`F#NG??!g5FHD9m}BOi7i4h|%pfgD|fQ< +os1#8agHP_ZLcHTyM;;c1cMYv^#md>o)Srp|#bA+L7JcE0x90?P%ossCz3%C6FF%F@d=3HS|Hp?(n+w +U3<}nfHdHiR&UXm+9-u|(bV$y1Dt&Yqi|XKf|30dP{+Yo{r~QPzr$}g +h8ZFp%xpZ1Kv!bnPekUJ?)Dspta#6zm9D>@fsCZ1E$^;tuOwNO!ake=D8&}ig`#3c;IRgPIXrfv-b>h5DYSptQrx%aaehzBHA&f9oe5&Cxn{W!k +y!%g;+P+c={SY(c5hWT@j=Tzl5-FTvdQSkRAXxL?>1kO1Lu`D20>*1Zbg#ln<0JKWGV!EG9HE|2HO$l +6gwLsh#P9$e1o#{0ACE2AvN3@QK4fn#0IX*!PrjsXTgTEAHgvlIwhTDQx~&KBka+y7GjP0^R|N6ykHX +$7~hK@MYCLGy(J77uCF&$qs{HkIM~Naw4!-cqTUVA!gcvVB(6s%c?j?-h&+828%_2N3UuCEFK&k{3BH@e|DJ_fZu0;+xyrBXKRuLW1(jo`?q@LF&c+!3Z%KfSqnmwkM5b#< +M+dim$YW{#ot6hrc6)snAHP6MS0Xu=l-~mDi5 +^v%GL4o_W2|jlOksKj&Xchroap}UfJ9*!z2JxrEQHEIb*Rze(#{MCiv+f%4xA6s9=2As>N;c_NPcuFy +MC$DcjT6m`$9Jmwom#{;M^248>cv~b)v(CPu49U5jevQq#6x}P9=%VJwX=cbVIf)%Hawtsu47|g+mV|hNMgwCk +s4$$&2;=UJwL$b+#@GUg=$B!Ug5^k6E!uGHQLjxq-9l?}exz#SWekE~$1U;-h~*%6B7j%of#%oB(~3> +x_=bDHHJhjBelWO89zSBBn_;BJVdP9;YFdtc+ro!!~#s(fb9J3x$ClB0&goxI5*%;G>QgZ-8Gv6`L$Q +E2`J@v`A8#!n$GSS(}Pw^)X3Vri_dJg%L2bOe(Yf)5+QK`KPNV)~}gZnAJ2F8%|?5VFb^cHQxR**(%& +GBs<8&;ZEegCI{qd1+Z30p6>7c%<>d=XzGRmeh)@ORD6iAe>y*VbMiKReRMTFJ9;;Tl>rSMOVE6i}fzKrP`#TXbG?+NLEg75}pECrOb(9!xxl<)FOPrrO6)Fkk +o?&G?9NrS+RxN4kJ#>ELqTS5D!LVa5&I=Ks)3zQ6;<*Xt%?`A-vUSd(dXzkLP%oBF}cDSd5vNa1wi%+Jr(Y>kfUfCW8i@R_Xk*p%_~WgpXrXI +1r>5T(6&kdMp~ek1j8d8nsHHJXpo~32|O4Vl&_LfkX^AMTX0#h2gCqnWS}%PdUymolLOb1^l#SgVYEb +vs6y@Z%T<8YA***pL^bI(AG_EFF2+qOc?{I<6TS&{0EaO=@)=4NCz5n$8 +-`3lV`+SVjLTSRUfjh5kH6hC&~cYX%Cv) +DZaqkSoQUxEyvoel^884O3w3W;RflSKHlwai0BztI@wbo+Ve7*Zi37OBDjP78*u3pkv+2(QF# +o&>@7LDha%zPfqTMN~iwul`EC1u#g#80}fO2O=w7$oUjGSEX;tt%R0No`r99e>i +L*+~s3s52q*-)z`DE#6^ktW(zu->njAPO^B20=h2z{EB1k1kHYCqP70fwdMSFaa&bqNtn!nDkwyT{Qe +CG=o?Spfyl7c?wn)HfUAQ*2$a~xCkd17#*etDnfjrK)eU +}9nQq?LDKHQ0~+!07#R1`1}K3-0IO`vIw?c&{}7CO7!QZ-t=>=8 +S7b_LcmnpzJbEh{tS8M4Y$6|!*L0Df|J-5>MN3lgWO0j^QVAdYd;bCgez}opv+c1FBi}j7j3gECdJ5hTH(;q*<1#6mUMcmXhE@#jDZiL(SH+3;Q?_~#5 +9Fi>uCzq)~1P8^$jkiBUXlAx_fEtSL|j*MFkd?6`WPOru3p|(?bP}oq6S}mt~(uV=bPcwyCw) +GVJMrMR~VIBuK^}$x1nO+0J>haO73V}>z5lDbgH10>tOUWBBiNqJ*6*Rz@hh8+T5~Ul074niMO(86eJs{BAuoRl0 +qM0g0ddD*7zV6x+u3wM66FY1>B*B9DelbBu!hZ-Nkw#Fh6uIWms=t!gi@Mm+W&Xra+0hN^NghC;T=B# +Emx$9o{%nxXC-@@JXF)$=(M9@wDjuAp!@CLgmtt=u^b{owWA8Ymw!PLAO-3T#Jd@IPgPo1EzWsA{UU~0W|PivV;#Y6uI8qL_r%?hdFp +}<$mjACnJD$2{A~rtW?5j->UmYu5~az`#HOyN&GZxzDRfRlhW*Y8{xPdP_{3PXs=+8(&!E6REF$_Yq$ +>IK~f8W=2yEFOb1-8s +{71=%Me=z)jGgl*wM8zgV=KjEJ@c)ODX6raE)aduH6>=R<=8liZ4W+A0k%R<#N!h>Gk)L1I2l@v&hm! +a?jAh%_JQuJ|Yd2z8LxQf-BQhIX3VZ=6xW4(ufr0MpY8DRZ);5a#f5FJZ2}DTL~MU`WO(oVgS|*EEFK +L1!s&|_@l3By@_`_I=;ikW|;zlp<`t;I>mv6Gn4>KG|@2GfP-Lg{l)R96xwONvGq&jstxWLNe2g2`Of>I&K20IVJu*Gg*Uy +rbiG%j_juE=2$Jn!1IJ;(KOVP_0+vQ$lDoRBca!w4tFld$=>TH}9H^-tmkfR_AVRl3GD*OjvZ5AZF!= +4*c;L54;KlW!*8%8t8y2Cr+L)jXGWa_~Y7NLbg#UWlP_TqAopr2Xj$>ae@V@(uNU6MnpiRbB{@2pcAM +dQ3&&pDC4S*Ja8krxMr)Oxv5BB4uqfqND^LnTB3iGi;j-iJVGFJQTZ3hpG;m9L1~a^?9E+mO(^bjKKe +ZKdQj$8=b_TgBAu2K##20yRP_VVbX!HO;pqm6NQ41*9oEgrL9g<$pC(5#Mu=@w+QcF5vS>z0a5{oHw$FEtsTnuxJWo{a%fx~tMQ)p|>dd{D`D4}j!UF_OVAn~E&pdqtbJkAhWK2X +@LCNE1?zGOmdsCfCq +kAMEtXC;GO6I?we6mB4jd=ls>22nSR9v;bJm4IymcP^f33gJf$z~L;68sXp&Mdfg9U|9GD0xq9I=3{r +n?T@7DM47bCRJ4QKj-*R+W41uQn(lx3@hj*qkDtgtho55!x*H0=SAb^^`rI`WR@T;@Vwp2ORhz&rYv~ +)rU<`+jP0E3F9gT!;l~`19eOBADqDl1ZYj0t&`wVA!`sD)l}D0wMJq(2Dc77BV3Ssi_d+Dl!doSYs?FDr*#1p9jYXQr$JcGo77(ST +RBbjZTGaAyRkXeD!8^3qgmxQI~?Kp^4^X$n4~FUdOHb#&tXFa*H`n%5V%-$1X|#k>YW-Ck|}~iRHU70 +J0R)6#b^*cvArs}+M{xT1lm>l(nU;IKl@=kH&b|>!MI$i>$t6f)SifY$0&H{n)7uDh5Yi%FJmRj@IQF +W#Y&BFppaIyDfQ@{8~>8r)Z>vqLgupSSH!R>WuV4%c7a>tFm0u5Mjt8}zoe>{r!*cE%+~Y%QpDA}>FM +d1yesbNiE>q902rG}MfGsla+5DqTg2YnX^Y9qR0om0R$I3-^;QF4M#4Zn!hx5yLIf_STl!M)CyM7v9; +^}CnLT~gti`#tXSdGnyL-ih-cduBUWv*qM3*(?h?`Bh{^q`yS~~GhXAA6UYx=|>j+1av^)GmEnvX$}r;{}t$CIZtYoBQtHZwtPZNh7tIFi +~ld;FNc!J +X>N37a&BR4FJ*XRWpH$9Z*FrgaCy~R>u=jQ68~PH{{zqcU>mTtx6cng8QBcEVVGI(y^%5eG2omTva%4V2&1lRKk=d%2Xm6Zr|EJ& +)|zGKn{X-yKb|#c>IXLhiGoO*`{*k=P%FT8RygkXi=GiSb%+87XQH?Ov%&KPdm4dxVqFL6M2a=9zTjx +ZVY=Pi&L$Yp49r8Iu<}3KXW5+5hnagrz1c6gp^cij_)iTPcR_!T;xLYw4uUe?)2-XcE~!Gzm#fHY0`K +wyF>VWF29O&l^-DfO6B6ED0#w5zL&K9@|H{Vft#(!8IQrT3{oGxJiR!6dVFy_VoI~on_r&K&tE-zJz| +-o%GU6AZb|Gl(kQe}rdGd;xMVy}?6k`S-bP?qOHXt%_REf^m*7HWrNKupbR`(j2xo0XdCx2q0h_>L%* +afpT&Fwa+3mjG&@>9m1b})d +HgIOs_=~HFW4~~kXO_j++t(FF1CWTDQ$wZ@}l|W5eorb>~fs +HyF}-ZVMQ8>f}2pN|GFm317X37(n{JM7PDw3bbih#Tba%u%VbkM2F~Xa4KwHu4T+^Y;4HLJWi_wVrZr +`49>wUGHz9t$}2&HuZ1?K1T>&jSsH;%td+4zTdHF+y4$I;meH;ip)(XP?Z9-MP06tR^ +!NBup-JErLO?MjMnVW_zGqXWRZn5qWM(LT4Z1gnf`rsuEK%RNbd|yUCht(xSq +Cdr+jZxSH9OKr%B)`A(%w0B=Z2mU~GqIlr;Yim|zB7WBp#k)*^S%}kjHtgiW1uLQv{!N#N~sQ6w{a|3 +sRnrA2VfpAAx!F8o?|zDJPoBkz;|o;sK1M<5PC9=YQ+zzyw>*g?@3P>@Y~Xx4&C9WjL;jEim +pI+I8#*5Q5@=Q((kft&!r;2=ee2xS9pU{(n=uR%Cf69~~%0acd$^rC0fC{bqoT;uU1GH7NsK95ODS4wdBAd5x>k)S} +A}Ec6s?kY&J)imzJBbKG+juAg=u}1F|Ab;MWqSL9Kwl292s;1XHEZ{1}KZWNWw#xC8#`yrHGx75(nML +q_l&{_di6_b#*VzREL7krgRs(4#IY9=)wGBtT;b1Z|7Cy<4zRXNFf1jlTVdzMEkI*cgoeb1<)uvOGr~ +XxBoV1yY*@Qkn%)>G*Tw74NE5p%vCt_rslI2u^-9AT2=DYYsb0hS~$sAq%rRid6!U3<{T6%T|A+r5fo +BYM&W3v?wR|hi-}2tb&zZ^Yr%w(5{I%Y-2MJ78{i#%VB!Lr`YFd< +S$)Vl<$*kDr>+*kb%x^YkfPi5G$rT66$6P<2SO1uQ`;TXfg|Sj2&T!IMyL)WYBvh-oPAJJE&&XYZ-&91IS +p1n~N2&uw#={-CzqtSTbag5MLApD(aXdN|<~MwYS3Z2@r}Xam|JnUr~1@FJz7o#%;)00JXKrF%hD`j* +T&pVqO30!^yjbNFvZcMIK9h4+2UA8kE2EFPAuFGHo%|s?B|$Z;TJNr?(7tL_OWf^ltq3fT{J^E`h4K`z^N*HrI5P=<)Cu4W+NdP ++?tf%*2DO#h>1!3T$9_m9848o0V7D3aZ5W6jImiKxc|+);}y*p;z!tabqg&dm2{jJr2b6a_+_6_n2+HaTA-1yHSRnTnk7LQjT0 +f$^Mpo8c^BLo1ZTHcTRhsY-=?FAGHM$x6Pf(QlU=Vn*tpY@_LTy0_Y#v5hJLt1WTlzV7$~54QtKHfNK +Cx_~}`C}HG^@L(!qY}-w}qQF<%-2YygGEnHD#(1zOm~im)HCX8hd#Vx;YAiJ%e}JU+UQ5uSAGixsl@g +0Xt6g^y=GpOw*Kf}+PM2rL@6N)c<<)l7k!z5uIz{0VfIuY47!ON9s_);v=m$VEtDzi@g~+mzz#IBA1gAL_D`19pglCvese7bswPPDF%w0LKu?-X+t3vu#$lzj8n_eTEI)_JoBZSC8DYf6U33a7Ml +EYX;4H<}2AHUz; +K@>szI=a1E1Q!duydeJffX!?#AA_Hky?)``t~>V9*?U*u?#X9i=7RX_qE~YZfbtFHJBX^TR0d6OKO(l +Y98?%eci(r?-TB_shArHdnPwP~2uav%(+#(7RpS|>9CdVrRVii_dyKId52biI+ZdFI{nENCM80Wpm_0 +(V7>8)o;M%t{MNk~dnG^o8oKJq0!$1i_#nzGaB)?9H9lot}On?X +~tpM{5^nr|(YLo8yyTj-Q{tV^3eRSFbPF%j1iaGY0P%nlAkP+1uAI>G|#Hf8L+JJ$-rl>f&8bZ_mzOo +JP#0Y|?L4BvclLLh{<3F(_g%h}c0rY7Hof*lUcRHxkbz#b=&j@e-0mBukbR1A4+X_U>Ojaz0dLeCKSB +V1_p@AeTi4b32%^y20KCxTV>1ku5Gi(t#9pjp5s;Pxh*+{VQDn{q#Tp;FiM{Q34d`ZbiC*09|e~GCpt +*M81T*6#+kE2hqIt?ziGIP}$(6)*+-}9=eM2>n_Boq2LB@VbJB>t#ClkHbG2f3Ir +&=%+Pw1h0Z?&Z&59sM8J9&I};4|1Ppa`1=(Jc}Y+pJ8kM4{-XIn3AA->BVvBMA5bvgwlJ*gl|$h3#>u +;i+kCAQIwDuM)3@nGRlZ_D(ELKGu8se;3j+guf2A6gxSL4>)zz}cm@SJJyCDr;<$Fo +2m5kzSlq#&$^b5zg^J1DAW3%1$dq3L{ +(|>N|is07gUE%`v3PJ;O*DuJ{~qi+|lgKmM%0Zds4#N{|ABcO#plLCUOm^L*+-K7&&6@BBw2mM>DoYU +AWtBwCp{1-Rk2Abc^6I{QobJJv<&d03N)D6)4{oHOMjDJisrVw`+H8eBIp7soz;x=q{;!W#ilzigvx= +Mq9_F9SB{*-ti{ddW3Fu+f2O+Zxvt#*O`x}g}My+57xS8smOQoX(Bk|!1==Hq-QpcJuBY3+mI|?m-!! +1O9KQH0000807zB_Q@_)1i}e5i0BivO02KfL0B~t=FJEbHbY*gGVQepLVQFqIaCtqDL2HCS42AE4{~_ +4R3R}Gt+6voaPo=%|wk*r6Mh%QJA&D*i`;HZNW)30by?pa}i4)ku6b7J)qZoXU+7Z6kCAY`q=!Fg +XebCxf0oW5BY>Gf3xQRbvB(U2tCYXEnK8N_cz&3bsuaMe#k7LV7HyC_mUi9|fZCpp8MJN6=P?O~FQIY +2yNFE#H%i>pI#9phI)y^$3axqZw;c*8|x&->NoFH81RHCay_!My;QpUSFy^ +fcADvieJF~c*m9ySNAgN?s!db>ea_LvmK>+IcHbs6bYklqo$Gf#sradr$`Gk^pRD#NAFgu +8cwo>*=Au4R6W89FWw;q}jAD98t{O+kL^P>{B_H-2TuyK;ACqLwNwH}cf-%hzQ`t0#H@y`^IR>A7MjC +d_EVy*SgB*S}A%3KIQDH{`-F1!k1&YsI?6mwTZNZCi>D-zy~*O5WbotRXwDcP+TFlg&;WmCOB?_1*0a +fzKOsnca~bd|i{xk7O@p>s~2ldL>Bie&L5}j+=;zp^Ac_&NZQ;o|b?Gd9;ihb}9($xh4$;3VsqU2pZs +I){~Vd`ownK&?)%^(zNTSIp)2<;IVqAWv?o>t05|tk!rZG-|hkS!~co-QkSTy{35xyuC5@fapw~Axbl +Ym((|_dG-xpVS7s=0HQ7dGldmoptP1krOj!ARnIv%lg~u2Ax +y0M;je!$CjUcyK4AVH-NIBlT0F)`LyS7uW$7K*zC%Z6uPy>#mZc5+Xu)MfyggZ#eE_eBIWm$)n>4&-C +AyC5K2inyLZR!ssjw!m$%g!sYO4hvDm1ibuv<+&zW+#Y0jRE+hAKS#ME)fOo3tdK6Qd&1bEI0V3}Iip +mRH=g1JNC;c*9v;P8!mnf-N6pWqXdu#(Cymuk@0Slb#Uz?`p7%N?4T2j;Yy@&#>Np^|BGdkY@%37zKm +mjZ}-VV2BqWi$DSk*C7+X+a8M3fZ-&(0o_C&W#85qJG*ns4zwBoWoy_I>bW2i9;;^#?Y1@iti|$?6u8DC`5)k#Dv134OAOO8@<1bP~TY(_gL73i8EJHIBbqrjbLKkhr*KMU_ +fDKqw_BJzc#pXU0m*{7y|j3p^~U1h?3Go#kd|D%YA_oo-hM|Du5r?SU`DNyX#5D*4gFyP@`i#K;n}vq +QZFQ=1646L_UWdCZHxDuG_3P&E{PO69bZbmpa{buAjDUik1j}d9$7t+2HTAg%w!k*Ov!o^31;5xObVC +VXbC&SH&GF1SG>IYJeTcXY7)SYNpAl*T&YiFS4-q-^^DK&a()V_6%i(6dT(j6jM#Xh5_DG)HMk;4s%8 +X4bT%|&V**0jgT97y>UB~#M%jDp=abR#4u=)T_-iHhf?_m?g$P5lvGu($n%j2yNi?J8Vl*nYgm2e6pR +cyqN0Yiiqm}y9j2h0MZ*skeqbL9QhD8vWr+>;1f3Yvz=SXoCs4lj^B02x<{)o5fTJ+`fyV%{ImNwlz= +xo*E|+V}hEhl~6>5}LSRCa5{KJJ~0`SN5dRB3YA~IdDru?!QvtPM}fS@av69zZsj>6rMY(Y4uj6o4%oMPbi +c5-l<1@e~0qD6mMu#tKVo1nXm&jcwC*ot*v|2OmNXFmE{1fH6-(K94Rtc5;wd52^d!3aY_lbEQ^iu|- +wAEv;oLf;%!Vf5c@?ukcw=;=mm3fPmrJ35>2bgg;=!*3298GH`Fmd+stGrSvixcA5%F;>i{((e5ByAV +w#eKxRgrK!?jX;5QTs7qOnGkZ>Su;&~IVyLV%R?ll$|b{`ePC_Q$i?^j1>G3Bui$Y+0YT+w2mfjOZ^Q +8yhW4Be`QaRvslQx6!!EJKg=7)5LD&KjO`O+1->WmWGs$G-6t5NnRL=|L6jA!j&Zc-|dZWG&Hsht$pg +@=OJP395Nr +c$;d*NMgJ*g+bmFq?gNw&7@75a-=?iCge6!d9_stC??08xLklNca3pGYTX7qT=;+Ab^L}U#5V={^6S9xLC`W1Otj{NK#HqXQSOl^~LHICPuEn|# +Y$1$T2_xRGnp1bz;#KLPlDRzJew&vfyb3%<&i3B7n7!U=)s9{E4qf6C1l8#z9WdCm({F;!zGW4 +?)Tv39btJwhvGn5PMPGl~^3tckmiJ}B>^B46pQt9RR#h;~HjZ5zA9s~}M_CS2L57Tg+2-thmWvS?X1x +D?|qZf2X_boh_ZZPqJ{{iZBYP4X_5`DXm235^#WaDl&4e?}x={C`wfjm=TJjqR%%pw&k$uD>^u;=)d? +FB!tK#?8XTvTljXZY8U83%00W?Js3)MTVoju^#y@(~FIWA!xKz0lgjB2QXSm*kV2u|2%a2djjg|I!EE +&s^Yl>Fpe~_moUyJ4+y#v7$g%z^QN%7u{AQEcG+;3-||`4Q}2_i$QqanruM$u+bXtcPo44W5Yhr4h(6 +4e*RhZwK@G1mWL=QUfA*QxT^am`|g7q;ng2Y$PeAx-8_nARL@vccKe&9&6gT0g$_F*yr?KMzc- +C%v;(+BfZ|vz53Y!k=C~H&i|974{NsWE{eQF&5#vj!#|1vSo&thr|tGh@r2GHXVP)h>@6aWAK2mnY{2 +2&t$ck%!U000jf001Na003}la4%nJZggdGZeeUMb7gF1UvG7EWMOn=WM5-wWn*hDaCwzjZExJT5&j+^ +|AAFtSX-=xA~>{#ds{S39Ngh@*C2LVT(5zSmMAY{NmNP7>%}4ez0Z)MR+QxQx*v9zFEbp@%QG{SO{cV +FCAY%LMm&Ee{luP|eJyXt@h79&aZszfJK5fiW7XM?YjdJjChNG}k7IK*&!1IVHOwA6P_kkEReRjBH?p +wLpAGAs?6Ukpw58HuQQUJnD0>jvNYw^1cNpWw|H+5dn1^hw&@Hbs*_PrlD^y*J!YUnjyp!$y>oEBRLN +A2KD;kc}@BQpsH@QmP2+N^W9w_dF%}Y`7zP7n&AEZ`oBU-yot4dLu%m|VWH_JOMDib(ehw1m26#KKfUw?D;`n&witDCF*?e{lq$C9%jFV32av-0Nbo6EEBFVC+3mC)wr&&Ua3kX`V +`3>tpt=F&WGc_Z?C<99;sEGPXN3pXdx-FkX2j+@Kp&lo;0$T7twOO4eVcJXJ1U%!G)`R&mPgOl(-gSu +gzbmLcv4;F@K$;_U=eEC&6(rmK5cr4|eFg8$VVSC*=LN8rWb7RP;)oW7cHOV+gn;cl5H9j&}U?}WeG8Gewb`mjqDB}OUG57!2i9)c0V5Mr$Ej#em!k}x+1f9cFZ^@WsYi9#S5;F;CY9Dwl%WO&7!MlUk{H4>-rnbkEd&4qLxvT +CRM +SG<;zX~{TLn#Nl8f*uiU*=zajKai{dMD@b0|TlHT+&M0#Mk4TapSO>}-R`aB1zhIC75IX^1qCoWdoRX +)|}PHK~43hako|bmEb{jHMw*p;JqC3*6Xa)Ei0~AgOD1r>6Y)UI0{hTh6b^u~qeYW{n$Y(>o(8%(7Cf{Nx(0;WC5_YvsI) +Cn|q|Yj650_(G~qkmlq@623-q05ajR5hgdd`7*`ghRYvZ&U5oq)?hlwhK>p%Be?>LHL{&livtt6*`jY3s4QdCRf ++Dv>J(9yA)9(K6$(rX@soF0kN#bC};I6`980d&08yQjzgJDqq4TZCh=+Yq#G*L=}Yad;#^xd82h?@pk +2!%88ZC!-8TD=q%hBVvwBRo+7lSx8Z2AS1)?#eK$H?Me4F2x3@3NN0Q5dUnJJc9Mu9sL +}SkhCUg!R6gwxnga^|etq>JfBgzTl)w4*1EC~XsmhlPA!`#qHB7+^Ord3GT_T;E@#Mn)zd@a;29pjMF +lILit~jfa4MW$tn-OJW!|8TTAaqtG$7mFCN?huvV==oM{31Km_)}<`LO!@GsulECMXL_!1V~=6Hv-L9 +BjLHy)hz*}Uoua9x%^kHI(@tq?w79xxi60*kJj0d +jU$a0Yo2xRicMFM#ztOM%!6@&QE36$NOFf~NSsT7_kjx*!R_C7y7t?c4gmCV;^;Brdup>0#OjbAd3!I)-CJHm38>)42FUbx)T{j1vR +&=ryZRPTQ6TXSN24fCY4!h?)pKwXot^E(>3~yVz*dd-aUu^bWJX|wbtdrerytLhPcq=F{0Xwqv`01t1 +=qHaC2fWYX*+uBjIIf2YeL_l@&Mm@Rr!l{f9adbB8q{f*gv^VS-N2W)Ps%e_@(@B@?HBsl=`mU~=r<< +fMGvGAmx!)b1pqIUE7Ihu_6{*K{xmCoJwFW!cyf1k%TIE!-|S|z@$!PvY9)J)uk(qoY+_iAG9NXJb7* +u|Tr|k*n)nqB@IPJW;3{8g0r##S7Qs}Z)a4Ic*8G+pP1@=DlvAOahO#WO4>dZ}XTfDrIq|!OTQn@xa= +1Jk;L0)bRMF!GJnft?@4%0fqcdNumrD4;mKQ{;Tr3!r+QN(#pXc*4J-3KQQS_GRf1V&7nYd_5ipu1dc +-v=o;a-4~shiRBY(`lN*gEsAC#AY=@YU) +NbVFj=Wu>Db~)_gBJ?ffY(d9avVGFg=TA?Af+9DipP|RQ3XgG@3oVu6wqNMz#@U`4F;xBjP0j&_OI4? +w9dx8zGcp$!XCs^{HBX!n?Y~e<0|XQR000O8NLB_@Jbi(ZGYkL#L@xjU9smFUaA|NaUukZ1WpZv|Y%h +0cWo2w%Vs&Y3WMy(LaCyxdYj4{|^1A{5hmDLvBuev|pt;@|P=PdciU4U7#7XXh5fHSvmUyN}hD+K}i~ +jeUnSF3~xsv5v4m|-jq~*@+ykE=NZ1%cauDR&Dyl*;o4S!cdSruiyVs~rKE5WY2X2V|AqR*@9E&nu>9 +pCV}7wPfQY&JVSIzB2lZPWFvYE~=wt2rczdLTeiuKT!)$tMG?zb%-ryf14w+VX=XXRHFPuRMp`y5w-d->-jCsu`b7QwwJD=s;BUyGr|=UhGsBHQ +pjFY-Rus8+nsNV{19LhZ_{q4yXcL7M`MAg#*!Mnk`X@B8)=W{1t-?xqFJ5{D_7oC`-wipZ9?PapsA03 +=z(SNU=q!X=*sadrF?{=ov-`Y3nb^n=i3YN_%84O`X)?9X6z56q?%OAWw5ogD|YbO~GMVx3)=X#1s5< +SW{Eu?L^v{m}MJQ;Cc?*SI(N4bQBH%Zlf9mUk<$57}_lcFmFt%Z;ik8<+RM_I%SKD;n}0{QenaxP(v> +R)K^Yy3uJdf@FP@!CDaFc4@Z4S9Cm70q@vC$DOKd1F?>cMK07{Q@HxSl%g!mktcGtBI1xRt=bQ4k-$Vk1giJx*5Qy$-X0v;8QZP7V=u$RdcRFfwg&LAHwnSCc7eCX1P?RwK1&Z +=9krsqDv&-~)1Y$@@7BNA2&9L*%JI-VC>;NBDpfFP)j4~1`YhUFS +aW)if^1$?>7w<7C_6UF&XhLqGE%o6VU-=%~HN`n-+LmZ*SiCFYO)Q9}{YSpdSr7B^_cy)sEbDhd&frJ +M=7-l;GMIm^8S<3*5jg;t(5~Ldv;R(xy7&cT`thxOJeI&olOVv7J&~;_2oC6ALR_G$9(8-lVEhtK<+? +#qlJv21yL@bFjV0MT(D2`L}h1}>Ln2DjAw&qr|GTGpYVtfq5W>*lODOjwdHrn4BuaT1V*^hMMn3w~KV$gO)R!DNgTTD;Z>@2 +9GH^679EWiY<-$XzvJV%pNT~B5Y^O!RLarIKTX(hm>oEntyJ=oUyQl&5Teov-ZcM?$2ozxB{2PC$kh( +yUF{=B?$yAsh!q-IMV}Jgok{VEE11P3!G-5J(?W+k8nj#K6l7ZJ&i12SC00kJA>^1La-LiC{<0{RIqx +Csu{_E)&VV)1mr(8g4zKF#R?kzLORfta2m|0tX-)$`{{&7H`+8>1}(Q$_+&fZAd#w% +8U>UyR&OsF|ye}wV>IlGrdz&#q)giQA1$inP#`eYWnD#chVTRy7up?h*n){N!jsl81o@r*??$>3Oqtp +n8dg1tF^!@mFiJ7x};w6lY>G1aw&Of7cI%Y;+evtTrRNP33arX6}YHa440;*}vfIPX%&$(sgi2Mrhjf +hy})RdWSVV(-5D`-Bl6WM~?AvZhuQ{5V0BX+{nZuT=);8T}0603_&XA>ew*IAo7T +!MR@W`r^Fn-~b3Rd^-NP%exvJ(l?f$oSQS%-z$pF;OoqrUzzru?eE}aZq(9MHlX+|cq`BrOFOkla@B^PedUIwV5! +9Duq(6J*K$D9q}$$AY>9%W1Eg|oea611EJa!iBlV#WFctmRhz5U=%aiRnH$c%%Sr>~L$zVoG&=*FQF0 +6VSMeFo@J7=68pvfo2c=@zrt3);JFx82!P`ncZW;5M@pZUvG-O$a*>8ZqcDlwux)Z+y_*w{dcUwZKm) ++Bt2IGzyeDEe#AN(jF`sZQXT-ung!L$r$>mjd`v>RU`T8rVFwjz!pL0acp)R87uzBiZXo(X;W`1q5~muMdlmL5bJbh!YZYC +vf{R{jN<;m-DeK6Nwj*v2dYZ{bQgGImFvw0TLT;}8chf>@qgQCsO>@hgp3cx2fUz$t=FdBxW0z#-v@< +pjhXMVW>xygv9xtG&TYOUhDfyG9e}|QMK#TTgOpv3XH0Sq#j-pD}{Bu#RfXyhrkH-r$R5oUexq5EbEd +d-VI~!5DJ5+UXuHVoJl@Asr`{V0_i^i!eLt(&T3Mxhn;aqW*;KdiT={Mm%kDI5t4h`~_K}q!Q^?ZAaPdB-uL88$f%nYnWRQyQWp +|`-R?aQHA8aqiEdq7x%o@>pWh^E8nUNx4RWi?x58T+>+6o8)8^4IWN@Io6zNUmD(SGLq-;ZAaKW}Jl< +$$dKGc`F@e9aC6%0++c=RL<8(X?6DfD^%NU +g~DYM%36fHV#gG;{?*H?=6Wq2m}kH1ir_2NhnFw8!|~J&e`I(qgbYrupV#Iv=t>gvj7cj1L^xmn#|BK +TS=A#|Ki}347n-NK9E?eM8c~_hHf?dGacAL9dWT)~kKdcuFXz6J&#PYTTi#FmtVD7N?Njb&h$3X;*D( +6rbj83;hD!>UimYTE>s?+D=?>?I++_pb(}G`J38o9M2=+px18Z7jiq~?P{y$W79%xu6^e+qTBbs{x!tv3608mQ<1QY-O00;m`Rt8fLM#&m&000 +1p0000T0001RX>c!JX>N37a&BR4FJo+JFJE72ZfSI1UoLQYC5%fB10f8>_DI~}i7tAe_5vBm%tQ?d;< +M=Oi`1=cIi>VkN2C0qi;>+XiHThxRrrY}?Kw8Dm8?L;7-EzANtw}EX>!@Ra11G>7-D$feapZp`cG|&+ +rE8KoCnM&q&DvpV;$AYQXVn<08mQ<1QY-O00;m`Rt8hD`7Jl;2LJ$~82|ty0001RX>c!JX>N37a&BR4 +FJo+JFJX0bZ)0z5aBO9CX>V>WaCx0rOOM+)623Rk|AC_%T0-2KNsiv}0KrUSH`wfUBgtS6h9R+Ky3LI +&YDucw{`XtOheXnH9;=IGlJ&0oSfwn>K8PpY5WT0G^x}m{Md+YKBMyDbG#4G=ozk>z*_J$>NL}~5C(~ +|Sm)BQWmR(<6U-3gPq$Z-*J(Y@03u=5-CwSjUaUfhXsfB12aa+xv^3HAQQ_s66zw@p;Ey#N~Paj*hAR +oBW3v%D1c-o?1Lj!u174P5}td>p7%L6d1j4E;zA{o`J3U=nH#o<7^?WCLgUu}5nj2eIQXv9@qu` +)GZZk*;9tKh?b!|D_k~fYsJ2tG>u&a}?QcT9kd{Cgv%er>1%X2PEH&!{DRYF3dQ)7MjBz5ZXaM9+0orY0SsB-=y?8OzHFR!! +Vg`FdyRDE%HPNn&#V(6VSe076aJ|p7+pI_m)j6pn>sa>;#b%3Saa1AP*~HG7Qz>x_)Db9o0hRJ1KHN5 +#_0E_UFNyR-XnA4=KIqD_p`GYBc-a_yeh58{2hkOvOo=zdq<@85l0Ob68FCY@N_6dM^;r(AIA;frm=P +PsqHSi#Fg7!E9^xF9jPNuQ0eoJec)e$m5h{_`Wz%TRg6!ZIptku!w9oMX)D!pa$fZE0p8MK4e0qsUil +=9ckIE&;@u?obSqukCtZDMoYV0Y>Pk%lwzNLL~T=%OTh=3-C)CI)AA +oQ)VsOshbjDQ~fvImb36wz@(g9o==9t+HbM%f=XY46~3d$#km)DHvQZ`Efy>3q$H&ty|1Kc=_lvdz8G +Hv&#`c2k5Ux-t~i?7x`rriEoL(FJTEfWqOJKh8s2HRM)>E^>1~(z;~dBR|4LLKWHlai|&VnUs +3}|fnmgk)H{h8mnk2vMam+9k}wyLa6x{t>41Mst|7P08Bo}1!;>%1m{t&Q){>Pw0VR>5t}R${v$~m3R +gsrHZ}!lL{&(O8ke}2X*dWI@8#3h)@Lw(vYpK?(gB#chG=o}+Kvr=#l%bKTh?8u +-_eq=>+z_#x8E5~$R{}m^WBz*e&1+#|l0?f!?E3%;uJ^A1Jw88xP7aS81SQT&o3U?|c1s{ByJNB5 +u2^o(TXe_%k(*T?Cq3gzH+GmHL(pP%CX!%oKEKx{Yfn9l-cGICB@;>c)jY}oW)57zIx6BL1&ucL8l8n +pio!G+GvMioYDgIPhe^SVZ~wO|WaYhUn`1zj9-M&~RcMd@MGQDG+uQw{PD`fZp*o~h&}QR;e{hdpkGT +z-U!8IKbt)*qjN_AEIOGMhbX+>E!hll$__1I9Do=?3T2aW=H=%CF=to-s30JeT2^Nj$H1CFBG%@^57Hf$ZSNMkP=QAYpw#BAI?HlSbk89`%R2RP*K@ib5v^(YVSZ2y9&(PJ`f@By +4{Dl}~#Z-3zyFlr4j;S4quLCyrToi-whRBucu>oN0v+yvf7 +-BWmc#s9ViTalo0gR2mP;y$<{(z`HVl`3>BLLf}kD<=5cncm6E4lGApoD4Pha_p^LfCdL>03iSX0B~t=FJEbHbY +*gGVQepBY-ulIVRL0)V{dJ3VQyqDaCyyH?Qh#Q8vkCP{{x|7s14X!*M2z!=Q}h_wi#=aAa%L}ih-ad+ +Tm3eb)*!>H|&4E-$P2WL`!M+WjG;-W%A+k{z^wtbS|u9@APgb@{}c+uy)4i)yPbg5}&#%RW5B5MbDo- +f2MYYHYLmScB}Gj_&rW^zEQ_zU6h3|Rvs5@`)Uuz_tJ<&u0`_Y`Lm7DyY?Kb_dkiOlD6I~4+TgyTW|B +jjJ;R3oU!YoR5};gjIF97lfSt@OVee(Pn!*h9idwo0e?>D!XpO +)8mANl#+^8E$BSl;sP_TAMlSJ%H@_0~QuZ&!D3-(OyR;#b!z{^RxC)djYugRIEMM0zC#Oq`3fR#T0({ +&s1MHucgnhZGq+4d=V-SF+q|^99PvWfE@Q82O-dWzQ>>r5DO{_wI5vfO42~ICf^^R2D`i4!jYdS(51! +=Qb*n@ZnPKT6ema$roOT@}BE`K4Lk4xQ>_R$=Sylya!)+AdPT8cvwv{C>jNZ; +zPEM4m|4PTz6UQM%!Bf?G=?dSWS?FGy!|G7TTe(GynFABH*=&~n1lnz;*COMzj8zVA5L*g>X2EmG)PUA!{}RH<*79MxKHuVIb4BrwqT#5+Wn&6XoED0pj@<>3rc(M)+U_iVkbGD65R7=E+;m=I~ +fT4>6v%E%0iek2L6GTvL!95Eo>km*ncXhlRtp3Gq%<`1I&*ivo1>!U@A9^Ls(UEKCv>}zb!F|48IE4i;L~ud>f_0?V#3+BxiNJZP6V3y +$#wd&CrL%vk_0Z$b+UoT=2(RCuV6ti##f!!tEwlH!%2E=L)wPV3nw9s;*5BC{CA>JE#j0E3wc(-n)(_ +%D6smL?gKXegvQfZ0$|mX2;&4-_V-NZhA)a)cW7a1(?F;c1Oqcf(2@Ba$WUMX54G1K6tD%7P4~<_jGQ5Ts{*;hSfcOBOr0WXb +Z2#kG=wpVI`&(UU*J!mG|Y9uEV+t6>1y0*eIT+FQ7qK}avx+PaKLH~j_4dK@d&LK>;nvi++if!%K<$8 +I8z(9p^&~Gy>d?-;RO@Gf~ss~UUmpVTM{LJ_`DYuv2P~z*%^y+%^lLHX9bjtVdbIkHCue6X7V%I%HNz +eQGPngG(w>(z0&1!hYoWGW~A?Dx)hwta6(oS>fl90d%Uhi{p`9|qIK4lbt9;1lQL32TK1~E_p}I6(+^Lk0a(ZwcVug38i0*Lp36cCO(nMX6&j&^-V +YxBr}%%tewA!jSv2gZd&*@rpA^>;3YE^|>Kx44Hbi69?8(A-=_2!EO=WzSxOW+sd~Pk7ND^tST4$1C$ +Dvg_=kQ4;Mb6;O0kVK>9Hfu1GEn)Yb@DaS>OF*-Os8EMOF-qmo@57VBB^ha!ep+2D|F2SR*nq-T6qSi6oS&&;4*8=w#>>Dj&#X?nVn^9`4!v2tkot*4iR +c+T^B?-(U$CtFjPKqV(@!=o21~Cl?LzdQGxtKHf)L%ohYs7x(|MW0~UiD+ml(`M +1oY!(7Ih+~Knr&**0#===f+F+XKHL%OQ)&n^Q0P&T0C1hboqG4 +6%H9b#u3hsF#XjPZ=}lt@c)g*kQB778%J_%h#E}ret3$}6_+Xz*#x6ilM#{H(TwJdq<(Pr>EF$E?9;?j&68Z6wW8K*Z6PfkFlgQ^L~`hj!fI@$}9pXPz)iB^cRCbu5g%kS +(Edmx3a|KxbjgZl}jDe68iR{O7)B#bk$5XW9T4+1Ab}Q{Aaew#5N8m#pXFN)&SgqF +Alj+IEBGKkU@i{)1@yu5Y60y&^L~EO9MWENVS+UpYoRZ`uq9Z5otc>Al7}a{bkfe{8R`xIkMu6NEXjE +lP)1+<*-Bki3iYx!?owU$VB2g;!*Z1-&nELhntJ#?f0w84>#ZAoz;nz&aww#6uH}Ml>fSUIZ$<4D+&h +ZJM%dv8rO1tBgb192i0Q{z5XG7GvqyR@JcTJ_U6r-U^7fb(d$>QU(Kl$W~k^9TF%yeHd3G$mX^!h1<^ +pu`(%=c=5uImUZYv`2$8WeozpcInGOc4Z8xa92=zUYR1#c**gH$Kqn<1z5`Uf?Sc@G}(EZyWE?2(yD$ +9@}U$1Q(xJ$#$&+ +U3V&*j#PhL*qRG)O0!smdbUtokW+-{Wv)D*H_eCAUohBr=`WpRQ;Qg4%1_|_a|Zm&NFtg-aNzi-*+;@^0Q4`u|3I!>a234)JZQM&DUa@-OFg?3aO)R`C2lJv`Huskw4Fow@+&QiG8+KvTKhVP3_!iFVI~xDeFBl~_l%lV`~8HtvHL1`!A0iTyKnqY7CgrlWi6BAf_NgPT&Q$@5fCyRF_N_4ni5{Ke(@-A@ +BOR{OJI?3m!t+2oYSpV0O%^03yzq%=MoQx)N0apD)(=!E>GKtf3<%V>Z(xF##?aQ^~d5mvT%zsC6y@L +P%YekuIgWsXbYqnDR;W(TGJ0Z>Z=1QY-O00;m`Rt8h9Wh0ep9smGrasU7y0001RX>c!JX>N37a&BR4F +Jo+JFJoxxpPSMQw@Pot>STot>GTSw~Uy^hvW$YPk?))n;YUh@@DERb6hY=IN8CPcGL|RCQL +QS(Qt%mbDaFBid3drgENP`^*K+gp$to}BNuKXw@ecacGHJ>}%w< +-rph40aNOxJg7DBgWg)7hZe$aSDy!6Iv66+XliUDo@3IuIE? +QX^h`^Yp*sRNKz9Yg$>f&_2%{^x(^~%=7XN@CI5eWRtCm`00}fiTU)&G +l4YY7C5=I+-v}f8W2I(a#P*{#bMG-xdHl?fE*x*gc8ebyRBu6a5lSYHwCQfENv$|ev%*knBwJr=(u`Bv)DG&UOCLN0&~`sJ=d;;2{01#smA$ehe`5ThSp!WwjvkHzH{gb$}oU4hl{>=w +kLtm7(4Z;};2inncM=LOG>GMy`436I;(0}k?(t9Y|4eqn +|N8)o1~qAWVdyhYj{7>(-VGpU-zt7$oIO}+3xK0zQ%1*BVyIAUBk*!oi6F&89nRmOiYZ|Nwbz_z89D0 +?|ykRdwYEGb`Mp#*CuOH-3Qi8Th2?I5B>+bO4Lc#$QiGqnM86Ane)>?hPlbqi2J5l(IOm)$#>$HvXIl +KPXzp<<&GcD5i;l$)sxf^$~Fxmw)B~Yih0~oj7VYE<9gL7a6)Kw0FsN5Ql%;C1a7D?G!45=2x(L&j07 +&lI6xbkh$FF#J}|hC+;l2Fz>AMw8@-U@CFs6kB%=6t&{#uw88=m)wZqY9#O=Ll!uye>|0cOHp@UU^`3Pf~JQi3;Tbgm*CGP+hIghi?a33de4LSYhJ8)5O`$H`)$h&xojG)rnG +ird;UC0)z(W=7JJ=d<}X%NH}=r-$w&nyJ$4W7Wev);ju%-(SN-afv*A(z9BA$Qo4d;%+S^s_LbvWIbs +BzbdYFptGG_7Az70Z_GlO0?Cq%MF+Q7Z;_bIz7*SPf%~}EF+dx;J3Z*_7%>9dMKs$Aq#9%cD3omT7Mp +}*Z^G4-E&VajN^+);|NQqy8j_wO@&mLZ3d69l8plu=SBC>l@fCU`ZCejL;9~)L3r8Pm;0P7Ryo#8~TA +;=vVt%_%v^*H8GgC~8-Efn@(q%ly40kkgXQ_49mX&AQxyX&E{E=)R}CCBil$@Ffo1@+UG=km +MGM(C_7tNiRDs@*J3M?wWtHti^+uMt}H1L1;Cy|1Ez&;flm}I)T1~W`@P_0dXvjrnIC=Syc&f|4CdI* +z*rz#&aTqV!s2anDsb-t5|XXgvy^^BLk>|yZLyHB4{ox8>?s&<+qAev=1Gf`_m&jVSuQ?%S@kU@jq2a +l5{L{eIUy>O2{*#Uz?MF+Vi7K#kTx?|_%L^rHA~SepY9qjwlBaMge?~~>phJWDVja+lXFs-W~TT95Qbfi6PRy*I` +Rg^9SiAiFkcmAEj{o7%Fo0p3%9Z_QIyKNA}^Cgqs$h>P&lk<#0I2`#sRkmhL;*tQ*P^2QinPNH_*6}_ ++-*lGR>A*sum}BR1L1o9SJ2s(@<7h5HK+7%*Kq?FSu+tS_Npr_7NLp)GIgS+U+H*t3SzVbItEiyMSO9 +cJgS%=_Q4Oko5^RH~b`Du?2uds|gn>m&t@Bb)qE|)`kX$pFsr2$N*3{zQlDP$&6OpWKxt9n4;pyoVH5 +^0_9=CvT;NrXWE3oG{p!S_plshm|_SpoiUIxA(&}_+G!L=fd*8J^7Msmavuv_Nmj1JBCDtVxSwPpA$J +0n8iL!Bk-l>jV=aK}!pNppl-nW)uP&Sca`-kT;!LD1OI|1*%=wKjZ-62s8LfbX>5kHsx+26 +pd<-&5a;17!x_z>ln8$}ppPNL~E#&?RQK_m3L4<4=3Tra1;s&a#RD5BZli3dkEa25`(@|00GF&IHX37 +vX4RJI%nwJ^AbFbHA^C8mDEo8}>B6@&Terx}PTf-2pGvEnpqb_nG~NVHzzr&w@8kpR8qG&88->aTUk0 +4r?HM6@5`ctGJ>z$H_V}A14S$e_g<8If~pTDpuw_!_P<2#k#zsk7TzHx3Z=L`In0s`soLvHqk(QoIKD +Wa$u9dZq(LWX*-d1&G9T3ZD$cj=i7opdl|WPdIW(ZfW3}e9C_NsZB~~Bh1qVwgv1%~?t@FjUCB{OUgf +q<*ql>aqNYwQ|FO*gPXO0$lN{h4M51Q`@uqDVlpH9HYyrAQSg2z$PqJJ&9I>IR8h&CMIznJxI**y~25 +aZd0d)7neTpKmU(L(rP)Tv|SCD+vgNGXHwA>bLi0}(fw%=s86665vhjQCi+cq|_5a9e`k+txiBfS_qo +|6{<@=)Ra^-8oE_$_u-6eXBdIYvJYTM)2&Qh$GP8tNl>5?z2Zia|y}kT6WovyB`EHKf%K!u&U%K?teC +Aot<^DL5KO^&#TR_nCt%$SJxE2ch~82UUXOBkDbRxE#64hD3_?{sTnHRWdAIVwjdS@UAM01)Xw~;%~? +2znuQ^W-MNxpT9e2SSRPFm!~JkKY^{g!br9Uh+NBN)KOG!?D6SSUB78}l>~yW%PciK=Wo +qX4rbiYM80DTmkmdwfb5Z#q7LOR$OhZdAn2-V1Wb6Y0jq5X#J+N<^A{2Qw+cme#`aoWi=jedD*i=%8i +f=E>OYi_#?rC0gx-<&ZL-XA={*AL$!*4zb{=eGo51FEr0gP>>h2R>XJ_yPiIWJWK?R?bp_~CsPqSqO$ +nt^v*bikf0E@i2p-2Nka+MxE=qNq!=^;u_M+!j+!2-$zX>j=Gl#B>KcI7BuI^qmsi +L{N8gh==~g6&fE?JY2k6{KqxDkKxHJJ}hfzCSYV^*z3@_sP3Un;63E|WL;(WwKtugllR$XyAj29Gslq +5QYbh=Cg~de+(L5TNu6dUZ7YFA+Qf?VLNn`(RpH1^7QdGV$|Foxb$3>l8Yp~YmQy2BpEN;3Ce1Q0lMZ +oS`qa5V?-#h+l0(1u4{Y?LBU1m*WU{iKHgu +y)*SbJ&dTvNm0h#V{LD91K3)jj{@V&qv@4;?eam8O`-N#BSP~mGy(Avu7v3?!LUaPrCZNSM7H;NUiVB +UjICiMGEc@7!Jn*L1XL!vn=M@Vv)<(Bg83frMKXIEWq6d%~Qii3cYDo`Ry;H6oZS4pTvm+_%~VeN0{d +TH5@u*i!$AsD3u2TRW;e=S-}y@K1VCfGw3{fEUgJjAIUBF{gl_jilOySGQ)5Hsl(u1>VqjKFz^SP7H< +4>wvlj19*M2hV^wFjNYWe00b6|M@(_p%92QJB$7#ot_u01257e5Oqb>>71YkMOs$G@2kCZ>JN9~B}uj +fC7M8tf6xS`-BcJWB}eFF9g^uhfEXUquz;*5hb;*|0VI$CsOL^Ha?JR7y+aIy@UKDDgMCIh>IdzTuGOVxRLh^2Z;yNgdIF{H>(UY0j>G}(#?cmu^^l4r$D!=%MPGHhQR3jy2BsNHAD&(dXXHgBG3g}O`ks}{4nuw0Fr1|zIWgf5t_(I!qOn5l!2oz05MEL^E&^eC-vLO1 +#ZKbhQi!er(Tq402YW`gaVJE00P)xG<98qbNyM$MD?6t&o2L6xDONo!|k{4E-rsL{`vI-b;-|3eZxtT +I2aNie@|8FVOG;IAfq!D@>U{Q%k63{veu*Iq6DHN!Pd#G#KQ_y&NZFpyuG|UyXYgIb0+s$)Jub5TD5tR**OV2OC52hnjgea46)xXO@f?)f7E@#TpBmD+ga4tlldwJFPW4%IlL6f{kRE$Lz4=2ZhN|&-pC%8F#5EBUqt8GrH7ax}10GilG^ +DI9PBjjWMhDRXW13|}&7%=B#3*S6Rx9g-lqZ@50}E!51DsCxHod*57dNbX(Wx^8o==|ABA0&5#a$iVn+&ljG$}CP~_Hv9oDiZ!r5o1bIH3zZ#o#sUY +#MN7#DS1=zZr-r?nS9=b-l>75jX!A +owh}tFo?fn2OU$sU@}-U<$#JPR8{6+X^tq(ti<9k73tINFJ~E31O}^~63qJKGOfU=+TaixTnsCOs>Dn +^ltPjGBuovv;k}^TR2W>7M`hq&z_E9w|3c>`M8=C7Ps2$1wd!7B)wdMXfkLCzk66_lm0v=V792vYQV+3snGu<4_H@dr%Ry@A +o=-qU)VgjdvI)Lvd0$`!>s$N&c-K>bqF{V2d%_kUGJ!DV-c(uD1n+-kV+1BA0$`q^Asg;SA$WjXKjct +qdt_z(&kjj&y#zO$P29b#O%}41NdFvq>lutdW>W-%75?%v2)(w#eE^D|rP}+qx>Tv{l(CuqeU**A;a- +<9!%pZc?S>75~hzbQL7w+Llscoz?YqST}XTpgEa0YbmRbG}#KM6_U{SMfLTtB8zm=+NR +wNOp`p*s=mveN%BPzM628p#nX_Q=u`}n$566Y>do@758`Z==^2G_l}@q{lztzQg!|<-xZtqdhP!Z)Tomi%R=^0{k|8x3iSNX#Zd +uO|H_{+I20ZVGaX#$~hu8+1i18bR=GeGcLrPq`oSKnI>Eu16*eIeiJ!U)Xpyfyn1nM->8w;t +A#nd405wI1+J(CbXDf^)ywJiwMMtCcTP7~La^!(|;?9_&#dVmg5r9 +q_tyeDXid?tSu>ZS6^-omwnG6Sd?GJc`y?FmImb@44v(Yu)M&NRnu?J5=3X@KJ8+q)^^Ewc?1=13@pk +D4$<~wH5DKJRDXn@A0t)L5=5Co*M*p@M|Pe&NnU%4d$kjFR4OI_h$CH?s^rwVTtPVzE?|dbYPuk(~q3))FxvKaB(>Q4d;bczMw~Ml!=#6?|!GV@Ad~+5<`xC +-f?Q5x*GI-l!jZgAeR}2~N&2Z2rHO^<i0WYr)Rr>Y)U%ZNI*q-|4 +$1HHi=sSglQCVVW&6dhE%0g4$gFqQYvZ2zWguE(^*(#1oW3VnS}BCrnsoK|1X&{wR)a;$IRhh@ +~R+Rgs;<)Cw@`zgM-yg;aJva(EYVSU;)=(qz&CdU(Sq3!ny#2dbq2I#OWx9kNLs?Jf55M)CHg@A6sAl +K{#yDK@7`e?A%mFy89v|{k`0PnsIUHLssH&iNa3jOK&g3vC}joeEJjLT%AA-%19YvaDAJ~i%biu|Dl- +qjP{1jF)c_MM#DNz~N*TTG6;CXRfY*&gYd#lDhb5M|e#uxNneYHd +*83x1_r$Zf>)wc#2e~hx)lIo^HH({eW7JD@MnFZGg9Q)$o(gk{oS?VCXupf{c=zP2E>o=CP#&}OQqW*QoVi +{~(@dXSX(6Rp30B$nvk82XGj)|@8IJbJnSp_ +ez2~uxXKF#YGM71TbjZj4_&{V7YRow1PQFM-Kmv!4B<&H#{xH-%? +zHWbpNU!=?64o(WuQcS*fy0*{O@?}J^FoR)nsNo~g()+sD!n&Vvw0P#+}yJXkUaZ}cs=3Uu0+9NX}Pl +rT9iZM`FXcPDO@|NW49u<>IN^c%h>iIx*|6rfo)yzRI1fOgeo_d8{c>Tj8rh(m~@QJxHBXIJ%w~0I^Z +I;W*^y!B_Y;mF6iSmU-kJ_N;&NK;J_&vy$G0*yvWeh%_moBrv#9Gl-Wui68E<4y&Culn3u2D`xFE@h` +Kh01t=NV-(2;cZ6AC9Cl&A5F35n%{S3?SARNPVp7y!0o;8afbB(tmv`&j4fhibmk$08C#4bGYZ?8(X; +WJvR5`+KD*9HcoX4FaO;JqE%6oS{c^6j7TqbjA+>9AW7~o2lU-U53PX+O#=v +XzwGd4BRWm$)0a2?#boC7vH6@*ZS_Ehl`BGfwz3rTT2@{zSS5Z#qGq&h@9koyxC;Y|A&pFUHJ7U--NABOC$|}Z8F?Gi=T6@j$Ols3A@-l`{RtxVn-KnujR)H +f-ere@%@AA)d2dYE7(hX5x5UioVC52-)FGANVJ-&MW}?pYRkALK@8orbm>)TFY0^5Y+UDr_wyB>}lKF +F;_&d)EJeWbLGr`>ulzUAm-4kwFDkzcDAVist5HQ?}2kxvC?KQ!C&$*Fe>#U(AIcAE~QlFb8b1+1e@*G3Q}r(R^yeDhgZfzcR%zspc-$xCS`6qG+ +__w83tJ06YC}sGt?&ICjB=Sg#j`<(`5&Ilu{ey1cPETnAk23WKlOdm5x6_%MndlUDk5ZR$KR{$pfP@nm5%*~hMz2iQfwUw; +1a*?SkIjJ-Q>%RHVLGUHCbi_w?UxM*>bHuHwhc(4`pBtF`w@F4+;7e@P&7+_)H@refKib;eeXh<%)+; +%~E91Xh+RYn2lUMdIcyR2RCHbnHF0z%xGD~#>oD({MSKQtR1yZZgZN9)!KM2zMqA~bG`@*hdimutk?E +mo8s>)joDA2sXbtFQlU8s0sc%+rO0b!3u>6u+IwvQ1!OGMAJ?=KWoPhfYx8NYg=JH3;dZAqj^0;x_-` +qeZ2Es!B*_B!ie+$#+BB6-O2RvJ%-&xE#V=v`s#WVvs@0D>uLdE`$N?b_SFglyj!G%3jbmSFV>@ +ga=?|arE42i$>6!Iv(!?`H*{-2vl*_3*-YTQo|KJQphJn#1*}$=L!ITKF2E!hpKWPdZ4qWZ;mvEF%A; +#fb{x~jC90y7ST4?W-~w2jt|{zoPumGGy@A{7Vq@`|o%3lR&LqA|lwYgf^nH$Mu^)Ub6yF!=Q_8X>LqSU_9)rXDXtaCDnW8^q(T#`mX%C+1J&hrvAFc(T$$bv=<=V!9P+XfwUB~3Uc?99GqrWJz +#%f~WxC+?5$C@CZ8CJyeed0E_gTp2>K0^U8Jaj@=G@ZFg|U2P0_TYvvqT?#5BJw8cdnb@o0$zM{68V# +XGb_YA}CUmyFgblp);nR(2c95y*Zv-F82=v^xWEvcQiY9isQY?2za3WBnL$u>H#HYshji$k;_I^|Mmz +@DhNI^by?j5eF4RRkp0eG#DSN|hY#dMi(r{Ftva8>yuwLaOQ}w-u6-dQjL6c~r6dU+(T;iF!qPZU1^OHpREcwuzM&w}P +cuO-{s=N!Xn}9i8Rg3nl>>j4tRCTpnsOGSUIOYf0R1Q1{6EqbxkWzSo#6Zj^!zKqDT={afV1AeeS~(q +A|t7bsfbk8pdzw8uGMjesr!d9=(=$y9`9Yq?L6<^a_ziS_lf#Xg2XhsYuEXnmu_CZz5&!f8F;_*FrZr +{ohRxtMdyikQK8$?xxdDFjMu2SU+oQW?$h9uU7<^lokx3J=%5>3$?QMs*OE?~KvxBCy6ZzVK;ivpuY! +B1`1n_>JxGYm6=NPNyvxTzUc+POk-cBa$Nhf*P)h>@6aWAK2mnY{22+b9Fv&6j000vM001Na003}la4 +%nJZggdGZeeUMV{B(#D8eJpS&JrBR=r5t0)**?PgQgEs>+fkuHp;3EV4Bpvg8Ek5O`xAo +$lD2-iUk2Ga=a@07CseMrHL%XJp5-9Sx$WGy+W-l%}d-t?x{3c8`X8(vf&e}&{-cAc9tfBqk>vUsjVg +{vkpw1i-$+??kt=EAKk76%<|#_=A#_f6ud7R#h>$dtnF2q4q_}~F1}<(wVr#54GJR1rGwDm&C)AgjU@ +Bbz+PHMw-tu-{u{yDI8cQBsK1XNP1_rTMU;G}_*#DfP)h>@6aWAK2mnY{22(KON}!tr008a@000>P003}la4%nJZggdGZ +eeUMV{Bl*(Ys+}A(~$Ms4BA$NqkPa%l4G?KPi?~2`(9kuko_nBQu +cGC0=Hv2O3%*-<{>nzK@I<=Q>6teHNs$@{s2r|Kqy;UX@mls)7GHX{BlYFQB-E?EC}EErPK!#bLJq-JcHe7?E=}rZr}V$7d#Oy>OXtbWdU01mb1z +N(zGAxbvqt4eh99fYOGPn|Md_>RKRajLyW&GodXcQDca-R65|~MNdoxp7v=r{*SzeY#?x`$SEO&WvC4 +RHQ*sw5SYB6{c9CW@CwE}F=<4@wdkFa5I=HCCUpw?EZvB5v-^>X>*iMdqfV4sP3W{?xrg}7}5f(E~E1^k#qx2(8508Z@NJX}1gX7X-9g7~Xsq2mnu$ErK9!^6p0S#_LMc +*6CLwD%)#)yt+hR}Hq>~)a3+pkbj;5ilP4zFd1jAM +D(UGO3tsaFBe0OJJ)o-}GstFZW(i^8(62>@@_w3SBXoh^a&PNFQ$8v133uR4R+e$BmDy@Cac={q%*Ci +;!staM!sjlK4TjI$B5RPW6&#mSMI^7GhnN_>5Fb7FXKG8q-NIKjNH*h)87nNpP94oCti4W-Oa;sUJL1 +eL?+0R{ddK6Zq9RSQnwnIg%*XZeCOq;J@#uqldIEFz*3e39euaJjxy+SnB7^KD-%h9eiN`k!@sA&_pV +$yzU5QWDG^!oYHAuqw{6&E~rYDvbT=v|hp>uXrDruzWW_e?T^n7#%$8r0B9(2XgDVS}t&kqk@9QFzTLoS)t{rYnWw{6Q-!bQqIZ(=@Y*ywf`0t}w%v$l)FKfb%-Xm->=qaRCJ{{bBDnH) +ctwol-=4!e=@pXoEB^DS|vf&vc`N6@nR5|H8uCwWyN9}+tMO|VRCb2axQRrjaF@I<3g7!R$9F{zEDGA?atdXGtZ11MbWQdWm74H +R*4*;)CO|hSZBK2kpM(f06w*vwZJ_f)Iv7U3S-f%S5XwLSL>CmTWuU@J3iVoJ(ot;;La_Z4)-6VbqU; +ctwJ{R8m(;8Og0WpBUG9z8I<$7(5;gM=WCj24>Ir|5y_PsW>aE> +#$=2zD>rENzT!#OKtPT(tanU%Fv!el{o)ek=9*u$UjdG+6o(bq$FPAZ$$(&oX0qmfSb8I2`ZefZYsip +!T}La8i+1018=pm3ORd$PVNQ38>F3NWJ+8?9Fbmhf6V-oozPl@jjPE1(ax`o`N>&k2LBGnCl{B|L!$@ +5aK-hueW3Y(9t&R|3Sn27fv$Dm+C0G~aTH@XG-F9UKQDoT6mX7DbUgP&>0;cz7?A#hJVeZ9cq7f_OW# +X+%u`Dy8p*n#W>1hfw>`VmnVREuUpf#dOJ +OTXHdtNm`3fI8-C6jPBZaCLH|qf@FZQ%z!JpFb`p7u&nl|xbRbj;Zvg@?ZTI5#R2Y2hipcB0QVrPMjM +=*^)w7428z}LZwqEofi^eNgXy(r7oPFa3BH5ltliNwcW?jW&oA&ic%)vGI<*}g%=sl+aKraHfw?9SAb3Jq?5!@_CN=xukjY{k&2Ei8mZLXhtB)=3=P4V +f)N3bCOHjiG^z9s5;av~AMT(p)^W4jIa#Cb+~KE90vk#1E`rF9yIn^a^V99e+dHk5-JC}AFbo5@r6+Q +AS_H}%59|Cd_ZPmC$p!UlAWz?UUvmC8Yu9c!?1KJ6mY4p*_SjEsrq^w1T|wB>M?;j2mwRUWhNiZuk_} +pHFSPJUx;c$#OTm{f{>#rkk+U__Y`=oTgb>6Q9w9`~o^eQL1QpYe4$hCLYWlhI?L@CgBi3u)uE +%?rpa+i+62A2wxkj*K56&`af64h>zR~xfZBDilzaKQ-R0yhuT)MxDArRY` +kZ{^67GK+x`rW>dRoW3qU1okZ)sCh4KJjkYLI0mY9<}Iub=+$>Fi&h%6DJBoPCMU6zz(y2UrP3VyC_F +ALilhyS^8FloX0Y7F#YWN2g~styD<|wJC8zIrL3Vr0SKp5Te1&pr}C!FP+SbHB%q;v$QN*x@BdV;kc( +ys)kBQKKD$i-QQW$y%+tKDp)&p>M1_7H3M$y;B~fm`h>s*Dt=8moX%PQya5f6E7o5KFo+F|Co4Are~l +_HtYsjDnyp9)`{k-EQ^}fDPM*@&z@agkGB~)c||^nmf6+uIx-7&LUJwb?I0DoV1zb&-LjfobF +~>4DD?8chAcp#RnN82?unVTmdTdhlt{L8WRr7{=FczYBe|9ikgs~Sg3rNZuhmAsG-i$}DM2n<4 +q7fH{|`&E<%@H2LjJs;3|0-A$cj-uS~7c~WWErMs7vFACD;QfURsHohl{-e&h!H(^hD<-7PRYFTc`8EV3}Ez9&Y`y;g7+)JC257y0VN#kl=9LIcMnVqa|DV;T%&&;- +nvzDVYHiCGHzZLY{Pn9k`%uSZsig2?+v3hn22?3d#61l;uIhnJA%9f|1s_+lZj}?m1oSj$v=@~@vTiF +>-kt#2ESpIUs7EB_v1R4uJvk(`Bk&D+1v?*_M#G ++7G(!vM5 +NdQeYO|Ot$!<8}9^LaABu%y$$NHPHKU%MLWhj#szt?1A +w8AA97toLyH8vx2sZbV@EAT@iYLNPhxvyKuYH2Q4=p%(rPxISzXgL7?pfY=R07-2UZxDaL!c)QI3kVD +!V<2F7+O#;Ta1~Q=NzGsz!G=PHH*zy748rCmv-Pes2wg4hn01;g=K>>snpQ}OTxJ6%|hMm%K`Q5nK&N +JWw^%|lEFhVby`p6;PFbdCQKJjI5bWs}dyFE#tRN)?G(e)n%gJye<(Z@n2^~iba=?V&s9 +H-h+W}=2kEn!7DSr{Yn1c)J;`TFJK6v@C`PqHByT_eZx+iaW5@JWEB@j4#+|MzxXV~@54_@WvgEHwGDNE%^qQbY;te4bHNNIokiA`Rpa`I)X3q +Hvt?Q8KLj}CsM&XZ^MJECX5gB1CBiMXymX7H`zi`k0+Vui_QSJ8PwItEXN(j?Bj%X=ilWaK=)p;nUUz +zg(EQ?{bi^7;Bt7fnnK$x6j>u$?@5|$iei1GMYF1)OWw#<>tToALAE+Nz?tiiZXE+!oq8`$S81gCS8o +#)Z8|9QRP)n-hO7%TSaaYw2Rt1fM3G<3p_;fgml?6LNW_oBtl68JDgK29&--J$;3#3%N*o=bQzg>Fr` +<~`$5G}tLw5NsxbLf%sVOg<&wp>ofoCdp2#2v-0a_-FFQY#81JebCe`ALz;EtdZgmHpsNp9|r((a>;f +U>OpYZHy`1zw@20Q;!x6NE3=~MM*u-h%X4RTQ?X7>*ZwCC*9~FVIF6Yt(Lu?g(pB;ru&o{?s%il6RFi +tZY9_QLsBMA7-Lxc-JM<3#ooSz3sK2WX>n-V1}(T +5?h4y|2_Kwa4as32!}ufd^Lx+D1SdCd%GnB|gnBQ9u@g_eT;NnWd%Ht(`+UVJ94g^h=2l{SlYXD*W}x~b{3l6zc~Xq$S7n2g9=P(!~1bXGBF3w1|D +P#$A)$iV=}Uvoo35T4ctFgP~>Ck^1T7AUk8HlQHTw`dgY|j+iEOQh9T*aq1Mn|l@IWapt5GDN)?1GEi8kfrp`UL0z9~RD2&e|$nnX^F;wmq#o|Bo +aXedvipF+n^`}=9YFh0YkQ_)McqO-YZod@Q=e123XvXkPSo +D>k_*?M@y@wQdCVqFI-G8+MdbkM&sKQWe_$4DdCZD%rJT{EdK|wy(^26hu)8{X++Ek3z#dsp1D_>*4F +%px0UynGDLv%$H0LQhB}BuV0Zsxdo$=HW^hK;@V|vfk!8$ +~rNN#--6Zw}5+t3>^p`L&!IbmIIRZr83tC;qSPSE$8+o`ziPxA^PgK>?aF{Md3 +IS?G<*SOigDePU|{%9g+7M+0aW?n*B#M3{|Q}-fpQ2nPgvRZ;5W*A3ZA4-$YKlFK~h~I@ +D|Qz|SF?2TVqbD%xn4OHX;{#zg(07VDs~jXGZ(pakQP0;qR?)akmZp=X64E;O<61k6zNu`(J?*ihXU# +}gzNg5om+>UH}^K;a8PWU$9L;W-Lj;em_^w~8Aq<|ElXLUdiHPDsfC$mDY*pOIR)gBspLm3S@s%di1w +BfM$y_o9NlQQq4!J|bsLog-j7!PdnkIW7JI7;;0exET5gYT9>3?ghy{7XBz`?bQY2r*mKiw(~J)m{{r +W4_DYxVCF-*QA9KF<5$xS&`;<6nE97^x)IY)k&j7g*hC4w2-N2cDGU$G!BMT&MdF7yBD_+Ym2z&@csu5ee`kmgC{)JZ008y2M=3*AOIhV4v{9?7|ZOO`L`KU`@6aWAK2mnY{22)|3Ye2CI003qo001HY003}la +4%nJZggdGZeeUMV{BFa%FRKUt(c$b1ras#aV5S+qe<_9w7gLP!PnLC7j4t@ +Hiw`{1A~@mn^{>@Nou{m!~OR@!xxG2+vcvt)j`&lG#t)6^G?ZCqqJqx3T>6D%>8@P-!U8fo@=#YMlNb +zv2g$D@=urVK1^6C=De-!RMvL7WFa8_^5?%!khY^xR*6sl61ulW6Y57=LoHB8M}Dd%&cu`?4|D +{WZ8YY?u4=VF)AOqi^Tsx3+2tBtW@^@d0p3`d+U9dmapzkg5M7NW82jr)QwL8Q1fJ7@1y?c}|G4{pGw +MpUA((}@1GsJJnsMhl3gMS128I_Fh2*jv7oU>Z>TLW7NCUI?)F>(fZizU~*fcF?uaLr+2tK8;mX_ +uZh!W8-S!X&HD*{5EV9fS$oRbw|(fK&nAEE`XblkX6I-r4e-b5==XNBfpHOKyY#gHNBwi5)-a4e3`3h +cjt60N{}p(48!buyKzmXZSmG6;Y4u0-6hn5Mthm9t2oY`24WNy8HeET@%W?nzc-G?#p*HlatkXXc4-oip?}j +?umwdTrD!m^QL3^FRJ9ss{K0AW@rE2e7f*91&hivmA}d*C> +4(1T*E%^M5xIZ#58{FR|T)6-`4inF5>5!PPSxdQx{PYIqN2X;@^ +Jf!}i>F}-6vuzANLAv7oq+JloJF~w=Iqis&Rts5W8k +bGMd&6Hu9I&ARTWWlcU_fE`5_p9675K~tyTd%O0zWWAKd5+*^gEWs7j;mK!{Gs2THBbjlamD;zn +xKAbJA=O1NF&j(`-&85}hz7FJ8X6dDDn`l5(S9g;R#cU_u;M)I3CfBPF!hZ!>AOz4MrT_e=n3P=uULtMByt9|DHQT;E +5s=L(eb)Onm819aYWFv|k!SCo>GU)M|6M(AOx4yhpU6sG4_Q(InDh+o8;t4T>#I}~~`h`M676EhvU$(7+yBH*c&* +pIzTP)8SpR_!3XsnB<1pV1JUv`@K$D78iRGNKOVtjD-*N+i~U{@P%1%I8(piZ?6uMG~uFb2RQsvuWpZ +d5c)d9**D~1t`Yl!-qjDbcR!E#ykZ}?)K~AUEeX|&M5oWkEf@nPv5?H{vCDT-PN~)s~6co>+eH}dmr# +m8?^tng8gnKXJGObwBQRoHC8)VfYoC*+8V>*-ruliWBQw}iuU;<{1IZ7$^mh2~?F&fDm=^8~p>F4>Lw9aoZMxV3O{PajtNEFGyB|EC#Q+Cp^CMdHLll +=-k;o}y=CFa0GI!4h!@^v!LGmx^Myt;)EO0_pB{c=G{!_ +5AdOKWZox=B&aZ9lElN;v}{e+8aIZH?RM3aWb#?wRrht)-Gm(>n@DG+f$y=N81q++vs-eJg`>(29hMm +!35eThmY`Fj8XsFoIu@8V2qhduUsLa&dK#x@l1tk0>&8R_heP7?Ba7O@dlOuOK3k_$1-+hRAz+Nv!aUCG!Fu88)nh-#;jeZ;kiDjJ9x)afrD +%(m7#)W8$aLA;2!&IyNF{Fc9+QXPe#``*P$KkvWf(r~A9~4A<{%q6{9I2jtcy_MnoXbz*@d$;}{DPlF +(l28Cm4+jgr~Q{_N5fHy$Uv$wQ?oC0#2HUi1AyFnt&8AohP*6?2o3ZPasY;kuVy7uUH^!nZqTJ467@G +~KCjwgi1c57@{f9Y +EycdPFn#wHu|w0{2Fr`Q37CoCRz#>RiUtB;G)Dd7OCj^r+0N4b2(`kfbHhq>*L-YHw0V!jH!bmqB>b6 +gl+y^TEeZ}hlH{|4_)=ld1jHRuPag_^oC>qwXC&xVY9q2b%2%&*becwe#(4r*x0cpPqh3BS|6aSL}b0c4+bG=Ml+>|B=Uk08mQ<1QY-O00;m`Rt +8g4@6rP?5&!^cLI40D0001RX>c!JX>N37a&BR4FJo+JFLGsZUt@1=ZDDR?E^v9(J8N&`xRKunnE$|Qf +0*{5Ti$Gt1TmmocE?V}$jpv!#>pU933Mtv{TkL{7I!xKg62lb0`EzPQ?rBSj5^g<|`r>?_W4$?;L +n6mN5xcig^E4b4{Q*%Ygr?&XV`^HS8;tZeS;s>w@s!Y=dTCcox?5p~HWEAlqqh)Q$^7*oIMSYOw?08( +-(ssj`Iwvinoe)+-&+~>tk)Lb4It}U-BCOXA#VLgrHrCA!U3&GWj@veY%Efx!0oWWT&*VjPWaNnrmQX +SxWE1Nyr_I1%UO{FX)u;xzWm3iH?9XtTGnz#SRtDalf?$F}W&8zR~!-~BVs#~!SZ3m3bs};NI+lnJi1 +kj2$$wb|82``fZ7C0dOyyLujgHPgPD`7_#`?4YYYHnnLijWyOSump{voK~jfWg!IE&zL@pS~CO7j+Z` +zc09^Du6tfvXLuxDfz8vdi8xTs`5-odK|@s(!U3Cs)~bTq(%2m)HiSJ!v_Fctwh;YYmtHWR4WP%O3o{ +lD2_A0I2i6^yR1EQJ1{l2{4CHvKYANjv~A^D!y-KBmLU%TZ028P$FlRa{GSoQMd3kE;%GacAp2E#Ih&cNimd{gn&%=-%LcJ +3XSQ;GMMw7HRZ0VIKm!HDBo!fCvyxMRSe{SN-Ppe(4F)`K3xfmTh`qsYiqE +%f(r{SjZ?&cK0kZnLKEO7O~HsXJcW467DxMy~-DHATOTVu6WKL;1eVN+APTU%_KAPqMPZD)Q{=&;pUy +;OB<-hzc-eH(*_AR#tl2TOqqXuXufHCzcQpK*m{*rkRcJ(QBd$2kxfUmnUF9@95bw%j$g3v+Nkhy?jB +zj2SaAt_Qr8sFx8Bc6^E;NIFFfy!cLsL$*g4wgtaq{ih9+HqvDqbYYpX(dBXCVA7W*-?9uAF1jpRqEL +?6`cLeKrsk)50RB`DW(&~6z+>kfz$A8t(;=MvI68taz!OAhr~ETfx#!?oN{0yaLF5**URG*ZR1hjn@H +jQGjA$EX!1(nei1y1Dhg4YFN8a@klY$Bcux85$D?_Y|WZf$+*R5=BMG3*#%#jT2)dp?}0pfrB;=I^mF +2R7nI`+X}K6n@4@+nO67SoF*Tsy#q_UVM6STc7w +J)Kw2se(zJ2?2$fP&r^n%y1sjF>v!cFb7EOKom6tvxBS(B{Bj?J>20nsIsdms28Xf@SpcpH|R0yTwFO +ut@Cx_ue<|=FnHErD-d8yX7B}~-}*YuAl(e?VWRiIXu9XqVhllNV-2e1MH!+}{xLhF@3LfZF*GE|@V4F~@LUIVkj2w64TbzQ4YPf +o5yx9c~Uex2xS;bebk4<|w?AnxQ3zyIU!*^MY}FirRIpjX0FHsmXsk`{QN6W&PASNZJp{g6X_pd2f21 +_^n%%^Wz{CaTQyv~^w;{vUNp&XjR7^ryz2@qIFB2>On%gGSW!CDm?CtCXKC9rfkbrN=# +e>I|>P6EyVCA&CAou5((xgigvLu(Fn#PBmU|A=e293`x=*-6ZR028Ei3X_Fh1Dc-NvEI|woKH%%#sop +cTAKrQY_$o0N9-+_1LR``%mI<1$k&@5ADBpiH6%t0&pQm90s&8)f_5m7*of@`MCOi-PW90$ +WOVhfhr@%Vf+EPG&DeC19$0z~IF8m^+VnU|=m))L~A+JUJZhggQoISnua;=*X^t+?*7a)u~iTe+g$CBETMM3Z(Q7)&j8)z##e!%0w9(_-XL0=%=-dm%2p}t^?c69VAv&jb<0hYq-9dPuRcZ^o#<>P!ExIdg +-#~)Y0Gx#4qHPeg3;H$G5o(c>ns1PvGgB=vsutpz?2Z&D!ve#rEgr}ig3#`z0%uKpa+?K~=)EgmCawf +%fenn8kY4Jhm&rRI(C+eEf!^Q1Hz-tExU;4S8At4We#0RPhiZ9-z0?VVidunAIcQ82+U064TlL(bV_a +rwQ&v*&aupD;1V*TJ(6@^ZnBUw9#aAN>@AxZ;CSjRSf#-va-Le{(gq+WBb5S8J$;3Qt+BKUL;BQ#84O +HK(tb0nqP?x`3g-*!{zRo3Sx0(cc{KX5}rLwu24MW--|8{u#IwMk|C +g1=4;@w$x{=+|F1#EJD`S$#M_WAAk`Md1RcW>UFM@M};|F4Yjz}qjH2(*^FT-KS`W>D2;xyoeIbj#2t +KoLLm9XJ_CpK(0O3XNX%WkcRfVS4)aTHFstaDla#sf`&F$3iwW^-eSp%2YvpU9meUI!?AfR?tGaFcVE2Ph)9rYDkBnP^yHU5h-T4V!H^4gJ7?ybQ59*W5m +>ErUS5a>pg1#1_n)iFhR6F$WnSyF6Kavyya72K=uu#!9pD%8QL>b!7P~W&@t0VREcd0;hJ-0@%h}k6* +;j{DC2Y`_Dg_O2V8#NmN^9*GnFGj%h@A~_cMB~rh#w(gOe5v7<6M#-jC@X3V3wLK$FiGKb(E|{E5+8f +c?Bii)Qbkd|(#LA|>MiCvB?^tT7;C%u67)Yzmc#=7iTLirRW77&5@(jO2ISz8d)M(UhiaaVP5Ee!D;q +HhEPXuHo%sUWgNjGM5Mu#Rlx}?_Ff*SyiIt4mS25Df?!|Y2vkRB_&389e=j?sMXI}0gCSxYVj+bSzxv +SK>%ZofVxgLH&DG%7$r}nsQ7at%K}raN)#k-6PsNAL@IQL5GayW>z0Fm?T!6u%0Gcu45W +TcAibEVbeYrfCq{6fk#o$$fjAO`EGwX7tX*e$J#p!%gGM0fLnIyh=oS3MM#*`@PlT?s?E<4+TatXbY* +d~%e#eC|vfrorBES|ypT*DnT+^~n>6u(|^Jgt8zJcpc#SN=}&0eJ;UHdyNeypGP}_UwwVxn0_-Fq3A_B) +iR}068pUH3{(1FG+@~5)zu7D0a-EVG8mdP>&6%6PPa6X0(Zp8g?zfcg{4pw-BZHbzU6S7S{ru&XE6eP +_TmbZniuJv2m5@pk2nz$Y5JUiqXZl-+cScIL*WR0DCBP+#RvZ>=U}43-<;b%f{UyT>*e1o<_HY@b^$l +aM&F?jqHo9T(vO!9Ahued8_wvPyHQS=RJrhL#xjeB)+F?(=?Uy)*egHs~8+B?r=o9j*_O2#)Am;r>JJZ?|^cE%J<8fs;r)m8 +rAhwKgyI>3~RZLYdCn1u3dBs7emH@S6?L^g0{lRWpgFnPKzk^vu{L$QhihEB-s>$g`|mu_0+A(D9w8J +yimK=@=g6yG3NaVho`Jec7F(rGvWiUf}PdN+$?y#_m28^Cm6FXg9VXd7bEe +;~!2!_&&9?CG#W5*gx#aL9)q5-}xA69GO^@Lw(6ap6!OVtvQATPnpcM?i-@rsCSY4w^i(+==gvn0!FY +V2EkNE`w&Lk_C;lj4=1XDA$pt?#(JTvw#JW$_-e9=ac=0!C9xybx5gp&Z(L>Y5alOTmvUnw#moExDSK +z+OenG(4iZ8ki!{BaL31O3;%Hd;{}4$yIajEX*ym`c0qs%oNLjg3sJA}c1^br>TPUW$(AHnG-f((stmL>?7lhbd@lhv=1^o0~aw)&DV6i^i9QH{u=2CR*c}@oAGoe9u}fV +SoT;FQjbE^w~g5=68e0PZd^clb}N9(E(GjNsI*h}e#IV}YWbaV7AfY4{)l6Zpk6xDO!QqT +&&?)#+MPWgk=50CY}OA5+tZRP<@;`CQd}x?27+m3*!`{t{Juo*F(^1)r{dUN09(t~5wKeF@D+A49b@smO?j%cJn@s$pl=HCbMkz_KTtl%FY +i$AJ;5@b|1#&Hl8X>1lhnOvB`QSXOjZqavAewBt&>f*J*FBurKnsY++rgIXP7NSq5n>i*S?aq?IT63u +7hOpK^r*xLUp-dR%YcNx!Pq=kAiGap^l|2k{Go@+xhL93w_ +Vk%!-!5ID0B00AHHzxZh5g?pI$HO@+2AzXhmXuihd>voj*C;ht%uh?2PxvX`HZ@X4*MoF2I4wyXL_Ft +&b4;`+4OARtB%?pEbmywbNn2o=AIZJz4?jgnQL*sT7sQ}nI8}z$C8@%1PstmMCi|p|2%$`=-&MdJ@|Y +5x)cSqAVAs#m@e4DKO8=;KKw+%{vwZqN0DCp{}t~P=bB$TURkCsMqa&sjm(IzXi~8GpXr%yX?(vId(u +4H- +re=SlXW|&&Zn}G*6Uoy_@S1q{;tH;l}G!o_KcR1EJOs^v{K+pDxv6%)@VDX-ttZ^pHgpjmf+7ju0QE& +%n;B?eY{U>s$lPIVQMV3)_hs8TVggw2=XooJJW}vX)BU4_Bq|f@!9x|b?Sd!ofO1@$zfX?f&EVvz3-* +d=%>-1i@^VmL8#0YW54@8&c>;Mrum3aQIBqR!>P9XCGxm0BJ#zvt +@6aWAK2mnY{22&IL`+u4S0040m0012T003}la4 +%nJZggdGZeeUMV{B +&&y04AEnl0%x`i~v1(&>Ic77k;@;TfqlzS#c{INjIx#lNtyx;(Tz%lx?3eTTi+69XF5isV)vL?v>Y;m +BLSL)7iV824i(-@I+}A9wXBWR*USA!cuS}^AEQJR}BDCjT8cm{zLRj{!wmJgM>##PQ&o$4)d_E$&hZXDAx7A_6vgC<+dM`2~y+{$S8B&i*(EzS%FY{0(hqi8r{k9sbA1iC1S*kDJ|wM_ +hquA&|E^OAcR^!J22Vi~$KZG6x4PW)I7OsrS9^)U!A*_{~qqeRWW=%G)_ +^n`^Efm)`%qdP98^)7_nZi~+J|MLu*n4|Q=zS!CNvaAYv7Nnkut%r`!grJ3!2}8nb}zh#LMwb>b%(wQ +lAdIqY=)zI&Q}%(cW7Mlu#cnBS0?U%YEWCDwt%~@paSd%y^Fy^SJX-*Z*3vU3i5!xb65g?SW;vQ#JtT +&0XK$=^+YzMg-H#ilyYpSfh@lnjhXOqL_DqqJRy~0xy6Y(sWTJD+#!unvS&l1W5EPawqZ|BS(WYxwD2 +)6lpi6Ci5bKp6(GWIgvcjck!;crN!W|bF(q#VEa11&OgAPFI-G3{~A +4(}TP1)QXZvi~e0X#Z}X5!Ce+ZL7y@Uq^Yt+dx{Ni)y +^RP@iE-EU%7J;KYrINy=lSC;;MY9`94`k8SJTww?#5hfQp)J)2e?Zg)()&t}Fe)jxW>G7aFx3G#2TN> +1jL|PGh)C}}BP3w(u2z{8P9FAs8P#b2kTqlkP*ue9075cY&IxLIEm@Nt#hpDzy1q2UN02B(6VzY~vm* +bj^j6{U(6Vxva9TMqs#Wx6O;At&R&}bqWnsW@LX(2p;zUFYztDGI<4r%EO>4i~*t2N)atwtIYIxc}&p +TU)+a#`3=*ecP4_^rPp={U+XdUzfttB^0z;Gd_d2ztFH8?gOjGa-{f?KmkFKie6N5ifTX$|?w|U>B%H +yS&P|@!rGUZdzx9;VYS7Fqy}Nb(nw~JNrp?BL$lGN?fJNrk{VFyK!)+3z+amY7>P1zW9mE{%iYhD +J7LhcMcQ~GhFI>O@PJ$-(eQfUZ(e`Kp*bY7uj$QY3X7w&0DHk*#e2Xx=Mu%k9Zrl8^gaS2GwjwXz4S@ +;*#MG^jwn`c7=ZmWu!Y~nTiycIiX19`KSsBYS9&nF&9B4tlMI>=2eY;VNwdy`j~JFD&ke3BO%mPrMF}{nfz8r;kTf^5^#a{xjUOqm +mnzU8nS7=$@w&w`BXYr2si!i`^Qh~yRF)T(3Ee%2(bsS_c!JX>N37a&BR4FJo+JFLQKZbaiu +IV{c?-b1rasJHcH^) +Cs2(4)K?ILc$QezkA+@BfYtJI3P#oNo3BNu(#g1{1=t)_$QF;hoBP)h>@6aWAK2mnY{22)4}DO|z?00 +1x#001HY003}la4%nJZggdGZeeUMV{dJ3VQyq|FJE72ZfSI1UoLQYomWe5<2De!2k?IwxChw_M=g3O5 +FiE81i=Pqn*iI#AqWgw8d*drQYEQ)y}!OQq-e`oC9T#M(wx`FZ>ZsLKA+Fd9z^jhwg`pMUPvwV7KBpJ +%9fUzM(EPb&gQH+JCjXotcN%#xtX0+)-Xnnu^vW<)Eem29`EWd-Anl=Lxhkyy62lhurOq&OEUL%FuqozB|W +)hdt#=!;kq-1muK(i*PI-owq9(9G@pTS%U+=37t! +{^7whsQT`pG%0jEY8QBH<1FwAcH`51^KdlyBgzm2V&5x?MM7zeZLHq6EV+;_h1s2 +D~Rcw~eRy2XDwX|X}=L$#p7F$Pab=O=^fjj~^990SJdBe2HeyV~p~$M21@Pc)9j=k-t&c-K_UL2S$@Q +DcaT8jI)2kdjq)&66=ue^C*r_G8(q#S7nl4b@M{1*XInb&0x=Zy+?flH2JJgIQ+-B?0PiVGQzbLi5F} +3{`62in>*1L(>yaqgkaIUDVU_n#{6KVK$oEAfb}^a2HDcyQ!r6YWk;_tg>sKj9{2k9i$t~2TGdA5&6m +-*K{O$*e;)cjYm>eKYv4Qj#gBDY4RSPlOz8kqzd;{Y-9Q+D>x&zM$iTDlhGxs>~EfoW$c$usxngJBAC +7D>`8$dOI|J9^b^z@WglrA!$NCRG7(shzPxaiM`HL?$(Of2xP}_I{>Zf!R=5=oL{BFX>sv0SoH3aZM+ein2 +=xgP-_1%az%oT^zti{RA&^cIspic{s^a{)~IKSRTB{>XoC5;}K5uoX8)4=(9{!62Dgd-DrBH{R>b_0| +XQR000O8NLB_@3GMnhTL=IE9UTAw9{>OVaA|NaUukZ1WpZv|Y%gPPZEaz0WOFZLVPj}zE^v9xS#59QH +WL0GApe0-v5?f-%C4(T{2;^7is#CHRK$R<*g1l-TM2TI%U`9=Sd!^a@9%TDyeil?>~I_`NM?V{rmd*{oU +OtEB#o|Rx#elfui?9a4{jbtkrL$%*MT%uh(8w)>QojBUE5iYW+s${b;gSH2H=u7AekOUKDjMCArRvid +xNy{Z40>7X+Rtip3%bpz% +t1pQwJp<8TevxN-bi;kN4P9pETPaHmPTEW*C>){YacsPa;B@YLCAIZaXYVt6Ds5(gM1d<_Px_W#Z@|u +8>1M_fRhwaFxlCDTzmJ4ncLn3LtO3Cycxq*xv3J4#Z$%>68m08b|^d$Ji&f<6yPUHnr&tqK_!%0}YY9 +6Eei*atSGw*6Ok8~KK!E8}+R!})lVm%#CNNnR2rvuW8*6lpHqcyH5tQM&47RuYGCHaOo>lx8vNMg07x +;l(NFSY4Wcr=2Fj5YA7YO_8bR4bGou%PXrULz0tN>$iLe%r32}5ti79fFI$f3&SKyG=RHUOezh0x)73#?K4@2$Iz4#$#E7EY +~LY5u8n}u=>7H1UUJc$4~E&e%})R=&RqL43mqE*-E0w$6*g@aHgVP(sFS<^C$(vV$H)rrRJMZVQvk-t ++|pc@8=XV%1#N0@V6a`64|_EC=kh`L0-=HIak(G3L*1{aMolDv^AQJl0?V9jU4wWN6_B+7o}45|{gHd +-c5bvj&5XF6?l&z(zQK=e*)+`|pE%G;L0##~WBC(e0Xsz%Dx8I$!gl-LiV;)os$(_>=G#H^@?Z)cC5> +82YvZ2H))(AT0&Gwa8h?%VN0L(;GOU|D_&23UOdDo0Zd?8zB)bbe=(#P^(I7qLhBZbKU-;{eF4qqz8! +qotLO+pdYf1ml}-v#6A67rNVodV{t^8RgdwB1io?v5i6tX}c(QKFLa2LZXwPtHS +5jHfxX@`Vm^UwL@j6Vn&`ve0w=l3OW)>z>xv>O}AM>pASv%zwYmEO<;g2EW-iMe|0!~D@Ow~&SkAS%F +0Q*3?;DG!iJMl*C>x1;+ont4)Qh{-5BqHfFlNE0a|Wr+EWqFC=-^FqZ +nB1`)ZoO?#uTYG=PRCn;enXkM+_G(D9>m+8-v#?P6WVZi#Z{wddwk78dO2)Sd>8PaZR3KdIQ!MCiW`W +8oHNGS6R?CE=Y|8VkiQ}4-&s-n_cn-!!@BzP~!R;)Nc-kNP`c%E}-xVc8dfjdUlH-S|nE$T>YMyq8cI +5!w+upP0)?RkU1oKc4W4P66gv)@jwY+Hm|DwbT*Br=zCC3z#G2^rz>i#Ga)j)E!5a%On2qzv!|mFwLK +d=cjjd$2ok!O@r21LXcO@Z^tAeX?8XYg@n|?F4+!jx;;a){Fk=xaNd}nuldJs$Z)-V4#29gp45bla4m +lT7S<#BaD70(HyCcO||RvM40T->*F+Lv=;G)R`f!L(fY|8fX|Hzhx=wDAsPcQgj+8ftYvkTWnRw}z2q3dSONC&4x*!#tQ> +0{sAuxN(?EU~6Xv$?l*Mc2v9fYX{S2?OVaA|NaUukZ1WpZv|Y%gPPZEaz0WOFZLXk}w-E^v9RlwFJBFcgOG1^>fA7-*qe_ +F9;QMdohU1!d<(q{Q^JW=)f9K4$RW?@4VnIu0^0K`_nvdh?vqiq2^3LF%>@s+~?6t2@xf8)hAXI9gwr +4Cn@f4bu==x +DJgrtYL$yK6ywJ*(e208LhGx?V(CW`B!}y-HSUL2?aBh)#Obt~`PI*a(lvtBrezRckLb|dOj*q3@te{ +jncoTXk5iMGH92u7_`se;Et187htg1ZvO($G3=U@>v4tTQRKc63U&StY0w4yn{Nf`{H9qiaE0%0};U@ +H-7G^lD+T%ao{GM0!*f+Kg*(;q><9*os3tf}u2-6nCau>l)Y=1?{T3=Z>QA_`!(rKi~cHBG`Mhi^Yfn +xBv1aizE+&J{tZsNmL-h`Z#o{G5p}$KZl)sTwJ`kAwL5zN=ZX&r>Olwm_BtBKJLd(mE!cpS|LGMbfDi +WlwUg)7VWp_*@3Y5-0M9q`+nHRk^s*!O7m6`~E*Tpr6HmL0F9Y0^W9l??5nnfa +c{e5#I^e)DvC0~qkkQ#Rk`z&F~SbT7uJGezN&JYlhboOH((ii +IL+7^AV>iFF}TTnW@a=c(O`=w!P^&VA9!BIPcqPfa%);N8X{{v7<0|XQR000O8NLB_@K3(E_=mP)%y$ +b*UBme*aaA|NaUukZ1WpZv|Y%gPPZEaz0WOFZLZ*6dFWprt8ZZ2?ntyXPs+cpsXZovQH;2My$T57$I2 +2>z*Js?r=zN5LFQ8lz!9(UnjC%K>Btew!b|H@|m3gszR!6E(aXr)N(QZNC8;4r=gVGhbh>t~YqeaX0`R783Aa&5Apr907s`HJI(xqT{ii>!GpNi`CUv-- +fYmmcd`k^{0(L8Xu4K^5&Pj(`f{^w{7*#Fz4r5!5`Q2A8D;FSHZ=X>t)5_!k@x;3cr4M7n8?#JZB6XX +!vkC)$`Iz0&WU1Ny3~daDVGE07o6IQVpq^l;j=Gxl3{$8I?1L7IQ3(LIzK%h$Te&(-aC4#^7E=h5TF+ +@jji5?R$b?AdvkRxNov<+Xb{c1;=?_rI+&Js_9*u`4@rf#Pn +)hh+%2qo9fVvMDYZY5Jq+nlgs#ylGteb-OFl6#3R-htX_23jMb+92qp{HNG55R}Qat +nLsG +ExYAwC4L9G?E)%%nB=llAs>QnYC$Rwzm|Y4;WWl1=Z|Gd{*a8gZB^F%VsZN&Q@;?RcxFiE<^>=8r7Lv~ay^5uX +%4$wD#&!fKAvv51(l5!V7x5v@04=871$p*-nH~UP)h>@6aWAK2mnY{22-VBpLFjD000am001Wd003}l +a4%nJZggdGZeeUMV{dJ3VQyq|FJo_RW@%@2a$$67Z*DGddA%CjZreEay+HqgqoS|`$U?i1#(02kn(YR +gnFL8Q^WZfE*`jT2Wl2j)NsS=?zUS~Fku>F`2)cfVEVWlgom)F}Z5mqOP*??HdKz6it#ARZAJXP1a?RtTIm1b- +mwbRjw(V$;VL~-ezsb0V}6mUK-p) +Iv`;MM=3@{RV(|A9=T>-`7;gp+D<&(VR0@GcS1879~%VZZrmLK&v)|wFM!TD{1s3(0fAFga7F +=#x{z2PjT!kuCC)usIRPou>mqAuP9BPOM^H!_oFn+H0>_29z|qTHB>?X5@fKCS%!VqDvC3&$SRb5e1Z^(4oamr%2Dt#b<>6C+Ikc?5%z=3EUlZ@OKtnJWYkpivYv99)DdS3By0 +%XB!Hw5>AN2M#It`(yweFt`ULayL7dnh=Cy~>j8>l}2p&ZKwjvZkRG&=SaPEiQO)x!vj@X#vMsSGI{^ +dh)i|RyCt3qyOy+xZcC@!OXuht&WbPZO#X4#ov4M60-Oex0u$#Pk&nq8+;CCF`Y`&A-~G&w8`2XEZ~z +q(>OX9nxXe`IIiG`VOZnP!r)#+SJ>H+3_-Xg%kxx%@sQJU6Orj}KV +N*kx*-z}CDSABGgf3PbjJccttBAl`y!`#;$TL@V`SRJ%Vj9N)-OZ3Ci2lqL8dJ@!k9Gwk(Jyl&dD803 +?#)5yrui;12E`|1$9*(Ca>lbo!!qRuKaW!MXdR0%we?kGlwfbow;?#7@F-fLH$`?MH$GF0s;+d6|?D< +)m;;Qh;4C1W>jHaC89}8RBV~4A`(&o+j5!l7QAIk5pSTjF~ZFbQc6h~hv@xiGZ|p04rX#=yia9fbYu~ +r%WJS2(hL+&sSH0ZBm`y+h*cEEQWt~|AY6gSZCyYSjy0%w@X`~p*kVFz=9rf!Qy=fhX9^YI9tr?uD#M +zJNhn&3X?R_HSc-R0_Go#|?1RYKFriqSdID+i2ZeMGC6nBYgztHT;k2P_Q?q>%#2%cO1j&Eu0+bJRDa +}K@brds&8}gNgz3R3XJl2^rHIqI#b0 +2zyfn^>lyn&hnqGXP2Le%oz%^EF5J=R~c_>=xIlvng(Fn%v#nnU(zZxH4~BfoP3;l-NS@) +vIxtdLGAs}5vR<7^gDPoM0V_Zg=j!sP*s3Ppo(lDv;_`UIJDp>)}a-*6R4-~LI*YKnjfj5d_7w#YMK< +mL;Mq~3JTnN{=o`~$IhxUxF?_jOM5T^SJme1DGc88`FjQ%4MZ&_b+Ppd4l%~eNBP8#dx1MF1-Bm!vzI +K+eYpbZh~*HdTLN;q3w&JJ_2KJyi|uoISmZs6WFdo>VD)dO(;+umbeR||hiCWHcK*;}b*OKDnhNE3V? +>D_&g7B&>U$Ye$JO{Nmt;Xad8;N6ImYo}erYeR04 +;W#Z8AE>W$VDqdSbg6&!p6i=;A4J+Ty&5ly;#rsOe!v04Wk^nkp@wLtk1G#4xeQL<@%OWL~k0C*Ata8>^}5j9}FC2QkGkwBBc<9oONtF|2}585Aq!60us +FEPiHsRzFuGuM>a@AnlR4jk)GqBV>qsNN=srsCz+@NObqjRj@ch67EsvgWVnsTb@8VT0Y +0>uTwh$Lk3c@@pebWv^yHb%`fCkE#V2fg3iOJouD${sclnC4u12lmN`#xyfm6NxOV?Rhh6^=3ljrgDu?E^1Akh3;K8-fFC(CYO +@M?~K+f&8}*?&aCdxGDD$>URSjGbVGowd+5%MJh`k0&NZQ*2~cX&a6yV0_N@O$psFJc3itaA+wEyY2^#XJU7`7n*50Tq~pJ?p*v7mnMX( +kO&OXZ$oa4h=1BY&YD*Jy^Du7qBY;b3%_4QkycM0AXxdZg~wO74eoz2b30Pu&g#!(-1o*Z*)ZGCk7EJ +{P;IFh}&Kwf7^}CCxxJjEes;Xm|L@7e^9MbSA-(FP+e7aH0NnL5|&w805^OP6cP77I3|pWgB{`qoOh# +5#ygPSV|+X;hl*>AioL*n9!h@6aWAK2mnY +{22*vmc?!D-006@p0018V003}la4%nJZggdGZeeUMV{dJ3VQyq|FJxt6b!RScd5sxcZ`-!|y+Hp1=b? +}UxI+7C%mun6#W_IM^(@I=vVfo^%H}GG8c8KJg8cXMeY{ANd|CXE(ByZ&lNL=Ul_9k(Sk2$Q5#j50E8 +6YwUFu<}_pq*%Y>4bk$CT#8pZv+{o`*xT@3;;pzsa(%`7LaXr$9a&e{T0naw!V4B-cX2-`_hUWXtNeZ +w9uV=yLkV+fph|BihLeu8gQeAo#T7iXQ>%Sn-{qV)-Li8dwAbrw7w+LH#MR4>STKrIn~Tr50Fp6b)xA +?8r^DhAR+1FX}<0qA8`-N}qzvHw;)8vT0ab2DA(J^(4+nHefXDJEw-JEjP5=o84*Aa&4IDHLxs4SKZu +QTwLAU9LJGuCAPh?3Xc*-n@7iN$kc7!!{%DKasDp<*T=E2z(s%T(p%;lX +rSW-sxmX5Y=PNX+t(q5Vq!M32VaiEKPvVr|_#Jb8ypbs|%=kh>+unLwOQr7g;sUJWKpUswap6dK}>p= +~F1q4EL!<%@0Ex);Vo3x9_d#;Ln3+5&?D$pTYkTx+=`iZ}8Rs!F>DXjX92DPD;mL@J{ +D3RaMHsc4J3FOdvpKG;@g5h+U=EOU$obIwla)?U`5js;J_{#5+e61MXbQ)(KjeWPf#GT! +0X%R+l&8zG+=_xNLfSm)PxnLXLY?NvV}k0_bpXi%U%^jaDeG2EY>6$NhDFgO$A9Nw36U3N?nzsG|H7K +iTmAiMCc_>i)}48tcGxi_Us5S+v1PBUVj)8C-OVEj2WZSh}|-lJDYmfr?oI^<$2ko=9k +{jtD8g4!<>;lWFv_ZrX!#M_}I26epO=A+qQA2xK{(AR?`wOngj@>2oMIML4WV&sB=bL%Z2*PsXDHsVQ +)37{qyFwSS+CX60_qze+Job2oAn9GpHV~H(yS9A} +hVt?1=`2aFp&uoG9ce4kCy6Vn>xP0436UjJ>;t{$=8eo(gL;!WJe^`^-9Trhr~(nO%+~FhkuV3FotW=lIZ3|0WX#nwY^zg$$yxbJ=3GU9p|xlPR>zMYf*zAw5O(x~t5Kkv4P;Q$35czSss*Z~ij1P}I=jj0GR)BqiiQUW?)e}gQIic9<0 +lb1>_ow7}j_{HV^F*V!NE~jQ(2s;I6reTWyga`Qfn9?q-LS=wEGetU6hRx>!(-}e +}G0}r)_?r~r>eGjmC3f2jg2Kw20W0~% +dfwV04+9gQDI!3(?0xeda0z(_g>_4Q@g?TdaN;hQaB2Fd7s~dm$DxEc2#jC|ChAdB&FgC3Rq!)Duwm> +?yT=3>pW<*sM+s#2?WBSk1b>Z3p<5;UF8#11uR--mf$=srAyh#1t>wKWv;GJZgu>d5&#Cvr+zrEY>|d +AJPfWECHM2^zxZS~(c6f}`?o!)u*1GGnb(eiVBa0d4rL=L`V+}$gw#2gDNat6Jj*8)%2NDN+8eBs!1( +Z3p67FoG2895=;I`q)-gLdOxS{FF%JB*UHxN;CO2v+~5I-mmP(T?)!Se_zwtn>Zg^#~}Hi68aJt?Ny2 +`qef8?mBqXW;n{Ui1cMbdNG_oWTN#AQo6(8{QTYD}cv7JUA;Yfa8@DmIfdB=A;hKPEniMJ#jxRAPIc` +f-1$!i29k+$y3A`!J&Q_EE`_d@O6%H?5Q>G*Adx8f$9|`04M1j`3WVrzn!ER5cR89;9g!iMwftfbpME<=a-fPh@6aWAK2mnY{22&DR+y{7RB0fd@QLyU0@1WJ3I)=8rp@h*C)KP@b0!QZ +L3vtm7PyLCi8`4S`V?n6umqtUeSzFiwFQ&ct*N9qDXidYUdY{!iZV$54PP6kUNbgWfDDp}U81p?-QaERkyoUZLDANeo?y=E`CkC&_nvgem+Lk= +|GI}O&kHU9Po_sV`4((OXpQh<4RVy{p*136=rnmb-$3eS4%gjfCwE2IY-^b4?{;}tdP%B|WXj$f7BGJ +G%3$nT=e@Ov%K`kNnd)tyMqfQ5jWN1@qf&Z|ovFvoRSX?F>V|lUgx?#MurG2raka-F9RE*Sea$Y=Am`xPI%0*14H*zA=4oZ +8x(E0$t3odM%np4rdPo0ESVh+R%}C(yhVZgbhe0uMtDT{SujS+)u<=-ppC>`mv>*|;E8m(O|AQoZ#-MaHD@)8$_Eq=0F@e> +Y*XE$3gGLdtz!l68YCL< +M9mUxDjbq1-q^w7We5?&rAdhGh%s)Yy?*BTj94Xm+s4Cc)G5iS#NeC|)r6wcUad30Ane^W?=bIO38)) +#J4{ImQ)*cvzTEamMMOHFDq?F6y=feA(rmbkxpKwV?3f{Uj0#LK~ZqDjen8tuNZVBTkCN>&X23L6tZI +GyguJj;zT>pC+8g+h=yZyPm4c`1<2L1$!C6OBB9+Gs%g|(4<@UE!7r6d|OGTJ(7cgOh`kbS>md6;^hk +hoXtWa%qG-stHA{W?=BdQGPBip#z!4z%xHtNRo^#*r4RZt1Z$)RcLeN215hrDVr&A##A$b(>o>ow$7* +>=_C$foqUVcptaC2&K29PNm0WhL4k!F}{^Os@HC}16Kk%Y;27la9_6N9iUrw+J5N6@w0vFB$_`PN0tZ +Q>L*&>UreK~GWMGT`^Ji;8dookci1vtw}k2Gr7gUuwjhr|lJ%`;a-L +2rA!0&Y5^Y^^r{pJ}(Z%qN_QrYY=4;j*FPl>TfYMU05uB$03ZMW0B~t=FJEbHbY*gGVQepBZ*6U1Ze(*WW^! +d^dSxzfd97AUZ`?Kzz8mm=AQmX3MORAC3#b>z35pg?0XMn0hQO6(wTvl}u$ +P?qhMaF+6s^(LF?__!i@nt?)5aNY5ts;)cU*Q5h3?S^)kM)B0^Q}sATq+_rBDv6;xezKh+0)!lTD!&K +BbxOI6_(Jw&kjhu&c0q5`&Jr4hdBQM$FCItE=mqn?;N^V0nLyd~X5%8yyU*MNH>}#GEc>xQpuOYAJT5 +xo^<>{_^@=`Pc8SZ&<-TC4^2kEJ1Lcaav3CtD0D{V%ItFs}BbNnSA!p%Zo~KME0t`z~FB>{9kV`FBpB +R)hhgW(>c@8EfhH}CDhFD>Yg{=dBrUJ+X)LTD2HsXZOdKm5AeWIk6U1pE<;#?a8?oP8QBAc!ifi9NdA +#f+Zt>>IX?XCkgSlK6S%b% +Q4Gk|aN>DbwG-&acNl81EtEH>y0%VdP^TrSAJPDyb4Wr!}`3*$?Rs%_di`^xJ%*)9g2efpf_p`)fgne ++Ers5Ex{_4B-ikCp6d$enV41Ef>!wY0X^Q)nie{|f^aJlP80*&hFMDQ^#$hu1>V|wD5*oR6t(}#dhuI(vt +-ca6|nmahgdg>z?ibe(!@Czp9swbu3BJG3t1Vt%H!(4&S;o!zB%_g&>i1w%RWR@y@A1*b +uh*7)WnyL8?ajNBaW=j6)c4 +Y!(w|xKkD3;Qr3al=$LHn$P)h>@6aWAK2mnY{22--#i!bZ~006fJ0015U003}la4%nJZggdGZeeUMV{ +dJ3VQyq|FKA(NXfAMheN}C1+b|IR9>{+Xg<=~VvCm;Hl#;E3(n8YhOIL(qpKYzS#?HtPW@MYL<&qtCpzFc3c}ELGq0^J&Z#8+M(4bx)H`M7eUmC +jxr%FXvrIzH-hfD0hX(_<+2z}u7=~gHpX(>8?-9Gj5eF?_HLV!-PfzD&2INk)82~4P#5IU?9=79yX}| +z4ZFVFU8DK^_U`txKWzR7(h8H05E^*F^17E#CM7RgSYWERUGJlI6oMw%$R(}7-uSziG09tCEOC0vMc! +~@$TfNw!jHoP@?l%JOg#O&f*>;29k40_CR_A1$hy+Ca +aYP5E212{OpB>7Nj+#6b%roNTKk)18(`gfdNWLWWPZQCQc38-ndt1Bs%Z3s>lP@h)~izl8+AVf>27Mf +Kf@MfrVGHXOPcmcdvUGu?oPTih>1<*g#VzWOYGqm4v{DTkf9btyeVkXb~S(+=^B_O7VZin@VAZ>NZN} +8dym-i{2IIwGug)Y*rs9Iy`r8&LU$mxVQKv?;D%V>us)6L(^pu=4#06!5NncOxl`Y>@M5C|3tJ@y5;s +H%pq0`l>Sgc#xrM=BsrnYH?XFdP|!B;#wxA)F8+|tnN6m7(Y^HfNJ+@fI<$X^bywlZb^L>|`q5_$J7< +zGQ4*=_pg4Da>QkrDiur@e@IHAcKFA-bV@HW*jCHemqh(elDnlm-raXoMAr~9mSI=83)AVgmQ;R`>x;?Vy_vn0x4cx!>=KP +7=?cUP)h>@6aWAK2mnY{22+k$2sHBn005N)0015U003}la4%nJZggdGZeeUMV{dJ3VQyq|FKA_Ka4v9 +pbyH1?+dvGx2l5|8O0ff3({t$VB`oYhZ)tl8!Axw~p4RhG&3Ln+^uJf*c#_ynbuqT|o}QlMii`qdy%p +pTq_T!Qd>3tpZa%9*(x0rwhuM3*R|LmFOa7`) +#v%-fr(=@V!9mrO|HE;hD%#5I3I{t#A%ru(d-j9iGeedn{Cq?G#=x)CVI(WD0_t??u&q|X@i4%8uzCf8YJLmu?wqIn5+0s_Ctk>WQ{&|Qpr|OtZ+9a%Q#aAPp9=5{Q(k|**lxSQThh7>E{FKpkb~ +SOE{d)v+yv|=XuuOAG?w+CpuM`YoWp#0>$prJ~Z$R#_8aQpphBlCa?JOp}?yy@x8x|iBLk{k-pyAH7l +!YV{JTdVr=GwsOA+qR(Q|1G!l(m3!z-im|c~0`lmL>OAA>1j+j4-WIqP6vcB6y!N8XZYmtw=N|ZPq5K +g88g+hDg>|sp!j}8)&fBoBkC{5r2lI(|(I%U@72ohC3PHyOS&`IriyOdet)6K)8(h=o!5q|+tO9KQH0 +000807zB_Q#&kpAnFAG05}r>03QGV0B~t=FJEbHbY*gGVQepBZ*6U1Ze(*WX>Md?crI{xl~`?S+&B>a +9>{;tZ7FMm>ubL*=R!%_BT&+iw1s0?jACmyy4aE{$w`-x|K8D?SF*ji%YN9krJ2#pGp}0Oc3ddb@ZFB +G-Ra3z@D}k-by!G(sNMa6o1RFwsrDUE`mGOapP_R_1iooVhQ3g#GW4}mlr!99=sSal(-X(q(Jn9V39G +rtJ6zpieP(`7gaj%t@eA4EYX8G7X2?CnGAM%+;efnqsOPKNnwxXwG0I$!pJm0{7PH!OowJ(!`dje6JF +^={nWe(2m%v{W>A62$U46aE(DnDTvrpI8^Q=~^%q791y7io$0E)5(xPTNyHx1Uh!^@V>$|1ihV$Jg+o +Wh@k;37jGL6o33y(YQf>ND?IouNzGnf+OjRND>;HVvs17lQ*RyF2rX&oEjKl5(qG2kuL|`q?bba<_nB +qw&xuXq!7hwu4pIe)$qsi6E>Z^A2@l6c#(8%5JZ24>9!3EMA(mS!PDm0~dFogbZU3;JUUevs?f!oY+! +Xd4>l;6)AbIx?brJ)p-P16h5JHP~(ePOUjbrmXu|x=}u3o21{wf&p9%g#A3QSJwfnUEEe|RoXHN*K{` +rAYa#~WdrTXheaLET@kho-MCO46``AmpT^xk!&+AU`9del{(O*u=`w4eVwg}gC=~68LFK$!x<~_RLjI +0g<580S&^2kdOa}JbxL8kr){fTz+!+}X4m+lxBq=cmIsUXq`Ptx>dVj%in%)@wjLm3wP7jtsf>_grIw +{e(Z1?L1DExsd8P`T5=ksNqNNLvwxD2s`VYNn=(U}p-sB>_S3t>RPPW`T-htDona_p=Tq(K8bT8FDtP +T(ij#L=3#?hO&kxvoS#inHa>_Eu=^TAq#jt#^x@3BrjI>a2y1|?KgdvGKSIS5tu50gaZzofdHrs+2~@WoeQ41LOGKVSndHun|7pHkABK}d)|EFsZR#>^ +;7Od)0p#P<3<*uMTm_9Y*o}(*YM2VwVRznudvxmv*yihcz<xEHb(&8TB@r>gZ8?2j_Bu|SIjRTYeg<{BY%1%M+p+4U&OD|9vA{tjNV7oQScVg+n +m}ut)6c(FX?lmVG1gALs+6IQ}joH-i^wHz!ZWcybmPHe|V3WF2WEydT6BEJyaa1OVFaY?gC=h>khSyk +dLS7=^PU6Cx;;R{6Bjl_VX22H!RzXUHNf2F-Xw`&=-{0K9{0J#D27T`OhfTW;%{Lt&o$_^|B}!{4^i@ +&kwBA-WD(T_*o6t?6tX7ujeny8(kS^(5N6ZXblp1Qi6lMKPif^JP>1I@hbxbG_K0 +R?<1%eB6vV43($>UQR+mShO;ngZ;`!HO256Kar5$m?h(9({TQ{~d|jOYH68EJNU@*7Y~0|XQR000O8N +LB_@K9;td1RDSVQ*HnNApigXaA|NaUukZ1WpZv|Y%gPPZEaz0WOFZRZgX^DY-}!YdCfg*kKD$Q-wpUb +bSMbJL1wtN50?w}LgQpSo_|J>y;1v|oQ!$zScBcX{7-;{##7irvsx_4u>u`PW}cp_4~XNoT*puKD>GJ_Tt^U>`xc3-mK +W4^LpSywe1i4s=3xJU-mHHrsga5suKN*{kZR|w#i`@)qG#=lkAo^W!okDy!e=3L+=Cu5qgXwZ`!6R^1 +AvTo;CRmcRN=N%mG6tMPBR-h=18}UO&et;kI>UhQBFWf=bM_OvWa<(@p-7XMNsX^FG@j`d!=1cJBDkS +<&9ymDM8Iw%twM!#bP3Yim8gAL;3H`7qdBG6KSw09?h#akcJacA&iwg(Z2YHrv@{Xz#uQ&f%SnzHaQQUk8UEPD_A3_jzx4=jH1cKmH|5AhLZ4%u +L@hU;(qsBqK8S-H}1IRn5gg^!!E-&~wpecU{$^$bf;4fn2xO*Os7xHNS!2sT6||L +sxqcNu2=dP*3+=+w(%c!QnHYzG_=ZD$n4*y9y-OozPqgOW;{M^!uToEfBjX-x3oW!B{u{>mP-g?R}h@LB4EkZ-_J +mNE}_J+gX01tod@`lM%=V8lkATlqo@dMSjj|l;b;lHY&#Z3%dxMTE|bd;(Aiy3f(!Q=#qNaGXdUl|u3 +?c}O9ax$p8)Y`wG1s?BE6_{Av6M%GKi;|YpL)MMUniXdN8TfK?xy!o$G1W?9JiLOBSP2Z2KKj7tre`E +|zQ%$kvwpVq(t=;5OjMMECyl^LNOUe*->YLsyS4UV-YV&8t#zQpNoifPWYrxSEM}=nBsAuGoRB#&(M5 +t34o1v`h?pBz&P|I)X>c5?MgP`cRxjD|WZ5iXB35Ta{c6l{bv%;sAR6!1`9L=fHAk+t!>}st#XoxSol +ZiG@}9br66}AP<9R#-74t*X&Z6C|B$cLF9eUF4bPF3D;}WK1q_fq1K%(SOJjMla$kdW+NTR;NCUU}@6j^6&AW6Fcn3R~4|Sj7# +(A%Hyxymai3kE*x9krv)b|NnxIHAWKOFR^tlw1v%x&o2v^~g*eVv1p;Ge*{ApwBgezt8LNgRkGd~m@4-U_nNlCv|DK +VnQlG6*A3S~b=FN*|@1H&W=_fT2{1+VSFVfmeSp1H28?v%329yS57ZO0Y`sBmAi?`Vi&)!{xhr@Q;0o +Nj(j$|1HWGJzJO&MS)hy%yK9!b*z{nuMAsS3C}ugTs8TL57Q? +~x(1btS$)aKmH_HngfypN3_;673+Tpvw+6hz!nl3y|( +Pm|IN&)~xy=P(lfRRl!AiIcd +ih9I)Fhu7X?gf&)|ER1M&2*WQiD11u0=h*asCqQOmghAj)?@lt_$&Yr4oS2OlQL=Z*bWjLFKOry|Ig7 +~@4JD3c-D%jMpn%{BBld!YJ&~>0uKwBB7Kx=;{fVIUIXPBh{$9RYNu%Q7(5oj!ybL(+&jtg@agYQwM5 +EX*))RMIn97_UwAP7!{>X*|h=+=U-JFbuyCRJ*b%{Dq!3cS?=Fvscyq1v!&D=~!F%j=Hk^}ajYAtb(y2xPm{&lu7!^aVgElua^!6UraRKh~1~BwdT5~! +?D5=D^n8V{#L{&K$hubYj_Ax92rq*39EEwnQY`eRj?Iv%K!+-N&!n9PGLb>7rq0I8f_<&NOR-%aeb!J@Dgp$U +#ir&zriLz4`Q3M6tbPK=)}uLO58<#F^JtQaL3fqb|LmeJ^AJk<4~?eMUZcb1x7D!o`WvGD5qT9_XID^ +i)13;~0AI))vmFU6t2grHq>n|M04B*d0{@Mtjg}t +zwk+12$OjRjKE&L>^#Pv(AyNG+fibW^_vQJtV?efqM~~jqK$IerZjxXd(8Nk?~(Th?0O!SZ1P@AM3VqrjxJ~_XQWM+hE=9sS(1IiMAA!y&ePJffI`@!`t9^oN_{9NHj^*Ia +!gBxiA_KNrt06Y#FEgY&M#jI1%2PtV`%Sn*jSduDYED*;nP!XJnhFQab{$n3qKTQRD$g<- +Dc`4E-=wa2&r{?<%fuxE$3vNO=pT2i&jr0@?Wa;k}=>6LX*5tzgrZf^${pArr-;Y9zqf_hquYv&l_~wEycFc?UzK<4M21{tpWF|qLQJ9)NoBS`aQX|5ibMsWP5L6?2Mh?t!An<{f6GGSN=M)6;pHPRXq +Tg!dmIU45u2L{36i}5&`x<6iUR81W2ojOFy-XAJ@#Dwh6c=hR38Q^5)_VL-_{}G1-KCQ?|xI(){KRB3 +f*ZxMEmC;c=G~shNXNs>-#4P&33ND}r&3{;=ogO4kH+hom`g+klojORi70-Tnz}kAOPoeol^k%=w_tD +WQ%SmE0p`IYIvUiz-Aribcua+Nz1?7aU+r`~)5csOpcfsHVLm +GWy#fobb2V8=4e}uIATyaq!2mrNxTn0_hZ7^DPy3(NxxUQ;(D;bEWIOoc^57^5C*fG6R>7VF#VUMe9 +ULgE2|tq0RKQf>qwZplRR=KT4#Baw^b1us++!Q8uJoIno3KyY3L0=BU{}DxFZ*z<%4%%Sb}0%WB4c@w +mr<9eL3<3x_piKFpA=byjTa_tjByjHT!jI<^BY*>K*Bg;s92P(sg&(u7`5uoQ?H0tkW7Z2)I8^DCRso +63q@PEU-5TvmhXkdNYe&iCrhkQdvw{k@@N{OsBro`ynhfwmG~Cq%yI(3gfD{%UUF{-Cngric1{pJ7o~ +n`|UsX_wm1vTHE@z{?NpBf@hsfDgg-$s72fP0?wBd-DbgPxendqL%tcI%5ZS6U>h1Uj+Ds@kf40^~vQ +V?|Z}qh@p6aN_?^Or5xZ0x8u??eMlK{fyMn*GfojwhFEZvs>X>ta_qMWdzudLl>dTpVBIp>TVW<_Gf* +lF=a?}+Kmd7T%%>g$U~i+(`0dO3vpY(n5?@~e)|c{eFZA)>nOXL^5MgwQB;TXn((9UtR7)QpU5r#SA^ +kLC>zVffy2Xz+Iq)hd%+aaVrth1vtD_*=CiUK4Ho +*A&i15d?b^{l|A?)1�}&1|8SW^Y$v0p^p&dgQlquG#Kd|pp_U!~SU|p+|Y6T^SvQ}g? +dZyk47RTiwb9chkNhwwj(<%*21IB8?6|R?pSgAY15^as9{W+gT0_bogott;^(%;+Xs^Hp19p%?t=n7_Am3oY#Zh2oC5D9&2J&tK=ee5~S?R#$IUT2!qlFISjYq +X(CrdlFQ06~+Tbs7uu|2d0JwwhU9{<7b)`?J32!>tkVeG4MU~@<+S?DG@R$C^c$PfdW${VrAzFg1HMV*`|K1qCTP +-DSm&n>BfJhy~tF()9xC0pgN&fey;~tGYa2e|r^zR)2JjLclhYgpIId0cdR@Is@-?1HCcl08??4a9x% +CQ5p&*F$jhuaV-2S-urbGPld@3NB|S|P8}-`RkYh-^6~R)i52(a3HY)xx{n9>6(IDPC?#rmrj8Q>_ +1pm`TuQ0Ed_qce#%>^lJ2JOIG=P3_nS$!=}(KLxro%jZ~=!z0zmD-e`i7+KuW>9}G%9oDO6$-jd% +q|ldQVECYoh4V6aWxy)wD1bgE649{b8SH^X7HT7)mP3j=H$f?jNKNn~V0S>b(@$l{16CCNx +1JnPP~%;b|~!CC%=u>}#L+H03U3%Y|M79>(bGRv}6mqZV|m12&2m&V>zlOt->-^e(@x@VHtf4`YS~Q> +S9odB<&wiCY}Yf?*`+c|r?FH|1;wQTg3B-+i;zZ=%N_^f)HX`P4rD$mJCqc~P`O6Ra+cWM+$7zRWJ~= +~(0#^!vx*)b_4k|M>pm98X;%EP@GekyttGYgObxS>Kf`V^tY?aYoW8*;^hLiY0Yhjl2d5%v!}j-lpjz +iK)$}mv-N)%WncPOlxW5*lxd2jf#oK%;j!BW7jY2vknvLx&ocl0mB}Py7 +o)g2z8GjX=RiZcw!rTmpJ;V(X|xVb?IHmcTds_W41E7r}LoJ;xQ5xJ`-X!!T{yHjdV_Gb}E_Unmd3(2 +9&W=6yJp6XXAhi+qI&U-rydZqc9u(&s$4sUFm9rp6V|Xs8jimdam|VnVXkxY;NU6;jYHb!oayc1|ZB$ +n{fcWD-WIieTQml_dL|({?53R!JQIi6v#P`eP?NB#L*NrI^Ac{;QT2qCuLwQ5m%z+zn#uMMq_qh`$#j +BbUoV2NuW+B*2i`fMdm&eW^M;*W!ai@9o7b%piDbCApJ8b)TSkYu{kZWa54-GaEIyj|wqO~dJ9A?{W_5a@q*>OUcWBiBO_+r#=;+E +od3oI0I7&fpO4WVeRsu~YS?dN=F@k)=k#x!7!^25I9=0@GgH^U9Eu^Pp)0U71qO9ixM_La~QBfM6muL +BHi)<>#Q!Z^#B#d)9^lKZDQrKt`In$M7hH5R+#L}){8uuP{DF=$6o5D0IQgaSB-p4&pl+d%#q&Ji|s6 +$Jv^=?~%M=Kj|ezO#8PS*@xyngn>tBZ_o8+`W?oY~VkpBFhikg&K^S5|@`IcY6-T<-H8Rh2yLh-zR8{ +0dbUdj2cjn}qTm^3X)7jpFZ!{gRHwPE(tKwCqD9p~8oY3M$RO3Gc&&8lBF|fZ`_2MK>R{&bp)w(sMQF +;M6DZyb}5oXgsErrqRPr<=N%P^I>G`wgqG17{mEWSua*75;W^e%KaI6yDmT%()I0YeL72D`hY=R7HY?(;V1SMQ~J7$XED?Cv`$ZqeI< +!>*On(sn{Jvh=krk>@$0N=TDw|_vWaBK82%d?bCQru5KPFbi%s>Yb@h?%V@6-y8AGe` +6_Xd$uE@|dN*zJqd6~?hIr?{f;KU%rLfq7AJzf_lEAez~o?jGquG|PZ&}4hKvWALe6o{u$0LwpzKsV7 +-GED~oNj*1u8sj43U${B2wkQUCedsHSg?H-uJ}}>SL1nBu@U9i7qDEiBQq;(iV^7$B0T))f)cR7JPA+1tz>71(IkUCC^9~Rl(+V{2co`aZpk%!A=Lvhauj +sCEgMy~i=?3ENoG#en<9@}mie_mc;d1@$75i<d-_`+p$dF&Aqk%2>|(!W;Ocnl&mk^<$T^CQlAju +f3>C3x(m*ni9teBY;)zKxtLdB}`AdMX>mDO3y8Dg1`V&~}yWf2?#wyg`5hBcniR1N;7q8!2?=gE%M$z +Ql6FiUIHcR$BPPd$6G*3AH>E-M9=Tb{330Ph4dg-o^oa?~Mgsm?oX^eWoc3K-$ZWQHCB%H=9}*?fe8Z`=uaSNSBX8tK9X{M$WPv5-{lnX+`nne4l+0l+nGp7_b;B|`%G5 +5_oA{sfOJ?143Yt7Oan0Z>Z=1QY-O00;m`Rt8fCPxGfu3;+N+F8}}@0001RX>c!JX>N37a&BR4FJo_Q +ZDDR?b1!UZb963ndEFY@j@!8PJwX0}P!L!yJi_kl01nWdX464~P0*xU6oY|6TeQuLENMw9hda)p{xl{}CW>CEEXuBZq*l#22)@(uEk3$Rp)>NvePb|@a4^@53x~3P^K- +V`Exv56QTj@?()2{FMnzsCRR(EvIVr1UI(Qs0=Zn;OkI0`#tR%hjrOC?EaPL$Kk@9OCM +t3T_DS?oPiw4sVRjGw4ve*=pOz8X=01=%uHv+kCQzT2}-<*9&TT3Jbkd;(TI2)3CtDM53!><1Y^efAyc1lpx*ydr=EX3SAR;Izymi#}bg1 +`ZtGL<;MjP}-zKP@E{OJ<{pA-76R&)xpP{F@4VI(so)-9Ei)Clo!IPf(vF4bkX>C4nIb9*R$ilTVMZy +8ZLMw$SI^sA4cdP2#%$6zG=hOm1jDA{n=n0^`2CtU$4 +<9N(;b3J6XA1Q|R;X4-ZBK6?R*)e965zr39V3YB$Mp^bWwc|WiiDVcv|4b;6k0pyd6gx=8{RTVTI?Jg +Ksd?jx1tAsldM4!k}Q3Ky2@zz8quCU!7K?;v~=mR0?}XVYh|rdQeDptq_G#~KpWxZK~`~w^70`x;TL(76+oB=h~Nv_ZdB)Ayd>_y|%tB*Ec`L^o?t05 +hZ9R5Qpz6Ckoi;fLOyegf>lWZkc5`Hkf^6^+oKqhTXP@PT)K6TFx#vCV-%{sgbrA2x>=dyl8HkJrCQE%cfN%s-*oK*F{RQA667pU9G-jzDTh7Iyi12b|98*if1{jKSbdC;$t*9;aY30dvY77K_(e7Q+Kg#=`KoI7c$r-0c(hFB0261} +d;Ljq(`0H*80Awv7ivuC<^lw4%q^XIi*0J6ya3dY&_D~rjNTTAQ!Zy9Dr`uFiqH9B5ipgCc^WU$#5`< +2>($8HrjF;|GA6ztR-$ve;Xs1Y3>M*VsU^bpHJY2_IcxfH0n;G#WOOpNnZ4-%jn5!B-c&t=ISfd;bFk +}&qazQD`t`H`SjMw|wbsX#mT@oA(*BPu@eCw@u>^8Oe^)=y?uIp{@F7EH0wCt{RE{E*MPvLjJHaPzb9 +h!7aT);rB2`;nJ@*w{JC?}=Q={l&n>cO6N5mRj#y=Bw5+Rq#?;JDeZer}pBQ8b77Jgxlzk>PmT00W&R +Ny{*=Q_bU1v%t;>1+H!%FYIprd&O&9JC_k*>o{Od2lQU2(Lju|xD>pf4>BL9yA6A@NEE&@T>RXm>e-kyf +}VCeu3^WHj6o|0MSU{awe22{jmYN@PDuE8h^vq~$++bkF!o*7Ses-d>;VozzYb$1!xPMyrLO +?LL4;6U>&8E1+`0;r5hNJ))bUYWWUF{F7TRjw1`wcwhfXpoZ$v$L~QUKR +%L@BR6`OFzDZn~(wAzjoR&tpkjx~SM;zdHdpY9X!P`1b-FF27g4l;bu9~=OmA8?Xn8_WeJjhN-15mYA($3tz`m>hDDJSnU-F_Nm{MJahLsH%PhP2?~4Fi76qXF$0xU`4-*-2b$Lwdb~dK|@Vn*2itK5#QUu> +1ph5G^|X4|Ic00+%YUV69y}f&-K@$fv01JK>Vw$VuGO)T?YiavivgR*eo6JNVdnz;|X?zT9w=ID#2Zm +Jxa~H(4rvK8kX)(?_6zOjm-gNqPj7+&wq`yyWhK69ibV;b?pe9m9Hs|LXXr@%Px&^FxZ@5@`Qm +6v-TG9PgjT@q-B_4|%}kmH(x^a{7fU=m}p?YbeYj|rd7-+euZu{sbV=XR^daeI=)}VddeFzAqk^*@+d0&*o1))IW=LHQ*5du3Gu^%a+Hq>M^Q +A)-Hx&L2se-g;f$FE$vk9WFjAk!lxT> +V0%&51sL!lV!QTsvX}GuDxY@*8FEgq^Va)sJG681l6;z6^C|+0y;0p*Dq{4y*ip9jx`S@9`UYU6#FgN +lK@RF31iHqN{_!Mk2Mv{%qrQqG|Uu!c&fo6Djxhp+Z{6GA&QuXB=Jd@l_8a+>0go@rd7q?}n7cbGckT +m!y0y=eP|(LkN0zEVCVa!gkZ5<6y5$K~14g1a^+A1BRmG9;;gYlqNYfIxTPmgw6QC{PPj!t)E63dV}4 +S`GwiFbB|z&c09_S5wj}ReWZ0>y*oH3chJuW55(*v& +L9qrDRcF@LkIaoN-18%KB+M!$RW$k5Gq)VSDa8r@mrdq-IM9?e#8sN?R(1t~~Tee8KxLbfe!Lb*RDv$ +Glb3%R%4&F2w67*bGrbwo`>Pq_y`+c#j)`k9CI&h2Wd-^KPMc>^zCva`A{>-h*f*i@tLd8Y+j}U~g^ipfx16Rj&BLfc0J`>^7O`Qb{U_7x=&L9A0#xl=fu~;7H`T@tp +5mXudBCsYzaJw_I$WKH>IV?JCXl=JlZJXU*>Qoup;GaKF!Gxk^_#XF@-HvXRA}tcpBmE6od`h$Fj_Yb +M$H4PSYpqSPgoig}7ZXZNFOJ1hgEfecPr +X^i{rg*aSmPrM4UZ7P>m3_$EZdV2unn{q1M@v6ZAX^ptJr!#&$s*$oFx1Rx@S|Ov}~4C1woOAVAWw#O% +2c&u(w2QcxWcc%;#(h5jr<(ah9R*EK88?=@VDm#N-sWAmBz;%nJoX +61rYzPDHF&zDdaB6}c<~YdIU&)79P(sdt#s)jk~*$etH%SZeh0EQaOv>zdP@JsiOq@5Tt`d(4mD@J_jE5ngS`LPk5rwvd-J?m$;Z*H<6Gs;uKnGdmBD+Su3d;!r3k>265| +R{;=LMN1h(GFRR*ge=QY%hl +q;(BAx?tx_hi_fis_owdy4jFoMakalbYTT +-otY+`GYQ4`S{C<*Hg~NjY3n`&Zg;pt!5*Uz5f;#BN*q`tS4aKI5ta(s&H>xV1G9cJB>`p__#Jf{`e@m7Nc(WJV@9`~5e#^^0`)Y{4 +}{t+DC)kIr{ph2?kLo|de6#f`YVT8p}o`=E0|Vb#pELAf~BAt6hYsGeZUuRSV5sNLKg&T7rA3(MFlit +mIg=gs;8uJL+{)Gqt^T$J6Gd)0B!^=B&auLK$zj`!eV{dn|A!R$9{{#&+3lCy+)lPiH!%9VD|z2{uJq +Fc2K=q>~|N)f`@tNqt@pn{u=;wD@Wba5Rs1-P%Yf^GN3!QvN~7ntClVrfyuOE?+YdGe8)jR!Ys04mtAjfZ1d`&60f5!E;{@Rp$jry~@D` +O~Cxb1&%u({ZOQhdaCbzHHxC;^^#he)IbL{PfL~JXa*PTnS9EZFxb79l+Z+1bHKDj8+Z_4n@E;37>R!-F2K>SoX#h*z8=Ay&$E%q1f-47DbA?AoJU;p4Kv#kySVgIn-$sWF};% +N%7VGEt%|(b3p>nB>t8e$AaLo1Pe^tph(+{YbZiX5Yqvk`!qAMr}y!q>%yFgxK;ZT^1> +~3YYC8GZpBRz8Iwgc0fr<9RnTdgO(Xk9Uqk-ihJ-iVsD(Wna?-DfUpYsoVFg!$;gVs(40j!#ym@y;XGn)R_=$v6u)BMBs*3NBw*wGIQl3eh +-L(T-;ua~#uwy=BuW1%czAsuw5Zb{jj%d?o9F=-jwF7tYsaJ=X5em!9M|-rXV38nUVm^pk1YZHJ3yzD +p!~oQ-|T4v70g`;-V2Un`Kd79I5zht3e_5dL+B3t$YhEKJWM-%8ME9Ij|og!vrgFu!~O$MO9KQH0000 +807zB_Q^^?GN&X1{01zMm03HAU0B~t=FJEbHbY*gGVQepBZ*6U1Ze(*Wb7*gOE^v9hSzC|WxD|dMVEz +N)BCwRoLefXK3b38cwAoE3!Ay4d!7yZ6qHQ&@s8Et8-o^g+o^wb_)WzNuC~5>_i{iQPTtA*<$5N`ACD +rNnCey!jx!-5SzI`rveaN%z?afZfBa>xaa#aat-u}$k`aAu2k*2D7-Bdu8idN>^$Hxzk0ekxC?(Xf=(_97R!2a-$Q6$E->$zh{-zB9={EHZ`6Y-6Rx?tiM8F|DM*on9LH#KJL4{NpfCQ?CBt%&Cxyc?Js +X{7<^{4nf`%$SXS($I&##R@hu2svJgxvc9aU-UVwe_T%=69M^quoK+1i3}?sz!xt +&~QvlF%sSLyb^f+q*-sc`OFW3&gQTvj2F3RRnv#(O23(t8# +3ZQ;9?R82t^44AR!(o}+gakYmhO!*Z=|C($JaqXBm+p$8{dSm~1I;g{Et_lcgZ%%o)WFK<@*~B0cPFX +4#wMHY{ERZJzz2@4bxh_3 +n{Z;p9Ofm^Y8tN;Q-nVxj9>1scUVSz^bp2&igDTopfe&-=_en6|g0%(pV<~twfnDc-Js&_EzA-M;g1& +{5%~mBPzyJ~M!;Vb_U2S1AtrOQOy@U$IkIWeHBc2mhAWfm*X)VXrpv_^AUK@K%F82FH|DZkXUCk{+op +BHfXwO7@2WqYrs(0N(QQATiVA%hF-8Rd!`plj)P@p;=^Q`y`nO`5EQKk|Oj-{l561Um#ZG~3!{LKa`o +94FJpynZ1Smq~zuL1_Bf&sVyTF~=Zs?VBv*&cCeb$K1KU&wcx4JiqX!DeU#qS$Q05E&|QlqaFB@}=Q9 +<44&P+Em1_Ejol(HXF3tX7g99g!Ta&zc`jPgbRsayet9CAt=;h$X|c~j4}TbvL7JbNND|Ry^x<>WPm? +(?2+~ip&b2My;`#fumZq*qNg95hc;K|28KAbqIYVI{Vt$wuZ1z4>Jj^`^ +Ce0R+dD?A+axY_TM$^`3MvY$LnvV??rH0pNcvrWkqe0x!i&}D2IJu$pZF4@0lq|H;s~w*6{Zj%6N$CP +ff*~>ZwUide%>SXuUV<%CU9l@m>jr6CrD=jBgZHWn~X#!0fxxTgq;UWk=qVRMY?1!Dg?#wFZ?&8wP)D +)J?I#tXLMM=yEfRpq)`eSM21|Bu1^&8Z4Nk!nIu~cFf@A6qKwrH;m>T1^vETM3*8+NLq}5bV!(&|V<{E0a*js!}L|L2(Bl28d+{e|tJp-o){0ZVUVvg?J +#bBdPy_8(QK8M~>N#NMjf$<3&(J7*ZD&RITO4=L3E-}bX9z2}qSnxp(b|Jxb*!qf)Bnk;Q-$$=WMU1{ +py!ojlalJ~zzm-Sgy7rENoSTCi@EI-cQo+?MKInjyJmATskjeGbz6nlVWl0637 +|3j$pCLyPRkZBn|oT#a$6tWj53R~_Mc56)Vj9|hn7^bAgvC#^Cg)QoWhik2SJJXXY|JLT~n@SAk+S8&cT0&4A>eu)M( +aNg_SS8IXKOi&ysR!OtYsf?iG1n(5Gh?7d!{q;OL?avy?;M2Yn5Z%}3REpTe2*t=aUFI@iuJJ_<5#wF +WInRhoh94oeKgjn$R@Ybj=f_4OC|103yL((Cc&Y_FM@9ikHqciz)nWUXAJ2YnPBGF?ze)I<+nv1pW@--kR-CA_BOJ?$832lS+q1~$`>X~O!vK*Ud>EbJJbE;pn5+)&^IsIpcOfa5gwz8AnY+ZGrrzHC2T)4`1QY-O00;m`Rt8hC&51vy1ONd14FCWm0001RX>c!JX>N37a&BR4FJ +o_QZDDR?b1!vnX>N0LVQg$JaCxOyZExE)5dIz@|KVVukObI7KL=I5r2SB=8wO+@Fa&|jNGF@9L~0~u$ +1D2Zcck7eDH}E;u`Tl6_}s(0vnVU2Efnf-5b|()lWSFis%*uzK@iRE@4Tqdw43%)kz}{^MHqW~({5Fw +V#x_*rZlT~`i(oAthS;soqWork|O1W_zM%sOPneTX;DjFu(S~Dg&Ry#RhC?4o&H1fxT>S^&uf)OG)5@ +dYW^%%+sR$Hzq-d`+>ko58UC+%9D{x@(TN*=(9Gy +*4zAM8lkEw?)B%Fu-351goI#B}-*&2M}h1BK#!PS;8p4T;pA5ZU?{C8X=<_(NMnA8<{!_dxZVIy4a(V +V9?eTt1g|ueow?aiuBkR1SpW75MJ;|=z&yFqU9O4+=ZJVWuv5ok_xwwi-J6zks|2bu`aO3REx^OS@Wt +wJr3yQvJg&&CoEL8-?I^z&DYw{i70nHz5wqi`3HDX4J6O-p*{CKFviw|b{am6>Fwk98Y5>J=D@QoX|+ +OwMIOQK9)3|0?>f@}Zk?s27c^|p2nP}O3U)f!()(`YIM_~Q=!)Ao7)N$3!Bb~C4jhy+o9pMiE^Pete6 +`_Hcj7t@+(ECkj#yN2Fi_R~+anGPDu`5+Eg}8#K~Rx}34FoSU~^vQiUIs~B$hXbxcWGVpTF6Er#zuhJ6QvE0vBBK8Pu5u9L*eKC*B!_u71xCvudH@5>#$(;yC#SeL6ti +acbTAlH*Wvja5+sQWDWigNZd@GtJ+Fj!=*<-pQ*5uFV^8 +T4|@Tkojua*(kAO`nk={Nr>EA%b%GP>7iBQ^I5iK#Uf>?y|6}Ex1+qZ5nGAE)NZ4=!!@`f6aO6 ++Ze5d8`)8%haO9KQH0000807zB_Q$0`|b|VJ>0Noe>03QGV0B~t=FJEbHbY*gGVQepBZ*6U1Ze(*WcW +7m0Y%XwlwOH$GBR3HL9?1Wo`Z%%%dvpCzTEgv7?ga;>h349R2w_on$F>?-+LN@-xqp2}uYJfHb9U)Mo +Ly;tqtU!HBerd%GK9Z!isA!v +Kd1*sUtC3v|Ix@Tc{O3UOqiCaO^$04W*I-K3L*w +)$3u{`PZ<($l8iZ~Ylr>WeBBa_<&i;Y2p=NW0fq`vfA(6>v{CCmpR(?Xup;_xj(h&ajb&vi=oZRyZs{&BDo(WyKIZY+p+n4XE-wf^v)RnQytPYv6rrlxu +pPPpN<#LsRb-%CO{}NY(155xR19=(SG7DmnjwYTmsvJw2D%2zXG_h(A?0cXN)vHq2y^G*>Yqn!`OSvcOweC#lUt4W4MQ>F?UN%au$z8}I_vH1!ltedBu@wXLhU9tv9Av3RD0Bq$hMf*7$^YPCN*HG38o}_xTVO7WMmCxpbmFM4}r2d%7g)ASKplYAZPee+=_vd0 +81(;RNMO&F&=4D!93b3=MvcPCB8)$y`{>O7x{}1R2fAF|NKhP&*p=O3Ya5xz_%mycewW?0Wur(gc5YK +pBHhZ%QqD)pHB +6n5&kaHz0MC@xDs8F7oa#z1 ++Y4ROtDG3Z;fZWs2;w@z`$7yH<|2%{t)NfSxB@(7uZJFe`2WFSr7s7;dfgu@mwY#nSH=7hq+JA0;KOk +BRJh>*#szsu4*z`;O)9Za`F%*oDk5BDQMdlcaAevm+=yZy +jCo=Q5Zg7cj_1G4!|qW_hJ)3oDHwL}2`0B-{T04D$d0B~t=FJEbHbY*gGVQepCX>)XPX<~JBX>V?GFJE72Zf +SI1UoLQYm6E|~!!Qhn?}5C7=niWk%LDW<7;J~02HjxC;3P^b=Gd+5EcERsC(Y*3j8caj6h)tY{Z_-ag +##V(f{aHtO6N5>&s27vtZ{j%90Iim%O0Le$*9UdM}MSAa}D7_I5FOA#}(d4==X?vB7B74DuD4AUisj> +(m%zUG2G&@BgPhuPm}P}*g&g3v4|TNjpYeY@Nda7O+ZeRShGXs~(5pf4EHF;HPgh-)-o@8jHdv$V9BDleJ_-iY?@9_kkT(Gp7a2j{2I)S!3s-QETH1r4+qw* +*!TU(34AbL!#xSoWz`Pq{uwk5~2w)4E<@G$Vq9xS86Rk)K;&qj#;J{UA?K{iD*YcL~#Trreg3b?5Mr~ +j)=$2~dBr0LxD-ZIoTP)h>@6aWAK2mnY{22(4}U7{NT007(s001KZ003}la4%nJZggdGZeeUMWNCABa +%p09bZKvHb1!0Hb7d}Yd5x3HZrd;rMfV2$4+C#10k%HCK#Ha>Ad99&(OpppBXMlAp-5&(#qr;Fs8^)c +2x>qJKJJ|}<4fIm#}Tlq*Q*1&2I9qQdj#F@fAvv2i)OtVj=lCp`9xMZF8sr>WD2h3n!>2~s6%93wMn; +BX&Z^ftI$@_bK$Xmz$2B1wB%9#t>69JZC+I~J&u>}Y=~&gcY16!Q$seUitANvFa)@*gaqq2C(0U%*d5 +c4_{;>->+97zs+WI3l#=}-r?XFPH2_sCejzf$`Bw&eGuzVq;g66<3zwO9K9CpC7%A(%9qZKap +$lf4PXanf~E}-D!GtuMhA+d>qc8yEDM0eh6W`-CdEWuqkR*hcFa^qbLrW8sdaYO`Xi%C*8=6mtZ-r-{ +*C}=9ZwJ_wtmsjcEUpw+{ic){LQL)IgaG&+Bi-k4F#3bW4Z>H2_j2Ki91M};)KE3G&OGRE^66Qn%!T?V-CBwaPKV +bCvym`C(iO>a>!?AV%8=6COMAv-leSK5NE+UIrRc^q*yLqVyWhVPmUeM_>_On$M9>$#i%9u_b%Qw-gN +Y&J~2Da^8W(yOkTphC|{04%$7ZFp?~)K<%8cs)oP@6aWAK2mnY{22 +)}ZK=?`l005f;001Ze003}la4%nJZggdGZeeUMWNCABa%p09bZKvHb1!Lbb97;BY-MCFaCvQ!!D_=W4 +2JK4yo2beh0YJq!(cE5yKH0ZHinU7B{g+q&vwT0_OqL&OQ62l(*OOxtTwnF(&!C$V0!ODf>)o6W3+Wu +S8XYKSUnkhR{%)6^|ySVAN1_Yn9$k~Qv_r%GXgH&qToyG$I(R4E`J2O1WdUFd4w +$X7_dMABcvlipzpziTsPdh&`-5vE}{*DdYpKQh@o-49};PCjWgS6YN4YfS38i)pWQ~X?GEnluS@(~j` +(#Mv7A6@2n@6aWAK2mnY{22-~X?JxfX004Cl001Na003}la4%nJZggdGZeeUMWNCABa%p09bZKvH +b1!pbX>)WgaCxOyZHwGC5dMB}|3UaDc)@YWhtRUHg_d4RAT38P-$GEd_Ux{5wxmk(y>Q%r&q%U%CEK^ +q<{-IP(u|&YX6Dg^_eyF@I=SC-vA?|7Y1tF|r0|3Auy+f#A|D6U!R5txKyg*mdl0SEwYu9=4Mq;S0TX +S0=f-Nj9W0k3x8lNr7Obnc1MgZ2;y&8^<`%BSJ=aq7Anc-5n>aT0mKliVzix4dH8#I`!38`vpuBxFeJ +_k)L|He_ +g7g8)(N2J7|C(qM(Ue@Hf1=DEmRrjTy_*mti-~>2IAmYvN*Wqg+h6Lp%=C+z2n}XX+7Zz+1@SCC{Mg3 +KBu>irdT|D#fnn3&F+K-Dy_6lof+rdcC1H(t5eOaJ#%ojPjs7N@BE2zyH|5YY{S91@m>MXgw66fz-nU +OvbfcIm!A_O*jqjs<_l#N#T8271B~>=7r*r3b?vm<=)zg7P^4lQsPnPLkN9{a~z{5?XB3pe+WlAaKCJ-b +OM^7$38orB1SYqqC!M;s@pz6&D_S|LdRuei?qP*7H(vqXvSBrf&R=3PzDp8VK^Cc!JX>N3 +7a&BR4FJx(RbaH88b#!TOZgVepXk}$=E^v8`Q^AVcFc7^P@()9LNx;>q^blyFQ1(6U`GDX}xfd$Hr5zlkX_31z +%N&UbRzH)|6GsMDhl;kXcF)Eru7iFD*x~izUbn2xTPw6%M;cJW|uAt6xEWEU$O-11&L-r!MLQfyw=xeKKGJ-q*1jPO~&Guk(cGC&*%Mv5pgBFy%tmqpX +EJ0@Dr7_C2Z^AbG8$r~1a10^9m4h^IdjOCQ-)QS^Deyn|m9lVj{&1lt +!z%E>1ZX-`J8vJ8VqOq`!A5zHdA?NB$VfyQARa{Py76zGYk^~7s98G|GJ4=u&l!K8Ciaae!a?lsODP- +}bQw2vP?-TR65N!BGB&|*ol`c3P+D=X8v)Mb@6aWAK2mnY{22+vLmSG|Q001BW0018V003}la4%nJZggdGZeeUM +X>Md?crRaHX>MtBUtcb8c~eqS^2|#~tx(9!D@iR%OfJdH&r?XwPf6wFQc_al +vCBx4N5R%(+fCfrynOSY?ITjd$AA0|^A4A3Ou0ns!X7!FraetXWPuK=jg#t(T2cP-)4efsq2?_9f~$z +<~U+0|BxT~^j&-!*%srBG|pZc8n8s^}^y;Hp(ZKGM5)WqtozRh7(J)ucIJt+eWzTeBHxt$np>Bu*6!K5tAUcL{F80FtK3c5$|inYsm-RWH|}2fSB-R+T~k%%UAoU2J$Ts2-#e*W_xN#F +rLt|LOy5*;C+qeKuAe_!H)j9 +t_~M6bk5tUVAG4h-uDZSJ@Y5dUl~v~U5_zw4`}UU_Uc&VpB~Co;%YC|fkaeLN5eW3pF!4l~nd@O&D-> +-z{Veq!)=7S%X(}IcN$dr^+UEGCX;hPl%gc9k6|+{>AP|_(Nar7Adr%FatF9R(ru#@|K&Gs8S^*esZv +^J2+G?3K`F5pqwRfv|NzX3v(P-D2IfA}g13$Lg`*MHDPd?eJYs<<_uE|O*S8LU*bepxE9_>jz(sfx$f +R=Q9OT9uq%LgC;?>^CWSL)pM+Gk~RfI)HDiwu-B>)P!KyRJYHMu|M+ZcMmz3zr)8w(nkNbx{^sEA?A= +{XW~k7%2WrYLTbG`?K^rlTdit9{Axu2{_g6BcSZ%F@u*tb>*vobK);h&AsY= +HVuiZaEr1aIxIxm-pA~W~R=cbP48Ys~2U{RC14%5f(OjJW#pxP$y;-n^fi-)l)V+X}6MuVsDYzeqq6Q +l&#VH +u<_rvR&_)rYd(&baMdz%pTz>q^%4*$vX0ezBMagz(o8?`BMXWga2REWy0Wv=*x$Rf`KXdt()NZfKE&0 +Ze7PTFtbq)(KdAfL7hE5Y8lLXMyFE?HkqZgMH0D7s7&8r90w}`UUdxaQvMS-MN`b0QPpy?OPGMFlRH(f5S(>VLa>SMS~G0WG^|*+W)R)Igda4~ +>R{Dg8kJSUUv%sZx0YqHDK_fH9h5BGyf|F@`scol>~b`>JeFh#m{ZHsNq!Pd-eyZM&Z)g8tUik0+KM) +^Bd5fE6N6<0KL3wI%x*(`%qFuNAZ-V&I5P=#{Xe#SRMEHc$I@Q)BpooXprMZ +3WUG-_RSTe!`RLc9S#py7+S-^=@P>&inYP90Toc9g>CdzD!%t9slWqiFp7F#=B4U%bre;3FOdTi7$Ex +|wCl1V$hu|7$Bj|RP)!is*@G$zQK&;*sjR@bHWO9Wz|=72NF6DH^9Y(FDCSv>*tnD66d<tnK +i}X#wMW9n`5NaS2&zm9JQVnV7_ZHRg5VB)DI|y6zo8!#oZAE-x`KF(f+$^6rzt90-fknuwo ++t)W=Y2iP(Zqen2P#D|aB3HH@bQ)95q~xXT{T0Vm5ZX%4WoXpCxgP6QK5`$2}5l|BvKB7YF5nbaOq0* +;(j`$S-pYz1E;VIi5u{@PUDk9c +En&k?^q^{>gr;!stv3oI$mSP+)TipgFp_Ud{kW>`~4LphU;g#WUiZmsT#|FNS&gbt&)nLDdiK#=f0nyE6Il0Q$3Q^`n<;Q>cM1@W^Au=BwKecy3Lxq +!G`TuULo1BdtiBmX)$>iM_|Bm+bCy1{BoHp(>nZpE0Yg7bYs4>gg--T>UP^beACG~VN0LWN1-Uyo$8( +RXmTk?NwdIZjD>CdDzBsOp2eee64RrSmGesk%Y~9i$HUR7=0eEGbentR73&K$WKI_FKvd$IyTsG0h%+ +Ac;KFu(>?x&K~Z>+z?c^*({9@tFSAwmG6szH`+?fQuSGH=KM#>oNpwG4U)#-TmLG}#VQ^L$fsJZa@(d +S1sgZ-QhF2A_a#L!Xw$Vp`*AE5Ey*n)cJ16B#VY+eIX+JJ3DyhlR>J#NR>$C5VKq&MK-9z~5t{M)jaj +&=P{w9*IZg0gC%rK{&|2z5RD=^q1RP(PkU#Kq5L61?~Yb#7_zFBE*WuoM$cAEpSs9V*zL-7ar`etV;P +7ty;Em^@@lvRUtqHm|m?wKk_Z{GQ;X2#bt$(tgj3t@sL#zCL$M}fqCLMH{}b6Z9oJj!2w4F<~e51No7 +H6C;SS$s$&trDjES0WK%XC&?7exbE2W@ONyevuKv9PyaKLEML{>-k_!prZ0S3FdQUf_UHz3p?~ALoO~?4M*<#XoMN0uDV+T8W{yy4 +o;ykwiw@=ngvd}%Rz3^lDOsK>f+*p3$+)Bt$(BV1Ora}A5^hDvjy8*XT>PY8J2H3)bZupGh10?@+eWc ++qXwDvrbq*Z;eXj-B;gpnQVI_GN!GX?)Dd6suC_=9NC*A3?3^=|~^iuj +pzvNi+9mj_mt(=sY5xk6C>W76ukngIOOLnj1{LETSHPK>CzohN8E=_KF5+NsDr_WC|W#YJ%C)!Y2urf +hDtI$t>MelM|dRqdQ;kiPWQ_qY!@gjH*YtyM$ZyCgDE4XNznu#024w?I&aVvp`kS5rYlHEJ&TT36Vl) +b8I`>XC?S9sj+30hn2s(LCLZ%gu43;@-`X~qn06HXW+oG;97%rQ=f!39Ei%CcD&;(DPkfeh%b3C_hcS +3waxU_utj{7PF4XM*k~5f4GauAj}=#CxfXgjAm-5%7@iFsNa>B2ncd4H_|CiID7NMaWFl0(v#IlYT6?5*i^8}?0r +KStd~k{0W}xb~p{e7`uibgEv}!t*@@$tqY6xnU2-3M<%R`ex}iU$AQjDz|}rvRgX_J24Nq<@9EH8 +)~f~aM%=NKRYAqD*nMg&G9fi%Mh4-TD=95SuMp&=!E$C%1xC1SvRY$y#0b{{+M=eL(h7%W$e+yDJ)&A;NQnJE*sL1)VF +XA%DM{5a9+^CGvu?_ljIOdrmNWt<=E_3xY*lln+|;f< +~e?)MfH{ur7Gy!9r!k`8qt~&lD~_3utm(tsiS25(^QWa*$sz(?|z2h)Em%lT)B#j<0_nG97Gu3<-=)> +9CcQG?67>t57g;+V8~00k`7N|HRLif7tA#;{Eac_kj9BJ|YoqT6`8Us%R4zTGECz>nN#{NhZ@1QV&m> +`9o&C_ih!IZb5o2Sk0r!GvyJVI3nE`z5uT}+C +v1jW*P7@3bYGInhZ%GfG4d+`H4F_tHDIkz;?!Q(8<@=~<`%7N6Mr>mSdH6JSen7Ot@AFmztdWsP_%G}EqYFL +fErZ8>~2db(=wSqsqOPgpq|_@h?6<##QDnAgL>oyQ&^f{&ZwW+ELP?>VwbkHxvzm73J+E1QZH0z%t^Y +6_7x8?z-fHRDYK3()i7I879vDF;r^riu7!e*NW*uV21)TkYXFVhYW<=rab|3oh4kN~20XO2mf+Ra4pnH+HaKiB_;$ZHg=;ay%;4HsvlP0wEoi4CQ7Q?|w2N#2=o}prg3ci|M=+e308)JM;+aMoc?mh`Ycy)$QMI2+FRIAVBG1>s+LXNkYD2|#!SN&}Dn_&hLPd!gM)89c=p +p0Ysym-eTD+ud~J$LKgrr+-q^9qpU;!hLq1z%XOJU(gpeOfDq^=L{Br%!Bf1NDjwL0g#o#z*HHZ) +j81L0E%y4Nb)e>YN0-P0TKg%xcIZmr2yzC#pN*+zCc6bjf_IG$+FDTV>k&^}GSM^W%`Oz12rEA2a0YM +7Ya^pgkqNx?CUE9+p|^B%2vd>hNhWhwYO3pU-oF+(NJs7MTP#u`@6}l)eEzc=k%mETrUPqB7T?Q&G=x!)|?;H*$X~+(jL9B@N@Dd9oicR +j{6352bL?4b!b(o5W=35H{PtZ=NvuGy>04!bIN4ArLs!&9h5m1#&KzWkB^9nQ;KTcdYqVBuEN;g8{BB +euJ2V&x(kpoS`7N&cvy_JUo^9B*^Q#Xcg}%X?Z*O9wui>^r>20Yu=u9TTNt1MRRd=Up#sH!!hTZ?S%W +`lg+B*dJ0&VGx_>z#4aoK|8h&>@Vsr4Lzv0udsV~PKj`XD1^2KO-B<5}qxQ#XKSvRzZbhKqmF%W6qG^|{>GYbI{5S*tE6s$ +wywchuHuMb90G{`5yp2>g +K6AoUOooE@2;U8pFC%rsxP!FWu4|IY6fO-tqBzDmM15ir?1QY-O00;m`Rt8hZJFuIWA^-pYkpKWC000 +1RX>c!JX>N37a&BR4FKKRMWq2=eVPk7yXJubzX>Md?axQRr?LBLg+s2XKUFH8US;a*#<|2}Psk`t@(_)30fqot^#A9J;EOO0;rYc +5)@UMm)WF`eY`mdL`dTb#`|4=_j9laz{MhNU?6V+vd8euf($1?PT2vwP_C9mAI4uVAs3`03|$Zc2ew{ +3VyF@(P76!cW7mfaCSHQo4HV3TP?fuX5Up!U2YeT%dL_)P_^4PZ3i9dW!E%YWuH`?Y{Mtp=IRP?v46J +!VBI!5(cSD3NB#J5+x#l)XR@1#Cjg_o+{&5wwo>r-_j>r5pr4-~_W0Y6H9SOI2-v>b=f!JTubMXBm&; +e>l?Pkx<+55=vQ=ZP^RB#7rpxp4YP2n^$JRDpmUUBC%W_-&DvP?@$gJaxm>>h$0bwyi8zH&1HSmD{ac&A7!k6>`Dl0egSfbdQ@uZ +GL)EzlJ%le%eU6eOzrNPF?@>W33K*97yud@OYZUj2Jjy0?SHZrPiSLZB@T|Xdce=9JlaLC86w^mE5Wv +7GhPcV22nmdNL&<|Iqw624UT_yRs{mP2IK4)-oqOeaH{Tx+8}E6vo;rS+>hfp_a|w;`@xAJ;O)iU6C* +_r()mAwQ{}n_J +;AFF@j5W +w${IQxvd{MIjdAViN3)$xKX8q}aXogH(rYhmW7qeB@)KS37xuU_Sk1OUnvy>=I`MNzkI0;_qK0tw0t- +3zGL2B>XS3=?i@hTmaHDJf~g&L_6v;z!@g~pYjGeYDwoccmMH)uRCb46%1eoe?hVf|F@EBQSdt8PLCH +ukAy$XY5QwosAV${c-_oXh`i7ZtE;+cWej#MF5yIlnb7fzl2n}Er)P8SMyEf1`9Z<~UJKZoaGbYvre9 +TXkOIPhEMXS$t +3VLO$dHXYUHalf5Tek;%w1JC#(yc|^bRh-jA#}4Q3o*$j`Ol!UX1j8q!L|?SHJyh24Cpl1gl#<<0m#c +n$*kW1KNH;9!T^Jfv#Qo~+IGTHOYykCnLs$C*JPFk_S(pvlL#0Q(*Lx)^{s}Um{w}(X+n1S*Cm{y{na +OCj%Ot%2}nMRq^5lP=}ygL^aP)`2g$;Cxh<6 +`qCJ3m4jIbA|F@-glc(EQW;p?1`NR!2n!=~!9?7ZfS`P{-&V`Ynudq~Ta+uXy{SP^G%e}$gne3dMd7g_D6?yC(KY=^3m^;B`^_3!$zNG_S951eUZ7&&wFng6d7$cgt^AeYv)B +7^=mfI51Gaww-0@vg8)JyhYy8_2IhI55qffVR>S)TtTD1Z#sci*o%POH1_

#9TVp{Et`IJq^~6%`w +!?3!#%V`I%FUXBNxxCKcR%>>%tvN>cr%-s>}9fBB1X?HHUU7>HB2^KLSrxT_1^fbpRE9WaKI-sGw1Fe +9{APh~L8DqaAKf@M>FiH+m<)|Dp40#v$KH?y63Ewl0ZpS(9 +f0U9siZ$LDl?5G1eFE<#*1X@;mlB3a?GSi-7Ye8U1;g`i+KI)L+meGd9a8e`QHM(WTDDRU~=@$;Le7FUd56QpR@QX(M1Zae>s3nvA!A +F6i&Iv9T035(h>nR&C!TGYM^~)kM<4Y1rP}UH!1W(JdnNuR@Y|V{Yl)F)XYrJe?8G-<8m^Dxp>Q8-Z} +lw#}|ZiVT5yp7yg8Hiowip8nt}W*+y@?-y++|2RidRDbN@?_&ZBmMDR<({E6m1!ImXS_emcN4#ws{(y +??L{CaY5PGW(6PgoBvAArUH|6O9(Ggd6l%-ErPK@K4*EMos(5+BValkecrL9i7FdUq7zlD~a7(LUh;Y +p^GehHW!5aUM4@wOzSv)SG*E&v0V2jOcLlCpF1`t@e`>md=0@z~}>ucM^?4FNgnOy=cL%-;A&|3QK|n ++EtN?7!1IVXJlLxJgylko|~RuFCvpsx16vOzgZ*?!9hd}n_RaI$UnWx{!AMSEeqjJ2k3GKR+Ft+6!`=LG)@kp)!y;o&W#~BssaTZP6*((<^c +Mc+B8k4>Ucdt^96Wi%5`4Z#_+!L4RifNVg@7 +Vmwb6uCY2pf*G7PkP>ly=h{j5ctrUt(7*Deph5Lx(FIm(xv`=waJlJ-X{Miq$Wy;TGU{&^b841@R^Eb +Y^dfBT27?~l@CpRd9yIRW0qV8=Oek}N8rT~# +>a%DXbzq4qq*MT(WT?)a0;TUThD@RPCfTh)bg4EZDCA2NgWhxAU|1SdJ?*^J5*min;J&2u5*yZIN%I9 +f*u73RT!v7V6tKxJ?w>6VBPRn6Fytf`OTQ3&s=1{Kx@k5z0`7^PCCa6?4VhwV@i!GUR^0RgxN6aRxN-fKl_@r(;!pD&_Fm*Eb> +ooe+-_~hSdq@1$hLf)41pbSYZSAsOmVmY6ZWHQ{1Q+gD9}UConjMO!E$XINRaa7>8{$Hru6Kma(|Xc-1RM{zxOie6^}R$etz@txF1y-2onQ9wvM&kv} +;FQA^nn`wwI|Z^$90L+VR1a;W7Ly&2Iq@$k_bt=NHzY!woEo{7*C) +inAaBxCrA;V7l-0s5)Horb*(*kVr5G55*ctBR!coO`s#hWL*CYTDK2b~E@3lPi=y4Dk~@bOt&aGjX{1~wSc(a<{* +2C6)sa56Yp)Basnme!LohXm6gh@JL|9SS;%z{iK3!RaDvvT!t9KXnU7{u!=gmACu??UX6&4xbfW1Da1 +1Zt_@Ee7ab3aAzT_Oxu01R%EI~Kw&^LOh$;9IR{R75ZmIq#LhX##-IP#t#h?l$vUWQ1pcjl$Lm*y?1U +C?K;dpSj#FbHpphrqNKG;t7`_4knsl`Y?&H!LsELl$0hN*+XU{*k{3S?IO4^Bn-?eqV?I+GHu8z6;|H +2XK3G5JW>{VEW==F<@l%M_q(}#%&Gm3;V6-gz_ZFBKRw6nUo@nH5>9Fgn1@nB=x+^}!ywk +B0XuZ+b`NE%%n16BOk!-`TX>#xP8uijPf(O1E?OBD?Lz>e)S>r~4X2v-mL!-Eg&TsZ1VdMl*ubP$;m| +?=&Hl%RFULPPO#TRI--{6xaWqF9}d?CUwJ)W!9b;JL|Ex4rvamzPuJ?Iz*O)ct`w5L3vUrs(Ow0LaavMomYUSEPbPo +_fojO#&%!N)C?Ft+ybtVIpzC{jV{jD?A3ftNgJkf&Wb}K!Qqbh2{wQspoy3p{iQ}%5`q;J7Q2*m-zAP +Zh43i)#%^l#lh!K?LUFoDci2-k1HKJ7Gg6K;FeQ`FdKdO{eZLe9iDZXYS4AszrpfTVe+Ndn8)qqabPH +Nvbhs{WaAtrlQh^CWk^n1104z*-XEk=YDeJ4`gxHNu3hOm+Q`hVPmuvtQJQBiO^bg&X +u7@A311rv29G~1wyplH>Hg+iB&roqRPxVCs&k2_l?dt=Q}f+HOQNJuL34*|VAJxUgIk|39H_%`!*O}~TUi$epmjy&a;vu|%e;xlQ|7iJS+s3MS@n +B7n(;1WE2BJw0FCEC5QNeelrC7~;wVHe)DTHj0P{;!FJ^%YlkTi*3y_09fa}!DLGcec5QECoJ{#C3F) +++%F<^{IX)cTTj%Yql$OhK8$H_(SJba8+uM0ZNU9xj&T9!J{A%WgRT-6Arec-iWgxy--me&kc>Ntg&HF +cOY>d07!J!lfg_BHbE-o!Qdu=seUg9T7r(n7vE_=1^2nW$TfI|2PnO+O3rsT$%Eb<&wF%umh|+( +V>vU3c*Fgm{3Qdx0*cJ+_9og(AjfzL0@)8)?q^Jh0@?C>stLg}hVGxcV(LF=H&be)_swJyTNV#u=j0n +BJ{T;426_@aGH{aC?l~rA!Cxl@$a1`24b~!j5h`3YV`Ez(myfQQ(N6D2MCcF>XPiB$8a1fjx;5vIt!v +$H$?(pi3_+B4g6whvP=_mL(o?K!wMQ9OwIgY6Gv?XY#$@>vu>(j4Yq_1v(RR_J#vr*e^Aj#ZOZ#HJr^ +*hIP8T4(CzhU0Tqqc3@REjJSB(5VhfZ5sGcevg8!jkh>=({5J{xY1GsLg^d+YM#spTS~~1k<}Lw~6F`dPvWXG3D9Dek)0gT=#JUNzHAI3V$3(x_XR)7Pw>{CD>E)#uYJ|K0SrlUWQdF&+C`L0Ql$Oo~q +4qj1h}^2NOu9x#gZ(X|kiDY2r+-0SUZuP0`1`HCxEPX#2BXa!=FS_PM3fPw +Cb-vuD}Oq(Kh@EbCgKIM8!X~d{8YBO)3S)T*EW8CQXJ(aog2sz{pC7m`3{(nsG$B{t%2_6tc&eNXux@ +-~WI4tEvc_+FJbqDuX{q!wTD3>T)N>{R{H@wdFOC}0>OBi}OdV$gTDir8Xt49hErL36 +nbcUgMRMz%LIUzcBKYW2?k6}BGs^uZXmIsw+aL%#pc6d;b=_~vl_H_<5)&&jFW8Q5ZOieXS|zD96QJ& +a#=`ZrQk_!D|DzZn+X2DliJAOgF%++rqDVsUSX5VO^;clJuy1>pe_Z6G)K_*Aq2_^QRDMjK=|ldJaD=gpg5>g4>v+I{IC9)-kxV`fqry@7(nb6Lu +nSft!yK;Zj8eI#(_Zq@Eenc7=tfWC4j))jBoq+3?tyy{H@{thkt1}BFS1H>u4A}?%cyZiwe!A|Tv?ah +BKqbIW(9AW>t2+Mf96uRb1Zm#fs*fq;B>qHFDT|06nn4qsZ$S@sK)L*SzY(TQm;6BDLB=K5|_dR;)M9 +^q5C|zf#rm%XQ)#ht4N-uX4#i$|0gq~{twdWmu1$#GWr-3VSO@}U93r3(t0+u{6&(4y;1OgK66EO<2ckx1#iN70~TGl5l~2;>Q+Q`eDz>}o6qb|L{BjFEvjV +t~c9Pah~Cq609-Htj*7LwwVKx}M0@js`>1Z9{HgE@?hQSw)!$`)Xg90YKxq0z)zqi#L_TsvcgY^XzvP +{usL_1(L(xD|+2qj7aLFWn17JL+8Wzcu9UcX&^ic+QS&f1=9Qdrr5i+`@E-@95c=5a~ +xwz{q_2*mXQSXAZgFob2Qx7@&e$X+tKAlXUdwxNleqxz*SBo3%EmOm69nZrfrFRDE?7Qw3(iKJb~FL* +`&!fGHuj)eab%toYS|e00IZo&XJirf{GFztA1@nZ1Y3pkRG1vmy7cG +i^Mj)>$_4Gw9gZJ}P-f3Vk>@XmXQ94lkmt$T(QCLis?DIobOlU)@+`Xa6=*LeQFXajrNxii44QR`GON +aSWXY0;g=3>d@!nTpQ^a~(yN$yuz2yvd!`9mY3tjy+>>LE8dOI&ks^|!#11Xy6$VrAR7m14eIv%O996 +A&N682KAPvOJf0phXbdt_Zd)NJNC=crzvm%`MXbm_haIn3Sk&Itbq@FIgu5&CajAEX!AO7H5BdP%Y2y +P1#V3V8YDgXKXyR_kAOWo@=uf_Y%`Ho-pWExPxck@=&1aO(vhE7$W_>f}CCs^m!`J8Nd-X>p<{i%>x+E-k_jc`$5q$!8Os+cp!5`Nu)B|W6@d1N%Sk(QSolN$BdqNs_ +^fiP$+=XEaYGbfsiu{L!}JI$injkD{mz@Bh3Yaft{wmnP%^Ax@m6VDx1i(3&(c!j+io)fo95H#sY9)J +GiMYMz_y&$Pr7s$-}ln6~?}KGhc4Xs?KQ&l|Dctq?t~61SihuLQXJ*$GZpdY7Die<1W>tvhiH+KuXHi +2s0<<;)#dRPb_&$+vmMW;z0ZpQG<}`C(n!u=TXjbHzpjtQ2RtNBNyoG8RR{*!uXV})T0^+xq1wHSl4m +Ozj^fakAI$OVHTD_iI1CZL;8~UYQbztnX~xjLGLn81Scxm8XWm&-!}NJ8-5=NiAsJ~nZW;ZQ7sxbJQ? +rUESZucR<*uwZf`)6+aX9Ua3!LGmH9m8+@90SdD>*c({!7w%KvNi6_VvM44jjyE9ltYQ3cwD#*$vi9` +*a<<-Oo=vcamfI)>re7UYozQYISOk;y(xb9!+~Bt=_(T%TY;`D+PkT#@ +cTb&Sq%?U_s*I3%jE2zKq*Cgz+az`dx1_kE*E2N8ZB(`Av?l+jcr}eWipVdn@BRX8^$Y3+cS#cw|U~# +$TM-+U$x~*a=POCy5pX=H_R_+HIU}-Zn?q8K7TxVvqvunKAw3Ng@+Su1C18uVIX4Cci6d0hG}yIl!vw +V{&4Do*F046E#sK2Hu8S*)TQo#Uu_Ktn2~6)U&ca1w8R$d?Nb~@>WSTM8c2vRg7SL@xr%Fz6tTz6dW#BgaA5KW@$MA+0YF;=ncjTS +pbdl>u45E^A%lYNLXLAP#Phss&TS|y!Ndg4alXg(L$ +9&!%{S35z5|+$HGhnmCQ%=pXckk&Te5-Od+L<&ljgkR^cjfv9pe@%kHcB#wY%PKZ(S2}cFJA7v#(~l# +Uf0eoXla_zI21A-YXCY+5nMHA@HpPrmJ0=lA8hTNotPho`Wxk3E@n*ER!J;92r?HuO*jt8a*RU;+l^! +`%~^H^80BD(tlCCweJT~a-Z3(6Eg5qujnQPt*X7c*<^9^T^}@`sbaOhr$bd#MZ=c_bR=k^ycb#P7g#e +abN9&N@IZW}JKg{MD~;xT`?f%FW?G(M@gTXI28V_JTID<#AfDCpQ;_3(d8X`(Q!bS)>gH#=t@wq4t48 +ukQA-Rb(4{Q~$E6ZX9R--s6&4!dt&EuC%bEZgf`YM%96b^G<<@gsfaCuC`*x1_8iRGy6vbuPp5M#wPF +#~-Vjea;hjw0@BMcGqg66b;vs}Pm?ue)U3l&j7nfzBW~?o{GIsV2-eHo{65l9x +c77J82H018_-%XXk_PC-ym~=z#fVrpdB{}!l*OK_%8;$k-WquC9B1+s(zhYtcW2(Ktd6|3?nIFhypP! +lkEdu&1jXOF{=!-Q{t +Bvg#|wB7Np8*i(Btb@wq8FF;<@6aWAK2mnY{22&db>D +K)P004a&0015U003}la4%nJZggdGZeeUMX>Md?crSBrb#h~6b1ras?O1J(;x-Wej>LbksHzR8koGezr +=G4|6^UL?+ikzBPzb?*wI+^iv$y>F&WxQ`vfM8FqP8kRkk~Whd424$*yT!FCe>ymvQ57y-1|=KxIN_D +lq0Ho%oDqnV%hJlwc0Tt15W1WJ|mIbTAq1)QU?JrAf9 +DtHkm`;xscBg`$j)#pEdoQe?LU^9-IlY4=tD>0V32YF5Qo#V{5!_`zo!>tw%6OFm(c1&u-{a?K`%H=_ +-=Ga3uxI7@asj)zEhsrK2bG{pcz;j5cYW9Oa)jcHKh08Cb*E(7O3u-OelL}YG34Gq)Ly52-0v~?{FG~zb8Qdqb;Z{V!x0!R*F!dXU*>`n>ACPT7!0ns-RsOSyX6eb< +ysyvvrs_D#2Uvnzc5T?JOc8HS0}=7_DR|44)SZqAxf)Xv{pJGe;1nMU|(G3m#LI$1*EA}El_KuW6J|b +(gsyaGM4CcEABXGX)F%?di~pst%W>KMP!W8;I?)Y`=(+p9ZH={&Qq}_k9nO8TM5lmD-Tc7rQbtMV#w> +Q-N_hL3P>+V^D#44lUA4uULQ?yt1u(l?71;9K9`IkGK$nrE)!|^@aFqDn@rfi@_YLoE;Fd7ReTBA`7` +OR^uZ-qQ03atuj_rl42^mR2u|KuEJmZX$W|kA=Hy4Lx`LWuOr+lf(d%dp_z>XFuq>GK +!NzAS+VD((SOZd$>JXpGkVpX7La2gajTApn3EoJ*)db+y(yTfq;9aI{s|BJAEM<5OGgMrmZaRczBlhGUq`903H2}_`vu35tVyqd +vrfth`tOTa1E)ZnDGHFYr8h|d5^%&F6a&%twbnu#8#$0W6TD!Dt9 +G=LNOcVwWhxuB$=?E@Fi1}K*MulKSWalM7lWWm@-u;`6E7<1QAhbW6{@kv0Y)YBJj6IE>~oZCx~(kjE +51{fKee{MTGOps{_ocl8FE*&0%O+b4Bou%n%dC*Q6Km>mT8w^^~(B3RO-o6n__u9>WqGBqjV>7C*!EZ +;A`RNhm|A6EpCyT>KpgAAE0QujD|YK6G0*}JlAhT>g3@u#iC4T4U#w1&B+zYviUH|BVD&cg9X4e~@cmDs0(R~bx^7|avV<>_#wbbQG>DE~l`@4p6nhd0iw3{ ++Opz1tq#>zt7ymgJ#1$tC_FVBGxiOe8mLbklC)A(`Th9JUvf5kOu +9L9Gu}B8RxO4iV-s!77Xf|XfKJK-xAF%{fiaz$+p9L^)fTE<@U_*URo$&#&Bli%m;S*CfS6Y4?#u?rY +mkAT!OXL>i*L;Za*0i*G+&L`phf9U_gvuZ7q}RY1QY-SFfs<9O(y9r`rluHq|8A2?08g@Rr9sGr;NTN +Is+jAC>XMikQDWT_Jm~Bux4!0nY4k*-3Hcf2Eq8_b6L~IuDY6Z4Tep=W`RwbuPs`vrAg5ch8+bqIUX8 +@o&GBv~?}bt+|?=TpIqMhMoKW%c}U?kUc +7F&d^5Ks4Hp5t+Kp*fABK7{Ob8hi!>|0VmX0F4u7%|fanqv=HRiqj2zStG+tD3oN<2*r97DY4Nyx11Q +Y-O00;m`Rt8hl?y|F{4FCW@EdT%`0001RX>c!JX>N37a&BR4FKlmPVRUJ4ZgVeRUukY>bYEXCaCyyIe +N)`V8UNoi{tjC?o=BNHVT|LXOh`+B#=MY9x=M!Eg&3ngdY$ +1~gWTMavSke_~JW8eGJ$(L*;m?m}2k(=;T?iuCKYTyXi`TqL=IJj*nao9wzi)Xd!G-r$mg5AhOvSxf6 +f!B~eKIZ6Iln8)8?!7$#gj!kzDcjSG;6g46Z8VVBVr**(kx37w!Wep8pc3$5*`z2rpWmRnMAt4g +=>B!O#D*m|GM4@yJLSm823chb+(&6#h`R>tCqT})zV=pKTz)Hk5T$o$hj2^HtY;=4>jCK$-Z;9)VG0z +U7i<`@+K{hQ)eOwrLk|oVM!CI4vK3&}tJ`!neB21sc_Z0w3CljdYuAm2@Q=zds8UL>G4?rR1n2OJX9^ +osQYl&pVIL-QC{ SUg>SxVi$ZvDVeqm7-2B@q2C~_SKbh0JHsb+W6|#>n}Hl<5`+t^C5-pp%L7WI +3tJ*^WK-zB%kj7_3uJCn;Xc0iJl`a-sy+Qm@q61ylBQ>@VHdxg;Dv50QmxxUuB=+wbxdgtAoNo;w +L)9b;cE{Pco^oRPfEhj*&#a8Lh +O#3n6vL|g*`o&gkNUN$OQW4JVTZA^_hI-cv<`BX3VC|Ga$YRNW==j*~jM?97Xx50HoPs1lMymW|61y}`C +>kaZVQImlvA-PvN}q|O(?SapdHH@Rcs9`{rX<*{1R6fhU_y%)#d@3LZ%Da(qN%0^0Ak21>j0Dp=m1r9 +C{;BlfhAgM2z9jkl&T_!Fzl#*F1A_tiVYcx`-ZXTvdbBPeq`vpOUoRUu$=3J7Zw>D%JgILDgGU>oZoe +qz8J6@zEsAcERQg5+k#)#JgLhJqzALATFC8|vBvA@ur{| +2pPia4;VzKgyws%60?{(Nq?f8y-Y@xK2(m7Li^U$w0$~Orzzw+_`e+p&&!&BbOOGncvA8E%4qHjuc_Nsb +VI9(DD#4

5ZstB?f8qg +7pCVQzi#v6lq0R~!0ZdG1tB2{SX0~K0sBCjceTpkA3K`3%mDVC=8h#kGTlNO0$k=NYc^P(vkLHM{ROO +&K&P8TRzPnV~7FhdrmMrBmsqb45ORU{>9POq+fj6&BXsLBgzx;IqeTBEu@R+uOJRx+822|+IzzhCHTn +764Tf{Jp?-4TzNW)*_kTID=Nu#DogPnj=E;hppTF0N=kyHMHE9+n~Mtj(SeT^#qiD6 +)~l}+kY;RStp9!S`pw(dU&jq5c_S7HQISZHEz%4zUUu@Yf_^B$bKue(yy++rw0lazOQOivGrS~LrbzwsFeXR ++p8dt(9T%`kp&gzM_7oz{4@z2U=e0;wNComj>zAKRWD*1)#M0t +esCIPjO+B!x9eM2RU36)N)$@6T^m>}!>~N34xNR#nuUB!@j}yipcucu7Lqm#}YM`pjTu+wN4T6s5KDv!smb!)ZnWEL$osvm|PBJqGC2_fA(9Qu4M=N1y&mwB +P10m$`4U@B?M%^+l(A7~;?l?n6>N3-@N8EsXKU5Vcw4uT&X+_tQczWpaCydsG&q`iwZf{+Z8bg$~C52 +r*a_HGb?WXe$6rt=}w)s%(G`JOJg?JS(T*3DZ+fpsOhvD@l`_s-En!>fyJ|J5&D_!rT531&=?rE%bE7 +rM}r)4InW_Q7UHKw;F467kMa1lSy;{X_<9hP*WyN>4_YjY>t(NRK?yEna?Aw(kbDf(v?ZVlMieTlO0t +~k3dO-8rzcfg*Q+$)-6h3yfSXR%S#3ex`ErbmsvJU>B&-HJHY)7DT^GSKG_*fh}A6#shjThLUlW>VEf +vf7vfTQg?bQz1i4b!t_^B-^J`-z?|}wU(&);y_JC`?bqDAvA!FnuC<%^y%94Ej +hvuo7TEMJDjp*QLi9GNdwUnX(@XLnU6{|v0zoz6zYc#X#?a-HD;qLJ`g*urJBaB;Hy~A#Lr^hn3;zm8 +Z>U_e_y?Q`+CzKnP^{837Wc%9dEo~|6F4UfvWo|TJLP9g>()oP1mFh-?n4rjysk{qpY5KW4~fCD$TGNit1fjnkPiCls?k@QA6+)+GMswFR_ktLAe$kDz56pq*;@@q2NDn{GX +;k&VRVJAu1fXE>Wv#Moqt;wMTMz(_jtp`0OIPd-Y*_MCMRZo~XhBy8 +G-PnNL%v0i8|9&AXa??e4N^0wf80XOqv_Z^5Ya%G5sqR?J9w(~@s6$r!J5Hah<~u>^0JJP0w6^{#%Uo0t?-YqG- +K%bKVZkn2R(r8(K>+wKP)h>@6aWAK2mnY{22&~ +bZKvHb1z?HX>)XSbZKmJE^v9JSX+60t=ek(v**J>}D4LEi?(O71-s;euu5=@JodJq-kSX4W~S|zbB8)k{Ivz-<>$=My+!vYGlEb +X;w$s}Gcf916iq)6^sr7cX`Y@nxqSEhe72RyH}+ObN)PAhAZm%ECE?U%5OH?YI7S8+eAowE7$Jh~l3+ +`jzV>wmm1{{H%3w?E4gU!k?9Ey7|^UQiEDeB`5)jj%;~uv^s>wOj>yduS=*f#em!ma!Yi$k@9Oy^Ou@ ++FE4npAFtKR&rx0mX5uZZ4OJU(4fqBsuHBR+zOmg^w+(erSF|?ajt>C?BQo7j52EPwN8+b;GvAAjJAx)x>43ls&U7jjFj7)mc%k7Hj!qTv8BenA7oOV{D?R)sP;U+6LjZ=-4u?IVKT1q`8WN6bb=BE$0Db(p-oUoO4wroXkz>z43L40_0CRNefi{juBjZ;>84AGH;!wRIH5TZl>ZLs)(K`66AfG?SQ6x|$&>7isEj4~C$x +c7nVf8PHBRTBw%)~-ggS3#NBkkiYV~Q+!`$65p5KQW>SRJx5qc9mRpy$ +#B3((F)2#7q2=^oX7xP%m0YNx6(d4uNYK;`jDa1KdT(_Ko}48&&nFELoZ;RjBC``Hj_`HLt#msE!0>& +hRrgzPH1iUg?_(=<>eRu|7&t8!8v#&f!3B^==YKYnLEwPKA?V~FY9rfM^z_tF9wWBFR5P&4^EnYjwH^ +=r9TX}O;2%?ARn9VpWCFTxOwg*{9Xt-kT3uro#tn8pzI&b8YAmH#~o-W$C1(8TAfN1RLsDH{hmbhI<8 +`R(B!~OD@wVRfE%blMs{ruyo}RYYz5B+8Y{*P2{Dzq$eFav^3T95#Hkb&3Lk-apuALe%U +XoGfE}ZAG8+r!o8^4XUcC4{XmdPr^7-u92(E8Ho&_; +u%un%H^|oY-?Fc3qCFW-3enU7RYsH|3*>ita$mEd2a*A^+}bCEKVqf9#*_nMTMR>z{>38-14rifHSG1 +3lUmQqL9TXO_yi=al(d6BU3t72)d)hr@ubWhvcO{*x~Et68T^ZQ90nb*iA4-UQ|0Rs!>8GTfyOt@8+> +9xE66v(os)HLdQJrHHne-WO1GNLHf&uSu&k{J!a|0lhJyAAn{PB^hcfII0EpeXjD;Y`5;Cv8f2q&i+w +NTD?|U&lgP|wcQ5XzVt*JtGAo;Ezk9K`@2%H<9*nbJ9FHkB|My_rw`Uso&W*_5A^53b`2xQigZ +Mh@!CRpVxsQ1h~BJBy%c_zMg&xx3@`%|1j#l^xx5UDt(4>H*MWB4$|eXdRlrNCB37m}xJW+$;i<&ye#>v=^DeSTG7MfzS9 +(faYVBKJ>vh{79A0HCLYwG1-1A%PmvX%X)qZ>0HxTeN76giG(2;(J|x$fvsyelRF2sGjzpDIS4=s(Ac +C0OTW!=#G}sz=(=gE48|Rr7{1LHP9sINKy4#yoQbZC~`hS-g6#aC?A$+}WNSEPk +u&miI%hB)>CIji=RucdbHouW8&Glh^qc&#l1d;r`q_aU~y~Kg5TI9TK9nXUl`_ +Vnd9R_k9e7n2FFKBeQxxk7&TpHb)?iMe?-u(7}sdm?^O%^APCsXH=NdfCnAb1@6aWAK +2mnY{22bZKvHb1z?Wd2?fLZf0p`E^v9hSzT} +2$Q6ALkpD1X9xQEXx@mzv2yg*!>U7=p#zvepTLb|$B1hK56o=rDmW3eyz30vcIg}{dTLfL~dL?r1nYs +6#`!$m5O4SX^rEc0r7CNE7jJ;xSFMj{+e8%3Ynq@0q-iowVs!3kHkXE8lx3{vq?Y=ABebn2*=S-DL*) +MFim63b-Vp*#-t7Mg=vTQ_M@*)urnW!46O3mEzi}IcqGQY`IVlCdN^+J}M7R2nM7WGA`8(tLViCZ~}y +H>e?a@wGH=$kjcod0&7{(AnOo0!=RMun|m?q%Jyyb$HRm#^EhgeqxU=Ausf_nmIZg6nR#ThXLz{#DiK +T9){GFKTV{kUCZRAzjvdEjFsYBXP2>sM)DEa{}p0^K6o)B^IXXl-9j`@ru1ujnGV$3@2XVAV0BXTV}? +H3BCVVF0p?Lvx#EM=5snoE|$D4n$#L(GM&#sfxJ9wn0Npc*g`-?u)ij+{~R-|ShEtfIMS?Cgt*~b)+o +mH9bVvcnicX+uxPX54b-94>?o~jv6K%-%o!aA#A3}Vcnjd`C}AH~LJI=y4$PX`LYI02{Tl+@$YzBz0A +eND8mE()W}B5P1mjtz+OokB7$6zXk5?)qK)(r{xM7)su|>`nTlVI9+pJUxRh$WWhLWQSoV08Vof3AjB +uxm;0`Dwfxe^wC*%q*D_qK#LiK-<@d^@?(1T@3V``v!5@|Q_IU6cwdWV +(t!~NoWl$S^kix{kY2w(e|!G^{rNwfWO{ae0bwlXO;b;T(?kS8ei1e7;lQF86e@+|6U@R=y;5zFr>z! +4YC4}zrtJ9dY@t*!d-;OlpD2pntOV2}FPv4d0}MQ$4`mCvsCm5w)dICi+PcK#pjs~4#(K)bAm-P+%-N ++Z1^a+KHCu6Qxuh$RNnSWWi$rTZMtt|4yulNK`f%np;72*}LfT2G*wG +l`S#sb~h4P-|I$46s^`5alqBeCOSD+ui;yg)>)1vpOhz;+O|Xu-Z?yG~=`eTHZ-{vjP{8>Qt#>fvK(< +j{~iWX33R-EQ-_OA+3UIE`wXs%;+eR>FSvfLfNB+0m;ao!a+Y7JPw#w2BWYyPs=f3ukq4`9e22wq&(H +=w>#IEZE+WW3q;B*Vxdp#KDnGD1|NHL^=#ixaKecr|8s3BiL;rnvD?UBjg0vEZ(kVlTrKCqG@X?1BW3 +koOOYOsTX3!?addr?@3*dmC`IYVH_J6Wtev?9K=b$v{95m +6R(7oM}00)xR^3^8ZnPD&&V~fYFKgiBYB7M62uxSqO+c#E+|HqO}r7NJ=~KObRYXblsU|S!&2QYfdW& +VvSsuQ`8L^UUdWo?({D=KPragtfJS3^|Bwg7JNN@fc`p0`aaVo@N_*9uMSBhZs7iY}IOGHJG#p)ZtTe +@F`9P`sE0LLs4YrQTCQ9~pa=?Q$8->)2_A(s^R6m*LXwj2d?#Hp6whOqvpSGWo`Gd^@x9)_1*~l)$^d +`n-LMj9TKY|F(HwM+{bC?ky0~cV_@(oeQ0WML84-Boo)D21D)Y+lLN>TlOf)cdo|G@>?$8+I;Q_PYd< +dmU(9I_pQKUO&=T+$b2z7tzbyOH)bIz&T#e})*vU`~Q?P5R4~oBwg-&(4yr2gi6s(|hE4)oAdgUqdDx +yxM=@3Z(lg9z>S*&%V5TVY6n?Fx!vmJ+E!78Mvu$wrNnzM@TwAJ9%p@nFOqQitutKkmt^ +C4@H><&()&U4#Oe}PxgAM!~U{mg3%ymN6IpabmJjR5qr|2&;nb}QZ%8VbmtVPx+YJ<%zT#I8OR6aoHe +JF2BRg2t=od~$}7wzE0S+tQc|>$|H@?~W4oNq98;qe>hoS6zO3{#^oFh>W*71gm6qqyt?Cs@bc=i53)Nh(GpvNC6j=}mdGCbq_;x|6wH80TBBun|Y8K5%{cihgX(T$4)G?4;H8NudynCy+8~q6U^ZuXDEM ++0{+(GlnaVK@=L2yMgcX>~CL8*@5gvmXR${Kd#PZRwgJrCO=??Go#P^i|HQ;VE`OsE{pt&TbH(zE?S2I(&zS$%+>-v^{V)W0x4_P +$Xi%Yf}%n@6aWAK2mnY{22-x%FZde+003VG0018V003}la4%nJZggdGZeeUMY;R*>bZKvHb1 +!0Hb7d}Yd3{t}Zxb;Pe6Q61u)02Qfz|=>fK&-Y1wmAZ2Bob4A#`%?_7W4vKH0uB0rkH#zK`ZZlsrWC* +*iNsvum}itP51uCJ)wXe|)6cFLt~1D)4r*@w@BXno`XkzJyJs47SHdrL#+_R3$QH0y`tMsDsj;x|_19 +3g!Ic7sX1sP)p5b)yKHn7{F(>DcEJ(^O*01E~-kaTn1$gy;qJ=kW-8dx7VXd_U_`{$5HnFazd~B+@kh +?$z?w04ErHp%4_EsHGSEHIlf9yGJ=8UDY2M|4)mpXl@Ow_c=0?D2dP|}D*1CS{@f=5`YdHgpQ13)r +Gu-bJt)SzJgXd+YJFIPDjzjD9dvi%Z;JpKtrt-{@yjZc#?uaBM*7HaOOxHP`|Lx7mUHKR0YpKC+~PeR2)Kbkcqa=o0j#=RNE0tXn` +TD!Ojp&>S{SnGK9Fa)}N_H3e%9TpW;uOuSYhO>O|#Rz(|IlXMI!oMQ%ojESITKyzuJ1!-$$xHyd=Fi5 +m*+A?emL&xRgYkEdI)4R2#SG01rVy8Et>8)P%@6aWAK2mnY{22;ZYzN;w +$003A9001HY003}la4%nJZggdGZeeUMZDn*}WMOn+FJE72ZfSI1UoLQYt(3uz(=ZT*@0I!vqh9Dr-8? +`gI3N%g1Q%{ql}w#UW7e@F+gTKzp0VAe-gGNK+MXiM%>U2KXScDY13&gspFwuLu^zrk=XdbE_tI!o>( +zSIgrwqxj;5TC{QZgZtUL`NZx>HK>t|_=?nwJkR+gw#S;BibtXIIl&9ZE>%K};Te10Zh*3{sjd`WlW_ +s)N{M5?S8cE`}!dc~q}M|}GLj~|xv>@UKb%@^|ETY^g^Sn4e~PQhRSjj=H-25b2cGT|Z^2bCYKbX?~L +s~w<*%m<`)9wF#d6>zkA=U(gwI`UYf$2-I!xBPeFS3ZgOWX(4#xIaqW7=Dhmv?RQ-Oz0001RX>c! +JX>N37a&BR4FKuOXVPs)+VJ~7~b7d}YdDU5KZyPrj{T?9yflxN6w2)`}Wr5a3iq*z))>}UW$4SvOyd! +Fck{ENyS&}11O|!p!?{PWj2l9{$u*~r=!z%WB=~05`ra`D_OmemU78-`Fv!VrVGKEk5+}h9&=;MU +(a$xp`v{JAU)>j<%?3Loaa%zVKP-Z%4u>D!C=!})Q1GQF4_OfDwz>EvWus(0Y_ebA!&{E-TtQZ&U>YiFl +X@A-x0aD2v15)L*t6?_rr*DFKFD{^smaJElQ)P|9iExF`~N!CoW2vy?Cwm)YViJu8-g2CuahAXAAM6v|Sn&x6lC|PpB%+^>2VM*OJ@+ip@mnnb(+q|kb+^) +SS^Es*oxgE{mCsaJ6#D)z=^La#$mNlT;4F6GLw%I}no)E$1^_biM8kLq=TvhzK_oZxqU+aS)qW&Kv)aUOX6sMIn;&xvcwENxQ=}p4Br#_*lK|cAAn!og9Tt!Mcd3Ux +ziU(e-7L>dJ-P^Hzz44scLPJ_$ju5`wO^-FjbR+XxrU=<8rjw<>iRicngG2#{-K7{`a)TV|Hm^hHZ)k +&`1Sl(*3n&Dd0HT+sv1Om_@HP%Ns`s3lCYNzxumaiVmKKl^HTE+F +Gf#m>ygHc?XzaNBKDw*NZBQo8VR^|YLnmpp;UNTT1Qf$Pjc#KXz9D=0=JTD(0Q3MW*Bm8*-&4gr$zY% +1obu2nx%8#4by!2-M;io;t7C@qPn_cb)Ua?E)i2v4($$ll8kDGyYTZplC;n&K6(>P+wQRQutR8{6JN6 +Mu12%=(Pw)1QP7lwbXXx}n?0BaW-@!>gI%)(mR|@QfUOhnXwp6Ja8-@hkfKAZ;9oc+H?k&SE}^4`Sj&;JabKU<&5xAjOr}gGysSVl6n=(j5E@l|;pLnUq+vK}XHVny=@F^Z +zMj}{tt74~0l?rBNm%{fkrp6fXxY}Fi*|yf)ym``ruS4C6z%|=5meeBfNV(%VTXw@p2l_rcr1_YJ;c! +%r0@pM89O!)wye?!uK}Goubx8&cOAlP+_Xz?`LE6&bZ*sQm@j!PFj;JgDHhJxK9wbdA_-}U*fo@IT4EcK55 +^C4d9lE$Z$97A52r;ZAGBZ?2-iGWHwL|MHq4G`%Dvl=Zvqwsd`4l;HATgTPdX +V044%N~F-Cx`^e?!GEdEg|c{g(71=d-jKk4S>)t~;!p<~>mmo=MA&b-U|w`+dw84dQWJ{|-j(A>Xu%T +FYdM?F$X~zxE@}gm8~uH;$9e;)9c9!e>z+YI5B@#8ob3JU+r96veha^pKcjf>>d)cb=udrf4Jvy7u(A +|%W!``kNAFJ0U=W(T?+!kl@da##OmbJ5{ax^TXEpo`j#g5+`>=ATZbuQ!Z^PGS)TiOYwX9Z#ORAnVqu +p27|F|qVfw1LFcp-OEx5~6m-qZlDMKmtYaBzy16x)3_d_Y88=%T5Dx#szP{f$>31dT6Z8jHD1M3J&Mj +CtlZ)M`Qd>_aL{a~Td`x36fIrEXQa>P|hbhfyN1$f)Im+QdHaaGJHObhLE3kYPzGMq`412tBfSi)lJ` +4)iW5w8rKNy&o$SFdoN^-EC9-F4_^py8<{8FKHO6(Y&~97Y9$1Fu2(d?FcGGH~wz8;YxQm+Bvnyr(gr +`2OzQX8~jSue;FHA?1FUtU0SiS-F^ETfzZ3PW%e-D;-*F*gVpYzFnM)F*4KU->z2)^=#(hL7^c3Zp)l +Ny0jbM=(FVa?Q8pKe)JV#%^XqpnNlB#Ss7VbZ4k_~7bHA^`>qaU= +SWS7A)=X=vS5lyNRwO+B*I+eX#*ujG!sn^JypgK5jx27hBhSw;6__IH +E+-d@cbnK;%>R8RU59iO16z(4~U_r$=FgIGPs^(8G1j<63wb +|MbH74u~X9MD*5pjP*^Yzfgik?TGxTb_=Pm^!LODJM8r-OgG4l|^J|hnhV{koo0u0yK4#K-&NDH-*Av*fs*gbmP+JxEqEA0IHj78!LhsoIxv_2zT +f;ZIWdQ;%c3Ybwe0e8UrSX~h97q=Te)rqwHOSY1Sm`BwaVD3Bw4H%%PGQ!^m&;%mJ2dHK6q`Vis(csCLnmuv&uBbeh5@-)EM9R@5K2NkQd5)f@I}Jx7`eMc;JZ +649k^zLI%&rYKes!siDL(GY^U=V&~;x#(3;Rap*0m__L#65OG01TE|&yN +1?De!l)weT`)jZ-nuhBVZbIPFt(rEUGAO=)RA?)rN&J;G(yPnbs2NZWeZIPd;W%jr=WYUz{FKWf^_am +Srl0pq-~%9u(2xef&$3^Jqu2g5))%$54xpXq#=}z`_qq8eWn~D>@EdGHTY5flz`5#A>-`h09!c8X^ +TY>e9C43Y^`;c5Mn06>Uu#*|e26@nc9%Be-XF)L54HLPE)>8`**!s|Zn!griG>}$>%GxMK-Zk|1zTdtOjHouv+K^Hnv2ICPI-gRd?&!n(|vLmi;hK&AI +;zzar0Z>Z=1QY-O00;m`Rt8fb57#0+0000%0000W0001RX>c!JX>N37a&BR4FKusRWo&aVUtei%X>?y +-E^v8MQc`kMC`e4sPE1c#D9K1HQAp0uD@n}ED^|$OPf5)wh6om=78Ioxr{FPD;%5-% +43P)h>@6aWAK2mnY{22;6sevW+s007Yg001EX003}la4%nJZggdGZeeUMZEs{{Y;!MTVQyq;WMOn=E^ +v9RQp;|`AP~G;@;_|3#Fi)@5Y;&}r>c)buT_OmypB*XSU{piQU1N)_z_30aw!K`v^%@Q43#xCG_qm*f +Vwo6HDa?9TdB9~fYwQ)LDr429vWdCf^e6BciA*alWOQ8wMVOkVzntzIi{rE_4v0C&(`j}45b%DU;K*4 +XW_71n>viccljgRQE9SK!Z~=+&I_f44;0oym$DQdvpH10OcMx;5b}wQMTgou5YQMOA}JJ*P2Ye(Wh}_ +6-fRHJ*)inYDdQdIP{0?_qDDf2bZW#fzL|XYDV}@Q&b~d=yY1hgUq`FeYqTgE$@6aWAK2mnY{22%#ppv$@j002@M001HY003}la4%nJZggdGZeeUMZEs{{Y;!MUX>w&_bYFFH +Y%XwlwHVuO+cxyQK>mRf`e1d6(Y_3r0e3Z)Y$(unMd0LNuBR{(9dpseBWbr@kbmDfq(qUF?OfLQA)yY +>{l-H^QA9qUekMP?c}=1yI(os&RV@_xTh^8R5X>&uqA2*)a7Bd#;6l_TQQH--u1SM1skR9@;h9Rvd&L +C3Dp*4PSSemtv`ENwyJF_=1r-T7uQ +j7P?#6`&i+oGnOt&D6kbsvOI;Lv5_`>HU;<%+m49F10Hl;sF95s?1NpO>I&i +=ljFDUMxy~F&hJc-4K3D;=$yzwE+UC7p;b;cOz_3lO2?F8L!E9&&KD@3RJ9_!0<8@ce}bvCs9K#2+(; +Rdd?d +?D{A2sein`Pq&P4CA#73`lin2|qk(5EBc`#R0?fOlhA!!5S4F+^Nyh7S+IBDw{BccffP1>WBsOIkf$8E8{XG%lIFp1V%-!r3A?5nb> +CqL)^HI(YT!<|`G~5K+i(9X3DTyPous-Z0&XAWO4ioueyJnn@VhHK0cD?ahd6bd+zv4veyqG~|kvauD +lOcGbRf8+LQBG~z2-VwpsaJDU1LdjP~$nmSIFtXL%7Cy<%a>_Bj=lt9Gua043vlURd-ZJ-6#l~c!n+E +%hhBI{!FzbKk!a)3<7Ca8Eca|p%(Cj1THeM9tt8Uh0<>lZ+_>mKJod(Ak2-AAJ>-bL7Sa6|u2XG6)2amwVkFDT!sz){^Gp%E0t-!o^m5bLDy-m)Wr?R@ +aa;IW)5oY_X?s;&!Q^cyYYy@6|~0KZ*6R{+}^=d*O@R{4InMQ=c0TZG8Y++*#aI4H316m_eQ?`4>m(4 +Zy@+7TL@uP^sAC&!aEJCb%vjo2-gwJt*=}ywXGxM`Lz}~SyBB6huM*nbyYC%0mtVKysVS}LmH=RRvR +wrs$>1(%r|()`oo}#B +wp2(-x1DJI04t61cZF|B>y`tYf#5cM4lqMw}l@!$W3EREiCVJTFP0?-x3q}oht4D`5J`P`J +`j2F@J_vu#IWlw(>{foRpPcN+5sN+?!S}A+z0KwUOeW^#&&(+a)EJf=JKu +`9SUym_)7LvgPe6;9B00HThtn2=Z<~_0qFy>9$(r`*30eyJecZ$?9>_34~v@Sav0|XQR000O8NLB_@J+r5MdjkLfQ3?P6CIA2caA|NaUukZ1WpZv|Y% +gtZWMyn~FJ^CYZDDj@V{dMBa&K%daCxm(O>g5i5WNS;f0(KV%c!hkuL80tuxQX;+CvXL1OdTFw9NAnj_0e^O+tdY6^-d+WCdi_=$F&>{v_Yvo^_N_Bt$+Oh2>H2i+MZYR8v&%IH-4?-2FXU`okJrh%YRCpfJd9VfQ(1t65*|XN +#cx`Z{66U>+&oCfW-E@8~ZjCWO@ga%p4wUdg05i(MLE{L0~tK?O?(bQRc#f3T+L{ezu~V)CAtcTBUXF +^?+6U{HuyQbKH39yz4m8TOTaZ##`Ga+x+upBQ&UIF-l`Hv^mb=rZQ5)E<{m<2l!0o+>;dNg_A8`m}|c +o9S%y;%S?zgzA6dl3tOUg|=xZ9d>REW{e(}RCUHIdwt7a-wJqr+h*m^QY)ruF!?Jqx)8+$sVVoDE|Zj +>QT~N9NUCm;4z`)SIPj3rDB7NSsRhpw-8bm-XQ_Lvf=30AbR4(}PF*t<>Nt#)%jun|0)miVpc^w>c +{nsXQjbY89NYvfa+u-B{W`!)_NmJ?(95YL52TUODve37BplYOc&us71C79=S(Gf}K{BZ;H+0o~NBg*i +>9r)h1K~E3Df#mTRq1x_nhfL%=?~lTDM4@GF4^b+C3<@lUBtgWo1W_z6DUr_w*d-yB)n6OU#SMjspzj +?xg2g+}>Wx(4tW-3uMUOqU)a7}S;z`L&=FkJGlP0uZ|$TmO>lF>$fF=kw4zp>8j0gh3K6xd%`17cf7H +TUa%}TFUgen!geF{He^t&-PN%#<*M(SfppXom+)31jo)Eu@U3jI8K+zb~GH@CicYi7a+DtG8=raiCMp +9^Q-7>?`q86k|s)_r{T1G*VcnFKu +c!JX>N37a&BR4FKusRWo&aVX>Md?crI{xjZ@2R+b|Hk8}L6Y+=C>*k?#iFdoG1hh4Xy;-l~Jb3LDdJd4HRJH~{Bo(c3Z1*RO( +zK58?yX}zxHCarcyAFkm1EB9?u!1{LWvLu0MJ_NY1siy-q?~FAl}C1=%MsaKbf^yJaUX4o3;61(1}mA +wAxTxL4?Asr_2a11;~-krTUM$jbi*?Dz6Rp7ggeOTrh$jA@GW>;pY>>rnChlIku?{0!rKb1^di4E=cT +D~b((q3rsyo!5;TXL8{l~c0-jECtcH+O)NJa*kgD54j??-BDqm;J8TAF8j3tn-)W#47*)n4`icWhlvT +qm(`@GN>Df11iC)UnUPTlUekC#-j*n{B1*v`oOqnH4K2mEAAUb{R8L3+0bkJ%_$Wb|y^#08!9mwZK$M +&UY9v%#IT_KtMZ`kjT!6z-f}+2d+H#UnX9TS5LWoRE;mOxl;=1A++ +DlerC>2+1zb>(IO9KQH0000807zB_Q`z4?+ZGJ +~0P`mR02}}S0B~t=FJEbHbY*gGVQepLZ)9a`b1!UZZfh=ZdBs|7bKAHP{+^lq2b7scR4cQwbLrfvReM +*TugP4_@z~C^9oJ=G5|VhP2nGNxtEvBccNZWDQk3lb(t7S35?m}6&%R>eY*k8S*t{wdBW13iKg;Y$E* +FceSTygl{ZvYw-Iv@fn-L|N$4cdSb`zCcX&9U0c-8`z$dN_CC|_` +;@OldRdjZn6M9}$z;Lv33CG<3wVG2%<(L5|D_Nnmx=#T+#7fu?^{u(B5wrzQEdL +gwMdWU3V;?rW%pUJXEKP=OhvuLMv{1_EPS@Q1QpJ^+L>Ar2l!@1D(<61=D7g7Q4MgHKfOQW3xUkgfP` +YR+LEG$Fz;zBjN<|{9miv&d;ToG_;_~q;r#OC`}pm57jI84U{V<9B|i)%4F5ek`pP}M{ht^y +csgpH}Icz%fj6t~J6%w+bHiO{$8o)S}h45zg_T#aD$piGAJw8g +kz3(Zzfxrfom#V5M$L!!wG;``fhN`SzrZAbW?B?MYlXGU40%X^+Qi*^Nh^~+_BU!jq0Hh1W^-8;skbrw0Y`&fqc3eOv4!dd5yH}i;hnh<>adq!e)h0pYeiGe|g2Z2T5XeTTqm33DerUcy4Slo<;s2CZaF6y!(l;A&{amVvY +)HV_jE_P1P;z0trXavtiA59l}l)&^0F?Z7<7qNHqof~DqyvwVQH8^N!rIL49irX|Ov0ln5Pf@kVP^?O +`sf~_Zh#p*|k8h5^SQT4Ig*t`D6-O6kyhw?MGm(Tww^cXfvEQ+I&e3AIK?o}h%aHzr^AibH0f}gp6+L +-&{J=C2L^bZK;R&`R?trbWP;loc!jssw5RvR@#LhNn?-=DoM!D8fJc-#t6hiSl-+I>OOw6f9&593ig9U&XyHX3aU;bq?53}G +r&u+f(X7LsDWJdD3Qj3OJp!fl5es_(OvJ}3lgt&O&Hj-!vLn+}e +Tn2vYEwEOlm!~kCnp=Y-db?mXtDv_PFhpmVF0NYOOPlX$1Nmn~>nN2^#J|{8f#6# +0{7h3MrO|j=RJV6a$TTdDA!9nL8?^9=I?cKU;79C$5UhfZ8G1eopGT>2sv4V`hi@&vrN#mF(_k?|}gx +Y{#xXE=y@=$yPdzDv(Em^|7D~v`hFtJ!H8hIgpQ*D6Bq69Ar)#&QQ%WEK+g;)Q2dHtL5XfL83t9I+1W +3dMUSZvaS-M$!+7F;DuFVk$rSDHVZxVlw~8zlN@p=4Ethwc7#b(2Cq$6nFqF(7=}Cm}M({Fg$rOTzyh +M%aVgg2bCV3cn;vnZfbpVGOwNgZZn2Z?1nkZi*K7br$?5h<=t?5pFfs#!mQsXrdUu6g(&TBUtUh$%o` +w4hN^}s$|XHcu6l{M>Qt87<@reM +crP-x?vk)LLOkGRjY!P5qK(UbVOwW*|%+GdqZ*R!LhzIa4_INOW-Nee=HLFu{jOY~0O18s32uU +p~p=+|!pNJ3=+L=hZVV>_bv%VLj!Pugnt^a$k)hN;J$DDL|Ho&_3@8)EL{B^ubBTsx^J%wDs2OvXXxrD5iuO?Nj3giScWTM_?S=1Lu`P=OVx=Wp +ZORS1EvT*1#R^4ih8Txf5B~0n=_>bq2=5{t(t{_r-EcIdm3Af2kGKc_T?lPv&8NL+?k_`hhPi~`HzPdw7?LF5G#ui-;46tEC)J^lwtR>#N9JC#0*CfD1?V8 +4Ub}RCY?am_yYCj@|X{K$@sBTQUE*O;fI!lGZ{$eesu^n#9(U(4#i!5RMPG+gbHV_nq*e?O0uz6@~8* +c&vXOLpt0!B(|pC+yc8;WQVv4GDGU*61)0(Pfa01q!=AQ;=@r9GXYX!9KA0X~JehfN#T$HD`qZa_S)l +meMy*VH;prNEZ664Xj87J6C}9!BhYaC*W7@(At(&o!_Kw_$2li)2}33H2k_O2UFV!^;W|mZnfGu&u_p +Xb7O4&jpG==Oz3XvlPU1dvKay{~3J_eA5ZXvA<28imkhb9R^OVvzwgydW(uH4*W!|c+FhlZ{X)jU|J>2YUSK&1AvJYi2JHE*v-9=or{4|aljrh0q4%G1tC%reXv^D9wo8F?P0Hh!$OIv&A9 +{k(YS-yzR>4!^}pu>vv4= +js>Gdzxlho%N^*7BI&{e?81=e}n&$hlp-j@ZdPR2fNzW|yK-t)w>f{YG=NLerYS2`~|*VV^#A>iSQgP +=aPn3Srci)lQXYA+FI*jqyUFfpCXwD%V9Wc?!}wtFvHv2EdK;2$0X-!UTB43P>ylUqB9L=@fwIR-v$f +3yLie21V+bf&3ZO8X|H~ODL|#wX?%A)?1tktjEKA@R0(0_~-)|3Rb*myVIqRc%wskpeoCp8rA3+1%a= +P^J|EN97MFO-g#chO4~R|#@MiH0>%Wh&>AAhQc6wE1X&BQ`|U)W(-b3=TUHR}W$HDUuwjN-BusMH5ls!6DeCc2j2!%IUgKecM^S)fDgJ5eE!2R`})<_-%P-W-5uEyJpj~?b?cO^KsHDU6 +6^~E>yp&dBzbxTNF#L6aP?{$ubt7+ApMR%>(H%P({&Fx=okSB?>_g|wtvbIrQ)Vq +guy$sBLo87sa+YS|5^u={uQ_ZL+E!_?QNy9roRFOX6OgL?KH8Va=~(*DU2|Mi2BUjZ8V(2gmv>90Rba +}$^+sN+DkNewD0lhEZ*+wD)2u9*KnR{=efuh(jSMMC?&zJe-s;f>fe<0`1|$fE}Jd7Z@Mzx3!VDl_|!_svzv-!^|l7v-vo=@Knm@q3;9Tg9kE^v8eP{B$AF%Z2k>^}_kP=&fZcq=G~R0L6koff7mQ!Q)ukhkwmX5PHXWKvi6A%cAO#DL5}SrpQ+w_?D07IY591`n(ZZHb$@8lE6*$Wu +fIVMlRiX%C?T+@W>2c66)<^cuvFji7=T`jQziQC%64o)`1E4vL%pTTJjxh@1JkJ45s~Oqyi10rpOtxinf;n)F?Q8ANnYlQh?RL{GzTPo +iyXNFkphy3_Xfr*c+=1U?lkD6d7{f6V}KC+D1|mqD=vGAr5^oitSLz|DX|G4gsXb>H{~_{0`2RGHhFR +LAY(xawn@EWldcwvEEACo)1($Os<>#{gzA?z!FdCA)hf_vKrGRw=tbFJmoIn@&=xRKZba;(-aeFkAlY +f;3n$>8tKJW(hC~t#b4438tK_+67yA8KTt~p1QY-O00;m`Rt8fFHb2)?1^@u~5dZ)r0001RX>c!JX>N +37a&BR4FKusRWo&aVb7f(2V`yJ{QyPmc#jPMiyz@%CUcL~GX;dM!`cvkOsO8~i +)xh6IXkMFAYR6Sm%7=dM)%NPwN#S6D7p8{qy>KO5?&;Vk1jy}=xGiGHe3ZIM4S2-!j0R(w +U;7Q9adKCFa`pdAk$is2+d`WU=PYNxZW7ksHqPi|jE6-__;hD{5@yE|iAzIgI;iY4~l2BR*uS`R4I0> +9)%CW*UYqja%67z<+pphr4;?F#H+IW}eaPGrw4d8OEnp-!^*esw(M{u*7UjE16U@d)R%D7U>H7l%yH; +cGHPoZ5lybWs4L*zd=Z*P+CgQ4T^OZMysHcJ4RoevxLn9%bkIKf1U9y6;?vhYl-5yO4BC=m&`kAq_$` +3bF0*-xF)WpW12eqA%sFkv@cEgR3A7uGv=uYAS6WT+ihi!EEpREY>t(omx%(geX%caj1lSY>L-N@d}v +v0Kir4fA%JI-4qjcF~kft?8G109r0%os;1}`+O|fowN=)!PC9(E93?Kphnb6TA-!3lqXKwyXerf;Jw; +Z9{$srCHvGl4@XlyE8tM-!9{@HY<)$r1}x}DKtr%EoF^`<0Hx0-(}D8uCP^7%8Hpe_()1YwLf)){437 +aIi;MY}`En#hPb2Sx23R?)*#)gGk2gTIahxQ01bNY`m{LzquYM#e8YBDwC752l$D8z +Cm|FRz(w#7fIDqbN7*_t)>d{Bzy8eT?HgK9_(FRA}%#`&69!FvfMeHt +uif3Oa3663HU-H5JYBPLz05IxcY6YULNl_D>jrYATbBY|xrC+KX!LK-8hv?MQ$SYoterND`BnJK(!P7hFZ@3 +jBpkCwI+xSBs>DjFF?*pD~AyORW(Dm7mgvPlBz`84KH$5*=Dfwrb6@moHv^|7{k3e)sCdE8bfujdbAe +DP2O}i>-^~wm%`fAwIG+1K{AH3%km6m^-XcPEIM34~1+aUr|P&r%?{esBDr@iGV0nJVmo#i?IG@Zhc? +LSY;taXJ5M+l2q2NKWg?U1tQ}GU}>$nQzeaZ(NjLpCenB^jV21Q@3sFzLn!TDetsGIX=sA9MS`9F`g_ +hJS4^M)H|PA5L6e+tW4Syq*Ym;2;pV`X30sMGJ7!IM8$6jt`(*teu}jk2NBw7+(qHH}^FNxM)!_VcOt +J$QBbLo54SVT-l0n0HlN( +y+hW@baiyau;M*NWklK&fwOe~YVeAAsTPhfODB1ZeMKxNV<`FSKNns{=WI +?hLMLN0;hF8inOJ-cQ*tfoC36-PJ5!o7GBz7qXg20X6PHaXQ= +`aJ8&BW3M$xwA)k>VMR>Z2gJ3q7;9P(k5SL0|0Tdki>rww0wteJ=i@$7y#&!yxT!hBppI3Ao=bPST}t +h4LNs7e!D8b!j?^V3O?uwB8|zw0Oz+BAe)wU>!-yp(2;!yX`Q)V~#+&~AhC$>|U{`YZIx+fvI8LnN${ +q>?W6D2JO9KQH0000807zB_Q&wWVuiF9u0D}hr04D$d0B~t=FJEbHbY*gGVQepLZ)9a`b1!pcY-M9~X +>V>{aB^j4b1rasbyZDo+b|5h2grXQvO`)R^?(68EIZ0cT4YHR<)-=fqbxhIoy3R0 +miWj=@{uXMtsv~3GOwU2XMKS0E+}h+o=&EdGG067vRs*fo=>yN7N{vJt*=DLr8U7@-C>`a)BAK79ZV- +u3nKW58fEmm@>rrrBaxQV3Gg$U&C<)Gk-|}}ML>eEL6E`}s^Cwc4i4^!2j&2U-5G5~!3iWUi~(gR2(4 +pMrP4@QFH(BVbJ8|Yp2ICX&)L@Q^1U!yEanSnzxg-kaGqleLW&o|tmlj5Z1Btz!+fob@cRf-uf}@JJ^ +rheN6OtkY^<5TbX-2b1Ahi)Ep=TWX8?UdP+%!)9pFvq8X;Jy#Fk4^dju46!)4OC^F)fP#q>xO=m~Zk< +U)FAJ$0#oU~Cfuiwu5k6rX6a?5XD4&R5uoz`>=WSa^=RMh?vc@!e@9Rb<@uDb-rCG!`6FF{uq|cktxU +I*pNyVGd2R5|9CHZ#YSAiZUwX`PH$k-1xFr<$hh3u(np8T#mYythmchoFVqChpk*5WdA(S@CzfqSxHQd}a-^-&P*6cl(jJqn%4iVV(Up~w_F~w);gcsGQSbth6Gy}nX^idFkI%!YiBOHgmQXw!XY2q-ZX(HR<%TD-=EB@S9Qy*{zd4sl>J03QUn~?I +n{Faw}e$%11~YI-WH0i?hMQbXXUCf65g2-YgK7^OjCC7`47DDRWFWadfRN7O<|HaaHlPF&+jh6mgz!5 +da2v9rt*A8uXHqdeM_fTp+e`BB-?L|R2>qTo_RBTd^1DKs@BGVZSCz%VR +Qwqt))7G?ACvm)+O+zc2X-|Ch*kMrMSK6ZEIPx^i`-_8&>n|g&)zCIexSk|0jNot)+6pAQWqyEG^4)R +dXi~vXrh(L(ijdscmX{7kL7&!dT1!{R^EsohqGc_Xcn!ZFYfD+El!h|A;h1p}cNyvXWbiX&Q|8*t(Ne +TUrKu?(Xiom(MEWwQWl7gavmJP#awf<0O961#svb!KY9gfqy|O0TQh}FtXAi722SAM-_grtm7rJn(MP +FHOFlVm$4vv_nM}*)Xt_U>|oz5d5iQYk^Ho7qkY{afeAlI8!aFurKp5*oQ^d5s>Tgsf>rD=qF-5_2Tf +m*?cAT{W&QcGms}LMAE~5E)Ai_MMOg$8Ln_-rdwY`N-a7OAj5Pd#{D$^%Hi4HH>>f`h0mj&pFH!W)Ff +n<76WPb+8~E^x|GW)|xd@7$ZOsj@!ks=qiLpsh2#1nEaTyQ6;J2c~MxMCIPN+Nd8L>PCov28l+yOTNh +U$Ss>t&lY#$fVP>`qh+zTl!cON+Q?S(Y(gAyqyB&A5VtfI}loOjC8GaLFHSt;JYUF?hlZ#g+6Z5;~VH +L0yE8epDK{q@5p{j$MRr7ZOw2jaigB!wPEIoY$!$9u(;QxWJJph-lP>1tYL#EfPb04}-?x`r7WnfWWY +u5D+*GjTNvK?8x9NvdY4c`|YRauNxMlJ)N*A5RWJRDxehV=uYrs0v{7P>`!n$2}SMYRU#+zU3FT0Lnt +0!4u|1%9Dd$zS>3K|+-%nyC>oXdEpkGe=P@mWhdz`4FOjI0?%^+|QSl^5-xH_D@73kGJ!2W5Vy1yu+= +|$8YsC_utX$d}J+hdEMFJ*0z}LbIbxAv;k0|YbLo5%u^~7)!COm$J{wua(e`~4M6|dKLn#nZAe*5ux6 +Zw0lNRsGI|0dtWB0-yYVPZWtlHJ%;rZ!>1MykL8(orQd@vCM|2z0W +OT(O+!i)zB-b`(A%5SR}DneZzf!-+l8t|G|7;c_wi!=_QMNj?Bc)c*ecWc$%ZdCKT$R`az_FuCGb;&n +|J2>Uarx{QL+%ef$~xC79x4n?qJ&N>iK|0{e~yTZ>HM%>)VO +jlb6%Wj47xER2o!R?E9b6Ft}Z0zC_(D0W%=o%ZGwqQ}#|n+)ZvVRrrhWNtB(_AajU1yd^5-B7p-22`W +Fg=$S^ohbY`Q`64xF5Gcn*Hg>en|}dNO9KQH0000807zB_Q};K1R_Fu(05J{#0384T0B~t=FJEbHbY* +gGVQepLZ)9a`b1!#jWo2wGaCyyFYm3`P6#Z`Se;Aa4<<^#y(m*lIE-Br>LTQ(6NI&du#8?{H)5sbzGx +n~N=D+vc89i<7rY#g|Fj#XRbMCn>IS7JlQEFjCVGZN#ej`NAGMNj;iq2`Q_Nf0Buvu}dp7U*%%PewN0L +N0n+R%P8K9B2_vndGdj;$*<*WQ=7l#>%O)u;HuzxBpwn`TB(Xg_p;dYT&BNMq;C)?!6xJJD9Jfubb{r +0#P9*~*Bwq=AYS4QV>*nrV6PBYl&Ecq=g=$AN5kNKcz*Q*-LV(?tp0r#eRDpY(-WOWU!PB>tyLI0$!a +I_^z1ghc|X4+g>!dW9-KvO&R4R1D06PzQ|s$Cy5Hn~-bN|-oXa9p)31PO)`2LZcNf<`{`%X6KbzJ8rX +pi8c$7AdLnHER<|rWaOJ=N|viTYNMHOP%I&=Kg4@=FZLFo+fy(In$S4*{1CXpep$}*+vT71O0r=J1>_ +k=F$B_!H%n{2|`cZU#}u|nC7eFwhdgFp{r4*QDq$%0Sy6`u(|dFIT=&{hqJqo8lyLFCXkEWBz=jx;K1 +3p`qDwJXC32`1A>3#300;Uu2S*knHG-0+Oui%Fg{RQC=}f;e!^X;?51Sh^F`)*dc=QB#^^Bf0>dv2bH +;X_kw{TH4KSMYX;t4<%oq+ZGajg;~6K@$&g`Tqst9<{JbK+0(e42iFEMntjlRlb+bPCNZw*hm4#USDP +}IHk?E!_UNi%O9-B_qXvBBN=S|@yY#+BcBmOBOi;U$1@|+{kID_;P+1HVP3*?tEB2{hUl@qt!;Bqf?B +mq6(*v68n4G=|X&o9ZM*$suYz-;8*5-3}QmloIp^dh)B8?khZg99sK?D5;W8D-6@sZ*F8T~41rl0pNU*Z*jZ!5yc;OgRvefRB4e;?)wCQ0(4<$>`vmj(I61xlwv_lx +iz?BIB~n<@OXG}CXy10a4nopfs-_sUm^U%>%H}Q2QAzjVW&m@Gr-P@R11^gSyE5U%2jOa!3YzbgOgn8` +p!rHs7$8S{h2R?N6Q^*?SL}sJtmQSE{g&|5f-9nv4b%C5=)xl +xeHL!;eD`-7vuoIT9aMtBUtcb8c~eqSa?Z~yNzBYERwyVfO3kTM$V)9L&o9c>1#%KgQd1O4OEPmZOEOc7^?13Il$3b60 +8mQ<1QY-O00;m`Rt8h6YR5gk3;+PRE&u=>0001RX>c!JX>N37a&BR4FK%UYcW-iQFJX0bXfAMhty*7? ++cpw^UtqrjPr;#fPEk|fo))(Uv=sdbDX-~DDtiW*Xq*O%5sH?~9$hx7M +0LncY`n(Kp*ckEfOZ+WZhve30?*-yObxKx)Hmlw}PtBbl-tZp@z+oI%b_3ysX_4i^A&;M1Zy2ROQRloDlKXkM{h({omeinFbdmlvHB_jOgbJ61|w;aWw5L>Vj$!+`iF&BT_qjw8u`@43<{ySzw}1Uaeqos +gRKvT5o~))i9m%ZsfPd!~;a5_SWgwa1iwUza*%uWAKYKooGG7vr~Irk27~%=O7AM=7m|2PBfZ;{n!~1ohCeqrO3UMwWz;Qk$Bz=EcX`1-mAu +pJrFr~=ltQ|JZUhj0En4me{$Tz=Zz&XmXKu7*GdmQrg-K4h_(^_tbZW)aUEOuyBNe!a7T`II57e&MUs +aNh(}3|6yj;6S>v^8=zlv{-*|>K`2P=^Km3IJ?$4$z5g>v8X=*5~n`RDj#5dDqY7dYWtu6t=6R-<^C@ +fGlHRj5zH7v$Xhvxp4S=@`QHr8Y$F7E(0kc~uau5; +-u%n(%QGFlT~EWQa&T^Tc7e{~nDQ7lJ|KkC>9v9dLu$-UfRgwqWMxFXN0y>^A^7CU&9rQ9*Wf&=6>cV +=fB3717L{*Cr)Y+!u9IY#NZrfwM#1K=|5jVLH%J#XZMG_8EJ*9ak$#)VxY58GQ>p+!u(F&|0|0`34LT +F5vgkG6!DDo^6p!ct+_s{9uBygK%7>mQv9)FhSjlzNySFI`QL2E4D9md5fCD_f8<7Sa_Au$o0G^Z{aaSp6U +qxU!z2h5NKO24P>Q~-xo(_fM~Hp_^V0AndbpW+#2nGr~kJHh2S^~fdVGp!($7EVMWY+PCd6M_S*uYxK +=(w88e(lXN$i8Za0z_)zKSU#1>LVijhUN!CsEmgg0P@ZQ1n5l|$t&z!)11wqTW^jD~F%A2ncCK0w^ui +oUHun8-ZjRnxvOx7g=7|796fUBWzmUK%_4K&O)3ZCtxAiA5erbB +Gp)N-@@=wae`D~^Bo)EHOzA>ub*Lc<5Jwf_W_q#(X3}ZYK +BWH5v%$*CJfX~;&VZo>~3&FRFFwFVY&!mBFMtOiuSo=I1AP?u3du(pSBLa

y{BxbzZ&r4ZOVARAgM +P`qWcez&2vFlquodf^BO_LzE@A-Rmm^{NeLEIjC +Ya6~v?~Ljr=YV;OcyADrp)+I;!Dp69lm*ns2r>kMF0!#`vKZZ)d^2TO5}cF?`R9!?>iDbOMCgtP*qMp +*E3x!?%kvoIDo02mMC<5J_Lvr1m2&R?iWV{x%DSl3jaVuLOAu&^tIl)Th*@mUOxi7`j;A$r;`Qt6Cn* +BOLIO%?&$*NkK5u8s-_%oF9%`VYur`X*SqZ%flC!7VwR7ObkVAciUPh0GGZ50G1aIu}PFjs}+Z;JqN# +9oJI;;5Ya%6dO@UjJHp${c;QLLcZCgcW$N)dnVXX^TA|=G*Uxpy@VH4ViL?}8Uel}b9s&C_J!n@WB)17T_+4GqO0M+(Bv)m{fdT{ALE#|SWhi5 +5X7~)%nF3EX;^#5^-w$EAuVaM4P8ro+dt7I4j*vY8F63BtTdd8T5!0;4eS5e +vwf|ACA1%w&K5O|@Ez_%~DnZ|QvVsN(Oox|AiFeOG(HVVvRs8jGMHSHk^qDXY}N6M>h@+&sPk=@UBsV +`S72`hWkcQ|!WapiMj9^Mn0;CqNURj2WPcuO_;hFT5P%(e4K+DR6>!SoB5UdO*k@j^Dn%0ZF9nLvn<_ +Nt@iP+4VJpUo?o0j^o3q>aP5Ki->5EobNyCAUs2AQ{QAZ!(wKQ}1Dz?)MHYT5`84}Z< +cPth7dW3!)vcmF7uabTzX!bI5><&z8Kkt^w~T+ +^m@_}=k$9rebey}XHJZf`{ZTMr%OnA5iF*iE7+XuICl4LdR7 +Zm54AvT!GuI3%@d}aonBdpbc^OCq0@*Puy3USQ-5kCi##3NH)O(~%^+MsfP6ati81e@| +N_X+1<@d&?u%x?1j~vQaH*0zU;S}Jd=4DTPu9yAkD-)%qYomH(8UFyocp5P_y&_4>1y36d6pybfj`=Q +`Y3vUnO3{mXj}2qd==e(AGAsUm+$wod)Q#c?bXi9sm +FUaA|NaUukZ1WpZv|Y%gwQba!uZYcFGAV`ybAaCyB|O>f*b5WO$3|6!mWDy1@Sy)59MNl>G?Bt?^(VF +;8ovS?GJLQ=cgf8QDEbNx~DlE?>h#hEwr<~>qLlKl1c>)%kxszp#;udzcDWKhRvnxSK0uX<4&(bU$7*a^cqtHsh51H6|W +Kk$qHmF4HO8>L@ObNFpQAH3*nje1G>2Yqji$IAv4QFPR{7=qGXG=%HVK43Se$@)Q7G`%8peZku~IP5j +O`wy5-`DzA0;|GUDtsGb0?`mv7`GRwcNGf$xXNRT746fcnX{|o&cR-KBFgOGENFoNeB;=5+Fi8$KQd9 +0&=8vc9aa}+3Jtcdr$D9J4v(Bfn-mK;ACu{I-s!V42*uV+NOy*abMpaQdgcc`v(aKEQ@G$rQ{!#UDk6 +j{74ZX9RGZPuL0ZFPGDsx4?Ttm6`r;6gtWabRbZI7*9lN~%dm6-KtzL$9B4K2QYyb3r)BZ1T6K|jAXFVRq5lk%NiJ~z%l+F(KZ2&rU7VO}*aHHZ44Z4)o<4L +;OUsblFc2udf3g_rb%9BT{YLYV=l}2K&`k~aS+Jbc$T00w(pH}*oym7tWKmtug7fs3}v#kXk+58%`@* +y3wZV$iTW$+u^jz90#;OTr@FL-L=!77tp4m^i95i#3Nn}$72_z^b-nXls8@*L3LU7U;1Qcn^3sZ<29Y +5$}s^405N1Gxm-9!<(fWI9(S86WZHN#KTSL-(|-(+fWS8J`@EQV(RFNaZE_{~${1t)0%Q(3&-l9J8hG +3-o6ni!u1MQ8a?@0!$;vXNc2hCT#?l%_Gx`anaWeDftV#II-OC{smA=0|XQR000O8NLB_ +@It@FJ>;?b;I2Zr`ApigXaA|NaUukZ1WpZv|Y%gwQba!uZYcFJPcW!KNVPr0FdEHoFZ`(Eye-DuFz^N +!8ZK{w0dothxTbeFKi!^B54OkEev_#unWKkpOBwo?)zB^K+C|R+WY)>N)M56AF_xE>4M^W@v+}D+$C0 +X!_DRR&CiZoIzB~yyTne!7^QUCi>+^Dh@4@~ij1pQYq8 +Q;n$AKn}0rJUApPDxNtL^Es7Xmh;7B`w{-%@si0=DeR$OMs3f2!Sub5x2K}6S%F{9q!i=K)`krH%N`v +2<@WOZKH`ekw~qEBJde`WR)?&h#fmvj!crN%gb!Z^h +dK7=Xp)nEYB09J3cDeg5>ZuUuoUsUO7A{#KXLnX`0Hi2Wy&^IEro0vuu +lR)i8SPB!Ow1j`2k2S0~tm%}j>BF>_fU%Vp;Gm*4*N~`r3spJRCZc8R9t<=XT!8??x{}evJ)MT>{)mtz(+S +#XX9SeF7be8+LaPompzkEPJ29Y;tbfpJ_HQ6xgu=c=-nBJC= +9c*^~H8^e*WV@CEsek(vSKXyNn3DD1_;2WUYssA*Di37{e57J4JSgYHMGP!xwurfR^BiUam55@lILFy +yGFR$0dxSa3L1lb*{xtB^a9-#Z9+xr06$w9wt8`Gsi+I)N&rRd=+Ve!=D?z-dj+XR}KW6woMWRUskj_Z2K%wn~^U}69kIJ7I`_$kHAHy8Txl{JDz~jE#(Vx4xxd}t%SHGRGYIN&thz3Ks;v}1_H8#DO|Of +x0`S+0J_n~=9wFR0=gjO#_Du5@J}lBlnk-~#=}WB!$Hh$FqhiFu}hun*Ec;_Mz&5Hcmpnf!^w;@a@b` +xPRa4(wj(|C+$c(tP8^)km*2Dahbb{LZC@LN6hG~d?^=Ju^NyMhKE08SvNQ5&N(>4rC>~QdSV4XQZEq +5j;1019)pWJk#EymAEHm>g2(>8I{)L%v*-i)rN*xMaC46Y|`Vhs$%9c +yG!QE2(D-7;72#&)>7aGmFIFetEUk%q6kuIRpwjo-L$JcM)pq%Szj-6hSNKO_i6V%-FW*)Ij(0eziAhe@76_{@oPQs)&x2f@4 +IHAy02M0k&8Qd>f=8za$FIJhoU4^9WP+UmB7I0eJ|7yHoFrAsRUMh0GGF|}4^y7ASPG&O>psZ}WNnc( +va0q6q3mmQh6A!hm9WUWm(_uS&_(SkIcG2nKX2<5o+ugfncT75xf5Fk_w}t~eRDketGr +*5PNl$0t#DdKLT5b-3i|V6`+6OO~J1qpYy7n_?7k+H4p9ev>o(w~{o&ZI-4#Sc0-62RCXL|&v#)+%$c +cgG7BR(CX7_NVVWRCwCXol;*Kr~#>MKz=TKSDO6?O}8a$)1I9xVD^CWovpoj&;ECH&9Ch1QY-O00;m` +Rt8ga<&<>Q2><|*9smF%0001RX>c!JX>N37a&BR4FK%UYcW-iQFKl6Yd0%&EWo2wGaCx0s-EZ4C5`Qn +y|AEjxth^@+$v*nli_4|yHMpWF(i8_Y2n1T9Z8ox`Bc&$pV*mG>8Ilqy%Wn4akjNJ2lQX~hNEAiy`S( +5ha{ZCj--KfC?(RNDQS|!N>sMLE%QDN@lHEu6^)0V9;#T~(6T1Ce>8@#N)rulYSX4;eW@}aNvZt*OW! +9-OdO#ld9a~8)@|MXR64*~!Db{V`J}9}_w)S*hSFLz%OSy6s_M08A_(mvutQEUvElc4Jec(G$+zI@^) +&4EKaR>K}tTyi0MYT`Z8<~Ub>sBb<)+%A|LH&f?G_9;FUMB3WYkKnUG7$4mUVJg%Bn%#m9DB5pO`3fZ +RZ*+7dE8`5=(G$We0Dp_~aCWqhmCU0sN>r@FlPaCi0LF1vj9`NO}nPuE|r5_T(eQ&)N&=aRq_0U?4PM@XdstFc}E$1q+!A0oK;6uF5@Ii#Fd9pQa|h^ +_o6eFCimL4=yVV@||dT!CRiNubpn0sOoOBW#Cn!saIby4}tYcAQfoa<>1BC@VQX!BH +PEOs0VFET$pg5+4CHU|p+bu(^fvmX!q(M$GTtM&p8ROcRWgU$_Ke4xalRFTnF!^+!ea20d5QRq +bw>A95^NG(y3-OrHZ@vN&UKM~JCCEnej5}-i{`!VBs&4DNF4KV=5FjH6SUQ<8sX0T=fG!YCa#Phx_!2 +MHkN^(3n1wDTo+SVcQKuM(J_UG=L#9^Wcqt1qlCAa(6REv5%e|ZyHfx5b^#thSKr!=q~wp8N@d-`dJ&7Rjs|gr>%t7z>3058^I90Yx899BRw9xhD{%&=u#A>g +#wi=sQoyqi%wAusbnkRdcJe-Mr^gDV*oi>hNrSD9_~wva{U9aNf=>hrsa1Gr>ccuJH&m%l7Q($T?sEw +wh-|^VmYVy(Igi>38j}7#%Ep>jqp6iquzT63jt`2-=^k7mP1`)%O(5u>@2h{&9t+_e6)#;kAq=@g5(X +~<}tfcR2#sHCAxwXORXXI(WMEH`FlDNJh0YB_i7Sto$gEkM?4?T>}Py= +)PkX*B9wQq%v2ytY^e4EmZXyfQ2$5J+@IMmowRdB`8%)8|N)=rkhD9AO;odCn51N-u=$R9HRig@M%`5 +yp-4cz{Ih->Ug$WIi05XYWkkODW2Fck=nB}3Zs@`>*OkXLD62s{Lf)G9h55>e`e$d4Q%1V8}uxvRjD< +sLP_>z|Z1M2E%RL6WNsgS1s$m!UXY^Z`IYph83|pu%&wHNa1YP^L=u=HIDqS{HobE0C7}ZiDnub_0h? +JkaB0$kQ{-@HuslbfP$A=zpw*P8b?usD9*DbIP|1D{*Y_&xf}8xsi=2G4?V5S=Zpb5@gVkp_#Hp2n)C +s58v+ks)Iil5Or8ZpzD&N${`vd`-m4-q)Im;mum^702+(=xz{GZv}46}P;pCkb@P_0h!F(VGxkWyl^nNXXzBoU +|m)FTb%f2J?nsJ6XlVmHjPL$w?VYx2m2chVwk6IzSrtHnyr;qVw6QpfVw3*oA@h7cug`F+fC%QryD6BRyiUtdLSGF^VcHqVIw(iOTqxyHS>F+*=^98%?l!973NSzar&4Ws +8NU4D8*9E%*V+W~*q|+Vb-@d0gmMMqvLDym2;O-!Z6v~J`N)9gMcJO*WvgSu{XL2o&Iao;c-5-?sQ2AVQ5M5jYm`uq4-qfI1mGQnl-KBhk4tMd<)t^-2dyv-QB#oS ++IX$UvjYbq17W)n!JVB$*C)zkameL<~ZuBx+JME4$tZMv4Y}uA2>{VO9;0Bv40a=-& +G&=!$w4*;fdso+kFa0awo(^L9KZ3(hWi0`sDlN|eV06FiYVGjg;jr51F)Yws$3Q*vdOJ){97Qb8kObk +%64<8fGV={pQK}~VGKY|M0*T6}#-rMVXi +UDa}b{ZPuavvhvkIEfI@6!MOW9->xm7GmQ>oWL1WjuIls_OP7KC$XroeaDTZw!c7{Fo}uJ-9D>tHQE# +wD9}X>A{|N;_%QkO*z3?MJUs5ZrK$ +80E|Ka03ZMW0B~t=FJEbHbY*gGVQepMWpsCMa%(ShWpi_BZ*DGddF@+ma~n63{+_D*2P|DzCKGBTJMm +?sDqfXVwzJ%GY?mb`A9Sh-ID|+*+wM!TFvp9RZ?d(Ak$P7x;Qe0O7x9RzWypx)qP&xicTI3g8&PeQNNAfX7i(>*GLKT7)ekZM(5sr@PG>?JVQzI% +s5QYWm8nxkS!WF5M(VUK)hQm*bNW+dnU|-c&T6C9;&HVv)Tz*$EHBk#puO9vta8(rHw>gMu%(havCG$ +Ws)Ssxf#ANtK66X}3T~M&+q_QcsK8{G->G#ZE|elr+o~$e>GAPKSKE3OVZm{x%M){K4|iPDX?pzhKYw +&P6HQLVy)3gMi|CQX&@5ic)F`5!-qH04wZjo3kRvtCm@a@KCbuKMn>=fNr1@q8+?(G!y;DSnc6#1^Em +B$C!g~CWu?GCc{vxaK +&83(Sr(S)Zwk78na)98@LQIEJumAb#Xs*eO-FU0!K6`qr@&LChwnJl%pGpu4W~wlOty;nz06a;?wrQX +?bz8`7Qk&}rH39#JH`0rf0IVJWpnZO%geW)0R#M|(gwoYQh|(W{mwp~i+9Qtc}8?S38>v2CC +uiBQPz9TuDEFqpEnVZX`57tg<^@=9!h{v9(NDQYp87`73-Mo$FLvBTMT@u?mgTqf*uej_XrB4?;ZOkt@`*-pVst#lQo`{O77EcX}xeL +l0`rW3YeILhQ3`!SpQCp>_Al_?=I`t-@uAEFcb@A$8y&reUE96xzFcm{eZzvXMNx_|Nr!m=g%FVlOuH +|{u*UxtT0M4QXK+$sYrj(Rs?a2N}TCQr^Hrr1f +w2Zd#+WnFXSUrwy50VME!V0#5JR%yN{O$en)$^D?m(M2+1O7v0w +-BAqr11G!QT(Fzwxh&x&8;m-r=gSt}i-#iB2`D#qfsXSh +Z}+(}8-#0v#2wAR`5Wa4}o^oPMGo9G(D%U>N;-An)jueQ9g_;98UQ_+X#%RIqP_FiVw_Q}JO!nB&U?_e^@z`Ck|be6f%7V +0p0zQE7q?6Cyj}r%z|$Y#8-a5z{h_X~*^z=7_2VSaOXq255}% +ezq|f2T1}!!^n0$eX?Hi`#OcF~1?7RHWUy7R&ll45AA?JU3Cd|jrNmD)$Eg2?>s~@J4EgO}hFT*B%tC +%1#JsG`J#^Z1!kLuGEI{T^uxzsv9;QY6^HUjq%`kI=6MQPGbCAvLB4!iAFp{drU)KHZ4KfmPw^~c_a4 +H_Io?Op|6aLV})ZJU4`|wq-C)e|lL(Fhj1eim+E5?azz2qBSqmxNi4z +I2v`^ZFo|h3y1h9M1h57G2BoUT2dTdzQ_Nopi^$I=LRG?qIjpl3~T0i>@sjg%;Y679n;urnBBwkx`XP +G$794IoRRD$5|`&Ll|T5oqqn34cwCM9u1taO>oLBT-m`USCgcuyKtJR0UVbp=q&dM8p>86I>A4ohOof +%0?xZh<4|egcl74%i`N$yui{@`yt`PwdF>bC#C1`@A2lpun0z3eGo3~JBvcracJ*Y|@>UO|3oR&y9FQ +WI+d&w%ss|==&pV2iQ=-xRRwrOx$_pfu*@68ytIV!%wudh#4V2+;yhI!wIoQ8(-WoxYLF}7V>E7u~p&(Bl_mb;S4cpa3FPJz=dVG2moDNK7aH6axN;pQvkO(iJl$QGdA~!p +v9#C`)-E}ZJtVv>P-M)!xM$-VPz2(9UInCm?CKF%2U8Gq^|C(E4%Es)nhyLN%ZV#^h8X5TqS=^zJIz} +iKiz|pB$Y$J39Gs_CML<-^NxW#gRvywILq@6W+ +&1@|1^2Q$caRg`=}1&yuvJ2;LQDu;?r``CQyne1vB{nQ$l5x{G+0dpPzP5Z}pLk^ljj(KIb*G;KzN%S +FWGq_2RkP25Cn7zAynnW&c~py^>aObp2M^AzRKjPuKjAiyK#Gvyfj{l$xS@!3xl+40xsXP0Mz_Gy=YO +7AqY7`Nt^$HB##lMg(*iPf&J$j)4UJRXcY_PPEjK2XW$<0P_jFzs%qpt%eBh}QM4FjIaE1qyzp +`iogiCv#5GO=gtXDy#Tb?ahK{=^YIh)Fvs!TD+n};L8hF;+O`t4==D3{33{52Z$k1xe0J`C5+QS-6L4 +RWw}$%V}N|j?7?HFq)z+!-kUU+l`9zeANgZ=jR4nrJ9hv`e;6NO_Me->d(m@@StI2F ++a+2moF;Ci+cA(4itXFA*A0~yw^QNSSShji^+d3?*lJjMppqM)izK5EQG(&6^$zg>@rPkA4X*GBZ$vW +lo)t{@T|C>$<)k^Q^rYio$acx(oW?Liz&#&mBUxM`6x;FX&@7Mz)c9`gCOM|H(K`#-2}-Vi5woTK#)nor-FC5XG{?0T*Lw;5}hn+1M4R11adR8ehxRb@vQsRmH{2x}>0`6lYYsAVg_*$IQK*odLX>(1Igg(0a_SesXXVYR; +f~i7Qtkyg*P^`VEa0n%;)o?6DZ_^O&}bxM;ujwm*kwiff_meJ{*A2n!;4a@N7(g0lsnI)WxR?+9e5UX +_akya>PiB2!TY0CV? +jXZmIB4_!f-~{#h#--osyYpEa}M`Ygb3q- +YRI&>IM%_dLS(J4#I3_6OF|lx@&!))SFfR$MJ6wH|_Ujj?Ru2;+tys7kGEcLq_&wbF?9?_Pzn$_YTf7 +F_2LSaVjysRMv)fprvttG#f$kCPQ_ey6|*UkexlccUsiTsO@~&Z33@i7^OWZuzx+$M(GLERk(GunoD +cX&_vGPYuOX`FB^WoVl^+*d~_d`04%_+-(7S=$}%|w +Qd%6P=GqD#8PtcGS*V1HI@wm-I61xPpgp{rK(jq|m765hYYF@Jj)jWMW2r$KSbWBq~>{f%(J;j)uO3Z +^v9N_}>uS;(0ghv;SiV +lTfp>p&z0V+A$qxzQt3k?VDbFjM-NvyP?YbnF^EeV(O_Sn7ggIBp#wtG$%kKHSpGl7OLGR!Dno+wYc%q9#c9TF +<3%UiRWd=4?l$NmNXPcrUDy>hkrX +!22YuH;^blR_3%({&L?bM9D>BAx4h!~Gdi)2wyg1><~JD}M{yTvVn;#)DX3h$5toegfR+3)Jfbr01?; +%9bR;bpB8S+2QN+ImFnb`npxcJ9FmKMU8>Kf|>ZWQ~E?>19W;HuLCTeCalR2dCL>WS?5~$RHl0-rR0U>l2Q+;-Bt0ef`+)QV8g53xH&242Ump9*)+nzsDWrvx +^PVY-Hy%Ge$)wmUYRW0dkO)6LVJBp0OvSyv=i95ZiAKg+dI(!q9@b8X9rLBTbzQy)WW>-f~d +YPpT4^i&L0;Ejnn(c@mF+yg`19ApHXamt(CaYsb=IwXZS +ndR=c2xsL6-YZwseOf5$3UEi58W|qb9sN!S_qb(iH{kp$_tdg=2h_k=^{?eGi#~AszG!a@)1Ws->>5> +_bXKc=p=Vetq(6F-k-TL8N@E25<-Se>Nu3Q>`d3nWi{0=vphs`9t3UoijR#zU9PJiqTHGlQ26a|JK5T +3$+-`y!-0hItm7KCMU^anZI~bTd+>JW{YToWK(Qq}cXe2(OzkojD6X@Q8NfE!oF`Wfh91b6b5lnXXKA +bz&szZM*JA|VnL71h~ugVgb5eyy2a?}DT5-W&F>?Tu2KS<*R4ZsMPwt?!7O+ +pF$sb2e-PP40Fbx#Q3{1yY%hH5r8>>gJDLQ-5CIqxK@^FA#0cdD%SGCa)@$3G&eCpHTp%hT&Y?KWh1@1#yS!*Hji%d+AeeQ>3+FVU~XKCjuWuv1C>DY=HbLCs+bz5Jd%gFZkSjeTW$K6*rL59cCr01rEuHff9nT<3VG%xJa82QL9FYa7p`&yEV*qh>XS +Z?jsF%Nb9JQ8PoN~0=%Ge`>8JnM_VJ>0|XQR000O8NLB_@?+X9j$OQlZI}iW>9smFUaA|NaUukZ1WpZv|Y%gwQba!uZYcF+ +lX>4;YaCxOyT~FIc6n#hHKU}#F5GjdLXbVJTr7o}wT8Uj1s+G2?qOpCQ%*OV3XU3#y#ed&>XYBYRp=` +T(fZ*{xbME&c(JBMGE@VCjnH5S~xRj9{!i_~OtkU~?`+Et$RLEjHd%!$adaUuE5{)(EOvRX*+VbM}+x +x5A``N|yhuhy~cQ=1s4dFeSLgfa#I5H>rT%=?C9ARN4y-|bQqFpJyyij?L5uaRX?Po-TQj?ToJ +mtlBv^77qAFa +nivX$V}NT_U{alX-M;W1zJvnIhjJ%#t}LO1G+ +zUiazUWPtCCu@zK?36ab=nZ7aVHk3)7Aa<7Bgd1{;GtK#3lJx;$BDYTHGFqsr@+njqC)-7vrxbP5_0_ +w}tM_+PH#a{TtA{{8KEXMB4SItim_ktJBY5PIw!cTS0JM+>XGC^ptgxjya{|dDbRaaTg;7*o;o71ZQ~>;O!%JApjmds-^kN!kHW{5#bT#F+4wz}RL +>nMlC}mtwQWc3*esSd^HTX+84CFKou$#n>BlmY3!H*Yr=aA|W67H)|L`w#hMvYESU!RR$`t;M0;u>rN +)J1K^%~Pw_EkF5F@i~W9NXFjb!@%7h3>pi+lHfxw`GrgOn%hC&gW8a=d);0=1Ws~R6lL{b_ni;nW4@gO+XPp-yxCq_gsJ1dh4U>)K +3-XAP9b~M(IX9H1LI14>V6)bH&06f1eo#^+4#h!V9=7Fp?tmL$3;FD#PPe_wZ8(!&0O6X{^PHQbdW!d +iiTS9Ci;j;>dRdW!5dT^Irt@L01>8(2w6fz)DcaDu!Y>C`( +^iU24DMS5(Ls`RAK0wndqiT3hakd9^&9&~NCzt(T5whj3hpwsM^I4Zl}K_U&O?R(@MiZUg)miTT5%cU-qyx9lLi%eK+PtRCa|NkO +`8Tx-5>`|^xh-G=SU*JqUL?)Yy>*;>i&DpYt*)plO*TlWPocV(kpL}^tf$b0uc-)ie_lDybeyEH(l^0 +L-cF$9%Y;+>GI+)UQu +S=Zn7{ryF*o;$btQ2VD~yB^*F^-b3S$8$sW2>R(Vx0|XQR000O8NLB_@#3Ht$$N~TWI|l#&9{>OV +aA|NaUukZ1WpZv|Y%gwQba!uZYcF_hY;tg8E^v8$mBEhFFc60CmGTZ&B&0^lntP>kSuHFAR+Z=hgpk) +wrmexXgYE3LLcBX;J4u~vAcr=w$N!AKf8ykMem+UlR8s8d)^KGTZ5$D&M#2e{<4$OmE%H3i7TE%a{jA +i7gHVUzxi!)+Z@95A3`SXs!YGq1_C}wGyEM3tbP?OHn>%)Ue|P)g{lmMGY+KCfvxLw_G?WSDz$h*$oG +WO2DYjqy0Pb0vpSM~msQk>GG1|;7Rxy-XuoeS*g^g%F$C3Z8YMqcnR>Oq_w%3MPMAO;;Wk24@Dq9e|g +5(H7FT(D%b=yk=C1IbC*^x`hcX96?Ef})y8Pfx}k6~=dnBpg3tnhK!qLSQNzlkp)$Kc&^l=JFo(ba`k +Zj62sH4si7XUu<`xKEx0_n}u_k5NhQejs`}$oi`MZ4GjyLMamdrbKl_|<}SQ#{wlBlgIODx;T +hh)Z~3St^O8_XzbBZn>A)WlRw#T0R0T_4Y3WNzVD)&HDYcTs(60P4IGFw>0?2;GomNGEf_xI~%q1x?A +Oc>4@g@Au@r-cT%r^leGIelvud(s-KjSq9u>X~X;8@}yZS=^gq{i~)D7GA5r&i3wbk(i9|CtEH3H-F) +nieLr#iZ!TiL~r@F^LO1>Usb-*xgM5xYno`8bGsNY*3|!KPSrZT>i&ljJ@ImCeCyXgcGzE9S6iCkXA- +GQE@o~KEK~?Hv~eCSC%{~JrSl(`S2myFnWm$W6svSSh3l`Ew$35X7JmUyO9KQH0000807zB_Qvd +(}00IC20000003-ka0B~t=FJEbHbY*gGVQepNaAk5~bZKvHb1z?CX>MtBUtcb8c>@4YO9KQH0000807 +zB_Q)o00Iei8I07w-803iSX0B~t=FJEbHbY*gGVQepNaAk5~bZKvHb1!3PWn*hDaCx0rZExE)5dLn!| +ABB&LR1;6iU#qy`N!S#m*&C@a_j|{fVD%vu-8{Q=8Gk_8@>Ck|wM6y3yE?LEDt(@Jj!`i +E|t>$Gfl+jw$JcA(o&C(h`g@$>pnFMG_4yYyHwurPBuIPcK{!Us52{KB9?(r&n1qO(F8sY1=WK=B +%J?sZ&-zb1CZY$|hT?m{V}$A^FCnf`3j512Ft?K%Mwq_caJ3TzC;)=+%5i;Pck_-V0fo;O6%>xJVr)$ +;xpk0lz<)wJ0?=kfuvKH=kuCRf>G+D0l-NgWhN$&eB48?p(b(?LVIIS$$;)xc0o0n(?)pDvcp% +VfUZ4w4+SKCsNJ(?Bi4%z!3L>8t-Tn?36e=hHA`(dN)@CEGSE?&<aAON~R&7h +IrLB1e2qP~RkEvecQ=v{3M49T(25QyqliUX@jUnN7~t^y@5 +RMx=FXkpBNE7Bxy&|2_{-a*ZHrxvk^ZcNxX1Oow{WNQO0nQmq6hbQh2daOpS`=pKn0ks!xnWubL3&~d +ZqV7xH1-4vvMIxW-u8po;$6c?1KpUF_8%P +U~E$FK|Ng$0y>t!VYRk!X}v79M5`G6C;$IouQ6PhWQj8P9=N#u!ZE{G_Dyu*?J39grE;+g55cCrZm +Ddy7&Re`!eDq+u1k)Q`f%ai&$kyrG&OHOE>M^VR1P(LoH=dNZHH@#<)U1GvG5`{-?y#*I0724I}X}BR +M=&)*7#tRNUBM=G=DeB92Bf%~tsA6h`u^zL^!VhTed)zCst_O7X!!*)sx7I$RvDYY=*r56Qk})~3;|e +O@GEhE7HBAS%ea^^OgKsFP`W8*>D;*bo_egL@RHTJloK$%;eoe=7Thha!sxO=$AeoIV^E5M*8EV;)>? +CdWId!7E7Bd_M@?bo@7{!9E^DPkji5ra$q*Hj6zj(KCp)LzM!z4tP^|fD%rr1w|vc!r8&iZlmI?80AI6JP5$BU9y+nk!3dlgjP9_^sY-pFzw^^eEbW-su{<{)pC7Myaqi$?@BfXJcx^$OeQm>`XW{8wpE*UKV&1;1190ZV9tgl(b^cdsf!c5d5isHjCTTdC?&ywB%);&?5iF;u7ebMZIN00)YnkYx~osH7`L9z+1%b6Q=m=Hv1<{ +U-JE)mT5@dma?WrK@s4!5L`^Iv1c_+X-)eTBzpyd75T0~%#d8MI%efU5I+T05pUo~z7kLn<*;L{_h4z9TFxi69tV2qZ^Qn*X4!huE2+EFZ-e$v#)TpdIBbd +B^vGa3K=O#GNVz8h`}j}LBjnjgK?LFuE);cE+=I7k?>!r$(}{r|m%fI-s +6wMz{`+^)&?-=cQMp4oKaIlBKX$&4EG_C!OHluOqK{Q=|liM|O;`eO`l=!tVquk3sxKB5*Sa-Bc?4t)wa_>*0kK9-ET-3b(aGKuUP3jNC5%0nI{7 +jvkT@qz4$ROZRdW;YXu%*c&I>4n9!$y&1D0_Un**el=|M8cQSH^OKu?T?uv~BvdmJ9AtgX<%yiY;yhteu2Tl~kV4gPcnU{rb`P%gWR4)buQNs=t} +!nNf+I$Rm$J8Qt6iaO<2W>!okBp<%gbTBnArxe0dz#u)W_9k_QUItGYGc`cpR?J8%Nz`XusZ?DXX7&u +{PPso0HtJH5a5{3H;*c&1JMGa|R{N6V@ys>bNX)dqhQh<88f}{cx4IP4-8t^abbF#rhmbSKXPL8Tcc_ +E(m6h4}7=4L*G9{vf23_gcfD2Mwovse@D&M1bL)A!h&axW7T#lN%wjWjd&9FPe3Jj +~cFQ4AwExqM=x+D%jfNDqAFTo7-_gQxAmcm%<3`YwmIBI8E%W;2dQ=JN&I(;Eroe>7n5}M}DY`SJA#E +6{hVo^U^S`0d)z981=7Aj|22u9Pxf}6G;))kBwjzlgO7{NV*^XC0@r=9$|M;ri04;yV7%8!GKJBx0^k|xTc-%GB}}D +!z*RQpo2cP;%RaDRAJeD-S6NTgzk7#yS=V%0|llZH6=&~4IKe-5$d$I;9>A~)H17}ffI{N4d&{JraG| +NL+rDV{80C)R=Xu}__CM&u!_R~2!&O<6#13>G@9YHB&Kn?^NcL(APaI3u1v&4<{${HNmhSs+Fio$^)ZEK8fY0)r{c1 +Uj6VO!AsE~VstL++J%ML;AIzSFZ1tc(1-(;yK5n-UHtyrfjBk&DkKsFZ)Ra*d?y;(bW;k)i)0)uL +YGn>)_El4xDV`#w)gZwL5O%Zau$xE+$}+HoG;^l80itozZbO}(6>AeCLrsG_QXh}FhvQ*G-1g!!==M> +2;2hP=>k!x43*J`+JwTB)XCGEP9)<%$q*H1mcNuR=oFC#AH*J+{b_*7GbxnN4x%;{k!?|dN?|PABt25 +G(s$In*(|^#UJqD}G(1`>6X@L^qmT(ziK8Q+?R|3Z7n+J3!@vra5yboU!$2GT!b>_C`HG9wNH%O2=#| +rz0U!nFj4*|(63AgbMiX=!@Kp6bOV;b`W6^ds&en*NmPPQ@sI;%+8t{E1K-g{2IzWl5Ojef(XOlAcJ0 +*V1c!ReYNV4zOJNH_}A|Ai?{EcOI*@+Qnl%|}a4%-JT^y@D6Eae0%*h04&a4vuYoOF2Pgj@t}yEe@mO +3kyM6fD}n=@!DBHNSpwxh1VXhPFdjhn8g1~9S=sFk0Z?p#p0YorsUB(A5Oa2{r`(pCPwkAc{y2vX=SE +6T8bq%!}Fkt)-7A9b2=kjRWjY8$jFyZZ#PplAd=xdu=}VH#iKWbm~;`Xlm6U)GYUS_y{ZSLluEWhzS! +ef$harZ`bZ>sBN8<+f|7PvBKq(wX?L|!2EeMeU^u%zqB7e>RndET44X;)O87p}Q0}zby@*ttlP<{TuP*xdGf!HVmT%svE;kTXxA&i{O|)gk;pEnIiF~ZN>nXYRZT-plGkwzjaQL{ +x+^`;Z54VvH7+Xp^_saZI)ycT|Gk<|!i)vb#nSiTT{hJ- +(hYC(NDGy`1~e1u9Wjs>DR%Bi~z`A^gYJAIaZ;`jPY>en}vdN!T+aw1zkZ;SmE7c5$`4K>wK(P^U_!{ +>f;9(!ZLRp0uWS(q~skhO9mApn4mFPmIkhDPLS3ljlf$xcqi&5~Y~>Vx(toq4F8UZg33WVdQwE>H6*N +6C)L?z>{>SnhZo{b}_7yO)PN4-~@ur6n~kwhGGfsAH5MT-XEnNT;Ycoz7sP7@;8EU4%1U`iN6r~`Lq8 +3P)h>@6aWAK2mnY{22+hnZO^M3005t3001KZ003}la4%nJZggdGZeeUMZ*XODVRUJ4ZgVeia%FH~a%C +=XdF4I(kK8tTzZ>xXz?-cx8dNJ^QltTVm9EMB&%k*(4~id~_*w%GJtQCDJLx1y+YC(BaegZuXui^ctW_wSvEH|Gj|F3b9=s +LsT;J{(ll34LDoYo`>tNRQ_4ubghuxR=@9o?AAYc@3(ERiSR!POPPPKAVs#H9O)28A#vurBf^i4_ +Y(%hzKviwq2ySmL9xxJ9MhYW#h3ver|x+=D^EdEe=B@Ze#_2MiC{iWHh|Js4P&?AkHE*5McwP4`l5t0wC-sg9bNI+vt4~vm9-qm_7kG= +iFv5nRE&Qgg;UBuu5)i~L&RU+ym|RbR%hxJ@RQQrl!(YkIfCZ-dEb_^=(Njer8R05i^-yS%}=7UfR*w +8ENcfW`sxNox3jwz=rvN$nf8W%iOwbw&vvQ-UM3E-^X((GJ2G!yluBRgP8|j~8sAy0-{PoCeqay8etFD;#(K-cIdB4!`CNo +4=i>SB)qD6Qx3&pdC?B14ykEGx7kT(4wllICvv1iQQr(VG3Kg@+1WXW5y-APFY&3*1at?a2GirXm6Z$F!pvIEZpiyF>(7iH2M2sNo;G +0A6H%&esYi5oH6Xt`G>G@e+Gz;cPBiMvasY|p$`e37eIO+KFk^JeGl?3W7D|RC2P_$Vi|m%nY{<2=>; +x5ORo#MF&WV9!Nv2&Vy4?bGz}1{^HEEiungvVs7yzQHU?33#YO66vhZU?v2dWj&2s?w2tWecfAzpjXw +ubswO6ZHC5efqaP5v4}2G%`f5b7eoGdy5*Fu%y~5@WGzu?50+N~r8CV^F#V<=olLaJ<{J_1{7A1e+iv +CCgzpoW;m%d;Q4enZavx@MGA9;x7~GX0I6oNC^sg@VGu#O-A6}F-(_*1{vM;gb`Jsu+o;zR_oSX|;^;%?Oh-he2iDnv>O=n2RyVEQH-#lE6Foqlf& +XTUG%!eAo2XUz5D4u*L4>4%l5#{U=QHH!kth5&h`nsVh}~9}cytbqlTl~2CVi2aCy4>)jowJD(4BUM+ +ee8H(4Pl14W^kV(^;dF!gNhQm~o_@cS$gzGrA+gH^(OdF>e&QX7Ap+_IxrB&5&5H4T}V7@(6*9!Nmw><~spvutQ(T^TJJOd5TZ@^7DJZ!ET`4FP5hYcL{;lF?X{htF)HJJQYK;<&)>W| +A*EVBy!^|2T-({6=m^|@h+-WHBhf!>DDyV|3NiYdiy-*N_lRS&~JsRooxM417R@MD^6BO8Pd+~c4=S9Gcm%~l3I@jDV4j`LVNQxb9WqBSSFlgriO +U9c2ah~hz19||OTsh}Csf>MJQSNMg)F_)-E8j83pYGxq-`(r|N~agB>0gZjChFOwGROkCH6Rh1mJ6-# +mwCG6-qJ#svMhH0pB>%6mv!e+FiNb$!ySyDo|^Q*sZD@sFy0V~e&FvP@PYA6z?w ++VYGpBQgB=RyxCuj^*1Z+2KV7aP#!v)-3Ub0ApV#;(cqJ4FulMr>F +;ZxFyg+%#8fV0sw~i3;3*plUk}fWvS=0h^qpe889Hu3fVxsi4T7}|J_kVq4$pLIZcY(Lf*$L#PB0X_y +5@-c^HZact2=@w{%{?$@>bF{dg?9L$dn&ft51L%#Rw`%7Z0|_|ar=MZH1aKSeU5Q+h?-g2%5g=+NsA#q*tu{sh4PCuHGFJ|x271oR+VCU^Q)|rTUr7$HoZBC6x?8G8~1qfigBaVTPV2)eejWIhVO +cd4xB)HsTtUUqrJGqopx!#HEy2pe77y_$-uYScKdW&e40)V|Lg!_K9;AZpht>*0b!+G1k7+ +-#e4E)^80GUmSv3&1$QB%nOG?#{(jl%yH`O9H6BssekVDa*W3((h14O4BR04Y{BX7JCD;+Rq7}P3=nR +cq$7C7D{ov7kFlE>9<^wU&T@5#x4|71Qdq=e>dk}-6cgDZby#$)RK&eMFpW|K}_1>Y$c9SyKm;xUB{? +h2%d8`Eu2dg*Rr9MS>Xx<%bXn;B6;F^2t#27{*9*FESUb7YK9$ZbZJ^i!UJuN0(f{$3YV;v-^^LJUM* +w;!oy=aE9!i3ipd{ivKi+eec9DO)caVX+(XgLM!owFcpQoh+XcmM|+jEf_q|%hW&PYTn`XQ>LRx#a!S +ROcH(OYuDEbU>;2i;%E7CV20C>86FTa#745Wmx*O${wsCr$ZEDsr(?YB`#-PzuQgWE$v2ui@`S9;gJ7 +MpcIeczO+ncPtqZS*Y*HRKU1M|zx8&%gE3o)De&qr$aqu*n@NnUei3l)3Z@0&Q-BfEI5q6g3XrFnzlY`v6#7$vwOTv&w$w@?j|=DW$q~8MI(Cm<+B6FV;4x^)@$yytR1qI{2Q +Q%T$1A`12%koHONC{fc{1yB@a1M+Z{tm{tjX`Uds=>J2b}X$eA;SSfu4wH9w8=nqm$0jF?gsUY3n6IZ +x*ZtPowha%;^ONBTgfnS(Sf6VYD~Me{!XjrRak?fbV^ef@dELH|e#LK=$wP9V3!g5K7?@)<1$D^oll>g6jQOKqK;|neP^p*@8A3Tv +8G!r&J_yY0!xflXU;6ihcZdLx&E(lH`Xb1d!llN3)@Q;ixjM(SKGoKJ=21QK{c6e?!^f@r3U3W-9kiH6Sk{+jhW>4Pf};ELY~nUnKab2k?iwEzSzCwzTJ*8U;-|=vIPU1wIsnyc(D +vuz(xeDoW3Sa}RoWAQ{4azAr====7t2Pn_kUk{~>sY~3Ke=;9^!PfoMASsKfb(kEDZ3Y_`ukOv3 +DBP&GqqUf7g!suk#|Tr>SMu6+m5iwmjqRb@_%N8QZ2QWk6|QEu6s&(f?j(sw^%%zZKgzzVdEu};i=Tk +Q*TMH#mWWTqsReXsFfGbaJ*WWeC+n*#OwI$_TqxCuvl<;UVNR&v;IK0Qp+^2mXpLffqGdgscn3Kvk+1ThoX#3w#e8nC8W~> +x=eB#j)lO4ENf;m5bS|}nZIPORE*<);;yicd`*LPF1@6otRN<&E%;+~`XJBH259V=;`1znTJUiay%9N3}u`uRd7~9yA8dD-U19Y1astuazfhH(!-mm<0L9GJ$(2OC4dereO??8wPe +&Jn2#-5YHns=mZkbF@@ajcf+EKWVW;m*)~4FAO!2;@t3bqXlhj|FyC9K7ls>sKGQw0Ga#x;Xc7sx(JH +@L>Yf$3bm%YNQoV{_B_nKnzA!A}J4N@#>YOuoOmy+E3SWZeBVu+`>l(0()T8ff0!lYb*PnuejA)W;0g +QoSDET0d%vik@5{_>9CSfRSN0|=k$?fulrf0!uH|F*?vHWe*87niCa<9Ey2i>W +BpsDEQ&&5Sj-fw1$rdKe1`WbojxanQqFWOO_|gRD9!K%9_^dY{hAMn>gX+=?bcsjE~gq=5H$>W$F +idq`jE|8Vb8nS_Xu5NW4IIBVkQ5KDt;PhM+Ar3b~maJzwfFqf4)+5~jMf8~eh7cT-RHy$8vQ8%g1%{M +%QRWkC1XvII6h`aUalg@LyOwuO=&q(Hez(yF#FVKM-l@6~xj{+(@j+tv;CUDm*cAjqDTg}BSOt%yEQw +Mrhu(xMVu(v1w06>oyT$IwP7n`Ykv4y4*6L-*OA@6C0J(hZfuj6X^_-ZSHjD{ +|oI@34Q`aA`n!ahLA5_>i_|uWo&rjDG>g4f%Pxm_Ftn+#B!Hw6tp)j?A4;Y#V>|>#x{eS#;?*Q;r5 +Osdl&Op`xk!0yd`AF&ZYnFBm!($vx|G$}JY`#m+7ubu1p#CyjaSNseWWJXOqndM_Aw!2sxoJD^+-R95_=gOx!(+Ji%OJwKfjZbq +Zr?~ha+Y3tdX{WS11r?TU2lb5Z<9nk{oa+cagh+{Rx<5);j(d|GYGGn_a!=S(vF^zf7<-}S)fc?oo8m +%>2&a&Wy$kF(it&S}K%YkEnCdffW2=(rXUt6_pLAR*r1S~%C|`=DTPTO(56LMrd^rzhI9a_KWfl4=Q6 +Kn!QZ+ILMg^(wq#=(u%*RTBR!5rJpXcb}b|@xipVL>4xIw&AinhhEG4$A^d*Wn`dqWX31a30~7wrMmG ++o;QKb)zlRp5}*eY8niW%dQ??53(De*V{0S_t(HN)10>r;Qx?&&%!bHoK9QmeB&rnMU9l5)y}C9l*~k +OO1`+sQ#z|Fig-+{KTOtR%2;js&wnWvz}1wmh^2M`Undj6@&M8&?O|eZDnOy0dj3$-q*JRUR;Ydn!K>zW(#}1>pUr +jDA{(47xpUKB<GceCi-C16@Htr*K06Cf`8!35NJZ(lu!KbKxf8 +@6l81V@h^(N~VW3Eu59F;#<^n&{bRP9Hx5>qKDP8BqyLON}Eov@v?FYgR +BdiALLH>-u?gJx3z{v}?l?<7aGB{f%>X1Qq=;Dx-v>_zrR(bz!7X#~3(Jtlr9Q|R#`aMS&sOwJz7@!v +|#-?2RPYdv07wQ1q_E01F3Dt=88zso?5V;}LIsoNH%yi>)*CH^`-j3NE^M_zf6ezc}M(`M*MpQVDm6c +}D;i`bO8ve^yy+UBmcI`3aeBIrn`dkoMTUdZ!0mT#Ku-HUsT*DK;T;HE*d6%|iG_`Xo;Dw^B8$@FMh= +;qvF6>WC)#zs|(ot26Oc*InV9nzKFI|4AeKySc&U%0H~VREc|5;jA(?WUyTt%~G?*1LHi7^cNgguaA1 +Ilb(|zL}fihdD2V=iBBMyg*_77k(=Z>1uqp+F5B#D`=ZJV01Upbz+WtE_C+UW?1*GR%BA&>_9^g44h0 +dlXtbr2JqL~Wpl>qhS^^#_PNn1(`!54v>Bsr1$Q9tKT~pP;ziNqoDqoJBm8RFHU{=|WY0SZ`S1pvd~* +$gj-u?ePjb(s^OhZK?ZURNDKPh#jz5x$@Vyheg1vowO&Go4(M+;$>%uj{olRdjF}x=1(`UDD)tluo`R +zJ09QuKVLJKaE2YMBDVK_%M(RR3UHtC?dLv7{OYVHQVhK}^w^-hd0Hn7U@9wlna;^wX*+LS}EY`eF8$ +;a4YWZ&K;-i(FBSui$#g0H7h4Y-A*cU5&+v~|T@6aWAK2mnY{22%h40006200000001fg003}la4%nJZggdGZeeUMZ*XODVRUJ4ZgVeUb!lv +5FJE72ZfSI1UoLQY0{~D<0|XQR000O8NLB_@$&Tsf6#@VNumk`ADgXcgaA|NaUukZ1WpZv|Y%gzcWpZ +J3X>V?GFJg6RY-BHOWprU=VRT_GaCwDPO^e$w5WNTT9|rHC4rC2IltLgqbi1^dmQeO?WNQ-7#+JlLPM +Uw;krg{1Tei9+`j|Iw-kWijWj`?}t%8Cb8e|p78ffi~YS>x^Z+os0H2J_(_3_ph^I4W<^VxhxowFRk` +ncJ$?ZA;M$^#m0xp3${ef(S;SviO7;rx0t9RIL(|D@U$`8JkJKsKr^s-9Y1qB)FlU*o~EIS^aZp$Y$t +5{9iWd|x@XHTo$j_eQEKj})5HI~q2f+^ZeFgA=fGr6wLxUS$7`80%p96nW2Q8n>fzD2PeRd?LSuO4WP +OW3iggK-L3Ryn*OI0KJIyQ*yQbq!fG7;8%2 ++*j5WV6=*wM+;}{e#NRdQTO#6i$g$Xvjk&oCE-Ugup*CTg3$-b*c#PPRr)mW}q3(A6QLlWs3_^QYp3H +)ku^HI8Q%|EA`@vlTK!9&n}GN|C1%ZMY;F$_I9$IhlZ5z$KY=9YRTl+Vappj680BRO9KQH0000807zB +_Q@!R7f$9PP0LKRa05Jdn0B~t=FJEbHbY*gGVQepNaAk5~bZKvHb1!0bX>4RKZDn*}WMOn+Uu_FgJU^xmYe_-Du;yS1DB)zP0tr!swbbv +Y|{W2S)LV?pj$12I_?jmY=-fM!iU*RSn9W^@3kHG+pM=?VGZWS5=4k#u#nR`XSoNQVXtaoYs|PUHUOb +&7XlqN+?ToEi~h%wC}}hca70G*nlQSC~OkHW6;g=+JQZW4)pofc_*@WoXkbfH9Nvm-t_lA38l2x3^d(#oivAn#Pq2g#f=ET4!q*cGZv9 +IkEAVhELVf5(ow%b;;hnXm6_N%+M7W4V3kpiuSLya?5^^W2KFFgl&A*_b(r(JLost+XEu2yyoh&*i$q +yaXHWgOr77Is&zk}>8nS56f0?eHT1S@h)KK?h1huv3PX`xj|B!Z +UPN=d-_vLHO*i>_4MJxV??Im3IMBzyD)$VJG-6;b-K3RvRfwB9GFiVSqIkj$HXtdIjyuB`v@Y^?Qk=D +r9e=)L36(orc+8!nErLBjEt3$*5YpUcxiLk?6?JiDGH7)LaQ9dC2j2wT_5DG>!`&!Lg`bX^?2TXoY@S^;2e-$6&oIuhgm`KJ;l)6wj!kC{0zKN|*^Q5zgAxA#P)h>@6aWAK2 +mnY{22(+?s+s)(004Ld001Wd003}la4%nJZggdGZeeUMZ*XODVRUJ4ZgVeUb!lv5FL!8VWo#~Rd3{n% +i`y^|y$A9ihLjQ;wt^l?A+SBP-O@toE_*GDP~x$pt}Usfann%x->b3gd~BBLVrypJXx^J=yWokT^-W` +4GoRUf_i;X}$#=l}km4|gZv$KJOgo>A>0pD>ztB~lRKW1&^;@+g69Q74Fn2@vv-gi5Oxq%zW36MP)KT +?p+o)o{4&*l&4{++8L()6TTOHFZa_LguG$@^9a3r{%7^nvM>kuoncBVtEOR1aBDy(7M+qTlXEn=(lxW +#-1=}6@jzzgAvD~L=vzBZf3#O@?@Nk{5BWK$V7y0U~D_OySl8)Fx$r-^cvlT)xzN|o^H-9hZ{sq7)wE +EbFRLIJ!(_~Ig`5$^G4pC0YdG4xTE#ao2Cn>$EzX2vjTsze9rbC2AUix3QNfjtb0fUP0+8!4>&w%rd{ +R&bm8(ay#s5RJHR>3ww=DgT%lG41P839u2I=K(b8K>LxjoV-F+;ks{%+b`1Ea#9Qizx2_m%e-t7$_y +V?GFJg6RY-BHYX +k}$=Uu-*4MC5Pmn{|ABKcSVA3Pdo`dwbV-T=!2)#HUeZ92rIXB6A_bC)YXtt^d;B4Z +l%2G|R=?Qh9glb4eczF^?YK~+;dM<}yVf9LG_*Dyph?ir%gF*Z}TsZ`Nk3x$qsvfCd)-FUklmqI>8H&V=m +EhLGW{T{~^2n_|M1dn@=0NQshmOP5y4R;-bw}R`9mXS!s?#ORS66(19T7fMHUDx-)B0ma)8rEHi$~MG +4hBkO0{?$|Vy-C*+OB9kvGV2dTtk=*4^0^~&=@7ffkuy@tgoNs^dcy|Y$rS*=#LCW#>uq0DB5{nYu5g +`j76WC^msLN9<3`F~=&OTkyAz1M%ZbI{hez?9sQXFufWGh1tX^d?#n#?`DySJN2uZA!+V&V&5P8LX`z +G#JIUMK|N7s-RHx3GUztnkXjk#tN@*-v9mKN1{LU63Ern6NnwhUy2jC=ZDAfKnY%yi!~&`N_n7aAFZ+ +6*mFN`s!73sGEc0UXZD#rhkH-Y9;Z(ut^#!EAB99%-*A3t0O~#F~UQw*g>$`5 +cw(>vf`MqTbcE4v`UVK4P=&!uS1=DFQ=#>R$<@n_8^JKu?&ICCQ1UC#SBKtx+R(;c0E~r`@;@R>ml^%tP_)80DT%ZV)DYPR#9fIdEM*OcEZ|6I_l?HhMbccOnu)NtT +m>d8a2~SBr<6y-7$7z(zjdi$cy}FG07!yhV7X&O4#*vw7#BloF?#={3_QQ%bYP$!zHwZx_9?yI)BE^M +orkP&AG8Sl&L3+a|c&Q>b_?lp7h~s0i7Z@<_pW3CG7SmmA*^%(F%m%Viku@4kKfW*0(v%w$e5|Y>a_v +Hv0AF!gSW3;mX3<(;zpo&}hIo1j%S5{ekB~5IMDvhtM*Nwt#*9HnVHC?oZkD#lD5pH+3%uZ_b}xS5yA +W#Wzq(0|XQR000O8NLB_@@jRomHvj+tH2?qrEC2uiaA|NaUukZ1WpZv|Y%gzcWpZJ3X>V?GFKKRbbYX +04FJE72ZfSI1UoLQYQ&LiL%P&&M%}*)KNi9|=O3g_uNlj5G$ydnCD=taQ$;r%1S13qK&Q45EE!N}ZQc +_al>&aG0HXx}05bpp0B~t=FJEbHbY*gGVQepNaAk5~bZKvHb1!Lbb +97;BY%gVGX>?&?Y-L|;WoKbyc`k5yeU!n9+b|G@?*_kva4G9RRu8b?9tu4a(k-;dQnZRaw#C{Stt4lg +x36Sbw&P@D2qgdfGnQt)NtR^?Xn1)hPzlSA9S|XnFyThd|E2-&1bt+6NetlSSW#SX&QJ^878UEb}_OU_S%Vdwq0=#9rjV8MGpF9k8Lws$JHy6T$K;;B0$T;An=`H(bFbWRoO%}vm9D95k^Qy(IYE;m_K +h}R52KbdCV3Bx?Yu8dDyZXNSe#UzCd55a#<8<*ROFNIJUopZ|6{EF8;nhdM}Er}C$aJ>4Yl~U^2E8VJ +T3Nc-!T|q?xUFzc5OYnP6mg+WQoCF)aEeO2vk(#kXOz*s{7fB?eMTl(l +GzdAy4wAX!XgF1p9C7+GAj3-F3d41;TK$h)xZ>K`JHgPvnEOsjx@Hv61kMq{t{*K+w@6(4hV>37Hc4^ +T@31QY-O00;m`Rt8fbjl%N81polK5dZ)x0001RX>c!JX>N37a&BR4FK=*Va$$67Z*FrhX>N0LVQg$KY +-ML*V|gxcd97IMirh97{=cyAASRUYkXb#zf(tE8pb#2J0{xK&$73IRl(i*Ck~2GmynD})Y+1f^XrTkU +JNCKgT)uPB?smI3Q0K*!32AKJG`ST@vQDc4j5#vFfwRvBY~S%NFn#9tce~y0{_g%xw4KtHHL9+Kti!W +1;j6jg_e!glmBQHGipFsI%?5gL&SedmR>}^1_S&JYk^Tmyj_hqmhULwXy@#(o$O7ne$KHvKXF^)gNav +?sG-U?z+bI7ly?7zLO<+?Vv)b*zU6{8~EO9?u=2z$;9W{e(Mr04Z5- +z~HqSw3p{fX}}P4TV+uN;#$AfM)54DPDtp_B|?cSVz<=Yjq7g6ea*Y{6n-I)|6;xEIWH5tNU$n3=h7!uG5)GJ)f)Ix +qy;QnPymJ4LH9~@UbSVF5tp0Qys8|k<0rfKqe9cNQg%*zZ$dHDlV0z?mr=bH`y4p&*+@3d`MtLWUel3eTkoVU5fKHYqDd@L+mI^Y +9%ZB_ZC!%&EZ5F))U(o9sE%^#f&ezjOu(F4YdO={%l6(_t=i(vUy-R|~g?;y^{oTx9qC2O3to2xBu4M +Q(xFbB<4z1yHeEvfV;3m>xOC-pc06LeIvk{el#;lC1$N7W*4EqO}?Uh9T&g={qV@D0w7);Fy;-$BfOf +7)3(o!oa$TCPNHDf+bvhMlmWMKmF^ZQ+-i}sZAh>BX&iX%u_vK6`e{(iDim|rk4|Z40IDyu#o2+Wq*; +oQAR^Uf0G$T!lB0*lp;G1i!gA5(HPQVx%4V@gp|BDiGCUIo@0HX&d!tJx&4~nP?m_n<=tAtcqjLUAIg2f62W+Dp-GDFB!d#I^%F|GTBVSM)_fToZ3)mfS$2#)G6QM8^?dd>-$FFm^Dq$aa +Jwd=D@U7;`ugDZy>`Tof&jLVm)DVAGBNx)pi)aO{(7#VqbsCITq}uM$E;pP9ehCk0M!RC7S~JYG12CP+HNgYi>9$o_?kYifOJt1YyL%PrOQ% +VODqDCE}pj}u+majZflyapmZptHGRr;I(^;Z!&*&U!LDhu3QXtqxaOA1lM5v)QKzARHf$g3Hk~SX&J_ +k$2!-2zG3__j;Ek+TyLZS>Gk1BZa%1oNSjazitMlVWR~6H}A;AF?%KUL7Z_dUIbLdmFWg263BR-DpQl +|ghAl5Rgh~Y?%S4eoqj&G6$=$7M|KK@3)OE~p)vVZ3U+}vhA8K<&Ry)5^~C7=&$RWI0kQDawUR^fsLt +e}yo{jPNZTEgT$tXz%*XB#7@-K}O7SHHx*&T-L;-SyW=7H%O6r~XGP{GPRS2a4@^!G5u*2bcK{TOAHd +Jms8!qxG(!D_zw$%{AII=}1IkkLji9z~{RDq|MCYdp96auv9J$SZuhXUU6+HR7ns=_i!2Z>Ri4~P~^R +VSlQGt{Uypw+`Sft#)o*1b&v;Q!NQFjjmVR+;U!dH~x8-D3d%22e`_1QY-O00;m`Rt8g3tF8-^A^-qAb^rh=0001RX> +c!JX>N37a&BR4FK=*Va$$67Z*FrhX>N0LVQg$KcW7m0Y%Xwl-92k_+eVV#Q-D_ +8eQusy2R{C2z8)xL}YNN{B!J0YJ%&%lYlsk9h;6E<{%4V!kfQ9KZCJQcP|xr7S06UQ}7T5lyzZ$}S{!TV;Ln=_ixP^hvcYW$49swPD*+rF;LPQPAUWSkS) +;o2n+7SY+DD&$BH-`jMa&=0{`T@Xee&nyCx1D4{=ItkxZ0>s-x4cK<0nAbY+g$B^=Yx_)yESU-FZV_` +?^(Me<(WL@VUG>+cdh}iv}5(mFnxM{B;c!k>;!Jb+&E{?AH}^QJ)tX0iLO;{#Gf6moHx_B&26ePN%&NL +77Fk*RCetcg$&s#I@lH3FRkvxL6uWxv#Ix+*I8|Fvv8ct6^CCHpMT`b_uz78b%&AYNO{*9bHwXteOt+<86@43FZZNq|c7e9zT72_Beh1_}Os`B8qKax8*K?-QrTNjMSXMYIt^>{^j^@r!j~Dz8dXJEKE?|V +Bz<2rTb_3FWCYlAnjL8x+M8)WmCel9bi^Px3J>S^uW{{IGoq(-Y^;F9gEAXyHrq{tnG`f#A3MX3T1A% +{uk@EMI6_wth-W+sp!(1qQ5Mvlqp=dS`Bn0+I^cXdO!Bs<9hLdt*XYpg>aXGGDxZ0o*;O#*w-jLWR+1nPrGznZDuG{tt2!ldgqPf^{ABk{ +bhq!4=uRvwC1^zJ3?1iDWyyMDYi2(oPkD!RIm`JmCfw%9SarRcJ28FjK;y13z=?i9_${x<~3f8W6D5Z +I@m;07jJ7)|YD39PUm=vkDKc1hD2wzD%r%vdc)Q~Pv^8%YaIm70+GlhePxs&Ak*spE6olv>8Dna_cn^ +!?2n#s{a>N|f8Ep|*3-C|4cmx}yi;>#xPb`+BZsjsy+h&|G;idW2B9DcAz9B&?E3M7)LOAhZ|2k64tl +y67)wWNr|1)*XrkB0zfnGJE)kN70O|2?TYiyTHWE>IzYvaa$e(8crF^(Nugs+|DD*n#+W{L<+zx$t5+ +JqS~zu(YWzxzl$0u60h%-x3)xB+vIGau?x;}Q#pTWHV4a3zfNIE +r*)OSe0=ujNIe_KnD(8Jm2ZgM+POOSw#}y!II_a!2sFs5jnKTzT1mPCv?dNj{yF>Jp#POWo4A^@v(JT +bT9*QC91w<84e++WR@Pw+n@D>D9t+tsp}tkBOo2Fb)d3NdZ6HmB?}>wYy!nPUDBAwv!$!dJkvjMQ+PY +9%0P!7chxrC=Uf`h{cbZqVIT{@b|D7 +M8Zs2O||F~4F~_uE(UQE`oC9mlh<-57(TxE-)kp_*qahTABdHn;7^0aEEoG*0!F>ZHxOmt%g=Q7>3 +VR-9<{a1B#rp0NokG|59hxdO$s2*FXTL5q)m6v+;w-f#7HWjA%J}Rm};k^vH)pTlRxsBO#lNVm2_$gj +>Q!3J&-rVT~I=IluqLVT}YM?G6w-1PP=tF#Rt0hl?ut1vad3yujqwQs2u=hwH3zTZ%VPhU)B5gWS=RvucK&sHZ!vjP-qOn2J$i +>r8gN(iwR+1!Zs7FDgsftGcd5nUBy3{Ia|f!9DGG&kg*`lxGaZC-~R4vtF$l59QQEj;V_^8*(Ss5_le +mFK57F%B#Mxx{-6#8=zFSIl>$9{aUjkJ`<+}z}6i7T+C?NU*_)r#VA8`eJwu5PdIXKMGmKp9p%uZOfVg+VQK`xl5K$WhY{9Gw5V8zf{q +$#R@3QgxJY;tXV(?#mvfeos&0Ovu-i0x6*nSM0K1d51SlgV>EmJ?sVk#O71wC|1fPNJuLf{GkvMrJ^1 +XiN49@Cvr=o-_+C)V@u`D};kD0d%GG8LstJcBSY+n>HHmY`ntSM%$0f2o%j8Y^}}=e5}VS&<_4$zsHF +Q<%eQ;0Op2)BhsLY8U(3p>MEz`N+vke2-8xb0(Wi#tW7Ci9={Y{{P7WW$Io9r{G&22K?=)mmI&My31_ +4b!@(#Z&x#bna$PMr%8tzt9n3YL9K!!G>WYCv@Rs=-TJ30>feuslIfIw0rr)SH!=MPWtlZETX!3(8RK +m=va)Se*H-J=XBuC?7+Jj3-51hh9g%qn7u-rv|Q<(hIo@*nbS&T%aoM40LEg&pWx-BT+igqU^QA7kT +H;@ZML)cBj%9Z0)bSq@uX#vg}f%GQpGss~H;G!WdYS}_xl6c*Xq>7u;=j*yaHH|@CO6@Qm(4YmX7;>Q +m;l{^~qL^okD>MwN9E4ZO{e9i;vhSPj@ZjJAG~Ifh04ENbO_LooMRQPe-CA}BfB5RrSDso?_LEg)kxg +7hPN*I88yq$|Qc=K}N^@}NlU9)%PXe61?z%0CH*OxRoXnPB6q)MO*XgpOt}Ye>U1p=(q`XW-U(L?x>I +?yZ0lf20>4R2cxc3^Hh=S%GTNy=e(_2 +#?(3xh!iIoV-|qr6aZ2GQ6_B!1N?WbgsHlAAz>zyamV{;DlY>4O;y?v^__9I#Y821h%e9xaj`SX`IkA +trDrZYD?i^<7k2X5R2(S5O~L=a*zlyL0iRV&Pus_R-YGjxn5OUgViBF8wwkl6s`|tWMSNj=gbcbVv+z +C1KC2x+*WI{E_2f$4qO}K-)M(T_l*PZ&P@W@&#IS-q&t1k4;(bRkQM4vuVLU|mBpMLIzPoWAas%>*47 +BEkYMjR1TJPPJGDdc7>t32lZjcoxWsj$_GOMF)w(e&e}3n>;2=|B6iY)VGLxqoOmZAc(sF2BR34xF*e +SXW@N_1=5nlwWgtLH&vJyc|cphW~g2H4Arw9JP|5`9N^j>=?{=EZ1La0ta1~BlGqhl+ezVZFkO5Oy$3 +^9_XZlVBc91attH3De3!LxV;Gxzg=Fse#Ej_CgY=4`i*GaP>;tH&cV54C)CIfIwMr!Vrrvc +vQ!8u`eFsVr+L-NHdAej&WV;`yg`G@#64rO-P2@7cUfUc4xEb>rL4ihxM{)SOOWoCXf4KZF!Rp-F1iZ +^@ljTwYL&ZU(B_mkxyNWYN|czn_2$;#omjTJIK*O36oYx+kf3TO3{iWg!9Psy!{}z7*WmS1^4Wy?)tZ +v9Hi&LUR=D6}d~BY-EYMknajy-&;Et*JZ$p~h;f+fAb3Dh)Ugug3yve#M5K>l7n1>+4MIw{f1%j0WnP +M?|$ckwSgIN+Rl5gcA!|0;3$WT~lF`%%m(Z*-_5GmW0dZAnwZNJVy6JNW|tu9?apax%sZX!6wil;^on6Lsp3s}M0xdcDhILsaA1M&HS%2?1OYcXW3LcoBC24-pA8?1@bWnEv|c +fzdU)FzIb_d^5XgF+qXgmJXg|246d`bAOYie2?4?=WLuZ&kzVgQ;t$)HvxNpT9gKREmAz`M+XzsDFED +fibNDIw8?gw;yV1_WK0eec#Ml_r%#6Ezyph3p98#}8#UDLX0=}_NDU0fG+5j0cy +?>|tL%G9KdHeI5Y4Ul(PgU=HCSi2CsN!)qtB@Mi#PCA+qMs~;#j`i;h4{`>UUoR=eLZuRpQbmHhiq$d +l0j@-DoU_6qMW&5CiXSCFh6VU*h-CD09{rsBoX`F=`_sRH_*~7Lyq@XKFhh4pmKyH)mFndR?G`b0n+) +W2r2oHEa7RSOMM25*2v+|LDe;Ed59M@-+E#gr?}XvUikL@;aar?*Wufzjy;THV2+{t}Im;qTq6IZ3lf +l@x2&dMr-#g}pLCLdFl%K&#X=AHw^@$Ssu1D6YGdIyR;A3p0UzHr#RH}}X4}r2@$`$MXQ#9X#IU;i`U +01WD7$i)-=+l&{#pAGkJ+{T^F;(GHk&qW89>lp-MoyiY8L2bJk&yvVHScLPR&Sy*}P0=86p@5wqScv$yvo6hNq#GqOCsQJ?1j8NgN6ga!!c<9)3#wlC&AR +7#V&dT@CjBGdfe%H|5h|Vo+o@ew3tLXj(B^VkGXW_5F0fu#7hF~HhR^%KI#zxICPAsz5$n`DFR4N4L3 +Twk=f<8DI2Q7P%{VrnC|Fw7Sx%l>cL}rf@TuqMbBYKAC*inGv%#5mU5Sy^dVO&zump{daRHEZTb9NA3 +r`E%Tp$mip#t_RP%2ZjdIhHPfwMYJBr+F5w^_}>E5Ha^&qgz4xx+~%;&oLP)s?20ztHxuBmtw;nyB^C +qbWKaz#3|#A%N4ppc0tcqb>9(s-G`7KC+#d9^uO!JB!n4+a|MP2l;4K?&64jXIsZ|jsjjoK$aS>YF_aFmQf^;7HZki#?NdjSKpob~U)Qknfw%r-X +-Wy~Sy(=klGN?R!2pI1`ci?^0gb=UhT6d%D3*QL4EEh>S4wMzP;lj-Ek@;c+P-!P<{8JfUH}i%s>~^o +!8vCUd6;d@_gZQ*>5c}cgabg<6QcG`f}eu|_%yMU4Kn|P@fKmiQVQxm^Ca(O(Ktt)Bj>X#Y*-_u4NVu +{SVdVnuc{#=MI4+!8_Sm9-^WmVXvQf~W>iO!beXF?2eR&!*rTlzW|;9gpZ56;<2UA|vXb2fycG<)^YL +Og39b$mQ2mfiM@4a3<{UqsOFHlC#~oE~COQ@=bUh!oSEszGq +|ca)+J1J5T|F>Bnv^peL_FXfmsqVkb`bfvp_-7=a~Q&n5lt(j=28gl@wSA{dT7sV_9<@iOFIexkt<+XQ_K9WQx +0<`6F&~DxHojqRb_#vDyjz%7S5I*zl>bUUclC>%vbhY$OV>-(A~)yOSYT#ula^8jNM|0eO>op#o7!25 +3=bePMH*?s0VmL49ZA-?_j=A4L;;pK)CY9>Aw4`^-YKfqM-P4*_qS=lQw~SEGV#a>aUEbY}NHJZ5s2L +L@R2`^Y)RvAZ5=tXzk;EW0%-`_vX!fzitMGw8EfF&=-WkP_?HyM+i=qdS?`l;gfcls1YTU~(HMn +jNCiBbjkNK{EJk{u9fYH10g?WpBzkl7Smbzw5n;O6g*naMUzAr3}Vq|En3i9G0_z{BP&OpIUBj<jpFZb0I5k3R^UNsN_ +n);ND0Gx@IE%i6DXYq}b#iM228PEvokeF%oZS@R1igZc75e}K*Kq`0H5cs=xkju*0ToHs)Y+CFBUOld +Bky7yi&&9Qp#v-`;4#KFb5oOMopxT%-Sr&P^(}AQ^}()58jd_x*?pVTepmQ52xfoVl{9e)_c-LBdfe~ +mMkaJOI$)DiJ)8qK=(uwkI5GW@=e)$cR6%rWHo%QM>AMKVi0QHzO`V-A=6Ac9%>K_u@L3JWyPUmq0RJ +c-BsZw=oQsT;SOW5Y;-+$uRS1 +EZv{q|1jGn0^{&p!El$7r_Bd+9~tH!m!z$`n8Q(jjo)=Q8v^*3Z)&0X3~gNJ^*_nhWeo +?0ZY&w-5xp@X3Rfo9iHuvBPH;|{nIpIlWFQ0!P)bUG|f5TH2nRDfn4WfCG@I9AA&>Js8u;uLT>Y;sSTSj-=eMlqc#^A9&Nop +6&6elpa29JWr3N-$6ez^aoV-(S~VO!u?D%=I?gZp;_I{|pGtUngb!O^v_hZpm@sOYLL!-RdyI&(jnZm +t!U1?ZIaVgA*94iXw1C;Es*I--1LyFj_yqoY(k{LM3^;s~<SU>8!3c`Boq1i0bu3onbrE%{RZw31}Na6BLVsbxU^Svh0k4Wo{Mb|sE=0XZQJW^&27gD+K +G*#GD0~S!6yF(Gv`7Wx}QO!8~hP(XC19Qq3MU0?ByHJO9Q!?ucPMWo{Y3qEwU>%ps`PwVQ<0}vNusI) +~Y4B=?uA}R~>dLQW>l{S(D=hhjcv=iZ6{{KBqVMU}ki-Kr(39rp1r +o0R%3$rMn2S?u&A$zHPdrhHUrd7OEV)9WQ)lwrtgxfFilviKcC2l5PJ;`@BfcS(K95zn>EA8@bU~Txi +-x+vMGVwIw`KKf+D;4FRi-0usJWLJ)91Hp9rtg~WL>76cx!*`AyFR*##^0+=Ywmlce9`mjjwA_b)a31 +MWw5C*E{JPE-U35Gf;q%aZu#mX6=Oy5n{E?cw626?}-fRzb%iu^@3_KkHi+*+=3ELfeIHEx*7!tDf=V +7`}QLc0bx|QY~du02)tLiYjpK)?-^XItV%~Uyd52}iC=6;DX{Gw@Xt?>=~2e-65+JgHb^^O4YukSaRR*A-e-bj+3JShefcjo?15)So$tdn)B!ZU}nt)&2VpM# +-YA=X1hLU#{$pY--%i5mBrXr!$e2s`fM2y{dDu`owZp+G6iLQQ?hHU>K+~(6!dqruW%d#eDeqv@TQkGW?R_EHVbxqkcaL;InJHdYQ} +eei(#qbFq03i|%_$v<>bMmeiHZ&}c2ICmolASh?Q{jd#m93HeW{!B19+w@2c@S({r|E*bex7I+$X7tGU=TiEVxI*R3(#`NW}haYQGV(0$f)_T1-e*l;^yTBWDW5 +crR%HX|9*XZdUpKO{h5%ZEiLx0cvjEuBw7aWG$a?D#PNZtlQ@GLWlWOMR!f@rMdJFJ90vR?ppPZgmTAQuWk#*iiZm)KQf=3w<_{;q%US0p~{iHjm@z%S( +O~wg1|rooZ?jUUD;s`YK(o$`xd5as%}>&o^^chp8tC|)0^px7K~~YEEc|R2)6;T+B8@_vcy~7ovS7FA +w+Yz4!zV@|G1DM{@tnMOEyf1%tLK(1nD~2sxbwAO_d?3YMF?y~^lnPR9qO^-Vup +07AxJBEA(+^_|AabPNmUOv= +X}NnzRl$P*`nX%GAVKRJ{_T0>Qxc^Ni3!3r-PeMu;jqWCS0n%!BT_21~3BKHrOc|;r?38d-V=Fx9k-y +-ry&OCbYEXCaCxm((W=`x5Pc8iAGBGPYlE*pAmGBn?!!Xa +Lff}X5Q;rcv{fWml5-nM|9xj1$(9r6(uEZywxly>jz%++$Wv#mXSGoZYA=m;>y^B&DRVeTeORwrYfj8 +xI-Ibu_m>ANys-s)FP$&ghmLH8D%dCZ+Jmm4V4wR=!Fm?WKhi!d;4#+IgYWsPa1fIH6Op10R72;uCoC&N&)#&MNk@UNIb-Ic8I^%{~352;h5}WD +w->gFJ(#T}K8x@<*Wni|#^9$^-cK!&=6<7AN35C)|2fE8$!`nxL?M1lKFSy}E9oWgG(`J?EJN)fSA*x +6FB)v*&m0Z=)e#X%M6&1p1~2xtMq?bT;q|#z>ObTJXKqtliwJ$%w1^KC=H}f8?9%0S4x?P_hwz9;56^ +oi1&M1bev;^$sI9NQ$ZWGUVF}Dl8T*ezyy{TVu%qCfnT4bSexlU{2ufpuE8INj>Og-aPN&Ib0(&DZ`e +1$Jvj~#B-6LnuG@5rU2Zh!hbzHfg_6<$}i$E1QBphW-pTcSe+@x8iVR%1*bblLy5Dr(^+Wcx8=Q8VKs +NPl^xY`JtW1J?Tt|Z&|_)f$8sk<%yzmaTd0O`YQb~ +>E2nMs15bT#~O$y9j<0_im7mNGs_Tz#H4gb)2jx9cOR9d=sf-Ikrh)ER(Vx0|XQR000O8NLB_@(I|rmM-c!3tUmw%ApigXaA|NaUukZ1WpZv| +Y%g+UaW7+UZgX^Ubz^jIa&s9f9S8&Z9Zu)KxQZlCvUC(h5#&)#h~s|ud64e#KaVErcR;Xf`oBHQwUWo($vD88cvHPYJ;$q3^uprS<~aPE6(-;DB!i=x3urus^3%%+f_Rpmy5Crx7p;d +dRv~KO*akbgCM{)KnS?Ox?Qhf=fiU=hOZs)rtI1+>km66HhlfOIn3C%Ro>6oKXxdvamEYWE%TR!uOLdXsg6zwElUo3R~!PfdDlW>gKR1aK|kA979{G&V#6BfB7M-g#TllUgJdDAfl&^U +dMSYmfkF6Mj22t**c#)U5B}`zct^G+t}qf8m1{dUx1$?R%d08g!do^K^Kd?y`R4od`_Ae`#6OeAo%Hu +Q(#c$;?V}%wga|v-f=kyTesMw?~0iBF(ydQ{8Z)=)}}*C30hyq9R+>JS{4})a1l{R}ae}ThhKw;hlHJ +O>R;KP%{RvP?^#cR!dX1V3+4-3_gLPASG@BsuX-ykMsmH&miak45+D6UkCW}vn4KpjsZN^LVJj0&HL} +^U6iIxw&iIWpP#im-eu^Q0dTONBpqHS*Zd%&7!jVI6})6Ac=BqO@(+ENi3m9`XYeX!PrsJ_;}V`m&w@ +|OhkjEtL5^^)iVBlI3u3M5?rnUbAD(WE?-q8+bU%EAptaPIzVodb= +U59(X)62nhbua@q;Tqg-mL_1n79b?-~bYrf`qkfk=15yI!+L{+Zj{AXb6DrHA>jucqx4I)f&)tl)hVT +Z9+}5(T?D%lW3QAw~u4iNcc9?JdYUjuV$iTVEpm1+~kSrA7FRk!UsPns$gsWby(6FVo1F;XWa#^{ug~ +rx4xEVN^zIv|&HJ{oZ_|cVN8H>$K7V%dzJyd7B|h4P2cb4pMSopvkm_HrsFpke7#=0YTH% +TU9PfUmwyoZ6?g%T3Cm#t0u``CDqoJsl1tHo6%lRAffZU|z}i8DOy25|3--DM4v@=4eZmAt2eX2*0;b +iAYz;C2nO-Ql4)~{v2rJt52~e?K7j&!@39gt_0(KHvC}NXMpkq65|A0=2I4upyZ6jUFxSocNN*-v3HhJT>(#!hizN17a(3K +h6(r}AOf7<44Yq=x3-%6ka>|^0mI+x)P`R*yr>ut2;sDyf8z@tFehoxGT`?lC*skJG9=U{dHTkbsAS%pqILjLH!HB;*i_WRUR)*j#Eh9kz{pI{xutYj#xqo$ +zhYZ4v2uN)=1xM6BYpe}~xJcWsT<3t8IS;u7HS$)XJJoZ0ubWE4+8C?3!!cQE8Sh81YlwI+{J{ZXK%o +}eSbVX)fNCU*t9hoT2H4s%E8eE}rF3{rrmZn^0Mj+ui^7LaeovU?0#NJ7S_g7e1x#5LG#aa`1{;|v +oUeFd1Pr-ZKiJR__;)M_uAmOb!_%D?K92lg!EO5}#_9#g?KmnVYv;{hdn9_-@-JdcH!yII8Sny(>^As +B)<-ShACq`CU2!F@4?=4{!OhHpz0PD$dKUTRP+2YnZDrTk;u)eUC8kB91vZE0Y%3TM@OFYvrs +s+jRbWuK*H1=hQ+WdN#*n50XV{M-9V8M7HZg^Ok>G#|N(538LJl8SsIa!P6Iw}MGS2V2gX0rOqEKWtL +=pyI9iP{0 +Jrc?y^#odR`6om~z!6*UBan5s`xXJdlbr4zf29T_FDfqLmoKo@O5Viz!(R8|RN-%>78syZe8<+y{ +FEmnqP)U{1Xao$2kM>7mbP!s=h+D@gW!g8s&pfG?hO?Q0vr)PgQFsz` +sa^Nb_)_9IGXE+FBuQK4T4&|~FV_?8T2H6gL-W=RDB|owXrRKo~By}mC;?$LAJu-pm=H_Fr2eKM8{Hn9?qFDuUJr?+kqFx?N>Q(Ai^;!3K5s#*Yg}IO4#R#Km!UZ}OY$S8>*=InAzj- +L}^vtt)JJm-?2YO{a +Tn-Cmxb#QG6Kn)%isR~c0-~fWz{?P2Idw3K7Wkgi8y)pZCQ{@{l$z8_3&?{XhiZ>oP$6#iED0w9mV}Gu)c^yM+sf}#5 +yS-E_9Vh3G6DUJ7%oQM1$=MyTco*ez^e}TP+pmaCWj ++wNLA?&uvZnryOAoJYwY6xq!4t2)BI{eiP2vvyr@-)A_^z9kZ_&?91V_VIpJ`_VSCRNz#R^VwDDBs}G +{FV6Y;glOr9rJD{rMsD*=v$5l*3xSn#bGt9i{_R1;UB{4#|pPXQ-szqOv|<+!9hZSpRmBfn^Rj5dLEPsfH*b<_{fzHLxhUq=(%M|uyQB~$}e +MM#5ml()Ilaf&+J((VA<&z20)Jvt-7##UuFB2ksvbcKi1!>kdFGSnuY8Ic#BXBy>+zJ8OHP(W@B`7MWLr|i|K#91BXQ&eT*qOR)eb%7FE*W0obe8g8{|ytryVlWQ#_~xv;;BQ>Uv%~S|j7@HPfdV2@&5&;iJl6NC(OHP*k|6C +@WfVELt&D3il0?RD}Nimv1kg!xXK3_FWG#-qG{y%tb4iXj(74^xmL=!MaFK->f*nh70yB7W +#NcTibb7;=SwflJ6Xf4)6wFFPi(WZQ<1!o&YMZ1#!8v)#@PJo+>`A~QI{qq~)rnqwqIXXv%b4#zvR_Z +t&9njiKhUo~UXo2n|4n^;oQu6x_1lch*Qb~5&P_47vY6w+*^%>@^ESFPxd3jOEJl2DKi +o3AWWq+z{hloey$RB+kJ+2~8rByZ1Zm}q_{##AMLfT1+&Opm3bQ~UcpC2J8SIk|d2& +bZ@je-|5z~2i&Y4g7(}&K2Kh2-W3w-7kv}lM?YG6M7eb|4Czo<%sPV5NjqxgvzdFZBw{ut=|R|aeuE) +00DWFI3ziE@Z37D`Wec}QU%rxcbNdo%4*3G#oMT@USFM|fjB_3G33ZscVqyz%-$yaX@6aWAK2mnY{22-|9jQbfB000n10012T003}la4%nJZggdGZeeU +Ma%FKZa%FK}W@&6?E^v9>JZpE`#;t>NQj(MOOOZAqu+N#D_sr_alP8ZKec=CYbHUfVY$RLe1!r|CWL{oBe)I&}A3w_1btM{B)e +W}r?t`>HMfX*%3jWc)wW28Ud8`L6MYXo`Fx};cv}n21jm@@(rA^b>KTc-Jt3S_P{om31KTX)naywzK( +xOP`1)s3jdDcwWQNu;rRAR#3<`UkI>n5+tw3x87wk|jVKr`2Q9VdASy`eGAirg%ctrr!Ik-?t)W5!jp +8E{m}1~-8pXF}l8E)w4W4o?t3Y75S}t`@wIvE-@9R*B52+G69BzMbNy;Lwscn@ZfkSSewBGwdXx`z+f>Pd!^iM0)hIZ8yVx~v5_kt-SAZrWtEGRw)5nh(JbU +bY^?^670NBb#REsv_3;4;tiGLSA|5ncjpQ^IhGT1%R3}-NHc7r=?Dz;8xB>dk@2zz5$wXWgB=6R7fTe +iuY6|0H`7eMVzwqmzjNIK(c$xgP-s-pGO1OdV{TSPc)7as+Ax!@mx@Uxrr8nBG7!k(7;`i4H@97zHMl +_cztosWn)65&wP=o0$?jUj2h%}Xw!qoMnABJ*=tcfw>NE~zheA{rFX({RlBIks!Qz~2+iduq%n9K^d> +@?i!;1dkD6uv{cY|B6;kQ~!kjl0Tt@7*9tN>~b`I{OHy3yLYqqXMthSzka%qUyt|V-+VP1;{to^9pV~ +x&&%6fR3!q(Zc~xt?)GJ~1>ti`JHqoGef8 +Hdr4^6T-%=dr@t>0eJyjz65uUMI&VXGh2HPp2g4&h4qjaXhg?HCE#9@)Ai&(Pkip%?MIMPA+nM!{C*d6|msunA9oXJak+l8c?qdaKDQl|ZDy;lq! +OXeCIaWL1Ik8ML;*$tB+;OAZ2-hF>a9&>c`(%~9h@2{Z*SF1i9|4}|oWUv&PYxf1<8-0yW!&C@&D5j7 +nU#6$}&n-NMLL&qe-E99r(AX@72qpScFF%oGDN7-tf0ZhpfAkVx>l1 +P2RNAhAh>0TY&PI1FdQ!W!jic>bPs={dlp5-z@3Ev;p5&mG1M%dJvBj;$XD4`2x44u#Ws^rcPAaA$3`eq>X*+^>cuQe$HF3Uk5mJ>t1@j3?&~jYen^DF-W3W%uBDA13Ywd-iF7I +y6ub39aR>J*Z%S0DrGfAGsF3sifh^L36Q}zr7Z71|D6Y9HUbcup8+(KmBv6>4N^I|a+?u^t`nQ<8#LY~4#)3t*jcurEZlcc8av#h#{xXC0RVAPaGEI +c8O&K?;OYe{(-K^V(m2wlVeCm!XNTY_lvUH0_4p`~?z%c$FL*5~AZi%_1{!0G5HG +ndUdKGAHTbL(JXK(|331Gi72p+tm+D<8hdE^m=b9yZTw|9*yG_3!E#vEeegfnJ!nSO|29KW~=DAfLf&;Bh|m1c?LbVITBf*Y4~NduKW5XwK=dcW#q`pz0YU%H$&2?3+ +igb4h65tT@M5oe~BuihpqRn5=o0FLOZMyv;$L4J>@xnG*@XAY5X+Od69Ymol=)L%T<7YV2C4d#I9Vaq +Ahc(uSpVU2KPbv2!L#=YM?q?AZK3ROnvr^ef33?coE!H)HHB<0M&t=awWSXNRB9al~bBqTb$AA8pl#h +P#;XJ)z}DF|xyfpHXIi@;LPK=)aPj@iFS$L1(XrgRn#Ax6c+m$*yK{g?hln3z^|VpYcCzep`Fh_V0|= +zCzJ%I+fXxsWi%$?`l?|M +0$#)NgzmPH5p&e;OvdvW7FopcT6@Pebp0UEJ58fdLIM}L1e10HPHrfNYg&A~j%L0p2KxZ%784+=QYSh +gmN5E8+!oE#V2-icP^J;O!4dIPqKmd;o-S3gdq%I@V5acdp`^xai51pv3Wbt$=OK +G4Xc8G~C}arrPdr^L5_hSHQZ{pZ~Do_HNe|KE%)!U8ljx#JMVr{%vx}wWTkK +0_;lYP1D-Sfx?gsSk=7)B0mdrxBvXza$Ga{!JEp* +;Su;E>Tc=xf^jxAV0-k4slWPNu;y{aC&@L)6>3#;#+Hoex?*Ka1EVgU+h!liCO&0*eLV%Pcx!yd&Y&L +BZ6rN=nrA4!<+UpeqIR$WLR9J(+5JZY*v<1T#V|6ef>8ZPBEcm1kRe|ZTrWOu?ppEbXREA+gaoRz=jL +6mi;^;Q*qq^ZyLcbrk*y*d8$GX!2kFbkg1=_q&mU)6P;jqgoy#x$CL*S4r-Q;CBnEpkH%-uO41DTG-V +~Kz%0J<+JnHX_)I5e142S0V-ycuG#$*hPr+;f}<8Kh9!2>0u1eJ5vh4R&Nnb5595IF7E6u{W%HQ89R8 +R0W7ixM65m@T}dLjc~yVjOiWEeb74SzX#@}7G}zJ?KX@H+?zMS2bebC8>2)F@Ny>(+$ZWnRGvYgx29U +0f}fu25b_E3)M3F%>KOKpTtg3%@J0gjBb9 +_IDsKqqZsCS_^5;~n+1#|{b+x6~-`jCo0!Z_2Mj*EOZWg4E$H{b(g#(1vo5P!DlV6$wNLWZ_^_8MD0|0%%3mHPdtWxAy=6y!tW? +%k6_$@65Z;Jw?iY1a2>YA%#oQRxC6Pvrhwgt&(1`Gy``&(bVw!6N`KQ!*7M^9%L%dcH3{)>KmoI1QvjfUxltybL5(U7LXz(O(l)!x +`$|1(J2#!Hf0`BH!Lnm4zm57F0xJLgC(bm$p2{LJ;u!LaW@(DhDD(J)BiSw6zX@D5N@WB>rfs8%~*7vWXP +NM{G1Up@nNVsnF$d)v)uaZlDSX{Di_;wfV_y`W91}iNqTM(P)1(W@eP%04+TVrE}^&1=JwnMaQUV%AP +E@_2E?=J@t!A09s`e;2c0<_Pz6oI}dTF45FF2d^8R@ic0f>Ozwy(b@NU=5u&_ef +0Qd1wQRwyt4{h@olN2PTO(Dvdv9`!2{5{dQd;zJJa1q>U}VF?8J!s +8dG&)&ZCbf=H}892UMl@1LUXFz1qS1p&q(6G(9-D1_o^ +BSX6#c(*>&ooa-Q$612_1^WQ%Iy-O#AAS=EJCPKru1_`QSXdxLaIuZ}omifhNchubdFN~I^o=%%wcqi +$iyM%-W=51cJDq7AWZPIN}(y9wYbcqrtG<9p^SVfR=Z#L4M-f*G>oKmG?ZE{jb?Yj4QN#r1uw~1psc^ +Vn7oG4{OS4H$4f^$cpKHT!vUKMQv5TOO>#f1{F-Sw|{jhDNWDjvoq9@Ni}dX6{5Bxd3*=1AkD#U|Ziv +=ncV0s6oy5@CxH3*q3OuLEW>!eh`A2$tw3Bku#IJ3hMVEp!e}P3SIfDliOGgVqdY2Z2>hQ!f>d&%NH8 +ci4BTJAL4HASq0K&YM3Zgwq%;ms-)0BQ8M?DpE7Xhh!J3EqrWWdJbQPf3vHz@HG$u-H*QP7yaQ?UxH({mb~eZnj?7(~kNww$^my@TF|$(MZ>Rjt3o`L-UI@0yMId>I|mtDbR +f|x<2r_pwlRd;GI&F1OsDsPWdv>@wXs3Zu68~RTAAE7QuO6h!7w?)yR1}iN0zWJYLzh!?2 ++6HxcQ0Mgl4m!YY-(4kk6F*Q?BU_~ctTFIO5bxeaU~_j@t?%F6Ra4NSux8!PWn6(kx)xPi$CbD~fTM@ +Q#D;?xzkBiG#j_W`fAQh~)>qjyf!^3UZ%knrm7e%_jO`U;?Q^lK@ozEEqSMZ47ptOQQ})*nZ?U35%|o<(R~in=g?kYS!T>MwWLt584d){`Ym^;a5n7`FSf4Tyh7FTzA!nRr^*3OcIA +MC2VDAg%yKZ?TN-&FRZUS_79DF2E8YwGfc-`3R8<`iK52@dwT^a^q$oZt5;ii8QQtWIOc!q(y`yhPhe +%iiwGd`ql7{O$XIW@JQBNnW0pOF*jKUy=^}Ztp1b)$dQO99Emwt#(@6aWAK2mnY{22;YPhasUM00 +0Gl001BW003}la4%nJZggdGZeeUMa%FKZa%FK}X>N0LVQg$JaCyx=Ym?))k>6eA|G;Rnpt0bb*kD~2?QEIqtS1GXVIITh&F9qw>MR@ +jf!@^7tI(AyY^6*(WQubv2U+M8Rbye?nTtK6+Ew+XvBtx@z9Ip^Uuzr{job*) +C?`uwsM>*z%_!0(^bkk-*_@!tp06!7G214TGG0@YPrl3k0YYG7}`T$h+!QK)yHYwq|1w|9A +}m6^ZrWogKqs-`gyWN4`ZnS#ej3u?3FD$*>>neQEe*L;cH#;TAXf*m^Q77ylI=N$m{B7ku~{VoNRgx{ +Q=f=2^;lq|VF3I9YU{Xwd_0 +B$tP(E;G?w>oz}N!PDlt>e~hvWF7t3{?z%UL0IBrA&BP80G>C)2qVt1@M+(-eXw2K7P-@GE5>XncEVj +4Q-9x(;Bu|S9wi7jH73>MN&g!|1Ap!03VQ2ZpU+C9~>Pnbdl$XE@GAKRKW?v13N2mF4MR?7`N8nJyT9#e{`#f|R +%s>^C)zH=Xk*eCF5jx0*3igrYw_@;~9R}pDqgd;`IA$edgsON~l>*VEe)giw0dM&MMn#B|kHULIPRy` +-&bCf+5!r4Odzz(g_6B}Gp=E1(5a#$8o@Q5o-BW7;ym&n1HDlfVp#jp{0!5ah&+PLckn6%MJA6gyfQu +{oDAg)U0`qx6WpPGQGRsz2@AJ<}v57K`*VwSLQcr@kqE&SMog;xRB&HXO#VY|yR1@W{wvq74prQ4oaK +S#jhNUWHW5Z8PUx|Urr~t8>M$>^MJ@(MW?T`$jquF4aGb#08K)7Z9s{3d!kprwFi9L7{jjaKiL>HhxU +R`L~1-XutYx*aQ!5~^BlyL{TOI?kze!B9elX|G&t7g+KR%%)Xp#>;U{uo{D!l@pbyZC6QPZd`0zIrd)fixGa$9#YtJO)RIWon2I9N}} +Q{LdJvTENBmLXKOr4WeW{g4jYgi)M?oNI{UP>77;3Y3VhMGctIIu+BIFT5@A3Ps333N{)r;|_ +V$xC47;pC8e}QSu3F98k?*%L7xSN4xwQAHvq#=N4D_WqTMS5Uahw4X_bou_I#ZMJ=v#u#K2bALR|~qk +3q86q_-Jo{*hX1@31@Ns(g`(U4aq2f+8U+R?~ufN@o0mN5hmqTVpv0f-A@^1Nf%dCw|=u_p%N@2oD_M +DKt?*3n{$+{JqiKxc3ZcX`KLBQ%_7hQO;w3&+c?k~hzA}4`HZu3fnD0?09u>91PkiLD;IlxHXJ( +GyEAAZF@?QP84!>Zpp+g)Xdw@TKCq)>c(UeJ>uQaaUD`zD5c-;yTOx$V_Q#}_#sJmQKfGE-(@za)dNr +g*o#~bDMLvKiwhIC=ba|u!v9)>G3oyGypTHbP9IiVu?6Do8R}dIO-dD8*; +Lp++1PNLRIE?{Ve+M>2c&5?0W|UeS1&jzy4473N)Cfq0b2Q6MpmKkZC`;d4BSr@x+t7h$pDjJgOCxL6?w4}n%}s)>!ngB^X3SId() +sP3giKFmDfP{=xyD>8eeJF*{JP?cK*UaLFI?-5h*V0=z#h3%oa>ujszT)%H8a&mAAf5JZC+18n+u<;>Q2KwU!hpy*m850ux`TTfd>eRB!s4 +e+gK*Vd)v#V0H$rS8Mnkk^HG0`WaXX;FTiSGC7A`2Bdll0Q?f59lr>-M)dBz2g+ +3q0g_h$fV`409Ih<_6z3dl`l@;FtP*#H+{ +QcqR$S`f^H)leUc$AHSRHMFnHB3n1ViHUsTAfA{;+4zmp22`gm_Pc%I;f4^2#sRxBO;b(oo^P_R-hRS +ZBgO;yCC&a(f|-lhXytLFNaJ*dktD80Y!JDa8=I&8B{YINbAjI-_i#S_Hu!aR4g%0op=yZJ;u2h`&aQ +as8rR%%3q9-Gc7FGUEu(R^auo2DE{S8PAnZ*jt1-NJl5f@nx3@t+}?kfc1h@gRl2Ew-vVZeE7~GP(o9 +Vy(>5F1j9Oe+6A`+7_kKEMMI(BtutOzUfQHSwauV@pO@?)*+Orw^5x4Nfzn7$V7*Gu9!AQrY%VYlRv? +2!O85F?bK(vV_l8T@t`?djIa-G*ziGP9I35m$%I6Txih*c%g4^lWH3>eZ{Y9ufe2_xQ%938~bg3yEeN +^J5&JuWyI5;s+bm*Hu$e=U3!e18tm5weZ8M@!FPbca!~W!;hxw5jtgi@T3#@L}8?QJ0rA`v9*}-Vn*C +)Q^Cw#JMx>`61fl(77lmc`hNybAD>X;fMP52(4QSdM+HAA7b +D*W5z}TJ&WaEv1wC_HLO-?_C@UXv9`|N3)0ZLkjp@FH&v27u;zz8elBHox0b)TTU^=)lO}ndmb7#anS +79w0X=Zo%}#YZMVW~I11Z +0AO|SS-G8!1OpDrJg3ksKmUtiv6KL6>BOYP!tn$ATW>NC@mC$bbg)pgY4HWMmL16hSu2rIEwy1k`=+}+JC5QQ-2R>@mS1eGq<(IWXrTQxE +L<~~{$%zA^N3Z*@usFQMwW(9f#MMP1B=jwtIt^Wp+2o`WWS~9@V*yVS&V3;jy8{XR{3Qew&r3wQM2-7 +WP2vGP)PcSe@lhR~I%5$2g`^i8yEl)tNhODca2FSobN1M^z2PztSqpWtYK`>T~t)znrlmKA;k;+<1IK +4h2|4P2^FzXCsP3Ov4m8Or9uMwXT(4U-#->GN7*XQ;V%k)yrj!oCYNL&k`D$`&UrD^oY^%@(p#n4_gB +DQV)l}arkOD(!xrs;AW!QXUw=dcyh@iXO|A%toJD>L>UWA#p4LhFyAB>)OU2{!>Xac{~sni+wk=q<2s2IqjX-M_%C;@rm#~TN-qvdiP5SYyMH6x77b#E7Ms~*3K=-3c;ba|NMClUXnyPulxTyAB*Mn$5T}GAnMD-DQc +jT0LCc_!jAZoangF^6LfRh{V$R8^fjOLG7@cioHG4Ge1jDm4$G@nYrUn5F=jT9{LnQW>qC^k7%Hy55R +@OwrSl2>gj(Jj#VCP}V(?=lP9@ydeIoY)Jz8%J>6F39hWCSE~^*S_lh6I=R7)gcnI9=da6A@S>H#w!} +XonJa9xy+XCYEwVHkMp)OG4F{MEmj^>F(ddo +Ek{_;tGw1i8JG%znzG*M9S#`Qxvdj;p5sVSvXpC#zx-I43wJcc9#SsQO=(vLZfAUS;2L~F>(!X7<_#> +SNlVpIlwY^uu?ljz<1Rf0LiO=%xZH9@&^Vg}RItBsLyuT}-Pdq#RAZ26`t5btGGkc>sCwQFxo^cbYag +3JzrznH=QFI3Y`nAX3#+4uj?&Rd>;w~W3F<{X49Jn8CG;SOy%`x!V9i-u5MM}8PFKU<6{NoI}W37u%Q +gfz%v2_|5fsaUlE%hikN>Uj~F6S|0k8)}uvC?>DXwe8cj@2AYO=rx`r8IGWz><5`;_1kgZAq&c(cu%` +rvRx2{o5KK`p#WatQ_Pvf3P_6K0Ck~xbJ%ys6$+LEa&9qkPxm6Pqv`Z0{wL=s%EWyZ#B1uNsRq#HAp* +uj^Z6<6-AdnJ24p(8gd6udZ9{YkqPUuf#pj8nM0jO*VFQ`PGPd~t1pHnW0@WkhhFgX0X*PcFxFULMz^ +yo^}xurVjipwPFH+_ACNhH(6U(AFikJeo{$_v@6>Z-Mb-9zu~6dYUi5O1ZZe}VR!H;`oE#@?jT_7ky+ +JxgsctQ^yVS_H +-uc!6eE>Ji(Rj=xM*V$`H4s5q3YnyCe4cZ5kxjs^l^h_*TG3Oa}@6X5Oo->V%Zxp?CPDc~v=_#!BQ6{ +lt&=AD6r?5zV;L_c5n6FrZOhCb~mhxeV>Xx2;61`|a3J_Mv8%9~~757LgX0B>@tGxX8>HZ@fKsh;q(? +?6MN!PdKps9B!txhjQ$MRdMn~9NllEs~yG_49Ln+^q=OS79&UP(-*Dg}m_K8CsPP-{#J1`t#q +6mFfZPXDi0>tV|!NwE928)1Fm$PM~2&5u7Y95==UBrxRE_A`BP>NjBtJPeE{JJex}2oFweP|Iu3@75h +r4V@lac1^=GJUNE`p-QT(=vEmpSiiSxhB2}h9i36v!uNl)mlQK=O3Ki~lV^@*ij4s#=YA{@k16fzxbD38_I4SwFBg68hwsTk5{$Jj~B*uiATD`H15O~#yBf`nPjNQmc-G{&oNfJc9V= +S00>&W*FrE6_P7N7rZ4-0`#cJEl2FD@x8vnPZgNhhqT4q{uPr23@;Yd2s?77%x}oEg+>4$pAzKUf+OF +Z|`Kn!y+7)onX=3VC(?e+Iwa)Bf}A6M$NqX0IIH;+ls +TEjZ=1pBvYG8|x&(+{VtjOk(3UBu<%!iV=omzf@y3c%?YUT-J}Wvck5Pb9g|VxSt*9fD@S?=KKI47iYz6gUSl94<}YnbxsT_gF4ZhVbzqP}Uti*LcvY=?^U0EPia&IVQB(317EZ>yAgmRS&+sFH +_>?(6^43`i#=j@vWIPlu%Dh&Al7p*PEakQQ+!@d*TG*xVFFvyPA9Zs1GE^b=Iq8vDpK#~W%-BBLK6t +<_|tq;k5yX2p12U4X08C!>DOzW)x4Irmies@Vg*JzwdbZ;)o91#|6bi`{eP`rsvDPEv`6_SG|%PFwAD9+sNM}hH}10<=!WpHWi1#YQxy_4j@<_e@$Sij}~_&zc1%u +oaOBr!;t(;z@E&MrKS!nrLuN6Y%Tb(^_P|D}4ceD9!o>gwV&r+g)WTH$zV*aeTD{BQwWFH9xCi-kGui|qMv`78QQ#7oU~16=Zg}woQ0`qNf|i$?nv2nGI?E^%3UZb +eVsb9DgIvn{jh|=A_Q%jHN147F9lfqp79__C9(AlrO5~|J_QYhU*ui^|F&U%6Nc_+jrisl`p(F^CO<; +XB49tb(K^OC!As8ER@i}KcIroZ42jD$9XH%*P5Va+o)P4=r^Qb5FexKeK=IR}Oyqj% +%m~dpOJDao%8a*&C2s*A5k_5IHiBKk*WcPF#_I|9Np}h$I(tvFBNbn&%w$qhDOKOm8W=Pw?=AiHW=w7 +GuUsbki)SnRfCNG%`lI5ok%*GAi_xr^k6+49uv7UnJnQ9SXZcT}m7K<8hK~o@%;qQq0Y1CQ{8n4)A1% +LMKVI{on-3UbiZKjVTc05-&%piJR5~&Hbr#Jy=}~aUEQ9UCwQu +pn<%@g1Vt84e&Q7k>i*2xL!hjyL|%X7OR9AQEm8}W9|UW=^IBYs8Wmh`U+L>YCYh+X*E7mm>i3=`KnZ +x+4xS)k@`2w5=iJ`vWi@jf!UTJEu4IggLyz34Ho5*Y*OXXbFTpVE#z7xRrYp#?5v=dNEh`CuZh(OK!( +Zl-`S5|y`@;v^-RM$s;J*?wtCSpqlvrgTE3oZh#8Vh3w?2HJ;8QO))klDLZo!PZ!(bOcG7~g$D6*`gb +0GiieGll2#3V@zLUl@FH_$>I6Ht8n_ibx_G4CH+VT4=MB5$&vt4=~SsjL)h2V( +b|^QBO9H{zF&M(XU505-rVMe7o4n6X9LlVQTqEz;Gt~AmPp&z=XjA&IUb8Q8VDOiZf +^1w#9r5G{f^#5cE}t^LrZag2LVAnj9m5uo>vS4adiRhMQ)cIiP-wbx-k!$$fZDpQ`{7f4NcuUj8s~Jw +iEJZVDyb)RtAj#NA6u +D9tC0>+0a~NKK9IM=!LC7YLmld8fzQI +42_$Obkye&wV;_1o4I#s^D8(ix^7)sr*$3WeLKzYJz`9$Sw8$mKRy(uVIPc0L8&HILR85&k8hLP?dgv +r@U;!gn%OeVZ!%OBLmqlXS9H1LA3!-a-1JO76?Fp~(?4h&zu9448daBb0XFE;ZKHc_{OjWEXNkNi!Dd +WlO;_i`F!>7Y7x2h<_di)xbpsyd7&c{q1xL440$^3KaUC) +6bf<7r)m*Z8BfnEKd?lOHu!AKqdHsPUk;rGnfOoLRe6U{z^bHpPUb#sxD#++QlQjD3veDlpL07VqZO<&>tE`2P4u4N`q2?6C`VL>Z`b4Zn73#o+*3)L5BF4~IM`71RW};nr +gMLSckjEGN+0rnA2H##Vi(d|O)sSVYX2E~#Zo9)!+S4*8c&}(gYqgMqEh_hl_Qo@{Nt5BD3&e}o67$} +Vdp6UJmeBmo~1pxko2Bh)R`A51@^l~y@BoQ4lCrLo|*rBNXL(}SSObbKlVnaz~bZQ7o=J|I9=isGCpQ +y^QUEVtO8U&xoS6NR(X&@9DHF&?T$Y%rDo1)k$ezCh6qs~0u3U&AApvG +G*rrrC>qf5rW97QOlD$xnE5%Ta}3Zt&EQy7V&;I>Srcc=M;A`!%I{v&2Bo6y@l}fTec3Yi5p@_^R}lm +P4V)s7~df#2%B|XK$ei-UZ~uV182F*c+GRUGi>|2<)`JM+f@2Yu#I;Z9|oTgRdM_ce=jC`=|QFsSoWez=g4w>1FMKwrc133XIy1_C!qRy+P36dejR!}aa3w&8x^^x5YFEfRzVK~T44T`(xmmoSqITu^t?|}c*Ga~hez@)eup*$7&l16n(yP*t$ +%)SF4!*>D3^vbm9mCTd)*;(|MP7ZA7)Y36vOJiZSYw+O_iW)?9|P +4KppJu1q!@=V@$|OV;#&VV|+pn{`+hg{KvK##=2#K>9tU|QkuIX@L-8-#9yp9w`=BijME>^f-WG^)4) +Tg`IXcqaBb*HNOK=SDC5WE0_P?n(H>2Xexg(pT4m7Sus5a6L!TjN9Ue3q)fMh}6Xiuwm2`woM)UDLLs +|i;*HW@3!3`%Pm2E-iF7U+-Z)AS62f`-Vfy{ylz@i3W)ors?Z|(rfCFN<5C>jz!#Oix5_=c80G<#xiX +d}fk=S1dj=rb_r@LD2D_|wNXP2RSh0dOw53r}6-=K?vw5fnH0`q5V*FNwabSkmCy{ +eQB{3+>!L+3K+qxqk`#GZ1OhFMZDwgv6{+30+x_>>kdjSOl(#nn$yy@kK67SBi({)g!=&2pMYDhYY}c +tHtCW;b8KD}@gxm1CtD%GXM^Tw!lR33WF|N}>&R!bm_*O#Beia&@U$?}B%bdOITM5sf4YaLji}DVdT6 +IOst6NUOi{1!%7Fh8{HKO8D`~hXdj}ftGP!w`5;8O){pz6y;8!jcMmp^r#>XLLvRfE*U{Q*FZl<(;wx +x0hk=^wtZydzO>;P+m1aD>JrsYAomu|?gsLJDc}|Bi~;~1+l-MY_F#Us@3PXGUzRJ{_xxEUxz@}R +`4-HY78rhN*s&4?3saVv2D!^wb%5%&)O}0Rp)Re(dd;>tV&d{Fc2^4Xhm(`YaZUA3ER$7>|S +%!KP6%eMlg=Y21ubC@*fb>GFiHKA?Cnud-FdDXlpYr57fe^bI+#E<=qdGjGz$+w7o&p| +#{q}5iH?6OXu3^%cz>S!|)&D24~9hW`mY{kTmHOkP&0y6}3NqMx&Lw>h#+DLxf)|@u3r$s^n3;M!P=w +jj1uYA=_-!*K>mc>UUnhXugh74c%>iU|lLf3TA{zu=;`{MsK`tpF59l4>X5^!zU@|y=+Oho#~)b=A{- +q-A*Qhj3{eH${pTC)dyp&!3pPTUxsidkT3wd*MOhYOZAT_s6*dx@EC@lgixk={^M4e@ly4}a +yspbcy$l#Kz+P$nKdqeLh{E#*k+D6Nxi&%c +?&+uUO`UHuTYqVMb4w-E!zxS!a}sByjUqqatK`EhFWy8|$X7&~V0d%)_zF>e8CsPVhdVbNhope`;9gl +4_Qom;3leqq>)8g}fZ5okOcayzKOxQMlqcR&V^BAR#-P0sFta6r~f;X-#WkU5qF>sxj&%mMR*Dg>J96 +f%^exz)~y7Gc3K)f+8puUsF~jg+&ba_Zv)T +CYg1vN!KG@A_Wi`T9RUHsA_l@en1<;e}aT~@HALJ};IWWxPFs;^6TH0Z@z*wuOIa+03k_Ob4KgSVmRC +4*bUZSSWa!{0=RNwhwT;x+$F%IXE(w22Zw~F0v8~Y7nl!LT{BZq^73+#Cm%~{8(P%%Y;jvj?i`SxJT* +-o#{*L;V0%yvTJA2J?Gm2J7UrI1Qj%t4oK}Mk;#@zk2U|3aVM>8LPgS|-bo)iBvH?uoSf*CbU6oP5g96*T{}pS;uJmmSX|f +;=T)4)HZN>!C+EvVKHHprcsv*WB|`=m*pT3Ai+pHgEEeg>apJr=AJJzOX6ic<&|qP*t+4okU@ql}wBd +L{Jh@EtqB}FQ5t^5007u}8+9|QNQv-{XU${;XfIri=3aF+J{EkkSj++Nokr}@wdMHC0i^dHu@I9wgZt +4l{-dpV0LQph>lWu^xv`}o+ct#)U(ekBP{wn<0K_YRanVY1;Q8q}c0dD+uFhw5Iog#7Y2}di`(@lNXr +YSmuk#1Bj>3Q;O==>DxZd%wcaI#r{MVa#`c-exzs;pGA!~QkyI!9>}=gmxhQHZYyovc#C{2h3(M(ePM +QRY=v#V}HAg28-zd!00?iO(guA!boHP#2hupKW6D%VP3$kGXoq+fms5KBO&vRQ)z=4{R^)04F8{x@;U +3u@WC1b34Lr9zN?HSBojD5$Zg<^1uc8wLBcm^=tY)4c(18^9Yo*FLh3ZKLH2|r_abk1l!gSLWGnw|3+ +X#KrzLKpdx3m;h!J_S_VsO8Qh(4@T`DZOvW^Zfs5zuG^QkunNH02TK7jgGIy|~o@Ea}%ZJD1nk5Q*Av^)h-Gq~!NYi(cE=`CnR#S@h +EX5@rXLmU9BA-P9E@m)c{A#CkOjR5b8k~>^LIMWv8I#+w+Kt9T6nqx$LU6cA!^5C&o0rgY9cIIR=2FA +oQ))6MC2nqBOw}EAL>SY3b(k-+V1&EUxuaRn{X8&Z>)0fjp?jWe=91TB38EGI(=JWBtDoxL_u=$d!?x +eYL*`pB#+@9;s{o_c_!J)w!v>SkDt%f0g1vEA6FA<7U&;_ZTpEn`IBtXqvEaoS4_z1W!ZpH~uhk5+(Y +@edIBAC$pYCc4^UvPC(eFE6q@TRM94cy}Q#_mbd{RGYq>=N9D^Ra+W-h4X(KTiAbn^XA4!|0E(%N)ed +cxYk%NCf&p*q_wUa3*4yXQ0GP2&Uoj}z76tmJpNvBeYH$-6Oj)pQno`Gscg9(Fy6`0TAb`+`a2lFk2g +gR1=nP)h>@6aWAK2mnY{22&!biK>_d007t!001BW003}la4%nJZggdGZeeUMa%FKZa%FK}baG*1Yh`j +SaCxm)TW{Mo6n+no|3Ek>EElRCiaiVlGOTOUA!yNdY4&6U0xcceY-Ca;sl;B<|Gslb(V}EI2!;s=Vv* +zma+~4A9M}Ulh;+-gL%C +0SFZk0~i=h_M-d6lxSfEUD$;WGm48J=sXefM%BgqHGQJqdQCTT=FgC`Hbk!&wlT4&f1?_E;&-3 +pOvs;OTl2guD6q8N;oiG+LLR<8`b5t$Aq5H|q(-@Rm=3eaF#dm=wwn1EY+LWxmyF=a|KRw +qpWPSo +e+H3#iK0ZGo9p#g`ZC1`LJ~n9^cR*C9j*M;D;1z%_Dtq +w8S)su&qsEjF$5-)$(yporC@}fSv4r!tj8D^Zrl5553Agg7A`K>pSX0;cw31INaUl&afKrchrpIoz!+ +a5G)RL@F>2hAvMv~|h(fis@d-+Mo#X;n)IEyhB{iJG5=2I1?0Ut7Wo`@_zYrk+OK{|lHf7wq9t%YCct +s?88o;$yYYoO=r6{ESsMLK^yQPB&H5UfT$c5vr#`QW?gG@SKT#sATBg578V~OTo;(bO|#OczmJ@3)GP +s+YmjV#CZpBlpyF&&TbEjonl9+gag{EQu50>Ma-V2AwibDH_+jSQv`jr3shU`jY>V!V=8>mYs@q({J+ +#x~faTv9WGHH7JiCgEy@tBj?fxj4D0;%&{wveFR?*>BZ-eK@m`_AsNKNXN0rrEK~*O_?ieWWSs=cJ=D +l$#_69g02b@>>`xD@gVJ4m{Cy^L2PQThflG5vGcqzgKEY7{e_-7*iMcQbWAxj`c*}aMkudbdG +mWfpwoLtZ0X$Ya&#!?oes0z*N0LVQg$JaCyZ%Yj5MWmfs8XKd>GYwx_nz +2XL{QMRr@v19x!S>BY=+XNydrku5q=BTK3%$MNRozwddF6iHD|rY*4gFylz%;o;$V^N_{1soPGh)y?O +h75sa%tJYmzm&(4W`=9FI{a)AArnvn4vrSuX1-~wfRkG`fQvWH|+4@SR*dSH)u3byrx;r#Qb!l4tP#q +THSyq +XbGv&8#uIz5M>+G4fCp?u&)1^B@5iM(A)LdACar79}b;Y9HDi?*%XXwR}w4Q{odNRx)$8X!zkhqAeOX^#I&_CoeuOs?C|gvythy8$9O=F-)Y|l&eg2t%CsWr8{*?p4 +QjM}II_bSuDNec6@8soWS{BuH3dmpScR`b?Zns%k{I7J|w6e;!QklQDT`O^z`4N;mxoy(Cu)Od?{#&+ +&nZASf&x=+9v$w!}-k-SKCDBEk^)#BLDUP0|bNv4KXSv*nRIS^h>F9%+;Z#pO##sob!Kr``3!LcXt~e +Ddkg~a0{wFc@IefY}<#|melV`2WIw`s<2?AM`2l4w3h_NYTE)bHaH~g(AfnnFwI2Wq}_rC+VR5b_=40 +EqBRAK}2fPF3t;Ck`#BO?3pW5QkD$ZppHH)4YlKHvZdoJLa?Exl5k!CKSQ=Nf-}kav;lXC5cd=6d~0C +66{{F|f^VB@qxB)G9%KNPeq}YNq>nMAlufkxIK7{<-*QF%Zr0a&@IWD22{k%BJh=%g4E0D +{FA#R+pH=!Qgv=$z(I3E|L&16mptdBIqY4C0cAfKn1R(2Lxg=D#@|)N?>jl$RHs&PpC?JuE9-Mm>K7N +p;Kv$hWWTp|C)u}IqX4kR1V__@JC@{y{iG2*n9lr_lz<>u3HktBpkz05$b~`y>^=DBQwvBsr!9u{?apd3<~MOn4a;8;Oi3p@hgoCfJ +nxDu)NGf3-U^EwU=dA2qhZmkOCQH@Z17qgzw^Pe1@xn8-dCU3QHsF>9@oOjCeLZb&Nh3@ngW8;uCaAua8u +-c|WRv{|75OvIdP+k+$#O|4Y1qWNslToEnVy@7FGy?*=dhaZ1@@rLGXhrH^#MxCCVG}-z(Lt_j$0eP# +WZZA(%BiEqjwR3O+bFSMQ+AK95GGB5(sZN3g?%IRHkf=<-txLc((;`XKq`)aw^0phFoLvDsKS!EbkPRr*13g@r1Naith=3Dgi2u&Yog~7z%0v@y_VZij6!EsJ8x~4(1 +^Qm0a>-?bbi2DhJG;C0$dNQ{{TpyTspaPmQ(R}@Y7DkJCGC2&|;q{y9VgY0O+1 +I?BY1!c`YI`I)hQ>Jkv3zWhkie+Io+grVYG+(AOZ!8@GLdz0SYBaF97zW$2lA0s!V#p~9F=$-;(<58y +VHGoEV7qMPB*V2M#LL%U6Ne!BebVrbF) +79{c)OL3Z$bd0f)qqale<4blfnQly;GDxd?yjyN!~mp-Y^0g>dy;RvzOr$ercxp96vC +OwEP3mWQ)aRxURIyj_{6>bk(;vg$A!u4@9a(ss;_G(Np-(UoXslc$sUyr+iS?;OU8cQC&d3jYNvPdh> +L;4Exkno2+OzNPONqkeWK6bOw2fDFGQE!wB@hHyC&B7KztMkz;=pTeCcOv;EcHGg3|-H{$6N9YRT@k) +XQ0}cfJ%Y)?B{J%rhi@mqm0VuI9cX{vK6yS60uOxFB{0Z&>aJrJ<4pwU!@Vf4t3kiB%J67O-fTFri0^ +eW_f}3tLsTI>m?N%`0(PkVKwCsQ +A_KV)h2WsrI@|uYdOvxD`*)`yq$yvW}*kRE_&Lh!MgoZh34~97(jz(xbIM(K&v2dgf#e@jYY5fGvh9B +tcCA+a?C6FMy){z^Obe&7VAjyTqA@u@5Rr(M7z3n3iaB0w*APfwOK+l2ZF%0B_GPxZ$N-|%yDxF#Kh- +HsoxMKE0k{6qeY*Fo`nw%CuKt0RRq=-p?zCyM@dtjK-=wx6m5Pz#jk~9Us9mC-?T{u|H#lL#6Bib7R( +q`ACn5}{?<64}u!{>&-7nakbYQ4k8xM1HRt_pwcuL=lVZIlb@wJ9_5FE}@A)H?D5SVGq_DAy+J(UhS@ +$Ym)(RVEZ=(GZ#Ofpto?<>&~=391!W9@iFPiDe0@zI0TCj&O|!O&UW?pH8`}hSpIV@IhhW0c%xUR>kJ +PNFgg1-Bc0AvVs}o;LMD!%|f3Cn&kfMG*|btyF!y=9;B3!GeC{AWU`!_Glv=Wqe$lx#DSYnd&|TX<+Jz7)S&@P9<6a>b5M)ZR7M1N5$teJDP_u`)YRFx~?>I& +}j9oSQ8z(~^U|^T7PzYm2fFiMg`wToG-AU(2Lp~Xs&lB-plHMVu!R~ay+9eS`H(AlXh@OQ)+BXl*bzN3_XL)^$^msn$Yf@iY=jLDys&F8~h}nPn*8k6~v!_MVY7g%jw|x9ShPS&IHxuP(NU5f +Ibb;K7{xzDN~Tsy!mMqSjH7Z<20WwR%vc2z4Lpz6^gwsq{QjcvzTUN{G{Okf%i54d_C#@EK+PX7vVTB +OJ^2#amg$*rv{*p2gl5O+TE~3+oia5-9ky8IqHy=jWB^2VQv(ZZYY4CpCUvMAiY{wGxv0d$$QM~GrQw +C{9X9xznS$^`?&`lG+>m2*Frq;P3jk9}^*S?m#OzwA!H1k41>D#bdpQ8b$!mNYd@TIWdm{jmpwYmq=mVhm4 +iZ&I>_5`%1YYOp1jP8lJ!Rm%BKeHMQeHl7jI=IFKoRAca*n7^{YkKe)t~zP>4RegCpHpl@56xYZ-@IF +Zg!m@YCE4h94UR+sy0ACMKsK*hCe=jbzwbbyZXxnG68myFMWm1_kYJj@(Op{G3gZ6__}&k;YtRSj +cTcBvaxXPpt+G2NB(b|$E2`z-`87W!mf)&bL)Ff^F40ylQ|x{zR>aWvmsi5?8PbsXhuFL5-KBcIu?pEd!?Al +gGL=H6uWevxTO9t!KT;bsvoh1ZjLvFEE;w_cpWoqK#DuB0nj!-O_d)GhMURnZyM0PMxC5!nX!(WZS79 +s3pCZ8t?`)vndT1(@%sY>KPKl{ZO`Td)9)@Hzj6Y9FY?CpG~@d~h1VX6-%xO`Fa}E8yZHvbuI0cnpm2 +7*CtVSQzIozxgshzFMZ$u=dr0w)L-Ci$$Vz{psBFK~)yfa51Ff{fTxo^-pD#egwy3Bh91fo9((frE@I +pmP_&hSyDy|aUufM;n;xnZ-MpoJ)3|0$mODX7&cZja8RV1XZZNh#Rd8RW1@bYhxs>C8`E +2*abIw_I+JHvHFawglBLp4xaKTdR*h~r)2Fl&T)`?Nzx2+=@ +L9wAE6^VNkq{k9(cWW#jNj%2Y}u-9Z|wnCV{K +uh!&!zD;BUQR$GzLT8Tif;iI)pOwr&o6z7nxSc~g_eMpq-V#isN`FU)~-Ju7pq$gw6Bt$E=e-}ztPWCs|o5qSM@UaXteNQ~7iy)>8cZB(Pd! +(>NV`j_Hw^_BzP_St0eQu#45|4xhl&8EIRzcG7FoNRReBZl?ud*`&rs;s87%wx&2h`}?&xlhjlBw(2^ +q(yCV)pAvHUBRGkY>{Z{5!4ipXq##nQ!>xQ*%debQ@xk^;K3~0+RrSH$}Zuh<64gF{v|9bAIZ`^%Y!e +lDJ`f+<)M-!>9v+oherZ>hV9QcHcYlA@jf5VF=J;osHB%F(E{Hp9WZjly-c)Xx`5d#;!?F0{KYGLl(y +Y@hNFh?L*?GUEN|0J7ygP?W-D)oLjDHphAVmQ$e!KuCBlEkrfYfJCugm%I>Ppdn_q7L5j`Rr5a6;Ov1 +#%#F+_MN*$60VTgB#Vp#6&9TgrNDGe7t!kVvL7Su^dP^VwQOpNxB)lJdX6{dzL4>p8CNM6gs$S3zavi +qn5goS*1`w{#sfV?w()XAemPd(taJEt!D&K|g@vra_MiR$vQSWV^;e4@qw=d{-lyWd(tV; +C+^h*3E+la0vV;DT5~-RJxI#w8=W&u?AY#hna=Qd4ZM=)w|FQqE2(M95DNT-O&l&c7W%#KwxG`@m}0jr7f8Y~}GM`xCW2tTUb)jgChkafY)X;O{s0&fhU-9F`K-3Z +#Q#grbH*c_a#HM-z~=n{7!LsBDYJzBcQzwBy{RMr_S80kF9NRbUrtw%ly3hsP{aCAxg22W&ZA`kcDpae9wWl8(lg!x|dW0pnv0&lDP>EP`2#g +wrFhV>FKh&cN4Doh~N=RZKf#E3m%TVXt}(;-ENkTQ0Howv!py>H;Y4ivqbii#?_&&-4@qV-s2a=Ay2OpUp1%QujZndSyKBM4<&?4bqbdd8%!4j> +7nQ#i)ADQ`loS(FVFy0_43n5KCUOxg0R*mxzCJo%r=i+f95<5%l&G*mo_e0I~>DAb(J)+y-0PQ3D(P3 +FPJ)jxZH;>`&%IN9lcuW?Yxc{QPPsA(ou(G(pX4fHh0(`<}PIh#-Y0aQB)Bss0wLRqX7H-07B{3$6g6 +p_Eg&K2StJK?PwCjb(U)l~m3@64BPfVo%w4XVhh;4yk?ukq0L$q5}VzvYba+A3woeZo8|EWb|vw*KauWpns8`5QtMH!Aqtdfy*uq9ef> +(aV`8GVEZ_)bD)$&Bc-a%}^tRC5}G0$3{2$nc`^X#taPBL$Qs_p0?aVEWs~LPNpEl;q-^R^hsWzJ?As +vGB@oxdi6`pf*sM1A=d{E+>@IkwgE;FdtUX#~y;!q&U{`AC!&o9!Z^q{J~9dzeXwbp +Ml57J~c#6SR;pN0aGZVN+zWFzujr5WC>^zZ=+L>62KBXLz*xlv0H8L_&biFs0x|lvK&xTUdSH+L`$=$ +nFGgs&2*+Avdu1*l<-^-z1_f_?va}oGWltceJyE(b1dS+~@eMZtio$2Y%?bH}{DN!LcjgLe-%sIlSoY +PYptD-74#*F;38z@1=v~H*fq~=^R5D^mM}zDt^Ed)3eLA-Zf{_psg!-RMx3a9R|%XcQ%Y>hlnK@(pC9 +vc)j65up;AHi1}dlm%Cy(bLZvJNjdGtCge2v>n1~G7K74Q$}0`QJ#@PsmY>CTiGqglQb=1IbK0lmgPK +LJB51T+QI`Rk#=%#q2YIlZ5qBVRzE11ZUb}iu2jW(T4x+w`;DnD~fp@%IiN401g0R%*iTJq^?}5GQy& +BfAA(P*LgP`;|_pUXk^IZtId!kA@+mYy7Sg`V|Zn5})PYW_p^g%_53s3iFfdK6~4;EMXIQx;PIPJx3T +=8eph*Z+35E~q1A6(pEFmm5{j&QaFla}88I-PHAmRo1%T@PYvk48@qB3+mpx!pzTQg@uy8ynbdXq9_jwp<`W5y-G;BF<^_;%s}= +^Og?;2c%97$b(OVplHi?bMK0uRwr%J-6Kc@f79S>}_7Due^NHPwe;e~8T_3F2{;)#rY8sDKo%e4N~fA}`J4W^Hp63>|TVa*_;Hqfllp8ofUjyDahG#X%sO1|Er+9v+@N10zZF*ZK0Bi$2`d!@!Gmoy*x|*KL;nHW_SM*q(+O^!qC +@(%m=JER|+^UV0|fJV#Hj3}sB4E-E7k?U9GGDKKOC?lGPZW*m^84}tWTLOysBHO|ueF_s1%+_zwum@6aWAK2mnY{22%h40006200000001Na003}la4%nJZggdGZeeUMa +%FRGY;|;LZ*DJNUukY>bYEXCaCrj&P)h>@6aWAK2mnY{22<~be-QZq002J%001BW003}la4%nJZggdG +ZeeUMa%FRGY;|;LZ*DJQVRL0JaCwbTL2AQ547?ls2OSe|^8iD7Xz3wPN=c8Vn2jwbOV-v#QWE<6?%Fs +lwOcCK!;UnX(KJjg=!du69&GE06&{UqX6G=2XDgZ}Y0|_YZ~dyYog#O}tr$=71wb^+kKE1h-QwwN8C& +f1Pqv_r%x{!8>{X|`UYRboMuzpxWKoE6HlzDK3&y=^QaK|64@Pi{Lhgc`rwN3ru>l^S#el>TbWH|Kq- +8KId7n!$D_I?|I+k+w@g}Ux>jBXP#*WAh3zu+r|Kr&FONxyZgcs6H>s{Z+`iYr%v5dw?k9*WHd83xPk +~qXNwLCQbZKvHFKlIJVPknOa%FRGY<6XGE^v9BTwjmdIFf%4kng~Ckc$o28r<&P!x` +s2WG4wOKsM8u&D=a>8yZ@oZBEOgMwGha3-aBssz^#CMY%iIz+}=ASu7Uo&##K5EEbD5q7ii|>f*wF7F +tz(CsqCY+4E~p0Z9gn@Ze^3g-UN%T}C4-QjBnK +Rjcqt}gJ0V7d_n<2V@z0ZG=h>UU9eYnXWCZCMGenL4mS)rU$J9sJU=T6LKr1wX1giS|Ki!8lu#s^3?_ +Uv$C5mqIj#dCgi5BTF-RpD~yVeB6t+l_fCK?>E7=N7gg5uV91}df3q7AY0vm9QwLkvj;*%S8!5{A2-`j5vP_>;UzKc+TRH=uAZ&Q3&Vn^c!h1LlB!B3;z7- +kLIbWK~mg%kqVQm!cn^(JcywU;}lIKRX9jnyw2kDk<(5f@6TvZ@{n5LP1S&9SitF8o!c985u+b +LBILy8lZwSC!L8r*_?^TtemBk@e%!#oZLv}PZqhTng({H@tP1pNA`Zz|%F=zyMO`8|kQwOPZ9JAU-Yq +Ta}Z6s=B;zB}fqvwd5LfyFlxg|Z)Xe4GV>6fJO(6}$jp+VQ`hgs6VRC&{scRt*a*jWYaA=uA +Oybk8eU=HMwnyr7O<4w@)t1>+XeqD$}z`ZzPoxx#GE^{~Ug!QaV+f-6}kIK64ry_^#=kOLWm$u}Ury{ +u$+$vb`=6zeFud^q(h6s%FZX?Ut21bBL7&RObWuyfE$c%Szgb*cj^t--;AQ2$qeWs#b}>w~&NZepkAY +^*2Mc|$t8rY%Je(I#;Rpq@MDpE4Ntfu1e%yyj<-=PMis7F3|u7Udhy8N)o!+k6HsFn;Fq^gM2Fk<9aF +;M!Pjc%*bs*&6rt86>i1?vGnLP#!^)A|}6OdvKz_Ho0fiRJQpm`v`H-MoNkf_?I7 +%MF#N%gB`j2qXcKij1O>pV1RLI1X1uoLZIQqz2p0pjaUmS3%xNE77UE1#|UEHZ^20UIojWA_LohEd&G +bYn}cX7z|u9y6Hgquy0J%TV>3JR-&MOg%bNZyS~alRDWe-%u;84Wesw!nb_lID*~PdpZHLLjHCffKI! +|2V-U%b4mH{(=t06PmUw(CGofoQ+mW!;xp&yRw5ct}@rfnZ!3pm|BX@J`;wIpEf +j4_^Jnlc-69j@V%45;kbN0a78Z;#0Wnse)d)cxROy!6!i6V>6b<1Ar#hMg3v-ps_!5WvFFlzP*fT(sS +3ThAtYdbg49pR2GvPJegK&<7NH%sy)zHnBnk#JgLCo(9-zMn|oJx)g3IdBR1Al|n*Qii~@8DxdRKm?4 +~?`6AKha{&m3g6!kaN(L-JVB;pB(pZ+ZFD&1uYYR44J(mfIvl`-1B!L0{PmmL7>-?Jm|yU2`PdGJO~w +0|Y-44-eo=AADqOr?`oViOdS3fTgWHwG$(;bmUcgC#BLYw^9kTnOuZO?mJDVJ_e_F#mnVbZmA-o@e5_ +xFVca#;}IUnk$9q4I!eQ}HX9>n}Lx4Bb}n5Pp*69&I71dImdU<=H&HuA7Ru0@GYIA_tP1r$`L^a3~>gKeEq;ZHjMMx5@pV$vD4H;BK9Cx`r%-PonV&JAgTfr|Eg4-`h>hB| +T$`d(tmrN%+=`V(@>Dk2LleFg|`X)q`?KDux1=`xU1nI(`2#b01YQbg~k)_>XL?sYLJb=W6+73DZ~MoM=B8FU#C}W@n6ZUa;3i*M +n|coW_`%Cm?3MCPlI}OF~LxT`psPSGvPPKl#MG!d<~@#G7CTv(BU^Ix==Zk)gER18H%0g{|As|-Col-Dx=P +a|6lqGI+CCsO!D<#(&Qp)Fl;kSth)lc(msUx%oHp`Kfta-^rsSO(q0bWp9ii306@#}0*Y2HdoCnVN@E +7cj!gAA)CXx$tms)t5rNaD=BdQsKA+zz-r!&qmRe*3t7H9~*Pe3OSTU_cz1I&!hfME0eCAM#RPnS^vv +J6i?CSr4u;8bi>x|7Wa6Q>Bt9NUmBO^?chKtn17N^l#VoGF`{Am$PDuGNr{hR$uAh~NWKa~M~+`8F>{ +p$}Y}*yC!l;Os3`tKcI&Qq$p11DsoDl6_EE#duMu9u&#xL@O$B&*1?btFoaiz-WgK-e9OP6$8B047W$ +T(-rH91562y1QA5%Rrm0KydC;vqzh|yN97O0Ho*8qS7hvU?PVWy6KgB4#KK|&T=5K-nl>w=7)YrYjfykX_B9(!3v-l2xLR&DbEeaP~ +tU;JY38w@Z8~NzyapW%svq3dMSH{MPdXBEk<}r28%ObE{-G)K?7Qa71hpAwES6l|KMAem69g8`vTY+_ +lV{B4&o~e>6UalvQB3%LID2bP=}_Jhw>o +0r!@6RM?GWg9gIOVyCH1r(+J9%55l;nz=~QSeA6Ji7_Gvmg~MH~{srt(fttV4K(a2;z8?662u-75q+Q +7AlYp#O(xLyw35!Q`?WD28;F!j+S9$~zi&Jxmxp3OUME|xg1w7^sw{JE83?b8vwZJl!YD-9R^HBwNim +vGeP63_fA3XqLVwS)$=*0Vu!a<4@Hnvblj`frexX}mn_8T||`wR77!#az;|Wp77Ao%ftNNdki`Tr +$hSjU(*Q<*Lqq3`aXw{a0Y~NVhu5=;^WYBKr+~4;X@g#QCKFu;$b|_P)~A*d8NejcJr<%@SwF*j~vo^ +niGD(J}5-9_U4Z|yKdgzuz&xLKNDF|fJJjSVVf!&GI--Z(x9Yx7cEaF8?cBEG7!jx*LR6HMZs`qMwub +GeF&av@t-xqAG*fjQq<&d`}}8PI|eGz{vcE6ASuE$q2AB}BQc#Yp#`>}jXDDwV{i$&eOJR{W2 +Fw=4`&Ik7Dmj@O>NoNf#|n?)~N|UkC0_AP=xj@*;libZ_%WcyBQn^??NqETN&)k{~{+G-AcnjuM!!&Q +8dr0!@|%1Cy{(x>%O&U6*QL{6~`LUUKlTGW31F`JBnZSHtT81Dc9DO15l*JsCLQdh9psyqvxd +=geAcW0DxxuURK!Yh?&x5ZScl;=WN#l8P;KnO?5@DXw6JhBSoK5d;z;?@3Gf`5{w=xwCaH!u$U6n8qZ +RLk^*ZwWAFMFv!gROyLHGfc68b<8p9);wEz=6%Nl~L^!gjnpb0&dJs~ILl${mqNfx#MEzd$DN;W;@pqe0#QX+c6var@C-8JnaH2ja4V~#Hen)L2(}}cUqe|vFt3HCEyxwD4Rdh(v^<@~ +wy&g6`UP|5bR$?7S86T=aK_0OzQOb^o?M0(fM`mFElDj#N+<*in95`eu?g;d~bh`u(T|FpSU|R>YFk5 +10M93~uAV&5!XUnyda-KD64>aQ{1I98wBU#`}xQv+k_egCZJcY3yOOXtJg^UD?*m#d*L%A&xfqOtLDN +IO(L{jyD4J&cv#bp6P0=Fz7lZlgs5D;E2pAb@Vj=75CHT*Sbl79xuR4 +v^!=8n*dL2Kn<*S)fWE7HqsWCKu|>R~FWiXfZPZ1%}m|EMyJXu+`6|ReMnA3-&Uyq7u9oSQ15~%I$Xw +K=A;END_@I1eqDEaOz}G#AIT*I$1A;LmFAj?{=Mxa&3^3QoxIP;uV?3Z9f^U$EtifBm;<&hxY +y*dI@;aD9*3+ZsDX^-DVcNSBKKl(_eq&UN8Qv6o2xjW`C^xAH}F}4P^Jv{lj3N`k~P|dJ}xJR{)1Vd!EfTvZW-V@!beG-uG&`rsMEzX;c6Y8n8s +g%#%)}O5QIr>msUL#pWMW$cL*-Ye)V| +2Sw76@$>DK%KLy~98~j}z2Pl>EjNH9mTvO$Ls$sgW(#63yt}jO4Z^9`e4ox-m%jO^0cVDvHN>scp2|G +_eTTo}bicIek56_BMibJlY`ZJ{Z(%0krH^*(w?!gKZ231NjC7v5HjGr|i&&GtXz6OZXem&HgIw=MqytDOaWD}{{8>2u&PWD{Vmp`O|@m>rYulgEaman3l +F!Ys(m4PGh*O-ZY$86jiVP)z%q)L^kpq4PC=&DPKR%1HlDQ!&PdG|ABf&K=A4xFSpKi1qA+?B<~g03e +}6z?eJx|I{+o<>?@Qi-AXbLATy|7T%cFbbIHgMW>L1Gm-J1IeRLJHx*x<=mJQ78CLCRtv#Y!>USgqDm?D|gN^^5a!hQ8pzp|fwt=$q6-N2urargT~;_ +sUM%M~d-StKURp+tzb3-rqbgDEFLdkLcVG+ysgv=Y}BgD>`z3q)~#qjgUIEEGOwID +wAE)9PYwG=kCH3uxE!c=(C;1Rsq>z0Y7TX|tBBhaUUPzi8LI>f4xz=RMDOd0+) +Bq9c;=f^MmKqSg2aYpJsQ(8266EG+5Z7hO9KQH0000807zB_Qvd(}00IC200000051Rl0B~t=FJEbHb +Y*gGVQepQWpi(Ab#!TOZZC3Wb8l>RWo&6;FJE72ZfSI1UoLQY0{~D<0|XQR000O8NLB_@Vg84_V+8;J +Iu!r_D*ylhaA|NaUukZ1WpZv|Y%g+Ub8l>QbZKvHFLGsbZ)|pDY-wUIVqtS-E^v9(SnF!zI28V0=sO4 +xEDm`60D&$8!;~E;ou%#U{s_TU?c+u>j^&Z$cGF?reb13(Sx(Z-g>;t{`h+Gn-Dd_e` +uisSG<;@(J2&Fd2JiK=HcwoBg1R+M(BXmRPFst2@>z7EPR;GUP8Kiy)+{KYg9(PH2jZN8QjG +&*lY`zgrdb;f8#7x1|k54?fa?3GsVnf`G>8{Ra7hIj7g6-g^tKPPP=$X8siARzI({YB2c;cX=joBjrQlKCk#KLX00^Cy>5*@3QVtB<~6$YKzreCM<+Fc0#&+`5Gl;aGX_kwUir)^@rsi#Ul(7#E|c +8(Ke(Q;Buny-Qncx|wb{%CT{c{6_C_L(iX&OxX~LpF@`Ndhbc{noQ)es`!hBKz8FTE;J+D5kV#p}lmQ +I~T?ozfepQ<0ri~t#~x9;(2bmlE??OL8*+IejwU4vQ4YK#%qo!O*iqpds1h(of=-nf~@VW+dq2 +TWJBjaAd~?67I7kph$UGnvmAUCs9t(;>%(Ku4nsQey$Y)WmEIKN%K2#NHhW&q@XCoo^oV)Tmw?U)EsG +&N=Fxd8$LiE`2cLo=Afm+vawlc8i!80CQ}SmF^L<=*#*g6NyDGUP|+BjV+LFfhx-IxICuy@{98|-%~ZAVB+!_?mxurZnBNgPr`MoXniZ5Nj2 +s@_2G0e-n~4SiBS@+;-)g;OXxy#BQ)Mu6?YrYTR9AYIg5tJzNC1?u7KJlg+YD+*94NkfS?oq2BC2_$G&W?D%jdi}iXhTBeNQOZwbQ+WnXQM0*`RqpZFd24H?RjutpBu^^{HsJ=qqnWG6M@~{lI-oR0|@scc{arC4*^+}bS- +;aiE@PyBhEpIqsNO`z;ACCd7dwEuj2}5HcB;MEyaM>bjmf-N{}iF#U+7_FP}sn337qfr>*O)Qc|VKb$ +{pJ*DLFjM6#5qup+~l++|^k{&>!cAh+J3p4NzZL+Ab-(0t$uv~1Am_mL3u{|z!mZJrgxl*(*Ji6evZ{ +CPrN1leOW$!X&m_1=Rif|jDO$M=!4t#keShynSdI(`*k<(|otpEl&42)29n+&?mXA99#CV|&uFS0xl% +GtvXQ{Rp-_AY$q1;XhCkPV>RF7jJKrz@v*l)82yg|GBxn_laX?w@<2220$82l6B>dM+?S~Qdvwu( +P@lW~R*Ajl%F7{RbLIj`OpMC5&u0g1Tp%+>IioK}sjP}9{82dzj49hk&JUQbZKvHFLGsbZ)|pDY-wUIV_|M&X=Gt^WpgfYdF@>5Z`{Ta| +8Btl!-j^TJAh&11VxK$uc(_SjnLFqZKpwC2sCxv(=p+oo13OomogU5o35y<(`G}-DhLmm><$W^f}WUK6~C)HI}7Wq +rt5DWJeraee(*A=Cs^Q>;SK!6v7$$51&PhYtjN3TVm75`^(nilbAR7aeKX +L_4sEZS%>=u%{X2)r)?hf`f^#Lo;4zQR*4i8E(VgPV%+>iNnxE$W|<^uSkH?Z|T +`ACE95^$9{hT9vwr_oeclAo+El%wld52M&dGe9V4@$(Xo-`umyE6czTA@JrYl^>LM4QMC-CxbtuZ~qT +7gWBgN_d^h9V8OA(!tG^6Cj}YcUz|s +!#R9KSZ?hhYxbO2J=pFo6?nKb>_Y;{7%iqr+;ux@^v7yGqWL46up8qw7aS+wo+JMQm;kOJ%N6DKsar> +Dl?_PeJrgp;b(FKV&7-51*|RD1oM1uY;i3LMW$7)*rMYJzUB*0wUynjt%U2NTg=mEbtS!cx7npk$ISl +HtP;O2jKk{Wwkb*QV>V8EFl;bi1^hShj0BSW2pPO;Z*!U%IJ-$E^FR-h%T%y1uV}7Sc@{+5Gy+Zvvv`8S7Z68XMW%_D +L`;#(@kxB%jtkV;yT9Kk6jlHKn*494$%i!#fst7beq99E_2+3_)7C%A +l}0lZta}iCh7%^dmk&=L>6Q6NGM>TEb#%jLd?)@WZh|4G#qXj+ZoW@qxxCRU!Bio)aMIHFe;D{rHXgf +!iORY{K^mK<=X=;r-0%tSPM}GoL$ml3pj!sxW*%XB82QvAD*$8IU%-4>CEmOl5`6O}!3lBbs||pgRyK +fip+SkvVQI`zr0P7EabpmOP^DKx{)nH}|K?HLiv8{nafXg^_DIqPW+sog``TBh=CQzJ~y8nC~nEeO+9I%h(>4cs9CO +vg*GLMNOBSZ200(GTS*>O0y!dZ;S^s|s^_PrUsj~;$kCW_)(N +*(bP$qUY?Ym4FpwphJ_iJYNDBJ_Dun3>SCTaGq5`c8M{+3u)41^aLBd|V_FK$MVF?`rzlyjtZtRg@{bj9kE6X2@9VuhS}*EECQ1x*58B+Hl$x8=nbNvi@z7)qLXxhJbe>eF~(2-tK +ZR96_$3D{SG#sk-rO@OrY1*i?GS2#7p5yX+Z}x4P=ZugBJs{kh6KX)nGiZ)7XVX06&L$Da2-?p<}!L& +H^$ghu-p5deIHJ9v$dsHz=`B+IRCP(O#z)%Y5IZUWv}2(=@SlmtH_ZdP_tzego$`5QQ95xMCa^4r(!u +vD0i8P9HM`nfHhS9?>LYQG?C43oI2A`SZFi8U3(mKz%J;I}a}uFqZnoNVZxtsbOufn84uwh+ZG^n7wD +8DY6%fGbFU^v!R3r7I9z#4)pvCp;5CLOd-hB7BRe#O_<1Lh|zcN)?2y80(w`w>X4B(Gb-?`J&PY$-4I +?o!y3cb19{k*b($&?(NfAY+lsx17MN-WUd>N +CLeyc07DZa0(8!uhFq1;AVsUoySW@%>D^vSX}Ro`8bSjP^3daC1f0mE=SY>be4G}7R?8EG6Wve$>LIm~qj);3{ +iW0pK3EZh3YvNzBD$;(VIDl!+I?SiI6H*U0}_I9g@b$~BJI%5ziG+QQVeZ1(;6!9ptuWpaTX +^lngFWHw%pT67lOTRlXalDI}B$Y#%n8UE3Q+>9DBjg=iN#S`5PsGbQgheh^{7USsf1N(ax3= +XY>pcrOrpa)H+;0a7K5+jBb3X&kY#>A0?3jsP~x&@==|Dw%uI~U#cADQ6oLG(FRO=oK@Tivn1)Z{`F; +T|IR2d7UXHW53%ew6hZToIb@YGl%0f^$2rC9u{z+j4yk3Bp4N@u(rL29Xlf5Bw;xrfK6mWZYD#zQU5|Gic!+l^#BX2+I8bbl9_^cW&Blv?|R)(BY;kCkJE0a`MqOjs{3)ALjVE8g)x&SLB7i)d-N!N+$nb{9i#}5!K7yjvW8k*Q>k0zHDpaf!sE8w +Cm|haR04K{)}xOb0V2q^Fmbu3^j}dWPqAg6OR7o044j;joe0kAi|T+3LQbB4^?V^-;_Zq(u#_nP0(c= +Z7kHVpM)hc=y^xH-bJtw7SuXKl3Iuq;*QG9tCN(r5`87xEp0jAM>q8T8iaKEfF`wll&dOY2%TyQm_>V +ap<6ht{56L*)rDOR1j>$CA>0ak-1_vXU#Vc4p!gu4DADtxcmj+_dRZ!ocR|#Q!?alKO +>r)jo}|ZV{iLe1u^aSA6nf99y1*}>J~E>4Cm3mrgR0Wj_F89XiT+7YE@&ku6w9k-CG69-+MQ>`Yl8R_ +5L@#sKSm_(IZJCk7AHY4%S75^DGCwA#McUId{E@@kZB$=!zDW{M|7a-TmTYV~W(>Mi<#HO$1%n^=o3_ +ED;o!H8&8vtD{b2>$N?+HgtbweUQFK=Hiu+GbQuoJ{I7*?Rv~w0TtkdvdN{1?*T^O+ey@2(}P&P$*xE +smB`wzSQo1T2KIz)CTO!p5w8)3@W>0BU73@EvHJRy()W@&y85N0y(P7-%d)b9oyBmpbYHEp +r0il25;=m=s?{n4xg(Z15xx^{S#jo(*uYK12r5Y;<`Ohi^8AkdezbH|Rat_px5&BmaQ&l4;URUqP{R- +&Sl4h2^Zj#Q`&ZsKf?g4b{L?cGjYXO-S|T0k%q*}Ily>5}}O>t$fF +O*@G12jeErt=r18_e$9NfSG^8bR5}spY^;>n)J3?wLa@5WBwf2y54>uO(0mNubY|)2=B=qg9|$x2@SP +2+=W#nCk#`JnF2YS|K;o2Q)!#2p5A<)!Qv{;&kfCnJZj9Nj@XiUEAkpnwahIdGc8zzR`BnZEDV%)d~% +F>m{SLgkhQv-rNMUF0LeiWCF#Z5~G-q#fz#?(ymYMW9Huc8ea5!za$Gqxd`8>Vy)U+E&N-quOSMY-Ih +Yp_AEoa&@IOMEe+uh0NC38Td5ChEzHI#=7|Ee>1L6LV)0~n*cjcsdwuinjF%NPu_%s`AM3(^6wT3G&+ ++r2J94(X!G8g_$7a3D8~y)XLV23V38U9&rezDfJB&}$@ooL3AiY*Mi`nUygY;J6Y^&9~9-aFE^p3mth +1-YF``z7xvtZZ17&{y1G~C_0O%wc#cXK-oP5jWUFq&#WcaB~_ST|6&oy=!FjUHIbc;e!a+HIMO=Z25E +o2)3HZ3o@UT~C^1(hffHWa|-3gSPa9?o&E(Ja29^js7@btiEa!g6LOHv +obnW-f7YQI7pW7B@x9!f!-2XO+Z4p6c$T&(1+bP%X&2#qPPw%?F;KXCN!y@ZYUi5@HQSdr9dlPrSuX# +ME1I-0x)NX$-L20U;L6+c9M>ZY4rtxxW~SX_pGXyHsn+0B9j`Thw1yaJ$&PZv%3yaWFEPk+mbz0}A*+ +O}Aq`zSFyLcP~%i1BHl{W^0;eUDikTx2C?rhKeN^utudCOJot`rsR3!BxCRHk}Xl!sNvnFPu-*KQM3(E@BT% +vuLzkS|dgLV2iK2PCxJYC|in+n}|8)1nlA|~8c178J3ghZ?1bRupm3wBG%WHRHbj?Z^$p<8EuC!|Ygv +d=GYX1h&ouTgOKX3B$>gFFs_hXUq9lSHqllbT>TlabGl9;&HTZ-C!{S#120|XQR000O8NLB_@-^JqwV +jBPer)vNJE&u=kaA|NaUukZ1WpZv|Y%g+Ub8l>QbZKvHFLGsbZ)|pDY-wUIW?^G=Z*qAqaCz-KYj5Pn +k>3sYKlE|o6DJ;V?EnYd4KSB;*1Pe-&U)ch;xiP1re=CHv^eCL4{0Zg{(GvbAFO_GM!E#|;c$c>8nU~ +(y1U+0-Q{lIw7pn0buT~mReAmCC#8C|8R}KvG*xHcRL#vzS>Js6$);^~qCf26htS{BPd^dxT>SL@b^h +v`*RQ^P`|b~O_3rC()$2dsw9Q{-{Xv@FZ+qDm*OlZy`=-@DzbiY_@Q=kFN7KLF6+5}U81}Z~_xrwV>Y +~y=KgeGPS+Au1X~6J47l*z4b796>6*SiD(@*sD_T@gy?_|Ah+H7B}ZVQ}x)&k_Rl{;DYotgUE`mU(T^ +$+8xXMGSX#1G~TO#W!sp)V`j>s{0CU@m`^Yh2@nShuw88f!bQwuWVJUw$k)_g+E@`eS4UTAGcCg^dj0%eq2e~^Ufo{uYqRT@|s3 +}*Yt0ip$|dA7x*xi@tbU1D|v-0_=zW^ryNT_K1L7zd!Wbrv5 +W*0ssm>(~J~}MJ80%Yxz-ci_ACpvo%vw~6cx~o>Cz5$4bmeYkR!RPd^s#X^3gI+unuf1_myTwNnGxT=1=%;3tjCpJ8p{yS +hK7ZooKJK+Sh5!7-5^0Bt$9<`W<>E#fwJuWGt`9zv_C1GTZL-4)4shifKhzl8Sp!crJ_e6 +a`v;6@qB9dtUYtt~Si3Bc5z-3u>bp_P)uBna8n2GcMq~8m7FEV2VEcIS^pUnoq_r2AaE&#TO(2D)?8> +%UdvI$yG2)fjp=FA7>@3+#%XrKa6|D)g8*-2>c{0^4K-B?^F_l@5nzLC%`{=tZ<5y16OR_lCIl+^&u7 +Rq3~4D3qW;D<-QGZ`dyg$-#d5RRsacb8gqO@RnhTO5of^AOG`KPFY{1Zt!`CjgF(@nNiBeL)=a>T2#Q +;jyC`m9gYT)J-3GL<(WY(};rD>9_E84`Kt>bC)G-v5OK+XWw&7VT_rx)+9`Q-t!f@KznrI8 +N)*LT6jFj^pVGlZ2H98`K>&_gwt}JBK(>_da$u6Vg|1{IAgboO;y~00o<>)nP{}E9=&=_!;ACb|YC}EkA#ZjwGZXbNHuEEUtv+SU +h89#PnK&^{gSx~~k);B|IE{nDr*cU-z{PO>bQ3NTjx+7(A>;;0!NCD*9y_Iy +%}fwA*1n+&@{32Jq +pp+e23}7m^ExsWWVpn!#ouJsG;Rjd)$>153F<^#kcKHPprA60Cu+XhF01f6M8M7LK$PE2n%5;hyPWBp +*di`Gf+ZX@N++P$q%HwiV&nbtUX~+JQ*@<2`z38M9@xizZP68(zBLF>}Qs)T&hf{IbK>X{{!W$FzZey +L7GcDE6v~OVq?IX!P8XHox7k}f^B@iCl<3p^Ev5km#l0Lyi#wSUVKggcwWHdc-QR&UlViKl0oR4<1)( +{zV^li|$8*zS4L8bFCm7%F^D}T^(W!w}Ve#BXqePF +>ViHvh?}<9Z^N}jAeP=SdEbM7(WUOBGh;%a@NZ=$L1}f%i*}HH?Ct1W!x+;OA~EK{*Gm_zh()6~YN`G +g`EJD2a>5jw`Kx*1FcHvaA#UsDzUE4<><<~+#3f%8cZiuOpwaj#I%cS|*8drZNj!I92pepu>-(hvXf+lBb4sBeM$43%32qS6^_G1L_p!2%Nj;y`VEP_`>-ah0M!I3ltw>KmDf4 +~+^<08zm|8&n)~9~MemHHw~DEitOlAV%G7x!(u!5tDbi#}a>}*e^5Ddi{koETV^$h;U;o!gI@-=D@ly ++6wiho=c|;AtTRQiSMO{5+6;sSf^^(m>5<HFwnqJSPv^H4AP?-=tkR%oLls5YG58U>N +zlyu3&URoC{!rVi%hXFPG6u4+IZ_*Hxu$hU(E*7_1YP5Z(*`AFN{~wve;8#huhEkp*~|6+*qM?Plm!* +5n+4m8}r4ljp;oO;{Uz6vx6vxuo5UaQZH=s%dVAJrT%U30gJrGuIfh8vCN4r*tX`*mo35 +P!q{Q_KV9gj4_|V`k`%z(%&r7Xm*3S*DFOzg0tH;1F&&X-4}<>hXpeLgT{^1p&>puYL3kEGqifHhZ_p +X-Qyb%U0YU%D#x#~OWFLr#nF!3*j=Y&sQ^bLbz^3SV|uQMl1`^WD|*K!<10SLv+}MZp%%Nu3)~0xW$e +D-rIVqXeku+4kBb2|6Nld~C?nBIYME?c<&s#~q`5|pLjovz;$v7%bojyQSyiw-jFcM8+e52Fky?Vb2~ +y&iiUTJ`+?V}UG;MiPg6~Qi6n#i!Zqn*USkz=-#u<@r^;uL~s3&V2@XKsMR2+3oymXs+CiJ}4@hre`% +pEyiA)TtYsA(O8L}ox3mKjMk*2&lgtl-T&p%}BP5wW`zGe)!R};C~_m6wIvYqfIv#zES)_&!Be$oSa{j~2<>^MNnB@1LQJ3rd%T&VkwD+y!$$ph#WWh)_dHbw9EfcFRbWtO +BiIqUk9Ksb0jK8^Ywy~?gJn~88ub;jwm{bif-=d?1SB-6{$ +lG41-F5!me0L0d%~&MU)Pz%B)3tCq`6mQ946Z2bVbYNEjo)a$F44&Pds<+#szs9!Ml{4-2HwW1uU*2$ +MSH=Q1)SOUNPjIcBa_1PkYGJ+{^d3=~!q99A<|l3M}MxH03IBk5%eon%SWJvG+&vCu7R77gFxb1ItS@ +6<6tZK%#OM#6w3ZE$4GLi>SeLYJIyG(2Ax%oa9Ta6Rq*RdgQsR(# +7(p3wIdSx7F6;@W+yrKJ_E~o +Z3K&x|DsO&hhE}i>21$&@OzwK>n}&YeRf?u<`0S}LGukJ1*w5Ibe8-;R=GviHUmc=(mWylQc(V@tTb| +MO+B&My`!?mc+ByPbLC`M>L5YaJI^oK?#|xYgoF5Cj(4j6+b@#==$YYbgDa4K&}F4`$hJDr)e +9~_mU}Po7(ZrhsLoS-RYNzN&YGZ_6cZk%I>&viKoDR`#fNyWKnYpg;Fy_lOb`fqI0zA=egK^v4dqxza +HMlzPFPo2uJF6O*;4&UeKQxbq%zcNa2;TaSGOW{YH1a?U2(Wp3Jb7@-BP8iPL<5I=wXp*ArQHlKuJf3 +DheN3?5YAZJ8C@74AT!pwPi6XPmY)*G)}aP%#!(4lryK!+NQ@Smk}RVy>abo(LYJYZAxtD66@>+d4pZGL4st>-#*(9IN2U^-yf8|6$;lYBvSNnBL= +yI%tnC^pI*vZ}qxh_*j*K6Yo|;3XttzcC1y&L7FsulE3cMwY+-1rZLL(@$#S;Ul7#7o-Pc*KabqX~f1 +Mk>B%V?-LzVFqchS$^?UafY>FtyrNTfO7zddJ#NZHC+Y(!j+%DPz8Z>F0!jbX?BeE>iVTrlcogA_UE6?)d@x(ua!nONjgB!M7hgK +JFgcnw;1H}a&HDzNYkmYjt}k3R@J|ZI4l#b!N*%Sw-Nv)9vQr5ItnusAc>-D~r1am}qjJ9+K^?18rck +BDG~>mXu2ZaTK=A5P#!wvk!*&6jGZcQ%cj6c#RjjnOvRT!3{4E7W9uq<{(vYqNh83{X`{_f(`*NoTKd$XebjOq>k@ +^pb8{fb>nU+*|0_f6}B(g7cR6&jBNcBXVyqMs(3p?BAG>Mpf(MtGYg$0_D|^1cuRQQP5 +~Ed8i7KAYr6Os3Q$&zT~-amNkumc7cnR2Y7VIB=V!J77_3zva{m;2sFgwz$vr%@?(Pg<206^wERnhcq +X=T0OJSoZ~WpV$z^3K;Q;u87+Fm9*5cTvuufT6{lS8F}!X+9eF@WCPn08dbo4W~tSjZxItZ=9l?PiN3}uvVb50KtVTNMa&U7$su!{U*tp +@(dDo~kC}QTED^#atks66rlE4SzW>;KYGj3d6BFZrpSV +?g;w}7vnrprf4aV>Vh&czL7A{lX9mH-q-+_At>j*+gIU_5`0wl|KDQT&Goc~XNmKO{4xaJta}6VfIm! +}u&*{DU%F$?M&TbQo^meq9U`>UZX+9lSic~EiBZSJ$uVI)BLK#q3j$nw-KbE7V;+|G+Wlb`#xJC~NV} +6~WoDlLDn}9IW?%eV_1#fcRtLFSfQ+M3au1^zd>N=J#aLB~UoG4cO9 +s>*aWvA+jD5O +hq`v$MLQz7hjhn+T0YaoIZLQ*X+z^tYntN2+Mrlkc5rS(7)j1cavV!q#kTmE^~CV-_vTyLl)AY$8-rQ +^cgCp|V{4|iC%SGE+w&F71~2nOsLF69&A64-9<((JAW6I<>SLdiCdcEDCUJ}bUJ|wNN8}bBsar?)fUO +rbqScRb%xFcZhKutrVbsq>LXswdztnqp?H_Vp%MJ{rYng*GqpPBW%cZB#SD6cK4Kf|)BniSga+9S+Gs +k9ZS^i~m4ZsZLrsr~Lei}C=IVP>WVdjxE&yhp;XmEKDVwv3O)(-LNrS8Jh1iD_$vK~`KA@v-!_wjk5P +ASo8d#n`@ttXjvagLT1c#~R*ccoN^5SGSSA3`|g#Z6>@M<5eq=YcT`MaL5KkC_)1Fm3$x0dp6|8M#tB +52pF-xcj5KC&u`vG?_Ix9rH%riyC+r%G5RH>cb`&k +QQFp7lSc9LeYymZlpj-9;NpzDJiE9{F@~%*#*~s^QkbbS^c!Jgl2Pc}CsHjkr!~cGcQ>68rV4uinytg +NGgfDaSuHZ!m{ty#9;NW&)0+b_-CDT(m#d)~;cxf$FyM`+k37BDYh823?MX*!X_7Dmxw1Ph)n5QGCnR +ozLj}(J>Jk2fe(tSbO_Ln*d-dctOENykoK{b&#Qe$y5e^zP;tn)s +5VlOVeZTJ(FJIy{w)?}r$jauWtY2a&YS+J{e9g@Oj{Hl%;|tg;5MAd?jGc2a=1%tSP)h>@6aWAK2mnY +{22(s*1f>}U001);001`t003}la4%nJZggdGZeeUMa%FRGY;|;LZ*DJgWpi(Ac4cg7VlQTIb#7!|V_| +M&X=Gt^WpgfYdF@zDZyPrdy$jHPAe2L;KoQU$1vqFDw16*3fuuzM!&p +NvLidH^&#T^dkP~;!$N-b0Rl&nQi8?`RpA_sq(g$3 +txI+p&Y)xJdi!U6sd6@s!c@}yG*$emtLsyVDy39e3p|>_t?J{^x&;beR!^nr_w8_v2#}|o$(2U$!g7( +SW^gRYMatJ+CWz2M~KX4nR4E$UCk$ng1m%1G9C#>_hczImf&ceLOYzY0*2vVAou9>qTCdsW(P*TscVoSg{Aq+jp>E?lgjS={Xt3uEmFnBPI1(d? +ddXpXy2zD-7E!Rc(?3m6@@dN~U-6mJe-BX|yJ9rK8{C+L?V$J!}etRJZb}(V9{|)g^M53h{!?q^(f5B +~sHJzS%)g#+qL$sC3&dN>`@` +Ua!Qf*S$cmayy?1e2|v*Wx&|Y&Cgsuot(S538locpZ0*JYNhd9J0v~8fMF2>A8Lv*BQ>foJEDS|hkKN +5`rx>zFoaLB%?NhE*oAT|usUU-1z^|0*bHS8K-5?qADZ;KBozFXaWM+(HG=KPFIUZBu=&siobks8#Q; +VD{;0eIFy2m1=*9Sx8q|GRQ@`5N%0)P!J-JalVx9M>m9Hv1D$-fMuYWN%Y$LO5*(|Nj0wuXuexFp9Dq +x+mWz^JVX4_|GEuMj?`Rt!Vl_kdn4E6&f!pE@KVCz-qK~&X8n2GEChfhqCb#-fHMjISNz02clbbK52N +Dbn2G5iqCm32On;jqm-3Erq^f?#ePDOFy$J5^OfFFVk=Gj%h;eMH5?_2(4BAHB*({LlYOBbEjtL)QP> +E29HDu-YPdQYbA;56O54WocQluWd=$Gr>9i)a~qZ5$@nRAu%d^I(Qag3#72q6 +oUbWir1Z(1+JW&UXFkiZ>zc)V^|$455Yj7~q{74iC_wPRwyS@L?Kc288VzCluXP8=h^Flv`)(bEL8gZ3gMbJ%qo>N2L)Cc-Wkk@gT_@|`Y>jIubO!f(veiKN +i}?PlY?=RLgH2h!&WzZMH#B>T6#+h9V@U@XI@cjufQV0tAcA3H>!9ek!ecKH7NnLMST@p6o|@FfxBDF +hc1?F>g#Zood+BP&81<*Svclj}Z@XgzI0#HxyT2#erQb +d~A1)-s;whrRp>Gr19@iM-nI0(coZE;#bDsaj&Z@XC3GyK&6fOIJR)quXORJgsqH-2FHPu3c{#HGLd& +Wp^1qFt;eVC4xa#^x&{PsB@1`~(UU#%Av(IGAvDHw|5!-h&6aNJHBrV0o6t>4j-xRWl2YPIS}$@|5Oo +-u$ZKhBudKm<3}0??ys&i)@*$+759Z>664^su|`Dc>d2J_{Z+&r;WCCl1g~BEaZbzU+Cid)i7Gjd&6+ +?_wnfU7$*gvp9FK0+D~>kgEWh>JjO#?o0Ft8%pFeNKFGs&ed-(xz`3q1>0|XQR000 +O8NLB_@WIXGpiVFY$sUZLWF8}}laA|NaUukZ1WpZv|Y%g+Ub8l>QbZKvHFLGsbZ)|pDY-wUIaB^>UX= +G(`E^v9JT3d7DwiSNgnf(tK<{?r_u}M39(NyU;j??vQcjJ1UX)_)V1&NS^ngUpWv{w1`{SE+ +z9>C;M~u5E?T*-w5i!zm!(+OQY-uRjr7mSHCpEq( +wJ>~Ditwq{CS@OZMfXznN&LGJYK@maDe!pvCeW>9mE|u&720G*SY5t|Qm(Sf=oeWCV_S|_D_a}BuJ6K +&jzG5JRA|9XT(MrveB`Mk2?EzU!Hg)Kkaj04wi5M02>+tBXX?HMSEUTx=>gH{T +<#v4UP!hsxwXP(I7J5^B63Vx##w=r>za&Rx*OhrdJ{xy{o)B_h=0qBE*)3guQe<18vb(UukLQ~FR$Z^ +;&TNCodowtJa?KacF^XT9b~;jiW2`?lt;#crC1W_kvPSYPS3N|x0&yENEVQsGg%{i51syV=+*+d +7l#(8@z&-UrJADelq2fbO1U87=?F8sN&h+YEq!r^vzyceoM8TwY7iFyz&ofNfs;Qa4N3i52G~DwFUX% +tOz4h?{hNQd1?}eAduj(S@3$_>aP8Vc;#VT%Uxo%31cvymQkt4R(&|~RDTuPf;IMHsTDDu^DNg@)BI4 +S}?A4AKnsPjTpNJZq4BtkAS`k?a1;~47j!aA`5MZ(~jAU$CDFRg#1c!|qv_*&Y%iypdR5oDr-MN%S1B +H$sMgr@~t9plI|?)6TT1v#0MQ7U2TV;i8YkpgM2r>&4g5^&9s_?|HN`d6Ll%@IgCT2`1Bxfa&DN88@- +6TZfC+v`i72;x>oz)8YPGs^O}Z(Ja-J6W~KKN+CIG(bj@BzKNfpP<8}ByA$3Y(Z-dW_<4=J(LAIXIB( +uuCBpS(Hmxwjgn%cwRB_2eDB54dUEtfQ4RNNCD*)^zX)d)Qk*HicW4qbmRqHb7=+NA{bMIWqI(t*!kMSzi(?IOYy9Zs%0OwlFnBON +{;c#tkqq^2;FV3OdHv+n>iWzKA;6omE^um6^8KpdbJufbQf;PjoFLIr7dFRA#GtMk^pwHsy(tnFSXJAlQ>I>@Mdc$?FOn-!3r9K203?4y!Z%{I=Z|C+1>~ +P%(YUof4w?;P=04oOMZ9^OZwsyeby1_L?9Rz_BC6@2JU8jh6)CX9Hsxi|BFXr*xuR{0C(BpH})l?Vfv +6{q`CB9WjQYNx(#wzV{8ED8=Pmxi#SIV`l*-!U>%VJiy8)|NNnFeKBrm;DR4ZMk73T$L1GE%4pPS}_Q +bpYgL)RDzvv0Sq7v8yFxh5&r(mt1$u5HCi5mV5e|xMWU3)9ecCK<}9Ay3|y~LZ3D&NCRVSq_Ootlpo3 +Tuigo@*!hS)9bF0T4!>$i7!74@wlP8^1F9n%G8DKH9WF|t66XwxF{WAXsL@(3zwHeB=Zkjr((*F*Vq+9=`)cU!p2=ON-BS7jfBKNN*Q{M%pET@=`py08`rK +jl`kbyB^8zTLStR`OtPSMHM3UuT#HV^CkV6yhm@I8stoyn=$fNT@umvxWbA$sKGcA_8y=)?3CVsv;wx +j*|9r*fF$7E#lVWKz0z^}0XjvkT{tdJ|ne&i{bIwP49&kvPV>a!}ElA`-1p6kxbXoy8Wwklyv_`i+lo6pN6opaLnWtJ8@_fPKdJbS{+277PvkoPJvK9 +=`Ja8AeSJ!gY@4Uuor{q>yg7;t+;K(1OGN5PHSwLP8$JhmP1*0*H)B!Gs1-4%Mr*(#GJL<;xG0U|7Iy +<nAQY2)W5ht +Z!%m`R(ay)^A$iJgTu34e(-`^u9I7k1&Zg{E)#M(n#z9etk{@Ycj(y;27lNF+!4OSjr{D(fwfSSmGT+A2**6S6EoTiZS>(0;yooJMN2^u>w*zZNc=J+34+0-jd +D9JvEC_3CXD29+U2gh97kwkJB5>B0=sr~gpOq7QOu{q6FsZ+^r|mZXI}Wql)i>B>8o&mhNiDsW12nK^ +e+Y@S?)TIZbl80*=X)@BSb%tN8l(ov4N?8k4NB>y5$Uga%@-_PQe1x1>&h~ybu|7=MvC^-=SN;z~`|* +6pNTaWD!$LK3;vedA#}O!~NB#C)W!Ah{HmR8(Mrdemz3<&+Q=n;?^%w+@8tA^sL@fl5A;DNkajmg^(j +--!&~{_`O+|NG|(rC4mXxH2||6bso=B71m)+rway0MS}W$&fU!&`_tEd^!uDNe&e3R6sxka4iR}+`i; +szDj3djOYx?XPmt`+1Cb$YfEw>U +={K1*Ts9=ua_w=Vl}@_3-w3Lf=y_;e(H46Cf_8^&oQ+kT&~I0v>25 +v8(MvIHs*g&c61QnHzNY(~-g4^T@31QY-O00;m` +Rt8hcBC#t&0{{SR3IG5v0001RX>c!JX>N37a&BR4FLGsbZ)|mRX>V>Xa%FRGY<6XAX<{#OWpHnDbY*f +baCzlc-)r1P5PmoKe;5RY6H#SMUy8tm)TR(9X$gHPCWt+0bXvUH-R_RgJwpEX&gi5&DN&EQV$R^`*Zcsyb}4jAJ$uY +F$b(bg{H0i!c}Gsk?J@B#nqb-)hoKx778aDUdgDNCz5EX!2hU +td*5#R$J?KT&Mvd}(og1$=P~pe(i3RF<2Frb*$uAK|^TIBt+j)>&8eOo9JmxjmGgZ`2Npc>tawg$%T% +kF0AJ+zl@cq84~%hcPllEESa}aJKw(-YC~y0h{HDTnPsmbmWY=XUT~o=$KA>gRtiq4|#SCd>VxgvH1+ +-AXx#ckwemHi}2Fws+|yopmLp8q_>RwqNxQ(EysNgB%Cu8bNIa-jC)f9sb>Tw&<67_EYocH{KNms+q> +`A-5ucg$5`U7aaM|*T4kY+y)rrc;dU?nfgl9;HibS;T`-jWUPDi-aTF|Hy#P%!WY9>ltK5FN+TN&B-m2(5dOX +#yTY<3blRAT)+!z~SW@SOaX8XFoY4oH%aJmdMBA_Eu(+KX{dEcN_b#$3wh5H66$JLZUuTtt+!)ZZph_ +OdLE8sp34ZUN=RaP7t{i?niiY4|nr{!Ii}UzJCS8*;Y5BxplC*5imO%(eeXw&Kqt&Ej9p!1Dn<+T +X>Rj8#ZIXBIfBv`43YnddM9SEPs$VZh{o+zS +W!=O}4n-!SHEJI{M06==%&T6PysPD1jNS(>D4aAn|F)ng%PZqL68IY3@dFaq6#G#P;weBAl}%$A89DP +KO(|2^{p@+OQRm*_|0|4uf#cO;tQtNN(rJd;PR*-A10gvh|FFf3kd*>y21Y~ue>T=<$K{YC?1UCjdUs +Nn>-VCHW(`>jYPtgre022e`_1QY-O00;m`Rt8g6;y}*$1ONc_6#xJ;0001RX>c!JX>N37a&BR4FLGsb +Z)|mRX>V>Xa%FRGY<6XAX<{#OWpQ9FxM@XF5wxPclxmChxvhoMp^-dqeLznxCE!{ +NzxO6m4WCO+yX5a0hJE&3nxenf-#E5YUR*kKLVgo5`XVv{v-s+Nc)Wq2ON+M1iT5u#4dkFB*aa{%fUV1Z_=77<-*@f6%)~d~5j%(W4ZS_RQNA%&dz;hvlY&xG9m}2}+CAw(F0H +_%-QJYDlIrnCwRTKb?25vNXFAo?FpWHur_0T;np#T9?$Ez&cGsZ$ +hmHri6g`Dfe0qB;#jW*BCd-n}G*nb!<>ka5DFkv~@Fpg|#Ux3Rn4A-=Q1W}=Kn`2l +zdy*N3IthRd@nKSX;#OxEO!WG%|&eD?Y(B0rMA#Uj9`W*wI&>;Mh*^_e{#`?m!-?2BCsXcBzHG)3^f9 +XL_9u6+%*Vl5xrFxn>UlMD&QFx#Ql9l6zH+v(i}0|x4|>2KHWiKYec&7L4I1LYmE5h&o$Bq2T$%|r&| +!j0K*&O{E%hoamT!Nv~2+!@(aO%Fr3T(K;6!M&3;&s}blf^qZ!mglHwJi+d4+9s<-t4K_U@HW`iII-V8Fsc16 +(mWC>>vHEXbtbPV#8p-Eti`N8R;JK212>57pIO}74?dqvNc3v$3ujVJxfheLk)kWd8e$+knayWsh=LI +*bGq|kx(b3(WDLUV#Y`KFWJBpyJ1?sZ__uBIS;W2dOu0l7Wn?@Oue$+oSAA +SPObf8MlXxCg4>2GbNoiTe};VyP*6FEIJiu{+F2Y1^KFlKBrtG>OW9R0|XQR000O8NLB_@-=A2?-V6W +$D=Gj0F8}}laA|NaUukZ1WpZv|Y%g+Ub8l>QbZKvHFLGsbZ)|pDY-wUIa%FRGY<6XGE^v9ZTI-M7HWL +3HApZla;7}XqRg>O_I~b<|?IukZ4cf&fz2dO9P-uy^iIqi_q;}&5`QLAb4~e2Fx4nmeWGz01!+HE>I2 +1p0LTa+@+e!=3sHac3ZE3`A$J^cTE}&~G#ewK!2kpc)zx(I)tMc-@SC`*k{qTB4Uh+z>$Q!QU{~tTeM +N69%dCTDSUEeiqMJlTF(UY!E86g1S=rJ9OH7qaiROVdeB)<* +LlwJW_>;*MxxxM(xw(RFrTC`#ePnKIizHN$n>YUL?9^Oo*;jht|0S2K(yV?hX}6zloVUOo2q>>;wP$| +9&wIw2OKeGw!@VoR)6V%)dOqwwe3k$kn9)y2MtQort# +CS>}x>f>}NE4UykX$FY%ol&N-C>8e?Tije{hp0BEF<;&1)dNBB!d&w-i7_9 +{u2N?JH8^+bJ@v?KaQ(jr%E8;Cy|f2iJq&NEykvo?_^@4VYVnOoKDt;jE`m5YIp(g$K5L$MhT1wkQp* +l;zTntQxA+;QEWu%9d^)B^a7`WOWJjVVxZZu@>ZG#=!ezwj!Bmo8txFUbKwWti~2CE)-}@ZI2h&nSJt +`so9p4pe?S;(isXqiZ$ES`0Bh>u2YU0jI)5SoP8o*f(2z@(Z&RsC}K<}CN^Afyvea6RvX}w +}4{F9~P8W!0=SP6Rht`(A%V6$KWtgS$Fb7S1ntezVnEg!1vj(RWJlVMvSD;(tro%HwJPSTorEgsisFQ +L$nB^o5i2zc3wKwu`A-Ey?HJkRg$K>I(4mbtm!OpG3TfZWA0A5^>?`@qf^7<$YG;jK}Bo-i|SHQq!9A +1u841vWGqM6rzxAvCI@J2j+~0TgjVG(Iq{BKRGRn%GA%d5cCy`YWRMXmo5fMcj}c9~n`RZjL24wTvQq +4@NH|eI@K4beCw2=A9Txabh>o4A=rxwEWNRY0OY_o#vdYu&?pM?n^Lq6Ts@ +ysNQ+Ca|m+rRTSt07pVkj2XiD~EVSv|b(Ee9KV2;bzLp_Gy4YQWVLNsQOS%ZxiF8{*b_5ay=T^nQS{} +5f?I_e;F{kR-6(S85NmZZvk2KO?3K$DmoOYyb5CU^{eRW;lyn6fNn}58zDPO+&=BL*gLXJ>5Yah4-Ae +}Dg)0y=ot+rx5NoYcR11n%NwCwEEB%cSYgN*xZeD`^P4c9TkorI^5i>Zvp7cGww-_TNOQPKy&>vGKcp +iO{+SqDsf`P@A5N-k9iT@qgXF7s2*^UtW*(DYY;${)ZZ`nFcZZ*esxI@u!oP_a%Yk4Bhs#d>2>3K{z+ +q);ARO+s3lQK_xKp>L6;)|%1m9i&WbxjHA5d52s^Dxe$^TDt+0fU-~y{xniV__Ro)T9IxJmV_XMhgQ1 +}^ee0L{6ITAIOh+v0S8?yBLTz8zW@`%%uu)?_AR2_fkT_ATp3Vbr$p9j-u9DS)hGQdW8y2?taUN~S2kf$jn3 +FO(Y)8W%=q(1hCB!o&H!rAX#o$zqxyyIRXQ9!$##w$RvIH(`zTJYzTaAw5DgJNS9+`s^ +FRQtx2ytMX(SCf_8Qv>#K*i&5$v3^A^g8Y<%xv3dPpwys2v2G#KP-uP*<2L*(sfIlt +)3lC=Q7hfdUo?!(SH29fV(XNV+1Sj^2Hui9V0;+{~!GwhHu}A$J6Y)eW@=x|B6(ZhX4djvQ{Rnm5C?O +_dTXu$^OEzlDYEFJqDF+3Wn4SK%Nn6R8 +m@`m5yCyNA-S&mwEeSA2h;EmjBATgy<6V!17}De~+!%eS)oluRTI*Saf@&5>S8lS(*$OlY&9BBElzoY +xZ>FMH_~`HCPH5Qq5Ec$Ku~_sfu>F*}Ex%N8DxW-T7uM$kIQ4XmPOj5M?R_Aa$1AJNM`lb?bG7T(`Jx*wlrsZmWnqC +`I}|JB}Dks((W6r703M=Q)vDfGjn@j&&=&XKobRPdP);W#Z$vbve^$aI1K&Gb7zHP&MHQ96@=?PY%lT +VP}dF{Aw|kahK^*syqnE +7Ez^2GTqD8FBjI~PEOQ9PHR}MEN5q~PcF}l#IV>lSFH=YG|RHr457#5Zllo;8ArGe%+IHb<)HwrpX06 +=;sPL=b9MBefDV{5tc0OR;F%Kw6gBr(tgRIhLBh;+4n)EI_N?h3#cov()68&;O|DR7NW!3dEM;Ip09M +8(o2&%|(H-754)F{u?qY-YFzjR#RWEJwe&`y*v6V&z7ONR0pg9c6&<>#P0vP3L&@(*c6jf*g3 +JxF95 +HM%4^L?H6g^+X_Ng&c*z51qmGvW1vZR-%XS->`NuS=SY1mgLXm#auB^*+LKGVF%>MzAn;u6S_=pW-Q_ +=);&8G!{-(`;D+Y3S(J`~S*BF(!v(k7!u^d&NruSG3XK2ha<4<0TT>!JA1_y~T+~kX!wm!0V@>VuRQPLH6A;Rv6MiN#owUMUk4|8BUn|9T?O+ISXkxV56C_9Qf*PHeU~-;eoinqR3> +Z8(v;mLEix2dO2}cVFGCCUgpz$p4g4fi<@n +**+K5yP~pJmR&*%3c^a6tDzww6f^tQzS|h3~891tsh~1vfw$(9d0ZgK4K9CBdFd>(eIoOnG=iNUl{KL +Ps3B0A2FhhN2Pm^pgAo-CU)sdd*j|nCp2pG97P?W)S9*#He6xB1au8KnER*+F*{t +V4`&$XAy3V;AO`W#@}geTAf!B~>VSc6Cs;v!`C98Q&B1km7gY;*~EnBhLIljCsY(^wD!r +Yt=PJ3eCG%E1g-MoR$d3J5uFm9ev-N|*O7m{o~MTB|J_fryjq} +<80lx7;#8mYxUm0=v0A?{&kGB`bI^Vya$;+4T-{(Huu<=vHp$ucnF6Qj&Qe#&|R19FgCI@!d*pCY#uj +K5)VIg`UW@utedl?xsE&!hzq-QJ60HgrzSb|k-+cyT0A^xZsm<%Vy>DQDCRNn3LyJbrIw>Sb6V?Vq6H +&9Ch1QY-O00;m`Rt8f500002000000000V0001RX>c!JX>N37a&BR4FLiWjY;!MPUukY>bYEXCaCrj& +P)h>@6aWAK2mnY{22+E`Tvp%#0012X000{R003}la4%nJZggdGZeeUMb#!TLb1z?PZ)YxWd2NzSi`y^ +|hVKUdhrzwr!4dS(OP~wg-Lw>zEbP*~I!-)sL}kfH@-FGG??|#7yQxn4n0e>XJCkME!(goH_#XtJ+Lp +^UP158MPh6sbqkr@-sNpDTW8kDT*fYXD+_HDn2W2p4j1_z6(4JMpoCufPR-CNCxF80+Fc&AtKx-%a&yPoHZobq +;*JX|ru{%2a3eo%=QY`L*i|CY#gBX(+c!JX>N37a&BR4FLiWjY;!MRaByU4a&s;>o_7P`hl90`ew07iUes+89mhOrjc5_1ws +aH(_Liv}fq7>RWU~tI1OcOK;NL+^j2+CV0Afd%$AP=%Qvx!xx8Z1jF^#LrW$W*H +Ah`Ph%Lce?BrByI9S`l23~wmcNQeSMk;p}9ta;iT*x=mJ0JP<@yf+#4%ESB1DQg9(k6qLnz6C(MN}>{ +N0IoMPlj$x%^>>iWM6c)$o`bY=&sE2>jqJsPkvqefa#ajW>8zs`21ZGu{1XK=`ztubJa7)q4iXwS8+~ +QruL>y(Ao;)TyxyXMDV={!ST8*YaN>YAf~(0ogJ1kO{Hd*J5+x8wpu-pIo|Q{-!#6OdZNofPyOtM>j&6bxOcQc!JX>N37a +&BR4FLiWjY;!MTZ*6d4bS`jtWmVgb+cpq=U%>w`kRL1qj=aq_4eDYGq}Wa2z_1tV0B#TjT3Vtc!W5~J +lwBdszjua|96Qw$DRM67%$Z>nMO)Vv1-Obi$VE|bV?n5X$(4hr&Q)5$5f6k`7MRMRk<1m^)T5hRSZBn +(b##_QQ8c<4-H5u;#(~sjDO7phYkOYW&eFv+Wm2Xo+{5Rnm|ri#@(O{d{z&I_oRO`Wvy6kh!fd#&YSbOZP~)AcL*`rJ}$CBPVz{+lh}{m +-{Q(N2^~8xE~%EzimCU}d;#je?J+<>4p)C*zHSrT6w|j5ZfB8fNWaT^LVa+Pb7v_>N2y9C*kL +pIB8`t)h@^tj$`SA$_6^2eiq^^(V!77M@OQ8<- +0!-yxV;A#zDO}a=lT6q1rfb+7G0mG+%K5K%Z^o#QthJ<_)V#U7eS3R*H-vbSEaN$9Pgp2gm63=@eCVh +lIkp^}K1(sfeDM6;@-Nb0@bJNla~9-lE8WT*II`(l6jleN26kfI8PmcrVbRTI8|nhPiU;t3DX;+}Xag +Bj69;>aWHTb=iZ$US#hf#-wkXa`y579stX7*{G`--b$Wfr$Cc5Fumtsb{P5S(>S+739zhO6+PA?3Fo%YgXh+WMLkPl*rm +KThHdu9oK;eYiy{-eWy*~152Uq5C5q+i5->yXS8|;LXo{PReDOrj%bWSwAYTp4+Xx%X +;lpR>f{rGhihcEzxv}@J{c7#IY62~vKO3fY1nPZ_^z5FSGJtv15ax9Q=Y_Umi$MZ=7q2VwD4m2k+-zk +~5MY-qPaZ?M0Mtg>%4oEy>pgEsNorS=0)4hJXc)=--F$&?QuOB)%Vn0tp;*&2gGY_kK{mbI|W>}_2W^ +6oJE&q6U+mUZN=W(OVsV2(H#aA+L)46H_eO#Uj`gZN}U3w|8UkQve|WWj^)DPgo~`o8MlB@BWXlOb=?r*W3V6-Vean$k>(FR#G; +Jw%xlk#~s-q*u%TRF0JLA5cpJ1QY-O00;m`Rt8ghJ2V%e1^@u|6#xJ!0001RX>c!JX>N37a&BR4FLiW +jY;!MTZ*6d4bZKH~Y-x0PUvyz-b1rasy;yB;+cpsX9w7hW#(bEBI?7Aiv^C%kTTyhuhBnxO0fT3#j6} +z3B~cKmI7X2FzB`JNC|P-HhvkDKQ^$vQ51%_8%^3S2a#3iC)3k`!jfP{1V>Dtl&DiNY +X@a;GAEoV0VWxw!5>tlVpV~HI!m^XIaV&^rlGiOR&S|f1kY%-~Iaj-EXHKKTP2FMCl29Ds&=qo=)JrD +pElxW@?cXL3kzdSe8M-qp$oDkphj&HE`bU4&WbpFsYv+>scy7vTH_*jb6#z9lE3llgJ%PaxKcD6!;nD +JQE|sSE5wts(q93C@fO0m$J+V#5eIT`$0}?+eZd;W*q&iUWVXycS5PILtaL!5F>*bG8<5oNh-XOdBHP +3^!|yvPaI%=Ju?5-J$H0kvp&Z-ClCa|!iW90pmb?H--Azix`9dws0z>$^h&@oDU} +AjmXKH31)>GDG163(IWQJ{kxA}lyzmfd(sN@zJzn^Zi(!vPk3yZ&`o<4Z$Q-{crVZyhS|JNPRsu6>X8 +Ly1mbImwMSo^XarBEsf3hT}e>~C^PYn)Yni;`mDXT(xem9f+vjL!$heUY>#-D(BCM@5vCpe+dbl#2CO +#YeVk$|;;6~DsdO4od&KoY$;Bl;?0Bo{ZC7;XqbCU~jU5Xs~tL(h;0HUYGR3slsam$BN9dijaViECYA +tOPs+swn^z_%gZ1fAT7c(T|Ib@d9_f#2_K!xcxY=X)T60N@hZ@DAm*Ct7u${Dy3x>vH(ss^0Dve3Nk<1mQ +K>QZMwC)`glL9IV&tpgxcSSzaE*;_9Ffg`$uBL-g%n?~Y8~r+D{!&h6LhbjvimVfPJ{e8ZB +H*C$vb*!Am^@Z<%%o%Us4Ya-wU@#Xb4?~uW>RuS!^f%YEo)?_Hc#p +oIjRAPVhRCrRY7uN-L;UP~`xfZd5-#$&-4tm})UUzXL$}lX +AOUtrlo+l>Z(C}!f&A^Y!gdBtSKaBK_1byXQBR$Swf5mH9Bs2tjit4iA1-XC*$f=Dt;;}IcCt5|?T_c +JbG5NIDZGZHf|@R_O(d;=NISS2B#O41AAgPap#B8{E+q#T-T@h$n@oki@NguhidHs +5}WHj5gG%LPW_j!S_jCqkOin)aRhdi7A$6o^?cU+HE>D+a4KLG7UOiVA%8Lj5>Js{FqI;&B76KK +7eyHY?faqX-bDDR|&ox}}G;x}?hJs1w%<0l~WwOl!jsM4sBV^EO%pMVqu%?|3d&fh+SUP$hRLQQ +6Z09?b_(9J1Vd+S|yE3;$-Hl~-XGtWA+hW(w?PN83%;DUvsY3fZl=%?>v5E5SI_H}PI4W3IJ=>X1+%4 +bD(KR?!ou9l0ueMp6^}kNG?MXv3`WSiFhB|Y940A1<<97VqdLS70$GVYV91dsB^ZyXWJ$?^=de}`R7>;LY~zfem91QY-O00;m`Rt8h#6w=$E0002}0000V0001RX>c!JX>N37a&BR4FLiWjY +;!MUVRU75X>DaLaCu#h!3u*g5Jc~R{KJrwLYof|>ao8escxH4O(d=r|Gr6-9=fMt=k3fYrS2hOv*ao-rr_D0I)O# +ixVCtNFg8*kO^Lwm7p}9=Wn0w;@^YW_@gGup&We&==ch6riPz}Q;P$wd;tNnq0|XQR000O8NLB_@5nz +oT_yhm|=nMb=A^-pYaA|NaUukZ1WpZv|Y%g_mX>4;ZWMy!2Wn*DV>WaCxm(TaOwy6n;nIKO9XS3h +H!L>LV(VR>`(qDNUjzRa!+WWM&MwG4|B?%%l^+{{y=--44yrSrR@SWvURc+L0RLg@u*;x;}O(Y7f9Rn8obBfgY0;;a&}AM(6WJ^ +cxw(Ny=hsK&?SA4U&8$xd>&d^(w_#;tT_s{s;w3~X&URQR@9#bTd8)^d2kK-TH(CRMFzrR(Q_^qBs|r +fa|n2NWQmt|Z{OX$xczYd=Jn!__rERh>(~3gXih~&nNbXNJgT+up5ChV$Cp6?KyGWNgSOJ +@qPdPq6dfRsKm2Pk98PJO*6$VWyY#YnpUQ4F{C>-7;oD(aWTErP+dTFgu>@ +Y|6eHcKdxwHd_ee%UehA^GP$glXLOf_lkFU32{`smVjCt;<>Q2d#lSGYkmz(Mo=9IwqHQr|<{w;_d}C +c}ZI?n{4|w4DUYTB$=ykq%=5nrnkh(^N(4{X4%OStA>1Tt(D7PkWY$sbS>Nxr@I<;q5!)sK`bs~2}-r +=aM?{qkp#P3qKs7>saqi-a?C?7bCRcGp?NQFZ+K-&9!f`CU&heRX-apZhf-HtRbSq>r`sv}vqVp<|Z?wac3L+!u%)&>@Cpt8=(ByU;zXILriIXpKeVUx^L=bh;t* +J7U<{9Y%~Y*<^Spl!v5r&N|Pv3S~bWH{sC=^sWJJ;6z~{5hjfMmqQQsq>Ig0c@>;U_xzhow_QR5^f5Z +YVP7YOz$dy1ympc|vEcYuou&voPqdRRMwM?83=7jBD$RMKM)|t)(OC>kYMtGw29i=XOV~Nq&aK{ygOB +Ss&uTfq9HpcC?8j$$*jv|-aO*DJLOfhC@EyQS3gJ9QQ}clKDhyfc&!04yUSOIA*VQNiG716p!>}3{IH +$lE|0SLs)&cBnMc~Nb#$KY!kq#9&Zcb*LfsuxsdApVIP8gKooB}bH1DsawXb`h8^hP}>($q-)oIah50qdM=HL4E2d>8(IZz9?-Kg@E$yvTZs?6Ro?49CR0VHzBlsc5|~{$OlDXHz_lL7 +9jc-dTs(-99na@FDS)tIYR2P$-(9?+NjxXeair&uhipl^rKZlaO>8UVd`^SbeX$G(msthBMD{U!ewfl+SGA;WKS+m=qWv)_mo~Pzk^(~e*IiNT^JW@o4-6hCgx9UZCnoZXDxS9&wqxl{{T=+0|XQR00 +0O8NLB_@?zJib!~*~T0So{DDF6TfaA|NaUukZ1WpZv|Y%g_mX>4;ZWNC6`V{~72a%^8{Wo&R|a&slDd4ipmW2nUJ(TWcS&U+jpVenet|TX2LjHSaWLvhJyex&?Tq2ETzM1(n +GjgR>%j}m<$Y&;s{T&QVILj9si{ +pw;W_23qy;0sEv>>6W(vQzNvdG%%p=$3Uj+Dp<;7LIX8I+-J3T*=c7}}~cZ@wSQ86K%C}Fl1n2s6Y4* +m0ByTr|0bnKBu>@6FyE6tzj(BSE(&x4F$jgZFjFws;j`QMG1t-U;EEIMBv&zHC7OO#ehx7^0F>M9|Eq +?<^V!k(C1D;273h0R4dW9^0|G+&8$QBrrJ4g;9gM0FKQ%}u=zbMi!{yz4-g>nNO!57@xMoEt-RaW8|h +_;>#+_%LwOABLUMUlz`<;*OSzS%l^C3dqiT@fdeX;(?H&AENYcC1mU^c2k+!UdyG?&3>dV&Z)V_f}-W +#b$G&z>U9CssCz$qK4B|;#>)O(Nl2V_E(}h9{8R%pdBKYsyY-+{gGwJbUdwjl8rOH|-X}@cDOzK(F4D +wZe&EFmGKUbjOP|3`k{qi)H|V2<*qIOO-PP4EQ06n&1;hv#3nh-8BUXr#3CjmhlZEq0?4U-@(JSw5z3 +MpUopesZ)~dJpbOBe*M8602L235!{h!Vbd-DWL0+ckaDuJ0I0z*%XmnHLknFcNM3fW;;5A3PJ$pv$o- +Yep24W?7FG{cy50OcCMfhSB@)&iGig*K7KP8>(0TpNqF?JYkvFlNE1NfV|JM4yBKf<39eDVbDsc!V{= +0YHwCrR+(fdv0-$rpQ^T=qR@cLg#fz}%BdDWUMt3nYt>W64XYNz?#ifTLn)2#YvUw_p2PfI;w6lJHCtYH%udF44Mby`wc!JX>N37a&BR4FLiWj +Y;!MUX>)XSbZKmJUtw}*b1rasg;Pz7+dvGx2mBv|b7%sNduS;%kitSgE~WHP=wS(FV$1fl_ROG>cjK1 ++_iF5TlTFjahsjuadU<-1OR_B#+H=Rc@hK8n@}OI1iQ)YezoY6y$V9X9IlfdT@3RWoI-aUNhS;Yebii +}(w5g7)IVbR0IHFlJ%LJya@vwpWXa@2uV%%fCa)hPcZ{h^LSc3_~@iLhJwQccoMjOwnX;wSsZy*ZA>N +=uERUrB6phoYm16-r{HmD=K3Gz8<$N$lD%`UQ*FK0TBbe7nLsYJ7Rm>T*T3n(ml^Mb)4wVL5u=Ni2CO +jDg)w{jwa>$$e(`*RA&6NFw?4RPPb$+?Z6UX{M$9?%2>itN`WOfMIKLh6jgo^j`grfLC$sw +t9Qe8jp9ORJvJK0@z+2Aj_iXjq$eruS~TnvAmolrSWsoa?tWm-z_ntim6r>S6s_XCJ}pdxw2-+oJ#^- +$v5)4Ml^Y(KDAFepOAWQOrNKm^8Tc^#^YHZqseLKxHpa+*UJ`~exw{sK@-0|XQR000O8NLB_@S +LZr?76JeOwFCeF9{>OVaA|NaUukZ1WpZv|Y%g_mX>4;ZWo~0{WNB_^E^v8`lu>WmFc8Pzk@yZL_a)gN +Dj=aM5_@Pzr%8;Wu6qg*a*_*J4R&N3A-?_2NeG}d)%@g~e|P`$KbMGoskDVuWtf_NPt=cvN_m0pqrPm +-iKtwwJ=jwz +|ILtr#*h|#ng**wl$YM9_B;Nl7_I>E`fp(f*Q-RFufgh~ekP@kR8Mi!y%5fit-<;`T-3?>xX@}`M@cP&txtEwLO +(E(LtfE=a8dF=GAVW(VusZ~lbq`L@ReI({k#MR+3H7uDTePLd94R|H;~sGaeJI2`f+|sZJ}3#Rlievf +p?2zOPCrrmK`R47LN!D6xmzL!cd@`&uB|x=yYnc;zareyXb)OeWK@uxLm-TU#+q||zs|aY1zH1X<%zp +yF0`QwAL&v;l0V$IPFC~PcsjYX>dsue?x!q-^%Az0%#XrxeA}`^qx*0~LqCIr-7(-YgR6HP800$Lc@F +&dGUiZNnuad^7U#xb^|e-6!hlp3mP+C+)_Oc^&|s_8p){T!-M2c!JX>N37a&BR4FLiWjY;!MVZ +gg^aaBpdDbaO6nd0kUYYui8&y&L==249kZBcF1Jp@p=Erchi{3_YYFtfi4PbhW$8tmNw7cXn4v-57+$ +YUa)Rn58~=jv$jP7cKi9;uOf8fXewdlDLNNA(D5-E*EoApisS#i^zIf)t0K>kb7-P@2PaTXt0HsGn){ +QuMMAM`-b(Dpr0i2^D}&aop*Qxw;w@4x61|SZ?#%|Mi<#wm=U1$48~gUwt@6%3SR4_N~2QCqe6P7T5G +1n5jrFKG$1%)0=j4ddN*7WGPKOt>a9BqvenVzUP2zydO~tx8RVCy3OeqFnHX6ULcf??58AsA*FVBWuqEaSnWr1plQ6=by%s2W(K@(sfIKu~PYRm{7+OUQ*g7fFu(UMR* +YaaN0TA-QCfl9{EAy5y@S_8TU)m!}KC7CXJ+9pb-qzuaZ@-lEa15k%->46@y9P87T0Sk=DYgejPfHf_ +pEZSL;w@5}x+BdK|c`rl~!Gy4sLQ>}K&LQ?O$u_@=#Q!{7G{%AF$$q!Jg*dl%2N>vy;xVp{v^(wC#>S +a(hmw-IDyadU@lDwV%&AcgpjB1F>H#^^BJ^l+&O9KQH0000807zB_Q>k}dXZHvI0J|3e03iSX0B~t=F +JEbHbY*gGVQepTbZKmJFJ@_MWpjCRbY*QWaCwzjZI9bF68;__|AFUxh}?P=XLD(K3;1wtn&1{Kvc>KO +2@nJ-Em1btGN~h}^}2WW-|rccdhy$~w;vKqni&pfo|$<_xvsTwtW<06^5xSfGCXTL_?gz+^`8t^gCsTQr+4{A +GMKS~9e%c}{ybi#0_&4m3Tt(&lqN@_J>Ik#{v8mMKRX7>VHF{u-Z=hAIkb4EA=gVyOf<0Xt`g{U~*Y` +An;DIY|!Ye!-$M)4}8P}nrrYecNIwh%6(0A`IW9#L9Lo3|>ZHb_Ti09l?-{!+*+#HJtLKg_`MqEqzHgb+!3uLr!9-b#+(~?dV@#KLDJ`pw&V{uT5>c(W +jd)&F?5?qnaci5kz*lS#OTZIvtdIuAwArRXN0yZQw63_L&9K>wjW$xfIv+7@n9}5y<<6NzW33TlB;dt +p9Ctu}$UfGqDN#ZEuUK-hH)!V8*wjnW7&*-&`N}tny}4n_A?OSBQ=(DndA-$Bfp&Dw7J~EA +RuE^df>1Rtr;Yk68oa1$rCCKUrDvMS7@?8aU#j4{}5^f;W*G+Ibd!>_T +V9QEV{mEr#qQI0*P90sc#;@^vpk#}O)R7B-#vNiyqSB;_7D{}7qaFj_)Hsb3)+M(|?Gbna`tNby-Cp{ +A9gLPJ-;%VV}U6)T#>nxS+F6**T-E|exMcccVs0O(J_9Q|c?+o!6GOKJciA^Rvu%F%ECF8})*?SMz%j +JnR+srSI`1AB55#fl(SkltjMH#b*re@4qsD|!=3C$%nH>q3l!;eYHC16qj}cRWXL&D^^7`|DSnuy4Lu +ZMa$33*rIZftCP#X*t2yzILvIN8FdhStVB(V)HvdpVb;8nQ8{jl{2j5MVNv!^vV}~8;x63aUd!vHGl$ +z!>j-*M78zV#e1PFo(s~Q3qL79H0Y_2b`=L2u?yc4;;s=ejuCBv2QtuCW7s0vHU{xrcw_|AEoDj%xss +pvuxoS`>k+Y>uIIRL +(Xf$&FH^W;A%;=0Cm~Q#UJjh=gq_hekjIFzIRHup1nw(Qeamu1()K0{jusQ~&p)F#xQD+#$O3i>2!BNyaR>|~(1PoHE)fOh2~^E4LQ_Wxsf +uQnq-6}atepWr4h$ARO{dZL?5)AQHC*=QFQcbA}gjxV}RIkfAnB}W-D?~5Wkc%6Y1GHwX}wFIC&J$_$ +FHz6w?cC=64VCtCX=t~PD#F|(Z6)8=p;1h(_tWiXbK&~N(SfTzz8v4h;}=mfcCII#}-$fx`I!lVwAW?62J@nG@N)|A{D%sPOwVr+WLIYc`@`Q@ ++(@W4Q}Af>6>7qZJmhaD0t=f6MFMnprY*(nZQ`JS&q{jH$@f!$g6tE=fZi_d5&N#mhx^@t(C6-Wvpx +NHxJw2r!UW6{q^;CZ@&LqR3ny_5lb^1W|NoGA;h99ombxi@97<;Y!dDe(tN2=rb)Z@c;DQ2)!PXvC*zOy^0EkuG~5!h<6&}@UH|myn*AsIetZ4yhuiky-RHNz3=ao^CwivjkB7(j;x1W>D!&SYgS +|y#KBj`^PiOAj7Y>H+T;#k7t=$Ofc7t}%^+HRbq%EiW#(tzybI@DL#S(L(QABU5?NNI<<`^*uw2#TYA ++*(S{NM# +L?i3IuO?-2IKL^5YUP15K5Bn>M-g{yhushx4!kb*yfDDyu_GCMRPEu5=vpCZj{A|-UI28r3|;z0gQi8 +3!INS;WX1SN_e*`-qPws!csSs~go_Fv=?;j`t*#muj;NO2gvm7rE2JhJ3eFJIwelX=nhXe{r#1j4T{* +~n$^4a~Pw~eOm!Hq9+g5b=AP6ifJ#Sr1yD1nb(naqo%J$3GzSGb}$b@B+=#=!`f5WTQ!F2OW3%Ul1H= +D$}u$WJx?V>XtjuTJMz`;SglbtRTmMzEqnKYW;J@&U;vCj)e2;JfB%@2Pa`2}YU*sL_~jmHT+J8uc!JX>N37a&BR4FLiWjY;!MWX>4V5d2nTOE^v8$QeA7pFcf_+^gmp(mlj +=MJ_;MSQBfFfXxT&&jhbp8O-q_i*I&PzwbfaNc`5hnoO4evA*3%PH{U4-p`t{`%!ou$*CB+sj_X7^i2 +=((?Lfq7q78(3D*36Fij*#eGF&SrX>NqfXe_c#-JPXU7=AQ%;BycJughQ>2BXRFZ3K@%=wUCB=y}#T= +O@w?u_yBiS3h$N@=9$rLpp-6LXF6+)yAI$hL5>A!pW=%^{tdA6I*ysm+@hWh->;+> +B1PGab#H-$z45>M?}~E$`pfi98+zlH!SExvglY(ELBs48G}v2kVW<6buCwJ4h-}I9W-q?@GQz9z6pOj +vEe)$Rc__z+`zKf@iLAUNWXw&j6@)s+JBwgtCs&9E3^s|%#FlHm1%|PLiUKSJ{XIhqf+vK#)Ynu}&mR +_4CI_xPzidKi+b0xUJ5lMhPoLG4$tuxjGQ-RdP)h>@6aWAK2mnY{22;9sjD$x8006!W000~S003}la4 +%nJZggdGZeeUMb#!TLb1!FXX<}n8aCxm)O>f*b5WO4le;CLGRsyu#4>xe)!UY;94Gb)t!cKAtiUlQ6y +M!rGB`LeQ^tX40qU2qBouoi>aAb1$W;mSpW_$vlwgO76lzx!)26A0hqIO`nx>W^a0*t8iy(oa=qOJrq +TH?N}!O_9iwMMYxqZ6pQraK4gjLh9sH%{uBtBc>cvf}vY_()cbHV(8MKW&HKrO_3*t|8xH{bM+r!l$- +T{_ycpAxhXNndR$yVJz;i$y2?Flk?*vz<(!%BF-UBVEXHb@fEH9>kQ1VWwZj4(~^<+lhiJG!fNlUI7797H(7w*KSB +Ui1(@(KlI9j^;+3IxjxUTZ*W6nCXI6;!%tl^Ch7-+NALDHhg^Hs_hq(m+s(;{(vt}B!17O;+YZxOTTQM|J$L{}x0Qi+qkM!5CvqvF|TcH28a!v@mLj^QBgfNH+%5=Ac8U8$_L83uUWf +H|^c#zOrB|a?ad@kZ0?37~O)tG(1JHN3)lmkhG(W#h4Xupw%+-L#4uEVChJ6fjmXb +}$UGwxYHY`PM%SVFXyq;61}GhGkTB{t%V~20yk0;v3%Lgw6o`^Aq%aXvxlCn!_Zh#9;mlmLf7deimVz +B$FN@p79m7Qtb79VTxGO8$GIvx2ztF#oq6PZLA_(1xP4QjcZE*6R +Z2&cVZP_wTw8rRm?{Zkwea*o{}aK@+vuSv%Es3T&_TO<{CoRa_`*_{Fl6{41(*G_Dy+c|*pB>A`* +kSzp&>=lC!FC>Dhn(@Q{nA>8un(@q0VncmOH|tXnVR_h|zwW29lr(7!+pG5xS@v*A3QzlsMCI+q+X*- +?a_Sds108Mi!*wiG&`W#z&&>n^VU@DSO`i;wZxA6b;h|{;_b##)F~Z9w?sq7f?$B1QY-O00;m`Rt8g& +;B}rD2LJ%n6aWAn0001RX>c!JX>N37a&BR4FLiWjY;!MYVRL9@b1rasy;yB;+cpsX9w7gLa6eQA6aj1 +3V(^e*S(-J*)}%G|cw`5)t}mofWIDsDF8>zKWesf*dz3B#Q>G5b?mcYG9Nl(I~wQo&2b^F(||Md +_qgww8Jc8ed6UaF?#)`p$w{kf^NVdCqbDg)!RnimaT>ZBj^^)*_AIi)p&5)V)O*GHXL09Un3Hk*HvAy +>p8CaStwd#IE#>aFtOElo`FKQn7$b_B45(Jo!27fk#^983ks!E71k)K^RN^(HVQaaVt&w8j#yTsUafh4q(5zy}7=6NuV#@0PIPPJ0=UB{`6zGdiKMU!JRiI&AGM2T +VVsU27|#FTN_@MvokX1w*i?|EMm%tg!A(VEn^je)PwO+6Xo>#)w`~)@i +=QMySpJf`Gw(*-Fi||-;7Fbco9U8*cn@Z5Deo +sd`wYfOAWjaIRGAa)-R&K?!@75B|d-@;Fhxol-7JWWgkE7p31CtBJfvfN3ch2*_e`mEN1s&V=;04jzl +_9*l4)7+I!~}zlRj4fI;5bL?)iEIY_WR-6Jpx7$ul^UvWxeAG={!Ucf#96n9{N7Z4$w#$t?DGToWE^; +0vpK#^@|HIT&g2G!G$!ssts4nsEq{jsV7>mm{phlTszFuw3zX*Vu_u~;*9ki5=u#)L!}b!qXEn4Z5AD +QaS{Ng4sgQ4}S(DUyju00qUNDan>1m6-(VAtS*2VT}Ll0Q;(k`RtW>@jfo(sib2it?ai5UD#QV5Brys +q_*AH_-HMy@P$91q&zR7u7J_VwYkQ^PFU>$uu81=SiNOzAfF5Xw)8l_T}dhE1FYJF>S1}RF*z&IegkM +~o=zSJ$pZ?jk!1e1C7agY?ZgD1c^*U{a9Dfp*V1UHhHkUtw9oo!sw?G)*u9J$hNuGd8_h;mIKaYqB|0QDI +bJtluFF4+Ok%y48GaH>3U)*T-2~}fI`ZbmKKLBru3%w%3;!OLDO^(gkOiN{IgNiL!1@kiD7Nvp +K^40KDvV0K}okc0XH{a7f@r=-kbGFvNx*RUYmRlAZt%$aY5tj`&v?9lEl^hu4=1{Fpj?m;pA;RzI|=e +Qrg8Z4&Jv!v)_6t7(4Iv^~k<{Z}-ho=H7QzE2C<3whf;pYAnGZTwUuzT}y{ZUZ+)ZFqxtki3IAC+e1) +(C2qw(7sXF7g49@E84JUc);JbAq?FT4pZGjDOAnFB6iI{Lr!=3o`lBjusQ684>Y08YX{HIzS`V#sw${ +Fj0Kg2_~4fYwib_}A1=HUC_;3qkfq@6_Z5r`80$K6V@IDCDo0H=^wFtot8xaX6%+6aZ+FuNkfnQ>RmVU(m-Aon@huo6US;&t?q2oGjZ3{)oLs|LV%Hj6dK+Ew)E4fL +scNeHOTY4dB#p!t-*Fu)nd=f&8W@KXYNAWBP0(VWRS1dBV3gtHD`JV6))ghB24C*BDc51)X~|OtwA

@6aWAK2mnY{22&^}WXiJv002${001oj003}la4%nJ +ZggdGZeeUMb#!TLb1!LbYGq?|Uvp(+b#i5Na$#2L)CG;g+Ovy(P +Z_Ye02lD8e8Nwk^&nxYiE3h8;p}4T5xllJs#5dh49pX(G*p&J8o^C?Qte-s`kZGBqww&W~jScYE$to{aEQ4k$YGs~TAn(x>#Um)ks5T5NJ +7VUR0eLdp$6~)0A6X`fqETzHesp6N1~oRbOz9>xzo5m*O&9>LYw*V&Zy-+K!yRNis%1YPSqU}nrSWIR +Q&lyii0HNSjinB*ZXv2A9h8aYzT;xOfy@XpW}c3(Y745Us|l59kBFyLTiz +V^*=bLMgD!0ai#pZc`6?AtjvCV`@DIAyIV+K)@hpZ|Mfp%8?qhrsu$%RO>IdGq_x_85|uTc)b8x&?U3 +I%i(gq=L0(KjauMKND=|N#GTxT6dYqhI`O*uA!Cz2I0|XQR000O8NLB_@|AIvy2Mz!LzcBy+9smFUaA +|NaUukZ1WpZv|Y%g_mX>4;ZY;R|0X>MmOaCyxdX>Z#`_IrT*hXIYCq)XFHx>$6Dx>(0iS^X*ihHSItMM;)CFX=;>vBmReOg*ASk#l>O@w+>g-?i_f +b&{tUErfl+>r1hR>s7%w2Ab#>Z&_B-Lcc5bTj$l}Iwa@$J|q`OmL-dfhGYV;Ny&?lTyIOp^CSz&4G@c +EE7>af2Q$m**1z9uu}kJRFy0fQ49o_9ey59UtiD0A-SXFCD3IF>07wun{85>r5`3&Z>~Sg4#1 +~$ThL{KWST{9#_y-&i}Rbw^;P_C_G1ipZzumffMdhN(k!OAs0tdVtU&(CVn2HRjKH6MU9Jn7q*5>RVM +(_jKlx%@6ufAHrqyP%kL?1IhEa%PbLsye{}hn$OCVS8@elmNfd7v_RA5d7iqMCQQ( +ju$5l?o^7dOl`oeS$YuqX<(d*9vBD3Kydk?a%c%7*2dBE+0yT=TqYmCplCu>RpbD#pY!Bil9~dZZCs4 +*D=lO}g2kbzdwrfp3Xu!gUBq0mA2kM_nWr{W5k!p(;G@e}je11v5J=YXDfw$xLllNnbX@L +{O`<+mJNw19I{MS#X}!t3mWLN~NAsz62ei-J<3kIg$m<_%5=V$G?J>RgsgLF2Y1xVF3X=e!WIItYA49 +alWLu6r$ctgQD)>NhBjPD%F=)keo5)Or@&+w{-(r +aJ^J;VUDzJLfa)pE;1!pqj97Eu3EBKt7q)s$)j4R7E*|97G4Oc>$G-gJKJzEGrB?mLsyF=Jo$&kEP*b +r<3Q4tUcLN}~L2#^R%VMQ6W_C6wSWn{w$d?Jz+Ej5}tLPu&Jl`3Tzj-4lda1j3MGXj!PAa$C;zdfT_s +00EBO|L=DqmkwDwh1Ew-Vu3E;ixN*@pO7U9gv3I%yWIiyj3enBZMf +4X-iT@-fKN(cPNaf3hJ;KVRt8t73FDCq*gg#h6`{(nWmI~gQPQKXO4B%*p$02_Tat+fx=q0A8v$~Vj4 +25+H}vX$7#Mb$2Dxj!4Mw3x$Ak3&nXm4TqnI*V +zb?tOx&&hjMe93D1juAOW2`+Fg`yWkbvJ?DC4OgspHJ&2XhV?fx;4CTz=?XhDf|Q}lDB{pcb|x&XlY8oP?%IgV14k4W*O%A +R87Ow%4TVtJ=9H$w){M}{q>!IwkH2PjYcNDR^^vxZkkBH*^Qei=Z$z%Jp<^pcr~puuZ-O8kSgm8*q4m +FvFE6h@G*D=RaGIkg=eND}lnRHcTR+1JCWQ7$?JIA44ay9!=4y5Y28qQ-PZ89~`f4M`L8KGMW=o*cT) +N?+?(1da*rQ;JO3xk6YTG@B%KnB!Kltq{-vMb99B~DWDweu9yO2=5e1B$(he2Ks6|sSmd*CE7Qu`dJ4 +W7WQ&f+f9*hS{V;TOuwU+J=KrL~9>~kWU}FDm4P-A=$ut}R +m1B*j&vb~FDp^<4b~pc= +12wfu9Vn!7$vN{t?oxreXuId8-ZA`6*Xyw^_tS80YhbksF5_iyrdeXqxiHz;|AO$ygUJ~@BI_;mX!8u +N9&tNDvh*Mt5Y0X(&Lg89AZM6HV!oyMBl&W2JwMO)jzQL^aXy61ZBniyxXzP}&NquhP}~on1l +@+l7It#)24nA%hf53!>SWYcKMi`DwWo|Ay_A~~POGLw@>CtJ!%rjJs$IX(!<%J==Cu{{zIP~a`5r&uK +_FEpa2@6qR%fDZTu6baX&_Z7gx*r99wZ_9gwK~LN0221jI^M6&^=K5qeY-1FB*d(R51&|3Qt#fp6w44 +>!NDY?Z_B&s!eQ){)W%U@(@6`v8LL;r^SK`R_^PDC4>^27IJ6}IB4HlgX&c3<9FFlH)r4=%?qBd*d6$ +X(2)j&j=gAOcB8%_P-|4-uckl+{jGx5P>m1krZL1rM${f2k)wd_ldPg$4MHtTvCzRWgWSns|!k1A=cKM{ +qHn+)OUcFMFHifi;t>x7S8JHt^_oc;=3et}t@C>w{+T;uV~io^cLcNkaX$lw)_|L6Q_aqn_U2xXLps# +IZct%pfo0h{BpPm$O5hH%p6`cWCj(D&!{#G&bG<-C}G-uLK39~Z5>%*gM0_b)PbS;Km`4 +mEsCUI)SRlC7m{wvs@}}AvurG#<{lc47wVC;cdMESD;KlxY|RV&<{(ucQb3S}GO#|c)J>FteSUYQ2CD +4wM#Q5J<>>UZZmYep2?k|P2MnKSuutkoR~)J|qTSt)21L{MsgHHA-nn0c9#r*XXa>upev*;axpkt@`j +e{ng!`oaEC#gkwOfVGJ!8}GrpG0e?&i9RvJP%YUdpTiWz@1%3B*C|$We*_;+DXJ)Dd~vngV*MGU%8-F +oC@5?|THms)NfGyh@k&y0wXXt4oS_mlQe9MEb!bs2@D0lxAt?g#(DB^#iWIbC4GT3c%mfZ8 +PCWUaYLRD&DTF>K!FTVYQe8^&c+j7rqD`&8pZL|L0Qs$W9zf|UbhJ25p?==S^5aqWZZHBd*oJ +E?E5|;(B)`a~~s}Gs}3Kz<$#;2~j$c_%^_epX4@9$Gj*6%<{{Zo9%)=*FI9#sZ^wrphqd5CeUvt&;2OOBsdBUx3sI{9vptdwTSemK}G8oB~(r>rl@6S9fy&k;tZb+6Q?94FD3%g6St%g^6E=finc!JX>N37a&BR4FLiW +jY;!MdX>(&PaCyx6eA|1g2BLVy7wl9HTw<5grkRE|}$q>||PatsT`E@pROVu2L~;Bu|vf4} +aYH-IH&<#KgSioyajJv}}Bo}PKcE>@f^>awi2MYUw}dcEdV%fzbg%ACzOYxugp;W^9TWxeKXQy1{Ps9 +1{~yS8h1^5qwg*m}3wO_*q#V%{Fro3^N{ten2gO2NPU;>$0J^`>rGfM2!zwk?a9dBvNis?Dz@Z#S6`= +JzTSE4OuFo0j!*2{@bIwe7X3MRB{y+Ld`DR$W_^_Gg>5_Q%fl?3xmWOEy`9crTiI4MQ^Z@jcA0TAshr +A6nkDbzKVmeNmL~YJqc(SuSkGt2(v24L2PwnrzNz+5Gwf-r5GZ5~@Y`k`OXGO&u=Qj}lhpft$=H)qH?DV0ZZLPn)DnzRu->5ZJKQ9218Tg{x7zj +8O=kf2EhBeO!R)p+8Gu;%MB)#EPUN_0+dYLv{)Lk>T)KU{1W!SE;KwT@%9mfDe&P*KMJcKT#(G8X!ZbxJ(v-+|0Zo*oguz7s@6K-L=hdRd_b6DG`1 +SI;Oz<jXNG?+{f&V$$A+~QF@Xp&kQXWRCO=M|i*;9irV5I;R@ArU`&N?PZaz%t +4Ut8kPATXLAz_u)7EiHDvz*Yh%vH_(F1|Ka>#@9QoMu~~MOXYY?AcZGRR%^rXJL`>gk}&Wy)`HZhrtT7Z`hNG!&v@j_J=78pRl9Rt`R$A;c=|wftUM6c +z*8))Bc;w$&;&|$e`&C14pHRDE5c3ylquwh_X0fV(G#V?;@oPowN+Evuh5W1=|+DC0)zT&i?EC{Y7F( +31;4z5OmW$FDHcu$*xMS4h$`b5AUjvAbSaUcx}zHO2<%bUyNtH? +v0I)ED=1}W&Q0@Pcw*`bC#ng>xIL&*h;;3I9q)|;dMgut+e+__(+b681l +8j?cp>yu*YRgOBI08QMHC%ua1f>AXhcugyn=c)pT!Qh +6F_gJN5&=@m;~DCNHIcJ6Z^6Q_pU}3nb2Js;WrOl3&98fENNP+7ixm&YB=oC#!xvo%A28A_s*W2a2K)~*UyFTe3?khWp` +{3%U{ApwftY|yW4J1H8$71#I_K>76vHO()AOI+onM@dDm_o%73~42zz?3K~Ju!w&vZjiYEA=RG&Wjjv#CSyzZS}Az|^F$#%hNvZAenbAve485rZ)zb5U=Pb2*waz +y=r4j5=HbB6MRX$z2)_Y|n^qv>VRJAjFce6`9}G0XH@;q_v4SLkHu&Qb{RpRP#3oms;+44-O+OpTNfi +TO+cW?hbRCQll0<$$&6mXzoCb!Hf+R)bg>G90#BKPztwE^+&2S`A=Fmh^7~Ov@;9-{MNN5~7roX3vOg +oPZRV$e0i58U>92MlQDL%`W+f{+?q9+$PC!mXI?^3Qp2kpc$e?Cy?26GIn$Rl14o~0wizr=&78W;jqk +u5>B$b6a@r>4%}P+aEIl~_@V5U5O;7R2vG1CBH-Y#6*ff18rO@;?3c`-}58uhIMmpkgre4&7-2V>w_X +GUHZWHuI(nvl`CCPCA{h`1~5P3PrW)THSQ72zut>9hqd*w}LG;q{yJ{Po) +(FFeObT6+^=hyHXI!D~Ix7G29*2v%AQ0RF=T2>wPJ{h58}xIh>a_EF95&T*L{j{*MmMTgmbE3QdI6f8 +zbAY7Uoj&>3?gCjy(`M`pS4UEWzA()hh6NfDOmdn+<5(I&d>c`vWDj?g-?@=J +_n%mD_L;Y--^TiYpF2mfAW=;5Zk)RKMS>t>F)m$erm@cQU8u$3WK*bI>#Xx=)EWc4hBV=FASg9VH~{jcpF +98vicn4>qITi-qofd;nhTI~mmc9RZvv-6-K40MG-{j@7PR;pC7T``HR;1j2bkxok%sK^E}~#f=m#?l7 +0if|mZkOz_{j!oMfJHkFH|ZExuUqUIIm6oLi)!ATLI1^qrAgLJ&#v2$h}E`p+hfr?x$m~_!T!atw{0* +6VQ^l~`cwS1^|7iFEbJ~zWGs4aWWPL7X{*&+QiL^k+1Nlq4b?1%4+;*k9wQ-|bOJ-DeH3-}(eckJ4NM +D6E-jmE*OV8}{S8F1vNbaJhvM)|VhA{5D&|V3Ub{`TY5Fi}~f{!J@8bSp&j$3WNbvE{|_>hO!O5j8~9B}v2-{5}PZGCK$@(DqCrk(9r5q9s&fIc5|kjWXMNP*V&-FjXhZ55 +wIXie@GzK0uv4G4weXGEhO)?XFVzi3u9|bRSwkbI7e1mGrvo@2PQyHbZj>_U1T+|tohI +PPCB|CfbGU@r~I2&h8CRgcthVsEWUnK6}Vihf=>FcVPXJw4L5&Xy?kb~PZgK@i-n|&fJ?fbGR;OK)nz|D3yX(R&;ZwA98 +i3R~QDs2L0)H?Qhco`dy@dimA)xB=;1#it&ip7)umYHA$?(%+!ICz^G)ip2)P1Ed--8q0uNMtdw; +UY%NuB#Xb_R4hfHP*37K{(=M4nMEax``NT-##N9-KEgtdtXW;M8*ThOK?xGrR+LsuY!U}Qj3J1ikHEr +`0ICAK49g_`1nt8rCg2phSCb+^Fin-&0-|HtgLC6oSm4@%H>4@%Cce*RkFq3XpPOzi6K$xTaH5x!@DB +{b#z439dbC+F|1YS-CNG?U1W;F#d<6~u9i;x@*wTYiB12eR-59hb~0b4eVJK{iym2e(m-DJB|4Mt9(g +2(bBRRRfwp5@;~rXPry|k)L5Q+c52JtY+ +{1`4M-zsjmEzlOK?#O3~pbJsy1xU+luLf^Ws5(50@5WZBU +9YoE>jz(?Qm7O2pj%^6wzL<&`8a=Ec}f$Y6eJs=zxRiv;)F3< +$dUh-76n9w~wBj;8`yh9zw4}=B=5No>8YJJa*Q5*K1XC!ollXWj6)T1O)ZxpFQqP2IPo-{`vp2 +xc)n&lUU^*85|PH@Qe{}$!-JbRxDscMYsNO$hl6*qq23gjm%_@}R +&c!tQ~2qQLC9_)ItsiWYCvGn7eK%I88)HH3BsO_YqA0*_88{(ba>AX1>Gjz`sbPWdRxu;Q0i5gUCpy; +cDI)VeIB6?`QE(y*#?2v;DQ#3Z|`ixv<{%U$Y44Ut@5HgR&4{J%2H{Yh~j^-HqNhF5vdu^l7{#}0pl5 +s}0M13f3t18*aSQTqs^d={7or(s=T{G!=I_%8662aL@|oZrazejjjt@9}-rS5L=CrQ<~ueqi#TLK&t~ +|HfrC70%~K6UH#1EPDXk2Y^${KHj{OW*Hyi>;>LP9(UmQ(sH@TgdN`X?VPS!}j;1ZqW(5CF}=vr#7Z7Z^cs0obD~eGw@krqQ;mdJ#^8oD%i#_y9wNk|+g>_Uo>NNb{E5?B!k?F!5P0yBXX +~FDl(Qz(#~}3pSg)B1Y6A42;~QpBc}!3*$O{4|wVg8Nxb==_r)2+dUZNqh09^sHr(g&uO~b>?HU%(=k +CyXmuo%!fH0t6Jxlh1J7*(rB+M5Jh$$2iAJQ$Yn3OE%Lhrp0jc|>Pc=$i(#YTJ#NJbt_cp6X@@?6J&A +JjTE*?KUIen(c1Ih-b(e5m5^eCa=R@O>vV +1a=*iE_O-mV7>PdBfJ}{uN}qNgdq?gb!#_oZe|e+NI$$Yw%h5Rm_XSJw`KHX~DsL)wxG1#FhYrGrQkF +AwY}e|A>>Atx@=9M7v&W*_px6001^2XQ=^%vGYu~GbN-$pC*8z&kh_ZA?u|gUXqcL;Irp)x7&#}+Ji7 +N}v0J@aJPr1vTxd%pGU@5g%vArJ`cSNgSdh4_2V2Xdbcpo!-FJZ|#ZsIXC8w +vK6))qe*oUY7NN^$4~c>6|0NR32RyMf +pxtjk5U|h)K4R~SHPydJx3=om`k+_04i5!H(pctzTeF9U$VmAB{X?=nhlkqw(1}*8?@{Iy0HG6`7xOm +!wb+C%%m$CvVNHwkHi{xUn4Syn{^ADWM_SkkyXHG`{@~wO6@C>GeSuk)H7NZ%XZ*`Rfk@XtQP3E{71g +KGJC9yCsW3Q$PC?9&fvCT;oU^Vd^R%Mlwc)D9uo3xZe1p=&fmqM9$C3LE0b)o+Aa!*BLOzlg9%Mzo3! +N%x?ul}-w`m$o0wfN;^DOQgEHK#ui_s_L9f2Hy2+tbu>Ub;UnVW(}I)8ifmF-Slrfd;he#FPSE1&Haf +qXsrh=|?=efo7xDbH0aLdt(YWzAQ7&8GpG*WYl&z$Yah(_ufpd!>mfp^t5)$qx|nfqnrxMeO;Moswf3 +ya;pvtyYHKUiRL46g>w6h#?*Xj~SaGA(3b-%kzc{ArCPjy;a4JA+vMxP>m%iD}Rj8R+8xs{14s6p)g% +=;S=^#3JW}5y(ekmbLRadhGoWG|9h5PpZG?cb7<>c(viRqdI;gccb{eZ1aXn(O}#+8dc_saA^uOJiF0 +(mH=VaQ2%Td=Ua%*}7!JYE%f_-q3`F`=aj7U^EY0r}`05$Ris@+Vc=3Q6N5T#bQf@WSL1pXszDH6>v= +Cu%23D=9Lqi|zK)}jw2lQ75Ml={oU}7?F!PYg|daxQ86}&(dp$e&4C@a$N_$`P6m7DQgR=XLRQ~FEQh +?|pk=rGtp4}_i@X?RT+Bz9~CMbWsCoq$j=JJDRV9Rw{SnK`Q>5wa)HJ*t(Mh5fBWdw_705yEB547avD +^0~ld-Qa!_wk9}Ml(U_Y-(pD^oUN(}f%#ji^-?&!9IE4R#*vrw5RvOz`9A{L`wHn7R+Hr4-hTDcZ+t;_I<|YuQS{#9ziJ<8LKLlb+DwsHy^=^6?b0S#+F*!vno$ixDgE;A +1=#6xA#_fQ0UL=^bRk!l=;fDw&@#O6hol=4zkmg{Vs#KK}Pnql^{lVB9;1W+T&{tW(OOFs_;XPXPylA +o|u8B@#jL)H~%q-NXz9%M#o#ibe4*LZ@GOje>9^PkqVtUL6G?f|ZH`xR$M}B!~I0_~17x7Ab)TH&DQM +CA^t9Y4b!nez?*`haYC>t}hb|~Cs5*C$x-Qior*!Dq_j-pe7HCh#) +Sk2WJ{#@F27kF$et0q0y+n2`y_2ro?3Ax5$#kHnJkc|LaiDhy+|pp(>@hZDbU~8bWyjwjq_`^G%-piH +YU*yeVv90c3Jrn|E2%=nhR^F|MVl2W=Ok6JLzsuC2x30)fj*Zy)&p{mZ<-4a46)FZCjCUyD4w3=SXAZ +C!+#ce&MQp#E9^*QRT|DQy_n{6t6%sWhIDGF%p4>(I3k`L7=9n!aL_eYcGEpF5g29~(1XsO>>u`bAgDLIphSUM&-MVE +#dktI8Bchu7IbTNj7_frm&5H^LgPQ=(r3C=RNolF*KTPKG+mJLCA5HXrEF8chi$F|WD6iU8@aq&OXu1qGfVb&{lSRAYo?zM6vVUyYZ7O}|s5jO_GCv%r +$H*i4V;@E&<^~`A;9#_Az%if8O9Tnxy3UJ5AvZcx`2SX30Tj}EJWYp92e^;ie*_BXtpu+2yRiz1-*Oq +QyTd@7t#2CAoa5d-gtyF@duN!zgYFS^F3at(T*^qqTO; +K=k$5+^9bZMm9=%S7gI<$U)9uO%Hf|t{ZeVV*#{gLGUY?wO{WZxEWi0YS%sU~>6(Eu{Dg3j=%6DCoy% +u;`nWMhFg(d5!?lq<7dv7&E>to31gjGs)?8m^p#)WUwxxCaB7s9{g4DUq4L_b|NhKH8S=qLJC9O8RAd +hT8g+q;S)xTw`U5nphdN{&?%$TRKW5@vug`DgWUEXp+S^!Z!|`~6o=IO0}_6c4J@B~P7;7xwHqc{S?i +LI!54yL)fLU;4IQ?dcVjo6@1>G+&ZJWNo_(UFDRC6Ouq`tGgg0QzWl)W$Lc!M69yYXJ3W#)*uN2&++v?%P;C)$*I1OfSe!D6y^+SGYe?Xa^dhB4oown~EV%E0b=6jUG?7V3_H{kIU{=A>WV;P4jROfXIi9 +0JUwN@YBh@e?>gD2`#-Pa+79#3?*Ip{J+t^el=;jD|)YFW7-cI~i~0?s0$JB4$ooXDg^JoGXqtjprHL +^wPzZJhyx0qv5nQ)VsLvq=F=*(>8WH_UZZ~#j}VMT59si491xMp)39<)RlLp=vS1$!DBS)g|cvXnA&v +>?d(Ak>O9x`%_qkZAPd+Yvs3nzeZ~F+jS(h0VNcjI_DA-0pP^4;)}CU@hVwmZ;#?3PN;0>&?DDmKa|( +U_2T)4`1QY-O00;m`Rt8f|uidJR0RRAC1pojY0001RX>c!JX>N37a&BR4FLiWjY;!MdZ)9a`b1raswU +pm#gD@1v?*-rCklnQ4`T&IuwqA{~vWx9XB&7AIVKK)g*_dy?#A>Uxy=*Q5=KT47U+{!6_UWXOPNIc^4 +CGiyEp-Vs<)~~NF~;I3j%3}CaX<|k;mE{MVW+7d|^=)N$8uvG=JHXhIJQw;~Ey7;b1ep8EvMbp=~~EDXs%sEr?MkbGnt9PqHB^If27qx;an7NE{ZeJ^5#yZOwzRb^jns6K-mQ}S%9YSL`ZF&(EB6TrI{LQj(ly&z0{4u8 +nwfGFfd9u5YMNkw{$*9|4>T<1QY-O00;m`Rt8fl{pT+o1ONc73jhEh0001RX>c!JX>N37a&BR4FLiWj +Y;!MgVPk7yXK8L{E^v9JR!eW=HW0oC$bT4UQ9uH0UiM}n1&rOKHJTR^--1A5q>;@;B2|)3nt$IR^)Mw +livsmQk;r*`^UW)wZj`oAsohS<-T7ImRSj5kQL!40;X4G;irloHBawz{gSwZvLM-n6Nhf4K8#3Bo-!) +b!$*Toy4^2geI;lo9EPY2=sKHY6wo5e{b=A*K)-J;8MuPD&%zT${oJMEi4ZSc2HWEYTmhQEx2FJUzJ|gLi%T})quvTmMn;o?HFX{)3DIl)9U+kmK@<|v +{SUF*Sv|TTql*CBt0Szabir!uc010irOu$yFQMoIqj@p!tvm{W0->@?)!Ys2E(c^rXn!p3Y*4(fs7J+ +1dwD$;vM5a(vXcXCRrD6>uOp8y_}yNL}+w#xn2KUZ`a2X5k^R3xy;cAyciE#=HlHDz2msKEPMcLgg5B +K&~JO3tmfB*$XgkOG}7&IiWMQ9JB;rC`eV7dy}FA|Ss#KPr2ZEI6Shq|sM!bCl4emH-KwL>QXPmMD!A +WFv>1(e&oHZ0?z^fl*a;q&yCFNn`Mh&|h0P1B%i=VKRcJHRTbG)UVh`>F)Yr5{uG~5_01{4=TaRwNedmrc?_irecSBm;S8TaXQ@11Pdc`=WgceMQ`z +4OrGaJ@QTVL`Gru@N@&0_e!aJ0wvm`WL2MT+;@1_}uhL3z1t&$$S8v9J3@j(scD3&rl{oA)VL+4%TcN +{7{lcrga;W0|n(?+7s%Qtezh8Rnog@$@!`G?W@q;0owpP)h>@6aW +AK2mnY{22)5Q$YPxY008|A0018V003}la4%nJZggdGZeeUMb#!TLb1!gVa$#(2Wo#~RdEHmvYvV=`em +D4k7?g*zi{tiEC~Y96N9usNiH8rLnaaX;-(qN>2CJcV>5G%gQC(Ln);;7-=>0&ChQ}PLkwYt +4CB4m4iwcQ50CnFTzQ!KyM(I+Z+vOlhN;{Rw+NQ)8Sw^Sme?|sk6F3sEmG;8Cq~p8geYEuJi<+q{~56 +BvlzH9Kwd|tVQ}@L95~5rg1r$I{~3GIsn^PhoyyrB&6Dct*c5K2ODkZnM!k`RqJDkX)ctsr9EY*j-eF +Qs;mnq$y$om9-O({pvZ#vXfe8K;YphZDC7e|^ySAlmbIem991Sa8#Jg0Gg=~u1~RSQIM~*dI^__-tW8nG^L8Zn_(q+T?v6#>2venX{oVIj?Rb)X4SInD3_R9&uv?KH2|1L +fXok`*C&k7ybwGy%7&7HW1cC>O$P5gtWRqZbbl@(V&LDAVF0g!fUeA=PD7AXT!g)11ml1v4Q)lJt;XG +{cRcyhlv(>Mcc?Bnc;QwW20it>6rnNxx{x1d?9Sl6zL#7+sQaO7&+~$Ti5)cSbxZg%VqK#EW$S*`X+V +LrDZ-;U+xc+t(GU7bT`yU6xHBc;Wr+;JT*jz@1arocamOV-ez&(`EvTroul3UEC?j+=vjBtfnjCX{OE +8;HMg`vs00Y%5nQf6G;i4SVJ_mq-*OJdRbD7j6>#F6gFf=` +BnBe+&j6~a-25Jj0Dx^`f(TnBrDeYiQO-05XX4DbRG^gA>&_>eF^Pcqxa0E(!62 +k$0PO)+@_bM}Ew22^>@6O+^Zf94k>zmmJj=|JO;)TT*1TF+U3(?ILxs}Ov(L_2pSt3X1beIytXd)=AD +}DU)$uB?gylUqcMY5P*&93h_aVJ0c52FqTzx5lGhyiJ#Ik*v*VjMj?;%3297;(DBaQy48HI}hVEJY0Z +16^$tkv1Yjgl3wCnP~!B8Qq&AuJZuq(K7WEWTbuO`O`=&ve@r@zmbZXwJUK17c>~ym8m3xem4owesX` +*k{lwl3Ngd1tHjfUoiW~P%)SQy|z0P=$ +i9qnfl5}QS4SIAW@J<{=N?bz!RNfr&P46(DJe(+NHjq-($1Z!en#mAYFqe?|S-Wa&vBq8|JS_#0450|XQR000O8NLB_@R@Sdms{sH2dISIfBme +*aaA|NaUukZ1WpZv|Y%g_mX>4;ZaBF8@a%FRGb#h~6b1rasrIf*H+%OP^?*_la;9Pd8>jwy=r?e1gLN +7fni_lu*%}}i*X0)NE^xZ3Y*Is*@Ktpvg_GtcZX7tM)`vb&2klO=O+BvD@O50Zc)bUp3oX)$)V(=vS_Uq8AKm3YNMmREelAis;YL}nWRM{~>j;e62prBa +=*TAh0k6R%A@WXh-z0$y$XW(FP>LKyrB;HrtEcq!8JE{hZWV7~mCM;kZfUhzWZourkx0f(uJhB_IfZP>zj@n~hX{&i3@e-DDq;#>yt^}LR$1`N +rLg(}7Kv-7hHSga*EXt+0`1dFaJ<8lX5ZFMTx1Ma0;`vs?UK`AcS?Qi5~oHG_?wf*?&mV>x8LMh0;xv +x})rr2>Y`_Q>8$X9caE-L&48I@T%N%xsnQwL7m;iG{2DD?LdVG^YixL>2M5 +L+!X3mC8BdM(#Qq3_vxBb3b1x@1EM4F|dxM$yU~Ci}ag(HyK3DibF2yO~Dm6qI%pm3BOd&(D&@o9n+m +Twh(iTO^A=-u?-f5S*U@{Y;uuk$MK&&KSX24~QA<><*Rugs%tSUc$exelY*Nh9u#XNpk7`o-|Q&vRI) +L&Y7T%FoUTsQB5fkUBaMs4WadtIn@v|z%f1${`PdcThU1ESeGFkgy`qGrF)4dlLn{X(e)3803+gNK)bFssGMz@nOmOwy~NC$ +dm495A2Wy?a9#={HbboD)KOhmjykdl8xP!vNY3GxER$Z1nk8tKhHgVV=kPX%Yf}+SBo$&=poJ2Rt;4V +#c)QB9Tj+&)gm^ES$VeExTD3-C38=JH9Up0rA{2yvJVKs{MI8UgJiK@ +wMFP?vvPKfLyJ&AK|Ho^sig9*EIBg7x)8XD~VMvxz@F;Y5WqbSW+ZMlZ(FsimX-hoS&_=Mydi!W%zr+ +aW&$s#+=G$TEiswPX4Ni)uTYV%#d`>X3Wb9l2QZd9Tub~CugX+~p&xIzq~*47X$+7Xr)f}mUz6=wD5V +q=4CENOK?B`F$HzgEUjNRB5REJ-}?EPU9xH4SB{Xv!H*hiXgvTw*FJ)@j!UGFV9J5VEz>Vbh?wBsHLp +ASKYbpONh#E9BA$vjbwMl^J%tzvyL(2V{RvVGkVcNK?lr`&bycMSZkz+!#GNQHM;TjDC;Kbks}(a=d` +JX@Nq06Uv@SvhM@&8iyL|h1Kas@%a5T^h{gLvN{lf9{1@>kU3_L0Nnz?!n +I<@4`|DSHEBegNVSx-B%j66Z^B7(mj8gGM(M%eP@K~ohL6l9ze +utDO*awwut;5N6!}%|h<}qdBJrRg>~z~pJwX3R1YY-h^Cet%dpoI9aJ5RL%Jqv{W$!c`WCtku4EMj9A +dO0Xk}C&D(r#$dhrhqeBj}S!v=kQU1p3x5JA#&b--TEiOts1Cj~yijg0s(Uw02LQ`bly|UuSeA&obys +W$#o4-ZkHQ-&!}HH)VXkSAnhZJ~+fX1s*m@!#=N3o?Uc5qS@omkjT9pD(%?aLLs`hNw$^6$5d9J^u?h)^1wS9r&|F9hY)an^`+uOJ5Gh@sfTHfQ{<@6aWAK2mnY{22%@pd_0W|002}d001EX003}la4%nJZggdGZeeUMb#!TLb1!psVsLVAV` +X!5E^v9RT5WIJI1>I|p#OoVViEh!5%vx^91xrj=(g!@fbOP9)9#0C;H4!x=0+y9q+~ZJ`rmJ6NJ*0oS +8Pd3R$d@2O(*D<+Jwm~8B;rmXbb7S;0hqjTG&ApKVlaJ?DKU}^)Uqm)pA;}Rb9FLu}MPTrgEK8xT#is +`Kf2DUjuF99@&ax%h>xOJ|V;i%GXm4+k`uVfBmp`svUwpiHeSQV>+38n)GPk-E8@A*3VpX_pXMg3e@% +-%hGX|d*=vHyYW|I|y;`)sooe@oI)PSr91(;qa%r$Eyt|{5=EO6YC217!5alvtT7Vf9#9TeDRe1b3;aJN6olxzhi +0o4`v*b=WewW|Mq6<gLm*8CnN+s@hUm;HtDb<+hNL6A* +VlR00e*t9oE`ivUQ#kgFMS0+XG|ceB0vt2IR|-;ZvKv^QTT#Hzud)j2o#=m?sV${ej +8~fzVsS5jsu>*=)O{;;fX1WOr6W!0enxXY8db2~Y~Wih>tgh?5c%1+jr(69;^Gq&h)ino5zu5nYmf!G +0A~K1eH!BN1d{dtS8ycO{giJ9;9h=|28QR39-rdk1f#kbJ>DAdtGu0d}TZ1-WdmWyvz1kV1HPC!}x7MRu +zXV1*KdTE5{GwmIGMwboUav6jDRB_2;(TCJR<2;XI9ICM=}w4}>HkbwHCP>9_1f~m>WoNx3`02u! +42PR|w|z6KXGwR!8ambmZD?n54pHt+Jwda&;#rZX1m2rlO;3(qSs3e|@*wa9N3R1^!%Kn|5+Fx+iQz4 +qRc+Jn~{n{|zPLW!&0FNI9cMip0Y5aPiBV%TOaQ+xDnD!EPC#ZLrKleGGBI4F%s>Xr-bi8yUVn|KY=r +@c6aZ@V3HkP%~1iOIA||I)hD^^lrc!gR>vDgn2coc&nY+Wxsmi-@tq*Xw6>S1HWG?3Jnwqw&Z7A{oVUA%It2@PX5$dxo`zU)eJ6CGbl4 +yuP0)lzL0Wd)A|ORh_6DQ{j$+{&DF0%un{Cd1#iB$DqLk=ocCY{v1uPQ?z}pxhLQa0$9Ongb`va6axB4O~CLVfemZ`hxwep&f!!QV8lrY +FSeNjIuIX7jR1xk3*icr>CAvV0MZ8vpWD6FXw>VwjI#7$p;5yLs_4gd=Bbbvpg +yLp)pc5@*wyDflA;N7?|W)%z&6}m@~KVa+;axY>&7n?vDPI&zIgh5jJRtC&`db$^8ts~zbI8ydv8}Vl +=EMR25Km#Zg8f}TKtIM^_YBCL2+0g808wmrL6*Q+s2I#RQookG~GZ=saxf8@+jnf1iZgmxj{-nKvW#^D$uhDr2nBlvT9B6x9L!qPXSa +Xm?$j2Q5!6GqqUFB**X!Et+3wMHoHZLigu0xMW5D1Knpr9!6AOXuu-o;v6hT_im8h062pddZim&z2GH +yFj}XL~So_G;3`IR`68EAnYC{ajbW4rVkLx`wkRr@RUS%qB-AeAhSX8c#Cq +(=Ir0gNUmO*ORQ5gx1u;c?!giVapFs$-*eh0femIyh>9)Wi#~sNp@Dli^9E@nma!U?Uc>g5sIV{uK|! +LpkDaI+gV#p~-)`m|`gN9FwMwCuVRZh^LN^}s^y38-Cg84Z?kiMY==xzcPwHgoY@))b_IX>EP?Ki^l6 +ikdj2#ZTeb^igS=73oA>D%1_lq0z5Rsuu1624nC_Suwr4=A|aAN4rx^0M*Rzcpa9{j;%a&kL$)`bRM4 +9Ot-NVSZGxp;RzrojyOBoP#WnCSiW>&p+v$ZK8A!k9@Qfk)9fHs$`L5gn`H3`bIf^LS^sS?&33jAK2pGSo2|G!w>o5z-1GYZD7`^s+1I@i +aa+VRqWRhszxW9y$3?5r`^j}!myN3nhnmjh(nvlEMQ95wYH&xjM0cErC2iNey)(#b(u6=@{OQw6tXqK +(oL2F4HlTX0)hwp;!Uw3BA7t`NPmH04c(M5dx{kzkWdR$L-+2T*&*{_h&zZW{0<(hWJ8B}v-D0efm&D +@i82Pc?yMa)1ckZ{;rlmi7*;;IArMW0fbx`kj_g)jUb?%DVBCJ(C6A}iFdd-46Vq`&7*B@KvC#48?j= +dfhz}`<4QaQtjA*jQFL2W}ICNxvYjbozcsDrCWJ?GeccM9t$w&IXN9&@)>h;9Yhd&yl<`ai98vO=6{} +iV%n>~p1P8vV%?&2$MwbDlR~wg~tma4Xymu=FI;corXi?xqnjQ{}t6 +up66~d>94Kd8I3nKgA37>+svIWV=}POp_AXNyA{O~e<(h5nN5X}PC)QSE$V;%{a+`Ry`wKbX?>59;6F +IH-r{{P{aXvSRd+th6?^&a!fg!`VzCw2JoDxR8}(YywK){R5zCSE@gb@E+M6)~>F|~*xt;bH`ig5ALd +E}dH8USHE6II^o3+DhvT*!w)j9;w(eY%TH+{!8pqR{v$Ft#3-NCO3>TrML|Bl0QfPUwu=g@6 +aWAK2mnY{22&Cr4Sxy>000so0018V003}la4%nJZggdGZeeUMb#!TLb1!sdZE#;?X>u-bdBs_6bKAxd +{;nDQhYK{BfKw2y`(96(iDEIarncqUO57PmgE;~R@*)t2-W?>d9{aa<_g;Vl2-0brw)h~CxV^o7eRlW +Aw~dgRFez(sdcy5Du9?(ARH}PZiS?S->+ZWyX+!m<`>xrxDS5?CPf97aBp0=2kGkS3;s}Ycn^oD9eB|`@5N~~yA?dH}&XLv7+ +GV$pNf#>V*E?-<^%U9noqE*YQV$SOOD6wnH*Uy(Pvv<#z%NN$X;;p +5y49J*+U3}nr(+3?ep^7N{=-A^Us$#r3*<%MkS|uIdILtJLo1my+%U4{_pCOD +5i2>Eq-R81E#_7%OtygkQMGT7-z_L_#1GY6PpNrGKcg9{I5_!RrV%SkzHTJ|MIa(6E4ntwL7iShm63D +O^8$HjTWNAFTXsk}$#g60DIF0NW9uYjFa`tZBJeO891PlH=rCsgDO@;qh*S2p6!*M<)SyJ=q5+dS1aW +7PP89>iIjtc?NUMNQ3rMA0Lzp>qaj_PldLLC}7)PC%$9UzBv#Vj|2R1!GJw|ATfSFL6$u%vCANbtN6x#bFRhFFzL2BB6=u@18u +qio3Nor_Ck(qsQ#WMcf*gh6#QJzdBlHcNXZaTb|*#a_ZWq6oDz_HP>>YyP<~Cn_V+q&k-T-Q=mHE4bW +(xPw3H%}T(R2ucaTHV_#(rK}kpmjh?x=806DD2~G#kPKb9K-fzra1XNKcI>W&kNjYRX<}~>{u5$(`J4 +#0BLFWV)FKMD!Vx`*ZyH_WP%OTol?o;6D@<63RNG+Nm3G>8PR0ie?~D%C^1_e$7@$EzAVn>-(P2b^&l +$Na$ph4BCVe%g7=%s5bFO>0K!?thsO~#&3uDkJhP8M|_uqFNiP3+!&fv|YY*&~!4;<}Euao?@^EkBaWpG3jh46E+0b5T^o11I@Q+C!!jb^K#c&m)EA~vw~~7svxT%S;3fGhg`T#Hq(*# +=4U=vGIxed2hR1ZC!A)M7Oo|*kYxIRL44R%M><3{yW}Bf&mCsDv&IP|ufGpZ}9W>9G>R`&8^OU^rjsD +?K3uj#sVi^{&Pd54kDILQy`m;MRGj%h|Qa>JlaI)ZOpUDqjj^NW8@yMCkwK+Arp44K*?6DhE5`c}|%? +t_At4ZGG4#EGKm>qaI2*JbP)x~H)-czs&80~dIhgj?prlUb2bs5&P-vg|U8CJeYtR8JJe1rNNSkQ;Cgf2@uK;}=Xu7Mk)TwBSK#Lu8qbnHF3PCk&HO!j>BBsl1wmpjvLG~9?K*Z|nd}~)lFt +?N4|vRMFXhIN;Unf>xf%ekzF}Koy)+pXzPH8jz=m4mWr}7KlXhP@Y@i2Wj+yH12DqTKihJ0bjl$om7p +QA40K*O8E;-)6@K604s&loXD;2~896c=>mHUV!m(jpNu*E7L5vmEZsj=rAjTC1@Q+( +KqCHUTRbY$|{DkLxvd+K?cX+rMgW1CFt{Q*OhOmq&X<*oVcaCaQ4Sy +*XcB##SC`Q>H*q40-4$XuE5D3O@M0@M+yvZ_$)=c-rzex8`E5R1{6-hhRz-Y;8f!Ea_X0xyLE;=aVw! +2Ad*6tipmwdECEq!b9)b7!qzY?AQ|SoE(Lt83V$1VX758Gp$qiw7xOQSc)W%Z$_uh(nif>Mf%e7n{Nn +P}x7oAj=WpIZ_C@oL>EiQWJ^A%-e*3%M|KX2+I(&hcbP<{Oc?aR)@U>nyE17G;LsyMmm@hj1sf7K!iK +Acs@Iw?p+1@bY#IXPR7S1H>)(o+ME^AifbrLh(jf(RMz#l&zEsEV0)NtN27kHMPxp89Vl4$UofH@@%D +>%j?t=6EW-fZ#8p`y7Pp1tpaP=#`eE6Ap2VH`d9L4jB&V@a`fG~kgy*|S6dsXY_BqoMg7H)Va)c~p({t^$59DM>otp?B%os*8+(r-97&=67QDbrf-T)IRB0}@D~Je +le1^(r&TL!AjCjZfA8=|qi0}l2n3E?%%CP|W)a=0o2ByTvi2MSyNq-c)jyuU_@DeF*_|x0{{aCj=i_a +E@aN-nv?A4qn9uA3ih~CmW?5n`Xh!*%$7*4Ics$7dCECq$upvJ*x7$YOgquKY&^1t@M&uRak-}e7$vn +++s7E6MTPc;>{vtPN0S3^5av6{MMSNfYV~r+PZ=B>4z=AAO5(Eug5amV +38rV{8&jQ1=$ux7c|_FiveHf{{-?L}sF(hY_W!>#0Q?hBO9KQH0000807zB_Q|BNLvn&b#0A(Zq03ZM +W0B~t=FJEbHbY*gGVQepTbZKmJFLiEkVPk7)Zf7oVdF@(lZ`?K#{%*kkft8U~x@_b)IV1<)IYoMP3Zr +cj#0d~24HT|K?XuR2I+C)z>Ggj5&5)Ety=}MG`;Y@pK${iG84l<58OlKrywSYinlnWtE2w4}p)y_bYo +_AEgCGbF4-OCbrV>(z@ +;Q8j=gXH|}>+|QA7cZV)F6`jzr?19?Z{Ll_-d()x4@Nsg*l?9LyR%Z&k|i0Jcv`dsljN*1NxWkE2QwB +XNl7;>Ng`Z#cyRgV<;xeZUY~#KZ3}dLImq3?36N6D?Wq;oiLm_ifRH0nihod?$C*j)ch3e&Iy|tZAQy +&_(Y#}A4-#i=S+BwXci4!mMhh$0NE3QZd4UcQM17f#$cwVrl3Ymga;w*(BoE_{pEh(~fqS;vTV +AunPJs^(GM1CHmXeh^sW((zg;9G1^siGAM&#ZT0=Boq5Od!#h?+HP=!l^sH*21*VcoSTAUesKUSr^BK +@=?KK*3O4DtWPuP2eIoLwN?F8r#k9M|Qj&!2h{1$#gACL(<8vDo77W!Dt4%q5p*y)$7q2l$Pcg7`E@> +<%6BawXi+@6BjVdt1vhYqW1SsKLu`co)egX<5?P7W=+K5V^;Pi4a=ODnPEu&<_{p(h=6MtDm06uvx=zW^(L=ybfMYNTN`QgM?Tje3o;mqHY+)4VeL716S0s7* ++8#)%9w^cH4b6=VnEHnZBqd||nSJZABNuxlnErc7E^cyLhyUA+X+SgM8G;utNPLJ_sjmWs_rlu0F7&T +ql&&wkmZajx{R63Sd^XTMrll +btjm9Y*iOjT8-fVEw`e9f{5>VQQ-dJRt8Gu4J|mQ32j;oHo`v&TC`b1jIm*8(fDu~}rpA!wXEAhd$4kG) +3@jzgBrv>C|Eoh$}*YGy^j=y3tD=y^=KrsWDWQY$Q7O*HuDgK)AzWC~*!Nb$f>9oh~X&AViS9Y}?_s$ +!+*CV^ZwK&Hc}Zau7aeM=0H^9=pVa9Lo97B?{V=+D-xIHPIk +pO>NfMsU1-fDP5p5bVjg9uZ_ +wa0uy_6<{tu23oAcoLuBHTVvi)yD8IzoIuo6nXF90uGvU+i{X$hL@DQHH-_kXLUBjr=@35nj~7xSa}AL41PS~%ar7hdtth|JSQ8 +*Y(JVB{lU@s+nb^WJf*lN0U2<)EBT}HQr#r1`J!sFHF3o>(7vix=3dYOFi~ +4E7e@qs&`2;Kz+(0HF;82+V^J<0$AA9ElXsOzwgkt$~h)KoLO-KN}5h!I&ioA-`LMRqaP~yY)!U&Uc{ +(jumddOI2lR7(5yW$NYG*`TL}Oy*1#LzQJRjJzBcuRgiV +wquRPMV{+9gd50HnDFig=6Uq*!KQgFKRwuwS?`W&n%C`~a>3cCoIYF2pqG-}z1IzuC75hb8H2ym5`8j +b8eOVn*chXOqHmdyGp=A<@{S4?gZkfuYd0KCo$XIfGK|c#=2AHx;4oYc0a2y`QO8+O>-^qdrjgk^#Cm +Vz#=tBixqU}41>53Xs|&0!`rwJWd*vWO8r0+M^T*K@h$<{1?H=lc#(2!ljpJdl(A;nF)*%~I)3oz@xy5@= +(tV2POLPc{m0q9tD}5X3ONC#TtPZ-KENQHfw5V!FU1CPhLm-sTMtnH9H2CUFH}-wb&5rWt2q{-s#wZ% +o+4a;`U=hec2n^3O3mPzof*x4f(v^!)_U$8W!_z=!<)Gzy`}}vPABr4|4ih%;p63m9jic_GqW?tMexe +YnhiZ}5_r$Mn1Qz>u2T|hEazq{Yr(a{`{H&sTp{Uo9}q`m4U%|U6@jyTGWY926oG=>rFLw5Dhi5=x +52PJ)LJ5P`%@-bMB7Qf4WNH-)7Ef?mxWwp6kKz$P*n{Mbj(Wf_3+kwsN)=19~ny*WroroNKfb96m!2@ +)k9J5a7Nsjyk(Ijt5O$BZY^9B%_7-64A_cJ%bnTz8LsfAu!F475f<4P!DD7Y$W>M+M^y-b1?{z%l#b6 +TZwT5&wgf3q%vCTb1V*!Qrv_g16xHUONP&oNVy|E6O4e{(?Y4*qNa2Kp_}ltOlO{ga){5qWm +;_Q&&6h{7@}*w+&?8@l2tQGjl!h772@HLf_M(CuPHnQ4SEZw7v>V;W<*thm{-!%u0^KrA+7Yy&uerdM-6c!JX>N37a&BR4FLiWjY;!Mla%^)haCv=IQEwYJ41PD@|G+9QcZhRtvpsYMQ +ebd5AV84-U9+I*0z*0TdfH^^D(PYv#s2$9Nq4>&S?|RXDUu(_kF?r#)&;PBK2z!FgHz_~capsi +^LQ$B_^n4D=Cj7x9fU(ihLE4!c0pMq^?a7qRc&{j41@mu`sV%Z{U5BJ&$igWR}35SyYY34J1po}uI4k +K?~GmJ3JQW&!o|DM_ZJd8q&KPou@Yb`1u%>++w$iy^v=MIHJF51K(8v_X%z}GUBH|2%j<_q+daCXJmM +nE?Iu{pR`B_MQPFSG2qDs8ZOs!>t};4-VCmE-O-6SgKU4spT1=X~(i$ESc5;g#AHCImpo7^$E1xpxZ@ +mq;P4B52!B;2IqUMTPS@Eed)^WYtVi1tuc#lbx$%c|5lWC7Dy8LaGDMUm{b$Q&S%ATyZM}$)7A)AjP|U-g+p~T9bCguD%2D{58ty8*A473@{NjoceBn}bq+fuBC^D{(jjA)Jby +1n`HMaQ=UFm@^f(fvy^o_1+X=C|o&e+lrXF~^LjpuC(&`@sv0Sf~cSK9SY^)X4; +O-M+t{uui_R4z#0t7$0xo}9yP;O0cYA&%)zLc#jHK8Epddpo3?l2CLg)IiGLtGcUyt@cXql|p@SLaoKopoJ+AQp2v- +ralm?krZdHV&j-EQDGN2O>OcyZdPP?xPhhdf1{(W%LT%w#Ij%J%1&v8^fF039XnM4u%7A&J9evTe1ar +ul!k>=ZdX(o{xru{N?M*S8soupDtd^Zu0Bdi`lQ}!tkunt6E5&4hMigCK0N +TP01%1kmIWf1v#l%@sTaK{oQ^&#ozRYtMAuLHvE}04gafR((+L<9E>Qu;h^Ld9uC)y3Ns?SHdGXPLsHh&QEEG*GPatl|pEB +N-&cR-0r!IXowFs@+|FbH9^T5;uUw!5=UoV&)T_=>NlfR$y=nG{dQkVN4m}+RZd-nYOFnG<-t7mx#8}TRnv~nIuW*FV=9<%_);TFP@2Ssl@D1_aBBhU0~I +#K|qrG21?cONOiO>aB1ZA%c!a3S4NH;d)23rmgMq*!dt3sG&yK9G4Mq?-r@

91-;Miyf1qF?@*QTs78 +tax>(BXQq}?Z~V+8hg8|8U8wYP-&8`%`VrLGUzDW?4JeUrI3+Qp4KqPODhT~R4_!;(>q6d{2)iyv|0l +XX7^2VKR)k*MYl>gIh*9w{TLy_`iYK1>I7T#*(Kahi(V&%}ZfOySeH?%hdOF243ht3R+YLVagdAgyM6 +7Up@ln?M3A;nqRwLc-faG!~9IgMe9pVWHY+cZ!|wTK2TE-I8^-|P!T|WmmrY;ePznLZAu=ZJCOjvN1F%{z)N}9QWi!xb(;8%3AJZBg~anU +Ij%q<^yuJuNA45i@jtc<3O!MJ68!DARpvgq1ceR9j>S&*la~y#db_uPtOQA(C2om=YJ9jvQ2k5pxIqR +G2T)^(>w6@>t2Ldbv;uL2bB7H}K60ETc_~aZ#_)-@;53U&UXp71=HCAODfN9{Od=poO +z4jIcOBi+g!j2UwRGNX|9=2bO9KQH0000807zB_Q{J;tf)5A)08beJ02}}S0B~t=FJEbHbY*gGVQepT +bZKmJFL!8VWo#~Rd97GmZ`(E$eh-lUz^NE20SepnF+&P;TecNz*P-h=U_l_z5^b~54J4J+GyLCo4sQ} +=yGe%`0b*I?xqRn47cxnbKZd^Vq$bOuEw$)cMV6f;_no{I?TXwBy(af-&TFz1HCOcXBuSFflhYH?m~C +~pTEW88lcnq$!W$-P+Hj@ViW9fwt$mD^^h_!){fghslUtxSJ!*EZeiWt7$%mn@y?}p2kEZwq-?Qp((f +DTSD!~aq&2 +E;BW)2G-B(P}PRR#X1D3!y%^%~*ZRlv2OtnpI3QRQmDi{U5L2{Z{<&?(OfdKD_?^ +%@w&M>3L3`XZZT`1cL_XU;60L71wXfZ)ssbCGw4hP=wc<-s06Ib!MXGqc%!88L64p54?ZeL#BkF<}2d*JbIaz +EpS2sCJA;oJ=*@xu<6A8^lePGfTo1AhW= +0x}>~Xn;6PP9dfBqvzfnP}pna&j9Gr3R5t6|mfRzn0i8qbDW6KL~J +B=6NXPH<_0yhF%uo7+O2=6Y)mhX~Sj-x9A9i>Q7O%k#Ic~($r#X%{tQOF?^N``BEJVGo&aWRDPk%{x% +ar0-c35)fUseZB&P*Grs(M~-(E(}s?52_0STUfiX$g4>T*i2b=;2qdh0Ow3SJ?vZN^>|ndQ^{e|b#D; +vK^6&{DDJ7j>NFz)(4IXMsB9Zr$Rvg=6N4TzJRRgPMb6;ljR<)KW9gLNp6j$}Xr@T_4uYr*?F=scB0KL^|r3dl4}r< +Rp~TgtO+FN9!vT$3X|Cv5f`U8y+Kv{JwL(tjCYzZ`2SRP|51)CRiCz$=$>Utr&t^2wx<>Ft&wZc?}jR +KNPHB`dWdJHmoVWQPQ(Ij+wH^f&-5&FBDKhjXpSf+p?cN(mlo4gvn9vzee&K&Tkgap>hK-@PPxg2@lYMtwDg?@JbIUS%}{;PLy4%nP?&MhuASv$`g!h6 +R|84Pe{HweeO%Ji>1!ZM_snE`L! +PkVeUE&FQR4n}GFjocYR8{I~moh0!^0C0aa95X*QYj61519?_D#)5IQ<@>5(`u&4d!wfTr%Do@TZT6t +HbT&aenVeZo@~n8vC7gN6PRgOT9r#zivDE_?G(stJXlg-MK9z)h>HW}Z(eQD34`((wz*lEbFf>E}Dux +-YqPm|>56)g&9tq1H*$b9Q*$s7NVv7z#T>+0V?JzM83^?9)fb1fiUEaZFc@IwJM|_=ET~lf=;Rv&hjNd&o7JPMSw!mPwfl#Y24X6=YJ?Ud6+HGzmk +0n!c*5c`W6nN-#c$5G?|Vo(;4HP+xci@MtPoa@Is1fr0!ltWWNmu!%8T=NO%27EK@Io_lfSASD1)Z-`j6o#e=-hDIgD8Vl!bGYjHyGXlN +GaX)*xF=cXN5_s)sJeh?>3nM(Gbew~KHV4O;)wwrlI+voFvyWEkf915ZIc5%OyiOwd$xY}e(H5c1J<2 +0Gx^)QF`vMos53Cs{+NmOnXP7=c!hR19BAPa=PYtSWzaM*Ol4WrYd}Q@tx&Ij2`_VVD8%Xce%*x)wgY +TRhGd>);sKUZ$$4+}qfa4}`yftGO^vfFS9kyCCse9#6isoE#1q~{dTAOk%`4SQoujfPyExU%JLA=Ra@ +I8ey0A$}M90EB`Mph16V;~72AzBA1XO-IVLei$;EksB<~plcyN8v*$l +ezNL}#B=(>Nu)FWT!4%hXND2rTratTRJWpIztXqp51dC)hvNsv=OP>m?3K9bwRo*;P_ir;FD}`l8}!4 +MJm~C~gM%4ju8!tcKq2vPaDPem?|tF-uO8sGyQgZ7$99*Y5m=h%QPgXA;lnVyl}i9AAk^$U2w7*xWXw +u)?swa>*|==dvL3xW=;Fjbd#z28LJ-xtU#~|hnJWZFT7C~*Xy)p-DogU&*5d}}k-`50P)h>@6aWAK2m +nY{22<=pDjG8Z003hG0012T003}la4%nJZggdGZeeUMc4KodUtei%X>?y-E^v8$QBO}oF$}+x@Ew|P5 +EFgT#FJM8i5CxIxI)=0Fxl2e+Y#~8TM!e)gq^nh{r5x@CQd;w5P0|XQR000O8NLB_@VeDF`@&f<>@ +(Ta}8vphAl70Po9lSSu+bVVy%*{}XjR25hE{1JRq!Wqd9)G*(iS?;z%ah8>gjiTc) +Zr3p*>1OE3oYn!?!Zie2f79k&jODz)-f%YJ2kc|qWL+xg~yRqJK^{mfhK`A6+P=uTDtTC44GW*5P6Y2 +a&g`#!o&gAfnC7Kj^y%`I8=s25xA@4{G4sbVdsG}v8Ul#&4m@7NEjDXnHiZOqETVab?3>?qEDsjt}~~ZoD_ya`<-rk8{oOvmR{1=ZOzF%nHd<$WR`Bym6j+QMTMSx=QKu +ngAg5fjLes-%)0_E{Bi4VtoEVj`CZQJ8u`{=5-+H8h$o|jpe?ximbC63n_quO&=9Q)ZCX1ReYa2E^FW +>N0tLyf+^Lpqff5(_YE(<;m*{ZCokL!N4$ +pj7;}8Ei0Q~Ehm)D{mdp`@#mHckRoPad5mT1nGv1qd&n#&9&G&41T5f3NBEcM@%?87c6t+`Gw|(@*xP +xvlpoM?CeZS-~tv4o-h0st~xvy5Kt|8IRRLt|go#?1T(-ci7<@ts@2l|=X-UNA?+q+O++`0-DgO28ri!n+Jr5=JZkY~yt>i87LUX?oP7Cwmt>5v8U!B7Z1oOz3JMPi@}G3;_T@{MWmQFlAH4Pb +fFvEaup1oW=sE+MyeMW%!fFNwqDCJD$eH!Xi3X?diZ*BQAc;d~nQX^^a$Y<`cq4F?qF8PS% +yWC-F}$l*N!0jq}969!DvUuURH88pQ;dQY9NK#W!&m~FdCbN6J)|N=2I1!<4IQ@ ++j9!G$fC(s4ERd@l?R+zDF_Rb#BQ=XNmm8g@tCQ+1b$*E8dPiC0q$`6*Op;M1j=wp)~=j>u9jXtJ|6@ +lsc)n?up93$vdIEq*y(TsM&YkSD?^Qj;)P@v4{x)YZ?l~%AuixKOXaD489^BmIc1YS!1lyd$0XV4Bz) +t~Wh{zN-M5i^+ZZhiA=bG~?J6(%Y?6&2;*JC=4`ly|&mJuT@IaSNC{XgyMatE;QGa?cKqK;RBE4J9t! +RUN6c%J4=E#|{|&Qcpl6-X8IaYxx##MxbKx5()MH1W-!@1QY-O00;m`Rt8hKo4D-F6aWCdMF0R80001 +RX>c!JX>N37a&BR4FLq;dFK20VE^v9xJZq2JIFjE3fcJ#Un +`Zt8yK#1@1YXERYI4yZ!J}^VdZ`cYcMWhN~3)ab(s7PS;+Bg +XEqw@NJK=g18RFVg#Cdht*GYoY4r7g6)(DQufEAAY|LcQ4z6)qPVDRKE3*U>R0#He@#l?%VNQ!LC^cuu&4R}OMM5~AeK)FZaGv9y?BINn9C}s<;B6uF +$z6FqmmR6(D=!4m}N0UkmHSxd(W<+>#b^p=$2RHn^P`lQ+b+tgifhA;k{$V!1yg#1131@4h6Hc0HP){ +;R4trd3X62Yrj@UmdT{5r>pj}r09BA$U@oWn_uQo!-ge7aPcmfK_biuf0c~P>mU<;&JzN|D~WWoYqzk +t>hSDIGV4q7HZ2^Kt=&n(3PRw(>^0lzQscT{ix{KNe69`NQnAln4#2qOLCoA=MGkf&k^noxYwF~W@3T +(3jD<_!hFeu_B@b*aW|`ggV{ip&a~C@YoIgRl{kXoGS|AnXcB#=%^@kY(7m3T_G1IpA<$n2t|UsocY; +c}n)dJx{VC7xslt)?y&9?h3pTU@!)Eb=D?BwM82WB`JEm6jHDcoCt_UP +%WcZe;1{pHQOf5d0+o;?dJuE0uOWo69O3XJIv$@KEsnRNEU%UW&fOBxJ4& +jSq&Y%pS6Fr9?m(3U}4CgTQ(yfQUrH}S^#*Jd;$X#YLP7`XsW>tg10`j;Dq%h4?i)=@-8l{g +Fa;l4{bSm(AawwI=33apYgbXAeW?f-cX1fb{-nuzMk_8VcoyP*CSAbe4&*x| +QD7z&ije^U_%VfQoc`Y~ +yMbhd3VURJORpID2gsUnQ;+@m2_3*F6`@U<2zyM6ZXHDD2az#O;Q#-7p!mCCE#HEF>7wN)^>shsKTIoJZtIhRm1MB1`Bn$@B!AT0*h;QA_5+hmJJ^ +SK0h{XHoUE)vPl?0e=mC2=|Vb7Fdn~4g%}MzJU@_%#ex_r9P$MZ<$|Hm|LcQcQd>=Cwqyw#FguArK;<^9b +pb!Xd*mO5n;JP02Jl41#L{mn{~$ +1phMfj-1-Wk>edDLC38?(P|KZc&^_#P2bM^~{736s^TA(~XlR0qFhymomHio#Oz`>LVfX2j3ADsDAxg +Pcuh3txa;u;9UdZ!MzXEu5#bq*F7i=VjcLLu{ +SzrOd~~5kFq!+VA3vqq4~zxcqh?R&i%q|1af>2@ta8IY)34Kq9W+b{js48CN>IUQ +V1YZ}f3R2~NQ4WZS5Acs+9XYVm}#wxqNI}EatIiC8-Z&vNZb@F67bqO8rT>|Rc=rVmqC? +_&s$JYV5?IJLqhgD{__|0N6@msmOlF2(dLp$6+|$9oG~P^G3ZNUd5w+HvqH(0%xAxYeEbnKQ!sRFjfV +%i9@UJ4mCTwNL)WlbsU4c(Flh=6XuJ*tXvAmK)$x%fEN0^YhhYGaF}Nmb0}7!&#!FGUfb9#sMmrT8jM +iWi#zF3k{YP4YNcg_Mm<5-Xtocq*(3oZWmTmu$NQ!;|Ll)oy!+Z!S32KBzAqC>%!W^?tFMAPt9PY(2k +mqlV*#K+8vn##_fIBWTD*9LiSZIt6p$mo)tPWZTKpZusf%fqu79Xh?^6?`fXr2&R*>sBNe*Aba&@=?{ +BZv{nTF>a*VUG=_2C(&yBoc#?Wb%q$a?mX_4W`8}YW$7KUIJ}wF>J9wE6Pzn4G#}2Z`0b=`sf2;QJbu +XgVJUYcuMkMP=JXbAtW1=02s+>#N;N6571;-DIgFovYn_SDqkp(=Y|e^8V%H6AbuU*@8=wOzfmc1A&}NY%3fOp1f_&Ve#I=hs4xnW-g!<$5LG@?KGq7qrr;j5H=N{eUO(`c`mRi&S6=vM +UGDT%Y5{pfQ;GcDPiGPa7n@p|EZDj4ZXCnqP_&$a{?U+1XZ#x{K~2|Su$T1VekYZLnQNEeOIHlkVUGI +w6oji5X_c6fW2~aTi;D5uBGn|LO0Mn(r0lTg#=wBa!I90tK`_-sEt#PoxK)NFNiw8nKq!dcLNdNWvN1 +MJZ#V#13N`7^^4mX*_J +>IkH$~nwpvTepko+ti})E2KkiAV!)sD|lx|HT!(hqvdOEEGqwjw?^%*(Hz^=Gj*WVJ>iAfoo4%QGL8? +x<1X+!}yXwF}Bhn9*bpPJxs8Mw-UhpJye7+}=-Fb&&`S-{^ONPIM;7u$PLZJF%LB;V_(h#Gvv?a!5#KoD(wmm35B`#Gzl7 +Zd#c0MT-q6*QwqP8JZ67CW!SI!TfeelqkXJl$)@K=-8`7SZyMOc*^mwcR#rVZAr3j3y305iV9CmiFQTk9hT4NMU_M=vE=UwPU~cj?LWQBQ}KDI|73bV19Ij|nx8pm0lJ&as2RG6mworn9YRmO$Yp!j{1+dEbN&j#jm5tCl{Vr@k?*iFTDRSCW90s>FmlFrhB=B-m +iWDii6B)rPv-2%p=p0-&uKWO7xowc*Hft@^ZX&(;7Mh3;25cI?B2iLy<&E`4NwusGyuDU4L&UvycfU;}z)HlmMqv+VgaHM+RE54dp{C7!)6`T>p`i +~Jr?j~8kw^d)EX?R)!LG5u!2PQG`yAV-IWNezbA-+>rB5=+{)W!+q?xY +yhBy5ds-lA^=f_x#@G4Pk=6eji}{6C5N4Ts$$TUB-*P-?!=q1{f`2{yZQ!+$Mg9yUtn=J`Tq?*)33U< +K+`w1C*DK$Iv7#(gop>HWjdHv#t94FuZVI{}u(yejRfrt9Kja{#t)O(yld%tKOcZW&@WT2>28v9-Mxh +PRX+tqWaJ@tSU_;Xddpm2ty?@vOclJ_8>VnA)|(15NG(6VGfcL`RwM$@}*&%@1nEOFS`4C#V`&SB3iI +{Jjkf%pEOz3jT#GUsj88#D1*k02Za)+>Kclo(V-c$4i>*rNET?%EV2evfwuP3X80KSwbYhRjO^+LA=J +-n%a!j;z-I5h~jZB%%L!f;agY_n=g2FU{S67v7v_G*4^NO6i+4)f3HA6F~3{Q%s0HF=x*Ci?R8tE`|R +Ye|M?#e9<;(Q-0TGQUV^yMbt+48FxN!CSStP=GO4Q&O>Yy+wU^I-Ccs^+A`RawqVjBjRNN{(C3-1=b@pb)V}ZG#Ya}O2bd*CIuzG!M +94un*Z`*_-I@zz)yxFb1f6aKNVmUH`0X5cd5t%-c=|n~`byF`;AGi8#HqRZ<7#9l$+RnCN@6I>LH;Bi8*vs8|o +xcJTdzYea(XO@E)i()c#1BXx0w({oNxA1=tQDP;rjTS;w1e^U|P)h>@6aWAK2mnY{22;qlI(fDQ0043 +n0015U003}la4%nJZggdGZeeUMc4KodZDn#}b#iH8Y%Xwl%~)TL+cpe;Kj6NDkfMl#<%~V->3|!$9X4 +!0*A~}eFV};h*i5X>mRw0r8VvjHBPA=5om_J3T5NuaEs+%e{78zCO{T7Q7Taig{4bS5{@YX8}jBI+Q44g%Z(xefNKt1)MP}DQG_jzxX|Hz7Q7BRckKH%4YZyZKCHHVH +vJjW}TU_tM5IS@A2bdPh-G!MzMQjeTe6g@FlpS;tDpIg?fnq1j+-1#GKJ_I>h&})8+&4?(yKYc!jP@6 +K-aoT&-i#{c9sMb*WNZ`nW9v8FO$gpBl-uQ?P%-KiZZX|~P>xUjQDimi +7y|y{|Isc%g%EFo64}CKFG%>|Aanp`9E%5oB*xdT>H~u@tjx*cqks~~1Za|b-qR#9nl?9hgNk^Z)=qJ +(DF2oS`3QVODA9}H3x&@WWM%K-A#)R#~aw-!ewUR?OMmoq6(&UWLcUboNt?w)Ip(9@Dw^&kR)A1)F!| +0?woi@Ty>STv%OmB5pmrQBbqheo9eE_X=+H6CvUHg0sP{E@{pi336?bzg^DLq&*oS%X3p{#@aiNfbQ< +PYE;j`-SeKhWxa+IDr_TlO%}?Mr**pLpbj@0!QDje$lxpo>9P)W}-v!ry96HXZ!k?;ynHLtFBvW0p0E9bM5uq4e2B~ +g5A{*<20kv272{Ff_g&a1tNe2x!UI#aSqJ+QxyO{Su8k_T!NPX-irc288rK7eFlEeb!cH100^ +~A#{<7S6ykv!e%?Q#w{e(H?kmupGF3^v@jlWgZpq^5I8WD94~uEPdpqY_W +xTQpA$-0bgOJq>}G-zu(LEWq%O|fn5>;D!O##M{~ekfDz|$k3n)Zqf#{K!M2aOVaA|NaUukZ1WpZv|Y%g|Wb1!psVs>S6b7^mGE^v9}T5WIKHWL0GApe2qAdt4%Ro0H +1*o!#5G)PbbX?qvDy*oI23N3MWRcl3+l-6Dx?!Vs*UnGjsu8p<_9B@X_Xen|yGn|)ahRW6@7Zu6*a+w +v&$B#1oo6F`=u*Z)Ug0D%nEnx~V!yhwQjmeK?mGOe+V{%!SIm|S3%dCvztf-iPmr-41xs2ACOzi6L@g +o9%-(_jaic5A^jrBm9$ug(gxTMv}3}rG-cp-VtG(nnF*@m?*vqDxh&*?QxNHbwx0GX_cX+4eRgh^@ne8<;oTBL8qQjW=*-Z0=rjSN!RBr@7CAwea;awT}~$@>=gIWHF6F!_nCdBuL< +)dyY|>3bo#Xx?nJYNZ+YS(AOI$(ljfPfPUp5gs5D*=xs?VvrCIczeKEsPY%dg-?h9d1=I>e#~Uc> +O4F#2lr`Z+!YO036^lAOY^UDVg+0K%@uMl-E4R0U8BV57jAoUnsYJ1SVoV>_O#SaQSb%Dj{Cd(9-gF~ +^F3Nb#AJt6;1jS5;NI&(i+DJlpjt8T*c-33wBY#mlSN95%k1_updV%8Ht4*$NKQ6p|6#9>afaLtD;L0s#|=b&23egciABp~KXuJB +tC4jLCR`3IOpi;zz&mtO&L6&PwnWGdmdd#70r=a&`x1C&QMsuO=*}7MvKJ&rsLK|2{Q5*1Isv0&dwUc8ekIqfkE2PJ3*FHV*bDj)}N}W*{RP+@H|jbO1fdn +*ae5R@XphJZj@&hg4MOZB8UT`lDAp4(o+0xE2>CI5mzh}OA?I~R7EPm;CSUtR+6mhQgQqP+%>d6TeXj +Ru?2j;nRib>#k10wAO`nTKtTr?0~Ab<-xX@c#Ks$id#==wezcHCg})Xdsh{)tjv#Wmf-?q(5h6<&Mk0 +PIm8Os}f`LPjTX;z%1t(#1oK!Jz(2}7D0sq)W|+a<&!a46q$j;keu8wRt{?-D+btht)VUf{fwfaqxMeObyB-}5 +I~xR4=^qh1U&1yFGD-lSfk{z6l>M=}t>Tu{hJ41lfxKS`}*33JUtWft-vJ-<=5%U3SZx=oUlv%L(TVdG~a$Z0vkN~ +xAS4`+MC3!6phPe(Ln;K}rl8TG%7?bo$4|0R_tlU=%3laLpovFht3Q=2=RhwEG3?I){dwG%9eF`%a<+`|v;qOl4qBP-y$g`BV^=(zhbMRyk9aIzS}zO< +si+akg=8alhOT!O+7$25%y^5%>jMz2W9nTGKz3(Om~mG^lyhTb6vJi~&qZW#b_hB&#CcTa*5@v)Vq+L +?Iz(20lBVB{;!u#dqF6dbN;iIx&6#zHd7hQB+!DK*&f!1QAebouG;9MD$ +TS2ZoMkV(G6ZZ}C1xSl;s)Yl#rk($1n%7MEO1KEVrD?t;8VOer50U7M{0sm2llZq^Bx^+}^3hW8x(p9TIBrz +TN-i@MU;#qz*5JBXtM#}>Jv!~dNaY4_c`f3A4y7N~DzP2RSUukvnuJXkFboQ%t;Ce%AQLDL^SCK=w9^ +#~+qx8_M8Gjb@R}tQz!v#}6&2PQUvTX;L*k+aNV24u`yuzK0<=v`1IZ7wE;u~aIt9~p8Xzy&oL)m_MX +0KPc@5CV>1mTSPX}3R_^3_M2MKar>ilR?9XOknpcxvX1?iFS0?elbRRK-{U2PSUx*jvFL;|RDql$=MY +X-q{Kt`}_lixr)wk^IR&tCGgKd4ccM0g(&XK4&JOHFN(GQ6cy?Z=&+oDQBD=#w8D9K6kM=~m+5*K3`-O@NZd%hfS=S|) +~lWxJMl1;60ffXz6REMKVd&b`(;L-h`_iaaUidg(W;v(9#0ths0gS&aG%y%G1&p6E{2nys%Pdc +c#7SP4y5T5}5qs)9iq;!S=}YJtGrCG`bE@s}?UV={OXxvwrtmO$vj%C2UpS5zvVM@Mx;D*|%`VK|jyw +%oEVEkfQyFx`?0(JdD5wj_a4LN+HId1_JjFjfZb7VT*@CZ!QGC~HijD4KV?#2b*8Mgd;f +Oth++*f*A?RiN+3^lTjT|tY#yZH3Uj>MgSd|2F#3{FPhaRST~aH>UBOL@9*duWwSo+*%Xi)H=G_uhtu +c<3B}>b@zLqg$*4P*U?*JNT&Cm$yCz3plcVY3>6jcI&yJ?E7pIVm$=%7z_~r40mayq0rQ$X#4qrvnXu +5abSJB}y?)&xe(cz2fo_)Wkf*hUTz9(ZcJ((S!&JJHV`- +@E_T?2mH4m>nO_zJBTKzs?fDC0|q%5Ip6#@?~_?QZw6|>rT@)Y`F@0GqGuj4!RqaU|-Bk4xoVTLyaD) +XxDcuOf)DPADq&l%d=l0n9LA%y3Q^0=IIIx6Q9uB)Ka4kw6?Rb3Tp{O=!+^ShQTDj6}AwI+zO!&!nJ2 +Pu}yipD(_T$akq0TIRhx#O64JPo5Aj$ZX85h|Hp(u=l=J1$HDW(5S`u{sPzFga^0olDvr=K7{TkYxTVhqAZ#z^-0D->sVO@v_1Djn>0#> +8-CS6#_($0Qzdo@e)n-&W_z*t{7<6(!}x=6J?g*`Kp3Qfl7{7rg*H@=45?sqQ3^Aj+NoQ +U{$J+i2VEc39(=M_~RJcla)VOmnopi9geCZGPtDSgkV<;L|8%d?>WU}Qt)E +pLUzqLdQ#&V1biUrM64;`iRifJG0u$FfB{ah7ox6c{Kf^j-l9p-I`^*w+An{6&Vnu=hMLxM0YCb8V8* +x2hGUma)iY}O^p&A_%@!^pNcUM2Xehb7{6|DClzx1*wqGl8-Bwix%l~qv3CQd{HOhbFdU85ig +hh3eae#wSZl&M%)rUB0{cK6S6eBs5qMj!;;riTnEniH+B;)icmG;vq7zaPe*u4lrr6V|)V+LvXYEj^A +e;*dGa|cmShS4Dr|;f>eEtsX^~VeHYK)g*+LWMt!Dc?*D5>kVqv_Exf~l;Os-4~`*VGoTv_{N#H_aC! +_z{DqW#>ic^ZYpQ0WdHTw +%H8c{s&M?0|XQR000O8NLB_@SLWJIMHm18zFhzSBLDyZaA|NaUukZ1WpZv|Y%g|Wb1!yfa&u{KZewq5 +baHQOE^v9pJnxU&HnQIXor*HzQbX0ykS9zPOW{K1 +Oq=0=n^{qIWlU*)E2MKS!`4X?VUD8wqR(?;^gkJe4KW$ms8I7~N)9zSC6d0Fn3>f5&>YxUPREpO6Q!R +?=I)##t!3uzkukk$xO|N2MW!gSI?|9s!oJ&5 +h~LLvraP}?8rZ6oG|FS{XNYus_ho&zGQDo*#hNLZ1AdSswM~n=_%u_keNrP??j&S@;(1(rk={%q)yvS +!ao8%q<*?ev)ista$$&-3t1QGj+*L68ZZ!L?5Ut +h@Upp)OO^rHWN5Qw+w_(vnQF2JEa2M%yx}e4pU7;(w;b7+BnUQP=j?KRm&y4RK79NLMVB}5RLgJ-J-? +@CQIeGDmL~~G!OO1QyeZcev?QUqG?H{F+vaM?oSzzY&eupOz#~FPi!()|Cn)Cpj789N!A`zmtEwv0D_ +U0g_}_HSgA@m5}UhRdS6Mu}=;AIxTQXT +2D-m5~#On+j_oa^jG4SwJ13Kh38l783Xdh#6#*N2VxK-;ce3J51nY>Wl2~@4X-PbHyH13$$<0!=2=Vh +UxLb6fqKFEOqz9T0La-KPuGCUn^# +e9l)h4*I6T%IDMn#p8DqOPu*(Cpo*ADkRlD&peiN^Tv$6dPtqoxO+ONOx7YR4o`n{0z7sH>?ZiZTukIaxYv@_dzli&$(8!`0c +0`u~q!<2ok+@Tu1}%fP{9cuyzGVk%9=&L^0VlHNnOKVqO`9NR8bnpEuq6mU1@hP14(OnHqZ+VEjno?? +@)1k$i21w4N2e%R|SXBL9`??SZudliwoIS6ahN96SqX^E=y>H<)C%ZQ@;E~MFtltFu1ZslR(MbF64F +U%j~Jv(^9xM^3ki5}Z#n%&-dg_we~gseM&skL4v#L9A59POmzE2Kz=F7TvW_R=$BoJqHh<8q`NGu%8! +m8!w8ON`9~H7*02Yn(=^##WBIWA5w}Ov#Q=16_#}k`5Ho7mL*$tt+q%I%iQCju|0*6Cf%EN|GhYIu(Xu?Bn+N}LgS@6tw8on&6N&IYCYU`s6Q> +|5MPEUK(Q-cGgyV9ezoxF62ZOqP?wy8=M?wwW63x8%bD; +pPO@d@Pw+$Pt2$Tu3r1IldS#8Qf8rJyjLEM3~tpyO!OI@uD|ha;BctbL_Y1plcDe&UPuRt#sw6jFG;##L(hktcwvw&IZ}v`rS4Ek +gf#x^IJ1Do_t;Y;Yz8*uq+fL8 +WfLkg6Lc}y*hykWS;B}Xpb`12g5WC=FCPw;(Wi{Li>7}>;LvRl48FL*gN01huh;Pz?>z3HJ8h5GR)st +Mo`3e40(|IMq}+PHBDWXxz_X^yEmqO>jB9xabm|9>j^bgqH+Ig08YozX)cBupvlt=!{BKn(yega(ltG +B`--Y3o7fdP~IkXTuYcOYhkea++|h>P>Sn!N$2#L6PW(V;8o|KDc1ur44B}+IOTNC641OGly-db&DWDfb=PhqHRX&KF(o#v9=d?kHTbF9{O!;P +Nvkf2bmBe8ubxP{)H=J}TsxcIV&<>rIOa**^Ye2Kq7BZvqaOX(v0%D>VK$mFNXi2Buc481GEw0MInh6 +@Ijv*XxT8YtKw4Q%5uG;6b`C>mQaRuAka*Bcv^Osyte=31wcD%gJ4z+z??|i9*Kwa7UQ5LLy`(N6$hI +)r+X{)no^4R;&CQ)Got;SSPvzJUx5@?DzeF6kpE5n(#mSp`FK|vW%=xdaFY>$wLCbOsQ&eq*>pPKuy( +|RNU`q6qCR5))&nXkOMFN-S8hpoB+iq1(DJJ*S}d~e&I-jkYo&>s8H@jaohY$qCH3>1M=ptZM5imfQp +#)$R0Dqz9FzUHfBfj^a$2Jrn89#Cjb$sGXG6V!m&() +vZle}hR~C|{t5Q!^M@L@;I;hfxx-_6@Id)KS+C1j(4y%?{sU9CX>coxv5UUkV4qXgq(Wkg_RSXD1jLBvx`J(>3l-U|w;xv!NZoV +HZ~8GJry)xb&0{m^~#_10=#*j{zYfM0;e>3vk*LNc1kVO^Sh^9f+wJrfW=98G0{qZ@3n~+!8%}&bfMTj+cV1FYr|tUiM>tWAn&JY?d;v*^CKT|9Y{z{En?(P2B;9FzDf;pfUb`w74Ca4*g{cgudETJE7*Tm+cJ{A-fo; +vXZ3dk2sr8bzsVJ-xBUu-srB`GX#=#*zwS8*~#{~wTIOB)_9N|@|J(C24FiATAMnqK)4`m((ZDz$*N7 +R;18HpchJIMIT+D-cTr|%(1F$krU%;{|j?#QWE+A*_omsVK{%nJk@eIE)S_Ae9#PpuWKP7;8(hmdaLE +EYwovoQKP>!EA7ywGi|I#GM3q6DpGwjSSgtA1;&&_QqoPa2V9eQBk9bDXHHI{O)8%^g}m@F?yC#)Eev +CAcRiKs1#KK+>L^V1#{NHMv>fHaOXGaCRwY+*IAo##Eq!k$sJ6Jqk-rBBTAl;vt|xO_SPElR)?v~e(1W;GCx +`s@XB%{ttElQ5r)#b^8`_cO6CWVRmu6FKd2y|edu_zTe5YN-l$qMYumOdNZom*Yv3i!pUwN7CAG9XSD +zfVUof^&RdizqdG@YqaNtjWX+GncN?}zMC}Vmj37w)~?c#fiicl7@+t;vEzj^jGtev*Xs^Xio)6=h^> +o?0_UrNKsyYd+pmqXL-4w-fePfmZ>y{u~roaoyuR%3W`$Irr+BPg8`SoIsybA2FC?QbYRDq?&@C9M$Htu(8{92TO=c7X=0oM0y#AQP +Kr}(#Id|S7>!SjifU-@DY;w1sO-w4oE!tsWA9s?Fx(dFEBqcp5ZP@p3l#mg5=9Tw7xZI2}2$9mM4b2> +|?D-}hfz3Xp2UyOZ;VWIazaUeMC-C+t01KQys{_k+&=j!3rUzP_vQ(kEwP0mcB21K%2frC{>hit*+Xh +X*6dE@u@L0)i*!&EGFZ=Gne&^;nbM!7|OJ@WDc6SGx3YPlAN9(1_De_CtvtWneTMZuK61+5;cPu0~H& +|V%i)ZU>uwkvDdHt;^I3=ev{evR!Xh%cuQSYhxuRs)J$)!2T77c7# +ID9>@UQ1Lhh4@tgIpn2G=SANdIO^OMxu&@Pnl?g$@>}g7kpIwCw8aWOpYlmS+tKNS(%LVE$%+D>N^r0 +?2Uex4>SzE?lK?ByHlmK^r-ybE6p}Q3*}H1b>Hn{OSEA-Z0cP!erLy}#7L~^WmX)n&LFtMcpg)nM%;+ +fu6S-KgdBd>={=n=bN@4m8e7?Z*CBau;Eu2D_HE*%Fxv##&RLYW#6vHGD!s_c#q55ka)i#V|?CipmrE +%*iRijE!bl?L|2S{rHTvoFfET*$zzbBBx$*Op4IJW}IQ$h2*>9+DKb7wjGm2#GpOf{HYvyM +?((VP(bhNYaIIenzmIdQIcV~z6KG#zCM^q`s0Yx)p^?TKb1H?(~#&KrbPo0?(T$68{?uSV(D!XwSjYe +ZDI2NMyar(6Eabm)I{V7|Inp{Qg@1s`}f7h=T0BC6D@MT8e8YA2@h#eaI{smXW>q4mkmwTu2^bz=d7c +qzfJtKOXZ2*F%ox3%HEjcP>q&xRDnDfDyhD$a;`U|v)lBB4;4!gtAc4o?Kr>+mYp~>Jp;p(ZVylrF;L +;nkN_U=i$~C`gTqc5f>M#9&$fl5REHS;k`DQq##>S`k>mTJqi2$y>Sln*-*v$cfgDWWgf{Wg3cAqM_2 +~4;3U$WF^o*?|7jEu?1{Tt;P#pFKLzo(cqxUX*T38#U)NK?iQ%lFeu|d&njgKCPr=1GNaL_t`N(Bt?z +rKQj96JoAv#P-@u(|4Obq45# +>9YxP5y{A);CcY6J(c^5Lyw*?eq2)SX=uspM5_R$`Vq7ZK5D(-hZoY1xy;9=*!4c_bI)z%5xM78p +=K>>1rJM2&w}3%U-4O-orcr%Nsb{&!D@eVj~CfPIH)`>lNx-0;y>5jq}E)gp*P*pYyJK_++ve8T8{kF +(R$8{m4kLOpp}Q=!-Cc31CCg_Iqar_Z1N&wo#oejmXHZ-Bx)KFt81_Cw3Pq@H666NIXy$Y?OqXoR0i+ +YXQAd5Vm#;5n%aS;GRyOtN?HPBi}~g=iJatG_Y!>GRwqEJJj={Lib!(tLk@5&LqV#I(9f>>RYz=7c^mOn5Eu`UnMYA +#e^Ow3WrA#lKmxS@kahq~u>*s`yTw|DT5uW>%Ab`|MO6}o8~B;cYYlQAh-ZR503-qECu_=L$EQ+!ufR^0OxSkIkLuia +vTt|wDjNOI(duA{dwfL)>^Z^JKMf^3m1D0a2<0vy1I=wOKk`i1Rts6q+t5P6kFqbrb*lkyc5M##(;aSRW%dUluny(~j5*s?oigR~5Dcfi4mr`0P)9w +XZ^M3k`To1V&BxUH5zk0VX)1H3X!r8s;?+B#-OwCNVJUd-wOZslcwS{E#2CJH&h6^g@6aWAK2mnY{22-1MVPrW5007Ps000*N003}la4%nWW +o~3|axY(BX>MtBUtcb8d97DXkJ~m7y&LdXsy +?|nl`ww(1YVxvCnTH?%b_~y+UI?J-@#44K?x71d~3FXBK|I(9c*Pyx(igKXU?!+S!=W0Js6)`8S34$-+ +DHEBreUSO&bX$tMFAVyI`Cbn_88%sO2pI!)4c28flu&9U|JFM`~|495#UyVDsWu>W=tafFrn$a`6|x- +YBi{tk!FCFa#=9r2@_^b@B${90`u4>^zBB1243?A(46YIqPG}Td8!y)yFDKCzn^R|Ni~*-PNUdCEg$< +N@sk%Mb0bP>97#mm>X0trC2FPB@VldoRI>jMD{3ZzNxiX#Y!g_HWU-*Xvmi{;MAA2R&C1^fe#>!7~?I +XA*&Fmv4K}%K9sB7#`7dFI2@;8+6a{IY&5FIHFgA)t9}6tW~JHWnP-*0?REL-H{6aUO4)vBOm;Q?XG>}&a@K^^ZsS=>l8-%qIh_c6hNXgE0cYQq$#ap75Q5OxmvD!QD9dhxfYD@GV{eZ4Y%dx3q1MP!#7DeW4xHE?C+cPaW`bA<4rP8IU={t5Bek4k93GEl89D&EeLN0@&P|98I^0rXCqdapd}pCkkf)?JL +#PAT)h$dr&oV_goa>Wl$a}fwW>5KUq8>-tHrU2N6TU59pf=yX;#&6SSaq3#Rn^ZljxbD$@kC6sj>^+6N3xN)~YfQ15#!-k$ly?6Pz0!2=RzisV~yK#0S3lY$ODrbjC-0<(;jTfhEg>9njm|H0?R@Nc +`IKa+spS9cVu*kS~P48V^v0G|tSLnqJz6c&TA~Tz9CX)#35OOQstOV9tMbv*#0}XSUmTA+pE3XEvXUA +9uck#*Q*iV$kD!P|opC%ny})mPusv*;ze +l|QD^YKRG$Ja4+!`(`jDjYdhFpWNP6&Q_x{{T=+0|XQR000O8NLB_@2~!GceG~uytY-iK761SMaA|Na +Uv_0~WN&gWVQ_F{X>xNeaCz-r>vG#Tvi_f{yaSd#Q<7XVWy`sGWA8e)GjUCPIgvBTRGd;M3X&L8gbqR +4(wy3TkA0)_Sf{%IkRWxj>?EFDrAcq?8UBJd_QYrs2mL|9s(y_%*ViAfHP<)R*{+`im%NpD31=vNabqp17*k{yeL%Pmp^J8JD8GZC??0fzx4g2_a{1(0K1FY#C} +2z6V=v;;C8jYOQd!@!S)1YM9YUEcH3FmXLO8vjH3L3jJ2^LHMtrSk8a8|%&Ik5)dk-kiPO+<5+Iqq(u +s+}r>?;RXD=u4VCjcG6-WwhX5(zvQ79@z_QEaK%UX>5xYW>8b%;PFf!tdm{4rC`rAr@$&Q)h}@dUgby +1*)a7G$kPJh`;luvV4Ao0odKayZGgdMIWqEN|y1kh5E|(XH7`YHgh)INA4rX{yB(xg6<_LjHVhyNmcSb4yHx|=`SpZm_^H#U>Zr)ah+NX**ts@3{n3~PGoj1B +;GjvZ4;=&@2<`Ag9IFVOian;Hf@6xN(bWhFI +G*mwd*^ay+lFJBSCmcz&_HpwQ$f0}pOxps7_b^-HN&*wM!W{jLeK>XM`M0O=g(v8BOn)TJG%&4P1<_` ++qVDi;4ihjK&A?dO0*YV|Kr|yMIdyy!MCllXYNgRh3ol-Q$-HdsPf=Q7EuJLk-^n15=v_{TotqAWuaS +VM?s6}zvrYXh5t +Pl_*FLYCglM=msb)7a8n$gltdw|N@ +9zKB324R%PZH$O*aQ`ZVBOL7Zv50sbgQb_kcw+rsJD>g*3lD;PfBTgO?2H4&5RTvSB#k51134KCbb#8 +x$wO9F62K@7JZY}UjI3Ni*}=@2qopF_LP9hjU=hU*%=#Smm=f|!3dVzdv?72Z%$kTNNGm2mdDkuSQe$ +>ll`I#e?qVX*6J09{0oO&|83i=~I;U5yOOtVt`EH#Krl?d8-qbBAqLdh9Irc +HxhDQ481QR-f>@mLBsz}$(l~k4rsc-6aY{}M$4$85yU6pYHLt?23Sx>h>&|xooN)IbYEjWC>9t?p0Kk +x<+)C{v9qECnj-QwexuA7ZNLNF0TZ+nxJ3IxPm^f0-fVIaPp(9Kf&9}E^)L)Zu^?omG)IJ#r(Ol%&7I +Qx%1Pu`$U8#O55SB8&=p%4bO*6=09;GhieOYD3_7tFPa3}e^vRP)&z@J`x7>~O8X>NoB00(nXtz34qE +!S^rd(zbLwk}Xx=|M5Q^+=3ImsND6+i!OywNi4=1$653LiFJz>mi;`#Tu6L7;y(U@%cH=;wFpXxZep@ +DGR~xJrwB4Dlf6|1Ju~W!{Rv8wwaiLk>V-o1tb2+2+3h^R@SP&vx6dPmT_VAsATz;Vh>QlMgt9Rgtxx +;TrPW+dALZa8rS4ozCU?3sf7L_RhII^nK9dIf}7Mk!|NZ6KR^6&4?|_>o62oWH1?$M@{J!NolTisL354aMmP?FwQ*VF;R^cOn9MS55W +U~^#mRq%Q=FcmC7I%U_#xRm+H1nNX#36PeAV7&SMu0$k}Th?!Rg8o}S`p95iM9)0>mM-TX#Qg|f{yqA +*G(5S|e{4XW)vPud`J8+6wNAWqzHAyE_`MJfqh9v9jSh2>Ja8Qi~!Agbb(g^lWlO|96S--4n=#-LI?J +qEIG&D9KGV$d*JW3{q4epQyy((-Z^hM9MR`Qg!JwNfzMN`02RC6jfQn#N@X%n6VMYH%@Mj}{@_RufWa +k(%f5Sq2He9eh@B2Z-GiK%}8f{>qMysy}-!OH+r!cvMk!|AHHp|vhoa_I@)%HO1Lvqb)`O)vdV +-hZ2^8S!(I$XH-Wv&A>G-Kb^ZWwd~~<;dl@Y%O7juoE<`|uvu=tAe}%dAw4l{jUJ(8bkjB>eX)O9+Lr +6hc7aIg!Fng*W!7Q#?Ar3eZ6}MIt`>7^dBsW_ihJgJwu~5~ps~9GTOjZR&_SBt*gvz;b`K4-X+(Ljj| +5*7nb!pT4{qEc2!{eW~*uEg!$bv*3d})0~S_QoM(J +3i2)TJGsGZZ~`V}vB5D%=AOHMPSr#uYg=Rht^Q2(?ek+2=GaH6N2KOLL=tE~C^%!=QfSd_s$&W9v^gaI~ +jrp|NC|He@VWX845eY#L|VC-^CW?4s7b2Y{#gw<_P2;y$A94ApQpg@%J_G?DGfL!1FM3!z#F>kS +mK@gi95#%jIzTy->K`J2`%J`13WT>(v?L=LmJ%qN*jw^<~xauohHQLHdsxD%Y8#keu^rI%&<^el0-FL +aMn*(ajp#{IZ)G(qERKFHuo+1^k>Z-d9&X)Yy2sxw&H%_il*|?#p}k +$lx9s{3*(Nc>?@K1wI{Fmnre_#BYqdeY>=0s(D2!3+s6|O5r9$7Pcnl6!50RV`c$wN%t}MWHmq!T^wl +AxT+}>T)NqtcYIgT)^L2I(ZY2i;?RfLa`jh6PXU*q3HnFd+JXvyAY6aJ>e818WiaNfpA1zxyT2SACc* +SoF;lwD2@Efj9Z&BSrp7#*$9>V>A5KenWUfjv5Ia8{~sZ|Q3NZ%_mcJ|Oq9OFUw6hi)phVdOcgBH76t +H2J2#r~WEi1EZFz*By5h9r~MZGqwv8XH;wscI#@($3-a8=hZ#B<`HVo{gYxc^J-+R76j*K%u-AEk~yP +K9?jJ3=)5)r*FE#m&&^Vm(%VG0$dsm>x&FIV*AfC4uO3xR`?WD3(x^*YPHEkB3jeM5k;$=}Ix=>ZzjD6#Ow>B{}-RCAbap#9Lsi(2DV~Hyo+wjLd|%u6u +qYehxnVqSt>Z5ztC4RrxJVa`a^!+N=G~fK~;nE4R>8n&FSIs;mPrqj+$xY)08OhlVx9TF}dY0T1CuK1Jjy80v=jjGBJR%`K=2mlgOJ6h?~xq{QdCe6EqWe&DG#8z_+? +@VcFdHjRL~{Wu%vmbBU^Z$wNx#9Q&{WsZ2d|JDR^|W!Dmgu@ +kI +YLi2`nE#n)=*UZBEic~ek{^=so8CX|E4;>Ui4Yb{w#XWg6cxG7h2s~>$7CPR>Hj8yhy@gtGkuZtc@4R +XScbfbaQ2U_+VuXKFqw{EGxapB<=OgGQPAG*Tz$n?U?s*1v;tvGU$f%Hajcfn9W{~7LjTRiG{h~e*kC +!&gjcHd?w2&7WvAypL#K@=r~7QNSMkOF=9=~r}9cCHDJgT)>~C{P&PROX63IN%w8 +J^qXe#4AZx438Q4NM%^IlR<$#Bw(U8tt|7$JXimy%CVvYvzPJOCT>G@b_yC;T2$YS%m6t_jU5E&tCFL +n6W$9%G~b6yvGqIVc!H0}vt-e35{LX_st>t7CjYwx{#`}W}Yto^Qa@Ya$dcFc=M&yP&q^;~1=^SuvhCgUXp+hXMHV}bp|cgwd^bG3=E!LK6s% +^8D91_OadYu?t8c>DzTwAR2drd5+xl&bYJ}(RSpDNIu>A4p!c;g0L~gM +S3w?PrhIpFiKR$|M6%wvm1tiBE%gj1fK#7DS)kEEjPX-;}$k5sv3u|_zKWI|66J$97G+8BgOxn9I;ZCE)J&J#=k1pEHy>SE +&Tuz9R(0Q4lWJz(LA5BBHlHjJL^mH_A2yrM7DdWV+5Vt*ZYv=KMQ;Zcs6e^A(~ooUM>LlaGa +R3`@F9L1(zyAG!e}@BU+`gD4W3hW}>t=WW5)9#FxmM?+#uZn=(7)BT7YBl2*5&@AOR@{0uP21i{3io#oS-o2EEA@;(%>q6dv6i;;!{gT3?(yD%)n+O&Dmyp1( +ew{bI$uipQ8lz1Agx!Y?X%xb4-6(T?t=VOXhc;UduZrxB3hLHXi^iQl3}%0Gw1o8^L6L1;%ddTb!V=& +gel-hIaFp3zZm~28iZ!28qZ8!MBL&~8rav(a4};oD0UW8vvM?wAC_x%4G6?f$;%PH&d-E}G&?&hPh$g +6(D|!7`?|llp;T7Y|e}74N>n>1YO*i6P*w#41+Q4zS5CWtmtZ}lrI_;54~A}w{ +W-2rGpmsK~d^#?UhLrOO|=udGpbe&7Djmhdst4_4gz6dV06DcX&t~j$ZDyU_lNHvvaYu7ZeBm09XI-t +aV0ty-W`c8p1H3YK7?Hdw7s+rMA#$t%9{nRCv`8t4*0<)gaC&!<}{ZMu>}aL|>*4$(E3)^|(2eH?Z!( +@DCy|H9O?;ZOShAM-Ra1+wGwThT1lMpVyw9qY12{o$TkVAQtm?ws3}bhUhZ_=h1J&?s#drv+ZoS>!ur;C#s(wWy5K(S1L-3pX3&Zi?UX+MUFg>Wu1D}YGv8aqbcbH{zZhZ7{DJbaVE%hGT +*(R`KIaeQrcTkY9r~Ji~avlO9KQH0000807zB_Q(yR75Z5OF06MP#02KfL0B~t=FJE?LZe(wAFJx(Rb +aHPlaCz-L{de0olE3G@{0~^??nz2z=A+w>^VaRUvD^AJvGZ)FyL(AirbSq0Op#iG^2cWTzu$ZSAP9nz +>^RBoz0=*MCJ77%gTY{CFc>@#&x+Y~nNH3s@$~VNuZHl?*Z9vjVlOS>f!JiPsbukyy_*&#eCFV*3j8uu!G?PMJjpeKoX)eaabe5 +%YK9=G#tZ;)& +XH%;Z>eVr9>(JF;7eA?8&u=XMoT+J^?hd_)-+5h$p3lZ&iT^UzTZ==98gN#c6dJmlDAwsjABKWL`DsW +zd6Oy9Iz^aW2-j4#n=_n)q?+aQASCK>o6O^vnLMBk`B5gM+QTqurfDv40?*?eA^x9_{Y$!P8G-YwvI3 +*WJDCp^!i=Xh&YnO6&oknIa`+LS#FXvVp>B!6>Pj9H*!07`m2E=J7;|NpT^|9C{>XvYe(0M^piV34+P +eXjSj@*~A_lZ!B!Qb??C#&2!uY4TTrXvu&}cF)Vi$-9SX^l4b=sp2?N +&Hm*k&n#Ij0i3oxtC-EFMdEs;CbyudA~n?;+jONt(ym{d*g;>y6WSKHfNnq7?#-GQ@i1Vtp*I2>vw`@ +CG|!x-O>JiD`ixWeUYvT3ttC3rLYwFie2vg`|NdRG3O9avV>k$mJLqAug}!1#0lG4wmVgQdhzRr@dwnb}aZoZGg6sSD92``NGhR3 +QH2Rvbpr+$Y70?5XLV{DS#tv{etf15o2dIRJ5C5CS`L!6wFu!Sfo~6hNW5f_=?Eaz0HT7@{U=qxwc=F}br{X)5C<;W%$$X-s8GR?2LS?YX_s1jT2;esuIgguYazHcFSy5J^Q1(w8WBsSPu3 +x`Xgq8kqmdPvolob=;vx$9<%gHP*l>=vf0-S{1#wPFI8@<}w{VNEMpSE`o#3nQz$^0TM0WTQNe$V;Z8 +;af|S&e%GsI`AMI@o!>vvs&L0?D+qxAkHNYWE5S^C*)*OB;{%IgjBTb#j=0kb(RAb`3 +RY6190qs{U>D=tB%A@d9gn&3s~1f=7+^2(C@Q)3uqDi>H8jhzbS*-l^3RGnW9ubEsH6AB6To_ +^^3rPUZ=~tv-WP}LRZ7sXdG(Rh=fj&@sZedd-w3@V86++JXoP|iLPK|BP%n+OPKtiDG`ahvWGN4aV#ehmF9>VwNivg}ZKK3UTQ8P?|PP8#M%hnQ%-&}q%c)dP)+t +<+kr4_=#e;8bTY_w0>1n4E{1h^Xp?h;HT0b-*>EptK$uY}(2&oImzXnNa7 +hm(PkW>F%Xz4b^LCL7vHF}^%s&n`4fOQ90L`7S7?VrxGtnp@qgxNeQ=W?*YAW7rFKMrU)5Mgb8JTFV+ +OJ6x74ZfPskZuU%__&`Y)X322>qB7bMB=LfSQRt@Q|Ow)x~D}3vr}zg7qI$60HEt1AHz)EJrxhbeRzm +E|0$@Ra;Q~*HP3L~Fd)$uIFZ;#74$M~Rq@JF>ye9+?g^oEw4kOb*QjiIzqGb3XscuC6vh21STbxvJB9 +#K04E8I6^e0djMoMZ(cz{>j+)Ob$uolBdqoYwbRZ#~q^GBn7?sh{Xd{T0S)m9$5V#6nW0iPZ77C5Jt +V(B@?vX(ytr(QG{ra^%lfu%077|*6ZW~HOQ8chr+Jg4b0RXlYSPfeKFuv3R+psXy6?ZJSY%(ZbJMF41 +mA7ak4!DvRj(sg!<~6Nk3=oltMLwUN0D{11vtl;S;?k!Y4tYap@Mw#XMsQ^j37MZW~a0Z$%rB +s=8y!Drj6fCCWak`pS-U6T{OZ$E5~*FL@pR6J+%#89}bP_qR4SWW0#UoA}PjmwE6*zWmou;6jC!E@b^ +TIfaX3e+Lh_8i<&dvQe}NM(-=eq{7TkUC+Hc$RsK24cWxpUdCBFdWeX~Fp9>V|?$`*eHkNb!E+o?>zDzM*vqDTEr!BXrra`@_SN81ZdL +$3b5R?L7wK;G$E_=vUi*LWKLv#2|?MUM?kQFC!x+=9nuPqJd3&=jOn&+w5kvK^9FAc0N_17(LF8&D=l +4_iX~Ec2tHSt31|j%KJ-pI=O&-0qJr#7lhq^|u$g1bl!(76yDE34yNptoNr=JR`s{jQc +CCJPw-fOoTf>R8xp#}3;k#de89A40HF5aPQ+P!AyHIoSN=q5pVgXCYOuJEF5vn9^9*%wWU7Z#4EFs;4^> +Nf-^Xtp_dO(_Nr%S|v7+bZIjYLf+X1N*ID)3b%LvjWrVm>p<0C0S>B6*(_a6Hj*HZNy|s;!)6*_4cVT +s})Xq;@8A)8x_A5&`}OU<&P`p^z6a6k5GXbH`$0^-CFedM30~Ua>{oLvq1t?l`5}u&j2-HU|i$)X*r- ++pcZB#pf1V!oF?F2?q8zkqt0Gy@K~V_?N!zF=1*v +nFVg4Txz*uSU~OKU&7sRyjn|f8Q#18@Yyu+FIlN +T!yZLjASx^q-?NfrgL`*S<58>ie}C9HF2F3y}b&&vNt +l}99eG`8PFkJkoWfNQyC>!zs3AogccrR1hE!%H-_`fOc6Il@DWy>Bq!=tZ2)ih0r_Ik^G +xuN;fMwf8}8UM^d?L3a^NfgjjLRm +n6omVsA*uKm9=`C3s+Lxmal(<_)06Y38hCDg~I}@%y6%U*78N!H5Y;b7&pVwD) +PI#&I=T=qh{hST_6Q4R&nYy8j2Jk633Q+2gnL#n@Yi;YT+g4=Y|@^S!JDh$BIc`2MmVQ*_9shP$Q(q9 +CILg{eP@CKyb2{92CD{z4`%nlDU=(QpbP@S78K&mdfw{OdflHTHSr9M%-*}U0QL%GAiTx +U;GRO?D2Rng;N>ICFy4S%Y&;93fB3IgVJbT4+;h_<;lO(sI&<0jEXy>R~!_o;_O6z+A(Rgu +7?B^mNKEi&~D)WTz2j(11V?mSUSBz555zzgnJ5}?V!DFcJpPN6-s8;xbzy2>7qq1MgQR> +E)#NP^6o9g2O}&rbzv|kK%yy)mWle%k#wl?&NAOva+-7YC!7d}n4{=82p!du)ih(;Uv>k5aOov=IEVf +krZZ}zr&dKZJ0udc=SOBNTTJmCCdkG%4=E_)md?w8y9zNoNKdmX#Cu_0# +qq)qKpd|&^Gzh0Wu!Molk6YmM%zlk1qeP)#!3VD#J3~6`-z^~4qQPsfDE$H;wUH7n5uUY1IA0rfGSdQ +aaS_qpV$0FpUbKw2n2=cSE$LTj8!#MM9!MOxELAxF-GH8O5IN~*r&g=n8(l(@4U)o%N_3b8v9#Yzb%& +u`v-GDsSVR#pe;Tdsz5tq2{<)G9RXS&o%!>G|aivw+Ng;@Vye6Y#dhWMd2YzyLIqtbrF9 +u>N)2-s%ycyw1&Q616Qa(*ACiH8o~n7!7(;_JK)%wn)`?G$KDEj>ew +U&>p^+=;bBOQ~J=c>2zR!{_RZ)>jL46~Jf-)moHs^!j>*{IpaHxaV$7xezR%)@)v_Mno+_SD<8@Ts0} +V$mgMEDDvR8)C|D7LtYG0sV3qaCCZ?8_T +>!^!Qlx*=87Q;88OJ*T>8PiWOA+P4qE?2B?oHR-vggN7rE{2^9!K=vs^TCJmLH)ZSM=W3BgJU<&VzAW5n29GD& +B|gX%j&vcoV=6c${D{ccQ5JBrku{@LA6!wrHX{u&vk~6Av)~Fah9ptymCx@>m@{arex(nL5INcXoQ;7 +XmpHm*Qay#KM-K!2B4HANQhFdV>>$Bf)VuTf}A=6T_**QeI>Kgp*S_Y#2>zjMyKQ>+!XL99bOxaS|#w +S8F&Udku^*pAN+Hf%hCmo!Qr^;b@I;mTlb1w4n>d9e*e2pJCx>_iDxW(5wlI-L26J#gCWMGAt&!5L!; +MD!ME^DKjL6E>mh5ve_RHj_*C~mSDOX&G&1KQ+vkSk&bik(zs6@I(Cs~=tqKeSO6HkdpJc^JoZ0@{$Z +*CGUAC6qF}eeA7}GjrJT516_>N+G;>rZDYDX(qJ;vjoMV2PCfy0dCWXVw=IFS*LwS3YNCR9#y){ocF2 +>Gg3Ln_==chV{rZp)8i_tOYwnj#Rf=U8WDP=m>QON7Wn;-Tz;T +OUzI}F~=&c<~kGAC7m=dp=vgXkHAb;yj6*(gc}Y%hG%x%Xy}GrjhmP_#+KtBX^W3jXE~l^KS-3^LpH@ +$C_vxDQl|MuajruJ8DKyKbLC5yglS85JV}tv6FCBibT&U!#?jFcx6NM2G3@JM3rd^PWSR}27^BI>0><^p=M}>KGDidIhe#SkYMaPL^m8f@ +S^$<1Xv^C_?eoSpiSq#?&&dm%_YPK7;Q}*l_SI4geh?lg_tb`I4j+84Nkjh|`jiQcjSph+xCA82&Bi8 +dmwd$WiS6ic(iahD6AZdNo4VkfhIx>d+D}UdD(If`#)nU(=7qtrF<626dWAbJ=9HDIW4duo7uF3%xUn +KE5=~UV6GG0(H~e)=BSWqxkbvlE3{pr6F}rnf;^j5&oDJydT%TZ#cR6}VeYu^)7lvT9(28QBI(>f?rgdK|B6=~jgbCM2&3CjjvlK~k5&4% +7W)4d+1#hcIA{>CuM(gWvAMF8VGb(EWHUrpn=X^i1f9BqtwCeMNKh0IEG)(GcH2Zgog#3SEY+jX+mD& +re-??a>Yhw}HKXx9yyc<;MN-vPrlIN$qpUQ&k*#`kTichnnsd-=Mm|GKNTzkhA2`|rEz2X=1_h`^^VD +D|eOsa(H-I)gRioNHU!SbUS1&1J(trY!A$T%NM8#fb}3_xXMzvl`dQ3u-_lux+O774pUf)5X>Biqo!? +arkS62>XFRt>(Ifot}yw^=Y6ulo9oZFlB|zl+-dL=?{hh5GLY3_|XlQG#?rx4N$v9jpMGXGn_4o{l5t +TIeaZhFiP8Cvd8uHL`c!Hz*W!1l}elMwIolIi1Ll}2a(RS-tP&_OFilhnw9m~5Mat=?X+h99Jt+boAU +)DZCYS#rqfD#T36W!HXiJ%pY3@u4y|p_o*kp29NjfSoB(+-Mxm{^JEx3IZMzs3XGiH1p`a`GdhT4KiO +6y^l#iq}4Lb6Y+|>j$v53L8wW1PcaEitqv{bWlRbK3>8RVJ8jk!b1B^!gwboNGeVc)pMDBS9jZq|XEz +L+5cv7(kY+8|woZa{+@lEeJW-som?{m?8)OL@Cedg>g|4`HDDK>ztdfV72wb&b}vU~VqG>0uCQ&yY|& +KmNLAi+7Unt|p8VlpMrBe^)?;*MYUT#cK6Q^j>iBZgZGPtsE>K-yQnml1Ve*8zG# +QVd1^Q)|X7Y8#M3E7E9W?+}qaB;?)(shJZoF^k&2Zc5zsLuHDxG6H?VFp4y2jNcRP!+XshLv7I`vi1k +ximctz%lrVG!Qhu7HZGOI}L$x#sn-kr8}TbB&RHf-k +{G5G;Yo<=>?3S7xV&AHRuOq3JM%hU+2IFYth<3d~XW7FP&q%n>aKbvZ)@ui@Jd1giyD^OZ?%cjk&**Q +pV?jRf_f)-Gp792Jf}!u;ly4zl;9+C%+GD6o$=i?!jn>R#+L`62$t@J&WUyAb=^w0v2g46FRNVEo3v} +yi7umpo+izQ3v%22zRXO8e#f_D4NKt=A{659YFHM;7#-c{r3m^hrb-dP*E__W)oUO@B2>MZFNFd4KQJ +MvI=NNSBoZPF`ub^V5Qf5;X>iH+JmLnZ?9U9r0vjK+M5{zZo^T{#0lY3>A`586?mt0h$)DS?x4#wZd! +`hkKZhY6Cj~l1mZ`0vL2?uW5QUA2pt@`=&ZPPb)E<-+(V4oHq)F)6O0e +Le&)OoiR=k_e%rWn!5%RyTew+qIq{j_J5!xH}{9deJ!1)Hr(-=KRSe&N&Fbsx;ZfWW=5%#;`QVw(v*(5FZ_s5B^dP^k&#^jsrM|fGS9wiaFku%&ht_N1Ei_YuR>9J$O?IH; +HXmk|t#sVlbqjm$oLevwLS8mAFc#IbGty+U%ipSn!)Cy4)~6@K6UAlD0}CUI-{P*6~CP0l*ueK!2G>8YDev4+>#KR~&PnE8NXYK}%nfp#}JYWe|^%ymXJm1F@~Rs++~gLlhPe%Ed%(w5LKr1pI*5_TSZB +V_KBc7bl)T8b^9pyM3=ScGc`u8+)h+?|^ft`=wu==EpsNM21vf7&lzBz~4v$Jq!=GbiG_mD$Z73Ew-m +)v@@PP^!>T(-w~DsSQF)6*k-tg-7|`T8dr4EM(!*`hF1mBH-3*j0*3YI8e->Z~m0lQRc+_%80ip;WPh>Bg~=m|}vq6AZhbbI7 +AH>gkNttD~RRzvZAsI)hiq0IQ#T)N6VqJMc@hIpY&dgX84Qu*le3>u<@k| +Q<=lQe;LCy#pg#1cFFsckA$5FNlnI7^s$)uEMdQMoMF0=RDN{oi*fc)Z$?p-CR7 +-}RMhjZER_~tl1z%1yx#M9_&+7x*jeZwaV7+a?zhK-pabOZDsh+e9S^>4rZ>c7^X^vvCyGTf`QzVe+m +DVn_Sz%YT1VvafVRp>Cms|*r_fw +*{V0mS(5EXL^+if}B7<%Qz~hd@GEwBtV31_$T3MGTd8r=1#htXLf{#6z!v=lLNrqrUqKoo{oL34M&3h +9iM4`SrAdR@0syLUlfsP4JxmAas$tb`QBm$^t%M-X&vGZI1@MnKI(*m~K4b`Hy))hk=1OC-L}PWwAOFQjOyo2I=7`zp-p>-#Yc__SSV0NcxH-JZ3!w#^cNE( ++rWz9P +a|%ujvm*wprEz<&s<+v*BNkZM&WGXxhMYb}B%=CIV_tbu)9?$0jWv`y(dHN~t +W9{UoaPN04G>M|_$&P*WdRpq^XK(sXo&YHz{0>;9x%^5NyAzs<*TJttbNqsXf-J+XcS9EDO1&h|%QSV1Xf9Qhph|bntJJLkSs*{_FHr;is9yG@R4x^^uwP@!$$1|iZU$eRjaS!QoE6 +N~pAxRd-bWaG%up}-MoqLpsOowuChdRT^3%i14&W$tHro~rhrB*e0$HQ_w)V;KX +W52!p!qBzZ9wF@B&lFY0Bo>rG4bkHgc#i39x>zKL~}TEvPWO +S@}TW2hQzhAVOn(XAj>V6w~cNAq6tT0`=BUOTDJzE7(LgbL6gI6m=iJ{)AQ$iV**_MtDQZ)oE+Ps6h4 +LA_oBlw&i7yY$E-_1Jm9CB!-w`z;D0j5Ca1d*5eTOs4Bs|rcL1-+JfO4pO2VseSkK^QnD;JH5MKiy?3 +{N$Xj>FWY0A1krZ?ow$qGrNnlh!lLVzIFyy1pC=Pfte%&o^%}flIv&vCBCPtSq3-a4K)^W)+?%*KJua&&2T%NIhrA_i_Ok_oujM4oX=9`PWQ@%H^PS +?NfA-(Fi@H$rGH0`lbWj=VOU&wv({igVGqcswaF9lKWaG*HEz*5FtVp@0cZ(F&cR`gOgAnhbC{t+*3k +Jmr9z1ARMxfMk1jd$5^2?nd;F&ve2veGY#{LH_J|z7F0SC$s38Lxz5KgVD>aqobXJJsmG@h#PRgS*d3R==ecq8mC%%SNV&pnKsjn18XXh*v<1m2nlHq#2&=! +4o+#ZWC~tL3a9}(~+FPvn*g}JTS~`bN>-H|4GpGynGk$Q0$lS7}Egm;B8!V%myV{*!N*&-7&B@aHjD> +9@D-|G1Ep>T4Ey5fL3qG5@9cWEnul0sx_u*d@YLccwX`hdnBu|$*pALLD6l;lCX#7S_qb+ys9=<^kHK +gPyvBZizcfRItRomQ?eREpa=yVv&PNK?3=q5#81>rq0_JE;cH0)h9+s}2YSSpM_;j6H)L-~^f8N_asMo*4jw5(s9 +|ZpHW<}MQb}d}gX_()XjBImJ%Ay7E@pm&l%{18otV<2(=m7p$`{Kb+=JG6kQ@F|RZ8S1{u&f){g8WMc +^X|$w)dl>He~?bI43TdA#{Df)Pn_>{h>qhh2@SVS$YTb5v4b{VYBF^?WK9bjP^6h$?{k`AZSAf0;8~i +`9DNuo7i{MO7qbRV{2i$5MN8wLr5o$ +;XzTwZ(npa<|UPGa?-T7>6ow!aGIr6R-oF}+WGflCBc*`r$C%EDGz+NdEh`M?p@RkR>`c!Q^dNhF{nV +;aX*hhI!(Ymz+yLI2d$7uOQ!=l(aE!L!>M6j@=nDD`uya=P}oSk=-7E^GepN$# +3$Z(s81#P%OosZM>~5*@QnA6?reL@WfK=?8)EQ!V`Ke^yW8Ac+FbWz=h()k@A5)seN$n3=f_t+H_OCH!bd(!Tk5a^Nj5ca(KD;}G^` +hsqy}OVmlOIs+S1`nBo)fOlSgE8&BR(4G;u&u7TMas1%yb9OhA;w`@Qw`cM7Ec(0%pJeLR!d3>wg>iS +41t+6d-}y5>}g$BY%_pdVG`TpDBm1DvWFCzg!(uHLu6tB2u5gjhLDp-D-uS^N|w^XW|W`EE75%>XwVH +vc#z$HoXREamu8-LB_SaiR70Fn~n-r|Jzk>6M#X$QJ@8-JO#+V_`<3Mnk{poJ8DK2wqonf$))jS1aaf +c! +Jc4cm4Z*nhid2nHJb7^j8E^vA5eQkH!#?k2aoRj~6(kCTpQ;Z}hX{%Vim2ElEt!?>QO4B}yNCl8`&FO +fHtuWf|x5WZa7;Ws*e2BpO}C=~=0z0e%V?IA6;voL=5d-s>j)@@j?R`?czy+ts$w#~jmreekE6J% +icuN^$Y@-Q7Skl3$8&6Ql4eO2b>>&tov3vpm0DeDYMjJbl;!|1iPWp;Hl1G;i+NNg)x1nc2&M;xM%iL +4Jy9jHbec*#2!W7NA>2g;L&4tm5Rd5u|0XoP+2SHgtE(P@8K>C5#bOT6D||jma;yjg`n)KkD#pyi=mMZTwgy*XPpUbPL<;jUD@tnDA9P>!)!M+5z2hrPucc(|szk7A?E_(ajySK+DhtP=^0Qt?)o0spPp~KgQZ%%Pk&^A1d4*v?jqmyqBUcI +6Q559w8yrW(|KYsi7cSqlRdm4Rv{OZLaJbZQt{X2N}>X2J|^LJXBuMUo0_o5dEuMfUCq>9G?=pB`mK7 +Rl0Aw7a758(gLPmhk@Aby@7zd3yeKM6ml$L~&c?eC9H4tvqTyQ33C&dYbl&;nu?DjgFXsQTuR0U_qG? ++8TC5tPE=e0Oqadi3J(;1z&7!TR=u@+5!y$zTBMcrb|eqaQx~q}6JD`ibU5iZyLT&ofx}KtM=-(*)M! +xZ+hkNpmL3+;^)bcH0jlg?SnCqJ(9VCGS69#-ovj*u#ZHj1JfjE}k;ETx4+xNSFaH!$k>TqVIuunU4 +0NWw8K$khftmC(IqPBrfw$KZ)VNZB#9RmA(hYP%>U^EMkMxcs{zqhgK^vHP~Be;?qymUQ!k}=@{n=IO +jzfm&D9RGKur6cut+c-Xn4V(7aqAd%`hEo4_Ui2g$k8xm0^7MDD;Se^Ci$$8vqs0t*iq-dM +1bah(KOVyJQNLfMFzazPWM-|N9^~U;lw=8RQ9y>cGwFBjN9`wXvCrip7w8WL(E4ETbQl%DTH|>ENGGa +EJb(uI&;w@2z^AlcSH%UG&%Dy3TAXDJgh*; +Rk<7{>nSE|+nwF=d|0g_{laxO!^V1;A-4p;9+kD^wu1^@aFf7eo=uW&k{nm4cfat0#?!Q_fxwYHwZc0 +ld)i5N}@BmBDC)yq~$2KMyX0(WqGCp4IF+KaZHMrUn$)b2&?9{xjb@XPPo=L$B{D6B=qtrkm!

s%a +DQ6BG(fR<9A9Lx!SgN*(?rw{vcO~u<|m7DB!VTcnkbK_NhQJnfRl9(WCVTqxlPa8|7aW9>8oC;xhHCS +msE>vUIC4`yOe36a~O?)$>A0c>uW6MM+tkc8IEO=rF}b9GA|bFm0&)>oiOZ&^MKzBKc +vqU)`ScT2B+cOkbO6bxAAMIb6xh!dKf=uc)7!LRw>VIP>OmnktzKS-t^4Lk3QaMoSrZ?2G4d57 +#SD3b0en!-x|FX`Gs57DO;%yBaf3f$d>7fwQa`EgWNDr`sCg?vSO|2L0oN4uQ$|6>z!w1OhgWCqo0CP +zBS6&~ar0WEZ6lmFcxL;Ia&#&`B&wXph8W6q#XfM>r%lw|4tq^#9PZ9-pSugkL@0j$XsAv%9^!9c^## +eepQ@?({ho1t|dr%BAQyFK7Aa$!tCyUH*B2?6P0MERGUb#g|DxPv%dlc@YM9jeq(HtO3tyYG54f8&|1dg1P8X9t%c~N8v;$)GJ=(nnKTgEf>5=MaA5D3GO9sxa>!Zv@2P(S@-UM}~PNF +}nwV@oj8?I{S86b~B{*p(7IQjC(>JUXIRhh)sp}3+dzk5|6R#!L4RV>+2Y +ONioHMRZtVx+1cA!EN#JMXHJuR)@Rg0JDUM8_WavZBMfAG`fOgyC=2i?uzdVKz|txE#X?XZ!+WD4dyr +^?St-3|tMn;EE`WZ3G)DHp0h`T^!iP8(?{XA<-&R4W7_u@<#Eo6$4-~*DJpVJij7==s_gBjkX^UtuIo +L8J4(=Ch6s(Bw-u*8owsK<^k(;?e~47cOb5ceK>&ayr`lG^kOCH^5}HU_ybvNOK`@CcQH2 +4Qz;bFZ{pi^eqzRVvZs07M$j$)!n%f*~(5K}h>B($g6|in~qb!t1Nsd%U4FM~J{z)q=hPi_oCpSq}%y +8RfkaV&DF&-7b>KH__6A|bD#OnbFAA@;uozSs=lErjr;YNXhu;$>XsuoG~;H$@s33v?sOJJV!q*zoDN +nH9^h`aJcO9vu6py&?cIUt>m-jx9%a>zjGBBdh7B?|CH{`neF8HuQZAHRV;`Z3qQL&&1ltJ +e(l$UVQtHp&SC~4rGBF~o81y~}JXgz|VBcY)$a-u;PE05SXibT(fOUKS<18Lu1#>MA2u&~#k4O1+Goc2ONRhwi|yg2fk*$h?Zur@|sJ783_?d +07Fn2gnE}>$*n5G;~XMC(TDiDe6Ex>I)x;R4XWqM$p7UPSDaB20j=ku=KH0Jms$bQgwvXX3`s~K$8e- +32!WtpB9=^;${WU>CjX=zu?-%Wh-1j*6!wuZl8(Pue%Y|Hq>n~{S4~ +t?oek!StNK%AV*?6>ki?3p0X@Tg=d5iVJW=uJ9XR=sI#}{A#ijWdccI-gG9`T4z_jtT(&79iS)z8vek +5ugdUOS}@urv`<@Ae{7WfsVQv8d$4f=~1^qYD~y53~`Y>7n#z4-N;;` +YY+>_=oh(ZRss=t7NQNGP@xDdQ;GE{yIO%sfgA9@sDhIoR9eXjPHTyds%A-InXn}(;-RLP!h}0Q(;So +Ij~d)#gFR+^*Ys$UdHn{vq}aU743?yazghI?U{Kh(Gee2Rx0h6GR5!l{$jI|7QP$b%=}YVE(+w^b=UTHz}M~aADLQHY|8Po~Ro6BFkRjZg?u6$;Tp1BNuh;oxrVRVA|EM2nvw3pN7ql@N-L9Tag)#-SNwaH!4bnI6t@W}fS1T +zzJl3(C6iEHX8uyH$~eE|mu6wQAS-u~l7^00ghA3GPe;W#F<=)nmy&RgKTuakegkO^xPx9j%Zkx8;k8 +UJQ{bJa$@@8Qdv4)UarYF?P49sN$EtOx7F&~uZIR`H-wrKY&BJM%e;v4HWnL`ob22oP +)~4PU(-Bgq&&#ls8p-F>KWH1&9M3c-&XpHH@_Ap6xw`@^Ak>cnoFSN@v^W(f&T_;Xu~H5=uOJeek!z( +VJ7Q(4WTdtMs2Dh~Q-r&nUi~m;4mRbydO>_}4svk=lg@3zXO8fzrX@Wjo&?;;9<(1(G-jGkgo5=2eeR +N22Xm$n7Iv{l`ifjIg-wL3#O1YNGH`EvCIFOY&Y+6(w1u!Fo>cMxiGcmxQ(+DiQFab%__RLz^lETccu^L!t&}d0ana!-CUIDm=8*%k6FPRl@V{fM+B& +~9$;a8UA046I1rOX@UcrDD6Vb}VX~x$yrh2R(9_I8fs^$>kaNJE5B$-JCjV>N@1LBF?#qe#7ns&MR{T +U8p_`5=IbatbwLe^6l7*q&J>gTB6??+Gj4w}r56px1~T?>q;Lp{}?jaixu>JxTDtFq0dJ0658NowEJ=2S1D-ecfCX&&w8r>tyl +tVjM^Fy{P@e4~us6C<1Ar1FNbt2mW-nySKG--tEHQ^KKAn7G&63@(Rs-#0&&VbU%#K6j_3nt5`rT2ZU +9A92Yw0ccLC245Y=uKpWw}s-LhrDUVvfw2)^B%hDW+`%e6dbe>36sHy;7g{-R8uVz^~@7PPBPVlw<_2 +U^xpyLh@0UBp?tlsswPu*^f9Q9rtB1O~^rMaj%FO!^woYUnDxg%Z_bUI5KF&TLgx9se6*<6R%B~0lcs +YWrB#A{zQAYcWD^fdZ?FM868p7x>^n&yzL1uNQRXM*X&iha2`<@t}NXhTc3gs{AE8ilOK%s9<$#-LhG +FYw-MKWhK}>66wE?Y3y(3+q*<{d=U=3jS+<-Dbv3(+3Sjm-8r_ljTPg&x%UVP1l-lYzb*@fVKXa&N_D +6J*zQ$MsZxwI1Nww-!YK7>h^(_1{T8c^AUu1Uexr*)hJB|a9B!86ti#;_6s~K(~D0(LFpBB&)Yk@kH7 +fxt3UklPk>O7=HWU0c@(t{o;`nY_%gm2!SuDHJt-n>Kcccg4;K}lC|FYH{Am|DY~)?ia`%5M(!4X`+Y +7{u^i3DTU0{Uf$JR?@$J1r~1=3MiqUua=XNOZ_e3|UyZ5B{D9((s$Z!D;uRw&;{F>FzPt*B|N7@=Gcw +Qyer8p5mdEu{(8O}F$7+hEQMAaKw|_|yl6=$_YwU{Jyvz+k6?xCFT!Mxj~TpXtt-pAd<_*k^GENC*U( +xlKH%QiIN46h#Kbb&1;M8K=0FNS~z`U_`el^fe>u;yb*$Mxvv?SqqqKjS@X@u8UKRj7;6ilyGpxTY6o +t(oz8V8z75E2>^IuPM!_7iDm8+M-Q`N{!lE((BQr*qU$7?(LS%h=<)?`=2PqdGKWcA(KRWo+A7tTzQm +V6pibw5K?kk%deIE(17?cR{x)GphzV^!>8p)ZtkH*}Fp2Q5Ga*Q6KvY=6XVqAV32~-{Xy@gO1!`MFvS +if&9Qd~xjN1smu~j)x1PA98LB%6-LyZoXRQ^m;%+}i$Dv4$3`R$C5;ms0HKDO|blO{HU*%^_2Bm+dLm +@Jpl$a!|}Ix$<5M +xTx0fYiZx+WTybu3#Db?Y5%N>f?YKwV*7N2TREerC5@^z%OKvsldI-xmW-X5uUAxB<%nDt1_m!@6BhVl&cOU4XO1HvVD+*7;Rg+~#Z-!G~b`)|cIl*s7b_`dZG$n{ +8Gfw@ZE0_H^fcNg;@g-i=EMt%KSboP1yKJk-_311*$rE|ap=bsSmnWYwx4zul=LY1Lq=DofH(4k+4YtY)|~ib)^jkp<~ +%X-^*aZE}FQg9)D-w-ouHcgl$W6%brG8ZStvp_=cI8y_9xV5lA`^yo~?%gmX0qtG&kD)IFoM_=7n)}z +>pt*+BanvAF;`a+x)cxV<@h8Bjfs2QE&rG`HU%jA8CN-TpzrvnhM#(;eZi5~jsB3>mmI0{2# +%%^-SU<#D8=TMm3(6-WyF41i!#hbfyl9FDBF#)ZS7m4H5dW`3^+mv06mfYe(IUb4Cqw-8p4Ip2ul*`C +8USbzV<#L8tf=19#z3*z;ndfxzI8JtlX1@ZV4d`3htv6mzh?#wkQqA0k4cOp?6F%wlJHAqaQ(wvU5zq +&`Ywx&%f~8Lu$kvqz@yq6&+ybN2zEFe2sk(Aqm1sxp=wUJ{DjWiUOL +r-ZsARk<~~aT*nbe5E+;v}sKrfxJRiTJ-Bp@jM<~CG94NG#gsG-_#}#6*nS4^qnDL<$gRK%b2?%ta7< +*qI9c7X}WxWk98?GD;+d +t%OKF+G!Q0M&TUPP=u1kw!$o&0FKPJ*XFwAs!pl*4lIzgAY`x@#D%2Gt;6!aY_Si`DTT@xIS->kO11vZcF>MV$_m1gRWU6adn$oj~`|Ik!L1Kc5bPsJ4ttJ_r-}I7Ry+ +5w1wE(xWAD2eDue90V0QQeb4RV +>DMeh$1jd$o@lChltbHU)3g-|+Bhgb2Ak9IFgfNEW>y#`ahbmi2TSUQ@QaYq~`X>;n*Hj^6ajlTDdvu +bpGrhQCiCF9-N*!mciz7ZUsF6FPOzdjzc}io&MTHV)DJTmK>~q!GzSmoQw2&UhEL1X7cGS2cVNC84Pp@qLFft~P*7Eh@jI}TF#BjcSkO=3P}t7ry=+5()Y2DTb#i{l +xPj-)dj;!TS*(H#!ERxzM=DulML@3E*9qXDV!JLkip6^X=D)#_KPX8%cPqA)xi4khBnMll<@D;1zhTe +xp2KFwQPoT7N|NJ$TTr?pJtvPGxP&%Mhd`|~Jjsy_t@vE`O}~N^!d*2=Z|;Xws0|_UohH!O!9qPa=uh7 +n&i(ajFiO8hf8-7z)Pp~HXdC12wT_t$-Lp+ +`Y5&gi>$>usr51X2&pHX82vrHHdD!J>l|#!Ihnb*<}{B6n`wRvq6y3py7wHjDo;#!erK*)tedl75t^jtP8pgwLm21K>h`KRVQ2X?td12DWh)KR0pYTg_;R=LQU$bP6K2*#;DL}`W;l?CXjM +kbRf_=s1V`HmO){(IN%P#ZJbQ$$x)Oz0s5Xx}yyV-!*3ZssR-<*4=ynX~Cf(Xv5)kOT_?i{oq1J5yX_ +amdF{$Ue>7e8s5Tpr1!hpu@22JV;1?ERJjwhhzRt?xyG_t0Dbqj{Zo|{fJV1W%7I)6!);%tQMMZO;4V +TQhMJWYB#-uUS05=q*lQwMCD7@oUs32mKI^(5=lY6IG^1#O@1Gn2r;YKvY?vm2;Sk8# +4a$(nJZ`M682IPDU7&}y>t1xT**|{0Mx8F6kN)l%m-=xNY-Q!F-)T5a3;hOHba<#uh=x(wk$WtG48{aX+Rd`lNQ#tY1fu_PO_gsyMIMGJ!{X)2`o7nCFN+ug9$ZPb#0S@Jo@I*a +hlg+Lyu|YP#!RgGsHv_=EYE*AFo6)?%hwBz-mdC|iMf-3kz@CxvjyGvZKF0jec~z+Um>MB}w6!?m`ihz{)VpfVT3w +j!+j0%yhG^Gq`f>+UUyDT)l(45dIfjeIO7N`9G7gpg1oImJ1)F9yG(px2yZ7d>20m#@QY@eL9e^*mbh +3LU!v{aYe3+E}DB$OI2zz+OHEjx3R|1KG<=DQ13F|&@|2u$uifxNn4a+I#P$otFZ|n?k@tgy8%?!?;k +%{0I5M0Hg8(3mNt3g(NnBc)NjKv#*T+aKy2qr{K#F^zKxeV0q3edctVSUPCxG2^DSzC{cN3PJW@~?x) +*Fk9UYAHJsF>M0|PAY8HbxS`YT~}ed9>-!6omv97d?QNzth!u&o|lu~?loZy)#HOD6)$opx$kNP%^vy +4$ea*L7G;$FJID>07TDkysj$1xHpC*X+2;CbX=YA(umR%U6EDP6S7Hlrlo{#a*$B%!-*v7Z1_X68=2AzQu2i*6{C +07UQfrYSf6i2)a$~Hex|R7l+D@I~oqNxS9`#UCX%jgzaA|=;ZtgOJEFi&MGh*Vo%{ml}s_O(}+kBDq +m@biaom-@Ju;cOA_V~94vO&Xcn{KOFe34{cP8+XZAd2F_4hwc#uMVCazM|{hU{6CsLI9o<(7`j)O}TW +d*$_UUOdK@HVVfhyg3HA5fFK+#M60Y!_uEmYSE6KEZ+J$C~S?lmnDIyR#vg}O#v7niLgzhU+X+m`l@Z5cxXb)+CIeaVZ{*$10ScT8WfiV621x +D9cE-4pnZ>|Kyd0@zWRLu8zR~-6bS@g(QXqTBcZ?1#2SmaeZkqItKu0z38n_4`rwEjBod(Quw&C(F!v +?xC7dH(HCEQvEdSv?i0<}6qUT6yA)L=KdmMzO8&qa9Yr$tOj>+U^zbKV%E0k1Oe#EJ&QH#FEs9!5G&6 +Z8$wqxj@eu$>2CEK^Z%Lh4u*kp^SWhv6T6NRYIg|Sv){bc|R-K33`jnTN_^3%^ji)q^th~N@w3SuY;~ +%4HoL&DoiL&d;l3C~#zrh#d>N=bJc%6-_AIH~K)^4B+?{=A~Xw9WNlV?h4epQv#*GnEn3wljeL)0V8< +Q&p`njg=x2q|w9D*%C~mu#pTxRtlf2fdAkJF_N)nIFNdZdqiDc4XvZ;figCl<82oqParg9x&p$b&B@p +-I|$*L9?arY5!=StJIXXbWF2bNmn)lbVJV!w!FRUC-qfQ=VP)?UejRn%LACKU>rjYOaD>GD{hF=J1C{3#Cj +bP-OUmGkifw0abare{?c!p{K6nS_jWK-d^TQ_NPNRpR2HJQ>aE#PHRt&zGK5o;p}0o>5(VWFipDA29d_IBF!OA(ucMTLd??(jBKPFfujOo>jn{|L!sa^0IhrDtu=Vnz ++!kGE`2S7Y{~Y5z@%K8F9_BrH!lmL{rI1*sBM#u_H$CYuA>6ArO2;M3A=OV`QB-dJZuyMnt!#Sb!O~lwQl2Rd=z +oztq>U&7M5B*-BYDU_;EG*lD{*8RID$IfgT%>vami2Yi5XbjHy*9eJx?;(+~FdExD3{P>(M6)SIcEpL +7;)9g`36!B{M-zUd!VCj`99C`m~;(@FIuO~yb^Dm+5y{Pqg2Q~Xh%kTrSia%*B;P2hmFAWR0Za7F!(Q +bUAqp*6$Vj)+FS_NfcW*W?=QJe9^$ey+#@>iI$8^Dg<;kqc4Fw?h9V)li<{d}nJWy^ajptj8om6x33#+qh +we;c3@%i*i)els)lU5C~I89k@cI(suarL*?uSBLE7o&N6X}QggajGgzKF0|s_|7Sx@3&gLh=WDJrdRV +*c97b6fe{!fX<2u*YzZCdN0{~w!|RIRGG@gb+fAW-j?*B)K#8S=jn^9blA|?D>xbNWR6jMCfvagHtpH +lgQ)fEr;NNY&!C0j~a1{hr(L8xSe}y4Qj|j=M;nSB#Q40p*hF8FJt?>*MsEfIjqpZ_1D-y7Y194SpY1 +@*CF@~PMfY|`G?wdK*`&TQG$wOB=0CQS%XvPqVL~bjhz?r&SbTxOL*ks?JPGM-y@~GiO$_4yzofXl{0 +DY{i@F(W}w2n>>UprWnNRf1nnTaNTE(I7?QHP>Pu16#Qv)y3&1Z=^K0u+gxks4=ko +j$Fo6IZHQ*OwJz{?ZgBOuil<5#UatvG^T;QcgvR*aVt!pzP+##$f%5K> +;-?8K)9<}2JXv6U6nVKsk;*qN;-pSvxcuV;Cm*Lj{nmT+6vK|*~`g^6bG3uTaAEiPtQNMfYhz)WH)FVzfcrHyY@>d{7K6m%pW-&6Wrhh--A6SMs>3N#WQCWZb_kGhI+-LSq +T1o(IrcgyN{HutyE{)u3$zBZg8$u%(xa;K0k$3KRE@14#`wQ2{SW@n*XqG>xk?5c;9b>MdA2+42W`}h +=q7uSPW|SA!Pub#$z9i>MC+Xz1QF+U-kJpVi;;rP!+yzQbQ0072?aJ<*#h&CZ^4G6sEfK&H5f +)(N&u6?nXO1J6pTETaV!rJ=opddFJ=hR;e?*0FCp}o^;!af-yzsUYo5MI1HYt)?}ZI8SM6=-Q5~#z*D +QE%_Kmzcb+wC1)2p$*XPnbNRi9Fb9D16!$bdY=N}1DvaxHHA>n1;;b0ruEZcm<>2F8f)`sng>1gHL=a +Octu`KQ)oq$tmUpsDfF(K*>87HIB+tyA~yoyFp;1vk6q!r;SMastc|GRS9iTSpnH^eGkv|CmJ!DD*QFn5AlabQwy={uA)3(A7hZu?QIZ~--(yH{x_l_>`22<~*SPuE@a#Bg+gMi1KwNwL +1Vuu1gi+uT)A8O#vh_+B=FGL%nkEBdV3i|9^WNvEB5MA;^oZ*);X)Bvm&b!O8jPf)Q*hzxY}hUr}xp6 +VtsJ^aUGdiD>^{P+(A3RxkatU1Pb-C`QNX#%(gm<5O~UTSb8RpHWhuetGY%(A6N@}2+t`p>jh^Uue{sOrxsKtsPMFF$|$d6r(3ak>1QOZKnk)9mSR5OK~F>vBj7-(yaI4zjoM^?Xy +1~n%Ef}q(l8X_3MFC8GE^*Y%W(YmVH#SLIWPH5aKbr8^)|HN|?B@e^P}x!phv<&jd=TC)yp9Ka1KI^=D^oOa=@vE-(mbdxSs6_z!dy&07Fv^=pv!4!&$MigKvMmFw#( +(1d12zZ)?d;RTnDuCoNQ0L+U~k=a_)%VdseE|V33y4*XZ0O#>f@@y&_Vo{Egq!tg?Q=#CBY`XWT-Hop +(Ka+~aPw$G6BgoE+1GtOLxsEE{xX +*o?WHZ>`EST&qXQH7yR$UxwB@)5`rj?X8wY?8pmB3VGl1WO-xbmzYFo2A$Pio`iwjG7VKqM#jo>3P$e +NxFX8I`sTf#-XJ2ObK!g?3&c%cpQuUefwuY41eGzB9(ctWnV!h_l`xdWLPlJa7~T*E7)SaE$BaJbbmb +-li%9)QU0fiCeIgfXq5)=T=)auaV|x7_OS(H3-=aajeB<2-@l!0>2TuzOEgyo3zaWqjPq1uIlPA`!-2 +9r}jYj^SV}7Ap*WC(8RZE3tSAEUN7d@k_@?DCYKoIs&v9o6SCQ^EQ>mv&wgHN{8$mh*jae4l5t~xt~A +#XmM~@KA}6gcusF;}N~@5Q>SFRJjMybsNT=vqj=_8sEF3joPwXgB&q|If;gghI`9*RS-=sw;+1HZyGY +WGaU#Kj+PIfT@c9Bdlw=HF@Qo$}1IN0#fP_!RKCkd~caLOD@(i*bQ41lEacx{A60qD_L~mC|!6Wn_-DhnrTcBgHt*Gjka9*H8ZmSou>%XQ!&KtiG724V4KY|o +3TBP>@N+eQ*p5GHKZcdCF5F$fV)c6sUDD$ky@ncDm!MRZ-K8_X*F)BF;=%Y^)?a~sS{cU<$NlhH!BWI +s$v>>rig!K9`lx6oBWAiH7Meu +AX>BDq*xnt&TB(F7uwZtw*zpTat=*JKpY()TS?hRilO(u(y;X06rvY~1^7%uaeXCs3_*5&fC>Otdpjc +Nb*(AMP+MJMMKp*1e|~36(nr9s-bIXb@XZ=`lXUn$As<+Z!#itwA8?)OLGdl2?+iuZl6Heej>)@)4qJ1@)^6g+IMJBQbjUlXYHz&_2zq2x1$V|p%7_pOokbFLfKposH0BqBk +85&Ep}&0-fP4W)SiLll)Al2vjkSFD<#5<@VSCVB5EUU_Ke`~od-mCe*kW>t2%Z{-$`AkP_;Z^f&kKqL +f*cbDAZ|~)*DU|*fy?-AW(+*C>SITGwC`8iDncsDNNa9On@OVC?Ha01;gD!`W}@v(U3X{%u8j)!m7czdelJ9RBT;o&%^C3NXHSe(>s5bb1`U{O-;3=;+PK5j67syMwoH58w5y?!3 +m9o*hQ7kKyOh;fvqFhruBG9FSnVA6%}xT|*hOcz`&d;scr!v@+EYD3FKw1?S2~rVEbFx^Y(xZPJAQ5BWq{-ELCWh4Vg+G49Dr);Eh)ZoK}2p3RC-So7c*j)0 +doGo1B+T|q}mB>VGz7rV?>X(e}r&f)$Dp0T6S0H3vuTZkv{ip<()RK{FV8T2cU%I-0=_D*yx}AE)@`H +ucn_D3{F@&TKwj{<_WP?87|aZ+KEHwvbw>Davq#1;G+CzOq;m9Vw;D)9^*6fbf6!Q+ykzNjFCzVx7 +-o~I#s$(4PQ|@Yk1s~p)(`pRRT}+xowX?KCj~i#S9EusOz;o9$3IXc6o7>DshS +?11}QD;anw#;F*9$yLKh7CIS~h4(yhKf=M%m@SV$W5i@L-*zv#~|2B^dJMOQl9=(n{azd7Xul7Uw-Y$JFbNg +Tgs&@nR+I#q_lu2*Cdr^3dF2BgH^WqjZcv?_&@L{7dQx2p$Sku+wg3yJVM7jK2w&TSNM +LVwpFsttpa%OW|r9JAZv=qedqD@8|^Oy*l<0w+x5h#oLZF@K8JRJFwH>~V0J!_IN|eny=Mk^swg;p!D +`anp?K0>dR>FeY2Dj8_c>SPa@}Oz~KrXbrfd#IY#_Pt;6R@%RR>`Q-pjajCs#K?a#tmtL3#?hxA0C?k +(rf2MJH?OK%JzRl_4(FPxl$S?;QUG$Jq)@U(Zpf$=(f|nVX)F;b=DCA{C={Pvbi20Y;(+ETcIM&%(`> +ICoVKT>c^R`SF9knsVH${pDrein>#wbTB9VIa7xB4d8h4z9j1WwDPFD`BtndUWzFIFLGNW~hw(Zb?&(OyJx +wBQ>$e)@ip4C^muKNMp@KYFqQP=G}bqn+*DFK~y{UcRlr__CqEef=>_1Y3Gt--P`r<9c{Mn{Mc@o_PA +@U!aZc=&LWb{}Szf`PJ*zhJI^b?(A;&uxc;*>dP-5e+7Sczxv}BkH7rl%dZ^y{P3qmdJ|_zM=A%>aEP +~Yf)CC|LQ*?F$48CRZ{hQvP#IiBqZuwg>|oz4Ip?*vh5Ng@69yWKyX@ZI0i5E>w=KAFc|r<;cDBg1+h +a|R0T4EesVwjRdNgX+GnGXQ*X!Rn@AoKBx_#$~M@6&U0E@5UODI5bu{S+1SgYSnNcHTw7B;OYafd2j( +)W9v?Va7n{eHh2bugJ1AS~MM_V^-vZ-;Ka_xw!NF0UcPw1oazAJ_rKz&2~V9>HIPJ=ZJT+j4bdEs5dV +cZ;WNxEfy$2K^*Q%e4W`&Y;LinKB+&u@^$Nw{$>;3SJ!WP*7zk`p!s+7Cmu#_k%3s+yAOABd?^gGNEm +0#KDxEIIvDoOw>rF7mK+ye7OqLR}W4wFv~;Z&GG5s9+R_9iWxn`_4rW1&|6t}bCsBHU)%d1Ktt#0LU~ +drK=>)Cm*QNB>oIDf(vjL4dz=c2GJ{&P$duHRIQ5Kc$V$64f_BGaCt|FBgXn!vj)B +~7=0w>KA!n7|;6klAF$xTY}(NF;#zqiza!n<2osYOgZ=N?!L-=hd!)@8cndwmYAS=W!@BbIue++y*j90Ovs3(RU< +AtiTO4{MpqEyj4ZSG(kkZYyrTqOwk1-BEl&fg(tk$EJUM((w(4x&yFg;XR#TD2WU14m~*m&kG0IIIZ +w)Q@}%*G!508s{u7G<5JN|X6{i)VTxb?;d+)_<3i4J5^qPQEa%<>uN=c;$h!Q+q*I{}<>IH;AY|S22o14^4VbTEI_M;@E6ueiM5xlZQMg!JLR6P;1Yw9yXO|3))BA1d&Q< +<xlM6F0jZ3Og#A=hC~fH!W)Gpp``#$YeKD<6BJtP`cdIN>$qEK#xMERZY)1;)6VmS3y +N5XuaD-esthsqZm2+(^DXo2m*ou7hmYrIi_L +SX*0WSW^9c`n%BQ!~G1b6Qk?l!VLDl++65$f4(mCxd$hMJF5BC$-=&<>;L{0ukdWJoPW-tS$4g=p&Xz +n#G%)}YkjtE>~~dhems^QL?-jOssg+%8?y~SUfj}vSRfJ-y(^yanV@!OSH3*nX}TaJo1ALi0X|N#;?7 +8)@7q`fF52usS7G^vgu5*3s7)H$;3z(mTy~D2TVpM0x5HDXxcgotdgu3b#Z6a&m|fDcRPLEk`?DRqJ& ++vclvCJw*AlUQy$;7yuzrw$g()I!>B_|kvsu|#+9pAyQ>yRqPIo1^N&zgFFi#<#t?J-;a@cfEsp0+62 +O&O)^uR;(qrI1@cwXcjF=Ug8K=jAaMNze(JN>~)?jqy{JQ`R90LuJ*H$s&*GaMKseG2T-ZyD)|HK`>t@YzC+Hzi<_ +tW(e5_hFA2`~TjU9)0*WA{ib}9N0=TKg!?iX+-MVNR(9K0Gl^La9z(ftY# +F%G^4OhRCrSsWs_7~TTn;;QpkfRQ3Irr+OIGp +UeqDxeY_%r&TH+g^<cY>Pwnr+xj0goRzH3MNwke4JT2&%b|4hXr;Ci! +945BtOV%*AwyYdR5~pR4a&75KAx{H6#QvbmdJLxYGu=OG_?R+-dj6Uk +B@2$a#=&buuSo0pM@&&_I^Zfg&+3(H^RK`Z&adu7RstmB&~U6jJ*(6yFvrvcJPxq#{O-Ko$vM)mQ8Xf ++|s|GbfYctf=OV6V+q&o1cP4srS@7<8$%v=+CpVn4~Ie9IaLA^_${(Ji2OV<%4b{PT&2#7rj0>ef}-% +RR>2HZW#?}dXeSLXe=i7C8`-cAqj&MvPgqI(OB-~rO%pbzMKDM>z+v5@FnL(FKTi4fR>Dof(Ar)h#F8 +kT-Pk}&Hx!9*Qjto|9-?X%E3=Vz^92bB__z5ZN1mTEXu@M!I~|sV_hpexHLps9sXNZ7*jY1D8glfBMR +7fVu(Ek{?uFWcXB2(D@dCxnH6jhz)kiXq6~o(7H#^wqUq3)#>Tllww`SS6Ql}iy?H|JX`H@m6RO~$Q$ +c@2v-IlmCs>|qo^xkiAO~r3E^H`m4ap)lwfI-J-N;RxHchF!wB`8?hV^uJTF-Y_%ME1HyoT4W-+NfQ& +Fl6a*X*Y2^?uiC1HwOITn3m|UyBYQp~0;QBEzYwY~MI`)ZL+PL!!gcdCm|$wrJn4*^{_7FE!01l6Xgt +O7HH@;WrSNPs;-zL_|iz;P>OBJa#quCyjhT<9X$2z@tlKwA9A|AJhj>Vog_|gqlf1v#rGA#bm=~X+u{ +fPZFqov{}PkKMl+?qiL}bxzuA2>Y)3pW1r3E1>W);^T>lI;obS6L*o-_8$ +?XeEm{YpUwAv{Fwc}w*4c?VSEdR?nEXC1N&gH06+IA8sI13>fgY}KbWuJj(WD^N*gA+Ds8a6v)bJ3=Cq>tS0k_sznbO~Em150@$o +mzd#JJ!ho-Jz|5Vg0`oAl+6Dx>{hroi(DYLV+*9nlX;zoT|UU^l~3OPJ*q`)O0S9;dI=7_AD0Gkn_${ +05|8gV;w8MVt4$(cliY>V3RTH!Q3bsYV?j1wBLylR*c-Z@_Nv~bfE4)mGORZYn{6HIcF;sL|-R05!f0 +N!J;c8DA#jZ2*RywxIMmK2!SUEXHW)16H%!UG_sltqD8~#bD3agsK7ucQ*0h37734u(0oaOmoPAW)n^ +-9G+ODvr2Mq;qyIefs}%y{oZru;lfE)|K!F#?`>oLKLAPTv-jZLuzv;`KIu%N07^uXQ +o9h*7?~m?vk>Jg7^a8WfMqmRDkc~#Q<=r6%+oQ&p)rUz2O>VM- +Ph&$w@0VF==IUbiQJl-H=MLw-Iv!|v7v^qJqT-V^o066HXLbIRC6n^+t9wnv$@~(lRTFsw?r&zbM*jA +S=Dc%t}{ehQ(Vesxl8reI(aKgh&wqe;>SLXKZ~ssEGgcaHj4*>Y&*1#Wh&OA`BTZzb=a>}+`iJ-??JP +D9kjCZ5Pp$C_2$}68dviTbG^Y1VV1(~^0%FHv6pRZm3NH52Heo_dYXXM)I<_qQYUP*XVlw`yBg_F8e{ +}}0HJ{^zNg(o9eP_6-_YjBR_9jWpuXF$N7&vuacK-HgFE-y3ig=j +x$$u*B-5y%oEc6Ptl7J1N&#m`{=TaF+O?KrA^5)wA3z-yne<^Fs^DANi>PavMnX=K?s;z`7}OBu<41E(a!_%TZ(kI!5l}so8a5wmG-;OOM$usU0fc}gQTdkkTaiC~@W}RlgvmtT$E}Exifh| ++y$Q_9;wBw?HnS0%|1&(A4-Ds~{rQLnxZ$r6uInI5E`GTOkAtm`6eyL5Z=rO5UvoYx7l_or!X7xNnoK +5m^tr+5l&Aq?A8)59=2WvJ8N&vS#=GQ;4v0>xxp-0{jYu3i-^Y$0I{>dxe5n9mTiiT4VZK!Hi2!zm`- +MV~w(zZV2T96wATEoAq_9$f1eQ_{K%W-Wl^b$9wNO7v$N{E@wcX1*84){%aohol27~8Yk@v!hsT`g#^ +i0fQgcnJrF>=u#3z>=ZpqXZ^tq9L&(E)|s8r1*0+X$IxAa7v6|NkDX0f8~2J^j4y) +oHE))!iS%?x>^lj24(L-2x_lH{D-!uJ#oBQmX6Z7oSZ8TNd@~lI{6b%Eb5N_JsXLD{&6BGVSZK?lxdg!-#MjW<=^w$QN<(_ +g@ZXcJXaiSKxYZ`hrPfsto2=wHbX2o9D@7>gV4GZ@-`oXbVVV#oov1VZy?&DNc;kk4O0x^PufKr;|0( +t>OhxXP!U8n$19+gzK7|@ETg0$Czes+bd$sfy!HP{+M>X5+~2Rt;S?S6vlJu^qSFIEL*QKNs*{+l +o|=pZ56&*0&fxDRNc>M}#%65=1s^4(=vopZ98_0P8t5^U->l{B(ZbBz4#m{- +aWn;T*hm0FeH8jcatOvPZFwbqA|V)c#y!4$NIB!95p`KS;UG(tvJHC_)WfLcDgc~vt7zpG=4Fu}*3CJ +sE%f!Bx|%S&K>4+TO(y!0V~W;nOfs(-0#&fnG)Rim_`M1hICy>Vx53eyQ;>pS?y`8cMtVHK{j0{5X{C +COtQeCZI*dsMBfnOT&ZL4q8IWFhQAus;MZ!VWrJkUEfy6qcpC@^xkf;L&@Nj4bYHbaaD-^`k8KH;*{m +}6&YUgd$bqc;hxuwwCbz;L5JaI&2%FKtcs6_gbmzbtpgHi{4mT0__`5PVmg>(xva(}$s8&w^?NIZ6tx +a;wfjLgv-7-_gJ+p;fqNRZP6Bru9-g$#1DXhRijv6OkBRb}HJCp){3vuga~kJppOUwjEqu78~T@ehB> +u79j1<3IiJ4=uMchuvlnexg3gP-)Q&<-)}c!_E}bw-*9~3Hq8~GwrDTkmul0)c!~NVF%!C(Swk}xL${ +QgMk}13GclLHryKjz)t{3m>d1$AA$1fBaMb%`Y_g&;(o6y^?^Y3hmo;bF>!ZkJ&_@}xw9#A*`XM{q8Z$41b +f-D;Mx1RhV)fzn){X%`D^Tr2T#d&K0Bz8%HBg#K=QnZvKT=#bLO4{(E0nH+lK2W8?Hp52ayT3v4ou52 +LlR*nxT+DKfy7uw(*aV2(UE*Pjjg=vc4}anme^6J@O=yFy@o??2t#OG$LmsRB}aV&6N6H}3z2kXbLk0 +p&yM-3`brvETbqXwxmi=?qf*p~vE&?AM-h^?Srdv!6%ulpC0dF`CQuV7R!6}_6iSwz3U$RGM_QM-ba@ +-uVK;5XvLG(p)6!C8vx=DDs*)cVzw>si!ikBH>%U$ZU`%3v_2=n4OFH#9`7GB05Pu*o3P;IL +gU4jnfL1tf;@7#^WTq%!(4@-$s}go2|a+Ua(qFu;OJ=UiUXlg7XI2JgO)p(f8cu@gzEiHeZ-FUt^o!s +5Vcy&1Xx#t~h}sLwBpXE +DUKL6+jh86RLXPb@)^q&bnv7x8?7=oY*DhO4yKJomKWI%8R2Q^ES9Dj4SIYIFTwa{$?#0$pNq@--qyMb~?irM +iFNt5mf&il{+4GmF&}qW$x3PVEqW_omGwl&n+dZI~|?&7o%LJkEfdLzgg$DJgkG3kH(K0yAKbIGZ&J3 +_WNd%Zghl4!hkyld{l3dGV=T)C(X6Ee)szXHV}_18ATxzX?j?cCCQ)9m;57sydXq^603fWMTUEt0W%V +zrQKqM0mu}Uk}lhqQtA*a2_2}J1(Z3E??QFIbCb2Sgkne3CLvnE?hDe<4VjDhpp>e0`&nQ{9kcU8_6D +LXKie&ecp@C+SnF#6ulGLR`rZF(`F9V@AE^*l>oWhML +XCkQhMgP!E(33zbRJw_a@eI1BER=QKCwE|B`-+Zy;hy!Lj$jSvlo}7KkxgKEiwf0=fnW{5;wqs=o%ou +AK|CI7KT49v8kZ%;;v&|n50sB0G?Ai}x~26DN`gDc$HP8m2}rFJC$t+oeA>X5Od1s@=CNeKSDYb!SfO +|$D`zv>zAO`Rld}~T(CbzY^Jn@Gf9Ku*T_qq?;oaf4_@#&Cv$0=DVtQP05su_RtvW_C^m;RzcN$~s9D +0Q0+{1t94SxBZo1$bm?DzY?WZ3SI0!M2+8^mMrGNN{(|AA}d{ +D)}bD(8+^zrfd|&@d4ZQ>vrvIOC|>vWl^xYx+2>ipOauHhZ!6JgfsBgj1?qWK_BAveTh5Z`vnt@nhjD +B1t@LPRY@ZX#Yx+Gm+G9~HOKy^Ef43XNshbmC9s+CYoLMZ>T)Hz)E$@k`k~b_mOYYKGGbvSzi#t=&mZqd$aCa878VZVr(eRt8=eK#jpLs@{z%&E$`vZzuasp^4> +q+W@$3^aE?(dU}{6+`&x!$(|>m4MFJJ0z;YrV&)5Ul7TRVV0%0*~Fy-L~{dB_2iAACM?fw${-WNVKFh +;vnycD3r3&F^+b(bcP)TbH12iis1@Q&>4H142S=kra7ILenC-H!C=RVGPzt}K`X!mUI!x-OM=p!kfRa ++MI7Vt8ll+au_eQL*vUn4f;YFN{j|&0ltxnZC!cyv#dAQ*eASS6uoP%|<=73Jos~e2^TdY{lH&u&&>2%%8HLU)TK +kq-lx$S6tQM-+)3lc9Gl{2POZLO6mgqj*a+32sY>Wh)P8U;4Cxo>Olwveml;lTJ^~Ey?RTNepQprRk* +1NZ5YP+cUodC-FH&5Q(B|OECk$AkoJO2Ho@LgKJT{;TUle4{@bIixsQ7!bEczy>P(c9YD&fYnE=h1Y# +XM4NnYvsta&gp@32*a*=sA*F_P7YULD-X;s*^ugFJXm8H5C$VGRx7R@{2q^3_Ar=%Y2A3{r +`Uw%XgB^B{HNXB~wGCe|vnwQmF1=u)DVm~_PAk8G^VsO<}8oy= +qWVx{%H|sxCe5a0jspvyggW0G!7Zn*W?`rzm45NPO{x?`Ixp^3xwU9#@C5Tu}StW!ALPE$45`su6BXb +kuy;pHpmn^A?JtnY`}qC=aUwO!`=6$V8uw}d=u)wg~|^e1c22#CX7a7b*TGcZ8b3tYGXq<;S?@Y*Q~{&*+mm8T~s%CsD`)D~_-Dat#azP6>g^HFA%z_~S_6DPMD&~L=0A=A7&ku$R5$NGV$T1uig6s5zs>8fKCj2+>IHpIv-{hM{*Rk^#?3zhD;lQf&U60zRV +x~u(|?=UUwvi~&1)`d?`MX8Th;$@bI-W>ePC6mWM&k|VzOqw|GjEOzbWPi`LbzVbw{y~YnmZ_f~gDi= +mBdIQq>n-5)}#P4_l^VH0{2OM@x0rQ(*}De4fUcWhXxvTqbi&sz6ok28d|p2|**nNvv&Kruw>__Gday +nGiXFxX6+=OtPG=B-So`L%SNXDYb{II}?tA@8FJo0ng6q=4zb0M>`y7HJ*n|nY?fy7*HEp@_eJ!UBy{ +`zT%Q458iK_O?%PGYMp8AZpO6NL#wHC`w|MC)u8!{J0k0ZjkqX0d*#rLFOq>wl%uYiQ-PzQD- +|E#%7mM8iKx6wC(Avt5aof*AP>shDvK(LPF!B;X2Dz2kPsr;SfktBXPyGR`|xrKn+|ydHpr_O~xvX7N +yNO#9mW5SH$Hdu48t~@$PKvP6O^vM_DSM^C5L2yu+IhEaI#3fbU3R2=z#u1~j+w;90ODKcfWnNRnG%U +W?t;dti}g%V-Ce@va2oGZFU1W)R4w^RMk+{2JM}TV&OTHXyw_U~k19pxAa@bWuf<+RR^7NtHyb*SG&o@x?p$)&@JaC8Er~9?W&{4QSj27&ZqCL!#`8$( +|aM)9^C(%ChAj_0Jx0vVq@ibvEVl84T?1I4FS?Jf1N`%Aq!!{HcUBzWQnu~2{UTa}ERB@V20~~%C8R> +9{+Pud0vH4<}&gZzCIUhu&V=StXV*K^trtW8R$rq6{Zb+EFltQ8Id(n;@gmzlkK{6bqQu$(9p_TPkt6 +s7yMpZJ(3qNaSLrW__hhf#i_8_8~U1%IdvDa6uy5=s-s_jO;C#M-;4k(?h7Cuj;ltd~h7IWn8g)sH-k +>YDa*JMF=Oytk4Tp^Tuk^w);7U&E}jx;l4r_9YX*&!a!rfz0!^D +P7IP&Zprap42vq^pWrnd;p?53YHmXqgO(>9uZ?z<2)S8A%Z7DoGa +`}pz0sgU)SZI$`Cy26edS;A?k(lf*b$dF1y*-oglHR>CTK-ja7?C7@C-)v<`<9SpkJCay^StfE8*~Br +x|P5!#Msa{DWuW>V1)SxhN@Zw7h`dMFHdQq)p5q7d@O0;EqjLT{u43qLjTLWBom~qDH0OA)P0LsIY4NQB7Tj +08(0dC3w)hx>NO|tm1magnqo17C0rH^e|Ve~00ZIRd@i&1xs-GqWTOW^bX;~n@SxJp&CZ$xdY8 +vG1Zf?-Xe2DJBcq^-hh2MyBF<`$Olbap%T``p(vQ40rQcr&)>#wl;b7o%ur_i^-o`OoN&f7*6UotnHa +U$c+D!UkqS!q9iyot@rJH&7H0X}Yj**pVMdy#eTB0Q!Xo)C4&owa|D^M^HD_JDBzdcKzs!ZZGO!P|&> +{++MT2FGF3dH~a@^_>Y^729USmXm)lGN4|#sgT@23y#eh!-fToWUt;55eN^LL)*AoHA(>Ni5UZ|=+nm +FZ#gr&wycn?`ge^ofyHWWZwxSV;g~il+%+X6MMy0|!ONawYTVZc1!+!L(6rokpb~*6^oai^h;q4gsh! +N0w(oc^)LBSRy_z9N9qwZ6M;V1di&J)DW)7}$6&eLwo)|R3>KH{X0Sjtr(19m(n{-2DiUKHyFl5q<)t +J18h&G}GJL(=l6&S}lB*N+a+A0#g%w~SnbSg0cn58Lp&lW9}sbh)OJ +gF+m|%hp}hTt11blyiw-;-33q +~96WpeqTlc1gspHf#=Rya7x}_WqZF`eTi#08C}2QOt?iZq6sZCQFpxZ!phl6&k=y+%TotC_45>?zpU1wKF6lDD# +dcqA~NNCdN7*Bh|{W2Ple(qn5;XoGWZgd)X$$&mq@+gp1Ife9NZV=DJ7!8S2W(lzvRt-FL|jXZxHKv5 +i~L>)r6bG`3{GusF_^i@y+kEYu?xo#)a#ZMS_*l{C@U(XQ83Bdg2M^~TSN>=#9<424lFb8zeSV!pA$`LFi3uCHpNL +Nd{u<20^fz{!ey`xE?~`S44b+nd|yC+5Cpo?enw};6`gDs?d9p04_!*iK4&8*Pu3c?;}o(t#r}TeYG4 +=!9Qm%Ax43j1GE6B~&5G@HwXc1goa!InZ#5GPWDaw8;-?*4c`oN9^|TfJv`BHSB%F7YxG{%qiQ-Gtxu +U%mzEvvS=$k@nQUpqiw$eN!^u*4b|ep*%-O=mf$QhL#~ +cDA=0@9*lnLTx)>Y0aRnYtc$zZ?I_5;~-6&MtFhOmMtLb6!B!yZsn1!){F1AAGAaIFHB+gVlkabX;NI +sg`_;1#AyaH7q!Ha4iqaYj>1hsT@WtN=4y#=omhm%Vr9yvJ=UthlsIrILn29Hn-H-~&(5c*LNBoaWof +sBW5OIl|A$sA9kb$U8QsQ9$9xSC91L)!SF;MXR1tBi9&Wty@Yrqv;h=1ZH(FkFco9K7K_V1-NcnGCk5 +(h<62NT(sEnKN*hZR<(-9}q)_IoHjg7J~!Zv(L;^BSGJtg;Oqm|TM09xbgW~YOSvKz55#+N!rcg6Eyh +q`q#o?fXUf7Jt2OU6!FFt26Pyy&1t^^H&ZhIZ$~qFHeyuLv;>t(kaFP>q9XR=a2U4*+6seX +CWOc|+z7&i(gIA#mN-*c*3-?WH5P>mZk*zGfR!1oW$vPT)I#dEQv~h#8L5LvOJ)a6H#U0rOR?Di-Q|Z +R2ki5WqFtCo`uBUL;RUuE9oylu!YDr&6`M_yvMKxyUeedAul9(TMZaB0_hqXE+?DTZVQMCr--q>PSxC +Dqf*V%Bp(rDjc2j?Ic1KG&+8DxOVyU6?e+Ou_0AMPHep)o-EdS7g}+o7}3B1bKq7-L+9{N_PG2vyX6- +HjEu8fs3mB9E6t)1QaW57-@{%T89Y%L@aMlc3DAS1li +OCp}%weDvoFb~Ai*cb#S;=e)_r)fp!R5-no@H?=HKzcv7^C_O;sXY{NFS!Sc&;7W1~lA@ +RE@8!YKt8iLka})H~zXG4ah)}vW9XpV%f>p-1IyDb_cyT&+_6(93XebFNQf!y){tDw)I}Z&XNSifcET-&} +NI}_Kpcvj+q%%3q_DlNJ>2fG=8k1`zmv*L&Lrf_&ciEKjDRyD9Hm#OM0~*841o~+XGR3_a*9 +H%X(tQN|hqzbJ{HiU9%nlF-Cx0->5DoDy9#1 +v-8g+H89$XFGckygqd0f_WGNL>Cr;wPN-)3~|h$t~;r;_{jUnWJ@erk+Cy2U(_yH2?>%)2WX&w+cufwO^fe&S4v-tF_=0T}vCQpl<-#g3 +9<^-RB^wuV|tHJgO!4m2Ps?2#E4caBkx2 +u%Y8`yiOnw-`j)^a{WFfm(glVePbx@89fEM$T2Xiq_lHm?y35c8^>;2DJVr694jwEj>FHeRn&rXuCG$ +@;9*Dns@b$@SjSk?W+b*kT$<`l2E+oK5Mtn8< +y8N$pXoJ)MwtG6*Du2RxOyX>MUNut?q1~YiC;+t9Nz()Zq4|FD9c(1GAi;nIZr@YAfZ*6BL`!@UCOg2 +FYkoJq?7g0J|KDBs}4r+Tlu1R6~z`Zmz1))~$u*w>lE+M?)C@m4=1xQZe<2%U}!=A@+xHdFgERY(%3s +yG~1mddH{)AtO<~I777a>QLFMO;J`xD1Lg1X*I))wAE@IT%ay)G;i>_L|X`Y(WB!j8~mZc?7Wz!BJXL +xPRlP#pf@nSI)|f9)>vB=-z2EA5WZ-SiWwSJvqzIhXhOZ@T%vLv6lb^bvf^&up=;FATCOV`vqhzS`^R +a#YGDjGIfK2m@jjS2*n}STP>tmL@=cI0;0p|(EfVt~o*v +M#dts_lBm$g@cK1=7#+52!mSijM@Ls~W2sqd9J2B|T3sQ6Uvq$rhkjJ5ww6(Ib0*#fF|ku?*Om${E+U +VO%Fma{ADmr^E65>ET70#MerLk4H<+<%^otsW``o!ct^DyV<`mWD4x#cSXHhyQX^F&`C`#$_wn3(*CY +lU`#FhHn32MS3qO?6vo4D)s$?ov!d|Tv~tl$+ik`XTI4up7V7GVD)|EQp|PG@!e77&kTFwi10{?`Vou +#!<1V;)95FX&6^w~m3iGXY>$9qbk*olAXnr5>T`CBVMm;>3cV}?hnY$ny&Vv9kx)wV=mjgrkde}$jZk4; +Dp*k`jQ&=!mug3rpkm_j7ZE4ku-(xlY)Ity^S)IHPzHqgOx991RZqrte^L>f5>^ah9$)zRL}l9u(b*a +JNG^Cfpx9&=_js%ejxmzU07jFnDO2BTn;%WJ4w$=Ad}AZy<|F_CgU`oCjiT~S&QL}m~W|_NH3{=6g@A +dFk7&%f1Cyp~!&?2ivc4c=U +s;=V~pv(78qa9=+e+f!{q2dj}6X%Y9(NZDUz=FhWA8qWNuM*{KyddlaFCr4Fx!HcsO)c@K#l8G}$Li^(SEU{(y$0E#7zG9ww2IQzGMb?fW&NCU>3p&rY?xgnQ|N3y +8cbWF37JDwz@=&vIoQU=KTGioOT;k+DfN`t +hmY(c=z5CtM!~Wm*{_Fb}2T%Hszui4}1v^}!n^FCj*cHC3xT|(#mIRwfM`|XjlxcrgTfUn8LYk?;R$N +Y71-2D_yTQTz4`%CR+&5tB>M?aehAng!{iiuKr;J*07!ygpI%`Qi49%B%7g!bD1#7mJtUL3gT$sQ+rH +gdlG!M!?m^WlEoN<)R7Me|)|JV%hRmZCHnm2KNyU`4c73MvENH>=5Aw(Lx}dL`Fhjq^bDssIFZ+xz?)6t +&G~PvO>XFD>g{U*2Xr$ZI4PB3xGvbe$r&i))IOvCuRtTn!Gyf~OxGx00dYwJmqX!&g5ZTi-~HX<%&X$ +wE$y<2aHnJrPbcMzX}0uHC3Unpj2WJyNaM)7Kb>1q-_UY!(@|C_#{gTGjdBV5@+c_Ux82go>o%XiI4l ++BXmygXehKGkV)OXJj~_pK61O~FaxfW2@H~1v8TQs=9j^=?;a#Kkk2V%=KF+4|9D`eP8f)%^6XLbRQ? +@=(1?LT=#0v&aVQb;viC&^1SpGIuD~>NkL~^zK_-Xs`iWle4WqhB~sAEFCSTQ +*`g)BmGeIinn$YQL{EJe^KQkpeBW63h=eU*WD>v?7G=bxycsM`ts{-k&+x$y=SWArwVA6K&JDV$;@C7 +jH)}Ww@E&dj1AM0{zOOdA%oW_rf?du`Ym#Y(&%W*Z8pi6N0ST?*_bxa_Fvy1l)`Un#Ohw2L~{xoG_xH +|EfoO45?{KL6J$|rhdCkVPWgAwqz4o|x)U`|PL!)$5543{*@^X>Omsz7H?TN1$QIK%E4CbN;n4CgqmU +Fy*Fr +2gJqbaNxY>MJ9%L5nKqEOhg^mgzu-udo`)qp5^w=*ErNU0lW9GFB3*VqK1noFQ=(mJS@(-jF|YHgQz_ +wX_N_s#C^;Wyvx+SV_nV4|kz4^))NO!KJWJZ~JgqhA_kERD_R=;+I1c-l}n!$%FL-Nx~+D_j1-Z#i^H +=z)!zv{M%pa=vHHa;vr@%pFFtA3|XXb`C4Bb6AC)TF9|`L_|t4hH9%UB(A;zevNrY3T}}QM{`nhI%KF +nEL{i>+fn_su=2ps-o|d{dw~;2kG!rig55w!siwC4ijr2psHpXOH4An(cNXy7p}m5Ut!tM0(+)ITz71 +Zjh^o?P-2xj(0z4`tGt0`GYLB&|Qj6!N1PqC!WU0SK?c$4h#XSpryHntS7EWhVA?`~ +D>Uawt!gZxM@pnb#)FaDRd_TS_+y7`g4|(ahD1@u-j8Z4%TM6#0NkVQ>1A@_SUu<%8%GTILn{1~X=5w +(E7dLi-^pLu{(N$An@1YCwDwSNIX(^yN~#|j>ULa7A4+vB +N=dZ>bi&k8@{f))a68%@#ZP}}$Lx#t)aID4K*NG}jV%qSPRET!Jb9pQYQJKtW)n+luERwwj9Fr$l +4OE5NEgpI>yDTUJ^mcJw%-`%VBtJRSq-0W+fDO0g|?C_W){=h*tn(MVAt3bV)%NCL1jNc$juWX6UTVm=YMTTsd3wGWCr+BW(*EI+yAem&K=ObhBt;y~oLf^430qbi4wO>KMs^!TJi3#oEg$a=hu8z +WkxA+Tgw2C>q`9P-9D$v+QF=n8uZjX01T5G$$qHjvbtlc2E&rq+ +_%TSDFM|JUSd4qSlde6<`j|eo}glm)zy2`u@B8>LPu&q)ze0>1ciuaV+@LbPj9AO|G;1>hb=*tmEFs7 +oCkyKh?)hhjL3!Decr82891ECgWs5Pty57d!U(zvu<0q2EO^<0r>$V~;xwczeg?^GG)L_;F8QC+JVRKNM-b1;9QA6}Z44Lg6Cfu&fYQ#kBl%6Wn|> +cIvSQ8wxbO(FtzRUin(7w4l6}~*{5uo?xAnGT*#Q;2W`Ulwj~#lG;7J$a{eS-O^zoBtv5#+9D#miCYS9#J`>blTt+5V@x7vY}S~4qQa5ei +aiVP7~4IPmbcg2OylrNq7<5>bHy6s=gBp{ZiV}zn7Jj_Vn)>@z?!!0%NLd#~xQ +%Ve(4fbCSLUM8b7nrU!fjgX~`lk<{}~+)133I393TYUY2?%u?~#!teqNHaB`9oG#~cYEj?_B$6E|NQ4 +mGG}D}Ah&a&m(Uk41CZlOt2Z5sqp%Gq5t>oQmr0dfCXS+Z2_n#kn4KdfUsws=}505|!g!N)h*n6kI^= ++nBYN0e`LaRRY-5WFpUT_Q~oZYq-TCQYrfvZRxrQ;Nl{RO9QKs4gdKo+Xd>fa8u9MZM$)ug_@jgRgY$ +7`s^>!$@Ay6FLM1@8{gTYehfs(23} +1e(s*$Kv&~?cRJWYR%~NTVr5({Q6Yy9sPWbpZhw(aCi!-iont?GNURhf`%6k0#g;x*oEn)rl9Ier>w7 +x66LpVbmL=ktp^yEH*LO0*w|8ri<_2)69=$qz^7g9-%vFLM?+0v?h^`LzaByV +a^qkA{vsxE(okTop6h{nt_9dH^xI#Vh2RDxxmX367T^50_hX7Dt;8kI|_CqgIqlE{P1I00d&c1em5zeB!c-7VwFe0WT8ty|x +FdAK%cO|8WhA2a`K_|up&H=wno8a@mOXLToy>QpR7mBm$hz +jwfcMIf8@4D|w{j0}wuHX#K^5V_s+(zc%>qwb&w`|7Hp8@!VgjloEAdIzp&l;dQA9 +hhJqv+R6I&e4+bv6v6Xqtoc+)%*gAM<-}wMj7Ixt&915R&3tCKgg3HR%pCd$t*=MMK_g+1oTf8)uie$L{~Mz~n`6% +-rAj?DGd-etG{QyX*|J4xpq%d!U29xgAn>cSxn(5fypSLHu-*jx6wwS>R_xt~R|Ifee^KV~izM!>*6D7;VT2-Z`MD~>8_6w8A6TEP9UEHx +}xx8O3f;XALfy9lQrxL(zJDSb9GEn@hX;|JkTJL;$+%Oj%%gzu9Uc?!q3)ZlIgZuNT4nTyD$Q&)0hwz +Y--iT>1>|%kaZ}xyh~?4-&pU0-OjPZt>aSE<}HL4bTKYZ|P|p%r7o^lW<%c0;WisLulKRVy +Jrv!~~OjU?<_g}MT<&HNcC^l~^F%)PsDU)aJ!^G!Q?ivsMhJDR7HqFLdeee?#+L*G^j>GR;cSgYWF_! +QZQFRFDJRcK=Gi2n^Js)d;wYFj-n?c=Fi?OMJn@F3N?Kod|_PR2U=KZS#0aViS}Wl(3{9R`)p36W3uF +a&0M4&20r7O0bf4pO8NJXq;3ODig2BNNWLCn7x@kJuxQ_l!MKZi%gH@auON3Tqflvz7VYCdr$$j}Ju@l5iVZ845$z-K4 +rZoe_N&9M9F^$lE-8;XQd;i2r79WGb~5N>H61(9FQY4i=~W16!)oPnK!bLJsXZ%7P?wJZ+qFohk&SVn +jzTjq{Tp5F-m+4xkvhJ22fCH1T@}7nFH2X0a$cw2Lh>0y*3EtI26?ECS&PYk^ZzuDJrIJk(_QN*)0{a ++Yv-)h2^V%JZ+$QCXP=|Dqz4%r#CA*YqAD+qSGuJr^(dL#Cv&>lID{!Lz+>{AZ9n9l9)-h&roVvyTQe +jV41wOOIi&(a|?1VRFph{I1FXTb>?L{{Ksu9GnG^BB6U}s3D8)^c@%B$U<&BeOJ=Lrz0*@k`1HND?A@ +c8F3c;L`(b3Pf1OD`IcdC}Q@}&~Hl=T5h;F5gs;C-5;u0i@I}^Lz`1RLv^9Dk&8^8Qw_ao+x{Q7I4E5%RaP&Z=ig +>3unMjSu@SFDKyCY^DtwsjoK2DGumErhaw9vwAGAREWWl?7zL%Zk|D(!G*S>3YkQ)Ow+}6%7_6ag8>y +8CUd36xRWUz@;FPg5FsJ5>ByV7nMDPNr8oNtMnoihHOFB_uu(s7UFTZEzXj|`muY|`SYi@ve>4qE8{ODuhSR6F?C9NiEh^-&HjEQw|5e#q?_HQMa1Sb?Yx28Ca5N1Yy}S8B8 +xr9(~V(kguBguc5)t3fJ1g(p?rn>U%%xSsMP;fpuW62V3U=XX7_k=rGM0LL5?QZ>>-4%Cws#xs{;2U24I^()UE!OYzR?;@vzMTuXB@uiLuR`gKm)vJC?te(Rv_ +N>CB{IY;4$GnWcRPU}Fl^&ZB{#~*MDI5AGrGH*t>WoUAttgdKsl2Mxui?jdSX~z55g_}r7!Y$Xl`$^4 +kJZhpS=U)H9atqJ9{iMN&FH#w(#M@}uZw_RD +Lq+P$oTgtG4;kUW06pg74;wE`CSi^I-h&XGj`e|anS!$eG=<_c*Z5#gLc{bml-Yo8urKY_H1pXdd;#* +;)F>Fqg42~rfibbQauu>~qfaI*e&_WauiUHAeX`a0up?aMR<;fx&;bSQGRK>Jy1#P<-h8J{dI4VkUtB +U`vp`TWSH_;)PFc47!2O3@>!TfZ%_Y{@%YJS0LdJ3BxaUhP$pcmHCgEA#Wtybof@rthKg+Ly@85iwwF +?{pmS-a?ML=PUe|KBHf+8ezG@e18+1FK?0Ag+zy7yy0)82Ar>Y1GiE!a!*-6_GZ=4kJAD)w!78jArI& +uHXw*2jI2(`mtjt1pLsPPppG0So2&Cu|Ov-ab$(7H`mlatoLIfJxRRZd@SS*0l=!$;V*6kM)oYJT!`SH)ri%$g@(CJUXirSTuN9npgiTLOl) +Ljh0j3}?x)RfmTfoxH^9>aw*aAg1ad<$sBVXhaHLQRJFlu2kop+T93o&vGLfe^JtQ@jQ!QfkN-2AT;F +U623b6y<1em#HvI)D9o8e{0gXm?y>5~SaXgGtO=+hM%gCC*E-5>jYI<&K9))>aE{q(=JTd#2JbfVU}J +`5I&0fN;C@>gA3t-inmHb-4S;droro*3+-O$0Q5H@Bj0~_j^zKul8Q<9_${zI4D)wSYK!0d(Zp-_-^ ++}s6dYkJgY3Qp&(G-LM0w35S1k!GK#-F*xT#8+&kzjEBh%U^2K)tD~o!xo>$wfS=*r1i3EQ&HHhm;Qw)2&RT_=WCTXE=852qO|Lo +fwyDJd5yUFgZ|C0$7%95HSJVIwbxBk(XJ*lN5Fx&eF?X>O#AGVshd#90R11z)_=TkMP`O>iQChosmC& +ZK%AkvoN`sLq2=TMBQVQ4yvR$*R72dF%d?fJSeZb%)$8WApzk`$7JS3!{r(NMd8MStqx&X1%pPas#bo +OBG?eHr8NN-#7}p9~J)r8SVl-*yzOo{9i&8K-C716!CHL-PkS8-hdYynD*?5|j5o>zB5~%=zj9UPXv+ +47c)=+5AdwU$}Hl4-N(jj;-A&n=ktPBz+P^mt%=|F>np-yH0(sDor(>|7_(6g1!OZc(3X?5VK?x>Knh +#eNLGf34rm*SXxbt}|$4kC)4Jg;xg{HTIERo$&aGU7v6xh7n!9E!ZW;}Q*?rk6Z9R+2mHLPvGR!HRcZ +yD8O=OQ_@L`T|Am)2AS$yh8d^u}#r0;hCf@!SzZ3x92*`7G{h4g`Di*7JMM%`&?N<72&Td{is4(gOswrWa1nMEJq<{ftkB?c*NeWQn+hT?SG$ +at^)INUg6g(fA{}B{qbY)gE4w5~xkZYH960l*{JWy`1-X*;xDz+AF#;Xv57HITNU5 +hr!Lx0!{cC$W0fx)x8fQOUOpv>VgDb#upp!&~-Ff<U|VY)LPuB*)%ED-#u0^5jG(lRr6e-1MwmGAAv{E|g9{Je{ZJm_IpreG0Ws(~IQo2- +8=c1;Un5<|tjkphTxZp)^g_8-OUe-3y)3Xl@wq5D>hxK5>IOx;a0a>owKp_W~*hC6x@+ +|A>87AT&WmSVgVLXao1ff){2cB*u*;Np#LcofZuQ{fu +XTk?IHbG5^7Q)R@>_HoC!$w59mvdL1tVZBdVnxpFJx#6r8+pTl8f$#C_}w(2iPYunXK_g|mdypeU{!t +VCJr0*T6#8C&8$wewI~s051N^!nsrlU5u)6%A^5^QgWytMS|NC(dmK+1ZHgbyL&S67&Yq5@3p#1l?ST +VX=JD(HK_&Dor`7kdyKFWuCH-6)^0R{LQ=*DY>Sz^K=6$(K8qEPX%qtCd2h +{8;H|$Cm)-PNAehw&Rg>y&1$y0%UM*%c_<&-KQ?5FBbC%vQEhX2|HGdh^$A`g-HA=fjN1fjB@?Ck;4% +X{j-Huhv-3^T?D^JJY#=GTip)DGl6_?L#%ix)j*$QJP7@}{Ur^y6)osw(KIE`mEGyEQ7G=8LjJ*%rusjD!v~Y0!2d&v2BaRsGkj!2#yczuy~~moCRfcdqqGxU5Lr?cx&Jw3 +Va+)4w%r{YYI8h#lU^driQ|5eH#izMNp)YN!)L?GKxsU=X`WE?wBg~0sl>L0Kqu^l0BV*gkGni7e9f_ +YNP(yhJA$xOP+gphT4+&Aw@wSg^mGyQymY8Mm*v53VxFEOYxKS27<1>?VvB=FpwgKh6|ZDY?-K&v7kTc?6LcS~6D*g!*|1@?ly^gfpL@OK{UEgv-QxV8 +z`aZu{uCWo?;jazwg=(0Iu>=OP`vIYj}1fSJon(AD9)c9 +LwNqn`DFC@mRENWx;ra?80!AoWrq-!4mq5DS1?-Lpo!JeU`yNLhj=cl@0>ueLbe8BtE&KC}&Z%cWRXn +xf|Qzm-SyaQQ<_MuTq=F9{tk$B>xr9v0pZRwe#?-@;;+ajH3l1v|#b`mj?nX#OlXgJfM?#Gp>!n)#)8 +>{GoaOXwc_Z5=EqCxUi4bn6n(}MX9kuaXbhTs->*1w~<0ysAa3hknQqdSqd@!a*+PC7?FnxNQ$hDR?Z +w=)sskhot$M0vW&AbIA!?|NH>EtRSL?iaEf6`#ith4;Fd=YS9q3`GB0psXx3{t)*2|-&RC~q)gfVcS; +dZBv5gH6E=ZltFbWX~DFiDj2j8%Pr5Z-Serq(Hr{}~(P@XqZnwz(@;z*sd9le;QFY;$7CQ(V!u@`PU8 +!3s?bR>YA@n{&g+6GcH#;^EDmq^ax_p!r8Q(VzBDMr9ZU*_m%ib-)cOHDbgz}Zx42A;X?Sj&aKijgm! +&adX#tVw6QO|%0V(0fUKUcf*1-dy6hl4g&VJ>a2h;Fi>%K8%2TCdR-p?}3*AEkkXMt*@A0s@o#7dwh> +)#vah=Swa3{QRwVp?&9dUd}fW>Ml;Cit>&WDtjM{^r3>9;#R-Kcom}9Hu+s@<)z-*+Gn&n_KHwbLAe9 +JcjYo4d!mIb=Ruwz#4OJ=GUIEO*tZ6{EnlYK{!nt&>6ee9`y*e8xe)x{TTrVh(Zm$Ye(2S;DhZi>*fjRk$_eK&At!>&JImq +VTZv4|s*zf623ki^vxHb_B~^afC~oVmFX-j)R)vTC9Ck@aiM#wZh?C8WzQx?G$!@=j>}G^&-H=R~^?A=?hbUSa5JkB#g?R=ta0xZWFxugj120cI>Za7#2mt7rSgvG?oG7 +v?II`pDTM2r{oNUS^HQXov3R#b@9SOOig)D-lb$%-@qkPK|+PS^W{=1GODyG(LP1g)@};a^Ebr3m}rbNw2CBbLq{uwry1^(I9WD +dTezv9BQ_@-c4P#wSl9+4c&wLms(E#68nA^2^7g)R>wR=nyY5xa)fx%31RRi_gdx@ia*9`k{-`5eRF- +F-x;^5ia_PwffAVFQy|)J>fFs)=^z;qA}&^3*eO};!>`$XLFH~7iHkRDdDEaCl48#O8dqq4LjmGmlr$ +}tc0}t%I5!Q3LBkUf)w`AYkx{{_k#4jv#&`3}%)WGI1G+Ywiy4)lN$#C;Dui)1Jx^73LpeBAX6pqUsN +Iz+lYcdo^`50SS)$jtV@XLba)AXS}COQ}8g820h% +M107!bUN2=AWt)F;cqd>g!t7>E&WRb!E;~zTv{bM@9(thSvT}@GfgT*rQeI0g=8bFQwfk3Xaq4ns=`z ++VG^Qnv>J^;a9U>IUhI*1oq)u5DvsxC&;9G(zP{eq11Pr5 +JS*&Bv{7P`1{z7>Hhs)cWM@$T1DGGj)VPxdGay>1>cq?2yJe!0jlqX{M*g9lfF6 +tO3vs0dvbkQ#0Q_-F#lx{U#h&r=Nj+gxuU`}sCSL63HMAo?^iM4A=^Cuf>PXN#P=2S?#>r0q^zLll@4 +84L-vsoa=pb9)zPGRD}%$$az{WA1PodlsR2#OhFI*A(?Qz^K7Hb+I6*03rA&r27y$scY1b7F-}|`5{? +s2S^4|I{?w+le1?c-Bw(yV%cbGTfv2XS8UxoN{axz=39)%N;M>u8!|~#?Y_>4!#JvMQDV4ctD#}FV5l +S&;j6AGMsHAdL=wJm$R8+0*C5_hr8WTDd$zN5$#a$hh>)yMx+#+S1py{Xg +F=CZ3K9nNCLyt6xL@2j+9T}208_wP%*3-VCfl4hd!E!_qQt`h?I@YOp1Yr +CIRAF;Y|5HI_1uwL*RXnN;S<7I7oGNOKWeevb~z{VvOYr+NU81FO{lzgLO)U$-k0tKt5!v>w(peVCC^ +6L;H6k*2((DZ`RuprlfjT6Q&TXQOwEQ8QQ_C|^!Nv*9@n5SEW`bQGf@2ifV$0!mQ#e?Csx3yL}l=kxA +#=<+P-29-TUzmaDMde^eC~^fLN78I3=QIy +t%So9g1&M +EWKdq_)+w{rs9D-=A8z57)xk6qA^hb71Q@ED@@JY?3LF)7v8T=!;%(p7ZGRJ%pd4uJ~g{@u!T7CZIFlkeXDDc}7m->J`^@|^(t9msb9uJgKb-d-}es3YNB*CnG +(-n1$%Et<-*TgLS~GPw3CneMOkWjfew6iFLs-;1+me6aT^y9ayG=AK=56SBJX?(YJd)?0 +)m&dGzA(A^vNFTJFxl4dEGpgqePAQpkVZfhIq-LC&&bs_Wnns`3qGXVtD1@5s}l(QerO7FQrhHK~dXk +Fci!(l3HY-@baC5*z%_3#^4#cxy_zYu^1DKpmB@j)B^0IY`XDc*5fwV!hb0=4b&b)t9FN%z^+rrYO^J5X~Q@g +WmsBZ4baYDKJ%Up`&;@$%iCi+%*|_i +}J|A0o8Y~v_S(gh*Yp#Ol_FqKCHkLfexX{t6M_VMj2HPL#R?cD?J$ +uuI53&;eG2%Q#A-Ypb+u6#T)QCUi;)2T36T501GW=N2E12J>cfG!)Mm)OrNvfU=8$I@)~qIE2k!`eY$ +;Jy^#jSR2bdV0?_Lm=rGjrfQ%a9RTC;uH`qRutJ3>)Ci7ZjXDt0oFdO}7r)kM|2xeycE$~MgMce8<=U +83e!fA_4%8}uOVMZgZnw3(v9;~MzycLYo7?k+nNMaT&lBcs!MCZ1+}&kVP^?6I;`;3rAbw-Udu^Gqc~^<%5Ur16 +IC?9J=dm-{;bBfj96J<*vUasSpI=Qi)_;cZ$2!irJFcanU>YI5Lj-|wGo +WtKsQn>CQVaOoNVPa#__3$cWXoWBdD(b`lsr;9H5vR-J4#}GQU=kVn_L>DxG}PXvKAd6+5H4S-|3D_d +!%zp`tSSZ98(Jw>uNXCexMoNujt6;j?fRB#8jb5K9sKxibFai%u0`)~s+I6}N{hkH%?-Z);F|Fcd+5Ji$@AoD#P*=$l9S{Gd466^z?Qilxn0es%Hkfl;<=1ld-s`PY)1=h0ONEf!z?G|S#37iltNfzrx1#r +N~fco>5VVY5$8ObZy-KLaWG9t{1S$qX&|;fPUJ$%XvvvYaMbu>m6qnEf`*M`t5t2u|LJoM9T6eT(TS* +}Un@MSfDFuHRt>kc=;rt3pr-m7^)zZLnDg3Bb`l*|#{BC{^BOrksO$i3LZIxW +J7ZyOX3Zqo6968x0fUB=*27@ZVZxcUK1BQJ%&t@;C+f)bh@6KPEDAUbG)F8Rr^#L|;blWWsPOYrDS6& +H2&w#-hr)TpRs)VeVy18{0Fu9VB9Ct&IwJ?i{DldMPolr&vXn1UvOm%0hPH^4*GksP`WowX_`%kX}(fhQczo%DBqm| +BuGXkP6o|Gfcv-v(RDy*&7Ncs-dePnaa1L8;vX=cl!U4cw~L{SZtbY)@3>MRQPZXuk5z>uGHPQDvPU=h(FIbwjTk3#Ib2H-p*yE)TaP>9Q`>+d&>@g|xVg561)NZz +@$N%Oy2>>+kE@I=^lF$_1prcQ*uHc8tfk?vusA7)Z#}KjKHC#BJ52*{t8GstJY|j2ywh}BwWJOp;=9x +OiXY&HhNB`u0m=v;%%ZFoKe(-HtAK7__^{e{k~s(DkKJ*Rwf(*`6b67KJ<8sa)SghdFw2j`uIOrz6n9 +)wBQ5tiY5coU)8AzG`IC0^;s8#B7l#xtYu0=W3w8{6&`f#^g`f-;iMz!N&XcCAHn|hclQ+;8&PGSU*e +1|ReD;Qc6dbr+vxYWziVvabe>bt2_t?;TY^tRk?@LOg(<}odPWwPpCs0v)F-Ro>=ynhAeiaS!#V}QsZ +5gFQ(c>1gS(X!*4%I>C1exKSP5rdB00U#hV#Ny>3A*}Uagy-S6c}%qNLxg+x+@#o>HV5zve?uJz){%6 +JYCj^YLEz^mwNoF#Kt94PtmK!g8AZmk)2oirx24eBbCcnz=O(eFh6=F?#nQy%7 +mD4tbD%N21RhJ?%tM!dRArnDMoc{WSVDf>G+HWyx7bp!yz%Y98beHK#$udGGdUTU?j4Eq-JWA;5scaT +hp3O`TZ&AkwqHwaM`y2uV42gcy^cWF%Z?_Wa5uWdCYX?mWCuO!(lE<5Y04S`tY92R3;__ajYfU|&l5B +xs6e^Zl{vL=>VW;LY2R9gLDfl|MyQT@(fMP3L_=yj#Bz(?vE96)_l^WXdh4TSiMpLnoVhUyz{Tr5sxz +pspKe6hnTizKg4clfB<(HSY}`M!Q^D$%89-@%g^zBO293am=yXditWeJ((4bM2l+qBnD+D>YHI%E6I} +lp;z<*h$WAfr|uQUwF&+x< +sw$l%Vdt5sB>mk7c>2|aNDzHdnGPn<|&Xuh!kybR|V028LD>#W)PZtAP9(0E7%uP-Q{bOlL2f`}XS=% +;sEDFp*Uei5c+g5xSAFOWyV4tuGx^=?*UUGq&JjQ6nah}fg3E$;ouR$yo`XUBXFa4P=^v`sm*Zh36bf +FbY*ma{)7()RUpc9#5md-~K@7nP+ac*H91qfGmE)Z!5b*>NX7UrXgW)nK?>-=rUW3@hWc!FmgSeYRI#8DG5GProxH&Eq>-6pmbpTqK-Vz+p-9D>lZK;aIR0xOch&5KEgx1cqnJ4yjC-e#j +AbpaO;w^B}MhRXrFbxZSc4RfEHV)u?4wJ~V46Ee^V8S{9E9nh9hDSAy(T2sdKF}fEud+pY;u38G5-?7 +kMEwf{K(b{6Wr9i8s#Y#nJ5g=THKSR}zOqHD`{>PBzs2EK~?lX%rj8~B56LT8!lP-6N}(XCoM+aBIt>n#y~1SayI1692>=G! +5O0a5q%Mj+?F>ifqj~i6Xa;ipcu|;^9?Gkq7RlJNMS{|yV9jkd4%_~e_yaW=jgAw(SrvNNviSgjxFo* +S9oX>+XSjim{rCP$8iiY?xAEZNr+%)BJ6L$_ArkM2U%a>8>#VP@_sVrfQ*v^^y6&qlzxe#KPdn~2=#ewGUi)BFXOx2=pdJ9IQEBQQ1)Drp2(&}$FQPv@fkctE@$3AsYM?5O +`OL*Qtb!ipqlM!wOJ&@7~xYjsUm=^~>-l3H*;2eIjo&e^TAfPP%fMgy{t_-T>h0mFpJv-iO0eiMoZ(- +JO=lk~ARI65Pn1f7pI3!_8#6z{bvWj^Vlsb*PtkOzhsdC+{>J(Q`^t}6z+#r!LrDOxy^?yGvjI4-UY7 +>n{sSClhR$w8K|d>~F{WQ|#3rd}-Ocnd&^q5Y!3KsX0nxD)lF{|`9BjrH|)pDT)xgLogk!nB1WIKJLT +PtszLkJzGa`R9EUJwbb_7c<|4xPzayj{aC*SAOiWUSEL&!sIB>QCwW`b$?2NJ8E>`;JGU1=_DHDNpTV +CtNMKAW8lk&lyaqj4L=$nGG^&`jA*2E_jo_?G&_gsZ#Kr+`TJ?36@c1%=tKSdOL+Zk1iObzzT)l`Xt# +HH0kjMFVhWft&(JB5{1$P#3JdDP&r3|HMWC0)41Tru65bf?-g{d;%j!m1eQ2OHf|aJU7Pu6y<|IHZW) +|@z#U_BY!D#y6Zga^oiTjI^mlH(!;B|bX|)g71r$z7n9ivFU|& +Re2R4|J0HJSZhd_DS;Gm`s=L2wZ~hJ~iOp&vVi(m@gE4=5_8UO{rlv`Dr@wqieHOX`-{X~pCB{I*4=X +!w6B*U}l`tzt@#VwvAf&7IAU^*R2SH?v^zn6x7zMrkymBNqVbde23%g=OpMO~z5%qhu%2YzD$WhPok> +335)WOU}g+7%@MoA>~qmX!3D(GWCKk5mTewmdHQ!*TqzQ~_}el8PmROc?i5gj|G +${V!_1u_@K37il%!(mV`e^o|e!*sSY{R;=?65MM?iTWQI=t!<6cuF+MO_N$eHg!M+fmQ!L0C_8M>Dx7jEdELyCUm&34dXiy96(7&wLez?d?M*Ti +If6u2-DaaeH{mr5oDF+uZ-@Vl*D27$OQIj__(q@VaCKT67FM#*|Te@hQUX`Rrj))&lx!MFpf4Qi|DUY +e+{s(JrIp={Wabq{>p7wQ)k`8VrK;KzQ!v +D#Dl61wpqYBYHAEPfuY{4A_|m2^_T~x~A`u<)iiIC{Er?vr8Qh0!9Snht)Kk|3`YRVgzZ=eal~KYb)T +e^&BL+P0O{47)}PyET;`yIbNZH5gkyKs6rmcxWUl~Oj?`dn5`!;D)rN|u+^!9R@|3!%#b# +IjIlCOvxw^Flwckgl@c%%)CwW-ABf@QAUPT^NSJ2KgnQR6EknAm%&&lf|vlEs2pS|>*r{rn1wHEgRF2tj7$^bJ!lQCM)+Hj9x4P5 +ffJTE0=HmxBje&?bT}{^>OC%MMXoGT(;g&0c&4wi^r&=)DEO(_GY1Y&XhMNsL*I8GM74c? +obUhh}9BXQYy&)>Ra9{zgS{7DK+=Mc72E?rdX1#tOP03oJR*n?&?;8ErC81hCnYCFrK#JOjHhb&q>-BEGZ +8$NuWf^Nz=7F)_Ne^LSc;Lt7#LMSRQV8i5f*Um$>K{Ww2Nqo&+&HF--hu6hnhpiYrr;TheJ^Ewm;({s +76KD!RYFXtaonoA6QLCV_Ood}#Z$8{y%+`15eoUrP?D*eL33nV}4|Al@)#>s=s&<7XWpn +AaVqr!ovU(|z`Zg$Uu26Ss) +_;g70Xa!c#45(PRAT8gCoFPX>xmV(ny9s0#`8=9An~MF{;p!R#t^n0cLCL2iItH9Ya(*1unU4U>(Pyq +w%2Id)WJ|(L%oybU0}|!0W0mrTN4c)`4|L#6`pi-mpzmz=mVs&+$Q=Q>D7yXl28tv6jqnLu0&E|S<;{os@HN&E`BlI)kCbltQgkDhLq?UrZ)lpLlF_W+ +Y2X*Myp6tp-wu7o4&Zyu}r+M0e{fPPziit!kLz;&t?m3NNQKjP{j%HQt7F8G4hWffn%nIBf8cf1_^TT +J$-Fzm#Y3s`T6;6k}!8)GG-2kYi{84a*E7Cty#h%CtHrfbZ0unJDdnpe96wqi&=%-$nxH*YCjOCm(gR +1VDZ@?N$o+<;fpA<9W|exR!P9S!{yw6*{3Imp!TMJ-qzTQBkzQez8D*D@?E?h89W-%S;ca~QLhLoJZ&K=UkQ-G=xZO6K9y~_K9{9eCynP=(W#B4DuihhhAX*UYw@?t5;lO0u5$q^-3`63uccaq93$y^I7DkxsqRv6KT{KTl7wDSEhByqO805)}gYL-%4#%b$Wa +GtTsyIWsA)+`pMO)qb@@ +GAw{(;o8%3_*~bL$Y9LJDCzZc~q{2&UN@&SoFm_>+lU8$L+Sat6cB~ftkLC9KXw~F2V|rN%Dhm5`?o+ +Ivx@Vai>PGGxJeeU|4p%Q=9R%8omm*pq${3t9p%JxBU>WZdY@%EVu?IEiM{^0Wn +p8@uXTZP1HAD~{=4Y17y*{Vis{b2g*>!rOG_m0$E1>Gh_M85-oKk480;O+6BL$Vl4MNcz>jYn-t04iQ +1hi4(efJfUmGfy$*Ri|f=)c2iT?Q9ep>CH*!hDTOYjcX$e=z#~yG=Vq`lek4^gyr(?L>j8}-sV%0Z>m +4|Am~B7uHPY^tCl@t}mNA}t}gbc7%u8dZh!PFF9@nuTOtsD+{Ldq`uO77p}l4-K3F +N4+%S(9)1)wQyv$7#i3rFT(4!0hr-tmP&pVGM>%CF6o|<&{SzVWPoN!YH+=@-%(0e%&4MYWJMvm+kmT +nkV!1EMNaySnB|Q@duTCFm({%E<*<)~=s|(Yp&KZ;(MHYM<*;<@syYsfz&-H!fb49Jz9&k2vZD!$3_D +!pN>@$yg7D@?)V$hiR)%$%Ttx}MV*{-YJMJiT703{jdI{=G$Wlk__5_d2XOxD$5g4{%D%-?VRwH0zDF +VVw2#I`cE%(v4f&MmhSPDx%9O$?FzKO>ri8ktpbR^W1pdRr<>y9b0UWj@u702QYabu(w4*I*uCKCA;SF|x|3y24o +tGHyAtzj9e}0OFiq=<04aeZz`Xo_{`}b*TwD&5>}}c~F3^4vSknH048RyK6&lk9%hf;?(X6qF>q$M5d +M)>?xE$i@HxlV(S=kWoM}RsPUN*(?o8gZ|RE&rCKl~Yv&+yNWXYq1!y|bg&Q3VcYNr6;T*#gk*^>)-e +k|HDmjfe6N{|ja1Unmaz%P+s$s>Q7>WrrH>LIh};Ll=f;KfW0kwx;JuJ@fU7XCB2nv$p=v&3m(CH$Qj +g6}Q~itRFqom+BT_U?PR-M5yXf=Yg&47tU0`Mmy01-`>iA7Xa(WVe=Kf>{I2f9$Xb5dJNM%?_yZHv>9 +7dcUMo-UBv%gpdokj*6S&yuvM_|-hK)s0?2nuXztPo%xsi$rm8irVNz3bOL +(^^cNSjz>1K!uEJKOg0Iix39^geFUoV-_lu_}snVs_55KQbUEgBi5T9r9avDL`WxcTnDf6@6kJ59#XM +LM2Q8jm}C&bVrL6*UK*ZO82xBlFN5m}|HI%l=yeV})l(3?#l%0dCO_h|m!VE?leL9tkJ&%#dB{mTU+J +lIWfbl~Yvly@N!k>P7NaZjs-nQ#VCRn=Q>H5(MkIEu#XikEZweXgEyCo(^%y7S(F-$PR?<38MWobpT0 +WELWh75u5pQRnH(oT|X)BOgCDF*jBLiT(Ofn2ah#=6+J@YuxBPUVnu6SLu#71fEe_I8dA*ZsuR&&OZL +&|CF4z!_UUPRfL`KqeUmU_&Ybzc=Gg;4FVc5(8>UujW3ypaD&5w*&F$`PK6{in&Q&~|dW*@t%d!-v=m6H}>Cx{m{w9u8Z7PeI{2q)@VEV!-q&wyqKl7*6}Tvz0 +#R9sLnPzuAR6L^;5)euiR%d_EPfEb2LCNh&_YBH$D87b}bb$esI*C?^Xl8X_Ja^!sHr2 +pEmkMBwhneE{==V7k5z6x~?6{3-dOTTBat2gP_sIj*P5fGTsdwlNbISpiVe45fp$+uR!(_!-nSJiQJF +Txz4AU4V67@DjGm>CsS47J^ign(79dShEOtVVw;Oy>+R`R16)sAr0_#WdKb7-YmPkPIEf1<}cVlut;l +c9>uB5#z8~P1Tz{$#eWmS68kvmwi|Hdv8d3>3tZ%5gpkeszm`x+>CIJlmlxP?ar?dm)opf8_t19j6e3x!n_d4#qNGil +mM?=29CV4_E5VXS(RsDb7X$-E&t;d1?MgxgHrnR$+aWPA`d$SG1_3{fQz#0k~3N>d?OzYG;U2`INL!E +szL*=q9SB<^kYV&o=~i$(1hJ=#DmN)$ax2MIeTjzk1d!Og5;j#XM=V|Z$eO@$(BjS9Dhh~^*b)DUG2$ +N6ICgkf4>)M`qk$2FDu)WHKCXx->ZCTSJ1r^IRkll~^>oZp3RoUI*)zA_S~u8IiGuIPT?Fgu{>P4f^5 +#JIQ>H-44ZXxJu@$9d_7sM3F4Hdr**Ar<{Hb1Z^o4Zgj3Y +S>gr#&SC=4v>eG{T8MMkhKX_8G=EHT!jE!QF|>4wBuaM%joNAP0f+Qf&@cBrDTF5mjJL0 +}V+#|wxdMamUJaAlpVBDKg02ME_yKH0W=7b1e8TaE@RVjB&I8=UzJU1%IjXv%L(mHKHhNM`8)NF?tLF +gLb1W-Eka4%CRtFa|05V6hqft7CI%6pq}G2vukRxAmv!>_ +R|4zzFz4Ms2|^3dbkPp;yG=#}}y#2+~-G85$?3$3#|hpqZlgQnCUCVdzb*6Tp3tFjutnwYB4y`6~DvZ +Gs;s%$5lQS~)IHM1T-R!Snggwz8VPJ1%?Ibm4`NxcSno=(YT3dX{a_`dloTiZxygDR+NhOy4oL0&>dh +hB)HUN&U8Ad3i*c7xK<1P5QgWg)acTif$0j_z?i=ZA8)T?)Jum=+V~p?k;@a>20jI3_Ln&JVLF{2wV0y(IrhUK_sSZ=lyh +6No7-Q4r!hNgc|oIX`Tj)kyJr)o$vlo~^@yS}Q+)m_zHl=JFpa+;0p*!k}A;!`!ev`k$r)h%W~w>677 +4#C;Swt-CzLm89H)?DAwdkFMa^1fv6(2Lis+Eh78s11}eP8`R>JIrzbizeHW1cl-vBLhOfjU} +#+0N?8tZYj#)DB2>8fs7Cj4qt6BSWv_WE^svR^ajXx{3u?Cu356K0YtdBS)GxU_J|6e1dUZ$l%j>J4f +98Y8?bZ5k3{nGAJY=0&DMm50&bW$ZBNQ|IZ7AmEq%Yr6}_uazltdW5)6pfLGcf8!))+lji+jF0XX$7pk)YF3Fc +;a*xdfDK?88eZ$Q@b30UVJc6%+>;M(+BX65y5P}0V=1D!Mdn`5D2g=^K#Kl|Zwz}{MfjGJJtY~AF1?V*)(TvSK?ev*z*~}^0h_Id1h_OQFqI$~M~s1)vt>Es{1*zfAa>PwdZ3 +i2gFBEu65gQ?1A^td#wa>O!H>zZ5`m~CWuvW_cytp1Wj$1i7*;fiR +7?d>w`4klPIf9VQOo`8-Y<4q-w$T3Wl~q8f=Wh7Fz#p)-f1vO +5cMX@pUrXw5I+23zt4O-m$@CTcHlz!9E9iW-Md3bll?0gk^CGun0>DI-C`$16XX +DR`{spXyF~J`*_r!M_Z>m5paaVXlt~CsM@+e+KIOA!_z6AB4fs%9siJGFHj_+W_*f*&ZrdR5)-SCv^m +ZSQaz$NA2--xa$Yo{wkIVZV}oi=k5DieWCb`65`)9TRK9so=!`&EuEG(9-9p2l<$ht6h;Ti^thXiGt8 +q4XMw}sxMefck?3D`T@wFM56b0#YXhYE7ohwDVkRKAs@`|!Ae|7n0+*c^>6QzrlRVV&q0k#Xm^Vn!>) +N-{Z9I$~A@(!6Tv<67SD~{$}uhq@SegyE3gUTs9IxN_gmzfwD786}UL$hqwY?TYgb1q_MjAwH`SqrLLsT!^QO6M2-j@e2eKYGxnN|Lg#X(dUBPtJ9X|7L>5iS81jtne2_0hl{Du +SoOZ|h9jNOS~V30;AV!PgCM%Obdg4OF5zcNfS=9@Tu#OXezh3{_(YqgxkJ +dY1CY`h0&ez9l@k+>x>1c3~HlGi*?fPBeLrp}-1ZPSQY_Lt>xfu(dzL0B?$Exl-eUDUeFOyWO{xsMmDyu_M5T2I +x%gj<13O}HJWbDmJW)Z3IHzS=t6%S(Hp@^jiDzu(mL?zl9IF~*pgg2e6)mcYp^i0pv!XH>s3$H_QQ0X +!G7@YbWqF=m!l6mJW|nVqobp-JI67`P0BtQQouj@~K>dtyVIIerP`0xZ#TX6s;isQ{{>7JZplH(h*%V +Dq2LSy1V)Wyi@no9Ke#(pa;_ceqb3Qs9jYspVU|+Mr9L}YsyZ>_nY70BA2D`g6noMvw+WGicLC=fsr4GHH +MCA#x7fHU_z>3q<&yMHBKdOGKUhRM~_QMR~pATZF~&VR#h>;DB`*+4a;(bua~i&V4CXr`_xEi-_bA4r +I75|pdI(DXseGBl)(h)3TH*?81r;a51m@B&t;b=~VnVPDLJq4W-Kqr|uzzae;+9J>KpT3sZR+H|w>j! +rk{n15pslim5_Y}}ldWeg58AptJrzQ~Dh!S^D0`qN^RTXtGX@02Llxq*rn=?EL5Lv!Yc(x{~Q=cWDyXzrv+YK@Os&DWfSl1&PfEE-ck|84~JXjJO;B +R{$m&>dEL2&pC-os?)m-Q==pA$k+P8rQ*_atCeEFc7}-q|6?xaZGTa7*v~?YkA8b4XA>?6%?ZUs>6B( +YPbn+0>W=!duFQ=$fd>)5)BvXEDmIFq#_y^_`FIkJ5_29%h;naFi4R;S_BWK!&SE2>;AuPr+5H}b5^6 +9#R#8)f1ebDT!$u8`;H+^AQL9r90yXb5@&n^`c@I7heDOuhkkDiPQ6r^F5H8hfI7WZ6N7o?XpNTrPS$ +g(d()rvs`gRn68)<+n3jnZ+&dK0ms)6)w~C!ar~&=&U+E3iq}6Wm#Br+5Ltk?#`<5IvXdkI7h@1WL3A +^0cu}LNb0M2Q)mVli^Sazt{&_PORvcvsuTuI%Cfcli86yjCJd1ndtzdD-(75aYpf!l_L?E1dOKY3I!O +lzH(p|!lZ%|__M4L2jyAJ(_94eJ7h*Jxkl_uExcVO3>iOej$VrvhXBamgOOP4iU&MerBEVOehC{btSR +!Sv6muI2I>YBa>iR@z-4gVd@(&ti+Ry(G#c*VxxOCtdeO#4lqAvDUq|anv_6b_iCd`AXgHVYboUvE>| +EJ5*vf3;YWL2JP{V62>FMMuLLflL-`~xLOpvfY{@!EPqoy(vUK5<-DS@ulIR7!sD>_IsDl8^ivG?4*x%ZAD=k{dKK=E +8qfX3~i+*b_u(G{! +0UjbaG@?QYA9IAxU&WCPPlz%=_J6u##MYH+d4;~O-tWn^K30R8)5W#9aPv?{jj|qQ61tw~&$OKiYCPA +ngRx+uT@`K%vLV!uFS>gmtQU%p`Wc;%PA1r2Q3923C^d2)8ArgDHWtq(blABL37apm-TTzII;=D;rxp +kT(HpIXdfkYb!i0`xHZI;_9CG_P`pBeP7-PMy=1gGqBT6+ML3HZYXz{!MfH~_b^dI;!bhnL6d;uubwc +JyvCF0CSEVb_0pxNY=>TX8w_m@fH^U2-RGS$xU9$}x1I85FI5quXtDPlakAvOh@9T@P4h8WMUzi6Fzi +CTd7wcK`q}i6>PwBVUCE>punBcV7(vDFy7;9Xm^zn~qzi&s>~s-7R)dpCp7rTDpUh4`0-80 +bG2u~wNqOgXCG^g!)<@+k-h^7#QrK( +He_d*{HCUr$b&a%wy20z7gv2)@R5^$JbFmvqo=$v179Qz{e4Usbg35-i5D->U7`i2(1x2|4sU!4E^iF +t@HkI#nEab$ty|FbP?vP*SXnK}KVo$#X;zFLDZO0e$`K;KyfBzg>TAaeVncUa>jP>nu^kx+A=-*rSlb +r~k!45f3x9&p|mXvkJLBBTf3`K1-&Jz~?ZAjKsh$HDnXTE^JnnDSX0F_cC)L81@cD+eTL~Nwz*VNHB;5uiLS9k!m6|~*J~)Y+N?i(jJ^6p-K|Qo;AMW +eC~evMju_zXHQRT=#C0WhDU&b@oT#5D216FdeXJDa3AG6&R4c*foQRo`fLtO&c

1aUiEP`WY{t`~$4d& +qY0$vI;H!;IZU9~hB3dWfjt?VF`STP +GXuz$whk1CqW87)H^Ak0gX`j@di0A>}jy4ngvk;7u-6=_!RVJ6q0#xu?!A$|3FE@Z^kJbG4+ +`Dj?S<~{^Z)AqbNA_Yd+0yGiohCiQ!nNdbRa7!TDOLyw>$2mS1`ZCn3PLWs`c&oJRc3?onG`XT910s` +quq25T|r9yMW^D2hj%n-)r}(D(sMgQj*nUT39teB)K9Z5Olm(c}{1AkTG#Rq%z6R*>K)4jbrmj| +fWjNumdHQTmRHkg&%+sq?xN8T3B3XW>3WWfvPZjHJk*cOe)6~&G{*;?#Oeu71S(|lu};hE>2GuyVPm=|L<*VWa3%|7h={*HqQ92TDQX(T`(3uC!V}){VxnsB+u~5$bU0&<5kSY|lp53L=XRrE@y4&vo`*t* +<8fJtJ)3>58vc)%B#XI}fL9689nLt_;ajia=k`e&#ve!WB +VujBbw@Z+!N^RLu_N{~LziE0&Hc@FaWFvs5?<^ZgFLh%6qJebM{6Rkj`H0Tw2w-BiUtq8R=Fgmk-kY% +YzH4atsB=CE`Xvmw9raZsWXtLREQDo?SK?Em#uAn2;l9%15dtVnaU1F?hcKbc_eCogRc(Y(b>hb~cKCC4;i$8HV?JV=M+q +H{5V60kI`K%9D^OS36?w7{q;0x4Gs4#-!(gAYi;!#va3U_?PQyAhvb+EUZXzpnX)L`+C^=bU64WN~1k +6>HO-h&3*m1Tsm?YGJp|#<7<9s9rkmmgZ6^&%X9gh +WqbETX@P)vv;GDvH6$NS$#OjnMz*;I@%zErfkgk~%#{hoGk%3RdXk?NOE|Te}n2=L;hPD`F<0?%~W;q +&wU!fI<;BRHnP}-@TNUdCqFKPS+^OQWlr*q}$KFN^pNQ%)w*}o~3uu)ckIIWB@0UB=vbXZhbd_=K;4x +(5$g=G<%D!!1>U`5|&bxyXzR}6u`qLRc)$AW5$Y(^W>bZ;>^O!EnJfEMvnSkkn^CE+@g?-pD4r+I1TM8aj}8V0DI +!3zt5Ar#_@pr-l(Sq~T1=vDh_{1V?2+oH$Z08W9L&8Fg0d$uve21T%V(6T_gbDOm +`qI&jdBnFU0n%c7coEht=80^|n(E@UE;!u55uK>qmp5h^)6GBq=t)-(EM_H$kULE?@?Ptr#b;u(l{$C&))u +uysj$o<)DbkHptiJ7(cd?TtRJ47_@MfZ`D+Azg&Q&yE;i#jz*AGg9ITVkF1>Dq&PpLuPeb`9hB- +g`?=}F-Peo`MwaZJ-a&##F3G@bgMUP$@5AYhXroAV%f85+>CT^xipm-Rks5-nS!Dr-c0q&Q_yY7^mqL +IQ>rd6k(dyX(T<&~m*B@ib@{rC_Z^P0V!Fj#{mw&4*U}0X)CO>JG#)`iat*;w#P=e6$5j0gGaHi&8O* +@Tl_J+ogoB@Xl~6*cm>ur^2U^d)h{&u8b5@luNW3R(ZwDCJ{ffZ8#&vh6K(vk4^H;!WckLLBeDk{&4X +)^z)u+qZ@_UmV+LT-PzuD2vb1!sn8k9{9{Ml!40C<+ghZMs$9&iSH?153YFrg+U#FPRO%b-L=aH4Av^5zpm&?%^p-W?4rzH`F`8YARc;FH}t~|yKgIWy)|&Rx{bQQbp1^kYj#!bI8r1;%^ +n=TtVM{L8!e7thjz~R`$5azbcDp}{Xty8H8;o|bQUlM3QrGI@>t|yG1L-W0S6bj=klzmtunf-iZCr}b +lX^G$|hUAd%0j&Vq+=VVpgPH{`%E#y>H8XT)uC0`CHq!HG5Fi@NMl|eGu4ts$!Z-pFq#+LjEl`YdM44 +ie^NnPkE0A1oqL~QEiWquPbkOI}Sn829X%gbxA52+Le{D*6z)cdibk^-~iAwh1s{7P92vY02VMn(k)( +5K{A{rf>Z?@Ex07QREU0QbZDMXR`#(0js<yazW*#Kw)tQ!)ta+=OB({yT&!AkaYW+F*@4h!Fj9p=f2L7yG7*#uFr_1L8DnW*IE5-w8X7P +US8ic;}iICngv2-eD6%$*a`*L|Disx**nQJ!jLR&>!V{R-5vyj+keJ#fQ}^EPM +NSsXfh=--r>SD7Dyx&=YGFfAk~m69Ncr}FanETSqz7Ua%8T4wzo>~>ZpA{$0K|kr!px9(g!yDB&k$#n +K;%Ye{_5R +zD*W+f4e=D38qeu;?8I#NJl43Ycdr~y5paiUdvKA^dTbPLcyrAPBaZ(LW<3d@bv_b=t7{yI@^iD9qY> +^MrJ{|S}+o}=8hVN&?Azmy*?^@9>0TD-%+`qfK(fZ1L{j1%U0thJX&$Ipout&7aFYs>=GHlEyy~PZOj +rB(K*S~UUOp4g}vJtFgcZnj95cm%5f_2yhUzMx;%KFt6zfnivaHbW6qoW4=!u|Em$%0{~&@Yb2!SaNqVCF^ +Pr?$nn(?TYRt|LN!_gS*%y$kOh;IpE1r +PhLlwlnLQ96dG2GE=)sPw&kSay>jjH9}2Gk0@N9eO=M{VV?+yIIPl867{aNMivCwpJ-e)sgS|NXc7hk +O0U-|il~vLno18-D#dl|kj$nY+vJdV`MROSEj(ta8+LmWytAN^W5`YD~ljK))e!tCIcwp0?J%Rg_{O5 ++4R6m=B{S5NkzUV^jT(c1U1y{zPsya_ee~OXsY1y40F#b@H;nq^87`vB@>qN;hA=nXj`imEx7f2yNb@ +PS~}`3Ke1jdruYLtFE=|z%DYaRRMD9x92rc-3AmiIT=NlKv(z%V+~-KTQNbogkT4c;_ +Q@$aiSi0L>H|DuSYSjK6B5jcH5>~ +lsjZt}UuI9+}nFFGu9z~V5vP(r|tC^=WM +*U!(EY-AvHSKEmJ&&kdY{$08j#rAD*a4xfx3xAd3IaeeE#)q}VqEhD{McFMQAr1Vw^Hj5!D(PZW1#9bAZ +*LtO@#TROE8|^lKb{Y6x$3~3O)vvGHkG_FNyBkgw_tyjS>yg{oqeso8ecB$lEk1hWG|2^bo4#CedYbe +~Pc}+Vtkxf!uTR{Lz~-Jb-?gtw*L0M3>HNI#u_7PTc6suvBQ#nYW;;5v6FL|3iAViC{S(y3C!;G^TOP55Lh5cQ^PMeW8CJzyk+7g0Oyu`ugZmu(Ju}G{rxIn%xFECHS8z!0@XuAf(da=SwKJpmxil0cdD3lPe}yS77PEdK^v)s(uwD)PBjp10E3Hk@aAQj=FBpLg14gD2{ogXCBC)k#OB8joZOEyq$HG#pdHyH6;)*X`V +)5;o!)U-yCp?;R+PlgGWY5cuutd7Slt!*3iIVI39b)v&&sjy~23fVP|V0Z!5%R~fmy<#h)%#q3g4KMo>#>l%p0WR4mw&!0MngOg><~IT?g7BT +{)G`QaeOUFsd+zpRq+~}6%f&=2o+J&H;whd#t=2}Y37VD^FjGUrSFBlCo^w)lL8D9NCfE?;CM?b0Y;3 +X9_ASB%#wd`Izs+W5_;wF(d ++ehgY^#l9e##?zQC_%`1Opxo*m!jBaPi-65ABqR68pQ8k4Q +>Dis7nK(DLxVt^w)m7D1{aIbrfuie>eSMEQy{Lcux^Pv=zV9f1oQBVX3I2&UHG`=#xWySq2W1Rr2RHD +qu}SCn__b(=_x)RA|DA*_84lPAll51ZD{dmB1~EY6FIXotAlKWV(R+us95DKLwRMR>D_82b=HgiRiJtrd=bYdHKWtp(0EOx*SMTUeOfg50 +kxA}Wl34B;{ChpdVS|F@akSp--lu_|wa5ib*2S%;Z}APsQ?)Pg0LFc%n%7n`L8#dB^_3!srsl*U6a;? +wwVMQO?P`dzN(IHt~yo`$_q$=e<0+r#7nk!LWaDoVj0tI-SQSzwjc1M+;U-;8cB@=CmWj&6(Se&_??q +GxWCNJJfms1W#5=a_QsT`E=VI*n?ufh;TUTccQ9c-OYBSqs%c@_OKJSTY`6A*E<1XU5R7f)x^Wxbb<1 +Zqn$7FYOtlFd(#0RuIC5H|(l%oANoJ4ks$0{E*+W=uO?9mKa?xW)c(4> +Wmma1Fx{EBtD#lKwIuSyOBnduh%y`%+!6mQd3LR()gSbE{H!4^vkDZkpl=fufo8x8Oj0Nt6$%`;ZN{C +-Kf7464k@I3>b+kqdkhWKmSJ6pY|74lau3~$_eu)38DAFOJb^q1%1<$3i|;e6xv>le;1)m4Ye1d5@U4 +j>gQ>f%fL#vOV+<2dQXMqWtJh!T14-o0^k>*y$HJKmuz*Oan=EN{64{L;R5_N1;t>n*QfqFazVgB#S; +u3CAH>tL)o;r&%s|CHB2bT$hf=~^C>wbw=@n9N%%%*w>`k{rv<2^aC_(D@|T^kI-d*xA7%RTYqzyGV` +L5`4fqJFs&GBQ+C_b=<-T$fPV}QdWZcYJF|POgtYfEyv3%7WlYUw@yv$fLj^bJ7+{Z+VK9rr&bG8H<4 +m6_#-OQDKuTbJ}bjVq=ysh7n$tZ7cGKoD~qc$*56AWDc>9uk-(g}sLYI6eLAv94}xZR(D6;60AAm{=~anE?RxZiu~HvmiQ@*wEOuup;3Ed9z#d0E +UTe&L6shM6a>dGl9=EA4dt7)B9vla7I27IN(BY&HX4rOynVW_S0aBYM5q;$+eUZq5xqEkqDKv)f{DbZ +*NInc3O1oL9HPQVaqjoNW5=|$ZFIGQf9)E-H>u~6H)yxU$dZXz3$pAA1n%@l`#Zxw(MQk$;a9`q+*FO +(j`q8XGp&Tb6p#4WEy*fyTAoW>4Odr8qLlA^A)1EEpcPUt>l^H@vOh$I=8Y9i-p`BAzWOM8oBIMkEFy +fL`~?nq9Z*iEkOB*`!3T3J8F1H&3#-bFudu3#t3Vb&a+F)|oCYGsjFleLtqIr5O~Gq&5kZtNfB-A=ms#);Fta2IM^iyTvhqTg&S|!)s3(13ALp +g?H!S6*mxc$NZqPKgJ!qv~KWQ;Z?G6U_<;7=t=hBzFKJo +a6sWs-_sH7U^jbal^)vTnIu0M}h`f$)kf#$;+-g?yfHHnGc9q$J0@4Roequz|2S!J~FTvs1&bF2xWQ# +CHNQi47f%aU5(z^G4Ja3Th`A~o2)P4befu_Y)e)e}8`f1ge8;Xl^#v6*t9{oCKKNW4Sb0A%_z +*2O_NuweDC8dYo44pDOm4j4myClNjTD98(*U*3e#P^DG8d2d}8EC#}Z9Z8$45U){Zpv%EzdNux1)jL1 +sQU;igZkDEGIpaF@sSjQiFGxO&JUgJ^)R6!0bWkiMG+6qbSRd{l +;cc+pAcKY@n>0NtJg^m3nP^CxfAu<~@3}sr?TQk;oS|#0QGOp(yr8L0R)jp%8x=toD-@T@axpv_;c*s +{KxHe6e)yUhQ13cCKC0&BNs62OdWNzTxy`790Qw*E4FVC5{CG_@b|F@dioi)im8GK4Rt?-WUwc1Zyq=dL;Ohrtu>c(X2iwPOu_%$QR;iBjW^ +w+9J_gQF3pY-0YTe>9Fc8JI%hHZjG=RWcA$xeTwy?_7HG1eRCua`Q44%B2&Kb<#?jtJ8bu@Nl!&+hh~ +O)PM72p|-TN$z+dl0qpZwyLNHQ3-MwN+Dp3KME=6aYYgH$=uDc=bH9o4mxp5`s>S|(`A%7cn=N}~ZkY +u5!LD@9?n7KnDyvj|1L{P#P}N>c94 +d^1{M-XV}`a=AxC(ug)burnO%r%j0U}tWXvz{``V^>=Xz~4W~e%iVFP2GXTzA?${8jyj8)G1Bztm3 +1raMSvvMV$;gH{;gEAb- +WBBk!(HN?cE%Whkx*Ej%x*DR?o&pctaZIdTg>;$Yj(u5IHnxOKjwsvc;(=-XUG4-}T@0aUX5FZNaKuK +V8U;>ITGB5}N4WXi->2V@)PYUm)dUzS!Ygkx?^QzD+y=~SxH;6h@LeAFHA3NpwDS~>_x`i +Jo-L0@w~7q_-nFf6o)kezSc^mh&!xHPVM--?D2eY?pz>8PLa6exW-$X3>flbFAZpzUcZ_5r}wG-U0|% +lTG6K#qPI+PW2YFItvTIc^qX*g37e9ZSq6qv^hx4A4gya<$b8I8N?$Z@{Kua8H-tK^x(MmwnXgdu(D* +cdSkufXyN6mMF|N(vS`yIG_(|!*W$Bif7qgT(M5cw#}~+_+aP{`ZeW0Sa+z4LJgj40O{;Z@2JeLmUWU +Y%ijTCEI#L9kfaE%N_=-VUPbL(OvIm**oiy7YGR1AQ{%0njNr}!W$0Yo%;x8y?Sw5*&ID(KbY^VMMO? +f^?2F?KXvtGh_yHB@LxEZ@aFTpY3mv0^iVIBT4cAfPItCvabcIfX{I17eS!SK@aT@fZN4$tqEvn{K*h +Q$ja6_PXwZ0ZG!tAxW91nJ+hX|9Tt=6OnKtvYLM@z?DYQJ^hSnlhzLDY0ZgXPHtuvYHMr)rs|LtmfM}9>K0&t8fd +70<%leDwpI{)@n8uG><8c1m&L@ht-&H*ysq!Sy`f#6(&p>*%q^^!gz@Sd5A$!4A@YC4)bQze>!;O;Q^o +&g7&2vheB94<|JfV4eVM(LxGb=#UqLyl}N22H815CXxRieH^Xx1>XdcRL71lUWxd`B3|iG%33qStL;(LoS{Fa?6V_v +p7+M*>wHjQ-(pG;O}ezs^Q=4O9lBTU5H6 +7E{G8=GoucKv<3>8KOq +&AQ)AUTDt-PpzxnRxpML)LpMU(D?|=TcrK$rj(t%0=VE^{?lP`bxJtq*}!4!e={Fl$4{`x!5pFaN;`| +s(m|MdA!-4OXysR+ntKX~>l_TMj_{nN8gpZ)ULPn-a8_FM$oXCMCS&p!K$&pu{<|MIgRJ7JMR76JC<2 +OoX;qo4f8kN?UEN65r9NP+6N8OVS9@ZUfAU?MzHg_)p!@&4a^@&3QS-#_{OG}t7+vw?r_AHI11&%Su? +&%b!j&C-m{72)oG|KLae`SVZy>xVycg5)D)0XQ!{{qBpWAG~<_p%V@aT!o-K`Rf-SeecDSpTBtW@rx( +_{Nl+6FP{AL#Yf+90#%)9#l5Wn;a%-*1lJ}4ev4D(slj_VO3-wGRW-&avHI?%J_e9eEdAiK567-~=7> +A=ntN{^aZ4xkU;XefcGb&WNetQ$ol2d^vU)nn0gu?FPaA7&j?ub5;x`QR5N7aB#H4a=`UTFc_v2$YY1 +8r<*?43=VFMN&jR!S4gTk{rO84$rHV}^cv2V4QZnxyI +2M|2^6R@7|4RZoy5h{)I|vjp@!ySxG);0<*zgE&LF10!|(h;*T4>P%I&C?L`TBbbrY4x*%QsiX +RtIpMU$i4H2$=s_$bjvu_4Fd#$0@{JN#tx#!U2@cjwv6>p#WD;bqw6r6Pz;9BD1bg$^y+3^YjjII#XC +{5S0T@GjdBzRiDK>s$bL?TBRVq#;fUS^qs^u~I +2O69m`;Z~coxf)PP_|3*8SkYt;LzgMX+JZ)gdD*7oSpV(Cx;pF;>EqHY5cvZF;I~;LbA<8{yNDA!Ta@ +ID;zGfIUbh8PE^4FKhnJZyY6?iO*_jN9_e| +D)-ZUl60jhTl+0hT&u8wksKTWX%9OEjE@{ct8pm~h=Hvx+14=UC^7)wSry%DEOD${M8T%>su>20|3l< +@f_4E<5N%`Y#E2mqh@;6+z7D8Ovo#1!V&FhvZY+8yaOF4pJ38M}9FzQd8c_D4rd5BzM;T;*7R&ESwV6 +Svsov8ZJlc!A|B6|Ac4MM3KgthH&B=oCrz7qBKLh0e~ucd0 +kpn-X}X-Sh3nksm)Z4S~HV=c`fLOU>f?k6$&NgPFJttgfZ11?*_M`29e7X_#650A#4zzR~dg@#!LtR{ +#(c)eQ_N!dui{217Zl^(_>nbQP_fU}-2b8p6A9lqG^b}1ZHMjAK+9c@oG*1Uz=N4;U@=8nA@GJ6MiPv +B_Dg7ShQHmkglnEuww3#nJWycFqhe(U4~M+rwu=F7_)yJSkoiv{jCysH#*AR(u59)HSD>^N5gKM%E*7hHJh6p8B>trsdG2sFuuJ5cNDCpqi|C0 +5sySw(i24U*b~^IPz&0Gg=JnFTOxWr>BF8=mFG{@cFo`c%$mEN;t5!aw&032pA@s16O+%t-bC316Zl@ +Hl{bk5AL6oAy$1bY5?{6E3L{TmrK{{!6v22+Mg6#jcTd)BbnB!oTkE@tdKK9iL6@kYe4e3%NeAf4rV&#~Jqb2E3Q$SehKxhD)raTpOz;&z`(aVC~!P)%`R7 +cCiVPQC%Aygs{+(WP9!@~(mi6j$!3`;9ODt{`9(d9U +Cc3hm@vgj?MOoh*+ud&ex@6D3&9%_Y63e65we{w`a +wzuS40le~GagdWUI7pgYz(FrYf#*@o)1xM?t>}%?@qD1RRhs_bXfzWKbv0LPoF0rB9&-DnM(a1=s&U? +Cj(D=QUCbuYh$^KGZa1Ylf{nCIK#~)wwSmxV#q24?;h}5PR5V8#^MfNBbo0AuQ{)t$S#$`XPF5m#CWw +`}-aB*r0P0u54>mZhRe^zW1$HidnvTKWX^lF0mRUd`NQ$OsoV2i+p|oR~JH50+i>8#CBq5Up9G*GvVK +l-~?hv8@^rGWte0g#aS`U=^18wj@p?deF(c@A;J}>mON5yd@)*g_pJ?ly7J`u(`z(54@5){t0#T*8%{ +WQhI6$kfp97nFB5fTxYMLL8cJJaGT4-i-UP#r1q&nrQ5z#b8wC0bKAgSMCX4kbU@b1r4D%eB2b@bzvh +gpuZ=`Bp7fYz@ij&IKH`s&WXyaT0giWM+`Srl+{Mc+o3`VUNvx!muQ7NwGSZ6yxi5z@`R +46%?eZ|Gn5IWrX>T?Z6?$lNxwW_#&G9)4U#6j-6TmmQ<@}bBq@g`nMo&!u3Ra`rwLd!-2Q`C1(KS8@J +rGCHeT{q)N71EccVf=7EHXdm4O{xZ=e>YwEJD13;7Rl +HXI6|9~A=N*up2*Or{@t}9AKmgRW%>DrMTC+p{;I3z~Hh(k_zNSeIb|Gs?yBzVptZ>`GZA%X5jccam0 +90Z<;<9NABg4=l}EN>|GU%q5s%Lky6An;mzITR*pm#3pZMcB~cLZG;@O}h;D@|JSZ3G0dO`4N@+aH9^6C%yf54|jlIAHlERO@d?B +OE&9KN>5Xw|o*<6ajmBJVdsT^OrArb_ZFW=Y^7nGpbaVeTU*Iy(#&fhq*iNhK-L1J +Zd4q9DT~v55U(hQE^JTjtX+NarKr2S|OIXONg;q9-H70%`s=PDCn0Vh)G|AOHzn@jV#NW +R$1xX4MQ=NN_|i`DBG<{mLMBx2KErRp;*QVm0OqC)_i<85my)QuRk44PQ>_1Tuv^29ltwyCk97X@IDx +cPvh$k7ay+$KunI#uYVU8@5Ryi@8a*{^LHb0^4sO)BXM~&xgH;XJUyC-%a4=Gi>ni0;v +G;vAD_RUKtU&GC+F8T6bxzN{F#_~P>S$@rHK*W$y)>AMq1e0u`?9lbq0; +nG05c@zu#l98Jbo +sG0YZi!+iGwF^iWL$)3qmkIu#Op+$3p_ +oWR$)Hn{c6J=4c?`X;#n_Be72=cmRlqKTDtiIL<83@EJ_3BZ +9>Q(~}fH;a=ZAzhW3*=i|MBY*4k9HRJfs*-+Q66bcD-2OB`yn6ruvuDIt_NyQ4mm~~o=V +K~oc}S`NuodXRO;RZo3OJJ444f)9A8a|G!^_|IfedhHYqCK!i$A~oA`TD5KIR{E=O0l1>sEj_`(MOQh +r&7-iPuBY;7kB8_y_@i=+B~ChqQs{Su)~`09^g8Oo|+MS2zH+R^xM_=zfTm`3->V%{*vE*Bg0g-b2oKU$w?hdTF9jcS1I;3sW{jN>RD=+V5Lq#6w7)ge*94!yc& +uZ;+5jlP!H1`XM5nrWANeAP-3?*cfsSMF5{U+x)A?zsNXMFPKu9CHZ)2lHHqJ>n7S5ZyUfEsU(TR)h7 +xSuf=+H`3a0-O32iqLisLdrOI9^#pkw`J4Toi_eDvfp6Bl5)Gw97uk|a)QNY6l7FF;w<)b;9WQ5lo_; +q_+ZemJO;^JOT0D_hcQMVfpVdzUbn+Bx_`?Z?uk^c>80!r1j?QZ@2gCX{|2N0L1WY2NjH2O=a3o!$sz +C9Qw(o=1K?dctUo{Udr7ad13$$+R4E?8ZXwJDk6I^_$VGf#K>4V>U9N*6HKAd{qOnE`#o>!dOriJL^t~JA2%tz=j&Afst7-dXK_kW;Hh8DxMx$0 +Hz+ExFP+z=N`=**V$gQh+7Q1kP>Q$M(&Zsir2T4nxhXP|j@p+(-UthYhmG@RvqoEL!!}?GcTG+9W?<3Km8!l+ +%mXjs{zl{e(7)h4;5xM>WqiZ5P2>3*gG?ECHaj=si%H7XoWz2LD=|MhmVktwk73T>)?5j#{PPK&}F$Q +^5_cH}HFfr|R~68dS9q?XebyC$N>*GJMZf@OTE0mu@!autqjRiz0A25(|KZt}UCZNDgD1xQOb4M;qH% +N<&$Q<8e|%#WJ=gMx8nr4rrmoI1Rh_%ahB4m-}KCL>TfR0gnD`i05^qp5nyK0*c4Tp2^|pq8LKk-}5| +L7(y2vq%`1GgFl*!VYmZBs?J6YvM3!pePL7$tOj%!#Sf9nM{o1b|EZLSVXRS)G|e^=&taS#BC*sg6rH +5bKu1{swHa}x+|MPye-i_H0RL-b7%K}hbAu@i11n5v;FfN9yTZ(3u$tS88IgH4oFp_pQ~YMhH9Z&{Hr +Upqi3&3~>S$#J0%4rh&Au8-wc(9b7DugqExXQUb<~WgyKBv;BMq!#+`r3JTXdVaK`I4%49d3#)Uefv& +5yy}qc{^-jCNk8F;pToMA}6jn)UYFl;+-D5Dy8pi);+4wwART$#VyxLM5Xj49N@w3DO|KX<<)V)VPhX +ygp~ZgyH|zK(wX5B2Z;SMf_%Xpf;IxgRkkQ<^+P&S-Rexd)rqZMX!K=6Q+|TIhb+4v)+i{>&mBLoFZE +E1^D*~0Hsuu@6f%>W18Ft&4RB_5}et`79+S*O3C4u*vj)C*sle!DzO)N{k=IiQQ=ZaJ9pC}SnsGr3SY +7HL-<;;RRqSVuztl%nk|>L^k*I~ikoC=w#g1pf(b}3jq}7)gB +y|w38v`H+_XqDW%m;4X0qB+v2MD0hDy5zIJ?pEGn_MUOYC0S>jeaV8h3I +WnpQH<(OL2;&8CB{WXZ24~FLuszVI0adkdlRH$Eszolfb&$aXYNLyRcSTdT;|m-tRbGM^|1VO8qjmzb +AOyJyLC^K$%QS~{a66Py!lD{Bp}jWCb1{eZUrQ6bSu|X!ea +r%51q86(YRY^7Sp~1HB@~~Js4#Wb;9o<@8@0{-C~)m6E&TO^3nM-k2Atw2205b!gPG#tS)r38P=L)7s +|&}Chr1|s>FQu;xLpn>Dvn;ja|NNg8%q64dSn0P3uws_|rqf!Sxx*I_PmhHAwTdL +R`(NRX8z)Z3Xt?XcpWqaea~;=P|us@_PqHp>4;Vdi4sZzS;<(*UHSLNxdq0FjQ@&vl7d4m#ajo3SX`q +d~OBwrwivcVh;t+_zOQaCUwF`C7_0UNGA(v)7SGxtj5@hTWm~dA0~0T&>O&GA7Xb3c{Kq#V$a)sg!p1;ri_KvUnr>WslJ@|6u +@SXr?RDb51L&T)08#ECPs@a0wZ4D9Jt>pPjs|q#%$@K`6?1?jUY!Lw#E5i +P;j5su=hgEj=mUF0iB>q@|N^D5=*N(xaVpjJw#{twsJmsc{mXG>_NPz9&_+Dhd3mvI%Nk+7D;6^%%$N +Ns6r33-=+cgI13@wnutR*XM$nG?`|(L*atBA1CWcYJIIN@MR%W@*Vky7|u=33f5QHrXrnt_@bxtHAYBBnv6|V~c5WSb^)Mm<1H +IUGtB|$JBnb?2h2ok4A&nUNq?YX%}D0^;vnk1Q+Zm{N3A^%i2F_E*AP)mpQ=r`hW)j+9-#FP;>IxT~+zxCLC-Udl^vb&``fr;R| +eD?$?BG#-BoS9M}g~&KTkq%ZX6o`aXqs^bLzChV%H!P1_uniG$qaq_0rT3nkK*Z(Cnkuo7cx@5>Ml-4 +328AiZwH}mDuAVOqQMzCUJu5IrybFR8j=eS|9k^+3Do+xa;%_8)N&?T +Tw6Y>k|v(sxD9qbHS}N4u9^OuH4%e+xWk8RqE@$a%DDu?q8v1v^>n&_3=S68LVgSx4-t4jJJx%nsa9E +y7SLS?!9P6)k6L)V$J9Z-+}8?g-zkvK`P9+{PN*uW$Ze +Y9T>y|5A$&db_~3h{5d7dy(6F_+H*Bi-VO;d`(ZiCaTtIZvGA0V{kK7D|(Z_fdcHW7&vgiUJo27b&J5 +^8~Nd`)q%t&(kF(VO +o$htE_oGySMdP-arGotXu093vRs0Xc!(4K?Dp#r`4nVxRcjOJhKt>Z9!=9MalH&g47{T}n?G63uS1C{ +?dy=zBEAc7HT-p(kzuJv_sxQ4Gs;su?}V)1ktM_+YLT9a&v#yMk)a2yn?cb~MFx+*ki0H8HF`- +#W@oT5cIi6^y{xlMDb#Bj$@G0cufCf3yhliTBe6%)kj#7evgRGj* +G;4_Ad)zGJ;I|BT4jUaz@R)(jY!>HGsis|U+p6MNLDu3pg3Lxr_`xXt0^vGdofppyLw8C +?3h2jPJ~R*>4$#7Y8?7v~2Am;x1{h=9VYgN^1p0pcj=%bk=)f@|1o?|3E0R?w7->qb%1B8o1;@O-U!rZ;S$vP0~_ +1Asb`;U~KhHBcS7bf4Y03Z~0XpqIOotJK2tGoIf)w7N(j=Gz2CAQeN$T-tZC0SN`yP9&h6hHGgsSUU- +vcH4jigH=h~2l4?WG6O5c}OIl#LiQ_qRbck!q5%mbR`WCScalwpGLwj2h +auf|!6&Kbu#NhS;)-3Ye{`sQ_WD!YS$+fWhlTckiMIS|AMNgAGX%a)dq~FzqHQn|9QigqW{2q?CYbi4 +nII|J+B_0uJ>tY%pdGk_}3rTHCUO&&>XpzCv2v*3@e2LQ|Xf{88+4#U(t^>`04FnXyZ^{7Z+9#5osT=O_RbZQLg%>-WneH@{u++nt%X=unGj= +H3U9acnxBH)!$zuOl<2n&U=fBTR<=~(!ANA&#`$i +|2|g&74}|SL{!t$on=Q@LyG&#lr{WGBiCzv*_UD7rvT559;xr-1Sa|MLiD+fh8hHb@%$Nlco()wat$8 +eugDOOx8i#Gy+&Ssl;t4;fDIAfdjyl`a31d&a)K0=W0jxZeeD49?n^7)0*p-8>f|;h-q1{=cj$DLJ8m +Hdr-Fj71gQjuF@b*j(9UGOwJoi+k0w^iLdxH>08U&TJZvVH&0LY>S!beg!;_+!!PE@wAE^ydeNBD{qb`Cfpa*5Pv^87W$=Cg({-edd)U +YY!XuzqZAvJiKX~h0hmJ8|wz6xs4sdD_$gHMC~ks?v!0U(U3FY_MU2q#qoHIw)rPlhXVQ(3o0NNEa*o +<%NA#Wiiqs1$%L>!d{wHJ+}Myw>xP)2(aoEiT3MUIbQR3)k8or-h%(}^4-z($yNO*d%Y7;mt3>Q=Fl7e>X)%-!RnW>;NK=!^x&TtLzP6$-m +vxFiL_F1(sj{`DWK9!jo57)p`o&It5v>{_~-u&D{hQXZNrQ~HR`!B&8VQ2JF8lXaRW?Ob8EW`2D4Iw$ +K7mbE6XwVTvVq;YZ4VUf4JWEi@!nu{V*z+Q@=|4BzUXRDmk?&6v}Htmy>5S!Grf}(9Q@8pTMirP|4Wm +R&4pi47G`Z205-MaLdu3^0x_}JFaiiig7Y$8M+bOkM@SV)D*uy6M-*X>?R<>TDW&u3?194i4`S~iDQr +#%6l2o)wgPIkGca@-6%_2PibC0#rr^TD?@pHyK;{no?mV-&zVfGR7Y0X`>Jgaw|qP}j^%}^zJ~|Tk*8 +BFA;oVAAKK?D_~g}pYm4%f4)sly(B&pEo=)uJVe{hqA6{xXO&zz9A+=|TD?Ie>)&qL@1t^{RJ?a&4?P +DeQfLg!UkYhKAsS5&p7PQ#Zq>?$+V$*32a%=c1Frb%?IM@HY=Qwc^+%k&e?BQJk^t_SlY;fFysxKpeO +1Fxu;|wgUP}2f_CJphLcHJ(PV0SL=TWq(Dj@?99v~40Yo~cP=-;u4&uQ1b**vlj?#NY*)l(IJx){uO6 +ZNF@#q0^Ju%E|hD>BYR@jLnVqVyRoC*EDqXnA7Ct($XOdK2&( +UwAj<=2Tn^&x)_Zkv3(B9Xa9@h*DH9w0U}-}P%3vdfsM_hMETd|{MM$8{Qbx4Xm#=rV(or$1cDR>kvl +suqr{{qV_jn>{&$QTB;$;%gpQq2C>&@QAMX|+|AMcI%%0d?}#Elxn-iQ}JsX08l2IK}xx-r1;6+u*bg +XY1T7GRSeEv=dqIe}=Jc|}{Q8Scou{=p8yv5-#EW?MzDxITXE^VN +91a`Riqok()@k~@sEYsBdaM&Es}4P7vm_%7x`6TOBKS)MJn!*JRvk>nyp@MfHKpr>?4k~pHqoyE*}mG +w>#AvR_|n7PXCTO!ddI{I3I-#2?n5`DjTn*XQ5~~WU*=0r>e4k>N=xPeyw=&S;rOnvNy#H +hC+}6WvQoUQ4p~k9&ZBKJKd@;PUuV*)%^431!;y+<%8-YGY9JOwbYHNhC +(Vr9gJyyG!qZ>v<+JR9AqdjB*gD)mb7_B6QD8~mTKSGA9~Nmuz6r`TawtM~G4)-#iRz8MMyntLTq*CX+)dR@5iPdoXW}@<6Mr+ +QuJ^`sWsBRos6(+4itn|gT@Ek?ozvI!S$0%v5M>VZg(1Gp^j6*7!nG4Lo +@l9S%wH8cno1e184?D@Gx7!YV%f!`r_2$~1BC!w=%;iKOF;{Ue(HKbPyCGS6fMU8)lCBaKI55C-}6CRg*FTxzUAgo96$sSec~!tg3WN5#6Vz?+ +NwU>2#L)x(v4`C0+eV%gg@WrF9pks@f2XO5YaN71+d*rfkT>ks#F&jYQ&w1Xwo(D=60!kXNcE +r53fqQCwhFS>~u9m=5}x_#hLxmllPDFpR}ZO=X(C5l0d0W}qGYfK5%Zw%B?W!Rnr(R_`P{oG29(!eEkj`^=zW*F8;tT+T}mb_Fjz<gSPt6L+A};$q{0 +IylHtS=nBq6wD`(z;P|sXbij+IvRv3QU%2uMn;e4nsYv1WgKWf@zxONXx{J;|qpB7qzY-(8Y2T)4`1Q +Y-O00;m`Rt8hVnoZWR0000v0RR9b0001RX>c!Jc4cm4Z*nhVVPj}zV{dMBa&K%eUtei%X>?y-E^v8mk +4*}~Fc5|Jh2CMv0fM@4Cn&{*pl2vUoYr8P36s>~?M?r{MYr>P@9{<{RiSxN#Xbo;uw7tDaPx_zK|{JM +%Hn}<0-Jy^ej)KHHNqX3F>RLgVAG5H1de?R9y^L5cf?$xlq!q5#y$&D*Kh)L6>H#*64_6TI<#&^l+oF +;7dCIr(oXzC67y{~c6xu%M>VdSX6QjM1d?^z|5K}iPYh{Wxy)!amw(*0>_+hiP)h>@6aWAK2mnY{22* +``X$J`c002k@001BW003}la4%nWWo~3|axY_HV`yb#Z*FvQZ)`7LV{K$EaCwDOO>dkq5WTOo|G}XbBd +whKN*vOpN)PF(YBp!ff?3uo*seV!YJYwG2}_zZT!5K3Z=T-+IT~$|47%^7>bINi=4ACqj5LftgX**;G +C?f-n!urZ3&UE`o}twWtsa=@gh7}GZ$s}k^=?w@BZl}==I>;Sk>kUubRU-0;aFi?o)oq>!n&FH#`e<) +3crV9gl#t+oQMOQ49}j>dcWNeeJn-A~|1I>MQ_vQ1`eVs68p}MfYDUPy7v>?W>sZQ$^mFT+C$b4P!~sR`nxmay=g()Q7~~(Y%|R<)IKyP6L)pgn#FM2434UKmpFI +{yOPh^)Qb?;aqS;?xSG%I~9gKSox_~9I7fCh+cM)>c46uacu3HkJ-(Z#lQp2I_#o9wL=nNECOgX*` +W%i45q1${zH8F@jy@$w7-duDH&STD;1e1-m-v2)F`B)p&0F*!}JBOYf-#1d=-DO=o9%SegY9Cj-bi|W +5F4%928N`c#i0(@5kNDpX!3>7*uPu{P}73>cd~q;po1JzD&?N(x(6M-Pm%WsG{M4Wzjp5-o(qrf`fjE +OY(svriMKIkH6!Ya>ZZ#jqzF;0*(JdcU3Zj!NI?vgT{2Oj_hfbZgg^QY%gJCVQ_S1axQ +RrwOCPa+cpq>50L*rI2a}uhLECrD&U7LO}hcxP+%_lDA3Y5CPIrENyTx8{rBFH5+%`;6AYMuAQE{z-r +e`z@n~^ql(x*C8Zg&a!hh}By0l8kVdR?>+v}_AtDRN{)`%uAo}jLj&Nckqg0Uv&6>ltP!@3o}ZZ@BP_ +G4jTUaHz^B_-%y?2eavxC_HUkcNAi52CbAuAkir2}jMFW~@ibEiWHG;?!3^McZCqm697X*`~898(Fw~ +e|^R95%m+7qT*K655)oOUR8#z**}ZVUp9*sTYR|x{eE-5_?Hxud<8pJ6rvWkC^A1pj{&(`4WG`L^`EM +Sc(4G=AIy5A+sRZ2N8zlGmm1Il&HM&Bp4mgIg|VVc?#8zgdaq)EH(WoM@%Nj@qyOq-<92Q_u2~|OE9d +hv55e&IoSNk{Fdeq*0oH8E#Ex~48%CBfkOurvv^uzm8p3;Ohq1RPdp@Q#%NC#R5!hvqXB7P%$28C!4>jW~IUO%p4C{m!hjV5_phCC@&U^;o~n<#CWeHXI~H(i +$HY)m@+%15128kW>(?8Kwt_xXn<$L21FgsV}p^ozZT#h3ICbLQDmrK$)lj;0pw{1!1BV;Dx +g<~yX+a)l6_lz%L=j`8y|2p4i;LX8w4lzC@Qvsb!FZn11cf~ElNmKZ?{41)9Z^z)oJ;Gs2}&e8ki;23 +Dml~5l0-wJ@me))*XSPRsl-`-AM=?`=Sy%crECf=z)E0z9YG&Vdw}W^n8l6``o!&THaUl +dtC@7_Z@+oNHJyDks$PzDq8CW3S|&_MuyM?Sa^KdEP|cJC*ZCBE*&@+GLj8b9tw$k9%%_-5kGtt6( +rBt!wp{B`rDM*yTd#Dv;mXGa1`P)h>@6aWAK2mnY{ +22+OKx#&6p002w_001EX003}la4%nWWo~3|axY_HV`yb#Z*FvQZ)`7PVPj}zE^v9JQp;+?Fc7>O{11y +y4y68oKn{H!0wwg&yHVwJqDGdCv~CF@|K43YP3#^@+R9*IW_D&Ya+YOj^0GzHO7q*lzM&?#eO^KB1DK +|@*q|fhHUlFTAnXcj)mZ`)K>=+8swz#Qsu7I4X|VBO50%kmyi;Lcq)8?|b@0uJErP7>1HqHmyEKtUTN +NYRsfhRDl@DoV3&DUFyTS%3k7(;T>~Jp-oK{dKBCI{FzCvU57=jP^Y&;*w^K*Dtw!=TjRA`a#UrDs}a +Znf!w_L%vjR$UVXye?=w2-ni&QMvNvh*KP{c`Qy%mJD<7!~?jlnN +voE+$@tX_yFtoj79RZV^Mc49%`GG|igUIpf3;NN*)I#SLE@s~t(;%&H<)m!>2~bM|1QY-O00;m`Rt8f +n2Y-DQ0RRBh0ssIa0001RX>c!Jc4cm4Z*nhVVPj}zV{dMBa&K%eV{dJ6VRSBVd7V*BYr`-My%+i)1iR +<5+hE5*cNwMh9voX)ygHU^%U$~KC#T68#=fWp#+Ie`o}M|LAJ!|7)kf+eN9Sx)1r-y5?E^(Fxi_+Yy~ +06bV0a$WPcc&b()xW{3I3#+UiH@MgF{dW!g%@xerlQPO5DLlc_aPYA!sG6?;Xa=HR3U2;siaJEG39I8V%?zuCh)|PKVd<-%zO_`E{JB7q=b)Ju!52(M~&5ThH>6tLsg&98;aFTDaPr8dc{;tpz@Le`Kv3N +{eHWfa(_9xWcVN6jUqE!Hv`&Kx1rXK5}I2C{)|<;-eGW7D@L)km(33HHS!TwOkRp-9JuClKzM}gttNA +I>qYgrl(MW%EHEEXO9KQH0000807zB_Q`Y@W`7ICt0KGT>044wc0B~t=FJE?LZe(wAFJob2Xk}w>Zgg +^QY%gPPZgg^QY;0w6E^v9ZTWxdOHWL1xnfwPFJsDGtL_2ma*Yk|qOPyrKb4k-l()NST)j%X9q2}8Wpl +$W${`>7NKoTG+S#EopNg|QJ0{i~#0ttfP>60&O!PZ)rsa%{ep00{Y>UE}ASyWmiY*|#SR)XEZQ&x$8* +FtF(J$(}3T$$l0mKLj(%vbI2O0-{No{Bu-Rr^584BjrQB4Z-sGL332Q^oAtl2=M3T#LE>?dcOcu9Rgo +zXdWy6}3B1x~gMcSE8MJ!IikomB^LU@>aZ&v2IC3F^_>yI}%@A;DhF^5|!ks{H-1Fxp`0_iKkDHLs7B +whSg{#^m}?1&gVJL#C$%%aX)^#gh7>v;vy?$D#9xGB@C~AIXn9H;QIOGZ1Qb*bOgWg=T{T>b@dB<3;% +^*?X!b0!heH_SvwI+BbRwy$q@cb&YnJD@BtP4fX@}<3>U5y?2KvPvl1(D&sHFXoXMQME=!P}yl3CBV# +z+eyI`k(|N2#g#F_ns3dE6#DdV+XqX}%Lu&7>78OXbWAIplbGLh>E@aONQ-HiFNs*1W)FbcL5Wn9N=8 +Wox6p&ecc&|S_#^YrS)_0&$hI_nok +FeO0E%f^=rvnz=vtdS@60uv;Kr>fD~3~c6$6`(g^h*q`^#p#U+lI>nhEQDhuOOO_^iwRwduVJL9Zq>OX#Zk4lXz0Z +{`SLGh(vH)2D93kT3nTkHH&Y#l$K2rv|8NLjP#2s%3^Yv&TQ^!7d$rItk=1|Rcu!}vlKX|0imOYq?ew +x6cOok$|&8rFWM0``oVbshPeCC}}b2&Z@oYIgx++V9=40T0M~Z^QTqW(P-sJN|%Okgp`*M4Mdmd$xd> +o3Pw!gd=kpVvJ%g*+PI8gi8S9B1C)}rxLjW7mUEr1pJ9<`m5+9Izb|zb;4e~in`g>W9LRVq$|ergazL +Q(XWNf!z)LN*Ar9d@IK1EI2zl3gF*PY|q`LTmjgq)zR5)$;RoxoJURB5`$M!Yx1tHySQ +)Y#3sgtCgyh8b)qdu{C6bFx?I!1yB`;PlHJ>XxVV)PDPj~%dkI^c!F) +oycX`t_yL#GlrcRrX3cY8;>E-t-)-~U%)L6kZkFxS)>S_<(9=yhrHBKR5}?xe?S6bR)Ob@` +K&mSXs87*!qy{-lsADmD1ecG1+QW{NFRcgX_EIm;^)TnOlI!8)+84EAREVlCnucDEMjJ +m^|6&oO{N1=Nu`Y{K|VL8w7;Y;hD5P2~~*$XKD +f#$aX}7PQ>}vR)d^eh^R;=D=j+PJ~MD%>B*ws7PM470IjUrCXC6cvg@~Sd4U!as}=xVZsQSy~#d{4Lm +Fb*IlEG^zG82MyK^Y$V51@5e4v8s~Qk%C&`i%%GPMHp8_Af5CSl-$R;>ZdW* +>@M_}WS{-2|Ps4u@2NP&Ozpp^ganHMV^~Wmxf$2~Glk-eiY=!gj|^6F1>nW>+ALG-5*%OP3BDfuTn=b +HVrms$S?Rgjf4w2=oKw>49NJpFe{>G1-tbU;&&s?LwiGm(g@`aSOH&!J}4g(CEmaJZ;O6LJR~_kwn9h +evtd2u2zm>nYC+71MhIn7VU3abl;c*r2^t-nTZJha$8Nb_w%;PPw!V*=(h1gtDGy +N*=rYc$8}YKO=I2zDv$v~$b7imj3{pdEWw4D$8er$K|x%_8Gwr==Jt+m4|{z1_~zpo`vL)uTBx9L$gg +!qZ8|vGJ8aw<(rNcm!@LwT1Xg(XFU5MPy*`>M=eq&iIB-kA_^M3i!r-%9C?_ejkLIad3 +N!>*;CL)kql7J30?zp=;~wHbKD#2NIA`7l+rwNl|D27xT|?n|GKiwhR4`dI*AcANEikvD?EAW+25bk;u27R_q4$)ykXJhz;_^zPl1y85kxmZDMPxu7`|z02O+?v|Zd$Zs7IYpfJlsJG{pRWY*-@+QQM;jqe+J1QX5X{Aq3md4Aq4x6 +8d>(?Spx-DQk}H@kZ{rolE#4hUxwvy0{eD;8);2wrJPP% +1L-9KiytAjP;Bq;`BsKeh_X@Dn!rE7FzW}N0u|-m@^eih9xs*_7KW?=TcG~PSLO6fL_ei%H;4f_r!J# +&2C(awA^(|rA8uOGbie$6x&cN2T4p+kCHzZwNSZMa+~s5I$6bYiWmBTImAef?<1=?frI7^u* +^tDk>{&~iPFQ-&PPZ($vsyQ%<{O`(I)B(XY4LHePci)P7AqK$+q*=ZOmgbCjIevj8J_Y%+L*l;`3eu= +v6k|nzxN&kwK*1mfSRL$VDW8Wad$-rgDoy-HsVrq_C!~VK-)MNpdi>QBm@N!a7wkh$<0Y22!jJ5DCsc +$yVTT;EU{^1Y_qvwqkYuYQ!VYw!Oi|#x|R@ImQ!N6Em>UE3}^L6T=mIZM^Yt_fOI$+&X#$n*X@V +jGeuT(`L#2OAIy9{~#zTOFg*6?opks$17ZU-B)^YtON +cec3wCHa1a@E;-9$)C`R6uL=JW!%un@tqukHfFD3EsM;!*wX(}l(}{gV$v{cy5#S=8?zS$g!$Wpw41E +Vb0Op_82uuxYE&Sz4!HgUV4IMUE}S(H-Dtzt)_-pnPhn-vL0jx-qndw{?psB9{ +#?w2#QFMf$kU>-5xJ=MZFG+|;FsFDBsGdum9o>6hX`lSrm +sSS`p8kWhChLme)!V=4`6Bhk^^BDn>Tn9Cy%oT-giJ&N;;eKk=0nBqngUf%VKb9N&{>DBcf6N6iqlmu +FY+h=a)q1c6mZAnd6R>xYyj_oQC(-3&HraT>#21%WjPHE6XoV~1opI$tCotWdJHV*Q9gGU|89ygi3h` +Ieo=41MnJ|e)`Z?J#dXuH??>SMCe)0A*HDm`N9U_uJVbdus;|o04p#E3Sf7@;Jg=&vi9=6AWCRyU@y6u;@joWXqx9d*W>ty_!!NK@;CE~#& +$-&-~Kx)qF4;+<@`viXeOuxX-ps;N^zio>xZo9lwjD(rsxk8P)SI^-^pNefF;&3{A>9u_a?coPBYLzs +Ehi!v+pPN3OdWvPQdp)L!b{ti9`!8szPbp4~Ngugd<2CD7^dOH>{m4ncx~Cs5{N3mL`HcsU +&<;V2A3ZoO;PH(gHCyfO(MLFv>K<0#$ch#xA8Vaaao25+af`4>;(f!Li17MR8qwKj8xjS+O7j>xLeS~ +RS1wXIeC`>q)ErgSp?V=by49MgU3gYbn7NSSmU$bPSIYj4xMbff8e0pZ6{T%%+vBv5$GgSI+Ai?D2Y0 +1v_87HpVQe5q&nY=)y2Qq9VjGpBPnJ9*^-M~AGt0(M0Si&gg6N!4XpCPEXsu2U$elEim@uDSN_5w>*?YShaoypaj&Peh2GsUY3u?0LHkxhx$Oq_FC4=LQ +oX$^SN>2(b2-5o~R)TxnCZ!a<>*e4kVmGEZDZbyO*Hr())8;sipq!yX#*X-7wZ<_y|(R7FUY;ElTP5P +a96LB~@IXP)h>@6aWAK2mnY{22 +=N9hCBlV001=#001Wd003}la4%nWWo~3|axY_HV`yb#Z*FvQZ)`7SX>4V8a$#_AWpXZXdA(LmYve`{y +)W#4D8`4K0Y?sdG|a(qVhDsdWCMHH4WcELM!nPO7Tqn6=ild5_k2h*W(fo$gV9J;uU=KXs#fb=Puh|0 +2j?UGw2vHqeSZH={%WH_phsm)rRwdIHvHMEVPL;>)ii+#Z!nIf|^3A6nwzSJeq`Vt#LyFpgvIFf +pU3aw8u!_vYFYNcR({@9iRYTfFM=E1gW81R7B?X9G$ChHJBDEkvI#6v~U^v7K#;;tDkrAstu*LJ(!G| ++i?QCQl1GzQ>W%xqUl=lw+Q89X50VaSt)8O5!Mhs8_3?UWvW-nY5iJZ|1;IA&Rl7fu^2tD@|`;ciUYT +Q_v14yVH;Fo2&B7>|2aq!&gR|?2JWIXGlL{@dJ8xTiUdkURu+zu5vQ!OSP$hQr*RF>N6SeDDcroBb|Z +%=hO_Dn8$Ta+bALF;4Zm#eGyXx|zbSIm +NytH2V;&oVmKt5Yd<#o!_(npjO8B>gVNA>7~HHLecDfbn#;aDH?5@eU$I09*4Nze2Ggpgp1JVsCC#bo +~!Wsk+sW_!%^*=RjoMi{+wxW48$1#YBXjF7f50d8g93Q28KokhQ&{F;EC>Qf|1(eKXk5 +mzpLt$BzS4ZVOGF!EEMe-SLx{I7v-=rhx|F^ICHx3#Jv1PBd!u*Gc3o_~J6r-!D&g6SE;-fTomqL!j4 +u1fu+OyF606XC0}>h^LITrj?PyFBlk0_{v=v|O#OazdUM2pNl{bZJWBdJFm13fq+&QuI45k{9VGb)>^ +%ii8tImL@7%Z}+mzv4H!Kha}C+;~z=f;>aF)#($)XW5~FAJKHbExOvhJTMz6yXVUssbQ%p|ELF~@S~~ +BhNSH%dmu15S%Sg;zQ_v@lQOsv4S4dzRa?E{gW`ixu=dh;a{l(VA(g8?=Lp^>F1A(^A%i+8)zPHAw`j +^Jbp?uigs*h`VJrzKsqB;*Zgq`eRd3}aR&vzc%Y$;Bww?s4>;nkZ`$~v4W=AczdhkdN%w4@6aWAK2mnY{22=qUMF9DLFnmK1vFnB9dat{8CaTko}(UBE>Ssl8jJ}3qd-!q5 +N8@t7m1;>AL5U_7W2&v65ZBzB~_KT`tiX@_KlLdNJy)Q14&VyIAxt7rhVYugj&UB#)0`k|1)M(}ILJk +)-HrD@Cm8U~zne;HO@ILH);z$~7btWqjKo7^#BDvdE0ouc(U1_;EU+CsBBR;5F9=IU;7#N_6IFKV=XqZJ@ZN- +j(G6xaM$_9{X91be)rXRRX%Q84rQXX8f$~QiQd`0Cmr}@-WK#Gp81-9%s0#s6f%1ai(3r$Xa*i-#eCx +ak#OO6a)9I|%-bk4*f&gTh@F|gr&x>`iktqv`Bzk@_n?DhdKjqo@)Ias-XuBZ=L0f_{LgLzvHm~Afbld(`FsYT(4>aqIkRHl_vLVLsbD~q$bp&i}k}{e)DX-y~Vv|?^A>=iBZ~e|-F;d-`#k6vtlYT-K3i_;@QeNYkhX5$al|uOaHef|dAWj#x +Zi_Tr@08ujqw*cmqS*;a0s(~I0Tdbl|)FP*bXQLs!207{1vt5q@X;$zvN*^Gb+Q-7JE3n-Zl%Z`$^*o +iPYF?v7cHw50tI;GuR(hRYew$)nkM@*e@#N?|zK6WtuZpH^V+JO37jWKw<9ylxC;1Lb)e=R54RZ*Bgm +eKmnSc_L9&xdks9BReytpyyP)WD2cyQ_1AjZ7j{~?_2Re&vjT2*Ba}d}XoaI|#4~Mv?CcverEuGejdO ++6e$agVf!)B|Kk-kD7H~gc6qQVMG={{n;2B;qV)iu^lfHt86MhT93XWn9(JR`N8x@h>nMg&juxRUG1V +j^)8^Soe_EY|qGKO_fBpY9S2x1fL;D3YY0bUEuL_GBxUV2X*TG^Id)M8-vP8v?e{RX4xL +McQzYNwcV)=13+H!!^8BKdYT^SNyl#xb%dmQ@KNLs%|a8CGwTw7$>1^7ajEUNuEZhMEh~1+L-K%goFxvOLcifADvJWPwjo}tOjCT@pNzEpSi5gXI{ +wj7jrTxl1=mnZp!S=L2oW-xm_u&mC#9J=mZAWuhJi!L5Y3)VCWAH*_t^OTtQq^^53&1JA0<5vp +O8hOA#_J^b3*smsebENaPS?jYj(MHrD}P6^gmEb0|XQR000O8NLB_@UWf`JDhU7pZyW#sB>(^baA|Na +Uv_0~WN&gWV_{=xWn*t{baHQOFLPybX<=+>dSxzfd9_(vZ`?K(emCI%a4KVA$*XFcCMdQB=Aq4HHxHW +@t&u($#+Z^Q4`YeciquS|MgIGqLsED7g6;0W$Y@0Jd`zcia_(?0-|MFL +SoNzteZYEdWKJ(sy^z^_;Ou4dW&azoPaf45v4ooLgvnQ0rYyF;(>?)Or2sf7`H{zhb`zaz<1RWs9V{p +Z!y&%bb8E8HFbkYLw-n=Q&f-aA`L0SgH90lRjt +g;is#&m+<}fe*uk}2eZQ~@GnOQAbiFejFsgPWyuSLc&)}O*ALugEZcEnuu)}Fh>{-~R@b~qUL!<`*8UFP{pQ}?#_J%K;R ++=VOadx-Dw7TA`93$uHmk6cU{i6Yb3N%3CGM82L$sfddx}iXd%~9doV`l&r=FBPL087@H_Q&dK}vXm(1 +%x5;M`pyuHG9Kp%jq_FxSE~PSn5hVtWff5coz5<2_N2?r$GOv9a_4)g4l~jcc& +D1oTP(^F@9Ky=2vk1MXwklUK}Rcp?n1bQmpRqg859{iY>idLZ{^bu%hW3nj<9{j1gO=powf9>^aj +3|xDNrUoT5rHS6GZk{Hc@uBI;?C_W2=a;GfzsAG1ECJOvuxHH?ylkhoagFl8#o&gFWCVwa)31#6)1~5 +B9>slv=c7D5-{0So=(-dp4B}v>Te1eXneLkcUM?l(w{AvCajzmP<$BF@+=lI*l`Xe4+A^d!xjSw=Mr> +<^6$BK@1O#If1-q5X;(<6~omcNHKCHuhq;ufsdy117B<}=8f2s|t+8k{_RfWZv7#YcrUe4s__?epY8 +*X@035g++5`yyH#sCjp3mW!wCn4|MsHZSW#bp{o5+3iXP7xg{;Rsj)Fkl@EU8oz +J{I016$$dd=cJ2ErWeBBL!&;bPS7AZ&5<`X=IFK)oBT`wYOHj56U}#^u1S +!Ht%XaERrE3jZK=iGZc{}0&73CQD3%eY$yxks$(L9x~Btca%ypCvLI4{4s@J4dT?PAxns%ZI+t8x%=* +zYg6~^7L@MIo<5;5utv2JIKC`>*Xl2aN>f5d;xzHV|aHR^Ib`9=f~pme}R_3 +FVKH)t4Q5d}J9j{WN9Pmy#5h`SJ=2k4HiwdMUb0U87f^B*x>4Q9h@5zkdNP5JTujXVDzb&OwIe6@KWS +DItc`4$=c|in`VdTQLWgWAvEgvF`j!ItF3awt0*geOaK}axniF;T)#p+h?#Bjk<` +Zi&7t|V$FP`FY=}u%ce=%vfi%M;}K`N&)dM8#vZgg^QY%h0mVQ_F|axQRrby7`_gCG#SoA@6{Pnwwa-o(SEd +)`y;hT>RBP|VDD*j)9HNWrND-yDml40`xmEG~X|O4fzU7 +eT8xqvz{wL0ZokuQCJ@a(ETu#+G`ac>?y-E^v8EE6UGR&`ZnANsUiVOwLGE$ +jmLsFDg-R1By6<1r(GO^70E4dAZWSN{docGK*2w27!do6axTIO9KQH0000807zB_Q%696%NYg$0Lu^n +04)Fj0B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%gPBV`yb_FJ@_MWnW`qV`ybAaCx0rZExE)5dIz@|ABK +qOzsRN=(Y|B5MXVV00Gh%NVBCyQ3#B5idf5}K+7W$##>a3 +EC0`jJEip)t2J?erZk=I@gXA4IhT#$0K-A_$s`oEX!FWi_%vt(n;@TOk*Qoai(>BH1 +v&B`As7iBe0x5b}ZXxYPL$bj8o;+ +8&!xny@jR%&g~g`Y9^ORg6^K^Vs*3(587Ijiq4uixE#e!-^S&|7>DwbBj}7n)k>K*a>a3R*xSav8Wnd +2{YKScQqY5IG&->?@IeXj#KjJ@(Zx^}KvKs%&;n+(S-hb{8YnMP$?MpfYYkF$0 +Us8|@Na>Q#}V=F6K@h_@jttuA*5Y%QGwQ8$DA$vc_(^vzz4|xO2&Y_8E!Z%(nF!oq?m2WbXzO<E2BjU4t8A4)ZdKF74a +%2q)MZP}hFZJ3GP3RELM-zqTrV$#6t*a`UWtLWp7pPl&}dodO|nMS^9cI%g0}|Jbu>pgIz3j$Z;y|MT +B6{OfdFAGP*@O}r`HHlBBQm!1(Hho075rNRS?8fE41HHt=Ex?5*)U9WB>4Wga?CDIMlk5pRq +0Mr0_oOik55<|^*OW1IiU*S6e^upGBxj@t7gCA&d_jKqhmu^#f+s7Y9B(PFX!yJ+grSzLjCd0++`R(} +$yF_a13a1b(8A=IN^p;v|qgK92HT-cDOz*igmgB^N#rzq!Q`ZVhbnQP1&N|VZj2*(Y?Y5|DxM#=V={{ +tWlw>{$1H1rPJhDBa)sAN=IWeIlM25SeMQuYK?&08$&VA~rHuG&o5|Z(Gv?o+3?g&p~d8ky{5FU4}gb +m?oBp>+NzeI2UMAR_RODqpn_5B89iK?=v>2C83Q*>?`{^reZ*;5|&PN#VTPnCE8W3%a?dDSv=-HN%}b +FodZ){K>wr#pUkdJw)$p1b9oUq2)&YT<_JqX*Z?r}O_9^rkbG4>-j +8-;QGNEovMbM*GtUJnHabh)_Pg~*bbfPNj<)+H0K`OF{qa`OAvDf@+e@vmR^-8db$P5w;@pNpOyGxpk +P9Hb1}RY=(aHfhGlm*^hlmBcS~v^D0yO&OWLA}1OzJIZL?>>+mWwr+A7Ub12J`L?z56*BJ8UIkT78jw +i?nz2Xf;XjTm1^5GB;7IQW8|Vuvd?CD)157>+)PP4!7J6M4N7Uc6Z4BBpr=G%QQNGK%Fmmk)b +Ht3_`Tx4r*NR)rCsrs?KV-Tb2p!dhE3ynoDpDu)Z>r><>%@DLq6=DRJ3Vd8=2mPCcqO0@KK>92 +>gh3DG2`Scct6Hs)BM+P;N3@n7O2ualE2Kcro30#%@17{r3}{VfQ$Vxn@Jczi+Pcm?_t=Dg}O2*$&)6 +gQw(ATMPaHvxUhU{WIYIS}4#taxerR%P=~dB>O*5O9KQH0000807zB_Q%S-UMQ#BA0C@ud04@Lk0B~t +=FJE?LZe(wAFJob2Xk}w>Zgg^QY%gPBV`yb_FLGsMX>(s=VPj}zE^v8$Qo(M+APl`%%0GBLB~r!*OzL +I3?6kwAoe=}4MWYa5(sqA8lcXiB+HnH=z2|4_E`~iw*$rukC?(i^A7TPy50-53)v8)`8DdnzWcPRh%< +nnf+g`{6dK041O7BnypJH&+<4k&0gl{>JWvi8TiU>Z@SQ?}CCEvW{Jn%8tu|4Z;lCmarT?;JjGXUn3q +ap`kGv=1mEvA|EgTG5sL~hs@uE@xYS+Z%#ZsDNZfGyyuw-IUc5j-v>Z3jN2b9#}uGAU>(G|M#whK}%t +HEVHW=iBq;=f#k;568Osrx2c|Qr4$>RuCy9 +c!Jc4cm4Z*nhVWpZ?BW@#^9UukY>bYEXCaCu8B%Fk8MOU^G!RmjXO$S*2UNY2kINzE%M)=?KXvk1tCtD$dN$i;q{ZRZucAFf!CLFx4|QP~znR08mQ<1QY-O00;m`Rt8hNa`9iI0000B0RR9W0001R +X>c!Jc4cm4Z*nhVWpZ?BW@#^9Uu|J&ZeL$6aCuda!3o1K3`Oq-?_hjNpz#2O9J)a_2*y!TL1P=qY1gm +f#)ax4r2qdD!YwB%#?zWTF=(aLLTNJ@vzuNJ=R__56AYPqDK@uoq*8p)qChpc!Jc4cm4Z*nhVWpZ?BW@#^DVPj=-bS`jZZ +OpydvZKhhrhCs*gjL<_GM&9d-^grV&boM0%JB1r~@2^7XbA59>>NyRlw+FM&(!9cKNgA*G9-5|sUW;O(}VbD +#2Y(i>7#2bv-FzF^lHZ-+CnGGE*x7o7D&({FQ!)U{h8#>&?_$I|SOuQk{4H|3$dcy=88rh_8cR-d~1^ +5PuH#E9oqPOdz1o`t7lh=h1c!R^61mA?=28=VJHaNP8m`#E$SB$X@k}TIe##bm<}%&k*v1mYxC>D=4<-HPqj(lxy*@(4rzo(8w?3W +4A?g5yn2{N<_>^bXe>-v)EFVG_xF~(GwiQ*ojYW8_G-pdG6B+-1wo$1iC-#xn{DXki9zX#W6mGC<7~^&S_>h%Eqf5+*^H}yq +pWYPL|Z+{jH2yCKehC%lzpspOg?243T3En`Lo@6GnD9zGEBkVH-jqJOUrOMC1jBSG#l=o&~N|!Rn@L* +W$g~*N$kM7+OA>cpcaCbB^a{uk2Q&UU+2CPBrTX!wq^)@M$8u$l9{o#)5a620IsfjbrgivpGAW5aIFU +DQtbHPV`M;a2a!Si$9!8eWuIp4MN=Z(lsUNTuM9~J-U#qa93i|4LRYVa@Fc}Nl%Y6Q>6CksH%9?hfyq +(mML$|3wQ-}SNo7ZGQa4ZKGYpty14}zWxK;+m{d+bM##w$ahsH>G12$#p7SOm#vf1gNoQrysgRA5#(e +FtcwdbsAr`i)xk*4i+fuA~E%SGbB;U)6>+|h7o2~VpZ@G}Ggj1GC1f$asQ%DW9YA5@~tV=>TvY7^+Wc +gxTz$3-tGI&yBVHCObXo$8t8dyDr1kq;%uFo^ +?GJbGplo1dC$v2W=5EUz|UA>*#i&0BrI}0UQ>}lVFg`N`+G55ENLg~ko5TMuKd^zMxEFc)1L2*dD@2+ +zdOv(bU|_})@44_RwF4~Jj4#^wjEmQnj2J!H!E)_3Xh^l&TxjS%nvX=N5(#qWV<3V5yEIat#sR&Oq08 +6W*obQBczO@9VYp*rgdtX2oDa?`!J`2d}a2zs`zS~RViOnQ+2!a=m)|7GETd(lkL5G=dUe;fn!tKfa-;4V5g2Ai$Il$dYa_coW7O9DoVlMCn3<7zT}B +;)^0S3A_oyg&zn&3$Z}}yQl>fFrXPAO1g>TO$_cpzi0$lh;LAEtK>KMih_ky@oNAS{yHCwhhBIUG8-7 +(VB`jkHYu}-0kzT%g)GDjkY&tB_`({%6uu$T4H0he1z8JK!71P0R#y!6omreih+GXE!-@x`(?{;3xN8qPe+DLrJr5hi1NY0ve +9pnIoB()($$H@L=6&t-LodaFtb!Rm%>rtxYahP#V?qE1;O8}meXC9QZy3We+W>)wufad$EMbhNX2e +)j9XaCGF)7@pQbH;L$-I7Prx^ttJvkl2l*^#etnrBCmWrG8E6?hJ+tv6w`HRuazLg +-jf41Ni_DHnZ_tUtE+j!>(7#7IM%i+IHNX>j}SCn;v6EKQHi6&t4ZuIi4Q(7TbdNpQ*T41zQu2gl5>I +`V{iS6?%W#=H+SI*F+Wb%(Q$D_Ge|<*PxTYrmwr6G*K2fdtH%N}3A*(XQ$g6lvc}@48F~p{W5Qf*mlo7(bNGpCIZ +&|lpQ)K^8G}D-zRc!g)`NGZ>8`VH!+-7@yuk5|~P>nSOS=A_$Tq1WI!iP) +6Z@_*l;?fDCHRBLmEw>jSt48e>>sdEy!P#*>;H940GX1uFVZ3k`l7jhV;_Cqv_(IC`ww8G=>G-K_hV< +kzHuD?m3(hn^T*TwyLA0lT%)GF{p(veeQT5SU*GlLhWW(*9yp8JvVk2F2m8$etvNUozzf{rXtB0H2}Jbbuw(Qk48S$1zaqQ1TS)c%B)fQ!0Y`){ +<`po1_JU|@oFc!|a1WU-0Y_J7j(OtYpN*#>u&a0DY3o>w#S>A5S96NbI{ZG{3AUuCz&^qUp1VO|tq;+ +a1-4Knl&xC%1)lBX=Ph(AIGVUFaHV2jluy-SFCUI}%jcKfiG3cdl7JM^dBj^4i7?|4)(+4UZzub%@zi +Ga`5_s0<*)tpD!Q6LTh!IXJDaxZ*b*o0Vm$TP9(7B4bHf&kL9a#u2o +FmQG)0EN{48JNENUSE{Zy(^(SUw>P0{-)-q3vPauo3!ocSWuq9?DD+k7wSs%%a+<_XXR8a +@B)T&IuIUVFc?7VTv1rU)V!cA5S1Q_8HjlPg~kXW;PP)D2KD_g_02s58ZKRR^w~gZ~ad +v~ls-Y(h-E#oIF53CgGo*=>1{IOPv865nA&v&(Tl+r7Zl+Q|NH?&R)qzbI_vNVRc?cJN>hW52UJs?WA +TXZGZHEz{MeCOl3ahn>TeSrNSCWVyxOR_9*1$+e6FJ +VD#WkCSWT3<=F%a=UQ2h%NcR8dyRNRC&D;Q|szG{Adt5hSK#+Yo6k&wSfsP60utdHx%HlS+0SV(GdQ* +(t$ptj+2}?*p%6!FVu%oT(q>#Rkm~=XZ9@)doFU!3-S{4<@(GN>McGvU1v|+zL!IU^y*Zkf_^sHC5 +*~u}V76BE1xv&&XNiS0Tq%LB4k$Cwb(+bLo+rK^gM@(ZFLEt1peMM=&H{hCqRSk#@1F%LdREz;qUx8jGT(AbP3lJO +tbmL!A8qg7VYZP8gGBEHYxwu~f1NM4}>jH+;^kRJ}dJ~5mlwK|j3^p+06#g4ZBj9$xI#ZhfP@ddS
K(O2?43-ViN%e4-7Z9+&IOSo+P+&_}Ag%c?ln{J}J#+Ec +>v&qBIw1MmO?rAyio$RTVNV@uYF=2ewrMm{c&G_h1^YG*cTz<0B4x6B1XgqUw!U8ZfX5>j}_XFt*}>a +e)ID8KTNipvSBF5{!N^<86YT0;(Qt9r=}s&8I&cN6?L&JMG#PU5uYWyTq!BYzyZ{f_G%0Ulq$M0 +>-K;nF=)sWC-W5t%(Z-cJwcTnnrI?n&#t6>~_*BU0(r@)5bY$E;+Z%sEu#WH?CVW3XA0NOFNl*Uxhwg +fq|){shGlgV`lEPd?i{EUcPNx^;ZgWz9UskCVYI +u8-QRY%0I3WK*HCKiK2OkP?Fl^O +g{c{kn6qdhEoZgVQ~)Ztr98U&6;9;K#wsBDPqZrxOS(NW5LK1_m-@ekP2MCVf;SDNiXkl>nOB+=nSLhHe*l6 +jBoT3Y!OEx=th|Q|V+Y`3#lazQCV>USAacj^iGdl!llPD?NA^?TnArwl%aFz52!)Su14XbhO7B7 +pjz_KFg;%Tw;cUTRvcUu`!uVqwhSp=o=CHx!%@I^1{iEr?Dam3&S3A!;0TvO&ZSfyU8seq<9w!zaS79 +EkB7|=9cqSHjUfs^GZaMUQZfSLmLi#G|eGy)`pa~J$qdlbyYkvSsus?lVr-;C}P55(hh#$UH+x~2bq3@1Z%Yrlb>k=0Ltr7eMcFQS0O)&tN +4~_u{rtj-!3j|&0&EzS7x84sXZ_G}h^Zp|C8sB|TvHX7gGw@zPiv#|h0e*)$ssZDx&)_B(4c4j#v?-r +$kZ!#i$1G%jUI(rmOEbMuVn2*9^4$pYoEp~JLjytp(#Lo0F>}BRM-TsJ3pyAGX8{vDe;Q#hI&bl=JXZ +jh0+fO4SHKm6gZ6X#Ze*5ETPj{r0^p)q^oJn9=TByd(!Na35*<*#clcZf2l4`l9slN`68JCkP(%rdWu +O)63Qnx=3Ih5|39%2HzSQ|u=l8psQ82>P4o)Odolc0h)Ij(;l1yO1KjJI}OdShrFXGoeD&-R)8w!uJ&$S*bL^(V?1d&B*T@FF%JSb46f&SZC#+wicCG`oqX1cFlWt +kv)tfe}TO@9T;c_JibEM=e_7ATa*AQ5Z2D>Wp&!6k^;xkc|ab-lZ2T%mtZdtTynR&gxkx`6ISY3R-iT +{=?nQhRpsMudsr#XqECv1eq|nAhqlfjRCW~I$mF^*urnNIdd)qMvL5s8intvMh{1)@Pdiv2@msd#^mZ +=f{f$^VRbUG!W9iq!^7He4=Y-d5@2 +Ob|rd?P>n~ll6{2HY<8>l>(*W1pwGA)t)yY*yXfJpqrgx1ds34sdUDtW4HT-fGyP||6XE<17ISyFvyi=S@1imm0yuYwO)SQf8Qy&&h1HFqH3laDc8MSQJ3xu~SOo6qtyF>fawV$m1^h6on +T_I*5pGJ*8Cexs}&GpRm&;i-ayMDML%1QIq-f@~du5`GLk!Tyg>F~%M5>2=nIAvx}a~_rQq(^qrOm#1{bW97$x*4vv4Vj>ofdrrs7l@F#1t-xVw%Ez-BPGvT=i2y-l^MkJwl9YM0|;v!|v +W)r`A|w%o@F7_b!1)HIha2Vx3&95S+&p>77>N`4;uzGA{2AZM7QKQ1T$9(>*q_BY!=McY9yaQf)m?Mk +46Fd_1!DfxIfm=z_8ojojgW`*@()!#HfuLuMj11P}2!5`uYHk4=k-Rqk}wIw#g{YtVmN)UYi31GC?v) +kTzaKsxYE-NenZPW^RE{vV$DAs60{EoXn8(f;DQk*|~iKs3cH>(ao{CgCClfM*466{lWd=xA}VOA=SO +EIh|whs9%LITgj0%qcQnOgqpUbh?R|4N7cCd=mi?fGmH2TLwo#OH3~U0{@*d0FFNZf8b3TE_#JZ76pJ +IOC*2-H)EFUEIe6;w-|0}5iUf!#K9L?6D)y#&;b~Hpb3_kc(Ozhl3+tJ8wT{p|56!fudw+2Lm6=F*Jh +jg#cXelix@&0_a+m->~u1L3tfN}6`bw?E(bDzo9s+KBtUHiHnxDbt^xQ<&1^4zI=k9|HD@ +^){`7d0T_zCAN6_nZy2xv%vqOfeYT+{O2wn`kBE5dri#;p4`y<`P5DObQsD|$;KcaPwh(PRtJLJlQh} +Kw$iwo0VXr97o>>a>!%Z9d-Q~cbuRPB@jk(0Iqsj6^=O|`K*5gMocK;ByNjOT{atlZt#FUW1j4lSkl{ +gbQO)BOy-c;r$vK~H<92mAIHw#kt;Ag&aQ_r^F?o@I>_+5)}0f54b~vc!IQ`@S$2@3g#z2Wa_n{JfI +ke1C{0;u6*4sNY$*5rCeSb2#3zE|cX`Tiw)U%&suJOO>I$@5Z3vjZQJ$5p +xNed<}B*tuMb&(vdGZL0frv`=m(#*a%)RanPnOolXw0lo`#ZDrdF(ef45uIN}YxnYgbVtd(INDCJ(E2 +Supadta1!!`x<4cBtnX*Y?BU`0;O-->!R+*8svZf(jJ)!e5ko;S*;ol4PosbbN&BTv#ysh!XyEY=3BBP(n2k?jw+X}XD=)*%O5QSV&=Rj7cM +5&dB>oxJ{AlR!dY+LSy3`*s}`= +r`KI3jFZ?HK^2Vcaf*6?dk9;u{p!KuyYbgKSb3SPN$NoQY7^Ru7P)xj5k*RR8=PA8Jlapx4YEE82Cef +t@AdbKn=Z=P7qBsgRSg=qtN|)#~@aY)2#GG*ecyuw%=qvn9!wVq6-2m(vY%$g;^&1q^#KPhDwVn;|3( +g1jJft=$;4QI?6N{H2E$$m$vd*H{A)HtYd-_rW6!psZ#+HTbG+6=>z`{rPf?}HdD-=upGSL&Cv2UI;$ +}jWHOzUTZZj|s7`YBynl8_MbbAR!ZKVavltmx{8KGOHp=?qB!WgU3bUpZyQ^B@2_@Rk(~==s?;YqK6` +qW$THq4%_C{gC!V9(ovD$JOc~+kpBuxz&E847Wk=)_7dny<*qTd +zi;I!eh!1%1|*Gaje##Zj0!VS|@Sz71uI>n`>?5mDLcfA63_Gw+%A({&jutYg_62oy@ir6K +5lxSc3wiWvZ`NdS{CCDT=X>gW5LYgGM6nwkzP-xqdiHT5@tcZKaFy7|*_uI7K<67upZ_hdbg3lLX6J; +jxxT*t$V0)|~0bb#+z?jX58NfHGHeY({L+YllRhD&OvnCOsqJ!7!+}6W7}WOG9y`lxz5WMxe4gn22{* +U{&Za8B2;tts+0z3&xST?wL@!PbYNMu9EZAljItcx*?oTc1xIRs=loY$c{$R9-j)4k<1jDC|B)k(L6{ +|4IZNdE9ym;$;Q5kcTw7RmFo{2qQ=kbXb`!l6f<<}fm7;c~28UXX{HC-f#D+dA9|bkDcP2Gx6i@>I!>S^!W;9>q1|%3655bzOR!hKVr +Uld}6k;t<$}w3PwNK>m23Am@^3v_-dY4Z@P0i;>8v`w@@mruWEb5v>Wf)x`9Q`87&J5X9$N_4Oy=?aq +V0<>~@K94N(kthp#=4nu2ya$Xj9ux$bdy2~-yeqG6D{be^vqK6JY~XLqTMkRI)K6>QLOy%tV+v|Fjd$ +srZr4(z^yw?LZ1tDCLgjf-!}xJ8ZC5e6UA1v#v877xvblQ-EcGu|bEAEvinhD0LHWKb +LPo#|%_B(Tq#U&aNg@szdc3b5osLZhyrOv9T@4Hv#WO}TU3ZO3yU(4*IGT`UD?WD25!VSWs~}gCv>1f +8G@I>&1rHd8_g?K?vLGL4oIJu0nBdkNA>OyKzd9?f0M#Lj^TPUe9!i8;S*lkP=ah_%Dow~q9qP+n;hw +Qy4_KAc^BCb_&7UPS8&_BFw1<7D$wgas$tlnK=2&K!+!WZ^_miuZ4l1fB_)YZB`u?fJC@H}^zQSbAp5 +@zh?K57>Ob{L%tZg*y)T&Vrx^O^7l$BZ&zghRKrJ%c8?8wzx>BN0aJN%wy9qwij6x=ft$1X1(T3(Cb$GemlbWulERO@6c~dq*#{)HB-;hS +t2TgH0ue#FFvJqH0J17bmKBr;-h{7O1EAi}S1lVwEO8f__+3f@{R*i +>8!BCd7x9ve!Rv}ixHN*nz|``;1hs@(zz8vFLj+4!1s*T!C=|BnHZok+T9!*Cn9~US}*J`5-XLG>KA*35WGYK5ozZ#$xy&90zD{b3v*Cb$5yLGDYjQq<}p +&t4fIXWfl&cE+|*!67txCYblZ0b!K(A7AVzEY!~k>sP;#as{0_I4tfuS-BsHGGIWE~%1Iz}a!3-)b$v +-FZM@xx;-cExK&_=zeaMQH<_Q$`sV~X6uc7tzfS=ToHZ8|I4@rY4gygkZ$TnIhnvf^1&k +2kp)vIsr;!EI-sY!YtmyY$>?g?1cO%FT+13=~1Kb5}d(hZ}vzqBbuNf)7Px-(~wt!=opwQ727KS2LW= +U3m(&MSCdlGk@H(Z8f_{V<$s!9G!JB1NDhK+jYgT5MJz+es7i*6OgH$t?qKEA=E>TqH2B!G<7{Wdvst +}@>7;6T(vBL`QmGB1^VOS3eVwh`5YOiY<*4Hy5O(HWo64{qR57X$Fku264_(sYcj+yE0s&2W>k0}_++ +_}1;Zr>W$VoTi5vWagM-)n@IA=+igC(7`CN4v3eF}1zN)h#OBWAy##mnBl6DrNUKY!FrAWU~UDI2H5& +9v-XoTqV=3!;FpT1ZIl(@-rb$E#$DUuE^qX=E+0mVE%?hQ}bjoz{vvjuY0Rou^T(ra}yIy*yLOJ#7@u +WywgZy}~Vs*g}Kv-|hetIw6IpORw04^aeC%$O;PDwwNw^N_Rtz$b89O@6Dhij~p4YO>4zS?9*COLOL= +Ut-rhWLcuGanVPjm|=tAImk;yf>>X +&X3(Xkqj^W7|PS~xxjfp!w4&s$WmEwuEQiFtk4yJ(Nw9oW?46XJmI<7}tBpVGY)!PoG=HubPs?31gN< ++UQH^(&wM{rc-3BE3Lv0|S|GueWDswN7|%IW&4;JJBF*OOoQC^0f-MLH)zL{!_i(POwOPUC27y}-A2? +HuK@MI2hRX70^7h;^tZaK622?4&mrc6A}hyn?-qx2H+ThTG}thAtnpq@SGcH@a*}dx+t6csmgHEM{x+ +2?-u2jsayUbDTbKhu#s&gWQr;0X}9tq_5$G#P{oCyUyD9w4M&kDJ;V)$A_0=r}+d0mGrip+%h;uTx%6 +-Pu+XZibBJP#Fi}SQ;txr8Rs42XFN;_p5rxgZPrDzO-)TDJ80gXAKBU+z036&N@-!~JCh#?WXC;%VGR +eydb^9)9d$uvJUC^pk(o)vH<1z-M?8ctkUJr;q3(U}S?Upk+y+_Q4hIh2QTsLx2~MWYtl7D_()Fp;iA +~w#-lMngyM1RC&jfHmt+GlQFEi3yD+J#IduQoSoC>V4z57JIT>|`{wQ7g +a8#rCEeTdgWAyBVN;xGF9C?vQqk-Umz;$;U3a2lK};ZZP#5MRQXFuVxb0ACWAB5YB@$Sb-D6feeXP;` +;hFtV%(0_6=fJRU4Xut0eO0Zjg7s*vC=3xehD`-0$?X+oUw%cQ!q1<`{v&ij~Dk2dTG77S<34<#SkDH +_#}EZiqy`L2$<@=Q+Lvi{dYu}l0wpaoy1U|8aN<*POxEN#>TW2qS--YQ>zZiC#**F*ceBZGc7wBL>F* +Zmow-*3>s&)u3>!zlWso<8l6wicd)-5!3d{bR3DO*_(4MMF%we(WB?jSO$0&+me|rn^Jo-)zJ6)qZxi +^6mM&-I(nSi&J@)FLHc6T_`*XSzjef6%uxVGr$i`INqFE7(9~SJAKzTyrNkXVhG +8pS_9%iL;{EQ3@GI>sBS|QM|$=`u%wHRB$4(WpQTjk-Z92)#M8eDbpvXnk-%$^HK7cl3Z=&-1YBk9Wo +qD>0tQo*g?BxnglOd2Bi_^;qTb1kj8@Qm)Cd%6ZL72q1#0A&65;xjNMiHJHJ(-XS?ku`rvrFc%*mzxY +E~q>r8dXaBtBNSv$vFMV9O^ZJaOS*k@lW} +?;d?wB{GR@1N3;{#k7U&3xi*GjNT3x$D&t3%{PW&Ri~$p`SnN*a6(Gug5^OA=MQ2wAD^%+J+ +}5pl$EDWUtunkI+0eFr>7&rXS+u< +<~w^?cZ0Mg}m>8)hprw>=X6B%SrG+b1_Z6G)V6^pV{9Y(baqy~n?zr +v4=q^WS@?548P!7xoX8B?wF*FwMX;4PzvM5HLeBC_}>tilZ1t;RKA}4Dog039^CTmdUWC8U%rtI1PXb +01Oi^u!>*EV$dbOBu2i@kfj&I(B$SVA%=;T*8#EY+5qFriW~+nNKLT?gao>*q9w#q1(bso%;MDZ +*>uxHq+*-_l(j`&X<@zWvpR$2RHZVb;s*)Js?OUCT>!bYDtn&($WEFRTTA8qx@!<<*@>AnW8&Js&V<3 +Ec{LswGs@XTg5>RjUUTLGfIEb9X+=mYbyB)w`Uc-)QefttzmfFS`_=DGJx9>%|_Xng0X@zgBfZLH`C= +>iMCfI8dbt5L9LeHIBI2e`lU_Mks|e}CugL4WrC{?6Njetds_f7c}5Q#q26!V?K$bfVo(33_1r8%_C# +J3l@Y6NyZn&d+E35+8{qZOvUz2K|<#GA<8`8X0^aGuyMrAEBL($}&?&f}i(s&NlABthz0;tG9KEYuYn +y*tYj()(|=~?}dHSc&N9t98FPs)TInac!DR9h*Jxd0{UPPWiVW~)`KQBU?wIQP7kW$nbUzrC2^Kl+38 +-}ornblS;EOx!J)gj)sCuRrpch(nhDFhVW-W5f3%YQ3Tsw*F<&Dl+BxL2te@AGVQG<9?j8G#eE(<$kb +2tbIzLTJoyB5}Sw)W9Xm>i^9?B(;9a0F9JASx=??yryd4IhZ=icy8p#m^Nk=xvEh{zHPy$5Pl&p#sf6HWyg-a6`RnL_x5p4!SrKLWW+b +2`|YPZ%1R$c<6UogViVsTOGiqEBIP=q6;*7}tICPtP(+lF4t60!06H67HgQ0O +Uno_5HCit2QoA{p?BtQ$kEbxmCpuZm)+lA_mRBT0 +~2owl`tqO_MnDht`Vs0i>9WUZ3k2kLc_5$%k7d(>vu>8kUR;^<=2KQ+VFW$4A?*w%iZ~4SQ6=`G%4KW +*kowRFL(PIIIG>XPLmiKJI*R!L4bg_lkMd7}_|5F#+pL(`RHK)H;1RfvMIoa3f@R&oH3FiW04()v4H% +AFn2zHH|ujhMJ+o9=Gq6ET8Rs6?EM<^mj1-*{}W|y5i@qPJcM%uYD_nEV-a +Cxul#1;R4C@g*${=l4iniX$P;-V5wOlsV@NzY%W8-5^u=GMnX&X@q0OkIPO2HIi`FYPMo+bl +9Vr}C`)rRvC}W{+us$Pz+jG=4wi^P%Zs-}RqeC!y>0a7Kl1l`3Tb1>lG#B1iF*y@eI(ncW{~K6jm%Ty +>5qb%#zg7Nl%>)K>^0D59${JrX~o~E1^Gi^{6`+D{+@@Lz2%|mWQ?qCXIZRscQu^Pa3PZ|6|Yy;;6G> +BcZ=D<4u0Bl71c`Ub#@vebYQeNZ@Vt|W@{c43{RXTD?P;pVRi3=^-f}MTH{LtI +ZX#9iAL-ZQ)s|kYj4<#Tc1=MAx1M-^TlLrR*+ZZ>7x|4HgY+NXB>~UGrp#rTdAzX_;5eVaP9bGaFn9PTk=Q6G)QUwH{Dy2Oo8y|Z?t;nL +M7=y0I4Ef6kS-)L^a7EL@=PR#Ge{}F` +$PhR#jnEc0c{u;%Ax_GeQE_{_=EU{$fwRc1E%6tf3WuEbp|9}Hre_d@FysAEsXh|at;f0q;y6~0+;Kd +dq0`MaE66%d#wWz@V0y>7o=bplU%$UHJ{g~3=l`erTF<{`=DROBEz`x;Ta|ec4N=FG|@!JXhic!BZEP +`NRGO*bZ`g%vylAeJgukw_1*|CD4{}RQ*lE^wFe}&@ai62|sIok)@ZqQFBiF)Uq^;r>=ziWv{NWUlq@WeAfF#2YRjq#_p%hyBy7&-Ls4*laC=${T9%y-%E +W6;LS?kewg7t^}Siag6Tr?|bqNhL-TIz?xKtVHW37^veeNFiD~gxV&#uMpqL9mEYW3HYB1qlLZhu +d|TPgk}*md6nt_-)NU1dkzav!#azI*3|iR|1}1^K2xbV_i&mIX6OWSgggaaxYr6X^IZ8>9&+In +c?U4P&EES%U!Fip-A!)BK=`95{JwiwV{IiJ1|(I%L*sNl&)LAL +>IRf-|_3ZEP4E=T+CDx6PcoD-<890I51?h2i<9o<@QD7m-tvQ$Kt{*UHPbtZ)#R-_Xg`AYn6kH<5Ya| +;^UJN+lJ=ojzxSD3{B-LBGvRBu=cC?=?s>nl3kOGvnkK(zuVF{;v(H7t}Ki)0VS>I4K-WO+!WxGtyk! +EEkII~Chz^(h34ZFoN5vILbLC*;xZ^50malgl$q@0E7BqfiL0g-|-xT48E%iT-{t&c|L0bgczKD$ +fZ(r;K#eZ_4pJLz?Lf{laF&K`ZFhNr^i6SILF*t+ZFoobK0n=ZWg3_-o?%{ALK>;=y;1p+;5O{(tF>2 +~n1PU;_Y{8FhDD+Eg!^A5)@&$ePwP}C+$~t^g0>Nu9Vjv9SbV2oUC1zRn#eovQeuHf+Szx^Md)w|mSy +TW{FPl%muL9L1SR(YxW=qLys|aKvI`HtoFM}J?%+e=)+q9oq7LGwTEM6oCkSWApul&C1Pi~*}Cx&oHG +r~+gb0Cj|E70iZhXQQ>5r~e)eWF}5obBksi~he5AR5tdHwtJ5o8NIMy$l6~YO +_F~}1w-*a}j*GA*TIT>xR^pGDlHWH;2Dal>{`s*<@+|(`cHycYM-BZu!~Sv9(7!Y6AGi63{$|9w7xMF +g5D)BmFK=lo&Q@pfbPK2mQg0{aK3E6l#1cCx&xB*aqW99NEHyvw-RDj@?SSk}`D_v-?0zhF(~iI@>nz +CPZt#v!EhuQ*%?V~rP4i&xt7$vg^)|gu!g;&eJ<$~Rd|f%Y$eM7u3>m~!Z>*3Au(jO@*$NuXLAVGR11J?aG3~bjAw}os7LSCMhk*cbI|`ku6?(s|dF&Zv +>&gs`H7UE~n5XhKer@v)EfH%E_{6XF`OkmZ=l`p1{vnPtC6e3?jJn@v7qPd!$MrVJF^yu1F#lmkg+KI +#e(3z$b>}7{X>7Ic>4QE+*sZMgYb>fmPkWd=6uY}ez8WSZz9vq4NM{YVcl%PWP%aY7r$^`-lfjkbsu2 +>H_r_e^+~>7!#Xi4go|QuBSkl!Yz|~^R1`C~c$`w1W=>oqc;&xS46rN@nEnQEc9+Q-!?jBuDor_g!(J +f6yHKe;pS{O2VavmvbxZ!<Jd{_p*^s!{SBIYO*LQ8(BLy82hEK)akRG4V7VR`Wi`;aM> +ATbS$snH0nir5M5{Gg)R1_un)2$KX6-$ic?6#I;ZGPWBdWiF`N-tVrf>?ZBW{Yd|l8p}^qy)V`N@5A%oxzHcL`SV47UN~lO08NHqXlD7%HZVrv41v% +X%8(d9Cr;oPPJRVVidl>zu#U@4NUtJEELeib0DBRn-jcchnS^|Dn0zTBOnm-S(kd_|avCCPsw>Fa3t+KvGX6%2c+GL4sR@h +e~stS{(;;Y(r_xMoBx2quxggl7AK=BL*+cwxw#8^@cioX2jfy8d>1`YJnA;PU@2XnyYd59kyBNT1L3( +~taB=>3=Ayp*~Q`BRp$8d_>aNt~}KqkPnqwWWoVoB-NLpa8-<0rpfvs*NemsyJQw0nifIxzVyu?0Tq&Zy125= ++HX2K!7IWWHbJ_wuHt))H<+m@cl!>@+9lwI2(j#tss9KLp*dnnTU@xpeDJb)vQFi5gmqhxhiKiaD)sG +~}?xp)7W(i<7N;b7)SdqKNe7$>|u@<>6$pw*yCB`qKqT`t#~iYKKAn$dE_15j+V8=nvZwd4KP>4X_Mh +=uT)t*1oUl-La6LgHTZl13UJ(9p8;HmyJ@xes=z<@9&+a_K0-yrif2vx9Sn`Dl(_;Xr*lieCD}Sn#u6 +iV%I_d`28b3Z`XsxZIR3VA)`@pwXQV}z!;t{(T>k&ds1rupp|E#xQ}!++zz)jdUOaj&LEgPtglX|(y@ +EMkt2MmkK~a|!(I+kuMS<IXI?_9O^{_AvfMXlTl5+^14#vId(F>u+7)C0_CqV#U$#^T|l# +l)xz-a_guA2RJn%1xuuG57xPdm_g!dSkj`gx5Tcj0L9 +ZC7Zr^az-ckI1Y6cKtIEEKIQkCA=UO8?y7grFr`vm-5`_;1vZ=XD#ZXz;mMn{T~3OROZD8u?uf;`Y+! +7KY*G4}2ohRzitSg6vR!@nD|BR1{+4UV%t*B +nC|MMm+Bk`ly*nJZmh&ywzqUFfc@fJ9q35bnzS3ApVL;(JA!*!q<5bi4eD;1IbD3;qd6=dHXrd^3(S> +0yqQwo^^zxh~na_r3O;T%8$?{udh3q7nT@t-<#UeR(=)SAH05kP!*iNGR`mc +-T9YoQ^fr;Z@nunI<)gmagqNK6Pu^M{4l>VYvvbu{e7ou+v!W=)m>x14X~6yeFod>Z*25qf(Wo&{|9r +te;^&KMH+TJx4?n23Cso`+7M-!&mZ40*w|4ppT@J>aMrC`iQOnreP6TltfudZV*UQqFrIUF5XJF;v;F +{$lXY5nu*qM^sdqKoFokqbucT2I=i=-);^ClOM|df1bcz~Q;Wc1p3&zb}s@K(Sc9S|iqV +zbgsaMsZ7vX_u)>9wJxVD1fb{9cf5IX)rbPf*jr*?%5K(4XI_fy7n**10G3Q{yDm%2Q$28=K7`Os=$$ +P{56N@sKZK%DDIm_eu~-Q)S=gW1WOMT^0KFeU@*xygVTcheEETd3-=Xl>ihrWo +YLx*DSw!8(A4x{_-ZsP{e8Kp!I3Fh^kMQifT^cRX8E@xjwZz?_M#|e%&S*@RyRZPSz1){X09_*U&`z% +1~)uNYS_C+qHXr+v3fCu^sD86c4fV3?FSgW`m>_*%Wy3LESK +Ox(g}o#4|!v6EZJWeY7Ul&hyba4_m8kLw7|P^Z6-vbOUL`mV?q^n0vKaPy4|ADf@dhf+P;{VE9j4-fr +@nLi!&lSd5%QxvhHB!M9aMXf&&Y{f_z$0-~mSKhf|B=)H_-PT2nY;D6UUV+)}SOf0vZhP@^>qgoY=I*l&(~k}#Y^Qx0zGX(YxprhPWkTB+&C34}a&J=#_kO4N-_Y;Uvn$e)dw0}&Z!1!UyO-ViI- +$14vWiMJ**UBP&BR;) +mK4>p8_O(1b#?Kp1!6e6HyrD-B}JR63Kn95cXB&o{Qe-EG)G9P0fR=Eyw+VhRH&{e3!wm7mbrQ<SCaGxZM+| +03ZV7F3l9>LmG$dGo68^z&HTAY>v#07X?;ZKH!h14db09MdEhz?36#@QJIa^Vx$~$2+!suK!`J6g*9s +24+NVeTuF$2SkaXg2PpFAmrNdjCxN6Up1BzN_lv%fl+k;kT@B?6|4aQaybx~nDNDj!wGq|^LlKr$-eP +0?c?Gx*siYk_6W}l3d(>tZA(5qSyiL6r*Fqgw=c;i%jLIig1np(2n^Yt+di{okX?TC4~lR~EY*25EH1_MJB#Z9G;EkF1wXS%P!FZNq!e(G+FAyiCc)H>_BcC<@SKe`>{>n_*z9=sCWXSVSvJkL +O0~BS1}TrKqf;>EE~`?4qKJ4jNVJG6#g06Ss>|-HM}7ww5$OijB2%~23-duwrq}uz +k9?J9(JAszL=UsNb20xm^4L>3j=MXl*lj&Dn>(qviN}5a1ugypIe+(0Jw8WpGYkinL3;aIO8PVlm5`s|u=~u$@VSvgDl4gAglJ`Vu +-~d0~ss&Sf2ZU+U51EhO>*vG%*7+;nvyLpVryX;iz8{=t){Qk+D9^#AA3|*sb+5$ldyy21!t(Ed4BV7 +8Iqa%vkXgPj!vj8&0D-R&DMPMYrgNH(toVc9c{E#DWGwcmS)GU{)s&B;8^6`bjWe!Q8jb=fx9b;pzz# +qZ4U8nMAj8rSe9`sdJn%3ikK}~jlId}O&HO9Am6`g;4_J{SUfI77Nnh5=$$b}x7{DWqJdv-3wv3}U9+ +`)^S&+g|>sik8#CjuqER|boRR&Y?^eNNnLk$J;a!K$*6%9O)wlukhi`nV0q)5&mqZ(;0a-=>^$A`sp! +n+wX_#xl3Q}x_Tso0@2`h*lN_r#swN&;x@aBWuJ43oDgbpoPr`qS*5l!NWuZr60Z9mL|i)$wk6W!N!!02-H?r>C6?oSq8BdRF%dy%$@lUt*$khk~ITal92KJWU#lOlrkT*JpZ5D!D>B5Pk# +NxcCBf*B>O--V%qpz7ABgpnZ}ihaOh^`}+d+7fCutlYY^5o4z?eqII-BlX!0dq4B+D5r3)uK(Mx|2S*rIUp9*CDj-m#Sl_# +mXAvuws0zb-AO3m*jvpz?fJl9L)~2BaOQx9zyiqpzQfFhwIu8|P8LTNKd}lJ<>Et5f_^%An*ZIO +~SDjJ?2v1F7)HvOqKq1V5IN{f@1Jg+69XJKi3TOlcoBY~v@=azsl8-62YkuLN(!C;n8khn&pW;dyw!0 +P%boE*Xs#O6o=;8cpvPpR56o6457xI%e00j4hx`ne{6h-%~orLGo@cc&E&L+Z{uIAWqX+IY<#B--9dd +UMfCRH0q_B&N&>uN8feLRN8DSX2qFBfHk02DmrW;?B6FmX*>`dyD|c3SAt*4L%q$(w8F^w;1hHqzC!GH_ +}@<6NJ{(~Jg-&-Xo`CU}$Tu?V+AYA+aKl(Zw~lKFzm)Q6e7egV~W;VsTF>!1=@jga?<*YBeBy4k|>Fa +d$aGq>mM)QN-7jYr=<%P%{sYV*mXx)p(M5j#_;;Nj{}y-MSCe?c$$K(ofez=z`T0L++V#UbHSy( +P9i1NY-`jk?jNu8MCRUph2em55hIOY(D&D|53p9r=$KFvi*F-k3kzofFMF5AcjB?fl(BOBcIyZ?tZi( +wDEc(*+};KB8xYw9fMoD6M^mJXmTs)CtEy?|%tg$)eLR+;MMfTRMbc@zD)4FV<_Zhh5x!2e +bTCWiOCT3fY!FQdx5Z+q9*03!Kw_JRLZ`04z-hGYlX0<51rPH_6I=F>!6txXrZRPZAOH+V*mw1|NH?f +9~YfQPuZ^@ryw%3-Z#lNZ3$8~x6C*F)*Twv78%rcKOODYZ4+SqGTQV{?*{lOsQc`0{t&lqA0qoTZkx}dluoX~UXgHF4=+O1m)#S)hEj|K%}H8<+Y_wnobVAG%<=m~-Ksg30GxJC1l-L9<SLP*XStgM<6C8&H(cv&&i@CDsAAdT){lKalsG0;`X#ND72LGaC`$>tG&B7E +GkhS69Td3*+~Jkj>$*tOrP$)&tlM_y4>D8-yaW&E2?vAcWWr83~!?1mX^EE5<|_)B@?EACFMh8Nr2=q +qh)lxj(T<|hGf1}hjgi*AjD15@sav!*!QpQW@cg}O6r^PEzlmD+bq~mTXZs@(uL>9AO(YxvedV6oHiP +Kx+2yrXNfLYw=Pd2hdEti5F&dw!(~8p7rN}9mUiA ++Qha*9aZG&t)C)<)$$af0@osybZDi=bj3XrfKLcY--8zjbBVbIZ?gkGjN6cjMK9IYdUDcs)+ziDH0!LhUDN$H!oKz0J7I@Vpgq& +ECKxj4V6G$DJ$ve(zU0+<{0$m;sk<^t}`CJg585)Ne?W~I;>X~=ggP=&Nh30`K7JOYvAQTMta84-sh7pF +M?y}@*}Jc?9&J3@Rg+I)p|~|aA5GM_Z9vK7+QbjX_(U3qtvGX)9J&L@zbz7SxvNM2jmui>IOC`#z{L5 +Iu`VI!4!-DzlVO~AAo``T5vSsEQ}BQ`7NWmO=BiY4KLkb1|1JyO&LEm#W_49F6S#UI1rh>#1SK2l}LR +94^g{-xjzKxo1tUkWi>d&5r~zF07p-R;-F?e8DL-C8{_~tZ^KK!Z=RiFo?jPc0R;gRb&$_BeojI8f;<2)|apbNFcTKwx_!$@Y +YlS|4w>_BsPJwc#ot&bW3g_d&MPzx9E?8w-;;tg```Mh@xAP4?(wl9`w7kTLx|24$+=%TSLQe55_hT9 +Auwa-;I!?cq`+q7eK}Ri8UfksjX(Ui4bqv!+$L7Jhvjnz<%$$KeNV_&b>Z_ +*zCyNmAMGO&qwIw&pIFSH3IvZ^tyhLS!uqPRla!(ZbLjX@MD#SYd*AF0{6qg@Rvr0X%mFm+USiBMqOJCL7S9b(e2Ql(_on4UJ@&S;KEBN^+>NuH?BM|>GK4|UG(bO +r%KYt2!!h6%0NkrwPO@977C}XDk!(NumPVrV*_OU(l5v?u}ioDQrNRf?CC4D3|=XX9-zt9QU_(xPH`yI1IKE`F`CQ_rcLjrLM@GtgxVYdL;smucLQ6w^pZAsd2l#Uy5E?gre%G +qg<{%?YGiBs4rEJ`C=B_w2P3?uUqgw+W@;_9UkIpcHk$9OAg1_f5Ro5S{yw^1}Y&B89eIh(L4t#B?cn +X*ezMfC;#nFA@;HWMBc0hMGW#(vl}=tm|XPaX<;-u-a!kr4jX2juyxEXR4G8m>0Wd%17wMc|NHHPAL# +UhK-xb;!oPXo&sg~Vpr6_th^>1b-v-YR2*e-^LtzAlAPOZR6vHWk#Hdf(09VY)c5RdmZP1PSukeUtTf +qmVHhA2iCj57l?G>fZ`{|KzgE(T(UaZiy$&Z4Yz43-s_=ZO4#}wEu(H5gy-$S<7qoCjAGf;RNHrq;1= +(b->Zuqv%n@3wi#tNyM_t7q(kAYjw3#N7trf7Q$P`ssQHXy|}#nMem7257B*|B(YcKUOd!)pga`x_WC +kYD!3%67X(r=^X*T-qPQ>qaVf2fIrNRy>y%K@8ax+W-w!x-mQzm8P=x4I##pKl?qUr=l+%Usnuu-A}o;itPsoYSbP +x*=v^<4Mo1amAz6OFwZ1B(AbnM27Qda+BdIY2j)L?EwaAPHi&S*+X^y3L5w$0J#-L+g-mYN%Y*G!=VB +3%eE30+oc2?EkSc{k&#tKl~WI^g)i6wii`CycZfpSvkuc~srQ^S?1hqEvZvFy`O&M>T(2I$I`!~XH=8 +q2xKfYe^G`sfX$tHy1RQ@!tp9Cuj`>xCKGcO_1w3B2<|Qdwi!yK!)|9LhW@th7gNIs`BZ%jI@DmsbX< +hKmASZ?8*oATd|HD{PTReBUT4TBt{tDZ%UcRv|t2UF-~;fIR5_mfG1r>*706JfkU0R+k(Mq8R;L#zMN +c%h;|Y&ipENw%K7%z$v6}9qWlQ{Il9w{N;)JVgvml%MR(%`XD`hL>aa1cI{DIqbifhtJq!tyIBs=zi< +6xDlsqUEVRF=?Efgjhz#hJbp0z4kr>e={l6UJRRnJ(AMkXQHowZ +^W8+tmOhurh*|Nt$eEx8f8Lr6qDqx?F~9)CZ!qESVD>;v38LhG>y^4;8UMQrH=N)jg%PfQ*4H2NnI1P +Y&aQ)l1Aco-Py@UM)BPUuOngTKbhjXk>AZ%lHtKBZjcP{=x$(p(7tCbsj%CEWbQtEEnZy(2}bi&4cR- +H{miIbTg}=tLN)TJqh~Q-@HW#<5_Y;d7{WU1~O9RT4ugGMM&dFj|La7q?zHh#>;NNS@KgkVPs^Pr%XA +^^D&H0Q)U@bA|r+6k+Ju;0g*K{gERhvAnDf~erLz~KRUrTQ{Ml0@XzySD1xmpiQp&+qtptO6uQz$5(C +Ng2Z^94j$*{81pqAGM!k~F!EZ~ZZxY$@mc^$&ym;eX!35eEAqoDQ_`EF;$G6_S6;r9*O&8r?MEJ(?5^ +QT<-xSpLZop^waUQ&pyFA#GaR=|YCK}%?`Y=kRdx=lMMvG%s%LoWTu-i!TlCuwZ-x8rQR9kV~(8U&1&6~!*k$#qVb +|CxcdlMk}BOrZJ~{b~KlXYW+aQLh?NOc88EUlU8%dcdv>OG>ec@74vi?tuKkTo4{QpNz_!Zax=E;5ncnDufF9oek7^b%2^7a2K<`Wo(BP32jFi +H~ZhELPO$W}97L3pLWo0@mHq2^|IPwgff$(G0b8 +i}{EKaOq^Yn!SmwyTV%zf)7gHmKeliNWo>D~%<>{XXNZU33#RB{l;P2;A-}+^6N%kb&5z;wfU|_~3rA +&Gchu(_6o2vbzLt;l>{sF}8JOj`%l#cgb;yNF^4Yfg}`JRdYgzex4pKKV!r{i8nf}3nnY&4Bw0s!85e +0i`}%`VKGguwI{F*fU94p$=`_)TrFfp!n-W2Ymwyc>rVXsi+lYyc{1R;w3@$3P0c?^vaFPOXUvrb8O{ +Y;8FTsT9RvULmVdot;Gf>|ulnJCpwwLbl5krS5fPTrJ4>tq)RVq74p&ItHeB4hL=4`B| +kS*vgEg8o=;KR2&Pzu3X-_R)w;!ePCnQ{;jUI@>WZA>xAPe=*hy6K29IAEe&JMf>d#IX%GFYf^I|xzv +_w`}-O)-smx9prgo!vA{OX?cxpClo1GJ*v+q8F3U*z(5wpY))p~2ifYgqIw=IX?E`uGxiir|Z56&v^A +#@{b+!(0{{8r7QsF!`My`730)(6hX_#Mv3!YK=XyM76R=wxILF*UD3vYq$7lE`0`}TtlJ+&DfHH&?qq +Y*mHa*a4SeTlWN5<-`&EN$y4No!syY%*(Sn;J*%M4c6VNrpPciZQpX#mHh*TrEI+-oH39%}AMVoO^k8 +r5U7)ov(LR%I0hr%uw+QkV>TIbRUr!d!2pw;T<}W(oz~&PZ!_*if`uGKlzkMGr>-J<1>agW!xjy%&@4 +^Si{wV)7?p;rT2-_Qvs_Du+>L19w8czR9 +c1-93ky?DZQxqmb4s3abp#5Cj`I~bQSL;)a9XvzrxwImDGD#IoWsugPw1%%k)%)qw3Me`^k8}_#j#k>G1-lQ!0~3N>Xo=u@7tr(%# +q_vOsPrrLcCsAIN9z^Y6(G*?j%P-v5+J^DO({)9HVHl3ytGe>lO9sEa}vLBiyUumni101U5SyCN9{Ll +{XBAVDD{NPRjALF{&~S+)z7CL24(kWDcKCpT%+l@t@?)-wmC8*7Gt(^j`1jiy^$-R6ar?8@T%@ZNg9l +^$c@E**+*nP@WIp?pW^-_~ST2E6so!5h}D&<&y65Z{WXD{l^yZ89C+HByn?XExi9r$nMz{?#gWI +eXKaY-p2~zNwnS5%J;X!i9dlZTW+j6|E`55f$3{#WjYp^i$jZP3md_yqEAeoub^8Q=PgOLuuSo*b@o4 +H)E|9${{u$7Rl&YZ>&f34wP1qU-_$Tzk{N>LjGfaND=cY-^xn9yMj9bX~O5&Ck{40^9wgm_e`|kHQ=-kLEGhz%a9j>U^)n^7|7-NlUJ5 +Xt+B~MZZKOpJp^ty09Kf>kgSHb`)K%7}8iY?5?8J8syJw`0~V%;X{-BH!BTRAPTa`3v5AXJBWw$)Aqe +-RRc;xJx=la{;%$Y5TwtkEnv!ER3aHyXWwA3D8$epDyD+hqIbil>Q5kJ90dq;&s&pjCDxTv4Fw(_!FR +ATuj*UFj+DR4gw$I{EZFS^73xpy{j`;z8h~fv)HarD&ub#!K2WqDAOcW*L!lPhNn?rS#=UB(gz6Z53J +bBx2ZONysCas9fi{RBIcF|Mv;gz8L1WaGOvr%J$to%JJ8=_w!x8t*l>u{S#{;w}d1JuWSVdDFlZh;?v +ROa8Ki{6a|a+{*v8o585Rg6LJ%egz-(s8QUGMv(GKEH!ibM7-Un9M6umD3Eh&0P`Gh~m6Lq*3u7O%Xy +`^X)|2gq+`sb-M79NR>zRqRQzyH;B}#5eUF$lzE}`3?8oaqP#l$wNws9(STU1v{ltsJZ7POTOHz$aF0 +BwC=8Md`Yha2zuBSjguqFv8^9~?UqF*@<*_oX_1IylCezYdNC_WE3J)%ZQP#?cKQip?YSC@{QgGw*za +ZtFv?v0u|@Fjj2MDFC+>;yx@BAkEPx{bL0A*KL#jOLL0V$TPn#3c#1{6pg)DUFbnrgjjvN@Ya{2%o;s +T@|Wf17PbG#@{g0lv!pJDc@GW5plAnrY#snx}vQq6USojvt#jJ{6{UZIyt1HxgloW +-OdU>!IqLG)07}LlfvBgqjI&Zq=Es68(4diBgHWWGlXX#Mfo%iNxAoY=+i;H0_qVss2a`4o;{0(l}al +XH0%PT9R$KJAgnWG6RW8dY2&n855#pV~nS5;w-t~Z4E4WDInYTwk+d?O7CzKR*WUX*Cb(Xs+z6 +Mzu#EvlNTio>z0$P4e+Id_XM4+p-ma)u}t;f^g$6CydBSoe9VP~V^9=Dc9M%tSDtjCpg<-P6NJD)nq= +LZct9=g|`Lim}>LCf}=h>YyGuceqDK**9_!O;aA#6YzIhT*W7HFrC(XuvYNOrT69G3RKf!2*Xc6K!0g +*6~M_u>Hn~^SQsZ0{_!#1o?_sC`WWYVO&=di1<>YEvCrfuLLa=StzSUaV?~=5#RI=RTsyC!ndL8HXy3 +DZg{h_WO@htgSah(M;>R=19ZwCU4=(ly4Aj2xxq +XGcjU6FLrVxiKqy|!rXz+3P7iE(KLA&{dZ&&Y@JT5jNTGuikA +JV3LZ*oeMEm+4l$T)pf9=4{2%>v&ex> +fBVyIBGZZv-Nax5)mtUV#+8TN7rxYaz0=TdqGp27tC|9JSTewn6HZI_wSxNw{Ge6>n)bJl#!=pzV_DT +iDzKk*(im1$KnmYIxzF8#cR*9CYnhxKr0Ntc9hE-NyS5QmO5{ci5tXj9eIKtD`pT}V;@ytN +VG1S!CbyQeOcKB-E&~u+h=we?#VcZ@nl?>YE_#AnO0& +`;wfY3X^)@UOZHw&s!Fzh(yn>Er@??%p{{$zQr7zE5a-CJ$as=T@275 +o`@w=98ex11cGF1?+zW}tDCSn-m+2uimFN&`za4{K>;5P`0nf3OZ?bmu3+T>o(g%Ir5Jpi7pSUPwRep +kr%*OD%>BDYKWy>5<;4{K%SoFw?%GWy>i_=Niu$`BPkKJdUsR(0j=j%+f3mM|_{V*JL`ImTFdQVw6%+ +{sgGdU;Fc1N;b+yCR4KRU&DDi14C$!V96^&LX+XSL^)x-@;lFhbAjw=-<)jIkXk0R@efQt! +I)*w=CKEb7h$;zg#hQWr!=zVtWxQ-ldk;)30zGZ<1KQbL0beyB~D#D@8VXxY50Rau(Z7hTy%?WyQ$N1 +0mcRYLfIz`wX&Y*j5g>LOZr+;nJPquKR$yj@I88O&vE@h<&%z72=W#E=hbR$}24pHS;);x6fNS(_af$ +7kA;SHD>{8!UYtyNteKU?uzahcw0Vx7p__tuo(mb7M)KO-F*=s)@08$ZLt0(AN8k36JYN&;rRF +FkMdEZN_JV~U$7ZTRN_rw=rl?{bJOMv|jBKD$>a) +1qQ{lZZm(^up&$xk!Gj>#ZbBBC-X937ZSQpheYtS7;~$3OCyhOFd1=o}3?TYlEyP?De{)-BY;ic10M4OhMfV@5Am_H<=f +?qW*_?|@*@5RI+D*W8yOz+rArlvdJsF5o0vp +>L%PmM}HlcibnX@s?>^CQE~AcLn7+t7h(12+ehh45n1HoR!Juxm_sN&7p&%nZtV$D$%roO@rwguOSCQ +ZQ{o7R8}-nYKHPa4!K#dxeehaQ2HhNT;q_th=4hXfeOv$JdK0LN&RG!~=DF#0U=8dh&5ZhNJQpFp#d4 +J)oD*(;Dkww{+3UyM!u!6MJ|6RWZ~fwe8!bt+;}_mO%$qT +koOsaR|tN1PLIJZF9sAPV8d_oKorY(S^~*n^9ZB6t|e8=pY`iAx>rrl!b)zkGz?Vukm&V%0%@WNRKcmc#7D +(@Y<@&h|X+}fOQBtdJW(vqu07}a|`OJ>X45Yj{t-&!YF9#^{&g+}$9pQN~4;)bF=0TRPh+TpA+Zi0#ywUMaLiynDTPtt+*|sd +|TS)fpwPhI2zgYU`aPAL#evQ?B{ngJ}+b{&eFae_!gnimO1ntfeYsiz}TS5Q{w?WK|+8hxO# ++O6!-2J4wga;r+tXOp@*yZ336KZI*DP403tDLqkOy?crHRPY=-vl}Xv$#Y*+Wvq5A~vpv$;=#ccHU%3 +=14S%$r!r;XpZG9=GM1=*PIw1&+I2u+DbrlhLE^2_4m{O)B~E6pHBuD4lGgPrYgAj-Lbqxw +(v8=BJ>K(-n**6eqPSiNor4$arC +-|GLMN__x}w*siul{`q-1iMj9dA8n;R*%r47$gM-=_RGA>JilwW!kpsgtIBePf`JKu|PqVf2XkzXg8C +zMf7|FUl(d7oEi;kS4pb4;aoR9;6X0&15Ct>OmUspqFOtfT_3)C<#<3Z!LZ}yu@%JDE2JXN!EOko5zcBDh>^wWH +=sXoK}{TE`uo-1x9K=;=?Ph^$94Cw>89?oZ*JsEtEL*v7e@%H?*vc0to4wsz|SdzBpa(1{9tq1yjl@= +7xdG(Ojbg)Dxe)8dEI6@~_~D`zJlFY8Ti4qVM7D{jdM)!(Vrk@cdu@68`rSgfQq4-v0NTh2#I8)8z45 +e?qq}9jg0*r|hv)f9I3k>M~5$jYz$o_$Q9PV#lkD)A4`(wL$j(=ezl#yY_!@CqLpM31TP;ZwY(|hdy; +}im`nrlh`|akS#5q>~MmhTSa(9&6UfqNQuN-vVtT(w>rePBmt7`x(a(TV?8B^Zq2SZwHL2m4X`rC-9!WNVoLI3X>GNLGh;4nryfpliS4UiiqnyZE +Zs629W4JcS`-)p;*{5_^)qf6QX_LM{H#Tzb+)~wBn*H-hTB7lwIe0d6?PC!#B6~uS^po=8yVS8pmTz! +p|=s9&;NGz?J7CfW}?E68)zu3eSn?mkEc!WHgHC*4#r+s3$_d%WU!2G=;Dm4Q?CU<+N3|IqhqadfZxL +9g(WSH^XeU4yjFdCOj80qJ0rGt#fPiEFk&IRloHB&Dw4Sb(suKoxTsI&K>W1D|;+oYpm)IpT2ojwzrv|zW6F{HubMaG<%tkg!=l|tNy9N0sL6vm{}{02~3SFhw +Cz@YB1)x6_TZDc?MKdqs(y1$ZmRf=StS3isJ4L&ixyXBO=ztNVcYHa$f*CpsOQ~PQ#Tb6!?52Jd^CyO3~o+LFl>TisKZ7SM;P<97{y57lx_(plb1erTTj8uorM;xk;B^yG}az74!#)@`TB7u9iP~s%g&*FXUhJnoH))E0(O{Q#FQd@s +|7b3=zgBhY@>=jaY_1*_Uv^?P;wgMHjw;_|k9&h13Ka&yaQ?4|_0HZwahYOR?oFl1y1KmTEMH#Bw>p8gQWw9EAD5#);}T!H +3i1NEDMd1QU5N*?eMnnF-SXYe}XQtOAH$A|{@o(>=o(lQk~t@d0Hl+1apM=f12`o-|7$j`~h@&$GVlo +JZ(Cj7tzZ1=hkWaA3(MR^>t&h4;leXsTxLwWfy^aX(Y1r0LVA4`!n<)&V5g`VgURrODg`1!#7%78Z!s +oAq4S^p%V`UyYcbOaRORl`KRCfg7ts@sIQp(nvvcf>xfy1v+~55ujv4I}@KKlgRoz&YGh$zgC~f-8ouFt`FPy4y%>nnT$pwy>f%o^IV@7`gpiN +g_u6PTh#w2qdv9nXj;mL0g?-<+AZ^LvCefYdU{;LwC&5tu6RP^PW6y&9NMQw7AU~J_<%xi<%pN#|m^7|b4ZWck$Ukc{S3cnj +lY<*#~{!uXhx((DF`E9t}x4Qv;yq*8a-2gw{&QEvqHw);Sx)FUplj!5wv~+!|y1P@<=Z3D38;0H??3k +Y@qO@(vR$;hv-(f~v&(rOK1R_NDiwRWjy>MgQEbeu$tdIbQ+#H%(w_ZSyOUUb&$E9)o(zD2Q`6u(U2b +cOCtvXeBJ|g}GoR`L7)8QUI3ve8j<|^aIOw-XqgHz$PKx$3qr@YLP<5`eKJG``hY70cg)8Sd}s{#moG +`iY)YY=6lHIiHmLoo!{G3gz)Z{LWSaqgS;QS4!A=u0CU5i%>y<`j~53ZbTe+sy?ZkDR?$gPD#DtKZZy +tdW=?O&Xas--XCF)hds@HOT9jz8*-3_2!coAkf6yV*w1<#k#^E*+H|OL$CKZx2Iel)#B^0=i8qY?yvC +j1zkAcGkBRl^Y#4Fc*WI`dp;3mK{aXak1Wvgyj<~b5`Mg%%KT(8xEBZ}R2|gINkW-k?%+Mg{u!m1Si; +2ZRN~$^*9%VA*`Ue@K&;5o5dQPg#y98`D94q?2G=k(%VR9J@6b6aq2RQ0oW%D_T0A6W4chEG*!cCQDv5V27IrP9zeIR!22hiS0Bnfy8!XFma{apE&fW_hqr%yPSEy_s$YjH|BKW +7tReK%DSiT#&k$uLQrOOIQg|ad>$3a*Quk&{j%r)7=sRDrAI~vv6@9nO&I5=>AQ~Y$NpJKB0g?dGef? +z;nYl7^MefX9RmZN~hqWR?fdDBL(}#~a+?_?YEFm1sI;AAQl9kJ?KLX=C`g4n*2rwgVOOnr6&{bS%;L +6;%c++$wnklo^meieiSTA&$dldgP>OF^r3MImg{iU6?LatPPkW4R6R7=VH+h^-K^l +1OUvN?w74%m7jXgS^eIFAM=jV)Q?;-y=!{$XoTz@j&1|-$TCqw1s@|M5%?oCgSZ1CpyTeuK5g~t +&6CMr`1H}-Al8V*AVz>3vN76YT}wMV7Jo`nKnl}k4pq_HG&;y-{o291?0W7wa+?o^{x%@|lZVjP9nW7m*+KkaS6ITe)?Z1mpOsh`9tj +iOZw!daY=fjL +U1eAPxxy?@>MPVpE>FKx&y%Eo|sadGP?_t=jrkM+S70!(^NKv_d{@tH3!iniyZ^^|yBz{E)xoAy(BX3 +{%>SOlr_VcBFX`H&#oEk>rCkO1sjCz+{5D4)~k$7<}zYpHXaP3ukU%0qLt2MdB0VoHN+UDCl{7&K0?` +_V8>QMR7{1yBG0D%g8Wa?tL+5ExA5&Msq_=YM+XCl*Xz&)enLdl|pSowv%YK#iZ2Dl&oYf)IOwnME!G +!GH!AE=+gKf>3=xO3J;HOtOG|;ZiSg|2Y?BvpMdG#e)3m1`qxkV0hve?BN+^57#ia!h9$|*?Ri!lA-P +TNPJqc^k_4QBC4iC63O)2@+7D)PNziYMeqrv)fqo>=@TUMOz{#?0{21M!g#fx3W)rWhEQ!NFz#fVLIA +MRK-X~&UdIz$Q(tm~&*inmEphyIQ{A?pTn^G~%!1i0pg5KlG*RsuwXXRkwHlCbP;KJC4#}Bdj@kJYw{ +=U<=1M2 +;d3n&kdVPQL@}Ph9`u^nQLCL?9g=;lriI`=ZycGP9<@PWfp?gBt+Hku*19NPzEAo{lp9Odjy(H^oF@; +3oo?RWn;T*`xZhnXb?)JrvDA(^7q>rIEYC`77JhBIJ*C%{_?Snn8rv>E>$J%=;sA^oZcvncp>_=XU;z +nAzuJJD1icij@2W2Y17CYkJ4pQ==3Wa~Tc5Z>>jZN<9xTXy_gGbqW==_0yP7T)dlkbhzIUN%fp&AA<$ +;W+?8^aTXvV6UsO1qPkM?{^O_v9(`9kp8nJInBTwiW$-H@a!rJP$RtpOUtZcF@XXD#6}g8Gl72$E1*B +t7@v{vyA`p?7f|E(NlWuDdllw^5RW(T81Y1WwFsZN5(e>!L9erb2b+R_?Kw+sdNV23b!w#&aN)9QD)z +d#xZ_l&dw^7=2ais4h=;<+c4NMBt_(d{JUeJPY#Yg_@X4qD|gIK!ZWI`gS~$8;xB(#dR<_D^N|35=O5 +dXqK4X3s`qE3BSv^eMh+R#M|Cx%Sx3Uj!&H#*f(21wZ3?mj4Irl+A*R4qJ*FGpbWR+f$RwLb$ +mbG_MXhz>Qtm@_uh%wSw$?PVaTLQl{=Iq@0_e`uZte>A+#@CaiAZC?U9Rw&60@pFfS)nChX;AhIQo5I +R5Rdx?|RmS;sIG)X&msS>I{D*)@`;rpn?7`xhQ7ogp19bazQEsW6zC^~mM}BFhErajm)E`=rB#G!sMk +vBni#8+Qwe&z?RTeL`Q-$iWw4t?wGG=9Vas+;gZrx%F{VT7MWv|y@ +eoG+}A&Qx!Re7`fbLYGdoy&rI=W{ +UeW@Zy@SiB+^N&%rMwP`u=O_q;sn{Gt9jkJ8e-3@dFuSSuht#^q7wi<1=us8!Y9KrZ82JVpE6t(N=)+;4jcM;!AW6{lD9XR1_9=FU^6EK@Yd%Vehf +U@=Xb1-L+6MIkprr1&a!rdMIwzPxZ|LBSzR;|#NDUpT4#SclR%P8QBB~ +M1J>6FWR?(?tF^~o0_*y%fl_W^UdBRK1mT_79$Dmps+@Dj? +8Uw$F9KmG1U$bx>vIxv$)VmwgPQcP-ut8K3G}&WremTwUl>Z2JWc{xBq~BBMjV4@kdz_x6t2aBYP*j=2% +1H~U!?`bAfx+vdP~hZdgsW#Fi*2c5i{sd8Sk!+HH+hRT7Dm$@G5M6YRnhrgeE>vGwC63G`5U+qZsKj2*Jft +#6aZ`*~ehS@Y~fOrhT}_M#4=?Mtpo5f-;-ZN)2(I*mKXm{{7H&rWd{Eo+{Y)Tj!5a%iUS{fL7lCigTq +LLc~Ew@_TOuP3IxKU2|2>ZOmLdngdO?2qAzfA^2}mW0ValJu`$V+?wY@~y4IXZy&~Zv7EBldJMScp9e +(5~bOnQ0l*Pp08l_Up~k8a0XN~I78wbgM-izf@MF|)G!3-EHWQSx-r;w0;Z}ombDRKPHt8wwX+<|^Vz%hE@KkHiapd|XwH2WQpDKc1hk@;KPn+J~!GrVU<{S%w6=S5MhfPV`RamN3+bwJXdef&#T=R+lJkTZG#GI8&+Tw4^{oP=%HlTP{qhSXW(y9^!k +K@`a)FsieBuwm0bKa&&8A+v?nQ@p3lHfhu#r(l-eDseq}Vd<>-iaq7?mXG!lv1wgJUq$9ZS+C~#aU_{ +(#kPQ&QbyHR&TFA{~L{_aim8P9{Pn>l%ZF^E{~GwvmeXqRRRq;qN?Trhffr-RT}cds(MFvo?KRl_hRM +7x&>f{{{B#STS^B#P7-daR=pik$cbqi&GNbE8m_xgxg~N2rk&%x)q$?t`!<@%7qEJM(&;d0FwYK3u_6 +>YfQ=UfS-dvZ{d;(3xiQ;U(Z15mk)vQZB9mQ^XrR()_V}9jLH|sbXH4P%x{{O60=Y(7CG-C{umx-gpG +nY;AM4WDwJx6Bm+H+51qA%ZVSz$WN{os-kR*!gm&-heL13&H1iA;*};vPCRS78M-3&MSdDIB4ro!d<% +>dOdrN6J74SmLRO)+6L8!_xED&->g$!9-S7^=q9-^GV^@^9|QvD`V4RqF`b7E@ +m!V-kPxM^%ahg84pj5ydUm*_8#{Uw?|y)pt4io5AJLq>R#3l(KBOg)>E|_K5+WLHS_HyCcey02E9*8K +2arZ=PR3 +mUG-Is8hFlnjRGVYe=4#;?U)s@67$V6o<-42zV0piNgAORbFPrnk%Ep& +LryKqaeL<{mwyQO|dm+^@wo{9`N;kI4>vC&C~ogAQ~PF@O1H~9W@nJfFa21eqyk@(GIwFac&x_W00zg +J89xi>z|MRz8~yEuSw+fQqzm9N(1`BySuUwcKY%LznDuZbsgt;qa^wm9$k{fpyM1YRx*``r_ic0B6)> +oRrC02HT+E6%lUyv(bylvD!kTDimXKVt{aqG?W$J9ecOSE5E0#+%(RL$3EeUoGZ12van?T$nZ+lI^Co +Sr)=vnh>YBG!qm?2}_rs9$?h)NncgRWInV|Kq^A`S45KXVk)uVG*_FAH^hTmTphs4X?fbaaxkh{}!;A +OG9?0EFn7SFc~5y_CyFAdN4CdXYzX_5kyV(#&cVqO?=KE>@mO+2@;8{+2UJx6FYd%PGA`muA%*f*eSE +RT7VlZa$Y-Ea|2DV^J6cN4lUB~HnHFLU)fF~UdxICY`Js%&!~9vSk!Iyfw&Ipp-GjAF1yil1ls#5mUL +-qT6ZYKQq8NT+@4-!H5qSL^#?nab{AANR}g;EIQxWgxeBfoyb_%gfTz<$H`@Vfpr+`nkBOoPJYmG|>T>HJ&BdS-uhImZ^DQa5h2)U$pZCh*Jf-gqEKHFcC4@^IAfKh$!srJ}l|%`^Q^puD +#Jk9w8Nqx28wKlz{c9{$B)Ukk>+4*4N62V*FjKxvBPP=e)f5~DbZB0zE+$6+M8)`g#Lhp(kygl;mKO{ +1KIV2=RA(2$@uBsfq{;Tfp2Xy%{H=OrQV!(u@nAB4AU1JEI}>2j!TY-@|J!DFy7z{Q~30hHSr*wn_jU +zCLFiPmZ^j)7#G+e*LnIVcW#EeQvnyq2hIod{Yl(KZylbz<=C8?d`Ufga0R-g4WfII(#o0v!_xL_pYY +OG23i?P>0t8;Q;9J0e&=H)jSZW+c$Dsj0KSZIO2}FY@HiGHcmeB<~Au0GYM!_)pob1X-1hmFE7?#xQp +GiiWNMHC~-9SL;h3BL)-13+VS-;+JHc<;!DYKAVe*1tm}ZyvOlxU-|2WA#i0-+Wlc=r=di95GT5OlF> +u1{TI~5YIJh7sY)7Hk_V%O(<-%<<1IcO*vg+cB83ymUE-JBoy=Q$e|Dbr+BcfUA9ht}K~s{~m9@V*me +e2n$06?VvOb-|JUr~VegD>)R0|G7K#%impPx%q6H(d}0&U+^siF6fUSCcZeDdIBPU3FI>I1UtIn+$v2 +8|&p8tU|oOD!SlWF^9IlavU>mjg{HjcM1)XXqVThuS!H*5b;QCqJ|1ygTkh`4T-(Cz{XinpNDTVR4&v +ZStKEwY}Poc9?rYm!Y#oXx{nkcoriIu{GVd8|N?%WbAauuGH`M$+g_LWK2VVQ(iksn0YnCLiAsIwViu +~KEjaJdOk~MY#}sH=AfwkS`QcMak(WjD!dwF$3;8hmdf5PI>=0^rH>@gg@>@OXIP?98)|j$&bhbKBf0 +AMpfG4p?fXn&^Xz&JS0M1<+LYt|Fw@m=vb@NSk)n8I2UK}wgEX6vP@3W6ajx!SkYB1Xa0jt`3=dIh?J +kbYi@pNFj_X1Z9#ZwhD#>^eJEIxuY~kM?t3$=DgweKnk=&~q(c~$;#b^bI>kY=3*o=4B8{aFtyc2dOB +Y{uqt!NdCYCVx{=_FAF$d+Gb8o~O44#!s!303O!uKIvwYVUP{UghtA7Y??VJ#YkX;G0M#N6|D)n9EMo +S%aayQ4k3B4n-z?3tm*QVIDA2dYAvI?C`a+Th{t&{JQgyhbyxQ13|>AH*~EzK0H6Gs1d5op*VDeySo$ +0Qr;Vq_w|np{^^ekGz&$?yW~K!HyMjTtc-5deQo6dTB%pp=LbcU`V%gLfQ)B)6Fg&Od8Q*ME62*cC7< +j?5WaZn-3{w5<5xvmS)0F-F|4{O6}p~mz64Yi&4LV0cv+d{Gfe>?j;2<4h +_$bKNgNbzy$M0g*X~tZTi99e5Tj-M@m;Fs>S?78RgGrW;0C$z-c}F409?7Ukhxn%jGR+%ujkEID%N +ve)hYmh&j2Xeb=(*K}*%p;R@7%HPvc3M{{e+0*zYf>CQTbs8_4^^OK=XxUfcBD{St=pdV}&N4nA&3vQ +sIe3eg8?84-e(-O5%F|~WW8V9cztwR^ps`&1;>PwSIXb$Z<~Td#1m3F67TcTG&S_bThqaikA?%vT?p?%>LCBsY0d8k` +4t$6$YKfJarvQ?|t7rCOkdK>0P-)dSOK-Pt_?}w^uE*Pn}6MXU)FQ;mQ(1&QXYqbumX;JdZ-6yawwS& +Ak4~j`$Amvg +TuDd2EGLV1AJ^xJQ`{!A&uJj2H)W5k4;1FjZIkQ6MsG6?_lstwrwnuM{ey?0CNx-gudhq1lViq6YAoX +mPx|JV&9hfXS6G0{cw($CwBUq9>TmzYYxGlfZC0!d(13$bMi#UvBNFVgC0nZST1;|V@D#BPeWENV6zLQ+DuLM@{>7*}*%By1P?&?Vs}@WWkj6N7(W$};!R4)U%qa +LL6VmYCq=$rfd@XI_+KX$U1`+EuW=2ChBGJ$C +qPmoRL0+~c!mk5-EBsf+k^0D7t}=f<(4F-VG@Cg!;|z(J!C!B>S_V~bKE{)xXKq(ac`ueU|5z-P!xVv +A8SA@GGNWK&hHpLEan@qtq}hG}DR)OGck|PxE+s*abbftUhuDi>X_f5TFl|cyV8zUIQ$@zX2*Y^K^5t +$+Ca(qBsGOi)?8igNy}R@l-*}m8IG4gPv%hFKqFc%3#T40TSgc9t#3SFdjw{EZ7cQjqu^k8f+gkRYDi +TN*UtwX#_I1AD3=(pJC%;ug@$ei;f4v>IyW$u|VOf2Kf*=+znG9RwWl9{pP*v)9y`(5dhw)&RPn{E^; +LRKpby|9{*x|X)z`TaNk9gH3=^NtuER|TGC8rOkUOB^U{60)dd)N6UKQ@mBYo8_sx0K0s9qFALm$+&Y +N8R0Xa30+j%2lhabG7eJB8qF`Uf7#Fb5^Uzq0ShMZbRX4_1W+!l@INyV-jLte3MloN<5T|TRtFMaTk4 +0ddP~|4ND*bo}cmgU|TzkE)&^fgQh$$$3t3T%G~2nA6LYq1?g*9;~og2OEb%*OQ}Ymp3w +P7mUyZNt1;4$=k+zETyF>6-Oxmdz@PXVUAa~@H!l?;-to!R&H;s$tQ9 +bh8uLK7FbEo|#IQY|3{uWL_xFH8-C}VolBcxy-j^;Lpl^9H!&@_;Tp%hReaG%Q%qRrw2rNIaXm_>V37;Nap5kS;z)5(DmQ4F+RxJ{?N!Z(fq!P|PzF#&XPz!WwH +7fVubH#ECVc4D9|0Lf6;Hdq>M+kn5dw-X5fr(N({I4%FD;8b(@@*AA)WfQ%ewK@#^?z#h}a^@qdxh}$ +!=k;u?oU3|YW)dj3$FFlI2mi0Uyd^Lm$6q}a +gnuY^K{8X|>j*4Oiw0K!#Aga2AiR6jiFXSjuahqu3jTj&FCiGH+?EcZZIzLkf7;#B;KQRM4D@VFc(hP +m(i>~M6w_su&JkD!i-$0I~eMh-RlNlO;>=?^%?s7s;_&zaaIMFlf>@Nm1RsHvN?#DzTg$+2(GY@j_)f +^+k(KBpHLw70GZB|C2iDg3yKcS@F@W4~$lw|t+hQ0?ec*B_mGcQo%NQ9GkGDdz?$BHuJFFT{lQV_&|P +R{|pw#cM^87;K&2g_32oIV|obuqPFpvtc4{{T9pD^#+hLV`f)ZB%e%TLP_;VlZr$~>lXOW`p_W)@-%R +c5Y;JL`8FxDCs=vE9Dh00SNHUIn`JOoc<*0tq}QX+{u+5&Z>xgb*fmdDRL`&@JZAT!wEGk580dF+EAL +0cDBuQn1!US|j|YBuJYu>Pu&x1c3w@5a{3*yDl{=b4rQMmjfOm!4cu5|3=eP8@*_1AHvuBQ3yPt#^U#lME06lfYu15bbSdYtrKiGqRB6p5Fp`6U^lY7t|s^XaN2Y#os6EWz!;3V& +sp3k#{%S0W6YU6n+8}!9Ig=>tvl-Zn8E!<<*+s7-N7}*h&CHkVp(Pbt{amL=wOz2TWX6dYEpGDeI3R{&`pv4yYuX0YVIf5DA!QNzu(qWwUspL!kWz!< +y{II9ae)0%YykFOZm`!MWCgV66bcZ7PRl0B;RR5WcwL@ruD53Q7cEU7G-{A@{NAf`W@-1Sl+4B*r-a_ +$#90o9P(=SnbM)e~ZKnnC|S;-y(4>{(p(Y*1}U?k$7bzKOpgUa>IWgm7#yddcUFaHUA|lL;s5P{s}5W +-^23fsN4bL%@0&wv>#C!_h%?sU+4N}{o9QA&jwUY&#*fC1$)emi^yw*!PZT-ru~BG3&rLVjy@r)%a57 +65Fv3PFWeo!9B$FM8u?sThv1Ch1#+)!GHSw8@6=DRh|}Xmw=pY1BGpN19Ac+u(B9s7BDn2>bTv7>=>7 +Hb4$J;hsny;e*y^E~qemXYVsCpRo~DA5(*#NJL4h40x8uaSJ2ZZO;bWtBn5O62vTX)&Po1)8cQIHuu+ +=sD|pGKyn)nig3#9-SpvFy_4<>4)C8LJGjKPF?8&cZ!`ZjyM+yi0;i%$ +|%HMCAqreqg@)y6-47KAlPiMIwwjI3e>2Sd(?H>#w&oWF!|jtex61Bv!{F; +JN@+VAAHbglqD#NV;GJmF)&j4sTmskA)^n0)*#&!e{2YlE!>oS)TUo&NnrDt02i~*O+vS@uW0j4At?Z +vAXp>?GpZD`X$?_exDG-|Qh=`@aSPx4V1AX}g%X=~BKi``6@T2u7E_#VvB4{-qg%9ax|zbK8}?J%pej +KFd=5A4=e8a<&|0FKI3fdg6aF^qP;|jkSvuclN)sYh*4qEsoySjw5hwAdnNonG@)z@rEk_HL>veUXBm +W~jIX)dp(g2dI5}Y`YWaXGH&Z?*#gKD +}TF_f5)MPEBfPgjN8ZQj0rbg-;c^n5~eKNPnxdxns#!7rL&0FJRPU|%1q^qmR?CDz7I!eKhcU3rmxcM+N1k>|d$dueu%oG0BdT8PrG=spmDywK?^rmbGQobT9dn|+G9aE +bEE>q$g0B^9OPh7>2E$2zdi!D=jXFrEa(2&^V5MNJL17GK7$~$%-` +eigLjGvn4m_qMXP7vcPi!EWlHe)I_!koS=6X~-vQOt1i$Kjp9;s{X4?7pHRQBKM&Vr9Q^r`umeCs&gw +w9PTk+J14{Sln!noIH9HIN#o7f`}Iv(36*E6~|HD#eY2V@9nr4|ZQsBpsfwR=vhvm(T`Ezcm?*gqiLn +znIHYUc3xbEasF8-PJ6iAzH}GWf!jW-Lf0;_Bx}Ac1D{=Mh%Q^q?);6IOc`C*kQdmTrmtCk&EQsvbE5 +<4Lx*a9c%Wi`K)yQ%|5V~VY1lPTVAa;pyTW2Sx4N0^Y_WnqicD48rW5312i&7)GS3VhfIoa=Nf~7hf7 +0f_-%S;@qjs|C(HcvGVJw37^C;1^Q!&HOHl@Ypi*3!bvIpm?y`fL>S-{;I?|9q?RdjR+nNxv?je+)7I;whg&%wLE905KfJ5G>B(3=SsO7>4>(wg8;ID9{sKAz@v#x +lLU_ZX0!M3=$ZDE-%>zyjCLjc|hdKgVuE&-&&b$v_;e>;QNdMB<&;1F~K&c>jV(1k3C3W>-8&!hK8Vh +3m_^42)ME@W>ev0oD8+V)FUJ2<&@^*&h3!hnOGlOx$09@e{Dh}v +_lz{|Bp_k)vky>QKSU9CxMB%JIr)TATrZ^R5;G<0;kSt?SsTP8Y1%CI$v7(1CcRuvD6FXm;Q;CU* +ToAB6>%+O%-C98^qB&gzh`Dt=m*h)Y#ZpFxA`$`h^)Sq4)UlX|C(&&Dg)xa&Mkqt#hDjTY5*qSs!kv+ +ILPmWJ}JZa9J@v&OA}@$T7vqwS1mb>=+MtaE`c!viFLaMYxX{H2jdsM&%gp+4HrS^G}FgCWRHTd{{Ue +?Z?{ol~fkjxAb6+KAlGejP`Idyi2|T>Q3y2X +W#G$Aq9BcXp*}5~P6>occSDJP7zXdVhY*=`;I(NmDei=H4^>tJ)?yk}8S(fnTX(HY5N?}+!Mt3)}pk7 +qpp6rFDN8vUhQxA7-Yv=o)X#|)k)~&tN*;+pUWGvFX)8BF-lkW0<&cr!y +WP}Hs+k-1LJj~b4c^rR(L`QO7lfkfma;!`=DMk`Mgj4E2QTSBr?krmwH;SOe+;@bJ@f9Nq`f9b`6Pua +QK6W*60l$m-PNFqdZenxtbpSqbJ|cqk41gR8n_YG-J6THGCytY?=$plS7>;iG(|SZG8^8%0u2N@J>5M +vXo7aGTWc7BnwSMI$RGomq4!1oYZpmzH$}|3wha*_re9g=Ti*7!hYbo{~E*`+qAyD>p#YqUmgD$xO_S +Gdt_l)j$i<$pbSBRqyy~J-d{|D^%>pVR6yEq45ViS3i1HZZP*2L09oKt!md2zbMlg~8`mKKF-cLNfLm +EYgn@zTmGpo-;tZ&+=`D19#W8%NIqP-sUy&DP^GG2wpkgB!;NHBl4|H?9T<;Tr3k8N{)@xuSkixB~2m +HfQ&@NRBA-AbXr{j*OLQ5%Q>)awH~BNN7yw!9p9OKhU(f$?) +Di{rZ^&ZDwcm$lzyBMmxcu@#TRq$cLDu5s(gMI&=*ws>n?tg1@&1J)FkK`^zsqdPKboN!Uqxw +JJOxVx?L{po}(Y4@gh_-4`{NF;MB$4>loJMv8K$krnpjD%vB +}4%)FqcDKSFbZ%E6=<9T0ZdY>-Uyqb(uMk|8=}C@#D!Uo8S*c`>2HJ$Cvuep&l+v%t7Z%>QIWvALS4g +{sTs^@Ja)9q_Yqk#5vBx6RW)0pyc)w>*|LS)z^Sl$mK_^DACib^w7lt+SeqA1>(eqc@F!JXBql$Y?DQ +Iul(}DA|K{gzjny>y`)d?;Yh8@2(uTGSs{$}TXD)oT2>_khDWze4oTezUTP9zM(pcZ1TD2X4dA*>NBo{H8 +(s#wTL(V=r$vY}a9c4StR2^7X;XUj?ul-eNB&|BW>~yqaF}Pp)x0WqE7tFr1AKv|B@LI;mn%5W%^03i +FLG#;Hu6d?z7zd)>C`rFZVP8(Ff4{H)03*R`v?_MOLIzw-pS8Uqk~U>`;(UGXcx92RJylFsC$O2QAVq~p7WOa4&hd2nn +U+y5SV_w9QyC*%$+fB2#J%Gt0$ek6sD@@1|drW%KNro6zD$g9Va~+?qxpq +V{_xk>S3(>tEX5Izq_}CkD1S0eRfgS8cZ3OA#v=bsW(NCrubU<%Qb$R6}Eus-;b>1-)~)DlvqX)v}Ybw6;SupioeX@8{ra_*(98zf=vyWKRQ~qDM^|8>%vo)agXx~Q*{zDFk@H}ekm6&3YQ3-LWgvRzxnE+oW_g&?~Db1R@g|p-M?K7efFGxQ4oRSf1ukWis3lPp$to7>l +ggtJ{K@CL%`PItcftoA^P +B{lR+rXKj;gJhGSt*m^#|Fz^|xGgP|-Bf5Ppw+ej_bTG`_+zo?)nT1s?XKaKI^{U`fO+zLl`HG4VdXOnIis_@K(R +1O&OQ0{!?ivdVQTfMLiFKnx54LxPZ2@jr1rnDM1cW{b;a1$k2L +w(uLIUu*8lOE+miN=A0{(4RVuLK`trm-2qFHzdX=xs;4iQ6Ln<-DG32LKU+YSjZey7w3i@;y`(-ARqy +fESIbg!Pt~h80+?APMMEfvX+71r>k8cJj>r+9-4+oaQl?1NbFWelOi4Z&(e#~Uj|Kxr(mI)-eEVu{uV +@HhwG>_aCHHO^gNO3T$8Ep+q3I^uWO$u +NG|_r+}-}?*j~Ya3`ZT+Zxj8MAJr9oJ)ee^XI2-yct`j&T=5EwX}J1bdK*qWYW7z%E=q*J+(HtdpIJ( +OKf^Gs6fOTc45E13tOr=7zVJE4zpEg+W}ggk-2pqVztVX`bp|tz=3)?EJi?|eiTKN7P*4i+PpV$q6{V +Z8R_tGFz^;BkJ=5Apqxo;+DmmoMl%SVVW9+=I0ek88a`s?J|4kAx5Gkl%$1kT!{Q7=;tjMS8`dtGQc& +B~V0EK?#Lro$*_aF}RIEb38W>`4N78y{yz&v{m!w?SZ`VMRNkmtkgQ#{m{ONvK1te_>o63_A+`e~tvR +T9=GYRctnD~^*>7A%^(jx(1JRc0B1)(yx#A0w8gjuRI7!7B}JS-ZUO(o@OnRbyFAW9BYu4&q)EW&+;b +=tM7)y>}g)SDH?~2MNM(wwJp1ol+)?7sDCiy)6sPU?;q|veYR#+?zpNf4e6$%kDEI=+1B#G>p`vQENb +VPb1vjS-XqHfFq&-lVn<9cNhxj}6cbRG`11$`9@!wwiQ=E+Fik>&_+{9o%< +enDG4h*Sdj%w!Keff^o1b*fZ;wx#ubRT6tq(5O=|~={up#M@huy6g{Bq3*8i`Se28xI8 +{7t$#6QVjCGkWVn5bOuD#ZZsVcT|Kn1VS+76oF&wfbL43q^w&Pc#MK4m>0_I+ +i3g7%G|1~;(1-s2a!Vu%x6paP?aFXD^pq+K6%TdUh4wp-cC*-^97x-L123+ZvC4>hme@jZ74uvEad0=f`EA}2Z(^gt)f{%lWVC@Wh$J>&dOy06c4quw}pC2N +E$+>2=o=4=3JWUs#d7TvUzUNmi$n(kvh59@U?oWX2^s?lxE)!Z02JM(uj;Nf>eIBU@~uEOyPyi}-h6nc8|-T@K0~Y2NNo$7WrHV6H#{Fk}+nBV6wS;#Lavu&|ALD=PA2iq035{u8w|2mqnp2^z5;dwzT7 +jG@WHoBEuxAcR#WF_K%xa*8g#&on?IRsed^0N~-GaA3N(m{~z5IH)ghF@Bj<~Q-ts4E3kaz@!&tEmjA +cU^+WK_FAx0f$Uhx#EujSTpH@&ykga!x#w$^Ykw~?$o>vPj1YO|eWV}OAx$N<(wY)vN?G@ +9aVcoEx_yEtVw!%PROcBmYA3Nd`~_89U=E6^cF77hUu#L9ug>r~v=!SB59LGugTFN&1-$ +L0__ewrO-5=lbaEzrO>AuUSCf>E^t7^TZVS#qu9#NOaYo9~{p=HR7C#|3PT5mrdD5IdkQrGXxrOs%%n +#Q$;Ilvr+cFGEndILn1=w?klsp5dKEaR-7B60K-;NTS8yi+ct0y0PqhbZy)pYyboFaVe(pwp8R?@$(Y +lYPw{#Y^a(ixgW2H~7uHvGew+uf+!YH!P%-Dw41P|>qW6>*TA~V0o-ho%ByJvfX3Q>=v;yya!OppHrf +%2fK!yDa)6b!OjYW)HN&zHkzXI9>ETh)mwRSq1#<-c!<(tjx$2xXxgKLvT~0}<}MD +Cto!BmmdwB8*Ey+#Yr)#;o)oFLgRG-;V8rG4uK@4w?ZLlF73imddc%5|Qma>T7pHF4<@)ZfXIJYHGgZ +~?^p|$V+k^8A()7XZr{pO^@k5)$vUL^(Zu)~%zLCqlVY@T(nCqp}9h{v_WWQ(mxe+26T~_$VukKrmrU +Flo=NNk2^GtDJ>kbbSTx9RXV@g5LeC&O`6Illf=jFEd8Rf~$XBW?DF1^2ZcMET^B)-%I3-t(I-X2cgD +hK#5)_e0Py!hxH9sJ8$HSokTn;q-Xuf91cQazuT()Ahbb)rxQzKiklNSm2;;D-&us4Z1{ +8Djd6-79zXTlL<@PJJ3e2TgOFs|WwAj{IId#R%k-%}Ma?`jfd3dFUtQ;jx?_(jz-0oCbG%`#_05CSua +=KlqQmlRRzWTlkM%*oRfvgg=`u|KoqHYr#M4|3Fz3!x;KM)c^f~OYM-}x_=zsQFV*EpPz*IX`a6SQS| ++@`zL~oc(KGVd}ViH|o~!piAVV +zUqYoRi|)L=CYS!K~zu!na_gO)LjOW@8|QU(W%$r!+X~zuF_7}0K;BgWR +0j2mhw3>ji(KG`npKTGaV1p_Gq?g9Q0MUAl^us)x1GfcSake2Oj{EKSj=#c=AbuO)v6)-P(VSfnh4Z+ +)Mv3JJ>2sdC6Cm~{Wj*k={!3-|aE-g%Tmz~M18Ie68NCEk5!KS>ByDf1v+taw)m>o$3pCF`SKbK%i(* +{&?oY#6Ipnvz1#VO7U4v>-WdVaO_4K?D##{5KuBxppjaFKB +-<)RkGvo_4^`jv?|*1kITG8KzvJiU(1iqsr~tK)}|z=3P_dw_j*-yJ;|afKq&ZKhc+;u(OSn`K)Sgiz +prOw5*P4hel(tiyDzS+SpK?8H*WNL<=gAm(GZ0X-){HIW2Hx#ZWV6+bKg}}&=>PY#t^?HE6(=%TBF*0 +UYx(KzJC9SyEvOe2Z;h+-6!4USGF%r=5pyB=RO@ozW!FnU|ZH<6#|Xm-oHgkRjwu4c0%ZnWK{qOBd`!+5M`uAv(T3+P=Yk3x5NDM}~1&HOV`vk==)~gBmndff0!so5ElYKw`BzV=8(ES(}Mi>7w80X +A=5X=T-|&!P@Y0ca(?0a444GftEk}W<-0w%i894aqa-hmezFBn&d$s0-Jkrjyv4bD7csOx;~ZFTXqlc +9^lqETFlwqT}>`FZ|Zd+l=HB}yIZXnn4yJ!m4(;n$;c0h=ma=KA6zOC_)Qe1>WWTQR;~x{6Xr} +9cKTLe>Sbhr-7*-c*r#}&>GxQ@xEuN;IKtgzwHC#FiXnM^Z^4+S8V=qNxkX-6YQi1XZAqGvBhZRkkz@ +&EYUsJY_^O!H514KtyW@C#I&0ieg8RVgJH4Vj=ECl2n?Z%^l&|!Pzyp<%GZ$UG+0^>zPn8wwG#0?V=Epk)2ddM+Dio`sbr*jg-FHcO#} +-{06v8iC5t{?UMQ2T{os8D*R$MeKwEhR2^|7yGNR6=T&kdGbOHOI>Mv%9sczG +1fjQ}rgtE>tP-_ZB8q+FXw*Fb80xt8Cc7N{H2o|0#-y)2cIOc$Wb>EwyYOSW)qCBkcQiedh|BWDn~60 +IRJ&0F-IuopgrQ^NLITn0j8$yLvZb_Fjj&?#_xQGDF*&JY+6a`LvO@O+o82uF9fjl=kVpfD-rZJT#>` +Y(F-F!KBUBJQ$(Ir)&sueAD?+zmS@nvST%@HH&)FBtqN9RHIu4>H}0SnmJw{(l*sA&^h| +&5n|iBXY9CAL65dddbj;Mv7&}nsj?r@Hh}k3`#mybWfgf=%nE22fe)tY!>PWw1)PV +;NHBap5mfgonK!2%vsUO@(%l*HEXTCtV_NSPK1IY{Ea18qm!kJ*zCgRc_F$+iG_{*XF +t&!~z5IAtn52O*EKff<>x^oxp>Yq;R{0w-)m@ZD$3YFDtu!iy024t7J;pZM_P^wz<5GK7+7x4 +|$eLP)k%w)(u-ORe>jii*Qdzl3(i24PqE1W32P)h-de1`}pFwQYBIwfXi3bfU?J$AdWZv)5g;3g5Hdr +>-Xi3vy&RO%E)`LIf*7N>kS07<#Y_a--}GY?<*;jYPQ4oc0&*;LaD{EQa2m$JwbY(6PbR4;>YLyCl$$ +dz&I_Q?SRX5z6EEteiH?%th!BpIkZ-u_~bgm&g+c6?B|{fuwR+NzSW-F!3dDPaeYF7}LE3lMltLJWMc +LuVM7dqUrhQ4w9s(c~5%uq>mOMsHW6I{l%JS$ix~{WjS_4_}eVuTe?g3+&l3Dz`RsR<(No%Sw-7t;u% +%+4+J>a%Ml(deWp$hb+BfUE0t>S?ark;m^!IW3+p4%RE{v%L7~^O1B@#d_U0sb|C-9fwnz;Iqp9Tb%3 +vIOa+0o`)_QVEJD?*CS%mkTwjk{nYI>RlGRL`QB?&Zrv)`0C4Ws25pO^BSh<0{a1E`0iw7(MV$y1IHY;d}Rf)68=#>f!9G#vAaxsbJ +feGT%y+9pv=EROed~N|R5Y5eIrmbIn06nAa+YHc)IO*Bg85=@1G?Igz^vK;xB^b#*~y!>R8FneH`C)- +&x$c*9MuvjxqBSZ_(X1rp-s7)y&qU-KE|^P>iC6rid|SwF#g6s~RaxcUudaB7BPPFFWNYvNnMP&`!7Q +KoV+Sm}#86{XN8<_l5!RXzhHn{MHZBXzlC%$CY4yt(-;tsO2BHhrF&5quI!MqNv&xCDG1IPb=ptj5Y{ +=+eIu;I(*ih)a6yy!UT4As@<6lyN3Wv-f&zcbDl;SCBY+_>HnVraQHdG`GYEek|qwsatTg +IF>)F;+^nHD%#md6eS&Dhi?uFWq$giah8IX(wqr4uEb~Nr#iTXC0+8fn%NJ@S-js?0pAjxk%kI+S*Ne +D5n$DuO?JQbvkqn+%n9J--np}`aeGh5{s$6nF6;3nGQNl)^-go#+REB|seeixMF5gW!IrC4;?Jd+{rC +Rrc%I8rVHg%}r=j&*o0`SIlu_J3}%k2VMQjv?EK_>V{%)EAtFxFQE8c`)IXUvv@8T7FiD4dA8z{Zn?4 +oL?TJH+(KkTwMR5n8nCOMIkjuX`?R23SEGykk7+MKyVTrE?vUzET)GX&w`y+n8%sBd{af+VFF>VXm~s{#wC|6#Yz}pii^&Mog+Z7 +S>#w%V8YBH{#qeKqPVGRJ)_@#QrCDU}uD{BmdC6)k^{;$=hlNt*Zlq^qBqU|t$TX5%de{@nKtgP^>Bf +N$SQ8-73`V2X#(fa;Q$i#(zqRG_DTB(@fcz2fx)?Mov~|;P|BRViR-Wrv4Z{<~onpA)HXwYYReE8J^+ +coh?L?d=diK(j2*SQCI*rrL>Y^eF8>?&i_OTeQ>T`_M3~0geyF~-Q)j>x{I$8}@5SJUoYT{{xHA=c`c +`B8*#8(OYT5qP-cSPWyqP8d?+MqpIyn#2W0s6URS;r%3wPQ=ku(W}pVMicbv&ZRy^YFD~l(qurl-V&f +?6n1m70K{K&oPUxYXn5#j-Ia`ey-OBmM-zc!>h@kl2ZlA7wL9K-xl@E*cg{s%Z*+kmVhcZ4kL-eXZ&Q +|fFgZMZ6rP5r$H{MhGvS)i)cLbtm4^PJe+R}yqzasA84pUF6U$l?c~Ycm8Xho8sPvc8c{vm$WzI`hHw +F;q}tlNm_{;fNt)4VO*xI_q)C=V`wH4lIV~g2^?u&+7EDGTxU!p~5P^2?X4S#p|2N*Jsw@=Xi-roQR~eqWFNe4vw<=NX{=#{NlDvs4~G4o-Pxe=Q4+f3ura%VmmLLde7p;B +a~~vrzb<&?-j%zpmo;sT36`ifc_h2Ep5e7?Ln-9{L=9xAW!g`acWex9;t#D5za(72Klv{B-(R9AJL=i7&knkF9E5!o!H%E`c?2-_7x1qGFer7 +j&=VhK&FoY3xxYn;59}ik)~y}wrJv^ZJUI$lc9;dhM?V7fJ5ewy{)nf8N7Q6TdPI7JbauRkWrr|q{*7 +-e-H|5piTRMn{Sy3WoH&Ym(vKE8bo7M9=uu$`=10>R^m#y({7c9d9$q;p{tC$k1@TC&j +6hnzEw~9b{Al&C~OBr@mCNtIepm|=}m9aMds-InOT+KWv);ulW&zc{g&AJUaQ-lP2(g+|3U3(e%nq_b!{h$;i>42K=c(jm@HVzC^|m7a2Z(Ir%%!q3pW@|WmEc|kigRz$ODTDvmjV-R#x!M63283?ua-*iYeKRMwNddIQQ{0 +AjPmw~SyeLApiIs#?9CkeK-?s1w|OCnx1dglK3@wf05sHui^=M!EB?Azokw0_HyG~fJmj%dMTHY63~r_VmUXL=qqKQBFIX>jh%zrer_3U8|@!oC9bJ*%UxC +?9P!jAPpil#jnaZ*+$+$D~JBm;nUgFHOmdBA#Cf2g08BtjjGLCbGRTK>SLLX`f`pV?f2NxwCD4wQ{t= +RSV*nB&$y|*!1&LqUk_Dp+`w6bGMZ8<_HfcZC`*tAiVQ9r(S832s1e7Gsq>D|y8khSZ1$j=`lC0EK)~Ki#E*9`(VeH>fi`VaTc4F$=LxY@t+kokv= +`)3+4;;jPliB*u#pUCf@U#z+6+k7=*=%J@Q`9-3IfWq6l9-dV?TNmZoO50!Mov1fZr@B4S}|g=2WctAuYwVYJ)RrK{%ivwdE)OU89K{h +Yp@ip;DtmY&Hjf7z?7E~g0wIN4=2nR(flhdi588&jt%KTIkV;QL1^l}cH{t{&kg!$apa!r__*gX?kWZ5zc72c7 +r?V~OLkzVx9Gh1(Buo4jB9mHV=di@Q#Jz;#wchxr9Ss( +)%)63;@n&NDs;9hou@ziq>={bqbQV +FSrvl~nT)kT?#=CEFvo_!YL2+oxZ*)O!&n&UokHk}>g#2&7(%(4w*I)_x+h +A!&9f$t>M^ftG{{A4PoIG+4#-?wf`DO$nL8PhjcGw*S|`lKy`QmeeR_dq4C|Gay?$%bMM*?w$$hFz`gP@fgVP<~q` +#kQd;ptugg}1$kE6m)l%x6pC6|Qa;UFsu?57@6J>#@Ig8ho4)&v4*}F7lZlx1F;gkR0|t^lJg0WB*E# +Ut(4s~32_qA}o~&7=-(c3)RgGrB&^)}emY$R?H|5EEcro?jYV(RH?%BH%B& +8j^yE%te7&RR4fI~2<_44v{0UbyVNx}(V^kO_nAuchtbadA_$uo4jSp%61=1sM9q&JenZ_^pJtzo +0JYjZNd5J|&(Q0+;&HdaY*Ko@Uy7dQ!QxpWNaRGZQha?8#I0Q3IrUKU;2anC+Vw8LCeL7E%8f?dT(K6 +h;3~XXOu$5Y9c{VPQd7aflDNctO_Y_6@j}zX>_b{AEeVac3z(2^JF_2LVdW%>=G|#Xg=_6KdXdJZdvp +ZKJz%QUnD&8$b!4-Qf5~u5XKu`h6uQz4wePuX^jTf-sbAL+bi^Ds_Gmrt +9h}(%mY;sdvKC0#u4y;A-2~$wqZUWn2xWxr|WBLG`o2MUshw`(q%J?4|NoP0-Ciqjl{yH~hd3T8#X2u +*B!+R-D`9M4-bozX7i#FePD4OMG+x6fDs!-2k@d^EX|gFL&;ZHh~UprvYtTds2mW_I`xI(~*sJ>uIy=3$t-{NYx?*!nR^nHRky2i))`xy&*<~bK2!GNQyRN_60%I8i1Y^+#mBK1{$%mZm8X-vCnbo~@VrgwTfi2j|K0&oK#DIyph!nEjc-=ZRY@@KBy!@um;%r5ip` +!cei@chS3Sojr$7NZIcp8E34z5_nFqby~B1r5C=hvvPq!!;&7N1inRvxlU^U!-;n)O7u4R5%QONyUlS +alN!9vhzQf@(wX3CQk-n%|iAh8C#wVD)MWMd(4AXF(q>W*J_bXKmdKV)NQE5ML1axZUZn=3G_YaQ(&u +w?uXfhCksW!-)>b-7s1|Lp&9n9VVG1?V%B`HMEQ*-EJo?e#Xkak_2#+^0qOSCGmf?fm4;}by7B{5Np0m0ttC&c~v9An&*Cg!zl@5QLu{wz(`^dIyX{~s@Jb2 +m@_CAN>?G*8zNJnM(A4h#hs+X4HptGC$~%i2SG_-)~HOXoU| +V{Ol-Ihj)DUmspIOKlB~be`!+wDpL9r=y7;|0o0A|zo8zvwZ}g-a|uc3v}wnzab&cz@Tn&5FL1B}oou +}9#!%Ym>?`xopbI~q{GA^DNYGs)`A>aWx-JR!7>L1e4UG1v9Fh`|sxRU44a|LmQGcTFII3po)%1M$OH +;G{l^^Ly6@I#!fBk%a=kb95_I!Wm@qqvKeE;q7zI8zZf7J#}+boq`t@%RF-q@&?d&zv)te +O4E@@UVP*&gk5~2=WKUh^0H!xW5ireI^MJHw)#}W3A~E})iZ+Yb->`Kz6C~yr_~FQD?hCj;Z?v%JHt< +UV7(1NuR?8s*8&M8X*n(8+;567VZb$~hd{4N5WYm#Nzwz(*lxCr_Z>xK&Apbe7&cWLS``D7t~fTDLD{ +)f2Lvg=)NK75F&FiwVAbLAj61@`9q^j59<o3 +3r_$S@MEHz{6KPv{@=7R4mT!f&=_I3HwX10A^}D>~VuQuQB8DAT=7vs!-|88ssMkd+f5zSvoZRqn@tbW``sYp@Fz|5VX?x{ +Pb)L_smZ~Q5J-+5l5A9doRS}uAV))1{0V9tbX%Va;X}X$`gi&_ifIp%xO_K>9DA|0U*)HD9{j1O)DZy +P@n?Si#-F$F+E*68Z)V|-X5oFMzwIl%paeejhgK$#eBv4O5fEUG%*i1rYI6bsbNR})%2(mEp{BqH$Mc|Z3lt8Pi!$<27#LkijkcC%1ZrRU)@G@v-e!oa@$f>|G +F_>0M49!NUJy;UdcA#)>LaH;JE%v*LtK+9DEBGrC2mg(!{rVrPVcvbdEAXae^cx=vCsnYz!;e +&ljlkgio*aby&nf2U_77v_@~=-_p5v$GaYTim7si|OAY_f2@*dR6~RIsp6gv4i!*o{g0y%LqBoyTGI!@~{%f9XkENG#}Xp&MxRb}-f~*4J +e<6x0&=-!K}o4EUwcqhMr7(`8B4Ws!bJcv;9-JQ1BLEe&u_uS +n)Lpk<$1vO&K&6!!5b$dxSJNPfR_?y3XIa%j2m2dn|Wzkuk)wG*Ofag;fAgSvz~V~5U0hK8bc{u5}Pa +7(~B0MO2hyIVg9Vmf}YZ1N`Bsg|m#a@4~*x)ZMwm|=Li>)^FD?>Sp8Nys_Y9lFZ&K?n^K`F#%x_>yI*$0;vyYW%-3{Pk)|){H#bu+RTxgx6;u3aB=zM6K +gyGulop%mAb@xlbQz^;bGn|WD$5h<39X2b6NVrhOMKGp%atJ-olv1V%1ll>Ut4kTT9|YnXZ#90ZBG~S +rHr{`WTgqAl9NY-m!(8EG4))@tBU6fot(y5LOVul140q9Lav|-8XDX>Ku=)z{_()o7b1x-sS+&qWL7Y +?LDLeyad7Z2<>Ni%Cc?dMSVc9G8InWvt|E3+-Gl_B(mqKlcj3sP-mgr&X% +kO=wokQJ9t3eYHYXuohkt`Qu-(e}X?6L8r5-%Fs +%WAl#LAU_#7?z&a46Vr-!A+|??P_*|-KNNq5D$Nx!%<6_f>cXelmJc8Xbb~rJ5?_&R|9Vj*>9yf|csQ +*f-?5Awo_a!94;Zl$u=e%a^^GB-{!n9H^iqi|4FO=?Nz9_Hh~PG&jd6n2>nh(ydw0h#!FWPMi{fmEo$ +{MQkW%YFpicFa$$)20@rW)4mOd9jS>rHX8P6}SzVfK4Oc`ykF~rJU#EM2^vKb^b)-!oFS%55rF!@nGV25#}5|ymvJIs1IeI^gj6Mn8i +NUANr7Nf`_?3D8i_Nt}`nRz7eSSc-J2#R2(@%-aGmr<0HC#Xr_`!#}4?HGMD+xzRvR9dG`yLG?K!u*XOnc-}g`F6mUh^ioer +48fv#VK9PgFejjw=cTh_U?oHxN-J>3KaX`@S}G5O<9k{D&5zZ?9MO1A3GoW&SOX0?d$H8uIM6qbo7lK +gg=Zl|8@UYDonqT53?Zuj_#8E66ckVIPamBQa?zWX0up`Txb-mgq;yH2aHr%f(U0$_$c}&#}s^vV$Tr +^xh*~;bJ|gLM0e#W26-O$Me1GZ!@fwquZH`IVnYI~&`t2Lj+bFhEaG_@9s@|%_$6>UvJp?ui0bw226A +$2UBJfmfS(qL_KVZF36c*D-yGLQ?uYEFzXdACv!3`i7x&8Bi-5-aRD@6wJ-Rbjk<@wTj!QassJ3#s^Ol*ns +pYMe8}q?;z=I>OhDiWR^vF8uBB=+zK15y+-CvCU>qtxK16A-m4^YdOAno*24&^}5H`8~eZ(X)J +x81M2g&3-g9yDv?6c6HVW!)|RT&fjIeox)f#fzBy`@RM%IHw69<}-*88~Y8a>Zh3O1Hr|G8UgQCOu&2mV$hv@uy81iYRB8z3{xUlt-7Y_7X3SoaGu>|ZG?9 +H0jHZv-FXwL@P7>r=U2LgjPZZdlmw&b&Q=|ABPB3eN5;5_{QQNv^wkmR#P6YsaLxi1hcNl;E5j`=~xY +&me)Rx_MFWf)0$V?baPM?n@ogX_1P()?7g`T}Wro!mMOQ(w7WbDKz#I +GKh^~71;OA*!qYa7W-hE!^g8MYhN={|K0Ssjv7LcrM6;XZ%uy +hrlL|4{^Gd`4-r?4tWf-lT*k-si1Z?vlih2 +x$r#-Nrj=0Xp|RGR0MN@NJwe&NolmK=jw-#u%p5UT~b?-tB7&+!+6;}y6}`d_!C5OFF?dfE8RV@;qlUE*}ves-5kz +Y{IK)$(^p**rt9`(`}_OE>k_)@5UOV*$vGis2{8v?)6EtSNww+6c5Yc=jLp(tG(EUv6^V)y&tI#zZ&# +BpQ<+Yk6fVLgIyLVbSg#Yg02;au{0n*8Y(uJHUJR9CU2Bi`m#&xUTR@zy;~4`Pqw^^s<`RQY)MbBFMMJ@R`8@VBFW2v?9WiGe#VLJ5?_DVzjZm>{&nmEIZh?wqkIy=KCzJEsH5E9!o?A(pr9k7fPF+154zR#kQKtw +9f|$EbL8L)0gn!o{KHqCeIQPfAJ___j;hEVYVEiuBR(?1pL$LhK5*U+fS}K?;V-%%pZ?$sI=YsRa>{) +ue+iEyaAR2}`5wEFqrpV_n{n<<`j=9@cJl|_HbBp_a`0LOt-`p$WBG}0+jha0u|Xr$ZE$A4M-rT`+lC +qVQ+cJ}f71^7kfi#v>FJ2RWnWmyzGw%1-=_2O!Z)-He2a{*2dx=&b#1?mcPwLnU9ov~o+Y0jzl^fsjj +wzNNBJgyYy*Oy6Ys&1+Wy;!Cjp-EbiVk{=(V1rYu|$r#a-JsV(q^Eomj}i*(}ykt>QkS#Ta;*5G}po2 +=9q3_2&M#0Mb}nuoII!U&4{aGy}%O5o;q5ZiVaIE>poX>BF6iY&1$8Awl@8oQx;TJv7(3gbzT3>xY#$ +pd+r-Bh@sTz;&zA>B^u~SLYhJd_8b4??irGSQ~7Dj^w@U7FA5$u0~G-tz^Bn5{oR_WXv8^A3?^&@e(D +`-OkPM&>p|M`PEQWv3uY-PT$ZLbe|Iuqn!MzUVu!KvnsejapI<}?C)JB&{~MrFAR)l+rNVMGfnnqG$( +Y{evkIk0ms`+Hcv8PI7~YOpehGcpew1fogK&IvHF^yOjefY7b(2rM>@wSDVjX}`1IR77euhnLN-;w?LuPQfBO4A`x(VEo(809c)vERc7LgHF9XXW=e1g=6 +i`{snXs}Tp?FWd}b-_xC_2le9lun~Rs%{?xY1H51aW~SUZtM%1LS4Y(Cf`=!Tx~YdxM6RKmn^9=`}NG +jD_rx`g3opfm3^G|Rtrr25ZUx;8DAZQ+|DVgnS;6P-iPd{HjH0oo$gcnMPI)mo_#v6N9b}$=fb|ofI4 +3e3Jz)it@EkJymkIhjAI-9i-3pFT#(B_H2A*W+X1+S!Yv8gNdph6nySz4)TaG8DH1yGbfwly*y5MaXt +IW%E{Fo^4Eebc@bykiRTUWoi8>hh(r@f+`|J21P7W{o8X(k6=>(EXe^~oM?i14#fj?2S +vBwU9!CBg&Y1?KXTC(Y6W>#9f(bxScY0;)-nz_-UfBfZ+zd%|t;OBsn>1k1?FZPZ!_xI$?Of*Z=ez~7 +$f^=Te<#BcVACMYU1*)A7n +?FWNHh-5R7ppO!z(Azpt<7{R( +9|sVf$=KiA_-$A)8hs?Z0dPAzqfm&l1NF02bC1X=okva#T_&3FdV&sG&{Z&1$uWp#-y0#Z;v(@5tCLt_p~VO&}BBQETV#_c2#ukeE`M|A3MmN +<ytp&_ct*8dy+N8flJJ8V*J&gqO3h5|82@E$}p6B2gL7_xbwjrBac)^?6jdCf +jKdfDAcZ6zs7^?yIRxLb!{UcU#l@uc+Y^DxXgUaV=8B?od(_gv#mGlQwVVh)lHwJzxpwtsu0Q_^Mg%E +qK}BB=X3bDwmR($`+T%@PhPwkvJSU1_pn5*64-!XG%t23UdF{QT2DVi~jD3Kd2S`c+#JJ5d_A6*@;zr +c>aj|5WLF|IlZIjn?SxcKT#hI^+V3DIO=Ap-y}}cBP@!Nhf8tr5uqQwQRMJ8_SZRiG(qjJ4+T^3&_XS ++Pd|C_Zyfs_(-3vAI)Ax)@zLEoB +I=-}vV&F#k7{{AFzR(_yrJ=M7{z?LSo9KMX_uPrQMFkS+lKQTO~B*bJQA0m&GzhB*7r@PE;zKiZl2RA +(OFDDievFJwnN_G^Xf{HMOBuZ~DV!+Mj>MkAy8X8hR^Sq#s|fDb>i!2hHx=g?BPD*;~fZjZYqria;@=lXz8v +l$i)&wo?iLfWXCc{7$oZ72+Fb5K{&0IiaX*H6o5uwAOMvc{N9(!r=8C#BbHS4&J2BymTAg4FGAa@U3g +`f-mNJVx$X8zVzk>`OV`|o-0BaE|*EvLT3$~?M+|#8$4dlk!K-6p%!cf=*;|lDD_=Pkm>B)VA50_8$I +Lec(E0*)d>n=$V)Ka$=CBK^xRp5#RRc>0}T6p-Ist(t{~|(4$S+M#2XqBqWDasO_X8{ar0X+dRzDiC5 +SIlAz2WLPl$bf6s(%;%YL1t0E(-RTlgsEUJzBOv6zA|k`Yz(&*6XTk=aq63J>28gl&066SiYC9y`&naeLNY6_ZaR +_g^+|>#V>h5MrLOJbqM>++^|HylXF +(q2JAitEQ+Qg+ePw5Utokf_Uw$nAAuad?kb7SWn*Dv!^9*p_e&1;oZ7!2dr%=kGcFb<<1+~LD#>-R?I +|^JRBP}%mT1%>qr0cUW7`(ERgx#^1xUgb`7jZC@_IRqn@x>QPrZoxo*ys7whd2IiKi&R0bbwa%Mwy&= +NTTzXU!2bVGt*DpI;D=lvb+l4+df5PSU0M-p~frviYz +YP)ygqp^UT+?=U#%_bu}p{#ip5cS~v +8DTp6U*N;D|k&Nj&mBThh9w>d=$&V@PWa5=476e2cQd)WDkEe|Ivg|=+11-;88d4r_UR%i)^)ic&yZfKC>%Yxf +k78Dj1R4U06TOMveEK0Uh$GsdVy_OHtavG8CdBI0p-#jF7Flh1V0MT;GQ#kEx@eef8T;tK2`Rn|=ue& +3j*o+STgHEfwtz-Am<*)cVlzAVa)IaU`8U6gdQ@-P#UmpHLnwp|O6rymPKp~IIJhXs6@k{6v-Nf)CZ4KjJ5ljjnUEp7vr?7vczwT2!N07K6k6LhoIIajIN6a|>2wEL);X^@VM^NwwejSQe`GNOh;&_Tf +J{kE`sl!Je{SatI{!RY1ScZ9o6jt^dI&x?wJPA(@(Qe6-Z+A9*Keg@pOQF>nFgD)T8TnhVQ#)FE;!yA +sc=uE9{Ga-RbX{njHWAy(hpFk{c|YI=@D*>AS@TVeI9+!1gMPS;Ie-4r$|V1|t)Y=`3huyH33vY*F8w +7i-pQe3;qk@S_|3tH82&V>KOmUPz<~an{_|h9pX=%gUyE6etu1!1LD3@u5IC_DS;$*XD6f}_3q5njg;a9Q^!j>H! +j<3=alSmyBZ0jnm^(rEewkAQTnS8T9?*?}QZxu}pwWPyYf=_E8h7dGS>h|pjmGjvI;KQgqB7L6X+DA) +0eQ&8astjxR1pC~JU!MBGhJ!!RTm{B*)Ekgt!=%eeVz`I)Q(=F#VjbnH;yo0wQupdWw8T5Li$KD8$^U +NW=1A043)e4rv}OqDdH{CLmQxaQZKPQov?&jiBZG*9CV3dy@=}V4rJr}a2L9i4C5?x=}3epngZ{{{%s +Rc*hr9GK|*sL>oVtxCDS&z}1lCguw#haq*)#xD(bGdR~QOn9zoRLhoi2?%+c +Tk`&Goth_Fe4{f?nP4T&cvYVk&y?RL^x@fpJTjY+8lY=1$Np-_JNoPtKFdqQ;A{#hL(3*NbkeZoB4`M +y;&vrUVdLj`yBfQ$z=%{N--IC*_3H{1{UhfB<6HZdwb9h +?O7D)u32?M;{knC_n;Ync(wYS|*fZpe=ymF*r*JAg{!g7-zz+hJJ0f~dPP&`08?~=4=QHPVVspEJx7q +^jE+NJdc^LQY+2~xcLH9CgfoyRd9D|q~+plOw=QArYQNi6LC`YpRUPZHfKU0N7fkf_|8${w0I^Qp^u` +)z5_XPmOvC}m6f$ooGY!O@uON!zYn_GCd+K`QxFrU4z4uKlp(!I`HdsVatg+t|bS0JweJi9N`yJp<%B +J7j1d=KB_!gTj1c*N&|x1KALo0jM7@&AzbW=oG+TesjnPqFWbI-+lU2ckzHdI3>4w4xURB$}r`ptN(_ +=}dR~-=`|7BGS$;`Osp4q&dgz#$Z}z4SI}=5xQg@Y)NOG%~Bvh?pXwGoD)PCzcFv$4fOrFdLq$>+|VS +I8OnoiOForT+8$Bz;6;*6BEwir#}bwVOzdVZK_4uwt7byJE~RELX??e8%m8s!NbNpJFnG%LU}t^d!f+ +2S%;?|>!qw^}H=xHuXXSN=n0I$2AKlea#%kd7pe-+Zu%dw^@b +=zA>ZAQxCPZyr@N{naKIo_sJ(~p@8PKYd=19=VJl>UZFxw5DrP3O=AVr>5b^|7g((Cu@DynT@r($+$S +w1O6T+Glj?MTbjE#>-b!5$k;+uOk+%c`&Wt9o7f+0>E_kS-_QYYZYle3M${tMK-QqA7GycWN#mn$CM! +s1(%h9;vDCcR1lI48LL%$?jpfcne^w2ue&vSn9U>YB!$bM6clIx-+}dpV{EP +WtpVP9BF6b{8vcFuYj>dJrkd_T~ww2#%r2W5TQ(qO&{&+*bMt9_a>o5|52ogdO97A#Zdq^(+$XFb$e( +2F)AEyUGB0uxtRCMT0>=+G=K5}C84*0%5(gz=f^+UnpqgJ-VCn7r%P4uTvA5T8~i0Mc94E+q|<;MU#z +9YFG(K8-9$qz_~eC7nxPsifEw|&L^IG!Fw_T)2V7<~X-0(}M?u#Z664ieErts{#*gNgKq4jezmANDiz +gS_$2kep~AMRxArjM`Cq2RlKGSNL~n`%k{>9{;Wf(Xus@I!^xdAaYE|#_d!bNN#ru;4fq1I^<5CTPu48q8)%OA>GF|yMwXm6(h?#YMXI~dc7=#jm#H)*1}CHE;|UT@1{)(U5PD9#neCVVALe{SlR +17DD7)0W8VS#;jY&A#fH%Df32U!aV#xdJPQWG&vt$@9Q9En{f$z`moCUnkNe{2|1e`dZ+yG#)>nnfK6iwS|M0l0Cw +YYuCxuvxgBQ#&0%-UC{mTc&q7G{bR)Oxz$p0-IyXT*yHaCk9;@+wH3eXUz4Hgz%N;t7t&vXAhADLC=H +*2ZNw;}EgK-)I(N*u>Sd+Q)Tg3xG*qnc*qQH-gxRhP()Uk6Kz;(2e!jCcd?bPq}1-PJRgRv|P`R4>ZFp?LsPxE5t$t$6o3@5ZLo!*?P9`5D_|6(%w0iBq)Osi;$YMC +*ix2Z#rk5#&zg%>q5LmcdtqJhX?WG*ArMn`qp|*94?|k`tkbqU)WPpPb9VXqmaEXDr{ucT~39%OPe^+ +=dCG+>E^N=*;GrdG$Tf?Fvl;rjlqJqs9|1d^h9c#qpN6Bm?1_YK78aT?x;PF`xZp_L6Wqy=#|wR!>2^ +R$_o#5(R9RN%3|_{Yu%QZRsYy#@xE$u$>b|530&?%l*y`9H$bH>uPUZAvDEvI*|+Nf|soW2BA-;-6#8 +kfAwOsOuQ*RpEv_m*&@6?dlN12fPpT^$B(4+l{!^wI{DGTpw_$7aHF2)-ftPbypK#lzD;NACr +Qg$qFGA1yUAw$Ls{4tOL05q)qU+`&UW9FE1k=>(1_g)a|DzneAUZ&Xu@J%?p#wxm#c9`Y-r)_j4HE)g(z2dd)P0wjk?U!qPuuK_&egA@313*Xbr+P2a7u +!t1b1z~?H6#X3V%jNVUnAge0wc=D=ef1dt;_TlUK2-MzxY4k24_P1#qEC(S>Oc_g(jc%5sIhN6!aK$T +WTKdGEYgKdT8Um8oB$-LpDhX^Jxdcuri)KE1Gzr#YI~zWEWu(I>G>U{S!d}*-C(smnE`s`*`k7;x0k} +804T;%rZ9|P6PLl`VRz0R5mLTD7o*rU*3XPLZ(My!2c&_Jq-BDrb!p{(MHeoIZz}Ls+-SD%xWm#M)Js +s(wCc-?DTB0Aq&p{R^VCe3dsP*?I_c?T=ehX21-7T9MpLnj5F4jF!skRDPpUNu&7_}#C$=Bq0A0Wtvq<0dpK{9spo(d +Ga;(1c%nri^>tz+G>$-&APZ-+T}sj4om5zzq?5pkGd)j3+2mb%bH=A6e3 +guf%nT;W?eU-3VebM4DluFn@zm;t!~fkpE-UP5%Q|`U})e$e&U-9VOZLsM%)NF~342$7~7&9YjoU^iz +@?*pVbZBP{t(MD%@7`;h@g(ofx#I@(4Lw(#tzo08d)N!)=fiX0O=JJd|_`kca<}a~wXHF?S?NQX +f_e>X@U=K6p;$FQ-RSDgIHTp+0m?NqP(bVfaz1jXw&)@P}0s!am8@5617e&ic=iPw2UA%zz(ZVE6@du!otlOE5vuA&r|Ttb`eUQuvTWMeL#_ar1^ +HW@@;@I7_{og0(e_mkX^SwQN~h&yo`+SZiJPx}F!&yIm!QL*o#bWth6>`kE;C`&2xyh)2Q8pu +BZy1#NyugH`7v3XF9nP?6N8C=OuqC#Mcs4)ZZ?B3Qo92IWvFz24N?`mXs@&V2h>fPc`XBT%+o^vR +Ug9%bdd}!)dv;`w~a!`4tx%^RbvCuQ-X_b)AOqLxqYQ;yYIn!(L@l)>`=Zmm^1KtFNF*STf4DDw1s6x +K)j4uL6}`)6&pIm)6k&N^SAzBgR(9AGF|ZMQRbuYmO^g^Wd3m^n4hQR#^CYbXHyZU{w!o=Bjxq^p;5GGL*4VDkvCKPJ+G&ZeWjZIjv%&vJ*#a2x9bk#9DT4F17Pj!$` +itC{yX9u%=Gx#XJzTfC;hB~dK~|DLgJ54{W>D?z0-a(EmqLEX +SBM_f!8rTW(y-(8(Pv~dMvuMW=;7k%&#VMR{H(mTyF2PbRe^rmn2s{uhokWD)zrbKm}7?|9%#4=jKJKnB*(BlHdwZjB0o93#UvI{@hsfMcMP4@rYxm-)f`n&h4xF_zT{{E1Z`fuM +hH4y^a2@^?<&AIYo5_pHqi95d(^KhQE~|r}kjXsPjkG$7e;xU|OxUlYF7$L|B6|qlxxC;Mkx69!Hq21 +~RbXlf>nAhFM3LcB`#;c|3X?GKy?{^ZS0AuV_3Y;9BADRJuH$74u9G6$I>1D0b%va(C@XBab`E$R6im +Ui99o`zXRo2-oM^THV6Cq)CwxXrTxsU7Aw81&?*ei~4k{8|E6 +m{If7+p-enr^Q3p#CQ=eNK^GwDk=?upQsYn$?u`nmOFM99N7LDSRd?0e&{${}+)I@OlVfSV&-o4Q3EBol2!vY^DxY$c-vyx`H@WMw=MB%*02LV_V?yaPNGlv=hvGX!& +n~+jlF*%XObDy+Wpq?xxaiy2N-d_m>R!-_P^t|#NWizz&Er8ZMr#e!<(|L%x +5I8-4OTcgR`8V!sS0?9`4++3n|#AeKY>L}@M`hg-m=c(Oblc6(gn(n+xA_xui_TXYn~uHWo&x@d%|l2 +;Cf+5t+TBs=><- ++(RpxPk~FQ;jCoV;!w4181pMqMAt;~D(s)|nJuisZjR*%cZ9yb~s9@&1APPQ?m`aO6a_nj`vpBiUUaE +BNc$xsMsMjy?lAt2mvZq(B8d;-cf +kJ=5Axiv$1l6%-kr)fZs~8l>=zgH-Ix4!qK|~IqkDuv2!fyy93s&}(i1{S0wQo6BM=NCDH4GobT<{>t +wcm0=}tWU3C)iN4)jBXkbjZNgg%i|bi_J`$mj0e;_v#eJtEp&M;0A?2Q+bv*&XrTrwFhgjH#pLm4c6h +1fNk7AeByms)?g1eFMHLkJknrM1Tgras?mn|mV0X|ZAjh9Qay*2);iG| +N_k9OX5q6X?_B|a=`A_|q5F9-dQ2C4haxnGy) +HY7X&zl!y-I{G5MvBZw(aQ%#R!TWm?Y>ykDL4@7~tCX0wvEeeJ$IbhMW}WI=V#I{X_1e0xyi@88fCDf ++J{7gT;>t@K9Uf}3@yZfZbugZsnnY8JMe&|i2NykDR6Rh2>J_`9`ff2-s6{%#nvdUb2epCMLRg_k3#= +ozi~sOAlJ@_;!@cM}WMlV-e&l+Z3$i8WI2jMv~~Vd+Q;lQO%5_rgwNK`f_x{}f3M;#yB;q3V_agFehI +)wG6gkIY?)_B$q@j3N$%nP8`4yGapB-wBh8{duD{0!%^;Pp-T9ed@ygbpZ&<&tEf<%rYWj*c~fwIbn? +1fU(zvwaQ?_4R{Py=yW!mjrMs3>h4{&#%Famf%c;SFk{A_jS=Pk?#u6u^A;e1)xg4jfS2?k>yNy$m&H +PFID@d57#sJPa2vc@qJTL){T^_O$(}+PPwtu$49M;~!dE{*&3xBeG}m~@Srdymf<`+@~MQ?vq=DS^zr(6sf2666Ajyo>Ph5Qx`#HK^Mie9B!gtr2fZnd4aJt63^15l~;TPlwO##$s)Tl^>A=1(t8Y(0k;mZ@ix((!O|6FWa%BjEuX7FaLN0w@&c4U(0XG? +;0!`^B-jPu8v66D*cb9}vuYglSH3*Y6d%;(c2)c{gM6EygK2zT(6m(ziY2mCTD|tEQhmQIK5V(3)!(Z=i_F_nhR*dmna}GP(40u#vW{FB`)11{yI>Hz!Q;mE`+M4%sRN?yG +_(+Rle7RO{Y#f=O))=Zq!MSz6UoYPA%`cg+l;}KQYk`9y3Fiwsl(A%((8^fNl$tvkS9JRPQk)tsg +YIC8HbLr~9(;v^0+L>3Fy38_97&oVQ6UpQZu)gT`ektVu(r#mNSI-9VARc?v+iX&L77Z@7Y#a#A_vwa +Icj4Kz_fog$Y;q7zj5DxBJNG@5Y;1rYgq3wYS0K6&O9!RPV-b0{^ThcL#E6e08#>C_lF^2JSzEU8bYT +e%2k)O(R@;It$+hFRarX5cdGfof={E3r(J56pj>glgYc4P1%qg23>%!Lfb_HlEZz#Rr(Ib|b3cMPfJ=nYPzThRkg7$Zn&*&~dE +ELGch!HnFKGavKN}&=r)ZR_`qX_o>g=-bQ~=rPy8DH9keH@N`!19nj +V0J;Vrs`+F?QfGnA-30Z=)(pNFYUcv1K^I)JS0JHpTrKU?e*#sNzeiP9&c(G&`7NsY<=}rARmp!F +RTV>dTN$fLJnVk5UT=H=FN2IqWjhH)SPD7td{Uj8c`tE^-HQ*KqoAX2|4sx~rQX9ss_5T0@j*`C|Zv#%&%u1XluMR_F95h)W&)J$$|kKhG=f%M +t}2`-vJ6mY_sTX8I_N)?$N@^7C)_1(CGYUvT&wGeE3kZt{DfeV8;aS{2m)8~iNP4df`mWZNJ +H>j=BY*Jw+`O#*B*Rm<-EU(bqe9c)sGjgy^@0179=TLTf#D7;6b1CyRy4ZV@zm6JlR7L;3W_#|^qj4&X+idBK^Z2*; +-=4jtuaHx_z#cLSXMbmD@r)b=RXt}sz8jNpJo|VGu#mRDRL2GS!>W+R449yDQt-F?p?s#&p?xy+Xh-J +T9)eMnH9R^VD+$I>9$72-E4*rJZ^Lvfu<#D>lRYAKe+5lu&Zq03nu%{ZPbx>Yz3|x;a6TDKwioJql5J8j{+kpmhd@M_HM0f#*+B?LAF@I`I6qnJ|c~@oFrb_L5lZbizo3x8zE +_b=n2FA?+<&8O?;m2)LcXPzAu07@YE`v;7-6tbugBe$Naj7CwH?a +`9MuOOzhPdg4oE0zTjn^-+$1(4T1XAn*KiuYQ02i8@f%m!{SoUPaWWcJpbU& +B&in=%a%1Nn>T7+3_U*j4fsRm4Ae)s6zniP>;$EhR*%e{ptrG!jGT#&E(XPO(T(`C-+E_<;UgW{6nby +q4PYrvd|AtCw-KCQtHT=5%e(vg%N)nRbAe2t~+?Z2_mHIrYhNBnKVo{Gl(G*AQ(|4BjWlQBOi|^|& +GT+KJe5CB?5uAg^c0$=2zm4XXYEgoj4z?mGazaD(beIN2~MsaZ3|t%O2F5(c +T(+wOLNwd<$^N>q3q6;Couil3GZ?-x;uoyd-Z;4yo$V)7mB- +SNFL>*-A#KSOMJaV9xR=gQ!#A)6cgc|)TocMdj;T^X!wZ&36~x`sM4S`k +JBidky|jP|qCQoLo2oJp_6f*!n!$$C=?^-_eMFFvOsH7DM%_=h1)#RRHG2%Q>|TeLuCd%)Z78RNZlFR +LV!;-TJSP&s&6SWKwSkr3)ldAvx8*H6_h^m$%H{WXYWNKp-=P2~;mnmPmBW3Xi +sEV@+#EW45k$O&wRJtRGFT#|PVabEr<$!YAf^tJkP>CbVAwRcws}6zp0bymvJ;6B<6 +f2UDOh+7iw)!yOWNw4J2hpR>s4i7c_S`_#ajzp<~elL5hHi?zGl5_Pku6O-U_&{G=ujzq(p?6WG|#3+ +A6!K8KP^!?Ue6?5BEy;wTa!Ef(3@L2pObHbbwacYQ?Y(jQOJLOHsZ(4#FXj+CAsbkzB-Li1ecZ=c`?@ +J!me!7T(a0gx|HrTCXaz15-+_>G$k8JF`LM}Da7SX#4&Aqs!Z{Lb#^iOm;UGZuVl2d5=#`uT~0Ai}sO +A6e0A{zx>2Zz{rGf+E^ +zX%d6trm>;XpdF(UVGbhlPCw?k$1dd_+`*DjBjO-TCt%$_uD2T(+{ +Bw#xsFAnG^p&5V|Z{d3$2LJFN>$tynTWLFsz`zE>BY4%VQMiU(9}N$kKnw+!xp*e&H?u`L}YH96guwJ +pZqBKbGvr{#Wv7zZEO_PZ#?tUGn3Fe$%;$qZE!}6ih%A2JMiGMkxYAArvK$9eiOlgkv!MJ@j<|Ch9<6 +2cAKXR1k$6MYv-Gh&<%Pvd;+k4uq)uCp!Hu`r5%7_TlE)fzOVN4vNv}$S7gZL9c`52PDg&5AK2vl295 +wn9cF@Cp7;7`q}~E4xf(174ex;M-L_*DmmJ2_E#PkppU`!{e*pQd301A@y}E`jD6(Nk)t$-(Vsu~F~} +5sgw+p~(fvm7KkamB4x+n`_yv8LXIx9JQ-Fuy9%7IuZomh~{vPE9$2Pz>(&?VN@t($NUwTUlO|!(Ni` +=92(Y)I?y!sUmy6iXz1wM*M$4R%(Nv{B*C(S-+xPHMw6Cd5u-aJNa&McdtI-Im#E%Ockb$s30es1M-w +E6mJ$92 +Is$gh*ppB^dcZ2kT;f{YP@gU%k5-JKlJE2IR$TwQT~DG5vuw{WZ*$Rk1CypKNr1RiJneS#->K$91zuA +0`xy&o&A?2p89{WNjr-ang}~2onW7VOVeMQ6t@y%YJT3JAf3Fzk!X)Y*HK!! +)~7CMnX!=`#_$fS|hWHdR3_Vd(DWgX$skmBsfHds&i)oEIWNTh)k;7h}LqEjnMjb@xM7*Er&5Ive;ib +@1czH0s;#m|HQ*#CqYKgo}d>B%yQVcsF +RH0Ty4&*Hp7fvu#cyrQE?VWW88CuwCd4@?QNS4?)(O>I{kjDS5UQ??u +j}lepN8EPi@a4xXCl`3`o3Yp1z?~uFWX;WX~B1p&1rTqn|7D1CR@Si#4jHgGZ15Epg2sx*zm^(kcGM# +s1tW{#J<^$MA!doTgw1qHr7~QJ6+3l)`9y$DKHa5afq#?sra+{PcV7UJ!*p6`wE3i1aA#pxI9}eRR+q +qqym3IQRSQGdX??6ht2}utSH0I_7$lkDuKSLeN2uvOBdj`6L9W!{P2u3WW}Glpi?7FBP0m(FjWpV(i@ +uLSJ4+90epe{<))bcdhYJyE!r``|3wciuka=;77$OhClNg@NtjWhtnH9(iVT_6wlV-6cgm%PLU8Iut4 ++2#ZxTxq$?^rk{qM5_xt{x-oL%O&1beRo(1?%9OB_wfd9lH9-aj_G6etBAs)L1{u76I>>BuI4pE!a57 +}Sx1im~~nPu#Z{+2>IoGK2c_>fN;<#IS_VOj;C{M`SoSfL +g(Bgx^G_gSggyKWJAvT{%59vfUg)njU7|30ZAjJ2bLh^*FEa+bzN|c>_ +crR#EE-b?>s%-YPWt(q4^($7yo3T-&xV$F7j&&iXk*gV!Ne;AdH~N-GY9i43fqn6oC)~-+%SpoN +k64mWYau!ZnT^P3<^xXdPk1r%Ih3dDA0j`)Q%au0 +yGg~PkFs(66Q2KRR8f7Ris-8WWwv3#pNaL1&ZH{Zi)3~L{@p<`w)k&F70fJswMOZ$tNOJJ^GASK#V|(%rV*5^fY +AkA<}S%Xr0WbV~i+8eEuadoU8{0x%_(~pr2_2%nxk<`=JfkA?It$bzNV`h-%kJIZt-ypRD0gobRPhTa +CNsFBw2i*B4bimD*Vd{5gtZ`!ovK)Gt}ZHKlTxBh0$}%sw4vQDc6aLE(>Mq;IQyuMs+ +H_q1EDh?W<}Lrr)R7DK9$BO@|v#NFPUF=r~SqNJrfWl1zU4?OBPd#`4&QoFhnr}xi{xVQ+1b4#Yq`wS +W+t_ypU7!B7IlBilGndA|RyGqdjq(rn{h)sd}N1RXGh&Q(-VqcsXS>vn#QZX$ijQl1xQl;-a{Yuu`$I +J1~G6#A_P~Z=Mv`BqwBbEDZk4dhkhet<1k$hVv|Ncdcg`#}-{2(rzPI^LqKJR8&>#E0-$W0eq9u +myeh)&S-47x(OcHw>fgU1+6owKQjD0tFIOuWGBiV(27#s8WBZre6gIYTlhtk7W(ZrEm!}oyT`vcSDXC +`@fK@@#BnLX&(eIJp2dcufFga0}``r_&A2flB2cF|{kZFjr*XIlDbq^FLCu>JoB! +BBSW8AU!4iRe*0JvzpsqvpA982V@f9nlW`(;gimpGkl$Kbp+;mD4}%5<45m>{xR9>ibyFyS)u1>p2&m +3Dbb1Nl2>S1rNo4D|q%qD>9e2LBesZ#9{jtgl`EE-YHj5~&zz|!}HgdR=q>W~4p +tp4Z+vx;67+|m!oen7ZmfZYJ__e1&nLr|9zwR +^GNH@b_)9stmZ#j>33%Hn^k^eH!&Q>AqdAQnj}#YB1w`)ce{D0lR+>+z!-vlKj}h!=6sSv>Ga_6&yRF +{j2%h-W9sM&u><;P+Q5Iro|dMEy**^j(3iXv3P!e026qo=8VTXlZ<_-yqI%LmYfH +yHj@TxJ#DWgbH>sE&qC2|&cG&NMhau}zP^=>LPniu^U?2Hvdd*#Em{xGa!)9)0C#?&4w1}=7r)kSN-+ +Xe9GGnMELm3i>G;!ntuQW{dND8+7N5~dng)rVZja!~XeLmlm8%oxi}ewi4rGv5g!u{I&Zcwmk=feRma +rIqE*#yahGOJW55~f&CN$TMH0XQ+&UzZzk#okM?LDNCS|QiM*;<(Q?moR*iS?jsniBd_ih{I)L*G@90 +&D%q?`7(GE&;xn+O~64v>r2j#;f5KyCkmK*B+x$hN~_6rc8a$i}nQ0iVZ=nFcF&(E-8Is8BPOoYxQRA +gvznH@^MDyr;Arf%xK=u&+(=Omf}WftAO<5JcD3S_myFlW?sDGyfq0k0A6Z{G8u0)%v?yZ;;Q=<$r|r4lMr5R(SNBO-{F|krE8ZDkMzkuHa1ixvK&Jh>k>oNJh> +!H~1@jk$k7q(G85ylY@osn{jSd}$pTb^k<4(zh-82ZN8ZXS=yN?K9doUk>=c!YIAS}j;b6}jf!2{el7 +*uVn37ub($HDGF&$=%$h5?vEk)O-jiIp;o{Seam9?oNROm +v1dLVSX7_kiF15mYoPWNESm{4`84=EEcB{5XzME-ob)pjzWmJ@l<2!7-y1cL$4y^;p`6_b!klfRYru8 +<@nRlgG7Gu8RgDjad^kQ(GJqxnsvgd2zX%oKKY5fpjR>uci2O$48LfSx@al!S`;LNkfJL`^C>QiD6?a`SBtWt&e{Tq3%65CgVnDRUNx)r +fnYaNO%iu5*;q1%nthRWC>(Y`uBfU;02hrzPtJU`v=fJ98`lQg6rwaq8 +ph@4dS4z&sew-(l410f0-vqBw;e<`9h*9iRW}XX{NO^oVEpS)4n>1W0Zl;~oZl6S +J +`(3Jc{&0o?#W#vi79=m?#)MzOzq`ZN=^EaIgW~J%+=LyK70p3tl2l*BpMcK`5A`QR7{iucLynIQ=8B& +|lx0G+?m(9DX`cO6@9!_nUBD4!%C&AdF1;HD)^;KOZvINcZ~yxsc4brFdc>&Gr&9eh%wrBnhXV(m`v& +J>2XWXhykUQKi@=}VR(Jx2MypFhD2VT_yIh5btPtswUTQrbPbNxq; +zpq4`3%sk1%ffj_a$xQcypTarQ1VRnq#L`|CxSYGc&5KjdilT=xkhSz2!_G~PT9b|#T6+} ++O-OYx$ +BPL$7CZGZOm!eACM9Le9X`I6*HJ?e)`(cp2v9`tBYkBxhw>^6O$ONc=9!+Zn6Q#cg2Cv}1-Y$=(JxLG +&|>3c$YXrjP((&Q`RU61yIH#Zjj56DQPoRBg**n8Fh- +XCjG55`6y10O(QPNoSBx$ +1{4+~w{alVNS!tzxwgH8X*zyOgS+@Vtx;b-fn->Gdr~f4JesEqtC^WUNe-N;3X*IWaC6gVXMs2Vn_>8 +`P!Bk;-ix3?rb3DS%~OR;Ok`CCq+YMu^dkn@f0u)S-_(P@0}Ma7kQo-(jrfVctrxPcj?24@qHCS@Zh% +>EjK}$QW!@bW+gH}zTGwtzKier$ +Ud7|d&$rQfhQRq%I2UE#pjij0xinAdeUZr|n#)p{m-h)ePbEoM;VLt%BgGmt59>`b-=8ig}*J4HD)optxW +gcNt!^p}C5Ywy4RFtq+LtaFO&U!QX=*LMD;XVd%`>TmGjf4bH$F!3*~^V^9p4Bz1)Nnj)eLpViJBu>K +=3Gc@V0w<~c#~PKafj6AxL6Z!zKNPY}yMbXg@kbTIA(2u$m^`W`lPoO@yl)#RP3yJ+iCr +5dP`W%Vj;|P%+a56!U-hd?D0qvjSMD%D0pnla7ki(N^tkv3x50PAIu5!7{k8oo7&4`!hX$!wG)-NFHT +;ol;vTEonTjr|U2q%A-v4%GeqT&V-$^?zmx>v` +;zY&s9LrOFVQ2kYSnmgs6ZoV1{4017_!%%VeCzu9o#c?Y+#q%FIX|8 +@J9iuvHt&d3K1)_V+i-nOw`&=mG$ZOpdOWcJYDmtQhNQmUEfvb6YZ6h{qJ?}SqVo``0!T&Dx7uIVmjz +yxH*}qEvcf3F>+(poIshg`a)Z{1b3zlT;2uOhQyP~I-!zSxX^w*1fP+EO5OYd}${fjF +s&K2t5?MJ@KkXQ5TNI8q#AC4wBoVcW(c%5`?zR=w;0ugLt!H084NhO{dcpbP)7%i4nllQ5j!k!ijLL9 +MYs6Y*gk@HD6_D=rA%81J=4GtW$?hyXkitw?TKy%mgwvZPXorq+icdXI-Nt9V+qcpra#k`l-;Kq*iZ} +l__3Vf*9?+F%1%HL!3{WVuyorO-aId58U^8-Ko635D@HCs4kI-g9BW}dFa(@NKQSe5}UOZ%RB2T#6LT}Z(U&9 +1%i^2=O$Z8Dg~f;)m`2?yQ*g58|iaU#I%l2hU07ps@?fOMxT_|=rJDa<{aeEM4O*gu-hHNIa}qFWs$N +!X3j-M2rHD|qI7%HCm90Z +_NNP|fg#&bF;Z(@hhw|J$Zmom*v2`NnsYTt+k@2!1vv@2Im>puwA1zVm<#7%mhx_@MIp6^Tvi#Q?C1G +}zMt3T;@J;T7GWQqPh9%z~(CrgE +cXkg6;zq5|P&oBW;L)H5kyrFA_C)d*0AC7eoiBUbPVO^pzkKHDJRBkfe9#Aak5DVmhYg!WcA{z}Za0#*;LtRe)Ri1Uzjtf0_H2E6k$F@uL~Z<^p>5s)AM3n@$wuU5xx +`V?m6$OjcM>lWm6nljd`uY%Lb>{8hi1s)ydxbRN3btt_BIy~PvGNgR{sbWkHHce$zmo4GgJb`x#31@C +!^ysR3kvqj(hcl1IC5TfUfz7dTiI>FNyu)LhM)6Q=1Q>Q#UyksOkKCz^T84)vPDB{%7W7hBscTz(Vg= +^>r1`Ke)W%fy_y=ue3> +!NyxCQ;ZAiQ%kqhP1R!}}DhVCVMlv>BY(>cA`BOu>!Fxvd& +)Bza=J4)_bih^8CE9tXFz&~OKV_zpDFZL8_tHV45QtrXoSb$#eqBKx!GPEifx4P>rot*^m@t*{)!o04 +;|Wi|hjNKPlTy#s@ZzeE8MH>#(2D +H68Qof~M&B7{^UD)5LF;h5T-blF@ZSG;k4X|CGwQoYU2J}jwuJ{F}} +ChixSKLdXF=@06@j-z@lLJH|m4@Mm%koh4*WR5{J8w^k)70b>!zej8uoGCVb$ecO$>9 +g@B`{746rQ2Q_qEadxkAdmu`vuCQj+&J|w84zqDh~N$0n=+&c?o6`gy?wQwqhvuuE<7hOXKxT*d?c^V +$t1$9;{$IxEmTRw}Kmp`?8czgQ2Iy`Xhv%gPu{NNNCMb@ka_1phg-~Mm2pP#ZS|LeCK`h&s#*N +c2w>OXnaclH@YZ~{U}m_jKSrf?J`K@y`t7>0HoJc=Mlia?2k#rilSl<8CcWO4=golvN-+@DD0Q9)o&%d!CO10&{=TRthQn|BEJubru+5EpmI|tUjO74K2E +@wxhH1e#KSjFa@&*NJ@+wka9-`_#%$besv@*5z?sTUbJR?FQA~YOp?2Te-`yGTtF8UrodJq)-}(f;r7 +wQos5RV(I6b5JLGdvVrMk0@rkIsP5J<1i2iU;Z8PIjv4<6~*lxY9>5nRIpz8$~;kQ +I6e;`?Ig!SG=@_2v<&l$|~#c0{E+Z_IIP!zMa)37*m2n0x)Q2Iz4uBI1UGeypt_z+6vu@Ec+A<1*LXd +2xhdxTdbsW(fuiAJRV+Yf~()G=mPg^YuPi%5!(8NgbDd6zTr{6B*P?SXS@*3ixb*iP?Go5=wURZGunO +DNiF!N7Vyevo)eYl7)s7Ed;Fh@hMCTjA~|g_Ukp#1z}{C$x1v#rvq%0Nb*cDZa5Lw +|YCc_pegxUjGtUp0-qEV0<-Oo=}}p)*A-M#n>j1qqLG?ar%*5`>)Iv^#5P8^@pwfeY5ow*7dif>l9?-n>-RxqZYdP1~Erubk?bwrV>hcMB#rQHUZREs +ea2s{sLR0dd0J%2antN^Jm~rB`r(o1U+Ss*e`f<**zWgOm^NcYLpF;ysM>_+-d7zwvF2)I<8af15A4FA*&eFPtn5>4$uZP<^&>r^?G3p=VJGD24D~to7uoMLJ!LUeLfe$)61kOie +;#TmZx?Q)(@rA#=b<%!a^Ausm83_vf6ogTa5<1r;YHAH*TT)_tdG}|0(@g+uMzqRp;qj*8ZMaH*N(TB{Sv03yYMgtZH*{sv=xU|q=xT=oYit +|ZI?$YrNT~Sz~`0NDI+M(Sg1)t9kldIOWf~FTEdV$r@ +mOGunvFCfh=8x##McFWU1K6K+34Q5e>rgJRsL^B49p$1-Lt3OTTvocqpgsqOXWx$_+C?mK99v%TMq<> +$1cZL-lek69Q-1Fs&1NR!Ly_L^#_}?N)nG#xvFQ-HzN3)Xo{b(G=!`r~B&e^vin>*xB`0gQ8lpg~PD-y$hCkCA<9isY +mtRXSrACHXZL8Unz!tfSN=i%@Ib5d`wjgY?mN2aSM;)b=QE9Yh?)$iA%b791JQR16N@dxlq)g*AOTI4 +WBo`oaP_AxUBu}RQ-DrCa*OFW3j3xrg_E#j_2$FQ`;`UcBi +0z5T4BWWGo(J(j7%Xg(sEEdn)HQw5Xg=3OsRLoq^B#c^WpH?~zu+p7iGkx6KJ>;(On27-!yM#Vu3Mv)@-Da@Mmy}9{rPkin;e>$Gda_pix4z<{ISCSY1^uS??v$j5^mHn|F +FsL3%t8ZQ|ne=^j05pDBcQX5-$#Mh1TLUsvGqrs#UY7x6@SrZ)@urrd`44Hj9oKa$SHhg#2@sIexJ9@UE}#Z*e^`$h+wr+#_LAjRj>(9`GbrZN +`w1LCjp5}J;1C#Bjr_i$p~A}XNq}65Zwvc93FZ6Y$lMZJ^0xUjx}Dq8c;w^$P!+e!=pt80FG15Vbb<# +)I$UoCkz|qu&8riAbtLr3{1`)ttbdh+p0`V^Lbh@NiawMi5U* +>PN6(`9=1*ST-=DbC})L(@BlrsuWQYR7HhJ=bT!{%O5fx)0uX6AgkV*Pr1z&XHErn6T^tTwE-~rxz5wMX2w?zKPrDD(%8j~W?Y219CrRWHUh>xe|)Z$ +kdOAL#9`6qKAr4`nE8C~4gb@Gae&z9VMJeKzR3x2>@|LDX|RQ2a4eTS(Km?B9S0uhu#aRh~tHPnPxn6 +-_NBq*FjC=~u_FI$LgoM&Rs8?D$V*%M`$rb<;`v?mR-2Sd{@_GSc +k89V9ttYO~Yu&^~q6W7FU4m_8m>~J5n#MYt32a8Me=Mt~%W(mxXg;~4p8y=;cU(4U0gv#~;yu)58 +$tAdKLy7JF3?Twu`6b5{Zebh4@ruxvaXAiEvX`0<9UVW)F=vz?%R?HP#mD2K2XwWC%yMrYo|I-}-KW^ +#o?C77gqBR!NBv$Ry<%2YcH7H~-Nh#i(=t;%B*YM_NtT7Fn#tP9eai!>E{=ekwOTjT|%ABhS((rcXc) +N7G+BC-cyfE9W@t69Nb$U1J}S&<8feOt5`L1CjE>8hL_DpGZ_R43glQ=gVEj4st54#O4XARx3`{urU) +T$oYJDxqtxzn2Q(X~TsT6=Yrim_b%T^;#FUVShH_B6X(AQVjb2uH6d21dq%M?X;LlTF{lHplKN+n!#J +shyrm6Y6d+y=>w8HyFz@%S;pb@aM<{LMlkEeUVza{v2yN8woqa17%gaYr%AZM4E&OrulM1;)NAA%^U; +YoFwm>&g5@SE%d95$+0aWFu)8~Tm`E@6I%2wAS<^e7%?U;D+ac%bSLzkSMb-UVVl2|xEWyKlXbO9dz( +G+p&zFJtwAhZ9vR4pQT;T#<)7Z$y)3e3bfY{Un`KmGn*P6soCP(ZZyTt2)GsHaDOg3>R6NnD(I6k>vn +fevj(b-#!JCe@0q;lvK^tI}lf~S(msXjZSrh1p*tE*IadWaZ;L-G-*h}o>iI^rJ;nzZamh~s%HK*p!y +Jq*GS(FShfD;iW(;<%iqXaT8;eZEo?zm=y8L)Wpva*Y~YxmhMmLbz<6ac$|iVK18* +x6q?$2f1+tj!^54sz>j-R-cFwH-Oxr>pv2b5YSa|ze!V{+9BWxkv}mCmF9mk6AX(|F=>*_yf!AKIS8B +p$DwVT3YrgdLE71`QSteF;VbbQJ6%kQf8DBvc4?J|2l9-gkn;fPdxZFmCke@+^Ke8%*g#Zm5wo?#!t}WX{)Wl~W>krSSZAKpj~14jCc`0zI#&{Iw>$VS0eTucH)cy)+uw8 +G2H#>GkmP(D=&qe(7FM!o`2&*DmkxkUMuYZWG{@%HNgjc^f +@2?RSL=h{v#Yq&Q5Co^lpCK%S*mGLg#>-9j6gRc&Gj80BaBB^N_Gzb4yzw-Ck<(gVzU7jj4a#gYy~$l +u1>Fmc=_W?7$vG%V0u;1&l3`mBG2Z8buLmJ;%bS +t-p1}(Cd0)|gh`o?NVH?*uMz;;Eh>Y6g5dIQj`CASj#D9vg7SJdE1YxQF5Mi~G{5h6k7QRPV*2ykHu_ +`4&tvuv5M#Z0@tO!yTW!_L0u)(bF4wj7iPj&_TY+HYKR~yp$mF)13NDG!8Ci&ot5$EFT3?PiV)7%~)=g4v-csyMKS9(Q*Ey?;@7Glp +C3{}LQuOAyPY2^76-p8yCNAE?4{VE$4O|~3L_rXG6U3ndEaT+gz*P&IiZre9 +*4uvRxUHYLOeAu?Dx>4UnqX<*JeH;tUdiuq2pk8W4a+;c+b0)C7tNwmJ4vzr>Md7( +bj|*(g`fXhaa020Ev-csJ^YpZWDzG#zNSWWJ!KLZwve-&Q;ub=Oqt!rAW4*v0IbaG;a|GY)W$h#) +^XIGQz{XF3M^he!+fRS^h9PFd3zjovq5a7fgWySNk}W&IWq#KoMFVhgq5MkDH=2=FHb$Xty9h@@?Fau83 +Mc0Us1uCe!T$xAjI;*|d^UpKW_<5+f1dg7y(dG&q7OShip^xLVQb~u7z<%GR%THpSH6M~QsmyS6cW!szXp^8$ +4Nn1a^Zu>)pz)0oU`J*&nF^Czz)itH`pxc``ogo@FqnBHSZ|_weIk?a~co>J$@Xki@u~$AUv+iVPyT2 +ne>e7Q^L&>3o?SR_-&tA4K~==n)~AuJr;P$k`|Ya0BI#q>P{V_z1t?doo@^WM)xlAwUor_K}Vd53Q~} +^pAPc61bt7o2Rb=Ke;aA>P5;qFwWFM0&};wKheqp{*Zx17;L?9>%m^b%e%n-+l8-0;t4aD}m1IHnzxw +1=cgO!L`oDjJL6G|G`!oO7Z|RDwf{*2wB>rvalHb~|`}&?wjIM}~LNNGes1OaeGG +RpRQzr0D!5fS=00kyHB15+MU>nyf*vgF|{8Lm&?q|TnE>pV;Y$L&j*l>L39LIYJbst*4qO>sFR$U<-v +7)+Pi4ma4&Vt+7B`aLrGK1-MGPX}{1>WUdRt-oL= +fNVm2e}xM9>xK&FuT5sWQE7_&2hFH%Zz>zh@9DF^#-9~R+WMRu###0de?Y2E;;X@%(f7Jo{V8B~g4D` +4z6dcn7zlh&V6nk>^)VFf)yz+Qyp=~?Wg}}KpC>A8UdjeND;$kU{O=b5zFZ}{7)%`^QnrS>U?5U+;65 +s!WuOSQvJl3)WTS;}AIOk?E05cH_3h$Yt@RV>RZ(+W-Ah$^?(Gr6Xr_N%?1o7!`1{xl?mnptjjT{~22 +|4mKhl8oru4TRg!W_QA8**t?icV+ZrIQ67w}JR*w5}4@K0{o&+ZrSXE$t{*zs2bJAjXg9W0(-{29hjB +{;}FZqm#H37^WVu=U&P4A+9@oG#bn7_do5EuQYl43~_`23pz +yCQFkwz|9J7}Y>H1FI5;()0KJQ=%a7?_6h=J;Edk=t)fEF`5T++Si +q%!E6?)ny-n=+ePG8Q)ti{|SK7PZX|dm)QQ_ktLyTz>5rKWOLJ3aq%(;FpvMJ<>+mpv)XsZ2?NuQCaHc@%MKJ98Yksza5lSfAENS-VH1Y#t$Ae=)X2$0f;x;<9I%_M$cOBq(Z +#N>hp`cqOaBp8i!p*6#?w=xeB5C}eF#355Jh(NfuRsI^H&8)^Unayu@b+M1nL=BtDkZk!A{lHv(2YI6 +UW)hyIsxJBa^QxC_PiOobu*#ZrlN^OTfq<~HaJD>-AwpCfGZ|8TKxLd`bNTC_7d9pwB$Y|b$w-mZN&O +@f9YS&+F}nIUB3M)m88;ga7m2&o~p7`_I5D;*vvF8A5IteGq3x1oG$QZUiZ(PE1XVS$h7^*p4M)}mJJdQd?=kF6+WJ2qt@`4e4YZxZN@^|>=$At7u&(rA|tUE)D8vg +w?CYHL!a?^s#9;*?AM_2v#?y@HDi{H}ah=q~O?6N47TT*1?)DCa`A-h9vYPXB`W!$r2Y18fRT(i-x{V +m?N7h5L@4tuLx1ks89&ePjB*KU=Hh``4@gPTt*=kORkmc(Abkqy~xqx)_*j#9rR!6qA9s%po&>aPT)$ +KVuBc>NE?0h9#PjbaRebJhTJ?trs1K?9|Vs;C#J>g;SkSqGZ$bG}E1vwpTxdyiOE7|7yLN9$;p@X#)< +0tjPDQH#I+$25)qQfDaCvVkL26V)Y3%F;1Kt_if*}XW>D7N+oIm$<+y97DAEaFC3$P(SHE!9HUEIV(( +X6CU@ykIov~Io)#}uymdbR(0{Ct}+XlC++XK-rlJlDg{XUFk3#s>ci)%;`cyEMHU(o;;gl^feDCpKd4B^+x!U5-gXJH$_*Xjw{%VVVwL@Ur;@?-M<6PGnRh}bL#%^rJ0 +ui9oiLPb`O}HWW9qin!5Zt8P6FzBN(R+!r^eG@O-| +58N|A6&Q@kmh|=kJ#Nh{+!Pt@7DvvKG|AzKu7p8p|<|*3p(#?9rSNhPL*_dO +AcTr-&bHxd#F13NHeC;bzNz`kL9`sB@NhX%j>&>oT=l+>n*1QJ(bwp(i1%lTx?=w3aTrMoL5#ynJ1R` +hs(B>>QS^Swmq|{#o^oV&v`8h)dR;Crbq662?y_=7k-?AC*#fLVg7?E$=5;$K(nqm)Z;5B$0RFj=bAG710MKjx+aa$Ux$J@j;CT?u +w)XkWr2|UKv@M0j-6X*p$R1AvA5n=9WAJ55_PmRm-VS!;gDDJQ{!J%F6e#os_A+lz0-7nnI{Tm{n5G} +%;)+m14?b_t(u)5boexf5e(i9g&(Z|Wvyr3FDKm$k@=(xIak|S>|HcmZw}dNcNhX?R!9W!M%L2D<7mM +i{jge4sD1LN*Z0>0Ys8vjShy2;Y?Hy@g`PdAh2t%<>sxhxLeIzN3|z5Owq#&PE_Cn;$-=#j<~Un|nTU +qi9Q3r2?QtC3?i&H8I;;ImR#1I9%rB3BN(j>6w +ObrS@lA##5uQ2*#svl2H1iiS}@YCfmbG-$Gp*_p +8$(_U3(DQ!7E*Fca2`9p?$C$RQ>Et1Y(Fd?596?qiG`zw(%3|dRKDU^6HA=TJ?-x3bz|(T@IvnYSzYV +`M$uRlf9LY%JWd44B4)f90^!eNxvwyr;Xon{LfKq%Dx3BIGa>xH;i+;khzgY15F`)#3li2$EfN+c?Q3 +yq07{Vx+ASoOtuoch|82(cdO0Z9-2-hGU+9qLLkV#Tz&bcF>jliiCpisw5z}6)NyOB2S4eh>!NBR2oS&cd6UMzOUtQ5zPoqk$=Si`6v(UZa +buJ_C4%E<;R)3^F+4WkQ%r9WFceRDl-^Hv7jY6=SL+o!;Wus$9)Ne`dnTZQ#LnHgs%%4AESSOZ)g +AL#qV8M>fVLLztv5NWI2C~|1m$0tr%|+0!0Mh!1rBPs-)b$1ay0t9lM6DRt9WiiXlxB{TW_Y*Y&t{GZ ++yF5qt2(&p>v2OwAE&-@l2+*K1OrCwyJ-a}*HpJBc`EzjSCZzwFTZVGJJ3BnWWg+q>KcZWq0Z)7Vp*c +JQcB!fGCE21bYzN~$WUU*48u&l00q8zcN}_*C>avyih(}~7! +W_13w`-Q!1y3vcz>k10zV^PsQOV-Yc)Zi*Y05UwGd%9YtU|W32x2DXL<4%h89`%Z85+Ht%LscY&@P(4 +rnsho~Wk}MCdMby?8kxFR~iGvK)x)!c7Ad5oGd!9g_fU5IFJ+pOGJ2d@4#5tnzE4DW-2`2lvGHxu;o3 +mO@YYbeSX!$c)v^za399xr2PB=~6xA^YM|)R|MsGk7%2mz29Qc)?bMw#;V_o&GCr%)5U39^rZp*XkB# +@Ze%DXJkWnW3(SD8W{)~>FZ&E?Twb|^Kg%MSp(hs7JwFsDKS`4DJm{xW3Itc7G9zpSqLk?ebwyr8mpR +e)Ez>+mm5=tt$ea^ogw(@1*B1*`hqL045y@Uy5SAi9jUXks*_QZVJ!I$-mwX^^a?q}p`e@D%4`g)$dG +~bHpwP=ZoyrcT~Ln3-A5V;GWH +19U-=l)?5F=yE;TdY|a_k^Y-ihDRjFewrekJI%3Jz9|NM>G^aS({C2bv3;xnUMq53;dVV@c>)JkI=J2 ++`@?!S~4R)u~>k=<_Xwd)g)dkk??qvO-o^SEfH`(o0If4D`Igc@h`OI)1g=eh(0C{wQqvC+?V2==qb4 +gGJ-9X=42=&?b%Ry?OTmIQ97{df9Yj+-B#=h=8&b&{6y6B6te9XW5JotG`5q_M1$0*;pEtzAe3lP=9b +Ke19;cHB@d0TvAW+t^gtUeD}w`*SR{Gi;jNyt8v9+c_a1<|mj46yFh)!rMV|6c9+OX(RIIZS`@qYCI3 +SM!p@nO{Z@XH(H#pHPB$bc{}y1%cbVn`$f3v#Vz*P*s-LQGbeOBme`nCWhVwkgK6XxhivKnw*Yi0Jf- +lFJDm?o{np9!VLwKW|uoVTF+RV4&X#y7=OCP2s1?2P@4cGqmw6hI}Ol>L&s&Xo9?8{^r^#XTx +K6()8WAhi$e@xj83gUX1ZRUU2H!i$bw@!dl?4S*9PdF1lZ{tc$R2&DvE^5eRU0%I05s_nV9b<6>!Ctp +1BDvt781V_Waj#z_+9}uxGWut3B(3@T_F<;$D`ky;^>ybdPzA;mb7t1TX@hwddZwqBQmPoi_yIf$~f+ +y6x@=t$>GT{W`7wQE!fT?bHz_q9R0^vV=Zpf7+xr89tegHxxWZZHj3}_#V!^No(9u3#%tH5wFv2f!jv +bXH=qiPriV!A(fwGj@6n7H16FejtRkWjy|EU-kgrm!d(0ZhZF5}$|{?>o(u9Op{f?^(RIYeThAa5o;D +A2&0c}_KBKSbnBVE6`pRA>*BuZ`y;xrQvW0kb8#KPz+EAf~@tg;AC2=WHcBrCCW`32#8HhoQdw6-8*G +Ya&0?-vj_}Y($elCSJONKsr$)(X61eW$w$=+w`h|xJMdM`T-> +e5gDp!U2a&4>EyY``z#kz4q-`6R0;mZWcgGm|K9+N#Zpq}#pG7Seq=^>60x|2JOeYkL3J*Z3~mzrs?A ++JrYYJxhc{Kx*q_AaHc`o)`pU6i)rT7W^RYR?>ZXW>!YpMK*7I~PTPgj(!C-a2irp1gv4k&6W=A!Q+yktzD+ +XR9Wb(ozA3s-M1;54G7R0}(%xuvv0}&h7h_4A8d)b^JuzADtvbwC#BJq7s%0r>SaDQ50G(Tj9f +G@FSxGO!L>|PQ+YMom-)2wYUhN(H2cNL_0J;CD^Y1)O1I2u;CYymXWf}K2D?H&!PTit;3CXG`tb%m~q +B+@@b$GXXV69wmr57?`7U)?Ae&Wz$RtMQPE2hw|Ao +~2s6l=@M&-bOJ$n@N*zvQ%S}Fk+{;Y0fxutTWl2B458hI`(pbAA@=G+1G&wjPaNrUs{S&b%LI=l92sZ +{PvzOIrrLozUS0ij3Cta>Rhl7hMwJJLhr-LOE{)opc-f)1Qaz~nmy3H8MUu87ON!aI5@}BjK(B3?7>+u*CrrPyfN`R4ie +4gEDbwKngeLQO@tB?m~VA{_c0~A4}PfRyWHE%ExTvOLeBcCo62xLbE@k`*WzT2NI>B4+a*gDz#IvkjU +N+%mAF0lJxtiHK`qsVwe0zf4hc`7@cIkPi$%7NN`OU$QM{jQ)x5Y-pba31GtIdN7j5Y3LvcO#FP0ha6 +8*TA>sz3QH+2@N!O`LrnU_nW=g_DyIg<^j0`(Bq0LVS!rQ&zUl78>#r8 +v>40#2SGf}DhR-E18P#_*tJm+LLsux)UX$w+3!ukMjk4RV=UQ5&{XHM_zijfr*g)jxUDhlQc#&k3R53zS-Z<-^p$E41oV6 +a0?HJ_7AMe#hLhyal)bQ?-N!qvk6O(KyvGYVb +mLWj5=VmgSa6)dEtLY^Nqc#gM51+j5ODWsntG&Bug{i)Dr0brOIQw{ASZV!u(#^el0@veE)+4qk&53O +~e@4AWdp@4-9AWaNZ0Gl5;qr9!R9NRRMEQHrsCHqY1C=lb4wYLVBmz8?R4yiA`JN|vQ}w7&LIPT#4wgW=MeUAF5YG7*YBQNbc +9_CZ>P$x0?|aGnjWtJrlCh3J`FI{_Ib*wuLnGfbR|rfoJV;|-iRP$=jHuYF)pCct!bdQq%QCKWqrD+cWzcHUlASg@FI$%nj)M*Ds;w +|EWEE!q9(t{cj;E_%rmmCR0D;M6Djy|pwZJiSvdotbhRj| +E1Vr$Gzwp0!rY+5DoJ|_~}vqwn!ALK6!7b3e!s~@#b^vTe_=#hB*K>5Ex(i`BG?ZqE5;>>*oiK0D3i@ ++#v1>J8M~7wvv~yh*SDl5nmf*v_L`Z8tLEimDzfG~3Z;G=Ct_%lC@m@73!ZzDldBU +|xmdy9!qbx5CrX+d5;N_3@@d$1*DvQY+6rm%+xoYgBNBb>4p!Pygn&9fL==-`CR`Bg4E?TuVTgy3yml@JX9t4?!ORV#)XGiwpl~2ua%a^b8-2uPSx} +QKGfEtG;mB~iEbdS^@;ALtgELP;Blvlp1%g9NITm_=L+hX;tp_l@94J5pE0)jn*I199;8w*!GJrZXG@ +}lusW7L^Vn|IgM*x(-gDK25&QW5jv`)1`O5801OILZZFlETskD0U%XmoZGFKu2?W6`12u;q?|MWG0+2 +#TAZCad08k<9(HiZtNwl$6l3}R&{Ca#Q|BGxicogZY-AY$qG@gYw+eF=_w?LdeEP-N}F8fgoI9-*@}U +MqGAZ(TYslzsrWeHVg5xn87=3rLbOqGc_jM7B^ew;vV%075ySDWz6__s(I;);mKtC^i<1?V9^pg2AAq8u^r1vmH3YUX9(Uejvh +lHLwh;45X$o>S%0)Lk?^KE@9y4lUFl;_d4V<76+GsI_1BK+N^yI*o<=#xW)_U-b_8Shq_v)4zCT7eSa +_I5W*3DvWhNR}rw)gEl`D~=t{ImgHuTnh&d%Do7_&F@1ll>Pi53-9=DYNfLk2~bdRvUBZ#RYC-xJJ|$ +>$gUI?w~jF9a8J`Z+xTe;CF5j@E2@o;GJ3xX?fX~-$Fc1J>T*0&mK82#rpsj_CxHf{Q`O~OU>?!hIU@ +yA!fAPHZdTdf)gU>`6c2&)@i4~+*amzoy?2rG8sWCYRl`E>dh2z!3p#QhT2wEwVGd_eG!u~Il)_SPhW +@HvH!*lo@(Pm!pd`gk?CgewNe~<#i-#_w@M7Bs?>!G%XAmbDB#zlDZQsTu4ZMB5kJmx9pX$5r;wA@Ns +#r?l4myGHGU1sxy~{8TMsLP0YQ^O_mqp?U>_z^{+hYq0F4xO4DqtLOdo*CB0~LrMT;<2p*?z9^8YjZ# +nKE9{4SOECju6gCiPJ28sihRDp&hbXx*B_Z|DP>eYxI+bhhaY|~TahpS{0_Z; +t6F*r+vUp6NhNQgp&U-Gp!(U_k=gHH(SdE_2~>#>-|rO$#+99R^5o_*ZUk@~W)hLgjm71X-EBZI(LQ_ +siI>huAeU!j*bFP!|}0%bo{&;L)u+5hQ%{2NI7!yf(;){@XZJQCfM+>>zQL#NorxL%Ph7;n9^E1=!v@ +8At6Ch1RMEtPI9xCFFy`cnJEAZi~yLB#vaM`Bw(j5gr8Vq~1$@MainC>Q?~tVKWiZ?Jvp&nC~e5vs9Z +YwcZeID~CU*{$6;+6s-E+IzI&+mPA|p23E&acs-9hS2unP3k?`lD^w}A^(FQf7xI&ZLs$K{1(=J-%zt +cxaUtGE%(vx_wOO?hTwpI3gK=X!+s(-ZAEavzlF3nOXb!bj6jIoJ~FObkIYBY45V<;3T-#iPV`pxo*8 +{hwr-s{pU0*wX!lws@H?3&HWYoQer|ct6F>P(EUqB4QNG!ByKnp3T?7AQ+j~Zq{XY2$D3(6ypV`-?mw +e~lZFO94W*83^?!E(UkMpqJdSdLwcZAu4v&?X?WnCBq;ytB{En6RqK7X)-bdcY>a@0TRi`m;^&Tw&An+o8yKaxJ7+Q#?T)Y$LX^T|LM^AJ&iT{T`kO=Jr%s;8)tae@lAzq89**%Kn+=wdOM3xe-1duqeA +@_vatxa(wNJNj0IC(DwN*Zb9+@D#N73B8il8T!SD7e +#ZjJ1T(rob9UoLL&_3>>-(bJ@fZCit$D3uIU5IF6q2gUq +p4t~Ce__Wdd^7yXp7Vfr9o4N?hU|lXKLo^rt;z=Pot?qXx7;dm$gYvUi+e%A){CmGfQIXytxh^dA2;|*fIgdtaY)2Gc^b`MEpq&=UmpunX|j +iE1l&LSWuh^VJX^e(-JtLD4ysYK#PE{>!93`dVP=Q2d|a88yy-?jkSRE{?|K#v0B9=Ya4#Xm +ZPJb?kNn8z^?hWD)_(`qqxS3hnwIZr#KDJ@g$t%|btV&qfgX_B#Vvrwj^+)E$=b?F>yV+ZJ1K{#GJsa +)mJ$Q@eLI3&p(c|$Nw*#%OPV~ihXIKn*UeYg}+cp7RM{Fcb8UQ+FB8qk|O;>Q#|?s=9a}iMx2luztAL1zEpiT1Ryv`G2Wuktx +NQtr|>H?7HL?e(uz#V-f`bIQQU!ctNcBa$6?SJn&=TwAq#WF%4d@O0sF}pDklhgV4gwjNTE +}+Hj3*pfq_z6_tj(U%v)W3La~yNysq-yibDkNgH&7!mTbDtOj_Uh-2`OmX5|>q}~5&lmzNC$+g*zZDV +lW&;U~p(ebeDt~!z0x_}11zpZB^uDe2qXvqMV)sWi!~D2ty&+D+jvDwy8Zn8it{&}5UEZt}w<#T+&PL +EQ>e=wcC!1V?xesQb6IVfp{N~Pe5-R$B)KiJyS+wAudi|!o&>`*5M$?>5Nq}WG2Xl%c!)UfU92ZA93F +s}dLfnB^MyR%)0$x_@a_wsq;L3WBd~#K0doAkqtCo3z^n> +)P>W!sA*vM0YZ$tI+o~MQ+m(y*f{YgFv?V#q<$$`P@-Q=wPJ|WJjd)8-sqwv;-i?jcOgnrt!=MSp<5+52i?X3d;eIGOv~=;)0+KlZSB!D@v|BQ4Ogs2J+^*1g4vw0hw2a-R2*sW#nS*EH)|&nb9cL|Yw|6& +@_X6F6KMX?l+lIf2>|=0jC4x} +U}oA4es*^4_d}#-vT%Y`iE}NAC`UzR+B%Ko3h~K_UUNveV3_A)J&!0F9l`M9#sHNmOtmGlVg0(^bYU>;RVb*+k#*Dp% +mkF=#qK;1gJ6xYG5`U(^p`M2y=Q$4Egv*yenm-X +rHIhtwJGqZdG(2v9|>mOJDAprg7S3fQe{OhEDfPj>CNyS#pD*ObjtTxW7M~e*S6zi-?lb+*Oy +;_GOHC^=TqCZQ)#%#b-MqU-qE!awc#zbmGZX3d9a>TIE~g1iw98*fz9(85TFo{u=FnoQv#|uXh(FGw1;*i4~e<#Z0+@qn`) +W-SM5bn$KGe<6JXAl$$KV+AZeNF@}9rXoa8s6p~B0Y9omQq~zs}a{xcu^4(oW)ryOo#TI--r8swAP2#UA2lj7GG<5n(#;K;)oS+Yd{@4gN;M$uNyV-}kp2dw}Y(k@GT?NAZkc4_ldm3sy0#+x&|tn@GATpa{5fJF4d|*h*pyR6Sd>I=h$sjp+`idX;+#95)DzVe#y4aQF +;vBGH`noO&PhDS%QP&34A*RrnZjcp0wrD-^U%*?v-Q{tB&FoV}tawRpVGXQ1Z!vcl)Q*@0!2XxsHUYN +u4pJ7X!BfVz0ZGphI#&6qcWl-rze18}UAd+=HDSs?&MFRRP7;ARDn_I8Y)9yO8bEEo3B=3y?><*G=>6i$vPIdz##XmsrfMhD|v> ++3Lhgr_2N(Nogyyi#t-~jw*_*As!)^6*xrBk7_FkP3a#-@+Q=#>Be;uIHWr33aRBeri52S1LRhj#J9y +>=iYh{`!SD&_*o3v~Yu^v~zRMtOQo@-wx$75ow+&8?f$ql2Q|*nsidX +!wz40s58iy9sZWQw^8v+aejQ!mDnc3gK?N*zR5Qk8t8U4SrHRi`XMlX4FeJBYihZbMpes?jrSc;G1Uk +gn2c3VkS#<3uiM?f<3#TytBd1S2R+A}9pm7>-gXv74x$ipO +H;r?DmZ)sK%{P!b>QGDz}ilp%hlsG}x;emLnv>era~ImKx7A@8J*#Qp)c;!pDbL$d`XNAjPD5Bjd;BV +`zdN9O9F(~5tE|E$J9#YeMC{J8-vIk?2A5476h)DF}955H)L9+%ksFP87pyz}hc +ie7mEsH3lR79Yj00fpK1Y$UR1~kV<`h8a(_+fAQ1T>Q7|Re^E*lz7Gxt_P8FP8}j5K +DWo*H{>ICKv~>l^mnput^DMSk;CfYu2~qC$|^5_mLg?EcJWbd2$+NF&!HUZ+Mv+StC@lgVRAO^<9qL| +PYCn}sWSmo&5xrhT-7c_ce72yK)v6p!rI^J5Ruxvr%vt4U=F=wtTc3&<7 +xBBcouD|K6$cul{j(2a>5In6%kxQ)Ar?ARAq!U}JA-`~bwdYHE%iF*umaF1A39ZiB3g@ExBS|d3>MID +Qh7?j5{9v@^C%`H(ivYxiFQv0$Da1j-X*=vpuQRvP^7!5kb9|1p}&;u`xyiPjxpTtFo0yZn`A+xGOML +*OdMZ@3n>|ow2^)mF965X8DnrRRYbnwahWjeUh+B16+PV=j^%*XBJ1 +eC=SJIwP^wwF97{?dFK3N@NQ}6tt(}d#i@9VRa1NTZ&az5hNS +QzoEv6*mCM&6vtpq=mVidpl>+Ef!Di>#d^#LH%- +Zj2~)Vu3jauXY9c&M}jqG3v$}1gyiKkJ}#i-i`NL!2i1r1^Jr}1^F8e1^J0Xp%99~X#&M@n1U%3+uaL +9;1rGE5VX6S{dW{We;TnLZUR55&0%~5*4Rhs_Rs@?j_L&-{z`_&cn$J#I>gT%3ho~Pb(|bk=7{{-j){ +K?6y)b6DmZw?jtT~T=!=BOkp#ek-7Wl?8hP*$S3y2S>+oH?VuXJ* +215Vz_N6q +ip=$vH!F~DgH^#0(sUe65e0S6-b@Ztm5^$B4cWqI`w!$!bxGIB7(e&(2H_9!-&ehC20>w6(Pus+xbb4 +S3)hYml$}!6FLY +UGt>%vY(Wg~2^@T)UJu1?@Mz6g6x==UrN^qD*_W)u!kLkoF83<9zxe}1#6qBNC6KJ)Yw0D#!93iSCg} +x{_8mJp6riov8rkO|C6RcujJ8`+~z?AH^Z-spD8)i(`6Qo%tJAg11yx9?4e2lD^%kp|tiha8KLti?C| +9+pHzyv6rgto@7F@bDHp~_ICMpVjcl{S3T-BTR+&`2MNY>_&docufFZH92W--EF`KM1b?Dr+r4n0!l5 +laP3N@FdG#x1@D<|9Z`Wkhx@ +!eAjm?wmFE9rL>f>o1Ka8{bSO!YtXRX2@VMWbgZ?3@PyQ%#vMy7JSl2wMkyJhJ*rLh9?yeJkFGY8S*O +4lx%=$cm(GRkM`k@k34kVeN-F9E@|l`ocn&FECh`{j5kyW-xQ3JE0$^H0jjRq%bX6nQMm%rdDq5lHT`PJgKXb0vWNLo` +L#nD;c@IorWr?G#T@r;NUU%SyYkdo*?;WgNTt1&SMm7b0;)|#3xuidi8sUR$fHGXdIJSjGaxGipsd(P +;3%1TBKu(LQ)-hBseS3$YbcuRel)~rox;3yeRMpF#o$jS*@GUp%p+3c|Ks6P*U9QNwrWFv3Rk=QT$uP +B)hB=k@i8U}ATPPz8Z>0`-K8>`X2c`q3OVQM2;a=bc$x}gZtCLs)Q*Tnd!s=^qNg|%Y=u+&eQ!_M~Xm +xj}7GBS#CuKb}Q093Fp1u(%7IlbCj_hXfAdpz|EfHlrE^EzsHF_^kZ6#`WqCB8YsW5h4*BzW-3j#`w$ +JO+;$KOau&}?J78G!;diP6KodV9p>PL-)4*y>qjCroH+q*f4z!H9xO)F@xYWzlO9ce+Ye$>puG#T2p8TDSHBv{eq*{ialv6WnY>8EStUO@3JzhpIZdx$)lI3@ygVVA9@UhVe@<`Hu +si8Pn3>M@QK!_%#sl<2gW(+ov?BAI6qOmt#5?=?#~3p^wb0J{ycE6o4xz#9FVI0*e4I0*e24%+|G5Dk +$8fzTMagCBGcr}y9*qW2)1+F!?Th(qiR-lN-}&!j{L%! +#4{vY_Et8MysFoI1ut2<&+4ml+o%K2|+uRnVg(Li`z5$Y=iL7=BJZoLC9`(c<{fUdQMGq5|sZ;D|qQ{ +~kJ{@GJ(&x!X;yjMh+j=j4 +Z0ck1pDDWiZ47ikAmDcxX#1xa=nwqd{}clq5D55SppD&6Qon0Z`_{0vhjJnVNes(XPD^=gLNQtermO% +50##mjtCJ(^Wh(B;@vPUKd22&k&zjwCBUx_R5pD8mG;B%f%|-8xL;?I0N{gR(r5+ +f^x5Ks=+MhXr#rb)`w~G+)*!*K3W~#}|nH8A)vWJn+d$Yu7k-U0+7+3y=Lx`(#tlSe>xbk2T8!J-cv@qP~WY2OWhUiM6ggEN7`jPXuiu@im +##6EH09(RSGp}FLvynQr~A;&b^VHZDi|NBPB4~fHH84v0(9!~zf@t_3qa+zBLwD-@~Fsy9Po_{nR+ke +=2K>yzv5AFOH-L2ZS&x>dWL8^8dglh~R8OR_mY=TLym*&;;}LkK!u$E_UGu +|$HQJIfb$>KqbzK%+d4I-onfUBbRe$U0P9oUi?ThwYA0aFrQ5IK2A>nzOIUojPj*aw?0Z<@BBfHMvK7 +LCv{=|fi-$qtn^^6zg+!l`sJ?va=$CTT$|TnMGiWbmp|mc{Eyu(^;7jJ3_Pa(m9OzHY+mNo5SPEa<6j +=}>tKZ`^4Bj+?+;_%SN<>8<1N$v-fvgXt^f7qBEPM??xstP`&Yaj*Z6t|KUl&4Ki=Or>-&%H?K{)_Gj +kh-2iyA&ruJWsQQPE0L6dw~PlFFhO!N_4gFl?|Kj(GZk?S^VO%LFNesCa_9D^G%c +Hpahu^rgO@vq^Z<*cv|v3`giQ0xm4e)O53z&^xrJKoyOJLZ3=XrhnY?kBHB1xIdehjg)j)I<>axXeLK +M;xug`#J|~B#+X|UzppAdvuvV)4v8S{=ii8Wqug?Q8J8|7T{krY~kY!MWtJ}<)MvnN9e+R+Jpb#4=eG +_{NlD~uRc=Z-ks(A=h6a!qm=>2BKrZ$dyZ3pEN{Omd+EZju5%0-$fJ8_mB^9dz_AXY63ez^*}m|KdWU +A<`c1K8H5K;>!$%wNS5EqVUjRRh`r~a?%ENWrR`dNathkZ22K&Qt +4~MnAZ;v^cem+|U_B}7kgXzC<2+7|b(lOQ{l$HM+_XR%Gw3@eH)pOeR%b)qna{?ZR{_)xP=F~=;x4FA +*viFt!_E*vNe~$-uY-jj>VdRwp`#v9^zwuEB`v=6YJ!;J=(UE{p>z=|&Xm;?7;xPjuWPQR%qkIrXX?q +;Jl$6}uwb#30#Yc0)Dgzs_l_I19&(|IBD%}m%K$9u5w2WO17Pt2)xEB0U2H2=0SM#d(!DXYw=p2$rPH +*77+up2A6A}bVS`TRG9L{e7X28YS`V|^OXJi=qu{#!xzEEm)EAP}|pD;))D>C8$t +{)l`|1^>r8|5v7P%xp97k0-W$d*z?!t^tBCXg#km6oN>v-qiu0o-dkC6*BG&<8p7xV0yq2$7qTt#0-`IjI0S*OtxUW-1zT@YrmV9^&TfNZvx?_L6pt +UFnI1qD_umurqX{T1M@SY$S)+XTyw&b%UX0~R`HAC40JD6jSS`2_A*4Vjl!E<-I+}69yn9s7Zpjad6E +H)7=w2;g*!<|a3vj9htda4Rx}vxrpQv9;!e||+Rh+U@!kS*yncO3sBvG5WDaf#Al+^_2C6%HU&B(%^a +rK&Jei>3l#gDa!?LaS*ez2#52dLTA^N`dy20x~Pbd3s!hmv{&qCjxm)6HkN5Tdo&dLHYW0YhhTT?R-T +{-w}Ti(rqC@o&;Ig2iaUFK2!K@w+8 +{InL5e>EZsu>HqWpQ6A?XSzrGn3w}cfKVR(kFo1@jJv^fc93n9YMj-g-3O9Q+6ov<%$RSaR9X+J@XI2 +tNk2vONPNk061*3k2lcNmuCyeOFYz}pFE>NGLnImLE51fF0(l69!a5AQkD-gu7-hRzPTlLQbScBxq^z +2|_kH8MO9rQCl86D9f=I!wcQvOODc*t>TBLjL+SW`h9raXA7sSbeCAW`7OZ9@4fS-`(& +F*6rrITmJEmfq#0-Ki)C$w{KaJxS#pEFHE87$Ki1whnHR$#8;n`nn95m2jr$|`s{&rvcj%JBUvVMiS1 +RlCg(^;E`W1ODCol&n36Z=rKX?u{jw%}KS1leTP4AH6hhO5)5VU7G<)W?M2~}9Ub(l@=MGx{kGkIR!b +CSEvQV!JcO{Tk?`}hE^uzAtic<_Sw<`oyOV{U|N7~($%d%6ixz#y=!3nrp7(G_x&=2A)C +mM6IM0k>56DHK&V+^6I4kx=c_HyXyRsB8it&x!lc< +hauD4ia#s28`wCgGaRX{wgOa3Om3$)&rVb@)|hV=0qO}ViES@e0;4)IA9iHNca20qKN~9RCLwa_8;ir +Ro&Y0&Hwh+!VB#zYjM7V!f-Q4PU&x!Ho47zA%XyrsYQ2|pt(hp~&NrQ`)6+92FmWuK8?dE=Y7$9#>vK +8@<3RJ-tlU~QG86TRd+4>5GI6ZzC}(S5rL=c+dWv(|Rgz!McWF9+FZIH6>5VJ +U1Mr(e^bi-tm2y8)p9~vsO6=>0l(@GSi{wKvZCt4z43*5todGQ}&%;7dmS)jBaN(=UrZk`3SALw0?2G +?;Bgb%L3?%y#L{}ZOZ+|*>2~8E|+^H`B`cH9y9=dnZW!>W6MEvv%hTK?IZLNtRr-CR@ZXvMb`By;A}=T&sqVjziAQg5vPsXacRny>9*H{PPA@SNIq +_EQ_SXB=IE{s2`@z??BU^HCmi7P>K;ibY|__Wrvow;JYB0%obZ&E)kymOyiq|iSfQuY3KU((9l?ruyM +-5be#8B`e-5e=bD*f`RDURf!>e%eO~$wcu*}Ez91!(*6 +OZIRbxI5V!lgd-lr~|9=^}CvelLA`i}C-75O@~ZS)e5H(ShZdc1q4h=3jH)yH@0l%Q(R+?w+>M~&w!q +5(u@ZTN=%iGJ<}1946Ng?1wDk5u*l_~dH7y>MTLF|H0u;a{To7xRBAA7bI(Uj7%gaQvq<9RJ5I@>{a! +rx*A^^ae*T41;K#f?)_EFzV+t#XH)VNmjzXU6cK=hc&A4C!c3C6D% +@kUH`{JFG%K$PgipOv|A`=zpYcKIxhr`R#xY`m;n&gdKXdba=dTUv~#~@rR2egpV<7g8nqmen^RZ@&J +a3e~20GCsWwL)DdDwY2eTd_^{1=VKCk|cqk1VbjN?0=ur@KNKxDvlJR~zMkEQZZ+Qyf8+ +Whq_+37onFUMWr#-;WRMVI*U$`E`Gcy|Q6VHO@B{h&QOvAuIHT+dfF!)Npdp;CrEwtPs!5+hveJDBm@ +^(<9yyi*0hx_Hec;v-zuZ9i6UEYSXes`>J}41cP!<-ujakM85L!_Y6=W#?J>_KXfp`?*E=%;eudu>k+ +;7A0Q84_e~j!+VUSo?xX`j;r7JkAbgaQt|H}pz&KOMa!c74c`keeg~Ytdx&4yG(Nl?N3{d`TWJ^ITi) +f35oj4JoSezMy40eanfOqsis1@Bb;X4@0KC8c?6iF!mb{!3oAC9i((UUyAiLR&& +Fn*6=CVU}MhcR~c>ZCl0lNA?~33dD(qM^FWCDW=)VPD;-`>3&@BM;zhv2)w@nsa +3>)+J`E?mDZCAmFGB~nRSeKF3C<$U=~Jkb_RSxWRDWGIGCYH~d%jxK8bmHqwWXFCkHHo>ne&7t(x&G! +kqBo47Z=M?{t09?6UZ6~UZtj +ogl;~GU5+X;es0F{Jb$Md_+8qCtVa1JHYea5>kAje*Y!GhHL7lw9?s&ILd@?<>)`-qcF1eqRr1*GBC7kB=NdZ;(M<)ZSl38Ci;1JGFuW$fD+bM+OhWSM=NpDsI=Lo#s-!XvJ +u8*7VWxZGlHf<40oO8{Q2dqt9t7U_TXM*ea?(rT1!;=MMqbo#EPeC%uWkUp)A`6!} +8Nq0LKeAYJhdLsnVjKVkWZ(w};%{QPPU#`}ti +&8nBfoQG8((CcU=O7;f!DdEWRISJ>CT>HLxZH=O@};v)a0^Z%}886jbqpfMbVV4B{45cJR0?T}*}B>4 +!^e_^>gx{L8&L-MF+BmSqwg*@n0v7@)W4zG{JhdV~GW8p(#2l +=Q9|Jf)pL>|ec_#>IOU;SwD2|iVS^vDbCcm<`8qPrh|>gd67kpAfOV4uqeAF2DJdjApS3%}?j#K*P6V +;<)(Nn(|a<5)KSEtezvVzTb>!qfOelkO*%bHu;jCWwKrk~r+|Sfq9v<$d$<3x8(rUyDF02Y>Z}clP{^ +fgg~5)6p5N-+7=%{81TzKRO1IV3zxBPY>=pIRjPPT{!TYgZy4_xcouSAo<$Gvd5V7^Y=ay_)3g=3@{! +I%HwbHK*hnZWgNbKRSWHlI?y4Rw;l5~5~6P9Az|?Pv9I7C;~?N05c==pAmAGi`tRbPe?7!M;Gn;!c>s +JBJ*Xvtb@ +!!NNe%qjoG~B0&m}KO?~>MD=G(NoXeMk9TCRA-QRAxvV31xIC0$wtCuAl$8~J(7$NRqXv9w>y0N*CWw +OdglAFdzVQGH~g8?(TTyXa9Twg9qrrGD!5;YNdgUeNNgNkjZ%t9dIg4A$s_Ek=zboj(jBGiR4$rC +Zl@vc}DA>;A0Wpkn6jTrvk5Ymdkc!Mi)2eUa(Q9$#&k)Xedns?gLmU!LfE>xeDQu%l3-r$q>Lf`yg+~ +RwhxeUVdHR{;sVtJJTJw7xQNa@YF$h2|_1I*|VA`w*%yV3M!zccu{WHFrB_j(Lq$#!KJIf2}m_E)CrG +b58J4+W}1XKp+$+)l}P@O43@g2qV^T)VHGIS4#cKbb}!Uf}ZVa3MMcb$Ftg`&B^~5?i7SpfpJC?q}(p +Rxe1zFEc64_ag7Qcy@O$%w6dTtJ!=+CRX5y?dE(h#QeQpGLm4KArMyGuffe*=!#=J+?{8)m;L~%zMTv +F?Bss}s(?Rbm6dV6PG@qQPMJPr6l5^;1}YufKUcjUggcTE5T`ppbPWfJDcsoZoo92uBJ6dC{Uz;O;Cb +;KyaS&_?$!~L^aGmI?W;|K;o@-=t>sIlEf +{oq;#1kQI751GWUdw9pv2+NNv7YN%{4t2SN1QDF%Kfz471K3fSygfg%n-@7_S+MyHGP +qAbbpi!S6_P+xW{u%T$p$ud|9fmA#Fh0$mfN6en+s-S}zjwABp}bZwZw#!zts~{R5+2mZ%%wOwR($K* +hNC@F)xqkjpmL@sjc6V5y~{GI{k7+p9p=r#FsE{JE^7%19^W-GvOVJeH^10{9NG&8wkbrOQJL9V9>=l +@UH{g_VSqg8yrIr{e4Ny^Q{0GbM(L_s)Jw$M +6^YsT5)x+qe=ONzk3H~yI}?_wnCwssu`z|`mowrtcF$s{T3b1?-Hsm=f`c<8GvnMhET-3g@syl)ygN{ +F_^{Xl`e#yC6+pSv9K3cM2esbZzVFi-8s!h#Pwxo5Q78gBjYcQV^m!TVaiEcc`2H9*mVwaa!EHNgu~~ +v#?LR4w)MdqP3GR!*D7qGRpv2$0-D*3t%g&?Oe!yC?VLrJRxOhy#_2t<+dGYzVey)*+R)J`Mnirp!&d +VdTS=LH!B4>KL140SYN`$$B1icusoTpz=B8|RkDij-O)7`|=_uf$ExWhkoxUE+J(5m5>0Wp|;Ei{p;t +TfMSev`C*$?b`m-z>y!T=IduRg;Ag~t*-wZUzR72@4 +-9r#d#;MWP6+q9Jnp(=@N0Hu$57YX!>UAUtBzX?l747weoX4+LOD +b$AAsjGaWXXLI{}y`3sAK(A{Y!1sqLMkuRKoNFrQ3x3AY!O>ff+YpiAsT&OQKo)X5zCLedG&|I`CPGE +b>!e24sZ`xl^Kzfc6R8PA6gHU2Q0qFv_DdhSixpBzi9^89NO|kP4-s;P9S73*Dm@96ZM3lvC~N=FpA{ +U7L{s$pxVugvdj&^{!C%z{DSe8SfT!%Wk7yPPUU)Pkn4C96ag{q)_>J_a_7B_br9jg;z47nZ5ihkID% +Jknl?#8{@!vBCM)~C6|D^VGzbpSUY^tvl5{?XV& +l1G*b-rXtmVLtJX0#gwE>VGtxG5YB9+0ERaP^jQXGZEn*c`776sE332s7u8kfzA*?B93A +e86U?F>7nRoC&H7D$dP}PoQ}EohW_;D9u6oJ==G}x3QKVi}X{&X<^_PU|I+9%=L +xW383!=4qsV!>1=8FqTUTYV$$C1L^#ABp~wp*|X{CvSh3apL$EQ@lZYmNMp~n4{a%Y31J#zf|pv&IUB;dJIRf|Ip9aEE3p!S;)6(gh<)D7V!GGNX;3mNCvek01Llag1SwqSFrkOMdz5$v +Z(#|N>~4A2F+yk%+dVJJ +=Q|$YhgFcy`-KnQ+_zzN$GjOJ)>U27WUB9kH|^SVXj>DK~2bYE0wM$>U>2kE1>)N_9QB{AsXwcRTEE` +Bs0+H1(3Csxi&l-cm}xYXF1(JCv{1j63UXQtK=#dc@H1Ux6Vb%qd7x2mlrW9qlj$NN5>8U>yCw2l=8# +8s2RmEBQ2I$+;^1FLEahBiacDqvYxYk +?#v_YrW-#c!}DR>#Z8~0YJtd?HLP`gWgJL#_q0M&M>Q1Edo%sRUe9&uW?n0alX(pGI9i~@S=h0f9zPI +^eP`Q%>M5L}4m6oMfabUC1cyEFb%1oMbk<|Q_7n@DW=acQqMS>%jtAl38nrv$@aT9BmKa8=f4cn!L6M +x4`5OIxLSf3?n}OH6t8J?{W8z~4qN>4lNl)GWE0lv^kiGkkUv!3M%sTjVR;yx?3{J1Q7Z=Da`5K9Zis +&B;1isg%|W$iEEcry<{V1{ynb@@!{jHTf``7% +#Hhntuu7Y{lwqKwXF&v}sN{`DY;@aFojwH&s10^#Dp=45A`(S|oCNy?VUEV{TiARwHWLPn!-Y|mYm+e +`;i+nJh@v?p@u_D04#gs5IxdB?UFZ?xSLO;st2)H +?#<*Q3?!21ValtVUmzfB>|DFlfHIS3Wz9~+1^W$BH~e}J&AXk{N8=RX^POHW8spt#?(z6?|w#+mSJkD +yl-dasyQ3Hn?D!0(g`eAo6HvXCqK3G(`?q*c`0L)ozAc<-2!?*uJT10d3{7>7s6hTGQlt1sc`aToPb) +HhlYRKe#*U_Ey>`dF*rOAvni|!ikwM-xZ3l?U=Xp+SGI6Hj^(V>?= +z+e-@K^)EK4LrAhgvu|I)8T5xT7)}Kl;35Q8eXd=hbn16J>@2?)}5MS=#(5(2hM19DO|GMVL6$jYJ?!Q69C=Qa9;+rRt&RptB2LRa +$=rEJt!NLO-0{;#yJWwI<@4&(X6#_qjg}ec&KfuCt(ay~FJuLik4ET3o;eiT)e+L#Gs1W#Du<%GhZ9k +tj27VhizSvV|pD%5$B6UeMyB|=>Tz=i?g(xyquHnfcgD0KUQ?oQQ*VvTx-VTQKZ*92tHp>Xk>&6eLiTw9~+JTOnB#16Y$Gyh4ak~xMvJP +Jb0%bz$@BDJXs8X;RUl-L&;$Ci6qZBSZFL><7Z=x!h*bXsWgk^N^qbw%eRpVT-N5=7YL*bwBb*`7zmw +gqo-2Luc0kr5PLHsCuAd3FKtks68zbg|JXQR?7xn9Dt1$ge~fAJG1PlF4S$hci*}p)Q**rs%eyT<((cHS_l}@nQRHB(Pq4#w96!MPeNh$0n|s3XE)&S7eYJYu-oK^;GTewDIfpSH?jx(d#sKmN6a66r +izZ(K0fGZb{9ayM^gT;+e3Ncm;P4#eWcVe^2CCEbAO^N=wJrVdvJJ4elbG@ik**_EdzKV=!hON_; +y#ihnLZ}fFzI1wm4?=7xH~ +8zR+hvWqA>V)*Lj#uO(#4zcZC``r_}JO%Q3MKp$qVLTt1`k7U#4z%ghz6VW{WBFq!j1ak+cF>0*q!FIIAE7xV1@C1gP +jhOCNpMf6+k3J&S7cddyib62e&swDC_^!B5pRnuTCuvH&KKxWvQjsdnOYhth+EV>IH~AM)G!L{-cR<` +Bp%_rfXZ%>vVT_u*>=1kI62bzq@C~^LGA|&{7Jt>@+Ff0trqdXUFB_Iv<1<90kaKLw&*P|tE#DgkO2m +t90}_;Tf6xfKpQ?I`_YC#UfapoKO8z@pTK~T3fIDN(Sxr=7Dz7(dCSXS5VZHWvx_P7L3Ff!y}d#6>e5 +9{=TK)GJH8_xS$@KSdxeV5+jhXP-JjqUG-nD~BDx#t5uRURnUMA3p0Y-z0XT5qD9^MOb +Ubwb6J7}4Z9?kQiN7lTPTG4U7l1>sq`n(PU1c;XRosyVVtrX5$R61$cce +*AfHc!Rl4-%!YEQ-&2GNBcZeGl}^Wr>xh(99rRaWx|K +%T}B?89M-P5_)-DagcPhwAO`1ln&&mdpG#MsDQkT(&(eKtPrn)*Xvtm(YUUE5V+G9-Qt{RomG{msx!l6x@IBQ97JcPH7n&}o +>ttd%Pbzb2RDrM#R;0hp|e?rIfw+RtGz(5@j#yE?SC`(X3K7xYq#(@SFz5izeL}xs_%eCq6djq=#0Jx +Nr?99H^`oj?M%nXT3_vdCypHpf@6#f}+#Oa+SBFI~R1$_@#!#j11pgkzw9)|SWAvfCX8foZL-iSrJPzw +(4;dKn}{T|4hI>Oja=ptfoy+GfMB6|k~{BBz&_O*#$^>A$W6CisQn@wcvW{FW5N-j%47e&;U8o!TN_+#spnhWYhgGRVIf^-3z&MBW^}*qig`Fn|5O5$12v_ +wp^whyMukP1pb16`;>UeOvlds1Ljkmh9pGMg<7cc~$tx8`vqhrP7{Han=wKdFM$K8jUZaTh7c>WNu(qC9Dw9Oy=&|sTPTfg#Kn*s +eHC=Cqo0`GOE3XE?YuSJu5Rg)YX?ZcS5_}TwpGM_6Tob4h9&3=jZYKdju1Z*gw=?i7fsvoXeUG{)cuZ +PZ@YEk1TsdKQi|)e}z^h-)75^OWe^ny8a0*^=vJQ@S*gW5Ww^^I&d67nfd<55Ifvc}eK8Ia>z(~uG@KA#@FuBNDwglsR3&~356L9=*d9d71Q|tKrj2{IRVUi>W0w!q^#W0NCmFPE6hEND5 +U>ZlsPrXgj9a6WTIKlRid&lQ@CU%SQp|?TFE>0ipX#2JY`P|zC+J`uZ_k?EnM!o=k`<%b~;^6ly?567 +JuI;fWUD3A}$`<&;zk=mxPugy;n}Ozz+vYR?{qI60@&TUvn&m0 +$uptYzJQaO*wHFPE;+{(#xa0y@lh4>X`DwTiBlbc#fHWe(sO*IPhEDdf5>P@vPj&wm)fi@_deT?kqs& +rSG$+DFO5F3eO6`562BWW~&O(!(HiDp_lUK90oEJ1z?PsK#w>N6epYh<25YbwiPg(|fKmpCh +^&4EDl$y^u`Kd1Ub|-oKdS43$q>?cv{Q_y#ci;m&|LFUUEJCr%GoZw2Eq+5dx_NKBY<^{rcpeico6o| +C5I2jEkBhs3dfCEV;3dVFPwd~nUjIiMmq3RjXB-LnmDip$?mXyl(S*sR6W0C^`pP|#BKX$Iy3MgY6C@ +rf}22G@8YQ9-M497<&N +#C_AY@x;rKcA)>6Cs!1Fe9=CvO9F1lS$)_e +xMH{k_Hb4H`d +X))yhs$G&vFyfZ5H~t9Uu05fw2I->ckzJn3`Pr-v*k7`|OVZZO*z==hA#K-^HcaTy$ECx#s}pLGOpJaXPF67MU}iSd2jg4GmLC2_=kVac8kVR>B}lwo6;e1QXVC>->VDPf?@+%-B6bhJj)iG`tZBg?IEBUzmu* +rq|ytdp%%BNAxu_~N~KG&N7+qG%hFk9zCqEQr=5r-W)HaD{DY^wJ)_RZ*hnf~xB176JKMZHVx#it(6s9d8C-GM}oZdpEkHDJ^<2HId`yOpCx(El+ +}n0eF6&!w1(nY9w$wJ2L*~1-m_4>}_w1ASCyMsJw&(|K`^|zsxc_1hg`pTuATWedI7Y!3v1@cd+aVl72^dFk{L=z@K=0bsTlkE;U +FqmI|4FI6pOT<=9!9?v@CjwXg3Z46NEG-k6y+`cj|$AjheQ{u?VIze6Z=?f5zP +nHyV=|EUDeENXc!0^^P}2u+WvoxTD{)jveP*KgFK}l7kpdLV!tx8>6?L&IUl~~AtI4o08y +7Xd=%bb&$DHk$9%usevw^TKeU}hWj#GTKT)nmscf$y2l9&%(Sf97!j%l$%h`IxC6Fy>lcerfMoX&zPfa)R#%~&rx1b$XOtItSx}V +d6cI|{B$`xbkf6vW~yqM`l6*?4;fQfK319MDps*14@+^qm7*6@@px$-vMOCM54a=71u9#o_halj7ak8 +Yp>9X#pbsO&)b+bWTy!v?y;U$f_%b==kMoAWhe_-&lI1_-zbz?pp2nGP?`&qgQl&^p8+iH)dNG#&~Yf&b7^*mf=;up3NgeH55J$nqTI%`A!{ +ovTPzQCHf&y5LPm(|G^amgLu#v)4kf2k_>b42{Nulg1S|8d2Sp)Y|`BuwJ;_8Wmi+i&m&rZkC@6bvE6 +o|S~?Pvc(-*G;(u~C!1uTFySw>p` +XBgt{Qv9pKXSY%syG;VjQ1p}g3kGwxOa%5x)kBSE3~kkJkw*K0qJ4v5ORK~_vG4PvkF=x(xJ*GEpVl{8=tn`{qvcviT*&?9 +A2>3tITQM$T3fP|M3|R@r($+vN)u-76XlxTA!2ZKNp0xx;{4_oVzo#e&z8PT&~@+du?|HSI4rUb_i2T +Hy$1qDjoYGGLhcims=k_MQ;2J}Rfb11sB9+D56B@l=~S!>x~}VTJaiMHjw5vLp@}&pWb|C5p&dde`ph +!7cl~eAnzDI#qgL>7lQ=>-hJwBxtjIl;R`gr3}= +XD{$RPLKFBKXMd=fo!ly=>BYfyiEW&X9*?vmT_TlSub^_|l4^q~jnyCNP{s??*khnrU<@OjTP}b%aZQ +Y7>q@>D@Z0xgS+LwWTzVHBU!04CkCsz=RTc%&zWz3clwaTnZFITHhmB$e_u8TPMs8cEtN1t*rq`E1*9 +TnS!ffz5ARBz~5S(Lyq?%JigGakEQP)s>Qaj}YYwCE(Qz=YF#A_$m;!>NOFWN+hjB5uI-?5EKc9Z9qw +)1gD)dpvRTb%5d73reHLpbF#FoCY!EbXQNe>u5f(7|H3zJU)k;31rKune!t^QMgFnr%qh1!GV~YI)-2 +#UPp1`l9D7g~+7W-i`ayb_b1m_XI%p6jwLV{(`Oo_gE8`*R3=3-oz&ap*w$$emKdD>wAd2ytBbG +T&yA4@ZY7`}w-0kiEGFXNyWJu0twJ0LgWp%16dLQnFE_dBD6;`RPG%SpgQ-tqamjv4@3j+Z6J`AP_7n +g>8X0lS<6G{zrVSnw(D-Bo|nmQW`FK{exzdU<*UZCEjJUyp0yt&|~YK^cn7B5O&oE`q +L^bW*4t)cL;U2ar)g=e*?hV9kTjZr{vi&u!=`{*EOr%yq$v!CGYn*OENjtV~|P8;FcbN +R%c*WW46yCZKSU)w?Si!nL57b5nG#CvugrS{-7jsA`Os-C;&|Kq(CaDUOa3Kc=_va-p{_6 +_)J7dnC2nL~Z13H=)Ddwz=3o@0YwasDoojq5q6H)sGcl_>(4ghv1|x;V1sJ|2K$}^#2rb +`dl{Y7sQD-&=F}saP3e0w)IT6AUw6GU|(aJ5@I)a+N(WO6{?N^H{M@u8cw;-hMkjJ?b@DRAl5yNgBmx +PGsNJF@s^uNT?+Ab0HZd|)YCBHjCHV4B?E-g@(I4~NZF|qG|2I$!SgMsFZ>mJCR;So8q@(h7YmBAP>( +A;2A$gOC0F!;^=Nhgt|K#O9KjvV59-B6ie-3GLwR996Vvj%@Ra_9DmAZf=Qy$zv`d>?(!>cim81l*Mh +rX?vNErO?52q#BC?gU$~slt6~nsJ#Dyk4JuOf@l?uyY{nTJ?8#7%I_(>Fk=yDx^^WbGEZRR|DQqMf7A +;W2k^^yw|5uYyWy-Vc706SEig+ox0TGeHfeS6UM^pcv+f`LXkyC8aIT||tP(+xfngjf!d=3J^v?C07j +p?I>cj#Oo-@>-t(`8xJdzect!JPB9`u9)(0s^y&mJ($dT<0+$A;D!AfQ^gM;4K>@S4qNg3poB*0 +0>pg=@3mU8TmJt-!%RanaH5(u3nF!s&d59GNkeoHi=pHT^FUr{Qo=%ECx(yj2iT?(PrAmFFlyE`LFs) +Y~1PlpA`ulLLndFD&Bj^Pz%8hvOcge|+f&m7bwwKu!Q +TzuzlXgE^71mwOZnakK$x;E&WPelZ8B7AwklY1+$sBrS&G%IXirFrBmMEuD+%AkM3>14L=@qL5(O +Ajb97jjgDrSk1l=@R)g4BzpN*j*2IY^7~n1!7B&eU^XNFTqsTcvy|_}_D-*a4U%YYZ#V#--ydUMOWNn +{z#*LR5lv^^Gk7Eej=NrnBydEd{a(M-AE+ayAP^(IVWRtd32IT{<_xa0czjQ*K1ekTKiS%tE>%}$5wAWce*WNaATxF@;AXu>+)XXw(JKK^VsnwG0uu;C5YCy$$e8A#^6UruCp@2C>L7 +-CoXId+x!_!U$L%H2l!pqM@#hGcuh`~i13oDg2kln#2+R{&Ht!gMouM^{+$k*9tg^#d|j1(OfttUFg4 +dd)&yLKL_QqsG$NCX>zxJC1Jn33KCnK+@J5+@kjs1u4p8%rXgjV#eHM4==^?t +Ye^XPn?*HyOjjJtqTU?-7V-CoR!iw;DzF5wpFeA=nc*$mba+^lhArV0+>mB6p{gIDA{L(fdT&MsRkqO +nMhL*!WHo?!0C&2hb_5YYO?JNcOHBHfeOPcn +_z<)!U_PYW89%<61xMVJD7JdC0NGzK<&+Kwx04dW8hPkBnYKYp>D4lO73l-(wQILr`h!to!%{i>ei+kNo(Kx=l)tKfC8n`h6qL&7SMdPD2Hx+?lBZr +$<#n4i^LOXt-uha#97W)LvyMt2H$ZYG(BEY)%Bs +LK+Jfj^1h8<-5T!0ws8O5T=z&V3K}#)|erW{{#5K{v-JM-Bo{vFYHtJLJ%6oA#B4cn80BgBPbf7$-S8 +uCXr9$zFqI^t(mgn00He^{~d7Sw&Cxqja>O>_*$gFc@4$(x!ET-Rkr#60lqH(Pr=vackpGev1qh`^va% +IOoncYxX5Z#jbuu6>}p{KKJf}k}q-jXZUJ2_^OzaeK;o>0yxc +_P#M3(oY0(tAC;O=%UsI1 +scuhJ7?-(NJY`p5~x?Xv{K^Lkb$I}X7nDxAmFE3khuzTj}oM>m6jd;u8af1I{>W6@8b^}BOFpz6=({e +V~qMv^o^Z%hj%F&N(<3#MTVM{ttDA&4Xh6h_ET4Na)G+IRY9F4&i`)97u${Vw$F#k6400|oCAUP^tAS +k%tOwzJ?jH`>U}#<^f>N0#mP;L8BOTN)~O&ogYVu%RRtZ7A~##G>E*t?;{aND}+S5WJg}6WG4Wu0uua +oNI5Oz~1ev?|zDnXKk+?#yi$h)Q)x=NK(kgu-*4n=I)#^6See_$K*6!zB^y~0_~>&mSLw@C0sl=>d>=0T+P%ttX>s +@>DgN!4#zQmk!*FS(8=Cobr0qT1kh@`1f01P5kD*(y>r2u?wQxLVUUAg~c{ButHClI3RC} +&RFVaEz-4l0v2x8_kTQ=y2yBbWv?@&Vfmv$Nb4>eQYVDONxfSNz8A+JXkEX2`k;DboI7_e5xx{MC)q> +Pq+YX--^%6cf9XsPdU%0fT}dtn)9m9iC>Phlgct09SlC6W6(v*63RlOv;9M4Gd{MGyZBmZ_Y6YYITtv +rOtHbqRwFMnm1%+z*m9@=)nZsE*pjNp&R7D9TQgE-o2F*Bbm3@Sr%SQYn$yTp`z;F#>6I!hdo^_6r7s +Gm?Dpl0Pf&c6NU4m0MA3LCNexIgkB2I;dDzQs#9$bt6e{!LfN;-$kM5t>Ook|3_%A=sRUjPh+48E^^R +%e{zNeKB~xke;6fB>+?gDvj*aeKGE$2UCb*8?lU9>nco}nixeBTe@X$*^xseE;^IG+2Kf`=?xrNuq6@ +MW{6_?lp9^Aqjs)waavv=_MCwFEO#lu38Sw^754fz=lbcbX4(p5K72jOXcK2IZ#>DB6mF9tl#%jJah% +`m36fF7^`R?V<@6?4PR?c~U78GgQUcOv#MP{ECBW*Mf`SE+6ncUWo1MH)kS%o?+< +V*)6oq9TM-bU{GX$|0aMzcTaMu^8!TVb5%5BJL?U9rgpZp#T`;zRoDkxs_GSt? +u>!^<>m^+47g-W!a?L&xOMm4J#qU;68@eX3CezmxS9F3!suEnSq?uLp*mZ6`cGC-Rlqm+@T7^le1*$U +$EHK!Mwj8-NotWl6|&bSf4MVgEJ0J*WZ&{)Oxx8!1b%R$~}9I!GM)%8QmfcXTyUQqF;Ot;T8d%Vso^j6Ca86 +!b%g_fcnwur8c&m5o1p$`5~ElHxW;(#XxfOFx3Ului%x)YIx<>6;V)P5MGCw_q$gnxkY{HTa~#+7rS`6nso5Z^CtvyK{Vy=Ui(auqHeeiOREE9g-)ox9Vf_FbK=5y1+kNaTk2C(ya>TM+WekBl$hSbT +q$`LYa)^SvedH_EdFfNn;bM{r&;MP_Bc3p7_9xIi9ge +^p`LJ=`DikY`I(+kyvjG3b&tZz&=DdKL>OEP!7K8+>z%+SvA$D$6uKwWdj(4uesUq|x;Uoi4Zg5f>>{ +uN*WmM`$CZ3~uSz9S*O6!-5Jph47TO6U6*OPIR1KNgF5ezucfMzP5$_o*n(YtG?$+zq;ZF#sm>C1R*4 +e;{--+AsxL%_ykRFf!_uT`1SxIA?R}(7Vy~uuEA)+ke8!mmT-*Cg# +w73k-Cr4#czQFY6Of2z9d0|ujblCEoA2M+GrvSvU*kMy+L+S%W1N@0&Dj2`w)eO02KfGVes?$jq$%po +U83$dDI2X|x1Su&pW6G{Z^3kI6CF5c7$ZLW!s|A8!NyZB_bUTGgt;yRI1)|+bP(Djt1nM-2yOxP^p8{ +1E*6{gCk4MtkeVasdnifjXaW6MI3vn@GDq-nLpUf#@rSrh!N%f_=SxK87vu=BG{TMXz_% +x3T|2*|~?omdENfu{p5nRp0>1I_q9NBiK5^eEE!L%KfB?_pj`q4-WAcD4+oJ#WyiWG-itDMl({gs+e{ +BWr6Pq?}cK8L1GI%Fc$C~$4JC3QHx_zACb>0Sb^<}t5#G{?w~0pbWPHDW0L{Nf+7a9@*t3vXCZ3Qfh&B%~Y;;$xa4mMp^xdq^=VRjKmzbfV-3 +KclH3-13zv-v-m_F^|c$x73P+vw#Er-2#-TyO)h4=mGHExjt8r0ZLzIbHw|mG@*E5nfPO|?-;KSLI83 +Mdm3sn(IP6hHl<8TUe!%CHwAwy&Guh(y@|f#<$*v2ebz-Ijy59pZSdR)VE9~t|wGI8e9YBe4h&piYmO +fn{a~|kWZ`~kYdKzR0$7yUjZn-W@8sqAP2htyFdp|S+Bi9RoZ&4DTMiwgK0ehnoU4EjNdHGiF1O8L*x +GzmnfezHv%$>Z;UB~-I)kE@`yLVOS17VEQ*I0@6seNpGKpFy48w#92H6HFclrab}jTpxSf;&~))A1e| +=bAUfhu5x=6^^V}JXqSisnK1Ox>CR&uP4=-hxO_(-0?6QA`>e8Y+V4Cjc&*MIg50p1@04xt|e59h{<7 +OU!5#t6u{h8Mk*F;I=Vfv%O%Ozn^>LYXV$C|4n#!%fW1~qa@rrU3XVuU6OmzHm2WvFC2mEL%76=bzjkAV#LVMv{s;`-hxls&C01+HaSu@zNj +t1XQgU;#^w_3&T2mq1Jp&l;!R6}6*ZEvI%<4Qv4mRDXXpI+k +z_kKm&@(q#0W(Z5P9z}xx!e|{U%;L)c#lCiU^Z#NNsLI)7QMmH-b!AuqkEciYL%F`gOcg<$4ov1@G2f +m2b|dC0WSI=SD^}y%?w1%>%K0^kwc{OQ?G7}Y!z|2I_mBDVrA^bHqu3hr6|8jp$uNNxfFXHoJ;T4tKv +1(fV5PCVvFJQS|QQ)mc~^rKd&Htv$$o6j?XyHkroc-zs;oH!V_5=d>eH({)f+tX6arv9BFSHj2-)<{pxaYkhdZ$i!u=7Fs-CGsB2fVix-lIYPCVs1 +cA$YsF-tn7qn#pL-yMVQ`d(rd`ys=?qm-pdMl*BM=X+FMF%wLosILGV)55_~(yhrOX(%4 +S;Y6EM3NqiR!Wbj8wFSW_w-#MmLFON5kjXqLFm#+$9ZP*;YDPVkzrMxJ*Jh;)d|eB5GZW3lb(c1`^vrIQy@8qFe}dYBW6)hEVOJ-Z +A7$W9;bvf8exmE7kh&%iqc=l&}}2jZHA}(xalR!CaAvxQkRs|T@k{up2!Cw=C~8i;)B-GvjYv@?LguZ +DPG}(_2YZXv*|j8-AyG|Dx=`uvjMIIlCzQYY8fXlc5Aob*D}BESX%fc_q7X02R?B +niMv&z%)u2PdqX36O&Gjj$o7V7QVH~9PVtJ3rE(2%HNKt@ka*;d%Yr>bnBl3z%bV8V?3LsU9LT4Vo0) +d5G5?_)(IVFPV3yVp1(o~+w}yPGAuYZi4mSHj;x!Kjk-B~`F-&3sVGq&FU$#|!+Jg0catk=Ih3F3K5Q +A8y+p`hjT?!9kkzBFopXAIxIT!eL^6~QJWadI^d_Nf%|G2$M#_-Hra+c$IVfz-I*bVl!$f1ixbBfk(1 +XMKM{*4Cz57ZfMtu*>q7kXFi7j*~hD;Whf`l#Hi%^#ScHn&AZ{&*bi +P$uZxaeWE5yXy8ueE$gqq6tNOH*O~{A%|DrA=6u3@h=v6$G*U4a_zNRB=&(X>q}PP6x?0XC~{+$Vhrh +Jbafo?+2~l3YIUdz5aBC6KVQ)~Ja&h{v|aOj?ru!TAqVWRYu~$dbMVTMU6s8_fm#yp33(W{?OUD!eqo#_^XM&(|mxS|OOvt&7l)p +d;=IG+&eJ{VMeb{fg*9>CQK!3o)&V3!pSE7LRsX!EQ{dA0bj*%$jmJOFSp(IFal{ +174#(fcG}1gLjXFkX`!&Dp +dS^gqrnbfLqiN62%2ZROWbZWXGHUv|_e6d2(q&HO_jR9I=Hx&DnMPCe8885U}9jla=)DGS%)GERYIRe +7jDbC9ISx^!Jm0N%NC7kFbw4CfX3^&){En66I}IcUiGFXcSHHO5&Y{e!c3fNI&t{|Apl~$m}Oe{lI5Y +oJL`aAP9;=VS1y(8!LvePo;n9Xm6t5hs4P}T^EIWr&&Pm&GqzKUneB?ykY$A`3gR_;iH1R(QTJXf4BJ +u(Vmfu-a2a}zO%{TZSI%86M9j)>%nbLQOUol=obWbKbzRa-XV1FTnp&Eb#50RiuWcH9N+or_8QyGM{k +zBy?pw1qux%2-kf(^VegcC>(XsRIHLAn@mpSqAb&?-`L~2yyL=E>MN6-hpUu5zpdRvhIqI$Fcud@CUgc5 +zxLeS%YWZl587k{iE`_{&-9iqMwLtZ2+37O~N;9jZ6oUY?pA5$Gzs5^%=JGO{NUae^OBCyy#4Umy6*Q297bxT +%4q@A}A+}E@Fi8gCvH~fhV}WYxYI}lVywBzex{vCu^Q9*$-R_?5KG*oUk*wSEP{B9#?p8u~(;y{=JY9 +4Rfa(Nj#&5>ctX!vhf@hJ1BMli9qIa88qaIH~ee^JYNUx9Re0Z(%k*qb`(DKlhAm#zYq%oeEV;>sGO_ +j!*-86nbCg)4YEQug`ynEomhs;zt9G$@1bS|%L_e?o|P@Z=_*MOH@!6Df3LD~_nP@KXzTvTYI2=@)WU +Zop6EDtg@N)_`9!9Of0k3HOWf(tu=rM12QBc?e_(%D!C%QaF2|Co0eQmHP(sknH%OBWco%Zqf)ZEjTs +^clxgBt5s~zVI*Lvs4%eQhI(TCX7bzLwekHDGEHCSyY+3Kk3B*e^r6}JQcQaxKn$ex)sh*cm~dakqYo +=B4cT{LXV8M;qE76aw=x%d?TAUeqM)1LS6E-J>SG9ensQ+eVtca>BD2G0aGCz0((5Ya%b$;s}O8^kxN +iGiJ=!g(tKX*88=_`Vy+?diJgxFe|bEP2d1>tdSemCZ)wu^iBPNgjTDcB +u!bJ&S|gpeETq(-(D=hy&3wz6cQMtE@1Vc}2ILE05ipE$K>XrS7wA>8(c{9at6P^2<><5pji;Ns&7g# +EwYYd0Dss=4H5m4>BKdba~bhbq|Qe9WC|d@b`l)ux23x)0M}mpDn0_of=X+lSD-XK~w?wtW?+-hJ5A?iss}#1VV9V)RSR6=L +7`l6OZFMD1JNE~g*wlX+pX=WkGKujj$pXtK9SqHn=~y@{K8?~?JJtishS^Q`qAy3?FyyX_ +UHOhM$`8#@cU!skGH?yJ@DV${(kqse{=i$-2=bsul(8_UfwSYrkX0OoQ=7Ep#_sGOk38e*6koX7F85aPn&_3QD9mo<(Em3?u7MA8E6W9Rc^L-4#o;`ocw +eYT1LkiI7*(!k6~SAH7kj>&6a_w5=8Qfd$q)iqxV?+GD<*{Ex<_-XAdFv-a?D)bn+cE~00-ESdRXSDa +W27fdN98v+UQFD#t@3kNm~gy_AXzr_Xk+aV-=8+oJT +-an4RdqlE9;0CAAuMl##2qpGaf_L}#27nvj2FTts8NS;(DP(V|+!i9<2)W%kk?zf%@!P9(gZk}R9NS+ +Hd$$g3z@5C~1p?Yan%@Qs(Jm}ne-jo}8HGGE>hrj+Ds~~IPqW%cXpa0MX?QO1cUta}AFBV +jN=Ud&`9OCM+XLeC@%mB;7%fH92wGUTqOyi}E|8IY;d%FDe_WM}BKe!$Hc8kBeL*RF}_`5p1u<~ah~P=g-lgbp`O14E>5}>b>FM?yZ7>-Zol%3!a5>rWQ`}$>K>7ae-t|XMogT&=qUlJu +8mDYysgsF;7BrlYljxtp0pZBVe|)2fa>tsBTmlB2UIsU3FHaP>czzz;+(DE4neOr0bf#)`l*Shp} +j!cJ8ZK$8`B>|E*Eci +qs|Vme}KbiwQDq!~-p&%)%Nd|r4e^oIRZ!+{#o8x#o~D&uO|%I%oDAvz!L2jXRe>c}Px*taKRbV;}?d +HAj!xU%E)(m>2ss;n1$6;-#mV?g!L&&TqJ=)>;mz#lt=Im-l+_0j{@+q>0;P)l=6>kqkJa~B(Dx+7sP +Pqoi^&Up%ef3a)JHbIE8CHit;9z#R8Cvx?d*V%@7=kTn_#-HekBPgaA?fJ-8MHbECeKh2IWdqz^NCT;zzQp64aHL5M_9+x$}>S(l2ZyX`05kmXdVaX20goLMgE9PVk3&@qlFwOT~ +0~5h;ejpGuz1TMv(WiD7mZur_ +`>f6~6Zkn0&+kJ~sKeTlG69wKvecJ+8K8wk64U&s8PqPN{c8fpq`1S6A^}ZF^5eZ5s*ki@hs+Pe<)y+ +u?ozOuY-S)LydPRlwrCH!pqr5RkjE;d{z7-U9;S&F%yIJ*fFkrfny0S3$|XtwHhL(HEk>W%tUycZJ_( +oRkC=kgN`{W4lC;KePMbXLi3&6a5uE{x&$Y(PQ;HdMt;HHUk^TBp+hfC*<3Pw_6STymzhtXz%*DhWy{ +{U4JNTZ+ln#ZSOK2odnV1Np0?-sppe14L9eJ+phJs+Vd=dwox<_zT-UP +l&+%l|iZ)ixA=D}q{D0M~w`4cYvQ?fu(bdbmW(ZQ86r1-C`nbrh|R{-QX1P!{pSD?g0FE?f?+NQCyKCh%G3@1;XrHRN&_ei +U9N#!00JBdq*}m+ns};9*Xo|qhi4p3 +kQSpUNw6&JOb*95v>RdEBb%MeKWP3HyEdz~|NeFE`qwuM+TrVmXMO^`zkk&S(EZ|yAB1EHn1uFzUy7n +h7^QHWq7Vw+n^p*fCTSESDH26V7@-h|`V{|Qc&FPN*$v~j77w&Hbx^@Bm4l}5)?jL<%hB6cHT^uhv7N +H<>KNM75y;!=Z->C}?X`mLNV@w4yq)^?0syk3()Ms4*+hPUe_QZ?q`U0@E(R3s$@&e6X=u-g>`9cjy5 +fHQdopUH>_qg||0i~&MB^P7cbUXwNA3;F;rGlIneJ(tUH*~yZU67O;~$xP;Ge^>x3s91nq+vng7|dSf +?*?zk@vWK%2%fFCj%Gj*niipvVD@Bd<>f?vk-c?tw(FehWEL%CIgnR9b8y7MDSKK{prMf=()nuek?%V +&tF1Rhkin#_w&H_Cwz}Y8xDS`%Ocu4%OT-9fBR6E{dNr44{8GSQw1PbKU!IKU%%s2zh6x{zV^5B-P?< +7;HUTJY3$_#@AdcFL*5r>3|H#)?t1@XqtE33i#f +kXv=V>KRR654xA0x^PY&6s!!#>vIvZgug(JnX4t#m_<=3J}|1bz%eCu2y*Qv&j=GjwS9NE|)FFdV%%}2=(M3XGgQk{kzrl5PRqQBds7Haz~(YeNnpqLX)FQDn_U-t9& +lTDzL{xRCu%A-9lhS>1P2uq18K?ZzID3^Et;qz+R%ui>poCsiGPdMu)~QlfJt+0(YSFgsT1ITvJyNsF +9AX6`k@hCsxXefKFBp+4bC}P!5LMUx=Y*Oh*rOaj8{>NmQjg7C}0aw@Oe+UjgjnA?*4I6&|#hTZnR{E +|7kln1fl^xA|=9;7xi-y)e5mDfqsUL_M>(%6J(mv;>6`5ZjJ+lvl|Pk-G&Rh-oK|PM$!jD*2Rh;ln-A +O`i(x^L4$S=uu^HG9Vi_+@+YDRdKrY@|%IF8AJtFXZ)B=n-}xvKdz`;%2=pLKOg#m8n>FC8(~PCgPt%EI# +RDHhBcnx~Oq`#OL}&9|zAeeN@N>wNd?GFeh$-GKb?TK)YW}M&7ir^BNx&d+U2lwAb`9A0XQ!pdpWigNV{OcEW=UTKPjaf~2{lBzY4TGBw`Yn_jh$H@ZFhOkRB +YYwj0k3JYeJvic+-!C&IhlD;peA|ggTJNrx&@S|h>L=sTf>paA?L(Ctz*x%za=T1)s~pF@RgO6b(K30 +#O%o5#(}@g+!u1{#@^lrZaN0ah&=+<)p1QivxPJRC5KRPMao5(u#59?R7%H-zbekM-g1JauqTAi{#D% +!a?DL|K7AwV0#z9Pr_bIcwnPG13XtA`wU6V1^9CJ*h_0sT?@{eb8elpL8#SmKBQ!Lxu0 +uyYwLz@>TK*Nr=Y!oi)G+dsmWc7DSx@+=vxrDeU6#N-9Zu8l{(qPTt;3yM&){1Jbde$B +H#o?vdns*0pVmEc#=zE^;CItkzm&k3{rxMi!6TYLWoCatK7yk@`f7|sh+ +oUUmsFwSM|QV`(Jt*e%{jXtvCUJuNw=H&UuTm^0V&l8`S&0f{y5}{61#9H?6zZXZH*ECpYY~`vv@y8} +`}#0{+Pj`|N%J|Kx^!W~u=E%2i>=UxW1|uAs{u?~x**SA=`EPLQMzgiHMBm7KI*EF%C`4}9V>uOJ{+Z +B*Pgmkts{M2<>jJc=YoSYO>tr|HMa9}He(6&pLtosV4G^;jT0;2y3$+HYJgg^w|)FG$vaymmt4Xps|o(&|gHHaW+f+vlR_fU`v8$h>&A@DO~T%5qjE~vacbO_L +-;H5}HKV}VM)vh9~11$7tG+GjU^%(R6Z{?IMoF5nZ*2T9M#)@?8f$T2_w$ +1GBxyBp2ieC9a6H`^aQ1MVUIaTKHh*zbG%cIxiiuh7Pl!x_cAFg`sta`PJGp +~?b@B?;#7=0P-VebgEE)t$2w!wF7zK8w^MDLQT}#RWM~ +H@<{}`?=yCHL3^!31sKwK|gibU>kLQ>JM!-OSm`1yR8Ptkbb}yn?*5|I!#Izv>B*f7TI2EFgVCdue=( +-Ko`8F+29JcXK4bPYrm%Oc*!ymYljoXBRC(<4@}n9+{;f_->Il04`JE4fWS*_R6q*WKe{UXL)|&>%~e +4m(OC$q*Lv$ZNf>a!)-kihhlINQ3sVILz&y3h(c#3Iws=pX(vovs3Gnf`;pHc9VYs>Gso|uo7Kt$` +D)d_Cog3lg#n2X|`q9=r&8L+mnkbxTxG^|J59l(CXA^qL0G<1!$`9PZYq>&Gj)i#|t6n&Uf@%5@WEqT +n8nKK>~A0!s3QHjYdvo-s9+k{2LhT8%%>4hRc2zu}QP)f6*yOeNe%D^)nOM4p3CG(`<6HCJwA5qD +#PxW?PTz4V#`kPw{bN#qrRsD>`#mXf_3R#DLb`x{sZ?Q5z`%(}LBT0Pt1W&2Qh>pItypgW!Ui7ps-=In8%t26`Nm@;A+X^rN7Y}aiAxOdD7DAhafIY$3>_H<^h_rkzr@LR% +7lPxdD86%Kg%m08UY<4B^4v27ZpV{cT?W*@3cVMd+&aCBaMvOy-{ds9EUqZ!BE?~l{)y3)eoY_U&=uo +%xt(w5G5-!bZn%`#Fzuh?f88(pe`?MTZ2OPq`@x|B!AXPyQ5ZuJf&y`vz(E41a2$bAd;_=JFNpx*Pw{ +Q`PVDSbGkn*0QSau|8>&o-HyWgNl$a*Ft5m*Qpnr;Qskc&k_nSiYeb55k(^FKjcV4C5FnKQ|q;^kH=n +K-!cL%L)aQZ6~(LK48?c2M1V(PuHT~Y?^@EgbX+Q#&qo63qeT+a8GHVj?F`~ELX?7$j-x0=m}JvX(%^ +=$_Hout|YZGYRghCS~+zp_7jenpc^JyV|UVb&rOxK)SAH2zTr5f +H)g{*UvSUcT<>coAIC3{5uc4EL5wN7S?|2hx6Rfs6+P|t`Xv*+A3IBP@(a-2^sD}ChhIFZSNhASEa;2 +=Onz;HSq5&`}VTZACs9n$L1an9U%TKm`z|X#ZUtLM%1#6a6koAKZABNHnROfNBF5GCi`?c2(rsR1#lo3F%` +1j28U;X|%nqwNeVnVV;dhXN|!4oUhbim1RI_sSkosiWBG*X>c#e90&py)#)5&@wYcgw-VtHAVf0PFB{GZM +F*D(AW^Zk%811SPS34){u7)L1z2e+6^At;4II0}E+aX>zG7ThHP#k=W6v1`({=vI(#ALn>aQ|;{ioo( +NagT)q2KaIkB5e)VqL>Y2bj9ev%Kh(ELUGiAdXb1lpWFh)wO^|m2`OP_$b9iPfu1ktM$i|ygSH5P92bdxe +cG|QH(xV*L;=XLCAEoQYm0D&mu^u)gDP0Tc8x(O6*_HgO +>_q8N(Y8g>ueFW?UkarN4|yZ&!-snw@;eY8G65<>Cc&cOnDfJZTD0@hu?;qp_T*o9n%trbnRT?bUMKbrJ<8E*B6@U+R +mEWKoUn8R~5E#JwdDc-YAAcJQ<2#E_rycfHPKNGhK?Wcs_C!NZ&uYk$uvx^P6wZcPt$ERl80$9isw`m +sBR>jdfQq<945j(HO4!58XO}@7s0$vfGBbOs586`6yPZ{&cIyI1rN3?fQrXv{EyK(vK9fD{-wyapp0& +@ip*!tg#63L@T4S`oTsCn=z~xjq^uRtDR#xvnLWVS)kO7lR0Eh-aWSH9NU-EI9?9jV`PhuZa%`QVku|8EcqnSU)#f2JBOKIHHdPm52oP@E{rCruHG4qi9` +R1!0zl}w+rChye$BDC(KAv`Y1%Ad6BM(5}fZ2+-GH=&Y6M*iT3j2$gJ}1o$8^EL>J8{y<0OFaAUDJh+ +r;dC!9Drp@!5dubf-xRziUL^g2D~SHR*J0|yJG;3rC}9 +(Ij6b2@|d6{iw;`y`HV*5-wW&wYH>kC_$HDa}i6OZe +uhf7!M&6~9B&LS5nSb`DD}BEM;@OF0C?Otf_e?dco#}-q4rh(Tbmk!T);3rh+G{nQ)f4U>^t>8#?vU$ +GJ=&0HK>?Ym$?j?_Xk=kY0y+-@)!iFIitxc0R@F^G>zw2jNF7+eln>{?z1EaE$ +SXf-xNnbV6r^@LmRXI^|PY$H_!Q|;rwj;4>%2iHku6Yo`yKN+wDLYvODk)(5E7%?48>w@ZBVbM0d~>6 +K_v?=-tucU0byg-t67>n)*Biv~l0v9|w8Y7QgLwHpYySy9je{9`<%H+y=zdo}t-r)NVYO?r)p@Dg~6} +dlm^yb~v@oOC|fijZ?$vp6bcbwWR*J=}LADXcZ +smjQ1W8S})VD14&F{aP~$Ix$jmJ(~g3GZ=$6=wehELX$NR~#sbm*6fXU6?B +g=NYm5EwT?VjU$A5AeKd3;zTm}>AjQ)cP^y4?MJ-8$qlxapu(o^^d2Wgyu`I;YWg`Ec|+yDp0DH0f8g +xfQOugl!v63F +#t$Z^m-#^w;u3Mww9?z1{aJVN=UCvA!9P;v(oi-!AktT!9{nPN(7VsB$>mbJIAk;(P$g9=qrJ*y)>P# +8mCz_7QA<{~IPIQT&f%FuxK17F*pRaxmec}&Y8W9NDI4MfvC~ +s*vc0NZEd$^&tufK2@dwb4P$Q~;5FUDZxzB!k?J-yJk;X_Wn1?bsZAOEF&;>HhurQ6)zcMFO|zNfIal +ab2;S;BQHOynEk+f7@C}uy@_&@@-d8 +;Vl{*YeF=xApBWwMdyDGd5vYjoeN*4Db(5mj +BNYp*K_QeF3;*=yPht0;C66QlD)aX=_U%-b1sv5`EtS2X}!?H31@BY+Mh4I8pB3tYL*WYp&bW9?whzS +Uey6e@99y7+GKII8*gQ&RXSdMvU5`H*+zwN&(0>*6hqRGNZqSP81(QC4)3#~gYYR?&cJ*UhCU{m^#wg +udSk1PK=pzV6?DDSkmE)*g9{(sD9GwBw^FOZ=J+r+()5(H?o>f1;K440)+N^tiBDLh_bYyWEW%`Eq>}pDYe{hir3zt`hG%h)<3b)++lz1($j^4E+WMh8LZ`It +AL!^dPuBJ|-qx<&g}>=N4=x=+m)jGJ{#tDO-O~;DoyXgEPd8R}pmrYf5@mwNTE7$^Bkl6m*iZVi))-& +Cu0jKipJuUyvgXsP$tZ}hx!PxK;ec(z`*~E?x{W--^{%f$b+#!;2T7G(!1kW>ZV~XdZZ$xEWo +ejgeCoh3M>=eeL=97(?$L0BqP950SEjH_2BlfCkxIG?gUi&@JX2vH*iJ_fFcpq?8seIkiIV0qGY7Yk# +UqTo=-Ta+W!!D3Yr>XcG%ApW6>~GAu}bV}DSCx_{h_hE>@if4zDH|A7#B{gjn!`p>LyM%da~K7!kDYd +d5fnX1&PNV#MO*EO;O|+5W(qb3Cf)aMl; +>ES26Oo51~bSMXsxNO*M(I}YY~-)ZSEMEbzckV6ld^c&-Oz7XK*Xymh`0Ol?4 +6=FBBwD?cyRBSQ)#|1=Jd=j)G=qpquIgE1H{fe0BMcmOA66k1AP*2l_R1CsOK9I@B0H|I*3gaAx1*I9 +m0gXpykn`uHn%bnJkA0Jpe45$&@XYU;Ipw%k=lN0dGh8S3Kn({d#^S=rg|?Ds@e^p@QLyXz(Yl_&+n_ +2b}ud*?!2ylIVu9NCYNe0z(K4-5xJ6Ou;aO!=FK|g4``p_YOeWTblQ!jQvZwUAp^U?JigEo{YPiFaI2 +B6}t--@~&9lQ=#Mz%g{H~!>Apo#n`@kzfsPO^WoIa2sd=Lm#q9M0lS@#y;0r<&-T*9Y)4W^y3a+Ddro +3wlp7vIv3(c{?gzj82!d}9edzrSzDo|buiI5X@qfd~+ktEM#rj*Q1@F*;{AQ7_Gev4%#=Am-A*4O}g+TBt4uf22`dc!-Q(e2ye5lqg2?0%sfyYs8~wL@733vlExastowHo+}jU +hhJ^jl#Ws7{8jlcK#P}osEP+)a(s{zV-+D*D^Qvdj_h{fa&U>?ke9=P<{S5_Hmv6>N0`fxz2xenZWN{ +=fAp4;PdPJeZ&aRT9bLsMLTi11BRM8*}EbfMthP}PfsDkvZZH}CZH>#4PmJu%L5zYctzp!3I^*97+%k +brD^3H91Yd!j=sBe(whw?z{z7J0q8uySk21NZJ2^r&}kxQlZSkn?h5Z3YH&XhPT +C0^gROVoC7|nkj|!XW&IZWS><0Eo&|Hgq8D&PBrc@nHwImtREuut?wTO0A2>QAlYswqamrb0M)8o>k& +!Q^>Cbzor1SoxQFbBCz9uq2bgu$sTD8e($n?jg-;lbB&x))N#Sn8y;%@e0rXN_42CNJg2g&M#f*XMe^ +UES~?sXPj@D{&HxmOzroYI_KQnA;h?^zy=^iGl>-P*(G!(_c@UH`lOu0T&tc)7x?($aT4jB2I}d!O1T +z^HcuGqrJeQ6gR(ey^pTO6Km0{iZUKk@vOG$GXw%@*A0|g-#cT&+$hUysYjt8JRj}*^lUxJgsQ=+z4k +y=u@Y!JRkE{uYbg|&qpN4uFHnZpSJy&Q9A +sqpGjs#S*EMua&lY2hEsd3^>A*V(oH{+7aVAv{n&S++f7u5~xs~EMHNtU(2dblu? +3);gaS&;lCqct9&hDyigz*#nehGKVI#(W`84Uvt*zQ-3V4t<~7UcyD5~9I@KS0>0rPF}yH9%m$t@=e$ +r1Ogb(;(#Wl>lr4HoAO$|+G&ibYRvQZ>y6Oa;nR|Sbv95Bqx0{lL2L?^>))5=57(7P^ksRI1c)AyJ7= +&`czyN`+dETmv(otX&P4#(39FBrRNg1Q3b8-PaX4~UhW$VJ6aQ|{P+kyj&IBFvpHy$yqp%=+T>G +{&Xr{qg7bUd4Br!ZdCbP}Yy|VpfYs#F-$L9)qG<9+UM7ML3%4<%r +viSn|*Hnyif3ZTz9ZON^SQ1Zfw}V-+M=M{Yu)=MJEFwLK*h7($;o;R*lCBqam*M?|1TJ{RD#&`#LH@L +oN0L_QYpG!B9VY347d8y7sWD#(R?JWx6dG=7jN>$=*THe6zGt_drv}y`@qog +*Z2bOSU=V0n{G_)pY^>lGwmD35Pgu7(ADzM}%d^2xm8c2D|Tq{|AV1SIV7s`0_sw9sjebe~BUgZr +UF+75fey2%->55qs$hLV^?lK^vaLKJBTS?65C?Z>C|{jtIY$oaE&0&H!S&SJH+EV`5JkVCd(56X-4wA +>keLp>I2hB!8y}l09#La?``q$yHaNRy08Cj>Kc>(o&0UTB{qDcif{GSI(NPoEJt<$huKX=Wq(Ok$fEt +TvFeWqi_w1tB;B6@7VzQl@Ebr9*SYeePx)qZCqu^d_dq4^fhsou3F$8LGpKwY`~I4LzGU +E^Uh~hF4E)n;{`r!D-@oS1b0EMENf3!35;Lq&L|<6_USf@zK5vd8Y7ANnDvh`m?OYJM*BAG$q@t;J1| +Xa@u`2mKir^ku9b4-p2IEXePhE<2`}Tyxm9;X=uW}&3S24lYT_TU8U#Up{WOLMky!cR<5%eC9EcR7iY +1bhZHD^jUT2qGd4w}~r+;ISBzv!1u%O!J&Jh;Tm^Fw{|(LtJ%3cCR9Nhqr;M-fld5}Q5hl8|F5F*zdiN8j>`XH+8@dZ2@s_yf`V}jL`Vd +}35Xy`62c+!(|#>^yvHj1-8Xo5(0nI7_7-yB&hj^^oxkmOU}DdfWS@6B&xt*|5hHI?>0m5YzBS}R90G-tE!d;q9H8`E;e^q# +O`avFM=^XP4>7P>Aw%>=20uKB#*_D>48$=(f+bzM_`j-vcX#}7nHLz{v(4F+d!>;jkL_kl`IsSq1U`; +W1c}Z{fp`r%W!NAfx)_8J{gy1oRNctF9Ko`@7eeinl#P1=dOjtx~$2Pn!bwOP#2`P^lKF#bwWQV*uhI +i?1M+Ayq405zv)qeucKPjX!XrH$~qUOJ>!ZyE-WjseVc*ptUq|vTm+WK2}{d1uIpPct2%K!G9-$!=}g +bq~b7Phl=SC{XdQr~W>+dJ( +Jq|ke-x7ZW#+pBQAZzSh$p*Z*k;_16f>NY;k_B1+??5?I83D_PKyGz{PXV1HROBe0GxgODx^^$s7LyA +DqmwvWDdp;PEZCcgF)cq5yelJp^pgLqFd6ulTefcbGC2=5xc83j`t{hNuO4Q&O{|Oc&yD6sM|Z-FRDW$W&p;omV +13_E+}d}&g|S>-@a&p$T!-O+1W{gFXkK)V{@8`RyR~q(eYv#Zc?w%$_*~)LOv#mDfGn`Pcjq6e{w$@bQ0Y_^J(B}^$#v$5>GUrwPJ$;=r7Q1Y|ht4yF+p55anduf;$`x^|=z7eoh0>)p6LCQj3cv03ov +;ccT0p-VyY0k#8J-K2*3pqz|P#sKt5}-0mgAx;{GeGl=yD;pRNSh>Ij$$%Y4{&T&P)2X6<@iNu?wbpD +l25{16NhY}Krz<8jKEfBXw?)aw^JRjO4er*|kwB0(2SD^ME+40V->S=h{k3ePVLP`ToiAEi4jRg}UI8N_}AY~tSPdWvH@k;epzF!e9hK1E*cR9F)6(T7=&~Ejg7{bt?iZ{l ++=o3y|c~B+5>`pJ_^>F07^+9ExcW#54pA3chv+e3vR$@3m3uj!oTEHX#TdYaO4`z2n^ +4CX%JagirBdbz?w7(lkjGT)ox^x(Ue`PBVVc>My1=$aoo{(m!NW>X#t*D43^CfiiF6$fucdxvX;kkk8UX*vT$7;ak0UD%$na& +y$=0X?OS~;WE5KqWt$+bS%@=9P%4<^hjecs)YN6-s)f3OSbvyo=yKqC#Zl3wOj&IBI~+}qMz<<7917a +cZ8*0+kL6e2Sq@Ju;R`{n9t{$vmYnNp9+)S8a3}l3vCJI>g1RUaSH!x?zqJZ(aKOc8I3x4 +s*eSyOudomyhr#2#r07u&Ou7KDt7rpdLa?TlQm +YBr!T?vZmYOL{*iL3vBAo)h-Hpga-5As}Wh4I#E7e#O}KN;*LqyKEwV(h@pStuM|8A=s`7s>;lJCYKfH_Bf`nde6dBUYW43T?3P +JBQV*!kNll5lwyf`~VaTyvpYF96A&2Ah*#J}!>oBQM}G*0?*5m}>VG)=_7LJsn|HQvMb +9>!4W)g1)%5m0s`>B};`{e!`M`I-F~g5#(h!Ei8(ZHH9fBdy?q5m3BtoDYe}_N}gSP*{&kOXZeKVCx- +#d}Wo~X~>EjyBAcLhq*eY14uzwn+9Pd$c +MeVdMChjhg^8z3v3!X+^S9)&Nd2y5Am#k0o!OPMtKbCzECfyS3F*{(6eXCcP|!I`VJaQn4~DdQPU+WM +<*moGqg>lf_{w>_;lj3;;tWb0MA7}dtF_-@uXy8jSP%b>8=Av2EOl^>ID^mU_Cpzru~!*BR-n}Bg`FY +~>ca=$tMy7CUVFnvu|9r|kg`}G*`_24V#e(D~(JO8?0NaMSEEFYP!Z;f7U7`lbNIQpxT5l8zw%52h=4 +|OK|(1D+J9r!lM=(#egSK&+NY*lw&FSYzYtp>BV?EoyViRJHRmjy%cnndXsA^1-Q +eu{A)I>}2v6-Iq`8a436IMuMm-x3l +`$N{wPHew=$-6!{m*DTO +97Rb9t3cEAhC7VSh7KQj4yA^wMp38k7yzfJYIfq&4hQ@`&kA2AUQ6oZb0SS%B&A4^&fM`0StkHEa~$; +^cQnZ_|Gb@lV0_w@>5X)BvE{$#2|(e6h@LbfkDXL2M{B%4Hlv>Mtr&@x^dWz%p%|( +ZZ=Mdo)e^;Qu=)U2&h27owxA@!`717tjXR9dnd8 +mDHQmt4uHkJ!@31=JbQOLiuX>`Ta@2sA-}Z$EB4Jz;%y_bz1c2`hjx#OZRGaqUK$PVbU%;xSd1fYj|y +tnO`#is{@a=w;k?IF@SEz%;WL$ZW@#lV7#+O&Ov|`JIT!ZN#Eqjp8qoRhCr4dH(Czvqd~2Kc!Eomgps&Q-F}Uzk0t@_t!440}T5!#1u;ejXOPJle+OU)Ee$~`kI-fF-LX8Gt~z#MT_~u$#x>Sg4^~ye2>ZB3H5K#A*k+AA^dOlJk*Z$!wC$vI}~E(b;Eg0CE@9_r}i2A +A}}jxzdkcwm7Lcoh3JDrWZ9@U6UX+R1jYGKU(WWKLr11W{R?Dru@zlFbgbyNE~O9B-Gc$!7srgA#3@B +O8}8D9U=^-tF^2i`lU-&xnhx(~T^%6(dB)cxZ;bq@WQLIfp`Ua}w5&2M*~D4l)6GhB_>5qwdQ!3GB>z +pdytub+TcNhupqgZjfA$IVk#BZDj?kdgoSNSp$%d-7QUT#as(u$0KlUC#l`dg?iTKbHMAccx1;)p$>; +A^YG{(6bq3Bb4HvhSeVxVnBgH>js`)pgLKcDA@^-KQNZ-xq9z{Hde0htN0EuniN)Y=kBIi6Mg1d-)(8*QB;5++)T*WUn##VzMXuA*Cha{IK!5Cp^0)QLKb +I#1A9R<7mPd+;Qm-$s`*`-yDn2^}DYP4&_$7grW)vV!A**R)QrzXoM$OV`EVHhbSGTxLceW#JvCbbxB +Xl52-l?+o62+4g!<yq;vO3+5m(#2mD(pCSFg4}8i{yKy*>vxlJIgINgm`Iu|^7a +lL(Z4yaI5oBhEn+$NapMbB)i`DRCoI&ZpCJU9sx{U%OCW?HL!+5)61dT~`4fyv4HE(GRALJwHG2ZGUa_=M|zezV;Ere~`-+l+t*8h-$1TL1+6a#fg`{ePUCF>OEblekhq3B7G!WX$_UmnF#j`(z)Wp=snd^-W?REKlYt+&~! +7y-u8VhaRD1?!{`zg$Nvlqsv%$BoC8R~Q|p>M(_K5TH7oc$t{ufPv&GM!_|QL|oL}Ir4raI9@?RO1g3 +|ML;S~WlAoLV2N2-SRM@;AC8YMAszL3;}Kx$p1}~5vrC+AW2w7q<7AJkAFUDz1+NORcyG?xJN|D&DE{ +ApP`^CsR}hN-970hbf{+xlp%H|FFcL*z5=Jo!#}I;mP_Vgz}2H!JpDFUgx>lE3tfd5_ot)81j`O(pl1k +oj&5gMm9RLZDsWLlyg~P->^k8Szeq!27$V_`aZx3vcV+{!wq31-;$Gu${2}EhT058ofyT5kghQ6VldJ +5i5ZUt*kXK++VWG8+ro%6Zo{FC*W7`$#(x9KJ7mP{|S8B(G&0+@JZO=)35qF0$+@_oRoV=r}1#jy;}w +ur@pcKeAF-XGSc~7A$#xnS{?SnNpRP`n%Df|Uo}&WRS(#Q{xEd(V=Z1Cmpz9`bwuL;y;z!o@Vl<%3+C +Kdqmy3|M=0{-p>A>j)9o>SDH<`$fFBamYOL^MrT%wp>;sc7&iZSzN(|Iv20>)34{%<>PhO{AaW**`q@TUh()ydPp3NI^JAQ7DYUAPG|tiDD>)Vleh;4Exf=9e?{ +BY@929Z>4PvZ7)84H)h$mA^6U0W_#lF(-^jwv=F-$6@=_DWe*(bUKqR`-{+3^jW&eX;|}!JLZWZ4Dq; +&mzf#6*Q3s~>Gj~^n_cmXKzBP*QzCz;NjEhSCn?U!R>=qTbm&62e}YoT+CV* +n2K>@8%*UXGPxQ9q={aaeKgXd5X2X+4CCT_1V3ySblwpWAbiUHUAXHw(qwn`aKPFf6CeZ&Z>aVmi4!* +`pzI4_)8DRpA4cOSM2<-hm<(ReuXoLxJ+({9(dO~Upb6Q06QL|9OA)4zG&B5B_QYLG^K7)gI+#iJj&v +zDLgYKJyyw+5>YSbv~nF+Q^b&KNku@oB*uMtnEvUBq60(PBvuyOVM&h~Ehe#AcPYe0bk{vCpAq%Lvp+ +ryfza>WXbn|`0M_}Wt`l{y;L>c|q*=9AXVbfexT4hzl`b8jb5pq+3|)Io!v(E9WqsfJ;1Rqx_UQ#I%G +HG(@N8qWUE(q>_j2ds+T%E|x~j5mk*B>?J=sz;n{XyKEVV3AU$NT<8c7by0bm*_vO}xj2ekHDM_|D#? +RNPrU*%W-d_Jcq&7wTl4ZKRzkl2qB-rGjTg@t*I=U4%p*s1R9Q>6zRrNpcH%5B_u2$P6~Nd~qCq}O8F +9Z%ljlnSU$waA=4d1+&clgNl9cIjV}c(jhqOO^`N00qqVm8$Xw=SKS7c9XKlNqzx6kym<@-5wzsaPTUn2e59$Ni +MO6)mE9psLxp#E`#IMV~C!TanU}@lkq~$-tSn+3GqFKm3JV0|+SyGh8J7G)wHb7Vz4qpqh$H)Yz$@a2kF27C=!f>Fh9XA?$yelp}bvbo?nr=?Ur*@7r1wNDc;Im~rrwqm3U*V +;+%{IK)Q2`v}qe|PHsiY??WS-?56&$!MvL36HZmt%n(+-*P7lz36$hZfczCy_rsHe0WY6 +VHJ#%*3nTS&AG%bX9}Z=YZvhwZUn9MAme)WUgL(m5=p{g~u;h7Zs&Q%(f%dMpRea|Hq}8wKU6MddvHx +kh(4%?xA#>+}&rns_dAstcsha$@Rmck}JDH#?JSGJq=|G2|RXf)1;SUxDaw=I&N&LE_5`37T{Z=I(Mbt;I>;qh>sG7%r8NA*0&KrBq%D{J +Orf@5gcs@8{|o!k9At5Azgu)~5b>8brA4x9*z)w9MTPeI=3QY8?kDf&TyNaBtvP-7?@O^AC+?i +|Z6LEzU+|rK*y(e&OPljzuSj`&V7({&`(1ltr<~Nc@)DWLPSqr?SWNJrLbWoqzO}R4T?Jj%OJRMLQDg +NsMD^EHqO#J>2VjzF!L+XolYPNRH4vVyGTJ>nzf*jLa5E*QbFA>Ysk@JHduX_8Pel@WH-n~j*BjeWL+YQTWg?nD{*X@z{A!3f+lL|%? +BL1>X=5CY0Q6PWU4E!I|Cez0Bh?l921jjG$Yz1ssDxxDeWl^C>!4|*c>&oyL(UjA`=S*_HY}k;JjdFf +Ho(h^3wYP*cDd9mD7I&%#7Ev(AYr?oPa&%1+94=G?YYj@p@I6g#lM9vEJ;Oj*$uGqDq9b=XaERq`gwO +G98YKYsvxR)Mm&YyQc^FSM&%&nDO)_yiJb5BD>Wb2lr2*d4iGh8GRc}I+)xYHJ?qg{fJ +51e~j>Q9j9plq#jm6>?q==%7AXO~O{il`|IBRRQJcBN4&DGY1EcQ{`mxOF?QJLAe2%p*vxw>m+*8qHul0i4pxfUR!+Ln#!lp^2+SK&Yr1Q;3g;&XaKj{zCZk~qj|t4IrKMc5=-!5pjXB& +@?IO8ASN>r?yaRsN*=rc^=WV%s{pU<~jiJ8W7*QoXdj6=vo=#WekP%WA$ke$*zvz1n`pddggY;#>h8b +a0ou8L}#@UvO^-bRPN1%_a>fW(aYksL9@K>FZd@d7}v{V{8Z%_y?JaPwSY0ouauH9J>y2*>co|B5!ou +%!=2qnhHUvsY|{uJ4JnDCD8?ksJlHlPb1e816|yM22e7S<+&F`1PGN+Z8^ypbzT$g+|jgJXr{WXbA{h +WCNhR}$j&%LS@KdfwoOyGm{-8#h4~ytE8&yU&ano2M~OCaYkD~jl{=KVaZTcAcjcg3rY3P?#vM=|^F% +MLy33NNz+D)7;VL>vV3fBD9U|4j;LkFAA&Ww89Pji1H}v9hJr_~>OBfs`2(AhnF?yhHOc4a8lVcI~>5 +6v~SK<1~k^J3d3=_Wi=QJ>uvf<#iGRW>Bc4B3Nk-wT?{(N``IxdZ>2h93bD;1ky_M-M`?dx3})n#YNQ +0Sni#B@!Sn8v43-*hIrQrFW%SrpKN2@g7>SRMh#wS!K*?>UVUIWU(H6Nd5ur6P=tgVCSlCD>y-#+BmV10TD1;A^0)P(Es|F(7|+lpdKbk0}g-1VM^9)=(&p!A(GLpr6EUikV4WM;k0TY0N +nHH3vg=yIPOCnEMH_{)RqQcDG6pY#QHDcUy@!onk?yBz0xYbRF0l6r$!n)gq;VL_1HrdWh_+THW_qbW +N#aXc!A2A_2qB`J1b-UcJvl#|v}Q7N@?ni7&co|*+v^D?Ng1Sat}0?FxHVV@WE7+u4%dum8Wns2VmkC +>pTeB8v?qjQZRE`ew_C8^aHw(!CVZSY;KMO6iL9qGtU;wJe}_wO8LO4*;X#1}nPKU9SEIES=4$+xU3l +GGpnI)PldTVH(P5C7R_&NhD6woy=h;r!13Voq%r73~-Ezs!HXHNLHve=@!Bfkgh>_x?^KKi>DJeLn&q +_`)0sgd!;t#%UC%F>+N@g)xLiX&lf9Mo_=3V|=BJ=xTUJtdnDF1BMT;gbv%BJ@af2~4<@TyBMOQaBrY)xwv5ItRJw*I52(DA4y<{%I(`dHj7A%4ZdnZ%!pV +bQX!`w5$U|foBS$Wpgw&>JZD-JyDaaygr^qTzS@nAuLviM~uzTiIghUK@MC=S_O2RD8Gfl!NsyvlSP9 +y>+;FUHwr%RFbTlr5J^tFU!XjWUk|E!YpgwzIn}{xM^NV!`lwp>sRW4#PF|^JVcN{kIHKSN7GiTpLHv +=veBuTCNd<-B91|({+Mt7% +Xs`RKj8x^f4KY4wj>k@Pzu0Heve=&);Dh`s +rlK5`Q-&=vPb5-w6r&7}H+~>BoLS=uiAijLVH6yZK(ZHI>=dORoFIc;8|t(Fvqbv)Vq$=l9Xhi54DN +91&ECv2hAVSu?*J#JokNX)hu(ZegPE&}&E;dr_0?k(Sd#{#taa?US#<1ViNIH-_z#4;A$jQ|^_l4ibNw4k +At2IXt5$Y{ohC7Vq9_1KTq$doe|tZHKup +FSjp-20O(wdZ0nIA4kcl^Xnu+PC-vOd-W<_E4ej0p!I|QLW0hc&0Cq?q_FUcJzMRG>|qNRY#eT^4h`r +8&k2WcC&9xfom-MyHqi+h5pD3iAd^x*$Sbp?rs6@q?~?&Gg|lGH`i?T;2h9_Z)rLrCIb!$o#M>WsNPJ +7<<=y)YLj!ZMN`UpIQsTFxS-&rew)>SKihldJH>Rw6W6-uC#y_91&Ri}Xvme;m=^R56@>fHv3%}tR6q +83gB1>tPx^QSjmY+DUhc_$MJI5gRU9c1)??wv0r+a(2CP##ysg}8vN}h|j=>pzruP+X}DOg<3S4=c1t +5Ln=mMMp!f@ZlX{csY>Km;&a8UT&3I?r$W7bte=^Q66y^8+!ixDgQ8P$+#Q&yjr6kxLqOm|{SR +pJLTB00n>?aEQ^gmnI%*p0R5c)P@uhGAx=xa;T}pZK~CW5)+((+cK|1r?|AvENKIWAJ2ES&rY!`jIXo +}+}Ar3-?w*UlSp}4>rY1k<@NVdLSkmpRiD8!FNW|)W0_-7?V=zK>P{v6xQEGYZrl81 +4POJj*sdz+j}B(BZNh<#HG1ltge??RXa!aL#vCiu48b};RdIbH3A{%pxmEqS!3wV9h$- +2K#I?>^ylyg?nZn9puuEliQ4nt@D;RuGk=Dd4Y+Df^q(b%Etn8U#esskqVB$NPEedQ7+O3#u69gV+ri +u#KaeiEV?qsuyGnq#S`8h6g{Dj$uQ9difH0gpUWzZM!qlv7Rm_3(y>C<*nk1F2Y4ZT9e^OFv7@JuIJM +GACScV0Z>Z=1QY-O00;m`Rt8htO%QRh1ONbJ3jhEc0001RX>c!Jc4cm4Z*nhVWpZ?BW@#^DZ*pZWaCv +=J!EWO=5WO4lKMa(^I)$xE4=onhLy!h&fMVC!254_uS{&I-C{iVPs8( +qQfrybN*Ds%h@eQ0*S@|*Q)y()@gy{gd#)u^&NuUG$$4=+EQl!w;JUL!aRy)z!d2|*bjgw`S`Ye3sN{ +;&D?849U=n(^>h{*MKM!QQu!xnC>Q}0vynnugV3oCzN=1j9k|{YB8GCxc*214ma;<$hhAw3Xr7g`rlmw +6Sm-^Lu%}q~+-}F$@@r;hx+A0URnnpdV*|Wc_XKpTWjA*+C|AwyKRH+*w-`|vk$@OflCzu(_H{5~$krRCJFV +30cf_gWoKfrt<2oH}USk_M=72rnX5U!oRgpeUTE11o^*&T_kiQ-(WM2DIpjVwdLpoXC*G +Nj@llChG*fFAgan~Ev;M>=-o9g(zM9pysRFTgv&RR;X=J0g?Tz}qdhH%>SljGFy!HEd!lKM*9s$!L%v +XJ6sGT(o4~g)g9AIn0CzHzJys+&gqY(+#nOrFn<84_Ye-i)0vA*@MT8&KGsd{+dOkTvd*L$h267-|V< +nT4@hlZfnDZF)xRd^V=AxBwZ{~1AmbPteR4Hf5?qm>oMo_2$nLG(7jI +8t7$DtuL;s>tLN(_!q+cI6Ch4*nN{&_w--lOb`REZH&f7~M8jVNNXd1=fO0dJdMekoHZTnKEt(UMI65 +#}V$pVyLawSkoBb>iKTo+u1q7X*g`SQ)@}XrVrOE9H#C&_B|WKY482eaCgpGH%{fDK^y9kgQ%Y(Rm1e-S-d}o6Df +l@_oNUHom2Fdiu!Z;OJq^=A>uKkEwyy|FkusPs1?1jQHG>5KRENZ!P~w;LsONtn9JUywZQK-nt#?gw|?#%ZbEI3s?K;k(ul&ANsqJ6PL7Za +fqkIuO)}YGx=ZD#zx28MzuDZx&l^5a(g7n{++@O+^qluTT4grP4~@hB_@ew_I^LCzPt_`xn{T0P>(zf +yO9KQH0000807zB_Q{`sUSbzio02&Pd03QGV0B~t=FJE?LZe(wAFJow7a%5$6FJE72ZfSI1UoLQY?N( +cF;zksHUupltQSxArh#R(AX-kw!Bn!bRmxxQFD2mL0k72rI#+@05pw<5Op78|}AWbUurBa6%d**!Kxq +WBw>FMzWSFIbb4_)uN)gQIssyn#;4PLQ5{rKS&#!Idt;xon62Iw(Vh@fR?)=c3AY-9p+CO~1pwNZSQ7 +zA#BiQp%xEWt_!Jlxp5LTROBqv +YOQNJ?*F1_~hLC6slc;7X$QbUtac3|YL$iMmcg^9j*&p9ukg-`xI>jfLq0^EctM#_fA#tuG)iXfDc%+N`@2 +Sz6f{X%KvL~d5&yhb-hz#a3-u$y>Mp7)&qzO}&^#XVZLYf+^6(<=PdDv!4Q+B!ro|wBe_*^F|CcZ_~i +4o!3JS6URB&DjsmDI+X_L>%a{;6I+Kdb+IUI&#Fgp*pV^8FbaR~9#JC58>C|rM>C{d|_F{);Go9{*nu&~NmX1kT`<)LCLdp2%}80-@$G>HLfDSUY(ESuKhENm7LMP8Q&4KKVLGX(fHY!-m3zIksY`TvYAT +}^1v#sQiWejlAhQ1dXt_%?hIR_&Y=6XaoCi?k686I2>LJ4^Oh{jMTm*8h%j- +O)vKmDeSc!R~!P%EDpMSCWu4$gw+I~G9qr+#TSV4HENvu4iPmWZchxSBwn1f4=R7TrxhKj?rOg%gMld +(flHfzbWzvjYE-Ms=hQtT}O<>}vfeZSYdwGFKt23Pz0ae8a*QXOeWE=`xkF^Zt_{kSuy=#$fccf{tDv +LwJOa(xXOF|kP4Vh36596|i3zPRcl6qP(9I}YdjcU$(~q3J`1<~}#9!8hDoMr<_;SYD0>oMjIWyO}(0 +uY#QCVcW&bnr8biP)h>@6aWAK2mnY{22;Ixy5{FO005YK0018V003}la4%nWWo~3|axY_OVRB?;bT49 +QXEkPWWpOTWd6k`Ak7d=7rtdBJKWxbwjgb=A>w9l|+!A1Lmj(k<6l!^HP*IhbR2P+WR$)Z{`ibX>o%T +qLr5P@gtdr-gy*^?^yz$1n*7?nEe)+%c!;k;;2-rb^`Z@0Ji+ZR8-{qFwt-{&DF?bY2k56>TdX8yi?xV_!p+<*J_r|91Ax1a +BS++I9?y*=E%ynB;RhzWVSJ%9c3{rd+R`2POo-M2sU+K;ba#;EA~+uMil-#kv{pFaC)`}For^mqHy?d +#iz7}FO&e)H4!Vp$#@Kl$E2nTLN2_vLo?+J?W2Rfu1r$r$WUVP)Glx7&|zZomEU +)eTL=o40>^^6Xzf|LWQH!DoNj{_TUOPe1tV*ggAsfBEry+xF${?c+o{rN1 +=Sf1l`Yqs{N{V?JNrzI}f8>doW${}K!RCOY}*<@Vk4zum@yzqq{%1KXa*w)x@b|G&k2b^rRW4mv*bk7 +@n=cK7Y}`u^?B_S3^%m{FMGe_3Xl`NvA%#4dR8-pw{jyzBYve}5G#@nwAele=%D?N46a-#^@JAKt%t% +Ul2a0UzFLDf_#n?Pc4(`jVIa*?-%|pZ)Rw(c8of(e{UTe;sU)|M>Ct^^364=ij`t!(p$_p5OiS{Plki +{}Vgo`9t*ci--H~?-@kw%^zRCxO>Gr|HpT4-~RBs_uv2Nr=Q-tefi^iZ-4sTQQrSPKJ)PYzg#O(>alI +_zWXoR{$8(j7GA4kKE5{AF??;5`uN)F*K1|3{d(;<+tK~(-jD0GbB&{2k9qR)7Vq+C=5+KalfM1ovBC +F$Ti(5XeP{baeJ_2})9CE|_rLJL*YU~d)BC?2eJ}6EZ}0qJf3NK0afAK*L-@>JeB$$O*c^5IH>@oCb^ +kgx@SC@6vhRO<@!j@axILXq_@bwOe9C%!_~cLH>t}!Y;>kxJeERhtpM3f3>5~t?diLb=&%S>8LHsYrF +Bs-;>(8J5@#ClQj^FAT^~24w{jC6{J4p~p7G^B?Uy2NwaQF?r +;wVPH~{1(d}|6K8it@oQSZT?!~qi2sFi-^PB^>B- +jP(Y2Owvo-!X≥tCzf;aob8~;{5Fo8XmV#LXZ*F*Y`7mx#cc7qe=PC*R-#YSey(V^)_8A;-o+E!P3 +`=(AHF%dGGgA^eiq)>nUAaC8HOA4$Dz +k=^bO7F=b~=SuAqNhAWF#R=mESVL{GpVE}tfYB{E9mSTtpD-(ThF`jMFi^BAbSb^FW!#lM(rYEe&hG#5ZOfOaumReU#U}-Ta+ngFn4B +(`9`x(s*#vZE|HrE*$#x%lk3m=OPIDj;_!j3Po?=jLIZAMFB1(*?=I}9ZHgyHTCrC>`f9uOjO&W+bzv)@n!~4?cT8Ay0dtT-gWy# +CuVdkW{hFST9y)X9Lux#6mRKnH74(fMvhqI*uOZDSo5&-nGM%iycjzT*O=K=W7su5P_XS-e7a3@)4dg +2I{sSdVwhn38r^2K8UsCw`#;UC_E@|aPM9#8Ha^gpyI8Ff3l&||<{RtIYcajiS#~o$o@~Uhbv}j(#>2 +{iqov^yix-}w(QtHZotW^9N3-c-_r-z^w)Al9mFM(^*NEQ^I$PL7PU8{w-Z4QNUhKTi-dy;qla(zK`; +0Y;hhfYoLkx3iOmu7{>^)wKcWC|xD;Pr=(QqvM)$kFE7w;f$xTA1K=ix@wM@LJs`j|`Mqc{xqWn+WQ$ +*SUACYJB^sWqpf?JVAmF2*2OUWV9|Od9B6bMaMK@0tLRR#}H$A4H^0X_8`p>?xF(+vrUx +MZEn$l^CX~DLtyT_HYz=CyiQn-4I3_@Ix~7AU8z%vcN}>8PMgcO#BZa!L5w+JLi@F)W5mv}>mI9cteC +bDj|hW^ZkO;M;fe=+r(w*s5OhaON_Z#YSS(}wv+=`+T@3@)nCf^S9XqO?2>-{U`+;AW#A{;v!gZ{Jhp +}nGq?weH;I8kAo~rh)(;Se{Lbku272v2GJ%WL2B~hwvXE +B!&`WjJ_9~l~9WKf_I@|0ZWUSZG@rNpoKtT(BE;T*qlhF+h%m2O`RnHi2#iWS2xmN8TA@83}6YPCZMs +h^T4IxBBw5abtfVNnJ_XOJ!aYQ75JaZ!-57&M#o|nPW%@enNDPYK6DetB0K +~xvNx>vu+70Aa!YprON3x7i1@Y+wi;f%IpwU|05stI1Ak!Z?3{roS+rgzUO0LY-boCQ<~9T@6?8W6WQ +BEK)7E2jGeT4NUL0erU^GVzkJW10#~F(k7!lLxggZleOYAlPER9Zd+((4M#*P3>N4GnhJDx)b7FPfzS +IkzY$I%>kEDX0WjnNC8z}4ff+jLuW`w55wk9H;+(`XaGFd9ROFIvb5s~VCn4qSS_+9y@45yfsbg@>2bx2wy>q>BEWgq=J +0p~K5$l`WyUyiDkgv>OokZ)Qmmn~9eheR0SsoB6qfcZM#Pid#Pl|MK}r!1iyeti8M&qBFjW}#&i86$_ ++tP|p^M~2gP0hvVV5G`B;*GK@Q6%-s~CpKb<*SoABnJrmWP +M;KA7V2wm%0TfASR|k5u+|mbtWy6&hk(dx`Ev<^x(gN^_#^H5pBxXqXLjw;{g)A<-Q*>ospzvre9smI +oj^7yAB0a__o0^kQ2mUgDBGDydI~mU!qP^$R>Vr!Z0Kqbz-v;6vS(oAfkEMZN#uj%p(0LtC{P7{A>33lwkpw%LwjP+@5=gP%f5F}EixL6Z^I0J%VH{lW@JAzfW!=onpbjj%CPjwdmC}9vEOu{>=NkH0%(XfF|Csw(U}y<1d+C;O6o#@3*@19V=sa>qs +I+kS9J8?Id99mjl3NiQ&EJ=;a5sO(x~svta;BbcgMG;-*eEli4nx+O?D|7N4Y{>XVv83 +PytK*+_@Ny}Iz`KO-9ku*9c`uZ@`{btC;K0u~%j{3tXmgNEyYM`&_>SX$Z5CKkT|^vc!aVV)QLjI@)8 +4qpPK=~V{MNdYcPj^H+?)A187VvN@xtXz_T~H*kr5^JUft +Y!wJQF;IaCd4Fg!h_+ois*9-hjKn3Umc3ErAHmL$5p5!o{w}2ty6aL_z=CaZ(!wo`uAB%UM47TfpByq +54CmMIwg-)mO%_)OsF*dWHuncu2&VkA8xp)P@Qiz!qLNT@_K@Q)`&Tl-i8;b_67;<&MLl#)lM(R@V#} +ydcmh54M|9BHIZzFHkogFgCQ&x|(llShs^q7xedVmz$Tp)tnjrj1 +HTqmr0bq$w@ltHHL0AuW&OV|Tv+uG8-~zIl$g!A3yfvYXazll*+${J29omJ2$RG%&SWBx88-S35h1CO +QkZ)`hZ5l-TFg`(hhY#8DIv729>oqe+HY{K%l1&Cl=4pXP$=^2X8`Inq_r6R8rR4}X!L@a1!&@;nh7!VL#^$aV*pF(z +HVN_qZdR9p1r{cjAAYh58!LV)gtm|{*`Y5fRqwL}Q)b=(n%x|8WQ +d_^(QwJJDQE?xt$6yrVCTfn%(3>=3Qo=h4JAb?Mj#y@uIWc-!tREK>W#XXSyNq^h`ECpXP;OIO2)AsRA$uhNYT-Qkg5(9E#f<8jjRB61z +#8ViFnal?_j0US|*hxN#a@=mgM(a3Gb$f5`t{Y;3UzJfx$oY83NCtQWUgPP +;wa5ui9}TFD}ucX7Yy_JCNKOCPyGnt%l&H>Lu_2AqQMDJ>~oQoJhH%K(OZ5NtjrT+qVRg-8^73h!&ExXwLtKLm2J3~`Xh6Q2?;xk(EZDd1O|J-S0IUlAk0O!TL +tHWDJL?`Jb~hU+AOlFT%4CiSK^YY;U(sb)3PWs`WlS; +k%oaJVR8$R$r4<7ZLTBzvXcA1l3F}Y!liVO3z|vTN0oEz*T(Dasjuvq1aKqP3J884)F|o)=q*=K4EK@ +VaaZQB`%WyMUKAzfw$)X7pTWvUE3f7cmD3AnL(*m4XlX>zAfG-e-hR5#Co4S-qM0d41KmrmQN~BDqiq +6iVVC5!-Q-%akhu=M{vi3B$VM(aC?lKxs{!}^&1X{H$yA`WBI1KF_5M}9`G^Y|(n!9N?wq7uHG>frhv +H1SBJE?@6DYhqTTJn3O9|Vv1E8cBcLC_%ltgFu)T~25KSZWvIk>cTEV}mmRu))(%kK2UaDiAJ>x!=5h0B{s)k%b;d6fyc;4Fgy@DwDojE|o{X*)4p))Kc-*RC +#NWV<$*aA87h$fMyJZst=jvUap2^SY+2JkqrqnvgKHOkUH4Sk~C&8ic~3%tpaxu(Uh~ArbxobVdl=8Q ++kYicBLKTHsNomX$|Xf4K~FR>>wfEEX56X6OU**JEIo?<+vJ74GYeJQc-gwpCUFK8z0zYZlS`fwIbPr +2}=O8)so9^#wT#jOk6PmEQNAuI~2f{jLFl2auc+ +C=Omy&f-@frdSAS+6qLG&C|6nDBCu7;P~X7L>Y5zsDtYc;>$*(xUqBx8rz?kIs8?m;Iqm7q;v)IU_AI2bQ(O&Pvp)!1S|+DD*B3Up^-SM{1>H7q3l)d<8>2h|t62z_EV%UM!VQpiFfrYG;dd<#r6IGl1pzv>|2y+E6F`Fdk1h) +*c1EW#i1&tNy=eZy-E?6AXAZw--!D!0ipQdo~4+2hlaP*c7K$+%8?Y{ND}!yqX*)JnHE_IjDA@(2#R*`kgq+wV#;p@l-$wuiy9_aL2aJfNHHxVXif6E2D&^y9DrZGvUNP^KVIduByO1PRa)syP +Yj9^8Nmu7+8GxeTF8v7}Qy_-R9jsg5FI;iTun^^&o>Z?<#IjJjFkS;byjXiXVcS^CDmlBdi$I5^frA? +#r$P-278JY3jB&9>tQH2<Eu7p}*$Xi6ilhEsG~)#l +gv*jnS8`;JrhX8+~gn<))Wd<6U&VP3LE;tO_pPm^}*Y(9Ew$Zr-y(9D#g^>{x}sfEYKqHDN+D(Ee*IFmxUs_3?0=^eFZ1<+E9qh!Dbh#P9B>2Z_dhW@Boh@ON7g?)Af$T^bct%`2jAt +NCvXGMfkkAT`eXc?4@trU$6V^-U7K>p*H23dYB>Kl%$VMVt9o^3HYwFhk4j_RjZYx*vuk|*ll^6pN+A +!?AYA=03uh){WE!vL1HbUnLU)z)$9Mw?7U@ywLXz)#&ph7PW~IGhR-@Nk$$V@x)zh6M$De1LVM=268Z +r=-5s95y2R5>B}dvws{0&}W#Jr~;~q26FKf-S%qXSLGbw0=BKv?c$~-FDVGm1{quQU7LXqL-8Xe(sVY +;|1Wb>blbshg$+jDhe1MiQC3;PA%Hs--GYm2FdxHG4Jl1Woj9uUHSm-aH4Q7eMJdv!^lj7T@rgjWsT| +n~0FV`)yhFH*YPxRq6E(hYa_s%EhWlwS8PP-WMW?5b>q_>D~PxjPllyS#h-G($S8z4MA?F)Z +UCN6*f2Hk< +dJT~ppJol{I2C_Pgc*Rj>CHPsZ|l4ez$U`K{J@M#U~R%N1U^oh!GUBP^SlH*jA)?vSmk6%z-Kbz5y7y4R>C@rUJmyr5pHs +k$1ynstQmLP9?T(5X*%r8`azo?tFJ|sGPMIyajw|!vL0!`eW-#{Tch>sRtFNQ-`D5q_dQ7s(I_7F3!q +{x;P5kAo{vrR-x#2YgTlvMG&{pku(;+;VfIzqFosX2HZ)7UaLh;xvDYpuV94>u=Fem5t`s-Iv*{0f+j +^tU}oYQG=Ar!fE;2Gd+79s@Jx>>Uzu~P=(aXb#bA&ud8`Z!uNfa8hHWugiXA=tV!@j1-AE@AN%fd&g{ +xsjw;p7G+Z-)DrWrI#aj;99f3O6?NvuP_BDV-kXzpf6OBNXbWy8s^Fbk@!L?gRZZ;}R20~|SbuGT?6V +H)Y3-NtZ6vlt3Da3=3o72S4{u-a8oVo>@Tw%@ScyGv`HO0LLovUL)2t0WcNNC9kujz-q=s-oLEmHw&z +0L3|T#J +jVwS(#;_nJrmibKUQ>G4t2pr*M(n!u`=1*tx6K0>D_1^+mML?dqI;71j7ySW+SWZ`-LkM-F9|Fm3{@E +7xqgd5kU^eM~R5t*5R%d*djwv%D7S6pyJkcN1MD`Rdibre|2@ZsEe8&d(YDJBy&Jb3^Hu-*a-MZu`b1(HJyMOqkM9l8US%<~8PV~;Hf;m-&B +lODFqDiqyrGo7um2slM9&MI^;S=ZwpsRh+i*m**5wjYx +^WM`I1Ve)QO(d}qy-&lpTL}j8ok6?p((wO5ZL$`}^wh~n~gRaM0Pf0#=kfhS9qT9;o8eV7M*ai#Lh06 +%Kdh+(fgOK-q(_&0tAo{#~}t3Q +TLGE?%u+au)d?o(PK<1WLid_+|r6}M{}2w5@A&hq`VE!f2O4YG|BjunUwpQ4WEkm!+os@u~)~{uo@PR +%_?Xo?i5+YB7=tz*RzeDis7(FfgP$)x|I@WDatU(4AqrfXKt#Z+ajSynW`bGELGQ`#)(3bbvlYmtz@^ +g59Vk(<BOVO<&4~~;IIJQFEDM-P~k_=DG1V}!eQjLbnjG{)I-9YZfG@Xuq5*>OWzD2At$XQ1=$qccEun@7EUO1PgfAf%b`faT--yo9@1m +OFX3hP%na*u_ycx0y<1as8-vBZ*?yZc_Ub+WPURC_GC#^5f~WQ<#&pW8E1Qg~DBG_d%dn6XSBTg;%p+ +WK6bz^p+Awp`m5P{PB%5cI2;WC$yW}tC5ZB@1nHb05YYNR{}N#-yTJ+t*s`Iqz+ +if*fHaK?MnkJB6m;&5rMEWV`yc_LaWDFS4oB_lbI;z1KjR5a|dW(B?;pM)r@MLFVO@0@5dlUW(@37cPVF(!IV;-*)Ab)SLLQ4le%!pVA@)igshsec<#6EZ +8+YDU{uPiIyQ11{KH5%S~ECY&?ukunFh)nKQ;_tsg?|SQNF_FrXthzn8n|0llbKM;AIEM-n-W8exu3@ +yxWuUZYjF;G{T-22-zJ=#Uxi`mI)a#qwc8#52Ycc^wgyUlG0(qT5t7g*i%Ahp7F({0B(+StURLHx`QP +u6^pTod!c@zS@IYy#7x)J^R!!2bXyrtwFpKTqF9}`I7NkMz~IM6T?T&4&?B>{bgcD0HpgSqu{$k`O74$~hIn`eSr0BMYa}(fFy6A{=K{P^c|9Gd2JknAaR1?mTaQGBu}Y%|V7%+-lT +?&|NM`C$wr=H_Br1g{AZ3oXbv1!fveO(yu`*av!=XxR!z~xj?m&_6wq)uBN+9TW^8j~*bw +jeyA)!}I)>sQbvS(b+yz8bZy6xoH&7-);AcpuF4e#dUpGlvf@PkG}0J)ulY@L-dDi695q}GM#72S?*5 +#Mtn#j=b?N5R+D=}&Ki00pWK>mF7oQsGmSka_jiF!jk?x+%JC)8miqBN&ag+_Vad6mKn|3&lsBs<)y{ +gOsCyrP!6!L8Dy_&%B3)vtzqPM%-D+A7RnehjF$LhcS%9ZZ%5E7W^NtlQ03WY@S+3p|)cr=Hw~*mZDqO%LcAMya;KFH&MCbZD*-lP!UT6(s{#TtG^*@>7- +rVMB*`mr08}^A)t+$8ZIZNy9k4XtLIeunk(RM%(j={kW+b^Ro9~CH5HbGK+vNrx~-nF_S6Ii<*Kq+){ +Std_{sZ8HgwY7FF*=kC{m1^kD2DC=ypi4bE0|hqaX_@MMW)@P6;1w@hHn;24q0uaIcE-bOU^YNOxR$o +}$~$yY;;H=#2q?rocn) +h0UkT&g_Y{OSMYomUHM~lVw6Vgx7Ox0PCJ0Ifn9@;k7yY^Hb~n$;(Yhdlh-)g;uqaCCUJsC=$sV)#hh +ALtOvA;AayR-2;l2)PUc2hHU*b-@YH}922Uxsz^1uyW)bPNw6jwZf&r=ZhQ^n$5uYAL$Sn +-V;0R +tveoh855XU#{02h0Sg>mr3lt^r>OC&#|6PUy_inrC;(uDFl?dDyi$g(@0lnosu#pkblW`d2&df;434h +G01Tl_85~}l+C`9q``W5?Q&R}808kF4zvD{B6y0uzBG>9<$(WNAFNGt}Rwk;@EEIyewE%3P6)y#`7J1 +kvdZd=XThVQ$)Z7k9q`a&Um@>S11{0<(1S2pAP2GFOtbU{-jhZ?_r9nd6%hh8=w_C=YA>(Z<5lXUTWO +t~j2dQ93l+>HT_2J08ehMjc^(_ZKa)T(kt;;hvURVLEr+A|0^L-*&Ip9Kp>`+xokmB->-lV80iwyP97 +mQVO+dWNAr2<(5@9Wo#)FNhddZ9_BW56lavUuu1oen@?D1@b9MYn+LT0PF*wMBe^8=Q)h8|STyYI8*vBd>wu)VP%_Z?l0XI`nDK3S1yIIKkUIY=tzdU(&yxqTAgHxtXz)2t +LU}Ho%Ww%*ni%hogC9Qk7G2oBSSp`WT{03!nQn$CiYK$JAk75fN5O3q@wEx<*x}&W#AWcL4M3ELhoDx +W&WCBe}^9(o=M+$MrHQkI~SbD&ysCX=ffu@F=WGQ%+NB%&^7=cTV|Kq0}Wc>56U}Gu};0rn&_qkjxsS +XO{9v8h|9lL(i;0V|qJUy&)!PCWWcG>56XKa*mk_#TQDSJ+-AA=+FsKAtKTZ_H|P=GnLxr40_)jfaS8 +|6y1{g_2xM-BXafd9NGcGzt_hgi7QCJdK2hbXQ!oU0S(KC+3qWOx31{6A}Sw4kKHvbwyKlrUAAl=_|8 +)DIlfr~)f2I_UPGitAR|aubUU~kVt88#)w!Ws%L+I6r%Ugvg^=YC>X-cCY5~t=1V~v|7GrzfbUQpUhX +RAlrRTBe<--E8c{9;waS8WoL2-DiBw%ZZ6=*%f$hhV>H7uUbF{5A~dFL+vye{>}KrHk-o!U(8dHoa+3 +COAtSG@@O%-XZ|-xb|9%Zn&ihS&O9Bk-_SH-Z$0NCbeZrD!|6KSD&W)C2CPOsgs7YPfn$v2l0^ec-vg +mv3nc7&yLr%Ql?_C>7Vjin|A#k?C5_9;uD(if#*lg|ZXxrPkfl`-=u>Nk6G4LsjK%pn1^NWk60`^9B; +acP8mw(QR+pJ9xLIxf6@nd6cd;b+D)L8Woe07HFpdFVdm$28rrWU`A=dhSjjB6jMs2az^3DvdU8Yr&E +bj?Gl1y3FI4!c+R|qnb=metu!}9x3%ZnNE_WM?t`UMXBM3bs@d>}wHr9S>xyiX1sR;19UPhM>vBRVy2 +Z_GM6khe)GosV;oC_&%jxv$jvyleTf?KRTSd!ktqdT@fM7%+;l~^MFx?Pl&%`MsxPCSM*^5${P0RYJc*~2Wv9uHJx4gw=z;p>nhHxo9)j?O +^r8iEcGlxEs&QdRT80sQr)4HP>k)VF^zxwW(nn_mQFL3Wi+4{1v|Iqv(^HM5H5}Is%uTK(zV_U{jI-& +-^qj;p8^~a)=ypgQAxx^IqP3b5;lI49%Hntzqv8$nCL9?>x`^xG5Hrsvn57`Jr|1?Aq%YN;CZ(2Jmv@ +kR52k}kLsQ;l%*7{{uTXS5nENW;K+jan1oKuC@5BH-OX_<$F#L0;1UkHvVtL^xuv +`vvB-I +iXY`{ze{xsLo7SFCW{cWhM%zGi#<@(QTpV+|}>!fq_WAqV^st*q0X|5{4)=4n^jGH59wN#yKsb4>+r? +axI?6nu|D3;;}0tQnBPk4xVGcMCCj9;7R}ZAki>w}^1-&IT_#)LAR^q_*3 +y*X(gbnklB?%4y|I4jICB%*!ei-8QN;9d5IFRX8DhP`zl&`}Q!hUHJ;E+_Kz2wn~5;l9pcE0~8BM%s!flR7Tq{V9+q6h}0px_>+(?LfyO5BgTksIQqo%(+W+;HdpH$Y%{d+!Rpjudo7w}aGc$Uee34*0=tX#txZ>f45i|1U&cmw|;Ts4UBH*bo+C8N2#P54yFzIRa=H8K&^seZ3D7H4w)Rw$ +(YvgY(IYTt?bcv!v0-=pwQ098g*XGfOMy?01_?3H +$TR=cO8S9H6{UeKojiW_P+IP=rJ3qW1nFsyoof|Lr?Pq0SgHksyyN~dbvJnhyM-EQi4bYk3XRRF|8LA +lHtHTA04$0OB6ICH4=^L8+>Q+^o(x}w|WHI$yUB8TbA`|*htkAhlUBZTYCZ#g+UwQ$-g3M^iPmwZK6blW98eEu%v_2lC8Yb)#` +SVii^drM4j*&roH=31&e1Ls`cP0?-P4At-y3VBWU-Vsw~Kjn9173X26UdLUMk01;T1G($&T}W +1<94IKx*71E_u|va$xBlszNS*Q;e^@;VPQ9B3;|SnoHZ)u9^saM4)2Z|q>XyAe{B{ON6dG1^+dQ!84Q +_1p?p^8K2;{d|6z|N1{`62h2d*T$z5G67o{L0T;*#@pMYjV$-g@4O%cGZoI-ajQ;WQVQ{#@=CTx9d6T +Z*=%!QwH+3)g)@UD0jH=nIhXzE1BTZr(&lX%i6C(8~1U-HFQoC6K0lCuf%Khh^z2G{ +r5ec3i`Tp6O_RVBX)9~*N&&jx@B*Ke-&c|`FGaUACxFdhamg853fjHu%7c5Z8}q*WD?5~gY09yawP4Y +$J}#aY$02kWQ~1%99>RPWw0+{?cQpXp{N@w%0DjZZ)H?}nb?8j~TGv$gT|#Ii#>@{OGRqrN@!eIw!d` +D4oWUYJ?Cv+3ERS<)$E0%Z%PWE?x~(bH6pE7ZApmHnL6WM%vb5lZj^6Fkd?N0Ms<$O}4@-Ep?95yLx} +w`AA?VrWE*pD_uN}7E`l6)(eVj7bex(s!zBrN>_39e0=D!-2VcEqsUeN1_MAyhNf`qTdA;W4)f&n%vX +~0?L3z9_?v1_{=y`tO3+--(2uRw6qOS-Bvnu0Blr*x+Rsz9@JH~S?plusy-j(PbCMYoL_L-8vaR3UZG +{88>C-)NS&l;5iL^GXP2b}W4-$rh2fn?aiEcnGBFj46om--W-$PFNEKp?kIM>>@+qj>4~2jdBxY +^XZ@kC7%944Y2+ubV+NF3IKkJwLoY8philQ604Kw1ZVoSWgi`gU{-R9gv0)h&17>vi26)P$CKsH&fZ&WfM5 +Na)lb(D6qMoPgkhWU+&2c=tdB+g=`1G48$b`H?C9gCe*G;$XS3{E$5Ga +TteGA{<0g}V(r(`*C32-H2xdIxK;m&g(39*Kv+s!gLe8pIP`vCD^x#S*#z}J{idw;vv;CqJhbaf`PlEda--UX?wO}Y=$Y8-SOAnNYLU=qVAg-)h$D7r0sem{~s4`Y_T@GcTr! +P6gDm1&a1wri=-sry*`A%Xl%?=}?Oc5jL@$2OrxM>foimrL$8=jeFJM?|*|WvOZ|^bi&gy%iv<3PaIt +@ffg|#I3AWdX<@{dR4Sxvp$=&%@}UImjg?~6pQSxO)pqk*Hm~)$T>dpE5{&)^w{F#-7iD2?~I*vjQnd +XU9nOVf<&Glhm~7-$>dOU+n_c2p(8O9Bw;#IGH!F;G)F`|Y>vlH8v*^YzK7Sdq9VHV6+_W&KYR(40Bh +V4lXbs8!XwZq#d;AgLxqWdgg)MYf2dF~Qjt`yqHer8WxF%;cy$~8pav_8_Wkbij<2-s_gj=+P}BSn5^DY|`!Z#?}!P)h>@6aWAK2mnY{22-+RPJ>4 +S000aJ001EX003}la4%nWWo~3|axY_OVRB?;bT49QXEktgZ(?O~E^v93R84Q&Mi9Ll@P8P@7a34gD}{ +k3Mu1YZDVoDH3di$g45R5H{0 +lDR-dtbZz-ouEuyQN4P!O6NK{}Y3pHeA7-(^{AnF((!WJ;p;kWtQnkR3SE(o4cV;tMKMp#@WbKf}s^I +eM3g3g0kGqaA(P%GmaUxnXJSK9E@y9&^|Vus>ia)z-=qJ_42L!5dgMerNR22Qp2`jTN?G{|bu;&J_NY%6ovu)Q|}c7IW$ +LH;^G7gwB66mI+Fe%c9}4TIZySYxihN*O~b=U&9m~)eBFk(UPWRy-_k_^i*c39fELB@uAzD9Gh?{$)O +aO7~*ejegKpdZ*PA=>*xqR4Tjx)2g*KB<9ouh1Zr%>$Xlwr0Yz=G9ddBNhm+hKq;83K)CGDWN)r1@Qw +AG^+F?*cdX5%>gMv!0>O{3K_E +0{#9`8Y{nvWOvoWe~5Ta6JuTSgzycA{h?=62#8v*3xrcAB`@Evoz#qj$TORI{nYoXh%h=9CqRbso@z) +8ZZ#_+f>c}Yo;=K+lnU4$<_yu1!&{@@M_7tP?)E^uT!j_#d|oIdpwhSX08F++;gp9s0_e4Y@#_o$?g5)oAZG7RO5I@9Sd}>JZ48v_h~XqXN$-2lv +P|`WlA{L((H@uov)pj2<;G>lLFFI(l$!doY_w>O-3`I0A0p +0|XQR000O8NLB_@kN4KcO9=n~tR(;dCjbBdaA|NaUv_0~WN&gWV`yP=WMyt0vs=ja;uSL>;`(q*lc$@4EyV^s%?X9YzWLnyQj +;HlF(iKRCiT9Rn?t?gX}N3uHQ9V@X)-ew|aHBerVr(f*jd{AAdN2{)9nW@G*By2W~u@0hjRT{lOi_#L +v^I?{dpjo&d|?%vE5aOc_^3i<>T29M|5m7v^{~Ccqnk%0Q(BEvBSp&R7hYd#=QfV&)0`fv}DUw%@(L^qM&e7gzCk`IbmeVm!al3&?R9BT_%}$VRm$!8_!jO4}CmsK^8A*2;k8aU!lXD +vEWlU1l*5Bm1L=*_9Jlu#7M>55>4IQ3mw^T}hyv5Kk32yDQ_tq31&#T +xji;i$D<-BgGUi=t01r&!e&LR}OyHS1{?OqTt$yGZb0q^)!i)yxWM#1iZKW4(1sWdWYcszG#&KS2e_= +x65FD4Qk-iYJ1nBsJsptsk*cq)X;=UK)pj$+=vYcaw)p2d$t_Gf<7w{K^#KX=p%3#RAmu%!aC4xXxQ>vn6c{_}ejMvLPtx-x*`(;0@O4R|tPx@v)YCxpj(_vRj#)UKNk&Hgi;pwaBN>b)K`+ +FhtYr`GK^Zw3#wE_4RnPPmhwB@BgxC$o^F#@*J*i0 +au||t4$JJ`(uzFId0t|XIl=lVo*6k~^4O-~OA4bBPLYeuNnj=YmCm#}_3iZKF|H(kVb&Y=8XmsoUGTP +nSdbb3@40`jSZuFY}u49xy&Q~b<+-cs_9*o;&uitH65Bkk^%jnkdM=_@Vt4Qnr)Xme6Gq2T8qTBV4hs +P(?6yEkrY{hHCg@78d%OL8A+h`3IB=WfYM5)>3(b +0T9FHeK6rtFFF(M)*cZ+XOBGCp=T(;VIyaz=!Y9eAKTn^e^ODOskyr5Udp+M9New|JHS*%4L9JjF-m9< +Ij4I;%tY&6`9OrjT)1X{4`GmGm%Wr3L)CggSUV%*wqW>Zw1h)7Qv%=P^7#Gt +UsXgHdRsD*b7j0p?V9$n^8-!jPX&PFR~%ogT6me$L0m2u1g;Gi43cci`m`A7AsZ`mQIKd{mzoJ2~olrr2O +bL_K%&_*90NV>&BrjXr~Da>b?mly}5*`?o%X~0Z0Omafmo+{?DfW_f-A}p`j_plmoko6rZ +vqR2nog-^*Y-6QALpS7tm$XNV<>mUg$OJj9A?~v%&X|^U +T+YZE>D>xY#kyH@;--ALpOC-x=q_%Kv7ZU#DB$O>ur*`ESK}@E-J5fu4`PXQbyogKZ4;wHLBoVtwn2* +?z$uz`i=zcZu~sCDcoy+Kjj_#UnLAg2Flriux&_5C_vUwQ6x^g2X}gCCD`&pS=}5^04gF +)yHm3FvDteoO6FnjNy+TLTx?`y@{bz35OVY1IrihD{1UsP?#IXTkW&{UO)<7hgSMh)36@aaUyGh=MeM +v97O~g_J&1j~Xryac6erd5^9;%$P@ElEy6Tl2UG*Jx)ho!(lj`Q@+87AWpXZXdEHfQYa=%h{vODG +7zoEX)Y;ls3Qco?I*y$)b!_7oj!?=X@6OudWNDGK3BF%{qkZvi>~$K`FQp<7uV&`aJkN|q7Z>#l?s^Y +{5ex_2-e}f?yWzO|Cmiv;I6u39`5IxY`HD*>psPXzZs1XU=Rz?0-fR-d17?+mKyZ{60`l=FvIrhXZaK +x?<1-2sqXrd$twD7GBeW)9F;EYtaSR}uwkNfW(YJgiy(*w*fz}N?`{j=k?C(R;bNuv2Cjh8v|trXwsx)LP6rZA@TF#Y>-38>A +{Z6f7fSC2wkm;uNzfQ_lbt|q?^2oNsX$*5ek$_~ASZiz`ZH=nTky$i7w6@WO`yUzm~|AWu(6B0q`bEvs4TjO^p5C +I(hY&j6nITlpdSQDWM8PuV2O|zj1tjw5abMB2lFrE#T+`LC-~Z#PCKLd(+xS3;$)>#W>9<+Q%i`Y;@m`=vCo(C{PEQC%at--kw%Wq2wwhO);gcH^3v1$~NIeb7~`M#e*_Cn6lDMx&?vPg +kRg_#pE|Zm6%a{pGSn<7fR_S+$m$7>tn|Sw?4Jot*iDgtv0}7=C+P4=#B2*Ic-Rx^Rq~+4R|OMYHg~Y +yG#?UmRzo8mO83%2-Zv+v@w}Tq};Jfn=`bNbkl*Ip9O*$Qy3n!O5)7gc;)M^GTmgVRs_D!X(sr7!=Q+ +o)W&kd#fBn55xI|<9RYE +P}=aSrl%hpLy5YsfwY|82s4q4Q@Gj)w$6h*$uyK<&m)TEhVZ{MxVaCr2dJX^5lFWCkU#(Hq2F=O%3RKOtSPs +a}x?G0gA%a%<72$EnKml;(B>j^<0$EtmUE`zk8$?so?pHyx>Y^XQbxjzhh?F5;Br@*{3v4P>uBUX1Sj +!Mq0P_!W!u5nUfqaH&1#X!X-rlRXkuGm4|RkH&NVv3Gm|?Iuj03G<#2bMI5f_7JX5h5pJAg8E?UQ`&77W44*b9ZsUMaGz6;8d0jXb^rd&pgHMjDL +d`pD(!MQKl=etO9KQH0000807zB_Q}TkMAkPN?0EZL+03-ka0B~t=FJE?LZe(wAFJow7a%5$6FJow7a +&u*LaB^>AWpXZXd6iddZ`;Zd{cgbjVGw_?)JUwAi;K3(qg50s7OpHCl8hjAT$&=6($>_HU6QtWe|^s^ +Ns)RPiGT#=a&{hPX3h-H&JO?R^Wf`fLgP_?FqsYL^Z2^|2OW_;J3Tq0`AX2WmP@Iefcl{)NXB#({wag +Np<5-NgpTpXT{$`y33(zBZW3y8A!ML!ZA%nEcBD`%5eIjO0-<02( +uA4yL?1+<>*Jck-<{SHMI$DCX|G9lO?NAU^i?RK;lWM12&Bum$I=xl7UaYf +@nO8GFdechv%(b`hR|;k@t%aldt;r80Y~)C>Xh~hl`RBJ4JY9ckot&kHsNnM=WjL5X3k_^7HhO5 +D}e~Bx-&!h2Z{+%Njj^>lWY(~TDDfQ^4H=U3Ax8vTFZf>VH*Rz3zEDKQrZ6G~_^#*7kI@claMBg|vh| +BoDBhfLO3_MynPXY;d1zOD`2h|c~zn07(R7(>y?o_b8C+U+4lNLQ|i84a5?xmU4RGDs}Ph6`-zjq+zs +K)@AnBo3V`p`WL!ce#9a~LNqde!607wrAY|a +|Ct(>*6?PlFa4Gv3;gU8*eLu7p!%m@AXX!5nx@~=oUGbUN3v><(BZ%?-tNvc&5V|B+D!YhDUq+1svq~ +jH1#>-^0TkN}l`lP^+@qH-tc#6ZRw3QB07G~+r%ej%73P7x$P!_PD##i;{^JoKYGK9cqOmw{- +2HgDxM=@)`P=Zm`Su*)p3`|A^qvPe{5cP#!vD&2qZ8QAq4wfkH{QH?Q$2iRX7QzBO5(;&vPhUwz){4t +TTT$IoP|ibB-E0Le{&Mkozwoa*Z%i?CB5ZFcl*&)h2!D`H-EhRogo+wYnRuUltOozel?~eDvm?|p1S) +_Zl`{yy2U$$R1s(!@%kuzo`VaHD@=K$gT9bykf;Jjf)HgMr-29<1ab|M^J>}33P)lqpEiD~T#kURJjS +fAXhR31C0Apn&BjVnDGP)XCxl8r21UM4@Kw1?!!(A6|4X?hjgy5VwQOjn{3vkTeEuRLz!YE=h;W4Q>` +GZvzKs*dQg|NmTr;t75V5OegnT})q`M+7WcKHYNrNKgRpzWC^^_}yQy)zQ@x06$&AR+!fQi=oJrGp?}M& +|4Xfi-&Tiv#rn_lR;BU4FK_%Bloy4%-Fs4el5z}*JR+;!WX`z|#;Mjl?L +T9GF&FTKVFH(e3Q&E6c2`%wiW&=U6q-d@hoNEzUtgvKX2hFq4_0MKyVQ%Fud&qmgpQ)geYM*CJ20)LE +i9vAdcKCM&dssf6Oft{&{dre{#mO>6rl2X1rVNTR{1-Ra(Mi{#5rC27gnFkGW)Jg78$08w*R&%68Stl +MEae?4Ox>8*?vlwS(}9Pe|Q?1Gu?KqTBsuM+>%Scz-<;==>vA0G0K)4Xotwdq7Ik}Wn6H^*(r^%~zj81X??<8k5h{v5h)s`Vy9)hhHV%O^GWzah7;HbTCh +Aiy5X9)1T#8({tOd56Zi;rFXvZdLfm`pURmU3{dB0Gk0HiCtPHz4KD +?@9u2XT4nv49`nK?FnQkG6TFCDHXJ}x^!x|U@f4{JkIn~UYEirWtOdq&X8H*#$Gp4pl9AS~>eBz?+@s +5F$vd_Z|_;1409`g-U19JHBEDpc+5u#Zg5Gb36-QK5HOewTc?$k=C7WcSTuMy}I@cS@%id8qGVSH$-Q +7z$+YEa*{0$xm9vP&i<#=)khxxA&!ye;^fGnJ;G7LDYX0?HyQml^sF(D7yEr&$j;qP)h>@6aWAK2mnY +{2271L=TQ#k`u`PFW%u7U9!hWM0rDs+Z^o9_oCvWIx>F1_g^8k{xnp$C +(%vWxuBxiE}i11EmX(~!ZNmgdc#S=h?CsAg<8p{BM$yC1NwW`dJ702$SEnH3Jr@<{BgDNVjk +4I+&{m)><;PXc6f6=>Lo-YDQltwrTe&E +6P*LIFyJ$jUMTJA{O?d`hah!EbMYWi@Kh=^7zvEcV)_4ysZBWxNyDe1^@LPTrSWZgvE4eiVC9eZ|Gqf3shsEB6F5? +YlWZVKp3+fI=J5Bv7IoQ;nMANVu)4>)Pv9&LwIcnmbw>ZdQzfNIeWA* +mhvz0sSMUnkNmf!42+$yeec81a{jYor`iNL6vu#cvInUG-ep%L0Zl*3S0GwSX)m +^C16I-gU~7q*AYY+AsvNUf*%XIF2a>mo_xQgxW7cuS$J@eJ9u@_Nl +ZI5EyB}MWUFGTqQEW1spBNYZHra+h?Dbr9Jb?I&%>JB-f#$=-KzWsy+2|W$LppE_SAlBYy;8FxHcK>O +J@&QiIbir7^!P8Ki0DzaOL+@SD6e(d+^p336h3lIOG;TYT{BTX1Fccd@WX)%xN-F_*9upT2AV`O|GaV +EoQCrWP)!5sp%AZPj}MF+6gl@LCs+CcCP{V-$Y{`o->GbIi&%V=!pUAH5fXrg*%x^e;L+VlSwHGIhh< +iae+Wc-#V(Bf|HC(yt|v-Rs@?DKoW#x6Q-2keKcLMm2ScvggQ2o{U~V`rVbyIe2Z*a;wp{Pm@Dg|Xt& +uxXlH^~KmcbmKX3d3)A2?+-K@>m!>~B2Pf_3xi8V)@Is?zgz(KI +Of`6l@i#&W?!c6h&MsKWDlgY&0-w%s{f;2U9cIo5XfKOUghr_^aIt +n`&Fch1^uNYD{>AY`Ec;CZLZcVX(1>>vdZiKk3fD*0o$Rk}Qf%we?pV`2Fp9=~kRH9j3GG5a~R+L#gl +1yD-^1QY-O00;m`Rt8f_;Wf`=0ssKi1ONaX0001RX>c!Jc4cm4Z*nhVXkl_>WppoNZ*6d4bS`jtl~YY +`8Zi*PN8&#`i3^do6cY7P+QUL1S(N}{7e$;Rvpbu$YHVa1(*5@x^AY+Hq}C_To0&InX6$D3{e%ASKAB +*g42F|*i2isw_=XFy&Bj#|1Eal_tF1S-<*q*m2)Gj(eI;~W30oYXgrR}4g>boHB!F2Z9h6*IVQcWUv_ +x>G^smBF54G7M=PXNwbcY%Q=^=FS(bzz+HiazffNga_7QP~QvTFxZ?v4A&0%LNHefOlv3WeErrDPtm$ +1*4F2*P0#Kvz+LRlQFf1u5xK3Py(cg_lP6K$89Y>N~QIA>6dPr+P=q2*#o%d}zVKY(nNW>#m^$>pNw( +FU;ZGZUL$H#Fg1_YZX&+v#(MqtO#32C6bCNrjD3F^?{eR0;M!)Qv=A83bdyplx@ey9F(XUvJ2;NTI4 +F|I>mA#>k;0~%Mv6@Jt3OU;9bv#38ctZGj#3h+A$7`ablUWi28(Dq6L~s_R{}yH4c(`a5f5pf^ypeuDWpGjq=DTwR_1U>H7*XD}HD;VcPZIGG1ea3=QZ^5 +O~>8-%&$Yc839K;;PBK&gImA(*H18ZxqZ6xjujCAXYnAMgc*+M)(kfIWfY5@u*k#VpD-xg!qfo*Flwz1F)u3>O@_pie^8SjBF1jH`bK#T_U3+CN8fs$79Rb`d# +nd{Ha1Tba#2Si+~H80@?s7(u2K~&p~l6xMAB*9mj>DuvCXhblou&>nKJ=Cg#j7iX#bJJEJLM$+uzgOA +;lq%;%?QE6IX)4#dMO~VX%*WXhCTOT%ctnYsW{Rqn;F-&w@C>CvUGQkIL(AC>P_CaarG3!PkVYe>xn){b7&iwl8{iG8KtBkQhJB^2f)zq#u&6}eK~ +OM!A1}VmmkaP`&+y%kV}G`IzNcVfoV+Au3&qQ=;Dk(*X(nw=y*q?y7zf`d#UG9*kKVFQ3<;0I6TE@{l2|a)Wu_L;n^c2^7q6@+-74ey)Wn7qU{$~=}P?I8u4SPWnJVQN{2w*!D>;!Nj02xkv}cwLC0%Zoy*61;4~^nN$o=8=@{U4_fwDSkL|_;f#N<8H?;k=}`18%xVqvTV +a8cBw(*Nt|cedb*r)<;oefTdh_^`7U8v3CoAI#elpC+Nkj!B^guh!K1rRcWE+Br}Kw!;^w%#$OJQ{^| +YzI(}?bl&>5jsDIiTb9i%kv85Bk5aG$B@Hua9c`p|@U`;6ren;YeIQ6N0LH#LBNRt{Mv~JS`k#tg@f}0 +$W)z~a{)*M=O#+hVGUg +l_E&9f)Iz+Si0?euwNma6Q-h)Wm})#G=}% +u&DF0s5SsT_npw0}?$bab2QDX|&^$7xOumv;}SbHE^h8@e@nU1rSjRrb@_&*dszAdvD@S?x%k0#Y4v6 +D;(FD9}l|koW|h?lTX#TvU{YaUl$rA7C$}DTA$x|6VDpd;CT(+MpNGlYv@Z2g;AIeZ*CoS(j? +Cd>R2b^S9cNi4yl*-s5E{qZtMNBP>u0Ng)+mSctTMumCFB{u-LlV5yRc|;2HHd% +#{%8nTqD@$mwUHDh*2FTk4^IDXEK`Smab>frk +9wmXV%^Kc?4gN73oUMO(MfPY3jzZynl2@qA75Dw}q$&egjZT0|XQR000O8NL +B_@#N|auAOHXWaA|NaUv_0~WN&gWV`yP=WMyC#-JBLo_^yvZi2a;KBE*kf4L?9$yOIr{5w)-TwAY17u9%+AcSGtWFT`0}#;fxG@YcML!%nZXF=+Z6NoGNvrpdP|or4K#X9=v-g_VKHj`9q1+rc-C +2ihZ@y0MnyS2DU9OtjghLMn{VwAz=)|&Fy+ZY@fExQO4ERmFx{G^6gwV>q=e5EQ(MbdtPnxV*t}4BH? +Wl(2$=wd5!cxbM2G<|ydagYNhVK6X8?JDxq=kTWq(R%&Gi#aHWu6Ean%m@r$)-6D+ +oUhjcIx_9o-ojutK2JUR!_k9>lJm|u->&@KWj*@sQ- +x?ttrVQf+c%KYIum81O|-8X1)EsW+qZB(xNG*r1b`P3__X~yeLjGA}JoV*}{~aZqXEkP78kTkQWo566 +%Cc@dF+c_8^f`wct)_V_ipG8{EF?blPt^zqLC6kG?%R^+11o|H|z`44t3FO0K{OmrQ9iYbEEz0rk-iR +gVsRG}H8O=ShPh7TS>7RxDg{fjjrq;#j-|k&aQo;DZSck+Q7 +y2MO=kyGg{ZtYD?k*GODmvW-@-rtmPVSd)LsdNUk-HS^d4N=DE+{9N7JD@aQpXf_q)D-g+pyX5jBoQT +NPNd`GXk8x~xLIs-XAXY1dZtqh%bA!FQ+dVdC|>NI3Wsi0pi{2ff;Zb!XTH?8}95)T;8{!ieP**+nC7;&+|eM_Y)DX~=KOr3T +*O7lcd5)_U7gb!nWzD1emU$h#DShFqp&jAC$R(R9~g=V9~i5$KHT3r81z{0(%iv{IGU5M>&4Ud6}`8v +w#pqcrFZRh&BG+?6flwZ4D%_2sO@;yD@8tR%+)htroS-lm;1Jv)+WnR~wHtnC#-a)RPn$S#qb-K+g89 ++XqC7!_lOm^G|o3#n;hu-deLpXiUn|LR)IhvONBkt?HYp3JQEp+PL3k4iT{CNxQ6Z!0J+0jt_IC935S +upCK4y}q;XCF|9F7#>e4Zhr&PnVoX@GHp;CXeI$z@2?9ux3k?EyxC{%q+-1FB>&YuYs&=loGQX+*k5d +zX@{V|4>T<1QY-O00;m`Rt8hTMpy(J1^@sADgXc+0001RX>c!Jc4cm4Z*nhVXkl_>WppoPb7OODE^vA +6nQd?4I1tC*SL$~dZC|vN2uH&6R`-Ikv}Mba5~@}AO=W^pq6-OfLRZ}BzWdDp0aEN13hZ`I4XtQB^B> +z|d-79qaw5OrvUXi>L8D%+wX7OkHrmx&Q23r4ADuvVz@aVNN7rW_RD(VTH-usEx9fQ<9f(J;u`P|Bv2 +)#bBNu(Y@F(sCBQ79tAd*BOL5oMB%|;yChQqP%+AMMdKg55SPeaA?z$E7oOdfLa#QX7ds(~K~w>ORgaTcC`rY3+U6MsR+^&W*g1PySQ5kvuW +Cea}97kR)Wd2UZIF`;}pf^!H1C;EZIyMReB2AlaHc;5|YJzzp2V1ECPKu~~T(082)wT=Bg&Wgq!aWM? +zWL~%Kpus~NFI;n<3!GDD-1A(U)D73>e#n7^FrvnAu*}#M4aJ%d1+D^&KTY!ma2M^<-k-P#u?dW{9`` +p7nSwyT8H;ENxUeHqp5wSD;Nh}}SeaJjVL9DCxc)To09OJ3z>qlEAGoWa$6*|DXY8dY1UkdFdiSP%*M +&;!9==sfv(oC`U!XH)9Id#Nvrz7EpcKp;iA!mYTr%oJw1(kVOzb~*)l9lew^(?2YE*9EUqN!$@ +Z4*P_l})|oyjjyMq@;-|g+_H^Gxs+%)w|Y1v$j(kRV=5FeeS1w{fX~`$ +PRGpMGs<>*Gxh46f_LFvEWHh5F??jf^BX0+@fQU2}oh|5W~q-ED +j-|JkL1iY1^sph-{5v`+?g8Is8&m{b<7Va0KKSx2;q}3r}Fhr$BQmxvOkm06)__gx}K#Xr)dj!Oy3=p +%DuzLW+!W<7vazHGn@*jZs0f_G#5b@mp)`0k{m~j_C{E5%E_Kk-JLCcqDagJvPVAPkOaJTLXZ_0(*ZO +@2!h#k3SMm!9~&KU81Agi?I0FZ$b|OWHNx=F?YKnnn%8jCHJAR^E$D8^w%w +F=0V;#!iVoCQQP!45UV@9o>a4MwYd7y5Awjxko? +P(|e<4fk{gf@{fEsTQ)M&acc8z?8_(uwe08vIH&w)6}2^EHb?jywm|jirGpMnHjwzNhv51=`WbxHDn@ +%fN+pBWNH@y1<^ow_@|HtAQ2&pGP9QQ5LubrHI<3li+MX+!h|Z!7j7x15;-l>Rm=k8Jz)XmiULuvHH& +IOb+@;VS3p`IBy@;26tJ*c<9UvGXAomq1t?G=`63?{2>Ipg+5ZkA27K&%FCXl6;X1?I4Z_rR2 +6yPvzZp-)-o0Tk8hbO_bzwAjpbv|_D +w{}GE2dlU;HEw~X%8`EjuIA6Z*^)4@ZZ!UWPOV=!&dcd9ye{#B@LeHN?QY6r&JjK?=`kBMDkYdI7+SS +-mm;ebjjMMyWZ=S4z4UvlKEt;Zeh2u1hHM_|)9pJ7@^&;QhJK>x{RAq3I9p>I|kniLXifE+|0+l^$PD +J5hF9+`z!^%b<=US%BOxrpBw+*=dS~EApgnre>mdHh^q-N>6Q-3-i+7lzt^JhU!l-lj=7#FPeZ&XTf#A +$7gf>`rD5)2;SHdvkCIn?&E#Tc7ULv9=$P_OX;!r}4_?FfZ$4a=IA)@g{0+>+%}_W|Zhm49`97W*j>8 +2fRAF1k+;^90DJ*ThwH;^f98oFD8H@EAix|>Xy6(JssQ~|qsj|TfiA +sqA;pLRGe5qbpO`PVlV^OKPRRk+4XaPGvkx!{abqaQ8bI+gJO_vpZ4SlXJ)A&bvj6D~@P6^VUz-Ydl4 +gInAn^JH+ymDJIFq-+;gUwD@qmn@?CFS@ZtxTh~Xo!KCiFVlnexQg0b%*k)_kf|w*1CLn!mDxc%ac_B +v`Pvs$(2y8Q}NgFXsqZ{Jliuq93=dD?OieW19}fX)zyqW4fW#>UC*%YLo +3|IqgAfj6GrUNuHfr(No`&_3lNU2lB+>LMNUksLPI?VR)87Cd4h3>z +c)!1D*kwsBh=*j8_gqg0M>nmtWpayX;M)Q{*K+>3Uoxwp5-X^bNMgnaE(v$1O6X#X8hO9KQH0000807 +zB_Q};P(w|f`>0If9u03ZMW0B~t=FJE?LZe(wAFJow7a%5$6FJ*OOYjS3CWpOTWd6k-5Zyv{yhVKL9K +Q!PQ8=#fxnw~?iu?xtuln{|6EhQoF-WZhh$s$6L%16ps?XTZ@o~G;oae!R|9`T#$uCA`bTZis1zWD9` +%C`^yc>1I~K7H`;$@7Qh+s98I{JH##v0wcDcVCnj?+)eZ@#3e8tMkk9;QGy>T-=oJum9)b^71@?zkmP +n>f-hJ?e(#|zPvbG-Imum=3dVHJo)tM;`Soves}o!aC!aVa4gqv%k6JaWx|ug?ak}+4~KuCd~tnslRu +9iu8;nJc6oMv93NgE59hasH|5nQY5wzK=SPTr_>_yiyDeWn_^Q<2cWC_P_ym8VmfP#{?9=VL>#JYnkV3n>cy&BKenNh4k +B39Kxqf^5_hj#z^6C0xd3}CWj)ylFH)Dkq$Za{ldh>_tBLv=Gzqxq(iFY5b-sG;}{q}HtfAd+Ge|++? +JU-kczvYjItHUw(^z7rS%Zt~{ethxzaCLJi=Qrg8FK^!cDzQhf^8AjK^5{D0E9P&?;Ud4g`p<{sO};8 +|uYO+dj^s&6#eaEz%NA0xADH=7^8Kk?rdp1lXWsi4a`<(-Z_34$gx{qW@{c5$oBexQS$TCRA8!tCKVF +_dA_tehoWA(e)0Z#G{U<+^zuZ4NyZ_|H58veAlz4tkRSr@Y??0qTzA3r07y<=L~R&mZ0^<@w?8SH9SI|Gq8nv8r^~n>3f#mmd>1E_0I!3?Eb2=`Jb8_a8oDdAE +nx@2;*duYdY9$ky0P`TFbsDAT>I^CFh@^NSw5SZe(8VqcvzMf?1w_tu;?wf)z3z29l}^v&VCeD~R$ah +5ZLTmF4eTjuhI@@4({?#Ea8ew*)w6wi|H-0440zRFQBl`~^rrmyqwuOt{-|HY#|eMpeJ{EgXc`{Bz6e +?I&1#ShO;AKZWZ<9DaeU!0wO`|`!<(Ee|&WI@IQZidiLGJvz-6s?|)ZHolo-rDIYPN)VZ%GrS0{kZnftl-<3J`ld?| +zq(lwg)swPKTaKIZYjnrONh$l5>m`Ye7m%G=8tuHE)LQdhOfw%^{`R@Xi2g3mW#oDzF2Zl%i(>3v(0yKG3UE&%WXuH?9 +PT$2s0AfcH}fz>QJmTU-Xnf+4FnShP^#UmW^#9iOQl|V}XW%8^UZ1VeSqOtAa^kCdEyuw8q1UEU>Pu& +0eJ_158Z)B#BUsNpaoSfqXZHEE96v7MMa6+Y+q9parEYtg^7mk_)Wko#P;}Kw^dO6~5P;yXUy%ahAQ6 +;j4c^0v^lW5m&C)Y6@!vK!*sSZY&IzDtUWdfiw+JK3P +UIk&s(9UE4gx&O2jtpbzg5pI3QqBe~n>0ku8wd8E?%-A5iW8w9VVv2YkZyN@|uG5;6VrP|x{&V=E;IN +c+hAj3U6GBIWTb*Ej8x)h3jw$X{ewf%NhBFe0E;z&nKU)tz_!Dt-W^{Py(C$)fI$L*8#|vk+-r-F4a( +GQ0=3fX7_V(OMol;nJ$FFCEwNE~<$NXpJ@5Td0$ETIY#$!iioxeJ?-C;_nZLY_VDqLU=U7y)OfW0Oiu +&1z2PTYBiiLp(cNtv_>m9c}pIt^F}c&O4ADRvpa<##^;OY?Y_DqA{ugfs1lLIpaxb&<&-en8Zl9;~jX +fC~^Qs$^jgx7&fDeP)%JAu|i$Jz}9Usmz^L$K`tK(9DjSJ0f0&kXRPPX&MCh&?*qSC=Os(zPmi+hmNh +Ije?(`>na0?0hB5;R)|$JplVj4yqj^R?lyCk(iV_nWhu;W0an57)XaikPl3(g8arNEu)Q7Fol`TV5i_ +vSgq~n2c8KpycuE638e401b=RQX4%#)Gc1EI8$Rk2X^DRT0xpD!oixv!ebnI?@dMigA9wH($=4k?+>>_l7~)XZN}0%Bm&rXKBrG?o9~xE$oy`w06(HQE +W@I0FmC +h;F%aaHkuzqic-LbLCm-Z7uZn{_xdDy!lRhU2c2mKB*bvQB=Zp{_F2u~AI8dp&D2#C((2g__PAn&9hi +$vIzDm-wF=b!+*xJl8k}7rt8;g3hIAvhj{Bl0mD>W^9bpnP78f0^k9m%*qwDJ09p!d>VIp3er4UIgp{ +lB3_FrAE%=)!*#MgJ(%U$=Xt~>71z&OA-HAkq%>N^n90o&Nb8OY+&8aEk0%85*gpLI8n&Ani^nIMHE;fknh{rHJ~L()b!!94_0IpRS+LKssQ)%FLVvDKASr +E6~W;D-Hfyn{3~&|5|I*JHOdB%;Tqkql3+Hj(VNi64lvo?5X`Qh?8C3|aiUZZ5F485=%WMfX22$1y`g +X>R*-V34o1{CMh8Os$R9f?K?g89AlVl!7lo@M^2g@BTxBt*a0cSXm^z_o0`R6)T}SvGrLnnbGT7if!Y +&@yfQxQA*)=O{6tWe}>TTrMO?TmPZJ`$_JJrEw{xvy}kVWX@}Xea=~3{r+ZQ;2&7OwC;r21r#Rgk9e40xO`Tk+4*V`W +Z}O04GslH$e|;*98!nN%W{g>VzHD-GHdJ>N^@*~4s +9fR$Ael>ZveY_3f5FJ^WzjHPDju(En3pG_Z@zu31Q8vF;j=%j3dq!6SW@OI+X +$!=qxG?^G<_SUliN5fmIofbdgT$7m%rdmxRh_1C;9Ej}wu%g3}0^0l~lcs? +X#<$aFzf0Ca(YZl9|9j;wL^FCdmj9aV!eajsQ7<;|}3G~EgNXd2t%g9L3wgyUgph3Sj+lso~NRhL?QP +?l7YqBX2%^Ddg0NC?*!Hhb1h#>yadDe(Ub8he}yzBybm9onzmKuw?nO`5lRG2tjSi4>6U*Dk$KBk|?NGg2JNgcbeIuxHDVl7D)yG@xgA%DpEpGR-}j +}REq2o@IsVXr3m6$?=)=zbP=@cC|1FJ4PHaG?%C0BhldUiU9pj8H{(o8R-;;9gUuD`0W6h}pC1tG;$B +g%1rwH80gh^m#%X5mON?$IK7(@UXy$|5)fM5))B!8Z58zSvuZ@T6K^y3+>6B52U*jbCfLLXC0$EgZN* +4gR?4^Y18(v7yC0DU((kPh$u2D5Z5oDvD396W4<94%lAa7*})@FkeV{+v+q?pLUe34CR=EY+NH1`>GhXbwRVJF+p23E1Cm~E5m5B$wpQ%n<=u +M-RmH$uFj@?nzNCfB^o|c?Zg6pRjk`AP}mDF$6ia*M9i+&c~B(tBfp#{KOrt4ttw2=th&R&vhSb>T2+ +@{Sy=#Tn-qydKwc0UqKZ8vy6bqyHf3sq#0H5?Irba2gyND$_0g2kUED69BDupg~0usP2+m@C$ljO5H0|Tz&!)^gW}9=Qzr%D9I})l8qZ=dyBVVlK*B +P~lOg)Jg;Rjw+#nxg*W@M(ZG^U)&%yxsK#Y$N6#y-oiy2)RjQ)Y5LI*89Q;c%jIOj5wYqMxTPrwy7gBAomKG%!H}{LIC$5&xnv^oR<@geCUNiz$j$RM +8z^p!`#H`lc*qi#$u&qk!gRC4S|ogAUGq-AtSo#_qu!`B?hHtWb0YOq_zZT6$RiGvqBJs5K=(yGAwEx +njx#&>;k)g?G+TBZS@cugA{AmNX)L9t87qYuX+bM*zS4&*hlp=r3no-jhVCA17Z%5i!Nu@bNJEdKDaf +A>FcDWB$4!c2$wokJl|c}D;^S!Rx5xokArsbZ(%7C^cmA9>f8BnkWA~e02Cbr9iC2Pcq% +*l(-x(TFi$0Et&ouyaeFlu=3BWyAV9vRID_%CJW3H@<%A_@!UPx=_+Wf-q%In^G?XmSRS6V2wuy3ovKHd!<0CSK3Vhb+yrI8#*`+-YTeSgkq5C?U=C1w`4bk +Y;zY>e70J9VFs|WRaesb2eYI4z-0TKG0lmmMS*f?=(Y{@{&Uv4DhMiiS(xrxaCl&b^o#&W)(sV2Z=FT +mW~{kI9%wLrj@cekaE^lNq*+dkVlrHU3VL$$rO#LI_aLkrAO-XIH|yAV)%>_MIfHB4^z}Nc1ggL1~2o +-u`3QtXc1QS;n#FaX7F+L0NvfF9`Fc*h*@!rg2{HZGvExg;=V|5ryIGnyHNn-{ +_Hde}VWTrs7i{u|qL8b6WA;_Zjv&^bK13Jd&dh#?PM4Yqu!?t0~cxtP9Ijlh16hilZUcH%0{rRoJ6Vn +qd;D!ry^pC5n|IrfNn>TKKWZ$L^lxS4Y%b>%`gV@Gn=V6x{nT!I}J +e(8A;M_4rx6~siY)`l(H?LL*o}AgDU4ePdv4SI|pg;@GJ4%ddm}8IN#3PJ~cELDE2p}!!k%mn#Q-091 +aPz2GVtB-7PgWmrlYa|7DbRKZNJmC)u~yNS`fai0a$yHwj}(?=9G!@CS2OgwxhdEql4r|2xU~FVct +V28&smj2FBfah}8!bn+e+L@;07GUv&&mQ;G=+-D>M{}7oWCqvtW^5J8UGGi$JE3aEQg(m^4LS@zjNFETad&hGwmvapPQp%UhYEl9 +7(g7uvIK2OY{}yX9Q93@&E`ov3_37GzqKw`T<7=GErS%*oo-b5IK*ypZJ2W<@cRvZdZEY6-xL#H?a%+ +p=ggBlTL9Ip<>CNUE}q3s&sE+2wFJoDmv{W|dY>9s9SQ_4Ibs*Y^I`$)2U(nY};^6Y_YSs8GBDvw9Rn +qtL^pg#@hHPzDKF8+T{GS$gBaMXliaRX*(Pc^D{dmICWMB`qE~dlcr`udPluIcir9Rbx?U_$Hmf;7VU +CqYLgkDB{iBZwc>#a9gpe@Rf!F_sz(4;ip5 +(^v6v>AI(l+q{zwKeYk=~&t6{*zmQnMPi*&FF&s`_azL#+uhnV9%%3=)!D}+wk-i2m&+3hcQB_&KY^< +l@MaKer#LySf-`3kqk!ux;Cd$fyHE +=^i&BD^A|IP3v>dD&^;I>!jevHRU^g09}`A?M-(Vu+6bTq^J7If1>FTA^+=shWH%YHcKg5!jDf)`H +#n?G4<+;yE?5lG{*b*zG@l}|U4G7@ohc??a>8UD0%h9zXneOq|Mn>h@PRaU~Xg~V&s;bB?9dBPxuQ#$ +Nk&cMsOfv|GyIoaDQtJj6Q6#dQ~fWg&95Rqc_Aaxwb+1q3GB5igBNUq~A4DpM*bNXfd{qMfwhW-BnP) +h>@6aWAK2mnY{22Vv3FdNU&Y9V{ygdFv +KYSQYU^EQENgP6dGz}i%MC|4H*(J=^2vf~hTrvTH$`H7LLcMSyn5Xm#QnGmz*#-|Kx13_{af3pYs6pj +mkD$1O30jk~63JE+mE=v>8&Hx$Sh{CtQ*Yt#h7+rWlhD_7CONd^lopzIEF1dSOt+X^Iz1tzl(N;`l;WjwE)t&$nBa=lyBg=xinm@Hs~hWdpEl&Fc*tXc}5y6h28Q5pnhp +mavFJ}5SDt;D-lV4w)Uk@*gglfS9HL2bwcAH7~%ZwJ{0rJ=*Dqd@6aWAK2mnY{22;{Hh>tot000Pm001BW003}la4%nWWo~3|axY_OVRB?;bT4IfV{ +~_Ba%FKYaCw!TYp-R+k*2@5)c;{g)@W?$7_YqNYEQR>F?6HBz!adC=ZjUq!IZkFqE8h@^sk?Ip3H?uY +PU4QheMsS_s*4;$h#uJ%0J#ua0zGlQI70``w7uxy?*uf>BW~n#A<)}=2 +!o~pN``X&!7J7`1HlgKR>j=kK0bZ-=HASI{`iaIqsKQf +-{YSjUp+p>nm+yE%a<>{;?p0!`0DZ1o5$n%o8x=__~zTc?$`%R<+DAN_Uzq{KmX%ze5^6u|G{OH5a|HukH`0(?O-~a5hK78hs&R?f;rsMeHGye2n{dc_o@$dg1txfC@W555!d!PR+8uITyJihuWg!K8BFCV +w|g*YK9-#oqk?)dV>pWE@{>!*KHDtI7^hI1=TEVSPvdQ`Sz&zihgV;{c*#5e$G30ae* +X`@`0>Xd-+BD{hj-ro_zyGv=DYaF({KK1O(5lY9KZhcKON^gHRqoATWt>as1;w-fw=He!c(stK%Q@JIiW4i>b!y|MLf5#V2D*zxidW +XZm&g>sSAHzLU;&tKLuFhc$iqx1RX?OVlQ}|AzcAt(V8^SE1T(-lBfr{qWVd$G2e+H9KLYp8ftA8|69 +u>GMB+`r&);e)Ol`fB4zw&p!O!7oUIl$;W?s_HO)dEu^hIeec`nPl#fO;lF?Kqy^(xkB6VfdG~i8z5l +1rKK$SBvy3wOUmp?5`s`1ifAXgfp1uFS{`ASS-@pGX-tx=8|GTig=^@SYd`Ru=53zvukZMT}Vd3XPyn +4ltnfX_L7$u$Y!Wi+sGe4A`;>~fY4|$&P=ESQ@+hcY`4&;6m)Gv3^5*(0|&zr@tyE%Ck*qtvs@k^h +jeoWIQ1j(tc$K9(@~G@ayg6RT{D_a0G#*kqBX=iREPCIajvi+9O| +Q>_$(l{BY6nzByvY>)_$}df@4?{MfKOTV_`A**W6PVIJ7O_*=?O$f>n8@;u{&V=?0)pDl)JCnpmF9DV +w}cx2+U7fVPntJni~oC7V+IX%Q8nN^QhyF0|lIZkk}{9Lb?+7%}gKL*Q>Gr!o~UK>AR6Z`3QPEITSR? +1+*;u&?lpbTxddrCLw^RvA84R4e1f>BlKvob*1y**L*8; +)U~yX}G&7_9~yT)DSRN$uwGs$~6$>cp+vM&rsk-{2Fg=Jcy!X&Pg;x`k#8kzbb7}Fk!cqr&pYxWwt +H%k(io(oc|Kbzq2TGEfwu#N2h0~8?49$t@DtZ@cib{se^D*`+_AkwA?2%`jU}pbfWR8~obJ16beCQ#H +gLXHD3Oj>kEm)9v+C;(<%H&SYko#KBEze^2cyD}Sgb0K&LQ^_&8big?m>M#PJq>()Pgile>!r!H%_o2vkfO?ekJ#yg$drUIc5z|#BYkW +4g4yzY39?zKUh7V`?r=wtL@NZ|hBAh#7!HUM%BiD|t%PKKiJ2=ajMl36S?JUMYXGLD(K_L~KTc~{+Au +l10uFZqzX({iKdz@fQO;JWQP^%iNj6c4|eB#{Qu$;i%#neUCgQb1uGs_7u!R9 +8^Vi!z@*n;3^p^#4#SuzlQsVS*mJE!3%{Tom;z0*je}4(;pbdIIYIRu@NESy5q*|)pK+(=2!GR*Qsk8 +{HlOlJZ*U^)B_KTz0#^68wnG1uE@%SKocEI+>{JV^~Li+zb +t^e}i~9epz{l_O(WvFj=XW)pkgRNYYQf^&_-I`_yTnV#KyhDV8UCvjqkUh&A{g$tpc*ploaBY$B_c@> +8ytOz$2N?iBIAek5xUBK^S9%7M*N<1heOl9X8p(z492I@ZV{87x;!TwP7L4z +U10Y2g>9R-ILhSGu+nqmSFZVg@<4IE6O!0b4c_NG7Cv@`=Lmhz}OCW3-Y0NKCV24a*O{!oL*^a!%Sh^ +eD$h#J{#YXOK(?tNMXgo9uH{)i7U}G=S!DdB{_f5yuGXOC6V5@$u($P+v%UWRMI88G?kR;oM^DVu7ey +My-wpn8_JMnPfkC9;Yz)=JBwrj9E7}}YCw&^x0B)HR6ABLFbTrN0SFBcq*2tA3jpb +EXbLW{PuU}bWa53E5pX7qN2o?@2?(@`!bcog48Y;xb;rb(BwSXQpa6Wx6%+46n6h;+53Q*B5s0yh04h +!nZ%|l5!6{|%a{)jmq3IQpQ<~ +ecYS;T`e-`*;Y2_ys5I4;dUSfysd7GxJ65FiGCG5IWDk%z}(Vq^gjr(adIsrJ&+if +iM{-hNtli5H07%W``xc0P48K*mfcvqNmB<1X=q8i%2GS;L&8J4MTws;77_D>11ex;)E%UCs)YfK6VyYnodEhP9vKIPybyh&AEA@8r%nqeMlTffnroI +jttD&GeEB6x4IryDY;k&Ci3>Mbs?EfSPIfAdM2<%LW+4I_R4TLF(}p+c^0!7zDv+dX58FU8;ipf)r(4pn?V +4d^O*sI>oUvD-4`fpc<6%XwCHEMps;S~=Z^YP|Cy0G+jpG)p2tKpspEv+ZeO^Xcz7LbzF|L$Xkw|=iEU +uu|S0e=m(l>@guy68ij&oL?x3@Lpm$Ql1xWvMa&+Z39|&UoCw$4QrAH;p}#l>U=$cB=7X`s`gqbqF(Z +4FloAy7EMgiB6xO>K5D^>9?X9{wnan{YHNFwKRsjZ%3bAc$w!q;uQX1|CLDKL5+&c9f5ZE_lEq)|^xCF8!pp4`2g~En$cm`PHi}AIj8?d&HOhoYWZh&Nl?kNC%jCUy|Sj>c`kUcVVGhK +OHqTG&URW&j%m<#tULN;T*VmaZ9p>PwJW_))TMv%wPvH_AAfS +oX0VG8h}1DPCk0cOJRj#0phj%2K>SJS+jQo(&9B{)}-X=~&Ok_jQK425k6o+r~qxLE?!YQ_~2IJyxjZ +<*PL`!T~qe2R&gwoh<^WHPzoth1it&VtB;RR|T1x}0l9nMiaCA@ta%XpT@nK2|{CX(c!r86=a;23#vE +PRt1|!3xA4V7^e@7D}8+Ob>ukBD)%!8>_@~X241ON@D9q1ydw*5MvKLakDmII?&89KTfPvsBY +n06_xvMGVUiV}pX%xUuL2$%KxG;K8;<5N%+B{t=lj6q2d7xT6RM3oFdtj)OM!j5m*m>`*IBH& +U837wXbw7H}5HnD)KNg@spY3k^CBdbeFC3Zt0?99)9v8SD_ +Gv|Txvc@uZZ6b_ZCBK0jT&Edkg$uE@D#MvyL;WB`&L&{-;wO<8p#q_KEk=vU^1!yw7f2>X0Wl_RvH22 +2UW7u3H!Zy#SWFNFu|`b89d`)>#{wiF0h_no2%Frs8;(ctgQg4|6$#GR9FJ#ms-&oi-U&ShC^Eb~c^` +mMB?mx&KkBZvg^ee(oz}_t7Wb41Epv^;QmSZ(R9X_o{FaR4iUbY>Fb2zg=S9O)po3LhM{;>+cgX|}3! +BOGo#}U6el~`{z94!O!)jTdaJpNnY!OdSHqM8rJw;#9SA` +gu2oCm8xxJ#Nfv#uJydYvlAZhMPQ6vT~8scw&gUig1M7O{}7D-E*G$!Rm8CQ0C#Lzq0KvwdorTE-(*G +B{6$MY>KX~Q?qF$ObC(9BXy9Iii3!bY0;KXY~LLo4J#61>a3LSIctI1K{t3d^p ++|LKB;Ck8E`*TV?|ZR;n%O6sn=Mp#Lfa!vKT_;@6DSX?>_UFw_FV*plm(y0nlCaDk%PflQJfWO%YiP1 +$vyu^`l9Fa>2PHYqr~eFoSJ4m5sjO-WXl0D*aD@-d}m7p!$;H`b|XjL2vfmqj9;U~ge+;3Y*VLf1X=L +NeXFsO7HC5A;o#M~0OB3-<{~MSCXsaS&cD>ntr^4c>>nx|W9x|4@ysjAC?h29!i?IOdClX(csDI_5x% +5A9l&5im3(L>4x44<%eBn>DYo7`6&Wshuisakj +GzJL=!S631ynexVNGLIe0NETP(^oFTckl5?QYj(gNO%1BMB4bb; +9)hWd5;!EgwkAgjEr5D^|N0B{PtKta@`!NQ#GcQz$JK%$rcxD3y#b)uo#BTPq2}Ze$-xX)EK2Cqg)-B +lv6EBZFjeHxuZQF&-g6LONql(FH1E7SSgtt?ASaOVPn=%0Ezb#dTYrOVx^kh8Ou^3WeMfY#qr~g5U#< +PbkDesCrn|p|F|AX(htk)Yh);eJ)m)%w@!Q00qOPk3M?zmSA{OYXn682Rd_HZZSEY)2cMb!zkGTR5uL{@+CQy4W-Z9ch|hED67N@7= +OT&sHQNBaRqTtYv>^Gypcqx`krnoqOkV_p2n+n??x5LjPA*R$F+{^6)C+$!KmPR4@}FT!j`G}&!awAlL*Bn7`PXA(qkm8nawyL7E@^_qseac+tyW(OzyHkNFbg-t8Dnt +Igi0UZpN +?#G$tqd1Le3+v%WNZwUrWyF7GgWB@vZVG$@zH8uyBK;PZaN~^$$m0)g`qXwK@gImxvZXkX1j5%G5)P` +7pL_$WSX1UJzjNgxPJ+_ik8SvdF4WA;u{LoTGs4UyQ9Q<{g`PF5oX3P^9$ +X{A*hKx8=E8T~Y(mA{>P8<}3#WUG2Sx2(?xAJw!5Fx0nzYa(xFgYMel;R#(h_qxSd$w3^xJL%b6cAa) +Sf(s)2Uf_j>6i0^me+j-prcxigx(c*j)}(Ko1ZmUdb_r&Rs!$gJsd9>#fmrYI;1Ta@dn*~g18!b?j1-bJW|M5xLz`JE%GLPVY~W-`(lQ6JRk;?IQ) +0Bk?-P{qJ1C%(EbdIQP7BMDL}aA~;O{D%8b#?>=fzA(Z-*!&ju~H3?~y?=X%RN$eE|`Tf18akW74nXhK4#L92WVAe~exvcG{s8C32 +Y>LPiBT<)N%Kgd64fKp9m+l|Heosv+iNi0MP5w-N8&2E;Vr&fjXGNp=zl_`6+MI*s{trYMcS;lW6OIN +^u+01)eG#m%OV(PYKaH79dR+7Z5QwV`BU|L3#Msni~8NZo4Ql+Re*rw4mbC~c+3{jPA;#%l-1rbd|h8 +g!6FR1av28Pn@9@(5sTIxr>&Z?mND~2blktmSG$`Kl(C$iX?G-O$%d2$p*%i7H(Y^js+TPg4)ii8QoS +l|t36XIj>WW<={z93U1{WL-P3Z}BZ;$jE}>UHn4j9)mP1QxsxOi2EjoJ;}*l_q_U=R%jQNf=7LbJJI# +P>`)?X0hA08!~>6Ljo`Ut|$ZsUmWc-rmAZ{dHC7xc<1T{lS$(d%6+%0Px^2oSk8cBO9Jy=S?P+{ ++p&BEl9uFqzVm%xG*tK|cY|%C-6YO +~!A6S5+?22oi?oQY7ex$x&2tm_6!}`>=^L){tN?M5%+C%^ErD2QQ5 +!d(Zpiu4bZCGlo*?&06Uy0ZMu3(6B!FvO@t4b(?mjrpfqCP<>L?G4rpc_X*m&M8Og-fGF#?&~x+DlCZF%mM%$KXX_9obqcbUM9mjCC)-D`` +Jzq6FNHCXubmOPS&~UubP6q{*f&O^qDfi8EjG)>T3geL_=SG@@NGBpsxCGEM)IP$8=&E8(Gfh9OzrwA +!hm+8!~@pV=qA(g-BUonk$IExn~hCJ(lV&x%!*<}HziLxhCZsq@W+r)tPYaLlms{zSIx}qmW{>u)kTw +W7}gDFAW39}5{g~%=Y!lZbUZ31ME=Q96cu-fH&@NgxteusH{n%wlGOZ34A|KofqMprIR>C|sREO|yXdBwAOE$JRj^znNe(>C{j2W=!%j +hJY<%la!@oqgkMRURggN3H><9beqKJviYJ-#&03=8hw$QMcl|=GB-l%UL-^HLGYd|f)t}H%(Z256+*9 +ZYl@8DLq1{7aK>qMsLw@mHL1;35N4Kg_oj3xA?W8Obi*fJms!$ +t2OJ-}aFLo>u$sP3VRIp=7w`|DxP16*x(uwU%2JZF?Wf7j5%#M?=x{NBTO(jf&khTaaPvvFHhK%2uR! +s?t44>#$rKeQt%x5VLl7oUXTdb+Gr-%H>ST}D`kXZD+&oX`!vQ}4Ln@tf0P7DREOB~AR`m)Q!3>paDEp|ob_151wWMNb@h_h(Rgf{1-$@neA3x$GA7BS{EHKGK{j~I$70Egq6C$6yDa)$@TC}k(E=;5)!8>1T;UhFP4W_dx^u3I%3zu6{C*gFFP&4A4^%;2 +t|kZxnNc2KAv79ycy7WuYBSwjxEO)BMdW}*p0I1h4iA@rK>+{}* +(Ca-3eK=x6b=pgNhb_*>jTqw?ass|<9FEXaCw5V-?K|_WME1)6~^9^t*}tq?JfkSToT5!DR12Ij_8|= +-wZpUWPLYCkTARg49CjRt~O9`q8wN4J;c;5`&fm=7*S6-#;&zB8NbEY`P9N$_$QreNLFQ0+X28_szqf +ygj@{}R7Niko0+Ho+WERB+o?H@A<8t4KqpDd`38$B)4>Aghlcv4RyIR^7<5%5MyD# +geavavgv6BEVnJ|}uwrohk2Qd;^HD<`KAh+G8*<}2VZbgjau~@fhDcWM9EPM(K=OTeiin+;9eiPU`6EYUVTy${QVL7x&_ +C8o*vi@PJY4`#mvQ5v(^J*~N)ukrmw^oajI-4##hSRtHMkH*wq!}jYKk~|)5F<)^yCmF0MQZNa)|AEg +1&R;mQig=Y%L(x}(uC>0_>zT8CtKfv!>=a)o@ +yUB2mV?qX8mp*T#R4)t-2OzN(Y2PHO-5{=CjO!LALPP1@B&5qpa09yxT{8m#8!-76}B>TH@% +!#WM3NcRg@MM4yZf1xbSqA6yc&1bHEn7BZ{KD!d*&sVhBLmz*!YswpreCY(mE4HE|1?MEu0~zj1FEH` +J;qzRW&9S(Nf{rXDe$Cq@F(7-`7vaJ+$Ke-x=s}X%CBj4$e3(3%oz8`!+5Zx3j#D!{HHKQNi?hi{dZF +WNp>{PHMz$Nkklfpj)CPrt7aQr@$la|oSfdUhuX#6~4ynU(9dqB4GmE~d +*VQZ;~iB)Z`_#v>6f-6s^<1Y0=U*TD=5tl~7~%iDvndrO;)-y#nWubbs~EsKOQ*^h0vtUOiN+ZDRqp= +nOCfPo8`jAB#Hb>}x^{AQ3_rqQ#)!yBe(obyVt5(Y8nQY9#}_qJ5xE9we3teYB2Z(c#WRu?vMK*FV9H +*zxHiC#k5tJQdw5LBL)-3X^Kgb+tK<0S*S{IM;+jNb%gk6HPHOz(OIaft(JduvqMa%JR3!W$oQApc=AZeDz|N$c&2jdtmT1vwA= +Fcb5)l#JcQg3ItSIcME*Xn=u_qenlikoXKe|4YI~EJHY+B>w~Re9NG1aUhndQ5kp_b-oFUsY$Y2aY^I +`+BnUiAr4*MsW7V=loNMGAN_r>@%%stnINDNAlWE|zK8+0R-ECs3=tBKNl8pUD|kB%9VQ%##?(a*(`S +q;gSm+2T`bE;=VfCYOSst(p0|0^>%nJ+;lhmy$YLwTsX52(xd%@7!}w**Pc!{HXV$pc#QfXPwlhAw7T +mk@g4i`)R!fcVYq<=sWnW&9@M_(D}H4hpYE=u5(-N-UQaN{e7;f*F8S-~kHPW+nnQKnEsMtXE-D%rp{1%F$2SzJ-zm +E(**sh5jN&%cC13;J(nFME+o+KhO>8TM+Yjf^7TV2X(6anfmuFxWZc!qf&wG3O=Y(Uh$SobR$1yr2)* +Jky(@pG5)n=Q8*@*wwRO5?;WUN(w(>4@a@q;R=S4$t9LW@S96A=}x@o)?kh!f^VF28Ny9wQ%7i2ymoU%pMpB5K#fH+Gm)m9h7O!Q +wte;;l5H6=#9LiI}l1A}gWU`nag`i|xxP3NKEcK1oZ8v26R*ASE#v~KQE|+e9nNHol^2?3DY?`|Ab{I +G@mj!qV9>qIX_N^~n#_t$15iXllEbw@d8ljs}8(Jaqu&w#@_9aVpyL%mv^z4NV=RGK<3Uu^s?@g5zQ^2scHb2!BSaDkc*Kzq<1D$Nyab@^#ezL8 +kafo&Ase0ev|neDU7yIc^d?!=*AIp8qV$m(Y3s1?>TY)e+TPp~W`gEW~wrtmt3P{Ud;QKgczd?T{km3Bh-MG;&VK$$rAg?r> +n9n_AS^yDb6)x$xNa3vNzJeNwYN?p*xnR@ot2vVX$i*(x!8NWlu2Zuaha-?P-l`%F-M~xs!%^uzzV*< +MLGmard&H^%8`iA4Lr8>H-p*61f{+!4)$ndNhbeiz%Z@a=jNd^9x0)4~L* +)$z?lYFAOqoOg0s`$Z1T4BS;i?hPFmNJQuq(X1ZY0At*-_wCWM*8E(=4o*UfSZnI5%=t>!uKV6!qFMR ++BQiaBW;C<2R{2ww#zwfag}tfM*#;Wb#-3@AU3AXKI8-C5~0DZfF~(f8OVHpN!cB+YZep^J`jjA*dG9 +;L0@_cI^X14mkPglIWmMv9ojEpweah7E<@wgsyiT09~@TC@hu~##icJT|e}-<{qG9iDQSK)gSE>l<`Y +J9a~}^jwBJG2fYeoCN{A+=-789dwuiqxoO!5D<`Tm49!$yjZ +|eo>$l5_^QQr30#{K*k$}4_`&4OeP%h=g*2DX5K$t;OUSqlgDsa +X(s2|8$GyEGoYK#Xn4MPjNALQ;!uB+p9I_T4gmxrdO0^2RoBiM()m@MLwTHDQ+EEg6Q_*_J@7bL$R2O +@UZ++l|He)pOEyc@V%9D{RDwc_-uW<#iV#x+Q=^hza&sK4GX$K +UtQnZBG2N^SQJU8ZFj9)-6?P8p?schuHJ9P+0hV8|2cHoWGG~O+VKyr!wY$T1ZOnnjS6J^R^+PYb>)_~Rp5ldT$g2XHru}FOXh$K67#2|`ONVsF +VxlY+}WNk<2MljCFoAM*|7wU7#5I8Y$ek`x@b3y{CIZdJY2Ly6GJAcUG6(@x{Ti}U?G<=Rx=e#kZ=#1 +c}!Wm8Zr_yQUK@|FNFV9L`IF79t_;)wi_~jvyFRlx8}OU&MP#*=T)%VO(}zFCn;=7B +3kmY?3IR$l7@}4`&O>L7{8R>YPNAjHI;ww?QzYa8dI|d?rE_l&Ff{%Q~Ng@{ZOOsk;lravAfk}p$6=l +H?~!8@#V%Uw8aAmnG|&QlqTXqRY}SN4z9 ++ED_&qKiv+G?o2TOS;cHmn<}&iu~&OwU{kFU>)5T915}Uc +5-SCL_5RlzP%TAdjHyHqIMa-RrPB&YI~WIr4$&+-WElUvGB2K7Zd$QWho*-zl4LSI1XRkctghTu-anb +5Q|i;x{c*)5q_E4GH!Aqy77A013s~%wjtv;+on2gDt7t)kfew%WM7@mk4&4O!}dutxN5- +k2ljb;*s&PDW|*7KfnXHeK1c`Fb&G<+fXkv7OXD`b`et)MmaGtg=B|}J^12xh=epSHRMMBb6U40DHNf +|pKisT^SF@LZ?P-+YA*(Yz?UB2;Q+h{*J??-L?=ti9ilVIP^6fV*&RJ(o0)mW(_$zl3|$GO53MN1rFT~l#$x%heNUV8Xj+l6rJd&eerMxE}B6uOAKFD$&lu|N)w4 +8Pl(wpJ+6gQh?$oQ?^;k=03Rkjl6c6lW`?#^4Fh7Tgy3RNH(bV2Z;S9N${=c!QdJ8*`K-{Mu~IJDwj3 +?y5}Wv37ANCZxmO8>Z&gXF)^eG1%GR<5-<7yr6P7RlIQ)C7DOeLVZnX?lwSfHcFXt?Y9i>uFdn;!yM` +c?psI*w`H+l{?S#%dLG#cX3HDY!vysnYyZ0Z%#GCXKwv#3EN!+nNxVrfwaS@mo*(FTIl3sKd* +S%~r?@_Zm#E_h|A#yxvH40s67oUSMggQFq35hZbS5DTEDV@m9NkPy$bFrlN5+;k=j;u2oFGR#C9=pBS{Lt02GAIR +RB`X#x0%)bjdI6g{Cdf}H`S1>9dM(D{afUHsx6v!JP^Cow(N^~#L3_hhd0Td_G@f;E~~mE#nKzXh?EF +wl3%z$_+PzOaS5vTxJ0=W1)5(gv2L2eMSBIGy|2^`MBO@tghbADb*lotD*KMkY&g +kWFiFc8ZNKWaO$oUdi@q)^$bi>F|4G8NV3>n%9O6&jVwmN}AU6J9EG}&AtwKJT^XBzLkOueZYJ8uFVV +?GJdN)yvAyNSA{e%x&|wxkl}S$w)lHlpf^=}?F6*$1phvBo57%vJ8u5QXbZ`BqlkD?Qy_%k^d;~c+>- +aner*F+H&wqGL%(XCP%5xjh?LDa88UuTU$-`)*pj-uPt_Y2&H8nv4sW$?{iD2+6bt|6=HT*m1iMFU$o +MU%Uvz^>vvch-o9tDfe$xxZY}sgfre?Q0O6)y5_PeNR@PX5r?%j~_OAUNjCni$QRaNuri<)9fS_xMg!Jbz=c#~ +6>?-=i!>&9p~;1*T7^eN02UYDdD;GW&3EOxoTt%EXtv%Qwqwm~h`%r-Kf?Z&6aWS(AUB6*n9MwN1tIG +)6KW~V#*va^>%#_v20(s6uVJK^`^RPWD`v}|)*h|~lC&C{Dpm)&q=?H}ODm}&P)4;jDHqpj<+Y@#g=! +wMydqqPJ->%Pc)Wv?ipkXoB2U@3OaGv9MAo}jr>@-I;D=K_!*P>9wQ0_OB;(X`$IP>OS#fS6=VJWYATXW$3Dx2#IeCC_j2hb*ie1thVicXnZ^Yu=f_A>((hyGn2;#mhb&zW9T^??&o +8$jfU@;9!xeM^*ZVjqE4GB~#OMau1M$7&;q4UIeR0lQd>~mc-XE~A1>RXVjb-$U7qxZueKOjs=E`B1dVw!bBWGSAOV3lQM@)kGpQhU=4OR(jQ^t`U +8JjOU)*Zj(uBW$kOKx7>qhxC-mQ=e3rnl6aA-&iGrC4=oZni&cioHknW;{}flc0JUMNdW9&{yZ>o-Uy +#*`QTk6)bbZ*6I8L8^5G!+YK4NHOpcU_+K`FAp%Kb@XIm%ZH<= +|_-^t*X7;vm_z;R^!8wuFcEjCZR)zeg6y2sBOe86sC1V3G#B*V%(o%+^g}n2gKeIZG{E_WQx8b8gnqknwvEJ|t6mh9e9x_ZMn-u +d{RJd_C#OI1N1g)WBP)fgC^uoW=9@+0*B97-WZ@4MsEo{aOM>;K4m^~*)F})|%dv?53A68DO>)v={-N9S#Iop +NZm-QinY+fPkg{*)q3>2~H^jgxqKI;#M=Xj=H^;G#A7-{D>ri|ZX$_5e|;FQw3AH=T|^lP@5Zt~6_U8 +OViqIh5MN8txyLA`fFBm*Vw$P1x3aUfIj>khDWmye`aBGb;>*hM^?40klnwRIq#w6pP3#_zBA3fcbyP +)h>@6aWAK2mnY{22<>?$tXSp00038001HY003}la4%nWWo~3|axY_OVRB?;bT4IfV{~_La&KZ~axQRr +l~mhqn?MkKkJNt{H7}0j&^T44wo#>Gf)lI039zC(p*0NlD$6d~T@ul+?<_WU+Spag6U^nzoHN6mpC5l +<7~YL%Fc}BoEDm8fnFl}MMC|#yvvWu`2y@NXTrvTH$`QDMQvKvYFi+_PqSM!(qch>8&Hv$SmftYFf;<#h7+rRYS!#7J9)248DB%>@beTd* +BNJkqb5up+WtOc}FKu(rUh{tc}4O1w&Zb?(lLDn1K1xRK4@S-`b5v}5zF*}&Ur2F(2WL_UW4JeAqCqa{lD +SyNZ!X62wbRUMwSQk3=UGmlSU~qLg_;@t{SjNtD>Va@}`;XU!6nb}7XjOt&uCfi&25qI)id3LUy5DMA +3(JR%#elpq+C}T_lIZYoxi*#-uVmSVOYHK3))PC=bnDsY5YT=J-HPur!ex*=+`e-7bf_BRj?}(GW6Iy +c(|DRr=eOa+@yG--W^3Pe**o66yCQT%Xi*ACQ%>h7O?w7K(K*~>YPw9lS73c;L%e;)#j9ZQ?6gR!!*5 +5}_kQp9&*ayFq|6cjNi+C%T9h~CH62dttXunc8Y3~V&4KG~zNus-zTvCU)>^`QDtMVZy#EV~U~*lv^= +XKN@fRiu;ula$0|XQR000O8NLB_@^37`}iYEX722ub3ApigXaA|NaUv_0~WN&gWV`yP=WMyY0p=1P}7lW3LBO(;3kW>QY*H3+4Q*nSe +z>FVcl4oD~QnhN;s_OI6N5A}E`uy?lo;*!oKl$SE)8~)V=U+ej;=j_rxb~x8|LUXk;`J##yS@D5<@Lo +?`r_u*DP7*BZ*Kl}d3ALWzrT6&?)viO#r@4~dUCx3yI_KiMbbh)!-TvkD>ce!txVgT+z5MZA%=Y8EPyU@hrS#3k?Vr-Omsfwhy?m2CO +)<>-bbph+eSiP@=KAmJ5Q}zo`Qz=y?R(bmr`yvh-QE0j|JNAaZ`1pmcj@KDb-F#hy1ZL&hy}S%7uT^IhiOrqgA7?%V%zy1k1(rSe(+@VD|>CkBE!YAD+DU{j=|0q(@J`Pk(rHe*Wm`i| +>CM7sranr?HjmQI~JthDp9kF|*r?>-+aHcAoIfEq|m(^t>V)1&n5q +w^O}zWDCzN9XC=@6Nw{_Wbc@DLp@(J}k6L`b%s5_l5o`p84h`*7Mcr{^IiL?!)}QkBz>IL0-K|uP^>` +iVc5xx(owL7vVN<-~azM^XlgMk4`%7`FUA?n=XG!*EjcP>94n!VMbw!|Fq3K=I5P03om&2*;(p2uDZD +X(^c%m^SJ-3%b()eUtQhY+@7V+Z|?57^qWWAyp^1{PjlUJO5Z)_Pygz_^!VwQ|3|cm72?^y`@Ga#V)^ +dw@smHl3sAXwj|sd=FJE8WUcAJx?k|44I(=}<_$%{_IsW`VZfkYyZv1` +O}9VKZ(EAhmo9z+sCl~`OfROhza~AeOzbn0XFg5Cq6It`N75BzYWy<#TQ@vhEb)la`+k<%jAzfdJRNa&&7;KM+IH5qv7O~3?kUF@@lTs)we +MTJfS-0~aOfH)HSs%q4<5fo=F@tzj3#-AWVzm8Wc>8{qW8i0HFEN+49r46e8 +dvhsjF0j@V_sXlHqY6(CnmS&dX{29XF0CnVKLe*9-DiNtnIwCw0OaY(Xftn#N={JZr@|VWgjun9*^iT +vQkS-$qAQuPR%WrWAK8!_ZZZSH`ftU+k1SuUe5B^<7K_Zm9xg=dXJw{XUOsFa+G*!ENab2R{dmrZwT+G1V#uAXkC( +<6b6g&7RAOhaBMhg-Rg9vu*W;`;UQjbrkL4=NFr`?t8b8K_$`Qk>Ebv^`6C2%m5f3!{0FtY(2LlX4u0X!%gB5ISjCl?jm4ES +nJw|><(tuvBz^#Wy1rA;>NfqPZnW|ImE8V3=4)F?>cL-ooq9k7b_8lP`J0UPT^p&{#wt@bF|@jcHUxp +8iR^G8W>=_o6Ri`J{lKi4X|M0tWGRc+#9b73uk9yE7-#t1IVn|I9QO0FW|W&mZl!@QynF2qQ(c|TQQB +Dl{jJv!$#wMx!@g{Cl+=oRtjrAn9Xo_F^6`5s=*L9u~59>I_7Iu`z8Y8wD_zOzYg8^vq!N!faC(D&PtG +Ds`x#R!w`WEZP&g}7d*a8saR>UHu8oSWqxo3UD)>*;|QUs31$i`p{7(DRQH(;lSM}^^OkNIFz!luHwS +iK#`is7XMW^OFk2-C=0IBIwm?iu3=fWhcM+=OKwc+b|0evP-su7CiOF~&a)5AT>*1zq!W|6?Vp5SlS2 +UjVM{U=OQY`a-joSz`9}i1~-9hd0;VFcAM7VP|0#Op_tw^%?LFNaGQ^I}>2Cv6suQBm7l%f!S0nE}j_ +9hHEg_8H*Wq<8v~Y*?1pFzzZhkFuTuYpncOGL8;DMJHFY&#PSha5f+z;T=BP-g%IJ3rHwsj;O2T9t5$X<*2i4`v0W!m0&^SRjw!RCJJJgBA#5H2|z&{`~%oXo}8Vry*K8;yd=#3$U&kM-i;8ellFwcWn2wa5w +#9YHBvIZ7$hlPp15iSBV1nRP4A>jmQfbABHKP&?h(cDrjeC=k&JTb~$-UAB9oAZHz6~a>>m3|HQ0iAI +XCRT*6n9X5Wj-g~AE&NaNia2+uhVNQJ9q<2s?Z)- +xxAYAgqFsBWuVy~fX6^NNc +H(rVS$V8#f3*yA}$`BS??V#)OYO>o9bZ@u_u$nggG!*0%8>bz`|0+YpgZW6bzo*;%~8-{1mf_t<-wh$ +i#9*lwgcT4YQLesb$CqWPC9HFuS;!xeiSY+DSywTeI=H>hd5-V(K}|XW3H7Z)Qzm24Jcb3->)XSRp1t +u9O=n6W4@2u6(8gbRFa_z|z_yJafabqy{7QI`PC};208S$VPK*W9wUyM@+_;`Cl*sJYYA_!EDU0812B +$0uew3A_>+rjTyodW(=CgxOl97UBJ{r-jKL5h!pWW>#AU{^I|JB^8o`e#Owp0BCZ`IbsG_-xQ>}*E<< +m^rzRF2+q_roniwzmA2PRTrpaiDT)i`OxUg}3Gi%xw_K?h0R#!%z*@Yv}ETSUua|147KY|OAOBNE3Zo ++^A#vKu_VVqqY5yQpL(M%`_^bl%E-N9~c?gIZ{*Ree$%yCB>Of!=K>?|u>pl))r2mnSvvQsc6!*Qx<; +z0s~+mYymKgO>@jQ$}3*|5hseYWq*W=O0(5ulJO6z)k=HoRfi@YNM!u-46_o>h+6K&FLZFwit4n~bqR +^a4W)9EVhH0_iXS8Cm=YpV=TI3~zJ8D?t)EA$1;7L*jK=vG5?vRZGyC7k|LxKOiU +hvoA0RB10(s`dns+X)6RZdSb}nwJGpUFG=r~>Kw6qkM8GcRPT8gaii#fqi|qVxh@17xx7Y%XsmIZTIKz^Spo7#Mk9E5^c#FC;S7$i`)?NCI+2 +vmpZA1_hn4A_!3!B~DrJ2_+E{_(2+kLI6^rI)H_-tz5*?bd&73u|hX9ww_dM_849xKxGg$%S{`GLzWt +zXuKZmQZFth`JP}y(8?qNP{O(yVu-?xq(5dvlC?o(1_?Z#*)a26G#i;HO_(5xXBHtmfI-2e+U_=^T>w +Q|A%VPbTb46rXz2vfrd}9PlRO;)gN_r}T*#o9H8fzCb7e3?)34eDp?a{uhxrOKz#U**=oALJ;yMzgv( +y@Ug>z}Gn?V8v5gD<%)TDAP6Dd*1(r#L+7X~(|Ffvf?Ve0WzpRMRmNtRXsn<j*(Vqi} +({aKj=zxoJP2Zv7@&R%8ifCO-W{3j#bNdK^V&DVunyAD30tC}I>I7Quf0T9bSS=+LNV(iE`Q>J8$*;L +rY6dKX-Dxm?o8T+sD0rv2!y-QzXZ4X>ux`gK?5)>?)l(3RZ;v@+VC`;BuJluA5EEi#dVZ;agqgT9)`D +>I}f-X`N#O_2)sZ)}v$_4uXI3ibX&_tsUYf?=UOi*gP58gLS>JWWrQ%jlnJ(^WQ%!nJw@(&pjDRVQQh +wZ7LHRig6IrGAbIbgRX4Gkg%4=cJQ8hVD_UW&`H-BviPaaxIBhq?VGcFbCL7E{k?Z%ZTfUtScLy>OIPA8PP{1QTs--4cnV|7s#db3|t +Y +-t#D>`cn01Q;6_K9=AFs}^UhIaHUl0CS1O%5@1LV0ZxmD*}1E2Z9FpDI_08g@aw63Pmhe +tWMYkzzNnL$o1p`@f6&+Sp`TeHhj0BAr_WB!hU$}GQ|zL4H?_2$+3nC8B@%r6t*jq8ULCVZqZ2n2^5O +?8@>U5BGTYWOtzq#1bwq`hXES7cV!a(L2elPQSVVA6V{W4piL6d2| +&}VdHEgylTFsa9@RNju7v&=p#kp_mPu-=76;A{SW{LF8p_Q4O3XM=)ev(NY&TifI2>an6tGx{FwvFTP +k^6cN!-6})-5IB@|2qA~g4evQ%yHtUKQ^9rW +;<)Owj1hayyJ4Bp9{BnWayO_N@v}nt8l1W!j#TMw!NO~dyXS=zAo=}QvVZn$Bj3tw&MYN;%G8DU+Fc#Gj;Yr5>JxTeXm}IsAbxJEx-f9AC#fSKjDQ84DLAb +*V=FneJko+5tV>OBBlJlyK5`~LWhIN%#J0b=7wF>*h_aLyWcrFT@!}J$1cS*+3TU>+mrfX4kNdkZb_T +QG4(h(O6#W@8Y3eAO}9Y(3*&-{TRkg4h}U{9g`kP;vXe-2~@LdBlEnRDQA!+=R`jE^$PXgD`99{&)&j +6&1{TGLvQqo<`%@aAD1vsF|qps_TG#JpQdRxK2~y_KncsFrfAoMP6?{j0=TdxXT+ +h3gH;5aaDs?=Euy-Kcb`J_<6r +?h!SXt2kD%j_s{=%j-Vg`gLBOX@>Dth>#?HNQQ!W4GEFpATqh^dOu;m8dSuNZH}Nis=RMW;?QY=n=xj +?k83A!V~CgwFwl*h6_J?G?BS+_WKoHTXjX5DSzy2q@u#A+1?Agro=^OpfJhEC`u@v4CEyQWs_TY1pyv +0C$*x2_U(XmH{vU@$K@NcG&H5GE|AKdXnEfXi%hhT#lOvooH5Zi8ru +^9unN|UK^reT}ML{GLNp(jWwVMmO}c!sRAoz58WK^;fYrFBksC0mZ{AZ$P3(SQnbpxhOet8# +{y54AN5emkO*ZJd&~%!O}r`Qg^9)P>__gdp6x&=D9V+lzXQ^;>>48gnX{!r8An;U0NRB$&CGanw&4lE +rRlz_K4YaH=e7rFDr@17#he1`F{{S>=4RcXtnLRQ67+sG;xM^=SLGtrB{{+=vnmIwTI59rNdf|OwN2% +ds>}9(J0Z|v(Am@RIsTcD8&pV$I!cC5~T(}*r_(y+39DhVxS_kU)^CecV$xIIv3xEL~iP8LI_$r +iWS`knbyBf8&`opm(3cPf$FeftuHjQM+--f5Zf)?QgTUDPHVOd3#scSFsLd5Cs|`K7v~q&gE&~B0q{p +HWtlHqYUCmt#LNq9KU3$;77{^-Z_F*dtSwk +(Bc+C5RZ{o~Q8P2$Owozp#)9TizXptMf1qa;4-fBu{4cUGIHcu4~U?2_1`$H$78MHYU<%6zVYYT*Ay( +p9z3)o>oak2YJAU7qPWGR{Rb +VbIcRMsN>itcvw=I5+bW96f80cZi8gjrd1fe4%>=s>9uHPoeQuzY^&73!0@$sV6<6WwVyz0jf9bg4Nd +moU=Zqo(&=GK8nlE~qij*o@`#GnVI$VA7w#4nZ5x<&BS=O_9Vp^8^L@mFh5>eaUM$VZ8j<7F66vDLE4 +pUrZJ4~pVC5)87y@)PG2Lw?uxFGWl(@KA@w63Lw4#+FyD)?myK!JPHt!i?4Lddg4ULlU1-RROr}UhVU +a4s0xoSwPx30MK9*n5bbk>TJnJw0piaB*2LPw#IxsZn?Djo?PBD*u2!W7f6ckEcTVFex?VzsocMkLq= +49KPvB5s?u-`S&??7Sl7*;LDw^3+_k@I^r_kPw1a$#uy{h7&#@MMXQ +Xm3Y>ygYr{Fw&9cz#2W#F1imV%tKFcwtB{~R)kF3ASbZr}$qq~Do1%z}oG8uIcY7k6-Qx$o2Q?&hYKN +8M8Go~3rdR(xO&1>r4MC4sSc&#+b}Plaxg%#)Es9%6Q8++ +$}To=rj3RiZwIvd4@sj3Ea3zFbpfrYS*wc1vU +#bo1kvTw`$!fHE>?3wCL*(_Z6Y*k0*vYHr37|C`LT5Hm;`>@z8vu~?0278soWqV$%ZV)=K=I}8Ll_-T +S&`f>^jY+*vjDmI+bFTQSBDGgSbld>YLqfDpMZllP5R;lT)#Oy{Z)5ATaunjw;*VKXw%UG_D2uBw7TY +TbM$#Y;5m*_)Q;1Bl60|85r3?^d7);H%^5Hb0E!g$8DUT_*;~I7&xg6Rsl6kS5i3DRa08Uw`_@pThFJ +@vkMrb#Y|J00sXS%z%~Zw8u^GFTVsnN +f1nO0rcAT!l9)WHu(OAwkix;RQe*`U%039Yltf?2W%~lBy>*VN0xNl+B1+}z6JJPXo9AJ)@9t@P4U0d +zR*~12T^?Y~^g9rfF7URt7y*2oO;R^1=M|8SIcYU@kgv_qO8uM=8R5#0{)?IZMj7uoq*(%Vqc8)ztHW +Y+asg^J~_^)&U6Zq*X~w +U}SnSZt%3O^0fUh$r;-30nH&T*Lf0g2fXpfp-sws4u|CSACxhdb0KwKnM-!LS@$-z=wqX&ase}H4sbXV@-y)&QLD`sLs6C5unK689$>5dOGK&GY#(+;h(*Gw7=NPCATh>D9ki}9N65@ +(Y&KY7vU$qQi5Wz=V%h6~hc}^do#(++?Ms=~bV1ThyoNt{3`oP_rDe5^nNt!oA+mXKGa93koiJ??3)a +yyH$WbilJ`}H!}bUvMnxO-B#w{*9h7+r`eS3p{8c>fTHMJOp2HT@lI4zN?($J=R)kkBIHR9r{1G$* +pF$klUTq%-NR_vOtY;X6{R3`175ohu*gl+4Xq5oS4=0UgF{~mC5)gdcn5C0_+yns!I~2!D;>(!)uVi> +MYg_r+=jPI9rYn0Tz!~=$S<|xhg_naYQq;twif*;s9 +seY+h6ET|Mm7!SZ3Js1g>?N$4o{7I{{X8;f1>Oeht#67}%aQA-O96l>4x)Q~-KcJr-F!xntAA>s_cVS +^#Xo{D5%^mW(K)PdfqE_Egu!FpB+7#$bPph{c`ow*2KyY??RB)Zx_WrHE{3Uw}<=+N+6hW+6=YsiLt( +w1b;EL;dWU1%IHHV%3$r>uqr*3nGrut-bIVv}~-Csmigf!0>s84JYbb7<|pMY`z@dYWOF7sC6kjFF&& +QK@gMgU!ArFkJl&dra7Mw&J(i?lC6&avURsIq&wZZXO9VZS??--49q{L0n3xyU8vZ*k0M}@YRG4=jvf +K%>`r1HkH9}mQP4z)|oR);P|V;{jey&&RCMC6RLGjWT;Q8NqW+7^XMd^r_J5A2wFqRRHd>awaN34mRh +}7@NJw?%(BS|zBX;&Mwo|ZBW0E8xkvl<^6G1~p=Z_nSrFVn-N0Ex{wuv~jVwMXOyxzkg>>N%MP?~+ZS +zzmXCRq~JxG#cE2rk1`>LUt7h+bcW!d&Kumxb)Jq_8`S%B5cWwV4TxD3E~slV7!M_5+JweD_pi?#Kv6 +lPq}Qt~cR6teeZE23vp4kdmlareX)l-G0I#LNOKN_}_+N`EI{iRk+<%uk9!_wOBPY3_{J` +gEgyr6aq4B7uUCdIZL4kL37(yy~Sy;*VXgTlvi@ +mXKbejj}W*ji&Xk5(?iQFc6$28k|Kghv0UkK!hLnHdA4)f1Or^i-6XZ>FI@^!^(ec(?$mI7VLBn)RT=_-OA8cPFJswyIEkuGNQGCH7FCJ=0_?=+ +D(`h+=1c&~6yA^_f)#n5x^wv;Ruv0J%)U697|M!tvNCn^DL1=$xuM+>AQkzG&C#ld|b#+LlZjjpZJ8! +e#SWpxWX*Nj9y|ZL>qoV`BC+V8J%0jIuBsIYMdP&{>w=L!d|R+13d`^J0P|n>Rd>V`pabGz$vaD$?b} +QWPn{RD+GD*H@w4c%|lm!E?N4vulN2KtU?IhYT09_vC_+i(_30kSN(UUu)xvFy@yw@4#1hY{f9jTt~0 +9aDd9ginbw`yJk!2aKOTbCyD|k+YHeaS3SGKOZC019A{%BWAwB%1Z6G4@)^HAVfT#q`pyPolg0U=zyt +a1b}*4TDyS%_AD&$D1Pnph`uv!yLn9-FB3yu1Ruf>-jb95ISQdq-2&Uby2T+}Zs-@w97=;I{b@i~*B9 +l3HpX{xq&{|DAJzXPhmHvp<~-KvWp>@K4 +A_KS1imGSc?v@`vzLhz8yN*DuS%|8*RX^l;-lw=EgJfqtjgin9w<@Mx7|G&wa!ZMT8Kwk3Nut~ag%8a +Ei=p>dIK7F0g*0}YQjWG1)ixc*6hbEDjMnVV4CHAr3X6-=>ss0oF0TzxU^fc?`8tW8W_dGCpT3#g%J4G!J*>q!x7tLYXh3x5dc<-k3LQ8ESx2tokZXA0`>3gPBk +f?cPYu=V~nnzTtJWI+8KN2%N<-U%kS-pUkuew6Bl^+6QXQs!jP5Nl?~VRs +mdK#Pc{uB(MDIi(bwHv`3OopIA{$wRlXz8i;VPc0?^O9`|~)aSV7% +i~QRdS`e_YrE~)D#Hr0$PwKW))LgscIkDrmSe_1i0%9E&_bxr$Lg`5UhX&Np!{ul^>Xc2$!N~7&Si&+ +-&p~|k%HC-NCVQH2Kcx-E2<=k`K@qa_OZUT!qOdcP%_K*$+<`LR?=Wq=Q?CdR%z1}4!=pp@z&K=daRn +}LKiF4em9COl$JRBavr8+M20`2n-|V|7YnV$b@Yfjr@~gBeYRUkHGS2FnJ}F|>iRCEfjgl3l9eVKs +9qY~538mKm*m)D;zMweX8HumBWEQ>uz5Br!hJ&Ctj(X4?PJ-X?N$u(J(J9PU2ouehTrvTHN)fn$T)lE3n5X +m-5{h|L*%S{Xx14J4aE(G0s6l0551_b)FrGK(oJsv7g-VN +5%)qN2lA7Fxj>bgr*|IE;h!9{55)=!y-5Xi%>)ZR7+>TFn=wRk~%)e!39Ai0NmTad?1qqUudQ)=U1i?!Klo>5WO3yR0#=_pfTlUa{?J+fywln(heY3DbFewD`iTm9CwR4H;tM1<2ek` +5MQ`QiJCM`%Z1>H>mKq1r9of@3Kul1y<>exCE8Mfz9RhE<{Lmx`TF!VYC|Eo^g7LXTgWyj4J~G!1R8A +NI?srA1%k$+n@;Z&ANF!n;Ial@(G=)~AW7^C-6~ihlm@d>v>gN$!{cD~FqzN5A3wpPABO&T_H;wVh!7j2Yf +BG#y)!x^%v_P5DO$#bY4ktQpbe1}DXiEUY2XQRx?sTQw<)Us8>UclS&OEgy{$LEGmy%*;YD-0MsyWFj +M>5LC*7vMAo1Gpy+c(@J`0*eME!l95q4iFrQ6U|#=6px@0u?zJDrR3&ecT+U>>>9XAktocmKFu$f4t- +OsgEce37h}HfRg27UTj=(*0JWQdr)tEC!T~&`#@Y(?pAh%cZe&@p6`|xWuL}s56nvj5e8F4*~5ip}XS +Sj{ENARp;{VO~$89RT;OXb`*`t|E`atQ9PR5^@dJKBA7AT#D-|=Fn_m5Xphic$smq7ZKODE85CKQaE~ +czIkn!RwV|%@b}eUbswuPeBPNo!2OMa+Vi08mQ<1QY-O00;m`Rt8fAjO2*35&!@)Hvj-50001RX>c!Jc +4cm4Z*nhVXkl_>WppoSWnyw=cW`oVVr6nJaCyC3ZFAemk^Y{l{D(RDgQyD0NZvTvc;oERvShPXqEwO` +mrLhT4S^wv2n4toKs58~@9FLtU_gShH@P}hB`X0lJ^kKK_rNb+bpNQgXYa=obum6Yo7|qMw-;BZzo$up~>!syb(YPw+(yR>y-KyS4H?M112r$_4L@$oC7(Yb!8Mr +o>UXt7l{#+u^E#Gd=pJS&T2T0yFwJ$fztUFzadT_@?hNS11#R^UvPxw_tzi#)5vhN{wJTIga!TALNdD +4Wm9HQ4(}ZSqP*I#Y#-6Dub`{!-~I{$pMcfn^>ivkg6~vKUek@1-f0*6Z&5K6jY?s +b!tWgJ(L*C{tdfigFJJ}y0+JAR4c}5zqbh4=RoW*KSgbyc?>=08yi=peXZ2}xb2FOUef|lHp>e#0DTP +$BT%`#pL$X3=(+}7(dON-t-+d+t&c}C?v)fyBes!Zp>Uwl@H$MG%F}hLLA8)R&ZqJ7BKV +!V2S*2aNt`$84&1DXKA_#PnS}*@+7}|o#G*%1!WMJ^fAZ}u%5hbh5Z!>0^XLA-Bdm6R=q>`D+^0KeiM +FKa1EAE(?$TXJjBMPEnU%fiPDxE#1u*5C)pC>cWK2P(!=&QH6EotfHh&CU;JUKZYocwTnqSVJ*dbICH +olV|-XO4k}e*e2!kuTNI<&ag8PoV?Yk%Hs-m* +eX}Tfp=kqN27jy}C&E}P!8}bJ2a~PRTjZ5-WtrjMweBy|ag-NgQOmDRhW?Gmvg{n#2GRs^*3*L-LY_# +3l;1i}}hfJa*&n~B5WvLgaGHEgYoUv;Kv_d{dMr;0kY6P5(^pK0)D?vvN&Jc!O6>4uV +}?ZK2M_dl7_@!yl2VR!BR(q1wWuAKmWY73Qrv4Z*zDL(L884CvB0dBQQ}NU;^kPD|tI<0D7TffgGA-r +G*D0|3F_I{3A1NGehaCT<6Fsvl+H!CE0*1W-I(ZA20C_)q{=TprHzhBW7nwW?=F-W@EUT2DbqjpQaEf +bB?3quRk6Sj=%o(oU*IUmqslU8zIkTy5RiENkuoBhx|s<^<^4njt3e1d5dhE&8gwPmwQqcbohwZes?Fj_|j&R8w<)RfI>Bn +RuuYS`dOrxjEJ()9l2U;I9&TO!v>vM8-$I%&q>#JxKTAx)ew@eVHMRTdePt#WOo5|O)k384f$Nsx{b1 +iTm^DK~7d0}0!Mt#@h(QLP9nh5sw(q`UwWpyFXE=d%+SLI;#GnRk={rMa#nvb1_Tem8bYNF1Hcb694v +9I_1cUTchP&VeJcYZiKKsU&mSP0frA!k|jF3sa>v>5SqwtGUa_T{+)^WrQ=6o9gHhjK#3y2rfrTBOY6 +6OU)%|Vkc_QBUQLZLQRtI?o*8VCb*QzNfETlH(AM$y)_Pso1PAw= +dLDx`(91iM=tT4Z;;l~=>zP?<71R0n)|%2#S>H3kB)kSb-wg-ijXXRgI4DdU0R +^Fo@JH3c%HFR|D>QMv=KV`MkNg|xL#xF{q7t$PS +FOstV}b_%@LQYg4E)!G)BVd*ciRD?%lZQjsudOwAH}ITTENgQF!u+d+r3{x;+p?4qkIFP1v(2O;TWC- +kZ<#KkbvR#OLsTA+k<=mBhI#L1^#ksd=xmgVv}OQdbxMoDxAC5L7e1C!t=3M?N6;oe%!a7d)GlFBR}3 +`OjsoxIqp{4T1g9Ye5s#t8^vZ=L}`P!kvcE%ZriE>l+hiXz?69yX{R_5<8PR`4E0xUQf80jeytJ)8{~ ++F;JY6o5G|Z}`@YDlmC$#Ppo$1ZdX>_e2g_lnz_5Y;4-P_sAs;0H8E7zxIPfndN8^@9gkbF8ZNafl*}3pWrTvMU*Ose!&jWUvkfvi +!jZg{Kur~e=P{606n3c*dP6U)!+0~vTt;J`_hKoNqzyGEbp-CcqDIFhBcilGtcw^m;ZFVcT21&7atU{ +b9`@JGJ}=|S_9@9I`CZb3nzjJGE$W0Z1j@)ElIjU`iGFfFHQU}t>ATPHtDZZAdAE;x4U1YiW +R|RgyZW?V#i~eF9i{%KBaEM-g|WlVXt1oHr94g3{2j(|TJIAuQ({i&ZPKZ31(fulNOGqJOMtxF@dSVQ +6dkA(@xxw`y40E}KiQYp?EBND=}-`{I-MW{Xvs(;}=%tF)NpqL|?v5=L;1uxvG-Qyp=v`LrWr&TZxpd +?Fdq`{0X2V*g3qr*U|?aRTRQeGhS~wA>o=__5Us9^4s8xYEL=#zNsO>2~p#11ADQnK%WL+8`N4Pf7 +g;p}Fev=gumCG*MDEJu=#SgB;|Ra2#C3~m7^{J<@v)0}3#kOAaTV|nEcMXD+Y;}Gm=gltLE-;s~baxj +(-p*p!oO<}0D>kz4X$NGIwq^?%JD1}{ALv7$^Jg(%>XoGNzw&umc#E!C>GJHG@7hj%T%G0>DxPENH#gmT{c{*z?_K#=B;o|EvdH6$Xt$+HbaOvIc2;C|_f7MzYy!bO| +P_%OiFtS7w=UT3(y2)#ep~Tw3*lFnZyEq62Qt*PQ0S@5e(nsQ?UZS8Wtj$yUk843f=VU89>rF2|D +xk?r)u=;q|@X4JiQuHZGpddpF;n)3Mr+v$O#^}!Xd2T|r5H9 +>`WX1P?hXIHNZ^R(yH)F647=T>-jTsrMuh-pfV%?b`5+C$)Be%cVlvA!~^h`#X- +J@LVaBDk59lGugV}NJtLrn$r3k^&zl)>nB)seb(B +atnQW|i6h|ZUkl0~rqD5Ys@=*2Yf(1SKk)Q4e@KzdVtt|2t!HHSvzdfLnrAcV{XR}r=d)!z@`a?}l +qcfvXCg{w$5hZ1~E4=2AYrfhB=jb%@H8l(ljW)EVCNs07bUDN}Vgv4Pa|>Wrm1~23+`5Fb)bkcK=}v@ +@I3MMXUn}}3fs{9VF6J6I=i;S-Y|lnn1J>I6uFqOgHZvx6(KX$a<^fLI4zgARY5cyiiS!^Y0S#Q=TjT +w3_l4_4o8Ge?cGX{exPi#N1~epB_oBWL-FDAeo~AM^C6^XOM+*}@nz$v^$&ngKgwVGm9Lk8D6DMb%94 +GPLl;{=?^z(pLfZ|6}6^woVwlmU8brO@Kv~G-Q$JnCP!kvYYnFq#m4%Ht{LPH47YpLYrZxVAi4 +#(LRXD;3PL8N$dNCjclSC?klSCcC1yIAAw+}|SQzW$-aP={$d!8M&3-4Z#L&b~&5Kd#y~0Jn?O-&Luf +DP**cT12|=p^!=}s59~2QEf<=Zj01+88VmoRr&J&MfzeQB(;4MEP74RKWOFt{pc1ik`Qy&JZ*%^p&2YiUYUuv{mjV;0^Ch=^A +nx-1LrGp)<*4H_yQ#Uh9(wUn`eM9H%@nkVv3=2pVZ|V7Kco-4$__br_|UE;vI<^4pp1@6_Rt?kA|HD) +Xf--JREr5e1uw`oO;orb!E~_X;C7!Z>W{E}$B6GWkBD8y%iml9x{T^SEN;K4T?z@%+)ckBZXGP~BgSu +IsA8E;N&xW9j=d_2y0Mdo)y{JCv{2?ds(|liHf#Xd2ob)<9)BB)ia;P~@CBb|U|NOB`r`7QEpmV(VRPI_kvvcHBe4fyZtCtSqNRKd0Fuz1yt +(*S>?~unaylDDy$xz0ok;fn_-wCJ&k;@1^_b`EiJDc>1`8{-l1v +jl>^lQob{MdZ8};9f0o!Ub{QzowD|GxXn#CJLkDFu9VX1zAZEvfq=eDJn8NrtdC5@R@2%ldxL-Qyy;u +ztfu;RM|RK1_jP9_;vF3$((WVv_C^i<%`c!Jc4cm4Z*nhVXkl_>Wppo +UX>(?BWpOTWd6k@PZ(qle<=-3dcWA&^z(#1#t*U<9U>A@bIiswfMv^h`zA1s>)Xql^2Ocjt8)ENzP|fE*Ectp`SY*8zP-JEb@_OAUtZl@U)?^ISNS)erR96$XSdgn*J=BQtG`^`+ +`YNFFLyte$KRM0gKw@LA6{L)x%z)*zP`JC$RF?D+}-O3<}Tme-S7YK>i+8T@#=N?>7C~M`wzRkXRB6nm#f$BU+0Uv+sFIspWf!%e*Eysf9F@Fe0_QUZ{@q +|n?K)Q|5`pR`IwL8@veOL?(vtq+y7jLT(q0(pYAX3-?4r_-(Ovohr6F2|C*2Y`||GYZFzNhTkfx3Uq9 +?m$OU;Um$$F~>F%Bhe!Y8r{qsA1_xAR6z7@~@cy<5l!~4bj<2T=zudW{QdCMQKZm;h1oxXef)6MlOti +QT`b#?o2RW2XO8-Drl%imV)OCIIL`6%VfyPRLk{QGiso&Q~*{+Fx!hx}FX&+Pr@ol{{(!zb4AcPgmvb!`07kZ!VZ3|G50?vzPz;?e{Os +lW%?~|N7+l^C#cD{NeZc$GPJ9Uvn$>RM)@0Nl3mf`DXW*w~z1g*_q+%r_aCm=QMfp`LnN{z5IbU`10A +yZ=Sw*QNH~4d3jR4d-D9{voF5?>dEu+-S^MG`}W1t&q{f5b@hIswWPnb*8jQCKj)mk-sO6}zIwd8zIk +~6{y*eKKjcH+ye_|7{^cq+{MFTU0<2u-v3c|E|8FyI?r#6Amrk3%E$i>g_0Q$@?(w4h_5M0xlu-P;ZD +yLk?es;Sf>)nilxF!?m$(0RlRNPu?Z3SKIp_ZJ=I-wPqI`b$@W`Kj{RHE~&5lnk4lCvR7yRlU{jWUz< +`4fxZ<8y;+%GQKwtTgp@9%ysmp`$ketey9|BuJ=E2zo?`;c&Xb@Mj;#!bFtI)=Bo*m+!X8Gn8A7UVr% +z53<$?&j{#@6NUV)8(7X+bcHb{_5Y~UfsS*{9pcblMudqvvHD3_0wIx@{3RZ^z4OZd;0wspZ@8))04f +EFL}uGz0rDipC|FN{m|yKlutkXpXK;$jAK=P=dHj0-E6}Se%H+3|89AYCfmd7``?XRQ`sJMzW?2-Vdp +zMdwqqodVgv-lh4`1@;}~BSpKPeY@eP#e3HL6|E$T~JIe}I?o-lR|dtw*@X_x*1G_}L$xvnIKOKfe6oyJufK`Rd0%JbUr-`LoZzfBEd&Z+?9KB>yj`jn9s~c +3=E*dC%G>MF0KUdwDnZrCgkoJo)^qr$4@U_P?K|QH?&|&z^_)`HwHZ{qf7^Pygk|Z=e6+>GS-9k3amN +lzLpGV>X6@(VlivvNqQ{3XL;s0_IPHG&~$I&$4i_B?wTbMO}Vj^ +4C9ZBN`2-EN20DW9q5M_bQ{^96#TGBgG)%3zomxpDf}O0!(lcH~~f;zd`!iYA^3i2S|wqq6|K=Q?tEb +G5CqsHRDBL%pTFPtX4lN4~c4>?87dYQB{Bm9xY+pLpfwnSSN@I{7A@VelmRvNfTRf18^EQaq0!4?Dra +;_rB~$(v2yZ1QHaYhp9k6YH7uXYBFf?efENU{@EW3)6+^Vo??r3ya*<#Mr@L48AA!+`RlL_*G?S44q* +xOa@F@N;6CoDOD4jVP=>aW`>!KJ#no}Y}gz2j=f`_u+9Nf$J8+e)_^ewEF6o#BCrT7Ko1jlHqQdnz%( +!oOas%vv|?H@t(aCmtYT5Is901i0F!}ICLW!M8>S7@+^yU<>>KtC`-XkPzG2_6Z+v3MtYgLrvyMftd* +a3BamsVRv}4*a?U)Ws2c`p4ux()5hQOE`)i`;b!##5ub1666{K(DAWde)&oHp}u47laInu!nNSlHmh@ +C!Jm9Qd$=ps_p#EOS5daPa_|*mLvp1T;>$Fpo<}eB}}+a92KJS-Eds4^DIEPVOsdK!2fA +F3_8v>S26Hpxp!&Lbb(`PmZvFzYHnb|9c^FXBR4M%nRqSN>b8$iq65m=>D< +pkuK#u@R*?7OZSO3uc@bT&c<9ov08w$e+^G@u`ka_4KJZ1Vd$L46L>mT48Z8aUQiiE-VVqiN`8@geIP +Bo)f+0mkOIo_%C?EGCI#$H7BdhhIW=L|4XhBv*X_L6?t|o%G_|Phc=H$7i=`HIDH+HBWR4Edamc1&UUS<3sSsiCHoZ2r(<4NBv248da|C*Y +9EUUdh^HN{2xubKNvPDDug9};`fA0CtNxa#EFzVSPr1VAquL6=8A0r4ug2 +0-bOPJIeu6km`g>Xj=|^B2khJ&7tED^&@6+J~4zPf5M)ai5JfSdt#<%%ygJcqS|<3=uEy@D86`tBp{m +kFJ{Mii-vy0y5{Dk!GKhXUh)tbZY%h#>}6$nV?ysZuhn#X2m{cO%9iDvJQ*R`+lE^cVe!V_5|*8?3~g +*cBM07&u5RTeNFd8(v!9W|7p +ov}*78^U**n!5n=YR#!2-_#GiLeB8F2HAi&j6nRJ_!mrUU$NfMo#70G0tP16aoCD>{TFf5H@e +2KWr{8L+qjngNRo5E&pcKxBZ(0FeQU3s_vh;^Kr`hp^;Nm{v@IOu*s-qy$I_kP;vzKuUm=04V`daB(? +c0aBvx&Fc`BhR4hSQy>!%xBw{uQUas|NC}VkRXZ5kb`5(hf41XxrpuEx(LhS(5=w#d7LRO%flurYu7e{$N-T6A_GJQhzt;ki_3wRT0 +5Bbsm%kL0W!X|RS6lezLwSveBA}ffjAQGaOLu2R+gJC9mEI?!hk+tpcKOijm6Q)430?i6EE6}V!v*N@mh^!#8g2)OYD~PNhvVus +`*j7(QKSWq6(5yfcc9jFBK(hkPiW93Kvf{)lPOL)4D@dszrGk_SQtCW6Z))>^Oa(G<{v0p`DHWttkWx +WP1t}GzK>Aw)DGj7FkkUX(I~o0iu;fpTUv-ATFc~lfDaa%Z<)T6Lo8p@yO#>+nq%@GyKuSA__k^(IPn +ZIk24osetbr8RY7Ur!lm=27NNFIYffP7R4p@Mc7JKt1ge8B%6v#9n({N%9q%@GyKuQBC4Wu-X0{6-R3 +y{)4N&_ja?aiAImi!4*Ak%R&R8jxu~rU99T6Kf!) +fs_VP8c1m%rGbf5c*3~GEWxQl(vb0!D*OyNnwGoG*LED@FtWIB)`b7`o0IbaG>B;Fy4HS+jIrrk)An2Cy<#`uz3h|IbaH82F_)GlmSu(NEskyfRq7J21pqoWq_0cQU*vFTY^N1XJQ~ +TfXo0g1Lrb8$^adIpqzsTUYTXlyunZtGfXo0g1LuOml7|^nkTO8Z04W2c43I +KF$^alV +KD5?GZB^vWG0ZAKxX1xCPhyM|xOHLCORv +6QoR#GC|4&DHEhjkTOBa+=RtUgk=Jm31lXanK+jTQb@>iz!aoRkTOBa1Su1wOpr1`$^-%K|A2q%4rKK*|Cs3#2TNLJ_M`ziOz)4c#pH%DH(Emi)>1l`{lGWoQhYVK7VvOcN=c>@Ei^3=6 +};u-Jsf0>Y9%VQQEfrjDs&>Xmz%(!oOe>}p)5?ccEG +iZii;6|XViOh%2unw!Pp*b(!?a=AFm0GNOdF;R)5eE2EIJk)i;hLdViT5hZU+;0Ogp9>(~fD!bYMC#9 +heSG2Ol=D7+4G}1{MR0O;{|3Hg94&F`bxBOedxj(~0TCbYi;ru!Y6KVqvkcSXiuc^OiPmVY)B{GDw&m +d8{LabyTp93f3vgbp+B*QLa;z>xkMNfwNPT>vj^B3SsFK<~oHrXFr^^tI*i8i>xTTq=IHDVIZL6YXX6 +yQ_Aa<@(NOg*%|XHgr%YtIaJaiCxJ`p)M0{PSYWk*@K{J647df1El8-H!x^Ymj*3-n7qP5DSPGHF@kC +B~TTmIC+`>us3z-zT@df8JKReOmB&tQlSe06onnUG`bVd_HSPC@ONjnWXWN2(g#uqxEl<15cq7eN|Iu +!|=u~3C6mSF3ITZ +6C!sZIq(1*O1(Pbk_VlQ^uBiMvWd3ekorW|X;2E9)xY9jfN&Agk`jspUwKo_gJ7 +w|pd(gea&|cC)Hp3M9}&rK@}rZ>m4-&*}7Bu+aDl^4>y%P@2?GSDAYIiVk53c#(0+457auv$#slWR^y +3HMCJ{D(kX?WI{%=XHsn`#1xcExATB>2#Y}*9UV2A&YKRHJdEROTxZJwBj{~Zt%aHGyYM9ZF;aTr_-J +AXOW`4$j)MaKLV*HH_#gI?^^8NsdS;@c5jxi-^NtsET(-IVTJ!j5BfIiAQ-xtVdF%(CG|*NJMsHMZtnM}&@CBWr1lxguk(siNbQ|bg3w;xfd~e=>u +(%4uY9*l2q4W(>dVAqwiHQAjrOYf~Q?*{+cdlsIYZR1XV;%hduGihc)SEEk|ccpR}RsFD~(A4B6e8i49%Lbx*&R-?RUI) +hBC9u_)$yyg_a;_}|SsUDW5_JlaGO`*18#FC(@5XWwV$e6FqV`Ygph4N%k*A2nLn~oJe4TYp2RTM<=GUc5pBB5A2y)H%> +u^?q>Vi6YdZbObT8j!4uFzt?z$r|JQo6a!YuPVx`qeH$g`G-Q-2IJTfWI|Y+Zmoi7QA5r}ZGse-3EF& +mA{7MCq~Y2;R8B*%HH8)wT7wLG+dYN3Csoag_zIF$lqjdlYlthpNz9=$%&s0VgxvfH#dUgm6sk$F909 +zbz-eL#i;;>t^~C6#ifUyS$3kZ#{{yVY`etz)q#+WLA2xMN)sPiBT<65A^Fou9_Cg5=3DSMF~;TdtI&`88{O +$=c%qI4jo8+~Y%0sHPIGVan^n)T#1-zIPi14v2a*LcL&r|>BA5Np(E?q6RYW#0#QIULYYUX=?!v00}7L9|TiV9~q!G%Bta;?C{uCJ~sBmYP +uzD4ZOIcBN(4*)SsLj)lrA6s&%*$Mrb0W6~HMJc6D#TZwp$UM?cpX!s4d^UPnmdwMjOO{JKyrFvP0hB +OJ082&qL(mo`pE(kO&5_v2h+2un<5GvuD=>86wj=HYaNnAoaPFH}mW@?sD`-H|dK;9Y+gm7hT%e=0p+k!}GNCQDl-Z&esd=$N-kS!Z^5T{k +nX=yRkl%k2rY7Ng?=wK*wM-W%jZE%o_Qa=SwKGo@Nv`~+y4lR8zrl-m{aVcGS4Lw%;t0?Bx?S+Xd@HY#a0L5V^+-PD5i_3pefiOBCT(Jy>gL;PBl@rtL3YJF#)YMMXU5L +%sU$z>Z_YlyKocKCpEDMizp(TE+`}!OX*!HvwDRWKA!b}Q$>?3pw>Ng*hxP*L +~X^6Hr&*7TR@dB$!5~>LTa1tS?E59QfB!t7z(Kd4G{%tzvA+X5*DGfox${Mx@7^VZw9K5%Q?8%2!8HR +Ld?5tFTjzkd9Bb@GV1y}7E-u!Fd;pIl@oY#8psN%n6kF^e2~$)7)=aFXTUVq+GcZb5p>T8g5{=IuBKb3YtV>!29v6&zLlfqRV}^DNkk6fZAYtdop4FIRJbcRbeh%*$$ +=(@u!QnN^6qfOc8)5AMxzFV#?1RjG18w%$x)9s`QXw;0>yPnUgr}-Sd6x(0%MCqFS|rJD?@_{a=C!ce +O+^)j2Ki(@igoJ{6#rpm8!N+o|2x)OGNX`Fr +u>KUS;7GP$(ro9*u>%F9nmEU4H2d8Vyvs_)}~^fJfXVHbsx@gf^_8L_e0mtxb|U7cjZC +>d-D9yP9rGD7A+a(&#|po`}ld5-3QHY^o1+DVJ`LI^ZHV4$>5~>D6^RQK6<=qeaosudOPT)pJ5~lr&J +wEX1O+q~Ex5TzP8=;DmHtqmw_~5~Qo?mfjmQ6gWE%CULgve$LL%NqM0ZI3ZV60T<<*K?|LK5x7H#X0N +fDZjHsNv9m+;W26D8Ms%bF3XACs>xtoGrnAavU_= +P2;p)Ma%^#J{nd)L_zm^a+ibn8n<3&|R~a|p?vs@Fh1c`%w;oxz4`Qu5nN`!=8{y738tJBhcNZi`FJ- +7CxMh)J3FB+rDen7VF58r~%H5&?9dgNlmNR$oZc%}=+frdvld2n6_`PoOA+)`hL5^ft7pWEo9&$GBGz +Vx@%trJH5iTF&oDyw!9Yx)di5(R5Xat#Bzcy1%JirDA4Vqlc^H@&?DphA1!z55CW6I?==s7H`U{lriG +PrxJ20q6mT}7(lkFN;(~FRIpa+bVw2bGknCU6#CeN1?fhjg`y`3b)iYM5O@ujUr0DtfFw?Tj5b#lSh@b&7Xn-`;s%QcV1*Ae4S*i?@O|Y^?isW@yAYDzjCPe}l9Ydv233 +~G92l9nv7NsxvQAyIgzy<5shMaFqp+W8F4&W=-TQqyhd4g!wVT>0pdLL+vB6VfF=)}4x5RiH +pVH64qoh6c3wtrMLoubOTtaN@PJJ^bw!tGG%A@Wcm<X!mZm^LK4HYRB(&6iHwBq{~y{qZgRFYDClx +szXvvuV)aA~A2Vi9UCbIRjV<}Oni8_AvuL7RlM?T^)TYoZ_U4Rckv4m|`6gXNXjM5r}xaydx@Z;F7$n +~~h`*{){kv%S*QbZbLmm#M%^%|tv;86)&kj61QjivS*t@`>(M+O9RZnr;hyY^Es9znqS?E*)}L$K>~svbf-AFlFybo_Ap!e@u| +ltg2H}mcl|Y)^wvfbZr(G(Tw +J#G}iaT(8~e$+Nn7)9Hep6H&l3_X5n&COe}?k?9#N+`{J>dqN&>f->tHW%8Q%`a)W@P1q9Bf^b@UiQ^ +s{Zu}VTT2bQ*L8G0v{SG_Was#{Vnh#M$IUB&*X@*LDu=@_r9w;gs)EW$!DCeV72eTklIk9r5VbkxdP1!xoMz6Tx&Y`w(Zsse>>{8fb~rKH*JE)Lp>hMBEc8A +K-}0rJvELl^6S*hmH6U67%s+bWe*ky7?+SboUa(e7b5m(VwMKm-B~VVXUpr76>%>WbKFtfpHN$>wVrI +)kG*xJgt8JZFW85~?fz(rMDfxI$thUBBU~;nZ{DT@vb&5fL+_UWUYSbq67uPGtmyK;D89tgHMZQEEze +-%COC*$HH*>DIQ}R93&4x*V{WvWnnRXjB){?RJ8pzKf*ms6Yv{jZj5U?SxLL*m!N@)`OWigf4+sB?D0Kp>K`QHo{=Lnqaiuu_u +PGbd`k(jY0cfVS8M#mKR5>4X(^NvDJ0L;c4blZK~Ft$jfT`Cf=c@TSKaK_>+TPr$4^5?R!O3amrg3RW +}`7jJp#@A)UqFzau-^sY0mf)>JBDD3qiN5<;Qv+L!J-OQ%u=psALrrZ)#Jh4<+nF=`~=`y8^;Nr3hkZdxfP@Z +w^0+_yxWfK-P-4;%s(XecECshKPA6w5?ZH67RQObvDx%8S%Ot8 +Zjb=N(JIh17gm4j74Mk&FiWjb{z)jF!?&CbtYPv0|=W$8cmgtc3doT;| +L*J@Ir$nL4&8ogjKouTBm>z{I5@xDioQ_^ix2}35eGjfyU6uW~A6V3;thM8d98CR;+*$|~whkq_cnIk +dDj(OKj>T@e-5Tt`VW_ja>%8haN~9Eu1*#v46V9OHt1mQJYAXu~;CkY{6l%JyA-Qv?m3skUys$)5>e9 +&jj$-W;GdenV<@G9wKu5ZD9DOKC&iyS@p{83mea+U;zC&r4xQjmtS{uiJ6dzB$HkKO9(8KhVR)~DAZ` +bN)%u~~??RY=PLQ#E?FM{g>Ha0PXDeKERh#=|~M;GbpR#FvD8U}5#aqI*#)O2f<9EzHy>D?~sY3^bp6 +RAOkzN#>yxS^g46_RtIrbMl-Q%e$a(pWX!8X8*}a`d<}bwF@lW9V4majW`_roc@0&K&^tz)1vlbqEW6 +Cn|Q+t+aEoe@U3qdT5^ueeZ}=WA0>m(-#eOEg3JhJFO%YNSCG5d!9Ts-5M8Fd@2=HAxL${E(eVB->v> +?`Gn=Gc|l+M(^M@~e-{VARct>eR!L~-&=0K>2$E_P>MUUMKoE*k;2}b3EK*o33FTZ5(O1=RY0=twq}6 +n5OI?HVH6p}He-r|7z{K#13>T2kf~0H2M&ElO#YnzfE$GL&#t@d6n;L7&IKB#Z3OSRnTXLx!IL4vge& +lFCKlDvnReoFFwIxk?!)-U+y2L*bK)xFW;Jf;l>Cma_xloItWD?yLAp@=dI5eT@Pzqi0C$T{FR;}QfgY2f;CFutzhVbahwP;(S=l~#8)0f0eFDREngl&r3 +6}+3ehE*nByPg-j>9*?24x9V}_>d?+m3eS1U`J{rwcQ?|+Yuzxs)VZ-L7$_F=-e?+O}B=S8%s?u!jk& +Pz&yk|&_OE?TZTGpT(<$pkh(BO4k6ixIos}R3N_u9sb)4>CiG=xB-D=3?`T4fbW@h{C-vZpC^q>dev~ +LKJ&Vgcw!3aZO}9oNJhq!sx{uVUZWt(hRjWyrXVof77}~{0ePxl(iy|tu)mX@uUH@tyHqe2=19@-6%bmN+() +|ayE9?QL+Egl&xxw<#*I+Ztsm;)kP^fhspk$&J46av$ln35(djn& +#AuuWzm)Ln2@BLz~ydMV0?Z`GnLFP*6ic{lEGqueOU=p{85fh1er#3Z=xVK1FryxQd5lce<0I5`E~~@ +KkR>zoFAXUq7YJ4~cY4ya~$=S!j9unVNRT7LnD=hGHat-nDNK4uIQK<=`OGF=RvH#OdACbW7&T+o?}T +Bw2kN>I+tCt{WQ?P1XB|ciI{%2{jF_9K`6VFt788MOaV@4S7Elnv`cs6;xiM`;sCDq0nR!{?tt?eo9G +)b}x=umkRX}{f@fgg-=vxlbS!fBmfn>P&8E!q;H`hWf`o+bQe!#da0!2D$q*hj6Jak3zN9YsA|4|AjH +;SS)Q)GJe5$0MQXReYXrG$?OJyj2AOGT>zo+E;=1}Ca_?#;QK(nbzv7^AX}GBXpHaZSjC)Goh-cThj*6y&5zeR^3NaYX?mMt +}Ayc%c{B8RqxfrD6FO$GPyK;PZ3*IsA{@3T7QhxtE(AJb#YYZq8mXj$xH=-Lq`BQGewQ1i>UESND~iV +d*UTwTIz$Vz@y$+_3-Lmr6?B8f2qq&ucAj}YXxv+SL#{kAf08ms;1jg)%PIDO;=W=;qJz2Vx}sCiYmH +v*j1Y))DMZsYBYe;phCCZpkLK=>jX4YnR|%F5|L5cT#C86qt~Ww;`aJdMZrxadQKC7Xwout>Z>6YErp_}{o5(>I%t~d;ZRq6J+kEZmYt5 +b^{q^rLayz1Ll_xHP1+dVAm(Ng#)ZH(;>byotvz{M{0$CsrolZ%6(DEHa&Ot&{<0|4VB-NVQakjvKPP6+fs+w*?MKHdA$P}AZ>D1Legw(2h1tr_{utJ3z#dX_5 +%wOkC6x#baF@(jXa=03$ls8N9hSpQ&jwZ)8UcS7!Ca+id)<=T$Y`x>9?ps#QIOVnXy +ehB7f-$56BNv9)4d{L?poix_F+Suzo@bxth8!u2C3DfN9%W2j|9FaSnu)R0<6MBFtPq9^suFx`JreN$ +Jwc7uN1-*k)bFQofbR}Iv;OY@X*)yN4*K9szw6Oal3T?C>2UnK}SQnq!#?S%F>-O`p4y0s-udn(tFp1 +-qtYMCnf%9C|9Pw_SB(!G>ANvH~)+C8zFZcQDJs(a#pY|{)DKy*w!^ZF)JwZ%gFOns9mcrKNal*WKno +Zrq<)pYCd8do9QcbcWXE2#(2KDCy*_L5SK3P!GVLn_#f{Hh*k_cz@3H{H?!W=O5B_FHn?5K9n)RneDw +O$N#BCZ)0K6CokH+^J+w+55Q^>R}<$2Ko=*W=Wt>1X2z}kCTF_E}`?;^sAX@7nf&4?L}P;dlKa1oVd? +YKDxPlc--IV`3YZ&|KSJ!7f?$B1QY-O00;m`Rt8hEKTH^66953DO#lEL0001RX>c!Jc4cm4Z*nhVXkl +_>WppoUaAR(CcrI{x)m&|J+{Tgq?kfL>IqSob3CLguKw`&n>edt~(UC|NNtI8fQf+~|ST3w3zyMg$yk +DOg%skUQ0G5{GxazFclDojX^mIS{bWa1|vuD%)#P`Q9PS3=v)1%|Ft7Gx~tMj9Oh#hU8efh;RalJ0Yc +~jn&RlX5N^|BCUD_+A#q<#YNrN6N{$EyJ9JB9{VxJljV&W?EX2rzTRVeRNGI}Y`KP)u1Kicia`kAAZL6hTm65$Gn!6Utd~xj}N`1bc{bSLz`jAkE67#tsBcTnym +v?3f+OYSg=f0NvQEaqX8uUEep$A&-Qj}Fc@LF3zf6znf#Xe}1iJL-f+hVodL}m!xSp0Z;{qp?HwV0i~ +6+g}{FK1`hZ@<-zwZ`>XZAv>-d3UcNxfFV3O*LF#m%7XB`_osa*Kf@VPEN1Sj<2r7$@!( +2iHq6g_36=@SF=lT@#ga4{Ob5nh^wN2qI;E2>DnuLrN_LhwLX_cmzSFs>wl{a-D)8>OR>&B7TVy8qSO +ErxptfT$Imn7rmk-LMe9DK)^A0*5>?$r;-M)uj5HKSW;Vl+r6cVHi^E7{Qn$*h4;yWXE8YL3TYdB#s@Nqr4Vnf%%NTX#PQklPh=adA@g!5(f`+Fli%i5F4Vzdoj0N_{<$rxg#DYQH(E_v-#-wAM1+Sx_SKai=Xb*ksi`Gbn$a1z7u<2ez6xv +=B1+eKk|~lvfp%nxv#xoPerNzY~G3go%SztCh5N=`m_CHe<%Gp>CVe2>p$V0^&=(yQPqEv{m;bzl>L# +E9Z8+GOfUPgQrVtqFX^w*#IM!lr@oYv)JUJXw_Qor>(pu|>vaZ=L2u9=+GSoF_?EigvbTV>RP3~De%e +u0-=1`8N6h-SJ!*cEPXFJ(oTP+Le9oNS#=0Y|rtXz7U1v@kS&!k2AyIlfrv6CgX2>G$CuB8?QyJj&*S +>yhoW=~Nq|eyvNcCt^{i>wbv0brr+Q8b)PgsI9Z_y@u1lpg(N*ISE8zYbVJ^<#v5PRO@M%<3&prEf_+xaYv86@dAb=NIA{ +JDzf@iam0aUzJ6OwROzA64LajaysQ)I`luRnEO2GGe +G_uklPMbz@s}&>YYO%NQ6-g-n&Qb&^iWqGVmNd*bkwIE*2xO4nYw@l$`~DeGs<`o#Q-Nom)u9Fvi+(p +&nKlAe<&VdoO(3Gnt%vq?mjOZ?oipNYq)#L1JmtPjUvYtHV{Syu*L201d1XESFOac%(WVvi8we_)5!$ +eACx4X~01IwdW|o+p75=u<_+Fw%{oJk}sDxI_B!B*Q6>4d7s|S3()ZGQz2hDD$&ANWItFpWI7Rhd4qR +WRxu5lTi|QFzlW>3CQz8ohD4HII6$x#A)Kkjh*FyAb5TfC9Iu_5T#SkN05;GowP}e1>R7;9TVZ{$(j*A)94`<6fw^p*N==+FH+%sUYGB|9 +f0icpBAJnl=63eIW3XX>>@dPFa1bC_>)-=~NX?*51=V1+FB1b$0Ld`jNQ_n#Oan1LS2>c~&+jM5#IPC +{6LYZ&YU{--?#&j^s8CP6giabI~K=5qsClt-%wyAS%EI+R<>AUY8nNssX`Mo}(>%pvwi6PBTnq|(pKV +-Imb5>ezK?@k@gDeI^B0lt>r2Q2?%wINGMj~nUQ^(_Z{O43q}7D@$sft88tE$*PJG|(699!7vTqFkIj +iguAa2Osij%p?tcoNz}z49nm=#qrefrSh{+?>Ur5MCc{>kHVU=2ss|dqU^0|v7NC|C_@5X!~L> +ySf?08JHEt!PJC+~qm=jkIM8XBDG{f0ACumTv3kXT`4RJ>}B;CaxUozT-P +->;TZg|h3Jd*M0km+IsqI49aXhoz`%4lUY<{Rv@c-BT3p$N(~MW`ZH;RUva|C!XafrQJ-(oGKU<9^6NR0rO{3>}|vI{MfO79FhHaeidfYR8h>LG$IU#IX^|tB% +)nt98p|JJ%+)j;f`+{P3f(eo}mrwo+$=oZiOo1b$;?6;(v_AC51h)cE?2wr+j}HUI!z&6tlkc+JpW$P +QhHL6Hv|rrsp|7M#N`|5kAH-mKx{XumjI?k^9FHs#oB0L@~^W%!>ABU@wjVWTauGiAEUaQnWvo9vzfh +AlsvSj$&xa*2C-RgvQj*hpbsL39w%e`4s(Gb~vzp=KU}8@x%5)ZZ)i&15t!Hx*nBy98jLZvpyM3@oC0 +dAnVIUd`e8gF-kcO$07KZXHST!v`W4TimKAD3%x_;$Bq~tr6%)J>HLG&DtQ&1u8xE?MUG7FN3@^fKdJ +Jff|+8rX3lT?8eh08L$p?dW5fIqs3m{pdzF9ho`J;MmLb)96!{-GWSIAWt?0TR7@t>Mc>YAp#P_;9`- +E*d=VB)VUsQodg*&ccrI+{*KY7)X_T_{NUT5LH&fiv29_=KXnY`bPzVm7%Wvb)j!|yZ?NwGUee#+`G) +A8V3z^A+h8CsvCfGWE(&?3Bpm>+%^vynq2*uG)ylkzt5sF3-&|Ah5Zs!1b%qe>I^eHlrQ4W8!>H5^7Q +6|93x%2|W^ewfjZcUJf=N5||~;|lrbz4+Y8Xq2HxQN5phM=@-Wr3s^`K!w+V__a#-CFPBh_2H;P+_&) +%b#1Ow$y!>w+Pq16X2abGsqanKA1F6R4TIvoipbVu?WGat8(wKjy*}(r$-w&Dn!_FUZh!sdR-gA8R~6 +z>)<;#BlxYR}qzsE}iTsFs*k&g&J4#;9SBM-7*Z#<>$sfskh8{(D#k!~xL1;>X);JDnP$|bMNXD_o5$ +|9{Z)?vf=+7nPOaa^v(L46mAzMT5Q|$EV2*FrP_Q5`i^2G2;2ESs4eU9?B=2v_89SFP{z~g%ykq3r#e +(dmZ^&LCOsahYg6tem78Y#~^XeT7uT84XTiYStOwqAh!B4JATo_vkzNK)c^&Wg0}P!TG&Kr@d~(1>K8 +5m&>!gfvUO3C0So;iK-v)yQy;VfIH=)!11(@#j=TJC!V=3ied>9Cvk&uX$1hgmzs?aBokzkK&mJMIV0 +8q@9P3(;Yy#;{#XC5g)=kOK6WWwL5`6u-_lvFU2DouXKe`2h0N7jjx4JJ%aY5#0Uw~72QV#-r<#b;G3Of||L|E4+-D361uLRnoeIC$)SWKKtnx&m3p%1%AWDMg>dRRlJp+MDv+W~g&DYQb}U# +3*0|yhC}M)v*jLfGgrzgt!l6QseQ4w96gw6-Ckw_`>n-gih_qsL+Fz_&t$ +a&!jWdb~APq&kyjL0Ip#na`L<6lZe-D<0$s+!2E+#RPZDQB67XRX>XRbO%7}*2C +|10rJ9{bQWYN;q~a$ua{Rf$kTXDh<1a-<<^7Jh(FDNSdYw}uKR@Xk=JLH)9ml_$pgNNmdvWs#mVrBbL +Li0*$UrzliyKw9Wj;mEOCT;D4{)M@pcBPOvq2TOFoP^be))TP25evV=Cbf?MgYHhWjYa +QsH$el7nIau7QA|z=>dG;*YSpHq4}PHInDR7Gu5j_sz3;i%s6P;)nb`uZp&CPtVP&eDm0r?OuKJQ?cm +GbFo7IJA3nbett4Pnq42iIKO-=z7v18C!U|5UC;kdcvYXHM+G4YUl;)rLemL*yF5k?XVx#+=)m1S(ug +}ce{O*2J>}XIG#Zr$lZ$FgxbMwIByxQLFXsh+u**0}!p68uzwbaL^PnqW%yS;tBJbUPu*A@%$O!Nonf +l54wO8uVshb6_L-fBBHZDb#@EcKULEpmUjr7_B?f7+6sv3x>(@8b935B9_WCR~DYC5)cL&12UOzt}d7 +dBXf=yIPrNK3nsgYyT7|HLF+VSz4{we4%BI_;3$}FeWlj#D1vi2eH=gJM*Zr>DZdX#&Tw&$~+TlXZch +k$G>dh18Jl%kHp&}TGacd_*mB4wwIX8db+X?^{zJgty!oxtr76L*%py`s95`%c_7<7LTxaTS4(Y=YO& +e&&uOpomJfDQ6qR1-au>c%@=aTCD6RA&dn84@Zsu**MCM7`d9NuBFKr?kiXAuqnxfk_6~?uqnAJP;X> +;6a^XVm6!!_ow2NcVwUktOMmi$q)nsJO}`iH8!wf++L#4|T&dnisdPV%-OSeP}|jp!a~4U$LU} +!*WCO~fK7c{E_#Xev8Mi5i2I^h)s5EX!u*{Gk0cGmCtQpgKCDa4A*J{mA#wn`-o2t;rujxg-wk|Qbk- +O50P1(;UrX^vq2BL@1L%X_`R;>LD}DCwJEtb!+C>zlIH2CUI!}>D*tXCvJ2AHwZ_jr5FVkngSNz`ept +i-tAS4IH4XqG4-O^J4HU7IjG78!|=A!u=AsL$6-Snm@@()i*O`+yp_?-W{NkzPWXMerlvB+H}>3?(D) +O0p4s(O37o-gw*AH`xzh`Y2kl>!B$Yxru8UzJPam7~Gw&nEn|b*%TGiPLaYzFBO|pW_hJUGroo9_r>p +YZ;~ggB5{0?zZY=vNg&5Kpu^8Fg8*Aeqa1)I2nyL0d%fMpG*!_Wj#F%ViU$Vkdd)DQroK^POpY(?T&O +8gFT&EZZ+HOO^h1#;_n~&o{WO5E)3Rm*q>MVo$-mUu3ny=T+j7LUty{Lqxn=1-#wBbywlV8<1If|clI +xg_*2v7ahfH3u1d{C<`1+Wk250tw+sK8Xm7hUHD8sD^EbVA&)4z$K#0Bg=dY#s+jqRR{uclI&B=cMhi +T%UkK?KHCr3lxW5Gg=7=MpTWBPyn7mR;0`?T?Q+x6(;n?!x}B;c8FGR&u(u1(@?Y01Q=j{otLKw)|eA +N=;UL(9QLtxi9xH+0r6`M%3CMw{iH0+i8Bij_D2I}$#j+w>o3&cjYFo0RecyZzpP=DK;F +F}Q(6_!1-T3IP=_;Tx)APsHMX|cJzPPxzy87zxU4LeC(?#b(_dX$+qmxJUA>K^{@}XJ6YBqM_6O@5YbW{}wdK?6Ya4^w +@wkeW{tf2_{e$tx$NPiBYfJqVRg=0*V_)sZYn#i1<$L<-Kf`^sL(6yfSI(*b2=>)(TU{~#S>IPX(m&N +372Un-hT9I^eBjtE2aYTrI&kFX#luH$yy;MFf9?G4UA6eH9ant5R=c{kr@v=}01bYSft#&;wfYFY<7{ +t&u~`F2ORF0^&fr6@Gik0@lmc5{xPp6Ap8?P`UHQB9A~Y6l;boA!=LbW(lQ=C#=FI?`#2v8zr`R8Gn*D*N*&ld{{Q;-hA9$Mmf#+m@;A!>;o@RgGY4!)6Vt>FX_6MA1f8Z(h2VbSwA8?BO0q13Z; +A!>;o|pZBr`aEPn*D+MVt?R;*d(vlD4&=6Q9jN6z|-6hJk9;U)7+2irnw*G^Kw7%H1`8fb3gDj_v5-nxgYhXxF2wu`*GP6_XAFIKkzj +7123rjQB6+nNAWcG15a~*L*RatPjNqr=j49WlH&ewGWP>caX;WR_d^M%xgX{8Du3WbmA`N9cc%OmH=o +Z^^Okf!W;o6L!1L;UTkc1BU)*o2`+*nbe&9uQ|GaR&r2A1m&HZRkn)^{cC-;NLJn4SH^}$sO>VAwtQQ +ePvis^oIDyQxjxL5zXWHBky +r}16tNT$;QSO%9mZza#D!(q4) +C?R3AT-*2n?ef#}V+FLsASDbo2OQ}=ge&2pS@Vwk#Hr+3IK9b*W>-pH~ep~K$<@vbM{cb!T$?vz!dL` +}$o^z*pV_q*|Y=A8Ro>3)>=r~8|R`#E)PiTizLy^`*?_4{pgzm)Z&{l2nZTfe_-Sue^L&3 +Yx>?(XQlfk?zi=PP=9gV +Po58_!kK2hu5^Fd{C->AZ=3bn`u(<^kHr0w?w?iGYpMG!vtCEK-x2rYs=oI5*!lf{b9z1%_ish_yW{@ +4;eJkeQ`z6|iu-4^zu(sL0q%?YU3osZQ}g=$w%k7}&qvtjgX(i~KfWsR?(Y|LKj6IFkIQ;`vUd(VA8D +TtIIpPZ1H9}{_B!K!#ck)a)aLZLUdi(TUKY;>cv-SuSDufp?w7dVmFMFs>-FsSJMw&h7vz3H_e`%%xd_xI0=`z76PyU)j!?)UBYyVCu(+%M>U%dFQC +_m}Ls-Z|F&^Rma&HtY4i$5V0A`79;Pbbr5Z&u3QLZ|nK^KGVUK?w35DInR3idOnW0-_`zp%QGF^=zcd +@@4Rq->Gt-DVr9dSQupHJ?0qx&tn-%j^~^UAW%$JX-!p4a +b}xF2|N&xg+OKA*BZ+hxoBxZ}&R&qv^Xz@EbhIjmzQ`)Q9Sr;M3$zm)abdOow_ev~iEc|NYPUf-V2jI!R@`2F+!T(2AM7d#(< +`z600-ymi8`=zW`(*54KKcuXeQ^S__`C#Vr`u(mvAK!kz#Qn4K`(5qvbmjR-x?l2qd}qCmxF5v}KG!R +FJ~Q(BCC|t5-7S{f54_M>Zj$FC>3-n8p6#;bepl!ByVL!2mK&#lnP$CMw`D)Szie5rE6>Lj_e;9pH}_ +lm{g&JhxE%X@TzNjgr<(N&p3e^3=VPn;on^f(%6j>&zOB!6u+4gB<@e*dzRvTRmoptC?zhc)1;1a)dj +0Bt+h@CktXJZG+pJgOej)1>_IOJB`%z22J)T0=EBXDvr~U30Tfg7d^O3S%;Kld(kmtj1=~BM83%IW{9 +m@7>7x1z?*NgU-#q)8*{g%&mZBM`7a(};|`z@dCvh#c--EYhNW%K)OJs)@6PgyU&b!U2pv#suT#r?Lr +A9z{b*(h;8+L`wZXG!-<+%M_=?aBR)JfD(rzoqUk6Zg-%=QA_zZ_#-^{1z?c`Tb?n{jU6e)SuV$0bW% +1OWa@S{wch-%a;2sJs(@{2VP9~qmk7a&JH{u?^$n)_V@E!v6S!Yb;bR&^7~zRJ`(o>uio9_llvWcJ~M +h>udVL)>iM|g{&~^;ww@2_@u&M~pAWy~rlR||EBB*(#r@ln`)8#49dW2oy=zf%*D)$2~JNE-G%^uG-`ThJ>naWvi +lJ2+N=VQzLlJ0lK{jU6eiTfqpFJ-+F_gA|AirkN3%el|T@+>!r`%z8Ktk+ie`}KSz?zi)N0O#D_Z_E9 +tXUB7Yi_UQ7x5P|!zbo#y^?ZDDzboBu>-k9BZ~JUl#r;>}e&7YUU()@S+z-4c_X}q_2+wv&+%KHtIp5 +rmdVF!e_dT9u`S7KGh2wkzYuS$lQFgoEmimGFvin_8Kk!QSPo#b+t`d7O`QlpF9g^#e~+Kkzj515Z;w@HF)U_eK4vzc}^NW4(NhZ +|NvEN%s5Z{#nWXiu<<(_gfz0DRI9f`z?=fmbQ2bvfuV7H%aze9_1!*zjU0BVENc_Ke(;vF`n2KRP-n} +N%l+JZ+ny*+Ubq^>8W16mbWDPZ5^MA`sa)KF^@Zh`US@awd8gCEoHw%{g$#{qJCT1?}qvX$H$HAx830 +>Q9tm!vLEHs)DJ2zJ@to_^zudP)a|ci|MuYi9VYt)?zcV3&C#1%W+eNCq}QwL7fx~moLBaPQ+>U;1?A +`V1m}S4e!e&>%LnyT+&^m1&f|WNc23Jj(*2g)FWLR5X3DzX&hEE7z28pvOG&RS_uJ}z+fzJU>3;8aKO +N@77vYu;aIUDoqJC4){7}F2V3*&dS9-9^GU=5b>YYu}>y`R#EuXR_y|xc_d8hu6xSy}K%JM-y759(Yv +wgW=lKler+gd)BcE4o#0QdCLF3ZjRmb-ksZtiz=po4AFYis$~zPJUrKg*|OxSy}8%JM-y759(YQ*pog +^S{H>^DdulsQYJS`PfFit`2Z^_R_A@GKQES +#tM@g!vwY~8UcO8!%LnyTvVYW`?JN7G2fKu%*U>H?+jHE6q}TR52Z{TocXrwB@U*mig!i^saz8k-=zH +3IC%v|IzkkbzbU$AXQ?Y!c^BiWy{b*0c{X2^LEq8d@?(z}NbFj7h9qsa&;|IH3Sw0f?OS<3HyLw&i?s +v!iw7Z{A`%FEbO82|ve(5ZqiuEV*BJvdfL`m$+Zj{j;+BCGJN(Y3@gPe+N3ydt1h)c +PqMoR)_gi++XQ_gU^inEzfbAQPeAK^O1DFnjw~cyz%YN_FPpY5KSM^{Q>X{?zuT=jWJ=nD^clpfZ2+w) +h4W+9~ZL7`k5hc`50BYAN5q+KWfj;<9;FN1)MkN#Z}9Ff~V#F +PQW>JKU!00Yrhoq+MeL)>ZL8VkMv4M`AE9oKlhX8!^fi1{ivtn{!x1>?pJ?T(EX@A=O!Q9qkJ5BKGJa +xZjSPCv&l!W`)xP*_~w3;_jP)+f9^L&`Hc0s(*3BX;{H*4D(+W*bF;zIR`*+;+%M?$4r6Vtx +F7XY+&^kh#r^8<3R$nj{g%4lGxtk=Kgy@MA9%5cdIjBY$Nj!_zvTHyy5E-jT^-%;%<~CI_wzPYSug6T +xPR21?aTe3!@1n!iN=>>kEfeQyBzKDbhW=<+T$rb+9e(418&aC{nGw^XS%;dSuekK#r>$K;{H*4wlDV +s&Z+wW7u5YA?Si@=@RW7Gn?0Ujt74uHikDsY+j75m&!7mjq0JRi&FdL{ +0+Jil4e{k8|WS!TV0=Yy*jJHOd-f4{Bnw>`*B(*3skd|Y`x&h~f)=Is8lx~c=5QBTGFqxNiH?w54Gqc +a^WJs)YGk0tld#`BT5-|H*8Y`I@L$xY&Z+dZDrbG^2@-#hoW=qR_be^crHq@IfVNA1~p-0#=(0X${T2 +Y5lx2XKn}0ekU$0Q>TMfS1Mdk+>gt@mF>k&xbzKct!q6Jr((n+EbCg`m4ylR*`>g8<78fyLav0b#*)* +@qpa6y41gOd9}Y(J9$@cqjz%9-`K1T?&=L{OUp~O)wMybe|C9uP+MND4K{kqtIMmWYJ>jS!Ki|`#|L{ +G8_T^@{p*kQ2WK``M|eE#o99lSSX-%Gvwmjv~Y-f-KYn-3hj<-n1}gF +iZOZ1L8ki${;$c+;`k{@VFusMHVlRtw*3^*V+y7{>$P;W!=wZ;ay&;LUNo3A{Cqw}7|D@iy>VdS2WBF|@ty}=^FgZTJ@Y}T=Vms-RL{+9gsGkzebz|z+{{N +K)pIi+4c+s^BB>d)!O-s+w8_w`4BBGo)dp=d^iK@Bz|cQ6XosPHX3#F7SM9k9_g9^jy~f;i0Yk4fDDT +_%4a)oW=LY3{`+-4u-}V}m_w71^^1fYfP~Er0A~K7;*=49%>8(0L%`$HV3^i+99})zVSf;^Hv$|VNhM +J|_YBAKT>sFhgW>L2m7;08@i_hCWgR*(s^B0Ea_`Ln4LHWGxHz=RCzcMJFx4$+hpSQm;Xxz7jg%Nnbf +N|Hl$#>)p28?^w?T)~M22@Xd){~+)8Z_?MLNW$78J<>8eAbf+erQl-Bi54!+-y+g-_{d5??(oW`?Zi% +aEk$zx!OtY*S|Gr+^tUX@Bhw#$|9^M?K))8xL*tH5qQ{ualg6=i61dw+^tUX&ySj~8P%;I8G#=gRCTM +9)cW@Z)!pih&|?OTyVXtFdfb3zrVLq9XiTk&Z%>5dnYDor373&164&r+MNpRdj+-oFll!WOfz1}2Lkx0@C +l36-8Rj)zU;I_mcKfYqOio%2|TdO$UlPN3dF*;Hdv1n6J+B1oP$j-vsmZc^|=if&LG +{{2T6$vkSCx!Va7DJI;1U)#+fSYlKIKWL=}q1}FZ6bX_B0;}d_~=$+tTT^obe(aBBQXhdxnbP&Z@H>Cz|a+ptb!(4$Y(H)5T}faJDTGj$pRx|_A4snZzH9j#SOoy +LIfYOQSQGzK(3THOR@47xNwTHyp{47xNwTIB?047xNwTImF447xNwTI~d847xNwTJZ#C47xNwTJ;2G4 +7xNwTKNQK47xNwTKxoO47xNwS^))S47xNwS_OqP1~flf357HUG(TDmg){~p6v1(Co9L`DwF4!lOjDjAIPPl`L+~uYaaZw6jU4Sj>8U0vd5 +++?pUsS)9VkuJ!gRktu<9im_#(k^FPj+Lmk3t9#8ACVu-X)X(#FaTl(cGM1YaRoZHdSj*?|&Q4K(mog +4O!PC0`?0?M%Q>y-u*|B`)~}!ErB}sN_w8RWEVLw+N1Vi7z#BumdHsS{Va7R4NNT2>I8cl36Xx&f5el +orMOzL$KHY5rkGt7ME$Cq3mFYRf3|pi`)iHN&d^oM5)KLrSB|0+CH-$=UC9_D5cc`>hGSFmrhe~QC-6p>~)Z0yx@h7`G)P@k?iv;Um+zD(rJk(}TW|K>(t) +R>vmrxr)X!WB6tEHOsoQ&>Ji7I?Elg}M0MYVvI{FGqT$;^?)q1IBMjN$(h9(S{uc>*d_G736@Jn>Lzs +CH7CWt*W^P^OwE30EBjOAntST=x`ReVXvNt1V32GX(3K5`BG^aPs(=?^?vy1jW->t?XMkQUOlz +azv0}Vuk8Ed{lVe2rT)q_%G=-U4;EK@r~CWL?yHSBHeNs?;Yzm?wVsWZJ(aa}i@Svl3)SnjQEwJO +!L#lhO*+Qw3UWASuveg7ZN77Dg{@w6J0{Z#p(14nKi4dE7R9-LcWKG|DYTwmK<9xUI}U))f6%>Mdy-F +CMT|6t!*t#|kP>x;e3lgrDCEB%2=X7>NEx3byaVs*Wh^}Bi}`h)$k*ovNvo{FB1o{64~ei1zvJs-Ugy +%@a|y&U~AdL{Z*^lJ24^m_D0^k(!{^y}z1(Ql)-qj#crqxYisqYt7FqmQDGqfeqwqtBwxqc5T_qpzZ` +qi>>bqwk{M4=)VwA3iXAaCmX}(D32mBg03BKN&tYe0=ys%pcdP1$_H0M{YcN`|-v2mvB&}MS@8EvwTm +7wXG3y{Ct(tNvs4`@PL0gERtUm#Ci3!B9BIIUGN6}?MV=%eNmLqn@>`Dr`x?^@F#aTKQ%EoX?HBE@&bS#vO;goKg5W^|qG$Dr5u +~0UK)3H!ChEwV(8^h^XC>z6#Vxde7r(>aP45wqEYz(JUp==DNQ=x1Or$eD^45ve(Yz(JEp==DNQ=x1O +r&FP945wqEYz(Jkp==DNW1(yer*olf45xFU2{D`!QWIi0rKBdra7sx{h~Y*S5O9j&l$4qf!-f2(G>hS +smYNX5DJ?Z2hGVBiuozCCQIU<|l$6TGa9T-aV>rDDCmX|s-AOT=)==3PPH(-*#&8XGiU`GUT0LcBIGq +h;V>qRpCd6=BH)UfurJAxaoKA)klwvp?3}s_DC6}@>oDxgf7*0v0Yz)_IOpM{QPRhn`BfBSoDTdR2Pd +0`dZK6qFis7{5la1j(^b +aA|NaUv_0~WN&gWV`yP=WMyw_awu8K)Ln`rJGSd6^)0a-U+a77OYFps?^$ae`WEmz_>%;O#J&L3R&WLY3Ct +Y7B>LmbCiXc8KmNJT0kG>^`!1{v_nq$b_xb&c*6QWaxj}#Dj$PKCZ|||r43>NSv-_s-2j{KT-s;}YMZdp1816lDt~*@zM|;;!tN*v=k5-3+)Be!vonIXcN7l(O#EO6W<&l3NJbcn09UUzDD +=L!unEHO}`-8RKsCUs<|8(|SM|$`9E0@(j?ETi={T2LYZNGKQKii!)-MQoNi9?6(>>Ro6*x}C6;|C8N +v6ig$oja`Xzed<{-LiIByZzl$1ytwO8QJspTlQ2QaJhx8M@+6au=U38c<}ICYg0XVnCWhV^hbDjm-NS +Zd=}>k-k#}C^7wVqpW-@e|1^)!KKB{kzDD{l_~$yLKg-)UNq>&(EdKL64m;0Z^7Cnt{sNEFCjCVohv_ +fzI81+;>mKQ^@c8U=U*+vtUaxVTo&W1R4vYT=KTkI9Z*skc^Cx(V>+EyiVY`ap2^ge<10+20UZB`p9@79Kge{N2jd^}o+0@vkHYkacysot98(T;{P-t4B^HO{%VB5v +DF0lREXSHdT{-?K*I8QEBK?6|r~j+KygtL_26cXt;*O!Xq4>1@94HRDp?LWz4&S&Hq_|yminoB`9KiZ +s+`c}=Ylh;6;t*#J6n6~8tK!80`emSa)xEev?fP{{E1U7!^)X&E7&jP?bYsRRy1{s5%y`u?u8_JuOH_ +k#%U~Si8;nnNeT;+SOkx~^pXxHk0eDG_gI;bo4r +iGW<8=Y!5GEbQf!EnE4qpZdj6;;P7>D*rjMqzsafQhBSwb3&TL$A8-(Y;I>$`E#%Z+heHx7CN;~-~q; +{wJ3U@0*UQ4$yj{AP9I5WXxJuTxQlld$p_hbfiBIOs`?!>`}07>D>tjDw!UIOs`?gPz1V=t+!&p2Rrh +l@{X=Cy8;;lNbj*iE+@A7zaIxanQFI#uwzqVbn9aaj@4ZF`kbb2ht`m4ssIX0Mv9C2Mi`L4$;%Pafnk +C#^G~QyK%xeCthVR4pXEkHx3s>662t!b>q-Jts95-MY(b4x2@*JrJX9ECov9sN{mDEg4{TSFUXBU_;S +?7A$qzcPE#AdVQsvQ80TcG48{R>X)!LjaS7wlJ}bs0Hx6;;jd6&d7UPl|*TuNx#wCnPH~&DKqTD#d-% +=O{yIc^)^C_wrjBf_xP_^qJZ?Epc*ERR-g_MHR`7gI?4Ur>Tu^&W%I+v=|3cWp(2aCnLroOv(+1Fj@uG +#-*YPoMoA7te +jvqDq-CUUX4K7vs7+RTN^@X9-hzi~}yxx^bzfQZ&YkE~?~(@x0tP#Lv3nu;?XD0pocsaSBD1lDTnBjB +C1az1p}Y#)YB^xafH=al+>ow!}%baZZxTV_esbOZ&#l=EmVX=k3OIcdEd7X044&7>D>p)yB7^8#fyc% +jU-6bF(gSdbHt?6Jy(9yy&8e?oJiSjW4RG0(x3EE)`Ys!nowdb@z=+7~hf`4&l4697UBv-S`&8xa7v` +_?+4p87hx)(5s4Z$&E`zmAo)6EpbX1m)y97ajB@Hi*eA4y8UM{7&jXZ#hohK3**{Fl{R9W6W|8V8?Oe +&tM0}njF-)gOBmO!jpyaYp?x*ocwQS01&o7SYK$+$je|b#+IU_V_Xy+sO0SF?2fge!)7sf%~Qbm<=VI1V`fN{xLbefZhXE_&Yk#O2Io-3a*V8yi-LJ&D@8vldmLeMOfzt5H(95#nFei*xd&6N~ZoJTb~sennIUD*rjDw!UIOy|s<2LOZ=QDiEx^aVXgK^Mvd{C{l;ZXA>ZW +6|!c}9#&ZhSEq7j~)`jBf(t4!!+{&*a=NZX1jnj64q+V|;`0sh*!3 )-Tq_Tcup9Xo~fdXacEx{#*yNDW}4EtWhjpEw*bX;@BRVA +%(pZyZB&sc4(s44DK0%3YcUiTHmWRy;?hl=x-}JDGp>90k62To2ORP#Xw0}}FplvngmLK|ZW6|!eL** +ILYR!DajB-Fc@wAZTO%ZlL;HEbc&M<; +Ha}4tiRQ(}S`2)G?)T%TOHSSBT=08He^6OXJc#oCTN0bJ}iLE;GKJO5+m6rJ9OFaq;;sA&l!j-!P}g)K +<2ku;yZ#@~h*FL< +AdCdPHmIADLiE1db5am|{Fo*CC&;gm40`v$k7&A3hPjNpT1Fm4%)V|;`0sV-w2ezc{%`3KsU1LM#_^HWl5cV&U7{V*O(%ZW)SWd_(c6E>p +bVx3?|Gi)-Hbqv^%4<_TqU_Tzi)amQ-RLVM;2N!8pdR5XPmF3g~mb4`0)agPip~e8 +G%^T#mvx$Ypq(3g|@@#z8OYKKv$9oF{C|xMe7g@he1e-G^c=Xs=;4%(&(|I5jC==w8DG6~>D;_8JzvO9k|8{4^Eam$ykJ714~-0w+IJgK^7X9OG9A<6F~>OEnb<N(^i=gd>{38s;ErW54Um=VGmeX!Gl- +}M3?Ne?y%&9gG;R|~0k1obFmpC=uILKA3jqAE`(2K&jO}B9JPzK|c!8pdR5XM2D^R97ezoCS2&=VMkF +QKeUoI+7$K}(#FRf%uJ*TuN*5@*pEcktncu|F{ww+zNHzQOoZmy0Tr8!z}t!%ztmeBS-rkB8g_gAUjM8$@-L010m2hK&>a +qY-Lw9_6aMJJus_wqgx4;gKRsBnZeG36KQp?}9rXtNTcW@Ne{B-^w7)V?;Re?IgW>Y@FIAUVd#v{KU* +S|cFzAnlVIVra>DeEEe_KbptE=j??&_Xi@o!$cW&h3{_uO&p;PHD-cI@y}I)_gjI&^2}!1r!D(Yfn*= +lF?(hfY{a)_T-W$~Ac2w3kj@s@0(X?6_`&?!G0=peQfMejq2ie9!aN>E +}I0{ZYZySffiI205P@?Eg6pfs+TM&;oz2u+Q02LO8&#g$U!ux$yA@TQ+a +6STZhKMXx$Q%Bl3Vnhg_GTFGZkmLS7RzpbI)cf&T-FSDp97+RGi(O%T%1&lY2Zt#d+;Dn2M9yYcdsQw +8zJ7KdNlpcK;P}j*r`4qsqr^2~|FBe}gI?x9_0J$L()X4RdQnr+XWUYO`!D`i8t6RkhMqi^z5W)iAYo +BpyUD%&gs-iiZ%?%D~j3@B8nfs(y;wjj4JVRkg}qi_YtNs4Bl;M-lEoRlUZj)tst-hpKu%wjHT=q8et +_j=~;6F-)u#o$XN+m1nV|?mC8Qm{==}YiazQjieXxf==| +?OagtTk-FKs^#>N{r&&&r%=2P>(Nao*er!F;V&cs~GHxIr`2bfDaHt73kCAmrY<_c +Ce?3hXU|0hXzVt?=w9jM0+yh#V#*nwB*fEPRPDjjGDC)Hnpl0r9bZiVoPZ15eWdA#E=<)>qj_2zh()yg#Qu5Qf@o3 +c4`To-n^-R_D~EF8#!r*qu6*K^`JK%(($okcUa;*Yn0}r1J}U<8{*c6}|BW>HL!3psS?8uj!4qNawz8 +<89Kpv)jPRn%!gx^NwipFV&6zqxSr3b%TJRj8N0Xext9jW-Q_NN#_ZFbLAO25X+b@+GZ@{4@r+@{1NG +~jIl^(D{thu5toCjq{lM;nDki2pOCJWW}>eMDuLU|B)O4Hm2O+vq$W(0M@bHoZNj8_jO19ZUyvT=3cu +v3TyD2wsbY!Fp7^C&^qm)6BVBzpIqWAzh^kC;tS=VX7X;gnVV2TnJ9oP`=59*2D~Doa +&Gg%>ree+R*yvB!`*0Gxd2#xhMFEL*;o#*(V(7=SU9Ibm1ePCppX#I+1Ggj&e|S=))ICR#`@#hAQ)p@ +=zYgFOjUW1o>r>Rq->z9F^rA<)U2pNc^62rWK)D@>+E!t96j(Yt&rTL?NHoNevN|5?@a8}%qp(KxAY)aoXPF_Gh1;SWu)Lo9@YMxsgH`}Pf1pDHc~ +$$Rpkih@j1yVMHu2QNS^$*j^r;%4s(RYUy-V2EHwU_WHnI*EvQ1IvQOVy?rYRals`xv~GQrPCD) +>9fF-0?h>iv$(R!^^zs`8t8m60>?O7MXXldQ&YX6KEge3Cn}V(Uy?5}Y3L+fg2=0rF!chbcCraT)(N$ +tp+akjF_@ULopgS{0a->>bGc8p&Sr8+)=yaGg5((c=dX9kG_IlfC}g7610jBVSeGPx_;ygJpl^W>uzN +^GBV2_q@Nf`~F^kd2s*Q9`*OTqXlHW|5m#FvlqH&!&rxhzJK55f~b?~j0T;-aM>Sr&UaUr{?r0NrgQC +_S6#6*4SVFaV~3}mwyBt-%d5RJ-IdPjV68XmUGzIcwUn|{+uLqAt?+wtGcoV;{nbu)?M$!NS@B0|NoD +D~-IX{n1irRD(x?M}x%;LG5v;OpS`<16DIjUOEUc +zkvI(D>o;PsWdoA00nF{%JUsZ&4HWj+4hbt(MpBG|jS)S@to@KG6ZQ>|>UF%(4%NX4%Iq`J7$%cUc6lh*R+NZh5h8gwLX(^^eB68CAX79EKjwN{& +sB>JFBM-qKNR@+VV0bR##q7Ud=b`yO-*Rz}G0~#MU-Un?OA2;3yZ5kgp-Un?OA2;3yZ5kgp-Un?OA2; +3yZ5kgh-Un?OA1~eqZ5kgh-Un?OA1~eqZ5kgh(FZg>UZM|Ze7r;-(D-{1Wj3}@^v#1G(1AIw6z%>4Ey)$KQWRT?D&aX`SH?zKz!xGW#0?~~Z5;B}+ev3o-FAe3mFjTH(p+5ppO9KQH00008 +07zB_Q(OU(CN38M0QZss044wc0B~t=FJE?LZe(wAFJow7a%5$6FKl6MXJ}<&a%FdIZ)9a`E^vA6omrO +~$92cwc@DorTTDX95y`H`&V)(C#iA1~4k?9Mh$Mq^DWl)@Ipc_+ok@!cU(6_4k*Z;Y-;K0|8+}s)+x!fNd=?!iLn|H_8hlBn54g`n3ek +iyyT<;IA9Sv@duO4b^W&ghY`>u|LH-gRn=Hazly}|l$bok2k?r6O?KD>2V|KC<`yg3?P?u~-}jm_a`9 +9&GQ*ytUZwC1DPaVH->eAYor_MdScJ}!S?=rLGo%OMYn_CKya{oaH4=3J~<^f6Io5nIO+-5tEZAup}4^oW-a9@klqyw7!3&-Z!#tm!}C0 +So4OvdPe9BsJURJq6QR#B-k^`n3H{Io^wn(WhqIxtWwP2u@!}?? +f(~zVgryJoL@gknEwqc<93ex1!LGmI-~0;O%Jw%g~QpIQ`Jd=?l=ebNWu8FFSqZq3@xeR^y@Xoqqaqo +YTiQ=7v7LF$Mi5KtF?8Yo{+jKbj5w$ja#p=^er8hqIwCq<54f=zFK{p+Bj{L*GL`eU*12S3`AsimA?^ +Ut~_-3H1F$u7|$)R-e=N(4V{(0R5$=cQie>r{P&D=sU^jd#CTAKY4kl@1bw%*{e<;SMP92HYj;tNP4F +{t9Lx~J@n1Ddgy!TPhKnU^l>7W2vNC4Si +uEcL|%b?3}(|vYF~0`hkaj;Gu6`m2>({bov}{<;v*`PCvAQzOZCdfPUzmzK8zg<)QDPZ;G$VlFb@A{X +EHBZORR;xF1?^zsOaa0{0`&{h(6Zk7jcpR^H^f4|<;apwG8}2jo2WLC`O0D7MLpcl123(C)V{0_)-9=`)}fhRFIVux6KFE3MgZbU%Y% +Y|~Qy=s^^+BJP`cQse>O=WF^+C^3ALKmsLC;Yi+wH(p;_!(-|7p +_*N0cqHlzUfU$9+?1#fdO4^sd3{%Fcx+d0N?u>c +>sxyLYOUO?;Wn-NE535G;?#HL^`V}kGCWXzx73GvidnH>gbH97)myP<%^7<0>K`)B>j=a8QUfat+UFW_Tp854z9rxTAhVYk2s5>ZxBg>MzUdgFdg;q=vuP;zv%J4Yy`UPirEcfP0)c0$6%1iwkQa^JadFq#q`asRydVQ#8x6}uH-n_n);jyK@8?O&?G3W +3|)Q92=-kS@0xzFLT_4=Uub9n5jU&p+D=6>JWxIlebDDk@Id)I +^g+)-ALKmrLC--S1qMbDnh^D1dn9(tC7{0pf643RwJt~CU_QM^&<)Tx +I-&5ME8!hH0sTnYNNQ@Nm*9QuoJ`ZDzENPRvQ_YU=4<#)<9mn-ei +lCt_`TdQBJ)t7c?Nm+eihn6Mvp_YP9S$1UgE$4D=Ph%*0hgQM)9bsQ?DR*dz`5i^n=VQ)O-<8)dn)=I +1@T}&t&6S+XEmu}w^7_(RecQR*a;@N5>Rhh8f`>9Zd~A8@+fu*kbGfcEJWzhOULW*wWOx8c#dv)w!?P +Uf3tnG3jiGFFxl&qR^7^39Nqyloh6T*!y7c-8eg5qp`mU^gRnq#B)pwQF_X~J@THmMjP5Gs#_31DMK3 +F{UXH#EJ>sQ6bpSJ1 +fGzRIkO(DSpa;f&^ifMg1jDh#2hyHBn%lVzME!Ou|f6rNc=`eiFG0T=R==EC{fJIu;N9k_@5< +`CTEXM3{xYq;ux!)P>cce_^n&#Ey?P;+M)&eVy%8TSMVrWtj|B=sc-A`i%#%V-RqZYW3Ko5mG +S!3PU|n#>*E_!)TiH5;oab=Z|n7|p5c+UXxUQVc8eC&Q;tKHi>1Dl)-Mm7#}j$;dFtCzf3IbDpnNs+`qF`$lGiWxz|Bf +zc%(ykmX+7{UO%nsk@EVwowlc&$y2{<)c0P$I$j^-dA&a9r9O`VR0{jwQ_lVDX(Abc?`C*xsDFp6f!(Zn$4A{U-TLtN3*#C^((f9hrB)y+f#ow^;dTdk3Vs*4{YZemt0g?P&!VzZ~ipy6B%D +y&n3mpf6c{(953R@jG*uw}Qtqt6z(nvH$svA+`72f5U{v_N0%%1w~{FKNxfKFC#GxhZ15-XiShe^o`mzFIu?1-GxPu&-ugUwQ1WGW +LZd8J6SrrKOva+XuapZXe2*-R-;CoLe#MHwpWD^Ji5A?E557uoBpJl;p8<`ydzO_N#&YvblXnNgl!Ng +Io>lH`;`Kf7mjFSG^dzdd)iNgj}^ihaWTC5j?(mb8u +yRvpen&2Tz!ahrO+|=F7c}p{{Ni+#ZhBC!f^q5*!LTlYb?hc49afkCKY$@+Mb*0{$JG?QU-B%X+4Jd8 +7CE))Pnf@4NKPb0^PVy11s27}rjpI)35QrL_~MpSrO2%=xwR7fznK5F87(O-t!kYBYfAAyQpsbp?8q) +g#brS-l2&J*(G2Z)Eib=*_I&1ih8jTcEeIdK>f(*YV+|(s1iW#^*D0nxfhv#f>!|YBeiv09+mopB*bbq}V>^s0kL?JmX>10}IN0kQrs7C%giOU@-c +U@%G2V!nN|dQF6-RfY&Qu)Q>CRJ7aa=c=OvORnXfYK>bc6TXQB+yK9sE7y9PhW^N0s;6F;scK{Q;`H- +~JF)-fw?|Y7$$sGZCLcF$t|>9;(MtOd@NUk|$710&AP6>`4@pxH_f@P9dg~u-fM1-#{^ms?(Z?PotOw +)iHIRMlp%0*`A2sL{WP+WiIrOQPrkHneYD-RFjz6Mm&R}j>$C1Srn6)ItBpeP)uTK8}U3oVv?9z=Ayo +ZqV}PYxu`!yH3_QSoQM}tOoD2ek{3}-f@+#Jc?LxtQ)n*#SyZ+62~F8QLp2GiZ5}|EP}IIq8THRm)qe ++srUCu}pOD(jDHH5pqRL0$U!lrJ;B(KR%!lB&QRPGMuTkabSVNVg<8M$+Vr%5HuAL;dmI)0X37sUgW< +C+C!X&h2{tt2$CZW|$ZA@oU2d(AZO-;$h!)-i>jCpC$!V +J>2WR2`hD;Rbn?Bsg#$%)#j3%#AlBH3_b1>fIz&|K+Akbbm*35?v#oJW$%X)XfENk(xwTHy1(fN_&>3 +DSMMtO%GG=EmD)-X>6jH>5~e&<>Q)TO`3F+JqNW=ubu6kjt? +NEEoXhCClMT}{dJ=LQ+xm67o+RQ1{+0L!=}E-(7U*x2o&;RSmV1YEJ(#f`{0m-b2UA1+71H$+5eCb>S +4mIfHDB->`rb5Us33rUVPB92+?onFJv+(tO!5u?Lm$w6tlHBb(0#0?yE64OQ8=yZE{>*qQu~@3{9VMd +geuLELTEcQ=u&eN!I2*nS-R59THqxf4IrZy-k#eoN2zauul#^y{Y-`a`jBKjW?^W4L~?2&jQlalI!Gw}3Ca4; +Hluz@YWgrWGCoTq?U$fYKO;5$n{>)+L_Ft{(RD`xah|)G(5**x>G9fRZ(XIg*q;KE>e8u#S=scn=TDw +G6C4XJ_6OHCddKgMdpaS1u{Si>uCw)9z46+hd!u*k;O+ikeRzB8kgnjMnRm8)ZFC3MZg#IFm7V +Tg9`$ZNVm11@*2crN;b^@#TD#HRJof)i73}VM*KX+cIA$t7^VGSgCr$TgwT|y@_OEm|);5P*{c--JMDUyII~Bp^!Z+IbFI5|rQcuM=#BLZ%du~CH@13@R$F&t^LqDkZ+tAFVf<43a{NmCYW$t}y +YXxB>+u`$oAF!m+wnW`yYctp_u}{C@5dj+AI2ZWAIG1>pT<9kKZ`$)zet`MPw0X7e^5&U1QY-O00;m` +Rt8hLBc!Jc4cm4Z*nhVXkl_>WppoWVQyz=b#7;2a%o|1ZEs{{Y%Xwl?VW9 +x9LH71-|HNGhlvfy4v}5ez4IgxAWM#yTkPs +gS2aLmuot;D#+IPhacxpYHbW)VA*pZw +~rLk33#G{^{ej>x0c+|Hg^h?cqzuTPAY!$k8J&?F??!wtL%8ZQSYhHwQaUUBB7c+3XIV+P!A}-)?ufy +)(Gh-Kq6%Z4Y*awJS*zTis{w4ZEk4!&kb)i-XPXmTAd+OnjpD++epi?A_^_e?}*27kV#ux9*vL=o7W8 +{Vn@Xb)vT3z0sMRdi2P-tLtY^T|R$medEHZ^|KonFP(YzLhWSj{?Q|~dJ=xd3D?(b__TX={X{LAgwHtPII?Fy>o$UIf7WfJZ5ui3Hqx?ZpL4@__IbDM*q;44_w0r}`}6 +KcY8$@bo{d>vbi&wTZ7)5PRdUdv?Qq>w9iEX*-^TzwCZJ +{K(O3ZilSdRz_cSKh!z@Yi>K(SED!GcpKy2blXuFf6I->7=PRS`p)-x$Box9{;u1>PWwM`yVyDZeK+p +3|AE_%#`uTs`A%LRx&4OM+xQK)A8=I0?`82#w(T#HDqX_9 +N29!W^9NRn%sE0W}jCV5PgT+t*~G|4qhawQ`<#HS@W#ET?{c#-4~FOnSMMUqDh$st}OIkZzGImC-3hj@|X5 +HFG(;zg2chU5@0lH5@E{s9O_*rTP8oYCYml6%qQGLkczT!}Qf7LlCMgktWykCOM51Kq$UXqus$;&2r*_xaqx#8;l16W;_HBE9KK6& +gxlhf{sCP`kNPcCv-B9i1QNM5!kuUnto<>x1mT!}Qf@)LviD;73KDm;R+=EZ9Ns^c6lSeek6-{!+CzrV^+K(pp<*rCcUbauZsI@Bwa`z7~?h{DP_~c +&P6`3YqR(FNb4=C8?1^T~a&v`HT!}Qfgyf7SS3XECt9XJB9Nzv$lf3kbr`#GtqxuQv~O4Q`4G9+ +g-IYV+Unp}}2KU6ztl~(aQOg=e7a-mONwkDTWJbn4(9LYI%rEHSd?Rs+edR}IC<&bIeGD*&8@`xn4_C +azVnp{M3Mw8FkUC|;eqG*sS8sv%wIb*IU8sv%wxuQX? +Xpk!!)}Ec}#;mra>N)AZN^#hz2?H^0$}-IrFf7OoBWnK_1f}k4ccnB*+Ga^aOY`emkD>TVzNzN2J8IsG)6-|@eQ^9jlBge2LUP7j2_&B@$(con%+i%;?j#p!a(d|sO>z%QS7aooAMvC$IrFeSvk1`($)n{axwPQPk-T!6+ +%7_NudblU1Iee<`I6h@9LSg5CXcKncQ2Sg@<8$_B{^fR_^`>Df@gU)xt2(B59W% +JkX(}_XKeB^%@uVxNzO@fDakoW&XJsxU@X_B +v)xw13`PX#1*FM&YvK=M*Fxss4vi8Q&$CZ`LY71899)#PO^U18jn71QLLP0sCvC?z@Pu1Ga`8$h0$>j +C5e7X!C&@XRTncg}mE_i3ac6ZPc_4WyD_2A$_ +px$?dBC&MtI6p#h_ob^kz6Lp6;1LLGgqV}=WKF_mt~XNdZ#;m1IPo&OL56dTud(VRy;^@8syb>$s-ZS +8E<999t)CsE7E!=oIfkb?NdSSL|q+_YabxjD-3c*kypiAsm2mS2IP#lB6%t(lsgv%^0L>G$3l>6`??@ +^O0EvcgCq}b`tw7`yd1SYYa> +BF7j3?UGFSUl4}vkW%bS_cgcawWo>5*pF6Ef4(HGOh6%gliaWf4nx +_;$*f%QVy*;DUdeiA!|u4^j$$wegBB*~dg5E)IbL?l-<$< +=a`oLhrfwkC&oX`0+VsqYS0AbBA9;x&1R1y2uqXD#DnK{B75A^DQKD^+_e$k+3o+WO?~$OMuHl2>*Y# +KW_6MYIcISh}(plKWb^QuYI$bxZQx5PSm4HH~sjqg;znu4$A6;DPv*c0mxlq(YDN`i8cDpwL!t`3qKH5wK)G54lvmtfq1Pgc3>FWn +Tx76RrtGN#$}5R-yZcHmWjV@KQ01$Ma>ig`P+nr$(}OCfQC^6A*R?{f=v9~R=|H_>Aw$(Js%iDiN_Pz-Q<(fwMlCLWV?|#iiO^QT +2QoIf+l?dG<*6avZv%4b8lXe%uS1HNhH^%gdwJONkfI#o^H$})P#&XKp8Ki+&+Q*pgC#;$p8HAx8MxNoIit$GtYBGkgQaR-xi^Ca9}#k!3Mda +KFJ-rKT9tb#d(s9AgK`h5oJP41ubh5FNc7$bMwNRpSe6{+oGM>-l&{(MPUQY1pgf>_wwJ+`h;n9Kxk# +1MD5tk8_n^udue>~z2UYG3)lZ+BSh^=reOP +OsnV->40{cZZV(k_1k7c6NH58{MZbcZauk`jdFlp}Y5PT^nrGp4h(KzdpR(8TJPKC$okJ-QBe1Yu&AZ +X>L$^Ww5h3`Ad^Z?Rc#{`B%~rrw9GvPSOy5y~(#f4gaoP>}+qFul0QA+E(|8-6u~RJ@Uf&^)r`VxU!+ +6y4pB*b^YwA%jZw6Z=61N>hi|(mo_e4KJ)D5+R57etgnn9s@Eqy_2ONnpbvFEt|1=f;}OK;d_0EuT0X +u8@p?X9hj=3&Z$P}6k2fLS%Ewy}Z@Y1O(Wm;azLte8dvqaN%V=Fl*HSuN&S4v}0MdnYEvR)NUyEv;Pg +vssmA6qQ7dREhqYGHrmGM +W5*tY>q|N3K4Gsq?L?onQ8%|w+o+q{uiL1b+h=Ul&F#32y16}Nqi$|b+i03w_RDA|d%CSRMD0wkD<^8 +Fd0jhEJICvh6Gh6zPSnos`kE89Q@dVwqIO=VuP}nBoz(TF6SXtC?vC3D8+FF**l$?Qx#RYmHtLSsNgH +*??YC^y9k<`MQFq*a$3{(Vt-^guBS~$oc$d;hG6SyJdz40!*jl}Chte=h3ZlaONi#{SUc5VLCRweu3- +=~XV;i>$cP346NYgA{sTb}>nn^}$&BDD%E6HfBQ +MeOnB^fod+<(^wlZ@7~?`dyNS|+0`Xm3qg#+A-K$ljQ=l8owE(B77`Oh#ER*_)D9l2Iee=mi^0GHPUJ +{+Q{it&+5|&w3eyX1p +fBEex7lkK)=QZB$D(+>T^P(#pDJfI*X1@yRW1#7ZOk0)K*0Q$x+tLIGohN;YA3G1xe`)<~xUrRap|^i +DVHmpd3u@@izwzJlT^$*Ym|)?JL6@ydGU9!AZ*XmQU49k~NH=74CNVnW++LXVAe_&E>w&B3H@qo#>WpjQT4EY|nCzz~T&`UU;M!)Emof;qF0h4trn;PTqU_4DaOK +$ux7*C0ta954(8!4mP8ZML$-om7>=xJw3fBaV%H#xVniSaRxkC8OG4HM%vjGL6(S;~7jV +T`QN?JVW}*D-D~Zf6S{S@PdU<$_me=wdT46|_$uUI2}A_WP*hjAlRRn|WrfGVru_yF>X|Q)(m=*QL3CkI_-Z8<7RB(9%J-37 +&l`ZL0`Uw@g!UL4Ml4+N)=QL+5R2IO}24%{=O48*}`qe{?{>XvIXHi{&O2o9U!oO`Lbg75e)hE1Ybc>miNP7(zzG5Rrv&D9E5(|0gz?m +`bZJ*&kpy9Z|TFet~gwp@IuA#%`QgKQQh7!^Vw}g6kbi0jV!uhfLqYu*nn#<0A~Gi9##C#JKAK +jsF?r?j6F%=1Zmp1IAOoc7ja#OBPGa*HPOY +Dvbmk+sJ-oNwyWZK_*dFZmhP^x8jU7{VIjNp%uQlW3?&ne4eYxA+-stRJ@AWpex>*}2voo;-HynbT*U{p{Iu=Rf!P3m4ZfJ$L!a^H;y{#m=?so86ag-0Xen<*i%&!S>GX@b;ZAk6s&nb +@a8-o1<@z-Wt6Evy`wBON@_c4|k-5fsytS`hXDUb& +NM5cUCHy_0$n_5ohPlZp`b0glh4CS?1djpH+^3fVqrRD4jpLKPPoQc0ppD}*DG +k{^Xyf>#@9AmTK4|0k}2%Vv$qkoT^wfE~i)o;pvJ+0IX0fN}CDAqGGWKe!J& +fECQ%aS1f|?l*J;*v`{Pp-b`66f<5zRD;7cHg<=uZc?!iMXnVS15loCiu?UJn^DP#^gbBqWEEbU#yr@ +)^v~b+Ca6Ed{+K$IE(ZM3o|6gO6@mu>Z5S3FN@>?7#yEOEFP)h>@6aWAK2mnY{22-JO51Ar14Mmdly+tgyqmOaI+<4&J%Gv|HwWk`Fu5LZCzIO1yp~m5N9d4Xozp%1)@krz +H*5ij44RY|n!2^$PtUuYfv~ubGrKfsp7uGlKKmWw?#)aP2{hQ~^|J&?sUD{Yb*V|~UJb7t-W2hFZ5Q;NakZ&M;aek-(1;Rd8%jrvw5U(V&x;f)vM+|vLlU0*H-O+&K+r->RnvUcR +hIE%%f+|9)IxE(uoI8y?5#4>4zUV(Ky<;cJM%B_}5xE(zw=W9BSOt|MWdM1bFV&Z)XXO4Z# +$T2DcNqVy#52ZsB)-V_4S~1CI5#CO#`&Bar^)!|1>Tus{0s8<#rUtuak`9uQSL8}^GgyR9#6Bt|5xJT +yuB{*HsfEGpDXaM$o)lKUzPidy1pjQkJ$g~5*OF&8xj|v`%Q_9dcP%c@wwlY_yXhKk>_80?sp~L>PEyjN)$LFK}xrD`C@(YQJv;3 +Blk#)L{ +-2WLbQpi7z@K?{{=bIGuX&hUJh@fo$MqeT`7zF@%#Z!6%ny82=Ewe3<_A75^8+81`SH1WFFjeLUUSWP +5eUOlp`{KnC91*KTPwt;%I)2`N4!$<_BJ7evDsbe(XOg^JD)i^P}8xnP1cV8uJ@!zUDIXoixAZ_ +TXllRpy_R<_~55y{h?Rxjh>510PfKYs{}{euen~Pq*eru@&YAoHT!ZZjZ+Nn%kpke%0;CEb~hSj_LMj +%&)mUxC=-1`6s3MG0twgJ(}jPoB2b1{#b5L&6*!{H7fIKKEKBNz(-_$9L|V7e`5YvntxKvkK@^Gx5rT +JHJ54cM)RkHH*Ugv4M&=EuQQnIF4Xm>--pn%k +o>KkzE^WAwV1UvqoF%ylKaz{l16ZFYMkqu!0?pOo7Z%KV{j5AfaA{J@jj6NmW&-5$;7$1tP%{7S+b%K +Y`ZJ%C5l{Mfz1{5Y5qHNV3A$?dr-=Er#$nfcl6k<7Upb9<7{pXT;hd>7mvh50qNM`Qj_w`V4NeiS<*^ +D7DOM3^6=%qsJ1Zcixl10UJ#X++wH;l(>GjJF%}`GHTn&mW8VaXx2tKK~wN{?N +HSI1M$;?U{_*6WGb6B)oCv_C)I8)tEn4!dti71AN@MJsR^HYP;rPw!2_{P4jDRPbl+iZjYw +-PC$F@LDfuQ7it&95=Prunh|F3;z$nfU`XKaPGX65c?~uOz&H_sQIzSUtR&=2w}Y=k}DCY8N!W=JUsL +dvN^YGC%g8_D-(4nLk#-8%y&~8S@9aJ(1S!iNySIeEvAhAE^1koO@04gW<>R;Z>QRS8OXW(k`sk31*) +5PA(khv~+S!o95S;ANZ7aaseK7Zja{k&-7ZIfjzvS_-RbgDJTAdhY>J#3!Wq!##yV1 +!tZEg?n-Cn(6Qp_LA=dauC3HA8{nZNF}I_q_NW;x-7S|82r0X(YPqcMLhw+G`-i{{suALBR)FEf9MVe +a7e#M1nl+Y@Wuo>*>AET3PS+cPP*M`Qk2ZjYw<>vns9*W~tSnm-crYns1a=AU7=XV!gwZIxzC^M|@U8 +uPcflS?wmG_Ko|65cyAKS&@ke-zD+-AC=@!v15f(j4gagff4i+Y{EwrOoXLb9)rck6#GmYJSb<*W4c9 +<1#bxRXmV#w6G3+^ft#E1Eyl?a`QjQVDM)%^%AAlSz0(nIBb+H>)#_<_}~3S<(EVKL4cV_Ow~ +^OJ=copVP32(HwF$Q*WDa^0<{9v|e*ZiSAzs +CHbnqNzJRp#e}S3c>JbbEkLJM+iV{2KEEAD8(=ZD+=JtRg_Y3oD32!j-a}Te4q +W9yhPR;GX{=2RDV=@1%XnxJ-ubKHZw?|`s?7yd&AMkjZAMmcO-Z1OTuO+&E<@R(q;gwI~eq?^&b-6v7=C7OifzK-Q*R1 +(N-JVFAUz^)gGxIB&AMmbfesI$Mc6&mZKbGe2@T^Yx#NExCSwd%Z*3JCDr)5^B#{9D~s}uOB%&*ProH +ET1c$b+U@LqZ|3*hu-mRVqaUaM0+Sus~^(=>nReEzyMf2hwtskuEG^M`7Fj8m89pA_?J^Z9`%=1-`zEpK2CWPVNaYs{}{evSEo?=JIenm0<3X7g*BKXj31{MH +zE*&g8IGCw}oVSe`c<*k{RzZfSme~#}z=GQd8=JRWsKa$&{EYCj^nqOJGVY)QG=JRXJuem*1z+1QG4| +RLEgIC^yiTI0g67lEw-X;Fo4tTZo+9ne4PQ>idR_)oV0dF+opSR2}@3qAI#W;!ibA0bIf2icw=5$7~` +6mg*s=Q_|t6uMEt<_*KGb|_RP|3e$IF0O_C&kF;0^FIlgz9KXlQ +aNM=vy;{1x_$I;Ye_JrlTam=1T=GPYO(U@PG*`v+r)R;ds-;Krmp^_iR>CEimJ}!APB<3&1Nz9+)dzb +k`=X7eCKXB=W37I{Cb2`Ip{y1jOl+ET3?c>tS9*z0yp3|w>{QM4HdC?Q`7vm)2&++|7{Gn}Jn%NULrB +gF|_TZFGWz8Oi_(NTOP4b5}@YXH)V@>G{HG49i(@0rHMVUCiaBJyR$yACu7MkuS;V7Vw}YMIlgz9KQ!LOFk`-lQDc7WKH@u +BW+L7VTezX-C40cAv+DK4n#*t3YAc=pMEu1#iTHDT{}F%aiaj+;{>gYfaU_2flRuW%6Ds**t +LJN&e7<8|?dcODB4kh##sX5q~27DO{>;CQSYr_Im0j{#f&RVlCAc%j@Y_;+JQ1mWV$o{zUxtbEl8^11 +EIWJgsM%CUk}o|Fli(2~BtFCjL;xpV@UA%BL^~jxuJYpbz!{x@Ynjm*81IHo>;a>W`r>u1dpFh_S#X@AcJGExsSXl_{MVy&lbvgBfk%22j`iD|Kp`Kh_&~L)Y!8d-Vp5`L%U>H0IB^hxZlv5sCTpaT4?A_}*pyP`9V% +r8;MM-JUp_Ka$U{F@LDfKW)qpd{pN5`TWluJaF*9p2cVq>d!Wh)lnv!10GpE_OOgXC6I!_V|ORmX5ve!81!AJiT=K%)^hIX&h}_E6$OTRPA}jKeyxy`T}Iz|&pYva&53>QwNG6p(EBVZYkQYPWo>_HQCZu&Eh=j}Y*AU;{T7wAJz!C)t) +OIkv(I-0YVY*+oIveu-p&MS@9}n1pv*EYf!e#9mJbDLZ|!zlp!U9QcLZv0>h^*_?H%2g*X@W!#dW*qu +Po2W>-N_cmDla4Mdfw-8;i>8_O}+5*X{2tYAm|>;&>mlsQFnmU+nvkMU8znUr^gIiyDuAzIb3Bwy2RA +^PL=h#B#Oyc{ab0qwle(`DHx6n4|BtsPO>iyE*zki<+PA?c$66xJ8ZFX&18Q?=5O-!?k$7MTfO56h)k +{peYR}=cGl4r7aYdowA^*3O12_8ZgG)+uWHXN$@^@?R_}@5sja6{H+xB$5iOuuwvsXWRXWa5X4DJ5RMFX*;G-W+V*0FW586jD`V)vt^VS5wn0nzQ?d39vt@@3=gZscLw`N +nb9%$qGlT@GeQR6M(iSGM#(@fu!)p)ggNsjqvngliH$R(WilN0PZ&1jgHQY^!-icQ|0wGSOXcT`ngnZ}1aDLZ`cc4g3s|*6A){0} +~F(oo*K!n4pZ}Y0k}I13!00^fc#C`3Dtw#YzPZgChLsm}4+LtoV}U-)sQHE}(B~MXAEO2QQHG7!z{ujzO|cln+S^TXfr`Upce_a*kYjE)a +|ft^`FxQDgpYiIVI!$i%Qa*$6KSwc_+gU>Ne-^Tcc<)bHC2=Z+3I^(8TDBN3bGH$LZG|KJk{2-D +$1d)5|Eym4dh2*?_oyjb>i14^Qf0H-R&w`@lTTBimpXl~YrmS|^(_A(_79|Q5)BWV>haWl7INCV7vUY +K`_t4d?p0VuD_O?#0U+AsgYyAAp-qzCE@{_%z_vHWfo+6eW{cm-7?c(L-i^CDlT;AMVSzZet!W_ub*8 +0-=#)aO-(v!=Vj@~Ifxa%WadeW4A6oa04@YH+rQxrVn*40ZZ=a*NPF0F5_Y^^-iTiP(`%F(&|y9=GpP +$q_FrV2>{vJpFEdVVEL`)K7$-9p(bOx9lNSqrg*|f;HP*b{=H!cXjWut#Ir}1M +W6j%bk!O(=3nOn^BwA#}+M?MrCyisx+ikw6By+5JyUkuRDV@T+J+;2pvp6}ixY*B&k02)&b#oOYc&v5 +1%`pdQ9$2>nzW+$|z_}f8)uel1+z!}BC*@-uHxBGb`!wyNvdq7ENdGh)6NyqJYumDkL@|h5;N`l*Wi=Pw9lHkYf +`UK--Iiq5JY8S|^L&coqEs$M@ia*Xc*>$+aBJv@(uBiBrB +EKQG4i$5lw@CIRD&{nAk?cv>gdFD?#pJr2d7xz7!{^2EkUfciz)ut9Nzk9SIMky!2?}(;oIq)k4bQFT +oQFwJp#x61O8SZIQi_s1lLU-{@=Ovi63R14z-TDXBmpC$Jd*^BitX0( +3A@-yv=CWH%WkUAUAX~qR9>2jAU{{HzSxlp_|c5p3sftk{h~_SaL%*l1gsqmfJuD%%K~}BsX-Uc{Oh6 +*3P`4Tf6BE-Hbl+gl?pc+|aGvu7qySsMdN=>htu<+J*I}HxHW;!h@UqFG2#8|G7EPpUw`(Jmz?rQ2rM +?8Or~ndVn)0EP4>fAaheR$>&WAWRD&< +2m29oiR%#)g4t27UX;3u$$oo7S%=_`7S+9ER&9(JA#VSLeiv>*~A;gsgN5Ppb@t|ZEZT!%T2g6GFYBC5j>;)XVWzo(Q2{k ++M)AgnBs!%kv=A%h^bt2ccfhQt~(m^>Qkc$3du41XjzL0UkgsQG(P!0A6y)h%(p!l<-Z;q|Plu7; +GAE(q_)(Mqd0Mnc>UbKD7Q9Y+M=TvC`jNU`f8D4Z*G8l0$J1FxddF>yGJ?nB^i%2s3ryxmbS>RfgHj* +cdr@g9;@*o&LlO60R2qu7_oC8J#Jv}lh9cKEq~yobOJ{_T+plGWkf%qHVjphnsPqwWTSujjh}$|UeMH +>WQRyRcTg8kl^0Y^iF0xG^)gB_-1XArGvP~e>9wKf6sq_$W6G)|p$Topgdx&fkNTr9!)9_~0k}toZIM +bfhO8<~;8L9RU*_M%N{}3Jzr+;YPHp^7{hwxB3{X@7poceOevMO|rzIPQvb|_eHq*7HKFq1t2?U-d^%br_j8;&Seyoo9T@WiJ}LFVhLrlG)F-7r7EP%Sp( +*t-e<}3=no^&X`ZQ`veWXWMihZQ9TJ8&CC#M|zNUyQn7$m&La%0erSnRFT7$hvka$}Hib<2%GJ6dCq@ +H0z|LDGaQcLuRzRPGECj#RldNO)M~-XO84O}RNpENWBk4wBY*xjjf&IRz0BQUr4Av{JyPlry2Z=Al{$n(7e}c +>=nrDCHrXL0Jl=AHko5CQ9YSI?CW?nV>1W;{WG!sIczKJEYhf2XLe|3OfQOodc)cf1H1jSYYhiQ7khc +k03!5Lmyidql*nA)4jY9T>@O_YX3fU9F@nYU8WKRe`$$6)ct(*gnyj93n&Pih4D`YF@$dFPaHt+d<&b +x(d<$UAh?LuzjRNgLR3m0q3QNNHaTqHZxFyt=rRdfv5!bPG)iGnSh!@qLV5U(#*ZW8 +xkvdmHURo+F#|yA(2{?JBLIs8L2EckL|X6Q7Wk{H!~%f<%XkOe+t^=mo136v+G@xiy|XW(*{LGZr6a( +T5iKdo)#4_H(VI4z1^>fpx*t?tzd2D(zj9fpS>>}5AH*(9<%wgn~uf3@4n+uS~LQd#Uv5=>?%4j +V&#;07!4eTix^0agLSLDruA-BtmR^)a2%#l{)b<;(osysO&t;p+!c} +7ur!a1X-JTV-t$m<4fv?8w?tr;cd$FvpgxpkY-XDL>`&-zer(CY +BE#pqH`W^`z#GPW*F_Jb&J4G)Vi5rBSVwE)tbdYsc*L|~5r&yDZ@*G5+qPLIY93-8h7m(5%8+5)Pk`{ +_BaYu2}AB8!#t4*}+P-tmiuOLDmifhqIR9X1Vrko;Aq$tOxoFY%8EC)WlToR5VOI+p3(r<_z&tWUKrxOr=EV|k6jtKqW131~>jugkzUK6sBCz(L(V`1Tx#)#E6mblOw%cL&+*k{900-yLMLOI~< +0e|L~=JlYn`K9o_~szqZDWt0dnw?$J9Wt4VtPw}=P%6f{b{~!VxiYP6EmD_=Ab}3%FlJ^6lY_UHjv$( +lMdPP4zadV629{Txd8I0}uru=m+UO&_Hb{kXb<7*O1%fvO(GI6uLOq{g`^JS5r(VX?5%;)6t`D0f%R# +sP6&fn$gZn0L0b0eTX85@juz>$}Tt7V0_xC_KhV>WdB?CGV&#ddcotr16fiiT1&l%k;&4FOHjP>!Z(h +|m-bnZFbb0Zq|RiiR3BbqhUqWsxObsaE?=Kg`_OO;U!PL{&~OW1pHc(Sa0_3bQU}p+3tyj73(;^3U!PJB(Qpf2pHdUia0_3bQWw +#13tyj78_{qJU!O`JQJ1ezsgG#5g|APkk!ZMuuTQC!Xt;&1PpO$`xJCRxlI=vpE#eo_h_4(Hukw)%MI +xaozjBBJm(nYTL@H5w<&bdoORpS~5tDr7kVq@auN)GuI+Cv(5@|*Gl|!6LlwLU`mK`j;a!4#MIN~dZ! +~%omR}S$)f<{hx;%y_SJn^?+UQp+>(pPGf-o)n|CJUtRdYq?nl#Yt|KL1N3(y_0#f45PL@Sq3>s1e +I%d2Bm>d7LzyS01#sR<(#gI-j^kDmPI$~ja7(fYUl@aOzmSbi2Vx~UN<2{)XRsug@X&LdPbm^7r`(K1 +VhI#30_of}a^8wK+DdXFnJq<0l)yft`0!y%$(=oY*!=s$cd?wD`HK`Id6t#?69L0mM;kXosv|;*njlS +Mfb{=RO9KQH0000807zB_Q_Xl`)3F->0I#G003-ka0B~t=FJE?LZe(wAFJow7a%5$6FKl6MXLM*`X>D +(0Wo#~RdF@?Um)yp6e&;#-53m@Ak|UB0^v+}wQ>l?tX)=s*65H`2OR_A>k| +H_r7Td8C?{=*5i~ov$N~+MnR~H!GIk=5N0q6!_q`9}NfLq&Fcg2Yhow>U|KXYR;Ju{x(^Y$JrZqKI2k +Dc&NedLsPbG9>?-a6~uUA%Z|pee_X9Y6Nse0Im%o9vz5zBiuk%;u+W-X6_&#*5SYH}wDQj~9FM*^Tks +o7~x(%@^KP)y3}ksRxVkdBwguUR<2*jCXZU`Z4XZ-X~`Jlf~rTSpONE^)5_a8t*>PfB0v;7pA-Tvvt< +HG`=-j{`B~=n=*j=%bZbt(fse`fT+GKW4l_Tlj?WCBo?RNmsAY7V53qG2?a0=##hlGVN+ex_X1QXc +NX)D5FalZ_=*X3D~>7?2k4($OUi{+;&JCxiPQeOtOl-lshcN +sF%&MlT?Y|DY}C1pX%-Ln_}ls2E++;+upqBro+XDn^eq_%;=T4DEO5%lf3J|Drv0NsI4N2JPzm^koCm +)qhh)kF@v!?TYT2AJVs_Z}mU4E7H@Cs2FV`_fKd~^re1EpWPx|{fsgQ{pWP{s1(1Tk7yIdFKJJd@hd7 +ti_G``(XQye{55?z72^ZSpey8nKAa5U!N1UFkU>A7hhB#)&x6FFM^^g5XXwMpZaSc+VYfwEe2%v0n;| +)%=Oh{21A0u--zQEQq>%%UNFxUrY2*MSGjf2DLJrW=$N~9M$N?$R$ +bp{H$N@$gIq--ya-c;TIlxFG2i_`;9AIQc4m=`_9AM-|4)l~p4lvTlfsslh2cD5e4#=HG4lvTlfrqD& +1Lt8HIlxFG2N;==11-|X0Y(}*&{YaK00dc)105BJ9QweIks~1?8TQP`A=Ww2Q-d7gkQ}9RI6w~U*)(# +XK`oGj)j2riV08`_IpjKrMdUz>6mmek%*Y|uInW@#&cPxFt8<{MG;)BEMh-k9jT|^yGb0C1=`?bnMRC +X>);Yi^5IF`0IOyxr&5_wyg%-JuRoFCX;6RJ~#;Q_*1C0E@0m;+Af$2yC2U=tW4m>-vu?mdL#wxUMZ> +&Ot%)o&bnN<$fScR8L0|#a!4IE(P1`bx`z)R&;Iq*_x;J_m?1BXSG11D>KV-;FtHdf)`Y2d&iol)h$@ +6*5mMv*E<&mc$Q_&&nTfr&~Z2PP^ja$q5DoXR0b4vczc{e$ +{3-|Q=HQG~a^!&E#ko1;$RRgYSyVZ|$ZxCyBeTjOMGnx*Kn{IO$H+mSzmlKveF);Z83zs>M>X>~#03xK$ie22bI4&aw +yIPQdA)MTE2(n?q*N|FtE?7T$9gNU$ojhI91t*n-70bQ5jLMg9+eA5fjWn{ZWY#H33aRF$RRGWZqd&n +E_et={vzuTBXd57y!wd6BJ1+HRbZr%1B{IM9Ho}Z^@)d^9!V?JtpX#f&LJ)}R9uyHxy~W4vd$p~t8;MHDpuz}PnnT}wN~L7nX9Zjs6OK0lYlI8u+}QH$ggwA>sGnQA!m`pT +HPuqItR%ir{{>(*s4-19@Y#wSe*lmrf!vIS?7@ZIl$Pk*ece~!Rj0qKPC%CW}O2qHY~Qvg@>GkR?*|a +>W7_J;IQZ*2ctmXz%vR24vb;uuaL8D4)()NoXWwvIV^tINv?8W)N?z?xi12;z`?22ijUA^VI}0?LaXG)DljUA94=H2i=kC4a&RgKYph~F&tV +ZcST~0lIm87IEjq|8eoI!29IUa5MGjWwu;(CWjaBlX+;s`95*w>3`C+GkBm&aizf!@&G9Bb{xPXt61c)7h1&{tK^|oV3hDlKzqo+euZ4_AZOhi4n7Hpp0Xkb8svA7TQpYTt@5XHu*e~IbI6ecI +z%0cthY%}F5Tqo^I@l&=^SOcIpoMuNjC?J9GtZZ9py(3i*63FgIwIauUib&h%=2WPD+6*=1ElYogWyGl? +lXRT_2at|Q~B+qZHVv&PQAxBrmeN5IxBA_*$gSA$nK^ZCJtj-~}Rypuwev(_q$iGXrzl{} +q;^K+mdNatXY!=lbn#Y8~4&ViT8|9KA9S|#^$us +VkvIUML59CDQEA@2|mIo*IOA&1yQ-dL-Ov{s2@t4c+VTBVS$yS2)m&cPxFS`=9DkkvWrWv$|n1N1UH< +itTv_nFmthoILyox_E(ip?OmSntrDvC3iwIUZhIy+bs}n$BU-SjDDuu%T6K)haNurgN~sAr7swXsnWl +R>{*jSO>X0C>MsT4ksm +nFRes<=SD9^99B{}p$nl8Gd4Oy-hrJAPyi{)gxZKOZ0tZ^8fdh;ZGRWDFbCjuau#r_3RSq_T9E|+HA@ +`4S*&N~wau>kSBau~fdsNjwzPVlw7CBfi2di>a(#rvc`#eC<%kXlr$Wf-31B~Kga$R^iz}PS^2N*>ft +B8M`?t-J-96<7{{&804kjLb%ePopjV-<%SY&M4rH-{WK#I;AbLb-DPIBTquXOP=7R-=&BY~tBAFVF7}nIRbu359OM>CtlO(SB6pCxaC5NMDo*EMtyLUylv;a4jvV5uRb{$4+;Yl)>`GD_6T +dO;<7m`I>=dT6Qf77L +fyt{GnA~6o(wF&S4KZz{sBmC`S%f=U_9)aZuLU&*8*cMLz39SLjNWayjH+(>XS`_6VzU$iL3P##Z4OC +0MImAP3$mtF?+t=a5^gSU-ow+9ND-h-0f9SgS04ox??J73=3X?GaYzU~7+nQIFUvYf-r_kb@M +hqU+5~9$*t%m0ji7*gQac=^XYPUjj&f!4iU_-0qd4Mc%$W;#3Sj9TXUE~3ZGsw|Xm5NsBh4t +_e>uLks9Mwb)FiI$~4thP@9GuDldU5F-EOMYly^B_X(WG;DHFR^hK#qWfR?+2SRp(%hRq~)*ywtiPhu +qD<>Kyh`$XT7kVmb$#Le3(GwG?u>gIun2$dN-_wTcbOWmCvmW7WDNhsB^=xz4d3$f1|X!$+)=mE0WSb +Pg~Iv{vC6`H@2&T7`#a4y}?S2di_469Fw+tK>R|9630hLu{?GsB_4RkI0du(K$9u=McL&oFIn*4!Rgt +$^%4;;(&uyIoLeFYWl}50*74Xz>wqy4!O#~R;|J_>fq*J{o``rK#LMo4mN{)-GQT2l|!5dSV@(mO|m) +Yq#qTN%NnaVkyT}8kfWzs6v`D>t+HsWvY0{6<^giZfd-Ar;Z+$q*27p85C=J(TQ{Lq7Ih +95IjX61l1m+OS0Z(AP3IF#zDTawW@EBgHA|Q(>c~&=O`07XRT^HXu|{gGb6}t<^>q%h&H;LZ9NwzPA@`7Ps?O +n?e2&B@9)%oW#}K4!O0;Uhxq&6VRfy%3i5lHnz$Ea-cNki((QF*L|QMPKQ=PF&e47CBsG +0fIPgN?1?KF`r0M}r*hkfUod0TY5$HJ<~Fb=5h*Xfgpk+d2n_9M#l0z-V +Ht8sw-RaVT4m8g&gvW-ax@4KZPXw~gB(#$4RSQdv1ByJQFr734rE3SID` +)Q$4N{sJ(3#aXpkf7sX>kgIhKqDIqHraa-9QRQRH~}__5>1PN)+=jqwZ9o$-s4>3GMxd3!V;-CT_4`` ++UAXyNTlcD(6q;f?Q4_7~n{>MiD@$#gQk<1<&ri@WpblCH*Y|G}Lbvt94;y +}Q$!i@T%6WHx;w>UcKZ4|~2b-ks_0X5Ob}^POc&y&k+%-f;Psnz{3{>0+)rGOxFM`}6VN-o?@0o_?)s +qZ_;9$M>H&d;Hk7=dW(}g4Xu6=gvL9eg3&~SGKQR-oAY0>1VEZTi(NHh_rK|+g%RO_4}<>Jam4N?#J{ +XNe^OrJ4tWH^iGoAiRs-Wy&KbeNqR4)_mlK~OdllagP1;~^aZ2}#qC7oDaa{lZ-9{!_a+!^+R+^3l(= +IsQu4kCMp9tkOh8g%A2Z-5MfNcT{xHeF90bWp_Av=TQf?o!5G3XHF%3a-o_)+ikd)lVLt0X38@I>WKpJ3~UnO7FG^70W!V +?;2CF#=8Nbnp37ts94=$er-&}((ZN%73(@goS2G5-R%=9R&e$Z(E?!dHZ8 +f>Ad|3sC3>w3aa)_4x;xx2ddV?TD>LpDNwa*x7A-#&x5LckF5wvp9WRK?jU;FGdQcYoo^5wjvoV6`^y +H=mU|Xd?H+AK(ES{!O15Ft!Sf($x9=c&-H(GBiWa@@pMn~SHdsI%$nj5TcY0>r8L|>EUOl^pswb}%iFn6cV3dgcXjzhLjgm0l^)o|N5)LCt_6$`|UM$Hc4Oy?u=#|YiM_*_9(IV +bAR3%@$?ah@d3>9fbL`x6--VZ!_~cO>bHNL5E%Aw1wL +C0|Pc{2c`YZzMccvmP+&v3NxJ}yUtzTs&3<6WRQ1H;in5HDiI85)ir0^hvB$Z*0TFoR>R-Qle!FoR?E +au{_%dNbQOyxjz5*vys=Z#aP&HZwipEhoUR4bAj~H=O{ZGc?nqZ#zT0^w7+XzVHk%YC|(Q`pz>j4>5B +SUV8%5#7s_j^$9T0Lo+$y^(VkU56$F+SD*kxGc=PEUV{Sj&SrALt56#QJv5UOUWeKk&Y_u{@JiIia1P +Dngx8|Bkb523?In^m}61_4O^O4Qt7#4P6W0?#GPJ7>1U;6V +pFy=%M6;nDK9hp#<;a$)7WHJ@GO5^M9ya44g%FIzsI`dvd0cQBs&J;Ts9lEc +}!fxdNh4u9w%(ATaG-tz~Bu9q`r>xYJ}_wV88#z0@a`sn9>4PD8L(fpAiYkBeW9~-*9Tw*o+#L%_8c> +SLmdUyeg>98#aK8=C?xgqPwdOY?QhOQ55JocA{uGcc?zcO^amJgeigV3;qMf+<**U5Bv{SORXCz)e<4 +-8#n6yEbUhOUq2=)vR=4nmt!$3INh@DxpZg0AI_4uJ$+&#u|jzr};Y(DB3B!$Ej6iTp);S!H +8H9JUc-a~Qcc5=(F;o%zb`ajnuA7^j8d +B-?TyhlA-JQ#W1{T=t|rn#Cpxp^>W6xJ9ynNLV587oc@8ngZ1!)Hw;}LSLo;~hOXr8VxZqNbR}>Pzv`fqnMy>)kVOLuU4G?BK}uduzCZO`UApx0JMpscr-#^b&1(f-ZJWP5kK(4LvCkBxTs$HHz#yL-1sH^z%CeWg{ +mS9MQSS5kHPRQF7Er&QNRbyHNALUsRBH$8Q!Q};J@Ra3Vzbpca%Ep@F@Hz;*kQuiWt1yZ*ib+b{I7VQ*cnL4)A38fArb?m4EMx7<<*ia{gIttE9#z{>Z9Kijp3ocM{{c`-0|XQR000O8NLB_@th0iGY8(In>4yOTCjbBdaA|NaUv_0~WN&gWV`yP= +WMy(|8Z)9a`E^vA6o%xp=$8pDh=X?AgU@-|LM6iJJ;cqlxa*m1G}xgZH +~32d-fn)F6_iE}uo^Kv8d+otmXLxe+mi>R5o#Doyf2%X7bRTT=2gAzs{1EG%7ak8gXY-e@cZQeyYn^p_B>OSh>B`If&F--Ku +w(x;rz@Ab_dDy4?H~GdxVUb=Yw!s?|nS6*DbeD&P+j +K4#FCa{Ze>rIKfNddBZ_2hwp}iTnWRF&ebs7^x>DoaMg#eg~>yf*Tb;(;TvJ{5WX3{8$R+g;h4Y){zi +BvFoK^Aj}2LVE*!zrm25ZK4o3=3(d8(s_F?GN1v-wemN6TUgW0`+eAZWyll@V#(Ee6 +$n_;RjJ)^h6^MMO? +^mh3E;C$W-vxTu4eHgwU&hyC|;rzq#pS%-(E`;9=-wucQ==JbpA+3+x;a`1z_kS %~l&v7HcaWi| +4>)CT$vpB9LIIdY7*Bp);7RL?FaV5ZUg>zh?99JxkE0p7E)*M$T#}&$Pg>&3Q{#yOrz>G781c!{+KhU3ZW@zi~lhT!zW#&bM`YHW^coa1Wt9M4!B*Ag68OU3cj{c)V*gg>r19M3o$&v1^b={YXpk825z8=T`xMvqHz ++{F{|$EEbR#pm`OdOn5Y8t1siIj&h8=lSCX=eTBZobXk694CAgjdR@K94CAg!XMW-$2HDzp08p!99KB +UHO_I3b6lewS2)MDfF3sj99KBUaeq9Xuae9LajuXC$VR4-2kMq_!367go^f+#P3<EEUI7S?475x +DwzvzRsywdK~9CUU#Taj;n|qC-it+jxWkOrvvjoIqvM1X*pi=_2VJ<4$eBKh#prs$CU)f6^r8v=eUZ; +@q@e0$*NZ&I8Nws+*iT%IE&+Rs5=zmxZ~!u9Ovrs1*toAD7@bvChf +jIHAWC%JF#h|$HxP*&Pj1x5gd2%NseppkB2CW&GBqOIc}2SxJiZMgdWdW9M4dWYn< +a5&hZTAc!qL3!#PgWk7v_!oL8@c`zjgA@eJj7#^QJ;!EvHqMQEKfHI6%7pGf_9)z=-Cm=&4hhQ)D>bD +Z$U)6wH*LG?JR4~Wp?JdWdahY|I7M1LITIImuXc`X3<3!!z?DaUY&S?bnc*Olx2#yna9N%As@W&T!{EPNH6g$MJOP4q19UnR*pgACO{k9M|KT!|`}}9Ot--$Z?|Xkf7p_s8@-}abDdaPmhcA0r5D_>jRR|;}RTCyk3RXG(S18BT^c+vWUd8z&u{a +*PUWM{i9I&VC%O^SBbQ}-4lGc8Qi>k*(>c@E;Cw!HN96t#A9mcC4H;c$|6O-doeLzxrJaRv$lkZda0a +YExLxAu(9j2`tfDb<1)UA<74W5K#|uuxq3YF{hT2w( +&BhbJx*|(@Kr?W$76C_#8+8Ne|+W~C;V}o +J|LoAh3BiJ(+8AH{kTZI3f>1qaNOV=Hyn-=eLx!L_{{5749aoN{^LZw3c+!Wb6n#bC)PO^qh5vUtGM` +n^|%nn6~S>A-_w`xIUauZ(^}^w^tgzxqFEd#d=*~3O7ePKg5!i9XZb2TJzv`2{3-S0MBSl?ufp56R)pg`e>{`W;{?ZvdX?qk_+t7hilfKVSmz{sm6-M8Y1 +JLN_oq^?LU3HlSE>8D!|>a@guV*FaV4S0=isXl9G|_fqF5X!d=;GIDs7G{oa2haaYBzz&v8PJtJ&*u# +pAf+W9q&N#c@S&+{Gj9uTu5<9fn`qIjwW@^fy{cw{|Jah$zxt?%ayzn_PC{7{eY;kZFLPSlSRb%!iH9+BfjUp~QcmLAtA$0h1jG|F+!;rJZ +<@g>#cLhGDV9}riM`(@737ZGGd6gke@?l5NAArIq(JwAtJ&Wwd|5qq44af$5?35-YF?l8kK&f4w}x5t +Tc6{37RLoqI}-60{ziR}&z3*#9J<9bdQr^?4&e7|zsl`qb?T!oV3#&f*%l}k#Fi<9PYFfF9R4 +#|c}7C?6;EIB)xLDLqcv<6K*X(&JQHzKc)!SU0X7ceeQ+Tg8{Fl)gmEw{5M#IX;K7L!KTdI381v6TXU +s9w*AjS$dq{ILlWd>~X?Z!P|f&^f=+Ga5&D=G_<#tA +vj>jH{tkMpX>W7^{qw#uPBJ`Ki~$sSJ(<6K)MF?*a`<@B~n>3flsJsy+ei)xRj-j&a*R$<9;qAOqH9L +Mc(jdPq;br{he$K^O~k4xAph9$=djuTx#OKOiNR;^;c0o!}O00)NSbJ*>04z|kdtH*g9*Bp*3oa2N&j +&pqGwu(eoKEAsOZmaP0IL}rQ;rOE4DlVRit>Sk(46pQq!*P)=pgHJqmAD>PERN&LoFY#H!k0OD9FJG7 +Lij2;$2qNmQ$d$Z?*nB2qoh!g$20L&8>B$g0C +c>~S8(lUn8ER;zF~SD`S@wa1BS6)`#PA0iQ6yh|v@)2SX8=>nQVwTk9&oUm0Ss#PTHab7d0z^1kFY?X +*|oUm2QBFpi_>~XPWPUScrUZqROal%&N>G5=yIp@&L$>KOqkF#u*h}GkS9*IF-}w+&H>|UK{1|8@wmbU$zGy(Cd4ka*-+bSBzxE8>8v?d^;n +Ny@%*LfR@UjL*DyoQH8@t +3v|g^D0)ESMfN3ajD{QzqLwu9i-)s6Rye})cApR$0ceGCERfy#?x-)Bru*x50D7Pi5{SNsc|Wc˜ +TI6gwjE6J6gr +HNyu?xk(1!KNGs=p)HHR#YtEHFY2IsiKInFCqS@a#UC|4zN3lPQe% +bPaFH|44p|bmio!XrP>w5{;}Sd8vTDZ(j^i!)c(F<{I~}Tp(Bo3Jii;;wbXfDNoZ)O89FFt!xJsSlF; +_Wpj%x{yD~=xLaU3sJAvmr$94GX+LOHHk99JmE70PkESViF+Cw3lJ5_+7+@kQ6;Ld7aBJT=GtGG{m~u +{o}1&v6l7>m#S8=6R^E^{F5qI +iOKOr^;Kf-twQ+Y5vvX*b{>~l=VWbl7;&AGv(+K4$CKY8YhD~*(sfSb{qb<1pR)D1!8uMea~hoE#5yN +YJ#KK08=T|3W=@_S=dE)Zoa4M^PK|S%Sm!i2#|?|)hQo2fSK*Z%;vDDgtrBsoL)JPc%O5A2IiuC95PC +f39$7>)rzTw@m>?&%R}rZ^B +tTBAbn-SIClomg8S}QuDA}6esn2MaRR(K$1St~>@5MiybHaz6j9nIibi|9y!Ze;ecE%8_ +4IS$X$HK%|O&*rvv<+rcS@uS<3A2$!<8v)2Tk>?XnV4kt-bJyzYFKwL)~~6N;SGolk(ARkfl}kV{k_; +?@eQt5e}17xBmstH|eAeOUKZ#f6gg2w&Qj!QSSy4g=e2bnBt`DPoQNXlf;@w& +mGDfT0&Kjc~vVskQ4ntngw!Vv6HA;5ve05`hi3iJ6Ry-^#c(eIbO9Au^&j|AZPV;8XV; +D9twnm9IqoMJaUnKAOhqX2RTtkPJ!G3HP%CcxcjV7i=7n6nRVn8$X&hoem!#S_gN{O(VU*{7E_TEjh# +G?JRNH#W@D!W$a#vK1#+Tlg#bCO$O({3GgTJ)*%Lu#>;vQ`L1F7;#}|G4GSk)E>E3c+z +DA;<}ii&P&P2|-S5wL)+_rnAB;TH!e>tgTjN|AaSUs}&ZX8 +ymXGN-LrRtrP(mzQ{knGg+|&ZF+;u+r^ShJ$vu*X`Y{3_Fj8y9dbgJ=-1(y6wB +2Q`b7ftwC=$o{!t+;|I6;>y>9WwtBaRTkT=D-+OL+;C^SbIP$H|dfy(dU-?3Ru(taz?Zs6&QEBb|m5; +;Oes4I)4+N*Td-iAD-<8YljSYKRH`=$>JI`)Dclzj&i`TENHk-B9YHRhz&1)}RynbQz?1eMeR&QKgy? +X83`D>Mx%Jz7i?4?qx?T*stA1UR=PnYA`#Z5VGTs$ksGZ(Lx<5d^0mE$!Rub1O>7jKl~4Hs{g<4qTDg +>nD2)}?ekUGzk@9>aPfT@PYi%GV=CPo(QvtS9pIG}h&WHB3M`W9?@^ms8e$3UsUdf}aCZo@DJO!IbmX +eilqQZ|$eSl;>Idc`)V7wVw!6&RnJ&N5kCw#a<03&|m4bFgJgh*TUTVHC_vI1ImQC`K!AY=H@T$T9})^u4`d#{-Umhx%n%)7U +p)^N5kBH$#X8u?U#Kt%x%R-!`yzwN5kBH)knkJe$7X1Zj+Cpu3J6dn7BWw=b2R-6L%+d>j$(Z?oH}8u +hzt!Nh43IHhEvtu)4QCaaYpFld9Gx?nxSXPPJ^}j--*NRG+*bX;`gVpST-o*P-oh;O8<{4F+6Zas^Jfr&fgMQryZAR7Ipua6?T8TSO)8CXdt+br8dJf8mkq-E!5oV&j}X@ytbZ~18Hci{6r9A0|A?W5tP_d7lseyvu0H2h +lqu8+cWz +!m80SrZv6-??W_CY<$#p2wKfLc}t|NY#ARl&@5IZj}Op?sGYI#X0$# +sjkn!91_=;?F@Udbam! +bSF@Udc;SAMb4B$IlcrSGr1DKz}AFBH?Xu9y% +uli4ScB1l`6<>MYmiTH%VWnt!MO$ZJa!Cfe%#tHKZUj6e!_RY0dYG5?q_)CO^Dn4onG1bCd6&_?gxJ7 +TM)OoJK3@G?+~|{yC3hJZ$rF5w=-Yfg1F7v{j%*H1zAKOt_B?h<|n!g<1uY&-8lIM27`()}TXi=QhuoImpMW^s|b_>U +oO)6Lwc{siJhy6*VzLEQdsx$yfCE^>9F{8NY*xw=vQFNoXO&0JPLgSeetr(t(KfViDqr!RIsgt$%D35 +1>h_VHFBJlq)n55#S{PCD%TFT`!SPA8215aM=rm5cuo#O)8YThsm$g!6P;y0QG4j~80Zo#bl}&U3BqF=L +HYV-8zjx*99Tnr;N%fN-8^(@n?MA)IIG6vXHo5Y97oKb_!58!N}0ej0rf!g-!fpp3o+;XKWz%ieR|SQ +XZE2l+OH?H%f#=d~HDznm}|eFwspN-oRqLfB^MCg^(*wu0In`}+_s=F?^W1Blx+-8}pUgzcrf$8fliS~pf@IgvGb7sB?!Rc^$82=P2mrzA%|g0Rig9p%Rmw(wPM$bSNHdkgE1@*afqJXrjS6vBm;a1-arZmgPe0(tZ^2j{YEe);OT^Oq_smFwN!-Sy53kB1#=+F$PsFZb6v>(5$8f3q`O?X@3tR*vWY{)GMaCtAnHYt6j>*4w +?iTkX5~k#1}a?sqrueZ0filUg12SNnst&S3RHdt>GQTT7VgY*!!Hp;&PTzI5iwi@U@2v5q=?ywSbgUS +HklZ+3^>hn>}dU3XbgCtD4b|H^-yL*DOnHdfo4x4Yfd_0G^PzpUICY;``?vD)h!_u98Q!LaPiYGUAlbb>dV)z-?;gi&wj3b>-Jjb&fR<6SMIMr==C=So5QV#kH0wj(&)>huZ+Gr`r7EV(VL^U +MsJVa8+|bP@X6~>-gxrPlXpLQJ-Z=1QY-O00;m`Rt8gER+z@E1^@ty6#xJu0001RX>c! +Jc4cm4Z*nhVXkl_>WppoWVRUJ3F>rEkVr6nJaCx0rZExC05dI!1|6x?Vkd{ykNl2PI>A=A)I)XV2t)i +&19J9bGi?H)I6W!AV9LN&e8 +K}yfGI}|cnJ6MKQ4p^!+^gq72=FP^;F0-jF@J=mI{1?mQg{XqaAR~5qQU5n2>X(K#oCgQBDF~rbFMGG +aQ@E76JD?&1Dect(r>}-H>kRNfl4{ig}ui;At7@+$8f9DVr~mY^tGXoI--K%c-N^jXN% +3mYZwm@?3cKP}MBVtDqAh5+t8^$a~OCW_DC9MDV+T0(EBr!t5k&UA@CPe=mD)a@u#X{0c^fnY$z4L8N$!dBO`+iO#-aIl>czMaaerQ6d!xhF|T$4|_O(M)v`JHTwNVckpnFV9W{5VzOgZd +^Q&xHKS+B3-l6gCk^+lzWD=18qId6J$N7wtoESWavZSiJ~W`$=nvZFu+!*6Z`kkIPOFSrW-JMG6w+<6 +P6ItgoimB?#C79w5hngWV4_1bQjB2gy)aC;&v2=Yz{6^pFF%${A%jWeG|FUQy#+poKFzMrB;}4adZhDvfxd&p9s8IoFs8!L*J64gzW!Ae?z3H*NkT%4pihjOkRti+p8*05$?Hw +*XQLNW)bQ5Hlq54$0RyrdQHt2Mt^~eoY`PNw+UNtgDbB(ZTEohD%``NR!49T0A|$M;c%}N{5_CaixOK +y-NKE!D|wG{8#_AD_HDatC!B`{4TAmS0`MnbfUJ&EOitzxMOpq8atd9# +`6657|#=SmX6658%kIs8161*bDtCaCwnM{xvudI&?)`_vcopG;^bz-dNeZ)ssDe>0cEqG0g*Bh0C&zt +;Jq%6CN1c~u_#h8)3Uvf9Zc(civS{Yr%jC2*zMuNn6bHwUs5M$$r)zKiv#u1E7Vr(A4*d)egQaR?&(! +NtZI3_Wg^}_^{QzS@?X3A)%ig(DQrrr_b-TRF8TT}0d@h)8*1loPFS0@r|5o4=1c|YgGTN!)9 +Wtqp7BO1yGum%WwTRI|#xr_(;@bM?( +8u-dYRd`+VZ=BH)9)SDyA1IfKP45KEJ-cyg4^M?NKGXV6yNgyVZQk2WHIJC_9 +L8K=09@+3Toz2InReqne^^ZAGjZw!ed=wKxtwY`)}p?-BY*UNuj45TDKdLH^1Qv_GjCmo5h>mBzdM|mHbF3Ogco +Gpdy%H<@qug^AQXD&B8&?Gdc2qi+67#vZAY$>2p*z6?b7+ON&`?)77&z${71cq~mSbq_$EsTdriYOoV +J7xVVM(vGyC^ZCl0-TH{sJzqfe4#+I%dpCgStIM{yAfUPh2^aq;*Y2521{|4@He`{FwzC;Dw4(nr +X>CS$Y9z@0r0|ajCdomNVFz8M&L!Xj8qmQ9K#q^yGg~O9KQH0000807zB_Q~lfVzU%}50I&@J044wc0 +B~t=FJE?LZe(wAFJow7a%5$6FKuFDXkl`5Wpr?IZ(?O~E^v9RR%>tKMil)XDgWVWqiR8ADQwj*8*NoW +OoB=P8KYISiY#Ma!)VP68jq8*zrJT|9ws*XsA_>^&)j?Fo^v1e)m8mPH_qK~Orv4n8P6QL8BO|s(;3^ +V%Zn@WR!kEumO=&z^;N_qQhHEdMUn(Gm*Saqik)y3=#&jbY&bMZQwY;G0QML|DU5*OEkARjHmpg-WNN +^z!Bq +7%tr{r?V`kA9WoxYMri$w;SjkNi}E)hKh`U_1(veaTt?+9TUGK!{Kvr@7!nXR_?2Z^!<>Oeb}s<4=9r +Q(ee(zFm$c+?M?Ct{%ky|wbknwe4+n-|c!rme~-3?yleL|Q}!Aq@pG`b}vIuvU?Xw{|U)5!A4p4eNEf +Pr}`JP9sjilkQluMqJZuk%-WGk3`5aWeQT-*u`{pRC-`Tne9TMfdYP>o@-)({rq%WB3PpL?e6}(9b^k +KP$Mvw0t#YdJ)4MoO9|4c_nzl;IMR(s$Q*cu?BNEGAp3$Mr3F)#a-1bC3jt&LH1zH#bB}uCNBY!r-QL +)HyoNC{gP(i5dI{A_n##QA)SyMy1A@KJ=_}=Y6-^dDs2D(>%Hd-y90rF^@v5&4NhaAl +y$fZBeAh9AkE{mK+`3v&7Cx_;Y-T*kM#!2ah>i#ktbo*Gltcd +QOhNg6!9WkHs1b{5`Mc0vBJB8Ep@_s!yb>lx2-Bp^(t7Da@N`%wo|=DqIo8sgA(Pj(;iPe|G}xcEbxK +z<#d^o@98o0W&k@UNPPf<$$Wh4554~>gqer8VNC>9GavO*t%N0%3^T1!ka^eGqN>pl2Mc>zrw05;@z8 +spyH7akP+GPJ&wAB&_n{R^sI3E!ng}^b`CGw#-FcJ@7=o&9 +!9s7(Q{<{osut2M>d(3CEn$!_$rjjWG(rgA^(cY<;A~HO9KQH0000807zB_Qvsv0ka+_D01^lQ044wc +0B~t=FJE?LZe(wAFJow7a%5$6FKuFDb7yjIb#QQUZ(?O~E^v93Rnd;xIuL!Y)PERhUqGT1=&jUt)rXJ +*rDY*Xf>x{Q3YlaA?ixF?on570-!W`9WW!4L1|fmx%<;@QW9RDX@&n_^Y`%oW+@CC?35*vj|0leHy?X +oR3gSJ&O7k6;On|Ra1a9C){oz6|SWEtZ+E66fGi}g9iWU>AG$aJ=6G6o>m)vp!e!~Y8Dn|_}1ABqoB` +ndJgyl$UheIKG!Yo(P&|jS^ef;CNAyE1hNHj8wDQwFlXL~cI9a&z|W_t@AzYC+mVCXn5;``tU0igpM2 ++^Q^z_e<|SJG;}EvU_o>3(rb05?qEL%_vO^8>sCD${~h5R`VWJw%tQBz$(u@yXV*$bYaGziQX?Hh|m&@`on6!C(0hl03Bvs3p5 +b9%j{C!B?z)pSDCNrP`8@`mq+3~xko>UlA3*_A%!hMVK6I1&ruK4CUqjGgBzJJ2p)@wXD~`Gmwhr?@@CaBO2kB +&5ova`ZTFM_&6jKllOw0h$w%`Gvb~KrF0L*%2)@z@m%s?G#n1z4L=Nq0PDytz4pLl`RzYmR;19|H<_l +x)qlE-c3KsAuGN-=AT!;G^eGadow-PGj8r*=%|11;>v;O9i7~&qX=0)u^QeizBKiGd)5+TZ5j0V|?dZ +dB)P&8(Zmq?3uUl+C!zL=pKmYUjMWX>NHBno>``84HX{EKFeQfO)-&QF1Ndz@6aWAK2mnY{22(zm6sHFb +006~h0012T003}la4%nWWo~3|axY_OVRB?;bT4gUV{>zDE^vA6TwQOYNEUq`Y5v2N{LtN!$>MK3t$9c +?PEV$t57~}d{S;wbPS_=o1)NNFwf}wVQef9L7lNIS_Do_Y0vD$aR8iD9T&k$m_zxTmz8s&z@c3|WIv& +8m@a*s_6kuyV|5Ss?wScqGy>flqgTr7Zz>VN0_>b#(_FfQPfg{uQBr*dZ`)=$?>~G>uc)_g*A(%tVuw +aH$5l4=FD`eWu&BAvbJ9YyQb74+&>}XLg8Ww-0h>*D-uN`~;1D-$D)+z +X$uC2O|VWFcOgn@5C%C&SBukp*vl~LHIHH^a~~doY>*N;N11DLiYv?kcSyV9KiWQd>#19dXSamxzo@N +AF#gWp%4%S^Y~sK-e-6S7U0-Egkt7KY7eqPVzB+$KY|bmZi1ORf55bbKa-`xV~<666J?e8<@5rEB9g} +oUxY70S*G*F)N>uoJ#-!6M*{2!ZZR>sUe(wU9?Cd96gUbbe^Sk#LAY{zviCa?MluPSdyQ;=DP@M9`^%y^0b6m&b@+qSQU2$uAdNJ%d;R?B&96uy=+x56|jiJeBsrRLW1Gj@#OcjiwW$Xeu +r=SqtX89H^4O8#WH37XU1r%oI36BPzGDfFjwh#s@feQIMz9a(` +=iP6;l*%&1m_o{^Rw|_4`3`rR?$h7a&^6|=(*&)31ofFL~OfWl$HOxJkgOnNN)z$_MMO?+!3y9U|`G3 +=JsK;lj#NiRZ?h~XH~7A!JUI2#C5n2UD=FeQ_P)aq*~Tyh_IK@is +AAc$gwp6p|0v)O2vMx$jm04~OuRP=$t>2DQ#7g<9;|1=MS8`u;6LN;xA{_tWtv7Kwz7vosAD1^v9>-b +9t*Km1xI2^;T@bTxL+Xl%d4|kini81E$C@En%1< +!?c9KK?bLU!9fP6gkgpZvl50GGR#UC1Tu&c27wHsgkg>hbC$tOwj;q@5`0?rC*#SgJJGwIy}@X7HmZ} +P@kB}mmjr5mkLBMasb-pn*{gP3Ow#dWe0ef>*|h9HEhFnMx30jKO{$@K_4KnY9l_{wI5@qOwqQ8G79e +~7M)D_n7E@~;CzmH@zYT^G^4|}RXGhUxY)?J$;TOo{sIJORJ}Go=JJLK#Y=;F!c!-p5JmKlC?eNkQ{@ +Tt-DcFp@eS`212kuqp!~c|?9-jBxeOyge5=AYMR8}O-GLd9O(JT|iVwKz^hf>95jzb7cYMpyW` ++{1~eJIsi?xVxy&=Qs^m*&U;xP2&9OLLg@F8M-tJz=S0X^tF#%b`?jnWIO(uv)c}?@Fc89KGTkX~I&K +m>hkS@22dO*Y#YfP?6ws4nV2SCOMSKRLfy%s*==wTR+frMdLfF6e703-~<0 +`xFUu>mb%AYm8|K*BIAKo7%k01}2_0eTpQ1CTHb3(&(boJl5O7=2?Sy|X7_7>CV-c+wVYO`qs +D^dQU2+1F=qZhDYqJ3Csfrk+}#X`p4>`h4Q<(}uzxm}O*I0L8FZH;=WZX*7G}LdZLeW@af`iM=rOtES +jhES0#DtLRoS71@nJVk*U@8vs&!lOMxO3)0AedxfNm=|0I?M;K(`effY=Hapx +X)#Kx_pI&}{`LM8sCG0Nqw_0AedxfNm?y>b9cb>CUUz3MTOOytbk^?pDG;Yy}4(wt{ZP=o{lJWT&zD^ +PQwidftaQn>^onnWr?{${1wML#xuVdtklhOSMV$=ZqYzD^r`p|G5DUkM?=u7R<)GPWG; +v+6RUVM}7rH6udV1o3T8W&M+?2mb2gJtuiw3Kt4D4tY-2oL3UMXQ94u}H~2gCw&2gCu017ZQX1L6S00 +kHtx0dWB0fLMU;fH(kgKrBFaKpcQLAi9}zKxwD(ETb;Bf2yml;dh(#((wfHJODqeH+_A#q+6p69xXhV +fhrkHTjY&)XCw>tMm3lOIAzCnoJA-x^d>=sM`c5*lMP*(@VwTLqnTO)v3*kEtVEeZB5Xo(U?>*FUb`{ +)_VuS +q(?VyC$BSnPw7N^TKyhH=>m7m==;Mz&-HnKR?4@?^GgeC^KT%Ri3C_;W0me@EZQ0p6$TY%^77<-HNm% +KJvnq7&c90f=v80lIIjnSV-~C@*~1Q!~e0AEJNC-?DcJX0r3BC$kfy;6}i$eI+881tLoGI)NYCm|-D1 +m+6D-JmH&=-J9&Cl8!^o7ZhQLT~-iYxpxAmO%~POTO6CRLBwtq+T1o`mqbrL5x=_Zss5%@gnr22l^^g +3tE#QhCkZKqDxV~jJ?&C$q*%ieg+Ul=%AK}Hj%r=AnIbURGPPdnb0*jqhvTMkFu)(UdN81Z%{_ +M@f^8;JYbKAXSi$BPh+wPa76s^n%`p(cHtX~P&j~iiKm?lw=z`4wh+wk-U9dR-5o{Kq3pNKJg3SVS!R +7!&uvx(FjlReOh;Xt1T{t-a5l$AM3nvF4!pQ=3;p6~BI9b3C9MsvyL7llkPugin?OY$%*-={9%N9j<+ +slEUo2%hpMh+|MVFK>*i>fh?wOC9M9mmQZMSf6a8DoXhu17yeTd^K5F}vd|RPpE$KBL`$TFW$AJ$f0G +4C*x13b|;C&BRKJs|5u3ah>b7VtKJv4G6HsdL$rlF*!0C-Gc#f7{jH*7`{7w$iN_umrHi}?c~Z4(X+nmjCZxG$LWY +l;kYS+-sboPI12QZ!AiVv^6{z(6C(3*bSLQ?hwvy6Lj;sw{Dz^4YG5gKFREz_>#c3y5_?kS3&}|7fAN +HAFD#lHB&5iNR0d!l!0f;SO0qeHpM~`sHURPsjudN}rWVNZk0_e7+A+Ndy^Ov(Hjb^>k%KqB*MhCBT+ +>moA*uOmS3BnV-2BZh9%10~>e)=h(K@^m#Ye#oat&QIK^`nvshv@FqF&2zN*}<2`6b?<<$zxqvu5BzE +T~s`mspHz|_SUkD)>E(Br_@o~G}^mfM{Ub!Z{Bu#{G#GMJEqau^?K=8M&||k{H^M%YZ~2Mudl9UbT@B +DeST4KpFPv)?RvfRETi{2-eI%4vrNO<^}4ex!`iq_DeISCRNQCZH2S+*t=eLwq@ +8)Z&Tave`*@jU9Y35WlXnkU$63xyP9Jf&TiKu)Evuj$enS~7%NlzQ*XXef60q{A$f5w-kWx{d+d^UWg +6qF60K}h3RkL|`ijBId^sNtRazLQ{6A1j0|XQR000O8NLB_@V!lkZ{|5j7i5LI?CIA2caA|NaUv_0~W +N&gWV`yP=WMy!HA$V~P$nVZ!SViL3jB3^bU +7v(HtH_(c4Gi^IL?$_T_e!&JCmgvq>LTFc2*XvVNIXf$V=-RmL4XEGi7=y7v*ZpDVH#$Ij_VL3RO_qX +&S}vq#xztgPAnDV+`j2#7i>97@723xedueH31muW7*n!eygR}twsf?Z{0|~+#@hV)k7Mj$Yf)e4(Fc5 +)nTWf){)hhI)Z3R+!KECR;($N=|O%F=PFKjKWKsYqpL^6w)S(=5dH@IvWP_H7VD +`(o$8+PvB +p3u>JXNW`aUr%tN=%B0x-k^d@)1>4e&YNBW~Z8np+L$FCqpOz; +-Ljz!7U+Le$D%e3VM8|cmw?u}9B4lM2KUcWbaWDB~z$-o$osXH7|n;zPuNv|{Qw@36a9X$-kMgvh6B6 +W1+(lTFf9X*H4m4ZJ}Hxm3#GJ3oFHgTxirn?NL&+@r!>`j%6!S}7;0Ab076=c$qyWwOjuxv1h=-Xg +RX@8IBj%s`n+15eDAwSNC)=ITABQ6<={0r{@ERz7g~RhSXvpU(dx#M-ocTT0Uv0Rrfs&TF~rr8N^l<+ +&CI({?T2r$V&iys7Sd<6gi*#_juuzG04g|1=QvUp^$pF1<cB0O4^0F4hAC7$trjd9N|#1l3wTJL01#gzt^`R$Aui=<3)D=w9rT=qLi%qa +M4sy!;b~JvE=XY6@uFSTH_@E7OhvTnfLKf;*mV9(8MT-=0W$Kb}J77Df;xB+oa##S;a(F52YcXe64c! +~cQYfn$SB=Knmx7s7zrD5T-lF34#SgxV{THS&Tp7J*_vCLyY_J>G-5r_ld@)jJjXUY#?(Ou;!&C4s#e +ve?9YnO74CG`tyJVXS|_lqOxo86bw=o)rUz`G)-TiCYbiRo=qq;77NnEo8@NOjN+5XRN#JevfbWWyZuX8`T25cRq3uNzvqi6#8w!T3i_2!6w~Bpn(NnxXmdncv&O +?C11`<92AQd+?EGpC)4kn{vzf>Oj-HKBWyN&HC9`KX-e7iStB6vX~TCSA`=~(>cLNfxEN@R1Ff;Tl@W +H>pXMg*eZTuAJ>*dlR&-B!7Ibek((Dlm_Y>kx~%FL-~+Qz8rF;QhVCL5cZ$!RDs`y?@1ww68;4pIvMK +>WxAs!wyx!dLt|+G6g$hY#wZyONSj3WgEjU8bx^=OB~QBC*(bE83b$p>iit;TZXfStybsPkH0JFoTmz +%<7_2^^UKTG=lR8_FMn$}vtL>Vy%brV8~+YEC+i_a-y|{%t@I@E`~Hs_WBDD)JsGm41J(ht{J2pwMlt +W=!PCC7&rk73xug=BI=&-j;i6jk$=_AzC%?|<75!_(0 +#JLN&?m2WJ8`Jz1echUg*J)7ZV0+VbQ!ZZDKcUL-SI|Jdq>u@|IHgS!7%8Fg~baqRZjIq#a}8N*kOR? +f}wr6RIUSomHMuiPIm29uajB+%rEbc%3wG#?~Q!rmCQTYB)FH@`va?v_vTiyEO!g6xj$opuUc(2npjO +rprmA5NlWFqM&~6)7w$aYfspcln$(I)>8(Op6FD#R+ybU(`W2|x*f%qC(U&g;heOJL0Z>Z=1QY-O00; +m`Rt8fK>r@eo1pojj5dZ)t0001RX>c!Jc4cm4Z*nhVXkl_>WppodVqa&L8TaB^>AWpXZXdBs;-Z{ +j!iglNhjSY-Bqi_Sf%mLfRxi3;VJjja2M&KA)@4m#eGe3 +);?;JA}U5afZGF?f$6q3@2o-zJI%d$&$iIu>})^Lq|pwn1+G;hjC6|D%gf9jVIxfC`}DS)X>mK1tE&o +5Fo>F1v3l3FhHtL|L% +)4li38sC2HOS+6%N#!>kiv^WRP3TyDFrfNf@%$<%DMzL9pZdUa5FS`XWAnFaqj4u>h!406928@L9&Mc +)!Mf~Ls{}V2)fP8m53DtX~FuI)06j3`H=((a4w49qQI`9BH8AC_}C>k-H+JGT5Kt%MrR2E<*BR1dJy; +wx(xAo3YwbEI*pN3QDQ;i<)aG;1f~@ZoL*OeDZd%oF~Bkm&3d%MoT2Vom2UuUZHch8K6z +8%-zB8ot6y%kx^p$?F3OV@~iClbu4b)tWOzM$Z%xW{0+0gn{FAdZ^NByM1@^YCY(>lcD4L&>eZuf^o~ +6xSeUg<-vIBjYqyy!z@#p1v&}ou~@$cdXAVYiSfj~A&l$H|5r@3Mk9FyOR}Mu@Q`AwML@7x*1OA+$)# +8%PNU98SRa7RK}b`DtzuY4SgZ$WwlE)=u412rwJO{;P>P5*j+yXLzsu%`-Q`lMDzv3GR&>y^%JpWWQN +L;Ys5bznzTG;tz!^TC`7B|CzJHr5Y@^!#*;yzVuh&Y>Fc6qb+j|{6@u_*TC*$NK(^=A)qT8Z$FWD=~a +UvG8m@kN8L||W0o=I0+e-zx`-~J44gZ8xl)biZcFc^#;oj#Txe6?QO;p6DV4;szeMp^$lfBOP+@m9F` +{o+4@C(m)7_m}1&f8^rpj76t2DuL;m)98KDpd6>EKXdwXz +?R(n(5|F{?I?%iE!_sg8*@>#-r9=Z2{cPZYW)#n0p +heH+;>H*V4~mGWDKazgN`Lvwe3JTg7K$!D)ME=)T3WuCf0@PP7KU-SKmcS2^rOJ2kDh5uHO2FdV-@P| +=jnvje+yjFY%h0;@tIR(sdF{!mCOJlNa&XQ8X}a$GKuQt+TGf5NrO$u>Es+fS@6;dUw(OvsstBXIOM` +jt!*e5YfLrrC_$*o@uW?_Gm5p$)GCn=6%#6|g#=Hb%C%x@YBvg)AWT!P|894*3;fP*Yo*`qTsE1g4?tqMFjYNlu{cun0!7Nsj%eF +L=3z0)JYwC);;qf6}>WIC6MRHX(qai`cwWnd)vbY_B#R5fc@YWb<^~Qb%WO%lBYx^`|Mg3LG$r%M!ei +N(bAsl@!?8(lF{9Ss26n@ehO9}o3P)h>@6aWAK2mnY{22=b_1ivZ;000^f001EX003}la4%nWWo~3 +|axY_OVRB?;bT4yiX>)LLZ(?O~E^v9RR&8(FHW2<^p#R~}V5oyBUfg29njpg*za;Mb21!N{6oEiXXPc +`;8YGq0{q;M_){AX9O@|2qVv%>x-Sgb>sI#;B2X1?JgE0&To!;2%!R>I;`5R8yp1po`2L1|RqWF>v7D +Goy2wX!VzwndR3H~%YEZ1h7>y3u8fh&_Qo%!JxD=XR)mp0T#bU!$s +_jOokQt0%zR56;gE3{n)*A}8GSKLpL+itbKP*PAeG}So46ZfS;G#zLgi%hXBZX0Xo*Jnd`uxf+0gRaX +0#hC@6;I#-C`|)K!gOO+QWQFnNHL!)rZ!fug+c@^7v?LacMThvLdXOtjJV!LAVCZ;5&b5W1xRGX7aO~ +liilX*+zqNk=VIQCXD~!f`NACvRK#hT&SM^0?;#ISXauHVZ5Q>bRIG1P@ph>|UlM*M^EDu+{Ve(uDos +o9uGz}t?I0bH3L4B<3RKv&^*pD%HxN@ +km4!VcUi2%$-iWUK7AW%!SS*t<$;?dO?_pBt0WEjK@0X|3HP-lw=&iiaj9}JVZ_n28@o)dh>I|jHOs+ +q{*k0^&0pBgftF(Rh-(0TJfmN7N)GUL#H5YI`DgoteE%`Qzbm|@ACy=_hTuQ1GiEeYdUIM=gW7k*5yU +({bdVa=GmoF5A?>}7fu&a==H0Gk_j}EdAMRqqgg9CCl#oY_E?S5*zntp!4B6Sy?b1utC;*<#<}l7*)VYLb +Hf7jdO9z4_cobcsNtHN(UG;n>K+ME4Wy_P`Z$7d$6O`whsu~N#DD!aWN-!8uDXPA9lyN(pX*3XYjG|G +PRqgDP_lIo?D$jn;G==4q?G*^0`Q!OM1P!l!l$*Un(CoSH#61XG6eX6+CCL@xJY6>4{-S~Yb`xYpw+S +jmWawtG;c4%!bBuHRY*|z);Ov6!)OLWMpU9_BqI#;EQ>9SQf3mVoRihyV<~G`84R}eB2I3awbL){p6|Hso_>9-l5i3qX7 +4RK>&*dzx9$(ZgwF=a5D>A8_i?!)E&*5RL@j035376*(euY+*tzCHk(}atsM0$KS?U%?k%Uq;fFOD`mgj>!XX-W7U|NGP5tv!rsy9_WWMQAB^v=j>cM}z79)i0u(Y`t^)cTV-5TD937F{I%nr +biUp5Qgh-};ZgcVOC$8U_`GJ4$_UL8UJsQm=&mWA0`YTE2Um$kF-Wms~9NsuNO>0t~|3m5rbV~PKH|) +9BcgSgsUHS?8)~IIV?17X0zfem91QY-O00;m`Rt8hr`Nc!Jc4cm4Z*nhVXk +l_>WppofZfSO9a&uv9WMy<^V{~tFE^v9xT5WUNxDo!InfwPdo~z`k#FOMCmvfq%v1Q3oPnJEFJZYL_B +#MM2oGDTzNISY~`rEq;5F|)~lI?3d$HX%fxx2u=J=g_!aA3a3Y43b6BEvzqHyZcI>F~1qGpV3Gc>e5w +OjeX!hHS}v+auk;rG#;E5&X(L&n7oMyQ3k8H_plqIgN-*BkDv!NE{%wK!B1l@L9xw@QmJ3FWAtK1Pc- +wgd@XL4mhq8^px1eG*caaXAMtSww8#{XPg$z&dc*VvEmW-vzxTo0!~@5LiqopFL#D1vm@fpcz)x@kB0<@E2uCufB<6Gxdkqu;#N@+Z^8WH>LQX~ +>$%m8c>yy#s<69sGpTJviwiLzI8;^lx&@8n5XbW>k2^YQV?t5T4IUNiKlaHuDe=r&K#$(dIye22)>g0 +Md=-v!ZuF2KS_0{FLXMvX~RfZNOHQ72f^a5nA1F$FLjm^Zj3Xk(iUuOx5pAo__vp5yHHz1j(0S5N|eNFb8 +WSR9$#8rqMaRxe{*_5A|a}n1pQLZ{lbiL0UJ+(Ds+Hy`+8SL6$cNvcbUOZOY7@{5U=CBtzv#0iHZxx)?9zc}rV4Rhya>T!`8|H&9BMOZzU{{lC-6adT1%|PItb|*Zh_K`Ifx2Sy&S0*?0+F?#)UESiO{eoMXjXdfcmbzHHfbfm#CJduQXYG +e8a@esyuBEV1{XIM)5-hm-uV6H@C?XXR;MN3z8{>wpPqi4^rmOMNv}HrSbqoEU^&5h!#rA_*FJsz`li ++V{I%czQcEV#8+R+o>z`XkNB{cwZ-qBc$aisa1=PRQ7!R6n-n{y;*{&Uv+6U&l!9 +8!bJFhymM&2gLm2q?m9Id?^uL>OPy&SI#9Iy9s{7~RH+RO2#!0~D?$BzY$mwP$dM+KH2YTr~CxpYCMQ +$+abw9cuwXh7lOFQ*FuZ13_oXPpd(3JmZ;uG62^6*uTX +U`k^|Vu>)Dp=1nTRTwxciWvcN?`$uoY7nS3AU0;ROpqO^wiVKcL#qM)Kp{!=i=u@2AOS~6O8Si3TujL +Z3W7{DnG91Ex+YH6whP@4=tHsMNy{j_>PZtho5@@NDe24(bDBeG15J`BZ8ZOB{h96E+gq99Si)hY0@C +hRkfWej4X1|rq4Z(mahQ{qI*lz-lOb%tz#u-Pwu`_q31fI(mDnn-k-HFVA&uhD*Oii;AMvPPHwD>FCd +Qc$sR;Ox^2$)f=6PEbqew6f6-_p#E*?5f7V;<-8vsh<&|K&ucyUUbSZDm02z%mL#Gym2$>=ec17rHLhZk+m*+Eej+}f#prnB68ebpPZhO +(>_75%{S?wW!fEx{X+0I$Aj}1=GsQT^1$8(VBgQ80e`LbEl*Fq3jGTPIOqnEU;VbIz7K+4i~NsPjki!Mp|?OAou2&tF?D{Hs-1QCLA)u{& +1gl`MN2@H&MtR%~aAv(p>#sg{hLT5n;F2RG +s$Aqu*^R6!J$hm7dv<(|WS*NmG$#?bj+n$?gQzQWJR~HFq-XOU?G{|C13-^`lk*;{skh7{wyvpmhSnPb{5!D&gDNrFr4sP*dv3O5wcT~0_SeJ>a8XJ^_4ePsDlyy}t;h{Ecdn4HsN|2$CMxgLSRBd;XH{9jWIm|uA5x))1bFz2h4!D#;Uc8y8%Ao@#Wv+H#&Z9 +Gdf)&_8?>_#N=Eq9Y#)Cqg@z0Oj?HD!l?~kb7KKuAZ#LOY*G0u#G$mrMkjh>d0?k=5Uljet`mQ(@RLH +ut_x}*)MQH4_I-PW%QmYArq$b4qiTF)v#Q9KRZiL6qDjUvvRG6lQE=OrrZ0_wWGw&Gpgo-w<7xz{U%_ +(B)RYg0QMiy$LZ^2vr%bU^JbTFx!pb=H~0%PTD)KA>vc_bu1eb6P`W|HcMj{^3m536K+dklIP6H$ZMI +HLWGT+R@u(LT$ad!kUBLtFVN`GJ2C`Mgy=M>4)ZwwnMh3)ooEG^r7BD5#thhy>cK)zok +tJTYC*@yG;EreW#J29A9>azRPcd^DJN(CUz`EW3bW_a0FeU4{+e!eLe!BEFP`so=s8K)0jIp&n><|Ms +&Es{$iS{ws@9R?s(n|6DlnvqqPrDafzLBcpnjz1cvyS1+8v)eo!c=_&#G*1kMX_f7MI7=1US&-s2hrGQtFJ7f{vWJ&<#XV#>ezv75PY-vV0gR(yBe&FlRo@XfahTguP +mW*Ll*Ai}zcyLm0e&VW+iA>gAs)+GoFA(b;qsniLqC{JKGmS)mTtMxkTVr8vj&-E?uB1C$(iz1o6iY!_aq|sZq#X~MwUzG@v>g?1G#IbK8^WPt+1VVMi(~ +b7px?*OqV;uOdgzTi>z#N9trT34a<(yuG!pStl}I}U5{);cQm01-penDgr +@AuwMK(TQ#TL!lQeiKl1vb;uX8YNCNQ@GmifHk9tV98wM~MqtI3Yn|wnZwqTAd{*B>Mwt&7@mwwh@+|5ix7}n1n9Z$j7f?4}i1&vlO +PwK&2|N94h+=lA!Xg2kapB}XhsD+SLFzJvy3}l~`WKlK{m*@jau$Lmg4KJA1%qM +4znyHIy7(RBa$|I#!rjIVk+O5tG>wd?{cRY_!-|;=ggW-B|KT(#ULi(xvMN~C}2VT_URZARR{~J@~eD +-8x<)l2xTsa9l3}&jf{{v7<0|XQR000O8NLB_@)KZ>MQ3L=0APWEhApigXaA|NaUv_0~WN&gWV`yP=W +MyAWpXZXd97AkZ|X)6em~Or52NG-QsP3YQ{~cBCB!7CU?bb;C{LEL$FRzJ7wxW-Lw|i| +4d$NqoT}yp&*j^1zL_03KR^AzgZnh_U^wWxUg*NZ(C_^TAH<%2{&Wt}9Kly?#zexQCo=@5p^$$u&J#z +f8Kh)$D6$S72xb_?KH?j4xj+Rn2Xg|&CU|IcnkD4x1Zj^qSDQxw*C&C*vLHat?tF))1gZ^@dqpPEQJ>*t9fKHFWdVngB+LdWA9LGsOzH1fprcNE +k28T#AhjBob#+m8hlFD_4l1Wp3Vy-W@Du327ogVaD|81QNtRBC?;QvH*q5SiZEjQe;$>SYseNz1&zem +5~#5Yo7p7ZOW@QNRcAUM=&&s}112iqIdy@)5F{1*PE!R_gi>Q(@|K05VE8(Sp8ZJ#UGD|Hc7venMK5< +0Op242TDgK^#ey?JrpiId=#5$*#rdk(u+$8E>$Kfa*`H$|hf);sJa751b#CsUn6bqC(?c2&%NIpea8&~KR7sSD{6h-?%i=bKZ-~0amti_Z +D04z$p7v9K@<|$j}hqm6Ynt|ME?_oFrt-yf&t|n=scsLNlT?rm}<~CibfNDzlW<6>DwaqM)Rk8=+uL; +d&-8}Ua606G*ITx`^L;?%@J^J1vsbL +v+Re>z@%s|}2EQJ#U8i5HCx3reUR<02+0S27?>KGx6Se}|1|ZjXdosR`{2Ne90|XQR000O8NLB_@&2z +cHzyJUM`v3p{9smFUaA|NaUv_0~WN&gWV`yP=WMyV>WaCuFSKT8BL6vX>LzQd5_&L&)8bs +&g%g^hxW?ULPnyLp(rEctV~-`@2&!E$DP12e`Lt=>Hs#5A@*pl>dgZbaDIf9Z1P{T<1;l=IV*5XN +KS#&G4k_MfWqzk?)pge`U57iETYIpFE%-RSzCFD>Z`MD+mhDX4@9b{t{{T=+0|XQR000O8NLB_@tc9W +!1ONa40ssI2BLDyZaA|NaUv_0~WN&gWV`yP=WMy?y-E^v9{f&c5W +O4le;BwZB<;#X6QCCXI_z%uqd>PCbhC#d4HR0UY$6gVkW^eF$ba7%QnF898Vs&8`T;Gf6r>wsml&K>UGJbax&`YnBt~Z!MAm%fSBs+VU2jBDfNY5n;47<}-iac-rwA>TLo- +!6sZ|QSl1Psz8tH`LjVEb5Yt|h%R_qt-&hD@3WXFmJflL@SFgV^>;JUGhUKHnzS*cqLcsz6U_3{;4bd +n%>6Y_*LFdX}D-1snTkbm=8C2A}H>nn)-&~@BMC2R_cw<3=`dr^{wOnC6mVo0Sy~SeD|*C4Q +dU-ch1D@u6zCd_eZkPp1kp(AXgMHQHkMh2O!5}I7a3e5ntueRVU5fY`~;N^hup3cKm{dOlFC-t$WWN(a~t#5j~ba2yAcesi}KIEd@(#7UN{uH{3Kk=&h*x24oG}V2f$_^`F1l;SSvDS%m +^J@s@row7wsrW@V$T7~?d;_Km}6dKMnTG6G?(PEd$Mw4#5k64XmE_6ftRQWo&hiFQnFoTL!RwL$@ll0 +9C4zllbx=yn2cLQO`GPZ>NuJrj_}r-)%jTJB=$*PeU{AkEbZZ#JKH5DQhXJ +VP1^4E$KG)0$(W5s +pxG*BDzahfx_}X?mf~qy5Tx7Y36&dK=?VS|@g@1C#Na40lshSVV&h{vSDEeIF)c?~tOQm!%?H#|-gujHc=$0vCwICx;TwukL*pxKxpKZLQ*wIh@HwpIlFYxUMe18BAJQ$1nM}06$H^B ++jVG8F!$A#^(W0OQ{9Ma(h>C)=u_&T}U9XgA`#(@i0|XQR000O8NLB_@000000ssI200000C;$KeaA| +NaUv_0~WN&gWV`yP=WMy>~Y-ZmvyM`?fEu~WTLDoFY%v<08U8Sga)as+t=luFC-KVwcZ(pmj^mUXIzD7bXlj>IzqOfwsDyHGT8km?E=Y;MiMpe2RHqtv +QSA6x=S1VVvB882!3d^ujQB@e86}nZn8_A3+qc{`oXGB$El42b3j3Tj|07NJ)#B_-gZCkmb9d7YxYrl +e;6RgyZlr~9>B1vv!m}XJsfe3Pl**%m5k{BfcePo6c;4h%0DyoDUSFTvOq9eh{4te;0l%>5y=b-{)Ot +Qdmmn0_bCN&1{jU$CgN#YR{QySf&*$+e_4*5ug;gTs)QedF6$g2v9IKRWtjT01jOo%BQ6`0#oRwO#uL +N7VSaS=U(L>8uk-VRFQONloFpf +U5vqYEW=(@LT&AUcFSCB_riRrF*TtknS>~cVP+eQzZY#~!ZXYM3utTx>0>)ru3@bP=h{4!-u^VJ-k6l +(63CSSr3MNbZR51#aBDPkVn1cyq69oD|n%UCc+|t6hx?zbY)$^N7?J5qR +5wXLh&afMRqh%_{H=grjHdAMiCiVO*tqM;!I3Jtb!N^!=hC2k`g6Qmcoc#)q`gFRDz8nrSvK`W{;Aqo +F*Z;_-G^3(#8B#WdA3SsQ0LU>X20QQ<&)&RhV^rKO-QJ6>!f2z`>S)=BRYtH#tHEW-A6z;;)MFKCXw0 +$OYI=n5X^vocTEw*8=g#8DT +R5Fn&PB6Tw2yTbOY*}O*-=)7{V>HaLrcr%q`xTsC1*@O9lwA}vHjM=vf_JUpjBP7-BF3xMpP<>;Ozxu +K#p{3Eb2Age$t6B<-Lu=vBswawLfk%peS3Ofi`sU(LOHY9EDhV}J3IN)RV3{wPLKE}fo2yN4`C~9i(I +53*uruP}{S?g$@BrWexOcL=indFVDAk2pRlo?H$1pL9^npl8P~ft5u)t8LS`TIHghGu+L{6xqW)8&xQ +A%B*wZ6W-TqyR~7>a-@YSkzKs@1m&PQpR2gi?}=-f}QW2>hbJgns^|?&n|XLya(LlB{As29UCSS0n;| +QTRk63t05BD->qj!2!w5KmSeorAV_U%=FeTN}zPn`AhpyTjTN*=VAcyMovLVxh@DTSI<3-wtVD1PD#{ +-A|a5rUfZ7Th5L(3p=1+6tmNnUlF_ii%VDSoMO>3{}6)UrD5a7b(_kX ++_T*C+>6|7?q%*(?se`>?rrW}?tM_PrS?rnYmIC&JYf4t1y&!5h{hJTMjKh{^14Q)j{E2%O>!}I4+S({JkmgZ +9w`Rx;YPSMK?6zi%$u{szi=oPY2X``!^jWzd69M6L(@q-U~6){q$hO7(Ln03(_w=P+itpV$bb=4ZOCa +o!J+PY?4w{BQBty|WNb=$gQowM#*_pJNY1M8vn#CmEyvz}WoLP2?lSI$~5tyk7->y7m`GiJTB-e;~@Y +3qaa(fVY4w!T=uW{zbBGJ~1pnH!lCnUk5J%y4ETb0%{(GnzS +TV$9MOz*q@kr!uDjV}LNf`0|9;b->cF4KaY14K9GzEd0Zv{0yTfC@YKa>s*BREbpd0HGA*tN8e!o3Si|@55 +T3v%YmEhVh?303k(WYtA_(<8EUtS$m_mGc`sGu|lEiu!zr&yKO?(1h!PjaPY;oEPd}+QiUz=~tx8^(Zy{VaL^Mm=({A7MMznH(y9h)1NJ3co +!cVh13+|b>~oHnnS*UcN|P4kvHW8Q +)P?wa?^`{o1lq4~%hGDpl)=4tb+dCv6Zj=T_H-kKYQQN3?6r-11J801SY0J!V8^C3`Z@MBQQVBjwh3O +v=8ajtnvtCi)1NE=<~^pcUBLf|(P(L+kbrl9d3^4nz_VR*OHX74U#Zgzcem$Yz0v3 +jcXkTjHjK4mlDWzy7p6S(nnBNiq)@+UdU)qEf6*o8UoH9`LCWmHHQwhOuTY0_Y%+GNB9|jjX!|58^Z( +m6duLnc*>v8Tg2(Li4xrk^PbF#D0t>_#TH+JDAStauSYF&&`WXOR4Q(5&@`@Ga~ph4=T%H|nAs6#j9c +N7IXV<(4#Ta_3@f<{h+U%Oj~XwsLZ2wbeXRdgnvyMBw{73KtL59mWbZi8i7&gcQZO-4vlK_&3GPV27% +6Hme(S_vK5d5=3b)G~j}nR(_I7ZBx49@Z1Tz<1+<%O@I7w9uiQs3*I6y&=Fn(DEGqGJ!Wv*BCRF#ATu +e{v7A1M9k3b=6pe={F6#_=tD8$ZAg!LUEaFK`-v#E*@O_!u6yS4*YeJc)Hfds?)^YmNCZqp{kjS-Fl?-fkNqv&m8mp)Av>1 +6m>thv%3jW1$zIJ)WGAyz+3D=H?Dg!8?9J@0>`eA{_D=S0_FndW_CfYx_EGk6b~gJm`>IwI1HBvYw($ +rMy8z@SJr~U+kySjMc(Em*6HoY>qZQHkp_1P#)+50`#B~dd?c02894%i|P}8a$Ge1rr)raHy7{uyjj? +tI_*LW06Zcqid&^G-71nOm8+qeaO`>^qO32t^LUD9Kn6+JHI8S3p)g@xAbOHD!ZwuBlyZ(AOuE +MF2=D^{w(60d{_1xYcie`k!*(B61iQ);)Z5-$`j#T4O+ae<}@#_UoOyzkJ_xG~1FDKFr(C@k*@n?ggd +IF0j{ApC!b(~jAG{w%%-dYXiIfW`t(@hiYXy5@MTjqT=OrWYN(=Nip!PD!Y&cVbu87I>4G_nF)+=O!^ +t>8i4{8g_|G>7w7Q;ey4Y%S_+bw9HhQs_7@F`QNr(pU|(+>8w9LRN(=PSH@%83k2UrX7!WQSH+P&sb8 +f=NPp2^;jWC=#uM9REoHYDkO!cPbC8=wQmPErf|EVL$11(v$a5Ql*$cimpuO-7xIYDLfuZK@8L3kYvM +f*uwPP;SF7DW=-v+2X{`Ncl-ERG3>mK7|5pOl1jY4g}h1z&mw|)np_T)&9{+`!AB?V)sh_?V}^MYEtK +G35k0b!nzOe-5h@dcKx%Gv^3x1^s<9!C!Ut4d(X3tz%P%Muo$-xvUceS9w-rzU +%<2@$`h=6YID`}&U>9}IuXynnog*se$-M5o=xAb#Dq5LhHd?N +*VDyZ}>d3l=1#{&SLw0X&G0LuxS$sl{nK1-yhWLkc*9Z{s_VWITW*gmTl<*v%!)xd>@U^7Yb +x1zI5;r3F)pOAeZaDR3AZ@5$!958qf5Ia=_PV)+ReHK|3OL>C^;vUt?{)$LT^~yDe=kTss7;7CO#h&Np=F-BU6OZa?{WC4~#Fv +aqPO+!YQqMoA^_-zA!+1vM@$|@4{z&j}$AyMq7DV>9`Lx_C!pmflYV-p0HV)#;v1#D$8Ejk1e!*hf1s +;f4#k3l7R~@Hm}F1al4SoW&V30B8$mLDPiNJUV7d9|u|BHA#~Mu>KbXs3yGhG=JrHcGT}L_1Hk3q%_u+C`#`6YU +bwE)(qv(XJA0f@qUOnnHuID8rIg_(_UM550Z(A5 +Qjh0qNueQ_TZiW8qt2nef3}GW!F}u(3_T!LHk@~ukz3Mfn9)mGoin9eKdYYz4T2*#h-f +nkJ2;_2U}=yoHZV{t8j1ZYSCvMhmEgLmwlt-0KV3F=rGL+@pSE;dkeVjcDBFEGmz`#kV3KKwX{;!7I< +O2*I#KJQ|rR`D)9C~T3!I485+ljA%a>nTPiG-5d*a8$s)r4$#BB0;9#(y--GK2ZC66HfR9liM#rwwL92Hp-*MA6EZpe +={U0LiFScomz>`7WyR;zx3)lnmhL}>o%-j)3kcirnU89rn9ZxF*@ZMZv0{GkG_#XLrV91C;$uz{X^v> +6o;r!jV0*EZTb?($0>J*7)VdGPs~e*jk8gX=ahaL)TVfres~4_Q(;g;5@h0AC1olod=kgbWx8m%Y@^T +5R{|VbY%B)J4rLD_*;5EI&JB-3JA2y#h`)yZfM=Jy|q64!#BQF0h0 +#eaG2xtBT4{5#M$2k>ZPSaz*j>b7gzlF5Sy4BOQ0lT5}?62it+SwK}=j?laLU^Sl-?N<$1Ah(eKifvx +gWqE~e~nDFA!!>?(ZY>Yym`6ozr%0z8PKK5ayqiz{0000W0001RX>c!Jc4cm4Z*nhVZ)|UJVQpbAUtei%X>?y-E^v8 +GjX`U|KoEuRf&7O74_?wGHC}tDF;b=U5~0UsNXI(3*^${%n}6Sk4fOPQAMfMMpqBHX{CzaoNklthgf~ ++@4~`=nW8iVj{aMuDP73&We1P|6`LI$7(S-uOr&a=N;u)o_T*IO)SxSc!7E8mTo!EvtLk40|XQR0 +00O8NLB_@`0@oYmjeI*Knef=8~^|SaA|NaUv_0~WN&gWV{dG4a$#*@FJW$TX)bViwN_D&+C~t5N8&%M +bPwiK%Gux~%2hlVaPCMrh=9{IYAb8c9`H71?d+~g`1hS%+nj+|%{|l)#yj86H{+Ra)?ATsnjHk&#)^D +QIaM*`~)r1Uw9VaxGKoS-Lm(V2=fXt?2f4=g`h9%Hzw=BzQx7IO}WS2&H0%Qvu1XU +o3H(yOj7*jz*1%iZWghrK;Ofaq(-=ZZ+g)t$%W1J=u8CL+eBvvZBy}1G2+q17MgNV^yDz-Ozb)$&`kR +i<=(5z(^?ba9r4vF=rJsA9fhQ(@JE&syh$e)=dHGA8w2sS3EK&63BQ+D*~`nKI7cnMIs;6zjYLZlJ}h +|ku}s=OtrKrt*^$K20cb$Hy~|INZ5vjKlGiDT>ps0d^z0C)$h2g812@Ld)Bqjl<@Fb+TM{Hc8NFrgCr +!zCN*rgYNhzadnWza|d?#+eF&j)Y`W0s6WkDh49+XTyOFjR2nnkb;vcCD?PW+rN)l7!wWgeD@4O_x6S +5F-tjNTq=rMpo-yY4>~lyPiYUKM%S(JnGEO->U>lh^F_nQcAHRVb;+J62$w@6^{nG|2(Aw*{qBGmKs1e0{lDUTo@~$<)wYUtjbx7cvz;Xy +)VRifriA)4LD~H!AV07uU(+Y5pqbQO)_oV)^7PC$I7!*ZfD`_|vPLCpG8Ki&g3Sedu(_;XeothQN%fU +Vb;r`-^$`kP7G=2d82j%`nqi3x{N27s%3|oHQ2^r$FwOzCS-j`UUdEpUoCeC&-|R^XQ+kxdrmz-TCvi +S4Z9y$oPvlKl6E8AW!$xwSS7dtFm7C>r>>tu7iHQBI_9Nqo4i5KLUHU1rL5E;`O#sr)XpVgVVX=Ij&&~IX1-WHc(c;fuQes`X8LcAtz+ra%Ir8EUHH+^0 +zDdyTG427sf9kHYsVwUfCr)x>JL9Id%Yi9 +K;)No31QWXiKm9xqptyyYpCE1HrwG?xk6kmJLn7b9|dbw* +wcrwJ_tBS{>wRImXNjFF4;>FmSRj4a}Wjh;S<*BQ?x$yxa*Hllpy@c8t}BIj!|qN#|9`TGQ(f8;qp;? +pUOzUZebxk=!U=GW=$!U{jesbu-u%HPE4HM|b+VagMpt0yJqNW3_<^WkNlBm?|SvpAGYNL1|U6N@`0A +9#v3EX-UI3zBlF{(SQE34x#OT(f-8*C0_sg#e{vd`E2_`gf`nn5#xe{8!IA}FDr4sZ5?N_$F#a46u<0 +RT!y=utTtu+ngu%3pT%n1udX~dG7>5pAF##TyE0WSRJo1GLRGJC{DUmB?m$r`7?jCJrP{C0c+G%4eP@ +7>?2gi=LX5g-FZC)~o3Y2D}hOVJGlSQ5qGcBktZKhWd8Aeix_l)LC0l%JodBmrx4@JhJm?m~d6VAUB8 +F|=b5Od%TN4w|cvvO4!s)Tj1R#Il-h;OGknQx^$;97TNn{Fee!BIc=hesuij7sr43>z|%Kt~SFt +XCjqD13P@=`IjMX(vW?NrHd?Ag-~H2**BTQ4`fZ3ab%Q+$b_xOY{DpxVLQIY)YZJ^%Bl-TTZj3s^4$8 +(3C<3$wyvV(yB5(2rB^^(U06S&MtZWE=3>MG%-$%FintEtv8UH|0+3Vhr_a+jlgszvn~zu5v$Gy#E0F +AnrC;Nmrw*8zZ!jG)BQ%`@@05QBx`wqh|AZldJZKnWIXJ)X!8@M3&I>2dWWMpHYRFI6{^2u0v&yE<-5lyJf3zkgfm=2n&-Y25F!Qu}pVW3WbE^ +BXK}Z58Hxoh57y`GOUsF$FZ4kUVxx1VH=Vkv{Ke(mCe@}-0yzj$|<5#^tL0W>UCUc&UD`NyX$M<;=S?kn7AFrmJ3*85||GK +01u*e;C;I}^q4z4)ji#XIcP@Fu~+lg?qIyT-U2$kn(&D{@kd{)j +6#)TE9US_dT|1sE?K6v*JYpQNxH*15|}xOW;O>L9|FP$QIZ%sJc!J;rWY{X23G#b9JEz^oK?Uqf2)>N +*(kkbSNgqjFldEnV!}DtMsl)XLm84hL1Y-K?t}L%QG2|NiUT>SEU;abRjXuQ7y=q$xr75OCTi81B+B^ +jQtDF01sh}+vb&v1cPh30U1&V)!Y&6BcT;d0GUdns%#JJD>BusZkKaW0ybKX$aJ#=?a8$P=#L)} +)qM%PKdxAiJ06U~JRZpvwYF=4L=RBV?;dD^o?>`(jAa@|fgwHBCIaHS0x(U5r==|A_7fhZP;u+r*O$5c|q79OAu7K%?(#^%r>lJf;wAQcZVcl1d_hj6W5P$dvI3euh~7Er3y +SpZ^GjX1%9Gh!hM#a4U^53vCz+DY3cP`nBHPmUj}P%h}cJI-FcwoRGkvjFd_t#fydhpiB7l^8EFiYe!P(3l4i{VJ38goS +nb#K=|E=-h+(zEai3=Uf|&^G#4~gDg~X8S#KQ^uf~oM_wHcqg(eO^ +WMph5%qIo~EQUF!5^29sHN(uX9<%xHal>(@dm50zxR>lJrvZ^_tovf62ovb{B9b~0IJhJi-y2wh2`(# +yjV=GyiJ*<+ImhTj4BdezP*&_^_$*LYr`$9F4Ra0#33)e(ejghu5V4bWQ;%;BCMzU&-!hIp@WYrLp`+ +_x)RYQa}qy1KEhVk$f?0w;ETij~5Y#LtwcG`Ydvo6>U?C9~EQ1Nh}&j~?q-5ZeLaL}(93>fZt8XVbv#NO>1@3i1qQulxJ##YWrHz=fzn#&lk +n{cxEcIRCHn%hHlXr$>z-AcGe=#HCQd-j8qqft%W=-R7WeO`4gh@4y7n!-_$3-vwi(77Z>rN@=s3b}~ +Tq9MSz*7ICylAb-P={Sy0DNu0p^`@M49Rk7k9t)99h!m +v!2c-5_L#rIEPEo}n_w8Kvmq5f-N&?;**?%I|DJP}WQ4__P5xJuOkGX-O|_h!*+vq2VF!U9OAAnT(TZ +Ztgo0TM+KFF8{2pCOL37=&>W%b@qN=f~LNDo&V&(dNKDA2)oZJ1DQ~8wN}D%)=i^a5U&H3C$Mm(A~4x +GsO-XJH{~M_Zs4D#M^gke +7AN4^)kg5fIGw&R5=)`x8H7!Irq&DGtQ#(p9>)aT&dGhO*Ip{q2_L|r76;%t^Y4gk?GD<`9D!I_fQAa +EA|cdP4r(a=_R~vhidfhu-NJBqJyJM<%oCzb>)YbKd5SOw2OZ0MF&Njg$|(L4)H%aq)X_5W0~mx6%)f +&z$uRDPboabUf7w_3U;&wDVX?F3s~w??5`JoH{&iD@pj{X0k%HY;xpwb*4t)Mn8S>Ur< +W}1QY-O00;m`Rt8f#!e@Vs0ssKZ2LJ#f0001RX>c!Jc4cm4Z*nhVZ)|UJVQpbAX>MtBX<=+>b7d}Yd5 +u)xixV*reqY%CVc;HaTk4{Bf^e{))`}-`FL&sJN=duZHkwUJlC}1~H_85*Y^`>oG?|%9zVDlvT)|xJk +D70H7CtJ;?2bwJz_b;3gq!CtZgY71_zrHL%>}Csg5YbE24`Rp;nV$m@$k5S4HuZF39t4_TVNJ{bK7s% +N;-oTeAS!G(WFV5Y_zH%XVP%14qV>eK;emS(Fj>0aG5gx5?3 +1;5zIXKfrx|yB@R^Ak7g{TwU5ESug+I4(E#g=oxZU}*pzIDcFue^Bt|uLYtxzi_ATHPp;z~uKM41_o@ +^=~IE^$qkH=Eeo)W7wT(@v7%h_7s@>cIOLik+DNc`l8v?tu&Z&`ASwt`{!3;=s{F%uZwdeIu +)U@%>qi&*3j$MG-1zv(WDk&Mis4YbBIs6c!Jc4cm4Z*nhVZ +)|UJVQpbAcWG`jGA?j=%~)S=+cpq?50LLbq}VKVijbsl!Sawe&g#Ln3(G}X423{Tr%0PD8YET6`}8}C +vP{X6+yuo?!Vse4@BZrUc+49zWa}+Yo?=P +N84pKOZ$zRb1^4vi^yCeRI9riTk}atKH$FY_7WcDH3vxrwFE5L?(R^OKT`9ce+w0R4f-70A8J8rG+cg +MHDnx0*ZhMj}Ipy04qakpOLS12+qR85wrTN^)B)gwX>`sBmU225q0M?RNw^H(CnM)XP&bULy1+aLruN +x|-ECWsQKn__ZSPBbxmX!>|q;$0mDS7K(8*r3B*NHDd$TkS10fe2Oco@z7@$_M4RT_4Vt95pECJH)9G9@`+ +nH#%E-9!8+fix68s{U(FRm(aHH(v5DoH^P!+d14H=3FrTk%>emEbB&YswfKtRX#qpf(6G+2-(I&dLM2 +l6MsG$F51laK%bLUzS3-6HhRh#<9$yZ=iQ&%SUc)1Mm-qf3AUzqrr&z_H%L +B{V{Remk>ANe}+-~7;DSy)SSWE9Jt?pF@mO1jZjh|!HBhoC)_qWsg$;dRY>?U5o0kXS1ihz%^h_##B0 +X{12m@C>WBj3vgV|6zT`_S_)E8`LD(#09!T%A +v*Ob&E!728!zh4xP4$fo2nS2~RdW(5yC(uHU*jvmE#yn`n#VKKq4}n-F$lf!l*?X|ag8+mwxu^Fw#tW +^|(UYa6)Y*o_a&lQRmRhBB+qB*^-ZZ=9quJJ~J7|-LaT4PId3e_2vTDbrrEd_nLk`Gzm9XuaDLo$JWC +nEQC*e&g(6BV#jRJKQ6^s3|eG5f4{q8t^_8vHdB2m23=~8RgDQE3$);ih^s~;SuW$TiL{NdlZkT(mS* +Ybb8KNO2@V!ho_3>)8&aYSScdvqcQ%OznE;WT?f^Fl^4CgUV~a`${mRB^ap$nQ7gf?#r1q|XJZb(S}E +-xih^d95c5?U&QRL!W-I_5Xn;0+D^9i0TmsK)2&7`Qo}H~-W5mn4AlBA +-whcxdU +hOP^h|v9D{x?q3U*GMs^M&UjZfxUr{uhGgZMD;vZBvjWVbzr)giUin=EWvySxt08+@FWQ^A$Nx6J&lS +4@e)B}>G9xmWTKLpF{MOu@2t`$eQrRfGR%UAw}CsIOoh`y-R@RZ?%BV#J@fvJx_R34ry-C>bgMQ&T)1 +d0j4#x99Rird<8d&aMhmfI4*T^}}FXlcjBuKSIqIrM(K&i35%@dkrIpCA&K8%}XFm{)dedmG$TH)7mm +jEB0)9P(?RA|f8kFJq&QS7i&WkEZ+a&{8`;dBW@}v?sXj$x?a!eHqhwR;vNglIqzqHyuopCGzy-KTt~ +p1QY-O00;m`Rt8g%r)VAg1polg82|tu0001RX>c!Jc4cm4Z*nhVZ)|UJVQpbAcWG{PWpZsUaCz+*+in +_1^xcyG;Ydg^BzOyUnnsm~WMk|&b)W)AiJ#D32D8aBgJx%gsiJ;+&s=tySy&8B-1edC2k)La_xn(TE} +JiTd_NWNj!|DseF|@VE)ueYlUDoGgY(e^oE~=*KT8QDaY$%NnvfC#H`m=>f7F9XoDlEuV8Yo9Jcws=# +sw^5dU|qrApdHhjt~+u>P-(1!o*Kg_!!eJOBk=S;4=wDi^J +cK@FmK27)|@rg{tf@XI}C8I6o}!|SWJw;M=bBWa)`^BDE`99?!b9DL|s80y!qV8h3zwk*0Od}bM>IY( +py<8e%5F&@`bl1y@21l&^vEqY6=)ULeYkD#TQVx4=|iE7TfXQ(!zI90UV0%MxG6I*>8weS(-uP +eg2yHeo>^V4SlBh$+dVtk`)rzY)W8U#xh9vLsUQY2*+=#DFpZVZwp{#S=fA0z=lkX|qGAf=g5JWQI1( +eU2I?ODtH-WLwn~*jK_AJQjjb2$pb(-uvm?@l4HDW1T`(0xBGl)GNC)Q3r;zVSeT5*I +IR;=JTv|k~lZUqIt*^(Nz7&nQU@YPQ;vYx&scho67lV$Ioo0O?*gg0;CWn&Xso-jF|dfp>E` +D1=*@}JJV}h+j3RT)HQwnnx^BRZwIb0%U6yr)-q~JS8zU!uWwes7N}(nw)Xt@!$ceDhRsn2vW6eOOkR +-HDjxS125>szBtPaPb2O^{y%tCvmG +s}|v00@^$A2*`a*sZ9TBPA4uWEn37pM{cS9-|SrcJYLu!LvdKCXbFu$HV-*;u%)p7%(*3w+A@#=i7>uUuxwrQW$O1S +x~%3<@#Cnqj4VWESodwIjggc_?Mvl3Y~omRREHdaL;X-eKZ-eiuwoLS1Ww<)7@p*t81FX~unt;SA@>> +~E$$BQlO-@1m@FctAG7)MH#?PH}Te>+Ro%T%w^LE_UdX}RlNB891(K_=#z=;j$&^t~fEYL<*>S`Kh3i +?W5B=(@wGqB%;{o}_=D0Y41Ya&n(6AJt@i;VN%}JF2xMjyT6NVRy%c8vT@zh(s>xj#C2LAG8}AhCvcj +@}#3c3iuI&V2w}p7$~9s_j>)4wlSyM9zWhw|8^s*=;M#}nY|R+r^S7rLfT{hx4kE7d+yyz#G<&Dt9QH +0tle`JvPL^R_zO@=0|XQR000O8NLB_@Fy##4WdQ&HTmk?99{>OVaA|NaUv_0~WN&gWWNCABY-wUIUte +i%X>?y-E^v8;kwH&`KoEuRB>aa-dMFsXsr8~HUaU1XX{wRxm8>w(RoErNRPDca7Qyu9W&^u#znysxCN +MjjLGB7;s{{^Q&aS)?1{0aEbnTH$wP%QyF)Zd6i8_{He1X@~JCi8Y+C%cLe8?@XPOA%qSUaq$3O7S;?wgoI7x4Y-{?aS`*IrXCI4VtEP#8A7c +68m8g23hur)JdN#gBzI6)umcY{Xt&qu7f+%ac$mK-#ND4P4u^llP~}otze#KIu1%nt{Gdv&~baH>j&{ +wBZUF>HDKKzt%w{YFoBkI)#wDQlc=Ab^TmE`oLrBF%4*n;KKv-KgnchZ9oFSJ5ay1GI2l}?a@ZLDpUM +d}@6aWAK2mnY{22 +;+Gz0Y+k001DS0012T003}la4%nWWo~3|axY|Qb98KJVlQKFZE#_9E^vA6eQS5yMzY}doRk01ml|J!H +U(SuOdi@{b{turXveX9Ejh_vMZ-V|Z_e4jv43xW$=0L0(cNf}v@^43?md?$#}SF{>guZM +dUbVUPwd~{7jcAu1+NOkW*HxM>)1o0@XK5;4oE#m$JUb +4Xca4aOL>!&IJb!)i^_%n4*Jt=+NC+;faw+EXMccGhI-iSdxhgB@a8;Li+oW^;{^=*5ej=Zgwf?h8^` +G^+#x_m0KK%3(0Uz~yoqzgC`YujajX0s7j;pGy4u#kgt17x)Mu(y(VJ2_WiaSCNF%vJ#BBenW2s(tPH +OzdT6^ruX(WUsKc(MTm&FKtCQ(T{%DooUOISPH7H|tee!*Ewoo$^mpe%_?-8hQ#3+ad$zrUq>8CILUs +^7I^AMzvVL-_*?_!hC+!W_gnpKwoGiztmQnqq10J*RP_gM%LEITll`B@5adC{3?oXsfVKQP1|I-(WI? +%>ZTf~?^FD%ExNydF0+FEs8@N`$j9`z4K2^E6s!QgRw)oFeG4#tY0HMDJ*U3_LZSYs^2e*FxhbM$Dk< +vws3wo0{S-;ifB>JD<=KrAl&K(ukfalo2ke!k3m!VCCx;zy5S6S3vNll>!=tIlbn|-<+Sn;y;IpLXEzT>MVZRHaFkE( +#=!Z^;J~YcV(4)d%YEa)$ethWK|kB>c` +-CzH$OK`$Db^J(~K(8Ect?1ZB+{WJIp7D!K!;v1%J&yZ+k%o;7LJlx42ha|VmJ;YG- +Q?a+7;<#pQipsz}B<}IMPs>*Av)|3#FXyeo1rmP!NPDi6r_i&8=UL3BF`N`#psT3iMzw{%a84pZp2``Q2ErBm69*Ccc5jx? +@PcFc~b2==VdlDCt{xU6t)BIGBpZ{bh?tPX)rk@ls&JHan4(J6Q<$>M;%Qs~6Ncv^hEbzKT{WElD23; +I#yO&A&m@!x{=LZ69n=_gbu^`rf1q=ws^io}sYSvx`{8n$Sdz6{2NDOnteF(NG`Dcx-R}o3*L|=VO(t +{CuE}b?MkwwPxcK&5PMonx4)G`Rr^@Wrv`ip%KciPlG#{K`YBJT6*39q~d>Wnp_4^5sah=N +k;|a&~)?ibkQtJR6A9=j53O-X50w^jH&$V;PBFfsR;&66#y`IZ2{2lk8R7b82Dt`R6 +K+UP3J>YFri;jGF$i`ODrmN3xS*$b5IoI*>AK5MW;NeSF8Yo2JQFWZ&qdTg%N3EFy{8D2Mp{EFW +JQ8HZ;_$CEX3VS7T=fEuda} +TF84O(&w8W!4`y0SWN>q%rx;79U9R9S9qg5IxI!`PH)NT;5Cr(k!?lSaCQ9!g16q@p}QkMR%+1}JD}VMa+pv}6l_dQ}No4bpjCfRYuK@-f`Al2t3NH*G);cR><+*@!?pJ +8%N5pN>(RjeiWsA^PnB!kX|;|2{^W6`uV0<>~9=qo-%b{U``p8xa*UaALa#wx1H9LhYOb#~1i6t+K@$ +wL&BAW6S#@fLSLgCQuTj1VBUIiwB3FuGSp@Fd%1Te494mb=rU$t`I^nnP{#f1MaJ#?}M4bHTL+6gD)s +)sTQ0u#WqJ-0oE+a9QAE71&10us#nNQSVOgZj9MJr7x1!$47heM2oevH|u!Bedr=@dKgaH% +uZ!v`QL7q#0R&tzeFhU!TuUU!9+vzI^%uA<+-7kN@q>$?M~1BSRvk59)ezHE5g}O$p^97=!-&88qtCy +m<+&LBa!5iy-%I)AdxmMcUw+gZ^%AVu4M_8i7yX+9TP*afu0>U+^uy5M%q#)ZEk}CvcM~c?#S~P}7qU20_kHq(t)a3`rwEJ5_5J5>mvXpxprJ2hWx7WRaX{?U}iU=DH)@-NJi4kItc=6yXcIK&AXonyYcFHglaW}IOC#i|P}a#A(RHg7UOj8}V27DdK)%B~N9rBuTl{HJC<57@K6fB*I_ +s;=wa)(boh=kp|sp>5c%5+HjZn|dk7DNjW@8Q6RfjrOpe5xz29xw-m8B_MDP1U-nC;7=^Uvze83_|X$hK(8ZX)2K3W#W5{hEfL!jM`#w=2aP0EaC&N-h#WH+_Yk^Zi7+Wkw6ntNr +%94=kfW?EgZ&BN{ltXCK{%;fR$u@lw9Vps@bSyL+JvRrVDb_3Z!|Jch0$T?p|eu>*_@sQi-KjvT_{pa +0)R!7N#A94Q$zC!36&nA1K>AG*chjy;AAzRj9{Ed=mf++9al==A}O5AGEE6eicLjcmbVzZw=4Mw2w6o +ahj!8-!q8}e0>B;tEd+yPAvi!8CUoMwO;SlNY+I!{tkt)v6g?Aa+yYx6ls33SA!7Kjj;m~C$Y-|Dvxd +eT<}4GJ(3#5-z5ac^fMg4_@v=)5Ef~Owj{eOwdNlJdqAC6i;(~4Z4kiBT7np8;(HMbfz&TJ!kvd&X97 +M6o0%GO{)elv3wVm#{~0DUbjK0H9y#nU6w0%!(QV#z)hxi-kI4g ++x34;ha`)irkD4sZy@*L1b_QjD7rE0D!=V(P-i$o;^O++psP+m6Ms+DEVpn4t!t?NQpNvc3NOzi;LJE +FO9n_&qah$F8IMXpZbP&2(v0~vxW^fk2hM8MFT~Vj(V_Qbwg|PuNb8%hZ+s;+>q|0P190kWk3~(n@BF +deyl<5JZyzo;_6X3P@reR$uyXy`f)-I2a;T}9scyj9$C&IBLKX#AA9^ew}*H@L-dC-K=~6>g-n5}l~9 +Xy*#hOzFJ+QoWM7LsyG^ywA(>KO6g2JU>!OBbp>7w8?43+a0GHwx6YxOxx)eWStV8yrB;^7IXFqc(?2 +yC=rPCB+=^Dh=37LnNR{?Xwr!%c1@fS>s2fV->-r|x~eJ~J&Aw4|6pTj6g0@>7Kln^bTFp(tVW#Y!g8 +b@@oI|M000iaXWf57DRG`igH8c^x#BNb?2g`|#N$I7~Y&sNU~`C1m>p@f0c7hQAm{c-p?=cABh^!+pJ +NM7gPvK3F9-B|?++>5V)^75;!db^40sA;Oecmi?Se2y97YCa!NHu$^>@XUt0OEp9v#p8F4gQfgM$sk7 +|-S2_j%p|%eRKUu+ox%icsDHFf07I38A+Q7P6h88Fd}l^35L{4|p%CSL;{nF{$USV%9Go+|>1zS>eva_~+wj%_K>T0#|4-)gb!WlbQ=$*?_8V>KX6dsAv35&+BQ!Mdj%ZsTHnV`0~p`^jzu +^)R*G6_O#kQwn>jPOUSru)DD`~Oh<1K}>lKMJ6g5%01n7! +4HpJdcWNRKdZ~>9garI}e4CsH3Q +Ax#(?CiMFc0;fQCF*|O`5c>at|T%*6y4OjUCFIlfUmHIkYzxVs8&w4+6zZlaBhL6io^}_OuH%Wl5@s$ +0ctCcB0ru3@&F4;$w|CdkHo!U#}q$^Yey;Yf@lk)^MaV7ypBJJqPtRJ7pm^UolxTVPfbxbdtctBMfNx +C6DMf{!_F1{A7Orw1hL)_`1O4evn5FY7b!r7hjK;;`?DEI@esHn><6U_9U^2 +3VV-Ct-j!{{Z(@cZ1%C2z=jwf=m%)q+uYv!}FkiIHt|RkjW9k7pG`wFi&80u?i&g` +v^>NGvW64<29Y#DOZ_7|k*LvOlsDVAOLaMx??<$ana~bUXM7bn}a%?AU_|GYm##8ukxx+^jD5zxd)0U +v}TrLsFqXTEHC?G@yMO(!K)yhZ>OUBf{8r0(?nM_pmA&EX*6!d=__R= +C~Z{Vlx=RLLgjWX_aP4>Qod_lwpE<+mV^)|DA>QWS%nK1tQ~mnEjqTiKf-7mRn!@8eAFn0chMRHQT(p +N5s;>Bt*9w0YX{*(h9KxtX}EyVGJ2OS+a+b>?y^`q8OFwpo+M7DgBL`#cF`wtZzs>{hU=%+0>OjdgGE +Sm%8S$?20}Mf8xtik6Bj`yZEX*`P`(RSgkd%nMvw;!uizOT_bWuFfD}Mh5jeQqX~$Z!Lafr&{-ejAJw +d`R001TR5f0Bm^{Ui>jX`|OWF%Qlrwf5l;%p5L-8-(6A?o7(AQj6nbZa6KGNwujlb_%|Wn8#cr^s_s> +=tYQ_7cyQk(g!n>;W8Ywi;NnKeJzF*Hmt(HY{ywV-~B!Ei;HjDJAB;-1S0=ODCzDW(U=~pgOamKFYq3 +#%B2gOM`Lid>%Sx)$4l?g0u7dK$SU>1fD$aD%S~a=M&m})I?{tGbkSszVou0*G;(!TpiV)DSqS%QZse +42XcmI0Pv*2<=T%<4JcRRv70eiWJNM(kqTVRHjLGtcqUa*Ex^iRY;c^(GS`&WEX!v{DNtQ`#Q1s6a_s +2}TdpF`lo~`4S+%f;o}L|@oRBAv1+KD;g@*P)!y0YH3x$HG-UShz%k;pEzA0C#e}d7V_8UF|$t3W}6n +>U9!W^k}35YMVn##<5H1X6$WQ$HV<^HK>zsnwO{Qg%;2EYH8>eq3|MT7x7nrcv<{4Q$>Lr-9j-gc1Mc +A0fPQ;5O?b|5h5=+AP?&cl*vFC(ILx5j#YmIQ}2D!WK4d4|Yf@&p3~+0Wb;)M$vSatrAyy@}ptV10q9 +%Pg*~gc4>FLFYGap|PZg$<(b39ks=6QQmb6i7WE^R!Yypzmc;6q86A#@&4D55*i(WZ1(sEXo;WQ#41~ +Kiq>CUNZjKz+P`SMI#@CH+t?$A+>u2cQOBB?tE#-p)1|zzVC?U>fk_)+8jND#F0TgmZiX1q!%B_!00t ++s?{%U(+3Oy2STb`AoD~gF6Vshn5n3-ZZP?xOs&$JlmaG3J(k2djX#+g-rlNRGfO#_oW?JAnb36F1zc_r1_g@50rsDTXAlrgF0J*w@k`PI&-{s!l0Crug +-Xx67wa!}KL|O4|wEC#LM-SS!ybTWUR>nfLjU)BNx>VyLiYkp;pvhY_?khTBJ-2_7>B%92%N6MMLIss +mc6T-o!?3q)F&;ab20beZJ^3WhZAw{cz(@zuT?UK+lWvT<3E1#3SoI9qQymmwSCq(}tDyo+MC)(<`lV +GhuiB!JSz<03=(nSCNItyo-DXrDD!&$4!i}bsHKNTc*ZEf;S+Y%sW;#^1Z_8`y4cH9>7!@GEAOjfq{j +LsB%{tj4B~Q=a3)B-<5^jWPAP2EDo+R3Upbn$B+|uXR9Yt7=0w}VS(OFw$R~<|QRfAroafFXcbXm#<{ +M~2;9zxxwbswH&G(@J$GHG+X1Zakwr1Arc4pe}k8o3dN4Js+)EpVvCW+0rC-3W!7Yz7k*V2b$c>K15L +%kdPvOrN*N43h9&4ryD`Tb}k-zfz-ttpUxI1}k4Y)eQw*dr^0V+E@uuivY9<*DNg=LOlUh1XHxiF|ia +5Of}xtBQUbVViK;apxw>4S$bzBOE5V|w@l@<39Ud=k_b$F#fc@(e}&jQYB(1LZs@AL%Cne~OR<^v(KW +h&F^`=HKFv%=z+jB}KAwx@1PDRxDw}zVH72JmL^%ekWj!%L^5HE{Zh^YZ*U$iK$|Kf9GMQwA+$RNJtQ +DaPa93qbS}S7JoGVPyJY_3Jb#&zl(;2!#o1BLZoW#Mxdfd-$xE1tB4v+OYfkqjf&x8qR(YRWpE+@Z4( +$TITmt=7A3Y-zMcnkjfTTtN1RLhBwhjKHLVJxeZnc>it{GM&3N)kkeG8T0c(43I{7w><)?Aq;#D8Gx= +fB^_m8r4*fh` +ziAY^OmX=XA9&`D%bV`x_o;Zlz8#UPhB=}|s4_dO$sv|0r!A7rcS6m;_UXk9S1Dg>ht{0d(GdjCO#xh +z>+^L#J@AQO&4wI;J6Yw_*LKT2MFCn7bTn&{|u~0+)Fk18bnwOJ_oMx4_I5nx-Zld}|orA#|?h>YG)M +<1>XIm?=u*KlueSorFqTVRrvRbHE2TEdCSS>O200&nf-;6E7^h5ZQ>nKlL=;Y>Xgg$=%2&9d~Xg=5G{ +nz0A!LJjYA?S-R6T){&%^;zOV3Xe56dKRyYC^p6p&oL<@M1z0ngyLgC$syxX;?IbvDSB`1DVrGAh +c>m&o9goG2g{GB`_91C?6=hd2AF8srt72r0HZ?arQ4~H(_)n|{Zt)KlI71!mM&%0qs`KqmN+&IG~h(Q +Eml{zqv_A;X4#1wOHHIZR}T@DT2|7uRzmO89;Osi&XPjeOFS&4efBsWacPOS82NrI;su-hy)FpU>;-M +5eO9I&@sR0;(v@0pO1nhO-%rfo*oZ%LtJe;jX0e1j!){DLw3#ThnA;AMvIrY!KGy*YvC_DBWX!8!R?= +j(J?zD>>T<%lf85`-eF493BF60a@%(92Q_mhSZcC(PyT#%(xju_`S6`v@OEw3j7vt=>SiaO|UzDMo%> +D`|Bfn%uW2W0Eg}fQN*uPp*aRD$DNG%zq2X0t4ND4rz&@hSw$zf;xl;N5vOsuTDwVzY~g_0ut(!GoRPM~pXOU&`cC1{Nh91>h#hVZZ02+wO274>vuY1yyW +O(HrxgU@Y4VK1mR%FYdwb&j`}gV&IowSPL1w|_Q{`mv`^VyUtgi+V5)~hvLqWy~F9c~Rz!6?&_xw_37*6LXim(>arJ$SGS +JPN8Qh^Up!7=(kO-P~|d5ED$NPAQkIzwMC5CSXasT(0Lkf*eF?9$g_X6#!6L^>W0x%?XPdvrV(wh`>H3_?Vly)Ff1zs+Pa=lB|oGv +o=QJd+3sfsVBR&d5?AZXQt*~SPAQa*(yDlQUc^`3fxa_LpKFj%_A#f0Huhm7 +A-R)PPEXyVQl(?5Xs{H{Nkq&fPD~sx(s;jK25XY@qK6xb!c}br8YzTL!a(k>#K}QYW40O<8KNZ(l!kv +u8y$@9x +rV^mj@wjvHOtNZA#Lk8q6pQVYKgwaG98ogKR3+6Y?=_w7un@(?T7-NgXb(X2i+9vaDJqgKhb*R^ +0{j~j~=PI;zXU0XI0_?#x=`Yx9MqaBQGtt74$72I3&#fA@96m)YU(?ek)ybnLYE#FH6FpeBmdLY9%z_ +OZ%T+99(uyI)K1*6**tzwClj4(vNaY_Vpw4og!{21M-QI0o~9q105VC02q>k5UkzI5tdux<_O9Fo(u) +HQ34$$fm5uVpo7)H;ttJqDHmDq>QAc9ttddG+zqi=Z5(OCB`c;Ay8nSnq!YF8Ll@{Jsk;GUQo_hd*o_ +eW95Y2z{fpes(5jzGqYX;@U+68SuAv?4@x*Qr(^@kCCWaR$;5T*x5zUd;#Gor;D{jbDCZkMRH +-L6^DLS*UXDc#FCKf=gB*jNA6)ilTA}f;(`4gpjYe|D>X+X*Yj*|zBWC@mH|q`IJxo`I=VJdWG9q|Wr +pzy)$4-YX$%%)iJ7qlAJXZ1gVNnzmiYtlE5xZR}DM=JQ?6dw(B{w4XusP1(IE +sqh)r`FVUjAi7SnA%}un(8`l(m-kDg0s}i37&_Z$lU~73?8NCCRe?n>PM)7C2W`RT27N|A2NVC#t*3N +-{Z6hM7wP$}o|D3|O)X_4-IT{R-b=(igOUE@};Ngik!MaI&3Nu1% +Ztz;Mw1IR~v1H4QW4mNJ~qFZxp%0!YC7e){06Fyvtgux1phw_A>`lyTQZ5U3A;m^5#^fSbKV&l=a~7{ +L~t~r!h8T!p${4lU)$1Hd0?VAhdWFgUcQvaW;)FSuiyI1`g5mH~9B#w3%LB+H7SQ +w;06D;vYBtcs$XxNL+iQ^oEFDYb9GAW2@(;Q}Ko#0CF<12zuh7+MyQ5WXYcQOv}jv9?_LZluA>l!>4r +Vn7Qexr+9pb@xv`DxMQMv58MqUYt+JL_2L5Tz{z%-pq`R#JeKFZ^qfk$AUoIu75L@g>DY;rLS3_q2f^ +?RAzo%!o4d@19g-5{a6W?M*Hojo2($#-=Dg1^N<8`}S@ty@AS|3r3p>w>A> +kena^*Jq;%w}GmsJ?`o>kZhqC{R-l%{N@dO-ch>4*2Dr1)6a?-$r?ETrMVu#mcj~SL^N@Sgo?(GQR)% +zTRcyyH3ovR``)pIZq32rZ@$t9`vw^J2)bce!`rOu&ux-Yk0l?5HU;Z63QQkE67dLtV!GS6G1P}3?kFOxJY8st +gwT@;?-u6TFsWu3m}T)&NSg$Euxb@tBi>U36SWUP?l+vB7`PkNw$u}&NLWY=;@SGaRhPHZ1R +_re0ZRnVc4kJ*xmAhIp6js##qAo+z7|#sNY8V38d%kXQ!zJ>Obn7hchae!l`q*PZIK1@(EjR4x#KyUx +qG~2K6t(*TXa2uhVw0_TU0?L9x<|?T0JimOeE{UE;*ge5xqI|JQUu@$O^8iq5I!K6|J)PHpd!khl}Ky&&0(==VD*lUb$<$ +686(Kz$N}e`+F3OUK9i&&vpVPGSYPUNvtcQ7cr+COySpIJy0>3r4RzeTJ{8lJL<;#`aqZiyz8AOd&U +N^myk21w>ch}qA`Xq*ef}7S7&QAVB-`O^n;D)>2b9vL~WH!S;nIOEkWN0`H0oXy7Klo|Qn{7S%_E}J* +n^~Z4E0(%m55HrMj%rfy=n`Gmlo=IKpL_Chcsgt_J2b+CnQEYxdD`q#F*Cchva*Kvg|RJW1!3uP@yW< +LGgW#tkidf{8!|=;Co_?U6&PEv;pTpPx&o+SjE1vB3Lzu4_W@L*UVgN6Emnt>Q=6BU*VkB<%eR~AJLj +T0J)&P)N>!19%o0@~_8}*`Po5pWJU@AUa{StT`}ak(|F`)i{`+YE%lYN~-^mx(Iq#w~Mh>iG)e-=9(B +%v5%u-x+WsCO&nDH95@k@H62VZZaF4xfSSUeQ>D)ePc$dbH#se?k6%ao4v9RT>sD-{6ZijpTn@e5V?q +Pj`&6^(L8eHm)4Z!eVv4QqPID_yuRuNa^k#jwzj&u8k~CWmSg4!%G`-x10k@tObyqvorV>73}4a>-nk +)wC?QURi03sa^wErE2&NqVbZYd@ZBOJ@t^vv?S@&RZ4W +#ek9+7&Jx<43>7zJo~YgbC~m;W8Z_uS;AOBiAGh!D)V4yuJ4rIrTaGvyjh>p9KA2vBJYGy!fVt{>hvS +9TK8(MIyW)+IH#BD&N(_6i8zZ8~OW8D2y31>mY(dx6;!PJwaM}>Y4J<4~>={tqjVsF#nJt+VUuZ3X(e +<1Bd_p%b+KiYQjCp~TKidu|AEIJEm83I`!8Aek0=WpG +I*{W3~$T&WZ)zCzZ2K~A>7!36T5L?m;+wwyBZ$R4bTd1((tt7YUy^>@`E)y6;A(`X8%vKn(8b&ftoIr7- +MO0&^j%=AJEhyfODu2XOB?Y-UCqtln);ajX9KJLxyDG-Gi{|5pvij#!T4#Wl5o|NGkfYG<;(PsK93z5&x5ff&ljF6!(Yzrl`PIik<18om$xL +>sNRACSeI2MgwNe&T(fly%gH<<0-9Bu=c%+*nd}8SG&x3%#;zEMU2m`P)C7qIxc_6w0Tm{w+s&oryxMf6nA9XEXkvI*O%EH{PdA?8)QzU1xf1+#8TI +jgNWSri{xx>@5a!_ew2Y|Ir*n>wPpj0Am|Eb>rXrS*oRFS}2)d#a?}HW +x3(-dN1EPdQ(u^(nx3Ct46?UpDQ#_5G0Yu|6MHcpnWj$iCJR1UeJ`U$pf)`bm?iow*t-Bh-6BT`_Fk7 +3ZD(EP)$0+P!Chiq~L}nkF!X&;*Otf_c1H%ERSG2emmeuO%l8&z`}D&hprwWv>XkI5(Y(E#>5ZP2O4h +~ZVPqPXS0SkKlox*kQc%e5297{`jMi=BkA(`j)3YaaU&-my3*4>onp&mXhN)(b*FK&hgwOl-Q)<(p?fm}9ar&c>)ruAXVTZBofmxJExmxpbvxA?<<`dDio11#zM#2%qq`*KHaAeNb7PZT^ +2Tm_YN+m2#p8}^?K2~~x!xw!a*qE$(B&W=@%?dNIrV$@QYypq;2g}~ +V#(Dfo5{Aioz1nAO)3?a2a$0eN+OR{+IvT6?YNtKTI@9}^Z?3^=+sv9KJwhqJaR&p6g>rR_ +O1a)H%V8(wBVdi3|;shPm@{)yoh2Dwsqh2jTvDKvoYu#2fcFxa!u*?t;RmlixoeWHy=AtP{G&ESmD^q;yrPM+L_#Uu0-fc5S(MF{3 +>I)8x&k%^`CTaZ`ENgjCEB>hh0K(J8@q%qjQvTFz}jWyyO&TGIUCq-tVZ3>8a_4YI^+CQqFknsR_a_I +R0FFRL@CEZ864gnk>(nb$98$d_j|{gJ6S;1rKCz`|{#dOxx6aGpH2p#qj?CP)h>@6aWAK2mnY{22+TW +cse;S006Gc0018V003}la4%nWWo~3|axY|Qb98KJVlQN2bYWs)b7d}YdF_2`bK}O9=yzB7KOm!;0;o_ +Nzmw9v%8fE3d#83@J2Q^GB~MWh*_2>`01beWnC$-dJ&%5)8zkjfZ&G(rPKE-yPj{a_eO`U~^g(iTc$C +cQc~M;*C+lu;^hbR2=_d#9U-GnGZJOfhx=XUBqvVUvKL6qf{_hXT`)iTB*>u-+mAtDL-Cf>@?m7ow)DH;!?l5&f9DI +@3OwS0*vUtYg2nwx5fP`@2>Tmc4NN&tyu9d-SLU)@3W%CHqY*7VuiySCyi+9bu$yKDmtr|09jRd*EP_ +G=dab*ThX*oPkp`;-L#!u1HQ(|_f@-I;af5PE0;QJnz~Unm!ivIZgN#MdnsRxlb2`jPoJH>Kb?O2{Ke +U;)0b!CcQM`|kAVzxQj5RQv0$D0SPbylKU>5x=a9h9-NQ%yJmVe7b7tm1w%nIN +>r?z7$g+oT(gnxCf2M=TmWyz}ss$CP8v6MIer!fKH=lkoo#tf;Up)Ohw9qj-T*GVNh+McOuwLa*_3fIr6HXC_~!N7 +_wO{ip+>se%mM0=9RK9!7MlI^lc%p=zC3;PELaq1ZV5QnWk3Do+4FbrspLB-mZi^My?cN9;>Fq9G)OJ +!+q0*y-#$y}*S~##_U`@Jv$Qu;@a^5}@83Q>dj|#mjziUV|8n{k>Wx(U&tHA}dV2Qbd+g|h`dI)rQ%2 +CN%-c4ZK1Hg@>dT+StQ#G3i^MYU$7#|MH(}aJmicN0s7w%jEioelHNn-~CS9G(tr=-OfOD|`CR9{KH= +Sm!C>N?prxZTVp-hpNMJu48q(BO(W@3U|6ZmA&CRN=mfoL{KU5R8-H;I5gCO$b#BH*#@362AB^Y{Ps- +s4b0@2^A!OcK}weEb%er9eEYVa-aBH^XCkibay{THK1JF&I%6K-@A=zrwX +;1KfpoCYwaIgAeDbUsgVXPUA1dRSJkkAVf{ODcLy^3JaWEye~}ET}Y3MpXktugvI<-KC-5pX(|gV1W2 +XpdeumP!B`S*JYPcof`oh^ZdhT0yHkFIlUa&E5)a**qV7ORv?mqBf|>82~O&TyCj&Hk=+65LKzxh)@@ +eBjOZC8A&p=(*|vDbRQKc+tbj2p$Q^Z`Drg>wDhLBW4eJ)hkt5z<-AlwZfaAI;L_0~IFNhA>m6#QaLd;{bR3Q+#1 +c03Po3?Ouj9}k4YiPN9dp2LFP(2OJGmE1G?&vkj~P +H4vO+kLo3xQo{{1vA~z98b0%{Avodr&u8)S>x6Mq{)h5&O_u31|XnDxL>d-u1f7>Y1#*kOuR{;^Ymj>+#g>A5%_2bO8zNN!{q0>sCaJ2{dNM=tsXYHlz({qKaknOUI|arn@?|y}p@PDH?0{fEh;o2rh(%C`P +q;GJH6)!+QdX6G7N8zB(I<0pxxUI$a(yL_TQF^TaYbt3dYho7GFXaa5Qu_1T3J(4;bjdo`zPwhK4c9X +`h=uezm6z->l)Rbz_QL#lx=K|@3p4=zs0Pd*YXC_Sw}p&|e(I2}DJ!GhCAW2iTMP+aHFM9RP$uI +srWVC);NSLkGe5+j?H?U-C)U=FZrHA>HDZMLSdQkibHQ2kQ#h74nrt~-Ikwi +wG{hL9xVJu@|rve_?L3DSL@{^upoQJkM*#r&M|74X6Wb^LjPAa&zrgHF`^bOH*!d{w-6#bdJrSc8kj| +mYFl*#I`;|&xn7EnI&aA?HuVe`Ie>f2&YZ +uErq-e5c|tkCDwMueV3fi+~}RmT@W#tw_S=@$)bs$6KdS8H79$O&Mcfu5G`>Xlg5y@o2K_K#16-TZ}BJ7{$N +Pbe&T{kGI!G<8#REJ$hvq>uM%Tl^eARsQjX6U~pjm13O|Jk6g%Aoh<7{uv~M57L6BKvAACW7ZG#iSiY +v6m{tL~xIo7Th(3754U#9hzYd0Je>_O%eVhY&RLc=O%CVl1VUhabG`aNN?1V6VDJ=XA&XvLpsRoPXwdE(YwqK&!Om_Gnp`540}j6j!qs=1Pi{*y_}>jCPIUy +RXFCK^oLhob(D;7J(E;H;D9Ry5D3Mq{3^-cUWe*rTZ4TD6F=k;|-TfL0dN5K-mf2F6D8yzm_k6+A$Lj +YcSwY+A%96rP*N)9qn8yorCtCHB8l}TC_1mSF1I-(e>m;mX!N|&tgVMx76oh?MlXfKsP|aLTi9jao&r +?KM5*y?akSnqc6Yu8d=JY+Uu|?=FkeXGP@=pv&)cRZRlVdMp?j<+%=oPaxft6t)Gc}ou!Z4BzxSB(&T +ZHxfYmbpGs}^uQ;(3%oAFBIR+W{fkjzbR6q}fT*|dIgO8gr{0|!6)p67Bqh-|j47aaG`rMXwIj&*FY(oXK;CX>me&r)j&_QFEauaK&mX +-G7d|+2yZ4#XuydPx?t45T0SKRWKsI+>^(y+5a339#RMQ)R +r-%tAdf(tM6_64MZ6PaV?BB+?|VWBV4NL~aMdJ1SFf#O=iD3PWsX6rI;tGz(24a-MqcX<^Uuq50VYsafIOHBj1CjkY-0lGoZ-C_6w8PHWb(jcYd2MWN8xU +lf@wHskVgnIu|Q_%%Vju73*5^jUvvf^6x2QH_YqY=(K>;BvIHQ!u1i7wT}RDSn8uZUaiQZ*!Q|C@e8@ +Y2G8vYPYlB`^(#?2RH*;&sm;ujj@+;w~9+w+b>l>%5IIpPoyE+O+e;JI7Ce{!G@o%c5`-YMB*o^|j%t +{A=S*|U$*?9r|XlFpM?Dj4!mS9uzV0?|WhpK*hC8Ra4zD`_6Clw(>+lpwEMn8T(V4{X~b_7EaRa>CoN +GLMCDcaihAQAwjs9P=e3=k?<)-&??`N}?hoEKN3?RzHZo4D_qyp`Yh{Q-u5794AsEJThc6Iw&a9ss$8 +Z9_P)!(~8YX>NcB(PC{_BXm0lYp|^I`E=csZ9kaC8bB*!fBEB|Rh*U~LMfF2(6EmeNl7XpDDZF!R4uOm ++E@w6de)UzL`!swu$OKbn#;+~PFs;)mN*#q)Q#2cGyf5eep +v*Vda{Nw)%iDliHP8$d>~fF_^GR2Ol!FEC^z|27+R!z!T$gjJKY?}DL5WFk90X;YFgrW3SljMl94dlD +v9!?;xi;%cb)ZkOP84getEpW#9cl#8_uy8X%7e|Y9`(?6+}eW;fp*h9xE-hRU^8=Z``}jWx^DC`Ti&f +*-mIQ@$V|S$W$ebVOHfI+SeKy6mFM +$8YIq5QlChz3#U)4Voxwhp9N$y7PNXgIgF9C9SD?AEGr7A~*s%I)d_6X<5z7`g4Md%LGMHXkRr)?n%b +)3bGBI#fw`_rse#^*-#GXI!#~*)`68&gd&js716$Pt`d>;2FOZEyHL4_fK>8xIt9KJ;Llze{N0NU5BI +{HO6%$Be<7m4^tjTSykKo=;0H~02aqG*zS5>r~K$I?lKsxacg*LdwJdW*%fj_}b=LJpyp;?x&_5AzTj +*V^x*&c1MvaM&-d#}M+)^@|65f_o7bjU1LxkE!+dou;saR?W6Ueagd>px9f3*59bZQaSJ9cnoYDfupw +FjCSkq4z3rA;vN;D{nL=HHI^XVkjN=$AIg#?gF4RLh0x +sH(MStcsasO1Q8gtveZEew!)Nf=D&*+NN%Ee&6u^{VcHpC9$hnVd%iI1lju_oB6cy5=8v~|T4eHirl0 +y2kC0DG$?;=|efzXqTQLP+q^vb7Np6@Wj2+)fzYUUrE>5Ly`?!<=f8sMsyg^x0? +E1TOLDQla}AC1!Z!Aym)#SV{jRqQ#BAw2zVUe9HYFxqAVnbqp&8x?*IF4!gey#NJp#Q=9$lq7hN6p8E +ik#a+-erICP@6{h_o#6w*$=TK=wx6k>}Z#o#$4iX+zr|8;{G5QlthuW9wImO}!Nh$7Meg+iyTT*)6ju +WPAUJJuy{T=$xuO*+U7wpaF>a2k}`%hFVFR$tbgcrs_FRU)Ajib=!l=1Zvsj+XK`sW-MCL@Q-UT!+u% +Oc_MkQfr{#u((nOw!52f*OF-xX@K3bEU+UXe$nElAPV=2#n%xIViyjRk^rG+w1)E^x`58jmv!T#aCEL +{`KV_`7iiC{Py)X>f3VuwG8>*0`Ke3zrcWKj-OMU4(Qa{vg38gp9Yqki8qjIcS}I2S{+e<>gcx$-!Yw +=V>r&TsMs#N6dKLrG%Gqh`#*TLokO8`kCW +l5m?_YrLA!*va#vIsNV266yhUB&rU4OTdXo0cd(X7@l_`VMz^0{^d&DXHKI~juu9FGSq-T-Qh&>fOpq +gn&%I&9Cp8PV3fqmSbXtTlZzTW8I4;isdxqD!5D(G^YOKE~z5BWk&FTeRpRG3gH;}WAugVepsE@2t~k +f3jA-p-056rTRLecY0{!QCCph8(3RtPn-TK~Fp4ED;t(9U`Ycj0jgFF||XSy)Fz)@2}QiFFa@W0xu*j +?bR9JIftytf6tArg +VEvzP7*SOY)w7FEW%mIzgJ8Q^UIznn4yOZkqy5y6)AG}Zc4EO$rxwjqU=BO1D9+#!E_`1q(^Pz{krqx?nAWabu)Ihx!L_#^ +TUoa`&#yX8n$KQIj{!Tlh=1^p{TS?4y;}x29sCJSMcr7k;aZgGUZkYe5*On44;hR5u{zcnwH{Ll$eSz +b$Np-FYW;;ZMO0qEHr!dN&ql`)Lf=CQ;Sd#6;BX$HJ@ijKvJqj|9H8_PT5C~9_JFIOPh^$a$9O=UvOs +8BmNkpWZ?(<{Q_MGdI6O|<@EwU$2;)|ntI$m+GW4n+IyX0Y|gMxN7-5`+mi8ma88nt@f+~SooWExT3I +Pf9&DbWORxBwm;g<6@<6uw5MT#bG+9!E(xt +R)`hiMddSkVClPdaP$R>cw0`CuiSGnLfOqR(H+u&BDu#+Y##$)>0lPy4i??}3GMK)$ksqIjhHm?JMxf +x~&TlJLxnfYES(n(S29H%vjDinb|y=gRqIOWKR`JpcfSpsDXD)^f{8@mUrvxeQ>|Ua2gB1y#vmIoP0ToQt=7EfpS3@jX7-Z&$F}rh<=$u|uMW0iBl8zBhD`FAoc2$Q +7`kVczBQ`R~BMXgu6muu*==(o=Nk|P>lJT=U?GJIbDn7u1grdWccaO2b=no7m10AZ?jgNH(%Ul +6<;*xW!7Qm0?Ej^=ZXPC~zJ~iva1PThW1S@ErmjP(9Wy#3+==P1+@c1Z{Z;%UlnJKq0AwNy1E290C2=901Y+mN;``nogi9kuf +b`zArD85g!@^rW%!h^gkcnYg5AccO9kN6r3rZ*Xb{$TUH_mgKx1+yUCIpQ?1yzp08UeOGbajnq7e>@l +t&=(yBZt6l2jgS=49*U5=DsqraNN3cwr7COo6@vmZId>#Rk)2-6nfuWd2TT2V;=mVOO@grWyZKk>`t@ +14r);+?l&Um0f0@jo|L}ld^!Kj?Ilh5ViA~R;3)BvR=MhQaDo({?8*3Avl2NLNqPdf>l!VR2cTh>>c` +2bQ(;DnIH8Za<2?Kf{W(`H?5NtjI1sP%D(VAZCi*`jVSV+4*KJ*V0mJxNY0%Q_{G2-fq!(dHRudBVLQ +oHrawCn^Waa*IN3NMXixwWY;=a(Hvc^3!G`@wjre<>C5wB!-*>o)jgqo6Sd-$C{kex&eGQfms1rLRs+ +mLma*~VZ3fg+fU^`4$HuR^)164u}qfEaDM~mfb-!Qklv9`rjVYVOl-n+k}1~57bIjhNJ;@suLJoul#` +rhDV$luSdoBpwjvW*(@840+RQMg5&T=nlL8(j^O|ha)w*Mg7TEx=Pwfsl`tlFogsW4u5NA<2Q3i1uU~ +)rw;bg7H#P!tRU=Hy^C?TzbVs;s_|51AlR|8Dl?Xe;k3Qt(9q)#!N@0I#xQ7jT8VLt2A_t1WM`0=63^ +$^YT>KgbUEcBMon3hgp3`}$r{-?gnEN09YF_yhsnGR)ui6TR?wob*RMYw>uYdW +y&*Xj(v+A*$S~2RoJ`v2;n+{IJ|(9JJ2G`_L96|otx^beVrPrXJ87XgZce&2RC&y|1QrSt1#Dau5=$?88Ug;t*3@0x6C=P4XGyRVE +(6xO=d)=2a@A{tdj0K!}R9S3)~Enj6d?(V^Tz$nqzG9*xx*bjrkHAn{68842?lMH17?n20mD@jSSD2( +*U^9mV1Mi%x4fLFLY0ID7dMW4^$k3sBuMKx(ocOXOzJpfsvi8hLQLR-r0NDPmMC>zr6PIf~WKJob^#^E9uGS!hi%aD5D=CaB{F25lGi{2lQLhGudtgoa;Bgv55kkZuv!$brLB| +6%xwCz?VDR78r~~_jw(mi3@nUTGOe$(Ik0ZxxnzmkrI^_8WOrrh`N18XR1ipDwm;am${;X+&p#Na +o$=5P^DFn_eAu!KwPwQ@-rF}71pP*al>;q6KIu=rAMh0w|Uh$`z?iY;wPV*q`rK^m=l@p?ig|q*k1l_ +N$??&rRA`5xtX=LJyBqR3I-eS9flL)MOIm4NhyY7@D+|HDy|^LqbQKTjz@6A3+$p4zEf~cWhxK=N3^2 +M6LP9Jyg4YJWR^vXCvm9)$l +n2f1@DR{(BQYFt2B89o6l!sbkC;ethANFmPx=c@^x-r%08RoATtT0ImTBdR1%J5DS@Ln1g(bzeqP|`< +3m}5$v(#@_?@BDT0Tt&H-nOy-XkKY*A~_*5+}s;lhBK%CvM#QYpnO(ParVl43ryuy)~FqVk@`+jq!t! +C?A3?5bLA7*{N#<7grF;@WT!bV;1_+JV-SS<8H!WIYFg+kh@B+o@W|xZlDE?{;N`3%0&U1P!3^tQ^Ifqdz8vVkvS^N<(k2G)_5*Q0>UYXjo8AKa6I6qx1aQJ!(m?>ex0kkE+)Nc+#ahNvNCr +I*zFJSdkPXnwg+gX-BAK!LU+R^EzOS|CfM6}=-asA84AR4wCZw0;)KA?$XzxVJ#M)?EEYt0QOsFk<#3 +N|;8D6&-n+~3pvsf<;^IhNRUjk@*jstSr`)1(?sO@jtIGYK0XV?uWdMEA_A1(`ITLcA4M5TxoAD5QB+ +CH^xpN}TCM3>wox2D1_!Jal)XVj-?Ucv?JIl;@-QLJ$_5!?oO*#O_?0LjQOg#p`L@HyUl0Tg!pY?JfP +{uXCng8dO^6&B(J8@U6eaS}7kG?qWvwt4|xdzk<^8tMRVfU=F;aN*vW`()Cn^)W>nXfVWgYZYNdz3aKcEa0*Il4>DC#dnp#R+*TnpTju+y=%?FZJ(*>6{l)2dPD))=0;(0b2vYud2ljk2-M4^S+uS +R_L9HRK$`x(ED0M25Ae3@w(6rLk#C%d83{6bw0O-Pe +rNB~fHyBo-kXbqzjBngJxe}wN;6lgV$k_=ix}RcjoK)lF)?Xp5N{U(e?>Q4@Wo5wJCjlnIFc~m!ZH~9 +^33zMi?|n2*-^sGe@i<*LdPI&p8N#oVa6$@xKkuiVjH>SlvbtngvwG0=mF^rJ`3kV9y$*coqdFRGVZ%x#i3@D9P&!LHe_1;vKz>GCd^Cf!Z&z1&c2 +NP5@z)9xp661j(_Ki12>$R`@yCv`FjCqBa}WFxaMSsECgwqSUcY>A&o91T2Os6{2^{CLi|yiXrSHnqO +2@9lK2}*k%kK0%c)J0C^b)CkU#V?tf^wVy5_-8SlXjNVbrL&X0g8|!TV0Xt|KS985jg64;~j_iin7Gb +>Z__~RpMr=6&}}6p9gP8Ze&)?9XmsX-MpX_Dr(Ul?tblPJJ3~0DqBbB_F9Fi3F`+S{<~ODk(`xlj&ni +n=W3s^jHRe9(l+(s{~60T8mQfo=mDQ!Y$v2i58FsiiXi+QY3!OfSC$b3{2k^zwvEsW6I9L)o1pgG6rJ$SWU!sYHZps7oQ0*Q0a-YABIJyux +hgIQBX_|Oq=GZ>_{l_#EK{IrsU;ewxq=#2LJ`#AWuiGesfa}E;gW?g#@R4`*#0f_0g+8E=G}^{pyrx` +!K>MW4x +=rbX6-)YtQnd;9$md#Dqi(tCwP(Dp(^spj&Y{K&D4{o(;jJ|;p!}*T2L +>6AUTmW*79vv<>n02B5bkD}!S)0fQuCBmJb(s}t-b`P!W5LhA{LdGN<$XzIUkMh!FB^}%d8PFp#P!sY +W#1c`2Ww5;^8w3y*L<69!E4s+q2@>r-UMBgT~1e{@X8FTsEd$`onSE_zYU(d&GBp1h!|4cQju1RoJB>9Vt=jR&`=Fvcu&ESJs#3%`Pu%vok?%w!v*$+fsQSrNT +~nGpPNych1DcX`J};3rjZH*P=1lGHJ&_G;g2od&!B_JO3mLQ8G`WlUe#$$yEDZE9meVp4Tucp^_-G{4 +NF$&*KXV>90YNA5m(S%+(}sLQn%OeKg4=n_TT2W8+oS!vCE6J>yCysAp#XhXN%f0X#!6~mi +ZXk}{E7@|f}`T2E-vU#fJ%1`_pb<3`Qsb%rnPGjJ(#OrMM;e&$!XcvV`5cHjN=fCMKLSry0|7sPjoZJAi+hx6X2>uvz&%CV)(zdEaf4pyLiXq4qCps#&Jc>9-+#c12Tm&<9Z4e(c#asW`*?;=mvqH +(UP;dkm*sf{6+@QbJpI+iP4npNTH=gT0w(f5Y&;6MxR5n*lqTP(>XANVq-kP^9GtCfs&R)@0IZhb+1H +R_r}=(npNjQDuz8LZuGPy*?h>$naF25Ynpp{bH#s=nFfsJ{+TBwZg#=>zWUo(UK8i!T%%O_Zqvh+WSA +2PgRrpxPz2jTFs8vC1>P*o +NOc$3fR~&J|Tah93w<$QC>hlyn?m8DpD +K#t+RS&FbvHhgOwCeFmE&bzNTb&&6Jj)eWsCBumms1TF4m%a2=)A&TURqdCm+?IXu-UF+*T%ds*M3_~uzlbbMN^N#n+?o>H6kG*^3?5^nM4IGezyjXQiqP=t28-etR=sv# +40pguNq)X<3}!Ug?xnx3Bf<&4mDBT*8tRU_@64@!!5sy`;IOqhkECqAAjh(q|6yruHg_xS5N1)qBHmp +O;hYGiW&Km4i_#HL2pm5J#zbCgfxIK-XGfSLLZ@{)EQ8 +u6y~fGYBjY>$PftWgRHGW+bjQuG~yeJWAx}_7i1(PbOncSadA`@Hv+dFn%wIG*X)f4`3^dJqKwBb?5H +F7sB^}T<^?0xQNjQIQ}xI@L&TH}3#*N+vI&4&1`{k{MtH0^Li6}DvY?bR!yQp@P3g~St0(ttvso&jG+utPspKBG3xj9hh0Cj1UDkZid(P$hGfoV*sFE!-!33imPIVnWP?p8Gstpi{n#adNvKpCm}6TOOe?6p8l!n +l_8tCRHPq@ZTA|xlI3=1N6@7>Grf(4V5#;L>%NKjo9DnC6asqUgCesOq+kf{4}gQT1 +tzv2e!!EG7365@@EFvHsoy6G^#sm&93*cUQ#whLO9Dt3#An+$8uNka#>blS^9+o>X2fi5jQ2%V2m{uP +GWRqF{1ZHmdGQj|BHm_W8t2W}0SR^P99ozwRo`3`O+9iu5Z=<1>Y^?d+rk{%Av|m0p<0Zqt$T{Mucr^G$De+JV_X8KnNqi@a%;j*rEQm54)&Lnk1gO;*QCi-b=bLXZHH1*cF; +^f=QZ{>l=LrS*uyUqPTRY$*==rokiC++n7JCy6TL?t<9}lJRzz>!O@nW(`Fq6c`f=WP8W#%so2oDLl@}DAo&lzF(;*yDMO&0 +8Au&UrA;TU=HqbV0Z7>s2|_q@*(mn*Ln!ZhQ{)L>DX;TV#w#u*)OF)t(13`^1s_v+8Z?KBXG%{lsiu}|0evBkltGY;pY3n_?}C-Mb)fWn8HzOVE +VyuUK`V#g6U^#h&l?vD5x*M-v8=;(ZJb1Og&I!tIr`5JXyMP84a}I`-1vv!{c0+-IJoa$a1Kh!My6Isz!U+Z&=fAqHFlkI1QJrq@QEL^(cQC#Mg +L~~L+bXkHwF2PNlb(b;>ELAZUBy9bF`7dvRol%93cv)6FN;pyj(yRlsc${5a^)knDU`>-%O9BNluwCxcdPUP+cQgw&uk62z7=4#U5Sd~wqk;u^#by-w};w+r4%g7gGEISJxyUZEx{B$_BO~dcfj&e +P>_-8178h|7UG?d`EvQD9wpqgn3hSPl+xSd(UEgEeFX?Pi#m0udbIuAO)51?9D2x8pp*n(f3p-=jKN` +tGhGRx@g2ZoSI=wINQioRn~qdp%>n9}lvAX=CPoUJ2cz;;vuq^p84brT%=A==P-GIdue4?;Z6k{M_1OrADXmhPdfx67t1(4JbbLz_suIu}&Zvu9>li>-tAG!Ez0 +k^CTT+(Jf0L!98pBwD|6N%hePy9~g!UYhi$wLdMHSiy`nb!=(J)Z+h^(xY3||TNE3mexf}AXXyJc-*X +$w{AP`YPv(hirWd>Tb6iq>Ls$DTJu!j&jAtHm)@sm}k>DpD%yzw8 +VsOq$!i^FRQ~4Gnz~A82tk-p^vTUkP5}V7peR@-OU47K4jNl*r&PqXKE35Fu^hW){Q&q7pIJmW~)NF~ +U|1VHW0|XQR000O8NLB_@)>bN%@)Q6708;<}8~^|SaA|NaUv_0~WN&gWWNCABY-wUIX>Md?crI{x?L2 +Fb+qkvg1LQyOCJiL-dX&fBE_&TgfiyeY9Uz+xCX@CSX<*9|ZEHuCREiqMz36}Md5{wI9A^{YZrc?Gi7 +b(a=f(3PsfXh1i!%}BaguEpqSBkQ-{6z`_a4HZc##)Jm27uf_%CMS*^{SVi9aP-coc8KACn@$CNGmnW +~GdU&V}Ad@%pHDc_!ZE8+{0s6yN4m7Kb{4XK*8x2w}8Lt7Kj29DZ$;lzW+J0`^8q@$&M;#j7_Lf&QRH +n8o7753jDTF8}cM`iHAG_%I;cl6{dYEp}nKOOti|JI%LSprrntm-TJAt8|h!KdW`2@<^6V3oZA>CQ0S +}ds-bA_wNa~HY(o(T}p(^mf9�bzg2U_r*~BYI!Ih34K)>!Msdeq0=jB*@j4ZoH=9)y4Cd-(y4b{QkWt1^UG6FuDo1@)DHlgP*T|l#!kp?mu|& +fN!pMNh!Fwl7*5bC|U^`Sa4sFU}wP$PS&XuheYo{gU%?y_9 +MpZ-n)+epAm8g&UBUd7?nUzWe}ER0xg#1dX7db#vVnQrDNr}I~NCM{_#^6A{k +hacr8HE;T}OPfFn}8y?QC~4G~DF1mbcdvK;0_A){oI$e5|eb{m+Z$PbZ}!bXL|ONGex?!5KAB)bKP#He$5nShx)F2tkK6OY6ILiDIGGj_S-2@OP`)H|)p +GF`%)LlKR%*cRK-7t37^q;=nt#^dbw^D>C#ZC0fz5Pa}vA}mFjWH%k6ABwj?G!C>rqQ-_*s(}-rFbcE +=bP!%Wn`39ZL5&3d0JpO)7%;zkU>F!iR5Htfj+)`Cl4SwlP!tHYCkeb7qusq!1Y|b`KL8;|H_IqYQ~w +<(4@_6!Y6+tA-lGMK^Gcs1De62M?hN{e=$wluBghh%MwTPf4zD%Ln4A7<5nv4V*HS`aqFx^OGNv5R=Ba&`yQ$=fXmyXp~D5)!zEeN84GiFr!E +@=RFRKI8+H@$cOeCE$wx(AHTB-vNc-na%JD2z~_+H}#hsS3mlq9U9lmO}yiqarV=G}O|O>}myORH2t# +t>*B{P6qx3IHvp6ilQNCsZzYx974&`hA7UQZsGG0d`xp-1~bwDwm~&?-!X@}X5J70ZJ`Hxf8VpuyfJg +B|Sh>KoLO?5%M=qV{3zdsz5Kr-!bjd4>lhF}LGgVOb)141u>H?k-7)?+L1hG+djCRz3Zo1=H=^Px>Xv1 +=`5hCG|odv>y+<36PIv}jDhRgbX)-nLZ}7>Le~9DPd-&5IM=dkqiKt6`A1*CcFLCks^FPEhVjWZ8r?% +lhw*7^c_`FN8+gv4D(GeinO-M3F7o`#1oH6g5ASK~&K>jFYmS!VRj77oDvgFgVtlUjsMD2sts4o?8?MyjDnf ++(=6L1LbOGodFo|6|mIk69cIX|i{ilj(65^q_HjhG+W1_=Gp&W+I$JlBPm~>p<&FR3zwjjX4b%8YL0k%EWmy2TaGDim#^L#(~H`2!9{CYul$(rb$#%N?r$q}N7;@GVTM=Ty?S?p8@Hp@B;hU87;aeNgjxdp3>>=X5LG<@ifZJFGVPihbD3491fy +QtF9j%uwTxI#h&J58WLqe809C5Fg^vV*mu2`JX@dL@K*w(Q_Wwis=hp};qWqps2FfRoaNJi%*~8S}p= +2DT?QE^rLUYpiyx#w-D3h%iIn*$kYj13-|j%Ck+3Nyjsfo@M!&)<^GsE8MhF83`!s*XQk`%pi39V2G^|Z;?1; +C7ugi734Xn$VhREG2iuc_aI#ao}jy(Tlg~ZRz{gL%^!@u;_rHYBbG{FTUUbojL +YU{27uk2=O&K5mMVKLuUnXjo3k|1zHq^`060VK0HD~OrF4n05OklNC21KWGJDdhS~Z!ITX6*4C6=Q=y;2%TTqlDE;i@0Z_DN0JxnSzP1K<)*s+hDHGfz3l0i0BERV}8 +*bK#D*4qROxnafWs})PvHQqa&?4X)p0y4afyh?r4xJpD?-Qfe6Jygi(CL@Z^Zz6Cw1bYUNH($M1guYs +I>U9Dr`W!ht0M@Y3@wc1EkuPB>kS}792zaua)gj@l)&jSYkvxCY{w8(R_U8y>PR9eM +GyVf3&+?zuSkN-Qv3%bkryY8L!|F2z*GRx)>otmTNgb*JTBApW%tu2 +l{m7VlcbuO-T(CZ0=hNjaUS_m^$iXhRSdR}n@`xLAYm=RyW9Qr!z;Sp){Q7HC+l*7#!yc2cIT=+@9ut +2Gm2^#bTI8^;(8F5I#D!vF2n(*C9MKzs|s)2!}p=fQM{fty_AkXLCu1rBrxsG?bi(SDA58xClVAd#TX +*aEo%&Atkr4bl1U?IeFbs}`pCYO4y6>y^4}Y6>U%6a=xs4P3T{S_5eMf6DrZ-@VQM^pcZv5XtnO8pQQ2TR+B?BQzvmvP&$Wv?HkF@ +DvicL71ITyTxthJ;S|9TlGTVC=pTSWtS=-1t5!A^eQCM|jG!e_t!y5E{ey~+at>O9J6vsY=_Kkc=$ZUNrf+a91}1EId}Hfp#8t(qUbf`ZEvtSh*`-Kv7S-2m{yBE~!4LqiAi* +PE&U&Kr5o-ghSnDPKqYjjq@R6S_izg=VAbQe$jaTxiePvX1nJaJS>mrqte!I_nI^Tap8{*Gfv?!Uh-7 +2HgrzPh7zyI)3URripapJgRVOcC#nO4SC@$ZUnw73~d4Vc}Af<6erj8$D>Tck>NOX%JENxX)bCev@3T +u1Zl&?h;1BFE6`Z2SOPn}$IWDejQE%H{RzaO${$cm8f?PzJ5d?x$PL{$>!RC|Hr0G7lYI|4MYULd}OP)v@Qgz4hNSh(YoFRH95UfA_qJP&hXb>HdU*-hd +uwsigXr+IGY`jmy1-qMIYi}qwLo}L;yGG(8zN^;lwh&jUK+N?M~WZealN~OR44gmg$+iKF}hI75erplrPo&W+ds +6^gM$lc%(pM}WU76Q#d94g%Vx>g~cjx-0usIrgSgwixWltw62HLr$K;26(L#1-x6vD8pLO#SB-7xY%EnK` +6GgSc0@1yxJN^(O(GvAz4sW(zSr%J9jN-KCgj0JpC%buHlvu*{n&4F(^63`;;q%(gy9=7HPy&(iMGf{BI3i7j+^6lKoV<-i7&&6I2_%R|dI*HnPAL7%O=<-aKxH$KTb +m~wHvDS9WJ!x+5ECdVU%X15f7y#jItuxMF`UpxSN`7HR*?FncfBp+u(vcXGhb$q-@GIT(lD)6QlJm6w +J>EPqpua1m{k2>#Yn;0E1*c1UG-jyw0>okvU%H;2uRsH7a|2!KilGDqxr$aTn5JRXZBVe;?Kl~P^#RF +5hIRjnT>fHmJ@&@CPGs*^!B-6fW`0l|A1LP$Gc|SlPhE0w283x@n@Gau=hBI>-dI0q3F_9J*i~wkPp@ +unr930h(noS%CA35sHq<~1iJQu)L9&KbK&~LM$Z>m*oJbO!$Em)?BUFVvDSStqLIG|Bx&^sl@q<*y2% +#0ga3Icu%>|A_kVmoVewmC;Ohh3gZvmpZTWpmVWI;5_@2leM;t0C>R@%Bl!PCJ6L|8e4o6N)5Q)2w$~p1UoSLd#uyqY5L+W0Zl}upQb`Sbjn_io0QSDuu^s4 +0=IJ<&h<6BrIZBg&WOm%(FIF}WACqBncI!no2qMdSuUFjJL(*XgF?rgywfFYizEgdy0M*aYz*!Idaos +ETh=Dc#**-&l|es|9`cXZA6t`FD^^_lg-x}RY=1*F^F0mq+Q!tVt+UtsdTups(LH@65VfI->2>d{%_u +-l>Cm5wo0xuC9gDTnF>wA?~pWy$$i1s1tX-c-_epN98EBLo(;fo2rKsYQde;uRRPMs-3uGjQgUFtj73 +@`QozEaK4C1G1ts9(GLW9*XC2SZKTE$={L!Ce2Ol4^tTw7naRJr=<%4zbCYK{1_P@;Ii4R#G1LpZ+$& +h5Na2)eEjskzy9jU*R5Ukq0(JlcfLDC%4UBzEl`d(hST&Z9V4fnX{Jh3le}<;6OtDL8Svt}_b$#BgIg ++b()$4rM42tY54b|2)H^%##FsV2Ba-wz8zX`Tp>KcCZbQIfe}*-Q-_pY2`GZI01F-ue+Hf=LZdo1~ZS +9SnHoNU@dY*M;B9HFIVd4#oC++d*F8-Xrn~&hjZ)>zB*yOkHpPj<6VP>%jnDrFBCbvr*-r}P<0&I6&= +P`eQ?XT*unOSimU~UyyQ;U6->IC0Cc35DCICsZ${7s!58T${h7=9cL9EJ*Yu6lLWcCo^=ZhZl;qlVN3spsYU1wU{xE}~_Dp9F+ +P&IbY=)OE6#_=XEOi*5zqht9{>OVaA|NaUv_0~WN&gWWNCABY-wUIY;R*>bZ>HVE^vA6eQT53Mv~xnNBBR$-K +zy?vmvSdxEYSyUX3h|wc)X~l055+wuTK7Ac<)bpaHO3^vM5x^HqhaLIGq;o;zQ}A#61XR%N}i@>Q8xr +@`5WXTiKo^I|y(cFp4K5BTQ&_fFxz;BmR#SNU?)1kvMBaQ@)o`5FB4Y4EqaNcO>tyO*A&R=hnW~JX$+57J`)qe8+djUN1vRPGGk`j#U^*q})!BhI-NmZ5ABnVD}ZIvuH$s{OB80 +}3~S=H0sX0xAaAVE@_5T(#B>wJd8q>;s{>Q{N)zzXLUimnj} +>fp~wojI?^L6g01rp+Ee_W*mIL09T#Oi*dQ$H5QJzokzjH5-6$$qc}ovqlI9Gd8xqJ%h22gH3XsO;dQ +S0Bo`*K_CjiU*-E)e|lQnBK&i>!jr&#LnE-VduNs|^g{7p|=5P5`KJV83b@3H +I>^7{M0bX6fI!lr{X@RhH@T|2KXa2YEeZ)XZn=4AB`kAFTg{+J!MR@GZTIraX%22+Mu*=*2fro=@=)6 +s|JDaG0&A3jZGcboy>YDQmf*4X%0PriQi!?!P|Prv)>$&XM +wTs6(Mp4`8`-EZ@_td{g4#DS&Rf+jUtuLzHdz06mfoZum*?5~wq5m7rV@p|*-u1bq@b3f2G|i(o?xwcA|m^etat3$WM$c74*K8@ +{~=tl8lo=j$A>7k)F)6vqw}Am3!=uDSR)elX&)E&0WVdny28?Co@$%&(IrOrBI&0E=3$%}QK43g_#x& +cabdC^mslenC*&Y7kpo2d6mycaqtwDF!;7)WDQopT@)8Ol{u^f0&zc?&0(W +9&}hD{gIP9Dc1Sv#picHRb%zN+5QYi>tn{p0?&@&>n;wvI-t2J3snV+4ts=O{KK%HzKYk{Yg*yiK(=>#clx7Ca6m +Ujbgk@huKzRvcz@KLyy#(^}2yDAUOQOG`%veLUrm$K`2q{t=Ic-1X719{`ReGQPMqT0m$QjEP5t<4V9 +rm3NAC3Zv%bYwN8%Ef|K7eFsVXC!(QBFRLs*2pTRiyhESco~hxK?k@ruuAHrX{tzO04B4_w(De`P5Dh +{Uh$E_v0XHR+3RU2Ti01X9GZ6MKUeLLFAtT8kHzsG$!PFV+XCqxeK*^Bh}dGj_}G2%zuXt+Ediw+Q&v +$qL*d0kD~XtKS;Y1=m#>2}paCEOAc;vDew|s&rb8|3a#z9906PX@3JXBDqA+ffDqjAAn}$BlemPg4=` +Sb<{9Toq{SwMr&VW-?`wqZtxulZW>x};1uGXps$ouPE3WA+mXPYj&lcD +j;d84)*0PxJ`f+0x#Jl&3qLTp9Y$j07+87K-L7R2(WN#aycM8$uh}{x*<|ZjkKaxaGN(PCcwL*AiaVi +F_|Uy&}lj-p+d!t!L9+3iF-LE;6M`-wRoBjK=Vm4&k#h?4tNKY6bS8he}gi8GRxtgYQ7rD(7R)1ct&H +2ObQ18F=SnW+zlhMC%%gjAK_x)g0+i*3}iaZ3lO-cvaOi(T!b>u7DdqLhR;)|m4lcJC`_UikJq5t9!vAg! +v9wAN1halV@kpnQ{YK1fa8mct(u->MC4k%VfR}udaHb<%~h9kD?mD1_3NUG`Ds#V$ZYvv!`*-G~=qoQ +_`7WRsZ>`^NZpX(oK^os|g3Z0|Bbm;+IIdD<-bg0rNc&`36>HQ;$txw;7EekSajQm-X79N@!mInJR*R +>|hmlH4YHyIu?kXzDlJFOE->OF>^FB+kLt)6~JhOTzC!=6-+JZR!kg`=V9M@|5q9tO-P6cuy3iBlD|Y +(1XZyXLydyRqL-4n{(yLDO~-1FG#dK?$_330@Lz?I?!Z;IToH$UB*uX|mMjVJf8oE8wL@_NxD@Tt-W{ +nFp<*8hP^K!YmxfFP^?_m*Wn*?V&Nka-AB~O>glq*xJ%p|XJQ!c}bpk@es9 +To16@z*SyD@T0h>ghv_5?fUX%tOsd9lmvU(A|KQeRI~SfFTRP7BQ`cf(wiIjAkwQOoieOLkXflk9nAW +*I07r2Y3U&S~(UxVvDY2z3Px+5nqX#T0N8B)7@l!7T%GM%~U6QR(#EAUH9Vv#U;9yGN(0Ocp~>1j(pc ++f~QRP`iz5qDH?f5c;jM97R*p24g_)c39C5ZkEhCearPV+FpZYegmr_)yN11FF{a2nhk;xtloA@7Rnp +Y4Uzk5&V@w4G^804zECBU-rBd!qF6VakzsC4L<#5-QtaJ~1VpD2kVTUUCYo+3wQ_7zp4)uAM(9WZQ6g +y}sS--_G|3_|FOZt*1xWfN0o~@T|0-}u&Uy(-f`z{;o2TuF$SM|v&q(EOhpLo&HQ2C2{yg@eqevli69 +MPqqCUj)_0Z8&gLDJ?vQGAZR3*;vwr^HJPBNvNyXe=#t +GK#3sbaBJC9^f^?`V+73$wAwAV4Z|iPc`2pgLMDm9`kc=xsZy!I_PdwYApT4nyqWNA--d5QnL*W5!RE8A2=%S~x=k=|5W7hWOk)^asX;Y2zR-&cpQMPOwj%wRM6|1+F?LVRefk?GE63X4Gd|h +b-Cd050kwqP~ab;2ui3Y_*)&N=a7)6``k9D8NMmNWL7AykBs9@vSLq0d_7l0UxP(eo>R&LcY$MeGmaw84R0>j70d$nAu31 +MClF+__$WC+3}%E*&ZsQ)zTF#=2EP4Z?W^Pbv`zW(+qUnCZ +_(7lSSJ@?SuWgzNN=1Lpz^E&|Tr_t8Yrfi`0q*I-84vGYing;bRlFbktV7!8W+g_1jhNw0MB8wa&$g6 +%VVG-W$X-^+5m7**Fbb}}`ZAwjGrg^a_TT*f4AW8Se&5UOvIm;D79fav9`1;f&(L;|KKF({wto8Z`3e{t_Um9@x7Krc&PlHWC;vvhC=dSr)R?|Mv<{Bg99iAV~N9OqT=5!1fqH_t0Ax8;1a8EWh`a)zC +XbcOG;rh8fEH4&_*X1o5ePR$tt426$JiI_J$kR6Xp9FTltgiC1L^mK_G_=nF0k;D`)+i01LH8 +x-C3=QoI)_7jWhqrdRdMEah9Nw0f5&cEAgmov6A^pqaZlF;&_GjqqyKYYkH8aRLx4&~9}ZNI9jWUmKb!;A6us2-G!HsZ5 +y%YGX`Q;F$fZ_l^W6Qs(4lZ)I2s-5&X(BjSvw1KZbm1!hnv3)V=0R#NYTyZ`Q+7TMEvdssTohH2+ +>dCM6PhvLJzH;bvc8uPQkL#A$Fl(+ZH#avJmKI+Q4?Ij=16quTO&DF#Tk-D@5> +e$CeeEB`kS&uUjT%=14i8p&{@Gslfa{Ekx6uI`M{N4eb2EZ6(2=bk05YVyd@{ckyEN +$NdPE_$Z4aK<)ePWTU)~NUoig-bX5V{X;J4BZ|BDZ>Y#V=i&k9Ir)>xa;FI1&h ++%M#%3(YF<_KMM%4T&eE$SI+YW*fdF*=h>DrXLcLNwdcaXwk0vET`ptWS(Wru51Rlbkr?X>$c0!!c0?K9mR^vjt~uG>bjN<8IIak?y?s=F$15pN-jy^4G6EJ!m+wMQ~^ +}lu?qw4X4jpDft;Ke(>X^F7D7~QeR<2?CMsp66h9637hm&uR|7U#!_4$_D~|-b8EutOG1_&;_7jKBby +3Bn!H=T(L)b3kq+K^M=0)mo>}c19nCR1mmN2<{++SgqM<=KjFZ#3_Bq4C2e!%&R2|S&I%sSYGEI;ZuK +JO-l6f#NVuONV@H6VOp+o#x<}Ht7<9%!5Se5Ivre1=6Z(C58>nthSb3-lDkgKMcQVMNQRAyGwsM@zrL +^Q%y<88T(n5P+wp>%VzjG~dY9<5#N3qR&zcl6=NrIdDd)g~o!ST{OmVNmH7kUHQYa>NMni!z*UM#r~D +ueCz2KeVSpLWl{k+G_0R$+QEx@GF-n86@Qr3iucRS|!q0yulFQ2NuMMI|#F=^w6;RTfg%mlb<=kFVLy +D&Khwfi`tpYnrJkNlQeZqLNrTq5-V+Y2CUXv=Gxr1as;luLybs~DpPLe7wl-#d*GV0S>5tUvrZJ0^9D +N_Vo^H(GCQhUw%C}s!x=wgP=R^eZn48fSq9O=_`xW|t+~yrRkH1OV55iYd~p##wQ=xyaE^aIz^@;V!c +IqwNtvLX^Xdxi6jxVcJm)QM@%J++pwE+s+iyxdJLF|Zag)KKmw7*wQ;IpIi=@!6(z)m;w0$q&H|bz_}330M)g5bR!sOxEAF +_NQ??db;GzabjyfNtlAXh^pA%lCAtnY1H$A=w3Yv#|q*YSy&dyw^3}wJt%r2_ +Ks?I3*A~=UxhG|CfMDn!~(MTReqytn1h2BvXPsu|P190kb+XXBx<`27gy=G=|>9C}m`MPfBPg%mIgv4 +FZ>KfUfYRoSNUbE;$#CF_@qkWpG-v9|9DRIhN<2=lD+QQ=VGrjQFZ>Q^%Sy +t0A-0#=`&NK8e&g*IW>j7<@)=c>s_Vosz(?fmYLiXtE>zsXbOiCCI!*Bp9Q3lKF&$4V+Dua8~J(R{rZLOMuExGI%ckhMJBKYVzea2!3&v+RoIqQoN +io4RS*GjTf;?g=QQIBR1Y7ct^RjurIG3Kx|ku +$r|L!K}Y67N6!@EUf%ecpCl3`j@XwBl?zI)ku5#@`Ja;g%=CGwqtsUX>sT^OVD2V4`;O0k=&m9ocKch +lwa#$awW29UMTz{#1wBIX@m7rQ6Q$@4<^zxo5M*FZM?Y-_?1#HFGmDnZUL;WD)#o&G8S72e!gToiEF)3C0ka*g|$( ++r~I0;j!1c?$nq}i>kaVC+paZnHtdlvz>UxvhzT#Z=WI1*glg``AITD0xHHmk& +HVx9yl|#qe5C%znj3n`Eq7eYOhqD4RuVt#C^qM$?$`qHoCf95D>;)E!4*bkb+B8L-v(ARM$sb=YUiJp +c2>_ut)}dt%M74zM!JPLoxO1lSMoOn394CYMNx#japGDmqm%Kys2%Ve80K0pWp}QV`p_@W__$7 +{huS#$#qikp=jDag_eJ2r^0NfCP!UYaxdJS~g>c$XnAo#uJG9nD+=xBb2v?o$-xz=&Hle{8nu&4qT)^ +o?RhK0V~9qx+offet|LS(FnxX1*xdFUBMbV6r&_uM;?ku#^vMrjPxPB3~}Rkctv^y|4EPLJn5ZyPWl$ +yCVgiL*kF-Ql_#rK$H!ZR%=D*NS4sg4%*Qk!54aY!ZH0g1(I +@0aZcsKr_f?pWue!z6b-AmTEv-d%2X@}}5@3>o~j@p#+$zP`3l= +8__%(nx|&KA%`j}~KER4)%pGqfP@CeD<1LVBad+hM*j)VDgm^1(S8{ZISmQKZ6Cw*~j4&YI0m>Enc4e +>E39I;D>3Eip^eqzOr~ra&woWQ#M@!2cimN0QVxkuqul~xX3+l_6j0IRtigC6I9!|tME0zFE&U3TZxClnj%@ +ju)Sef)_4I=3NPn3%flkt5yIf=e}_Bp(J_{HVlPhNdEI(ZYyn{#**!kh4oy!aSid;!&dD&Xx1K72I#^ +8NR)oen?@S+W6Q_>u(>Uf2MkZwRxef1h;v_o>^z&n$4*HyAOe84TXwpBMb+M?wB6rAD$d7DA2B@ilaW +2F6JXFT;~pUq;Z}$rrCa8o5x1GTm_6%Aq(}zj?FGAoN3#qhL>)sl|SY%wtQZmBeO_3?5aX!DKH=c(*) +AT+NWad#sZiP1>+9&`$K^i8S2da22-^iC2jp>S>%}v~w}7+_ecW|DK%v^5E=`|Mu@6jqgwXbn)eX#Ha +VBKmGLP>{Xjil41OZlK`|`Afyq;DX=oXwaUjxyAEq}GA3G(rUdHF$q!(?yv|U`tlW`(J+EBiG3F@Ltj +VfPUbNY%K;omY`UWIG&~OczR0%4K0d{H1?LrV2yNWtR)VQ@XbOx%@bg+u)EY!)%9Hh_P7SR%>AwF@|@ +!i9-@Dc7afEu7;ztS)FEDo)ERccUu2+{jZf8tJSGp*4HO7j9 +lOom}9)_=w(DRpb^UKHd#XCwa@QGRaQ}fGbp{vvK24khLZ4xQ5`Q>9*G#<2TbXQP{OHyLb5*x%sZ*5S +vXVE&@%+drXL=fJ)cklc)+Dbq_j}%FGFdFr{f00ne$00CyJE7BmXLNQexklKXa#hmVIQkD&^uck!>be +Y_sskaQrbXk!AU+>fE#LvM45^s}&EQ&nz8mS>TB9pAz)DdZxteNuOBvp>pA2WkIds1*_bsaw~DH%yi +k+s{60lu(w`u3T4T6-ho$mwO*v%^w3B_#E;mck>8fO32_GPIZ=!&<3Wx<3A}?E~kg?uS!uaO421M~1h +pRIG$IoGUuOo|%7}@zg`E-Km301nq8S#~z59n`Ih73iF0WXk3nV^u&Nn>M9{7hsE_J(8ui5ELy2|%cj +d4cvAHni*7&YIAGuu{eV+LZGWcxGO3c2mrqNQYGD{;yE`jCx06;v6N)PS?o{?erHj0ml42>|vIZh|qT#Jo;n|5Apa +)sQ=3w4^jtteH#p|mi+muD*$R_eLrb=*AOvtBgb&q2YDepX=~D&?z|eahpc<7G-|NnI@a`vrk5EHp#rKUr +)dMDg9{l)66v%5$>ZOcc*(td9ABWlK;Zv&uAlsoa#Fz(gy*u^o@7jbxp`Lue}`LC5MtOTM`+nmW$Y +p4UjR^`#i3E0%2v>4Yx(C`&X%wD73vp&XKqaDzex?+`!kcx-CoT4p=^dR+ab9J>n+CA}gF*CCRsk*g~P^HG;C+H;9EQUFXgzMlA<9yfe( +cwyJW@IqTbCcrdUi>$#&g@>T}pCN3${{inBlqFxg?9Xj;5rBcJcqNyp-AB;ln#rhR&~EiQU(tyBKbw6 +9`c?bY*SLGfknXP())kKv;Mddmls-3uAI`N4$0JNgiEmz +eJ~}R?^6ag$`XcZ@*3Z|*Uk*}qo*%c6*~u(6L(_}1|1d1TiPIjidOXA#AlKbZ1tFoj4XN$>q4CRLd

eU0Zm^lz9{>R^Av!4nC)%(deiH(0WG91vySHJmz&v#yEkRgQeOf{^n23~tz4QJqA3gyO*P2^WgL(lGQM!nR~ +?`J&_{e0+Cc${2d2?P_Xt|3;ow7JKtdW?Rpmf%V1KZs`52lH9;Z0$W1vrwzOSJxfD#m2J3pciCgbL5Y +R`6#7zsYTYLwdNdWZL;fxmZ_qIJY9iDQrNy2^=0E0xvkxL|dBww)N>cgeDL2hXB@6%qXuZ}FzOl)bsh +(4X9tFfO1K%Q~2x9o9;{w_@NUyxYIYPwn-7q0B>w&UgTvH)s-#-IG{+Buz_8qcwpztUdk3B*Bu(1PJ# +S^R?AEdikq-uX%B{$u=dZK5RBHN$QV3{usRc{;TgvlbNV{UL-|*fv1M83Pa`w%yvLA2Y>^+4VtS!`)T +2kr#(7#+?J^RAfEt12Z*kugGSed1x1&025W7rRt_h)qg!xAhI?8ppViJt(!vKGQ>ou;;XGBzs<$xHag +(j-wxeBi(0Y-ao}&tkpv +!#rX8IRC^F#XF7Vl*M28c=neOvO9$0)uX#Mz1IN=kkcR_}%QUDU5;7+A6cwj4Vhw@5*^=WU)Oq_$Opr +%;^?IeN&ON`9=fMO&#Oa9#$$2LAd5KuR?&~!^-7uN{vI-6QMyNar`bH?DRA3H>EaHfumS6cLT&n7?0w +kg6v#WR@WuH3$RGj)4m@mYqXTTv{KR;kxMUOwZ;#nCYtz{dW64wK8pd+R;U|;dWlC9y~sKTOD8$0*GjM&SyUz< +)3wd(66E(tyK_WV24xw05%D3ct_lBP*0s32BT>kxwS08>6UH<*^S09Z&|NY(4)#ExY6)XP}cP*~8KX_kup?9#2+d+FOTKvOoA7S`7zF!><*H^3d|K|KeA4%_gWkN5xrl9rjPGoh +wVps+1~l=n9>6odP}1a;z8pX8v#p&Txp6a0|O%bWaL##dk|F((iC4^a574pZ_-AwDUV{NUG%Di(%Jw0)<(mJCslNwYZj95y%7vl`woAqVzs19z)w}*3Awtca;j@$+ZawJ})9pq+0(Hc*lZ=eq;)+% +?)6&r$T4#P6RD2(ku)(-CEqUe#3v}r^~aIl>P)xo2)wJYn(IjVH-W`w4XN!QL#$PH7D_Z}hkPN(aKZ +awME9gR=_>2}Y-wt&^WV;mx0jlqp7YDo_E>6`qs2l}69L{8*#hdNJ94CIcZd&0yJ@<=b~5Ul*@oo0hBm3tG4xXgd(q7S^j0^|gwNK=J +j1&ZWb`_t&{3T^CaufRr2uW2J7lXn<&#OiyQGs~O+D@1B>HL+CWpo~B;HI!_}IBZBD&6F>{Rs*HMYo} +-10!nWTXZ6=7rVGX0Nwp)!3EgU0*%P@IloFvoRTkJ*h>^`c`rgV>ynovDD2o5DD(Y-_Yt)vz-G&VtmF +H5;rgxY|d3OUQnYxP +^FEw$$y_=vN;Aqq80uIB2M7*ZXx~E2_=d#s6bWo^<;K$D9Hh&Q}SAL8tf8a+_7?RZJPeQIJ$p;Ssy)L +sj-S-Bt=HTS47!{a$RJJZ)t6Q)cb{3u$x>05^%94Uz^2#z@>TRikV0U +DTL7JLMl%3%o789uTuGK5G(dQ+Z)9j>$*Om=8eRN~re&$3!(bYy+`1?Xn(PC1OPimDr;dxvQBuwtEJ2 +rfVopBNg?b0dE)f*afj7R{16NUUrN{K==^4xd*<&o&}$re-_;5PoH3RA;z5$Ur%`m>L56e7Cv@3(neR?GW*6VUy;SwziCYkDe*xijQ +RVk5p}FS~Ff4;Y0qQy2eAn~^!wSYPtmZzUm`X-T9|IqOy=qf)QNEq$S+Tz6Ep$9x5Z8S{?!g1IcX@6M +(n)vv`4^@*}fTGQuL>Ak1X)>kZTBS}-+rqtT@dgtO%nmYa?I>{a_SgjAxH0u)o>6((;f|qhNkZH3Tb +t@qHZq=D$tfs;tbJlIcfx1AH`~pEbRqhGV)}K~dCIMHl(X&myrepa(9NSHFCPoiU)i#aeT-7j&M>ZG> +_o{3IO4^;#?>SX-F;GX^9>hVzU}|K%_ziH7$8#JSQrUMyM4h(9gN%Kp{FwNE0|jLjY1!T$50#BvL2yS +;g<@V94=$R;l<$H^tk84lHe-u!>ULYk;VS#NABQsTr{8xr>)oaA;ih{QG{P1HexH5JUN>SM4+Up{Uh0E#bWf>&6r7fAVBW +I7I=Or7!=gsL-=l|8qgn|*0|8?nabOscuB9PCiHpoo45w2@YPp41q7TBG}CliygEa`w3W%dU2biNPoq ++!{MiFs-t2!MbBcEv8wXNrs2G^oK&R)#t*wQdv=LkAZ7=y9_rXYP;9eOJ#cM8;)UW*gf{VW3^zI5cFW +HvMqXDsE7c8J!WGIy_kVI*nc?osA;mzwqeFhpfG!8yEdHfryy{#&W-`-p3im8sTLX~rX(kp8_U^+)DG +lfI@vNGV%Cx&^pV1OLobc~9DH#R7g8-G>9z8m+QegW#G +LZY?2;NDsFJ#zj_v%zvY?|2Bf&uO@A!u)FJ?4m_6UQjPaT<1%v*b9?Q(SAVH+ZjdmNB*W4cZaMC=(ef +)b{3Si#qU+-48#aD|?$lO_Ws7zdNtE?DT?ST&k!CS^%8AQdUE!)CeIrL7{QXP{v}RvQBR<@m +Vp72w?-V@08jGb^FvVzk7}l;>g9JdA1tH^P7N1Z;8RbW9Evz~mP^`n}QP+CY@j0t1Y{|z=hU|1=uKJ+ +|+NMXOHBuQq@p`Zu%E@iaJtkG0TnVD3S7wsMtl`@Nco~qe5)eE_GdO`V`&?Za*b2qB+F$AFiuT=zmXc +ZNKvxKhrfBD$wjRwx{W8|XuxMi1@v?5t#95PVOqSFr(t^|P&Kzq&rJYqJZ%~9^%_-v<7>gV&fUB!;SJ +cpWjfUwEdaGm!b8D*@K9LS^-Bgtp6yBRAC*pel8YfFx-@%e&4>feH&J6^7(9-vVGI03{5UrOV3{|ovA +|gVVZ-5+G7s#S3^yI?{`9YiH#}O{tUzAyiYB`+YG?-vXqDCNG?Le|Zl)xjGe4gV~c@o8x_JYTwGIDDS +nZjWtkkB~#m}WN<@-()FVEC(AEXLgNox6`%6jRrMdJRkXIB95kgWbbqR%RXOTKI9Ocn_DD4&Jo!Bh#+Yfts}{GpF;H%v8y`o +5nL=SY(QPvvN)qW+9np?Bx8B?KwX#QZ=Mx)DKF#o85Hs7dV;&mItFpNmJQQy&miB>fhjV0 +S%dIit*1qrbEW{tsAfo3Zku+C5rzm6>ZAY|wn|Rp)Ql;WeQFnF97K+i*SQq9SL<$`h>6taNUCMu>QdXOaKtJ-_Z8| +*HvkY|Qa6%?o$H#n>!Q$F}c>Sc)ra{4!4Y6WR%>#~K1-hwT=s_U;Z>e}o`LT}E&yJuWh%BM +i?naz^)KD`0+bB5Ay6^;~h%=cPH<~~<0Q|ipW6DrV3 +3XV=JVuV^$3n{%)wja#FbfWhpYdO_EqvW?nC(vg$KDY;{`2n1_TO?0k%w)_=(I=vd;f4m=uEG27{SQz +}0|XQR000O8NLB_@{~v!|ix2<+WIF%=9{>OVaA|NaUv_0~WN&gWWNCABY-wUIZDDR{W@U49E^v9x8*6 +Xdw)T60{s*?Xg`~_@*A%$-!fRUOrs-X<=7A)8TqKz*OSG*;7Bx|dV_-l2o^wb_)WeC}Y{k5xkwxHuLJ`I3Y{bf9I{Y`jdHQ4v{%89lTjlX=UNCpxV>{oz+8M$>ui5FUn2VI1i)pb8a?U=8GL3>F7 +O9WR&N*iv5BCp_&ky|Ku3$kLvHg?di?hT3{CIJ4c8)K7+T!{1=TD#PCxMhK=UL7rPYb|=6?4v#SQbo7 +*)&eLWO2#@7Qw4Lo|Jm0r%w)kFXP)FfmN&!Gz?>j^l=TYA265<5bgki6bsI>Tuc(akiOnKUSuMN8R;S +@!g=*Q5wjWKQGFM(ddzwCC|44ZnC4=^e5PN%hi#L1a&Q;&4DmGMp;%-=VP;Kb#DVMFj3U}*EEMT2&x< +ikpCkOIPsZaQNycNg%PyUxAdRP77S1(}Afh~n0gbUg=@0kq4;y|a-+XH{=Y~!U;8`E_kGwtmISMUHsNfNixMpW6wq4w+W#jLDJXv=l_rl +*|9A2I$?^E7gR}F)6XYCdhYxa;pabJLor=rR4v8!zbvI&k#pE|07T)OT69ykp%DtB6Ps%tckapOX-(` +u&jfK$oN)~)UWWd(~$VEOWU_q88E1Y~A` +I8ArxWpn*E}V<5g4VQlpgCod(41Ofe- +m*^00*?H16JwG-mQouunKfzN)90~Q?lWZ{Z^v8NI6@^pc|1GfXIXNhOI>TtP6_P((d@=8ic&=%mu_J# +mWNaju#ZJ+LPj`(F7MKcs(@Z#|U1F#5M7+-oFO{2;ozsUJ5!Xc`6E{YxnMns$kPs+gK1q+MUJVJlA@% +y$H;WjKkQ`LVN8#Z-$_hH#4?{t6(c1UP53A!5cz6$vK3r__hLx`o7NSfP5qtA)~bNLM~(QWR|OH(~eW +yx>cE#Cb!R5`N)!v%cSXT&W(WyD~o;d0sb|k*@ldiO-@RSm5?NU2`|w)i92AOYKJE0l&b79dDkbzIJ}GCeqoE))K; +(sV5L8QNrYvI5Ece@EPaqk{qXukjDVNNWxae$;A4qT@fz^wY3a89K3Cs#@0YgDMr&0kzgz5 +68AZnQkD&LEfCLo;8fe2&;#6b#S&vP)VkUtlF@L5lvJLE8rWdsXJ}EF^%{iyR@lI5b4Kd!%(;Jaa@#H +0-g?0slNkGJ+53SW>X&cSg)>W@vd#14ig^aeL2QH4p5R2La_kKYSh>7W?roSVr)q#30#`AE_c#H`w%0`!YI-$XwrIilV;68oQZwHta=6oS;dEat9tItzacqngGqXqs*)2W +@moQpDv&}UI03*vz~rax>kRVl_$r!sW+by*9@9PJ$+emFS4@Z%K7A=E~F2?KrRMUjnOzKlc&8bWo8FY +?*T3f@a8$~@%w-JchWWD9l%#~jHu0)Wgl$ie%n>O2LiN#($uvN9VeGn8enCaAtaS}rCa8F2qg@OaXO1 +ze%BV?aS&8w9Q73BLu)RZD!voFK$X+QB73vJ6%YIG|L3OEdyB9I5M~T&mT$vB4Gq{Y=`T-wE8^22{** +4t@mi*C639CMyyvFUq0`jqh+Hun&->(mNZ5sjvpjC9eyVnO7d^8NSwUJS$Ww&;x5dK!&Mg7y6~ikjEK +H!7`xQbZk}FVkbX9N-36D<=x=l0!zM{5M<{q6lxKn36ti*Ey>P?(JMy75M(R48iK5$LPjc~ylg}eT; +#hKdAT;J)X^0x{}d2*omAM>6Qk|~mfSCLr;=tvER@t*q(MsmIMUR{MmIFL`)TVbFf@Y*SkER~YiNfo^ +cGtS$!e&%YO)4=JfFf6740rNR>L)z0c2M(O1?m#iPHIirJi3 +wAhT@6v`I}0Niq!OK{9zOHEZ#xT|8v*V9wgiK>em+lWmh+mKOTOIe$o`g?0@AA2_g_Bk?`<-xRQ^Zj1 +6!^UUDNh`1EVOI~Q3_Wpw@O)77tPSrYE6dBYqfQO7+B)($vicODOX|ahP$zQA34Y29oJ^*vOq(VNY6Hg^4pp +d=LUwYc*d3*~SuIZO|vf)k3$)6AdglpB4Rsy;~iFd0Z*sD|>Gh%y8)$L+>e{OHIZe7i#^6gyT%~S9{2XRoF0B%+Nbe^qH3;DcL8{#nEh&WeZ4LS5d(X)wNL +pT;>_zml{26MZ@-8?l@fKB1FX3A9?8s|hnV?*xiy*gZ1>OAr-0!#0`Tl_EDL&j53`zvwvJ=1puSOHg} +fX4I_bS(l-8@}+D3t>5nJ0CpNg%0J%uV^qU`A&9~#SX&S(6t_WIR?K}q(cch6$c3m=a6iKuqDVu6{gSikbN2`8D?JCJ07a +eB#8Q4o(`X^WISN8{a^B#_;S`$@C~RuN79e +ouT9L~mRZ>ksqw9)Fn7t5r<3t~2OPD|3_OsPKc5Og@9^3d@*Q8ioCG<(^bwC5kd4q3b!osRqA +=}U$t}1YT9mHRQdwq@Q1M7pAW~b`7@fLe5>8!9v#ti_#s%sjXqd^)iI*T7Yf+_#^gT#d1?BZIo?#YC& +Mm0`4vT?}d6Uj~mI}P#6Q}C3Qm76*o5UFlt3)-ZVUP|Q>QT8hMA8(Cw+3fwxl6D?*i_wzGG{GXkm%5g +Aja|s-qF6;<_>Jsks#6&< +?oqPiF)@irBP#@Kz6T{G$a}tW_<6Ua;~j|fQTo-3Yd_Hfb$McQ6uX%h2vOtgCcUW$2Dm6BQIeh5M}v*n)-L=1>ezNhCyblLHY=<#eKXZSs!X$?#3y8wRI+jmBc#A~xI(!&? +Qo9wF+pV;b?=3_dfN!F`1GL_jG|;Y?kG!@C`LF_m$D<};ED-kL-;J>Ne9 +4?R(*?KBMGF}2Mlyb18)D^q8gjW+qZn)1XgnklJXqy^@_C5rKA4aYx$kuj*t`{8{s!?20ztz0UNCV5lSjhxga +?@bvDt^5(dbgf>?EXnL-wGRWy}mogE#$d+vc5l)I`EOLNDzR!+dCPkposSa-C$Jw;+`w*;}4w+aP~M* +9NvTjstVu$^u%B#JT9qr}SON>={ciZeKCz{UfbWvmkO_HiMAA+9raJU_!6O;tqWW>2KHtQHzQwr)Y~= +gQ_p;-|U;RU5W-gAX4)`+5LQz`F)C3=i!t?>*={j!m+@4vpYs7-ZaCcNfrcyk-+oUw+0r@Q``;M13PW +l!d4`TMO?&cD>nM&o92nij`F;cSYTIb4=m9PK@e`>-t|TrzX|yDqBo1zNxm9iBltcns^?3 +jNkJP?zoQ!Q*pOZzuojt;XZr51eW6`TOTDH;ldG%tiAlXW$I(ZLx~aXtI_w*!uiC>7N>6KQzMvnAAII +NDxb;@;n|bRbZsS&e>2kXUyq?`1fVuNRs{j0L{1^K*u6Art8DB(vy(78J?s*aQqqVg|K?V^NTU +$EJvuP5{I^tDh{pZr3Bl-?U8se`SmIobDIZJn +!d8TDx4v^c~3uiQg;`vnxXTvDM`g_2E4Q!vFx|$Km5#XT)+Lr0RYQsvOyh8E4$}})7oWbP ++%0OCQOjamqub)#gok)v)A98*zn*t?Fi^KcZFq-A-=AQBTjl~72fA->fY*+lf*L|Xszgt5Lp9H0)nOa +h@Tk4E0+|&&rIH19Ug^VBgr(P)YgnpI_L~4#*n4B_T;RlX#_29D_6k)vk3dv~tck3#grzp5Y8(2|NY- +sgC>)p@mO0+HPF5rROA-nLwjBGT#zPTfi!Hf>zu=N~Z-~%Mu%dN +6#gF|@4%)gWUY>nwkuW`b%8B&umD9GH0dxP3sgp;Z8j3AkyK)1Sf738@M2w^O>k3*{LcNt!{_Aqhhvh +-oQu_r)Mk174p$Bjp2Lrv%4)0lYHi5mG$yZ4UcEksKR=P*xu9EeMZfVXLEJ~4F`-#bj3j2w$mQ0or65 +;wX&$L!B8N(9T8*4%(jVg-)Q`Etn=x`S4gk~CWa9xV~MRI((AGm``?#7?YIv#pp8h}!TX>~oPmKqRKl*3<<8nWZT$iZms2aub2EOjU}R=oY +siEF|SREjTRGVj|<&;Q@h%FNu)AQ*)sW6&ahzX-9*YK=2VFJq13%w^o9j(Lyt~>R05=$t^l8`ZGF;P; +gdIt;ynn7BxD2BJaMj%mnE+G4SPj%{6gYcnQH|j*R(aBWzQl`=!uHi{8Dukm4TL|97KmG@nN^Qqb})r +(`oF+ZowFjLCM|!Tb-<_dOVN(1v{If}f3mpY<5N2S^|Dz6Y6)U>c!4^oJ2}Pp=P8Gwh+E9)@OMk#tZ} +4MD)IgW+O`fjsG8Y$A@}-1-jtPY5|Cbx4tkX2o)f83GlL{f}*SS=Pxkd=??EXe+anOD&(tkbrkv+i+& +fT4b}YuGkd&tLDn~)2|IbMA!xlS|W+ASjLwecz8{cJ&Hi}wJOgk-zmP91s;!}c9HcE_<$vKVW12w_SN +k8)omBVplnCzJon&luloql3ZWbt?bTKox>+a%r5)AcUdTs59;o6JdiIRG&=GlInZ{43=>^>&uBGXSme +3!VGDDx`9C}I@Dy!gtNLJD9z>l!+?ZnJUn)Wf~P-`n3Ugu?1*x<9=+Z@<58^LN>#eJ`@^-d>v?<>VrY +hMlbdAEhP_{6p*hntp3KZo`Nj|(UYp1UJgaV-TJ;(ln(1l6B{Ra2{I9EU0lkF(A?b61p(CX{0rug37| +34BPGUc7E(bvv&R2re4G(I0yIeOr|DX)^Lcz;DF6w!o^ay9q89Vi|B<;?9Vy}aY;$ +PNk~Tb&PD4T=9`vg_Z-Izx2IUaC|CreO3~>H76d0o1oIRv`eh#UcsaK~YzQy0b2vqyNH{HqzVK8bav) +_*H8ns6^T`yNbjV`s*{E%JcPnlDFy=MjmUIaczZLK0C}_zrxIFEoQ53we%d7U#Fh +T_9(#Yy`KatmGNar^?N2m@mq)z&L!@vlmUbcqm8c<;=@k^6kHL;*V>{%0b7%MppEfk(Pxa8>pIrI}WXHcO#g%nFog@men>RJ(u}~6l^e?p9jKbT*BkV>rO) +f<*Z@?uetRLZQvzT%o0BF@uqRH`l|czthf-N;8~)c36mShsQv3$IgGa`vYEDZLt?p-q%%!?+A +SfH#ktfXS-3`R>N__2H1=3qJ8`FF7@4wZ8BCFvatVQM}o^`yKjx0zF!0mOdUrA=V*(=!#GS9O#tI~*U +7C`E-u>Od4Olf^XO@@wZJP?X7IWN+XY51r-tHK6nsA-7exfo!RK>3giU%0+~_xSd81jFlItqBQ$QaYc +dBKV=dHP+Gxi@I{0mS^0|XQR000O8NLB_@*aqv(C?x;@=bQil9{>OVaA|NaUv_0~WN&gWWNCABY-wUI +ZDn*}WMOn+E^vA6J^gpvHj=;hz5Wke`|?Pol47fA_tv}KT{lr$UmYjUcG`3um!U=2=9(f^lCtA%^S|H +B;DZ1NP_o;*-TTmMV~GR?gTY`hGnfH3gVEzrFe}rnxZDYr&Bf^7@yVy3Y{GxRvvP4)WtUe?5Iq|Nldb +JZaB{_h*LTfTSp>)BMRS`}EO=2ai!^DnvKV8nW5$Am{b$p|!Ts>frVCbq)1D{p5JLSd>*$2T7g>bFIoefrrudc +xxDJkGJuk3I6l>lgW6S(7DZ;tF%gPi(oR|8jJ~(Pd~Y+%6SmS7t3Z@u{aL0If4(CMFs?7ah^4-O7a?; +7O(L6tQIxdJjrrdY0l~zu#NFee39i$)%kl}7V1Y{US0y})$fWSbOCTIfj!UaCeO~NA7*TUWEujNH>|4 +RYaGkMvvR&j8d>GIsQ~W&t6@;nx45}muzDCY>_bD}Wwm)yT>~gtFDsyvi;6Y!{jzC01$C=r*2L#`@in +^}2A8af>6s+KSYRb9V1ase1p_?9VSD;?EB=d(q4>0_$||zSGuZj`lK?(2sCz)spO#t0(w0H8I+*k7H0oX8&&uK=&t}bP=uN}sizaAFXeLPmV +Ak6TxI|;djOA$!JPjj*Q)MPVFOz)iY5h%6u~~UpWdC64cbuFn5qVQw7bS4BYFCa!v7Dc?%GcC>ag*d( +x&{?nz)8MjNWzTQ6_7WB#qu0F7VN#==aU$zT&7E8{5XaQ3iD|iPc +R*T%!wN7EP6qoe6_$ui*?Z)sX^c@->c7*>=JC^Del2ZWavW_zAFO$dH3#@a9|aOKYdjKsxW!F9lzM09y~vWa*-nbNbnWX`w%7>{?#76&XWeEXgK5?p +nbyQRFW$ueeHn%kFN=Dy^~TH|3m-79bDt$KDd;dDVU!k>GTJILKzKs98uNApU&&>2KZ{ +F?OT2)IX_dI +CEL1wJS-6R2o__JaZ*X2FupGkwd_>w`vW7N%-O6hY=LqGlXH5UjW{s>Mhf0)h$IA;q72pb#QD^!AmiZ +#dwb9d=WL0Ah;0`vi`{-(aXj0h$#YJ|VHoON&rBiQcW8+}imFNw)3WAmrXriD{ZEkOUxdRpz=vf(<4w^J3d!-fBK{=Z(QOzuiO +VCrYf>e{MR^=QA`^79@rVO9Xp#Z%Fd~y~D3kDRMyu3w)2J|xg0tFFFgt`>ykpnX0o@Wh|z(BUw +C_mkTg7Ft$YBNutho1pGwK*~ZwHo^0j84G%aFNZ;J@zuyu3vlvv3Y$2{VnqHdj|(Cx;1N}f$?y>TmTh +Wq^VAx0-$M8n4xo +@Qq*8VfNb7Mz_DK%>8Hp!S>(4Hf4Y5la>sGka4jI^o%c=-8BJp=D@6_9N)oW?ztv7MjQzUiN16Gfq;^ +)XEVCaVf4ZvoQ+>}`wBq>=A)a?N6%Nl!{O#;Y2{?-QOzkG|i;wf+ofVwy945fOP>gsG;Y^_&a+!a +Z|PB3;wQ3vYM^759J7I!+g%_MvisFZBfmvdk)iR){uO_f(o^iEm%QxD!KF!RxH|E+>}HB)WB6z$J~Pu +U9-D-7zlTQPJ5&je~e@i1$NB&4}@upAf%nHd=TV0sg~U_dXeNcxlZsI)<?uFKC)1BdIpU2=J4CYSKl83PaT#88(3(8Ip1W3jt{myP8LW&q{u+xv?yUQKF^sP4MLEn)sWAUA!m +B}BUpG#a`KskvI(eHFj61@!#ZfHI~3w1l7Wc@SA$K89JolT1}$;GT6)z)(*{)>^afb?Lb9=DL()=&IO +B^>k1X1B-j6FbFK<{x5C&#p?gtvD?0k6{f$YbEDcIXvK?%KKI;y(4Eu)WK|k3+8r +rvFsE)-8!SDFK|;L_?@=$`r&SAaLsPMT5=&uEP(p&()P513c16(F4k8KMPuDIy~<(7Web2pjdWbZ3)z +lBd|6~a0ioqGS@RwWs@3s!ye(ae&Y>sH=&skge`ufqn*_T(qR9)a^&_;vCLA;Gyz~LnG(x#MH{1yVXzTz40h-}X{YoBm +$%k3{`8ZN0F}N^5H><3{@w|arm3hJei=$#7;7sWq{9D^904#^&-b70olJGVVaI3F^m0mmBi$)|ndGq>Us?$wxpL>0 +#2h*3+!;@pli;?D@IU*79$1nB|rpJ3PEZ0qV`s3c{Z^` +H%TcbaW&ptCeG`Mw68{i=AlnIXyhEii1BD+&;0Y2U(6IiqEkGKH9iJoQ{1OO77wuEt7(IO!CJ*CLlzc +3)uS^&oOP|A`dkgrD2?_|MuU+1k$ATt3NlKBNp&GQmkCD16srA~+~~0UE&&0jFNnDl@!Z4t3YH!t +#BObfM!<-1|eX~0`8~_36hF+Ex>(!SdYR{I1YlnG|jNn3GV4aJ35?t0o7{hAeG4QXo=vQ!5A^KX+nHA +P-8i+m*)|*Go&|~-ltqVuCkKoQu2iXGSWC43{1KGs2+h<89WNiK{uiy4%lTfyOX*{#5qo50Swm3sZ6V +8mgp&{Sk97~sgYNaG)j%Kf@f$cFH#jm6D7;09HrzyV(DNUpr$iR#PSacev=eUmNY_zA#7=YT6Y9!O-X +gR#MqxYh{*HYI#@=*W6+ka9uKKLekHX;53IZ@WrmPM3OEJF{82^8g}8g%mN`Kur;XVgvZr{^tEpafD>IGE6#mMrrI*Iqd)BnRTG$ +eK8gYL;INi4wc$i5do?)8dx^%YQk1>m145P&5--UawOGtY9i|XCSJUp!$;UEBI3|vGpHPe$l-y6kY=i +^-uN}=PLU!%a=8bk#1XJ$HuX6F~WNO(Rw8Ucxn)#{@ +_B1t1Gs3A^9rR?e&^{E4qs9?j9WLg17>XI!(xI2=fmrMq3D85oy}7rHbQk~bzUSp#>?7|@L& +Lz8v)N*9n)osjz&21?x97w4ea#uyHleGqtc7(Tn;gJR1Kx#_01G3*@@NhC+;8rO;5;U>GBIf`9Dm0bg +n8;B(m&>r-%t-&m69NkSLgi~u?@tL#NaP^uw=Fg^71zxKFN%aD4YC_dmz7|!v03N@K$Oh0~4g(ZW#v` +M@SRUgSwb?<7W^!Sf2l#oAiGZK|3kCK5CW1bcP7{Zy%wu)VhMeZgNIq-(WAr<#jpfB2WN2L5J8Z|Cls +F7xZB;|iz6?~?7R;JJ>G&)RMCTv_vR-r_+-55=^fpO#X_hLprkf8bX-VcVUMv7h9 +sxfFjx8by9z+2e%1p%R?uxM6-et_zk``L&i;N67tH#A*Jj=_PnaWKILRU2&(qXRpn_ZsQm(b@Id;y(7!YM}CB>+59<<=tHR~XuJAuf%z@ho|=}xn!JhX#;kLv~KZBaM`yPR +mM!UA~lvo0*93=&>qIjSwx9Sa*LlyF-hX*`q>EJM&8&9|WoZI4?*&^D*k0X&3oc3@!gBL=>I@#UA&rO +rfZ1BH;3=^cj58&Ub%2I>${)IlXTNtP$)xz=9-(T@Kjxx%x@N;n?QDR)C@N+(Xe0`f#m$fAtj&-4}D| +09<&?^jVfU(cG{r;Zdy{E~>ff?q_R}8e3tfUYN@&Ug`cd+ohz28CeM?&EH?eE{?e(C%7h`?2uYTj2Ub +w`fRye!ca0%Nr@F?y^q>3#{m`*FU|gai<`T& +YNsV`lY{)6U74lZLBq&=RhYcVfU3JxVqOa}_!_HpWAHjdnO{6&h~tNG#I^Z)Ks6s}QKeNWus +tm+~Sa42XUf$W8tfX-aAn^qqHCxV@O=zgoa>S^isK1#~n-1PEp_vP4JHP|PLvlS+-TOyrVAoL++QPp{ +AVJtG7adaMBVK)kU@!Y4ovL@`6Uv9f;9!pYq4LIozl@cgPIbEY7*%Vxsy{cLfgV^QRGnJH%)E-Pw7dV +uU^_KioF@U5(wFbjr3gj&FmM}ZFp4o>b?(G##;1eDtS>CG66E+Qd+m0Z<~5liv+tTqbbnZ>1%%e&a=` +9G!a_~rh+GlpDf^;tbFQ0%2I$|{n1<=^ie9qu1~ZR4=up#r)CUOJ!b^-gUewMp%fwzt&P>=dm#ktVHK +)^DDgY~WEVa7UcYu3JM)EPuG}kax?gE@8_Yd7xQLWn)?mSQXt!ru$J_}K` +-oZ7+d?ND}}yWqHfOfLo-E3$+1Q3KJ^*?{L{d9nZY<&_E!mXghzn`#cO6+UuN? +*1ca4vS7}XFG!W@~Jszyyot@jp#l;&jfR90rJOov6SYx<`RVkX2Nl*aTVaoQH$rz}bVC*}c +{nLi>t!xRKf~AUP?dkTJ?UxXgsaUPgA#;TK{3G`mr2FeZI{RqqURzL)IItRTr#dMDVzOh4v&QL1dq(; +~c47J#GmMIwL(@sojM2*F5b!x|n~|2z_d9h*ZczrPFQHofoeby$&11piHq(dr;5LJ|Z{G&>6$WxE<%Y +GfX^}vke0{eVgI*@kltwLazRxfaiv?%?p`|j)XkqRlvX2KCP$rN%KIr^Tp8C=mZi1l+Imh@4ge0qC4S +PWmbSq#G?wyrz48m)4|AFFwRqgCOSURmQJp-3>>z=Z$K2G*pm +trD9gzQl;#=YU3K}4wHmbv`+ytT9)V&xbUQTQzDu#d6ouCxP8=LgJ&j_y~xHJz3E{%@nSn>n+NH$27G +WB@L6hU5|fqdn)Tx>3}zZien257yJaB3t2Jui7d-`@XIJy2fR!u4{aigVw1C;m25LF5CS$3c_^qgnQ! +;f3&03uUkt-mveFBdpux507kUAvi+#uz!*HkZEG<6stJS>qAAg#DwiqT{t(6cVpPQLS0cl64VUH%EuC +MT+{G1Bkc=QBmNnluS{9hJIV&qRqHwNRf_sI{oxTZ)OE(?nb`f0NEnv|b&9e^xhRR0Sn2m!g5P>m@K) +tY%iv!ISN;x;=-whU+HF~cE8lOvb-K{JcQhT3mEJJ#sz~vFS4p|UOt;wg+lcdsyH|W&tu?X;kPGO3nm ++3z4*hZ~Mnb0e(xMh|5wu`WHa+vqBZIRS~CXpPnIhM`*e@Hx2HHepc +!L_XGOSJyknUo+b>-g1i5Hp!nX70ytO)VhrrPaCKNltp4dy9ngP&rnqk|D`H9|iQ}<69h5gaCfCNw#h +Qwmi8L>#CwtHLPWC8M=Wy>OW@v?rI}C>$<4LXIr=NWP&2)MYI}55qmqgKn>DPPDesGtS4@1{UoZZ|V{ +^dls(2Ud_v9y@S=fXNyQ?5?*AIGl_g|eXP)7g|Nr +s;_>M0v3xlA%fE#vK*#$ERu=>3A+=ca_|40gdq+QbS|0zrJ)CSk{r$%lPrI8kYhAwodU`lL+BH%`QalEg9w|Dp)i~kURJN@CfO{c|Ai0lvQS){AEp$sXspVuOP5uS%UJ1iW^0&1z>KYTuYE1 +Qa_0p(WB`Jr=x0A!w$K?Y$Ey5?fYU&j2P}ZKsM1MYgJq5%MpZ(CH& +JQTY3`4n~0?T6Niu!fF%<{DT>19WTobE+>{zv?WtO`8in?F3c0!%J4MD +rw3}R#Z!|qsB*@d`>%1onE5+{)nJ<+P=^4BDzXCWXdplH|mDx%8z@Bb?An==or_=ny~N$DtcyS3#n&v +imCSC4Fj)brD`=+_($_W{L>X=rCEW`We1N#-Dz+B5ciIVA_u-fjP$Sn +cz+(cmP(wv~;B?xhNW6Wjm$Mo9u<1ey#*;e&F(9Z)F>GLnuWpmV3xueCFlrN+_P)p(!)d4Ext;0gfwg0;8kN^ +IZaFI5|71SoL=8(sC%RY}sK?PF4Gk8@~hm{iAoMsTO5u5m&=)g`@*q0RaV8A__J5Z9Fnzs|uc9n|>s4 +qjN~j-@j;{H>7!EMFt__1DM_FgN_~TO)xmHjc>YU|W~VYR0%n5RVBq&%-9w%_f(vUb{{e*DHQ(RnlDR +af`h(HhrH9RU+w=XEy_?c0)FK ++|2+MXCkF;eJCZ^cso04Q`*j)U0m(Z<+EUHKthT{WvqyZ)BbPNONz`YiW$eoZ2Hde+M+ZC`rxV%%uUI +j|aSoI>lM?qZI6H}+paqwGRf!8?TngZH$f;JYHuG@-&5hEAA+sNBCSm}kxs$~W$biKenLyjkTYZx=$-=!E&39O9MC9=@KnfvTB3?eucq^Jl`Chzvw$Q4+`Ts?{u +pG#*N5ZdS!h)=Ok&e`r?k<&!Fz{dvm;vO&lV#7D43lSdW2C7|FvShP@DM<|yNKa{%3H3TgRFm22qS@IjZlJfb8W9M9oxC1zpGdJbeiD(3AUwPd>%L2GY9=q&Wki-pj +yVhk+JtXNtvS%@(4Jb~vQOjc^;IjNqALsUEj|*r8(kl=*L8Qtrg;x=B+s~Y#R;NYPHTp{BQar&r!LVm +F`G+D?LhH64nBe&qQH=$993<)*uN5AWmJsT`@rYL>+2>Y-`$-@c6jG_v2$W&9*Ky{U2*RR@_|6R47Ad +>IKe(6Tv=b=R>-1BWvW|B~Wdi6?nilXp +b`{5cfgDgG$aB9VN%F?NJ++eQZa9!r?G`RJR*RoEVnE;2#&6(_k)y<`O8__2KRTTs@JC)=?@fC8JGYV +`&%0m4XQ?4E!kr(jgR9{+|fJ&(uLy>odD6ebw-)^F& +`^Xxr2DWk7S(~Bu8eYl|=SHAuUKL09mx`Q_5F~$ToD^n} +Kd`VCV~m-KC_iVQDY}cw~B?zLa$EwBqL;8aKCIyqp#%m(Nq1Uv(l=nN7+JYkAc;5LO+NwJQ9lZg1jAd +oN=T42Q5QU76Yg%-tgI+H#Znyrji-V7XK8W!zBp>+U-~q^AY>(gDfSYfbcD-|~liDbjA8H&ik@>I-{u +8isaCIYBTFji>fn$0fFVeXF&}gQB8HY1wfqZiq61qg)|mQCMT>Q{1U(1xXDo`CNjOEv`NH14H*pIfOJ +jkV@sX-O~;+Pk#=zm?%-{qk|NNYlnDSKG9|>&wViOFqx~nqpTNBX&@7b_Fli{w@5j%Jj6)_+KYBVxA% +#)udRKCuH#-!sQ~~ukkdXoyrzdeUZXQVGncC7iS)2krxpPVbx1Cc&22Jt +A=OVy|BTi&9uLU?cv(8*paDo84z{%#AGbCO8h~qp_AWrEet>?$Q~nG3lhKP#G70yVo{I)337+fOC=Cx +xA{&R>l<)S4zXn;V4(!Xr=mH43!AvWV&VFEr!_9$plp1??l4Y6PuSSDdE@~Zvg}uCutg058D?}9+l9a +6GSeA6^NLy1f_R_jYstcs>Jl5zk%Xrm3L-MFOG={?hX(+#kFP7j=TnRe!K;WzE9~pOug|N8xVB}AYkjvEOF>`bXOt(Sck+K!798<*%so&3r2$DXYEY?t5n)sktxsU>m9);dLVQ1|0cuLL53M41}i +LL{U+A*`EbSSxo72zKBvS+ZD9bWyRv8o@E +(v@DM~Y?wRdQdObrp1u3d+*Ws}8piW7H`ehc{MHVtat}h4D?7_ovcjDj5frp*avPLYzxC7aM+3%>*7X +ojvhhe8X_HUB>Ho5EGrimZ20ASIeEi@gDZ&TT^f6VLi?PX&-mtJ^ARpCU1++`fgEuKclYx-jx73iM>- +6Nn=-OdHIX*~x03Y5kO>_ZN1Jyx$UM1xyeyqd9f8T>+TJfm-7uUFKTKB#X6$a+p*(GPYl#F>s^RFwSu +Tg_}~FF4l2f2cT=mfncYI@%uTLZL+c=en%62k@h`9Nn?0&YV|aN&g8@O9KQH0000807zB_Qxb&k&&Ue +^0IDkh03ZMW0B~t=FJE?LZe(wAFJx(RbZlv2FLGsbZ*_8GWpgfYd97OiZ`-;N|2;td2d=?`B#8%`r8M_vOy?guW_517Bas5y;o~P{9hxea8zJ2-m(}$1O +_%J31S5>iPNwV72yGkSpliRYWYG^`2heVReT2wq!rzfW;=G8(fv8QqA5ztsT)ci*iV_21-=k$Kv*gQ-NRzTEN>SDO30sRgp@+$WD=}fO@H^ +T@Zy}6V*6kQ1-_f%uNpil0XpB+r^n}zw@O_aB2A?ywtOT!NtnN1=XSoomNR4R5m0}--Dt6UUP+JlGKc +1eRpl&Kw5N?^vI!PiWveks4Usq_`%HT(?=sA;$nswf24?e@Y9N=&#m;w;wYs3w|D`W~PY9|x}p!^U&O +qmi5n9cxsKVHPXEo$Zp1}iRky@_uMnMZ!mqVWV9iAI&_*Ezi#jmNDewS#y^UdKTR`Ztc7?xHOUsSSiw +iK=-fOdzcJ2}4iZXgquNto`v;QG)1EA$_)j48Uh=h)3So`I~JEKhu&|eA~R6vQHpCrSY16v3`GH3xTo +JD2hAU=$F>SZ>(5RtJWrydFz?0TnWgo92Bdaz>#g;hvB6j!~=aj(|85TcRZ6O|3Qhf4X;uLFkfV1%ck +efQ0UgEF*n#Sra_zsBm7SRCByBOsY&GXG@h#VkI9mWB|riH(p7sl5 +vYAII`+QvYxfyyqc!8Eqez7m-ll=N~5V;<|;Ltk}cNnIqVtRnquY#{I3gZC7c)}VP3>6QX<= +ty4FjjU8?-)M{jSMdXaTYf7*LN#IsEs6&yz{)n>V^JsLm4Tl7rYBmi0tKe220>q29`+eW)c(sf#_^Dd+^!511QwHLCyw@Bm(LtsGXEH`LoNFdIu~cgu6|MtK7t2f +YEPA$j65r*ORih9g=Oy@U<{=Smegu>}aqg#;e7cZ(@UtO5zx(y$yXq#gh%C|Wai>!=A1#f3A=UJ$RCq +D(>(F79Np!*1~!e``FUxm?^ElTLL4gtJ<12g{~`qXQc%m2Ri3e}4}+RT~8y*aUO56NPnFEO@39%`3Id +wAWKCZBmAUX?D#kk0!R39%SwI~#~N;Bd%2>;V{aY?fF&Yv6j+B{x0pOSFo8l}Cv+w +TRjk3Cl``dtSQ|nJR|BetP~jDXEE> +h-emR0t4u1>!OTLc`zJeL*7#kj!4eIo^NOxJcLhHw1N`DF7W(C*v8^Xbq)0>^D+l21&aDxon3e<{>q|H3-*ut{qTC!uCgcH{p@OfCTu(Lm2Olc{FMUUo~3 +f4BbHpE$6)9u_YWME_}xvXykb^(Y0Yb)rQoj*kvg+1hdkH0|0Uswx%OqLaY~~ +Ka39YK`rJoN8T){ +853f0lJ%n>B5Nn$4PBM){gCH{GU-z(uo-gkK0w-E6PWHpDdeKD(4-Pa|C$a@)8Zr-RUs3IQZF+XQrN6 +J6M-l0c%_B@{o!G?AU)^-rsynz2NjNyLmO;1U3Pq<{j5=s(r`&#f9v|;m>!anygB6yjH0m_iOvQ)&H$ +VpsQt%q@*z~8ze#1l^v#humX|(F%jarRX>~~jKs9~z1EJ{YSe+P&iT6%~))pH@Cusd~)`-(vw%~pB}# +&tIIHG_11b6H5Jlwxo4q$hPPxvX)A5l*R(;DV5x!ejU{b}SaZ+&lYs}fWmD;5nIziKJYp;FOq +M`(WD!(8hkEx_E&iq8GlurSF43H7J0bq6KlvM8gAaaV3H1x!s=y8GVoG%mc-gsxTrpk1=Bup$F0s@vC(W5c?F%jYm!#e;n ++M4$gtUB%YeRJu}k)Q2Hzqh>X+rtV!3q==poTJ!8 +UPM9ToK`RoJ7`5Hb-H_w*&;+^ZTVAKEf_t<;Kl8fv;`()J$wpxK&+}=IRy4oM +&aIN-C&fTSMk10WiVeBnRzpkpHuk192X`ib@gZ1k`PoIv(saWjR(TIkTtvEzJowDyL_&3r$n|QD_yV( +Z^pwIGsQQbb#@>i&&uUneoqyqq*`itb{uQ&{?fv14vR~1cyv{7Q(pA;6Z +$CdYy@PRI2CBCX9{6pfajyp&>PWmq+R?(#s!zdejprr0Q{RQjzI?nW#igSSIX6t?v~H`fW3;gaw5LkU +l=Zrojj8N@TgN@eDWN8qJQogfp!%o;T-}TFJ+#Jrnxn*S2ZXqB(c$S;7$FoeK=kT81;DW>zoF#SVI?? +<2T(5dvv@ZJ~S3!5%`|edgv)if^Gkf?U}*$ZWmt`g;{Rt3834(KIzJAX$!OoEDHb@0B@u2zhg2pbjf8 +8_rgsIjnH>gQlAb*?VSzZ~9Iw8cN@P;etaw?OgVXM*qkwCBHxdQr~S0Ze9rlTHkkCC1`qJSr`dO`JGc$9+;+Rm?ta{9rbQEqw$+_u(%p8uPZ|i(CBO +Jz^z#$OfG(6JMl3N$QEq-G;C<4bEZ=1QY-O00;m`Rt8hkHF`V96aWARMF0RE0001RX>c!Jc4cm4 +Z*nhWX>)XJX<{#PV{&P5baO6nd9_^ca@$6d|L!Wk!W4 +H!ic)>!LGL=Oq~0`gf8om$0n*cP8qu1+TvfS(K4%er{ouCLKiCIu9jlZUNVecsXEmE_o!UJXMk#uz!v7 +vpC_^g9R5^Swvh^&n{wIcXhjzaZ=s!@wv;K7a@pZz?K}y-s2shE{p8|C3VN)CvdVFJbl92AN356N*T@ +)K45V=z2oU!9~Xi`0D+GyX#_C%oUZxPX&SEibV}oGMOOViYWa+ +!mxBczbfK8Fo`7>_VrWJFfen@?|8xz{CC_7#U#DUZh6sTWqcCh`p3ORmPD5 +ke*zl_z(wNRCo?%@Nx=Ex2a0VG7Xq9wPC47eas}652$(eZ1ZwQ*le5XY>znEM8~-EwBtHk=^ndYjCA +T{|Z$hVt+{}h0KBKreys)6}TzoEkKW9c9Ii!zP+!tcW!azOku=L>MUzm=KfhNt_@*S_Wkxb@&qoU-QL +aT7ESz{PGk6W|wY!x<3-y(P-vn@5m96biV9^WK>R77(!ceYnnH)Tk82Xcng<7{hv_d0Z%!i#!BFq&r_ +ol|7R}oPsrmbEMs~C_I)x2FwegIE +XbsBb^mh}(P`cdRs%<~56%UW)-@!kpea6bxgP#}1kAvS%>C!O@RfJmKWOWA19==Bj$4;1Ez#>oP`!f8 +jhQ`^l0yMOhXWxZyJ&Z6(b%{Q(OFjsUBJSO7a}!b6A=uw7ux{sMuTl>+>h#qt?!MQuw*g|sf>>`?-di +d~bdy#>=Q{A`BwszvsC+Hy{q8VU#{0#w6*v#8)9n7{HV@Rb1r#p32q-^2NQnuHKMK+a-(QA_Od!xZ +m$C#Y%M}TG_YO(Mq}FI%uAMuqt%3o5Qzlal%mER_TgGJ%+rBrQR^+e97Ys?Qt9fr>T4Jm3VwzmAQ71Wh0T@m|M8`*9yLKtNJ>|KJ +(n#r^{`LORs3cM)L7a-NzB!VPDUcZ@bfy2wFe;oN`OCksA!=DA9Wcn%5L<@IS%U~V?Kq%U0?s7kW}$a +YiM$9(TBj1!d)BWLVTc$RG21T;@NsyRwp#P?H$gZ8`+)B_Qc- +hYyebowS;O~+9`T`O3fN@@^9S@aiAk(9l(g1PA)u-$-Em`6uGm(L0+fRlsaYr_p@YKa$asu%>Ee`5Yz +d7G6fiXimaz(Kh(@uV!fsY!dP~6a&tVaQd@r(MzAQK`jbZW|r+JAbtA+~JxWLo1W44kqKOT)Xn@td>! +!lV1b3Xcybtov$9w89ON-@&w7#$=$UCLFLGaw}aq*lvdo&6RAY7MetInv*U5tfs}WP~tbB;$Ov4x`l$ +JHh=_Z~4z^jN*8QQe|b3u#F+&+?$vju#@?my&|z-w1usSLu6Cw65mk8K$Hu( +-=l!(Q{u^XdAnIJ%%k+z +_WrtX#)nHKc_`ok&|(dX34)GsNW@dA4#Wov^k%quQ1`3pDhg2@aj1Zr!4qf^Y*}JAeQwO7L8&yKTVt* +$jR_QiLU)0!uoko^-XKu5AYZ78GbzJC5I~m6+Vwm1n>2P}YTL{~;vWqH96ohBurJult@((7k?lMZErK +0Iqgcn~?fCg$V0=ryowO)Qar)_wd~2YWL*7OfKF`-gPad=3epd@22tV>AUO6<;8>Y*vp8LLf8)m+x=Pr#dYl1yV?)J7a +{Yf?p6eaF*&hvGkQu!j6b2oWc6o3UJPhqtj+(7`IEg0pjWaP;!qfSn|;+H$#K$OXcVdI +jkBOP<0ENwTE{4gs9;RKybS(!eALKxm8OQ%?d!*@U1PcGlE<2Y|DXe1-6k^^pDVfBz@*YcLp^^v+LiC +Kv4W$<^fM +=@{XFUl9Ym1M;)2Nw1cXV0gB37s6giJ1{L1L68V+Q<agJemo +MEiBHY6@j-Dx~N{8lG2}131>bop-j;hti;21R=pqWbJqQ*2-Yf9EGYe7g$qYHHu3@t(!3ab7VgvQ$BM*sK$fz6ckpCeKr%xhPPl05 +x+=Q!N4dEr=K;v8r}?!w<)2^kfau3@BhrydGaB$?j(!6W1)LgV>2RY_zkSD)ia8;;t;wl%LLd@~}XoR +w0;@BRr_73OHI_bmj}jDC5LZfkZe!Q +x9eFt+Cr&t^$#Oa^JAR4Ybv@Fpif@|umADI=dYuQC+QZk8uC?*CJ-@OjW4EzP(an#r +shVi0fu~Lxbrmd4}anizZ%*t$(n~lB48;7!x-19(en}z8L(Q_S`jmBdQ#_jdITuaWDXp+K545q2>Ue7 +IUs2s#Xu~0u9l{M?B0Z{Oj3JYU1HS_x_M;f3$Ux{5iL?C>+aLW>Ty*SG3&-x=W|pnmg-z?+f^2F1xX% +MUGzQKI_K#w5IsPqbzCGQ?8AUZsunljxlc$@x1jlI@PHv11hu~{Iym@k5s;0+V83o@31HE-VL9{s7U$ +D>C`06%X2BHl4nv>Ik)xk_!Osm2bjwAf!1P#U +c!pnvsrk95f*-g+-xuoh;bvN!tUOo8F#EOu +Vd$ZHWUn&tqiX16HvB#oQuvC_E?dA4JW%j8m|``y7V1SN)#-(jzyf5!u%Bj!T_GLXf@xEk}MOPJTl_IfDh8#n#qF>FN*_yLMNUk?u-^O{4S|^(L19S +1K~N?bhSxjX$z~b{?am?GLY8?Dr$<{gtVUGL$wEy_Wy=;RBiB_~?jBZXEGeX-(dl$5zic~)W=M6)?P^ +YIRceu6$UQdu&r<7gW(|;5X(x`&5?YC4D`m8d6kB9%BW*op@*d8=l$1=nEt{1J+Hl?MVm04z(M+4Z^j +Os2%D>yBsvNaxGqtv`Dw?Mp+k~nmSK--Ff{kv}5kpQW^iVLr%!e`?LVCmdbxzfH0~f{!`)J{WPFz&en +0lE4E16Xxz}%~zC5M+@|Z2C`wxG($*X3f8U09vS#+BQe%7Iku$9DmfhM4-vwXS;Es`q% +-Thy=?bD|7#d8l`8R=bZ1NG!@Zyi(*%Gg21F`7Zpq&r4U89&h^vQ6c3@f{|_Wr4e78#Z6=5se6NZb7> +AUIbOagxq`y+Roi`o*#%nsv7NyK@{TCg#_U-JQ%{AS?d{%cIAgKzP6k4%lf^bEr#_`W!eg$DKFD@j@S_Rqd6tSkB7mmP_i +cyu&x41A%Wsk90F#MR?R|wund%&_VecvZIwtiocBxEeRVa3->(l-Sh}-G)ofR9Xg@#-%cE-YCHLHL +*eXj#uxL19mB}F+)?apPk1-Ch=@qO);+D*$j&M0102;gXFE1J*ro}#`+y?-=WnH@VcA*YCU0SttfOgf +(q*E^xmt+11#JDt!BL=xEOgq}4cfj%x#gAUkLIZNV*om@@WBBP#ZGdsWx +w#o|3*Wq^OtyXg}zNS~V6j+@#$C#V1ZKB7jbHJXbZVb|W+aU~eawJ36{Ql(E?)nGd>In~W)^4H@>tCOu^jNGO +T%#mog3n@!J^E0qaX{T0$Yqqo#sZ&+aY9}QKK_iV^T+P&QRL_0Z>Z=1QY-O00;m`Rt8hBfYt(+xc~qFdI10%0001R +X>c!Jc4cm4Z*nhWX>)XJX<{#QGcqn^cxCLpe|!|x)i^x+E14vl%mN99-w}eML5waaaS3irHbf=37_&= ++1g#L)EwvbC04sr{leL))tJrE=pQp4^v=6O)o`<&b!yn)-i`k&40TI(eHMUgmIy@RmNEQ+@?>Tp7Hwo +H4eZQa2`@a9YXm;-0d+x7u&pr2?d+#|@dH*)X#4wB*{#2D=_A}Z)C-cAm>w&+lX|H85ucWQuyawUXyhk3*E4!yE?>paJwR%owW_rF}#@62*UEk69%bAJ4< +v+h|<`H;)P?1Oz`>77JuVA#ko7REr+6HlhoK#_OrpaJmcY^Hz+n|vl8^j~4Nfmwio2Mo- +M?r%V9$iTFwK{L)V%;bN9dpBnoJx0*ZWrlwYKZjqvk%#BR6MBQNujbJ(dC;mk4XYmHAB1t8t@nKn{QY +S(9MtQaqk%DpZ^4WPtxVQ%C0uAu!@7otAWZuTeP-^3XYSYFoO2phulX(jewL--fR^X~8r&WKxQGA$zy +Fhe)$-Zqvu2iIcC<_{VeD^7u0gSpWdv8yWpk^7H5eG|S5==1Ji>?c;wBFRS&E0VwFI|sL3#;8{2auEo +NjU;0~4)&gPOziI2pNe(Ecv@4os~h5v?ZkKdWhAbWOkXBx)MKnl@rhOroYkIp0u|DN&PL$$IT-uzb+X +9khhzlrZk4q}fJu1Va3zS20RXbFhL@jt46YvWqPU2g?cvmG|Z7%33TzJUiRt_5Bp;47vvGs_y{@6e2Y +aN{v0_5vay~#1ZA54f|dIh^1tjs;YjYR}}`yxkx(%6_e!Z!9u1@fsC% +6nw51&v~jh1{Bu-+wf9Lvocs+)zorIgVv5J-D=yJU&jZOGHHQVdf)u`7dCco$GLRB_-9Qa)l0QkqB1Q +1?l&-QJL&BXwSKR%k4Iq&cl)Mo|xd``5x_quZZWrREfBn6ZkR@@;Dc3lyrrsQGfC=Wa4?P*t0^=C>ky +DMakjoV=FP3ORlADVzec+Fw@ZfyB99H@Uf%qEX`WXUX#x0GPi|J?VzTRiqclB5KZGMzg&F*`Uk|TB47 +9q$Tn+@h4hhua>xmyrv~?qlw?s5)Hl&$2pm3S^f%K!_}NJs0)B|F~@AT*9+w4^{PBHO3krBnj&{eD*z8_z`_TRAyckp#dcO6h`kH#hZn!qHy6t!53)Fa10mpZRX(>B8Qm?=mf#b)+`d%ta4uFb +4$Cgl%jOfLQ004;Ha(SMz$!+KkORT95PWQ+DWBJiw=;4-KY`nqf(aLGdIB`aSYMPPe6A;S>j^w-57kR +4km@Bj0-aU$clnM&O_NkVv#j}ZoNmC)+~nzt6!Z0>W4dq+WDyrmui~B!`It;>)nkyVl><#{H;NG>G|< +oaDl)Ox-%d@e6VvL&C#Fv4a}xyspY+iJ3Tg3HXdL$G8f@XhL<@6i$qMc0b&1Ffs78YpK9^lqsSuW#y5 +;(!SeQC;QG|h2bIdTz_2T^LyxE_pSp3t)khx~5o7`$njA1lzBCe|0KxRsQaIH}pgVnvpeM5E99)#4G@ +BP<#Uj&53_aBY@hWbZF_@(USQ|$_7tYzyr;^~`eK) +HUd^RE&K|O#a-7FJgEY_Y`?cX~5inCtO>hrB>x;JIBS}mDnUx?JN=s9P&*?DE@Xg8sB6${w#82z?8&_ +(BwJDJy-PDiBb3_C}vs~?4mdJ7|&7#3C{s#D9pbPNe_DI|DNveQEanTB>*!rzm*!tc+Y<&?0^zF~m3H +D=%^>fRsyp#yZ=VqkAd>Fs=RPqQ0&Z2OH*maXeP4v;?fUhq;hhw>A>U5#NZ(AN*l;@xBCRR{Z5OoyUU +Y+na_95M7Qwj~5SGx5gf`Ky^X-RG@ste06aWULPy5iS%QIu@6EWZLRr1*F($%5g{uAB#(Y +P9zCRJzr0iJ7h|&w8UJRa!M=F)L{x@PQNk8-^6-I88$jeSiKZqhcAFEL}^&1RI2X5Hh6urI3Q(D6}c% +5u~Aq>u7PGBjr|5t?#RU1BpA;}V%J>-f|b>0vjI|_Q-L;Bsqu_G1YFMX3Dg0+J)3+05<-+256q9OhoC +*AJOL1bl}bh+f_)75Dq5(8kCPuyM2U-qoSEc#niB*)MmYkqVSSN9$RjPu;`~EFSC89=jUj)SMh8vV0n +4`yn*HOe@ZBc4&O}`(_NXKcxv%RC`55v?fS;2GL8PmR$SM^B1Qyu!IC3o{`p4Hvh2$NO!{v4*-`B*TF +k%A{nvA7lqg6@)sx(7CQb|8TF(4DrVqkf%m#eJ`Tkm+A9}6SF?PGCxd+Lj9Jh1KB2;iy^ag|LG0kzsy +9SuSb`OV}cqAF1f=unlrfs`Aq8g4$3K*k~Skw*bCJY;MhjwXutAP!kQ#BeoY?hJJP**xt0*oWS5S8m86R>wZ+T!dcM#1r9{F5*xfLsWh-{n>tuJTgiZxP}T;Y?JnB^=-rmvuc;m69EdDnrURk2n`CD +(#RETzchW`||gEJz$LL#AXpOP-vA<(MJ1&$-qpWQ!GRjZld}NtanD*=fc^km3%R84UqvZLpqti(EFY< +*2qm!}OU*rc6o>MhHl9KOk~T>}3RKv<6iE6A)svgOxp`P(^{hN8Fgr2qgudaQn;-F<#P;DaP;L0Mk2< ++t&kZxd&Hth|a+16dE0ZMNzG^@n!WOB-~!_=;0&Ed?aXYpUF*bxQ$^TtM;A>{Qove&)QlF*&sikjyWx +uvxD0Q|F;)}n;Hk1IzCmdjEfJhy}WX{j%%^l4MWR#gvWuNu&2$~5lZt{5&OhsPft$Jd#XGnS6Tx{_`e +jKgC1pF(W6f2Q75<0@U48f1iAvU +MTM#Hr-u0?=1gY};o=bUir5o8<^d^Pr>-lk6&7$l?K2xW;d-E3&*Y9|$9}ifn^2a5?`q%+yNg8cgeQ` +atnO$*v;bMF4X11Vn(9!6~`CHnN||D`6r>j~?9-^5AlwT?a*^?#JyZCB@O+m@2pL#1y|71ScmpYUhjP +s0RpB{br9e57S6=RD#7naamo&e*9G|TCwcFXvu06!+GK%bMwU};9#4+@DJ4igbg6P@}Y3tBAWwEAkfN +}#LuUNKTna(KtU?cI@)elcp`)~*drN6{E$^BUc`I2g@?v)5I6V(-y-z>e`QUd;KOG6M47Y_1VTP5k;4f& +gugG3Anf1dbo)DRszXDYO&7&;s{MT&z)u;2p_%WN?-~T?88<8ZgmBCTK`R7}O2^9J9qRH(@Q$IC_*U9Z5s_~OxT@o+i+`Wi%H=I{5QAwmISNNMf6#ENcrmD)Xdfr404s^$#;vjc`Mj}0wPmVH2f%__B+$4r-Bt)e}8N`PeVu72)xOch1MUTv>T +?oq77jm^2f29}?5RU`o1r$%lMu09Jog0``BJ;o%JW3k{9&f=__4U)MM&H +}=E=0OhFdZCD{T_A>mmS8-vnA9wt5eVQEZ7kcHek}5|q5GgpMWYBfT(Y3)SM;xxf4a))PaS!>^-9XNu +65W+h8lONWxXR#)7j#|c8dUN)Z +Shb31nriGL1$04q#L#ty?Bh(UWo*XRzw%sq-Vl7&XO*soPoEv30_c+wWB;ibvVB>Er60-U0i-cU^23k +6YA{iF`+TB{=?~Y9lwR}Xk;Ic=^uog{{Q)spwmy$d^na!_a#``H9W> +axli?RG6F@+A8ypCF;wD=y+xP7qFO3+M)=cGBkLuRi`c^2!M;USr|?}bDZ5KY` +6Mwu1R_{x_>u(}#HZ83pQf0s&*8GASYF)-!dqWS{ubdn1f77O*)m;bx~GM^Q%vTd2DqOBI`f#6FD4PSbU&WL)fc%~0R;FXu+|& +2*L5IKxj>LSdjn{U`^+WE6qIcJhMmgdt)K>VO~Ci$#o7Im +j;nj>zqs4+;q8%;EOkgqhm`_7K1t^~|?10%pcc!U=`$_ZZ+Ex&YkKHCPbt@0t{v;Tl|7mt=_6Dqv5rn +mPu6WGur?-^FPpvl?*d@sb-WaDaf2<3%gYjV5WO*>9-wkiS;b2>{ENBf@PRz`^mtqKt=G0$Z@i9GeLt +IaR)gmAeZ5DAo4%9Wp^*P9jNVQ&JK?n-=~orLXK}a}&_oF-PY*o7~fMTe@H%T}FtRyhO;&PXxGYV3oQ2e%+6UB+x +*EP*k89sID8)yz^trOQFF9Alvw2ls`zb%j5jDZaGg58$`In1ovw;Q-!&SYQsnNh2R5P+6xjP2<$snTS +l0Ri8!5Ve2XW=F4I*aFwl0YP(yBS)c-o8F9>N-R9j+CJJc4(g}Ob)ubHHPR`t68ebXFJaG9!F%;9cty +7jp?$hyERvJbt_{4(p^Ow7ogySoMt+vr_%I9;seP<3oHVP4LRJIdZ}#l5Vt)f5@X%4ssw8E-**oPO=NDnm=8rB|nFdG+((-&h_emz9HN##BE_|@#Z0fpCC2@&%cT^=((;XKFpP?Oa)S#Vn0$ZW +g(c2EVU*UHqx6xI|EWgZDQ%jy+TO2ptY!WSY43It-C}hCoS18q^^g>?ysN)2C3XCWO>N@Ah#JnQeSOj +YCPngc|Z^4)|!^k{2r$a{T?T&aE|$;wH4?j!8ZFPoiJ(>NAw8vZrGI59Zi?g1aa`q|3kOToI=YV +SMxy_7#uY@rAtO0V6SbZxx63#Cm +3`@*H4sF>cs^ci-j>>Jc1%`)g@#ePHZ>JDNxjLl>cC0Qi5e0jY=<|r4!N8o(|$|Dj~N(K`4<7YRmi(F +5&I4E-g(@=n5CoaN?a&c!A3#(50QE^RFsdq93S%^R*_54h~Qd--{>JJUuqajwX2RO +kljF9LKr7KX{ZDME|p1D9yf6?8kdX;S7R+vtVL=}B6^&T==-2q5ikBD8ELc!mGeez$$5t-RSwyjmgqz +w*49oDK^I;NsyUy?Ckr}pzYXz_o +P0WGse&EcUXJAZ~-8UMBlCfXUCXdgbLL$T!B2t}4Ws7Iuc`!T|AAU9z*fwDoXUW8FQaJugF!H-*=w*o +hh<}HI?G9#w5gYF{XjF93c?_UQ<>VqF;j_f)svvQhLdD^{{ynwS6@VlDGuG0W6HlAS+ehS9KF*1AcH>U@Gr<}##dAMmK6`z6M`mH&(LVRSJ>0?SajkKJmk +C|Qcal;9C{OoB93m~$K**SP4KFvFsLOf1qcUmz%&^@TCQA-7+8Q{Min7DHgFc`J;z;BwF2}Lcvb4uu) +E4tet1-c;1j#e5x4M8=h6$iixMF3KtTZPDF)SMPPMSbI(6M{{6MqZ2nAl3oQV+iBJvb6(tx0ZG +pj$B6xV?SPnA_EAAQgyFX9ha(m6cZjOi3{_1JoXXe5Aua#yn&w~#y9X+ktZ$5$#R{AO3n|$8uGdUaH? +e5Pl=U-1|{wFGy~4GL8B%%+mDoZL5k*DPC|6>@hz|6Rj0rau4S?Th3=3Tui~Em5pEJ~5lJ4`(ueGeWb +pS^@TDTj47#lH^0$|$~L%!oq&V|dOWHpUr&PnMgfJSvj=`1?hY#k(p1574L_k)&{e2>{R^VM2gxSH_=|MW|s5 +j^@>a>YFgsu3pTq+(ct%rd1CnVy5YN#x%c!+jR=JpDO-cyxD$4F=lin)eJ6hn(E0t4A=4k9<}7H)W)S +~As_dfJify)=;Xir`U=Gb@H&Fno-zysBUmg5$t)J%QOMyT-_hn7%zDsi(xDk33n(Ri*4IUomyArdVX~ +1?ru$QUXKC_{$>d35dln`em=W}1SFy6OTRZBNsTB +;nBDy(v)RoZBl=1Ut{Y2#$6!bUe;{5D@9a>`{URHo@k7tr~-8}U@6b`aOyZU^1`<$`bHM8b{Npm}#uF +25Z2-%y0A_%)(kC<&Hd7UQ-JV}s>3H~H=yx=YQqJe$a+&fV=H+rGfFQ2H*LclSAAEwZ{jq2rx#ICuySyv{oUo{g}-P!k+A+Ayb747$8RKWQGwj*8O2EJbn$&&Ha!7( +R=YA}s#UwM+(!CuXwFf4$c_I3CIr0~k?xil3Sn_=;UiOeT;3*Z>Y5Vqxf4* +%+PG*%l4=*>#W%)K~O};G%e+AuD9xtVtn_PqTx7^Zd0-7~2QJn%MS3KzIS+x`MMdZq!RZ=DfYgXbG@8 +4>dCMwYvpIo}>Dz9Vh^RP^>oYL-68J$Z8pRnOpRi?beYPMH78<;72C2g{xW{j~>01B#oA9$NREZs9ty_@}6`@OeIbk`GoR!^8btPOgUa`Cc^ +#YSG4NmaiS}Z{LsE>sX**k~@dRHy`a@Dm~b*&B092V;P{S*eeAsP4MsZyjGiecsJ)W;^PkL9Y5%_2EE;2|?p?eSL0 +U*Me^`*C}pT~&`Qr<%+c@;NySj8k2WQVpaYfpk^<49`bBUO#iM&$GbEEF}m3gJG!lUM;W5XYXCA^7+| +h&yZd|%Oo`$5Bd3A)KofG>-T6jk?DU0uuz_(hbpif8B5=t@cw{35D5pQQ+M=? +Rjt0FJYz|}4l=J>c^dA8pum9tXHGO0XQDmN>c^6k=QR$3_4N^*(WIO-xay +>2D|f?WMn6^mjk~JpsRDA= +PdqVKwwmuK>!IJKJF}`dA$L$Mq2b*RwFiPDVVwpSGa;O4c{i)%&`F<(z0^|YF_Xl~9OU(~VxD)TFib3 +oewEw3R3$$+m+UEf6ZGd7SfW3`kHC>^XQ%qlk{RL9kxDc)c)NjGDE9khcs<2e2&xm6QC#CHB)ni7i9jpSJbep>%HFkzg6yskJ>;j +KD8p&zRUviGr^gU2v~n@EBS!#X`(Ni|oCkNPLfd!D^k4-^qK$xQ{Ov<7@a_&_uYv`)s0941{7K7Asu1 +M!3p!UXKRS*2XNhlTCEP7W+vuoEMX6@xWAX5vJ9Pn~igYT(RA(XKzYoUxiuXb`9oc4dx|;!P9UZ#z|p +TaV@vfz$0AC92)p8*D{j^1Wgpim$vs%n!~BU1% +C)JQ91<3iDPHswVx7YA!01h!3^UNRj%PQAyto;>FfmwOR(Un>=qz{J_?mLf*P<&uBG3MXP-jGj_w;!- +Ni`prLz<#d+;T2ihIfi-Dyi{0Uw@*mo{l-Ddq+ +1LlTCXTNc4WDJ#9G|cpH9-pfrq%RDD1NY{lLB +-7NTQvB*G7fNwk*4Qixja4$G%0X=%T=2|KwdYdze_yM^#5tNclu1&1|xGx?h=f4RmWbVbp#zu6e&~g* +N*o_(S#hyhzcoJ)73S&X8;vP8n4nNC=l9`f)M--GVT2H_qx4Rqy%@E!-dlJt&~3GOX9_%%_*iJBB3>F +iEQl%aX_unW*{t6lk~ +e~4wrQuM;`tPo2Fgmtc`I0T?tzLxj>&_Cr_I*zr?!l6aI@{@mtTIB$uFU12<4Lt-g^@>wq+YyZO(gy{@{~S;PoIw<5-AWBJ{7U +OQ=4REu(hVyg)jaaA8<)R&!o}F4IeWxLgj>uC=yMvp}aK+R)n;`0^5p78sG&65Bh8sX>ewTSCG~7_RkbX$qO3J~z~jpl-CQBNJERwrLf_L+oXoEQw?Q(1lmP`@)2)aU06+D*uZBsEl!zYYujqUVwEv{VI!Y|QF#~z1+{KXL4+x(tv{my?C +YD#y&4)}y@6tWq2l5Cbu=tLhrhv=EKD|a2Jg=k2PSMtZ(5u+#THzer(}wkCTy_cYhG|^cdJb?wKGhK< +Nb#A#3tUU_mIZ%o*3>I&-x#jGtZ-v5#K@YsV`a!%kP#o41blp!3V_2`t!(d(6&f!Cr$v4_%q4m3Ahuw +dXNDSNjT$}M=!hI66oV6deod<8tHP)Lr5ijs)yXziMzQ7BrWpE6dJMt=!*b4>Xh&)SvUg~{N<`)8-TO +6Di|5O^Y|?&Mw*h3TK~|HFg0qyAhK#TXEziyzjly!>6ags1>(?UVeCkd-vWqoN*o###^N@R(>$E09LB +to!>7!@jGyn`3bYM&RSGWx +F>PWG&Oz+MSg%>U-+smtK|sS(vITF-g1~JM`FKbMiukg4BQ_)1I2v%NTDQj2Y=_(#zrYuL-yN=tt +5L5{TP}benG#e7xGRG7s&9KN3U;l;Nhz3?o3mKsk22OQ#i|wq~*lmUq8e(!3+7>OPjseIc$of;HQfsi +(7L?{fLHF0oH`PmpVi-@v?-I_*=gCJXGQEOVSr?n8n4OOB`aUdvGp^%k$`CA_`sxsNl4YHD3K!*4GZ} +;Ha;a6SVil9Lj@5aV#;Tq`R;Bz$(L5YyE^r^pUme5D{`fm|B4qp4;zXGC1*BGXt7K~mP6ny*G{IeQd8 +H4=oIHkG^)A*?YUWzJI23s>>B((q%ZRssxt4N>d=<}`U`ogX=U)3bj&`T-|sMg##oDr`7eo< +SZE@m-~vfdEzFi@8ZFEov%tR#+jjh)K|XN$OXPY0n~!C)4&D(j0aws}IGtJFudJ>Q`t?%0)YG^9$CMM%3nd$P1GY8)v3RoJA#E_e)8E7YMUcJi5)GTg=m>n^iymTw&po +6aNE6@L=GIG#-W>&NZg_!ag3Y_jmUsT0M-5!fr?fR@?+&i=HcjOj25F5r%?}Sz}Hb@j3!nN%`{@JM=o +^q^Rv9teB#Hc4r|~f5BU%fq}nNFVv4$Bt)89*>)K)5F36+>#CC%$ +;b(ojvF|URQB~jK3wT4L1IrcIX!(lWYzf_8x2ub!&DqlCwe3&hmRf21lXs&oA1(JG{{XJ?V_TyC$WKR +#5(|+>T8UuOtP#=Zx_k}Z<=KIVg0Q-N4S5|koJ5WsB$@}IIT5H9+5yGaq0obPuI4mh1Z}g?*ifB^J(T +0*FlOzf=vv~-Hlh~~vL=JxGe$bN&7(o8^D0K*2d%{YjEBUBs9}FjC*o#_S(Q?hG!L;vA=cgpH64X`^a +OzwlPxH5^iVpmk;y5P4(cL!F#*xI>aUPWFKxBNafK<*N9Ed0_O~O&mCx4Rextq3)!u%ly)Dq*p3>f&+ +FO(MR;Il*Xm1tT+bZpCMKKN&MpR)0po`@a3({sEnu&Nv2!g7{@xA1l_7OPuYNPZ%no>g?)wwsXfxI)k +K#dJXpxsj=3Yk +0ZDj{tl +d3w1^!z8=@9kbd{5lD0S@ArVviZo%+n-TEZwj(rF0n9ECv2TVKN4BJ(i2JP+rwdQyYY8>>;9-dJsF>W +!s!kk2yc8uO4`jP&RWRXg?4)Q{B8orzgpmZ0aKu}OL^98J$3QCgmav^;z8N?Jx&5UEt2vxn+dYMhf=H +;8Am(V~HC`2k%c(f3c~S<-qKDfq0sB-?(<@s5xK%r#i?X1#cE!Ul7_X1=N1P0bquKG^%Vp9`Y=DmLY9_Rv~UiErzq`uW32{p1q&8ux| +KrDV~7hO$<2;6nmq3Ol&th1Z%_D-(=CLk#M)`dRBgY->>o_?TiZKEkkD47r4d=w-XR)4@b^IhBhR7_vCaa&JW(PSKOO40#e +9Tw?_~>I0^jUH-U1tYC2}PC`6u?{1{9$Jk1lwG;U!onT*k}d0`~^zpt$3-2ZY+Z&)Fst+22SxlbzTeJ +I68^)_z9`@wA9!oer1NRW1!(~a`Q!1ElTD82VNGcw_-Lj4Wgt}^#iRuj7k(yRC`*m6uG+iEG(zCQ*w3 +DQsvrC<>jdB%qGA=bK{v-c&r;EyUxgrjtJKov+`Ng)xDJhbZ^rEx`zR}DS*b0^6>Ph(Z&eZMQN{*s{u +OjNDqmH96-qc(s{fXI)E#Bgv5X>62^L((bsawY4qz7-mBf+Xn__Y_nyM}cA}7BoY1NT>#z5aD7*yCE@Y2#N5Ol3)3J- +b!%#rMAFJHt4=ePGcftYR8xVOAo8^IJYlVK5PtQb)1qfV7cmnuvU +n8u{HJFkWpduzpw@;qu?6>mMnVnhd9DqiwT3dV#o{>YKk00NOXku@g}4 +EanTg4uJPM%w!i~hNRArx6Vauj1=I3^tu#lfBfRb=y-nf+CZ_3uX-(n-c1CL-{N|-Y<8EYR4w__o4;t +k-)^HrSq*leIUn(gw4cT!oz5%aH(S>vcUP|)9aJ8l3ZeV3!m=!VrW!)Nbi{*atvuScP9Jho&OEJyWjO +sD7lp?zZ+nCmJGrcbvEVm}_NpdZyS*hi$=!%;csjflCz^13{r{wck1VtO(g;MzptUIM?iMdI6ti9aa9 +5^Ld{C&%m^kAuqhIC@+f?FrexQIu&AbTzJCfjD8iIgHO6yrt#OK~b`V%#K{A;^BMP5f+VkCKjHQ9Nc- +OT_^bx7CLhRCLVMoYT|=*~;a0MGoK1N7JV!`>CdjM#1@O-2dZ?knH17*d-=0{PQ>M`lYmRSEiE;khR)n!)Ktb%7p8GLCwXCdK3^v@6rmp +IM(Y7&qxR!>NY#T0T*?*JzOcyOSX9jlW8CB}RY&?%K)N!asF4-2@e}~Y<3>G6Fr~RbwizUNEM2a&*g+ +WX9=>e-46P)R7=W=<#9$3D2)s#lohHBjP#Xz6>#M;4nvZE3tG(~d5YUpi;KCaPxDHGSnlJ1}9e6&S!D +^ux%}q{WD=bd=n?3+8zp^eO=2JjCUpgnNRq_-h4$nc1gMkE2F;3iUHt?BV^#dHgFVjYdHw9Yefm%BtQn+Ih)8Hfo*yLGo +1d{o`~SJ4OvPl18l)9Os1`Vi^Y8rGY8mrmhPl*BM8Ag9&h%G+xy$T~#4l5{MhfeWWGpJV#{HH(09fHH +@-U<*21<>xzZyKQL4kjwjobtF`B!7^?n;8EvRKKbe^tNt0Rd{tbqz1BXGEIWTOfx`?R9;AQ@Fp@v+5k +ThR$Q$tk>_^bxlUbawWMw+R{A9!NUiI~f`E=@@O+w63>zITYs0zGm(k(h(%|BJwxC1&lrx1V9mxpS#O{9 +tT~z9^sfpM!h}C=nAhCb~iguPRD_X$r`abmi{Z8y0k}_qwKwCG$IPY@undFtuM8B24R@qPOGVtT2I`b +}@roYQeSh0IZ$^wQBg`vf5hY`X60Tb)j^rr4?a@RinUN3JY3-=}N^k$;hlH*01n40P#xA~Dlms@eQ@N +z9pbRUbA7X~W~QkJ|Vw;+C}w8ZQf*gVBc9iGg-KVi79REiyWP%9Gq<8q}{yojD3E~4j$u-H9KJ{G=Yp +$;9M8|Bc!oX2PTja5}k$%j6MnQ;jHR{>qmgj(Aw*;ZB=54^{V9u}~=12u{2iW&!jp=o$h&;p)HNmQyC +xi;mH$KOxV{$vQoF3~PocZpofR}7+;3w(}GBgkpA1Jpf$*nuX?8t!0yoAC&)Zx$JEGOO!z)%C20tV*R +z=ff#llh6+e*Mg?pbkpejcvw_!{l2gikeaG{!YK5RIa+v#&j$_eYIp&W(-T}k1x_*sUB7fst3f=a)~w +W#)aEqje}BREg;aF71d46*W@-~H3x@G{?ogO%!;oWuLNw_utciub$p=RqJ$Ni3tWc&71dh#d2MBd`NmLZywx?t +9Sl|Y%{Bal3L#5A)Zd9yLj22+RBe-Mt?117ds?h8fwXBgt$jC12;(5A+#(-3Mx;ucBivw;Ti?X9fLIkg-JYSchyrVBGE=rssBk%DF?Kmp98fn +AgCXM&YvDRj#%r>b*P)SO;jYuRYR(`FJl!nJ+}#oS68_k&d!mujf&4VI;t`>DV_j~r +#DLB)(8CBMwVTov0r0PUM|Ii!^t>HCs;Hw|Eu?Fk{tyD}5WtR{+6PUo;Io?=)pY*Vc2lOD+E%HyE>5v +1Gaa=(petd*3ouTq=JDpMhv=;<8f^ynQfLr5y8C`PUaQKHQ?XN3A!}1wDrE5ETHdgN^2V@bFKGf%`8QgFY$IwX+f7Vw!iCnzu{hL>5!xK3?!g}+ +MZ(sS!ITkdoFAW +K|#{u@r6n|&_qVP_|J)g8r1-f(U%yI#UF73UCiXi)JqxYrZ8lLk&o@6l+!DQhd}$Eiq1>LqI4mX?j_j +#>D2EH5>X50cG-+Nl^D^3_dlYN<&;XQ6I3#*UTln~^S}3JE~}SJNGWlo6bxL1z)U%{y)5OLbme-aoKy +`sKuKqaULSanB3O%0(+Yw1CkSXF63_&g(|ne>F9iuG%c!K)%ZvsFg90oq_Cpj^Y5V+^I!8OK_Pk0ML_J6zy?46HuO#8wQ^lM!6lK=EuviF^QHsXN3WVAsFx(Af1e9V6NGUZm7h8(9b43~8810 +y>y~xXMfjD#zsru;~PgKCijakyE*YWrb$3+rYPLF1K8^Ng*e!rVI6AW^^QP3+$^R2OdfsI;Eml8UwEj +VO8XZ7ZV;dl&i>>(PZ6yQCbdqI!MUyR$YewRtIjw*0NQ^vTCG#DD{Kzax}@rmqc|*R(9Hk71C^2^wDt +XLF#>d9QhtVX&xb_LP%~`Dk#Ss57KVK7!PeGOVV%Gb_=6PcVDv@8be`U2iSF4q!3HeeM9`-KYm%#Dp9 +^K--IxGxY6Dp(nTUvs3dgql1(&N6(ydZ&3~hCHU#s2cF5kQEllrEaqI0 +;*-2ZkrNOQxZGMO~xzCYpvRGN7DA`#Sx~lXv8W7UZKA|^tXfl68d|T{=Pwf(NLD$@f$qwZpZg$;T;B~ +fSi9TIc`6G3$K9gzN}vXrQZ+Q9lUNeTbp^Fu5l@WT8I9_E1UED8Ee|i2eI +W2V&?w?4o6a12Lm~2)&1Kfyd}AKF1%qmWLtKHcqz)4pNKY;C}dReeNsxZ8^@hJeAHcrS?8)J6ir0?Lk +0p>*Hu!bw~FCw5&RK=xdf$;iP3%jTG1lIUM1nWmWZNCnE+CZGhWWieZYkJrtjNC?@w%4DUfq#$sq!b@ +0#?c2yM>x?R=V)IbU0+1SBk*c|(PGHi(birQ6S7|AjAlVp-HCPTRCQo^o^S}LQ#3%12E#0A$3m)K7Yp +=0+`BPa}_5!AuAH6tjRbUW%Yd$4D*xq7kJj4qbw#pcjrDGp+3v_Nu2fwYt_SuEvFzPjm>k1uPww1&@Z +y0n1{oMaRIp(a&P1aqtr#+T}mXi!CmEB0G9(SBR(g=9D{_AG?kObc!irZzocKx?XqQP10y%)1s~7T77 +o%^Ji@2%(}al?Jf{Ay#oM<@kJrYgvfT?{Y1-;8V~ns}^kJTCTz8GOlGR?r!fvdbpa7Z7_f{%+2jRGPnUx@?}C)xNbVp|si{<}7LAK`<8`=^5Pf)E?_jD%DrkMRms-&*OITmM1_RG8rq)+LI{ +EO&R#b3-mqEO4I+Pa;v|iS3+G}(l^H4)7?5<`=^>bzv#-CWebL`x3sQn2pWJ{Z6$?j}z? +s9Qeq)1s7Z(W|H9+7=DA1~fbVEZ!vs*)j)_7_PwMHKqG=+LV3}C +8RrVn}TAo;jJ6J^njtX6_%4}}z^+e1;lm@sP2(gQPd0m$Xr4CkpSJIk2 ++-tyg>OYk`G$T;1h3)DSly0)1`-nhqNdfo1i;rU +spxoWmQ>h}s#ur~Injq)3gQ~ATu&{j9tAc5)1+nDNK2{&kszFAPeo}*8%NEa>Slm%qX6cm?E&* +zkmkbm3ANBz)(CT|>`~@aSp5HLPL%+f7&yLVJT<4X^>>XsiPlt!9_n*ygf-P@@<ab?yBd(vU)hG=>;#kY8TLg3v>zY1zMt2m?_7VDI*t{-Ff$rA5zOFd{e27&?hQIt$Y9_ +Jb60>rj-DjrHfK%QG&YAtZI%HG(+JBupVWCRyekU-t9SqzC8LcGF9brHACvcJsQBq+YJH> +zG2r&6(C{nHwv +wkYo=BB!fd)(?-YNX-l?x)ZAI@DSQ8H*C!&^B7$Dn!j+Rw4u12OSe4DA2iUBPlew-ZKN?VTerL8m8aN +!#TXN^%OZz704qqb9D2~OtAbxvj~?_@T?UkChs0Dlu8{?7HzkuD&9T&-_dZ6o8b +K;5fY1}7AB^KpfJgst*1$ab>879#^Kiwz2rb-6UH)`JHOsHgV~m+XD@AF8L3R}<=KeRWbjUD{V_b$a9;ITf@iK2v-Gbk9`TWi_N +IrQ)^pg4O1Epu=+e7`4*smLufO15ksDHHr(>8i5mUzaB7sJH+2~0C5TMTRQQcrA2lPN~wuW?JIp&fZm +p^>ut~@xzQR7zCvv~sd@NzG|-a*za1F%)}VQApr*i@Ukhr1r};)8Z9qBXuKfw06;y9?>`&@#AMZo*3n +fhV-WV9x+x`bX#@FW<;@{`?T_sVwLd|4Zq6UQ09_mTsIkXR~cbw!}UZid-YD^cU186x|M00RkH)@(y! +fVALZg11>w4`n&)3UxDO5^sn6m~+qpA8dpon|ZTXxH4wi1AeJ2br3-^_Kh%ek^v6z{BYgn4#Vwx~U%v +s&7@GzBRX?ozE`_^VxI(-HSdc_E4V``$l-NczJ{ui(icPVzCW#(AQXiI-3Bss6aM}5p!a2x~KstC)JO +1VnI!o@1wd=*MRPELf4IGQqc$}7B?iyojI!9UcKC`5k4%8qkUMsJ;H~@=d9N8_?`Sx^kLx~_F-{!v=5 +5|)Q83GYlJbTi^D!FV!s;Uz2e_Sc&~Vh)w~zPZxJTci(8Cyg|Q$k412GLeMk3OK~rzmQj>lwV)rCGR+ +J|^Rum^ZR?JO!tSCr$teBqgSTTWmtf2O)GPP5_H{r3OQ#UFmZ^JCEiS?xF-3F!UCMtEmnNXV0?H{`OL +zfj{{(kslLHP)LiY_Y-jdWQ-samEe)_ny84-9*(I56z7B5;J0o*M15;^QlPRvcl5F?WFZW}g+*s2tg_ +v!E1In|KK_~0ig%}ta9V*g`G0jz&G`!%N(pI_FTRy>OOTK<(zD?oC@dcQToVTC8zSQtgJV5+8}ZKN97l^!chNskq#C}nE2+5 +ICuR$%$*Nh;Dbv**!V^<%j-E>{i4KE%__vA{hZ7Q7>T6x{9T*v#ra2|AiDYF;XWzthyJ_x^~+uUIswR +l5eh!AnJal$Q#Vu2w}7UMfnHUMlFIC%jY~(mhnr?vL_N;T@?~ZBKZp_-jI~N_wcE#d)vrP=O6az3S9R +h!J|#?V~(YEYjwyH#T7iyVFZ~2Cko}h!@R6#S?>+#9Mxr(5v2~2Eqt6%0mU#tZ7u6uJBNSpd&m~1aTR +Pc~PYbB={*8!1v*~%}>?>P7Ob(UX@bysua|#tMoKLZ9>JOPl};le+qmtG(HCZ`p&LtjH#`jwsfSUiB-h^kf?Aa^{2%N>oYk*Sy|(^&!1h`mn=XtHQKkWpvsu?r}J +qPzg2JN12$$^{G;zERx0!c#@g)2M9)?_^epPG&{3lX(jMV(>Tb`%b0={(b;|n?+~xJ92pK3TQLc9*U( +H=mkPXm`G0iHYsLrfS~pvxz8AMDUP280f^rZLsTar(4(-IiUE#K*5{C?hGA}kAl<6fzd-k2+cd9>PE? +KWlB?N*liWV@%~yQ0twS-&9n{&_72j-YH{Ok3Y&(KqZ0q3P;`ViN`@#i>lv}GjXkP6z*zk7e(#5)kk1 +&}Sf2(~7No&urQGZmiiJ>7(8U6xY-Ibr9;{teJK*!Dgmy^9({&J${E%eA3zdrZLL5wI8Z0oa0a2T}kW +qR7T611cLM?OzCd-Uh&=vN(wztrhskHssJALl|FWOONxmxTxA6g2#G%f)yok6+MoS5>JW;Z^wvU*b2q +$?uM9wxfd(QK$-Ww_jCnK^S0q*9SrAa}#E+Qs#Mx0rH^_=bG +R~13%qw^pf6VwDD*nUE=Y&N%ac}XUuoPY8ITk8NW;;yrt42$j^h2IunD8ID?5=nv!g*K;^Aoa(b7(zhcxr?clfhI&d51{H_t(C@=2P_D7^ +$BezjnuG~gx)WN=$1S6JR|JpXnN2OP6qgboRhx)f-6pq-2EB=OUldZcF+a@=bjrx8Ay^F_B-No_~)%7 +fqWmRpixAqU%ER>!&l8=8Vav7YoBSQ5{R1(9 +H*@O0`~fnMX-twuy`3Lc7{e6G-EqpR8?}_1JaTQKmidivRnYKXZZ7Ls-iGf +}i}x7y_aMG!6~BV-sql_hZ`Adx{H%JsM%uheNyQE>#h1DVmecFEwMhpN7IN%>imc{LlGWrQ4I(dJpfQ +?ew3QF?DF)Ozg)b|aQ3-av +osFab<@J>0)8{dHTZ|=+_Rx_yc4@XO@(!YZA)o2-XT?6+op1RBkpoC?oQ2mbDWxrTYVs3R9f3oRF}2Y +P!1J9buFj(o9Xwee%&;X$6J~P(u7|?N#^!DR0gLYbdzg|F{r#R$m7im(u9<9<@^%muQZ`kUp=9LF0Fw +!)nGqWiVpnCOPUt0;4flpufdy`)945Btif_a)BNdzS?*CX(X}vi13CeHI2MCNcI=(Ba^s3SphHK5bJ! +OcLB5){k%cDQy&#QW6gb5fa|c6i0G>hSRA`;dY)gUeX`NG3hkL4Ik_;Wh1PPE-Z7q*^P>Mg!r?wjz#2 +99<#j@zPGT6H12n*axHH`S~nEjN;TQmhp5Ge4T*Y!_yAGFKi^GmeVg*(L3q2~<3XuG6?y>*r3MsO2FcA&Y9A1+(w97-&NMxo_+X^!+0WQYn1lG^{YE +=v>Osu2pG7G4pF`;%@W8!ladn&$ZI$=UC7(H8e;rtg3M*q~Ai}5hckaY5h$yQ)h(k3e7x5ts+J(59lM +P{ek7mz~PaA!w-`dQvBAlz&?&`7kIQoUugZDtmiY;(nu*&V3V-_(?xZ9S|BT?V&{U@b)SPM0b5NEE%3 +t5RK&IJPMbJ?0aa*tGdy0vyls*OioM>T7cPCiYlJ<;w`ZAyC7b%NfKs8xfm({9qWNc$o~;NujA*^j#` +0hdkqzu0>hfT*hNe|!%NFe*BjTKGy-R0^M=29D++pg@o)3Yrf@83aP0VFtA;f)149i0Q3c+T*5KH@kJ +amYTf-F(0X^*~8RIOY0Ec#5Bc3_kVr%IcGQ^kafG?@9+2j-2-d(KKr@W-fQo@_T#L5)1@dsX7*wD#`( +BSUmVYiT~U6H6Q049Z&KuPoW&@%MMT?zqHO_E%{rV+cC_Qt;dSaUwfXBHxK@27&OBbI?n<~(M}uS;C3 +{9z<8H3@bWXnDit3oS0>%IL=93xK_n%_(nMIp8zY9xVFO}C7~{M$BZ6r*J919nBp%1kDaMK6|%$RaWrGm3dpz@eiUl?X-M +Zo5rt=ce%7Gb)9Dc?J{;mZPXb-a*=}~VokT6?(xKk*{v~@B_$YUz&{X`UCGpV +XrC$<%0k?ZN;?L+`6k5VPTNT!946wD5{SDP^Hu~9okPFnOa!O_o}kq7sE@!$(&fE(n|9lSsmgS=K1(bYFf$R+DH +g@OKH{CO;mvKCT~kl$e)U&;1NGWpff=>;s&qXNIiY?ZYLEQYG@$ul&0mF@gcFJsdeQz5Ddn%A8+Ra7u +aiG6jpy!&*+|?&1>|AN%koI1^z*+}z7sWFv3HifdYZ()P)I$t1oAAPBCO)#DC`l2W8p{OI_iKrspyjKC_K` +|-odqN?L_!|94>#ySZUiXPUjDoI({p{lFO!xQJnG*qWs@S6Ma+M@Kfk#H@c#Z_uggqY8CHS?b#~IkC? +FcE>ndqauk@$b{k2OWb_(^duGI&hy12zLdw)L`>i{puIZ}%X{dSwmkbFmXiaV&)q476O5-(_ysU$2N5 +aGGEIef_eOf+SEjotY6{FCuW#uBHL@#l_slr&gQ2D}om+`?IvBK<8KvP+LL9nCN%UC)MOV95`T?^sS# +*$lcFv?gu4(pLor}#DOu~pp!1ppq&$y5|)hl&N9Hrs9cl7+)pLzORXOqIrxl~|?pEQ>mYay+(QDK%qB +fjE)uVap=EFqUFTXi4Fzvv0r8>{CFs_*zCmaKAX9(M%0~>pr}{ui$*61eWA8%Ya(+S^c;!i@3vt?Ot5S%tQ6SRZH95)DQf*;ap6_A%wX>+dh@P`C7)1LBFeG!#fE?eJ;`FxlPqvITeUCA@CWz&gz&UVd}Zy +3FUiT+*9Xox`e9%ciB5gZJtnj`G2kz3ouiu?%wCz6vYsW}^nzu}={eypMD(H`_jR7S?IM+XD} +}c1qK0m79%EXthJRsNdX0LLiMOVNVD>QIvmJ;VD~CSm6n|WZjnsEA-S;yrur?K%vS*dkc=#lWV^g6v{ +*NLdgOIasw8HIvKa=pzyYBy2-_hr-Eb&jx&b(3v+5iK1guij^ngQROYZ{;Ql`8c2P9Yb2o|b8X)fUlYJ6| +k3np%nFA}N9+zk!(5ctkY3|sOFQE~)^ZCceFg8O +?wDEl`cZjQEqU%C-OZJ80J{CV$LeNB4Lf}NHBl@TvhU>0l!6h@1fqs({sG!X28{aKMT}7Z?E*lCJgu7 +*Ms|>e|a4ZV!&u!i&TVVf!0cArAT)vx~ae=;A@VoAY}yA5b7)G8Otgp{Yb#Q +Tzkdf;I9b4~DdP4o?$V6b}$(kc@tyj?SZ#N{@M#f7}?HaH{n&ii~PPe_8@#O^-5;1F +g~(@6j)~$>qH~Zq$Wk23I +@fQ_OZ$grhDbB}l(iDqm#Sc8@a^)+{=hPIXir;25jFwxF%69Tt+EI)}GMbzA7kq6kl!gvv*C$6-T(a` +{(s2Pi +;l{k3)C1c-CO+HwZ)zX9W^{;yQ6|+ZJln3O>%|TQOr9^PT2xqB$1TaIzX*0w|P|WFAL|$CAgxpEc}|= +cGYdGQ*7>C5&7mFnoV~Ko8f2k3%wQEfej?JdEts;Lr6Jh3(8)`H}l0_M%a{&_FLsZ-p1G4Q7EnUU}!$ +ui;6P7?wue(&!srbP>4;CWsqVVmvxK#gvI+U3JCc)Yz7Ta+b2>5Pe{sY#q;#?Cv&Cc6q*`gyq`%#u?B +?B)KTwSj2FtxSduP!IkAgTbuWgfHjk*^^=Xm8Dws+1a3ZI2e6&YNXXzT|W-J|ny|T9yZBzA|4WG)J4f +U;0=xeKyjuhau28|T)H7+MUO2n=7eJ~eou)(sY(w(9~XpO?1uJz+X61vMa<@O+|6x_1q8}jj`bFL%kA +<3+9RbEOX_ua0YFg*`dk0bkZa=~X$6y{LXp<{=BICP}V9{Yt$=Up`vsw0J<&h*lEo`oFZ(+=B!N*M~8o{x*ZzwAX& +ov4^|12UkR!wioKm&M1GH!iUWWw6_t<#V)hn85QUnHdL&1gv|&_Or&w2Y60w|{{Y=8)kmPL-K>UYAz0 +9zNY0BRWVP{^NT#*kSr$3kFbY%T`5mOzU0FOvgZywHZ>pGovy|{YVP0ptV$0a&1I!(}4Aor{q_qW&k(YNTDBx&ruca(%G`Uba>Xn!nHY32U +PfV=#=Jo)gNuf455tMbpQWa5T7~01M-4a36Zjq9 +bZoQ0_Ae|Pc>I0i%?+Ft$V-grea6vaM7j&!Vf&g$qN5KVt^<2;mTtF;ji3IopBe|D|t;6)=?`8D^ +L_xqhhKS9Y(jNiYZA^Q98#HEBkt>3sNaCNH+=7S86RxKgmOw9i?CJ!CZmA^b^eCQnh#ukiHdWpr}S(5 +F{NIW`Aj~Fb7MO!W=5SEzEAxW?_a(F9~y`wBdeWXyyfGX^n7-lvW8dT3RN|IH_2e3DN>#PLlG3IYr74 +=5%S6FlR|qgqbEK2s1;97G}0&7G|ClDomR+SeOf>AYm?&0u&Tp$9j`=6I@@%ev0HHtk5DZ)#bsu0@ic +Fid`D%J7L8UW$Bo(u7UNSus#Lr2g14z*0+T9IavQGtQ%o{PFRIz=C#6F4(m!`#b6~lgcW_dv_M#)Ra2 +Umr=W}#eknzGV7e$x5?0K`r5Is74(naQie5$v5mszVNkPI2;Y#W*tY-xM!YZ^j`v_}|pg$MZ3xa-Otr +heOt59WkOjxlxCLI)3EWS%02rHC`NN?pTC_|Tq^s?~qgLS>Ic7*jYVfBaAC9F7oDHRKA0IYUl4TLpIS +c70q5!U{&P7>C^u*L}MP+0F0)=*eOgmomWLBeW=wY#uJ!g`aiM#Ji(q8x{}%Q^5!fb~aVodoN*!a4=k +Bf>fz*8Rdd3)by9VmdjqfvzmuRF}8OOX&ReNsyk&5lVdZCa;l-OPTCKvbkAt`IGE2*yNR=xMT{Kf}y5 +i*#l*j*2!>bn31sr+Ui|neNA4GO1vR*N`5A59t-vP-DRD^GFREW3=PS%>1XUUo5?ycQ@f56do*Ca*<`OQGx%ZSpEsT<#Mt1#zZfB? +|RZ=gHK&L3L>)%A@7%f$Ldh`0sDu3zR+{f+CV;F{%zWxn3#l07!h@%Hl+rTtD1CjGpu{?qZD@*ci+ +em*HskIO(WZ5JZ=NSDV5oqZr7)ptay&fcf>DO>jn$!|oshrKg7wjYt|Y4;;3+_7EX`jq`+7fF?stGbp +SI^ihSRbJXIX2AZ-J&0CH=6fQLA|1c99gFt1=h9tRv>=a<$aN>u|A5ke*HPh7y-ZVn$&klM?sXL!Tot +ZEo;yv~3Jj)Nuu+s=Y +fA=ui>=CTkv#L>8-ww6P}*Cd0=VBS7}}!SmsCGtH^wmURMRuPXd9Bp!cEW-phQJkJT+3r&~VOgOXh~& +cm_O)3Hk=zR#UF)u}tI2&HrE^lj-P^AOGT{`$K)24BW@;pUwoZu6JeFQrQ-XYjR;EG&% +${G3aDKaH8B{I*Dd4|jyGHb}ZK;{K9Yssu7vyRNVGCg+KnP-h>ty&r!MEes25`@Af$}lr!Pvt6LsI|+ +~D=y$^ZHZ8EsA0=&L+Z*Fh1Dr_5LjOaZLgkxzK-&JYBTb^^aYi4+-5IjyP_3V9U**H(fkD~O>iqd;clQeKs!qnH*j}%hW!ZJlh;$XWL`kB<;_AT|G{dS +&X_f;Y|_2~G&)_Su$39+EoNo)U_+28oYkwL?N4tT2IFTaenL%dt+WuP+s3sDXGviRdYjR8?!Jq@p46L +``?P+`>k7dxX +>qJEAl?@1F7rrvn%S9JSh-}JEKm7lsgJf=!_+wi3!*VyHA#YISESOjqF%N8u@`wq{ZQbox{ZzlLbFcSWf)7jluO?)RW)FP3Cs;@eM4mZt2oj4sJK-{QSdFR_lCmoI+{Y&n9duQ;;54ATJV*!ae7Cs-fB;jN`u +Me2)$1=9m$V=LnsqIlJDWF^sF>hT8#-frodlR^rPQg7U4&(cL~=j#nm>bf_o&C4Shr5T9G-mjZs1a|L +0C`$4-3`|9pc$Cdwcmn3In-Wa+a4q{$*3+@nz+Uj#0_X!pwUpE9%3(U;)_A(UT&PggCWV&1qO8<2Hk` +J_%c@YY!K3kaBO^gh5pI* +v5b)!Qm*mPeRoKRbwMAIl=mb2KQYZcg>5l-ijM=;<s|egl-t0)hwSd!RsEqtD~^(x_7>D%7U`F7ltyS$uMRix^#hQZbbRJ_(#Ts;QdB?FfaG +7{T#G<}ut7koY`i+O&XC>`4>mVfXKw5qK1jZVD~&u2bstiqBq~-M9Qd=Iu?Q{@ +sqUJkOOyj8NP{$Cpf?hdUY1u``~~V9%gPXDLIU3kvXt2J0;+r_c@z{)vNAz!LK1Lvm4YZ2qoR|qAV7( +?#;1-Vva1Z-;#uJ=1@DAyMMUF@i})n*UC@sY6`3YYoT;f#bZ69swha<~RYXKcopHq`tQeLeeJ)b3!ZD +qZI-7o^hrOq9TS&+r$2BVH3Mlpy$apxnf<9*%a#jLPYObdx9~V`&WX5s7*zc-n5uYC9#tDg +33P27$W?JVFH?z=mevVIn`{(#ux~Edz8}wp-po4zp~Maj2`EGtwAZb(Xj(WDi&~6s)K?|@{SA?h#wX0rqdW+A8FR2KvYiR +!BaGWRcz)&ik&M5C@b9AA3jDL$PD^+CyM(jnPTf`{A^)a3{XBOrc^B4epzMv`N?E4vddgCDjVK!j%I+ +2+<+k2mCCM&Sp5Rp>OheXXg)EcnFM`>(-J}y-@A(l(YbO)cEIAvgL$j8LTue&JnA{d0K-W}LgDfjUvc +VNXiBp2BA|Xjpq$yhF5z|-3lE;-+Bxolxb|#dS3`69m3c%*;xfKCTUlr}G_LWB1*i8ty1R}wrQB0HXyPYXjCyN_W{4$n_`Coz +N)0HZ_JlcF@Z&3rtb|u&g?qE`^{l-3B>DInE4InwS8Vw-v^(jxbH>&}p!mfVGlGa{m`uEG&2%6IDZkj +qs9t~(&-;kD@q;GBRoE{`=_0eW&@D?TJ1Muhk?~qm6R@OaDP_ +lo!#VDB386?jYw@S+|s|91^;P>UC1S)>`QQ(rTo1Hq=PjP-{vV5|Op07_?eb;0&!PXL0V+XR1a^rSXu +gH$~P{DRfw>CAwQv#x?FTuKK=SPi4K{l;~(hZ^|B_HwD4}U@et2t#STlEtQVvg(9}9kml7?BHfM}HI; +_u)l~kg88wxkHdIr&vw1a@FKN_NKBG}n`N$2Zsm%RVHI?y-n#w3eP30&>P315}P2~VZO=TZNO=WkXIz +?z2b38AUaFq!~Q?jDU!Y_qn+_a*~+!q@us{Bi1MV0XsqUvppqRMSTbH?OfQB=86Ra6O1)F`S<{GU-&d +F2KaRqjzWqIAR&pyLzLFDi>a-sB)yN_hi@&DXJW*XKK|~1vS=GS@8wb=CoHe +RSp+Ab9$+oD!HopWHXaBRW5+$liP*nlW!Yqs+9YE&*YAV-VZK$RaIo-ILO3_Za&@p?xn#wg-f2*2Gkz7k^D%AkLuBLK_phn +sl2CAQHgNPsHhw!R8%5B^C~LaYgAO;^7~a(Hl;hIc^#EX>HS6}m8CbVq;ldBA_ +R(4WX5nSPC-%px{}H{^-3zIT&JYc93%9hC|W9i`u$oe(GLHES}NCEZ9z-rnP;2PQVGegDJ_*wA@B`uZEI4QJL3dNMFmP#m)6;L_(DcEYhXBF~<~c*K4UX$_+N;;_`!4X+OL5ltPmVEI!84vI|X$*p2j7?u}7 +2sXV^h%pTooW;+Rb?J~0*f(Ho734S6!ao>_Ycz4(GO9y-JcJ1iHYDkjZZEfwUnEI%TIrV{+1JLeES#_ +0iFi=@pbw<^?qWqGRXpSLa@yBt1Pc`kbx0fc*Ri{O+U9QioW5q$^x(VVlTA}z72QqirJE*ry*jgn>U7pn*N_XQ>={hXW80$~zcae4|;a+rylj6u)e +Mo%pQZ`adEL=6p%#kJnaYOWulQjEHkZ$#8H2dw=;F_$@<GbC#pM3h|=aFAMKTUgU&6+ZAqbr0SVdSHJ0 +)luTtXRW5`43G$~_HR4JknOq~$>KNIM+lMJPtYYqHzttfz5+O-?w0B6%x=?LM%=)?7GcoHT?=%z(I6l +s9-XZ1Fs&AI2Tim3kS=0p#_pOnHxP0Px@1kj@wgQJlR|V?(T%&7^9CB8A79{g$$szKJs3NYmCeQ6|K((_HOv6&`WfPf+?Zp~I(AMEGte1# +`6H!7`o|*Sx7PPk4FOIPIq#xKeQ8xT^kC*y4uWtmU#Ap%c{bQp8(OF`{H;LQ*agO_@DD`_5V=0aor@Nv$zKPrH(oGcw!%gRHMoPX-a$=J7=e1(7QhI)t`jv_F1 +kzGIHqoflqF8-RSLO#u47*6#%=lq(3N#7Hr+c5om=GbnQLdT7vfaZdR#h!bn4%$Yh +=+Q*;N}-?mAG)ZE;~fwpNY`z1b1Q?3a?vS^5NN=Yv9t2-I?vUbGvsT1mT>V8DbrjZVSW5~ST&C%~Z=m +;O$)wSPJKLURWWk*S=BQ4-hWUNG&Xa&XObuWSySDeniP%_O?~lc|F;ePy#`jY^dA!{wc7(>2><)7Q2M +A?4{|^F4&Zh}!j_z~q&OdtO^h!>M{U2$!Aq1?!&eZSu`EoM+6as79?8> +<2gWj7UKM7qrN$5C5Kq&OlyQe?Q$AB7KP#5e0CW;)wJ5YdHP1O@Z6Jfcz=o)$e^O~{@y{kU|eff}7><)tsW?zwcR_wEVFywh~K&o?^aFL0@Dy4p~arB}_A8 +kK^r9n#)l7AhvD5_M4*s*Zw|d-UU+?FjuaZ~J-CYm7EJ?m-`f?!OjR)1C|NsUUg&Y@ +>#}hkrd`GrrIg|*IgJN!VSScfN#jh3{F(etV@WDz4V|s%U^B3WB<|kR;nPBDIHi11>mt4ByjAb2toCs2O +b}dTkbJ*V4*=MPuSM<)yo(M~b3fq1M^&{5f~ym89c>+vuA(ptA1WOaw+l+U@1-O{z3gqIm+n<{b~rxb +)aa||Nm`LoI81G(!KS +kWt`rGYj67n$2Cd~I`b2SCKCyXuKh`y)feKnI>A5kRpiQEj+iEub(AIM){0>H(em&>kMd;W>kIY9_sZ +oZs6(|&u82Fs6UZb7+ZZyThNCY`4`#a+rk>eH702j{U9HRFh6FBeU;NFI;tzGp+E@2$=(@D{Lyu~yI8 +|R%-L~GRxMy)G1Z=yH(AzVk5{1!+l`|6b%uI9UQCv9}I@R5!9-D$8dHkIzXY5Pmo(cFEn +Z@RF%@AdtqLYB~clBC&CpizIXuYczf^-zfW{b=CJM_u`HcgZyu}*tb(6jtEV +VuGG}Ch;0D!onc6K`d4?yv70(%=ei=CX4FZ?POAChhTk^sVypIXRSKmb@=b^y($fz>vP^dE6hfu36dy +~v4mnO1Rs7&%vs8PRJt<3;x!wf#kYt&gM3QChOh}eK1mhuWCPUavA*bAQ5;k*lNZ8CRAYn6iaUOZhg# +elh0dzhD(4xFD@u^tZ`}mEuZz$bkiy`uVz|DWRX!9%bDJ$?REAorApLLux+5&0PSmj}B>$=%_OId-wt +VkbYKabr*Pg^S-;vYLM#(rta%#<0GZ()Hm_+apd6I|QX05>nYMFgmj8YB^IKR>k!X9T1tKNf?oA?s%8 +Y3L5U=(UapWe;2XEL}z?#>`os(hHMBsgmuEg{oFNaXPP)G&RZ1-gT3e(|Zk0;w9AIi9NwFY7-@W&F&@ +P^Re}KLB}C~0Z-M!uxtWB(J+Df8MD}xwta&y+?C#)=F?#_4C44ecv3R@k9Yr05@wz*%OgqJ6XsS0Wa~ +kGNy$>DI8_Y8ZSuadCK8Xy;z~?a&xQD2CD9e{*Jh6`9(_%r@3!#G_r4y0bM$>%pIYu?>({#6*4wdD=h +&qi@x9f1#JBT3JiZ&Ti)@u-+d1Ndy$$YQJQT8L#0gs~YzlF!$E7b^71JPUi6dK{^q)aQn=|^iMzKKO$ +_5lbrCJO&Uu^gKD2hJoPx%W~6LEolZFWOjgS{25ZoG7IyB|xubW-`uIxVoHr))dvd3YLm@jh!PjsHIX +FFZR)g!q`?6v0mfzBlXGtps-vL=&VD6chZJU?ahM1V;&K2=uq;SVw|Df{_IG5d73t$4(F&BzT=*Be`u +MC?_~VP($G1Pw@zDC%B8?UV;S#D+sm_>>&7r-~_>s1iuj6)S1E)3?(oVOeB~|kWKIaK?%Vcf{g_45ga +5qPVgf^9YMP;I@Xt9Ai-S(u>{iz3J6va{Egrpg3k!PBd8;|neY!J7)mgPU?M>p!2*J11kVv%>#7swSW +I+f5v;gHcU>93+r!MBNiefH+sy2#&&@1jwb|`g{;iqi1emXjqkW3{U7ZdS>y+^XYm7weWyWf#=|>L=X +LIN;pWG+2No*_|z(O_lA#6B{V^JcV$?P5$!y?#t7Aah!>2Cy^M}H$Ie!d8kru_A10hE{g7vOeTsQfYE +K1jF^Qd}5I5ZEymu9%U`LYP@7{n_+u6>j&iTvpJ?Z9Ijtk-O@WNM;_nXhYHLjLjv;CwDxtWY1Reg*}J +D$$95d_)PKxj;Umy13f}i78Xj&N+IE<#!IDm^)52LDvl~%Dn2R>YTjzTay|>)d8_%Vd8+v!om5KCPJU +@h41@u;_3x7iTZG8hxFu6MX@bIf*GUvg`yNj?=V;<23f#2sse%?cKQ$jUZ8beL9W_4A5R8~NZ$!Rejx +>7u2gK780LwypSbY4Tw6sABMJ5SU?coHGmN}Mrvn`@>@5wE2+sDtb<-(e1$+M}B;r6-qd>bU1Y>`H+J +x5ezc%C&=)M2W1j;KG&Y^y!hDu+qU5mw3v=_6gFiL{Uo(x7+?kp|);F5E>JfJ9p{Yt^2J#0($o99oVODzo6T05AH +u;;Gn@n?g$w=Z1|m_VIxcT99c=u;$LOp!VkxugxLeuo9P8h_WZSS(u7;ONOPVDn?aQ1lPy*Bh;k2veg=J@U9_DX>H`AkH&swwAX2DL3;vnHCzax +5vImrKXjQF6tCQ2u6sRvJ(#sa%Wy(@T&T2Ug5VWBz(Wj!X2w*|f=g*-xE4xjU9ynl*8z*gfrZ!npIRZy;j@ +<8C1uoVGF(U;|=t(mLosH}J4tIU$Pz;;kjT}*0bBWdf${~;9&Qa=DPCc7UnOe1`WS@DO5^bE%kYC}e> ++9R1DenkMXOdDcR=1B_!nvZ<(3buae9@SW!EQQKoYZn_ih{I8@$Vp+LXB@1H9A`<#g<9|WdQutw6y8n +`8(@xzM!z>cGgsW!+%G9;L^0;ncpyXetDv%kZu8mw+aaWX*AVx)V5fkqMF{&-+_OMNY%m?2+Rj593{V +@=G0oe9{(XMti}4yrQ9qE#&UbkRs7Kl76@)^I^BkR1Z?tz)MZop7L{k3>g%=X-1#f%%u{-~Owr=3BGo +i^A8_BVrvH2RrD0BKqP$a;)YWwB+aby{SMWoS=tncem=ho*UP}XRxV}9O*3{39O)>vE{vE98uhT^x$gM{naq{RrQ^X7qqf?%6v5I#U3pq|&gR;PbInk_ZX6z?31DROv+ +;r6}LS&GMH7!3f+rz!i%x;1^nH}IxCNyuayJBXY$xI-#3z;W=F|)2@=8=i-O9IHe74DbKtOuF?WJ0>x +RBOhSEv)>anfQ;=ArrD=37HQdA2Ok*Xcn2+QZ}; +}n<6lQ*$~?W9w}+oTDk{ng&#=Li4gv;*CQR`$usa72PEPQlLJdHbF~h8kS@bZrAa4OZ1`5G^n884Xf& +P39?7t75fb0H(+<({mUH=zNHPg~xNfUn!LoCt!>2B~(8-J8psWA$xWT`A4)}NfevN+ob)-M-o7V4!Nml4?_Pl$1XB&@$(8*NU>053gGN +=SLp>%VUqPS^LD3Pd)wDXV$HMcEfYe|Luj1FaG_dm;dp~KR3Pl+U6}=U*Gn|n{U1S&b#luUtY1ja>oZ +dckSNu;og1wKRR&m(8q^A`Si2Tk9=|T*zqsF`ufDlZ@xWs`n&I?svoM){OiZFHRpahf8pngmufHna;5 +I-HU4ieOfRij1+y%*OGD{RIjg8kB@pu;(KUln^m;^D;2a6wETHMjgizKnq(o!n8 +!GntvikFd871S>@A-S03EE2{N5}<1|AgFj*F~wpej%8$I7z{oO^77{ADJtN6k0@Qy?bKMBAlp+rt ++LZCSxY09Ak556JG9W3b@~Hu`bNanH!KU5W3Dk-(s_Ayji|2jmS!~=VfQkkwd6H!Y|iuYuc0is8E`T2 +%4Lijn7eLoi^bn&jIF$wWE9{uY9UAt$tJk+4SB|sf}#0>r-FGb1gP1;!In>oZK`^KyG?Kp1S@!P|UIA +J(fWtO)HtRMaqMenI)d61$o5aa%ys|P+IfmrIL+m1|>wr(r+NuV?WsPGc2=H$uT=K$3k_Vo!?{{^D{D +Smi)ZbIhKIbbeqK*&~K1E-#Tb^X3ii>PC-94Vk3U2=N05c0W+<+IrA(zHg`r1^0HXxW#*)cN~G$cSOf +02=h`d*RL6~yY6|I**YSIbSUa-PTnn3blQhZ8>CVrzWq=LxZK+m`^NnSvsWU;V5~hi=Q|Xf1nKrEkVb +d(4%C-Ih_I#im +ICT2GSdSr^YUy9Z)-W9*~C%`)1+emC6%f`Wmr)N^DOgntqWN%rM2{Ox0`?_+_0zm`yVJFs4<3%F=b+6 +Wa7m0<$v3jH6VIP?W9S=-)3(?0E`$h9pgy;^dzd(h0|%Q!GMxKJwJ0^UbZDg?MJ7lW#-$a+d}Rbn3pz +No5!j!#x_wWXI|el4md~;^Ap8X-`8|Cx9I=pZY}#y>D#jZl721w|Ks+S{ofqWvVZ?UE&JyTZs@;H|shPI +1xz9O%24WjyH?#2k{LHyIN(z&5>$7Atbz=o0%KV1%{2F +F3+~*HEdXQ^;Zkj#YGL|Htds62i?DL)zAeT2u&0NoTd$uie%tD)GQf_i)nk6D5)yn>&(C7{q&R81KPP +C;(Q;A__;NhXn$f7VkVQc_OOhc9&&dMYfsfg(oCF(>Lk)4~bzTF))@t&wS8iUiavl(+Sl>bB$Ckg}!i +6n$1vnXAn%}W0Z!rb4{Wk`|LQ$7-1;xV2jAP6?~-8HgJmk-*rUD8unl@vyfhI+6(EG{ +LRJ!R!P5B==dKU9h{)`0U1;`}EIFyon0+ujg%vzeB*5GAP$bFh5S3)M#daOryrxggwpjAm0MEFHcw!B~Kj|v#sfI~7H?HQMwN^N7dHPyP1aQs;C)I^J1$xq1bNA4YnBVN +|L6CqSG$-1%G_WTSaHpZTwPSQJL523U)eU6^^NWm=EI@W@dY8JI!q%mrqd8t;5yWEozTT^~Xsn$$PW0 +F~-?2|S=U9Oc_kNOoUU`-N$+`6fak)$Ydi-K)()5G?EpZ^~}skjeQ-ousm2<1INi8E1gPgKlgCH<*N-r0)#JjTQ>HqMdbOo>&&)msT)tl+#vF_$X-4#mG +jNoO?yPG0;~d0(&K^PH033yS+T1;39J|IZbFe;>Il)H3_eK7P$UvVTCc&wm`X|B@r{es#uVr)n^Rl_*nG@pfZu8<|VQ!@G6&sFj2xunRCD&xrte +9JGFnqXTzE0r}JbUWdF6{KP(zDZ=Pk-@xVVgh$$b_Raj|Q-mqghAqxIX;hBVV1k%-E2{a!jm)q0f*0* +~}sd%mgC|LJ5Wv3?>L72q5q$@FidbwHM6n41q*&ir@snae^ZRpAZ}**hjFNUBGpzbG1li)bP!Jp*tJIH*4U?ah!1SJG{1k(v75yTOM68I6+oihu3Taq +vTohR_N@IUlj$F9c}S1YQ|MqM}?>>bWF`Gm6|io5FeaK@GAWlz~&+?xGckFJsVTiu)U|E=Mh^Z)Pj?? +1nG&dj6H81|vlztug_iGcU$54Q}Y)YTrz24c=@wTV +-9Uv0YQ9gzv|5(!DtSm?1I&8z$(mUG6?K?mrR-m{HbuT_KBvcL{-fQdtr%${2JP*q)o2U=nw9N-K@u$2$@z>MeXGQOyeLwxaTvkL?l>R>FMekRq +5GUMM^yAMdual+ducxPRekHwd&*{mNC%>N_H}oYkQ|^nse>~?~Q}|+MzxSt2PGLoS!~0t&BfU3QBK;! +H|8?ooCMLM}PmC?@(S!T>^x(`>0oDSjVSE%Y%H@PIGK+F*d8MdNpFS)&IG7C{JeZ9dHHw9YhqIWN7*T +c;CQM*SNl9$#)TwO7j2Uck+E_Ms?p&6YmBr@Go5!qHtLQ%p3k%tL`)Kyk1EbjH#U|#M8^u=K7r`FQ4Q +EeU&Ft+ZBiKKRW7rc*X0vT;^4QLY?`8)#+{c36<{e +Q%beBJ)UZpJE(u=2<1vWFhXlsFiD?Y07;8;@(}o&*J8HlI{3bSv4`I{z1h$ah&(`qOY%712eZmi^{VD +56@q1DH!4!WK#gC)-Qz`yjiobxjXUi%6QxyLdieFCgKc@JnH1YdVd^5$rm*U$f{z{7fBE{cM@efh_BN +YEDivKOeucG*8DgH%G{6T#gOQ5Mw9!lvYh9#a~GAms9+wDgHkxeg(z0divJwN-%Rn}qxichKIOUkIK@9r@oU`i`%?;cQwo`s!b6n8^OV94O5r +4>P}82XpN4RDVFG6t@8|5&YR)dd%Gs4e+#SCw#UDWNM^pTX6n_rIFQE9#DE?ZC|2K-ih2n3g_y;Kdaf +)B9iEmI!pcBRKO7VMA{Js>wKgGX`;!mOY_fz~86#s7&|9y&oR1?49lOjJ_r-+C#;bX$Z`aX-hJ*ZD$u +U>t}xqZXO#Ky!#M#e^r9vvQ;G9aj5zdnKE#`RWHh>Rg``i~(?WMoS4Z3r-KT!7*q8y^uBJDQS@iU=P) +COoA-0`%$AtEb{06K@WW4UdS7p!mRRVE^FYyHe_rjE#>~pRry2Z%G+AP);GRSI>an;}D@RQ(>=i&r#<C~}9hr36K0Kpq!17w +M0QkBp6qkBlFCYnM(+0Qlb)G$4-2=|_kx)KW_si2zFZiS&n<;;D$^qhjL)5h4e*{0aY*$RTE+5KC!v8 +9TN^hYl3L?VmE-JH_u$3zU$>ssHVc%ZCeBHb%BF)3k#{91YIbnT-FL!cNRJDM1&-aq`#VS__jc^YntA1 +@&{$pL3S8IKH_3PPl&pjt(h0U8c3wiLZx84%6!l&gguqR30*hI3zjvYJL +M<0D8WP?-Re9gZ3<{NhE)G1b7UCqv%Im3QBcUH&-KmYtQyL$C1TYZVMSLyxG6-_@o2p_E6Ndq0V%T;q +|pj$`--NQ7{{f)KfTi6i(9-F{-v-|mBwwfPjukzD0e%EN?(}0*jGx|IlY*+QC_`@l_nc~M%{Am>bev1 +DP#ebUOzee%*Q2hFF?!R)%f8~__Tji84N*~arOBbAg$O-u?Pf*!F%s1_ciaX5D(-9yF+X>p|@)K)beWZ|&BzPtc$?t%Gh=90CG@Z +XaYY_y$p&+iq&twu}EQw-0J#Ftl#v<>h;u(bz@r-R1V~Z49m9Fvzb*Cm)?puR(aaxAH>zH~V{Zptm-? +TeoTzOzo>@pRS(W$d{sYr+3OfxLe15y@Ll03dT)Ul>hC)!GXcSJ%a^(-RQv{YR{hXPF<0av5{DW^HiX +@#d@7448r4}L=Sm=2-j%L5}PM_QIB+ZYrsp%^?%~|JzEvP7e^>M_2|(9g$lrDkbPUVYDGPZP^L +^JB~*t{Pa%$Bs3rbKJ)TOszBd>i<%;1E>>u#VGtVT`xbybevuD5i@y8#(r?KaofBoxUr^)>K!i5WGX< +mBj`0?Yf?%K7>rPJwxM~)mhNTJDJNe4$#P!7MP{7}~zm~ZOaw{LGUQC974Fs}c!Y12jzr-$raKmYvmh +iM+o-+%vo{{8pga~eNye(2C45e{)kMu7V%iu25?ufAHiZQHg&64|004#)Vsyu8SjD_16vTn*fCJSg +~TH?Dal&18{FTe1ge){SBr=NcM=C|K|`!(ft8R3Y|N2t9GBOF#D? +r+7Pv(Xix!lq5|+&=}_$|G^Rxb!r2W}=Wfut1AY(wm6eq%s2+O}Pn3X$?c28t{J|ebj~*3u +aPs6y@eUl(Ca4Vy^YG!r0?;;4A23mGXgi3rckf<-KkAU$KSw=WyLOFhA9}w+?sf2|Hhjl#!T;jLi=2i +b!T+J5p?$%}0N}rW|9<}V+i!EKb51g|Xu~Lf)W@DZdjtUY-Me>-wsPdi5n%@n=mWrSXeX#6;P(2DoLi +1^KKdihhwkBg&Z`8=z>YFUc>`bY0cZi$+4Ldl$ooML$F6X`8;(XQeo6j{^KnNwkNA|zA2d)M4ByN79YjOU=bT@x<2?Nk=Y!vKZ%y0~ +{+BLY0xz!!2?^;%yifufs2;AP1-O8ZfMZj1pxjaBs3Y(V_#gc)&DjJ(;|c$}kI6KQ`GoVk2-gur!!Y8 +TydyIHnTI(Kd`E5*H;O-v8C>oE!T&TKaX{lbQTZI@in;(_fj3k-(8j@M;Cr+S)kM3%lX#Nzdx(Zu;{U +Oq%QTES$a&a)nTEmC7W!}JJm~%3jz9IqtNZlnGmPqCCE6MKmZsCY~9ow&y)^dq$ts_dU5i4|qpz)70bcYYu;^=M_VT4(&z#c?s|bpERZed=I{EOovJf +cpZH7^}a3qh4~}-2Ky-f*ZjK#4R~f$aXwX{A>nJz;|QndFEs5LG$`$PAo?WgQ~OY#)RSoF@rK;D(zB! +~{CoB4<>r4H<1x-(S08A<7!TCCKzqQuN(1nxevg+wp2lBVIFkQu{wV$|(eO0UfG3su&*?v&a7Rh|n6#mrT3;w6Jwi5CX+MG&$;e5baz3k?XplHhJ}tLr^hvSD>f1ADP}?(TP}?*5q@cG&GM5_Tk9LOfh2$(ZfCd#B) +6rBLQE9=mttgDYwrB)@qG8(iM1!V1$13eP{IJ|7h3=QfXSGl2M>O<) +yUBh@;7{`XiebZs^&%Oi1o)$0gbAJj4Ul~v!}#lq!}zO2!#{|IjYPvcYjgR +9vsE|L*;Emo^QcZj)%L8&82#RB8h`W`D*uldF`_T(LIt!};0@e>yGjfCe)RX4*P!3TScS0-&wEQl`I| +(;*2N?ErbQ5hd=fAg8vDhmB +1T#!30jg8|?vjt961tUMnAC`~{8R|H@@|@pp-aH;9HUg+v3j1+-_-@T@|^T7`yHpdo7ve<*`!NRQ-;) +1m|oc+e+}(8w6WK4_Z%(Pv-`gB$_axN#$Y>7|!A=Ag*+1Gv8ivCod-&-XeV`avf>H;!HNj}z9W>st+eOYH0}90g@z{;8vaatwDP`i?# +z^Fz$0V~8lU3`p8j5pzkAvBPiNFQjlZiWOqdXxlauoya7BAW8(6$}v7kZAU%yr-Fwv$O&#{_%KHu?*T +gE_pE>qfbqo+mr2l%7zqoboy5B$X!Ulg(zWErQ^$qNb!+%&XAM@w~r2Yu3(#W&ENiH0WnndY9(D5q7c +R#i~n$@}%|$5T>L`18*{&oM6N=jU^q&Bj-(SRvZ=mtTG<`W?tZP0<3pF*cyDf-D4i6!QT*XwL`!X6JR +k(0pS^ihEi=J&uCG+->iIHA&kISgn(e+oXtScp7!xFY$-F983G{KMx8>Sc@qS;jznMxXS=f_R|+tM +jjsBS$*pJK!3Q4*egN*k6?LTYm0BO554?wY4 +&*x21=<7Jv}n&%58C$pVx#uFZ`(7F1E0JR<_QZ6dvxvEwPGCvc`I$17Z(@ff=S-HcW-{%ZMSiftpzT? +18qYKph1Pkv;e?UYTD>q_dh+Gzf)?a@mZEJFh0Lj7{RwaGM69Q`#Qh)^UsH=zHfN_Va=K~yriV${OZ- +K`O>9Jf4=wLdwF(twkUU+R|;MN4QLmj6S#qn#0F9%D4x3f3bqkAxd!B-9;fY2qRBFMowH`mO +6fh)ivFU0z<^*MIucpRNJVe`M<2k-^{>Y6X+1~Abc&_>W68n<_h+1 +j}W=FjXmuD_vef&Yn@)-PGIP}tXAe^w>cBA_j0)L`=#qi<7dr=vdpgq6)?z@5pln>SoseK3>P +5PT-HSMw!D7ywMKO|G*uxDfkDnHrfH&0NMo7MITJKU!!zN{QdpSRPWF3*s%jQ4`R>9=SJ~IzFPT5Emr +{Mi@=BKmXmCOQ^le^P;t@0t-0oo<%1bl)zK +%cCYhhRs#s3X(|#&e<1KSLJ^y?-J0U_KQu?f$~NlL}Ry@CxE9g);6)GFYrg*s4@ihb-?%FC6qb +Z&>9@(|Lx`80Dmq2qmO7j#x|w}Cf3-ePoK^ofBbR5-`a9l0pY+);B~xX3;^!x{s_snPyZXZ3;eb7UmA +N>f-f-!fev-OAN>dVI*dnJI#9N+zy3OZ;)y2&KdWsR0J_z+dB{z82hXVc2h?Z2;OXi4@8IsnUpp>pWf +-k&(^8!P)~#D7;)5=P2W`L?yoJ6Kw5Yg~eEPTF$?wE)YdQkV|P +-p0iu$O>6D9{4jDUaXN&$NiYcKt`4OSM!dknbV?;2ryN0Pq&fojZ4meivnc_6mLn{%8lpkI(&qxC{KX +>z~w5xYvI`1IA9sFX;ck2N;hqcA|_i=S10~9@O<+)C1np=cBJhKSpy3v9Cci|IxS${K2~|@p*InuI-E +9udZ95oIxXao%rY)$p{kF6~@&re=zQNppN9`|1cmAoO}2~RQK>=8eHC=KfjLo@N1*z&p*M~=o;B1+aG +LtDyB{`*&k%;8l|tvw^%XD{|Nkx6qC!QPBA?d(?>ClirGUkM=NHLVshEk!EE^5P5}EI56<`aDB#6fkQ +K4sN3e(BVx##Y-m#<7cs;PW2YLCE>^qpo{8)>{GJ)j%Nb2)(3>(MJAai$O8a_qZU)FM7@D1nDpUHb{S +PRCU!X1^IPydMX&wdiPw1fj_?dIj>btld3?x!|gi~fvc&Y!8jt)hPE4E0M#Nj`s;WZNu~f$cclsp0(QjboH!>x_++6{MQK1Q=fa7`pvhHF!oH +b--vxK?5|)=9BZ+kUf}%TPdBvJ+XMad(xppNAAR)EB;Wwq3G)YxkLcGi7NM_(EDJdWez4=2Ce`m3Vvh +&=omkVx{tnjUvB&mn`C|@1bI=@`FJ!6nd(3l@FXU*Hi8|+j+@yW5Uv}^J@_rxo%C^)rwRF+oIopUIME +=^d4~@?}piD4+{p<8m{(>V(tYfZJ)&OV!i}REpHdtgkV`D-f4x +k=h{7^r{9z`zCL&)aL~i8U~+2V+fM%^xuH2YKHU`)z_BkH~v%*y8~|Vvh=Yo!BRwx>rQqt>%w$oaXyE +g@uJ#+Wl@-o`nh7`jy2IVl5HtxmZWk=8wHs>>npL;zzTxM}xgS>@k5KgWr|=2Q`1lKia+0#{2KU1<@g&xrR7^ACnsK<7j2iG(_RYM*DOkf0`$yu3ft}Ns9;a2A{*kgZ(zFsbYTtYr?NTp7* +PJhClqmxs`Akbx7W8Lz}>Ur#64c6$=+G%qc1=%F^z=0tc*{pe*oUeI0vE|4{b(u+N0`Q1B!6n6S6uQ1 ++J=&5_};rI`W;!H);)_r4qF-*_*$@g6Yhzv7|MV!sP}MOf3s+PKP(SQp3s3ic+%9+#4TVcJ-c*EkJ7j +@s9-{vne&ozB#!o_Z>Fag3hSybDQnx$DEkX&6WAZYUSY8%(p?64w0$)8 +ZngZ6w)MaR59Cn)pQY{(U|xW^9m)iK56a?#^acO$kp!_%fc5E3%KG|qnl^!bCYQ2LR;1v7NAM%%JDuS +8rH1Waou8o{{(tRVdt6mj_P;2TWSTT;>12TDt1Q +b0h-i+XA*Q6$keXTAl$K7`q!T4gRz8@tu`&DH-&zL`p_5sCe)FIE^Etrfp0n3JYp?ZPuYLAGpTA+9rD +5$3`E;$C(V^WhxHT56)y;%4dYd?tp=wnZtHf{W4k3E)&d|0?}p^JNnyJ-6m|4|l@A1I4RyJ5}g^e2adr#^WPbm9tm%!sM4 +mHilfv?-Y9z??4f7~?C@#rjF?m?vVI|HF^5*P`zX#u5JubL?MvS`_z>~+4DSd+#OD6=stT`GCUd8{BQYt +-r_MC$Ek)NNRHG1^u*9jrPapT6hdEf;L7Pz?!FZMy6<8S1@x3&@aaivYhhJC_$?}BXosQ=fkTel&g_i +h}BwjS#;SX%-=EBL?z54iVzwOsR+bK}`@Z*3)XkL#X)`~iR9Khg&O;|r`MV%|7DKHkm0;ut1Qoapi$@ +xZH*crxJSkUe<-J8>~1BV#Pcat`=N_+U52{MU-9B9K?W`$g&L>3gxpgE{&kLxu!#F0j4^cDcS@fPDAl +&|4$o{RUfg0r>ex=&K0g^HaXMM>ek2BD@?w_JO_~@*8uxD1(?w$GQgUN5eV-&H>^AUWa2rULbBGX5u( +sdF7RbzMod3aP_~iurPV`>eWNNm>cIA$1ry6*kG3~U4jD!3~)8Z+tzo{J56;N^JHU&fjW`t5KJD}Dy;diq!9uo~th&>m%HXS +>*dYgN6xBF-1`8t)r1`Ou+5>wYF}mp;+~ta0)GhsNRgz?(O34z66e(#;hkHe$}nt113M+Ae*hXJAHlrgMvVE(DHveH{8_)BTm-~Ug^fg;Wz_TfC2mzSgbodRCW_0{E1erdyJO~QZSf%W(Y-U-=o&?M7a8oL~vKq|7C{1hGQFI6Y3F;)qB3<;LD}K#aVT}Valc03FAkMUolp|ScO2W^bCv(YpocQ*TuHsl}96WSne +4eFm~_i_C#^!L!$LthhpU5q!%R?Kz#z?dTr#)0!UY}hcj&W#v`e5l^DI=HRCcH>F(-O+EyxB+9Q>1q9 +38iPIx2G;{$mM>rK`h&Pe1nX)zmnbh+`r-$7mAiib^ptv^8*g?i@(=8Si(6Qe!}SB6EycDuUk1OZKIH +NfW2jjHm;O0#{!#W2>rqZ|E(Z=A=<*bC8t3vKyGw!!(fPEt_gYAE~;q{2qIBx6*d4aVeyeG8Tm`}oMG0sNYy +=Tv!%dW+i-=r)i91dUOONZ}A4)2^MmyQTS-auVnUU@;YfXGsa!yWK=ID$mk&`ns#^=~kdD-JdPD*-OW^zuq%s@( +ZR!-KK+-@mZnIbtSv->0(6`7elJ}@RNCwFLCc1|E`yd6cmlPKA|X=J38o1K^AzJ28<_NLTb2Uq5#rQ~ +G?a;IGS4FAbao0tbw(oz$%1Cs(7X=BrJ>c4FGbszU-doYlMv`J|hks0{Yj!MpnA3rH;Tv~QiWL_X91z +#g!M~z9&$VrQmR*$P({5|m=SEp|ExNNLekNTU2&sGnQpE5BfF(x)Pen7vZq)UHX|NQ^=8IAD!pl3}<4 +7s1=lIO`DQbW#@2-=y(&@h(Hrm#0zGo_u!(nyU^b@lh^1T{~6S}jxessB{J +RvT*(T4$}VcAqw0%hmF=dD=7DDy>|r(q7eW(|@ZE(_hu!*3an84PwkSo;IqCGsZVY&}e1$FngO*%~j? +m^Q_sCXYhyl626A-;V1abVujctj)>1hs=uYR$$HV+YxT6_>=AalJy$X(*%|9ha<)2E&il@1&N;{DIu^ +J9(?`O|ZKNY%#33mpkIW#;NC|m^d`p_rmeinQXdiY&dsP3o{+%9SbTl>_`-~XAl@AejSbeSj_I~?a`? +!6^ZX%Nx`aMWSJ6`1mPN2=)`d}KFv&ys?2mgzF@WN2BkT0`vdhR?YCOIR;C@${-s^inBGgz(5LJ3^u_vWeT)8* +9%lT;xL|~ux0v^sL(LR3V6HQFo1dEBn3NCTC48Eg30$ibH~Tezj6VUmw$qw!kCAy2_pZPKQ7-)2NNbr +;A5k}|2h>u%Ex&`e=h3_~@5U+Dcq~ukPw^Fe9rugLVxb87!>#_-eO9uSWlgpowYFKitT(M1>$DYON87 +{g#dfLPN>VvhelEkD>CRkdg|o@o=e*;5;M6+!(u6p`+#5+W=}uH)lV0RrGMTIfUhXBorpfeA^gFtfon +~W{0%ftXU%8-!sLj=uAZO8PcQC=W8n51?-lvXG18SB!O`WY4s!ypesc)%gRYl`kiWbnav{~9pkTqK$r +9Y&X=o|F2`hWBmMk}M85oL5XdKkK48L`Iij6@^JNH#K!Y~v5cbmI}@F{98}0us5-c-h!(>^BY=M~tIJ +jq$lr3-TFiHZ@zCipkA;&2WAzkKi479RCY{jla*2gN$C_q2fjnEqaJPB2hdf^2BuUxOhU8ien(R&Hb( +X-C-t1`5RfSt-G!9)@*B$^}Myk+HSpWeQSl;ci02$adxHssohMrl@T&tX2Dz)$r|~k^f~>Ud!1>{lTL +;6o`ah(6vEuyK-!SLBtWvrGa%EO$fx8qxj+{ix+1zD8RO-WRWlz%Fxl= +I4sYNX56`_z%@AJqBkV)a>&gLl+hv^3!1UeM9`My+|?{FP`YD#US-Zry2jvX9zd+M{7?vCaVJG;ZbLz +Voyz8A)c7YC46rQ-m@~*{K{*zEFbt*T!7_SN;ueE57p&k;CK&(4#aNkePC#Tqn24L-Lf2fV;z>0>r~d +Njs{79wn=5wQBt?;~rz8aX){}zt8`c{~iC|{2%y_`9JnI11_BhSzaS6)@eQeCO}Q2SW>PK(wjf|eD6mR0DL`Z3*SG&k +BB?LlrdW27+#Msqt~&TkNHg(>=r5h6>xAP$I9d#@d|JIcN?QI3^`@&!2@$534NzK?X0NggIE$!1bXy3 +)mTC2h@mFcXk<1>44su>Y`L%6!FAd#Q11lA59})f($}8eWF+ +vS_`m^>pxoR&_sV<5e9i0k0j4sw!&(whL^+R%115|Am2K2D#c%jgPP4A``umeF#0f%;e|3uDb#Hk;0t +u_9K=*0VD97CX;6D@0KhQ?ZnuN~{v6Bq&cQE0kiTR9Ua|R;Q{3>MZpMb(y+SEm14fkJZoAGpernwLER +A_J}rHdt6(h6>A}SQ$1W?rauRqtkU=Dy5R@C8)Qr}{%90{p4A#H%{$EwrV2XM-yCQrnWN2gGuxbOK5E +W4pE8%5rC?G=~4O>RoFmwhP +7AXl~+O9o2$LSQZ>;wXm4nzwSj;Pi}dxpNDwQ>DgtC&gP3^G=flNrEr4^k(c5VRy^G#WyU-%w*K71Rj +bQyig6>jurJs_aJO_60UF9pKkD3WMR08;U5&GA(-dciI2o~?}T2O1ItNIvyyk4dk8*7bi=3#R*p9`4r +7GEfqiq&F+_=~tG!u+@S@AALxkG6yruohT__7ipsc`MBHDtS_Fb$r!z1@L|3AQ{M>ftlLKcCZgvxw1| +9v$6|DajS~&;Y2-5rRg+-PN2E8fX<{Z)17pW{Hr`Hj{=UIl;`9{8R|51S~=~U4o+7`bu1^=NpJ=`!<| +$o17!$&T3no4=qz%4;UT^x_`4H{Bk5!gSr2ye1PP-ZXeU6JbUFtl=?y@bT6&ST0eMbhsbJZP*-lo?d| +<;OUHKUWSY8ZxP_6g?Z@OYuG&02JzDAUUkxphO-jx%sa+6!UCs>>~o&Z*7Fi+yc`6!;s)4>y%z;pRzK +8+XfnS2f}PoKVqVJE^D| +k)ng>B)SSBRM2Eg^c1loP9y+w4;D#cxELi;MY_lk6GSdpzGc!Jc4cm4Z*nhWX>)XJX<{#QHZ +(3}cxCLpd3+RA)-c?iPA5$ks#zMs5@@ASFrvY@BsOS@bW62#Br0gsAZRe6pu|uuFd`&Y_e`2QJ;Tg1j +?UufjN?2y`i!%ZfHRN~$ikKo6{EOBajB+214t6K)O*gk)tzQ>=6T>2=HBUiZ297cuFA|za~XBKeB+`gzq)VewnSpjz`LuV12Q^mUvC3#otdcJ$?DvvRGP==~7dAQkuzh7#{w_(^{{GA3L7{v&pzmGM +TLSV;a1g>`63fflw06UtvwGQ0#Ky^EtPsm##9KX3&k<^!pOHZZMn1rzg&Cl4%)qAlCxv|BJO4{AV`RC +w=$ZtK^4P$#DJpkkKI=d-Az?oF>zvtEv`1AU^0efLcK|Nrn`s4eiApMB^ +H-)S!n7iATP=QyRB!;%dqomu}o&uo(NwoBolD;HAyZ0Ba2XJ4c!%M+QUOA)^-R|$l1)wDBoVuZT1PC_kDV!F9$Mv3 +eIR95|dr|5^IuJ8MLo#cP?xiY$ro&i}S?!;$7n15<3j-7W!G#AF*BzorJzt-hr3md~e?8{_sQj +>LS<^BHJBmfOi>?Mfh2x+A*N(5`#Q2#-zMX)$YVuD$ZAzkLNf3@*Xj=7pQ=@#ZzK_tfY_90L| +x<1%kaHeO|;CH?}eoU(A0EYA@G)IcFuy>F5t&D&GQhk<50fzY= +)6&?q2;(;+C|GuZAEM!O!?u*bXB*YSXQ3C+y2RcV>k)sb`( +LHzv0Tmo^gu0!si9#oZx2mJUW3JZSjy>#HqTDrZTeZWj|Qnu8ch=k4Q29%u$AvaE22>6HC4E+!y!3fE +oNV66-Y+f?%N78rau}L_7XoT-9!$c~$lX^VG%Zqp3dBl^Oq+c +N^+BE{Zo<6*U6$DXyf(};MG7x-Id#XifY)K%!5~cMR@@5O(*SrAu=A05t|hpQO4ZX7aBc9B!6WNZKZY +F~IuN*no;4S*-p?+q{#^~L4~)&jz2L%)_x2Fjcn{)YLwd7jrADb9bp!Sq_DD(JC(^hEY0?hZE4d5r9~ +^6ZUlvO21Bso~9TBKsQYM*NU}qyW+k&-GM_T=Cm&D$K5>u4LE+=g9Hpt7mpw$fB@jq~{^rr}(k8yLB^ +1WFC?G@tQR1Qx8+%ueIrrcu&%qQFOI(5f2P^6wZt8Aarg#EGONVDckkvCk`eE^y@hpUGWZhG!212=^# +2|=FB>&$D@9aA)1S8untX#t9c0ey#C5C$ytqVt`F_B8y}GW$lvOSv-1j^cn2(dD{tS0|#1Y_yzXy +dkoq)``V+GIDikX)4OqD#-e9ZJMWx9=L>cC95@=Qy4rrcBbl=)jo+>GOC@xFoFg6_yU#DTrk4>Ky^j8 +u9NhCHr92^KEjgul5a_?f~1_`SR~=(>}m?NF59%d0yML`_CkKoON3B0a1juhSFuBRKk9&P~Y3=#JG1tu=OW$l6XUj2*$G1(7kk2eQUc?M(yX6JrQwQL +ogdSifcqVU-(?6ifb{PMe18`b7>ON%QeUSc@c?+9rr|dIT-;(aAOt|cBh<~YRR9>~R46Ael_!gAzq)A +zje87~{VSG@%(3Z?o$Rn{QwkGz_HlJLk&sf0)7UJ=718&VK**L?{{`cfD|MM1u7jm#a;QiZy +9NohzH>h|HD`y~Kn_8G$ceaem`PUs--K^VL3R-7WFo`| +g;{1V%ldE5s8-62C=*Qs!;rNhmqa3d*)U|J)Uz*kp@;cM?|w$>JyO(p6im^C2B5y$3~L;{%Gi*Ti2WV(&RKrZ+ZwKovD%sK66O)!#8(X+V0+W{9$p +;|fm{O1>d4W6wX`8 +P#PDUX76TuY{6S#!Bc6$p~NLr;&G$Iti%$T7b$^A1HhvlkwKyQ$!TNGDpCws@5WxA0i^0@Yg~nFZ!yi +sN@G3_!i%>Elp~o3VDQ96y`yfh(gs@t1bn81NFlNaCT26vjyfaD6id(@?Xa|wJ4XQjG0!z2aD@+ugEM +mP*Xn&qIC;>}P07@cVgOV|6ZpipIDt1v;yjwE|@#}B?ljQR>Db00*4$aNv +b&ZrjO}T-#&jtgw!^AJ_FB^-sZP7#!?2(OMypUq|BBIaH4KbBJA~+)c~jdLgCqFzz`oFhuHyyn(v1>% +=Jey_gN7q!R~q+Cf1Kkh(8RJUoA-Ed)^1QifFs~)ET(D*uRPjVPcTv9m$=!?=oQihDnW$_KLnnT@NH3 +Z;~3@?9n|SHS9L%zk$l7+d +JhwuQ1@e2O5SKTm?<8y%Q65P+3>h)Ad7V-mIA?HquPo0+I +&$abE9D!P^!@RHcZZ79qEVyyZgVw+in>>f~bpi#sLuK8z#aQ0oBqyoF7Lv(zHEXHCHS)mKxaBodLF5V +j!IN`|&m)ez=Fj>&mn({61@g?pPkOi~(QADD3;d;vEJ!-ZM?u;jdxxLYT(P +z~|9bf{Nd8lTIzfLT^rb!`r2FedU7DFQoO?FM+wr~g7PBB@XJDOr&kV$PMRtj8Xu#AW*Np8nk;6U +Y3thyDcwskAek$_E|aU;G51Z(}%RM<=fMK*by#ER^!ma=6&kJ*N(-sx1g4PW(A4M~BK3gt;^F3K_k(X +l5T7uJ~*`xvKbOJMyfL8x8j6z3tQ4&027seDg}YZ8#}S108wMrhn(?+JOlQ~ngWR +)E2M>Zt+5gk`Y33d%CPSZX&Qa-ps%r_qxSJSpIESfyO$w>T$fx#$3--q=Qfv6rA +EA(o*&0q{*8Y@$v-d%wVXOFqo-N|*!rd&c-D4vv3S^;peN?@zi`M5!2@=JoA1Gl(xvU}wkE{=uTB*A+U!2Pis%( +PNY^{ZLH1Wv$?K3xcRI^H$me6dXF2F^vpws_%VOzA=BzOumj)l7iGp@?iw%pw|?slBdNO~4@|rcxh{- +vX1Q4OiPCmwHD$5jQJpOh|pg)h58AS)a%@o0|BZVAjtUm`!hu*j23fo}w(k#JfIx>f}RQE~x{gjbEOop{-ge?&G*4NiX3WUa{-G%d!wxgur@e0y5bL4)@XJ$V}xGBZJUfl>(wbp7w!{c}+|j3HxrI2+R-gAe&LP^`PznnG>h_J3lZ;<=p +^VH>*`xxMdYF +PU)O8T?9rM{z*(w|gSItLJ_U8*^W!tl!JQbZIacjL-5slG?2f3SYtJ?cx)J1M6ddE;DQyydAask?W>1 +{k64KaG07ek4vpi|M0}a9LXHnguzwkRGtBuZ)dt*67s;$uJYf6u^WSv#=P&?=>(Bzn()#U^t0#`w=bt +ArlF+mZ2^f8=n*@GPkFu0NV;!)u(}HLzb=>mS~UqFbWY2R*=C8GFU+dD;RmmjykaghECN`51m8}5hou +V>RV6?QeP+M4b`vC>U#AWSH9_Dh@9(yU}Hz$(2?qVNP1`GaK0~6G{qM#>hUS%s!3%w(gfujI%DVjyY7 +z5al|b|{p={J9hLxO7qgSa$QT}z!t-2}2(M#{0bCr#q29pd>Y_1mB5))thW1vGylANVPzffnf4Yd(f) +yGH>kLefukTyGyYqI4FgP*HXA>+#l8T(g0>ti4Kgf~Cwo1K+Gf|w?!c-UN*mLigF1 +XU%Aq|gB&SPHL=T;gUVO7i{w@|8Aks8=o#YEk*(bARm-W}Wb|y?%F_@Pk97cLRofAI|6!_4B)+x~VH| +B5U>o{$3*GsFFDq-&J~0o#MCjNI9+1-%>Gn?`4fXw_3Ht3)A9+VYm-HnJ%(zUUp3EJE{S~-Qi&;e0@E +hoMT!V-ntKYh1tiTdW7iZ#NR`cUZXi5+Yh|hxcP(1O{HI#mjYJKQKL`l2uAH{(9e@gsaweDT`tzrgaG +mP>CGUK(2U?7UV=*rd5%OsArcA`o*AF;xd7PeFGu4l(r&yCvM6)2oKnt5F#jN&^?e(i!qe>9As=grjv +YW*IrrWK-aSjL8Wr!W?jYVh`UxlYaXH8zS?pS*UV{Daot;%EguZX6Z7SyR6}v*|ACM@L)Mq$tJ2UGoM-it^EhNO25N7>X2Aa33SHTmf@wBbr)|N9 +jV{2Nmwt018ardPJGd3&c8SBKr!n8A+DIDl@lZ51A1m8386nRnC;Jz;9ciqHoQ*L-UYV1!doy#&hnzH=5aRwBL^&|N2p7x +@IqaOfh>(hrAAzs+XK_6<(yy6e6ex?DhGFC0__^={l?%k>||d!%_n%8h(*1)Okt37r$M5#)Jq44^yUZ +-Pxm5`zVQxtc*=%!_vvJ%+y-Ks$aZ@ov&`zl_v1Q+8di0(JG1(>*&ur$EoX&|HAg4n*^wJSB}X)y*f% +*#?Y-M9@(d)q(3?0**MNZ1>#^CY0f0bG($cE)(*jBQ4heWUx25VRz{xnaBiaGo3U;nKs;(Ik>t*y_*g +C!2%tG)@6YpUlHSb9Dckqz&l)ac8E@t0ap0fEG2dtv<#l$%*xu%{&s=Mqh*X7~@KvBfLK`JicH9!?^H1R=xHFA}iQ8sn9cyo$%m ++>n9BEe^6%O2QQ>}q4Uxbgz|2nxkzFo#a$jC)rgu;0>9Ux{@F`Z6JVuxSiEV4Hy!}!&h?#7VG9GhqQRRkP2$4MUiAz*xTi2#gd3PPnM{g +?TCMFF#|%q8lZ^5*gURPezphoQ&aGv5|vDJ;%ol{f!6 ++f!97v`g)|TQu!#4Dju6%72I55T{P`wnRLl}tKS>X-D!?4q##=-fe^lxIChtfX@+kf0zz}r;Dt*ra8w +8;Kn*!8j-Y$)&jw9o-LO*w6rt;*@7z(;EEDC}1cu=)Fu#(j;O +whQl)k8y^*>>_O^=o^%oJ7*FHnq7W566d2rP$7^z5Xh4XvTuS!IiQ|?gyiy)OF%}iTY{Kt^$_*The_D +K8_HgeWuaqpaO>TI`py4&*wAmLBRiL;zJSl}mM6zRN`*3G^~`|4r4RL+3KB+AK*F+V?Gm#{5znfm7%O +y+(&0P#wBneucT+0P)$iRv^79VB@I$?PDo3cn%s~Oto{3roDLer=bBh$7$eIB2aGqyBvS#m*noC7-Yc +3w=d{Tj1Zjl*P;3)a2f6`FDnu1D-k?PdUxFTf&%25C?z~%YaJ6P1uRU)IPDS!BK-ovCDNongKuF#v;2 +6O?Brq#}LVphJJ1vGR3-n>pfYs0A>iaf&*#3I?S--fBb&KMl$lniWzb0NN*R#B)k0UAp-`;>;H3glKK +ePA3v+c(F9rFchHY3_zhg`Z`Y$FiQt=2??4D +_Z-h&&RWVJOzH*mIHvO7plgLOV?B%!H- +`dN-~s;n-WN?S1%R%}lWQUmKvlguW6*n0CM#0?H7Tv+BP_*G|qiF1lH0Wbv?Sd0~~gdc4*>sQve3QYk +UB;NxmyVd3JyMZsTM7AW{wnJUIoLg~3pF}ZiH!ZhRbGVkDsQn}x5B_s@(h=+$)DA&=rKIVbhw5E4Re8 +68pE#CC&}NBA7Nr)CE1wRIA|7ZgOqOP7!$9OCVx5uA>j7EJk9*65L +`PW4fU2-oLRr7TC59E(FgfA@cfG)wZ(cpBo0Y7HMCfB;f`C?STi$tvrFK9h;d)3JFag-#pp|rkFUVc< +^0TdR2$8}!dE1Rx~$(rU23Cj!t-zYPMr%0j|kj?>?I^yfn)fUg1RBy+pl8 +VKG~6D`wM~nCxS#^NVx(irJ@rc1U8;S1W|j5yW%lPe{J-oYvxS5wKRx?i&Q921$hl=D(~ZgBA7r-95r +HuR>NXhAiodu(xyiSVDwJ{OK)=-to%y4-!LkF?D(e56-B1`)ML~u(3^ +S9UmFQ%|%Eg71{13Vtgo5VhL->_o7q&G>82a_esC#>jOn4ziyK5HwcdqYQLtH+%qPsbbg(lhcD#ccv6 +5MXf?Fn513i_mw;12bD}X%Y2~Sl6rmQO^ZXGD}07w54!ek!X{)uqn~bB@2nYGjZ*6*>w}9no-FL9H}? +5mYWB@mV1Rd3!i(X{{cBy>93L}Dg7(u(dx96I1=7$CDyel2@TjRu$3}JHXx +?=Xtu3G%qX)bVSOU&;N}@ZunAI;wfz>cW?idt^3HXu*cY7Vx5WQD!sJ>h`O9}Yl6JK$o6zbRbF2CQ=>az +d+!|G5Z!8+>eT-hs#*v-xbTtfe +Qz>o>b_G__O4hN~-(XeYjDF;Q=HtoA=9dJ-|~kA8Lc_vbxuL5uf}dQCGeUCT@GhP$C^z|gEGo|)F4LQaM@ +3jNmP%O2-*y5%p?j=_etCW7VV@C%!?1WJ&@BXO$Z5cUZ+_9Gi>p%*KxE}DMh>>Wf$AIp&=4)-QlH?yk +aAZmU)QUP@$*x*7Fnj8w?XfhSoX}RE~$ytQ?JE(ej%t3!z4|lR^C`5c7>tQX4O^kPZ7A4)K~}Pp{I-( +i04^5&%Y)!rp4#@k%2SVAiiY{;SbM1CVq_btA2gJ){RRl+!veNW0XuslS^tpL7P-`(Fnblw8furND1|tn21Q;PnpPb%!Q9DI4Y(c$B=B;DQFyHU{kfl8a{81x +HpSxmbIeT!rF7YEv;}6dR>eq3;nuyjpE>b(y`*q^IP|halIzb#+IjCIrM=~FQJa8le*;=AmPY7TdyJyl*qFMz=S+2%ygCq^4WbYjZW)-jnQmIo(+SUdHo5vYmLiJx_K0C)R8kuQu^299Q8a$ +yni=Ne?zY1?jo)92=)6sWf7*VB#6?je;j5u^%c<(QJ~x(nS9M?GPm<7GtQU-Idq<+rd$SK8BRWf6P6` +KikxJ#61I@%QMkwzF0yH3d@SP(sx?qqQ)I)76rKaQ(re~qB@Hu-t`h|FHC&1!RpV|zXxGrv^v%kmqU- +}1w%<;54u~y)k8}{dlyX-L71?99NH`Z+iD!0zX`E*W({ytOlTK3%({xz)q1^j8>8qsh{A$?r8+@#zm> +tD)krG$|*~7ZwQbxiFE(vkkC^)DyKh$+~4Uiv@o?*AwqmtuFbtxnm98CWie0RW;zu;9lU1Hvsl$Yo{6 +4jFv(hxy;tMM3UmshBpBC=y1SoT6MJ0-GCe|Y|%D}bpJf}fG;`GR`F{^BN75_ux`w4%)I2NV7Jl +$*)kiqe9GSs&F^1bO6zJwevJ#i{I9*d@5)bk?oYqw@Kw~QUs4ttVxhV*fKJy@l-OjyNvBb-f0|iwQWL +h(hz>#Ydk+?Tq}(0SmWBCLf6n#qYIlBLoeNp!jEmxPa){CAd4po!Di&x`iq31L^oa`_;& +SIE%Ae;Lh$hyP3#@CaM;ai&Y7VZz$kk7SyOC3&C{ZL~Z6tY3Rf?AwkWPXUWJ{UAt!9^J<#kP(mI> +5sSV4IUJNB*TObz_Lk#Cp(+!IvUgpUkC0`HHldR+`7|l9AyWq{08SS}FXbC*NmTT1@YK$8J>oB +~#@+1)U^kpWiaXSz$BZ7X9*Q0Wv^SG0!rl%kvY3(ZPUXz+sf`j%EFF@bRm2Ptev +lQ6tMDRv{jer2LvIH@(k|No$0Y%^?U8btBn}C+P;{%EL(5@-*izi>B56s)f8jRs2 +0vf@q!{rzE0l)YBe!?r@!lew(g(G2k<=fU<2EC6`WKqd$8L%Phx)-%V?u@CSHS9cBlkLSDXbOY8?>bG +4Y5g%uD9?>6gQ>Cn?_s4dStmgp>BCdF^6}p0Kp-nss|sisxj0MLW`VQENs1{UmFjY1TGFrODQgg`(!V +>rGGGECLo;EHeX*Cj@t&+2hw=07B6cE>8n$@vrxLIeBLLz&x*p_vjCzp+C4>HXh&G@>l)WKX(!Bu#M< +@tP}@2y;;@N$EYsiv?9zAC&l)1LK&mTPn`CLO1%ThBy+bcL;KZthm<0+6}g_!H<^zZzFsIJ0!9* +HDBSuhdj;;AT|mrtsa7krqAlgX9iV;Wtx;J$+ +n6usNcvQVFcbpkvtihj-$ljITg&RAk()u)Ilv>`BLiIP1~O_?7!w^iPpK2J^`l*kASr?Y3)TFiE!GsK +Kph!X?##)&jQJiNf;8Z>uWF^iCsO&$lo$pHst+LTNB4k|q$B1J8Ltr!66rLTQJVy8vchnR~e?CTw3d|uJCq(pqnN%X(~884m2w5o5vt?LLhm<@b8hL?BRG6Uo?$lNnhfbx_&?+}7+AHtC7(rT +$`tSBe}FCR7NV87x-#O-_iC`?@eC`V#68;Uqen~?<0? +nb^+_d=yMZ5~#w`QX>A72zVJYBS&=NxL2f;R(;L#g10uPZ|D5UiKLdgP06$I06q$+cM<#IR8dJ+Ldc1p@AgiL<=&q2Wi-hd@?!Fk;|m+SoJqjBiC>rSb3` +MxF1qxq^uYV_-84klH;_v30>#W$T$CQiJp+V6V7XVcnEk3wG7#VmWb|_6@t3v7_z(Es#jYe32&a{HH3 +5^#QkXXs&}uy=WzQc75_hBvn9{_6Ckik +w`YW*dU;;PcEo6qZ^aH6JA{G1eTv8#%)>VOOaPsapgueS8go)))ey_B^l%>^CcVZg&+O}U8~JpO2c>v +cpTw(Ro0sI258jXRCQv#$8}dTa0^Z}=8CLc--4ojuLZ#0yW$k@h77jsQIr>^_MQP=1f| +j8f*x>&2GscRR=CLiY>Y9mShm``RPTd5&(s?;@mUSskS{zx|Tu{WMg+_Kw7>O@m7Y*xYPXL2W2JCdu~dAx +?3<$9Ax`1}95PZLZ-*A$p?^{Q>n|fmZEigw5NsVIE!-BZW?-f6Pi7R1=xy)G|bT8Va=l@SazPz6~e(? +Wz{oag~Pt<=tIe|5DBMFIhge0ye=@Q+({;f~NQxpHqE}e31HMo)%Y~1YjN^DfJB~o1;DX^XR;xE!%{O +4E4TjI*xsjJ9OX-xwB9ALB?4K=Hq(wbYKl4*81bz+Xr5^%2xIEJ-GX=oqbs72UM0>xA*abs@xu(Zw%h +T$#ML!-4>bSR7>q~om~8UlJp&gXjYDTtxk0nRIW^ZC>1gC>4CuH7VBN9{FF&|lmJ@g=CpSvn$v1t<@D +LW@D}Tp@thMNr}mC=`b=OXYJ!|CR=e?z6((zM#cgt-zlID<=g5WkH6_aH+``v7{iv~95SrLA)rZF_2U +tIPN}(-)85qLos3a6%rtN{vi~iu64~Y6dvB83TA$S=QQOjsi60MMmS5(&OrDlsYL=%6 +8}o&YlG%N7r6Z>u>bxNvI#ivO?6bV%%h??`ff7l6R^V+Za?6BG15iP=&E~Ugr+4hOxbhdqdwV&o)PuK;3<4dh9_KM(=6fpZBk^d9WdP{6vP0{ktJq9_ByNCB!u +eGh^JMLzI*<_7CCWF=RF2L*~?A<;5UAj{4XR9b0eNB>^{~i=4Z*%^Y1asR|*@42^&STyJO`T*x=V>i| +Z|-c3bB?}c{Gq&Q(c?V=W0F5eiif=$*&Bt9Vv~s#x&1qU;j9@1EHJxw! +!A^?G9$oDqFS;Jpj-m+r4emN1QCr~2!@Au?$wDvyQ?|*OgnFLgn71oWC&uwXqCP+8`?OW8r!3YASRXe +AxL~_g+gYqdrQ;9E{3Fclk4)bBjmgxb4+#u2l6>ynxc}SGIE1>;qg#5vPYzo`=oDOft#1KoG4lh~Er< +bN5>8~{>3C6c*!nU+J1eqO6Do6=FCjt-1#UlWp2(>^ +*#6a|%J@=@F1+y=Tn$S+zlP`8hRrK8vGat#g!qJ#ZP!L*3Vy@k1G1dDJ@~uj)7y)2eCKJea(1(7*EE*|i`~q|BU;I-|hIO?XD6 +4gt7tvEDp}BPMdlnbOpwn($jI5zjs|ihNy@5 +z!hI#S!K4N!`m+EK)*u7jqgSKrZ4+7OJx8o;o8IFw4@u}*GCbQ7x)yuRI`296oae;e{rs5Gy9}dum?e +#OFEiFXBaQcOXRLo7b1nG>v&IYk%W9_3oAQK+%`rbA#}{M-#IRCzWsT&pN);pf|vQ2P!mPus%G;1sD0c@*dV7}J-LI@O +3eK65P!sK)Q|anDIM}ymmom`Qao1KgwufidgLMM3uaLNf|(;vm2Tk^ZJmbM3ugDo!zCt=WfG3Xs)jM2 +vaTOF1UA=}@Mbx^X>lU&xdp}8b1l`Pb9azmbKGAGS4zb4GyH6r-`y!+Ero}na{MX)^kR59(FYIzgIW^ +hM)Y-#SQi6q`rRF{_im(^KTV`~=Q?v7z+A3Vo0K< +^Z}$5~%%Kvt**g+Ap0yQxJ)%NA2h4GshEnrl$dEo!dm!wVEBJwyc%p>nRNX$0|Q3P-U<;-8>FK8WAb> ++o9*T(AiY1-vHXUJomL;52^qFTnF};3cYtpC_&)^jmk>UI9FvdE;?3%GMpHUNOZShoy*Q-rS1xVsd6I +QO;wT0x^Ugx??}(-36(mfOqSmGrtcoO_^iU!xk-7bu|&Q1pLe9SK{2C(G6prpU`7LpI})eyE;83VOa< +VO*M1=bkK*C`SKCotm^h{Sa^=apEmNRQvUQRe_Fzye#xII`O^>i(<=V7mYx{xL`ry!C%nXW(0g0>hO? +^kA$@BmC%)OkX_Xk~o{>FN0Y4Lqul&kqUGgB=3;t$j +i=M%~I7}XNc8_`)T8HkpoK1be?=rD%`C?@Q*aB#oa2SjxXp| +o*HQiTrSkj2ia(A1xiNbYFzy^;3iZ;^cbAJP`6F16zYoYo4NbS&J~kMT2zPcAp?&Tq0Vd9W?~N5J&0U +|jmb9cN;YRCNR3XB#z}E{{CGxB66SJ>7J~)lWcaVu+v{dQN!{wNAnmWM|h2REZy +)HEi9a?Pe(jCHY35SsGKDt-H2Y$b8DBc_E_z*cqw!+e=LsR&QU;hav?NaZ0lt15MTt +A>w`(4yx6m67uOE5{(E_ +C8?kNM=`vdj|Z%Nw<`bB&yM4;r%3#o8*kI-i} +p)c66LqFB#Ysq){)thqnal@fl0rIqz&HC=d!?O#|{_?9s`%PShM^2r0^Gg>8LwxSDVgYpe3LKV}FBS$ +;t*Tp9&K!pU5;LT0!=*a1YC7j+PE!XtBIDR47H~(5u~smdL}lMGFSv{^_u{{u__0cBzFXibF@H#aM|t +f|mnPSn6T}7>coLR;c-7%?M3|Fe**9A%h@6A>z8u+CH9`W`w`k~7u4H*0P2O}8~%h{dk2Q0j-jjp( +NEEYFeR0z0Ks#N$7bs7PCPK(qkaiuDj7hYUu_txJwH)SOyo%qV3H9uZp^Rwc7tw$WDUe`F56FM2kX0e}rH}9?c);^7+VCX1?(;KnMto2psA%*mK-g{=u_Gcn!2?`xGRcros3OEVJ +Vj5~gFn;xx!kXL)G%K4{=FXqyt#S+8+r89t9Shd1y=#$kD3&Nk!mK;+R@SdR%7TnYg34&v5OYqKsEqGgtDO7n!PEO2+VDXn?dbbOlasjVu4%`_PcDBDV<$gO?68 +jRAlgJpi3YBp7P3S`%VLhxE*_|b9z_ZKSF{N>KKOc)6^vLw0K@F)$(qb;<|y9xI&a7CA`Dc4_;k;PN#Fz5;~lEVQ1TzV>Z^JwAkEluc_5iSoRGV3yuw +h4y;|{Q)akyc?n(yN9NM6peGC|^uOk5Fmpky6QWWuu +QJyD*A`xZaCwl)ct#(;eUbf@=pKK_<_ep||+-9X@5d&3$@J(96E`v5zG-b88Bjh`MzQjx@tPAg5LP-L +~pzh>)bmg*G6zn!c`jjN3S_O=&y|T!P_ +gMpw~Q8vy^C&VOE2tHp}&n0%I|3s`aCq(y&z*~ua!iLo^_JdIE`L@)0WUg6Vh(Ws|3oUpAZWPZa+2FpF%V0prG#NIu}6OXEw{he%_ +e_$mW=i?^5N&7Juwz`pJXt}^V`GUC6Viqpi`!36jq`=L6VixWT`Fd86v)Ufg*G^Oai3$yNryU(MTh-y +qOeS|rKww2#!G_g>(tS2ul%=IAbF!SuUX*w3Xfw?NMdnN4^mBRlK^hWjLt}XqBCf^GK1?jRY()o6C;D +##cZL4wyVRK@$gJ~w&~9JXxQ0REa-I)0(H5G04~ypY!V=tORzFN4KSmLc-f0!qjYN}nl*0Yx2Qe-s@H +Mw8GrCRkl3Urx?%r9j68A%^Z`YCQoAqjZ&oa7ciSk6{tT`}Ty&er=-7WGYy&0N!Y2&=1BY{znB?(q>4 +1XyK9-X2GGlyouI!1m5)t*7QtSMTFmSMC=ZnB0rYZg2Me630-iZ6ouBEw=Iu?}U9rg1yt^T$m=S|XF@ +eTES;^A7mf8D*W#9Jov>Z{|3$S&C#-gQ$NEDpevGtx_0R`^i5e#CnuAAVqiNVSdWf9yoY$M_{t;v|_wE$kI=4L`YHuKw4t2wYaK8+53T?u#1$_jS-ooAzWp5W%Oh00W!QnAGq7RHP7&!t6# +g!aOfZLD<_B4?vI?YFKp%1{JXJ00i;C0_m2)zyd=H7Ie+3ExSzrU&Iyo4w{vFK~w?scsVbs!27ho(J# +3AP6)QqO{WlSF!87Y#p>b?0vp?L&5VEoueHY)@cMRI!0(8Rk +&CF{V(|oC9E>Mm_*dijKk&0QQ90Nz_L`FYY@a+rWQDfk@{;7fF27};RNm^Jw9i-G1jx}&p+6*>WSL{@ +6)l3CA#4;aN_UU+yx-F5&)ex^ZJb<8d)b@wM?Z{5=nAMV4Uek2yv>mxPkufMJ*)c{qp;W;ui8S&aL#t?KLJ7dS_p>mQ*Uw)? +{)UFf`HE6zLM2T3o>pVLt+%zFDFKN~+8?Qx6UZ?70tA=Z3m>M4%=$^=MBY_?!(pvTXkEW`0B+wF0<2A +Ey~FkR^kOEEp%PFMhS0Oh4QSyFjVZ{st7@wV*2rAD`9BH18bq0H0USSV+zzuOasiO=%%-bS-LCj6AGx +6v*Hx#4(`UHNoC3R}W+vVi`>JEi*@r8NmSfY-=*;%fEsH{%W$@=$fmn}g04>A((OGLtYM9diCpl?k0L +hzk!!P~V+{Tt?1P*ygj$&8;@+Tj_VpfDZmn4x^)G@Z3{M1E*$# +CaHSIntgmyqqVIykWcw~rWgRsGQoD0^QHTsl(Th*Py#!lZRv+>U-ytxmO3#%qbeeWxurphV)oJJ{Zmn +^NttktJ<-|TCQrUVN3Ln=I2;J>ftBz{I`MK0zK- +wwOsb~}_SgliZ4bwhc$sPNYVfBk_j?PrERj?gp={-6Z>@FH;2g+EjXt}6cY;7@;`qo+ED^*=niKhS(69LrCcus* +H8y+^>C9%8hz#;ahnoQx@wwMl-mKB6YQ}67oX95c;ewB?Ajv4sy1w9PNSK~t$ecYg3id;8y3K|SbO8pNSiJN=soygGV=dJm$zIIdKmA;YsJ?AYv@- +EnV-KZhO@%1eZbHn$t+cpB$||2Q@4?~ +IRus1E@zg%HNN&GM_t!2v%zrY$m_pWmm6?k-W7)ddJ6*n^S|1!Ntk_O-@XCpClmK!+ +OL81uMT%Il=qSnbPDZIhW%i{-lJupkWy&~9#F?7_$Oi}lE}^?Gsv-h;n_+x7n3A>xg#Lhwf>(zgdOWS +dw!G*(7bcIu+#17GAWvp2G+&r|Ek9pVihkspj@fC>tr0{QmP0lBy~End#@fuwJPBK=sTK&%E?;;Mu)H +)5G=C^KfT3@>mJiei}?W6R=?6o2SJAv9w;Y4CNy)-)S@+C`TP%oHK|>bOu`4vbhQw?4vlN8&n5}MA10wcxtu9h-;{=GmRwu1X02YFYdh^L-|k}{7qhQ@mS!pJRVi4(I22y +~iD#9lD4H==2x8PF@y6|PCK;Cc^c~UNgaN&(q__Ir>Wb06h}VpHBn7QSkJK@&hogso&T2#6d~&*y^WC)8vVs+DWmR*LzEAJ=2G1ZlC+(Ksvq-Se +q?rS`Gk=XfO{ub@i8znoRf-YJEGT>#~45-r5ScP#28J?|w`bp2>i1piN9{vXIYLk~ReAf`;#52faC8R +zJPSEQIzY(cy&<#KTZY-_>iW4xqb3^e-mE9#s>RQ_1T_L+`4FLk4}Du+cIr|DUGH+s!8Vb75opEMZH~ +j87Der*{WJJ;oVTcInXo)T%eQw?Uod+aybuw&2|9a6?nI0N2W?q&36VTViv9sh;Tt$|+0Wg47$arkpN +q1k6w02gVGQcdDzAxAlJRXOESLIUny+J#1GoYerTuuamxH;OF1VIU7WLfPrEiy2BPCA-)DWjosY|mAR +YPf^HDr9t-OFtwv`Hg=bs5me0d$a;4}W&_cL#Gj2{fvqCHw^=|=k5dq{wg8)figHZYbCa=Pi3(>t@wi +{7s5%QwkA;SwocEbPy=2!sCq>2K2UpU|8vLy0 +LqygfT5srK9IU;bQJ~jyW~Rr6vujRSF9}r)%D)@6CMjo8T`Ho!1W^G8Dws>OD_u78GK6l6mrmp1w=3?cAt=KKH$%O`CC}t(*vU +5`H(ef_vnsc3es3B;Op5B-X407X~jeMY=ou8N>|M~UHUMI>*Br1F!BjV4YoqOhI-ym)L34y>@e% +6Np4E>Rr2fsmG&F8=id|o<-CHKLG^gC_Jr)K#P)+E;Sle6T{d1$4T+fa~GOSZ+0`0fTa<0IHR8>ncn$ +7wUm!#w!8t#{$gXbi2{r8(3I@ouSkR0r%Act7V+3U@V@-b2Os83!X1pn|Hbr6K6Vm1#6BooALXw4h3og|yVL@V7k9HRCx;HK`|3Nw#a+ +}#I(L8^7CQJ@>z|s8v(`|nIl)J|61gvQ7p>+_(&%35VJdn<$Ni(xy}bbhFi5c7|IiTg*-X^ +2mI*VdSS^z-B&y&=IrdUGm*SG(R@lvN09%lvg9XM|ocakGmX_$R3Bdm)dzMV^A3>m+}917y5%9Y(8e6 +_xIZ7(ljoX+vqTCD?`=Si{J=kY&g^be1d3Ul*8)r6*wNF%)xl8wQ#sNeP3^{;ZL<{jB_I`AYR?TjSln +1D+>@|nXi({2Ec|!{iKE +;dTNHw5P)SbQ@0tuJ?#s-E6L&V<+$qcCz`fs`=)j$6HXOL$K=gu+%Z39to@-ql^WDBo*ouyCU!<=X{) +-j>AEdD8wjJ%YaObmm(ZlEt#j-cGb13-BHBjBV=snxFgZ!YaQ2t1|k4+HWI&0a)-L)IBCCFj7;aG-{k +?4U~1L!PV#jA^cX1buJ!IJXjUZL)`e)^gk2Jt9nO)5GubD!=pP+u8mTg3IZ^urT|S&>vnB!%sKkV>Oyb220SMB15{hDihD%Afz~O#JDiE`&@ +_Y5N^*zZW4J@Jc`m=<2(1QE5k4|j#QmYKq6NZQM#69033@blg0@pyf)g}7C;C4hv0<%gRO^NQ&tLS3? +$e?$_cY+n#oYfH-wpad`~M&OpRtCx|1(CD@P;%YINHoVRaNcI6@ugv04y)OY_{Y^Por+$E>@p;I8cO8 +LftDcx`tKN=qNI*y=>6+dC6yJpG1DFu$t)htYKUL&&zaO^I5~|c>?+SddR2w2fI;8AN?NIGryYbAgJRppXY`2zAf>6wR;-5J0F +R;J0G|z?n_D$%L?YX%7hU5qSE?J$JMf}NJWsF2FujD!z|nJAtx3a!LJR2)Fhd*I5m|)l81Z)pxR1L&S +I!E=PSq(^L>UfitL@gIMogK(L3gSDen7R0f3o*E$(Oce8 +<71QRQ@1BNKwp9#md6d&%t!k>{Gc13Xv67Q7SQ4Ov!4^iMkZ(RURY2QxzRdXAQ6Tpw_V)vL_c#(k@g2+>l=Fe8t@xF-JMgg~m&8Hx0VwryyaEoLh} +TuHE%6HEOh6F=Nh?CFC~VwK`uivfyV=m%TV3a)TFO~Fd#4{1SA8qC@2D7f8J +Low5U-)(0>N8gaoQB_Y%v*W`eKuF;@iY_+d4cZITeipDrI%9%!(IBpST*AeZ)xPt&_~*bFO)sWeO;P> +VJgWv8fxLr(WTL;yr5GS;dc^jwCXcC@}=-QO&$Tixk4zF*IVq+f5D^O~_9kgc^QcL6Ai~bn3Xl<)jxMwopH&!f_*e8DWjj!=6h*Z~YfF$MML}}>(l&HQ2fhvmxD +ldMQpo|MkU-x>l3|R@bF}yfHA)2*O>aoAag`ia4Fc+34EFfWNoO<-{ygO}!8Snagg0vJ5^}g`_m`!`{ +tgJ#G>%z|ge9w+-k#2&PT3r}JmaNMg16Czn*OK(um(l){ydu;Y@`_N0^2)QwxBPG9m5bT6027~8NP+# +Jb`P86V=6o5XHhxBr>vsxnulpc$|MtXssIG`ViPxg(f6v^_)!~WM-sFha47C80wXuwOcE}>S9Lro?p? +PVLugu1C2&E=GiS`G@IS|!dT^uZ|03R0C67177c?k)^~i1acp~>-5|1?1Db)5~B+}IS9~qIRHl7n{s!@t$06)|ySuohtL +qEd6OB*xHVq-KJPZpPm%1N6L{D!_(+vden-{GDHT0N6c7&?@aG7_kblCgFFqe#oxd#wDOPC&waIMHeD9Oo#_nAiJ +dRj9GOjmx7F#RgX|@D2n_D&2>EaqlBOxKO$wsu6oUN@z_=SqlX60Z8R2yN~8lrib6vpiM1?W3j)I8SR +AT{&Wl5}nBq{OHiXBaO2@Lx27^#}+1cpR0~bc1qSyklFjTAuNoLtOQK+Qo3o)rGe_${QRZBbyRhrq5u +C|*JqEP+Lkgy&g3G4O5C{(BjjYpw6k%U20-qa+RQa*B=)p3p3_%h_1|7{?u*Uw%Mh>FWrhzzz+XsO5} +$ZV}%gK?_|L{FTK$D_L0h)4Attf9M~hocha_G4}5MWcH6ow!IQ1eYX6qmmM$QC+>&^c~Tto_);p{~C?z*c +KGJ=1~RT8I5WOWx6^s)5W7v&EUX` +_(`6kz_U)m8vX3GW)2Hnz=|+Dx>t0J`9e0*T?vC`fHAEY+PxSgILeE^r!>8jx$uW<;m +DE{T7A&4_8GHU7s?Rm0+;s#3ozRMppJBUIHFW*(}l%lw~0Rc$o?n^0B1FrO2uYQ6cKP*qQv&k0q9*y- +F*RSV4tp{njM$3j)1;>|-^>hnWYk>*5oouEaC?v013nsTu)RT5GWbOK=mkRp)*#<@|dq-d8Z9;Hf(?l +S$`C{-kZq5d--rK&WF$18C`EFPt*l)j?#e-)+bZi-TMJ4LCwg`!jy@+egW6s0OJ7NTldq7e2O9~oyV> +KDO(8?8$9y&C6O_y70BIab!J{pq4Ho=WhuM{(=K +hCpgwLsT`HjIR!V^WX8B*T60R>aby)D1YkXhf*uMj4uf6Q=j@i|NL;S$mU?s{n=8^X`wE8;`v`27V6T +^|5UF;QHS>8XL%d&v%HORGkwynA*WHB?O}UClGtpvQ#j};^i>x1H+RyR=;f*}-pjGjP5jjm(?WSaWLhP$cKU>l+KVBQ!uVMZj5gtCKvMT$Zn8P&A8mN*A=0tlNFHW+ +ood}3_`GTYIkZxvfgf>KRSD-e9@iQP4nTee#$tziwMhZQyF=UmZ@!HUGHkY7k5uGypvNetWz&%sq6np +bCVp`ZN$LA^id9x*1o00XjQk(=o&+qaBk3L(V7MGmP`u)(cqDkBprWW@WY9M{L{vNyHG(h*iZB`8c*O +$*vf`5sNzBG1CNan6+Rd(;%Oe+f02)*@CLw0ClKYV{5pw`W!~3hc`watvaj*aQ{vCW)_1#@v-CccE&( +P4PjfJ#oIs62B&9TI*&)e)C)teCOP5W`&o;Vm4GQBJB!al4pc1o#DBaRZ%zN56jGv!~`& +GSjtCl!jk+Am6L`JqxV}5Uw2)BFDqTfb8j=*ijsIcOxrzAgNTm=EmSlP-?*avsCK~N7kk;!zbP(7Ble +PCn8|PQAHiu{=`?tP=QyN?DsTV=K1U?*Q!$iTXnL=_78m=`l}nyt8Ax?` +kiUD1ry7_QME<}Y4|O*z7MSfm^B{Rb#5q__<)-o-d%KN6ih62v)hVxa|t=`N1f5e0Cf@I6hwrC2 +Zz-7gnn#-*o(FRFESUHz*uj{Inb4aKl~6b70sNm%aE(fB2`!1!4+$liwO}KZP`WgnZDne|I@8VA$-4) +IQ>L6E^cm_8Uc)Y=|2r?^aZv22`y$C-1;@#*ZhB@2PKuUzYPNe8l{>z6@1(a>shzZyKP&;6=zkZ)mLE +%Rv%M<^grK3Vi`MWi+EnH0qVw9i=naEthcQd-qCM*%BF8?6wBO6DgEno#L*zpOUZzz(j8u6oIN4Pf%| +@_}k1)Lb80(#1-iPmZZKTotp1xm1s2p;gw^>p3Zc3tb$t<-P3$4h-pFzttR%tX`#`3Y0c;GGAsBiOQ2 +RVYH%8S2^dwr08XF1FsB^l&VC4uT*@!9UV%|}qG^sh&u%SkzPHXWUNbo2nOaYapHUzokU6qny>5WV4l +U&oW^$G38ZLYz36k7@!k>5D!mNNa=Wl^^Tt@PeSuOVl1^bG(70GnY&b$LtvuZ?4b4iQbfo{MaR86J~j +Q(Q!xr;wG`1P#Ukvj~V2kFR=I{>57}698%-y24}Ex4Ab|n^jQK~#4Llf +4p{_SeH=r(#c2ue{dcH54JA!*q#ATOrVpKjGEhtZDGKsnrSJ5~ovhj8x=AqpyZ`a<+F7lwy}Pw!uQw3 +Ut%Oq~K_{b@Cf^*??YrH?5~-d6P)VAu9xrPpmBgb^k5>QZ{vVq6V&J5!#0jV>4>Wr@R6fUyf-L!)z7c +KKhOtKRrMx9VnguTfTvqbt2&EyHgxJ$|0Tougb=dU~eT=14+t?Pb;5Kt!K^5j< +K})7MU&YSg-KcU?N&k-lcY3zH_$e}xWZ8A@H^nKTy_Ztaj3uitdV!7LAPO185dHwsU`LY+yMRJXj1W< +WFj6>ug-W8P(pj3QVGt;LukE)0gI2)K~BreIpZRUP@@yVt&$134L`jy;We2`dA4Ka>I9`7w{dUTHL)D` +|M-USCGu{H5!F9s^D4v`p_Pj-Jf_m!z-EJ5{o8JTM!fy4-5|jE0hXhs;*PAQIClv?SLybDo6E%{g +36llS>ZtO9iS%v8fP?I9#yMfo`hPfFV@WekrgN<`7EfGOi3@>Q@*?k{Z#_;W)b$3XagV54Xhvlho+!9oJN;6LpXzfh4~*;sMn-r)PrHC?%$t +N*v|Jx2T-n5G0Po{|4iKz41SN;pI3-Q9^8^^r3#|A!o2rI_(TFO7A;;jM7=BhxDOTd4S+WnfXTsR~YF +V?c#J}Y`Hjb3a)&Mw@e|Zh4rQ>cy~n{F$K@*io>T6Z`6kebKm|A;i&yV`qJ6M_$Hp=b^06M#Csi>yH5 +{bVM-EfjKz&&&+T5ffb`N-J18OZ`TSC2f!e|?>PfCq^_fcFCjIrCOX!xOi+2g*SYsWkrTcPM&eEz2JRDv^d=bWWZSXOa?*2vTBF71Pd}P=hD7B +blP+MA;~PU}C-5h(*}(C)CpmVZ{=PVYF(c%E1+}6^?j%0zR&&QJe^IsF6Y;ng(f2K +n!lxV+@pu-r7*WUYB1lZ#C`WxCG>Ce=}Gy8PzJd>79^(OQ7xZbbzjJ3B^pig^`d!*MuYmieuq>@tXD# +J&&TU5ju+%N9z;^%DS78^W9(6fw=w@zoYNQ8X=nk}3DW#}$zM*1#oirL`sR?D62Arj=_UBJU~-tOY1> +8}$Zr0fW+o=zxVmvgxe+>2W&0@0C%w>1-cIkdQXN8vOU2&aQbKf!J}VmS6lEQ>^d`3re#v2=#zti +#~v=28zFT1WE~YgAX~h{+FB{{*6%Shk)JVr=X-M-V9SJ0E58fL7PJy0KCkoC3L!_C64~4Z3&BQ+dgzYJKq&XdzWmP8(b?1v!izADu#b6C3Wf;lePzJ7Py9_RBR +v#=@)GH6!7FFMToYx9HzaTx62I#x3Gd4cj7~4zMbrT4@C-w;SHJLrMcJ4o+ZVdyPt*7bd88An#L2T+Q +B2l(!zEbki5%`-s3PVqfHu)Phab7vFe?I08+jajjTvs%u%lFQIx$D-2MMK2Qe_Tm=4|`+%$8ldd$J#Z +qB@90O?D&@0J7pYRkyTk(R;>P2))re^K?zfxD{6x0H?4oa@Sr#o)?Lh1FtX{EVkh5B+lEtJ0K2WCk_* +N*3>Wu`Wub}=9Z=Gyp{Cr!beb_Bu-rO95^2@yx+*qix{!5t`gdDbVyUf6QZsl|FyufF)3wp$nRW|05I +mTUOjq=Wy3v>qJ}RZ9Vt5_kxX*-{|~mDl^|Ou;d6F`H@~2HMH@>Ac}sbUFj?eyWg}^u-V2I8oFHIi%7 +Xw&>$?Xn&^mMXqiLI;G|T*cai`kyu=#^q~@JnaAldP6=H%hxRg;?o$*?T3@qtm@dgc25>C~GcYYbJxt +dM73miP3}i?BfLOBQv}BjnlARPvw0=@m!^q2Yp}?goYa>Xo<}#dgsd82vm6K4G+Zb-ctp@r8b(lQ*zP +6$9jWQe>>4Bj_z1vIG;zxxW!`Bg*Dm7PZfeOZfmVj&mF7MDQ+jt1yV3Ll5RDm)yN?h_eJnM(M4$%-e5 +{5E-)K5s$1Fb2cOG886X7_~gjktW*@&Do)%ore`_EC{^IbK}v#DYXR{B +Xij+qPm7u)sIxO>n7Ar!cj9s3J`5xw{XPvYBX`e;^>75z3*&5c+3^*)==b?WRIs(4!pp6WK)HLFx}4; +kR(x#TsMtW^Ekg2!(qr(UxW3q)bFditw@o@-VSSvlyZqk0uGOfNd%5b&;jZ~ncDR8rq`}0)>(BQSRRe +t_9)S|1pY{7L>5Gf$TFh~IA1=<8jx2kOYVH2$P1eJX!@IM)Pf%S3c$o8)d@o(ZJ*D2D%|C(D7}pKjM9 ++SIb$VK)PmJ5+W5Tyg_F#P-23&B0_0cp+vwM`2+ebO_`cZ21MF$Cs(vwFy1`SO$EJ}RCNp7fV7v(uHN +;2xeTJ`NBv*(DvHYMP_NP6eJG&ww+YAQ47_nl-l)kDqaY?vg^HiZUJ3L&Oty|Yq +ko0&DP$Nh_`qy=+9-zd>*$p~s+;f$Ma!9cn_5Qwv?{6H9h0q(T^!qBEaYEW`Jht9iGFu0AJ{01~C>i4 +n71CzlQHoZ2!+9OBu}?+`OiFw_Fpxhg)Yx}YK48R>qxQI{8dFXN(;Cb2U@V=3FUBw}A0RhkHY7o-guXP3O-g+ +6>glso2gR|5eCT4@y>B5uvXbqr0l&Wfu^?>&xrI#A@6+Bs1Sc}CXDQm=wqTD^)Li>cM$$hV(>3}Jgyx +Tg7x0{qsLv?d9t5qS=#04Rc^}6Q=KV@5{cSeCL6~NQ0znE&#K&CAbr#R#Lh~7O)M@kJ_e5Ecl3F0#oT +4UihEVZM5a1I5AIO2a-2}I6 +h4Olv&AInB)6AhCwUa)d7ZR0B}z$@E+t4UCG%mt*nEpgde1CX7?tC3F)>sh2tJP8h?m7-bd5NqRL34J +U8LFQA=Q~A>pH0!Rhjw-lEx&ZXqz9yi4(g?+xLu7s=-~n!q1p`R^^Zqx)=9PaHWNA|Kbx`=KYOqML=I +%zWGk}M3K4qCm7&8?6YT;T1Ey51($IWxAqpH)TkF?E7p4A38KRKMtQ>L<`BjlmK0%40DhT~-W4WV?h>$@OTg#zWUwfEYBeT#GgAOtfH-O +&^&ORi)ogDhUE<8O*@ko2S}-eZin#DO_t7TESQLCI=tlB893g~ZnKyQ7I^F_Q71&5kcX#Jwbn3919M8 +ioTb(vbrE6dHFr5}s21=vz20G#@HDwBVr)g>`zhD$4lgHc=_$zs;o?wHFF5XHJz=B(IPN)E*xKZ9Fz% +t@s-zF-S|#e;h3+7ecLyoFJD@LBo2A7;W+^YsEPYPj;&E?daoENpJG8N(+toHkBu8eHk{~rC;u-Uwi< +=zY&|D;&wt1-y$PX=~?-|O0&`_|A?3j*iq@K5t-tS_ONUZk1x +Y*;q6)yJByRipd9tMxw5~U+_WjN8ZT=qJM>m(=CHY1&+E2o@e>JykQTMMq|K&!-8QcfwM#_4!+za1k? +hnmJC+8{STy+LHl~Nm(iYOOsPiOMhn%-^P;&x#uUs2EmQ$b +A!d?k>N(I#v)iHk@RgZ8t2CSBG}BkS|(8SbcMX(4OXuRS;Hl@b&8FAWj=`Jp2Nt?rl1k%{qBf3R6-gk6}odJe(m0c> +qI(%RLzqDF;%-U-n`aaq`t=>>UgF2Zkic-!Np6{3%0H?bKoXzmPbbiWk9FOE=hU1td3k=8JMxM-Yd=6WVWH_Ejl*1X05`i4X +a6BI;2Qa)5al-Hx#DBb#;yRPtXLP9NK;WjH>~BOhTnKI9|sXE=R?=Z|-`y`Hm&;dotK-pOz +jm*s5?$0M5ZMuy|#JF<=8_!yv^&TzadEiYntKH?UJ(Hj^Sm9M>BjE;=>u9iFiMTKaY42!}lVtWjM +}^lt2)_*sVULA;vbcUf!x1+z9AEg9V;CNT_y~rhI&|5yw7YxVe8 +09*KuS2|s;rWP{GkhE3uQI$0@#h)73-R3y--CD=!=FdIh~aw?U&nA%9wTQjq02+`;SQV)XSB6-Um-Ry +0UDAB<39~CO9wWw%qE)I1cA*MW;2M{ECQPq%qEE0q=3y*X4AS@O^x;~$o+18OBi^(a4+@v{$kdpfX!# +j<}kAv4mNen<}b`95^PQ~nlDHl@ra4r~rGo4c8f05*G>O%}7s1e>>+%|d2l1DjWwO#-u72R1 +J($(a*F^BR{mUZC!HkM!IE3gI +J_vi4oPt?IeDiR%3>FY`N%#&!m0}!5!VU1dFr0;GbbV4lu%NFN^v6KpXQ_9opA$3fV3pKX30T$vth%5eP-6@6Oz>?`XEW70`?Nzvop +!6Jqcm@YvaYSd)&ygHLACMfs&EDfZdAYVy4r@p_`bUA{~&VwwpzNLbWGGLr=3KeI+}3JsQe7zX?g}QV +iKF1hPmij+QzLgm*fQBBh6!f!mdj*f3U +#O3&3BL26i@EcEYUg0-daa7?q-eOPXxHwK8xZZVo`%mi>#TjQQ9r6i2G9d+i0H6W4KEUc6vw9DJb0e4 +!G4mT}&X$WFQ1~$vcAU`O?%1Lw&1*t3Aro)c;;Xe4LAWMTQLhy|58(+$xt%f33%z+n9kZ0C>Jp_9;ZD +@EZDOfS0ia%ZOi@C=ilGX_%%~w8=bu+8Cw=SiGnOWL$~zxcl$v5i)I}YWg=ZC|tni9X3@*IlF9sA|(T +YA638u5o5fds$DZDFi +;#AlYQ?^X7QHaNT1S*aRJ(29aPZ`OPhNeWqL@px;duNEa`vZZ6y+T}=@)z65h(qRo<}@oRNi-bN$&!P +Juh~{Ec-&1^~GTdo^q!7jXlqnB~2wXmkqzPvXy7oiTy}7byUb>H$Q@JKXSXUp~T*zQbJ2 +6P!8SjECs8crHiBvuhRH1r#EU4@A%G$*#4andVcSctn6e8wWFTJV_s7xU`5q~xi2-3oInplnPZRWw?0 +aFq{SR@)l!-Z?e!O(ekSO5Js;`)`>09+!JJ>MoDt?kmP +&ZPtN<*DkiPJqWzt&b%w3<*L71$5B$&Jp_CYb49OH53$2uUX>4F1pTWXVrRFOTY$@#(jXx;Y~V}u!G% +U8R6C9_PH_{|AtW?G`2fK!bidI^-^;_xS21*%&saL=CwvQ6Ig7^}n@r(x8i%+3;J2YpsJ1{OP3@Ezt% +Uw5n((LUYx&C30PrxJd+4ErmU0g|dPR{vxpnz>NQ6A@5@e#wOlCY#3trfNr;8c%17-B9xJMaidshN&c2-ksMb`V7x&6Md*gC11K}2?Eb06nMNd5=w%dC(Ixb>GzS0X(b>N(fco4 +Dr!JaLFh@Io~O!1&x2g_lItx9R54$RD(1Qg?7uS(19%vXIqO9gY@?%BMoDj{Or1&f%7b)A9O!+CCY^5>I?g9o=vRLi9rU;R~wq4&3_UdG+ +EPsyG3n^v@_+GN;v88kgiO!(8Oqd660%3fDvR;8hjt&N3_TMjvly+5DKr3DD8y=={-Cv`eCa&xQjhepJ1> +Mab745@WBEfmhXE8T`N1+U1I2SJ)UPl +@wrimPpn%Ww6d=e;!!U!zn>bs#@tY%=L}1~D+|jPx$mTHf4)zCU;NJXWH<=s4bwE%i!(L}p6bdhqUpy +VtMKIq!DbmzO4oF9E5LE%STzeGKKF5X^~L3ef9!9#%qUk70W_n@|Jr$D(gsQIoz1r^HI=2yg-lEHX=< +;C1GNVsJ_Hz@u8Vg_Lg+T81aiIYW$g)9==Gw!wVHIYz(&$+}=)N60s}J5{R=B@)bzAqg}+TY)Ys%kGqo4cou2&J +Mcn;VhT4YRn85_Gn8{Y95?Crc_u`Ckx|&j&a%8ff!+%>(ro#gjuPYVXyb&$9tUx=mpA-fzK^1Ss2CBcj(iBmSo8C$IHt55&rYor8CX +~==g<$mvLg|C(UkS|}$*KhvYI))lRV*SQqESc#sY-~g)b9wu(~i=SgOr*ybNa2*!GFUKKo6_~eQ}*yC +|tp!9=j^mSiHB2S2{eff*tydlAcDWFx0Qc&J$I5WR!SQW5MNCEaBmU-&9;bYY|l1q$MYctse-DZemLS +#)EQJbQmy8gHSYc+kP0nWBoz1Y?JJY3d8eHeN_PgL?*yp|2-NrxBM^P>gwaI`(3v7Cl~6|n-NN(t#4F +{oty}gxL1H)Df*g&;P@{B}6&U&fV{EH_t0zvFkjBM|z8I5+es)RW6)(pu`R0|blUh?(qJH0o{g-iue) +?v5hw>QKiU57Efo36r%6dxZ54W(IdF3kC#P_lD85kG(K@1J+F0;cMSj!Q#5ZVelGQ^4g#{5mBkG^Ouc +#;2{g(>&Ll#gW5ONx}WU)(S`g2Sf!q4lnGgIU~kgQc_h<;hu{=myLE +-8H(w#NTzT8*FFLHM+sfR5w_U7OorYgG?7motbXB!90SwZm{>6t^87($4Qx8=?3fM?kO!*keuTRPPH4 +L5^20I7(3;$2cblpaA#O*=J!sYdeBuOe(#i(2vtKL(!1`RVu>QaDpt=YF9&Z{FV&V0Q!mv5^MuO5nN% +6{H!EECQknj&A@@bcYeCLYm+(+$1=h+ZtrfyxJAbD)l35tjQ^$MpBhv|DCremu^>+9*J0_~sES2 +mUDD~mR6cT-ok9HnmMC~7O~j@rtCnaf{pmGQ;K;-)R9Z$MSq4d{J-J&W9c-sj8L*ZVvKT8EsT(LwL?y +bPCESa0oG@AJo0@AFAj@AJ|j?s}hZMTr*X&m!u5u573GIeN&i>U|C#;->ex@&@%j@3y+KeRCJt2HCVB +oaue0Y9CRauQF{P)K2en0%Sqe`}{>az0azONYwlM#jPG2;go}{Zz!2U3eD|p_)wWdXL_yf298;83&EaHlw!~c5}LHn5Ts_Q?d2zsxBvBmRs(8qVC2zm +evHSUU_tFEgEI*UcusUqmAc8Z{9w^szcSKp~3=o$al6hWtmlmP!uMbNh*Ddl(0($0ZO9sisnXv#jQ2) +e4HBIp@R5%dfK@T5D{z*m9o9!qp*BeL|il7%z_i}glY +ZO7BIqT9-{NJqz`Zu^b-IXHfn_4KxcA*H`^7!Aa2>LGnze5r99;QT0RRrB+0tusQMbLJxa_qW_pdWs$ +b4Ab}O>U$LouVrcs$CPZIPG4vFw7+8f6_OM;;NyCa@Ei`an; +a0xoYTcTs5>eR}GE#t~yZ-z2>x{bfy~mk)N>4H>4Um_a|4aG!(e0hNeBJsv3F=)%@IwOkirTccL2lPI +uMNbKF!z(~eVBOcwJis-eSvRW)?eFI+YBx2kIBFI3ggAG=gTpH@{vS6@#xG%F1o=$}B<(7l(qM6hQUb +JfsyH@V-Z;%cFhoH5XI1R+PETplccvRIY*1=OLl5J((@B%X|Y8_p)K_M^jC40`XOgs$$(I$+d;4ndvA +T*{$;*hV^44vq85nD*~F-_ZPTF`xgrGT3lGoByqW`5&Z}m(za!x2^N}A70?u>CtZf*WES$`GBSJ-~!_I#48D=lP*yeQtJsff!{m_cnMpPLoeMBGjlP?zFcoQFlT}WWztT0_wd^k +Eddw@YJl*Svtg05VmCwxFNO~Xz#GcAA(O)^{=zZWvyeSfJ;3ouL2b$=f|5}{C(~u?oe4@%>qWseBuEHO>olTTou +Ml2GFE!07JMu +ADZD`pr`?!3eEAAMXnqNb(Ujw&7a?+3zffF#OnEwE1)wGvOXp@um$Goc{dAbr-jl$EzP?7#0VEnnoL2 +Eb8kR{fMfRXnAWvr!D|uc7`lgZ?OiC=7Jxby>2ur!0M34-*p0KJzoMge;l`){;)pJt?JEzv2M-8Pa~~ +Lrqk{PR(RpF?3G_3Ae^yLU0qm*iD@8CFRz&?QDc~#c+dD`Jz0ga2_ucKqXk4ll7(df`q$TcN+*yesnx +G&-8S=+oBePCjUDlow&wHQhf1(R2=KNL~nB)m|G)0avbJf+_}Q +|i}9uU}juES1QalD1LsBL2AXS*I^V9fnQuI*q>g4@jN6ff$onk#!g`wHPRr&Qgi9pchjpVk*tl(90=K +PbHoc>4QS14K*G8E1~c9WKoL&%zPNkP611Vo^GuKC!r(?-$yGXWL5-ZbpO-RsDtuT(kP+X30)dgqt*; +HT_N9U@vN7sD(byVp7r$BzZ3PTEW)^;;4ee?27Fq<6#JFF7~d09sv2--{@@~d*r8gE+^kS7evwowl92 +Wxq`DD8nM<^_g2w53R#RJbGo2ohqPwkb#$NaTkrY_@_a(&8ciA&yE7r`! +t@rMD87Yu%apHd96Cm%)-z2NI?Bw%+=5J~t5}{&PqwA)^?(lp%Bpykj>y;2wBtBk#v-}c-C|x!PG8CJ +PL+z#(eUY`pT|UUQvdx>`JFN1=){05!i$bY4*NwggFPw>5hZCbT3%dRUs_Q0(EmT`d6Jpd2C#IrN#Kq +{cP^JW`aMN%oaTL{h^0D}2iKp{1CG@=>IG^31FYZh2uPOV}zk#xUH;->ZLjg9-V};ijJ=5AgXFKUt=jb~gcUw^hs9VmiFy%0K0il;U#!oc1YM#xCGv(E#P?hgV7H6_QHne +WO&vFN%3UX`Q_y>Zo>sQlV8ZR8W0`&<~g@uA^;{ujAo6xbACtkjuIF-u4HFQpvA+SY+M;NHaQG*Swq$^PaC+W$C;;9Xq3J~4&5P0-iZ+x1gX`0`WJi%) +Rvg`EqYCO8%dvtIxB0j0%Bi;;r$pS-WzW+Pn#H>sV{mG=|62^KqkAC$Ebt);v6uVjq}1(B3kkERP%Wl +cVIHGjh+XbxEdIKE3JTLL9+ZrqG|<@BSAQfo1&g+Jh#LmVsMxJ_`>*4CC+fY +wYm+I`6(UJEct^h)!f1`WC^(N`CIrh9Dy`P{@tOD!7Oo0lJVHeBE=DyC4<;#g+8{_5hmro_Cx_*ShQw +qMHb5eeQX7#Z^>>HsyR->5x9GIN**Dp=L9gz3I)BTrNA(^gbuz2Z5dM;B5%??B{ROqV+&F2MN&`}h}5 +IMt@kbHL{KM>SC5H&TS2eym=k(+7c<6Mz9Q$3aq2q>ReaIwGt`VH7u(5($K-`bcd1-v_LyGau>UH|$L +jn1RoSnxUaY$pBNwZ_vw7yOKBe8fcn!B)f@UZ}WdI6@C18W3uULztFt(dhC))_iwbemaRM05gfvI3qj +QZ>9Dg&M+`(bX%N3s(S#sYhv;fia&Pv(A8O|-q?JuZ}|{XPKXy~0xML<=N~vM^^#{=T*K0bvlN9sd+P +L)yza;a`e@wsa?rU|n7YXbgRdYRhxwo!vmNSSUYL;9*mP*Cqqp2L%eCV-E6l*OP~a9t)1f#qP(DXp>0 +!J2D5dF;M(ka3x31|>kiLkDWjGD}tQBUVCG*0h_%LuQtiM=T?NJgR_5xnQc8r#nVWTW8j|5p<>?Y4)r +W7za{GtACe}ncYI;uapQBj)X!*q1N1UqCYLXhSKMXU-EN~d{1B2s*Kp$u{Biof05+aw)@@FzZ{8g&;I +9ydT18YZLM)T)Ss3tL;ooA8{cR4J5(=B;B2T2$>Oo&krPJ}_EeZV&2BSCs=FqzdN~S+XaN(jAAIqMeN6_gt5 +D%h3Y1YjB2H=pUZS&LK?1<7YVVfdqblsMMc|njh)+ndpQ*#%(^&`{qb0%O4Xha^%x6I +5FA@8jxmWWteM-86}i1&`<@g)$-oSlo7lHsmd94jo#Ji0uW2*9;>6?P8Eu+u- +977E*aVBAMjEt^d+9G&-Q}X-OxqeYTS*>3l&Bb9C +lJd)i;b&YbAr@>!=&G*p$FQ+tZApwD#{wD2wLZDCf$B*DVY{0M1@2Aq0Ip)G5WIYW-i{t+G0YL^@>Ur +eV_NrB-DkZp$o@vd>jiko=x+L$}muO{kPL5vq6<@%i!68v?nnZiwxM2vz7sI72QG(r7PethZ-^Y9<@T +>7DK2F>(C`N7CdjCyC#6R!elzVqj-6F(>c5E-R2u=fASIxs(6=I{bEx +(NOp-hx<9KY1y%V0kaV>nFaFoVO59Ny32pEx|iVI7AuhbU9^~*v4$C;)$o*~ +Oa3_a<h~IFrLWIb6r#4h|pT@DPV*IQ)#m77o3~F&M(3fx}xk9M7S^;Q|iRIn3 +d3Glx4l+{59^99D98hQlv7{GLPYcwS!|hI1IrVH}5ZIn3tp9u9YN_%eqz9R8KV?>P+M_0fmJTR4p2P~ +h+m4s$r%%wZje>Mxns&r}XmqT0K6^(z~M$|)hf1q_xQW3c6M20Q-Bo0+|BaOduw{WcBzWeGwQ2@>JkG +e|@Bbm#45o`%MgpUhwy{6W`A=96SHos1+gJPy@7hKwf`lHiJaKDmvUNj#ZNOw?9@ze!{@{7r&*@?8GX +`QHd)fIP52gUe<;|0C3XG_@biZ3s!CxrVtJhxjuQE9BG)=`4r;?JoPdUbrXz<|ywPUQ!a7JywWqo2*}B~BNVU5NHZwJc8X;Xw3**PQF;0vR +hV|BAgb}&<7ZVt7a=io3^#CR|~hCzSm2i<8 +njfbb!%iBli>*pU3*i9c4+`UIgXwP0@z5Dd-*Z-yghJk|yhYuM#Z1~N$M2r|YYINk7u~E_E#!rZuI4O +4Wl&QDI8K=dY5~j}(%rj?M5^tNGG-vL-jnxhy4hd0M(PV?}1x%2nB`b8>BW+4Drlnz +ifJZ@7Eorp;SOe6BrVU6z<+&rP#p?XCu1N`tz$0~c7yDBy`~%I|i{2O{vyYOpuZ7Hg;e47419#EuRvgqh|EE3gTsX$8urr>@+3pvCvO0l-xk$%cl +NW$x>;`DxF5svLP+8PsKjLK!(FukOn3@xtUTAIU&uJCwF?Lc1o!nnn&ayHU`*fUhM5cnrNP|_spW@>{ +#DpUG<#Ih0x}psWT+!3JT|c>S{2vjMWwsx`%2R=ZKuo%h_+8}lptb#1*{-RK?<@g(#5vAbLtr +E@9uux^v3@K +=^@vq|AXm`bERjaHHPu70snR?1z;SVwIsD|w#BH%7yS?9e;JL{O4iYwkLH}kOjvVjEnb`dF=XO(>pK@ +xv+^9X_MOY!u~%F}xzc(KFS*Cxo}VyRHY$OMyw6d2r>k+IV|){SCBD_X7tEq9(N1Gecl8mgx=crn{~z +I>_9>-P`log%g&KcbJ4IUOQa%|@``b)9HW{dtY;DISUANarxB9#B{Gt!Wqgbb_safnazmabBcjNJ+WQ +?-C8gpJJHLAwn*57B)I%2JjjaW7?XVDl8bab;(8#^_txnW__uc46#hbcj9zNJ$gxY?H*_vo2f4VlEvL +EPNfjhTbFxs02;b8{Rw_u%IGKxPi(W*ay6=4Jyo_u=Nf0nFT2jgOoAsp29W$>}>A}saJR8T&8`S)9^HpvR<7S1MTYQ)~k+yw1l}k|Gz`9sK$2hguizgBAXMmb+FHu?m!5v6=#K +*;l|IdGhYyO72{;qYq_D_JCx!zwu`0}f-<%VyZ7ry=OV&kRnfB5mIrskH*KVP +}}ORMrvFVJ+o0CPhNH2>4(|DR6(-@ZWBwfp}H`s4nqzgv6g4`!{K`F9=6_{^@mEb-e8=6gGs%Q~3v>t +MdWgZY6DW_*U%-TYbyGagMqnYtW4+#jD|Ro5C9X62;kuAL{QidnfiXg@#CYM;G)r8P}7=VavCS5t?DJ +RWejq~?jnG%;(9H7VETu(|A+9gB`6Ld>ZvtkZJWv5@o0JdpY3(iNmp!~{~Ng}DE`^Yib?FUSXh&hwu9 +d$#6>czJ=s$Vtnz+9w%?ZQwB-!r-bp5Oh=re|BqXx*@}!yB +b2UXa*0&y&)r%OaK}$0!T$>n8Vz?jtoHR;IV%hqG-KDe=UGM3&6(th=}^3Mhb=oRjrmYr* +bm%u9b(rWz#WKmod{Gqbh5gU#%Jv!wPW**G`c32N7%};IujiRwUZ6zVZ5fYT{~`V^j~2Wf&5vbAuTuE +YRJtn*wnRl{Eeb(6>a-jqiOthfXitrqxq~{{UxlkK?7kivzmi=SFcV53~CQ42xJ>J3hH|pVtJX?<*8t +qot0w+9?8z@GM=@WS)w)1mYQZYq-Ka#yJ6U9N1lE3@~oWE)|@rN)W9A1rj4&KHSm~a&&^qF%@I}qZRu +F;tFv-aX(dC|L5PueIdVm-0eGN8G@WI7@-=N^8C{#R!#r<^TYV13gVvhQYepCa%Bv*V9`Ltj3us6*3CV)aqhpN|e>s)wz62ZxC-?gIw**(1{() +_ve}BN!o8;KA*nL^w`foXqPSDx)lY=_(@A}e92p~04izy5*UMUpvha3m$llKZC$4Jqf64W^28>KKM43qH*;_wE$K71r!6!kBof15di~~cVzF8f#U`o(UZPnH_|IT5Yh`E`Y1oUcfGdOBZ-hBcfpuN{cDL|v>yQ%o5o*5{G +lY85I+(UttTO4gBrWl2h{qN>uf$L-bul|{YdX4{-n1EFx{8*j@FUhb3<#@yzIZ35LBepN6l|PFVb&XS +UJlvA*#Q1w-7QuqODw-{sE-F=ui5m`;q?1zNA0I+kb5D`d+o6<-vL_(MS6b{a8jP43EQ2E_}DC;lun$ +7|=8<*`I_#S;FRq)OIfqvIU2DlaO>Bl*I?j;tC%+mXPqVZsEN>i1%EN>y{7e=jJGoP2kJq;Z6enxtp2 +M(i`b->nz`+#}P7dTwA)n)W0wBo9pfJqa9Dk)8p|v5`&rm4-%jWCK^KkF{B3*Lo&?!qPr1;yR`j8+ff6&-~a*eMC@g3_G**nso42XV~4AAT*?YW7?T +?+zf@6o>GCh;|LQ~Imqrs!A5O`5$Vh`ZY$?O;kLA4*p>|MoeA9E0yoHU9&2kT%Ro?QJPwcIGEv{hXhp`^6HWq&eA-BW#q3Z5d&nj>DEhZ!czVEr8n*mRPulr>lriT{VpbzOWR__Y47Y3tGmb8V +r2>8&YZDBOswWND=AnAU;f0BmhNkjR{oAOm~KR;IQzz_ZGZFR2Z`-v1nUP>ioB#*E6NPp5deGciHoJ9 +IY&nA5}GYJd9XpIj%X!`k+eqtE#QZI8`4n(`azrOo$Ld2^d`N>hL*GT2OqTEFr +aPN*z5CN&PqZ)TiM2Yw0^_@h!^Sz+Aq{iBc`a*>6UB=1K%88j!UZu`3;y0R62#~NT>*3x(^A3JcW)8F +4u(USlfquhJYQme@?@s;A{Q6cwt2jA-{)jw;Fd}Kj=e)fNs#20-gKbPs^61=}eET?rFf-0%1cSY{*=m2GqMA!ndUn2x$O6By)bS1CP-*r=xAo7w +J5$Z9MtSbC3pn^_Lmcg!_^3=-wp!`#z0f^*w7t%DdZwQ-YF$-NrgK?1?`U(g>dir$g_7I?_E^5ACiS> +29aJV2W$3#5&?*YF`*r`vRSSA6VP8d*J&t(8dB228i9kJs9dfi1di>cE4YvuHL&=Tdt0~f~)?5q1-`4 +f5zWN$7{}Ky&&x#QQhnv!$rEn=^m-@l5iFRB6$BygwNdeV6 +ADO@?P@V}zkEq<0-&KK3CYS3Ydx<*xYg(K_Jyx;B1fV^cbeO?PxUHdTB4O1fVX^2?VU+GKKDn`Gk-&< +FaO-s!IXvR)1UtV~E1e7oAwv>W&Z{s-OyzOQ%h2ah%oasj?qxc_Xx1D%Atr=}kYeHrjm=*!(~fhhq=v +|k1@$U}|Y>w{_;H~S@ZA#XLJyruE-AYRcL7<;fC_(3}WIF^Of_-csna^BBvRe6!OT|4mb2oKUnQ^I%^ +$8~D<)ljnz%n?4Mj~K1#6CI`Lqlwh0@`cS8~?vyTViha`_K7y2LE%uox% +Sr=i3Kp+>ddVYGzo)cs7-`z)X4}iO|lo;=*21s+hTuYzCA2D(lSLzYakpUJ&7?dFKa~(k0Cj?ElHBDnNBmp* +KQ%ruP{w4P<(E>)pa(tj|bQ$=B7Kct<$rzt+%DFM!(N_&cU_CPD_*JndrzCv!<;Vt;x9yv(l~cnW=X2 +ATOiKpOKJN823CeT>y&V>ZZ7pOX9P0^HkHUgt@mRSfa*`O3w! +ARw79Mxz^RWYiJSX*{o>`NrGmcXor7hD9t451FCL0tu``Guq-qtnQ8D3Jp^m2ZMxkG`FoGzMqD-_-;; +zK(Q1#kXNi!J?0HZkTv8{oo)&BB8tXOoHdaX_O1HTx_Nv*n))<*x&wDWT^X|d81)}>i(7# +oyA#KYRJAbq-Y_V6_4nXJnlD^^(TNp@=+}u?T+jK_`%`OxTm +w>|1B|g}!+MWQ~u)#}h;>o$Lc28bl{X>$~POCE~jTIA;egtDl&PrRw%UyMCPY2}9r +{!OtCt6o0XF)o7lrn&kdwD;C77K~d(~_GCt!KGC)xI9`c#iVOTq~=XJ*|MtrdN^(P%-cJqs6A7m`%wl0G|wQGSj`+uj@I48|Cj#XiDDe +VbF`3j^~<-hb6y?I?RM>h$gloT_U&2<>)gM`uloAW??1o){I2&?+uYcv&X0UJf{+tS4QDdWDvG;1>Q_ ++PnTP2k +LT|scmI&PhxssCs5JZc{(`#vF?U0kzkhqy{#$-sAFg8VT_67WpUwBb^7sFp2Ri)zkF!Kl0++Wuw6yzp +20G}Jt?RcAh7}=fQu))90QxbP<0+D}!+yzIKEDjU2uX{!cyi#Z!IAmru! +0E$%x0%}-Kn4h$%B&fR&=K$e|bd2Z~r{@<(n8}0#&*~~(=DvA>C<}UDi2PO{VIGoI342RJiMshfuLj# +9l9O^i1@n-M>hxHu(mBTs?&vIDHVGW1X93J7YoWnOc+|S{w9PZ`tc@B4RxP!x@4)Jf~P~UN!>gVQ{>WiHhx<9)!{OgE2l{7z +MKjya+y1w?{~9eC=XM@GC4|>^7_X;3ydHUaNs0WPlEP%%^q%eG;aVk?{A;fk{!4!y^Zsk~{;&4`_lEy +>$N%?+|IhFL=I__;k9c(WdGenczNI_w$2nBn`*Plnhc}UbDICQ=wZEz7a9!HJ25#ZMFL=Bg)BZ82=Cx +zHQP;>?dA~+xd3pa!DpLKytig^mhfx|L0{jZT#*rE#1Dp*L-X%9{ND{#NdVmZDes%)f6aw-F;Q0WZ@Y +RF=3jl*cLCyhtgj0Kg8SprOmfj%00FJO4ChIo95uWMC{5JyB_Xq!jAuPae;R^x05n%jH5Pq13qyzj3z +9g`30XS{|$V!0609-$S`Pm4tzk%Tffa3>(oOlcP0a!bbrG@aGL9AS5gQ(so>L1}lgPEUQ0IzWSs{lua +Gkib5r-wqFLAuWXya?Yjm>+ct3#iZU(sk@MM5j;Hw4vD!|^ifV?*Z`~aL80W=Q>ngDz`f|cb +}fO#WW9z=k*07DE1KO+DZ!Dj*-;p3x04jrx`djNhLNk}HxHv;T7hQ%KQ@DO|(!OszZQ$Y;R2S0HDuZ( +47zY4H53i1s0B$|*_(X5Qw0KbB-5$t7vw~YhY2k<0-RpUV}g7~Weo*U2d`B#8LV<2x}k7po;OoDh22k +12g@&>pL;IgSu2Ea1`zH%!ee+2whfM4Fq;*kL^i~}Bk`dS2VO&lvrIl%fj<_94z93hTyx{>8M1z-_;b +>P1Y;Pdd+1HKpFU*P)!;{jL!pA7gBfDcZCxd7n10G@*HD&VyMk0cVJ9jc+Q&uuKNegI#;jnQO3K+|kS +TZGYbppL*l!tdY{0B;1CKNr%1vLO5^8RU7eZvpth0z$R{UI!2tw=n+5MTER?JFAyJ0`$Ctl}ih7J$(D +Y|2BXx-NF3t1!#q-SRL4B0=%%8h1AOdGm{$S52jE9{GW)**{GQ_h%b@ +KqV|Wt4LimyaF9P`1GL}|3z}ysu+W;!5(3gXsnB|1rxtxXF0k9z*WH-Ps032;)`Hum(%F6i-;M7c*Hv +t|8@NM{N0WSv_pT%$!z+)V*0hqiJXbU*PR`>|i1z82OTE%F%5n%NyUS5E&W`h^-Qx4Fy8h8@x4&b}1S +v`IM@OTd74e%O(LAk742%Bni|Gfa;+XQ_e;Aa6|+zkCK;Ee!#Zej6<0eoT$^S=k+CHS%d{}JGx?eLA +zP&i&2Vfk3&5Z!_&5Tveh-TWVbD{oe?WNtY1TK^1N +`6_=t{x94q(miS)Y$k`z%Wr;be{@ya=BK+vao7C&0G|@FM`T|BUC}H55+W3w#TB96v;Q%51B7i;s&mDw% +2mG%9?|1|F9&j7LXWj-L1{~r2halg8?*vE=vvCBW-w|dX0C4pYM(2Ehkw>A7U>^t2rxN%9a09^i;oAm +y9l$H_?Ew5Lz}{7?ycU3)j{y&WeHp;_j(_kM1Q2L7ZR{$q|&G>CHz^~wI1V1vsB{It=LQMmd4P>4X0Hd8iQ@|0nTmX3jaD)NhGM+*B^ +0%yAyb3V)63`s{*Z?{&G1-ZH4{`qhAK(ZxegwV(JQLtUKXUp29M(k0o0t|rv5B`qfG=@;FF@aBNDKS~ +04!)`{ZA3Vci^i7`*MJvHFF+rVfR4Fzz;&a>+w9$2jOkp9$_ZO5f*Yk2%qHk2;by5!Vfu)@OzFU47|* +6JUc&<;|S+)9N~J7xn$;WoCN=H}0b&Qtb)JeRUW1bzRuYo!H|#Yzx@yo`QY_i1!7*?A`c`hKez&I +qX>td0y9MF8>p>Kd(0P@K?coNSnF#BQOWGnb&;-{AWn~eE!@(*lKOg?(lU=)Zk>{R!jvP2}fRvY)lMg@qkhHe8624@YPd$U75lsQ)-kSU +XqOV%J_7pXqQZ8Q254gH^?Z%Dh;i6J=^^{W649-ZK$hgYPThM>asf>(MRor})xo3m@RWw8VSHT^B;AabqpP93pssEaqQ#4_C7xelUBzc`W4S-8HA62pLEpUENCsJ +ub4f0KA%``=xknu%+e{_}GEu(p`PwkufpM12wmtIQ7h+Kh{vc_<{We<7A};g3~=~$p6WwlWSV8p2{z$ +{!b~&FZ17Nxk{bUKhU>fcZ))uDg7}DnK`3>s&u`MIn(%Q-k3AdZR0=YOs)@C{Iq&x1Ix{~Di1M@N&y#6&W0-aN8l!-jS|URG8{UVQOI^6I +OvQXc={gAd4;Uw-N0a~mEH09nztcb^Z|#ND>LmDe-gAzR+L_)f;ET(SkKDB~SP+47Ea8F0d?E#LjL>~ +%o4JpSckMY)o(>DZU^=g)s9!xH@mfXi0Rylb|iytYi)Tr%vP#q*buEy~mHyfz=>JMdeKe~Y4g+uXdDP +%ge3WVHbUlpvo03i0G0xz4Bh@!_AI(Fti3N9lC^lMO?L3?UH_5hOA)l1!O0g&2)SVm6y8?dHsxLl!Jp +KyJVNcCvKoQnERHI$5z|1zEXrC0V_CHL=_6wEx(&X%l(UaVz=5hAHHg&9S6#MFQEeDxU1jHIhf|apaA +HN#vz`GkK(7IoY?%Mk?=}MQWd3MTWnvkP(LzGNw`?w;oZ*xZ?_$d{QAxPADY)tU_j;Q^=h23dw_S{$~ +nV{Ix>T>J_s5fC@!w*|X%_xpU-`Pd*_RzpNwgeXo#DKmC;QMMFaa`SId64pH&CSh}SMcXC8pektLcD=#bY&pB1K;>VWA6qH*r0SLQ5kQ%3sJ?<+R$Lk`M?# +2*O7~_$d(H0^x6m@GBtvIwhEFhw!^0{GT9vIfOq0;lFSTKNP~pLHIi$ya?fc3*ldY@P{D$X$W5j;r|B +VKZo!Q5dI>BZ*mJidMF`DM&R|uAUDCBcJFTqIrRdJX)q5_&IA*sZVXXAnL`wq6f57{N0i3D5T(g2d%$fVsZ19>Q;j@Q*?GKSTHm2!95`f9Vz;gpVX>$~F)-%6uWbAA}D;oqiDB(48n#A^ +Z{uzX8HO^k3~=34Bf0)<2Iyy+JN9^)*%6cbgIfi4a4yL=3Me4SDs{P?g-En +tPMROevvho9bYQsi>(`N|l>^;JNMjk*4}GgYwvaL3Bzw>_yan4rKZwH +smnI&qs&+Q*=*Y@dsy?YC=m+P9-vU`1cmy(t`IM9DQ&GnYYG(FcTxxM&G79Rz9+-K!thB9pUUtn7=9h +YZ)f<=7@p@$JI?Uu82*YAz6FoagGY$v5oYoTD|v)`9^o{PaHTdWS6h>Ey)P*@N0ahP8Y#c7A?4OSa>7 +5w@GTkM!0`PTeh|ZtXZTqRznI}yG5mUl-^K8I82&iJU(~@@(>U-X!#~CFJ`CT4;af0#2*dYh_|XhMm* +H12{0@dcs)H}ttZLJLWio{s!;EI}xo2~~#*KV^n{;ztHHKLtBFtus$zU*=tu6hUHf`LfTepUC1akym= +6@0V!ECnrH^U9xx_N8YEs>^hi-Cs^HyI6KMr#Y)(73U$k9IvGGSp}>n#?AK2VSjO`1^-g?;>f5w8+-= +DUZ6=R;|<#8u|KoH|z#@9^Yb#Fj%Y&>eTb#>wJTsc0&^@!yAkMHd(As)u~fYy)JNU!&?o;$X*spxW(#;0qS|wt^2pfAAh`lPt1Vpk)rYVo+W +Ouf(^KC3XhDiSR%tCy-M7`*PAvME#Z;o$gWR&KB?V+>&^UH_EgmIO=1ekHES?#(D*0DZ{0bPSs +WQ|i4;VL86^K1f2+B5C{VEQ7@l3b)~Qp6Z*X3>wtv(b8W7GC^y2GJckL=rsPDXPs#T>{P|d(_i#d|H+ +|^4E(V(lQ9)?av<1>|ORS7g$Bdw7U;To3Czibd;Y8z$>c*LznFYN+&6=60yuUk6?xL1GF&8>EK#thj`MDRsZ80&J|wkFfv>sD)+v3KX1Rjah;k0vWdk1*ez-)h1wy*ocqscI;$n+&Y +37Uk}B@P`#F^s(ACds|JBhR&>OilQH1ZQb2#cZ@VLA0u>^F}_(`=I=YSF?Eh8d0EvT9^cH07!lsClgn +k@_{^FJV<(r(iltJ2%is2L|5_u0NpPN!jFEy7-Q4B8mT={v5%;);`>nHM&goPzya_rs(_ol$d!Bt9-&xKLTTbg8mz*)k;~BSTrSVui>R)~#D7^1%;3{ +7~cy2lL)jmU6z4%eg{+e!lYc*I$d=;Ov=`%9%4~l(T2gDi<$aRDS&NN9F3zmql)Hjn^9OODo5+D~9tXNrN^M%Nw5IJ!Un)>WQ=yVZ$CWj7j^p`ruekyr|jJqj5k$0RFhEr?; +o4ho?_-FZY@?pJqL)=j+$Fanr^DwcTqqdxFnBeHu1x%J=hH3VfnooFo)NLN{ShZ?3s#mG8J +A?d(K?Cbm<%)PhP)%{W7mh&mKR1e9dQ{eKyC<&CNeJI5-Mn(4HHAe(Lby!}E9@O*?k%p!4U?6US3Jbm$N^sOaR$lf>(9!gZ#BIL>}sR8;f>(|UUU{{8Rn+ +_`f~SXfvaMU@kNGE!Gjf6@V5AlDjN$7lHy7W`DC0q%{9i)&L{T#UCkw73ua>oLFguno4w7#%uvXaf8g +FMWb*oulu+|DN{l-AnuS?GyI_hI0gbf0kjEuUWHZ@}^CjCUeRbK7IQ1-U$f +_=G4^GKAcwrH~gH%zi!>SZD!A&J>Z#Vo&oPX%x1G6#=T$suU)%FoXey&Y}l|J^DyLUSFc{ZcJScAE#H0j-ASJ7SK +P;RzQy*|j&Vo@?ET`;`4r}IcHl2NXpkK;ksbI;I^<{BU1^b>advi=xwGrPM}H9fKmPdRxhzLt)`=OQV +b`u*0)Obo(W6I&3{IauEzW@>Y=UiA919BzMTc!b9ymg7upPjB@x>Pcf5?#SpCE_df0s|2sg2KX@%OiJ +oo)D;`@#R_&6~twNa%l1P*4--F*@-7>Z`A4>(;HrGAGWNg$;xMkjLkre=a(3FDNJwwsPdi5%C!`zz0C +zU?-3faNGD3QOr>y!`DP@KPL+Kgvfs;moC(?q?P1`F$ +d*TX6eUG@@n{7R)EkZqyGE+W4j_m4mO;q5Ew`yQ%^tV3zfLUhXF1Pp+qSJQ`|}yVA9~_S2lO6#?MjEF1-cGBI +{D>#dT&B7Wsd7Y?QasChTfc+vKNLj!ha4u;8{_OWC@9n{4pBPN5CUhYi)3A(bK#OMo`6AQMpYizn8I=ZU&!9otGkj9 +(&zSEY6Ey=3%zy7KZ2tvfn6FF2pZ&eif3~$$GEAK!<-*X|OU41J5%JI(xQg_G~OveNxa@YJ8SHsVURYaBHc4N#M`<{oHo#+WB&hG +6VR-FX9ND0S(A`BrW(3u~z0~xQ2L(oB{2_l#aA9xg)J%8nT&&)l9>-#iQu@V`roNjrzVhv?66nh4cvgcqy@eo{vPWZ_)Wwr#5T0;(}HLV)371A6Xj0oOb({O)t>*&J}E8 +ENOMPo(X3b#O&@BaDTB>4XWRffad@wwL7!u^(sGQl@MnK7^q=ub1>V35N8kj!VGqDt$^<@MpC2Rsf=1 +~7$FoA{Bc@?9)3AOr)4;X>dj<_QjfTY<4f8?6@GzP=jApy%3Vi@uW^w +q0ZlihA78&{vY^D{qNkla}(eI{ASIX<)p!tj`C=^bLTd +l-noQ6nQNjQ(}QTMX3uMN_Pj!)VW~#LzgUk_M;K{#tV#o#$T2uR_vF6pyokSFD1U7AAajntX?^?lwZz +BA?*^{0N7z7ea8dsdC3+rRO8BXhjdo@I`)ShMH(Yzz0of7`ZgwK&)LyTrd`%a-kj +3>gwXc<|th>_>$hY&IM9?%kW7efC*;^UXJD;lhQqV8H@`57zg<7c?LiAWld+u!aE*@Tbs2#6rxGKgUe +_-vj;85dHf(UP4HI9Zq@*Nbn;@L`=+Q +&u2%miNiO>h&4qPE4saH}Spbxr-bq?}6$O85Nn-=!Wa?soJ`!4qU<)-Dx1D94rc{+CNxM1<(#bO@>b8 +EItDJdxw6%|Dd8#bh7&6*MC)&dvc0o%}b&>*`jE$GlGIW~OjSIb_ZZT}4A_^jp_h|l)PCffAYP&)R-M +!I?9Mj^|)!ut=47A>L~GiF>%OG~3^)27`RFkk?U963_(o!6B@mp}vT0(1g5(BVo8_8<_4q4$W%9?kRw5Mprw??RWn06Vo2(V^74BzW@~=jX8ns`)e*HYGAsz&O=q5UJ3H#0X4tQbDdEB^h!hTIA6N +QF`Qma<2gdA`kFs^izrd!fB-_2Cix<^IpHzI#S{saHd_Lz!u*c*<(1@eZ?vYv`uA}%gY_*S-c5#Qizpx;stVF$ +2F$OL)<8Nes&^C5i3xR4R#fq2gJ;te?Pn^h`^yAyxCuIhDIuiL;4bU}w;3o~cVB=%V%PU{yUc(1APXTE2Wa_88MERjPEqxI6Ke`9Je}2569eu{1isZ}=dN-C~^pokFZFWlvw=5822Z0(BkeJ +#+~?hYc;l!~FNp_Y(N)^&dXMHO9Kqf+P0WUwrXJdi(9Sg?{V#E<5gnEaMA9pLT8jT>pnk|jc)r46HlZn-y)yb0&f8CgGIpZQ+p%9Z~c+@1LA0lz7w=a+&Mp8^?>^R2mFBx+n3OPj^8un`l~!z&*BOW*ar2-oGl1 +^5gaq3f(izjKaI#IizM^?b;?O0hmR@IKRw4;}HG-$^u+L6?w8;&J^x8tomK*RGrRkdD4DJNI +NejoSGx!-hIFX9{(71#a1vKr>{;M_NmW4#vlyby1>ScF_khzy8?=*JmE +U6U47e=Z?~Q)1$;bW~#Oa_{t?B>jkxL`s^9C?kk@k(CZ`WL#Q(b^YLIwo%BLX;q_}Y=ebe(=P)3D{HVR6e*CN#iu&0Xp0``PAzMXK-n&E}th{iB3UG}N9 +HrNE}o%;EaS4^BZF@DOFDZ}-(SKxqs6Yv5J`|GGRWoz|5)S0j!3VlS43AK&sT77BKAhj=?7AtTN`ndO +Q?b~&J*IKY^4H)v@InyBOU8ogdPZN9NQXjD|j`|8}6QahY%|Cf?S20&Noj!K?vZVZxlg*wzJNlh>-id +-OV!e<29CQO0@BqjCnX#g7hoeFPa(;cCMPIuEp +YByND%|yfsIj0{H-m9V)@qHq_6Prm`RB};6aDVH??$0EgnS%$$aS@RZV-5Y7w|{8hKBlW&Z~EAqBMQf +=~}7&A364EXAaP%Gl$qFT=a3I(OD}iZ+^rS>~mm +G7d*!Lifz%km+o2%ld^#f_Sn7chOP@t?=9B|MyU4|t9v_%EMr1V3D3G8)F@`}VVLm7D*yY}xWTwtKM-M65?$2DK&5vuNPJf +r9R`_L^N~BjdPywvva(bI;}fz@Pb#xjFy1fLbE=ujVE9_%Q}@JMbHOxzIuErK7F^`_R`BAOrXXzyTNV0)87l6L{{}v16W6u^NT2|G9JLMrUMXM9D +Qb0SOk^TbDC9Yk&HbYU@3KeO>|H1!EpFVv!#>1XGVm^EhY6~yD^p +dDC!!B?y8h9Z0c#$uorh>ekX?W*8=625Sx`(CTlR!KgGiHqN4R}^n?khrG;5F!mPcAGhTy>vwi}^7R` +x@c@OT$Cvz-!j5p=HaKiM?X@M(jDsn&Jb_E#}8OGX6rBK(EZfVSBPJ2^^qfsBvKbY15`n@;Si=o!d!& +MfO19XW;uF=iJ;}=-(yg#cW00KY7rG-E=SMK9 +?yWVBu$??#utZS@~rsd0*ueaH3H`z!1v~uOjf2~@zs)XNhI2@OGj8R!xS>k)X=aJ0J%!u^#^zGT%*+Q +N&hC$A3(}(o%sHg4RxfA=Ww|ISD*qU4u>2ty8OX?bU)73wO;TzmH!Z*Q=fUEp{$KGu#C~cIQ8xGRePFO!;{ +fe~$)+&L#R$9z>q1K8uIdF&mW8U|ZBSemKjn`mHud8EVEsnJp)_z#`Vci+T>rUh_$fp$+7@|VvfVJyT+%#V14bppnQU*_09MGp@@4ZJZ9c!63GX +hO`!eiC4@&PMFc&(ALbi96@95gH +@E;_MgTBZ^QGCVT;GJ-O?XY|h)mXVY(D`Qc{>Ws}91sO*& +iZY5bs@Xhjc;o&77;UZ%;nD1ERNO!DurZIn%zBnbM +}$!;%twcpq!+f`8kVn(sNel|FGbuAAv#c!U&-Q2gzia;iP)h>@6aWAK2mnY{22*@G&+iCH005H@0RS5S +003}la4%nWWo~3|axY|Qb98KJVlQ=cX>2ZVdBuJEciT3y@b5jR{{w5^TvCZl=h3uXZ`H2XbsOI%u|L~ +syVv%zv_#unQ=~#te)O?_`!^4~2}*L>Jy)lVB@!3_gTY{CFf&;H_@nh;r<&c@`Q=p`L_34v`O|05H=a +Lz{wz4Y%7TOY_Npp_qw1o)P3tW9wwjldw9TtB#;QkI7QEiu*?oJo8@G3Dkd~8RXaDW-;ojfh9q%6=;X +^F=@kjY|R@H4Vt|r;I`S_!Yx|+g|qR7SwtO>;XN%nr88KuiMyK9U5Tt8}VMD5F>I+xXQ`jIw4(@qllp +(^B+`ueY?D%Fpoy1dNGOZB^I)YsX4GOnhxywE+V_1C5vUuW&dAGP)U*2f+6$IkG4AZ8n`b5ANFAI-Py|k*A0 +4Rh^|18uf$vDJN$>o!%!32;$p-DBH-V88%LH6w9jLaF14EDH1K=GbK1&MQPb7{D{1d%Su>=u(Dz +}`T+Q3O7zTBEo8;we-VTE!D!aEoOLO-=eSZU$g3OdHG +vewWrOKyp&+}yPd4+({u55`|UTK*FX&F{aYCB(ZTl4u3IWPJyIh)0v7r9yJY9pySLwW%AK4xh>~A_j| +PvQtZ)5wxcT)b|Ni-J|Mlpf|2_Kc%dbvPPR|foAx+@NA5F3gnzbyMru8*UF#kz-F%1L@SX*1;F9%pON +CPeva4F*4b=J=75?)Pm;>u}#Pb?THm5RtJ+1;!L2F05I1I4|p_;ofa` +#mMt+fJJW{)py8IMKMz5%NnzrLB1F|ldL)%+6-vErxixvP(1>8%RJ|YgORb1HZoWe +%@`EeHZ=XV6vy9J{DUav^4|A&Fg$TO4XYa_ce}H1YvIhc@Y4?3Fgvhm +p&3It?Pvgp3bC%fU9tYc@fyR5!glj!_aQe(?hVnzermAKSFxnXu3GB>OYKf@zSd)x$0D +{)ne%2`(90|w$$gQVV!~042B$ohAmVprTH_6+p7vev{3APjw*&!no$2iC8L@*LQDif86O&rxvGXe8}D +O_WxIkpN{H$Q6vZ}fL8Oer_0Xh2fiN~wNitfhe0a#nvrVDG6i98LZ*Q99O-lv6?0F>$%2H7aTLt!T-o77@0aZMC+0?+n{CGzIW!%f0i`~(HL*PSGI+X*eq`+3)bmBVcH`{S`I$L&KHTKNl +b~lumuNaUb9qYR;Uyg+aOJlJyu>r^XPxXg$wCQ>o{ZET+?w^I3 +(bEuTLd^4AwHpT3MG^huR9W!Qm}7Pskr!<$r~aq${=kGckOEAE30n8u*Fs^-OnG3Wxxq$$23E1X_^2* +^7*Q5!eoE7*Bx)NNd5ZBdN}t4-OS?xviwF0z*}^L{1727^2JW!5H=!$+eE<1q?)>YjMi8T{~m +a9q#(OfUFZMVT_%Ow5V5elJq4nTC_8opB~kFwchq`H0I}nmW(snKR^T^zC#zGq4ptkps`HzyP)}{i(826vfR*a-{q7(8&k +)SWwvW8a2Av88)T0G(kClhARvIAY+<3E#Kx=))~3k$|_1f4s1PKY|b!FHWB3o0-B_)SAVz6Bp$|J<2*>yhp=M +UD}qXdj~W3yl7)u>8p-%n^JR|F;4&#$rk>oGvlg>u}}U>S2%)DF*&V!LP!wikLi48+65*UtHvOLn?@rM6+O^YgT9Q +9;(V-6D7KHhp>zeBKY*YN$@cOB_FZrwLhKcG2N;vI#=4YC;0@sN?SSU1n~dFD%C^2I8K(_yVe3YFgMTHNa}&?1S!DHaNYEd)mCE6`qtm!tFfIvz1~;PK^^< +H|o3yrUbSKux&WP=chMpie_*eGTVqYc<%}nwVWR3DBl9LR$zDgUkx<}1D9V28Z03&J(?WKQ{*X=xoHF +Fan2$_b^Fdb1KIfiF`8_AB%hR{4A;xeFw`do1obuZ4k!zDut)yRE0#_wDa?zNay7kolvG( +ibobHIjhFHePybuaHBvlW_8$!_K$*}@G(yc4YEjEv_z40a+21!c^MMdjY7{5LQ#3?2Npb#BuqnIS0!V +`oeZPEiMOpYQAEQ16}`tKyFZ2P6NXm1GCNcth0GfH%Et^Qf;^RGB=e0?q +C72y;5W9>Iut{<=V`2`h(x$?o1EV#*V%mpD;zyE4c=%_2)-Exrdzg2BIMaQqCYYDM@KFln&JZ#3xqqU +twD$v6R!i!3A{!AKcO!@w1>k&M|2b~E0BTEkvvPM#+YzH2ssr9=AuHG{;N^&!j|)Za)sL{2zR(D0tRF +d9sP#>a3lk|Rb#^$ivR7!#uwp0wk?LNFGS!q^Pygp=|Lrii0O~;X;5;2)BOffX%XEB?XaX%(2x6 +c)AfXYo7#oq;Reg{y@DY|3FSXM@e*Pt(VCX}PU9fv+%@vhB31QS(N%DMnb`s9$BqA1g#V6{kExnP(J0 +xKyN0Ydmo;Yu_X%o5f6%hnzm}F((Iz1(6#6r~>No5DC`Xn6^rAA^G#C|69$ZHi4qO%ac8e$KAqnha8$ +u$xpmG6kwCVLmL6)Min8nI=`eUYFm(~4U@j4jXT!iIVnN7KaKQp0)j$a;bg?#`#p-2u&OH*L +sBkgUrMbX(M%1pO6k-l{Anymo{cN)98Ht4+3*;Nv`hb*=I2`B|ttgI4>EI6C{4$g23KEx*FEI!$=&@m +}30=Eq(Jbuqa+U_TYeXPZ)ZIcQ2a`)fgOFfY!@fz2c_zT|Qh&wuWIUmM#9eWdEP#-PDCX-n|;-Tbzu?;NfJj5A1)l4^ngn44;Z)J#lh`lV_+0F# +qWUXpu=Ox_0zrF;8W16LZZD%S1VXCW{qerFi-oD;H(u!kN+qzQd22EkS{1{h`%xS2E(a7e#vM6Nw~op +FC>L2y|l_vy8g#nHMd<7Iy_M#EYU)^BB`So1E?AkN&2k3uEzhnwIG$+S!phF|=-y*9y7qgqRG++r`uC +34ZspzxzBZCQv~T2u8LP=6jB2i|Z-$CyUz2V3q!iudp{0*`4w2gw+C|V)*Xw2)vfnoZj_yP1OLHD2JmM3o8t7;B$QPq1m@qD3{9pVxB$n}wnFdE2*D-@Rxq<`IJ_smESHd-SJ32`F@# +9>n?y_`2yF>kXq*Z~!hb{p7u_F*kx&>i6jYC25_;|n}!#1RC(5URa4+_E(WcZzupq{*kxP{%w3z~3<& +xg%9_(7|fcMX4%n$&Qw#|1njy*f+*X?+gE|<-B`R-zW9F9DSRDgwtW-;w>5-_?z<{)Z=-bO^ErlrY1s +fxg}VzRXvGpUTnU>vYnRs9RQ||xqAz)o&+TsP|$lC2edWG7^Ixm?4PM(VgpI4;0T1cXI?0E>khd(NT) +X_uSVjOKsQ!qXddGRWC%ViSm1bOQ?NLXGhnMZ`?qreQbVvh@?F8s2!i6T_&F2@aczx?6EFxa1MJ}rv= +lb=0iY@JCKw4DxM9O3e>{S$VTIgqNTHKGuw)tOnCb1z1x_78IIm!lQEj_2dWYh8a905-3fsG{=H;d7; +6bC7U5J@i1_!WXq!`SP#LnE(>?n3hxCuj>R^Q%Mt=PeobE;JIfW6-X#~x$$f^3UfbRa26?j(xC@${nv +#(N=e6>>CdFr2m6np%sbusyku8Lbg_-c?|jjk!r9qBS~*3m?_)V1bWywF%EhHV!P7WB}26*VEeRWtVE +Hm&}^@{Kv<~V0&t18}K8UqVO4frCtfJ>Xo^8#?|azSZ`7g8lh*toNrFrN!-fEOGKgsw +&{c_P&J8_3m=7Gbv@!*-0SRNr(NoGw9qKw<>GuMi+D_To~LWjq^`~ +%CmA@VSbs8#v+VXHhzgv{eAiqNLkpAEQ)qI{2-bpdk7D2fr$nnE5j9LuZa|H!rOCqqIxVgCJbTxazTX +#uH_d8xUHf0&<4m}k%g9y@fkl|MZMG2uA%n4gW`)NvPf078U+CrEg-VB^BfJp)3 +muBvLxc#D4Li<0f9lzswTe+QF@BZ$Nx%iQdY9zY2uW2Cjc{;wIbJy@>#%60L)uT&m@ku5FY6L#bgjvr +3(WnS*y1?;KPaXKqC;kqHr<)ezkz2;rL#Au5lWHNe~h-D$Dzh_Faf$ps4}`1ChI#vu4_ +nv)Of5HZUNyEUYzJ>EZLc!c+nF2)5L%cCU`y)Dw|`2_Z80J{~SN-yZ>Y4!5u%VF@e`W!xt3QcXs68Eb +;!WGID6P=YQv~Zbl#?lGhJ%7RKH|v=Slv!p1LohnTnz*N&%fTkjdwV}a>7He?P#6IG;do2P5qdSf#@7 +50`V~mkV^dxA>yYR7z*%H&ER!Wwpb|{;UozI=hb+>{AAA@g989j#1}(e7<(PcKx{CiqOnoP7oM2@%r= +rO|IpOcn*A4EgBl!w;*rlW8+X##5nDz>bu}W=%^D`2%uMiS?7X2hoFU631&b<2>Mvu>_<({^nJ>`We- +8&r@c`@6`^)0AJ?s2IYpJz^m>5@;!bqYt=G!O2_dq%-Gz$ZF8LAT+P(1cFhB}Bus1|53<$2+ffcfQ~K +CV91e^vZ6+s3-~An8=|*(U3ICVet5|-JtViA?&9LMyN}OYKtJ!J`b +3O4diiVqs0P^Z8{I9%8>piB*JD$n}F;%+o|6>0#VqaSK8?y6muDD(s7>>gc +I_ +K(Wx=xC#YZj*OXgbD}vVz~h?;n%>?>*S__$0Dpupxt3N>Qh8vN*H()!ZJ{zMxOMxc`pVh{P>sR><3q1 +>G`V?NAwE?*_R{xGxU_w#{8gQ#b|fb4$9iDBNcn6Z@Y*XP#N@(#o*A$Q}Oe9vv*jL|qjHo+EZ}v3x}- +bp)f^qi%0|1Ph1uF8bJuUZ}1%vW`r=k+}&_cRGjNY&vVv_xyG!-NR;CeU;8AgvYI)B(r;#(-O>=jNsf +Nb%qW@8K+}NTUiVp-Ltfo=arze<+Y$Y2LzQyrv*Ixj5-rHlr69Uwu@S_onsB3qF%5M72pWJ`Q&whZ7R +7~3KPIa0s5>fyo7yWhZ8qy1~m)>(VuP*r*cX4j(*xPt9al*bPD-65W?%FPrqL&l@lI!1ttQ?^Wa>`?j +67$J!+{5oboT&&Ly=n%BY>Wob}1yhnFt1MUCQe~lHUK)bKc{n$7id%e=K0p58_ZJ`qm{*~PFZ;pSD?mu~mbQq{c29cxDsiD~i$JeILX0^b?U_m~ +0qk-^C1G|B6?A#)e=__`DlZYCcnCT0(T<|e^Jv0#Ij39fjorTcF!n3Iu;Q>@+Xc9>GjLa|PctlK9G%| +=|#NFUuphNVH+oBp-N&H?`O_GzWCVnrgCg@DolGxP4@od^jf|_fB5%9`Eil6X;+kJa{_>bhUlRG>Nqp +uIXL?nC_o!m7~)WQJqHh)ye7Y#@Ul-g +cZMzhVTx)iJt%of1eG$?gI$D$Ne(?VfXOw`$xNq*LNM0*>pN4ZCX+io>@q=#0~7;802piR6Mzpa!yi| +ZQ{T~^OKPF3=<9-S;DjK^A*pgWriagrYEb9=d*?mI_Qk!8?!FqTr{K6UwY-$^b&dhBuN +P%(vsJ;AmgkhQ`&rgl%Xmv`tmA(5RD#!)EpZ%+Nu)XTfWrpNxWMy(gD{E~=NJd*P_$9&I5hjGN} +lio#WNk@HB9m9;!hME`45X8r9Mx590H0H{3 +Z&TejR;1xFo&_|N+^QJbM#G8u#_Xvei{6jmy_xiv|uRsdVgp8_4dKRH`~YC*e8G}uRGwVbc%$<%nU<( +P%>oEgQl5J*(sF5Ep!k0q%3Ext|cE>G6-n2s63_=9j3Z=uz$4o6IZWPM}7T7WQx90$I#2P4x8Yrl2W8 +@YeRU{Vr1{#3}9o~DExOgfLBX;$*`=h@}pz*2)pyIdDA)*Z%#<*2cI@l=;Ay@cye?O0wQ^z(Wx(c7Xv +?6=ZXCM=bxEYe*W3x+}a!Op_rKNo2q1}N+Y3N<6w|-7AvK*`aveR8(djrO%X3Kn8Cgn~OoVK)07*nTOheh*w8SMOYfF`tQa(}=Omd+~Ncbk6&y_o$G&*jv&D9Foc5vTy +=fDHK^g2Vcg&pkO8IsOtHTuc9g6?6yxQytEkouU7`f{%1@>T^$)V=+`jnO3^JO4>?nL+WhA-F|l1={`VLdf^Ms0vzDX;I+&iaHp7a(v+0%2jj(`a)fBqQ+o1cG{Tx(KeYy%LMNa~?DP`I(N5h?}*fVQD>HRhOwF`fJW{4+b+BQ +!4nt3Eu5sqZ0|=LCn!g+gDZGo%Q3>o}Zx8F`QF4Qq`fKu=Vn5*YC?paF@}lsv%9W5Ov+bOCvW7CeEnP +XgIy(6gvidriY!s+0ho*Ymi@`C+`!@A +h@*2o({68Tt~}LOT0fH>WjVPr5;PkDJZjIF=lboG42V&8~AjEqLobK>M +y7`|nZQ%%Zx@{v99-CjzR*}|r{$1jHX#@!YZ!bMB!jS;b*w%3F%Eu&={oNQ8+V3!aJc_JyF16p+wC{I$@bfC5|Q}NHj +sqTNxJdN(~Z9*XHTM&c;gIzCTHs5hUdi6|IyH5x|NQt7ke-p2md&Jwg1-NhdSJt-AAXB)A;Mt$ +=BBHY(%?>4A6??7s}LuMj$dR`&E%$PSznq#5oEYDsqBoD|=+BLcoHktOAwCL0R~OTGIgqUs3SVvtZmP +&@rHJaf_5>t9s$cM+41fDUet3P&De;$dslXRaLE3)1o6=7Y2~XWJg9nr|=3)AMi`I0Nzw3O^+cin2lWEGa#v@=QZLDLLn;m`628t)M +uX0AapoQHdd~)?@b~Ihc!~`u<{Ib*QtfLGGs%r?6R%IbxA!LyG1gK!}Ve6B2NX{5lKPj&I=y2oXC~od +JPT?`0m)uE|sxOixa419d0m5*`3^775EIbpET8f1Wl2tU36vaA5Di6p0GdBeR%Bv(DPx4$!G2Z|{R?2 +FrEAA{Yp97~rI+&soFb#t^w?zs09nd>O;51J>x94Yc;~xrHjXg>j+NV73LVszeegC6o!mi>eBuXYtd4 +82%Q|#}-M5osLkK`T3ayL;U(oU5ZkM(chpeOPORe=(v^l!5i%1@6&PL( +5|SmK~~CJ)lX$DV`1-$uGIy|dSSK#?<$qQBw4u8f>8Yoi&y;?KGtW~1XgH!o3pyb{wU^ghbKr1Jx~_K@cXqTA0$WKb`whmw) +7`7cZNxRbbe<$vyj>C +UVQeK&*I?Q{Elacl;_%PYzw#QD6N8GGp(ug5rcQ3mr +;V5AQaLD_B`gK+B{f6$$Z}XfA?}?{4~*Rbgt7LH8NuXY6=s`pyONdZEoJ)-U2Qr8xvpzC_ik@?q~U?kmj1r(9!8g#-pWVW{(gsA2plsp +C}a*QOG1sm@sfYoAHihh*w-w+(;hv^p}P?aF%V)>RzBU(hw+$IMYK=x`2NyTzunEvt&3L%mW=v)U8M}0%3Xyq2&@W10%@wh55M%0;64rtIgoioQ9A@q +%=V=z?j7BQT=&KlFE?gYPGVYS3!t;A}BM@Fqm?{&S#LnDaWmzGfn-ZB2in=a1lv}yaF>((|=CDuTwR2 +@46?-dMT*#6LhDAjC6bd`U&T|5(!r|*&9FSR&PgdK1%s$T$9$=%#j}y<$wt7a;V*eccnD%fdVKu;20a +Nr!#Z3J-1s@IYOxryu?>FSLuq*8$vpT;4JcgY#+2nUZv(XJwGBd=|1FCDe_pm7qEzm{?R42!_<3~%j= +!UL2WRjK{zJ1ncv3{f-+6&T1o#Gh7WdMop@wmv+dWokm7FpgZ;8lh0#VC2BV84xa9$$W4IP5(!b5RD;mf|De{4 +@CsL!ifoPoY%a6X1hFASx;x-g47g*9BRVYDMo-Nt$NTf +vW4oV^NJ0X;$@hGk4&+fJ|2vbSmC{uE@dYmjHN^n`HHU?;19O48@voK%Zn^T=e-CKPhxG2zh(6{38@D +3C`d8ra!lk-T|N{-_E>M{^W7>tdjJFBgq&Y_*6VRkO%0TD!KUW5FkSNV%7K5BY+E|3dkQQj{eDDEReR&$B8zOB^xzmNbR7 +R6lOtA(ZG>2n044R_ZVXsF{)8SGxiPsfGY##akur2E5=ofocL5cXYn~VLpaQiE+$lk8_{VGvxDApS#( +liGYdYPqjEv=J)Q%~1bGB6^xUoG>6gcy!GM+QKlIqa9hyFHJ6+YJAs^$S!t6p`}TV8MK?I(l+o23L?{l2wi>z!&7n0kD8&zIod9IC +Qo38k;AneuM_T>d!~Z6!6TND#F;Q%`L`|wts`+zyfi^xS5SgIed2~nwPS5csrHQ;j +7gR2Sx=9cm$%`t>h!V^O(diLEB2#H;W{c1}o(lpP(d05~x}pE1B2F?z>I~Z5-i<~D!!PY*7P#AB1D-g +$8N(J~c8pBopWH8GpT6Cl;|n>yRG^^?yRl&tG9Lz;3%aXsT}l8LyOn26w`H01S_cq{k%{cr3sMu@^r# +|}tS|}g*W%cXD53)XR_M)Cm@k676B3}=S^eRj`&g3ApsUGeM@AHby4Ue+|IG}GMUsgxgoHeDF}ml<;IhPjysdtG~uhqbu7Z?nve0!A&;;g^Gvy6wKQ_895K7DEDL3% +6iZpW`ofN7k2{6@Sb0}om(^`41#YgjL{@35N##6hGGA^~ET}9K4rGqE#}Y2fM{JC^E{9rnbUKYNZ{C1 +)()+j++K1Bu4@K^&xM%ZUCj&B`uSppXuf1w2x)=_f)kXBOxRuohfK+@DnoN%LOc{e9j$?8Y#_18dgs( +m!hdPY+eu<>l(z=URA@IpuCn%e1qi38)B9(~xWW?}$xUhX9OC-Lqpj4W>1^d+HOk}F{Y3r)IOkgrfdFO~Lv#sON*4E}G4EHv_&S%*qPvfe-+{9m-$ +7%hqw0!jZbMhbrSjD}BhSyb9(-}y?pm8M1ci33q#`@8qbiMY;)rULDS>U*8P;W1l +B+TrzMx~B9og;D$Xu)*bb1(HhXcuv_ZC9)qi19!Rw>F2J;?L4tnI*y>CU1R7$OdC%a>V)+tK49ufE9B +vZo;=i>j9QQJD9enIC)4G~wAMC8=Q(Q|Y=@(d*@l+r#KRnYZnbd(Rd+sk+&N^zIW*a^7>k3x0KPxkI! +&0``ZgOBZ}lG7*sz9WY(oh5@FB*!T+m{VS;ohITjbBfH<6;Q>uR9V3HN?Dw!MxrC=>Xofuvlqu?6i4N +U4qexbtf7huviNr{mqX=xTa|c_G3WD#Vw+(4A2D_57FT@; +m(npuJW#EGDa-^IvFByYBihr`1yU?5#nLEk +L}d}Y%}0;W)GGHrUcWO-MljF;$>e+~ZQ#Bq2uV?8368H$u8PulLVJjt6MFfP!M>Ct~?TzcL<5%WgUqV +ML&FRhCs@q<~wGuun~+2_w*zR)=xP#q+$hWFp4z`F6yEab&Q7m~oCykwaIJJP&p&=w3l&+)w_f5|@f6*W^VS7}DU?ZYm~)@SrCt4N!Q4 +@gy#XVIls=@m)_S55RwAZ+D{;Fhnw|-VINF%;HR(>SzB(C_5+651PEWU2wVX-2Kya-B?!jZZbkujm*}vLP9CzIMAtHw64*UJ*cUub@ +^9DJ7xEtuH*@ZqwqrI}Wp^T$+7}>o4X-;X1`}YtAZ_pE|?xSmXqU=LU?>u(PJ^z|4wXkiW>=53n;Vu= +P1Yo9CpU8(hMhCOA)PA$q=hyLZgeCT^SG<3DfkpYOt9cntp^$jDAUR6P(7oHNzA*oJ@`y?gup+x;Kkh +6_A)r<7`X^c3$txWjMHo<8*)+3-s#)1YuBo_sYDAPi$1%Kq?Bsv_fg9rTq-(G|Mp +efhs|$TFsx&ra$J)WySgD~TzfEvkC6!6c`)!zqkqBxtQ$BeJl1>aZ`OwC=<0s>#)e +hRe5q*7*^=?zKCD;`re-R6ld8=7wmix`RLrOIXsLyavH$pm1$I?`!kXi%Ib45z5jxOKdKPAC||G3 +0^FRt~pG=@~4BYNIu8q`;@cA`_{N#$0OC>UL1Y>^%nhSplO}3tQ)i9y4UUM8yj2n-(oE5L^H@inzp_m +E)qU^we{%D)}tfXI_S+M-SDQyIq0sYrDm($goULgE!$`6jj=;u#~v`rm-XIqJGFx3q_ +s{U1)Qm3;Sl|L<;Mg4184lYjnx_IPkg_SA%K3 +`q`mcitTyq0Q4*=j5N8r>DbT&rTnoKK>2=;RnB;84%v=9lhB;hDMKekKOwJJo)FZXOB;R|LycwYWu&; +^d$KujDhTs34SwF85L=a&(BRmu1u>w!==&$j~^4u{70^>Ma-?kWPbU3wpHn`0-jW#Y}uMo-QqbyV%~YzH1Qq_9JfC?Wl>*5A5bNZzw+k1vX?Tz}MHH@rS +?Cy=CF=;fM54#!0%rC09YvI(8aT|}jd$?+@r?T!&=;mAL+i@}0Q}7bHHhB?H +@hvVm&4<30e6`}Ir*3&kEMy$<*hab$k_t_!bzPi_yzIG~Mh6-SBCxC*+y&8HFFoRiN#ml#rH&hOBfY6 +8>XCNfXd0^!K#M%x7qC0QQtzRSj8Z7p0ZoffdeqfSEf9enXoe!Q4?EN#bVNo2< +sfrA;u_Rhvy}5gtyeDH=D$Ziy|hLB)X;p)XUBpU2K~6G7uN8E1T-0x1g8+b37i5;A=vIYUrUveHKJEH +kerWdk$!OlX<_tSXCpkv{Q4w`JunGnI%EW|MDSCY$ep5VN-Fz+M;Rn{0%mi6e61r>V%93jYAB<6N1B* +H^?66waKy833kyAikrnQO(_B{ro~*{=nK;n6z^MSP%gKr|9^`tIm*pLiebrgaD`SoHtrVWXbEB}Gy>8GYvk@Ghyd_jDfL6!FgP;W5Y`|L@)2;qEuxDhsZ?@jb9f=J|_<_D9Uf$eJ +IYf9CSTJ%YysB57f_^}$0e{aMGE028DFI`h+ +GqaO(ULx^b!N%(5Pcy&eJ^iTU16bg1|lHHytz#@PO;w$iNNTrVoEecueOhpqqlpD*pZEelMtj&= +z`kD=$=N!9v3fBIgai!?FmbBk*jT0=I-IMKzLm)@ndhok?J0vv4KinV>^*#?O3eWAI}U&dsShUINpON +B3aHN2*Rz?-RD$?$yT>ka3WwI +l--+tSj|(}r1LmBq+oF$=YR6Z~mN?9xu9q`y{s|@|k=yrzkXrm$$|Zg%T@7Ze;d}r(1$P=~!9wSR0ok +TTXXyLjzM6-QiI8GZt$I8kc#2#l=t3~$yyPuign%~^q~mr@;AoWHAcB-Z@Ff70SDzDRQ9I(=k~r9IfX +*?cPlH;+d3KfFWLz2}ENqC-I}rQlc(N(N7_jt^)D4xiX8(IS=!cG?6TZCsPhK5o1B8l?(a!K#jh}mbn +}tQ8uvrH0$|k+YT(Ou6I{-;irtX8x+Sn2(;F4INa^oco)8z*ghdk`wQ8d1)D$JcjU{Mw}!iL4X#7n3| ++2~wJd~vw$3Y(5_Cp1=uRinRxy+}F>$L+tzoQ@w{M3nT4&>+@OZKpiOkDpPS=SKpQBK%OVO9tl%JIq$ +!9lj=Z>SjJO&=(qd5;N|5avsShd=m`pkQrueYpUK{E^I<_S-6>WU%=%Z)6~pdYyFZ>$Vu2JjbZk_cTe +dTsV!ciV>@LN#{038>zDdiCy>jFDD%+3{-{g}CpY +-3|3x5{^g-%vkNsc+BEPwjx3%Y-TvSML$$P@~|q@wai*lHxiSI2K&OXnO;E+*n_?(TmH&~=%^uz;axC +L@#0L6N~w0s^)QjEypibK0>Alj0&sk?2sd7h2B9M>%wma_5=|i5)cVVc1rE-B4nL@(SjhlBn9m;JO>3rwY=ra7*r!2hi?^luidYG_y~GX_mqoKmw#!Px+>M^X4Ob?lFB4AWy;s5vB#qa%QkcMr +uN1jM!#@5RASGbCT$%wre@GWt)TC5&l60{nE +MGo!$It3qj5HN{d04Ua}ypqXaR;hnX~cKRt-T&uVLpOZMEld_)rZ0gA2W64Or?u=aVK`*%72a)NJMl; +RZ?sWG;U(XI29Qg~4vbAqhl!;x_{6)w)RBE4tt_6~inqv=82W@oW%*^hqz!1bU3jgLIR>1Y!@$0Hc#f +G(8JKxd%Lv#4F^jN>ojFC1!LbM?>O#6;V+i`(}ZLzDBR-35U +Q%Ug(`I@)|O*GVt{4CdDix#?wS|l(80>y>s+~?J^T9)0DaaQ{|7RfC;|oaopU9Jjkd +vg_sXtC?_%=BMx3ww`j&88;?xwJjv36g?3SlZau>M%7?o<`-k7~nQv?+&WEO08@VVs@};|;aITlemyl +8Y3DAPPaM77qIb~&rL9cAN4o!zeMa%lG$R@~27==>k=$=Iw`nMHk=M_N_CJ2s5k#zteWN;Paj1q0cgAvhEN@5bu=ED=|4;2W6@~Icz8XLvf5Eol+cD5C#n8zJ4E932h9GdBj1YUxWGnj7&WGQc7F*|;c{!@# +D5{6Umc(iv%|tdL67+t|9LVZkJIqvpg7a?-U)Un1AN3Dtn@#DuXE*3wuo26>WG{uE60+#3MqHU1_E4m +e4~$gRckV@>%@moE-vZM`Cuha#|ic-x@@ic*qPz&}=}0xnpJSLG<9|0Ps$?a65#r0@8L5?A +jPE77Seaq2=z;58o=VN_DW$owHKSQ!V6+Vi5Zl={vyAeixj#ZCtX1XMlm`=G#}{De#-utaP(x$stMgg +<<^MZf&+j3lQ1vGyr04s`C?I=*qB=3YXPkL0tq2=yp6-`2%q;3v{c6RU6K?R3eX=;LJ#b7d^?FO+8Yb +NFao-j$Lf-+?2EBPmB$8Y5K%P@qJr*4r_3Z?uQwfFfg*o3h)yK%oUf73$yv|MszV_Ic2hW6J!~nA27J +r)LI$MHo?KK^4?OyCFJ5v#l;?6$e>9Yn>ofLZV0^>R)%5nxh{Sv?(N5)yF*gcmV4X1<`0h&*hqlq!Mf ++GbjF(CjH&{X!z`%sYfgk&{(H(h?9I)SYPIuyhc4=n25_T06*vVPwq-vuG;Er4)s&dwx6OAKkQUpU^H +YUO;2Ptyb=z6Fog`38i8R(hihuARnJ)yow*8_tsE1M8;83NtO&4MFaa4^f)UCw+-X-knumEOzbsa^vm +JLkyohf)vhW@!`?^Dtm12w$+IeumKclc1j?LQ<1p5;>Yz=HLi--I*spKUXz|TQ3pPLWCH8Hi%HzX)uapyS(&62DnMfEP$_ +{Uxl^N6<-(OAx;K7ZEJBzvYt7Ha#mEb<{GgW{w>~dRaWRCbn6b~*IGwd~nV>4KG2_fk+MD_>0ikQE&O +uG$M9@PO0^Z$E5q+XCj8lAb-m3uekpg6=<5gn*Fpv_Sa5-C${y)WArclwBmx8LXhhmsg<3U7ZIVtke% +_B8>B0)+{97Oqrw!u+;ACjGPAUsNq7&AsOJ{1X+O$%e_y(PfFfQ1oVLgUaxsX>f=dDR-FlaZc+C~-Z* +r1^QZh%aV`VxYaYMsrknW?B!cr_twXvkkIjJ40?z0tDw`s} +S9&OcK-)w|+|QQZRu!Ej`NkOh=}b*%RyV8SMUnMbEe<)7 +^}y)dcQWrRs5yAgRuhQgth=B+5c34VE=GIu!TrIWQnlEbTKd`OYn#bR$xvcXleB55_CUI+GZ_(t*D%- +l`5XcF7H7efD`0;PcXqfjSp)M{n2;nbu1i!}wW7(uNS8uyC}WM@`MLHtyWeiVdyRhQ2m42R$NPu>2#i +}gK?VmIBSw_d?DGBKo1t5G`}G^FhUFG%(if%h1)0}HXXnRkQe{twdiVY}`6IiV!DulDb`<^_P0L}-1k +$kqnK#v!&O3E&rYlDDTl4h2Wuo)CM|h=TWVPA#g`it)Fj3J+_dupv=+227*(1nBDp+)?R>z0wEgK>Z= +(oFT3uR0VEB0JD&luqh*IIv*Pdsu!sA!noB}GZEX}QSSu%Vk}aV^PPh1e<5m%*H}GK<%`Gjt-TTOFbu +2MTV8g1#-?0CK`#<#pwpUzFW4edz#t`y*pLPrX;$I7o8Z#B6DCCJw;<-j~If{ZmkS&9tyO!nDnY +r*i}AKuo!xbgcoALE}1tU61k#h2n&QK3)2ePQd0{qH4$opXFl|$$AF_No#2sit)fy4%3%ONG^#)+Y|i +|w2ON)-76Dxs)qnL98yT|up+Y^3GKxaI)MX?{cXKuV!(_-3%2#ojxfT0qD7hg$p&!Xrb{N&Ow8D +FL7*DaiP7_*L@q31zs%n-s^3}ykb^|vHuXYr(?8!rfy)f^;-Syo}vva6JG>aOYeTvPUhHCzsBi +vTv#LFYo~&4Pnd$Sg<5k~eJx;s@RBc(vf##z4gW4$v*PzZ7Qp5esf4H|K8bLKj#-R!4D`uuF>J8gX`&!1!c7ndF;s4MbWj5qPhgxe(9E26%&!O$!7!PxpQE8_l^G +LXpXf1q|XpXe9Y2>@gSz(6?9gabd6#^RL#7%mhko-UZ^>qAAow@3&6n0mNCk{;?4%v4;8ukmWQi%D_E +C!^cYI5)PE8y!$1zty1pb>DFf4kCC>{LJS4?U7Z^*V*4@SOC__`VSVfUwJMPH_7sNa4oPwRk@RA~aXA ++9W&XO9&R;jS)Sa_|2XrC2Vz!j-82g?14qIy|-iAdP&7--2ziJJv!tSKcouoYKqPbBr=8#=X)>A&Ead^MKaqm6yMwMUIWzn9xI>Y&GV69)YYM>)1~6g+gmifkL=ge|)# +{951JoRW^hbc+BBHDqn3btn2=Atp+HS{{|a}sTfmeXI_qr`9ySc|47(v0G`Y^obt`k42SaTw$Lud=0t +*Cik!(M1T}U}W}*RV(`f@vomuk&&)+zGFNBCCNNXGtF{9MD@_1K7oS_V*uAt>BY4!Rlu%V6oj+&XUS)u4uie2%?kXo +`{uJxkB33l8b_}H>&0^@53P}NAZD4C^N-UzB)gv^4J0=?e5JZz&JK1g#H`i0vHqlJsToSj1+-GkD_3t +|S2Ey+3ejfzFrWsunSc~hwxMmV2?+x%iqd*I_z+n2^u==ztj|9EKxkIaq@$ +OCC>1>!9A%v9eq2|W%ota6gVEEJ5WKioNL)(}{&D3xqiknp{+Fu39gpQAYvcdvr=K`>FFi+pa9pJ(kN7%xdyG^^M2tYUaV-r^2yfCc@ +*ODj=Cd!ZvCP524v~4hA}){Fd|@)p8(5yz9JbFQy#YQ?3;$i2tMsriAl_Rz{-Dl^jE`M;wlU2TCV|Ta +Y&#TOO*gs2=fW1#gf0+~j0qCoiWh{Np^*cgBjw3K8%B{yXw3{Hr6!1wAl42L24V%>^P8(RvDmr8)E&y +GF6a?qOcnb)e)G}(CLa|4LF2kS(u)w29EHZ$?Oh>xq0r2n_iY8B%ou0p +PoV-LnTz0D=)$*iBLINr!I)|s%k+dNM|@d=S2o0$&ciiO{wt5tuzSYef1c<>()opBnnNy7TXm$ +12#^&0@&e7K5hQ>`Lj5I?4c=_q)pTGR#DZHe8F?4ifNz>? +@FEA%ypCDhDrgu&Li+T0kdT@aoV|!Jz7*b*G>~fOU6ERI20>>K?bd7N-4JL`!B8o*iFPL=F+*%DJUMB +D`kvZ{%K7@}nzjEl-d?~datFh0xvGM&SY%gYen#jaE6Lyi*Szu-p4KN+)>4cBS;4xhuVUX06N@KN{!V +DxS+B(&=v-3$pp2nAe;Mt-f0M4Bu2m~cnqmVW<^bMF$R!!VN8@yWcI1@^2Y6bWrTHU1SX`jIH*910|C +VGg8w6DvGE$M8)8}wgj@-T0$k3UL|w-3MDJx-4IX*1WBju0fqFxVnt1jL%5e2QY$mMJ{RHz2U+HzW$d +o8R@>udQF{AR1F#umw9Y9*?@jWk}jdm@a9FC43ssyR48{_>|^{qn$V3Brw$E_`Bl-uNRYh8=9>8;E2? +o4vrqsLz%1925{2XAaI-i4^T@31QY-O00;m`Rt8hd&^q#}82|vcUH||d0001RX>c!Jc4cm4Z*nhWX>) +XJX<{#SWpZ17bIaWGpd~>@I+7z_r3d0~Kk)_v4vVLFYH`H-!OS@#PpxqojT{;}8Ta+5{#tAe?&JvQj|2 +VMB}l)by+>}*q9r3pJvXT^1parSk(N~S>(rHPMC&pBt`zIlCedVb;;Hw6olDSQ3>>ASaYzWU+a_ixX! +(5G(p_V(`Id$U-^e8H1~Ah2}C0yc~JO*Dx)Tk|Z3Qb3XA;T2S4%Pd_-Qz}?3muXh8S(>r4le6y8QLl6 +VUe1fvvPjc7?}q7O2|y7J;*j%2kQ7m90QDsf_wPjuf)%IpIh5=7jN`{ymM)ktz%ltHFEXe&E;dV^s~W +2!iq-dJkmY=w@t;>wM#Dou<8ctj<1ssC7tU##ErK}ug-?GJ_&6PA6n+;JFyPFAA+hqu`IgUvaAUL(1u +eDD<&cf~s;H&K4~ZnJM(ZTY(hOV9c`+7~;EZrENGqPfSR~PWsPHY7x#MvXEckfrVZHnJ!Z^rt)&ko7F +^E^39=)MJ2Il15Rg^RM7bI*|wo_B*)2w0DB|tVik&xYV@)HjW1#rq|Y&?#Vs2GpkoX4{c%hfmh!6JVw +rW6L0$~ikt3%!K+RFKqx_W-^{>H~`sSXTLJLR4}*6aE|Ey=M){&Tx8up@|K_-w0Sz^!Lv4AGu?Jg7E-lc?vLlN)1O_#euuHb!V!1KET7Fy`4D3-&=kEdyv`%6+o|P@}{2LlHv~37cWTVS=ZkjH4i=MXz}6iWcGz;r*088TLk& +7F8Y{i9D?LAMtOb%KMBM5?ch#@JKW?vYSG!ZaeKy(PTnDST=rF@DI)1@k9Qy{q4V=d +(~+UCtIulX%05RNHbvT<|Ob+|~wLkztr?o`?dL0^4L{1yc{FOtCN;>Q>fOyR86Pkk*yWxiBbX- +9|N78aR5%UR#o=*M)fBE1DjG-< +VUY=bQ@kp02t0=Cv!Bn&D^JJP|g9LIN|B+KQb&Q(nlBf(|U0lB%S{-fmo9TZEU0gZ@1|%JjZ%X(&?J# +OT4j5O)pl}k?rPmAq9lcdNWfO3b$fAvcShJ+d{kbo?AK<{*^;HyJwE?;Y|3bPwVAGbI4TMhFd(M|ED# +Qn+kecAEp*|?1S#*PpbTRA>MjiGpTk*DT|7!)T0uqFbDG)H=p{)}kV~CFx8qMwNE7@Hq4YcBMUR~QI3 +g=fu!qxscayMAq3MCL8GlNLtnb|qBnwnrj9}rvgW_5P +AVd{c$gS`0YLNTsOF#+=jPV=wZ3%z1(f1Wezl +VmnrB^*#_UIeEo4C4%~~N`^&5D|6Wc-E-y#y$qDaV@#vgFG#ju7U?)e^kHn^-_mjTpYv?Xr%PhKp +Kwpe?pb{zSzR8_K1qfX=gYwM2v#zIap^C96egc7VKf*B&XuHA*j06$Fx>FXN~H)7(+A9?VqmY*88_1d +OK&DTr?u-Tnv{Ue6eU?1w%NyZw>kCplW#Q*UH6;mZ`PjAIPc(yMJAs;`K%ZY_}mm4O;llpYN~mjV@;q +AY(pd^S=%y)KLUyabv)M$;-F=@o#r3etYTrr_;|MZD>-$*O=ZLJ;M!;AqVVe@*tA*8y +eN6kHVW`l{|&MDdiukMe0WhcPvoI5c*kSYOT~yjHEP7Pm>Y>~-j|(uioSoRi9THBCY$DCHtC#>EQkYI +L}-0*4b0vxN=^@5(r52Mvr_lmIwcLNX$l6+ElOO^9#tGq6)_YU{8o;}Hwa3BcIu_PvrJkn*Z^&@;qGo +oWikFHXlRdsZ+AX^a<`Na%oBl3oMd()TPV$gu+3zMz&cL=QxcF+-gA(Y-Y(R-+45sxxepc#>_?ZCP}+TJTTdapp +iVfe8Oi)d%`n>3vo1MT|*gDEf$2U-t<;xJ2l@X_z>S(W{s^&0f;uZO{)QZT932&)DDQCaZr`~UoY2n- +HPK~8YG(l+{6frYohX8UXrCDCHFV8wNcq@KW-Z8XcOYT3km&11H}ILuYBM(Lxz8QvwnvvCBh?u!i2WlDd +iS3r0o@t!0d=-X(Oxd>87{ec7JxltH1<^jmKoc|D;60wbJ$ZZb?fBiRZ_dg0EZhqco6ZD(h4?F@->~S +Nk@x9)TB7qH*|>%DhG}eO{}hI+#8${-HH~W`>R8OLd(&YX)eFVsip;%{L*B^uO{uPKkbO3FtjcJo6sR +{~sAKOGg5ETY!-z6qR0%ELjV(NHE)p_(VG^1ppxT|2f7`d`DkAd_+^eIVC`XdG^tl~t7tW{>7fy8-Pk +ox(pPrkLD-d6p-{$tWWAhvT*{N^DsgFT6i9q~h8>hBcrm7?w_HUL08%%$Kl{qGjW(QSw#h!%e4}}%uf +!w^YGM$_QN9|IS;K&S63QmT5x@NGa!6pIACoQg8fUqFr)#^3o(ZSW+uhxAtGXYOgd?V6Q!HBUSPkhGv`UAfnb +jKMwC{9OwEf4?OvR*OrKnv$916Z$Xw>>k{#;i2xXF3&36rx$7JSkK)wZJkbg6v2TPoXG52i%Ft3cmxo +3Z_!-F=b{kb98!XNteq(5yhW3uF{t)&AY8Wrtb;@A&N7SFcaLJ2`!aey1w{xW+b+bKxI!N1r(!_@4Ny +=?5bsixlir9d}r+lCa3@&@$&A7yih-oO%c=b9(+|zY2Aq`KK5e>%pG@j~jro*?<=S>W;ReRpJFyn(jd +B(slhu-lg}MK&%NZ2@J#530TrP&{%=AcBM#rd6_?=?JWs%AVJp9(k&X{WiDU0!unWvFrA}c2c6%DaDF +r8FhUX+5ysK8Rg$CQO5SrFx66qe#IxWwSY8F*<%EC+F}!HN7+1^lC66(y3qG_!!RRf0zlA4y5a8^^gF +g;b@a;Yt27u1hG{~lrTBMa8-3c!OIoS?A-)*+VqB;NJ>#yJZ{Vr?g9~Z+9{utKJ!4^inGnYCSokx3~d +vSR=+QRr6+$y;aD4`_03bICUABG2GCKpi)FA%t89E6-jam?rFe71r6mvWZBUF6UvfRZ)@L(I+T58s`< +ee-&J_VxtksJf$V?PFVe#rbMJ=b+ToQK>Qs#R!Ruhj*Dw+@%(n)u)bEfm~jYa4wTu@y$pc)cqJ?Tb&1 +sxpbIG2>8P6Eg43=Fj7}si33{}7G<7_+RRotr{cTVqXS)x(}%jk;@74?Gt~C;)x^aH9a(H|F^RIKwjj +viKc|d|ovN)D#6#`*2{qJ_=^&WRR&dOXOjiZNb7ZQqh*jr>9P|Fp1XqmvR;FG?eVc}1ZsO}SLOlZYC1 +1_N7iC*vVSUqB)G1@e7dQ+l)3og9%(QtlhT_3a^1%2M6`Px+cgmawp%vtLFc(oZ3A_AU +UuX^bp^cXIniIudB@3g5cDZ7S1dW^t%L}R3br1?|8i*q*)GyY>XlN@)m^=h`MSjye$40~j62TsvH(8F6&Msz{+J=oZOO +dXBp6@aX9gbHC{Oy(8VhHBQtj7qbquEL-WF8PWVpk!zi^Ux9SLu~!L;vf4~(CZiUD({oneIM?KdsTa9 +D(X3SWQ(o|F5hX}wURCo&X6}=1+Y44P6_Qv%3}*CnqcM>(;wI$FqAg|3O8UulQf4MH=CpC4lr*KX(4+ +z#X)wepi{L#65vmTgPzrYz%}epwhM6YUu*RlSSn)lB8^ER}rL_lDKHCGZ_|U@##gKM204S=#gRW*E0iBR!E|DS0x;N<#_7Ic3z=sE! +!~=LrS)w55u!a=mc)DT&6R}~)A06R%b{H>0)gtDY=AeQW0CXDUSJ=}uEt&@4UIdeHgc+|Fm)%PWU>ta +2NSFP~L3KzVa=I|oZg%*zkC6;`_u(DhRE?hB7d4(f6QPbiR(SfXsdL#SCd-P)m)T1L0lx`@*T9*{kPV +>Yz(4HJ!=&GVj-L6w4=R18(vSaP^iqt}yYyem(X~Kc_Ymy5ukt_ekXml;);nvbfgdmW!%+p6c6*&3%n +1lfnS7&+f053!O2BM}sR!+!M^oPAvl*uF;jH6Xtq$7*RdeX~p7_0IdkQ}4EK2&yhwSN7=6)D}A^@Jyi +EcIlE+@SMBCPxJ!R6H3MgSH(o;(i4a!FFXx5-ZM+}IvaCD_AxrKjGW&(8JT93TQ`I<;t@5_J3~dsIv0j%d{Z7ri>PHJU6EDp?O0j?6Uq{lD@qd~9;TRwwMU1|!1NNP!W2_8Q%0Do{Ilx1@-Mrzu5J +l{F|-;5Yu$ciU%*kKJ5S5Po&Atx7XuV}MfWNM*R-)CYIQ3b(1`Y`ec-V%Cv(UPC(h^+OXQfiAQB!`78 +{nTRt#jvQPB6ZeoroG;BQ%mjru*a<}?5Bu-kt+7!1mqSSG4sd98M)s<+p!vuJkPz3tYR6PvZjL>f6JU +x3h|@DsU4D|GLMRg?~zP=fju{9%n_;o#7qi1$|^NYslq<= +pY%Ww9&HzdV)5Sswzq)Eoz}jju8-Cfa}8&W+!sj;xf?q@<=L%Erj_n=rgzHf4)OQ03iVCt%Q8rwd7R@(s%Hpw%o$_@w@jZ3Uju%yxCKyT%*ihY>Oa!uN6 +bZ%&IYWMJsLh60}uzXZt_P8+~~43HHS%nz(&QdpKZng*+$6%AH2&?@U$&H!h;`sO9~8Ix+L?)!TQq_$ +(gi86DihaBo3gg1B_hX!YNCMnf4_DRbs^ +8Gg>m=O~(?1M)50WXfCTq(CXx3_kvg5pOeg=8#I`(M@L2pHw5Qie{sE<3Dx^c}P0nh*Me>4jMw%Zp)u +gb&U$r{&By_P6wZ!R%`Hi;p`lP64S$4m)7WdIdoeefGht5=(t?9tnqM*A{q<#G1S>Kr~41vm{SF&Zdjkt2C%XC@2xnURJp8RQo+zhpu;F6XiuUW^=%d}bR61M80fgSnmVnf +yfeUAO$FXpjMb|j!eN#E;P1L=qasrmtDSKmZL-SE+iDmOO4wH-r@3-)=P4qkFDO)q7=x +b#fPouC

i$jUbX54<{^)8(bhAO6=1ouGtPMJ@Myzco0{9-nVa${2_dR

Ju?n5**vcJfg@>t|8vK +->c8Uk4dpsOUPc%fr{8`pm0_MC_5y2S~gh<~uGn5$3s(eDg4O8VTezj@o88`&5IKYa7uHEUlP^oZZ~d +^j=miO?P{WiCEmySU9g=K&i}u^ONsDeKnmvCZp%lH~(y*85j>;v>tlhXZmO`Ja0)4AgImYQ}zsKlRr_TO +}Oi&NeufVYu|ja!nIx!4n+>rfBrwwKvzI>+6RDx}GVf*f5m8R!U*+zh5u6FIibqs1zwD`G0AL+dxd=+ +O#dU6iJXP~iI1rr6_JmTI2{-jE?>n++%)%<0v^t&cnWz4L(gBKQff?f_38|13#9G;9salSISA(MZQwu +2S0cLdX49NC`Bm@>Jdcr1o&=oT5DG8cV%O5#7luil>ND$#b%s!%+uedYy)EPw?9#$2Jq(hGBReqZ?C; +Rc>VcB;%p7f`0EQDD#SsK3xv-;20^;g!UcPZ)KDX7qp(!MY6lEX@wzn$@$9&IEZn$$j81u +4<+43ZH$7FpYp}FyE}uupE;@9CSmFv5^r95` +7UEX%m&}6h*c;-1Cv7lT6YBBpALU1Q!izbO@3if`iECXS*kJhcfAuB7RK*#apN4fRIpWHJ)@%O7e7v|4{_YDudK0k-P)%oA +0?>FX`!F%C(r@%-$sT>VGJ}q@n-fKDDnRN1p_2InkgW{Cf~^y{b%WCn1vLjfqBmL4Mcs`z!as!>jcA0{R +gz)@WmB<1ogbz@a^z7@awnm@P6-4dWEp9=F4I70mTgs>+a$20g$hS6eOWWB1Y;;}rxLCs +~bTGhk0H%Tpw{SFhnzjBvRwB)YwP-P>ij^9_4n1eIbBYP +KuzJexp|vhRIXVdVK#pk71scoFrwK*0exc)hjvU_GqI?YK#M99D#fqu+I!5&u?&(?YRu&mitHvC0o8d +%3me-6-G?XxIg9TnqcFp^UniH`AcQnVHmPz4|$bi7|Gc$Lpd-(4Ap$Sh!m{|4^T?zC)0B=)DK0RhHA- +uwvc_0dn)8R3(b&(UKgpyXlHMsv)`Wr#nu4u)1&2*b_)`5u3X(?s_ynP*H#l%fd9kd2Mqa-$+V;+*cd +Oi<_|fPPVG}dhZ)7e40d^3Bo*-2%b5qKLIhBy9{M0fs_j!&$~vhYG}=Ob#)VbCT|-E5tp{9k9I6|g5~ +7F>U7}&6W1X$oJNZ~7#AA5cI3UWff3%J2W+9zvNqy%0<`6|agA}k3yc0uAg(hqDAH5Y85*do? +!tLg&2>Rp0Gx+8=DNIIU^lN<<)Kn)ZVsf`qe7`hV(-h?mCJ)W3kbAr#d4R|hx4YII|os;6m&D#(R@Lk +uM>HIgfAkCS}{g0FDP*D#_mva3jiA8fV``*nX2|ZRjY>8r3L3Ap$dAj-tSz*dPB5TlXb_T|CU2H$)&C +n4(63A0CEFQsP7^>@QZ>KEQnI>08r{? +K6dIAd9$YrkmRt^5y>>YxOXsYIvY(ZALL-gbMmOpYQM3>NnC8^TdpL@uBH6`Md-Ikdv(x!D5Qu0+quy +U4u3JnA2;RM=>k3p>HN4Gocs_TKH6UTBIVFX6n}Hb6`>0*f|h7R?Rg-H`j{`X7Xl#zLMgfDTd57%Uxu +FInjsFz^S;Z&I2}63W6Jq$~Y{p?wg8}_93Juln>)9=7Bjrw}=){is6|&EA2ZAFiKW5fNwuUio_^Cg#l +Yf@R@FS*Grb$+O(;Zybocy%N7cpL6US)lH34sH9--Ju?^J7Z`ivw=I$8RbTNy!G+oT#(-zy?8@^DC@; +S&hfZ6(Jw!uNfS<8PvM3MZiooHNk!Ot}l58U +rAnCo}FMB%1hIQPiZ&K@>Ky6-~|_Xh?ea)uGKm#KanBBG>F8rM+8E%TgwUgXXm^9YrwEbAe)74VZ$5Fd#>RE@QpSiXm +ftphNho@aT!S+$Ymo2df~(N>ffkO87)v3Une%Q11D9s@yJ9ZlxIWXv!H4qp95##GHO(kiUubkmrEEM; +R;U3#{{>zXDx|@~}#|RNaSl@cODC)#GuKkLYkMa+7PI+Q-RnfaJ&RSvZ^o*&6y{7Df`P&NhzLO?@GlluXrCL*g +;4-i<8*As{g$o&)Rq#|-7e93J}fv<@{ZNv&HGZ|0yMvIgRY-xseq>(iU#eh;k(ZKv(FR!vFY`y(;emr +yqcYwwI?W-@b@*uZ$FyHYLXh__zP4l_FS0U)qtOpeGIN5aSpjgj#D76M7wdu*vl5;b$=J3JI)SBQS`( +`6UddMbtdkC3=&o%VBfWdW%--u5=HT6sc6c2;YUH@RZ6hbA0UD#)DMroqk%n_( +X*ZU$wvM0T!!Pnvy(EhtH8UU=#v%#*`92r2ThTP=Dwg~WI=)6+Ka9FpNs5(&DXOIlx@@-vK6f~+zhvP +pHd+`i@GSOZ@hQL~7jHl<;k4W()piEX@IJM;6QUs^E;XK#YR^iIUE&vJ__hx<+(A|sFic4r|91#1Qg6 +X*#0gPSn);5052F$&N@~vvDJ9M>J{$4Wc1q4JidC-dRDK(E_Ot*Enj3qbgxAJ#z>qSd6T;bJRz8V3yL +VE6?w{)b-J<~A6hLF=d}xf=Xk~Tyye)Z9<&EwJ!MNeBud-J&cx_c8XW?qORcu@<@KP1-db<(< +0Hxfl$YEAlZ*VRm|-BR7EW`K-xO1Esp)Ev4f69Dm@@@)wsQyIe_LU=sd1R8i%9)zhETiT2bp(6DvvS3oOrDyW=mMY(ut1TdKJ}x{5Ey}*4MV-*1PVRu=Yb^@X7D2sEV!b-~2(8! +A!mu*IughrU6p`|$HNi=4)WC+u~7k^GA%q(9Czsw+{pKteX{nQaEOXPYZk+mG-s4+tf9Y;B6yo@p*|p +hvAtRR#4yZB4L{sy#GE_37BU*CNLm6%vh11rSl8mQ3lkkiGe=GmQWLz<@ +9{}~i`nUNhipzvKQw}EQOPl0JvynwuL0eK;uUoTP3sU%PMn=KUk7%1a*`&G{o6oO#ux&f!$4)dH;9k; +&$EVyqX_P}-6&A*w56L##s;k%@o-8haGN_L_eGc#5TylH=9TbaMxc5?OLIWcZ*xFuMg&X%HKx_JVrM# +RVL5q@fvIS!*)8RsHTqmm3!=}`vhA%<8O9dZ{8@+?w1G12qNBeYq|v6^0WLk{`0JynEZVRsO1)D}&hg)Um=p=*&Zw7ji}i(&MSLlC{aA06&Z-3mC_ZRv@laI}EaFve=lz@HwF8Tt)84O%C?Yad|34?J!x +$15siT+g01i!NC$|&ZeuyA(gkd{b2LPOErUVfDexXY~IKHzAJ5-p%oc*Am2d<%VKPUDo%Cbg_%445s +b9QtgMNU4PfV2h;kcmBkP|Ga&a7dP)Q>Oa +nbvfeKkv}{k*^#p+U`LXa9I7|xk+20PYNTb}W!&;ET{6rWqjINcDz`$jU9u$A?PNB;iW$F0`7kFTK@Q +^1U44m@6+p-Ch#bUGjlqpDD>-2@K9&VK-U0JsWg2P+enZg7x^SU-tEO0(uEymV{HwOi3hO+-Ty|PG5m19IbfKo21sL#Y@(j8%@$$v)|zMkQcNe2qN1a;T{{ +pUh~34#6l*4Evz)hX2X}9Cf~`*U4?&Mwx>H;794Y4TFK}P~Lxo-5Rj!cI!*&$$=#H=DWSNGk%ymYl4kEQf>G7-SkQ$TGlsqLwk%D +0Vh&xAx`tQ%D~VZH%sewdxC`@`9^?zIt^ov?wd7v^6psGrsn!6J-@!HW|<4DvLpUJF+8%Vg%5j2M2Fj +S9(BtvU9gP1nf_%G@5`G9UEgPqoc3F+L(JA@^cY3uX$nWG#G23zU^|o>vF-jo^wNF5OB?w~ZkDBrw5L +kR~=g*LTRZm@7}24y-oK)=@8IBbb$p&rS$)vM-A^24>FWB0%016Z2PEjcP~jc)7x|M+7{tXC>N&-1P& +o`wOUnL8`C_*&gyXs0aq9MO<%XYCPoq3gCwdOHE7YL00S@1{h`H9s@sq56fr2fXptWMJw`fAZP`9$cx +2vcKb;QE?nd4Zd~P_3I`XfplSot??tA6I_nCiUywDb|68c!7sSWR?WPlgDRI)XC@kPE(2H3C?V*?n}VwShw|ydPFxa`EJkhRlQ-Qde2xpa!YNe4voQZms` +o0djus)ltwKNMkk2!cob*%p%#14K0~HpLU*I9*%CYC;Zby;)ms>})hPG&VK<#YExfu%#RW>1F78qu=0{7uDnsMpEGXK+XHgVDs81m};vvtQ5nHz)^kv~MNmf2`E!?=+lqUQ10}`n!0|$+$KyA +6)Z7+`JXrlbRF+cEy@XFeHk!X{A$&I<#29oJ5IOP7PT8CoEF@z$8cj +yu6I#NOh5G0G?FCL%BhA_ +R6fBE>)j(EhkfPd!%AaBVSgJO-S)iUR?)9%FBpXN!f+bC=bH>u3IvI)KOr2(NrJ98ZzW0o3^9i{hs? +K-rro%WoC7hcBdvVe4|z3IeHlnT2VB7AP5FTRsf4UjSLgEclD@oW_Y_$m$|%P{KMb%~cvaiav`N%6Dm +5Msv`XgBm)clopuRe78m2uBwG$XedQgl0ma;d7mAVx0*$i7jhaW=y_-*Grk2J?DbvqrYvS`C*7#?fp# +yg>X%ln__#!dhz|RunoB6BABPmH~cWnwaeeQt{f5iX;LCLb65vzv{O8P751{ +9UUM$PWAbu9LR8N{`mhUnmf+y8|AZGjH1WvT(KrjQtSb5Hy$u54`=Nj}#yfb$Q@97ZjYC?Va3W@go@ZS18^{L=caW}Dbn#|ehhog^O4^BB;4C%n`xveTw|zk6%m|>Z>erwME)umKI1`S +ZT{tsme-Miu_hz5l(e(KxsQecN0N48PJVLsvSnfsoPGa3Kx7Gr5}#ypm}de9=`?`4N(WKdI8BUEDly& +664m*rCP3KXCjw6Z?A{kf-9FbG%TC<4md!Wu(&+raT;Br{1i8CdX(?`7Z6f~%3v0iR^~t +0Bm6Sovz(3!vTB3xIl2Nt=^52)k-t6W;kN;9OjQ^h +;2i3y5iB1cEbHY4ugP_m30RenLN?5+aZ>O)_fwZ^Q(%C(?I4XJ#Zn@q%(h|l9B!}ym513mEJ2X$$~b? +E+w(b{wp3>00`v`IRpNP%t$O2M=Ee5wnd&z!>NzD|4|I*v{Pk3AKquV62IJ=0EKfn)Ube2Bh&_bR-8@ +kF5S63)8jV6|BaqH95vmWMN*6d;(kWLOIJ;0JI@5bGsZTOo$lr8har`58H`&1^whZI?E;;hrX_l@`6x +S65>9RS&PVy}5=g{773q1-0q$RWRdu{6_Mcwx{K}Z1&UlN!4N`t(v<*+n_bos@+$vebF5+TgZj~NK{+ +TO7@Jc!)|^PCpLb6F%$r%qi`_}Eod=a_2Z=IBdpEYZ7N&0WK%M|3i=1m-oiX8b}f<-~0hCpbGV|%6SjLThUXus9es4{$koO*c`uG6dFp;h=UnM|wDueC0M3(nL +B0jS$t^8JlJ>=v#SOP{?4Rf~KdzR6#y<0h^@bMh+Cr&hpB4DsSI7PuE;$W8-Q4oX&A`0+lCDDTMaZYZ2ORUnizB?*{^h0{g7Y>7@A{)v&V(9^51AR^x +X*yCC}WB7PKJ3I`xpZ`5K#86djjnq+(iIlMgXmOaIqQW7IP5ccpH?*LW6AW +Mf&~(g@I;B#)G&E7Fh*oSovD@$5YiG=czxQLvnS%qh@B6$Lp3qL!TzwN!x%;RZpy;R_JH)IXMh6bL}! +zXdtx%(pB{n+#mFK{mcqq#Uk{;d;BqULRf=doc9;7E0(K#K@Qoc?}r~{8IT%Y+rSYQ2n2EAS7tNXZqt +J1uNe4}7SK6CEfS-7iOGPsREQ%5Ti>yT9Bb<$VgUt2ADDBp>XxVN1t8C1jPuHi0xCa2+-5fLSzh%WFL +|XKO1%bTbsO6}lAi*q?O6)B7S|d0bc7U4F7=(!^iS$Wcj?r5uOVv*~@D06{wr&Z8ABna-Q#sPaR99{F%FT@|qp2^^^fWQm2<_g3j~DT=cf# +T^n!TKLia>vsxXd61&fPa)F$pXPXnR{j?|mH>Q#D&>4{uDtWaoNzkt0#)wojlR9-%eb=rjwW#VCM=q`LH8Hcf{yYLZp_TbocX%z>X+7=QUyA7cFo|OvXt1REY~v)k`YaGs_TD9xk7HHmS>=6#`vy{KqAPQC95d9->)h(9?iyth5L0gKLi?X*{R} +yUI$ngPU0$tmvNKVqRN2`nRj#vz2n)0sWrBm3_4@8tFAs)BgKBYT!Whkgqpn>EPu|)Hb!XD~ETZNfgc +tQ2@uUe2jZ5sJ#q(dL0PoI=K@6jEOHkTl#Z-TJ~g); +5F|Qd5gIoZVn+a}aTq;xEAGc3y9@nq;9K2vKluXJ6~}fT*$LH&K(SB{Kwp6F4lr~*R#{V+_sdV+QC61D;q5ukkaiCDZin&_oeJwOk3%#v{7HXle7I +J8zKWd?JE%dw=s?tLLM+@Cmirs_}k6;9l>#d7=Z^+wxxJM67IHd_Y^4$r +}1s~L*7|l@)0`k+DoasR8F(noe6J?cq=0SlDe@SH=V)tR|DcDr@m5EWM|ra>o3q=gKrC|(Y{shSY|`_ +nshSn61wlMjd@s8$viBck3gEYY8*BhJ<-s>S?qk_ac{WKMn0J#_V$O9_m!6!RV +FMxJ|FDHZ=9h(lW@4z~s=C-bAPCAt-dAh_GoU9-h>aM=D~zpFdaPYjk>A$Adsov+98H-=TCD>(0+rX1vCag*2zl;QLe?Yf0RX` +L_Q9^2DwOJ;gZX_9<}g0I-{{G@mdc2Oa}a9I{{rO&74*OyD#@`a(xdnd5TZJ#;ghv>$z?x%S>J#wDNu +>U3r*qX1#~t43)@M3J-;fC+j&$zdYOY+QZcDoXlmyUEY5LzxC1{!9&x0AH@{4Vu9hS=0$5L24r +&uzzl4l*IXsS05cQAkjxPk#pbG6nEk^e5o?Pfg!Mthfb5_N4IF5+x{8y9ha(&0*t^n5M^UaHi{K0_zF +Fwr+meV9h@tSf#^p%Xo*UG#7nYF}q@GY!vSJr&-K29X?2j=MkyNL0-k(C)Ns%@aI+cC6PZROM!|6dgtxeyWFj`juv^oTR&=d}`8o%95+CP*`fzfDvH2T>l*O06$YCX8t +OXWbu|N-ee0!4&sU4$ieaSiN}T`2gjMIQ#V-pebf-ggSk_^F7oU|#LOLNm&4LFv$Tzswpqd-Su?}IZP +wrZ_O~yi5`gDiDy9~`bp$i+$T7599d`oGKrg}7zHU_ux*_fAc@Y-o6c0df(S-8Pxk+-_(x^ +}@vIahNu(wS}HLb=ac~{W}7m187?SBl259d{OY3FCc>3-_Gqf+1ocyEDSqb!JS!{?$2LA-q()e>?kvc +5o1e8I1Syk2~RFVCMeGZWh1DIdZk;I;F7T$;vp8)2yZgk3z^*H2<6*y9bkAYfR8#kAke>4paP`ZV@@n +)AZ*~Xo0g_dv+j5-1<@+w2Es-@7o+kq3J|qEmWl*Z(prB(Kd`Mwloc8oqk$ek^Is^GIBQ>wq# +?nsWbS!d~s;f=8UGL+0(4S<@rjMcyZEZ3kLIp%ez~xim45F+5UDzTVet8q2rYIEj|ZMZ{O)f+x06HO} +nV0dgTUwnz;RbhQCVQ?4w$0K5=zYEtTBbi#Bx@`R~4jez@5u2|e~=>g_%sri96!F{+7J8rn{)?p1F7{g8&@LF3D|4U!mUo%a*|{RFso-nj{~P&<8`vmLud(!#Hi +kaPtWoW($=ZY)Z$oBtONHojqcxs1$DqY525RC6`c!^8neb#^|??ZgNGzw(6yn<61np4dx`kdvs7>)Ga +9Vai9gurg~h2LAT12x8VZ&LuP5CrDPT19>Fow)c84|da8zs+WqyFuHTxE}WCkQynw*68au5HBB=+?BPuV +h<=;OZ~;8a5G}#UyQHb(y7!s)tZiDZef$#(w4kD{&#NT}@5`4IOk +K+&B=;aAIFMM!#KD4Zem0macC7mWuK?J=E`iaMuhz4AuX4i +T!p#Fs!tP(NQzlp-_4*i#c7C(h^1ZVlO>GzUm_lyc%vy8VW2aEemVoJ5Tq3PuY#o~KZ;fr?y|TraByt +krLlp~Xdt-O6?L+>sNDQeoCg~7>9R>r6CWA|MGq*P2uG}HH7fvuQvjdx5?#nTJ?d+R)j-d35fZR!r>K +GS|53F-amD*G5_AF^=@H!h#xoTW{zs(?(5=lUKY~?*h`N$I;d}BU2er^u5Srj3n_h=*+>g0PW(BpI9( +n09fK{Ua%}xYR_fxyJbTt0v*Xe5TBOxtgIb?Lmj~s*0^*EGWWE=pLrM#OP)8dM?^DuL@C2vo9mm>(}!nn$CX +Fj|aKnnpe3#~8eb9LH+9e2I +^Tq9p&7%PO6-ulYI?8SAP6Bopj6wB|o@oc!l2(T*a<%Ehk?Y^ph(H$eYpCenD*<51I7iv|Tr5=KU7m< +ZJ$#TyyUDm=njxL_I_>yTV*k8J%0 +hvVNGE^|2MsKasVU5nIj@i+d_Xnn?O_4x<>#=l`M_cy*dnVI^!lbN_%`*nX~=YIk}9UApFt|S*eg1~~ +ALJj!>zN7_;i~1W=z?~XkTlpfD8S8EQnx1Dic?I+2DN_TX2dA{9GMcaPuRg-MP+#LEAJK;VBVXeSm-! +k4r2?UCnRv>YOlGLOpsjEvGpM~M3;V9c~P*+AZ5$T@(SZiq-AFN(6(t_dO+(3s}qMVlxnd(A4)|Ie^9Qrh!<3Tig;m!pD2g +L?iuol@WqsHcdE%uk6I`x2ivn2GXoBDKA+<^y4}l3F_350Q9Qp1%sd-P-BHc9vdTo8TIt2pF<4yOn<% +cNahOln>7?N4h1qBruU(TkO`;!$&n4SFOfvk{@EN;En`GG|axGslNM0`RDZY(-rq%#@v_ou1cXN$)=^ +nCjHTuq_K&7NqbyJ?YiS>|mY1EJW2dP?>@Z1*Hf^PV9o%LbVl`1R`3(Gy^yJ`AisUk2zwC@qV!0RDbL +m(Va@dOuA&pwbdSOFiB24fJoYB;j(ZIWCuw&os_+wSSKNO&`(w;#sg-YbP~tU2bV%FPAboyg +Ti`c_FS1Wy*04dTqQ^NpX^wY(sUt%Z=k2x?^mm>O^qW=Yo^Qv%M7|NNn3e#N(S$exgAThXCtta0^i!o +%A7;XfuKeWbasX?n}S}0ppz+RP68CbOd8lV8Ga^MO_oEmTymN^FICO$*G;%BCN#E4po44uA$;1at=yy +QFfPqd-5)GZH4iXNTnlcGLdiz11y}6@9ehTUJwr%oV$F)FDSt?=?Zd-D5vdmDr+#}EC!S!algkdn!pU +6A?;)@-i?_6z$`%8SV?U*f)BRQU_KkVL#Rfdcgr~zGsm+w32ta@3hpN-+r{*S}hEp%EwUD9hlff5IhX +8ftv;nAU6`#}8sAljlx0$l!v>nxI>(bN|WwyPxPe`Q+&q6<`+6lco@1U;qG};XCWl$kJ=>2d7u$_`Cr +)h0>U?Yu6$_@imUXBzx`>+F`kzAmY(H4bp@9>TtnJpb#Qp;kGP&>c@R;|K`ar5>GP1D3q}gSyD{o2>&ooeWn$b +UBtqAe6?Q4(2fLp38&=>=UqrZ_woG>R6xQ~gASf!q=(fo43F5u8>i#tFk(rDkR^u9km}E%XBLr(S}?cMYSg*Jh&QBt8<(u1T(T;`C3EV<#&`z*K +mM%M^^yrWC61he9~aU$Pd^&x< +zUKJxCuK&=biR*vs8Y`~%Bd4C(!rG@%Sv{HT(7}E*TI6{6R8Gnf5Yq_}eO@~PDW`FV%Ztonw}EfdjtD +!gQ$h}yP0MsNbGnko@&PwdJ^FzWBzKY7!0VcqoBZrT;`Ebp6*>QIvh1OEDIPqflCTggjJALEFI~8ERm +-|brFX1rC_Tfn_uZrrcfYHvud>5BdWI|qCVlkOy@%dzH-T&iDD5=QEclY!lqxDQCxVLGFwR4($rAsy+ +OmH%c|3UzLt`lHD*(GOn-n7^{anxg_$RJTIwZ;~3oPgp7C6b@YYXitFJN0j!o=o_s5~`fi=|sT?X$PXOI@Zgg;f$gTJPU#eGKe}Br8+go2!{R!8SO@}wJhq)FLe&*3JJ8X&^Mo7G9@`J5L`GXS5g^ +l+ec@AD!L$Z6SzKyzEOu@ALU@Og$1`^;Vy-woYCZ_4`8BOBdHb$!-Jo7W4ayP5)*Ub%XBM>;NsR}X|)0rdaeVa=zdR2~G*m!mKgfc;JLw9$Z>rBvBD +yn|`2FjH5sV1*^=BF(kn!h%}Ciq5#XQgsg7hqgX$J0qV*6qIb}3#o!$31z1?Ei*SMKW?iqH-m1G;vZP +8WCY9TrmPtH%BAly#YI)+Zv(S_kvCa48`W&3MKB)-h{d6#<78llPxfnd;^#trlnexm;x(FTnK)$P9`o +VpSK8-lZX9=tG=YM;^FUHv_K>$$Xo@PRLY1e+h!dsS3vu+y1t^I*q1P16Bot@3l7Jg`E^^B$oK+yh6s +rB{Ctft662ueE`Iq#gYF1weMOo3QF{12~cQoPh#yew0+0H9P*%2M=xg;2=eCMALWp3hG@oj=?U}TJ-x +_uP$ig&(MP`&(4LQwtZilm?#a9A9Cs2tJ;v#?{qyblIn!UgPor||hq4?Y7g(r27LyXdo@KF`wUUi$1rr^Mw4;4$$ehJOKJkid +l`?PO9=|K^0MHXRB<3a%CgUWE_b-oU>7JKi2SU?1V%<_=sX?IW}fz4UpEKJl_n>8Wmb*4s~WEzjZcO= +FGef^_Ivh%2EvxW~5OnZnz;>&5u$%^Y{&D7U|9Z_ELv6o;%SQ(X6j(z*RDMV%1uXT!u?Cj~It-lq8-i +}5t>(Ja8D76d-uh$BP{VsoKQKOfWHt}zsdCn*r?PRFg2qaw-Yt3+br=M}c`1%+Wg2LfDRKcuGbL-5!E +yooM|pXS(ali!Bef5CS%{l-}%_9W(@!B_x3>&1)L357D~66QqbU`0R#N%f-$2cN;n*h+|zY5IMk4DP_ +|!E&}!gO8OU@blO#CU$)S@9fL)_Um}FDPBr}n2}=azXBtMW1q5G!{bXplE;0#LyX_g=ZW#nT;MpHXc9 +{H*dZ3nyY+ryoaurd4c2PxS8SsG*4V!%zw={{Q^aXve7-QLUfgb+CyWOvuV(}r#eRsuahsNUvzGcirs +9#Z*qtBknXWhVVt&9zLzkLQsspuIR0Rs<=BMQJb7{iCbUEh*NQr +Z;;X_ry&`+&nvCL>zAJ2|Ox>$HNx$R$zedX|AqZ;3F_*#iRLA%1ww#<7%0bSZlN+cnI&ir3VwGLw&S6 +a4dkKCOvM%O<07*Rpi(Q5FDfoScG&6bTFfoJH(7l6Vmi}`4BA$q;|>?Bog%b0ENoex?Wy0EIosTaxE9 +kC~<|%UC(CYSxk%+pYNqq*@uCE$~`^~+Os3I1t!5X_|n7&w?DMpMRwkEDWJPx7jxbUX^YG)JE<8HQAF*>LW +T8$v#6HF2OQSS&kze9Xhp|osiErSZ_#1Axw!Dc;(ed@%V~1({54gua#Z~+Gz5D}W`~kiInKtlCNYlvC +B<(}S;r~S+3wVmvldLTCB6+h?vr@O<&c8-N-cm0Ikdwh6QCRI$GDWFSqtzZXCm6^P4IuuEBp2am<654 +^ZT=7BvbgLXj{O;z#m0m5_<`WnxRUN(-(hdx#%h8=Zg0MTu2b7?3I3rLuWcds;}$RF`5Gy(N29BCEZy +a}lA$5PjIv4d>k}h@@kvZB8}2Jha-tQd{o`npJWe+?WG`?!|PIu9Vyf&uv2~pn<5^Y+Wc1(u> +3TU4hKfa(b0L;;-xxlO-;Rc2rK#nArFSZ7>KGQMANO;3*+=2faXIERZqpTEmw1vHd~dYHdXY~;PhFI +|1fxyWDcWcm!x583Dv}4an`mhKwp5)%BAHPk^=dko2KNo&5jh&K2DK?@dn!Wk*UOY3Pp@!!!J4c9E)5 +q+&pd_*y5x)Eg_3QlrXUigHG7$IC>TIQg>4mj^-z)qG<|{&);e@JmfM;Z6!C7Aj$fH_peCo43mJL|;-44-NW2UOvFAx*~HZ4rTZ2nQ5&!X7k+XchK2_`(;EV7%FXy^_7h0PjL2b&rwH@ +#ojnOTpt5{aGEod_#P@qiLCMAEqHY?ppc6^0p;JIUNDv41QcqJmAhPkx3@OlP*;BU8q|Ob{m|@N;w +w-a*`D+AX$$$7siHD^T}6M=49IM~fq&epNLcLV%jUneBU(8^j}r0;ukX@S +Vm53}1btx~mD@q|-;^uhHovxP%GZ2X@=N4(0&-jrTj4I{5R$-(L9pHKdK%H?OeB?M)w6KD51}iLhCZ5mnkaeHqly*HH#2xIanG!-(8!fvWUpHEIp0?x>ptF7hF}tu$WUiLsU{sk8!YiuWPlU5BU&L_)NmVvOy-&nGW6~?*9?bPA{1ADp(R@H +j2Zq$LdUE(j%amSlv=UMXZYq-O0AV2J&$2JV~GM_R2U~ +;V$#6_Ad6iYSGtc)<3WWAQOeD)p#b&g(&`U!(qyWkaV0HGX9LOh;?z>5LAR5P=GxG9%JM`8T%8GAz87 +)JNAk6+MEP&)8Vn%m`7Y~g9{fcfSt?jLPejB-1@7vYNgN85~d;O(Owc>8EO{~C9olRFSDJgVH{_MoSb +&tOGwh#M;P4RT>Bu^-cJVbtm~>Yo->tYT;cQ$}x7t-JC%RW9&TCP|xfkz}iOMiMP=q07H`lj?_uF``7 +UZptBTqoDUj=*rz%V9x(nu3DXY`Bkg*0_@RS&7JItcvbRZP;{rko0x28q}pLQ6@6G-aw#sb!mtMYPj|ltqqXKkwrlfY5AHahL4I +nUT6h&5{0(kwC*M^4nWVE86VbHXSm-2>65B*t~njl}1xX14kx8GdpB{!$jt*shq8oeXxj6lcpdJUIS> +>&oohgUJw1h*LYnSP^}sPuPFB=q)K?D4wDoBI=YDl7p7BRFpx-Vi9frcxB-LPBBm+YLNt?GUyv1wkfTP^NPxKo}=vL +h2ljG#Tt@L`=C^|p?7QbEE&vJ}zIDqa78jy#i-Ky_+9xpEwBT=8_zHy*3t!LQdf7c%b +YGpHzK7)}tD9JIbGQNhpvVIi>k2|2m1!Exj6F*`+h_ +l~&3)p2pjy&K*NcctYCu@lPE3T|fyRJP-L@yLO_DYq=TquVr_EY?Q;^5-ZHkAxrRO^v|(J8@^}`)35M +Gvv#9BRSI2f;3O60Uty~+rU(}CZWcH8Vm&m%pxww0g32w|-7PJ<8*D=H0?P$(tHobXBX7G)Tvx(^erj +pHfcUrmU1sEM>q;MIn2?YjtyqV%owBX;$wU~BB`FV-{xlKB{#EKq|1}ZDt6Y_3rNKlPT?&*%rB5Wnxb +djmQ2Mh(cn5~3m+mv_;UI>yOJBlp8ia8hP2IH4&#EVDq;2a!6MzOT$3WfPYpDNBt$v)RgnB#z!40@)Q +hZE5mUQ^WZU-3s3-dR#Eg}8}VCaH{Glg_pM?mFvhUE)j|GP+cxpw6}SpUj90Gd{T%LKw#^xR>>l`@6= +`m~CYEGH?+@{q-Gs#vJWu}v^~L5`&wX!g&j5c{E-j`kX<6h$zU|AvOat=K%o2^pGDeyv9NWpI;Q=`W# +3cx35T4@la{GZJS$D#fVTbROM>+f|ByXiA_jB# +_$9P(<6XBwgF1wB*cRwPeif}F3stydVLD$KIK!86heIv^o<&}~O>1P+E(18~aHH{T;M8@Kh;YF1qn{+{C`1ZoEy7hd(t2&73{lFyBQQ4D +`68{XBoIp4~L!^&oHemliyBD&)#HP=TMf%KGB=mXZQY+q{< +na_nJSXA~m)ql!oc)?s(#9`vk-ws5k19zUf1SsJN{K4;0~9JHs1FU3wEx11Kp1xGGb(Ury8fL!#i(D+ +g*yu3`Z#4YxA(}hPaLnjg1=e{lVw&Q!lw^*?#2t1YA?@&;fLXBy&I=Z~Eo&Sor{QjEW}if_W*Iy!6=NyX*-@O`g!`@foZz($poaJIv$(#5YQl +F)Qi`CPH_ZTo7T{0WJVMY1MvFwU%YA4!ysm9iC6Oer8~-(W31g4_^SB~c;LG_F_%hJD!Z;c#d_C4&Gh>6l`$6AJQ%?PPHU4#?vrZIw$_eF +wehI)xONRkC!a3Wo@#TdRwX0qJcWX{wN?!}&$>w8V(sFrzz3=LbGl8SM>(y+_(7+YailT$Lnmi>b5ph +2Bs;BQx2Z7lGGq?n&$2f0WmWu~Dn6eqIe=bdVzx@=9!R(pbnZvPB=!OIPw7{tK;1ynBU%Q~_G(w|I24 +n#s$X{I_^o*P#Vzo69sEs$KdVR_!t*7o`ltx=hL-xR>|CB<p&)yt$7my3%yAcONRI_+v`sd8`@}yIT|Za_O8 +C4(gEC7u6102_)z|4zjHm#2bzfkbvEQZ375tJY5i))oyPW3Zcsk{OsoS{b+g8?2rrcem&zY^89bx)E- +HARo$`=Pm*NPnt{NL)tj**-HE+?q5OZvfj@tA?j`zLN!_}YF(CYo9DE7ejYol%XEjHFYBlKyP_42EJ) +flA4Be6{cZ%IJMsGDoZ_=6_z}y{%)}==C*U}+5SZ*w6%5Q3|zSOk-QdMiUDr_GJH*qa{jCgaY)yQw~8 +{L5a#D}`@vkwWA$SqK(<^`zL@-9gHw=Y#Src$@1E(Trrx-j;qe@+;Ulk3ICuF1j#Ir{VpO!-Xvc>&>c2-nKNw2mlrgWzjm~d9q9_GtSk-7M5OYsMd`I)7 +79Uu)EwUC*iR)m=mrnfmq3`!p83J38nNXi8n5NA}(@MQGC%eA~nPoY9R98zNs7kEbVgQJ3BR=5<$wRU +$xDU}jGwuiaEgSt0NlzZd>x~XH_lr6^1n+nBv3Kv*|yFG`UBQ~{)Yncv#DlSloTL8`;bp*GyW8?8GgV +E~+QtX~d*Gw~y^2fb2;%75;?`V63Yk3?u224R`57P0*Q##eSmQB=q%*gwJFSr1%!ZtVdeDTT=Mp%ulr +k>4u1@U(r0hD{>Ub^{h<{tH`^!I78u-S>yxfWbx(`sPVB4-_ik^*Q;#L}q#(=!`=8%jsuAxs9i$MH}m +N&6pNgh)a>wiX&zij5Sec>P9zX?;Ig=cAwlQSvXMr97aeDE|&*!J7L`Yvf2_)Dz}I-=scZ?-kp&!%d~GP$!QNaw()7{xBKF%L&}r2gj#)z=r8RxLJ>&JJ#!E~qD_l*b`4r +5Bu*r;CE^`x`f0-oNMkUFXXk+D@5Rh1rGCx81r7$p82UlmU1$M@rIs~O#6FTasNZbm^1hZ!ZfmtKPd<(D+}@N-HUyEV$< +9ksM0^!ALl`1Tu0R#T&r3D6n|A^Qckz1R}Wu8HM4 +?E`-o-Pg{jT$k!^ER{;NniDJy$U;%B;)UFl;)&-$o3xan;@{{oHf#8!oJf4NWWAHcY1Q_0FJ1PvVm4+P^MnKzIX%y +Q`Vw563od7gP@=9!r@GoG0B5gt_@7j$!}|o}lY8L)i@WAgm2#!SA)p!RS`2>|B=?+OJm%o +^vguba+ntV7k^#hPNN)lNH^Svr912!uQN*@5=cCw}3@%j&kh3AFevs&+tK*r5geahSTj$zMJikqe=$v +bd-~RkE_`fXJj?S>{WEq7Vs>o!Bmm4>tEX3E8j1Tl*lEpd{=4YWtZ)e%T}$~v~gcR&6kU~eVn=(XN+G +`atVzS=fA9eC+`>#LZezzx)uAJDd82E9DWVzG;fOcM-w@K&*BQIz)jen;`vnBlP=iO5 +g!?#J)puJrU+|`NWkG#usSc|s?B0)yS?~xTU +TBTYF!%t#fVJ5()g+onQjU42+mbGA9K;oPQxQoNX?=$cpY#Fi&1R1^2uGc4)PF<84-7#Me{MKKE4>cz +*F{Ndf?{IJ?BZNC-CI&BK5WCpBB1qV!PrtjEHA+CE?qr)uAADh&blOu3dHarSlV9_GI&>4)#v_Iil)x +;9+#WC7z9O{}bln^!qV(;(m-Dp04?h{yB9eV6-oPkgD!K{m||oI-2*N@`g^#@TthOP+oajM<$7psr*u +1eoZfNuRLVH%DwU<=T67(q%t^v#)=`3X{I65g^;Q2;QT4@XVX?cZ|xF3oLY0I&EpPxC0538qYd`Y7ti +8Tn2?aaGu|&VA-yCz?(&!loV!7pC>?7ksmPStT?>5R&di$~zq-%$bNuRIZ@CV4V@%k^MoClkMNyt|RZ +Hhn`aTBZq?#)1qg(NuW!RCq?MV-Fz8f3T%I0ecd(VE}v0plO29^&E+d0S0QWDduWWvSJ2?6#dysCm5J +Yxc!eL{G7By>X8#6;o+X?0jZ_IyV@_|hJr-V|F!gE_2wj3j6M*1!wet$`7)TLW)=hv=(wYv82Py0-@6 +YCDgT3Fks5oU?6A=J7MMHYjib>Tz?&xzbhfdZ`+KHOWtS(NsO2jwd&YNr$`D8DoayMxmaizjVEdcspZ +6-W}web204qxq}SI_XYA{lb~Eo7zL=Z{%C5(hD^ZZFtSRN8`#I&oQl8o!TWUS&y7922xb}^QzF5s~xd?lxyOzdOAt +f!{8dWzzImHK<2h>_-*Dr)K-ct!dVJT>5l!A6OQecJ@v}7spuaSawkOC3`Oeq)44WB2gum`iZvFzLKy +0YviJFj3LHMO=kOFbhJGDV_t`T%Qw;14K)1hiu|Hpf2eL>>ms=51*g6P3w}b!OFE4>`W59%h|h2xbK^ +v+A&^k{9vY)yJkJ?I6oZ9#{bLRJtIQ>Ap&PHvN&v63=F?62qoV8NlXDr4O64m2MgeTcBYp?curw%Z^G*w&L6vr4d_keywu(0a&q^sh +nji_F^asTUWsPC0kd+`XO7_!n&8O8(@8#taEg;tqQESvGseVpRIgOM@zO=G5u^k$MhG&dXed8D<8wA +uoaVx%9m`#B9ihUTk%bnvbR`68QaH|H`oIzi1H#^Tf(}Yt*u~P!B%`Xu9ULX4C{Qh2EaOltzBWwVk<7 +5RZ`g+1Zxsod&7DMTSH*Aur(akK5VtXdK+7#VQtUWp|G~pQ67djZ}v!l_4guJaU`=+#nv&fo?`2GSdX +#w9#{_+@u>XnS|5|y#yY*jy!ezCp9E!N5g+yH8RiwPxjd@6_=b5|G?#^{OHi2C6wPHiyG-vL)+ei(dW +cCHTxw@zD#H0h&cS|RUeQ{-foe+rVO~Qum)ljBmSJAQG?zB2ORF%i1kJ@)b!ivoHA-_i?_d{mnAaH1< +)rEo5au;rbNNtp2?_Jc)m+L{m+&wzo96PO>S77=nyI<0R$ZdQyk=`Ii&U4PVP1ur%S?8eJ}j(Hkw$&G +L#5s|o=(zMqUmvt_QQC=y)jN#-|1$0-ZP-`Ct?sO4CGGQ{337m+T&(g?-`K)qqE$x+w8h;e6%lVVFr! +M@|do1om|5jSFm~&n&(3I%5O-ys#@f`(9hda#FwB^9kDmx<1UoOkJYGnd(`h-jOTBT3h>0?_u6&P;ZF +DdZuahkROkMwZuU-%PugxOSN!`&y4zcmWApy09(I2kRXBEfHa=ym$dI-aR8aV?JKnjAC6(M-<_n~MC#etH*wAdmJZuuujCEReU20DomJ(Ry~< +B`d-!eU6GPmc@1BngD6p?WxYh{QQlBy9b45zEP&?0C0t!vV)1bSb5BthgG?{FRT9j<&OJ6qgr7xS`=H +IpSyYQhi^jq|yspyr)LPV9U!38g&9N4tDMBP{xj%{Vz>uxJ6MI!39GGF7iGB{&f*%{2z`-}}MH!U!$8 +_UK%r*13*Ly2pP4IJHP;Bl3U#}%isv21&tjb%fJY8%V;^Tsj+uh`D}wP5wEc*SLRt9XhxmcfoLzv2;> +mwSbi?1jJGX20FB*HbNgXF+wzHef=7pOUPa6ff`{I2CvCKCpe4)O}#vwS8dh6~Q~pK$oZp*3#`k(&5=gSY8h5M@2e +BwU-7uhZm#$z*%x_)_qE?#c)we3dn?B-cP(zAD{c;j2@mEFz0?p-B7`i!P7@9hj}U35q8OOk{<82KQ& +At7c2iL|m@`d9onVeJ72U*p%OD%rVg{qM7?m%!L^>V?JXElx +)@M>V2~Tg5D7=i`!UiXN_R4C|xj?b;ZI(6*K<19u+2XdtDxr_hVOqN}HCYuIr{X~Fl{SBPV9d23i--c +;DZUE3Na^{rvA^0HlqZ6>yc-NswPzU8f9T*FmfsBN4rODh&^5WB}w3?EM?8_wKn<(cN{Cb2l)B*qnkH +;F-`^zI{r)-C8kXm=CBZNu4K-;&}&cn_&ADK0`CQ|qbRj9S7Q&JrOXD +l>oA*wsh`cU{q9KdtLo?_gL7PPSVV~&Nc2|86p^UGY&yxHu=%Ut!&mz!;^REC@{w_uam5Z+`qqNN${rJIg@0y0a|AU)x!x3WjTE +8Caq2EQ>kI+sgDRRZ^@f5?s|M@fXA&6ov-b#z?OHPX68F7G$Zk#PWtQA4i#c@qYWyfh>WCFIINtrf+S +XrmxZ`wva-5S$OY(Dy^KAV;kxr@f-_vYIOq}`%qC}A5bH;xVlxg!TA>2TVSOhHpDbn-^^+u^0zzOYX@ +?|ftuE_f>ZRfcXD2=yvf+RY%<@ewBo&Bc$2(q>T9_fv7FnW`8n5cV9tcvSniS9{Gzuj*l5;WwR%$G+m +KIXYc*-oV1w>d-a1!1@oyd5xRq23$Yw7tn>)?9S!KmC)WNOZvJ{qtOz+Z|bCl&myg2r_E6$s>mfhYrl +wEDz<>rNTG37XnWfDh`NGn-<++(HODqI +M7-Aw7G;ecd(GHwtdqO|N=6^AY^{91#FfLj +CF+N5jB@R57GF3=A6JqH8NMPuyqW83h}TcHp_tg_Yg`pmj|2~<8q)g|I;Rl+4Wi%BIsQDq|Drc`KiJz +V1xs)Qr(#{=8mjp&QIG9&dh6R)E+>|@Q@4+JsJ9g_8Ws}ZzW>m#C2ImG&4r~A^jr9=yUnZcS2ueXXT1 +M~!}vt}Cgm2DB*nWX`DaT>dAfhFQXqgUd>aZ +wFIi{7nyZ^g=ssIfXtCkw~>Jo=nZ-q9)GG0UWsS*T(oXv$)+Ue4yj8*D;gY=Ia+F^j +97A?4q4EyX_VNVfP$Ts#_5RLM47Kt6r9qbf5-F>()yw#BU0Hm(=Aa +Al%NC|!r;h3Oj2ZemBybNk?B@iiZCOneF9lpY;_?*-2yWJXB3kD3ThLQfTOb*L^%bqPQ8W#9B&MrmLo +e$AuS%|CB=}P(9MWwT6`Xw=QhxfqY8u}O?={4ljxq9b4_a{{4z&GNH^2sO_+HvM*5OdufSJ>;chn1aC +iGHrmexj`yH2w)8#l5j*)RM*$nzhN|Cb`aHM26Wp^uLa(Wmu$y6MRd^k~e4Nu#xnN7zel(4_@NUgx_7 +>^Lf1K3b=70UUU=Kp#L<53)g*lbY?HO#_MrryqGCDEpkiZdil!TTYaArQ@V^jf$LGDF(Fnl$FPRY?nt +C)v6LNq#T*d>{)#Y4K3{4U6~B@4To*RAk8-6k6eLjqOcTJobpDmca}CEraJ>mMw(H``B5fMhUC}zq_h +s$S(C4V-&LEaBhrx*dEj3Fu3gsy1S|P{n|(m#dzaM@PlwyF^9+^+q;2$lxb@VkFP +vFQ1N#V{%zbg1OLpXq5h_q_WHOCwv77dc@l=8W`pq +fMNXOMPD?;2;7&Ox-4%iu>{~d%}x^@mu=*ZTBQ)*o(XkoqnPIgxpL;k8?tMUZ_dv%@=ACda+JIk6n?_d5dSEZ32JWdM?uomw+W +Gy|2?JjC}aS`pOBxq*qnV4U)q>XI|$d?P$Qcg}%;a+PTZkh=l{rB_$|LH)s?8wtYjw=2Xo-l#aXL%#O +jHmuEJ0Hp5Qy5;uppdu3CcLr)QIyN<6n&{g+jDFcU(9V&>uMtU^jbyH;dfaSr8HL7YGyY*5CC?mkx0|i4vk)(onW-opU(BfM^c8oZ +l6>UQ&%`w%gZdoC-{EjhWjIuilFoFiotvq+KN`I^)enXZoBKajweJarPYLjum9lyY4y=mtb;egYOR(r +{+UgnE3G-L=eL|p~3W_<+v14oBCU@XK51R&BQf(lu^auEPf0uP;IeU*Hz@7ryV{d3^YwPzwLy(qGW7$d_hyd^YsxqzI?FM@ +_^Hmp;A-32*AX5hZ9)3IXEz1DXY-G>`NlSYg~QLAW)sCxq}@-=t9Ka5G*NmMt0%gY%sJ|i7E@^$G_Bs-kSxX@*eO$DKkoEo7 +cV|Jw#wvq7!!|!J>Bf%9CLkzJ%lh+ka+OtQN#&$YkMUtyl8}6>4BqGsUubo=biI~u-{mEw_l=4H`27V +O;ibS?6FijoO6yk?Z+wom#|vCizEDN4+V2HlYuIp<(IvQna906s!Hr99k_{O){u(Zu@44k)XH~US0>p +~OZ_+owbtr+wa(`Ab}~&n^KaZ^@e~eUxyRzw7+dNcWfqE5`DOWOuaY?@RY^GLIMKfRgkD~IAUMei=HS +3pPot`!Z-5+EfDMpMl%M7U`*hQpvJr{6Nx?)B3YOE4Kd)PAV=Lt8)R&T_@mW~%fkjpg{IvHnBt=N;deZ0(#ogFi3-&fVr)(z-O(GtPd|aooe^y|8uUQ +2TG=#_Vz)z3{nXmw(`q;Ifg1^Sv>*V&DJwfIfLV+aMbQ$7OEziwoCe(r^9+ +u@U7adm3!u(UpiUht3=JY+9?=7O4a2auXa^2^#tmwfcvSSrrIaKA0X{TK`(n@)r-Ec!4?r?`wlHt +;kXu_=W2ImKX`|Mts?qe#O1eX=>H?1ryjTUY-U%T*8DwP4re7@|`*Yyo{{!VY|!)D?jn49sXYimAGhC +DuT?g{ilobrqA7mgyGn)k{obq+7tc`>-md9Ya8hK2gr8ac{6EsjuOzmR0f^h{9B?Zn9%W3efmF9B+#y +mJo}Rh+GGec<5wl!OrIGPMI? +U)ijg14v7r!27nxhF!Le4*S|RBDgF)bcQ?cmc6(R6oh$I)33d;(wB;6J-h6&&s>Ki8ba-Uw&1WmwjRm +`>bKYPAvN1y#{$5tWTiV>Y{Qil7ar&`a42%9{UX&d?5y2mZ=30m=Twqqqhq0D}J3-VgQUTU +tk9VJ0dK3s>8e>D)>q^b{7^I4?~zRW~~T{bPggf=|bRD?4|N_)R0`zota_;3__-pU*HNcAgeh5>+E@O +e-$Bh!6cA~xlxZl%0Db|olN%3VCUi#*^LqxmH(x8}I6Up>WgS{1y%)N=?ac%;vG?7Hw-+x-^~cicB3S +zbRyyB^UIe@-f6?$dRzWW_fH*HPIo+L|Hl-|pu9wPT68m~$1!K+AB?AtlWS{ZMcF*?IUVCBozwj;ijN +tR3@nce##6Q6IfvDgwzy$=V6?Iqny}E*;_?w%4?{m{J^|rg0x%gXyWI(MC-JUs9wZ*U{Cbey^7_FPY{ +E_ViTK{0!H*;!%E{iBChTl{go^!DSVJm$k35Yd#^}a5w9He$M!Fi$=FB8s&BqX*)RP{kmG3V82m+A +K-R%tG}5wwoN6k57LuNtuFo<>{FOM>o!`Pq#NJj2fBc{+E|Sa4 +4QS1@12p)f5#t?x?YKGVD42$kPKC%zKDh#-fBdXH2!ZsXNZu;I(=&-sQBrO^yrXXRyYqI^GH-^=azKv +H{Hxh?GXf@!lU+x&YIYX&ZB|0ShW$t*SC+(7b=+Ap`W!wmyHzQ5C9ga*y +{7D`(Ct+n?dKeqDK(sSp2$1g6(riHR#u+Yc5+2Dq0co3_mIG;J?a +7rPH%ocBAQ5|}_HIoJk~iS5~)VY&<3%`iQMeL!KXXQ8uk>9C#wi<%dHGq3Pnw}s6syVQ1_U-+(jrBay +cSy0)u#;5R>!eXfSb{{^^VZtsjqmw8Tq-t7(sL}lS+K5?pS8vnKjxQR=P=KcQ&{!Jd;|aTxoqJfbG8J +P3g7dKBL_zs?J~nHmcj+osy~^<>7*6U{P7cgloaZ(=n+@KE5pBb+<+05>eWkpq+~wUQWWKRdPp^M!S$6OyT# +g`KrG5Bq(3Q+1-iy`&X22KTEk9;EMb(_b*Xh0@lheB@($9{rUDFy3sS7kVZYBXD^KR*BU9uWOE!cn)}{>j-tL9o+Wd +xk90X4}giH0j9faX~T;u&lBqk?ix|PPa9FbTu!H*{t{CRh_t!3y0KT|R9>xuUbd(XD5#I5oTwJtd>-e +)StRqxdJ6y;04ME!w#$pk4stYT6s#_3G0a~0h`c|xM4exjgbz$WU*F5A5nE56ml3+k~t&$NFRZgHx{d +QZIX2&(o&S#`C2gLx!<6Nq}7+{cfi71eK(YppzxXS_|0Q>z%@rrjr(k>|Qk?!+^0wi|H}C~lL(2E{tJ +$>Gv49PX3p?l^vCT$5d{Yte33p31DbQtr{rT35yohDQ(t19$-ak*R*7uz +a*oVz-~Qggc;^_kQe@>F(YQZh<)=L;lGxo(;6Ns4=t>y!*zd-6+4R{mktm2^+)di~T*`j%Y2*$2I2t@ +$M0AD!ro_iwV_7LRs!j^EbESMSprwauJK=F;9C^u38!3(dJlFC(L!*N&1p5d+A*dp_OyGZBhya5 +A1W5#W1Vsdo5^N$kK=2L0c>?bXLfk;ml^}#5fnXxRkJRygMR15<3&Gpuc8K6O!S@6g2^wD%q8&jmf@p +#<1U7e?!wJR{WD?j2789%{c!l6yf=>ue5>yfVM&SLM5UmKh67(h*Of +ZTd{WpC5OSBPeBG^N4oS=%poA5RhbS3CZFo<9zK^lRLU>U&%0#U8<^Fg9%F2ULhSMqvnutj`1#3Gg@T +Equ$TSUP97MEY)yB5*7x8=%6^vvjTX*^6U!^9K3eum&{A)<2fVrFL867q5;S@ZLSm@4wcB#|TXgq8lf +i!_l%e}q4cMrRUCqCW~IMvGBmu;?a24ECO)uNWp`IKRkgFP}UD`yhI<_WvV7B-PVm|BIIAdmilubIhU4@p;+-El0($zk7p#&8AMbBvuigBM*|=Fvl33ZWSR+b=+{o10*>*V<1n&;vuW&>&go9&uv3Vde6 +pp|Z$76ok$66fT+%3wJo3#Jlj$v+LV)t=oI*`n@XuhMREn1&?$g5Mkb7g|gq}XarP1#s_P1(znkhFsr +w@NK<(tE~!Y`NpvRpCL6uXm8EK{kbI+a}W`5hterF2!?K^L&gCA$j{VzIhD;gLV6}~>Etmkd9`9jGfwdoH-qWa%Umz-Uas=arWiJ +sk8I}FWTMqfIpk8@Nm}`;sb`U?QmdDg>NC~A(YpEc`~$hVvb?PZen(L{qqK6#bNQ$o&S9>hUjIAfqAn +dlE;@9abiUO`L0X;oy{K)`|u*TlY>?6<#w^VsGgoi#dm7}Y{&c2lty~7zxI9iZ&D93n`-kcO6?v(hQyTL-RZCq@x27I~r#UPS +UAY~d!hH^Ey4A&7RT{3Wk3oj=xpw>zLX|<3*;IbF)xs~xP(Ih5ZYupwrVx6H`B%tqkfFU!=Q2=hkJ{t +p(t8HSG;{Bi%Px8Ru4AFbNvp*RU58qB%)+-ThmeUe>+#Pl9AX8Tn9Q({*&OaiEdtjxtRS-m+{wggkcU +3Ch}L8ll8GDL{mE=g=Gspz{E<-tnK#3o%=Tms{n)}9&hd{d;ubQiKD3bhkq%AfEHXEdc^{b%k_k0(F` +1aS$RabI>qZ{;)98OeTN1bZdaDp60_o38oU5%}4I|q`iV1GdV%i&PeCW=9LWC3Fkn&^>HC5|-)!0buU +gqWu`W>j1P3;_FTs7hrPCKDiYWiJRm&+{ePq2FrcJHCN5N}xn#)`da +MsqDz^)A#2=)h)N}{kC6UZrt$)_tB3Wwf+>Yak<2GTI%Z_$K%{!MhyDY{L{GB8)x*bgZ6IoB +G;dvbQ-Zd^NzB-ONUOGNH4tm~tzG^UrwFDvXyRQG#sr~a=IMV33-5gJk8J1b}i1_#(X=y!XaV7~=?2 +!b~)=Ac>6Rljh!*iy)>~~JG<-nR~&9&){k@m^f`evp9`7do~wjWNuytmtkt&BrZSe#5{Xyo*E`Ko +2`@&(nq>T6KNqGq(Sj!Aq~VwT*MKHsWy9FS}Lb8I4^^3qo&yN@-??)Yg)Eddl_X{ZSgtT?2u^J%v2lV +A|Apc9Kyg~xVd|HdU^Z!Hfr3&uW2)rfAi~Fw7mX?8(Xz*)3)7BH@7$6(xGEOr_Nov-g;Z$?cKWf2<(SQkpe=@|29sd$X +oy=j2Yy%eUF5&zL#uzWZm-nOh+KuRrE1{<^yUuJpU|Zz%OJSNkif=dX5%d4@l?Yy30DpKthczs5g3uK +9nO{r?M|E6a4sf42m<)%;!SQ%eHeYX01>@vlY#{;Pj~L_hvo(x36o=y%N|YjoFszgBu{=&tL+w`_>Qd +Gj4b#Sbi4ShC2uxOBmPWA6>P2&0~+RUH8QL4NpGx^fS*s_x#2eUVLfO%dfop+U6}= +Uw`Axx8B~ieMedO&Rx6T*|T@w{&(Ly@cst}4;}vS$VVT4^6Amfj(z^cmtTE-{KPlko;>xh(@Mp6mEZs +H_vL;%KcoW$@HT%N`J1! +IQ)$B_Q_N4~YkQGN^}ihMrBO3=GbkK^|&QtY@p6^WYro!>hq +Mt$VZ*ynxc)#2B+Fmv&=Dhc{zFJjBN9;RD1TMDOSXr$n*|Q&B(H*nQb}doLpkfpCaEEGANC_=)$d7u*1Wt1B7)|6X5S`hvrIO2Q^)+1cx0C^ +)Rhi>W~GbhL^1Qp#OPhM8+kn0Y9khB*vyl1(yZp3baSr0_SBu{)YS2D4^A_W%!!UhqjYEkerH4H~ugA +oU>>k$a>0R`Qb>yK&R*(|~%*e~ho@&juxiYGimo;x{Ms_L}A{7tC>Nd@uW3!s6jO!#-AJXB(%i6M?!g +IxWVxYj{-Y7%zYPvHrY*Qcx`L@(NgY&gzXDBnKRSQ!O?{vD<`ejV3R@gKv7rD{jY|jT;qel(y7II~}T +{?C#r)tWIyKie>(;C1Gm%6} +6Pi@4W2RCO=mN`3m};GxlQ&Cr&}vHuSG_UU)WEGBK>et*ejM;sI?R +tWQ-8m;ou!t4Ssi~ggYesO +^8$;NQby5y6(L^RL~YhU3HhFv?QHD}%jmxCdiu{MH)mb@7cJ+HiiAu@o=%>hu=hS=;|W9slIStH=L5> +FWIUoOE^n7bagl|D|@-AI~_urNMNby6@`wGecx)_|4k>`|9{NdAo+rA#XKI +=f-UoG4z}(sK~|lD~G6F{!-(%3E%b{7WF>=`Htw{`yEkhH{+*13*2a25~X%S!>AWZ7B_h&TA|b?q}rw +=i)r*`9NmyMs*ZTXL^ph{Gc03b9%i?n5mBV}+N}CeiP{}b*`Ak2U8SDeub#=N8MeVWd5P3DXIZtBCR5 +04YaYdp%E`{p$+AY-^0H#6Yqe1+ipc!@jLF$r3Zrsr5D<5}4Q8~o(E;W8HSz>`QKVwHzGiCaPOkAeY4 +$AZVA5rVr%pxKCp|_$)2^4c$sY0cEL+B)SvKpaoaBr&Yt)p~Jn@i5qbnSZOwy2cqAhJG@ejJbySp~_< +ADZg0PfoOF)}SJkFg2&7?zWortytZ=mriO2v<|e6N-q|RCBAA_(&0zm6NZ(-4Qc#c+4<;)Z`Q>uaTre +OlKk!N%2S)F>Z;rJo=9%di!$-D(Z<`%p!5uSdQMRhC^!V?0ww +Vzc-OGEM{Ityb)tRpWNTgmF&_f;qlf`DQF$3QVr^C;l_KL3>M_ikI^BAO`?v0~+2oX(m2qEP&&cew#N +3SRs2n?``yHigB(+`v+Vg(kj)@_dOB(HSTCn)nVXttb(KakQZ#TaDK#$x!<1z4A!9bustRPH8ZPb5bhYH-+-v5=5 +RUq0RcbVRlk4x?|MUF+_QCfsxJdcs);pr{Ue-BM=%%{Tp*{Zf$922Xxt72C--YOZp8pdMokD-Et-2D?LAU^kJ8=~v^XO +*_e9N1*3uuV<(;LuPZfe!&+&~9z7fmQaP`*07iu`q)6DsrzeDpc($ZN@fTe`TwD)xyJ{z?Bp4QyAYWR +Ji`G2bUxAIYWq4P}VxzQAV(!C-7KO4Rw|NqHHYL9K1MO+K~dmsM>AJyO7;PY=r?7!)0cu`vQZ+Ox4e- +`h*^ZCCn1KRWdxF{rek$&aw>AQhCxO?W$ntzMt{?ym1x$a5TocH)~o)NOlEoAc<3SYkd*m`pV>CU^Xn +ikF6e2w8FHFFDvKlsGSC)$WpPbg1}YdHP6>)1AeI^+?@?mlJ~DaSI8^|~_rk)vN7|5b>dbJdubZ$uxu +@oS5ChF}B1T7uOCD+nGWSWHkvFqMo>i1$g`$TnoaLEf-HhGg7E~S2!YiC{H>jo=;vJp +Zn~)YdriZ+&+YSK^9$y!0pN1krek%K9|%h~ge^5-ECrYxzsK|8IK!==(MII**6;=<&gqUc?9JD20u$m +eaL8X0Ly?Hon&#zjfCjh{|SI+dqZ^?deV76WTTmC~G$@ZKE8dRZ#sR&>wxh!u8zY<^5l$`ah5Q&6Wp` +x4P1Bq?zEcN+HHv3)SI?A)F7F(OPnce7sI3;vFIr{tB6Vb+el&MB%g|6JxO;x4P-L@fRaQ>v2c=_tfK +#XQn|wjP45?a9`1Y`!fx=zutiRz6RWnHsG$*}&d#k~2;u+tX_p; +$`Vv1MId)HCq->K&P&@6iQuX&%jh{Ai;ydPiU#(Pxs_n}MO#9fB`aLl2Zz`|JSU+~rcbno6>3>h*+Bq +t|}jEoFnx7*c%DlDY`La}1S3i0&QPm4F+ctezxm5EP3{Zw4Od|7B!w~zzV3vR%qf#}i$g?_@ccgBo^{ +CZHHsV?-Zo-t$gYy>zX4^$tN2QHBJUV8VXmopTX-e=6fyK?Zrfdl-GCQ#sns57!Uo&9IbuJZk!BFjD6 +`;3FW7ZE=_y;^-QK==a()6);`)!wUBe;i+4jdx1Fn*8xlm;yC@^*!qXhd*%O;2zcC0;m5q@>ky}e<_^ +@vFHmo;8%U{*RSdAJp38|`PGXKR3A8xH1K|Tsm`v*64*m!`$0?7UQ$Z&a)qVpU8;s3$MeX1YLDtIs@seMNhoabV?WfQfbo3{F(B<|0>Uv5d?AqsY!*^z#4jwqlaO5Y)6!H&ull7xE;e*7h63t0*tJ2g(q-_z& +pKQ(&v=pE_9dOuHQ%Dr*Z?v!$Kikw^0WyiSDDWX8G-?4c#(tBeG(l3zmhYJ@r36|pfg_zsEz4Z5KFNK +E&;wqqr@zKDf@(Db$FrR!IW)eM}I&~6(fq^0@C`b$#FhE2`MvBOHd +suaJXvIBW{Rm(r;5D1JZ?Yc%$XzB*(1dB_YDxQ&J7ce$uVN_y;0)9oJg@M&m!KM*I&F)7%Lu~H&JX|k +t_Bry+a&Yf3N7eU5eXxO3`zV6cM|n=<}`=;U7pb{(UK;K9b^&V^WOxT8eyv(cefh?z9w>j!QA|dnxA4 +n>ZmyKvlJ&zoM5@AsHhO<&U`1n{Z)#psw#2e!UdKUJnlWHe@G +C*n}o)9nGlUhZkkYKZ$=f^EUyy-WKS_pju5ltG_gW17n|kF;v;!jZ%;)_ir<0a2T}Y16n_}SA4~BkQ~ +a5-xmZN;S5y2KDSjEn|B&LJG{o;r@huepE{boX_)94MvlM?P#Xn5(k5c@vDE_w;zk=eQq4?(w@q2Wp@ +hqw8xinl{W)ou55*kB3OMTids)Qdl7xHLNA-^0UlvY&+ +#h*p-7g78*6#pfPUrzBqr1+-{@k#kepsJip%Eq!r6rc3qgVg?1krE@#*9ke0;*Y2J_fh;uDE=0T|B)f +SXeJWGjZ|lE5z9m%RolMeAW43eh!wK=T|)NE6>`J_LQY#PmjJ1G7!L;TuLiuxFxqM`;x4vOUUB{A%_uAKrpbm%<90LpAr!nKRhljCN8B75@ +_A(#v5<&^Yd#r3^~9*o*zoTO|1YaU<3T4V&Y@t;^JfC+t&&}{#|b4^asU9$Hl})#}B@_&GlLU_}|*K+ +c2S~AH`YdTuTW@0F8f~e$TLYVsU&-Ts#xOIq3W+{8OTPT7W_vrO{^a;1(@fPym;IN?-33OOF`JU^w~T +Ja{mp(9Y!_W%4qGGz*G}i;gEQ4{pyy+%#AdkBI(}k-a=jUO^El@hS1KF&dWfx^%@x^&S+}!_%$taLog +}ij8*7EX1aS_V91y?dH~eC}9W`<9}z~fCWJA4^%?5lJTyL%_!w*OM*7k0P4 +4}Iqrzfqd#mz8=|__w#>VvNU&q^!KCvb?vVR?K5m)<(uj4IcS;0k9VZ?D#B+C9CeXgJ$s8^RO6cKgwx +vT3bx;{5@=30libdRTJ4ipPBV#JEPDDm3-7_sTmxuV}b(kZB&8~UjfN#CeC!%Q+Ce)wUrYSk*S +X3ZM0ZrwVuVZ#R26<&SyRn`aJeDh7#6+SL|TC5^{V-x8LyLayvAAIlu>jo#k`C5GQ%{SuY$&;e8vQm8 +i{rBSMpU$vu@XIg1h)b6)iRBlhc$wZ0|88h!dtgIKKkDeHUM`zN9o;PI=$2AP_mpTZUlTp$b}>Tk6Vv +1ov0Q#3UY4h*|E@B`rw%cJM)bMV*)F@C;`gQa7K$H7@yAj8X%v3}#a~15U!nN>DSk~q_a8mwKYGglt9 +nWstqo|?rVTH(wB}jPHmy4K=+Ofwz_u~BY169BEw{DzZ`SN)lC#zwx_0W+rBjdQ{-#@-(tDd*I(F$oV +VnE6?-AG|P_*lCTh|^pHSW=z0yJyd^yYTAbn4opN#m|JYYt{}*V}sd`ucUHIJaKctZAE8H{RBxiLYHR;&6QKLYrU$=B>>(P#UDauXsPWcD +6YuTk^V2>VwxL=p@zb!B@ATaQjK&G!9Jy`R*4=r84YY +Y#*qI-n+n;(Dt@nq_E-a2#U%)fs8@yFBD_k8oi4?mnD^Xqfx&Yhug>B%p?_~PZgd-pos+}r}g!^3-MG +_}&w!6zx;!=K?F${HQ>b)7qR?nowh)$AJMwi-8XTtr`b$lmtTPd{Bs<7l~K#}0Y=^l3@`sr=-VPcT6x +zyA7bN#kz`f1*KBKl`PkC>2EOi4Q;g@Uimp@`6Ew1_cYXobUtFKFp+oZU;lm +sbaY#pi`$>xP_{%T9JZJ0Ht#e3ai*Yy{cjo5iMlV^iB!Tp5;D*nG0&l$W#^92YlDm5K>IL4lijI!%ig +f=h{%6mgm88oo@7S?pAL8MX@4ov^mX(#c_=>89i4!OI9XO&+P#tFT$dMxqs2eB +`m?$^Y9mIL>z4sV@lp)oBiE_An`LZ-V^!_`!SHqv`aIZgu|M~OhC3QnA{~;kEogv2n;Q#*n@5{H|dP` +E7OVXLS4uk(FkNx}iGXVE}`}T2NIePRc+d%``0LUBa3Cak#ZTV43>oF-KK9I8aekpsrgUd0c>|Q42$a +kfD?v#|joIihr>Povmga7vJ+m{Cf1oR;tehIagC=aFq_=4x)E9gO40C(6?_FsMV6$9+xIrt5HAqSuZc +!U2a<0rnCGWruKEgwo5c2LTG2c!(%M>On~@;0KO^EN3vyeZ|9FQlxlu3r6T@Ta!k1;Aeiy)1P2EA<5R +0P>Rjm6St{N*VPr@gFo$8T5Tm%3ef6_NP*wua+|Xu#`dDT}upG0(wL1Ax|8s~<8zgUK_5vukZ|o!H1r|4$vvv#pK(OWfVb5;ajp1MpCR@3AM# +KAkp$E&6J5^1SCj?h3bLWofjSO3gWRKD=qBm~p2QPU4ksGoNd5nH&hY?OgKQq*4(4f`l?r4*!P3=T&(k(@ItZ@0%i~;fqqG1iufG3sO&+$aV7{X)JH!2N!eFhDBeMXzqb1(6Imz1}H2I9Z@4X +Xe9IGGRX!=KuFmVc^iOP~*-&gpdMw15Wm2Mx&<@JBnjd0wP^@%}LR9MSM3(Xh^@;-7y;%6lq_21Ux`Q +)+!in-uqXO??IpdVK~BdVNNl)b%Y+rtYcptx?a=zmT5g0??pCT{`NkBRVa3wiblSS7!H@FWwg>pPflG +OdlYBsrpWS`kP8a3ehm`G|^zF&v9CPjy$5aNg?m6{j=UCbs-u$zEy9##Q2kbzqn7IJ{?F$DFXgz7hyt +XKm&9hoff#EuhsQ3_@Tdr&Vc95!ce(oZm4{jXn28Wc!p?rdu5J1cc$W+8k-8Db1Icdh+dyH9iz+k`te +75q04{&{{1_nEObD91>V37xa+i_?MHi$aShr{^i}BF@NAzSBHthyHqY%ZH_Z-{8;ORx_4y%cla^17l# +A~jBo}5xNyp?US&$Yjo%XxrSDzkY8jLzdcTLA=2!CqtS^f#1CBPea!30jg8}$Kr>t%vA-l!j={{@YZ| +6L0Q%56l$>qNtAbBG423#iYa;R%g~l^PApKttvrxnK&>kRC1Pro}J~c+e*GH|Q9B-l<>y(Pp3zgB}5R +=9y>Y^Upsop%>|3ER(C%lS`K_$wQB&%5q0P`Sv`bf$GAmM8iv3eMXzK&OT7C$r~tFI>y~c>TiF5Kgj~iKgo3wXnEj)2js?$8=0PKp+lzyah1cb%AH01WEs)$7SX`0yjod*`w#qe`40;V>kJ%#-@=6pT{P6C<7 +%{Ax>PMsls_upSsW#II6~xGT77=SP@gwwG_2BS_y@_+l6xa%Nrp-T9@a6ae;!7#<}~-e?};m))+lr8f +0vIKF(NKIJNsSWiu#B;Fn8`;rokv*e^e$gQK#yTvFdw1-Tk6V$3T59)#`Jd=c@V-@JHDX9Xb@{AfJ8q +S=POv%aoLq$m!FkyJ)x?9ak$8JZO_%n|lrQnP{k|ooVQ44L&Vfwyd1mPT8eP7nzchBA!{<>Z1jCqi;Z41ziaGD8>VLP@fMzWtY{z(RgG1bXR@Wb&Qo-eO^X&;g9ft`|Y +<)r0a~*@$c5HTi^8b^z5{>v`T76xg4H&;t6@@op;K_#6($IS}GrY_+k0rgAX!37~cb5(15-G{e(^j#x +S4(?J49CeIfGL?TnTmJ`McO%Xc?SuhB84t2zeiGuos_XT}5lKOBFBhliKM$H&iu{E)0Kr*VIu&6_uGq +&h6gPtKe3)JIY&cy;YKK&A0#$)D0tm1|90s0)R~EX`^j@f6YYscCm% +}XH~~Q|NQ)%D7p2YljY~{ZIS1H`Q-?e_chNytXQ!^78MnpUA}y|oIij5FL&K_m(0q_V!qS3l4S`rpk9 +DZ;08MC(t{b2-2taq7}hpKR%MLpLEuXb&J)Xpd2L`uqmrgypttUB6) +biSEUH`}XZXJS;+e-nMNU(*XWp-jM1C(~z2)Du)dl#7orL+%Ho)MXg%3vQW7{v3vJ!MW|~&KG%vr@-^x|I$r@8F9IJbTS>YFzAEPWK;#$|xPdOn5bDB$1q&p#S=>(>AK<0~;-H;DTZ9MlgE0{RxPz|8AAcNkjBDN9-Ty4^F8p=ws{OCCED7(FXu`eSL)V+BN?L+!=r4_?P;gC6G(>L7+pQ???NAwhsM~kq+>7%a$$j(MKO;dD +iPN0Cekf^U#~{4w=!{52($2+QY-+zk<69e`CLB)M1Re&DF{Tuy*ZQjt{yJ9<%{p$QIg8(4yl``sq`DQ +Qoh>AGlEcV)>{3yGS2@U5yrf&Qo8%L)pL$<)E+0q0G=0VJ!h`P@o04Qyzb%ow+Lh#`zz8EOoUqfqoDD +2k%&y13l#G!-;F!t581t%oHr!z#qQpBuAG?M^LD&(66@nn{mg3<4A7!Hv{ +s(cMpHdQ9ZJdI+qgCwc{yUnjN?K?RH-J}{$|rdGu<>({7t4?o%9X)7HVeM-+_ODW=hp`(@YP|^w +CU{X13SN2+b_eOsSe~Fl+DIF^j+O;QJmQ4ZOrvbVbbf5$q>8UuV3CcdV$?oeym2L0+v$_YI;xKhA2kj +v#$Mn%aDPhKL~Zzb%JZUeJzf9SM4vy`bpFc(XQ|CSLha_8NEmCTSZ~BS7uHuWCyu$;kIzYY=;v!%>uryAdj9pa0qx>-jpfPARjTbWY@jb@5$Q +ODvc%qMapf?#Gte4$&T3zqMTG?w=^-W#Wdd}352hQL4?L*yf9>5dyUq75WCZBdB@jT`dZ4Pka4^pOlr +>>hOext7Y>fa9-%=8e#kjJDt@))75nFed}SZA +G%^QUoA>dKWXlZ<#EZ^$`JJXmkToGR8AFekj_k=#FAGyLv1Df0-I0f*JKHq;5McN+7DUNLLdtn7kd48`tF#^Ws=v!P*3`acTL_NgK?04Kc{$fCIJ5A39k{NlEJJ)vJ?G7ct(4eh#{U3wQwY!}S@w +Ziw|N%&R`H&22xftuLTXV0{E@g@x8=7Z30l+i0xa8s#5#>%RN$%ck}}Q(qszxBz22@C0oScyUhoOuqZ +i1YRe={PZSmetm5tZoj_iDi*I2MtS42 +3>)z%sf&lT+-@^?C&sgFJOSQ6HTppOF&eO#@7HwZky3$#b@!-MtP7w@a76ZOfXLDuSR|Dj{go;^Ez!G +Z--!H1 +#l|AWrMPKXL*9GI{A@2^X(@!@@T7($5nI3BKvy#e@fq7&p#VY3n?4We+CX57;LdvW`T~mbLZwvoH%iBP*9M(_uhM +X9(c);B|LYbkA1*%nBc$uZ6o+mw@=1{G%4JNBwJV2|GIVSHk#@^j|0)yV_gPoOQdJXapT4@-3`q(*DV +{pAJ@OFq~!74^VK)_6aSGn=^x)=EfMp^v9Yl{{|X$YO`FDiM|+^Jk?1m@&mrsbK>b8@R#w(zlI48Tk+ +88FWBxxHQ?&=Li1*8;OqsF=Ydn~vA3b`sL|I^cjrwKRdI9*Z%c1^_h;C9}^&9c?eoE_c8lV3l)GZr-^ +t9LK_>l&5JMbHGxsXB3rDI(K^~1Q1fHFY4fOxfCu^_yj}U%ox?KDNuWQ<%gf`o0pF_X^NJ`h@EUZZO+Iqu$WwnZZ_Xe2P+P +<8e|>o9I`GRczbx0RS;KS1Xd5x-q^~Ld#k@Ix)@z+`nn`=fQ(^{1M^Q?w{F$H6a1@ryX +e1`IZ(7SX!}skn>KBN{QW?@C=u%X$zOf2*TnxX0_xWvwQmhR)$I!-j=R;`dkgrZJ-f!eyRLXbg}Udj{ +E2i=X{hiV{PV#pS^$=rY$m@$V21)!}{SGRE^y^jMa8g_K_y`M`h7PC +eGkTpuTb5Fxj+vmJen@If@4dD3V}U*xd7-Z9VQY;IjU +|rNgF$1{D|=@#tIm#1kqS&CE2-J!Q*+<>vu9P1FyBk^k~pCP`iTD6MxL3|^Ebv2YF-}>k +xpZz2yBYkWeu3L9tjXc~0lhE9^YYw*^1I_ln4cI!6=!h%uA6uuduZz+rzp#jBS$h%(N3c*zuNhPd}aO ++{{C~#%sb?}Z$6FqoAvnHw{K@%P5&MT?KX5Sln2^VBQCI`FE`3R!t3R)f0Kl?&`%~Bjv$FFCb +g<`T_s^d+#X2=LzuVM|NqIT>Iq9};lX9l^PtBj&eR|Ih=BcUK8R^!1Taq;|KO-l5K!={)gE|as+SqIk +x8>RM`PP+dOf6WAM=|rQlk9mJwprRcx#U@=*(n!mT0&mN^o%U)WNW_5)94+;m+28A!>rS-S>`PK4(O1 +YADcZr=U!`G2eUmRauP0%7|NOTG8>{<62|u4Iw*1 +j)XK0?H)QX)DgNRv^vz(zeE4v{-gTO?EhH*+r#3*ri9H5`z7q@@E5|jgl`M~I(*cC@dI)O+&7?L!14j +B2CNuFIQYhXr}|y$XAi9iE$KfZ{J!wH11=Bn9(d!xj+E9z0|l>C_!2L}gI){zEJz8e3c3{3sO +R-P+xG0%vrkV8ac*(XhkKs>|Jpm>r>c%Kj$f5pN^Fb{$xuhCWiYXZ>e-)X&+eXGG&I44CdQ&gs{xWAm +r4w@YOa<>OsFB2OsT<2lqO(|;RUu>F))S_uuj6HXsHr^phUEcAt~yOK?VtN`drfKi~a|l;h8&gU)_85 +`+1({yJye6vx`>XOq_$);zIl!ejfiCm*H3OZd`+FaXoIrt+)e!gu8Js{tSPCZ{rv;mCPcEWFB$IgXBj +fovbA5$%~|XjQk;Tl$;>vf&DZ(3nnQ;pQgL$exQGfPGr;A2_A4~xyG$=kGWUe&)xfkE3(DUf$KK$dr> +P6h_A#{@49!(n=Di0BQiriA@|5e*)6Zi?O%m+80lZ9T_FzBYNLz-%^U=1p_ZG@5R6$&8pgZL +E#Aj!m}}w%40-f>!-Z=e{If)=ACXc=0CveC1s6P-gBQ7+yHJ +a*tp+=yH7hqx18z?bpA@aK30PbO1HESW*>B}v3150J&=$7BQ9PU=aR43XLNUYbM~(Ur8Aj?j3hxlESJ +>e)N2gZ+b@V;9*KHo(4Mx7Y+8&A-j>=HKP>_ySJ3t@xA6~ +nClB)>ey59E;U>HF?qRpZJ?@6xo9>7^P2`B5iQkBFaag=7+C~p$nO>-0(y!=UI?2!Ti~X>fV3kd^OKq0Tv%j!!+dtc8`;on9Z`i=t!4r`)B<_Lge8 +JfXBookD)QxVTb$By=1;2rh;G;n95)iuw>Shj^PY78=^2iSICTS#-=u*0yUZV$r$WM47e}TWt+r(#Lo +7XEpkvCyHd-W-u0U3?69rnD9Z1@1Lz)~mEp~#?*(MzUmpYXqB(`?A@j_3g~A$WTcT_ZOjk0azZnMfzo7}`er=yf`sWwUl +(;~sJoM5b6L4vJ>+zBnT;i`znaLGLZ^Pu>S!*h`QP%Q@IS-N7H>Yk9Kwq_@XA>Rt8*yh(B{^wqE|QkT?Km7}-lQhh|n_}WkRf9gNw2mO4%z<|4qaDitLzm!V8eFM4oA3_ +f4x=0n-q|((I@Y?OFO8rr_s*|cu4XJ27Lq85TrIO@j?9 +zK$U?b6UYDctZbj6yYL}X)Uxp4y1Bb~mEhe&eW(9mc$(f8!qi^DP`~?0Lo}OEibCLQYgDDGh7LTb&*)3~4*z~X+ +h6N{=_i=wW{sI^=UZtXu*>aETVv~?17R?&U!UKb3l4V5nTYN~H_?|cHiMVrWbzRCEBTPjf&Qwdd92ud +9o*q7cc!48@(Sce`AfM~?vSTtU~Hr0$oJ%<0+>%jkdIQxM<41(gTNz7O;NExI#DGlsx&x78Zcj_o>uv +4y(&^As!UY?{lHiONB&4#2jdHb17qitr#eBW)Twpap_ZdjBAN?%526ydLM>#u3x&}r61 +oUSBfh=GU1PTSfAp|6ffki3MCN;Jo +8+4Oy)~)_AUl?tYO^Qi1Y5(oGpvi}sZoLVaB2#QiOsT1cyM_NbD^Gwl$YX!3)2p0G=3_++7_`d?-fB&b!U%yf7` +w8!+?zpPP6xeaqtVQ2n>ReL##P>=U{?J*x@JBy-LUBIwn6otaBj@*jHOgnk3KdbGc(N8B7g%u +-B!bE&oYZBg=4$m@^us5wwh7ObPD0HCUTR|A{e^`s&a)QvD^xer5l*d*m@Z9o2tV1|<%f)$|g0Nsh>7 +xskg@W+aZM;q)5B}OO&Q}2cC-7`S6$FG_=#}?W6H1qs7Q;Ku3CxXfZ +>-+S@;$cr3CLK0zzcIlV|eDWe3QR>CjS3__^+}>tlSLG0Q<-vnrSZz6=oHMraEWdFPEQ?ZGs>(>(Nmr +L3VGELlv%E8|K`$9%tJhF3gg{0ava!JlSv?a>(m)YPVbkMZ%e#=K-5I@{VBncBtadMNma$UGRFj@{n@ +39Clndk4deDBLnkV_AGSK9)U!OU5!ZK9+*RG?sq_`i^D8C$X{I`+pnD#n!htkHlS($Ten9zlN*+hVyt~=@^s5)Gf=W~A+2tAgkEr2~N +vF(+0koF>E5nrs=R`eK#%pgzL>q>I((W0Ypa*Fb_`$zDXKyil@-gq9eRT`2gpZB@z*nVyIC>#S8Tj?@ +ww3x8s{eHH?%Nn)pp|C(~hs@fvFR1o*9O@|O$q{U25GFVn)Lv}(b1ul{bCI9IOl|-)WMQpZ(*WKsGzy +5~TqTt6=xz6bSi4@<;Ai_`-n4hJF#k@h6vqPNgVF6fE8vns)8b3@+KU&U7tp%QK9Fh<-)X;GL(OBkoF +3?b_M|8XGz{kJpy_benSCeZoG7&U`AFVofHnGQkeVlpCK4KYi8q9;On_D-vDzn;c&xvH=%BTTeXf^aZ +Q)K75Lc}KYJ+G&z8dLQ?j2OljchgOY@|;^BT5q=P)$;J_OWU*gWi1`2Gn9O$)SNq#@vp|CK~LLK!d +B;%Hfa3|=i0Pz0Y&G*bA+?;c?V?BzRh(_?>a0Guan1Yg}stH|KZ-TCiZ24%n +r(|+i-k-5Q?Z{1lZHLNPRb&gjh$K1FThM`=P`*^--4-cKD6Z7uF7~W*81DysOb<(fbK*&N+N}dEfy_-&MoUgX +lZh{Xp5tU!Mq6Jvs`mv8bj6x?=nH=SK2vrE-)P3vDZ23HW=#CcnJ5&bi<*x)2y%L!;N5d0gN9A@xAc7 +7=Z?m{{4XO??VD3v*S?eM_mLah9i6sx9MlnMq3O*02I7p^;BC4fMHIF+C5qsE3tjr?+4PjheFw#uxxm +$&2UUV7%vmtjzluuZwB!0itG}oYeONURFgp!@6vvr5yw}F_;fDk +G+^D?*aiPKD&U2RN?WzWFuR^B&FW}lRJ^RHHhiq+{De{ulDr^#wV5+sZXei=*HO9_gt#2XreiAWBd=i +)RVsf%z0v<4IeacLO#T8Vn+=-{;_s%JFMl-WgO5WDmvOpe#2HYH*AAcvbqFUCgyd^$DhEK#2xIa(RoT +hYx}(Mwd(Sf~hkh~uI;AJ4lv6f4(7h!LxswJaW)fvt(v<`Nj-=W=Ks;W5BcivgrrBjOlDE>1w+bLRuD +0n{)DpkWCB!-}k4+8=&1ubc45>nVgs4m;v_WNk7+?ye63Nsophl;r0r*6_3*Jw>eEC98E7v3fV|x!(Z +LyF0z%5*q?RY9Ktm<6F)KElSb{N^CE7`LX`g<$iv3YZ51PkoOpjeNMg!rU0NtH)tl!%NIa$3i#X*Jy1UXA-7sV_?>R9ETmZclITK?Pn0KRIFTPJ&5i+?MsK0+5CL8s#r@8bi-36?unz?2nFE3UnB^K7yv`5A!5Mz=EA62qoIGe~s0&h%Yq)hIsZ{m>oc$Qi+(0;U#u>y(u)98hi +47nV5(ok1*Jh;iJ?{rxMYLUe3b?WDSNc~{B95vc`Aqg?E*xMIg8k+CdV5J%y%~NV1U1mz8c?oey!*_sugBgOQzRW?H5<^=U3PA=O?je={1~9!xkextWfrB +TRNTy`~94hpT(JCH2fs&TZ2vZ94CJ;>gVh`boVt8n)VRE3U2O)eXq-w{fbs98>!e8 +cQc+)OSpvFYt$lNUwY;?o%H;BhET|vgRdjW*`Mj<)}3_R=;cF@bZX-%}BA+)NKI!BuXlUQMbrkbpuVg +M4oRb%(VYvm!aaxXzz=D2Cdnf+!L20Cze6GJHm;FQdN-h(xd-WAhGWWG${0T2#&z71Lf9!+^Kz`n&{kCXW` +H_>LSFZyp_Nk;9aB@y7ERg+Nb7{Q}5G3pJ7TI;L)OcD9)EExk#VdZDRtDCI5ui}%!6Yd{_2xOPma2}b +ZUBG|uK(K@*Sb{rx1D8>mqX~ijq(OqJ-lm6N8Ftfo +9H4Fto$-AwqFzvz&bQr)zh^8=$&^kPTHU7uNyXs!_^W1*9~;(54KRS)rX(QMSyvm@QR%@+PX=hDn`NLsD>%N_mnR3$skX +pxKkY6`wZs?PG%fTLctjNfz*cPH7fx3ioGzfES`465CRQP`wR13fOlT=}ck-?CV)glGN>UOQ^c9gdolj5b)u#^=`Z3m`DtZvAu_vV5827C{!Qm*=YoRbS&%m%hNMs +k3=Swv7e7bXU2vl|G`o&YtTnE#E$xasL(#q9zxEZWzL06C~?BJh-~8s40W3M)+ZoA>4$`-atz_tO#LmbBpZjELFE& +#AW-N7wE2)ryDJeH$GoI!j1df+yNta4iVc`+7;f;izpI@TEA>EYXCp2qRZ4gZ2}Ys6d!>ZxKsNHR(Tz-@<$_(ZHSC8Ad1j}!f!;&a_>X)aKds+j`j4mqbe@_Q-<={{EP_h_Yyd@wCe7>4KgvUG`U_OWlIu2X +ud%(i;j8Gl!YH|HBY4PQOSed3gFpn8PpCva7F`+!`>n)9=LKzIM>?N$1!QA2s0)j>ZZ%M1jgU0TLO9z +BP;g;f0-sfs295WU}M1WAo%1o!YFTuR7uGIE&EB}{l`V#1MTXn)AURixc`3uIBJWjW=Xk752nXze;38 +OP)7`f)#NiVQBV4(i4O5345sswauGlb(^6 +T7(_G>{I{1tnvM988a(0`s}GiVnu919`DzdaKxip&CDu8EO_10kR$FBKYErZXV22jap_P}_8;+(e$dA +E*tztDKh}HFCb?cb=I75MS)8{@^#M(euDN){vb{Hxz5<=S!kk|B_OUs$AR4z#6;B9DdmyY_qp%@0p^oP>?qPKy_>y`PT7 +83Z*!n`QNnl^+JZ|sYY*r>B8G+u^N3H77QGRBrlOJ!H)9JO@$`l9kczRk{URp58*eV-!vd3I +{3J}$BwlxC6rckkG#VPGwN4%Gj2LTj1H-kL$j=*~uOmUx#`~d8Y;5EHoa_3-NGmAC%L&eYC@?_Q8ItL6ZrIG0|2iyyF%f`!gSrz4af0UClNBV5$(1=544rW^Q| +Hf_Uu_5%_TX{IbU%9tX!O!?e)d!@9{j}){jPA1K&a8@KIvx>Kd_T@G6slItT;tOhNZv_G! +)fLm7fr+7&*=vSFvIz#|mP_R^*{PZOf)>-wZ(fbC(IE(G37a494bltq8CkD8G)wz!Fp@(NX=qh`ggc! +6n*zP)f}kEml5k)J@U2tWC-%8Q4@%WBT>0~0mwRldgC-y);^Twt2p#znxq~(?+rbar4N%j8%)X|__Z +nhBys(*w9=HOLKkjZEY$xFEYw7l`>M$VHU;8KTf;sh#_}&l^@4)uWt{Kt)lK$pPKmW+hr2J@p(%-uYc +JY)@5h4_m!_UGDOUAdQt)FTh7nk36V-rn=*?GIBfk*f@YqHnCr<@?WTK4Yj8i5t;Vv@v8lNA?m|6g_l%exlT>DIErMZB)3Tuu +Q=(3Ma#B#_+_dJILCkeBDzo%msN4#BS0u$r`$YoNck&%Yaiwl;3eMFLfUvdhLoocxPCk_bwfDqcfk=j +TeVH5@iJYcc4vk_BfO)u9cOSAE`$W7S%@1;#;HhcG4qFmUux$Y8VMEb_q;&n +Z1yNppwhm9l;y$F`C>L9YZG)bho67Uhd@^{vHa!W?;^eI0VfP`m2HEz0%Tn`SjtY1l)DU{b^-t{P$`J +$$qt!`UGfjdC#Kfy1rWVZ@wfBa`6%l(h!_cw^$Fu$>mh~J+nzd+{s6C&^`WMXl39yoDwTZXW6sOe=$A +sfLp7!Uhr)^uF58L+92k3ng)TS+%6U$3V%8ig$E1DgPLMsLBEr=pMSGzWX^0|@%i}*{-_g7fhPOodRN +vsBeP^v|%MX%`F7QmBy#5)tez{y5p0Vo8EU4zALhaY_yi>mL@Ob9_6qzr|~?b;3SyA~J!6&LCJ%D3Q3 +#Bu*P?RO7wi;l>>q{JkUfaR*ofQWS-( +xkP=#wP&wm=~ZnAEJ$W)6^*JRz2&K1T`;Q21zPY}GV;n@2U!|@xKmXP&kD94v%@&o+*_a`+p1Bce*Nz +7vqP$%`vAP$=68}f;GmM>u2_}2Mhiv}P<5~XDQYYF5UUUXf#GbSju^X9W@v|rMi +gLP&*wFwxF0;s+C1T}C#D&E_mi?ir%|)R?V80qYw~1;wk`pt`|JcvS(gap`9^chhh}pIX +V#P1;4djxci1T6N`PqklbCc1Ad~pcURx9PJgyN+?JQ4)DKk0vN#RHC!Fyg<$n>J!FKO|qzCvS)0_yt6 +>sTPTBfT*rr+Aq`a6_%FGv;>NaXiCRn#5lise*ls6j@Y^MESM+lSCAdVXSKnOBdJCqAs;ppFk}d|MND +$KPXZ%Xl+*O4Q}MX>&a1W#23%w=aq4)M*v0<5`W?09Lk2^O%fSK!->0$+d)0wQ+SP}1SIN3j+G2}^E5=G5>5lMl7~jh5j1P#@7gjz^{lLfN +G2QN~oP+R!<47IiE35|PW%FB0b{G97S^z^-}@l->c8^g3=vPy6zlPz_h}*5Bygb#l&bx$C%Gf7UFsZE +}6&&Tyc=U3(NjNL>GTmKbX~Je5e=L$Wcp`&~guv~f`F9<*v8fQfd~r6ul8lxTXF+a`q_VLb5nY%2wDv +-YR%G%vqaT>oPzm9wo+pSmKKbSUxxTDYn8xlmmhREN!Git9_WL})v*4im9KV{Gxe7u!K)A(QCriw|j)^s#+DvM0JdVL;?jI>l-;-;Zi^p^~dcOO-Kdbh$D_8=u^(|FDwCQ<#J +Xj%I+BOo{bK=^eUlQz{fycP3$d5^Lk;J7Prx#G*)a8mVh_v)VoL=_i#RNIAR2irv^$Bwd+ev?zC|ku2 +pJHIk+bRl6<0=X@w%p_J)kZR#$26nzO)6nq3KkZfPNa%MnIQ5VJaUuG<|N^GBQfrvh)OJb!3s`a6wb0h95OYckr$R2$cEvU?d4?oaec$hQ{mCuu` +)R5dabBXGiBvvlxDTxXkZ&C$Tf%9(tzM!*cT+KA-#sK{uw8Nz=wpbSZ^F$UqWa+QxK$bO>B_)=nnX-t +LX}qz1kj;KkSG@UdvGN2RvIbC{x*bF*z}{#=i0%;DujcV_9Dpo_)tUxK9!O*;m=*Gk@P>l6Kqv?eZH0 +#&{>E`-BZypy6V}{|ao$DpPpF6xTQi +HR*3bF){o`Zq#lnZLqzT6(3X4lHBTy6VL8oT2F+SIc_-vC#Xd>#dg?50tO%`D$y9jL-@gQUkHNw>-fZ +AMe%Nf>199NZSgGq~jgT2EVlhE;_!qR2PT#@)RBbrGrix_=U~9to)IacV82TO0f-we=9TrMk`ux(g~O +;;B(AYdD_xC(8QSdDNW|=aBtI@h!W&m_~(4I^fIgWVy>3^r<6a3}FTN3pDhRQP2h$&oDB747jzSSGJu +ExC2dZ?dQm3uER=9&f8xJF_XC?7CamWF&$X@ZUb=g_ooYJM!JO9aK*hxs(BGxyvfB$*%CSIv&-QkJ2y +l{hR$%j^bU^XPd2i6nuX*JPe8O`zvHLSx5Z3U8Cr9N@sI%6$6PX+M$Z7UJcSz3jtcd}z~CKx4ffRtnN +`}bzoANAb3F7Wtt^FLh)V!q6glK8GaP6BflL{T8jfTC;Btg)INJU}t7ES;f*I;L1Gwl`tzsJ15XQmF? +(Nzwhwy~w6&KD9kZRyfmXbDU?tHGl9>>w=`XRuD+ +NjAZJAMm3n^o`*-_jlAu{X#eHOHJQkfme+fsU=1m1y8B7uU3Cre%M$a}TlidPIVrTP!Bn +7ve>}wzV4aYdgXhU&Ihp0&N1dHomTF4DV_O@cEJ9 +{V~@aHso_pPg5qwwZ!g%f*cxK{U$Yj4~MLCl-`aG63R5!%_Y=vCdZmHUQmJrLcD|qb6N;J6`7)tA(v9P%XFk3Thf!DFvC37^J*sbCN8@Xq|q^C$k8lt_OAqe +4nvNdm7T$q0F0G=_9A?=yHDT`}YCiyi6hGV7=a3|5aQ$I +!I+7B{d#g1G!6kcTJqmhIFt{h_%R1U*@s3rTCyF6V13wC8UCoa&VpC81}&KQ=fyJn0q0tL8&Ms16Ik{ +@R%yqRh6i++X|n5DqNkaIby^=4$cZa5`YmJVXe+s*P)$6^pT37qn;Y<3cyzcfechCij79eOi+&}D}#+~$}QUG=Wrs4k1T~(y#Thujo2Q`ybd0b=SU73JAp@C0}p*Aga`Amh~dTB-lT+~hYgP+N=Y +NSmNY_aTcFt5+8_w|4_K6eDA$oVas&jn&1evIG9U1sLG-bQ*9nm?njb%-VT?#_g4cVgqz)hs9j0p0g?a)pO|zkP?fMf_BH_^y>p`Bhn|&8S^ULW(5+rnu47_8%RIRmw(4%AdCdAbyqlXnyH{N&7jL88^ehcseYDvo7@LtAu*~-$ +lm3DBKFZc1iwk;|0zF#>%*!U^20e#^ne3O?7TYBmAL>@t3J=d=r5|=V6}l`|W}#U^i2Ca}ELNsM&>Ny +&MlO*5+FVq909n11X#;Rtl|;P7#m`=Ojb$!QLPgNkf8og6j*d2MsJN<$Ip@Sg!R>&Yjj=!d3#XL;+br +`BCz0G3N~YoX6~wEVYaxORiz4DVZZ~(S32E4RvJQ7bInI;1&@MDdBgko{9PKgit+8BzM`2@m7e1iCD< +`P{s<>*A*lC{gwAodR=`doES3`rfq)8q@$)OE(0zWhB2fR}a8q9}Oied~O^uK6%zI+39H7&>a5vEMb%h~s^FR{iqvdq66kFD`9-yoJ?B07M8%fxvIXzmdl!`}=H=%8e=YZdCJ +39-XnNBPN6{bnD6teUXZpnmr_Vo`)z{()N=d#=F}s10FP10D=4N+Yc|M()WUS|BCNr|c7wf)l*d8dBC +V~Wk#--+ZNvtjy7EK7GKs46u3- +KW_Rb+V{0+KOj>>Wpq(ay`OtSR3TDA6G2wss@Fz!wW&u;F;YRiO(1N2>1{5%w1M2xk5r3xH9ENT6t?# +=^2N+sXN%)n`z3lakO`&}Yhhh?SctpV7oZPMxAr>fl{@)X6f3?mAvbE)dS>C4HHj6$Sj5R$#5rcFlYR +hIK*C%|C^wK-5G!g(HXUDn4g~4<2APr5vF`mlvD)Z3wfh(}(So{@K_*NazXmgs|5!=rmdXpHB{v+Uze +6kWm98(Lo$H{6N1z7oEWP#^&6uNCvZJwSkDdauh#3an42|KeL?SrL~k?Fulpi=MBYX8## +)m|F;df!MM6~?HTqwK2J`1v(-6C?;gqSM$uvUy*VE>+n|^4&^*V>9WR8^v485MQ9m6plwG-?)0tHk86jf_9pON7(j}K{!_od4VXK7($8*;jj*SpAF?s1Qz_n8ifvo*=rCPj71P +!BKeyy*kbzeTikH!aQkw#|sTJ(CFmWtZMjy}@TJXw6Fo7Yfj$f7rjd(JqrJPX=8)B}(|-4VKNdSf~Cs3?%cwW#`D3%Byntfe|u`Y%~#{-|9tkNUgO$LJna5Rdy*n +H5pJ)gEurUBM_QrDf#v-z?=tLx%y?)6^6R&9IGl|xe8}^yvE)~o&`r|V1_@UZTDko~!iH| +P9DZ{JSB{l&<=D>;#g$`ANGdzU{Fa#e^2As1>IM^6Fitd)wJrLLSD63^je1W*>6tZN*L{s%*AgchMBk +@hE3UiW+iB)+rlPz3bydEc@4e +ntaj!!8L!5@Q#O79zzou44KjuTo`JoL9HzdQivgoLi0}Cw=Q?qQJlX7X$T%6ne0YbE9vlpeqkOav`)S +p(w__XDM?S|wKc%wFI*UTEo*-ioQ1TYuM*|k%$ZX-MPVK$c1Z$G8iy+>v0A(H2i|*09P`NtRcaG%Cp5 +WLf>#gUwsAD+(`5QstX6ZBgnx*SL_1uNvfF^53BIjzzsqay;}Az|AN{Z(J5N +=Qa1c!=gQD`ugJc43H8MDfNZ-8A&@)PAx}R;8-opm-^Tdj)(M;UD +-ETf2t97|*x3x4b$wGtCjJ2b4Yqkn? +n!VlW-4=)|q>lrwwvwjFa+2=n6z_0(*@;(xWX<_emc0X&D^ +0Jg0^ByoM*Nf}=i|Ztlr)~LA*x)y1OKZ%Md9P$MVQi6kyQgj02{bv*8yei@SLS>qi#Vscf5$$mR!IEu$=Ghx$m=f%OSi-Wm+gJEfoRFh0Q$*~ +27Bj-==P|{^)NBcdL +N!ka&XpPCy>C$ltNEzSF5F6*8mvLvK*5?&^~o`w=fFy})IRYSw&1%OANdq)Y%(O~VMKWZk*juq +@-P{qHW4<+*`;meannsaxQ++08trfVeGUm?*61f(g)~g9#c9BP{fkT@DTB5OGl!p=vyo4<^?l5qKczz +%ATyCHB5^EMZ8qjppN=Ajz^2*yqf+ep)J}zl=dWhikF!`4 +SnLFypFA$Ja+uH^xU8nJpd@ +N=6=keefy(y~=BpRnJGI%3?3SiMi$Rh&vgx +V_*3T5f5wHJ=kMOcgO?duM)sINOjZ)F~+tkFBl5c+yU+{^0?x;6PIo^UwzYb)!X9M_m4aA$~{j^dido +FT>@O)TL59H9mL4zHeEJc_-NFU0S9eIa7D;W++-0oEd^$D&fFU=JYmSmf3o@(oq%%YM3teIIo(jmVbFgFL>+x)DB->DH=AG@EWh)K|oZRpG%!UOw0s(?%FYy8F +aen1sx*(qOT%WBvPZAhx|?o{-qq}#j}z}&{}07H&?zn3+mKK1dR3BqMoCj4FmR)>gfK#4cs1VjpzFzB +zK{?(Ng$Z0DQCx7r*fQ=Z7rqxBB50(xtk;*?6I*P)-1$t{pjb +@Aub_5udWjHoz +Cg*;MjuYV!>4Yev)-f(4LxP+thb95AwrjhcJ*kF95RQdX0gI7NK=n|tu`lx3bX!7huJr1Zaf?S2}bs` +1O6S$Lkl1jOvgKA9wiMCF-h6*-c^`|RxM&h82P{)na#B4JPuoYvI!btg)XBxRA{R_xq7IVZH3}##dv0 +<->}+DX@=RKpi?In^PI$-otj1zg!Rv>j9 +036<-sHnF<2DE4Rv|4}RYi6RdI}E8D95{1I&0b3v@!3OU7fXsFFSS{C=hMPJo(pY>)S@JDT>GsSg#{I +#aoKfn0SZip@@9VvGmP>-f4$$^}DIb@g3%|)!)uO7M6Umr;h=JP-b1k6hRl9H0OYmsoO^)|FCJ&mrdX +;uAWUi+O5;-~@vGr@m3oYw!P~w=9^-OR+7Wl@-iN- +_>d`^KLU_>C$PM0r&)O*W6=qj*wx&EA0+ib%I+5vlwDti3ToPc@6CwdAxilvndYfRo4`9o+Phu(OVzFeu~C4o4Aftb0JY5gl8-25xZJvPo0vbe$N*7K)d*E21G`7V +mW5^agZnCJV!3Bmsop~?+6~Yh30*Rczvk$lS3d=8lgOG#-aFTk8A~2&uswNs(J(7Jl4&4L74h-G95?- +STqpMeBoDy;jcHW>KCJ?vgf*;51Rz}@yH|9A#eFPB;d=2XhWc8XrSX@d|e~+lX0@XM2pZ +nl5r`;%3I06X^*noyJqo?c~T7u=DwtpQriVd+_=MA^}_{5i!un{IGr_gwY_Ol-c*nE1lTwFf>`+F-MZ +vym&D2KfG|E9hjfr^BZh0s55|?a+H>?u7d9OMb_6&L_a5@;HD;rTn)%)@(F(DBDyyI&0ou(ihzI;^pH +zcKN}b+ZfcFYKyjv5Zb~|fyJ{w0=A@TrG=cXJZ<7ne{7-Ckbk`dY=HSws6C3+;GoOIid)e9vFt*USn(WkjeC?#sqBulU`km +a*JjbJWsm0C&iRYC_}M2#>?^;yQ4aYua-bNFN-MmI6?TJ9m^e(VKyOj$_AN>#DW?6#*2s3kfIdw&ngb +qf=@5U|XF{LkWV};QUmajt1a~WpEE*pfuy}KDXfgi@V{2@T`m_92NW<~tN_s2FU-gl)5XN=jMZ@SjC| +Ma+wrWnAGRj*uCSLOz-<&G%jj6iF@A)K{j>$o5qd85_0f3QWjKelV+cr}WFcE51p;kr29dBI$uTU3^D +d72`CO(q^+dv=Hx5)xNUrBl@&IJwM%Z~YSz9?I@z$8}gPNIvM#0oqBmyOm=a~Y69qapGyA*RpPMfI%Z +=^@a&(gBdc*etFyPtJQ!SRk1M4Ak=Eqc{W3x#hYbwyFTyY>F_{ +PRRGuO*`$^I*O=pTg0bEk^VPHF;AOG5V@)~j`aqbUu7j}|D&C|075Q40?GE +1cV!T%K?7Z5-`)(enyTcsKWn4Ew@mE_r?*kiZBAml~(?H#@az@GFVU=9PoOe)E@2U8*068XD8CQ*@(F +Ne)`4x8kF2{pxl^PUO<$$o}o?=##?{FkV}T=fxWZUBaE{>7z0>Y>q<9muAV9O$GaXfKBasLIcUQIA{Z2T&L}oN@Ml-Q3(I@1M-sgsfwcaB;ba+?(ghTOei* +c{!}@;!K8`|CR;A?0b!jgyikGT->c1NE;vCi> +mohmUc^`+6a0uxDO$1d==h2lm7e_(#Gcr1ilQgF1&al5T5kHSE%Ru40wUh=1gUlF4&L(r%gR-Qohd`r +1B_|M)8;2CA8}U6ymhD4-!hi`+?2)aK0d{rJ|i)r_H4F_u_TnA419yyal*jcWAGDkQnX1sSemLkY2ce ++;oRgI#j&8H=E^ygtV6MT6gv_S|htj;=(2;dZX>y!)0_clScGCJMHD&{Ai#J67g9Ohv_Dg`*huUwyf6b)R$6UOSxf6Xd +ptXF!JI)YAai=$jD!K{MN-O}+{bOgmAeiyKZpoW4DxeIsBb2!lL!P#!K3UzIDyw&Q86{1V4I +#}NDmBB~4MJMut*-RFcEfd56m$xuNW$ +PUoie>2S*Boo!)`{W=}5+B_eutkRQNHrk3wtf%tO>Xm+VqRUVg;iE@cD0TV-lky-wY{7nhmf3YmlMVLQ%3Lmf2SI^O%fv5@f9FLAbWK@CGm>f9T0j<+B-%*H?44KX)P;yGK(m}S( +M2jY7e!lc*J#ea_D-G8cjkZ=CKv%X=>CQwBoxBK*?i|Sw(lR;@#YgT6eD}L(in_a+3x1D(EdVM>4j^< +>yFReNILM^>yY|Oj3DF#O=Dl9Fi!N!Q +A*HNF`4jWOtd_3ihNO5GF<{NNpq##)l&}pb|3;uj?xiTlycEkGwb?Gj-P$jfE#`9BA}(3PFs7G%8pD` +b+VxPq>I6KK=w9UK$Qie+Sn8Sd6g0N9f$@p0v01L93c>=t)NWhRMo3s{>cE*YLA$K#J_3SRLW+Dq{5xqg8teH4%3jily?<(_enaR +U6-RAB$7!EXXP#XECVV%f|Sb#=Z%#h?3z~S5S9N{q;h13~)oM^MME#3Doq5#!f98-AsW8fwqGV)pK2N +Q0<*WwJ=>X(DJ_X>FwyMYeq7EnH!C>*RHwuOq<^?{xjkW +Lf*!er6^|@&{I$s$QoK~M$Jx*eKeC(``Z3~2E{}Tk|f)ZclNwMN8{Hg_;SEueY!uLGj4L^Jp-ZT*i`N +~i*il!2SxV1#eX@JFF?f}fT>?Iy2t_#?Xs7FQJEU|4MUd!Q(IzK$j&A_Y9cnR+qSeIdv>LV7^6fTm~Z +kt%~HCD)4e5e(^8xX$%&!SZRjK;!$iapoHp1#=gniP6gPLFk}_EF?P>cyWANJ7onEu`p{#ER|cff_9o +D`wD(Ppnu^FH^*df6>cKv7(;z9&^Nsr;_mHL1mg6T_{$(Lg~d~#b^`q!HQj2|CvoxJ1zXHH*@1|0)dEw3`;<6Yqa5YZg|hMk}tD4HnarEr5dqt`qIJTCcztiV(+iL0PN +nJ&AX#s7S*5kiktF$?6?#z><2@HQ3 +adb)lt>9!0#LhvMiOsXxw*>gdK4gYt+#5H>#;y_Sq51(7Oh2v;iGt@p{3s&YwgK&EW22@l!Sb3a(G^w +k6nnY!gF0;6;$Tq#@Uw17$X;$Qz)Y+I#u1u}F`JU}uS0p&znaRh-ABKh1(bVmpxgN-;eU-<59^OhbO +Q*c&!F7i^e;fhVRs}}T>Jg>*o=x5WiTapm4Yw)pcB^vgr5{WDU{NL*MT5?T5IN0$!`Ur3Z?gRQO7*X6 +{tbSQg7uECAEk{v!0J|n^D?<0jzH*wTOE{NhuOH-4)eG0Iq?DgW{5lis9YjV|PV+GiCxxdeY|aYR3t% +LG{5jUaQ&1>MD&VQ}`!yQbB;|^7M$)de8@E3A*frU)_a(+|@q3Wz06fDFL?2*j3r`v=xyYUgXFC0kz0 +orvj`lJULg5=C3wO=0;R8wCW!O2$hE`u?_?hSP|4bM7@}dU37sTzIJ&T)kmm{7XkpM?5>QT-i+~C7l9l5^#EKDgT!GazDPm1dZ0paBoaK>8 +%D@_DJTkljZAk)pDhxe9(YTx>ey#B3ba`{SphQM+0Nu`rS3Wl4XF>jqpZ;OKn%JF<0oPneh@aqn+(pg +Ul;QAe2|6fKZ7z{>D!r%+9qvyCfx;Q97s~9MgdUE*wH)!_C7T=($!tHc25X8qQZ3dh>sF7Yi#qT|$OZ +hm?9YIe_M0!@e(HRBJ#||jPY-n)8rc-+V-g*u=J&@xJEYyxMWh@3m@D_Jn2()R9^jpI_I7qdCn{YLU% +K~58=QRW0rL@qotb2e-mIPeBd~n{J=jwq;6Sc18!r&KC5hi`I-Q4gG{)bmM*YfkHM&SCtX)i)OUWWHb +v3$Ntn8n}5m0(0C5b+oWur8ZWdLDeLPiAb+W|G$BcHSO}x`$1s?Jvp`>HU6|`n +B_#J@Yykr^RIzx*P*@?H{N$Q2`g1`w8vZMji&cpqSR0NH%Sr5d>bf^-}*o5;KUMaT`QNb@pO6)AQCP! +MSt61Ol5=V}G>H~fb)-Lq6Kr?9RYR8xes!Z@+qHsnT)$4_NF}{pz;kHVi2IUSlI!3vvHis|RnA-9DWe +|b4r=Bm;{ZOe*pMh2DcfqeoFTnXv0raeHPtx7q(CjMgSux +WdXP&RS)Qk_=p1N^p`lZ#Hq5MX$$fBG_h`g^|5QOT4kKa7(;gG;t{3LU#o8xl33(Q=i~ipQ*nx$I2S@ +e_ST}F)0gG6$f1AiAljbyCgaRRc>YZIZxVQV~^JYXb{V0+VLX4W^FKD>Dd$%Sz%U1-IJnvzbX?@Q$`^tgS@e0j^wMZQ~&Elta`R((EHYFfq5xnx- +_hZH)%27X86fSR%-88jZTlzbG4S%i#s|htsk#o+8)s|70-nx%pn7ZW3dirveu@kSSufyx<>y<{j9=I; +2UZ3J+J3$g$Z?e-B5##8FKP_n*>8Y>LZb|gAAP+=Bv0@fkK>?o +#K8O^RL)M4i=X%Qbpw>0-llF5A8rj1hLmH%dCWV)nlnHY2S2P!>+I)*EGO-k0;>tcmlE3xuWYjhTHIj +01qZ}SlNS}Cn7b?E-eJ&Ax{9E+T9y|JdBt6$OS&Je<+yeHyZIXKZs`$s8F +nwCrM^VJV>tft19HFQ4p)Ja{87pP0-yY>U|0O*tDGiylZ!PRrH@n%cr2~-56~6iILd1VW9%m~n&r<#P +>H)De}`TurWWkRdkd#76|T0 +sOR)u#A6wb#}~!VP4gGg#{!bjMOqto7qVA}(d?nV+lodGa@g_Chgc|M8`?WeZuAbd2_W2Oz>?gxfgcz +oUG(7|(i_F~hcV42I%Q;Oo$I-pZs+=h*07vw4IlGl`=FJj^YDrb_N`2ZD`RhkDD}%x^5e!FKeL<`HwN +-ZYj_*9{TlRiEyi|w^K2$19lse%cS7s^v>zqv2bu#3!=}&$oWr$XtOG@f4BElkgr1OVzxwJ3f#3k0R7 +S&#O&K9Gzg@fU*M?DEoYi%XO!Px${4@-H!6oJ|jOQxMRGvo{1LmkJvMj-|tiU-5uTN1Nldi%?ze%IZ_Z5A`*f*?O`=2`GHCp2Cn$Ho#AHN_XKVPxS;b-bFq}JO} +|9>JL_B1cGK)Tsax3zL+Gr%MpeLBWxLXjW>o +r5%JX076NhI5dP#YHxKG?O7@MoWJJo0&ghXOEF88~TI59yYjt{~f&NZ8;g)jROh$d^CK#Z^E1Og@?dm+4I|EFW=w+CUMmpqtas5GOR6NkE&@I+?p`E>+t*9cA43CF&RJaFoH>7`H;{=+H5PuAgG6TG +?~Vs#OLc%c9Cje!8vy`YGl^0TO +CP2HRh@m*=Q2ixiMv@=D(X3b>O8LAhn>PQyC5{@81i`dYno!{H6wfk9krXrx=2ciXe9Yx<-bb}qG4A|Y{)Hf8l6d=N^C!b1H)p-`wp?yq!RXDo@U7$7im*`y6q>-tbhDyM;bZL=2bVcxE9Y1r0_r12=e% +DZv>@R09>qV)R)n;ENY0_~I!GY1^lxOWfh-@tTCw#5)baDnX7;r%>v`_$L33FN4uq$Ec;X6`jfXcb+% +h^oPUaf1QJ5un9z&F2LE=cVDBs*ZD=;UoMDQA{dUn3&`rz?>=NWoPFNkUBK-R^5PM{U4Z+4E{qEp#u~ +oX5HqG-u5rhj3wRC(0HFs$*>^x|{;m(;ZQ^Tjb8?QM)v-6CkzB)ft(TKcdcGdUeG)(Qrf$bi*lRDokh +sKV9PAGlUTtYq4I;pk90ny*Mp-PNT!pf1ir)JgdbEXQMJZjt-Xryn-4JTJdI4#OinOi#U_#VJPz*9(hW8@Mm;)=kHpP3v_)e04e!7SD|D8x5{-V7du3dJd +NkP{96*UFE&;#KY>ahw;}zfOk$^GcCg8(;n!)s;qRG3tJ({yR?U$^t2%rYI}}@JRaC0B>nYyQV^QxK1 +Kp0aq$%90?%EVQDucQzJA(GEMxh`DP0pZ7e^@w_2)eb6GQ)->RNi45hgdFla|32%y$^H(}jRuQf$WB%HDjA{2@#1&~<;%R +rox@IrmyY$=aGkco7J<`tZ)4TLYk|D`6COR~h<_)xXnmx80bG5tH^mo2aYl?CWqn`ov(}R8t73b63m6 +p_I&pU=qyS>pS&tY$b$@7*s#N;{Z4e-3<*|(F?;q_U22h^GPeNV1Qrqo*3*zxq`%5}|nMv~Qi#?yIn* +U<5FcFr|qJnKX3B* +%<>HeXkB>Y_OD`&AWj~A_*0h=L$yn0N9?M><$CkZcNZeZv(w1V9P`+SOOrE#RVnoTA$3HkVav_Z>4^6 +{)(R%g`s7tMLr}I;(X_d{cvB04rMJCSnZ5JVBNR0{iL+ZZHnn(APZ{NoSHT>j8u-@E9=VmUcF#$L7)2 +r!pa^cdSk&%?9`-9}-y5J@`U*%{VKZfSTxr7F*^b3C`F4!8?m$j+2u8itGpo@PuIwF6BP^xV$(NEmnv3)-VJ(y#C2L-{LXY`#c)P@;l$+M?kGHO~G#ILADQ2lu +4b_j)x0A~6Ty9Ue#q$asg`P}j{4Mpwr5?&45V$6VLc44rWb&7)+C)M#o^Wf;tfPX4g6fq=726@cztg> +-V&&1E?kH>Cta^lhFHMME>;-n3E-9ZM7oE~GA}S}LKk4?M7QK_UqpQAlM|v#B2JChRg@5G@2yaHA??! +b`A5XJ=*VR~31N4V2fH#LUfPSzAF}~amV(#ANb(Xo_?mJ1Bx2!Q?=5pdq87d>;IG*<=4*FV7A9nb8Zqm?bNjLUjIj5zvubB=_uuj=yCVuX`@$Cvfq^{#9X37?@Q+ +LCAmi0Na5=pPqz-2A?JoWyU-3ia&P$MB~Go#)S7snDMRHq!jZ==ikGL|u5Y5-jKK_|+VuB8ow~=@9J=6G8w9VMdL(eGbrRUXll`}k`VV?a>!_M=KBA==$x;}LD{*l6~=%I|l6htIN=?c${^5V{jmOrAP^FQ$bqH#`nD58-u$G>e)&D6=COp4a%9I4Pq_A8 +ukt)$S2@6}q|=^VFrW$L2v +ic9IDzsw#y^`-XcdV~D%k!=^;jm%tgJQ!?@60`07vdU2;+Ns;gTg=wSLSrKF2H3d@W7!$36NSu&ejFF +B85{BcL^eK!Mdhk_cUP$#Ci_?R_@6~2AfHmkg~2v4de2HNP{OpS^kC`3&f+*=O<5SWUO=|A&dk_i%Tv3DPxU7DP*i^;R +1RUM5K^p+7hO@4X#G^^?|Q9dY(a{poyVi31L)!SoF@~9Uo=ES}7u}_h +nIH{LDZW`CM%Ri?yOmTV18(Q4P^-)%}w(^K;!PXG)e}{UBKdUlcWI~d6jnxxnBjx +wC`mDfLt>=zpj5?e#;7u3WM0N|}O{+ZXnryB6JPPKqFegfLKbX4<{I_LK+KgF#r?#=+3qD@MG&NLKlL!2#W5mj)CrIMOI=1(Wr}$2jp-QMxiu|UKwz*=aYP&Rs% +KNzXkeF8s<>WT{204b`u9GhB+`vO@<{>cD1Yg=lNEDm&-!A-feEy$u|9Z{;H^SfO^M8Z;Z`J&l-9Q!U +7(8CtCMHGw5&Yf7JUGTnE!fpHatS8*#nHE}s$za<9w@^Ls^?F;WU5Ka_HHC9254Doa;s9mMAd; +ZW5xM^Rgo3v1Kn47f=S<`x1T3?BVcvp`Um+H@7@P|T~OHrntHKZt@`ewXttF?M1wby +IbQ+s^{Yf-)D=A2Qg&GVJT(^5SPbDdS!m_2W1c{jbj_tebQtqAZOGk +ff1o^zOwyF;uVo!OHUbnXG_v4v!29Ni8~ClD%5=!LA}ZZ#%0lMdu4^61K0ntAvxw6CgN^$VdgGvnNMn +4AVUr%tKK8Wv^60E1QUGBgmAfvyY>q|>Vvjm^q6e@CTEXe4EiX|(zE;-dC$~!WIG)@*jt?p+1=Gdqnem-|Tnn^2wW(rB?+D$u%I!jfH>spV +^L0>rOFl6dihP#7=sOMsumipqGsQ2Mp&l@sHa-30`s}^axLeKJy&qZG_CMqMfe%WX((_vO0qj=tjza; +Zc+hH_EC9%)_fm;4oYMZ34&RYKWL~*^&Rqwfq{i*RC(NSYAWXMar$9XIcl9J$?&TnwdFX)?SpIm4uLC +55c6v|*Mi+OgyN)aDDdd-yPHDf7F`qptfee0Oo>To+8=+u}^ZyoizlH%qni_n*$a(YlXYwS_l2H4G4$ +S*l)JNPj5li3*_l4Mf05w=QxZKBz;CBBx4t@3O$;-!&RM0nC?Q+~lA%>1SR@oi%WtPe4HZ!Q1UvFDJv(@=X`2&bm9Hxp*8 +w=Wcd@{b(U^M|cfHL!mZ8=O?oA8f6=K6(p=$e9^)+9i$tp=t$l~on4NU1dCO%LVQrb%y| +pna_q5V~q}+3S<(?+4?@@M?(PybGygX5kn`ODDQMo6HiYe}!6!-gh-WFf8ESGo*&y^*d?RTuqcUe{rU +)NDPK8bZy>-`EDdgbM1LRpLQoG`>HPYQ!k`GYVFQGO>3NyFJ|%X_x4hr+kWyVm5#};7zc0-E+O-B@CT +SUEuP|>U^A2I&Oy(zrc`=zE66URB-XzT1$h=ONX`!yH5awDkJA}EO%=yCHK<2r^{1BP5gqhBYD`~>qN +akc=R>(X=n48JmOPE{893{-Om9GQ|v!2W?F2QFqpBCm=GB*k{SHgKzn1_(LUYJQ!w(^oNr;vG@Fp~;z +$ebCg3QgOd`im|?9nW-ptY@sT2muGxQ}0x`6`bwMGu!Zh06}%5(Sq7!sT({G8ZnYaM> +hWa^Uiva9Qh1jrT27`~CWsSOlyT0dEvhjc~bLxJ(u*MDYyz=l=AdX~NpYXq#1~`mX&uTi@*s`j|{dkxk&OjwVpdq&mLCdx*vya!_uK0q$W<7h!H%?6mmg)s<=a}V9JAS1U!Ki? +JVPg&?zuyMWax}Y%kP_d&1H}@BEtH<4!B)Sa3KSJ)fePspexLQKI5rG0+*CIdGRD_sk^_WoI&0%_X7T +0cL`Znf7F5p6ZpTMY*=VG*f!SUyCwTNF}jA +y8iXisC{>P-1gvxpf1|EK@79kJ}W~VZVZUh0|7{=vxbHX*87=icJ<*oj1bj$Mv6IR_iF5Z#~+c?Ds%5 +Q(qQo^#rWbTh#!=^L1Xm;pGF$Gu!y54R=szpr0uCPs)O)t(UUkY3oTAyz1XB3x2(j1y44u_Y(#0%x@f +ig(ShdkR*7t@Of&2@~sd9Km0Sqz}uArPon*{HK!ZI{UJ;5kb6v3G+_GVz#UdDtyE(x=YrtpG0n?2dla +7xxLL}8&&NAcS%WpW^6Xuj4ERGr2K*0q`PR`%cWDweo4G{IMn4(w_d-bi=neI3dTd%&P2>lev}S#biN +k@07ipanlTw2)sry_*NWoW<0E@{P5We(oRF6)wEczN$c!}A;TwOH$3A+%m=4XmGJc)l{U5x%B +6C5Q?k$ArGhP&tXd*?H62$=ae$OoPtl +~+X|It>s}Ito)q4wRb(f1vs=uuhlIq>LqO$k9HaUPOEM?q<~M-w(5WY|R_d#A!nyZm-P>J +zPpNX)$HY@iUEo7r?akRRU5-XN;Qkk3`M=`E59KKBZj^A1M*GvOJYzNXYmp8Y$8e0d$Egb-1B%1fvaF +V_+vY3NXauIU830#j>D*I0hj|cTN7Th9zO3iij&E`nTIpL8>ZHqQ=;j-W*tI(o$wmE0BaLz9ng +fp&j=xK>IZ}%xTW&U^7%p2_L<|elSuG;ojHD*cd91$eP(%@CEV*x&G1YhRw~bQ@kx8%e~}vHD@GFYzS +bL1eXYj9CI=VueqD-r`^k1I?hvXU5v8FvQGL9_c>xiYZuk{)-IH6q-BDBkQQWPQEpIZH>z>@g?fxR&X +Y_`BA<>P}Bx!E1o}yAXG(D> +)(zcR5OM-cZmoExI?j353bqp=^ZRKXqAtB4165^a4=`NnM6-q~97t6KgE#bfzGS$@<9D%ZIDU&rZ{@{h?v1=d_Ewi)SLBYw&`H*are*w)%Ii^0#lm20vc +s@GfOl2n#<;tc->yq`ccyRu+^LC1$Z?~%`$(VrcZR0n6KqhaP`OUFjrUnCK;;Ea^3!~>zmF9Lr~L6VbC?bM+PuGr0KA53TUeTL2#{%e&+j2e1E +RxMhx`7M?j^p(l-S=TcN6HDelcpcbxS<>mpTd5j#`Ra~w0sb2rh-uu4rnjJ_Rv=YYj2#Nu((x6yaQ4I +}w5mO{b1sxjXsi4GXmeD~JFf%f%ok$&U(hRgJBb>VskHN9IT-_cze=u1YqPNZxVnz!n?y0<;vUP^yS(S^0Xpub!zzqwzj +dy8*Pw^>UxoKn>tNpVz8kBCiiPjGyRwxTfhq!YpxuCTEm^(T4nTIBZoDWOW(-B)=4DfUZImag>`dEi= +cJ5U{6o9_8O%kzn~Civ4WbQ{pcU{l3seMz94WDl}!x2khZ>s^Kvx}`WLE#Pcg!=<$HO95P_M&}O2Je} +(u{+;Qp*`PPNBGECv#x;dMgKCC)^HPZTH1#UK`zv_O7Lqg%YDDl5(xm$g?PqPO=HJ_EP;afCb>dS<@S +H7lZ*w=IB*PP7Fl^eR#@wGIkgX)GyYIO|(<;vzDrx&ojVXf{I50QE^8qcIESULM+5+gP>sdopC3=GO; +iGEH&xaW{)zZQW37oy33%xg*DpqvQW-hvVtb5kFtoU4^sdOEm`RBun2+Q$i(1Le(IIqgd8R&Hr_GAQN +RoI@s6CJ)SE)cxMusm}146X5P+*dAyZb+ +VwMC5-gLq?rtx_HsR`sC8L%-!*_=@asqwOPVsKCn=vpaQC8${Q-2KvY*sm>p56JQPY&Jz92}F66RT2p-f^?~SpzWXgG+mFngFs(BivQRBgnD(1PviZlN`SDpz=XuQb?>+^clf%igpMtK4`JQ +9PGnqjUhT@5^9^gi`7}GMGIE{a`^*Wo)JJApYa$bW%S*bB6aks(W^rC1R6JHpjwn3Z%}`M+i5Ou6Nqj +>fvL-ZJ^DqsxrtULrh@iQGXYUJ~o18eqS{l)tz5zd|3=CDrv`o=%IS<%Twnma(0uq}GpaOOq3aYO%p} +=TrCe91i1r$_w;?_0iFHsKnbeu!m=AX_XX-c6S+AejR!)tU`<}j6V7_F1}a`3O>k@B0IW7Lt>93@_!q +X^VCop$t*UU|OIy5_=Gr0d-0-=e8CI}+;o^^DYxXCVbo9asJQ+l1Ytj*vpIdW~YyxC;}A3oU##NA3Tt +V1{Uld2d*2Hbqisi)TL=H1s!uLHvFZ!52@2-kh*cjd}PQ-eH9YlFFCVORINy^=eH1)l{R&8e{TY6d6W +8fQ7wbZ6ICrPCUj`sEU$CKRdtT`(XDJW%LT)AeOm;bm}(kI8Rd8F&v%u0F{areXx;}VBzO+)R?~4h?0 +5cqMXFP=Y5p+7l5Iuj9-u~N_o+*DHB0;d}K&-g`3J(vYv*@kqDx^cn782i&Ad5gO8|9=-MK6cb>T2$I^4R` +|0kyGeqj{{8+duJ6i)zy8Xv>cdEf#%88Zl@{1Idclgb6=~1)Iz;)TbAtOZAT<}p8%jgMW1^6+Cnic)8&e1 +Qp+Pe_y_$nrahGM+3om!n(-t4ASS!MC27n-Mtl{j(MtU-*487;odbMrW*j-!Sc<34zkNE7!%1RU*Gxt +BC@RMPIt=dOF<^uTIsMU1jwcNXNd;>IpGCucvEwszbb;4KLeB_nC5fGbu2tIIp_?PV(`uq$2i;{f6!P +ylK|FskF0osd}m&Wj+Xa3aUqVW2||TNu}W>etSq6R<)6Psb^KKPo_6kT)b7{9v_a_E=31JLxVq97QD<{XfA>K89v6hJxu1}E&7o +b1#r(8B%>v5A?Ok-`7-pn;R`-^SZeQ@Jomd&m$v&<`jDi-;HjTyU~1h|WDjS3wYpf +2qHT8Qk$KF%qfd>E +~AG#5D|vTG5p-V?+&siT0<*OF4b-_xPnre)d%pM>b+v-C}=R%&>8Uk^59b=C&Ob6Qe#X5gpgn;UC;11 +zoY3uQ#l6F%kGd>0%nVj8ujk^_b^ANBMK8SAl^8Twhw(*zqZ~VD=vOLc;#KRJ7)cJnA+FxaU7L$QEKG +ijp&FLpbtl<(03#8_8e(Osk;K)qPkNn#($VgH*F83>j%{Xu5k5$y9d8F+#9j;I|9Kw&vM +-&*OK53k6GOd>nU{Bqus6JpIda+zT0Ph+98*2_P&!>Dxk1dL&I2G9T{*zHIItmd)>+}QDjxB%P_?g@hQUgh*D$X`;q1wKONu +bDAK5-u!?b&mm|;dt2OQA7-*uRtm?p%M#kFH^){OiXI#DBr^nYZmMHAyRLknl72N&?Pix=2LhzCeSEQqeXK2_kB?ztdt`PG+`gStkt8kYJ&f?fw3tGA +autcJi}j59`Ovfp7UZ_g<=fKKuOmr6S4vuJ^Sk#gUx}BT(Z1nQi3YO6I8noGZr02<#|6)IT_5C%%0CE@Xl~zkg1gSt!)MuKAZ{V8SUtI;xo%A<*d +UuqkMqRC=KgKCr|45LJykON;<*lIVW{H-Q@Ard#T$=%qrOWjV?RV2VEm0w_7ug0wJt0}H3@2 +x4W!nhWr{MNxQ!EpL$5cAAFKk(})x+*vNJd{sM+SB>e@jZ}vbnAuZbF=4Tx+bu*l}=&TD^VNyHW{tHI +FE9wYULGzhm>#c25aAQ8BvnGXh`Bm9rt#(K$Mt>Z!{Xb0xHAJJ$gzsJ-4 +U;g`OqzK@Q=R%g}E6)ewPL9Ho*8R_7tVHOd8wseAN|+bM^Upa4l}9salAv&z4m#1AmD(t@h8K) +o9ANc~E-Rsh+7Bkfrv28`(N5UhC8QPQ3sfx9>*Bo$t6V)MTw^=Czf)^{YJh6#1UP9%;Ma5MYpkKlS2q +5IgiAEUSrfjb?*${IMmYMQ>j_Gvu*#@2*6KuLy3ATV`g}1B@n^Nq`N8kPj$ST$cm6yrOlJf21Uo$4yc8e8- +obM;1#{vA;1)cyR-Pc;qy>OSu2R?z8aN=ryXO@pE0Ue5GL*Am(`;Q9WJ@{L^F;dO$Nd99Rip|Py${O_ +2pdboGKg_hU!^|4A_9&qlXn#lpF`wbghmq?Pewl$Tk)LKIYeQyx;hV+<-1Pql{+K0|NlvN#H5BAmTff +fvJDk3fQm|?rz#Ju5P-phP(R}<06^R|-tqNsJCa2EM@hQE!vvqRRJZk?2QH%m=}^fgO3PR4Bd8f)O&A +UMNd!Fs-=hq$&09f&fZNSlaKv%YbPU}iXB&VmIbdG<6t@`JxvEx+qZ`S2E1JxIX@ieOE@`$7Bm;5$2= +M#@6@*=elN&tsXcte7I^jzehfXsTQh*xTI~RVjwgC;jJ=1NOb8|>AA-nWzun#Uc&Bbj@UQl +J;x=7}`bAXg;I$~174jYaJ%sBlx{B^AinbzAaC+&x0MrOux=KVtcvr#rwlBl%Y=j7;mY@mHC+|fibE>h-|~iXA15{RDjIjt*VWS$J^?oGtmQXzok4 +FQQ1MBTNug^N4%5DsBmJ=$n&MjHRO`8qtn|@aRow%XdwmHhPb`*|U}+3#8leE4`JU?Hj52!V*`GiIdv*G0i)8PwiE^w{La{=M%BB@wXJ +TZh|0CO1$1~IpFVtJNMUyga~YZJ@!S;`vNS&Gh^)W&@+cMtH!!lu)&_C{&`5#+B+DYsO;=MLc~SiA<) +x#MxQ16eI&!9)ZVs9vsZsL6I$mM&`j{skkyO3NGO34VSV_@ndRkJ1;AKt6qm6FUw$T!qosV!OdDwi~D +^1oVpr*VglPe!vx25voenyh~#g{i4=mz?%yAEGysYnU%)xJAY=9*m*F~h3AGU`b1*bZb{jxGJdzt(o7 +m5yO7XVB=0(?oqe_r{lU`(T8FDV0VCZ}?rD2Vxo;fL;d`FIltdSfU#++iqh>*W5ENKIpL|!g7%J^;_7 +|`^SJ!podm;M+dEbd743#lFm~WS%DVR<~*@rNbXG)N@#=>=yrHIY$tQt#-_R&hH@paLRZgV%Gtgm9_5 +af6iS5x<%>tXa)52!J3cA*`-+YME5-2IBYdw+?%KW~@kqmhA$c9D5Q<)fD_r)-mubxx5tY!B147w{|S +#UY^@)wP5LnzmbXc7y9Z@ +IgWo2-d#$^N(wy&qo3z-mqoaRkchh~+NAi95NjHvC&+eA@N$-v_RPt&@+p{P5ebO^Jx=;F(d8*%i(r* +cy?>^~apYsu(-zWWgldAgelcouh|9#Q{;y&pLCE&6PrFSY^i|m|=SB)T{X`I^bh0^!0YX3s%Hy2)Up> +%ws)(M^FC7B*oK-aGK`g?%f@3ivkr1{4^t8yJro-UPsnT}5ww(s?I-2Q8o%XNSWW$vyl#rPN|QUb44h +cQd}Y@%;EG~8MsZ&5#A*ScI=eG%yBx2_%Su%E#e{A?9DI?qBr8qn3$fA;v-)%4 +Znz=msPQe)ddXE3hF$;uq&(xV)nZ!hjnKK9-3=XE)UxRAPXM|)9~vE$psYKOnNQ3uwf1T$7^*i`4iU; +F9DwM!aLnziRe#YVb@u4F}?%}84mo){~?xUbu*%DEe_q_PcHw=8U@q$*Y>G1n*zL5_-*$&9qfgvrg1t +eW7@Z}(}Dp*;|xMJ7y9Pq$O9Vy7sjZ5pj!y%%=KeZGhCLvFh)NK-bRZuMaQg{Qoqj>Eeod`QB-N?0%9 ++Y%}gUX-v)e*uR}m?`0W37rzIlkiaqUy$$(2|twZ3klf(0iz@wBH?us9vm#-OA`K7!c7{K;kHV+L&Ci +hHb{6v!fz$)6fa;;3CBn{Q^Lg(u8{E868=fT9TGN3s7M$!P(Y)EDH2*GoGoF#gvAoBl5mrR4@vl>gu5 +kdknlqZKbP=Z3E3c7UJ~|~aJYnN65b$Tj)b>MxJkl4Nmwi4aS1<{kV*dcl(4^qNfM5eFiXN;NLVc4S_ +watP +*bOX$rL(Eb%w3(#&5D_e6(U7DGvamPkJ2^r@R2Nx!vaHOFnQo;ak +iW!B&hAl>4O|Ln|)wFXTSCh*!vNm*Gm_9tLh`_$gdIe)G7$U8ZS7YHp67{EbMrgr`b=4*V7QVhD`G;g +i90DX=bqYbm!=$XkI|&`$Xx>YO|!H(V)9336`a_=3xzwFr4NA_WKH=E?FE*oy!~ +ZncsUK1=*K>R4Y*t+ZcJm;CWxUZ036!kEV?%= +fE19gP!h@m>29_?F0Cu#mSzC&!-e>mzRMFdYs5KU04$Pbr`CKetUTH2kgYlz3gj>&Xz_-!A536ZHgkA +J0WC>A1Zn`IX<#rMBuu*~=3{zkZ~R`^L#4f3+O?QT(jF!4n2&LLw6r^#fwboaP_?uj_o;FYrMb=kbjbOMe@GK=iEciI(UZyhzvVtWnX;c`zVHq{+bxoI8!v}Ev}N&3 +LfD0T4%b>f0uRk1yc^o?JeHI(dKEO2;HleXASItSD4zn{ab +@k5J5KSpGgU5!&C)#{0n55HmQP{`Op+FOja6o`zq;r{U4!qh49BTe9T3QeTD2$1~Ju!($}Fay)|nw#5 +AWL}AHBbuq~*W47nnmn^jVD#Q&XOE0re&2yEIb%x#H(mYJ=Meb4%o9Qm%_%hwayx>g^XCak=E!UanD` +)#cr#si_V_0TxF}EUJN{jFlZo*0U2p8fnCtQSw;!_+pBiH42=I3%elbnUp1imbGJ4<~w@XMZGZ1;1R= +@vFyNilbv;g&GhMR+Ja#i4K%hWwfTqYDTO3hor54-E^Ci0o{Litf_2Tg+A6V|((#qYUt_=i1L6k` +8a(9cYZ8VI8=janV&tgg(POSn8GBvoxbfFdNHa}LH(Mr6wq|5b$+F!rHGA6h88c_io-_Bxo94~G`4nSWYb$8`WNss|5|InfA#OD@=t%=@aJ#y_xssdmi +fNi{&Vx&n(wwQ{Bk+pQ@&E4!-`zp@zdp-aPu&+wUBE_xOA7 +fAHZ)jUS)*_za{j{i7k{{<{+9=YjywFHH6Z*?=l_2?{eSy_+_C@ +v3jWjg%5cB-&>Qw3Kl`uS*y)N;y`TNAHuk&Q*f+MZ-_yo^ZyWo4ZR~V~sK5QWHg@`qPHW;?yz~)uvR( +W3FsHCMzhv1ASFWoN^Dwy2Dz!VOF1*E_=gKH9C~+>~9&;of_-Eyox=eYl!lm}?5{KL2a~Ey`9kT&*?j +rldk}?r<7G};Ctm0?~csN!US@@%82w!#3F38owUhFO^g0U0*>u8-G*ok!thzhb;EWz}QPQv<&0vc1Y= +Z5InwMj{HvO_Rj8F_Z~Ig4}fU_tCC!=pzC7Q*y;J#7)_S%}7yPUYoymai#C0)cmy-?_HDTW~N^sH&<$ +K_?}ZXP2)-=plnsvgec|J`%~!ChdmCA?54J5sM+2#ful~^&w>r$1;cG3jFlNiMn78|p2-Nku}?G$gJz?W +NCWY0IcN{l5Ad$G}8X3ulGau*h9E6G{KWFh|o6sR$`*qG};zk!s1270#4W!{-t)8%g>vYy7G~&e7B>hFi(Wge8dBPFt6hP@ +V7gi9sAE}XSXy@?gF~Fo7lg|IVz6H`V +obP?MuIm#yekb5Uo=T$J<~j;6!(+GjU;IaiYC=>0m8#oBGmPpC~m1EOeF>FR>T9wD7Iz*quuXi*tG5qR0 +?q*lq3-m)(f6Z-b^iqQ`-^m)HFTe6Qchu4nUE>oe0Y8G(OUp=&W!ky2N#)6egzTaTvv8bqI86l?F?U8k5ypDO&LH%GT$&s>#Zf3qsL6?Y_#)nX<&bq(p9Q^p~xxzptGc+i_!T`V8}oyZ`dL +8C3_8CO)4rc1Of2%U@}Nn||GV8t+Qy&qg6FpN}z-2ENkyrG-lzMfL?nvfrGaUs&pzzidqMa7X?^_A?& +B9o<#Wy5>i)uF2u7>vTgyWNoodQ^=kpW3MLl840cVdQ-;g0BDVXR +laf%Qu6%z8Ovo3Co@*3hLkisPj8x}v}B*PpSlk0p*CVXTKMlJx)$J!Xcp9?7sfV`_U0iyYl|uYtw6I< +xNik)SVv8AgRUJ2eJ11YmHD&BGW-@xy{x7~;Ukp$pfsaO7OqEsS+bHn46ZqnbN6Ml^)g>K!3DowB1Tt +-VOY1(+X-wDhdU^q2;Xw(Pol||hs#^xq7woryIfcXwTyWm477B;ezqdmI!k7n%f=+^W?BZFC +Fa(5P~>&7}^ex!x_+Zgmi*D|*9V*zy$0W3ln&2+{HX3URf#+g`mqHK-MUd=rlV;ioj?dH(+iV$T!6J? +Ha>NUN!+@lSs7s!tR`7umyrRVDjjCH|$;(kd_kKv)Lcm7`1J9!W5t$Ueuu8nYn%W`fEmU#>bU?IBud3 +@g+qh~QLy!jz42KkQ}8C@HFrJJveQztRD9PjfQAE=xrw&wpl{C=1u%Nj7cdl>2;>R@st>ps0}b97^C8 +=)}wSQ+b^!Prr)9R12cE4zYB#vFLRmSL)3eiCn&6EaTMAk^;=)^()87bgN!&#_spai+k$Hp?##Z#RB* +Wn@fa*M{g?jjr`J%xi97Y}kqR^SOU2V;AsdpAfLeuy7WaKaIuB%w}=PQ(2rYlZg@(Wk5>QdnAUkKCb6 +jpZsT8pX6s)AKf+0ZH#U()J8fYa>A3s5IdUiq92GuKN>f(d$TAFgD?LLB`8;5*{adhE1dOm#iGr0N1M5d_ +0n}=A`Wm-{RJM>TUpz}ax^@LU05f%Tk-Tr3TJ(jA7_1aTiE5johX-vAg!*Tf6bTu>vYgB>k8Gi&)1^Q +t3V$kaXtn6WZ1vbXzhmn3vHm=%t-WO5v<$2=-;Sa&kRGm4(07y^y9#v4>;3TX0_4Lome!|j2?M;K0>M +)OThc*PyEwBzm2%v5VzZOq|rNq^#;z~aPJNG-f-_dvPWb0hL~EOKJsG#Zk|4`b9!Fq1TCNe{cew$A)u +ubwU1iSSK|e)ScHuo`Qv@mRh!U`HnsLqy7({_pWK7Rf8Db=w(+WlZna$;(K%7syxvdO>b)3?G#pQDLE +nHkLgrJeM;h$W2Wj3L-Wb{tQXA|D$_dO4NTPDHlHagMOJ7$P)T?OJ!zk7@z4N_c&HBbp4MDZq7)ap+F +QP$z6f+zNckr=O*75GB<6TE}cD9X|{AU-DP+M!n$3%-G`vEXYg_zE66 +L>5ZE3*W89D9tEp#fSG^y;etOnrrU89GXV%5ZN0uB +P52)SBc}5)O8F8rJ=o3VlIx)B5^%5`g+YR}pFCTSGoQP~)sE&owT&oN5HVnK494S)JwP{bhUtO!!w^qLCB08}M&=4_Qlx~laL +CmPDW<4CSybUCw?-FjII-i?fn{!>(jvJ{R_ecm~JzdGVp2?$hJ#|SsZT{d0MV!77tgkDY_05k$UG2>J +w$4qndt4jI2DvV>LHXaaLCF``Al$g=-PXZv>({Kq=RfDx1^my +sbpijco?Gv$^FJP5qnm7ZrHdtS7FNJ>SVGVYJ8i^e=eicpVXI*C->uCoStxdJ*<#%!r`?`axX?*o|F^ +Pqrya{_ZR?k@eH^pf>BLf9OHCguy|3CU1C~-ki~WMfORBMfMwVmr&Rz0;kc=V7qxM479n6T +!j;tyX-Sd<`m}J(--GD*{@_Cec`YYx|MLxaOGRUKiZqr2grSFzCnu83y}Naru=*-r{?;=tdiV($v0p~ +B@X*#Twrpt)66Wl5bYBAbe3LJQmWaeSf<}#$Ql7~f4tw4lX3@=XIq{!?{9eaHpr{w +}VrDa}S#wO;89aKQRt^d5@PCa?bPyX$3X5H!=bZqnJ=Z~H&e)sWY@#g`<`5ikqC{sdcTmg?ZR(VX-6F +!|I$drDF?b%^W^g_WqEc7l?n1fiL+;jOC5R#1ZI?)?F-$D7TKNIU^7*nK|#}9MY +9W$WNy){;$?-!`KbHkmQ_-6tJ^WjU7SZ4EX6L`@QEd#^jwF_?W9`hvX{BoKynjNEoJO$W+6VLI}2SXl +%g4S9v$Tum}SphYQMtW8<0^9r`)2#+uM4Yit}eU3X9WA+@SLd;PvP85%9gp?z#e@$-mx!%QxvJ6_tXm +vvZ5wb~cY}Gc?hCakAv#IY^mTBK96H=gWHl^lVg`v+d4OZhMP +|5iJ9~4ac8N;aM@6P3ahmucsJOEjBVopsYY;VLor8XtyjeFe}}LWOyAy&h`XO2VI;1wu0yvbyBd4T_C +LFNwtl+$e(N)``9AhFIieQh6P@9Ngwmxp&bv_j@v1gA7^IgItyu|bPhYtQ=e`Zl_OV#%b!{xN@rR?>(@RC +$Ji)Qdn8SB*p-iVzINmz9}S~@yVq|v9w|7gYR5!bT8@xMYHPpj)Ib#b$%oR>{bZ=i!w39isQ*0wdH(b +K*u(c=SZjLH?;>Av?_5UmgA*jsNe)|BuH1|MekNX8!iPxKQ|aeE98umhOM$^Z$DtX!HCZuM*AI%K3gk5Fg$i#RMW}?eew#r2pd|3Hb110@{ykG)Fa9x^E$@A>S1o0h6&HK}QNqxm%jX2K(m +hH-y@X85Ka=^EP?7MMg!K~cknkZ1*GX6=VUC1zCCrx4Dq)(0DH0}0I7C9Dgs~DvNvLPqx>?exit+cDg +!K~ckWjLqNvB-4}dziZwVKmPK4ga2**KgEm2i5kiKOK`Ux%^BzRR0cR3ImuqRM3+J7(Ps-6#=*#6{oQW!UOh7#F#*M0LLUTmK>{NDRlMU2*xVmz7Fv55d!B{z?(4T$?v6 +O^8w$+`zYLx0p2%C(7y@r*U6}-aNi6VI9lXA2yoSC!NYREPw~dWtN^aLma%a#R|5X}T0!S#z!ND*i+B +$B@pYIp!F>8Uu3L-aB=}9L2>&hMoN;MxSD8gxNq{r)HUno4U>V*P$}iv=ycc2K2zU=(b`^L97;8cK!c6e@7Qx#cf +DcZBAKV`TJUL0$J;0F3sM~PY0}h-lXqXGQJ_B*#{wUx(8G@dZfT5YP+yU$GZbF#7fYvDjC&8jAB0Rz0 +XQ8j6bOHO>gnK;TB57U>=)3`WK^PaH=LUg)1K{2p1Wtk%rJ3N!slr?fsLMt_&>g%2teMVO7R(y~-<&S ++90j~)hAL|Q3;zr +wp2X*B@$$;Vs~c#MF5v7?>BTnqT80)&V8Nx(~sWdFICu>mEFx!_Lskxp%ml|($@&S{`%cDIz}*Nq>z9IWa{)j2r6|)zz%A=USrc4PjdqGKIe@>Z7HMq;e79QQd>`; +zX(qVWBk&L$zd_(h19a4gwoGus-J;)1101uF#ubzc;Lv+SpG@#0yb;K|0$6Y_+91q}0lm_E8t}IJgxL +jn@jk&Tf{)X^Y%tUPY}s3oHp~S3KY)4(b3EYe2Sh!o1l;?8puZ8Y+rya4BMd>?!@}JG82bpuMYt0*K8 +iRn6MXDZ!IQ0k%N`TvGQb^=$$kuQ?N;O$;VS`q{t@*V<=YGJ_CEnH%qsvd;7x=1BH-tL1>G>82AumhF +?JAKxJ|g{1HQ6Nq}2$x@hOZQaDNDJ(bFh*n9Bi2KLZ+IP61r?j2veH4?iR7*ipd3X9eGi08@9NKEZt) +;Ne}O?2ZC%uf-S%_Z@(-^{BTn6Fi6aBFrs-Qw|9HS%7~&AjeZOzlJ(V^#O3~>*z0FHUn0_F8YTJfNx6 +kQNT5Ci273rIQ$U$ZkUq*A3Y>+63l1-op5&nt~df-!%T3-o9O4MUjn@Frl{K&0k^+{GDCaW0XXWInCD +mlxU|svlwOrZA0+xf +?~FQwI19-mNe{3;1@Uz)5l>BnPq)eIvo)(oB%#6xN}P2riWF1XoITg7-^zg3n7c!K2bl@M~!%7hlF+DBs%=ovpa{5NNPJB-D3m3w~o(e0cb=M9+iic)ZPW}oY7dJt-L5mg)SLO8WFiF=IkB&<|THA3 +Kdq`Kh(^KYG#QyH;%4hiS;cW8c$! +yM?Ijpd-kh$G%QBdXO_%COhH*aQ7Jn;nExpOD0t*vG6yz>sbbmT7Qya5SRTO)`{# +I1+aFE5ryTwJzn#R>}WrF!tAqxie~+3L{|4n +`?5p&7K&d`a8cM-Q}~023JMPGm-dUozX(Cz9i2M-|&1;iiyvA?Y8`lZ+tXd( +L#RU@Ob4jw{?4a6?}#XtUTM2ZN&?Jch#dbIzO2YFa};eOgQPHjPS{>>Wnzww8e2U{*4DzD|?52@-8vv +##y{B7FOqH=G}e?p-MZwk-#mLC(|96zT`cr*Pv`3P@zv2X3_#=!Ckr~-Zc-ax#%awa;O4qlxM= +O1rE2k&aUYB|&44a6IQ*MPULO7%QS*7cvN=gG;*%x1H(88c?E+i$=9vO2zT<3{$UKmCb4`|Pv4j=%l( ++w9b-Q@(ocpyL6UV>z~M522E{!*PfDe8DcZde@h`3T`c7t5HM+yHs`cF7E==gj-jC@!f*wVOag(sd=j +UeZk5Dr)JHXwW}a2`Okn0Zq2-Hs;WM>KwVWmc-OpH3)pJ)54)b5Mfi5!Mfg{%>X~!rwlQ9d@4&pRZ(l +Vkq_4^X<;#BL)xw0x*C6;rJWAty(s#@xJOc*~WC;lgEGa38jUPXrnM@{@k&(f9H*MN9HhcDLcH@mVvY +T(dnXSs7#1<`D#BRCe7Pe%`66SO|dH=C;=qt5R9TA`9DaYdYIhVq*6@)7Xn^u48{G& +tSh>vyg4y>|p!up28aba4Q?~vdV_;R@sPsDx0uZWusqJ*|;}UHve^%r5{zPNK`oy^U>F?_tlWPqCxwVXZ%9-4MS&;w +K^gc*M^_{2LK}5#pDr(QG~9Z$bROB7QC6A3^++e(?t(ej4K6g!nGRzYFpIi1@n^|1jboL;MdB{}aS-L +i{fg|Eyp9#6ehJm{8ZTHs45V)w>uwgmp^u9yG!u(M&xyf~g-&W9rG8I{(8iJ0P+8Z_;rYX1o2P##mD3$8%^1P$;QS|#1BJ!^gqp*#HhwDOuZiQ=Og~@i2ob +Je;)CV`o(9RSvKpAHrtPFWC>`tW7r|o{AQNH)aaX-I>N!!X?HU9wk=G(=V_)sRnOGJANj>MBK{b}&qD +l#h+l^IcOm|xi2n@Yzl`|%5g%#Z^FHFALi}c5{Gq@w1sDo};g`Vh7%=PwhL3@vIa+03k5JjS(^Pi$Hk +F;bM`h=qQrY*1RbTu##2<$E6A*to;^!g$Qp8_}_`gN`#}WTo#NUnhhYAbuR;_e1sN3twd(pGPP$?Tx|CR!-oqD@%{T52TZ2;z@M3!F(Gq7@2)*!;g0}AWPm{n5PyP+BBy6Ai0j +(5hw$e##`iaZ$P6F;1u@;acAYYw2T0%n`WY#Hx|w`UfEVfAx@ISxn$h= +SgT>TQH6SNd9sBBT{W(vCWcc{tFgNG)+zI91=VR#`Fb*J;U6Z{(^ +K0GBq_aAS8|a(On^6%nETgY$J~%!ghosxYX3F!xz+8w?at-O*@QOpxgVdWX!E)0) +2H+K$SV6}-`wMgvg_HpLJQmMOlQxowXki!Tg9%e$D9KFob?@*%|0RK3}t}7{`Iff{rBI`9(dpZ_VB|G +vqv9&l+P8OdFB~DAAI437x-M^t=cEp{g`iT!(3tS-o5ONH{RfLgOevdVkb_VU?)$WWL~eAoj!ee|4Us>1prTvu +8hoJ-8zGp0NWH6BFr<$ezZYJ!5 +VM6U#NJ_v(Fo8vGO|yveglUjhKCL5BOQ#!A=f18^$|l5=jtw;)4~3}VtmKD_S-M9E_%u}qyLn0K!hkt~BRMs>wc +NsKj&;UTH+J5u7b?u3hS9pM9ocJXPO*`)%5wQa}3WBNgj!mHd$h731s& +ilQ_juOA;da^%6fy1La9Cr%v2L^+YjNOvYgI*1l3*D(2#SrcI)QIXyhcFB?@qgq;8=vJ?x{{a0x!0$t +7gUN*B+H0>JMD&3!PXDW~zN+rrxl;vw>W&>dINhgCol-yj^i#E=p+P--_%IJgaWF?9_mhb8`=_3IYUT +Fr+gD=BW~r#CnCfsi%y->&S2pI=L>q~-By{iIeN=UI^-Uv3jwIg2n$6}Rg!dn%|C?{VQ8AafXTX2~qr +tW)?lGXOW4hr~^b13=r}FR6taeHUz|m009C?2nh*6Ap}SZNDQ +DTZ%I->K$5D6yp*?sf{I2_j4wo5$B2!f0-_*$_V3g^;ijlOpy@SVJZqiYdfj{W|LphPw<-c3E?ls8od +t$IKc}ys=Y7@T+b@UyGiT0-x*;R~k&%%%BF9YVKXBlHY~8w5l;@%`v(aJpAAaoLzu!z~-?wj{(Uq#ID +)Sf&*Z|}WJ%LBiw&{D3q)$X*-VwQDzexMNBDd}qX}3${p*KZd_*&%jnKK7fSN_x>i&a4dC=)Ck=qqR+D9U1&Wn +tEPb6YTO>3f2^#AA&~W0EX@#h( +s%`T@*t{m7Dac0R71aMOtrj?#FB-{pvHs+JEF<{gE((d1B?9eT5gu6|!M*pyS9Ha*tlvBYMH;|E0+Ni +lL9n|J@%2Fm$UF>3kr7AwqTGe_j=7v-9%lS6e*)h8u3^sC+0y&#*0_a3Je-$rt<2`}hUU!_K%z{8y@@ +VEA05w_>SLET +pi9C2j8mvL`q-tJ4g>{#!A$+7hs>I;Mr9KR +i6VM*h{u8>hvI@A0nkf?`;u7>XwZ +=+8VMGW0veP%Sdx>wrFEllpvmQJ=wJ^%)FSpRq}8wwlZQ985oYhJVpGt7d}1&R`s&bi`s|Y@XU#)=%y +tzQ?-C+8o7@-AzuPJSHFg8o)3_F+B2(VsPnmA4i|#4hC#eF_bHYHHu-|;&F27#J7zyHs31FROLye)n_NhxM +@dd`mq;Q{=0PPawEL36McnlXoGf(1>29kC$GV7;;Zm&j2+V>aNF}I6UOztXWiXm8^pH-VQKQ&GY# +&(xk$vtGos2(zPL{FKMHAoJBRB14{V~loAj8PZ;YVVEwD?NqK4P6|e3A)h-=(aq;#=GNV{4W@h|JP(;F +k;vzfUJgHAle*5h=seC?BcF(v|woOwEstYeE2EU`v*rejDXjz^SEsN4)WI +Js;27gC2SeK*=vP@V@~?8850)pMctXm`$_$>y;ILRYxB9)8usu+wuD+X+m)#oC$Ny{=~WM +O)&Jfj#26~mK?;jb!3(~^1`La|A4t{CH`??T&u=(qCUwQJWKp#l14&6-t%As9z}SbqEMynMNPiR>-tA +v(M8VqjvYKSKs(W&4ZE7bUCkFO@iK%bv-^f_qM8~;E*yzkYk7krSlYuB3Ci&$p%?Ael?on3>WJ{=kklP4-RtzEROkLxT?9;q?^LDH4l$&n4Nrns=BCA%d68v&zW~NM-FhL3m3XEQV{`u#|?hp%w!UE +m+25c3v5b-Ga00Vt~cXgJW|5fu1Z+4A7+Zbc9qtEkH7aF2}+qP}jX{_^SOMlz8Z99z|IWld;h!NkZ9W +{I?DJhW$9(X|d_wO%HJ@u44_uO-`V8H@I5BWXxf&pKEpRhQ{VZeYrMGoI*(1*(u7elo13GQ=S^41B=e0rl{PyM*#DPm2BRriucV4h~@nTa4VQoj3B`+^e;^X7x`s=T +kn{U2ZG`2RhKnJ?vo?x&u7z-1bve(A89#}q1w*56${c|A3z(22>(nB^sGfF;vYm=NgefpsCyV3QBMT- +_met!N>^XJc(>C>m59yDl>q@<*neb>Cw$PyUP3vfakID)ZIgTN0X_xNaZg?a>eB(D)8!8@>o7z+QF!| +c)J)_*uzb+;%zJ^hQvAAkG;bPgCWz)W=7Vz}j&TV&$IiG~mO3|rf=IG`8$ZOxb5fFt^Vj-U_0`i{?b= +N{zGp3AJipLTAK7FgOVpcr4Z5f~XJus>{q51Chs4IlO1pOL3?F!(bAoY%k}azn_J|x +|50ESDF}$<&4W)^8TVwkdhJM9e(5X|WaNWav^m+UC?FIw;L)}pI!(d2EOq9NT`x@C=xpJkUpS@8*LK +iGfO$3mW?ZyccoextK0rzVz2O-(hnRdwC?H*5{^ZRHR>Kri75asm&q$?kZF$6Oa4!4Leo;^h}cu$#>q +NqY_bZdrB9uv@mF4P3|&x-fI*Oi`O<{Iq*Oo1L74oxv6{kRNg)CbWZV#flZw7>lmC=9@hHlP4fk_}UOPeM3LIu`vXF9poNaV$ac`McmAP`TJgrez*K%BZ7TwFcyx~*ar_DEDI +MdH1h4z+vnC*bi(S{>Y7kz1_5FlPp=X#K^PNVJ +5h3ZJxM^=g5rhAE?bNZQ8WyKSFyA{cgYLj$z!fO?^CJ78Mnl^TEaE!3Mp^7Pb>CmUfL#S6`vLUqU~$s +D2swSO1-F^RN1_*qW#9-@zMRgAcYR2hXrY^b+Vnfd$%ijVrM;_0sRI|JYorKAsTY6aVm>z8n+T;`sXO +uN%9|9-yztJM^OmDvv9#BJGBLcl}fCL{0q%4ERps7wjK$fIq@_vd83{>^*$2^7@=BugRfhCoOs +^TTrR{+oOs0Ji8&cK>Z7ZCZ0a1FI7iP_IyMQqzH8k)=eX-?&_C5VO5oVUIW~2U&7I?Q&astqjB$=rou +dSfO*q#6-cFe33I_N0Ga~n3do +e^{YyT>J`T>JC0BH2epdL0V%*r)~5Q@H&#k-_hX9Qx7FQXdUq{Zq4M&F<9PZoKOBS?rm{oTt^^eyeur +?`oGm(fGVXW7{zr1M?k*+jNF&ym4aGSAjkmwNUzeU7cF)2S16t`-4c#9<%=U^{oG2YQw#{=2>??-QH_ +r>*r3)e?jL@YI6^&-F$@$(=(;tNS}-T3N>+Ru^*lisr<1~z1~*X>FLv_CoWj9ARZcsoyZ^XkJxp55w@ +CGmN7v#+5hM?0efPZ&!S_6{C-|@9UwVZAKoe0=+hRJjf$GDtevt2?xGqPTgnM +$B%2ipEhO6lrippw~c2x5?lLndze}x^<3(R?)B-t(mx&$B#*I9kA_|!JtpMw*6jiNVAm)9ara7t`|r@ +gsBrod-t0g<_SA`EGVO=jG$4=hLGl>m^h`TA@)*>oGwW-fl(=~D;&?Y5tc{#=WYBM;rb>T-n((HD=?( +S_kNql=p|o^+FVJg4C+K&&*C(!+G-*=W)TvX)xO=bAK;4ABU{GJD*HrHG`{*-K4@Dm7G11$Y;q;d#4- +d@erY9R3j67Ce?0pBXAKVKL?g7L9-7{lMzl&ZGHBD;cRvxK~(_f)CVR~H7`cp>SZPvQSC6C?SuFZd9v +e~m|CoWsIEFN7X-zPo?H?*(^9N+gQo4z6aD(b51oZ9vZr@w$s&>x{!n3vSEW)B!{8%^)lE&u4&V~;(S +ruKh~?GKO_klV2**dF%cRQ1R5<}>|FpMd&wg;QT&>Cy@MOmmz**;I!HhLJ~Icd*XSey*+mHa|lTiTT} +q7I*KCeQKy@bnsQb>9NqO%U2q5onE7BTw?!Nf6kmaiO)a(d_27&;&JG(d9}T75IWck>=ExV=->JtyQm +YPmhrpq3aU#TwE)Y5}{VU{=WV(Hqcm*Vy&r~)L?U7g+ur*{W59%k*r=+ +BeQd!Q_7>O@-lk+!}Q?+8RbnoYl9zFVXdOXzVAAa~@ffw}G)Gycc3)punhxQ&3dsJWbtM2FHdaV_jpC +9)GzOvEKXk}~sT!Yw-{ic?S3{p#{uYrEJ`v~v=yTEzS!d_svv6;}hbLY;vp2qbkjQ$rC6eJcG7suP28 +$LtBs8OTj)?06t`|rQs&}Qp?wm-z@kQ-|+xG!=2`t`-0K(5f3VeB9FXU2>fW4InQd3-)Lhu*@Yk3MR8 +%;*K5Wv~af#!I|RPldQ$F)X``wQJT7u3@=r68NKxj0|HNxL4KI72y|q4Q_1m!Gi}^U*_6oeb!N1W9)w +@I&2L5;)^fJ^5x4-tr*)#&B^u@uW)U%KI>Tji!6cH#^C6l?Mp%fGDeSs`qSpko9%sqSGsl${>Icmu`} +2{_*_v@f&3lUy_oF@)K9K-@LiJ@S2)qHhU{CN<9w^Um?KSUefO5x(06v--`%Y_&l8xzzrS)B-`fqVlT +P)At?GJD<*~<#6)QHBl$4xN8}(xc{5o9+307MUi7v+<18DQwx0Zv#0K +@#*^-*4*(%t@zl34`wZN&E`bzSTON;AP3(3<0WqRw9ZY~CiDne?e{w>x2=@<;{vhaa3^;ne_+xa{erPu^yIjI!1_|gi +@DKq?8rg0pX5+~O*ZSFviwK(u=U6(ynN`Phs>U0r{U${S4(95^n1+xpUZRZl{a50<@}BI{2e=Xm{`r; +kAvMN=7JyCQ#UO<#+SS0pU+$V+k29@7JicT@kitdTpPQrzJIFweC#xIa~<}AUJ;n^+0>IbmpmKay=Tv +!TIVuyo0rA4(CKOFOiw-2ex|uIujBP5p5~sguomXmp7_PJCj5sI^NinHyGGv&{Bi)lw${|ce<|S^zqR +%UPeeq(I`KrA!LTNli)1>=vGtk=7-1_~@k6#LTv-$-^_oWs +Vy;q3!T-sa+B?Q`=?V79N(Gn3g;;DbxP%#%|%awTlRk{VOhy>!}-^O +-NHvErer3CM@O}<F1NJ(| +c$&#}rExawgv%K@XYs$8i?JKJ)t1dfRc5Qjf@;2p><-N)i%14*yl+P+(RKBKsOZmR?s`Bdcv*rA-W2I +grtSF+WsB}$fMd_B(U8VaJ^XZHB +WL?-?hFLzLvf)UmIV9FVYw5>*ed`OYjZ#jrOJca(sEdMZO~68efHPi*J{2pRdwahCI;$Fr5iW7>5mW(c`DzQ7}@m}j?*TTGQypi5mZ!d2@Z-RHIceFQM_i>T8uIT0 +W@%#AyAO8nXO9KQH0000807zB_Q_cT$)om#N09LF302}}S0B~t=FJE?LZe(wAFJx(RbZlv2FL!8VWo# +~RdF_4ubK5wQ=e)mV1$mMOuo9S7lyxCA>GQ>MBdUvus)|<3f1Pvqd_Is|4P`2T^!2G?`XKGFns_e7h`! +n2WR`U}r*jFHWDHygEAxtIw4erxWk#>sRmIo<4d1?)BRImg(;}ODQ8Zmti$X+^m(265sGx}iIwYc +5Qi&p-m3Qyly(51b#ijV+k^YLs#-;uWD2ghX3kg}w<7B3Q&P7?qmmV{y4F=(Ud@t`{tkci=TB +vp>MyhG@)F3bzh`yJc~&N$^SHXwZ;HBs@@i2fv-<0*u2+e9-azI{BEQQ%gbM#Cx)DVQBM?PH?^#k-fb +z-bvB+^$RN-+p2PC|@AsD4;ayj(=mgLU??9iK*Krx`&5Ssp6Mb#?Dm&pt$t$+X(W2CbJbDEY_Je!FLw +n{^8v^W=4j1^Sj;H7*q^j@C4JAQWj?l^jW`r_o(@ynB;_uDTgCoiJ*4|nf)?&rnH&&N;y+O9EB?IUG% ++t|g|Iq(cHij0-APeo1u_TnBFw`h#grj5@q* +q3QIy7Wm`(#MM+if)S6M0$lO1micmqIhc_mUW6=EVLG{L4g5#p*U0^pmW=jW?@rFkw%r!_w?nP=*`=c=cj*x#{4|@@7@tJfC;cd)$W0FLG+H`r|~TDH +-?iiW~DfE{W`{9@80?K3jFuRSyp?u&p;IZ@eEK?R&wbh^N(n;TTtBb=`T>ioC3ihEChTRi@@LYf!8A6coWJuRQbti?+~25h7ywQSQghB_}k#LaZan*E^r$YfPr +E$-KH)xBqkVyWe%$kVmCP7yJ&FCL`=O1|Z&uN>lPB+g_D +!f7av0~iNGE|m@dq0M{O$DJFVUO7zC3>M;`P%tQ0E&%1vKBkI(_>3*-3PE`nMB*fD`diFYtv4yb!SBr +=IVJpRyzksN}#LoCbjv-Dl=&?>Fh=50eM@ +>OQ;*=-tS8H~4p1Fke0XuzUdTgAXBp7~EGtMhX6=zQqN8<6>0iL%6(}{ps!_Rps +viRSLd6|NFNW55E2H4`&YsgAe6{AiO{LlL_L@U*G-m`W4h4`8(h6-1F^Ue{1}_^yMm*m0>AzlKc;S$# +PW|kp_nAVLTsAVlO-Nvc|M^7ts=cp$-uy(FIn{SzMOhOVE5lQwXxrCo!%DrVIcPnN(2}lwvkDCl-DWV +Npk&78Y5+0owep|FH^y`ljC3FyqODzzscAT5uU3dZ4C(ZVn>3>~G{;Rk;ALbtOl$;UY&(IWQobNEBiY +OO%C&*>+tIWpfLWFwgQp*Q=|6SoZ?n4t+YOd3bXkFx7RlVrN+-w7tm^H +&t#0hK?PDy(uy%dCe8jofIcab;_^(fQWvyP!V*MT;$Qb;73te{vj4(jo%$6WWv2_=jogdhsoQrZKCk6 +NV_l(3MiJ!&5NgBDR{LM>6=V{hL@3Ds;KkFRl)iS1GiQXV>4AiB!3YbdIOOhfYghLg#XJKui$Eq{1QM +S#wuMKYU^+LQ1$&7;i)RmOdtD7^p`!bb-`4nhM6f20YklvqF{*slqI+v4aNhY=)gc@-ag|ATMxZA1Ri +9#SANX@bF6C70>ZbJ)DYXMmqfSNzT7Y26!qP61c~`yXJbIOWcyW8p1I*xY!r7u>*alYwp~>;OYNf{o+ +I!yo&Xt86iwM2ry>;SW|JqUjCou5kTr{FblyhjY@3FRWidFCpzO1seFRAB*u7{TipM){n4tql> +(BuNZGEw_lk}=%Dbj26VBsmd=;qb?P)c4%p +}}(0H}F*S!A$EC1aSC{-9mX>@Dq7{B&;8R6RT1TS2^32{LTWvT;1~6cu2_kI7ij(LhZtZsR8PFX}x*g +qwxkPV5XW;@YFsh2f?|?;t!rl6UGk$XtZWo!rNC3<5qla)ut=KWa2Yyu)>k9zMJ(*aoV=KTT-{dxRgX +2EMy`$Kxx64!wH?EhD`MN{V!#*q(4is(4i5-$4G-HLm7Zxrd36_{xmj$&X)w`;<)Mg0RcK2=#*4g)&P +z2|C%R28%zg5*Jn#uJnJEwarxMn8YnLyCL?U=w(nYvT;2bXhFou4_ntHTa+GdSU`ZLusiJlhnruMiWV +c83&bwkL>h&A1!c$DqJ{v7!CT^bU<`UP(VJ!9omqm3Y!!i*MwuN?iZE~2Lko!)d2tB@TPquD_A|M$ZO +Uoy^Y4|rXk*${|KSE1XK|p}f2g1jHHo^d!tIABHADgpJ=xt(juM|iIF&7a!ntoqgPMF=yf!3ryO*kFQ +eqj}0rjC##g}a)P)}xFP|7!bvzQ1=Aup&h@Yx~is1)*pE(TvZMB*wDFn??+YV0jrc-;wQ{&!N-lCN7( +lnmH%Mi9f;%wjU2sV0GNi2yp+gLcYlmrU1CUBRqNFAFqIVPW8nK!7@sF;6(j><3Syk)jlF +r=QCg<1{Aal#a4^k|H>K_Wd6!Ga;r{8I&pVxdo(mhr4i)GY=vbiF)!z`qj@>`C0HmMd)&|>h^r0$N9k +WxhfvI|Ek0Z}3#rdEFrj8XDAL8II(-7ux8ni$X7msW%Zq%P5U-Fy@MmEYq59(-4VqNT9j6x&|EGsMg>$|1V%f81kG+a^j#D!nK1H|)$(wni-R(=hTw$Y{KlaaKSfHt$b0BK5cws^Rik$cuM7@Y6!Uu=Z724Gv1^ +nh6%dq7p1g_WKp>pXCbeDBt3Tu8@q7rB+r#a%GIKl!Hg(NL=g +3tqO1Z}IgW1&7safMry?5t@JOV%ss&tP5EdAMl>_G<`2dJQxdJ~f$4TPYcWab}4#B>}5U8jC5QTC&@| +RM5kdae}lR28Q6gpCYOo9A50C_u)uLXq$=BpvDN%I97UX2mVaX%s*v4L6x3o#?RTf5Ogt5FqB5TL6Tw +IqLFFOfRB%LGVEB=y*}moBm;%6Qp^V@7e!kpE=M0RBJdqGc(Gi7LPlNHiiX2e;E0xq!%&Zfc$zvKAFA +24HAj_^~e1&HK&yV|id>pj?iz8luK!ui?z-tTP_T5F@3Gkod4P1boW +mi>V+%QG65REl*#QAFx~8+>k&F^!O;gYp7f`ef_s5PhY=%X3ieMu`xaL#K_f%nl?8lSKC^yYQ{3w=@D +r3Bp)=`i!5T%7nt{Ua6l(pb!m%Bm^3oRH2mA?8_*k`zc_w(^31J6Zp<+Fa7ZEurP1Ot@P8|^O2Bpoos +{q|S{zXQ^ofSCJ)HP_kwgzv0gC`e=P}+gzAUA$vEjvREj}(gF;`*BFgr<6#mmJwnI<6MWfN#lUX-jo) +<0sy2AwgAa)JuvJW0`2iE1VLIHvTOTI#VgBe;$=k$y5mM2O1C=vu6l@a4$ez=&wUo=E%yAEPp$0t!lu +y~B@#!Jy%kM*p@1c~qCOBLm6^LE%iEq8HazDa15})g{u-QH~3|aF>!!(&HzmT?M406e9=ML@d;JRmEa +0TDH&DfmO?O>q1OZ`>Rmfx!c&D^DqS^+7tD`PQ?xk>>@!01pS@Cj9z7g +uB{DDh)C2Gy!6TTzl2lLXZy@GkL(2O^SmVHy3TSAhx7RJTCaZUZ0>dks2_`9+phxXKmfu$Ed-eHqWLJ +1eTn((;(JBZeC2;#w|`2sD>6ZhV>!9Fki-g}KfuwJv9o-BYb+0ag4p#1R0!H1BJE&3f8ZO%5-LKg9tg +`f4j;!u1RE#9!7%{3?c>D8n<3bWX;EHn0>yqXi2ZK+?$CTP$05&23%RjH=@P{TZdKDwkRL~62hErQ6SQ6ad~A-YbNE9QA}^ktUYV@$RW{FwK4~4lLe$ceRcNk_{EEpxA^7 +km*cl5^ji8F93#-Z^40w3Ui~)O|8-Q|WCw^(%bR@O1lIHqAA0vD;k^mzaLo`;ASUl_&9?)~Gwc5>Qa( +g?NW%<*SNf&QLx@(Lx4@FSr{(S&-f|qu8d5_yN?$a`){Z&Q(F&;E_WIQI(UN!ZB{sKPE3U0@5QFx8wg +9&)s$MAF>!ex}(H6`gn}{~{{2LhyBp%z^3h>$SN;+5y>y*F~x-i8?D^;B)e+cupk|+P@H@UsV$QzhPl +?m%wh`c0V3c`%2bxA83PdBehCW*18ue>D&+44%iOv=V0KLWY%zT{v`SlLEU4>roQ!k6Nb{WBm)uw7Hc +%brvj?02pjp+UT+Qh9_~FA%JW#EKLXL-L?W0jUH#=gO7^JV1>tGCsJz#(6l#NbP_RiI_iw95NSEGMD5 +IDU*5+@NgeIwmFLown1vyM(k}6VLb^NuSqvP;{Lc)G9bY$#?2BBS7;V^LvR6GAS#ZrXmCpD?V@bG!tu +u4HyLs+D!{8cxE;su3#d$dE?SlSea_T6V2{jdT?RGNOiKf#3Yx|#)a +N4P(3P}M0Sx>2M#Jj!Oqz?xI7I4jtgUOr>M$QEP94`%gr2M8x*V>an2s>i6*=-BPXTwdxZ}M8B4Rvzj +7RN>lrT$EzrrS9_?b0Y(`qT8i&Sj9C<;Jrwvyi`8OjZ24HA+~x$zq?(yYu_*H!a4LH$22m5i`kH|vW8 +=@Mct0fUM=W|6C^%FDxt4=U8RniG$KwYecrH +{?iVneJvBtt5RummGnLHLXdk;mzHrHaW~X2kD1Y;6ovZ-mE8Sb-%LpcN5G0*KOT#7;qcE0jmj1Y{5rB +wU1>LL#O2u2=wW_?>W&*4T&A$SM`!xGR7d(| +oNDjguAK=j?bKr!US~nP##e;rG$EFTHI@48rG)@$yKy`!7KYq;Qh`I)k{1(XL<5+4fcFP|>qV?h9M_c +w9>PE4OiUn#AG=)R?-Ow^=`oZr!v)QQ2FHJ>5;b#?8uR)~n5i4W7#;zf@>i2Pj0rP7uz5jHkW*uR+E> +f*08HparhT^{#Vtd0Oeb#F$)?Q1a@lE2Ht-Z}Q5zS^;o};52n74FU5u2f-&LGf@1M&#oO&(%}NtZTo)DShoD_=*uZOG8?P{GOZC;-X8ROH#Lh35OSb9w(f+{AqPkkOrE~DYMzD@yFps%Y +mrb5Ac)L5=SjBK~vYwzs4xS&4rc{HuxyQ?)BRP7?;e(qzDBIfRIeK-TUvOc&*Tnew^mBWj)?SO&nJ=2 +c&PvAQwkz?Nk8bejob@0eLY)`6)H=|lxA&Aei1)fb$#U-q4Hv +$yk7uk)Nt-i6rWgsi1Y4ZGnsv>7eTL(g>{g+J}EW-mpWP~ygRV1Ty&@{Gf_$;&HnkSe_0~XG&C-~WR{ +{THi~UZi6fT~cA?QEc9Q*%hUx^+HxKbo~V@b7ch+s#%LwnKD}xVx-I$1UAQM +ivjdKf>T4ZAvXJKk?X~ei!|Fr4+sS|riq9i0_%y%{AxSQ?<@vz{yS1t!sL38t5Ed?zT%-r3u8>QL1e+ +%%1vC>gY<0x6}Ka$0js&V8{HO!~63@TLC9=O3rn)^rV-NMnZj<;(^*$q7_7S +KTgSsliVdbtL(u3?i7z_eN|&*gH9DNsXRH$M$Dv?%mdfiS#o?4@Hy-y+Mnb4ZGzV`A8MB^_}v&wWyN +H9BMxitL?#PI#eC$8T=jZl+12B`;UG+^m4R~m$bv&0x$)^QKY*I1NEk}t+Z6x|ZK%3*Mc +{T~;Xb(N93t$SUB8_JehNydwAA9>h47~s2?S0;V{(QYW(4Njsm`E!4puxgsfqj9r5~)C3HfCCGBnSO) +rYMsgWt3Q_8Q@bpiZumv^w-*%gU{v*y?Q|A!d~w8todfAH6&R6`O^kSn +1BCZohhk>@RP8U>)rE>ndq*wZ+f+4c!HER^n`%%KYl2NI;(Rh*Y(izBfBJ6uPkp8ZI5P&$PI|A`ABG4 +Zz_Dj*Q$+C4(s$L%zlX=-Q*=zpPnISmbAjedX+E{86wzTDOKBo?6BUt4WZ=ya;9_)O4rYWAMh&Gj?eV +U?ITV){%R5t&l^aPZ%tmm;!Xu&PF&caIkpdQ36VqtMy4%1u^4N}@Pv)M?i;_COuv?%L3>I}3L-XqM?R +H=q;6X}Q6thdp;Z8A~RV`?6~|cf5D(PDB$|zQP7W%)W0}0#O6gj^2i&vk3?twHV9^^SxwD?@wkq+Ghc0HqXZ_OHkSHSW@h-K|l**Kup})iJWIo0K*?tWBX&fa1Nn +2(!fp+)HMY2{az~`EslaW4fa1X(4q^VJQT<+qA8k*|YxsNV5nLQrQqM+22@`CfN$iPhrZ3`48=Qp9sy4jBaR +-n5eT?_lY?5U`7cEZp#Tvo_zkO&J&`eCR8u`TK~EX_aA4kUwIA2*^*IgK3l^$o6$@PUL{3a3m?OTrBJ`mD=(L7XG!`(zsxAR^7O4+WYw4qpSY~wbYa9 +;-%uIq?HL$ABB!Qw0v&0FIUCY6|Pwq9woJS|(G=3y-_Y|eZ5$Urz$2}qMUW +QY6)>VZjeqs=YIjnD5QIib-<_Mz(E7Qk*j2CY*j%5bm4;d?h)&LGAh?`&?zqnsxmpLFU#NuD8~RAwCC +F6+t=@2fby6qF#3bJ1bJ4lKoNO-PZeWF0=#iQc&{gPK~WJsU--7qCI(IFIrQHiMH0^Vib&1~OpU9dn` +{m>sSY8tQ48y4KR(j$&_+p~hB+_)=_Jsf-onP}7O@VgF)#TT*w2br!qQ8qt=T(0yB#`ABs^)aG8aS%g{{=Q1kUPNzp~(%$SB(z=g%UDuhtRszxw +CkO=iqH~O0h&3`#?eMnHS$=$%MnmVe!Z4<>W0!vL? +Q?ss2P0s+FZZ&DuP%$DTk!%)!BiKJEF`I9k+;@}|G&%xTnFKFTQ^Sr^6umn-feK!I3T!94q=_<%S`pO +Z5Xb5)3m+)b+dG?jk|stTcHjkmt-MMm2c0yj!G=?kcL=F8U&(>DDS#SsMTQPnn4VV?`mX_{oe7W&RoO +o(_WwsY8ZIJ-K^1J+rtC@GE#t!V>fR?+CW*Fu&b}#O?f3{GeUwJS(Ez0?77U0j;*Xy*`$A6#%%U%L;E +cNL>4qy;f +Mqh($KVpa_<`%oGirF8XOWT>AC^a$eq9GBB~}g`EEMN>KsdfNhMe*O)Njn5s6gQ%Z43j^2jBaVq&?e? +N-yH=)`!C;@Btkk@18s}(WZvf*;N|Q_FYYmRb1Vdvhk{JF3&7@*uBvpsx5ftC8m#M?*}H5!)onu1k$m +B72*P4pVDoxtUFKv8t6u>9?o@m0S5CJ1B!6y=z<(o^(&yHsV}d7b7ZJ#`u6~#c*f_bWy&hwxXZ7u>An +SynZTuNuhiJfO_6(T?n-W~YQtUuxIEZ%qC@~%Hw@ZZX8U|j=aqu;cfM>*WeCH|@dU;-x^w*~baffkV} +jz4i}XIk`x|Q&uH$~iMOsg|KX!^~h(@nBVI3pW*~V<62UlJn*)g&fGc(y5*GD==gG;RijA(BLP_x6WS +V1D9SfL(SKA!(ivIF|pdC7mBm(=k7(dQ+1ymzmkz1C;-X<{U7p8MC~LDk;IXPY)Z*L2%6O>O>eKxM!ruUF37k2jdzd!WKY%cRXlpLcShBAQzh$oo@hyu1I_tgbBu0Q-n_x&Eq7EqA<)?-ZvO_jpaB +_IC1#wcRaoznKDA))hskx8Wkfi5k{6YFIiSk$0-O06)1I3NGXH5?9p8zwlaEC=8ORVTRLTl(kg$wd6v +bA<<{rTx(_*Qe$MPGoH6^O&X;X)wrt(GvfG|1yIr*9W+CRk;~d#mXUP88^J5)n$4LL?+s}EwJI^NQjx +N}Efy#nRD!w+&&U*sGq68S7GM8n>W*!ezQ@BGi_LlJ~q_icfIe8GJhStcdBU6rWVoOeFGe+XXMSYFUA +(;fO#mdScBlAe@vP_VkFuETaigUOY=lL3y*=$j}RB +s)_kBVGwgvJd}H}t#&;4v +=fRWQVv;JH}&Q}f8ITP_53yE)RgTxf%XHGol+$n8OfVO&=>e&jUzIEtF5N3#dB69mCyu>+9Sbipa-K- +)*c7NGMi@$xS_ImbPd+^$*;{uTx+v;q|oKtpxk$PBM0MkP+f|z#uG;A!9EU;lRo(L=+8NxwnfJvZmmk +*T2Wp`3G6)a9(enQ=AFr%VH5xfFYcmDlbax|0&!(ahsjov2lZ9MpqrVSLPDg&8(`d~BnPU?IncCCJ>@ +7u#vkTSN@s9p37+SrEMt!z?G4sy3%=s-dw4g4n?n-Ct}Zj^eBz!Ou9C*3-4dXqlN`>=^Rhl;wxKkU?Z +~@R6f#2z^@#sC>NH6!$3gQ3-HmdupzB)ubVrc>zY?VMCP*>r9+?At>+~qbR$ars#SZ6pZrkzc-nzR=x6i}ZaZ;}GQcG|j1Kck2fYvnXO}>ilnteWXnS4(U;d$)w+Hgg3QPpHPonWi6IfSV9|~xuwTL?vdP~i0d~CO$pmBHXgwEYdY8#omO3K{DXPA!saCtSm#c +^PKI9Tb3=lCuYVfU7>(4f&j*)es)EGtDYb(wW~#IrMON)fSdZRnDVn3g`St*c79X^?J+^;k5s`pa6~i +iLXY9P6}#s+psLvK;lgZbi|OvTx3Dy>^!Lc2P%m^@a@Z&RO1oP$xT=1Y2Kk^En}daPbT`IiPeC4ri_0 +DJt2B%YY6ey#$6B>-nyDC_!^eQTn1|Wdh# +|pd;k3T>0cz>=JBTtDP8}d{a#43d6H5FdYIH8$b(jpG3-Nkz5Sv5XWyizPRXtA@sJzIb83>~oeBKlqE +1=`9X0PtrC}q9J4C~EgqQ%kkj#d_IYbWzx7`dQOfPc}sZV2OpLX22t=~ +V*Cql{scWl(9L)f`|fbw56I!7p^|sF92u90TdEZC3*Kd0$-%XLN4&Ue2&sKtp1LCj)adVi`hjk?%*W0 +%*AkSb=`?PgNbiIPe?I^8L-fO=O(B2R3mQNIsI7rw$qf+R0w~(jXWIaUPh6)a40mRe))r}klRR#tiR8D8o{HcFI~ +2i;08Lk2bt4u;ZBnh*DHP)jY|P^TG*riVODCjxsY((#H@Db^h{eyYV+GBj^+sc*Mbv@i#G3mPK*SmwZAyq? +(#g-vjh+w060@Z}J-noyJhnwYhaK1w40QB?5rmH)@|zX7?w=W_Q_imKk#;QyY!e);D3-RYATC(*m(pU +*g@LUT*Qd!3p61J`e#_>hiceeb*89je@>+EOh`QkbJjNHzc+@ef>Tz@rz_6_#F7`50}{w2Eu>o4nP!& +5nE9<(=(T_v?XgUv_BSbIAKtzIO%MtBUtcb8c_oj +*Zo@DPL+=6d4}{NKhOM{#K@S7=2ZLiPjW$OvEO|x#zG`zL1(DzjA$--`3n@~)GG#2SZus79t%4KOqU6 +{z3ePPzCVjzD8(55*#DcDbDrKL>VI0nq`JZ$5dn!@YTRo1YOERwFu93&P-&l}8rk^>cziVwaZLDbm&f +Y7T@;G#j7XmA+B9>d+6}&xOVJpi;;R$td3_eZohp;sowkOi38ybhXbFo*JH?`~(sPiu+Q{1=|%=94eI +m@xjF+vFAa0gIJ0|XQR000O8NLB_@O-%0I*8u(^baA|NaUv_0~WN&gWWNCABY-wUIUt(cn +YjAIJbT4gbb7L-Wd4*HairX*{d^h+X79WS5HZkozm4iI=ObUULE0^@C6rsrLM6DtjY2C(uU&*n3ZbGR +rSYFMHW>)eYEZ!_Ym{RJ)BQ#VkK7>jf-T4O!Q@2(gjs#gTgNJwTA7FPx_|nqRXxN&HeshcP$uzp;B#q +7kYl{e<*TrhHUFCEp;JSq3d9!<2KYiUjzidM>?{-L%pBRmVN3DzB8Wv#J2x5VlepVcj^q{6vh-ZzIOW8IsvSg|Dfp(OCVj^dQuxvJ)?&R=Vk4u +NJYwJt$EWvZ_kL3~}+yw7o4%!ghy9Dn4CSMQoGOvxw84GWJ-{R;GP)h>@6aWAK2mnY{22*|C|E+@@00 +3ua001Wd003}la4%nWWo~3|axY|Qb98KJVlQ7}VPk7>Z*p`mb7*yRX>2ZVdEGo~kK0C)-vi`7bTlwTd +1bCWcL?qr%pDv*aK>(8!;eiagMo&US{i03Qp<eWw@nz6GVZf_98lGt5cU0v_0=81Z6|AAUn +>%81PR$aS!@Nf9b*I%8$f9h$qKh*j5s#VF;nL2&==v2MC((3i0y{by}w%W8eS*_KJsw>x7n^$Fuv)*d +0{`&Ii^B>JR+vmC@7uh{+k@J4S!e37~4>X|0>8t5+JAxmK`L1;5s6r`rs+kS +W;L57~+U0(SUMzS~!ItE$F+Y!2S%Hfz8BYExG`7?JL?_Dac#r32(X>@n6 +q+$kv*pT6f0`9Vwk-75U+Ir4y>Hb^`u%xbSM|vB{#5xv*L7J5mZ=%lW;GHNx9BjEXUk8g{C)BLFQ<#Q +?_a-u_2%94XX;G7tGi<;^~S%*iY8iv(AeLs&0<^K?r+L$cLb~%0sw!k${~2$dhfy8*0;i=As)QN0;sl +Fz%S<$yY|^9vz{AKvcuD$j}+u$253f_e +T?uk7GmcC8mI8w3TmWWFwdw}IE1cz&(7dDH4T8i%vHa`a&c1DEN_eYU!Gt9X!uNAX;5k!Tz~qaVBt<= +e8V^#WuFCX~(O0#9jGWKE-p2+8yELtaxAz +}CJpEU1Yh+-^f{;4Gxeto`S}TA6>RBNWWTyTDfJ+-dK)^)69FHD5xmk7_Kn-W7NbrOan7IM@hR?d&JP +xEyEn)n%`w7=cZ|c0&35=bYjV>A>3L=%zE@6<_)Cn5GylkuHYOdaw`NvdD#713Z4M=63h{c<$sev+`d +04pPFn_gfySg-%GVQPJhRd0~dh4H91jiZ|#`j54h&kUES&O=*d8}4fdUZ`s1+J!WT@*Umi~uq_NkQf8 +R#~I%4`Rg;!`V!oorQSZIt}+Uo5ft!qct$V(~yCLU3})qkDV562E|L13z3A;u+m~6_s9^i>Wg{*cJ%tvV`VDA;kGqSS&2+|!6XO(Ut!>RY+q5J +pKRfc|7)wRM0zRqp_6E<9}u65hMzVwHD1&(8y&O$&yWZX*ncJcDJbmZ%`lzegY1^%tiqAsXj=t;isVm&rLM{K&62S1}~ +OEkSuVSWuJ#h&+Gw`o~xAFo*y&(R{~32cS|KtiCzp*d}#zMy1qeq2me|N#kT8nXLB2BFG=J)QB&Jnrq +#(dC9KJE<2!Cl)2c{4)w*y7>K-qaY>}W*M{wA$gfo{LT`Rqf{8(W!q$lA^=*p#J(PBz0H?Q8Ad)1c!h +?TSLWC;A5F&~&lKbY9N0S$lO#tf&qyX<1_tv2o3p{P&x@0D>z);nI767q(el0NB8Z`Rvoi0}zLy2usE +gv-Pp&+k>2JsYH3N1FR1DXk>sp!^hxEo7Ea^K=Tus?>hX?%k6u=Ro-V2pd=d4?dMG9L}U0ToND4oe{s +!Bds7St+R3qaJGvOo*%P7097{-`hbf&Y(Yj!5JWiu7=Vur+ad`rL3}Trik#b=2{V)t7oNY=80vq>n_Q0CDcd>6HKa +f;Lm+aR41(7)+&4#!&5DKr!j^G3nje&Rry$I>SlzImQV;$kRDou~+Z>O?w*+?MwNo7;2m;t}q8KP1U6 +q`bRyu?Icsd3TD{q`6w6Y600V6HU!mQkD4d;xh8e}C)^$1>bg;^q(t6Ew~8$A6$p^h*C12!^-rc10@# +R5XxLiwdFm~k!}_Z=EzS3RC`QA2KA4a5kkjaKT2apvzn_lZsmP4L;989OZ7_eT^#K?oQ`|ktUMG0;B#O_S_qABFup +$=^8lil2Y?BMWkkZT=Vmtp;096_=I_KXhp`COpyguo#C#h)K7cARl_3RLzD983IB(44j9`R+{ZkEbru +@ncCO-->a3tj9yfmB^KV>236bTw)PR0(e=IIx6Dr5B2MK63fa_+wG7(jJCr12gj$FaBU;1QmvpS)f6Q +b8nPUFm885nay%QNuKpTPIO>~!KFp8Yogu~js3;;#Zv5JjEHOW{0WsGkC59BG!9z@#1+>8L6la +YDNox%nL63(?FE=4*q%)YVxiL=Y^^3jv&Z(I>Y5UUCA!Ztf8{$WRhQETdDmdFsqa|!Iu7XNXz$6^xod +RPlFv)>MJZu@j0is31Iu#8EuauxsS3QbH8I?*3}^np94uUcYPu6+kX7q +7egCeVS-l}5(|gOWuTguImChxwKSk6m)%yKJo@(Ae;+`>XpLm@lnbE9Zz3;J-u0v8$)L*d6SlJHa1V1 +~$nc;ez0d6|qPI1~T%Kg8tE##-;X#~z2|ZQg0fuB9@MDRt!FJ%G9^a1@nIn5S=5}tnq8RcCU1sa3Qz$ +8VM1*Tn0&Zrq^gW|AupJN3!+_^(ZRpv{H_1>@NK~jMguiyK-A{rYN%{b4-YE+`oYU2fMK9e9!u~1$kvJy2!9{4TrX`HOw~ow +i5_#y9S-1thmVz4K@RD?jUPWvUYpGyKGHzM@$6D8FHgDi`p&6u88w&HqX$vp}W6MPe35`F1OHt=7I8Oi#eN1GRs$>BZE^9QWP~uyE9ci>Ma8Cf%xML?rGc#|zva<7p1jvJW;!JxB5RVB2axtO9O+JcpaaDTx1SvJw%kuyEQShG +5m6*QH*f|$9@E6}EEbgMy=EG*J+>8CU}H+#+hjC$)I}KzvSVcNESXi|;pk!Zwk1B4C^x5PXm{9h1R~q +$VnaWqy;c<;Zde*Jd-Li;*svNO>dwqKZic-=X+$)Sv|VX3Nv@}AoFpjhXUcCXlI$iil;1UEg^5}T>s1 +PRs=-huFeZNI1%UpOV@h}$fWY>z+dv=|Lm)XFu0#sTx{1BWK~UnJy*|%N_3F%mOgw){SpF~i5c9m9=HTWVTM8RY-U=c-=KS;`pqSrMNF?fxk +f>ChPb07v^&pA36Z#)YA_Dn;fJD^P3>>zE=qf>7x?tCoK9UEC{uuyj&L-PW`F=82V3(Fvqt70KoYjX# +-IegMuYW*RZ@?Xu0x|KQd@ic(R`)U*PeI}-i;?WX0Om`o{_@g#J;UaCcFHL4hOwY4Yq=QG-Zl4gh}5AMYpW`zt=A +Npxazj-@}i}p9-ijDu0DVxx0V`~csdR+fn{)h^(U+(b?v@u9ofu4?v9(IJgMnpAp583oY`DWr +@!-fGj;)p1cLz?9Akwgyd>dwTZJp0Pl*Ml7XS@Do=)9Jf309x%HvF^Lw#9trN-?9=(nvia8aoyr`g}b +fQKrHwoL9SeHL{CrI#GF5Aaur>U{)z$;ZECMQ1uCm)ROZI`~dlI_aR(^O5Ge0tA0DB;WEkajEvl>bHl +#Pg0(k7+9{^)!cNDY=dP&;K3=fATp!Bj}$be1v7_(pM`Kh&ONUXzLC@AuQKzvV4!B>0g60kR#GGS75{ +iZN+I=Rg~|&B`5xZR@Sz4k$t_@=*>Cc*)x&VQVJy1@#oVm1?beQB>ti~7YQcbJkd>x(8L)>yP-{3z<; +WJc?h(2jq=W1{ZfxgS^cbL1f;S{8!WgHh%t?7A`2Wab;T}&53i$3$;CYYT82HYl!f@|BlPaSkuIisDQ +uoZ<_)4jp*k?;@EB+t!z3=r@kokOZH0cJj(rmp>oSo*5urvP@JP2T&fM157tRUXhu8b}F^pUmB0;XV5 +EH%hku>ck5Ef3!oMz1>`L5dj0PY7DQxPu(?z(BhCg)e2O(_gs#mCNvqk*eb3DA_KMKpBnTvKN;8MP4EcccAUU6~E9LNs31|p=;fN>yhW=t7Id3 +thFh){f(zyYJ5C-w@^5zOmPJ5%l^_@^U1Jm5VCToAbJ%cG)R+Gq@kO9IyP(eknCtj2l*?k2k@+vC@x~hLGSdNQua@P +T6ujWBN&i;HuWMy`wvFnSEi_?Rsa@K46oziKO!I;?Yc|h0C{H*8E!$!4mW(bu2lJBwmB?+NUZjNE#{y +g8~%tCzmdhzV}i@!dBXeq!7|D-6xWg3x}W^RKz>Q@GCyvd4ddn`i*e2`FJ8QQ^TU&O +Z!t)gl*aZG##@h75~P6U>OAoyLXN0s7jsUTOflJ!)ep#?%;qNLayL*MGZQTSI0%eEsat@Ny}h7Y>@G= +UlN}Bk=L0M>;ctM3bJ5NP!zpDjmM(~Cc`p0LB*0yFe!Dylk;XVF11}s%1~b2=J=yDlO5UG~tS-@6MXE}a-j%m&T)ESs>a92<-H>Xqx~l8ULli(pa!mNX_gYB1phDL!6mZ^wMAz^r5# +jG@U*lR=iZgo-^sY`zP&9I0OKp=-wxL+kY#y7M>hb?M*ves|bQK5dHb`n|@yT|kt+Ar40PQG08DL5J5 +|C3FgNlHu9Wb-mq`TCeF`=o+9oW|J+WD-I&fdFfFhYCVsT;u$eU7$|^&F^^~Jo3qowg6c4)GL758z+} +LW3!e#HDypY%aTqDvA^D7z56k)JOICuC&{`KGtJ%g8@6|7UbZGdvuBsN@9MKZS>yfW6avaeAUM%@#^% +76r@Ow_SDfx#O&}FsD!}~GgkP^!M7=5mGdD)>dJIE#<(9Qmhxi4l@|KF)853o=}?e|zw&1|D2&Oh=~Wo-w1|PR4dS%{3GrI^9A-5T*9kfN2T1S^Kf%Jp;DDwW?6y%ED^d +yZBIjHPDg=bVEJMAoP^$0}a2x^FU&=he-c0|PJ>@Hufx)(~Ut4$$3WzO` +OEF)v +cjf#e+`r6L)RQOzV5G3$sKb2gJr7-SFHUvwP57p>F9~n#TdTe}oC$nmYv_6|hTt_8-v}?thkprz9KLf +f5a7K3@%59ZfBD1l&4S*+WQZvxhQ!~>kPqcM7dRiU>tytHzs5#P5(|_H33%#ElVjwOTMU>Vk_D3x_c(tKXem2V@*0Td1sX5xQB)=MrD==`VQ>A5m%zlX`HZ>mm1AAR=cv8q +@?OFzftqw1MwFV|YbMih8f(w+HE;;(o^pqMrzGGp8t})9S)2*~V-b_TITQDq_8fP`dFu755 +5LNAKupKu_oJk^TWrsZ$Duu4jlVn=++R8jGV^PFT?k)&du9OMb(gTHv!)?HXx?jlsCP7_sxe}cXH1*!?^-Gg$8D_S!cLR<_+sp1 +r4onE3nRjpHWdKafL|uf?9@rSJoeMBCmXrGr?~2s_0t2;?&T!))It)uFIyW#PhqJvres&UK{xI&-9u$ +HBHm0xz9Iv58u2{AbbXZ@B&tJo5JH82sC)feUZ1cTK|=eNPPzM87ucCl|c-HCmd`8J)$xAG62WD{vW+ +mjn1Cm;u@UOK=lsa`AS_{jFG(du|ID3q98$#A|b9kw7_5kmYZw~dP9#<2O`|DxG$;*`Wi)A(C(^HVEC +W0D{T9T{#kxH{bB_J^Q+~raTi>42BLAykat;qo7xE}6et6)6i9*KP-!sK6JuDXvPdIaIyqn2Z(( +gn7GMXpF}TmC>C=|a2VXc7}grpMiCFgtJXwid_nY(=~u{L32Q8{3B^T1p1I<+x-ZGVWY;FP^Y6J`lt7 +M*BKoXo8p+??x!6#yw!-2{a}*u)gK?!%bDwtH_<#=I(czJzF$vfyNIdeE1D(l4BtV4?RXHFc@6aWAK2mnY{22=gq|CZ*p`mb9r-PZ*FF3XD(xAXYH0vkDEXYhVMxH2TOZxgS7XadaBezrEO19ga$BSv}BCN!!{_ +|{`Ptn8j|o4=-#ScqKwC{-|>S7v8{;pF|I7;`nU^&2lz}T*ZbNkr^!I3EvAmFM&1BC(E)*Sa0N0#>R0 +Tn()+>z)vnv}Rno4CB&1eXPQ>2}Y(PmQCky&em3TnowAp(Nko;J9QC6#@ME(GV960i|5`>yD>&{X(pb +cy-GSxQ4xTN&$sJz>Rfwz@3_yiHUGz>E3RJX3#MqG%07?e7>2+Ar9*eQoyP!8d3f3O_rkIflGBXV&pC +Gl6ZdHWlkGA$T%Qu}jkj5^?J82Fl*)Rx3kq8MMjQB*aqAakmk_ysLXddvk`C;3&WT(9t}MJhqO*d$`@ +@MERYXSGLeEm!&77SJXw>kNDME-+c(q!OK%YR_H67X|tgtSRCj@Y&u&-?jr?$ZYQB9>Fc}k^O13SLlY +Dcd!gvA5Us84)+O(He(GNH`3@=Kh$F#wi~ytNpxm7CUXWod}#v{9iDdwtv$7YvG#}`|MmWc(<<*fyZ) +uISk>cMCYfRPcLgrc^#$(T`zx-!?CM&a!`;=0eP9CZw}FqLjOMV;D8`UZr7yy{R6L4uQam=Iy=*Y1*E +ri(>kQHV!}^Y4B|gTvm5+y?WS^H&Uc&S)!Iiw$&{+xLH&9Ch1QY-O00;m`Rt8gOD*N5s9smINYXAT$0 +001RX>c!Jc4cm4Z*nhWX>)XJX<{#5Vqs%zaBp&SFLQZwV{dL|X=g5QdEGtzciT3SzvsRD4_GOAq!Niv +oYa?d>-IU0)A}}v{VX@lp6yd839=YdB$pulvAy2kelr7r1V~VJ+V0(ZtEP!e0)qiC-!p*wZ1iNrX6Za +iF1A@&EJm;J$-@VI_`~+n>^6@smId?o25j@$^G$ZL_0K1V$76gL&x%n2fkl0}-YBy?#1@WI0e(J +D*x0ZQT>?KUvpl46}Rwsk*6zoA7o*%Wa>p0=JO~YLfbIL=MNtg`R(?@2Mj*d0Eh@Q6g+?UfZxn`R +z0IG#+{4m*`Sy$fDUWV^|7vWQ}yge&e1+i$PJyyLO3p0C17j&R(- +aGtFP6Kp10j~h?ou)uw=AJUFZu6QnBcdS=pw&W|`08MLA%@-e^%Y~Wq1)GR)!6$k*eEe&m7P#IR{N%h +S^Mu(gJ)>HjDwF!DF +Fe^&3O5S(vaXXMB|vw+t`{^NXv~XXk7Q)H3BfVY6kJT<|$@jAF7#XkcYRK!8(VHhILa;5#rQ;=-~#y~ +x8ALmb@`X&?^sf^C{z&wWIi)&m}4ceb;`dP&hEBzoCwT1N4_2W)7AJRQB-J`dvbn&wG9YnL^oC6R6sNN=?hOnclCvcgB7x#%Ek?cf>AZ}&0EmlZV8Su@@gy%5xvB? +mLtq~gl}CR&`RVY(4w^~}`$d$@QLO8B({Of)FFqWL31?um1@ +v&)5wvU_;6J_xQbx+u09trPk5GW>DXNY`W;X?K30p3_y2Et@`cXFzLNO5qtjEe9pgt3gJ)Dg9e%gP(K +Mruo7MU*w7Ohx2*yxJHCp82SpqHg0x|w7-sID7WB=z$$1@+aW9BX933tz&4PEj(8%Q?sXzlwYYeDK@G +fqFK$FA&soCLj>3U-+z;hJydL5qLy)=7wnUlax$Eso=sx(*g*N!)wz*Knk9G+&!Kg?;pK`wmA=G=_*6 +E%DuB+{nP*Y_58^XgR|e{n3Ae*GLSD5NyNgJ;4A6TqWOxr)s`=DLt!$Yt9?%avDQ~Tt1u6ug0F0OPjp#r=LqgE6ZK0zZfQYRyX%Gc~hh5XML_^EdrVO$cSsossw_5#UHz8 +Z9>fVilzJ*avrXIeC2C`8oMRlA(8(kt%R7QKjl5KY%i?mXD&jN~#v$|JM}1Ovi|5-=HHdT0$GTO;$T& +dz0SZH6(q8t@LAN3+7WC_!}@SZqL~nRDQr8IUzt>a%4!{nA`9sU?|y9bDvTnfcELt-YXzfMtb=W9gN) +sG6K&r*pfVYSS3-nT5KON}Mxp6I|ymS6m&4a_Uzrqqm-wI@`)?0E8P;(Eo-|&$zMD+z`(xpImK!B3&XIp9OXSra2sK~$~{6_dh +qlGeZeR_S|ui3EqtV#{{3p|xQ6Zpa#u1-};b?X{@vCnYEev9`aUsYDrhXjCOd%c_%5YZ)P@T%w(1Dor +(8y-+QQV#))>T(nR&MFE6*b!BjALanVn+y4}v>*h8WpZzfg(JNOG5)m3-gk`J)G!+5cuB_TC1JORoC) +HWo?no5W5{b>U;afAXEqn`oHLQ&$?mXJu6@DN=Cip<^x{@at!ti_2&p;)CIs*JnB>ReR5hD7hL`zSA* +@U@wH4aU(vwU0G&#=j^mtn!eX<8P{OT{K%5n<;Mcvqk}e{iiSh6jw1J0`Pxbo6HTWVbh9u(rMb=~+ai +7r{+H`mHIZyV_POf*rtFm%*l9HA591(@{Ude;(%7QDV+vk;B}s)BF-9Vv7#FD{qf?-|zo&c>LF%%??% +1wk_T<$+Z*hn5=8Uu%s2fr`XJI7Oio_Dk6EbhYL_(0}FZ +d<~AQTH2gdq1M6w(Ug0Dg|K&vL~RP|s0O1vyLER21k`rvRHJ{MrE6G>H1_m|uVi3APpIbLdMF38ssv> +*+EdgK{ETMR5pz&@IdjdJHuLvis6_u2zMjU$i!2poW`hKVy>LG(7d5e;SVdu{-+DbM^1D(Z5g5pAOE% +lO6cq51tG(G`=`(yfSjv;3DT3nSqs3X6Tq(>{eQEV`hJ$d9Nro~CTU?jk-l`CNw4IitP +#62PNzyUWpN6Yj8Gp!)DQLuqus9{X8#T=yhRS4n?@j_T=hZJa%C|Jc3=Ts{_KhF3pf+d} +W$|sd_vq+f?A4)LQq{$#cEyqwrf``Dbc%orNR2zH)ujt*$BpQ-hmrgbXn-8@&?0m@3^WuLmL +8G%A(RBCGGf>%@?x8z_3iar~oKYdwH!{sOTZC~;i5N-F)EiBVwQFHUC(fI))5fVqxm~NgU_Q3Av=OrU +0Mmor#wGSSeX)J+9Lp7fZcIfMN4Of$9Hg{)dY`SuPh+dP0Cx&h1F?$UDiqFJYU&PXwRm1TS#&KB?+z& +8qM&3O@#pCZb{^S`E4iKi;2b7ENIaGBI*CB3U23#uku2#`hksm6?^y0`wcMN&sc46gvIe +`Sy4IieB;0n+S4_waw!3}BEh?`4sED^(R#oPBNHQPsHdlEiE>QVK39w~U7pX}+$u-(G3e_+&z#fYpY^om1;xSDw7=px76CAPER=FU7=g#6F!!OyV9knVw>s;u#?y6)b}(~!$C{EWM-dR+p%vA@k6rrcFa9rf+dcR+ROwH2H?P6Sz95lA^}e_raggti4-5ucLU}z9$~m7EX%%K%BOWPP1Fq`$r`_ZIH=fi}_~>>0tSKy#H2H%9zT^$Lv%yg4}b%-rC;3wKq?dsa?kSkv9313^#p9yS>`TOVuJvgKbV)_(u!F;yhBg +4Dgfe8DJCZAGb{4j4wt&k}7(fCtAHZ3r8uPtIANUb>;c`;9XrjU~fH7%pi9nK8zkRY7LcLs7M$__!ey +r_lV;5X)F0rKkEQMu^=`TvuH6K292vMKkb)x#mW^*Wn7_9Q9tfr`R#gYLGgdg`+*Y!>o1a+_h$Ht;6q +S)UHj)QiEiMhMyrmoQChwo|(>ctXmDo>gu#x>$bMMZOBlU`Kr2N*=n^Arjgn_h~ZexP{v42F(fr4_7W +tL4AWF0A;YR7%?m#FjXGLgPdI4cP@ginKL%3OH^%R?o0}VuY83b1yXr+HNA3b5$ENtw5BsxrhJ=S*h^LIm-?b}^ +HB>Wf5|l(x<2KN#28ZyYm9z>~0QN8FWh7~($;D6)6q+^ws&PQiIzU(1M=IL_mNUOXzF9*RvJY7BKPS- +*n(#3H6%P}E`5zdk3JV$4$JOz@w$u7;1@D(AnWxu+j>)_aXY6ncH0)uzFz0C +T1aO9W{+nb5=aGD>C1b6LW}TO{3Oc*VapVZq!MAyz(0uPMyAs(*zZVQvgSHsC51wE1+pC8-NuB`iD@R +2ESzHkD%%xF>Y#DXA)Lbt?MpYH7?wHa7}|JE7lJ=L{R(z}-D2VhOs+?M2+9caqN2R&PAF_tbi@dENyJ +>wWICF35p(+xsj^eL6+MZ(1ApX`7Tbm?}<(Fw97sVdO|VbGIn+2%KxDt=YHwf(E=Lbv3QU1ZE0BK50o +Kf_r1-d1mCgSz4MDm@ZDmW~;EY>3W+(44eR+<&QO}wP5h0y83k+Xdzj&7u+^HMwfYtQL|0ZxW@9HjdDzO+xd{`_+9 +6cGHH7Oht-C?k4!*mcg*%>aPp+`gM7~A`D@7|UoQgCCBA2ED<^l?g#c=d<6OQNAYsDY|N$DNmN(Z|Yo +uV^~Mtq5}pVDhor9(C^@lb-blUyJP4M{=sn{bt3gchvb6`jNfcFM~{8kDf)$;b?8q6#))ks?GBVL|De +q7~y(duSiGL~Ua_N<{NbUr80PcI+H}+@9?0dM^Ddeg4?#(6*0o+x_J^5c4@@^3uXMqYB&DMF#HV9!=> +lJ2-?Z-QKV0#Moh6t81xa75$T{#igs(+678QXzCbLz;=ZI+r)T;11W$+K`uDeAcP4<*)^7$ii37H)uQ +3%%39z~#%TiS#3g +_TCCGKzZ2q$iTbO4VWAVh6`w_t^=W?{%#g#l?x9P=?SU5M^$nRSmelP&p|VEUAk^RwfZH1X#jSwoxz! +rH=yxb#_FmYNNahC5Ep1YWFf0f+gi@3T^=mGUquAJU7z6cAXq7`8rS;#;~AanIQvS^Eh_kt3O6j8kl+ +Y4|R;ct0+erKsz>z;GvcE&0+uW2uJP_g`A0}XV*{91P;mIFEjUf2L!JoEOz=|?5sF{avvZqY%xk|n}P +W0EE(91wSj}S0wtq>M)GJb-4C1-xcajK%M2yLL`_cGPs{?YYPX%_{o(Lre_Il{tUC-*Y8E+oD>K;Y=` +GvMGAiH%ru>8i{*7Dw_mN4+C4xQz)pHoDToIkT4U>y9yudTJkx*vvobrDzP~?t+5I}bus_gffcIM^IOr_2o2a#hK%6br~&=Db!WzzP1@_~`&TN(he5_b +7A5kC#AY@n81^(`R7SJ1@EgwAAbI*_@kBhdJ$-LtHq!}0I>i5nfUKVgZqj@{ +M;$tAI+J&Q+Fyz$v6jGqD7<*fpq2 +-PHM@F9&41u6vjrY3SgCySXO;T0?(@~qFEz^5|PqUHk`Y0QJZP#-2^c1Z5GXBlt$)OC7xiU)z_w>?$j +Qh)}xl;>f=pFB^Z>E6y9F#j(P(nSHc)izFaJohab)g^AOKvS6h=UaN7@(g2(t}|NYj>lOf~9Y+%8PLMNyLSf!| +3S#4iz0&KvH>dgcjF-@^Zl|wrsVif;W!HBnbUk%l&Vc<{Jk5<9-q2@T)d#bsX-38tHRl)TfbvCRTsZhtrpmDhmD@Q&#jv?sT+K8!R`%uLlzmZF& +}6*JdK{>Q8I*v%ggFS#$urhxf@eK|ZyU3$M6M|0N3if{M)g`t$I@%-F2=0YH*z9>r=A&xFQhhKCuF^6m +fr9yG*(DXS3SgOvVyM09p_W&}%H=TQgY|Ga&~M0l9G7^I(4%B%{j&QNfh@i8rd!IS|UM*`$$P_5IF!O1>e&(5m}yMMg*oj8krEH7q7OOB+;ThNm^`tw0 +kV+=Z6;ku-;=ak_FAaCIW>+Rb$eg5@?Q8m{W=tnrJX{`#m)IrnhY{VxNu61W4l>>^_z{h;7=;TJ2p=D +XCtQnP~uQfLBRKoH5c|k30aO|DvMb)6H!&vfh_R81?C0Ai64LFNYqfej~lzu*uRXH%dLM<#;KV12v^J +T_*ki5^Sm|m+!Y;_WsgZeaGS}99)b4LXH;N*&+*6(12Dk@EWjN671~uqHzI9y&>wIIcPk|dE=f0@APa +QoIf?4Ug_*&TUFp_IUiw02?F;N8ABrEl&zDfKdES($?%=K^zm9*{vO(?MifQ%;G3@|A^#sXY +@+RjA>y44PzEjdw*UB;vRCyL*S@Pm?$MNAC{*_J04v$>e1B_{V*c^nw>Aq#_NZjgHwQWy`0S$%&>>92 +K`fm6UHH9lyc^hX^UZJqt0o)5vO9u^_=NQdMlFHiX0{9sxj~Sf*L8t8?|TcNf}*P?;?*R0VR&r7^ZSo +L$k;Pt{t+LAs#Zw0^B+IkpOb^m-O +)3nOgwBgeoN&}W#07d*dLG4uk}Cmy-+HpVpehPuyZV-Kit%=4=VRFZo4QO%QP)(m&qCvTitjbC)2HAG +Vzj|>t!vuS!Ww2zz{m6y)oB437AVBzGB$WOaO`>Su>Xh!Y6yX3duA5j*h_*wA$Van!FL<#Ck+1tvQpG_T>;Q6_%&TQvS1dcg1b%!-!q12(YzOUGK*cu6wQpTyq~EFp +)Z%Ya~tbDYsdq{OmbPXVqVLc8o5SSXSemdTdO&En~Iy((RZwU@Qvkoipw#eLLUr72cA3OCG#7UdU8jD +tU$3oFn_RO;~9!$q;$zc4{b~v?ZH<;<@>iQLT9$LrUQh`}?ae-)!9F>mD&$RA0hRHa`@qGUed4cxjOc#V0D$<5d%y>{0P +kxdTQP%sqW(KZsu{sGTIVee@_cB0Jjw4=H@!D0h8!>Px@$eIx@A`doW#!KoO}oRVb~^EnstRU?krBbt +4as+dh*64*#TvM8G&b|Og4ZmBn>^wu&qJ8*c_oDOp$#&V~1BH)psLRwOoxDt_u?O*m`L~b221GR94}Q +zyqTo;y0f>d9iu-2CA)IyJnO!qrk<{9%Y{J>!gZdIO(L%jv`Q;p>zUh$@IyBqocj9NMYR@e_N2;C^l< +d>iCCdEUEmf$kpy$EtciMcKE|)RKiN1SidBvN}}ilQ@b9$zT^>XtU}#q92HmuIuDD``~@EPTOhy+_3N +8?2e^kk0lPcLyCjs9$A0>j3rl~a(}ggQTb?!6gogLM$6}k&tqg>NY3Hd1XT@VdArH}`b_O!$g$bDGF; +hryA~4TDKcneoW=EiRgZ>!N1*jWqh#j*tMH(6Ub^S_rCLc=+Id0Z>Z=1QY-O00;m`Rt8heD>i}r +SpWb7bO8V;0001RX>c!Jc4cm4Z*nhWX>)XJX<{#5Vqs%zaBp&SFLYsYW@&6?E^v9>ef@XaHnQmNJ*WQ +zSH8KH5?O0GA5H!0zUw4T>Ql$gv7Jr2X`V_+w9Ji6>XKA!cf0@ln=gR)A|Vi^@lnG~Os=>VSNt2aq;m6m0iXHk@vQFNIU$@n&!6>(N2(|#0P6iE_A`9(CjjEh+UPpUkM; +_NnBBt@BL2qYg@ahj#sEQ+IO0$5`CsJaAzWqwgz$3=owrcqp$`6P`2ax~2+%c~@-;wohzI9z~0x4MMF +oinM|=}C*xG>PYcV}^jC#F+*hU8mJ$zN`=kbgwGX3Bv4S;mLeCMZ8p*Xr5lB+@`>hals~*Wzt6=*n@s +_l~27t8THEie1gG{q*y%L*Qr_;8YBP!aq0*Sv_zWIh)lDfWj)!a%ssLxBx0aPX^0MzZ9NUSH +-{SObC7oy57hSQZ(e0E0&br|@5y1319qAIYTRr_|O(KA-2;&~e5jo2J;~a#tq(9LmJw{BuHGWs-`ryn +?PUZa8y{9g~Kxpb0y8|K{}I>?}GtjSgSG +IXXOe(T@&~pC7$_ad`YHik?B`PXHVsgG +Cu_1oG~O1AYt$>;n$_&(9A}j;WW=Pma$|;ddWK;`F=*@$T^K0J^+?dUysMKp-zqPoOOve5i+`dyJKj4 +;U6sj6H!+1b@Fh!>=@`gu{!2{Ud;QhV{+zOavDN5d3I#v8vw>GZlts|2YYxGm6paTQllTud(0&q+C;@>p#&`V8v}U;ymBsPiAE$>$V*{s+C4cM*Qz?e^f?!FC5 +L#Y^BA1=PXEn$P~c+}=(eA+POvytYU6roi5?7S;QY7w~Hrc=d3&wY{^o^EeuQv-@agcY7E;*ai+Bbzc +@~KZ55klQ9+RMgKuyCZEf6N^lU&;dIy7Qlh6%A3uF!L4C|n{~lez0BphFz!IA$SL0E*`uJ_`bg>_urpWcD(RqFiJT99KXvj9KUFndftF#}>9G17G{+`!gq3uflNQxrM^{*W~F}=38GRJx4_hJu;uZrSOBlSo__sRax+O5m7sS}6nP;fp$H5Cl!1Sb^NirY9##&@+pBq+ +eTsk@-lf?zzb=sofuR9~JsE6AMVywv;g0hPfWqJ=xJ44=ufLMIBW@K5YA@>USF%%3p?;`l&+Nm7YL{L +`-QmOGqaJKsz+J})HxKNV8R-OgZc@M|02l##jxO{0l(!9rbe23X5+J^_+pBR7o753t_ohfc0|m_z>=% +FI;y(5d0DTjgeov3+aeS4Sf3j>>|gnX*rT=@`B)ZJ| +bA?FObndt>F}^eAxaNeHT5nXMQvqFVlII;xdoRNPYljK-Y;~w0;N3<%Le +X4NI`+>0WgBp?caBsHGrlTU!~>A~}p(-%^|hIJpfl2^S*CV4T1O0SOJj$q$9d`|cx4=SR_Kzjq&*DRe +xvusHNgpw8tF|fxTzISW>e6tOoj#+P!m+6gF^XspU4vt@)|1dh;fBn_ISs +_3gZuz6*A9e9z5I!--F07Gsny6(CY~k#u*Sg|Puwp4Zh#-9Z)$<=tb%lq)3Ytu#a*<3>j-0}#JxP#iY +b+lH>k@%_^Gm7k)zc)7;b? +)GmMg(3Iqk%r!GNMLq7BV)se10TU~!P&&M#Ps`lB*5z3<@6N~p9v-VjIGz{$7YxwK0-kzQBpN>Fue!Y +JVHQkkKG(G(G>C>o7Y^5iXBy%fWc^JZ64VkL(X8(O%!)K{(U%QL=rf%-V!OQ)(N9Q$wy8S<7yP;kA58 +84-H9%<9?E=J)>JsE`)j+vikUR)G?%CTnZ$QB~crn7MJ41;`cv3&w*WZNi^m&JNKEhi--{JM!kJQ=>m +sd-$9|@D`N5V0}<=xfSkKDD@UXNE(kHsH%NR;4KksyZF57-z?raK_6j;dta2n7(RySWx3XhDEQU8$|b +Cffm1y2jw{W72>GY+R)%Yta0SEKEiz2YIf*eI$-V4vbly!9BIl&v^kpp^aoFOE3)mH4^2WE(<+Q+!sZ1)t5J#s*6P|a&sJe~Yt)u-U*mkzO8iU +l^C-X`U6IZY_fNPfu7=*?0PN@)k`}*0zX^q-Hd2~84jn!^+G0)>lhO9FQJ-iA%#39`UeD#Bw`+z2eo_ +zh)zkWa0dIkzBn&rsIgp_-gPZJ)5e)Kt>FB2NRd{V{p!S8GAY;^eYh_=%0e7J*>WzDo5`2~x@;^d;+c +2QfV!jiy$sO0t&yClmgN_N1Kz~i{&BfF#_nP4&CY+TI2vKE?5pujG<^w2Ji5)4kz`PtjU7g0oI9&MY_ +?FLeGgl|C~Om33Nk_|EEXRmaPoj{FOff|21e=jwLP7P&{CS2a09_+u+xI}ijm=Jvk-A+~^lbryE6@CBU`1uLhfcJoTRT +o=NJC-@Xc#S58SyIs<#BEujiDr`H(LuNO`}=&B1BSrHE|&9188m;V0QU7))8qoUe%6KkffdJoq;T$|Q +7tb@H4L3j=XsvNjtl#W>=bCzipeIhRiT|fi+Pi)(1cIN2?JeMY(zLp18O4adM$C8BB_=|7L^}%8KsZW +gQzQN?G~chEqlG_esuiyNOb{3QS8E6sJEfZ9pcWps4ochi!_^dNfj1sfqWLxU-qJ{;chKGP&Ys9Ej;ns8-6pJY8|Ph%v0U6%mw2i|FI;5lcm^d8+~BWsv}u&-7z8@ibcu4C1Gf*HSDXG)po$&{p7Z +>ApnF=P(F{5}xh%TGGJ8g+YZIjUph*mN@vclyCoU&xN@5qCuXTEO(9_w5Cr^8IO|cOE4R>bKM5sPw!i +)|x*t*l{4}bW +U`QSkjO8qlC0JQPxNiD<$`|9yQpr-w1;AHOx=-)l?=q0BVXwC}9B9QsYLy_AV9N@(8Oft0!Db2yNwYw +IS%MbCxVRle`@jieI+o?V8_|JjoTihwB9#U3LUkpbpeUsP)hiDLL4Cp*DGY@hkkB5D)7e%65NkY&WPz +fF95@ePk%fC7b$1>=aXYS8(RKuc1mqMwjqcx%q>xK;CFv~pw(sYi=$=`qA7#CsVxh96dO&-`4g>TK4a +%w3UrcKNF1;-JelG${ThOQ%T)iH<9aOwPsK3T_V-h2)L`KWl%WVhTM)dyu`{*6W;pr8K!$J<5C6MH0{ +5j2+1s$}*WY1ujKi4XZ3x?)+k}rWHGKwdH7GXK?c@ZB44VKv=o_y+dj)3vVBYrUN(BJ}-GrNH^fn*UE +MSR=sHL{N80W1e;1_El;-3Iv4e*(om{N|gUi{JO(TkOTG#JtwtOcZOG#Pi8$a`~xTUX|VP^1@^y@Cv9 +Ok2mqK=Q5dKDdGHA363fZyJ(ECvf$!2FUX|Q52&(WLwR^VA==}CLd4JxcQQ)o$PTW8`%b+prKn;WORC +bkV3d(r_OIpsvWYZ)aT`7z(s!)bNYAE>pGM#}`3mrpk9t5=LIx!HzeOHY+?0(BkumPm&{?H?rhpVD8t +r0KM2RJ`nhlYobo<0Mr1q?sz!uOBJ37jGiA3sq=hL4}@J|6CT>SX2uQ82+2C#>f6n0dd?TO +C_#fn)-8((Y?@`Iq|-e-YPELB#jQH=V9rfe7^xmXb)+cu69Cmr;qXFLOiMb!?X%Mn&MQzuT9BEug0Jr +Eq`Kzbp2gg_n{FWiZTm!hPEFNY#q|yUzh=L0OI9Y7Kg2vOD=Y3g#ES*2+Af0Y`9&O#>j>X25L|xNp4 +jOO{*k9y7YotwPv&t+ZX(Pu4wN&EF$>RP;3(S5W>60o`Oq+cgPRIvHU^@g88BWYSyF(0S3B$AxFuXm% +o1V9=snj6_P_$S%#eA~jFG~pyy}8*pXXPoT%mu?+Ti2QaXKgXle#uYwxBUj8=5&o>GW#t7UU!nh&6{P +ip32zLvjZ&Tl4f&qG-6GLDNDJXwaTaYE=aCwn;d+T2!|tWaE2dZxXB|&>P@_P?DhnJ-sXJN>yx!e{k~ +h&R82eW-IJ~MQ9!Pw$Z4GYL??N`R^r2gLef_-p}aNo=tUAj+cSgX1i6F4d9@TWw3sk4*B;*jOR7&Rk= +H(qx||P`JBv=Zo6#zG8N!Rw(kyd0ScG$pSeunTJ}++=>IlJs +>|Tfe=s7%43S%{1tbm(Fkq-qfxg^<`6xQ4ndfCfLt-`qb?X-Z&ZZ)0iOvxsC8GlZQQlZymo90 +gRWJlpOTnc?Ab=#!lT!WlHAi)OdefDd*KnP>_yJPK6XqOage7$J)LrAh|Yn;f|9HZCoE*&zJ6|6tQD# +DNQB}hFm4uDKT->P_#$T@D5RFi>W)si_tY>;;lH)5+Wty~-*&1B}ccU+5o>M;6_^p)~Wh5?t*SfXS*0 +-&pKvW|{)yVlwm0Si@X{T`{())G{&eBB#Abw!&G8qCD8-}z0xj6u+IjGd~}C{J$Q-}KC$2N{b(pctcj +9NL^r!k8viGO0Jz%fzkk^ZJHf@i@LI@|)YPJa&3iqpIrR!KHfIwamEGdpaM*&mPS)9o)E+LM)BT;8*9 +Z(WLt7GCb))jg(VBNGKvtYx?RJhy;VNhaRjt^>W+A4cX+BUU?JiA8R>N-I@K`3Fr=fJzbw2$bX%uW4} +P-jRW_?h6PV*?jTqk|0U{4?AEWvRt@3x{`=9pXNTu!cLZV +KFnWFPdUSN~w}YcQLfJMb0R0ThD(0@6CwkxE86UbJV2J_#)9v+o&XA2pVsh>Ly#4T-pLd22f8HK$ +}YM%4N~v*~!+^r;oqg8tNmsm*nqnx&nCpGLhp`gIwt_6V +W^Ka4EM=AZRwQ(62t@W)Hh`M|3`JQ#=2Isc8!6C>zPIG6&>SeHYfus$0-E(chIp|Ypr{H~J0Dl;uA~j +Z3mPX!F#wsqw#G%}rKm|35>pWS(N34bzIp-j@^xHH%aueK;_3D|nOR(K$k!d(8`dK-b9NN5UCfu|r7g +jg#pR|XG@AyxiuC!oiog%}=ss!`NB +Mq|#g_i~i)F3!ifcNJ-oknT#iCh?f95eR9D>zU?^b7A>zlcys9%8PF$G}n=2dh;=4E-7Mo1_e25TXum +o74mJpV9Wsg?xHV8pB%9kRdB*FX7Yk{DW4@e_`1#tiUPB&y~ECma$s!(xy#Y>H^pbg}Vq})5H|NLZXw +qxtP+sDH2rirzB%)LBs~H;2*Mqv7ZalR6MWL35Cx_Ml=T9ogVU~TF`Mnee^A-OD$Mz<>n8&k7oGqMh&7{gO=u;QPxNepZvdw)lP^5XaY7@ca_ZNp+$Yl7VKKa+T^>kz%a>rC +XBOLl~PW2!oc +w)pPYmHU<)=uE5Q=IA^0So2gL8HAzLg+e|x0aF9>4)fTyBZC%=VY?y0PMc8JOWEX7Ei>sHfmqIEJ|Sh +WLs2>9@tR+lK4(GICn7&Hsw5afyJ?0ou>>#LwfP3Zg#p6?!7jxICokLNS7{?)?yd|M3iWAS=Z5G$S~Z +>0e6vRy0rb7n2r=aq;R~sZf^v?i+9|}x*T&P;4MTo#2*i>bTF%F6x}Z^CMFgw$-+-E6lI_UqRb39XW3 +A6k?OyQ&PonRlfM#b7*vc8ctG{8`o@z9S3QsK<1twqFR!^Gjs-Bdh4%Q8EZ5mgxYe=mntOt8M;pW>(m +_7{5r>y#-${QBj>QYM2)hLIl?90eZNDO(4b_5U~WsG5flx1r)biXgo`1&v38C{cJEgJhW5>tZnA5Cbc +CjAAI**KT3<)URKHzMmq+dJasBRKDz`|JwY{sLbL0Mc#~Iv*^X4hS_0-a%n>=9~;hL9;n@vv5fe|NZPWEAE@l)u) +L$Je;3CVloZ(k39p4RUtpy~$N`V7&IVJ$I)~8Ve-Bw4UL6FSqR7C9T^Cx=Zt%<<#rWTc~Gjh)s*rJE*(nJq{3P^u9!WHKiy3(=>j +t5n^4(>Y?^Wd}T?Mf4~;`B?7mtRy({KVST5;hGt*Uy!DVK!PazFn-5wPM)QP+#?4*qMxSL|10*7or>D +*-8L+PMxuBsGNmmSJURx!7emP*nDvh%|%?4P=iS3m2s0@+St3> +T7?bZ%~PdfeS~2VvX$oKDfRPzN)@OPjQ~*x3;85JQor?0~waAJAa+j*wqml<10w3#88*3x=NbEDdYz6 +buYamGs4VD;rk8DN5P8n5SFxqA_YP9n|mlq7QfxmH604+ca&u@Lk0Vwn+1U!mRw76uE4v?)fwn9-#r~ +Cg72UfNZk`kVWO~Ot8Y|{LmPp`ZY=z<{XfOq59-|qsRz(!=xk%q!Agap`+{v~F$pU6gx+?QsO*kQr+SW;3vF@kM$x)OHF(?W$Yq-}K0*$KXNzi;>+&=X{%94 +HFk`QHth=bcsKqgx^oV;%f(ZyTmJG?#9M$~ZvzoWsMsXl8Y5?pV_$OcJW +xJ=B%?F#iee|K=AR!101Yu9pcLelqcGOk=c{QH}o-qs;)WSo}lMPuIo`wwm4Xs!l8b9i~v^jOq%fPHT +DB)4RZO?ToE!lMIis0KwqN_obTwn%&w9zq3$%;1{w?O`cqYG+tl#!zU* +sqWJO>zO!x?SSQ_?x|k;+A&jd$rJ!Kn)s$O;IA~qVx +DW@MQST=x!1Q!3+r##>uzFk6eigFI?tl5^pKX|Uq(IVKUKA-d6xXh|_j%Re@tcMr-zs%jt3 +$}Ck*Av6t4gN25p!_jfcRIsbdEvz-Xy2=Ahz5iPW^9v>`MQO1T7W%6Lm0kU!}cXy-JwQ~zxsGFSWP4k +W#RC>k{sT?(hkCs?m6(+!;^QS+%8LsW<}8=rbB%fO=ycA!mzNB++Gs%G)v57u{8`W3iOkdcqdq)Wktv +eum*=+;5RB;+65(e0!i_Uc8gjH}fj?9yaar(EfBSWKPQqy7oGIEk0S~yT?-}z +z#?a*0r&7+DI3db>T-81<0O^TZEWVwV6#sq0OX`?HO*e%kG8xQdBFd$Al4TE#1f$}jijkFg@C@cewA& +bjCdvL=w)_`jP)wC57FrQ&Tk$_>;=Tm4&2v|qU;+UAAbac%D*Lvg$Bn8#&qFLZq6_I%>I`yN)7_1 +WGftSKC;L(}d02_-Z$#xNwWg!hps6{&Sf>Q#b$9SjgR!eq@13Hc9sKa&UHV@Z;$0;QSA<0sLwG1HgtonQ(t~zLsp2rcuhmI&eDqnjkP3}P*;NR?$D)!;}@$246xQ +M5%cm|v3todU|aL?+`C6>A{3@Do?FN4m=TQS&u +LP{>Ab|8k4qd_-2>Zt5Y6Cw#&KdX=#Ppni6Toq*eYEEW5NMfL#BpVQZd+WuWweiUzT0IoN2reJy>4EM +XfF`OL9qwCK9ExEy}@YRL*lrch^-%?j2OnDI7-Fx}ak+qTY-E$efI|$ +VOn3Q0$kH~IM3M5Cb*)wb{vk#e9mF7_j9E;t%@&yL{K=z7x;V=dI-P0h)B$3Moa{gwsJsXoF3Re8-)hET)fm$}AV4V(#qaKw(Y*pKIQKSgzNc8^gv@9}V!~(y-w>10sISMepR%FCJ{J +arDDmbB{Y~})pm3ebiQ_;;Q_jha;oqCo9d=c41Ah_G?b`G0M*pHs-6$jBA-D5y;NuqN97c6v`0W;%IY +nlE5y^9;iv*4sbPA=&Wbtkxkt=p1i>14zNaiq_qxeit8NEbzZIbf_pdQkiYkNcB6qZ14RO=K9;gf6*;I-${{F}?V-+BvZr#$ +sDvPUopK3ZW+0#@k;KblzpU +&kOMhd93{DKs?Jrgx}jON{L*IXbc?H-sdq)EnY$|B>cR@6VN1`Uv12l7=vpHyRj$rR7@Q|Jtd02;@u5 +SxaB<;8M&4Y9EYNVtr8VF9h(RTcY_}^%LT_p)?&mq(;P*^Tbwgew82%)q*8gZo3NwZPVnP8nzdG%T1-Dli9JsTO_$9Xn~(S#UC7 +TG+kUqmA7V->=P6I!-nR9o=T1+S5GY96;9;TQ}kL^xl}3g{|X1=4s%2Sc`=t+U=>j7YrMaUI|G@i-7o +_mXzf32Cco&;rD<3YH7WUT-Q9U&M@5AzN8-^_KzWb+EUzjp#f8{XmXOohcCq3OcLs`B#YF +kwF?<16W%znhAGXW{j=wXhk9m={$V>DYVwl*s6-K&isx^BYO|9h0yT)b*N?iE3CYM8YOKg!z_`<`%}w +gd!ySS*bIefq9O=q4abBvNdB#`=%$*!4#!wyciq4`)^CGQo11Eiy-a1<*Zi@h?pa@7D#7f;=Bp0TVN8 +5oGzKKc{g{XgYk|-H@$?RHkAN6gPV(|m2)O0n~7>f@yKuEN8lk0caWNB0t85&c{mo)3qzL~aZ9oolRT +9Gbgf};xLUB=<5FrgP`|Dn9ELCTk0Nl5Ztv7NCEJMRmw@DRx!-=?Ul;(H}vJ(LTlKQOT_Wv{{d+ +@8sAvnJ{QAWFW@{lokw21`>ne$fspWX}2dy7fSVcbVF{LCNU^vfwHky==Q$TCv$3_)2`FN;;npa)?VB +^3FggnV@sQ1HiC4bwl<@FKa!;F>&n`}_&ICt#Pr>{zvVE{8PH>Or1oR5}WDfl>Ih;>yPoPBOYT+aQf6S=6jNI*ph~4)K~`d1L9&tQA*PIVKPFUQH#&8NjSG=j?QVgD&EX#>K +)e#7 +_92K1b1(jSAD%Hq%%1bz>W09t|^pT;$7Q`OS-}3z{1(OYDw%!lhZx+U+}SyI(>(EVY +}3bVUbkgXGI40qP6qW0JJI%9p(0!25BoPsDN_-Z(VmeWo-FLFWJvto*~>(5Nx3(^$&rk>E=-qLLB;m+ +!|?CP_-R<{S;IM2Pq?A9m0?X_%F#UP@__3`6YE7?ci7+?kYyEiTIFhIw7=;NL%xr}i2nO83K5u=-{^2 +3&{zx$E%ZrV-K4dktdPaaVc=+3?EC;89Y*e(MJ+?o}R=A{mitSQSlX-=N +BGv{tQ^dO4jBAO(UDUM~h3yYY|7aRyV}EbVQLZxWdKUD5{>!ymXp3WK@Ylg#|LX`ke+1mkvu>y(3g_& +cUlJ$(C_b+wDBRJIw;^(=C%-@7{(8p)0r~MR=1ze7b3ik)q)}d6(wlXNCm_JOLbXdqP2@v6!(h0zv%U +Sa9uuGpTh3?%xx#z +CJGnOS;K)}>Uo<69FRMU*Y0%V|Nfcv9_g-XY5S4sw2xPjH~SJx*K&T6}|rZk&=Z=Uqg?YR*vDD)}I-6 +5#x<(i%3@5CF~KNiq3DOf(vMKT80;cD=@n1hW&D0c$uz^8EhaEI9;ZJpEFCx{nEjgI?z4&W)HdV+8FFL_gpM=MM5?p)@X|qdK+_;Lnnbc_Wu_`bu$hdQ1??J +S=NB{nuZQ3yC8n`9-T7ed!%dnKHeGh=&?dCD!k~Nbp%0AMjHm8t?)`>8)f}%Wpu!j$LI3 +Mth6nhVQM~SIW$&8CB4)auY)i?1Cr{U{}*#QlabigsqZ9AorjgAO2OPHQ$Lm<4buFws@v}LWiwbw{|` +Q9!A*Rs7g9K>ofonhB}AoK8hclUq|Z2kSc?orzoj~C9+Q-bz$__9(EY<3*w%WJVAQ*&h1RtIx4jdS3p +5?fxpe48CJnWhsJTZ8+{UM-FX!3NW9cHCT=4tNYaH_4Y-)x~5Th;w@*PR`AapHS0gR$5!js+5$p;iIR +!!^clP+Gt2I_N4JNJ1-0#r@{79bauwDB$XM#Mue|^16`Q@^?30>$>|jkHsrTF7g^~X^C@i +!ms0*2?H8Z;=hy8{JR4%^bHxbx)k?#{PsRGhgLzx`(S;m)_KD;j>fYE+=!?!$-QxT9jY=IW9Up_NCE& +62grSTKS2yN{bl#$>D~AhiAM8*;J|v!oVa*suvK4kVphHrPvsQ%mI5A^Kws>zU3e{|m6eOZ&v6p2fXr +U-?y|7_Z2^)j`dm0daD34*YPtgRY>bh&TXCck;<(xk!OWQU(Fp8JF48%x=kE!;?haHmChoncx{@_W8auBhHTrXcK~a3q`^7eKTA!H{%ViNNqERsZbG{^^;^@%R?!!>3PNLm1s!i2O +jSYmov9!XYOSUgaSCmvCA{`DBV()4%dkKVV?m41ZGB?MCG28@<}SzHgr}qRft03e*+tQSgT#H-jEU5K +fX7$xuX^JNcoaWwmE0v4d|399A1fl@ha@hQkJkwniysxNrOB*GR$eEfiyuMQ7zdUJ21;rqfl<}3xw(E +4+F$glb=#%e#l2{&7N|nQI$%yE_tMm;>01yj>9_Cr5rD+LFLF$6K+Y~jqB7jG{!(9~fO*BWO_l|oG9o +TazpY@&WhsIQyBKU;l$rt$>a1?{G%(D#}5$?> +&rOgQ}qbVY(;w39HP`s@M8XvhpX`h=zj*Q*C%xWyH3wuh3qj9gHt9;%nB2*aXv8uy|dP#S906;o#6RB +8p*bL_6H5q8lNk<|b*y@Q7}fN;kVs=X0ZFLPfXma1fo={-$V_RAJlMVc5ATXIM(j*|Xv+x$4tpuCCt# +AHA&1ZCGZ;BqPWEDI|r=3u!J&+w`jXvKTu0_#Tw5)xIHiwm9r!|7O_|nX0ksR9W)sHg2b_5EOjSB6+N +Yj9t+_DcW)3?BrUf#XD@=>wu%4O>REah^c*D~Uo0XInW>Ihkw@+DqEsu +y3SV$oWBU`BC4y`Hvu8v7)-)svrVS{5^;q{EVKB=W8;J}&N>-mzc_3Z7NHz%j(2QPvfEC{Ke(uK`@Oo +riD6jiu6<;p~b>O1-d_71D4gSVQp!y9eCbaq#Em<929DyI`>E&nE-mjD!Gz4CMc +0q*hQ=M9z>rfpj63mN<)8ajI@nu(1lQrZyOH*`fwEeN3*ON%Z^Q2}x{qgqZQBWqU;42gfJn%D)bqrfhY +oMUCQHVTpKJy$97rcpqiY8B;I)(&1((rzG?eRsj|LvcL~Bm)mpc^NHBcj)x#d9HwBjDR{?0Gs@A>GO8 +aPA8amyTIH*W^yqCSZco%v_-r+U}Q`>I}Tv@%t4u89HZ{I==?l=U^-iLKm4DcvyTsYKW7~}pR`ZG7=k2s48 +$BY?_zXnsC1r7bq5r#q&w_UHkYvb00^a$=!*`E#t8`^*9m4h-^5#cI9d{x=@wICz|4}(fSm6;>@L!=Z +1Qv@kj-x+ZCqH&aX9ovLg>L;H3#EcajyWPhznpZ7EN^y&rY@uPov@XlSfY?nBQ$4Z;C3(4P28`06jJE +Fz9yAa#bORRb#m9K{u=I>Haw*uZeum6W&@EsZo!o!j+jgJA!ov?8Mt%%QtlB>d(;`()taXQDvj8p9 ++y>)Q8T1Kpc~zr$ieoKWR~)zpRM)Qeyd?ZFz_Gbhp$vKrl3>SYQo4Coh2K{Yn9h@;Pb~Tx*-C|{ayWdLZCosb<);C19~V+`6C>G?4Uf)8zuSw3gPn&+tYSJ+ +Y#WyzZX;!V!`OwUm~~RKYT@&W!FQLq%qrNKU{m9JB*gDrNS>FREYse=E%^>HD=;W=p_m(vSztGBD!gZ72wzhSAoQ76Vvs$Eu*Lt_ +LCRK7n~Z5Zqo1! +EGNwC0{yNdV-NPs(5>dyudm`j-1w7oURRV@oiUPTC01*^}066ZvdV5wJDq!H8E-=c +On$DV{H_*q4`A2~>T%)D8dS%9J-i5ZNZc}O36Vv;0Jqz~Jw8iVp#oVko==v%<)SLU=-MZjAsANJ!hoN +3ZRM&*VdSTulx=9>hIBKe% +=OZx(!6%55S!#9%a7hcaYR5cXZOjb$JHGK*xjfCNK*eJFsx$0FjbPi6s61oH$KqBE5?$88gGW|1}5tX +)4n+<5cZ(lz@B^Q{Mo|zPe_)wYo*^xp1tMe$p%Jkf +Zr@8RXj~bE9)%YB3j&M*QK9f$MX^$=8Wu2HOPf}!KiMJ<@^Fc%-n>8PT}!pEq%uB1< +vaj*jqX~*Sf9A{Bf1*ovOY?XyHE1HZd(`s~&t!@5OVNN%Neo*uk<3+g88872TSy0k$M{l%<<-sCGA-7{`$)K$ydtF@} +0{q)+cI?q0>RrTojN4M(aay^8f|KK4!p0C&Hvm>w7i!=}oJXH7P;mZ?mplUywrs~Q(RiDmTlazd3i!! +>>>AvbhU*prm6f9P5l{zJ>IK}(PR?gX8K_2szwZ%wEpK&^p3zSpPtvD`JdKIN?Tbbv_OFX8sGD*m1BY +Ms0^yo|&+1_^4&GP7~oRvKU{V?jH#hQNC0E^@T{-Z2Ke7WBMh!zOSS5BVZIpCR5@B=(ImK;&y3f;VS) +zB*O6z!9dm(QvI&_Lmm&a%8nMi@k;bdfP|DL-X0qb2B6bGhdeG`aR2AFY!0tf3MWOiV))>!)RP+ktV9^+VrRR +AaJupvH_)Z#cxHzezMQTuz6bM5ptwn;fKzQqEY=^2A(L@<>Oa4}fb5M{j9fx-lSG1ye7YP&VEZP%M#Zx=Tv*StLlPJ +(Jr-zu+ir4x(Rh2sV+0vlmdbS!}`>T3AvhUKZ`pKME8}z9&PF`q5^*N%vDoosHBb5u>ODj8l?9JJ(ce +q9R~s$+f%#s=_SCLixJA9FNZ#T??Sl0?j-iZ`({^W&a{mOP!?TS$#YxP0?{DnOdp3;j3yHyCG4VEr4= +gXPQ))sVGBl&3XupTRb59>4r<9GaE4rE8IOf1sf+S)Y}*OiqP{Fm+KKe1~?o>6j)}e?Yo^KzIHVfKh` +1DP+@am40%p@l1!1iPTa9atvGK+^6H-*%p%b##&|dw)7J;|{Tjbtk30YRfF(n58)Z~{9o=Yp4}t0|6L +~p6HEck1Qg+>_nd+m1IAlOaSX8C3B+p!-U4NRqZbKg$3XF7#u~Vc@vp$|z*(hH~q^Z79(KQj#i{z%N> +pHQ30!OzxOfP3=rXjkFOVTY>GIvSV5*i|L$yVki?}`9<26^BpOk)U~H7E+GO*IWYwc`fqj55)lp6qyB +QbHdN?5X-qf{tUYOy*{qd--M`xE^L0R8{A~u}@fKUv73l;}+EZ&R;ADYExVRqTV~@n+3#_99ICcg-qj +@1<(@zRzP$_Mf0ZxQwt?4;2FY;?=i+DluV)|GOHca0%9~p^lnW@OY)I+Q%dVH3sn85n;VD4%at?5`h_ +FG;z-sq@v5;jrRVxbfWmP{3kDG21S~28sKaKM_i+|TGWu(vDmI&X8JEr}v75nfgh6ZSHW1 +)zeL=qdu*Xxr~mv33Zu@8VIJ{5?wQDlW$utL}c}7hG4EKbqXY4+RfK?q@flZzoggrX}GRppkTC5x^4> ++EN9}K2Np9iP?>XC0Y4Top~&h>th!4z3o#QFhoH5)dr)8NQ2A;O8w1R{AjQ<)Fp}2jqe785>F2$|wS$ +G9tDXlwAnN6nCp$f=X1375fKrlyO +hE#g`R^qn4wc7tRa8G?il?4V=ytbK*l}xGmTFtM0ybg9YanI(hKQ +IMPvbH&$voB#0t%46v6u(Aj77`Zmo)E{ +NH$n@u1Hli9E7q78-duREJ<^~X?urx}3QBVz1|2A~JOUmHtx+Ius7iVf>pY#SCVHK%GpG-7fc8Va-ai +lMj;4TV)dFci*c{&ru_XwI}+VyK8KlCWZRh4(Hb@|!~<6r-Z1hA@0%5?yf47{&IWGyrsgI0F?sBeNEi +>ot33%z_JG-M+MdSi2`d7#%y?j%@z=5@EC9ERfbD=lg5GbP*FT6Qq*Dy2L>{zI7|@hx9WUQ&_b8C(+h2iYu2jOhoXXv_zu +-7Ewfo+kn>K&TTFiD&SY#resvMJyIO$mN6+i!OHX=jBKVzpOM`3a3#$7KVr!j=%j&Kp@^^?6^Svrgd` +J|5ALz=^O=!f#(p3BUcDC_z@5eeM!eVpku%7^g0dmd5+TUNkzRZ{AocG>uUek=Gf5Ft@5W@Sg{k+im1jLG85 +0H>`2$p>PmuKk=Yi5-rgqlNRfN5s@2a&p3TqhX(x_TY|({PCye)QQhO?k9IXhwQ6K!X;M%F`$`p2o2}gJ|MCg=g7FVBkAUs?YJvJa_S +=4ePX@2xpO=;qTuCB_R8M4)eRW{Ql1LC&e+!|p2DF!ReU7D#1CAJAa)N*$)7HVdqSYgV_%OaJ9!Xn-q +N&Jbm=Y6pUGCwG+u=#bTq{Dn(M=TP*OS;<&3U%m*8m~FeLdNYCWluC0`z@v|6M(Dkaf3-`iIAHs&<9~ +TG^k%Q9R}De>_VWtjSYgM1A_N*~Xe&0-&tNB-YY#YDyVB`}@wDBEPw<{TCeHi-1IIcyIi=GXMwHnZ#@-3adllU~=_~nVvW3aQ^@1+u*e)L +Z=*Q%iPq6X43Yp-PK*b6?X6-0S%Ft&)qi|n&R;TLF5HXKDNEn>(qtJd@Aag9Qc+HFx&vXAO$4Enqtt# +J|qQSIwr#42z;J~)y!KfZeH*KZmbQahVnenSpU@IMoCHFWy5sey}bR58n$twr7z_gYjK4j!LP_8bvD+ +mX;Rvtx;8xfu2S{oLQLz)(6&4i|i +YBQ1?{mvkuUeioTr#>im4J}D)YL_0Hz&<+tma_g`0O+0Z%as1zT~)cP2l$gX(d$D<;ZAw|$Qm4}Sv!M +w`N%&n2N#$?8Dm``Yav)1d~{KPlsAZe%9qIV9DXuQFK(?=LxSRFX*jqlAFEpcfme;viX9E??S48XS0= +t4BX0A*$QW%#(6sUyzeXEs}KCPRyzu@$pKuo1%CvB_ +ik?ZMN}jna*`oKq@U$AbUz<*P&`WK?#4V6Y5aL5xQPe@#X;oY8J5KgYJ*Ze7>wW$|%>BWkqpl<~%R-( +(bfOX2Tw#-g3I;`rWkn+&5k-k8CozPYYpn|u4PlDR +!e#&hq2P%kR`+-^?B?ghG$uAAUVl>3M(-``v)D&k%LRU&&s!q6K2Ywn(l&s33x*<1fvkq}XrL~jnndO%Moe@ +*sJ}fV*D~kEY(4g^=eFp)@7~T!eMu%so2d}zV-ir~D@rhB_fCA6@Ce&&2IUFj}bIK<-^}1q4ITz4ggD +(Q9KWhxs{w7YwSY9oazwDW>KG2_!;XqduCz2DrQqz1{OMH(*HH2reE$@H(CFGpUy7p{-jJfe~T#J5KG +nPRp**}_H`0y$NY>;#-HJIZVBl}*b*~1<2;fv?j@ogEoLDr0ZNP0m3J+_4?eAo_+EPnj%)jZ53c9ZB{ +9G(V=&P_NNu8lXP+p60)yZ!nwX>HZ5Tkz^*%qJ{OBpZC&+gC(7?o$DhGW +M;4h*GJbvw{KNxEusU3?VSJb4D~?9J+>L1^l$)x*WLYguYI+g1asa{OISQAb!@!@zcpQSZUZcK8t+T9 +v9CfQ`4nEu?0Jo8-G4DV4?*>LF6IVrC#U`#nhc)DVic5D185$qFh0rg@u?UvR9;maZ9zW`R-AS91$3{ +5{jHEy-uGHhyiX-E5nnNLUj0CMm{wne3re%8Q+bY5LIV7mxMx3l)KZ9L&pGY;u|}E4Dca9J;(qhVVY^ +MxNr!VVNyOfF<+=wkBpL-j~if^g|Z?GB2a8`Q{c{7Hws5SuK`Sc2VTh<)rx}$T4ZuF9OQ4Kl{?T5Lp! +*g^hhfx+XL0;XU{1-e$BJ%0@!>H#b8#yxEnJjvpnVD|g+uq@SBK&7p|I^aHdz3C=FwZCP9K +Urf_TT%k`{bk9whe|`w)V>h5h{5098um_zS-O9^qRr7(yav%0|2d<;;rvHeQy(5o2)QQ(252`?!H4ST ++xb31yZnK#c~Z;MS?bL5Zm{dvyh+v)G%*VNfEMc?N6tk`N}!+QID@U0#2OW>O82Pg{!M`cw?g%s59MO +jdMKV!Y%_|)npJhGFfoeGB~X;GQsCXou!y_ppYh^#|jjR@vVv|AxT~&;zRs7PbuGu&E?CaBFFt-Gz=O +p+7w`*WYlS+h->dqx^z@tqF<~9O*S!{F*Ya0lS>`nUrrGOB@n%T7H#`^C>U^T2^RIkb?qqY6pQ_P-@bw8~ouI4|{ucmC$jd-!VjQ(bg}&Pc@`rU)3;s ++-Uh%blh%3G)^BZj_s0j`{A91WZm7j8OB=8*|%`2nsJQr=a;=Ho +IEmQpEw9CF#obo9y%p{~{l53wWSsOitC3GEb0#RNt5>Ch~L7lM(MA#r*XZ8AY-LZ@~0#b-X9Oxle0@< +-b{&;vbm9k5f1Js|el_thww%Apx_aAwxx_<#;oAp@8p5C;}&KgB6zi82g|2QKPj@99hge64jEBjg+Yd +!u`0+Pc6ev!JqwB*E~vJe!R$Fit!gm-q8Qh~Qp-lJJuX{1j0$hn}3tK}S}%Z*}#5jlB#PO|7-noaS>u +sW0gRli1`Wfo;bt*`TtvCIqZvssKeG#JWGQCbu!=bw{gYFc8$Zhz61H|kMXCSZ^igx@73KDe`Bln!vF^fq--!j&|>*5DB3LYU7R +X$3H%J?E_iZN#tUmX&ro$WQ0R%Z^|`8V56X!DKeMJ{KR)d&r|rHM};op#XU301jiE@|491$nx_R*?l( +qtHn$+Hm4i4S$cb;90}z@X6Bz{o!D9K>f-bo*+34uo$Lr+ndK%<7phlyHPxFBto-%!QkCwuxAP0k}t5 +FUUGVw)i(yS28^yJdm4Vp@;C`C +N?tuca>255vY`#u^rU843ct59>z*3bqWiLK8`h|dd?=cTWheaEEOz?p&O_R7g;O;%s?S6lvN>5Lq{mU +Hn_iVl_kmwEV@mI`rNTam`FC3 +mvd?LeXJ{a64^+) +gSeAiIEWtH8P#gaENF-XO@97bnz#1%{rm338}%<4rlqqtmHt?%T+#b;Z51B8tL6#t4v+5jj%@QCYb7O +)zGj;Ljt$gwi0jJ(i2;271|bv~y-8s}&{A>*ovoK^5#^l-l`@pOx$?W2yNk|>bcP8zGLwaA^NU8>pb^ +&{~OO3d#i{-78m>{H+dRrw#p*H>BtO*ec|eiJQggbWpyac})ZjT`3m6{He!_9!EX*r5WRS7a1ofx!h} +()r+z?1gM!_Bng#5Z0bg+j=2EFTyIoTs(=D0IfSXOT+88H-WnMT6@%fqw&g}2%KaL|rUe0M1Eo%8F7~ +?+ss=r{Y{UKmZR%{2Yr_ti_woE7S+}nIO>&Vn-u}MHSGh_$ut>9x0OJqCQu^;*v? +>0~w;Qv*cu@0inOx4v3Yd6?%?<`eQYMZVWX|;S}=)PV_~^r0HsN4g_asl^XSIs +0&N*Vm7dGz^3qH@G#~xGe*M+`9FBmIeqDBcYkCFg3v4&L4g-kR +QQHNwf~yESl^`PVw_9a>ml+|f;le6zBZFfpy50*k0L9}fX?EN?+11deU@Z1P15+1whT>I#lYFVLAkJ$ +iF(6{77)6au=zd&}3_c*FXf#tMMDnf5pZjY4pQ+vt +!}LZQEL4*?&HqB{Tj-X2}~Kj*4~DFa|_=QGY$vzeryOh_>-`Kci +bSm)0R+t2h2q3img>q12`QYH-{o$FLa|1tCFmW~Zp%eI3gMT;ir_7hdL|;Ji!Ugebj(n+Br;&Ww6E`d +;4|5nX`A_gBejrz(>p6n=10@0k`2nPvQ#tHukLCbEyfM1Jc{4eZVCqr@Q3z9r +!zpA!h7bcgcD8NIqeG@4g6eBPH#uwRf|oY+5%lg2jcLU}`70@hx84sFJIW*Ljth^bUQm^C#w_!~?X?> +EsSPNOpf`P_lg5A*srL$C!E}7V1AKRIooVPUY&Mz>gdVo9Dv~f^WO3l-Eho_!{y=BOd1g}Br3rQY$HF_O2VfvH(%g#^CxNGD)3YB83rbB?AyVaCY|KvF)O8u&n +esOitFYZPE5%XBIiN`ZppL6>~|-NW0B=v|@$BazaV=M;5mnuE+fSZ7{m=G9uD +jrwXWQQC){J72Dy83bqDxo$>cXuWWB2i+Avp(R$QESSj62wV>Cno^Pb)?HI1*jv7uzh`-w6SqmoY7D0 +Tikb0E$mrHpKkD~CmNti?cVxpU{LAsPg&o}btGvE>4S5l~rW;*yg-IaOrKiyzZbM;2bswJdhbJ7Q0&6 +K6IXc&=*mlP+$T@PLKVb;_CKmFq1VPwTOD(`8!T_bvG#EA8xHx7(Ozvo*BE9aCfmlU%NHAT^T_uk@(G +%VXO&_ueayBs=VC(wfh)zF%NmD+rYiXU0*nurrEO^#5T8sgrYswaU`#OLXefm>o1QY6qCvq&@TLmp9z +u46|;$YoqydF;d=T+Wy!9IKyic|Gk90U=0-30xX$cL-w9kDW_Lvu5CQLZOq;p4&L%7oK_vBRRx^vGMV +Fnzw%eJd^itg}&*o`JT_YRPr8V8^guM&&Z*UR0@Ky}Pcxh66dUqR1bqou{V8>6Pi`E}@?M}zWY!!xP};-PS% +J+n@TTZlMeIXBWmps2+`L5@U?B45rfB~W^m7Ip@8(k$nB{z(gFo_m~{&IZD57CCI;LS-m-I&V-|g9o> +h>$-(Q(P*#$D4xo>1X6J$17jI%-H=bq3a?F}l-gG@Ju6O1T#!h8=>k%@$g?ROWD7JY`DGFJX-| +-J5g3VyPSvR-$_d>05gnb6M(_=ZWi+xyU3KOLNR^I1*crb~V+Re+cA(9i +pqRTk`DofiueG{0;^=Gi-8lLI!*(;@9irsgHzF{AH98N5Rj+4IQYLF|a5cWY`5@B^s*>1%FtVAg +SEnQW#S1YO_yg((d)>S!kdh!mhfJ5o=ayFwV$Q<0RjqblU=}pY!5sE#ur{ +tESdYQsc7o_Roj0!#3!Iy@5-|OU6ES|q#7ce4z&uID@#K>UUWa6C4)Wrq)(9@#{VdXlJ1YIebbxkqx` +s`bAe8$Anp(szm=td(qUlJEA?c1j1|!?bFSayCklZkM9fT9O`$vLnBoNU(Y)wyO>}GmH-iWO9^*96i0 +TCmw1~t$?Hiv4RJa?0>J9l*jWOpzaIBdLJ!cgb{Mn*DbS+1Em@oe941p{@XM~qwKN6@-%mElApi_16X +JxRiK1lCpDCx8_uUqE7ECBFFd!fx~`Hh4pq-v`P90GLTX&8d^M4W&& +Lk&Rr|?l1BS-(vEzw^EfGY`dz$w7c0=^$7DzaYb_O?CE@-Vlj+NYq8;){7EgK8t0!z!0GLo7Rm+Iw3( +E*iyN6J43U8DO&b?7x9+3R}ZDA=K#8eanc#>8nMPxueH(EM`Ju9;#hwAr +Y#)bF`&CM7UwMtBUtcb8d3{t(Z=5g?y(94-M)ZN6|n{euwravnJGju%XMG87)W$Ejrn2O6v{O2#pyhN>VF?MfxF4@I^Y$A_7lmX5E5}3Kpqa5 +FC$_!V$H1u%J>`Eq0PqqP50?#m4NAwz8LQtcutFqjGTMKbPSc40N>Worm;Ua1=q6@;`L)p!`DYt)ugxlDWNU{C@&-YgJtUb +aSC7gSU<7WK~+w)xPsGg~BT+$x>hT_jL{uj6i^?ZVR4B +RGZv&SmNIka*o9Sv>{vz84FL8YWeE0jBAId!+@IhxGlK)-(0`m9$^O|N&)y`LgqUB3ZF^DFLXdxafNS +aDSerO59c{K}3ZdCG#m!)6uA2j_ya{5XZQvNIJ7Kk#Kw%=j*j-#`N#-?8vv^7t7k+@Z=zA;Mr5`uce) +rhTlH3l}Lt318@;TUpQi7XtKPxg((Gv8Lv-r`Mz4E4wLfsKtK)P)h>@6aWAK2mnY{22+!UygEu0004P +H001EX003}la4%nWWo~3|axZ9fZEQ7cX<{#5X=q_|Wq56DE^vA6TWfP1x3T@MD*cDaDlYF@BsH`11aF +jNo>i%`om@XEu1nIqm>GdH~EmNXd33`4U#C7icsZ-3N_E&(5mbtZ$v`>+Zh4-- +zpLXK{PC-t-Qyx9jD7FRrP7;*b;s8@}8=fA;*@Fl!@B^F80J7OOWH?bI4)& +3d)%`KsUQH45JI&)3`SUrAI+)_g1S8@_Q~JCn)e`Ll}`53U<{r|Kh2ubdwpa3bQwmXo{S6p`T+2{@JF +42r6ORcRs5#Wc_N495?MQyLB}?*)lb4QSKW~UVu}%hEtL1Dzu6!D4Kh*7Mh1pG*1)5$ue+ +q*eF|m66yJV0+0M8HDmcO%}+`UrN%Ws^})$Yv%X(oHJ@NZE7gr$H_D+=x>`{=$E?aXiUJoP1#Yfa3tX +%p1P1INB$?34gd~%ARuO|mK^_>PJSI+JIT>-RBJv|BGpnd1-9Vd!X#0bTX-??FqA=9%hauDRLyEC5F% +cEgB4KHwF06=C%{3>QV^-v+#*m2OCko88C?-y3c0I~c;+TMnD4?U!W<|7qRQQ9nQ4wnCGQ?16jAbr3% +x8lz^9!wrg#dy@CNx+=(n)SP6*$aJnS!FmW +Ig+}mJvPl#pAPD2I`MJ-yT9*Rk09N75;WSV4JIw`a +?A(}Gl8X=mF5KRfuG_z)Bv`A*n&S;HHZ*7_tLqi==Qj`&FnLUsh!IpVObw;RV7PX8}%X|xMMzCcTw#< +)+qu0-T?PC^VkF(J3aTe0DHrO)4C`(ey!CB30uw{%^Wj0r4j1f!cx{T#UdYEr=O-8vcOYPQVX+kW!Y) +aEIlOnP#CXU(uoN&y6BPq%$v*i}Z+=I2b1u`cfbBkI|!IlHHK~aQY&n+}LL6h5am=iR)g(fFx@=?^}6 +g9bxk(^>AH!+eII%@NhRxNGPEy&5j21`Lc7S_jtd@QVw1^HNvJSxbe!g^HF#Zb=6!I09|*GNf`Rr>ZC +DFe(e1G6J#Li3Zko}UC}D5jyr45dmDS~?<2hD*0(CfAk}N2LvxGNm)QZyB2&E*H*=YrFjBA; +3jSg*@k!xrgizu^~_J*s->neyX2R2rbf!330?^J!{8XC_nh*J%>mnW;4VaRjU`trj-E88KlVk6CrbVf +sZ`(?8P$D34^B +=Tw9dp*rOtrnE{U@^691(>o*&Ice7~d8AXZ(wB4*qhbZ^uqlsR4POHqB*3hhP>xFvWQO`NNkInCG6En9)DUokBT^;i_^G#6o4<% +hX9qM{&3+zQr54qh`A<%GGAZ!%*W-#0$q}^Kqps5&loH;i?L`<#jd?&7 +~Lr>(fOWbxo(uYp`l@vu32s?2}VyO%s!DYdLm)DhL#oj-eB~w#7ezb>dps`Qh!)B9dfE{=w(y76Vkvu +TBJctoKdw%Ta%hxAZt;Y7TY3|oK20hX_lM$8Gua9og$mGNN($2axKlx8X0X(X169YTa(e&WPz;~$?Pn +!MJ8FD1>+)-O3KjOfLTHXU}6=Ku~}{q$#R;NTST&)3Rtrdnc0Y(%+B3WNk+9*V7E38B4XJ}j-K^%i*X +)OZ55j1my@x1Vq+{PYjb-=km|xdkrXb~UDoV^%q}d(1zBBKs|&Ka7%*1NFxCZ_X9 +_T?jeJuPm$X@BU`ra$)Mgn;+k{e5LMbzI{L4I{+QxW9cbfcQ+K|ep%UL#J-C(*=>V`fC6+J6giQS_ri +HKz?v5LMlsMwHyWh*vAR7s^a*s6?FX0evehT>vQC6)Zp_l;Y6CXuI<$g9B-d6}>w+p~1Y?d{^tqTl)M +%BSz8?DJ$q={$-ohn{&O`@IB<=;1lCS(KH;Doxdzl29614#}ieCL@__q$s20n^~cpgmSAWCq=oHDM+R +mDJtoksxq|kKwnUlh2c>7B0tRSCmwpSYVDn;z{!R+60a-TG$z`C{8o6=OR)C)XOi(pD~YvFOF^*;WeQ +O=nKG-Z+ZkELITPo^9`zi+&0>XIC_T^G?khI{Pz7@wo-DTi>tRFL>YF$XOL%ZpC1Np +F5g4=c-R-y?6w$0Rbf;?l*{!ncW31Jq*qfzyx>g{^di`GgUk1#bf@ +^0`%82tcA@%jk8X=hnZjw;t54NCBEDSyzq7r+!%Lt>=fJJQ>-9UKa;lSk@fYW +O`uy1~f4ClAH%WUI@}NvA`6qLz7mZTS?P9x?d56-#0j|5d(OI`78o!ZybAAl(3-@F!sANwa?`j+#Cni +F~>i$-2crWZWovSl>T(gcE3I540#@_xqV{3gZY6|BC +Q{eg*y3z+XwNctrpvc-w9e-F1wiyb7!+rzzdnme}M*xfV_U4m&eniRC{!$XV)H(np}Lcy_z_m$l8VOU +!)<8oyq31aV>8*gBeX;*pa>X-C2H%J5lr}*$eJ9GjUlqni_iZ%fTnnuyiL#M-(E~1VSp37$&^%1B!bI#DIxuOl0nKu||sne;BA3%X0q7z}nPe +RZdv{jVP6~%PFjUm)BPzbdUYB4N4MLSp=xGty~4nnj;w39g~GzE#87c?O{O$5rQ!$Di>Kuk2~2&f_N1 +3(#cTy;WUEfa=N9fk=gjYJrsMMH)T{0f0lSPMlP@Eac#CWTly6g4^moz7RCXs(1KA0v^kMxu~LfM*n8 +;V43<#_mKl0|g73qc%`ffG-4uuu4Q8F@VTiCB=MV`3iOxK$rno7KYSOSg;82vq)_ui(PaQh$T>yw8T; +_Glqa-Oku&eGK19~D12cxwA8e6jg@N!KO=k?wv5r1F>D!Y$g&nAZ9*N)Y_X&u`vf$1h&t|ipgwg1(3( +1G4)EpfCu4aR +9~v7_T8iNO2&=0S*T$oKWF7CtQ#_fPi<{q8dA0!}r>4iKUjR$@dz**YLfD@3jvgYd-|#)KLr90gb@zn +%u78c1>>AA?Ab$bucHyVw44HU{F)=)CpDt26a*sO956h3@{wTnkAsn#W2iiI?h?mL+Zq!q=iGQhLr?d +jo_*qY+K#HiUuwdel?=1rl_g~I)q;X{2JibAn7!oGOqz*4dSFBkQ&5B6NI1`CRmyfQj|X$9OH%nX#hw +=fHVN40U!;KXn;h6aA*(+4f)f+p9YRJaHJte8XWwF@@az{+8{O>K?@=_8uG2h@o3>&3*TD!*21@zylU +Z9OIzQ%B&F^|J80SE=hJdOpCphZnOkU9n66ip0D1IVVLg>ju1gAN@v(#@%ZFzEaUGyxq36nI?#7G>@(R +yKCH54!|6Plwy8W0(#{I*#SL8nQK5*m+N;Hbt8Vod%sQMn_$~o~QOy(f~98<)E#io;m@N)}Ubdm=<^# +2rv*}AizLK1ECKZQ3ralW%5HP8qGnmY!rb)C?cV#0qxLS +D(%t=Y_Lc|Bb7P+7{0E((vi(dB1Z0_Z7^#5{@tb>Gd#%|1Fj9N0zq$ISQzQy!fGtwuMcRpz;=r?NzZ2 +=upK{;R+yrb(OkPYm!QMW@^n}S6&{e7-Pulit%UHiV&?2>%(7P3^t%c$#+=g{Yy%^k1NS`imi{TtPX3 +Y4;4t`nrFAn@j!!du@n8P%N*EHlMM)o{hEjB6jUIiI^{ow#(xTp4;31}teiM3Bv3@}l@Pj6_*Ess3+L +;_?cGvroc@OXqlPRR{&HV0TD%B5QZ7s`zQ25sRz0|d{Fn06zo)1Fl<6D8Te0eoExVa~dvV#~wPSb^{xmV)BdV`Xudf%YM +Sp!g-HK&*>>JN^v%Zt1&}#eny5XpZUgL$}ld@^wZ@%0(LF +Rr~ma0hMaKxvhj~?RT%Q)yugjmiSuk=2HCk{kj*}PFg~NJ^IpCqIGr%jRgLX`PeK1X*`8E2kShFmYn< +O*dsdj;2UY!8#%DzQ9KcCkBmL$%Ra`|j{T9x?8td@qwxOWr*SwpYq4pB!f{dG_kyC@Q7`|AZtvvO*Xq +UcZ1+XWV;id80;-8;+n#Uw?fXT4Go3srne)m1dv5(-w$4;zIx$vkorbT}o8DTyS*#qT!-0y)$^QT0+I +vkseABP&9lyho_D+w+5?A}S8`Q>Y^$uxr)Ax7huU@@>|NiW~f41Jdc{O)k_Z6&p^-CO5Z#UEn2dTj>^ +-KLad^a_`=}^A*>*0xPs@9nu?s~aI3LNeE&I`n!qiQ$?gQ`~gax7dPJ$w5w;w|&!^mL-oQreFE&R`E~ +bHoRvoAs*aayD<@N`P0rRI|MiAC4gSB_&jvkss#mlCnme2kP=a+hCcdg9cs8``r;Lxn^)q8V!W9O6EM*#7Sc|U +emG{7@9xd*&kQ%E=G^&YtA33@^}*8!n`I5mPr`Z3$|D!tp;p5g7d{%Z9Vx{aZy1$D>IZ-@uAd%x`)^L +DRxsZ4@!=k;`zIs&>yGrJ(EN;}Jp}vyX1v_fH9i85Sx5&0cJAIo=BFV0HS9sjbAZNJ8oy1tdNQVtXrL +d+C@c3N)IVXebH-k(-;+Fj)W<(zQZY9+*glZ%gx$dRNfdBv`0001RX>c!Jc4cm4Z*nhabZu-kY-wUIUukY|b#!xda%Ev{E^vA6J!@|pN0Q$I +s~v`Cz?njdN@vmTz&8b;NMkq$V29Oy(g<+1vkqRn?E_*HDx;$sO*NKpe5BySlo%-d#P +Mm-&)SCiA9lNiwr7`&^evws +`+3gq`ZV;!~ahOz7CIVjSmLRYzH^aXR^QI%HK5#r)_hDx(;dT@6`O#Yysk*T%OmMZwEBs(2@;$Rq--`5~Zw&1-}iq{n}mQ%!q +sdqXzuz3Cwo;=1f6N|V_{aeS6fs-`USMO1SD*}M4K?Ck99j_7}{=-)&7_k@~uPx0TEr}X7Dd^tTn-Wl +QlPVmd==?VRN`t=J!q3`N1(YB}C_H^4*-S$+sJ=1N^blY>?_FT8U&}}bt+e_W{Qn$U*ZLf6OYu)x*x4 +qMC?{wRtZadU%N4o7uw;k)YW8HS5+fH=bsct(}ZM!?Fjs7~l>@l{*vi#SGHf=72Xk0Y~%2YK;^NhDUS +M7iM4P!Xu3y2OpVxXYPh*dQ&P`r|CmXth3>10thV{yrGzuWO_mDE=X1_u@ygQ(UZh@eM64wK3PvC5lt +1}$}&&zhJ6NS352Uqq<_p=4YtR6vOpX;cH3FDqD9&E!H5eAUEP4A-Luw`h$;Q&fTc*K8f{UW|4x*#R& +xP!3nPt}p1YCae3C66K-9&dVZ6d4KKf`#Aj*0qDLDlDEIz`}(yxezgO_1OJB|?`;p*6I^ +|9mjY$?XGs-JQ+|NGN1zaAn*ql~eftehE584aFN<_WmS>acvY4oj*sI8sj8xFyc5X?^hfMr&iht~>mO +a%%5d9@*4uhf9o3dC#G@=mq9+YqlqHQ-QzI8rS~s +^xe?ypGj?69wW#wVbM!(+%+wDrl(wI{iCtKAECP(PVMmL@4_n|457RT3C!@{C97MzU+~$c>0dMJlny4 +pC8e`ht%|fnqG=8FHeYZ(MLFnVlez@5M!`y)t3x3Umu>e-+tzclUwAzzYC)lr=TI&VKblelFz_R;?c6 +7Pk#Y*HIQP^>-EHQ)Hn#cqFxm!x2R^boRrd4bi>(HcLT)W5;Kpq*w +&{uByqOxoWcO3e_GuF@RmOB;Sap6OeuZ8NykXQFe#epvMMkI8E6UG;^A-7${#*u1SqEed=dF{IECBi& +6N@IM@b|&7_`8`V~*-L%A}waQtBVSTv*CKz}$-<(dxI#ijG4$g6}#<6_^M`eNtua^L1)1oh;Ar9A5sfPp;=JhCC|8p|p<1O^%QGcm3> +zm^n)Cnnm`{2<&n67FOwnT?#TzA?KX1uT=mtJcI`_v#8nhmbcHom>vs8wtQ!u7o-|2o18kCb;t3 +7xzP_zxS3l0#uSZwtcVi2)oF(qF{q#jlLL_Lh>H;$?j3woc;_$_l~f5+?hNeOAUk`N+lf_)QYQWw?lK ++qi~lKR8x_|{YiEoyvrY;2_DY6A@|wCa=0UC3|5hM4dMY-BBc-<2&UD14s(4EsYC-UR9Q_OyzD;?Sn@ +6BG2M4JsgVJ}&ny=Aqbm@Gb)w7#nZ1KYq#^B|0XY3yZ>QXfSY +ixWT{#D@A$Q>y6QA>6bS$nScWQm-C-Kyw8fJR@y0V%9sz?4?kS5qH>|7dW-!vFRxitLhLnTXLt1#?98 +-Tu&q4|)c1t}+8MAo%`0A!-xTLrCPL1!0)ol3{U3jXfz`MmAmhAT{Fs1;#R-s%dce#i;6GtfDdrj}BN +aFP%lY})&%7$~tl|Tfm+gAD*t@jK!7<_SHL2%lHcPTOz)0Du1PY?tW)5$Mh9(s$KTyV^^BAB_Bb?JVa +o))ByjX*=W#tm^i$%bFz)qu7VMONkDypKoE;TwmBy_K9w2y_{mtmXq94P3P?5>4kkJfvn#~5&Q`V!^# +mgs+rwo$MXj=CKK3s;!2==Ub7d(V_&=%m58G7u#dXXk*(2}HSNzjqX6!nvt2kD-Q9LIfOomB>ht#fQ} +E`?ANrZ{deT1%*PcY^`hr32DGMOtPrFy9Y!Y0d-94MM7s~ev{02Wvov|2Nb%fgd(eR5<>)go?r7U`3- +!AHWGrcM9f=9*8nVzAT~4sw8WHl5@yA)s)WCbG>YMi%2C95iOD5We4~p0+Q3l655`3(i-p2C&eLWol2 +~byaaLnG6aZLp6Vca{4gmNbdL$9Vc2X}nI&W-pK$kweP{aTyf +Ap4p%*!P}{0#y}NqS0}i{5sCgxT=v9cY4O`WzLBIh*JYq|Y#F;QsmkorrFo_y0B4@!Zij>cGgO)%bt+X50o +I5fPp{1`%e?3Q1Ax-Q*lzxE^!eG^=;<*K_ss3tVjpBeWd)4Qfjlb`d*F*WZVZ!6z~JczdI*MN$MTc)T +qlb24p=|U^XsNiE&X0o&quF%VCZ@=xa;5z_1u^UgPG4dje;#XcvF_354>r??(@1ouw!j(a>GN3BxXYn +7_zDVKq|4F&@njm0y~o+ZmM!cNd~#cFmz$gxU==x4G +L)oQdO*N-AI`6tJn-H`U$jfx5eAvJ1MeLJL{IET!i{FG*0Q$9Vf~*8SOxPD6p;IYl5x<}C0JoS +Tqy&Yqq106k5;6x`NC9mft8SEfkle>P1I5KhtOt}VZig!)@dbiZ4pG719!CHxS!EYl%#kpH)dkR_CB0 +^Iz>a3(H~&FVLTY{*|<|sY5IuFn&59eImH_a1@uxlD3%I>0gJyp?f>?`u@(-en3-G@d2)^WuG||$g_W +2BY+D+4db3|tS{*KoZte+#4b=Sh1QRdbMk8=HbuAVml!spY0qSulk=@!+*dToFtlhM;f(6|u!n5$E?i +hw_S0^hYINxLVeSCm$X1H}>L|`9A<6iqZEioQsQC#Mg?;$A8^lhs{Y!kD9A$}s$$L5gxjJE1WelPMmg +76&pzjEQPgUh|l!Lh_nn1<#f_|H2bGk8yCS8Oe>vfu2j=TxKq#awk>iZnYNPf{|+Qm=*sw7HI+wp-cHS>w#n +b)$++Zy0#rV~tIpja`hGD+eAm?>b*jY%m`ejr@)SP62k` +!7umJ0PWVYi`}z*GA4i(Nztt5LK +%v(TXh&IwnF>llR}4FN=*#>y0;r9h~Le5cwa +qgpOmauxvbdCR@5?fsAr}+G^gsAdiC|(0sn_)(=u6sm+AVX~cM+nC|I%4ed)4@(OvqM$fGsSPXAPUAY?ruM>ONtl!Bjd +JuO7yP!Mv^VM_nzZD7Vjtk5eaB9%`QcDd67;>Wj{Juii^!*>A +rdycyeRcUnL$Uh;Y3AiY!wkExsC?GHUq-6z_zBP#nI7VdDE7o1!@4da9Nm-$%~-!_ack$?ujqeX9`N=dLx|(Y7PxB9_h +KJ*IcvkMy~Rd%Sc(TUwUR7bif^vCIm#NFG@K^cz=V6{*oC|i~t2!kEm_BVCQIE#Jn!~_ThQR%;F~y#P +P7@3R0Ko)Y3>C=g&kIma;Gw(cH0g;i6{XdCH@@NHBcHZ)-Ofin3@9L?ZyRkq0M8slG(F09cUA@$IlnUJPbz)&?yEDl}3c0kJ< +gH1G*^QNSHZ;UhtsDzZ0sEBB4BpFa4;1hcs~yy^%K(KeBdgjhImxq)LgIl>mP6K)62O|6gH1=e +hroT*(pu>berjAr28-R-rHIJP0W0Dq8YMUM7nqn}D?e{&Rt)$u&sc{g7nlvF+Gffl|*`)o6F``Ce~`d +K=R&GhQE{=km|8h5yrK3+fA4$7sdmLG(xvTS}`SUL}eL2u*R(kW37oZ;M1Ow+xldz1~qdUTi2p&WbXD +bvTK$GFm<4L_Dm4#;!10kewu=#_N5C)fbVQlKQAmpQ(zHR*nuaI=Xn|uP%THvMXvE7vKaKz1c}<8c}c +1EU8-ZCr)`tEG^m;5pl5&7=+F}KYb0nsTMk+aA_x^|@1!-nr^o9-42R@6(s&a5*K2p +yq6ST%U}7cHnOZ4as?@yk^lG{$uKe1aS26OdyIZJGpSbe1c<939<5>Y4gbTIaYlv{qV%{S|HZ +do8|=UuOZ>Q1kb8+=TuXz>R~)d(!hQ5ccVF~6?&@`~yC*z=-s2YRxDS8MR{d*(AptR%REI;Wn8=%V=3 +UV8^*(;QchAVdV+aTE?J7|!*t2~-%op|GSUzxa6~a)wr_P~+2>cY5UqZt=ZgG)0QI)6eQ;phzk5woWE +E4oo(II<{XW=|~JGYWj?aGYxHCNZ0?nuk3S5LGTS_EWuXE8iVW;%m+Wi2fIM6N?T`<_|HbebS@&ewF+ +-RO!{h{n8Adh}SZu;5N#Y^$!@p@g|^--CAIl_@$%U3Ax$T~^q|8cs)jU4_QWpN4 +tz%)YZn(N%jcBNN>6a!%w35hFl=7afG^Zg)l4!6GD${o3jX+1eWEnB#5T*gt!@JtATuYvKG1uSFN3*3wYjJ!}>ivhv2}OFmK;e;zS$~cP!Xsn}HjY2v0vy`}?l0`Ld`9y +y%F>IT%PpwXA%=qe6LpCPOQ5=MC;Rt_#W-REI&cwt@mdN|B^9H3~xss#+ +CoyJ!{$>-Fg86C;(@nmdpT62>+r+5b!*$(uUraun$gU^~+e5#@VcTKK*Moby*MfVx7XSwx4%-d@u7&n +@$7{;PX&d;anXFHtSX>`af;c~O-wPmfykWEd%umjk7gcF!Yu8hTaoyIT=c4v>&lPgQ@0s1Y9> +)!6vWGO;3ZiSw>Rc52%xhV&gLwgU*T78>V2HAc@GXN)HXF9mW{L}aaucKP1o4UsT~TGAOc_^$AMG&49; +z(mH>`(kN@4&RmZ6Mv-N(d~b?PKW2`nVD^M{;S4`7tvU+SAo3|SF}bL4678YxbBYco~-g1&v^_X!WG> +_C0*1L?-dkPcvA1m!jtxXPemC#wxE{`QYBwzs75TgT)<>hd4wiZc>pj_03;7#rg=!5)`F4O=R-G9YG) +IDPN6P7qo`L|VDmTtbc|&lW#4uqoYN_9cJG~iO@=3fqo`aowp`d3K^Wc$b0z|U$%#U54uWR8KNjc&`W +N1%6Tjh!S#E&2Q>>*`Z@BT&GUT*z`fp_J(S1Y%yLD#YqEzslD^Sj4k6opYv!-H;)WSjPk3l5}$s@kWDgKUk +zx~U!p5}7Pya26p*EiNUGzwN=bNfE>IRm>Qx6VIFa!yj2CaYOcBco4S6shV`hd7vn*8(RYm(;?Dt +Vg8~aHSdM{cBqz*p}_ropUuYxMd=hxqdI{#ZKLzSxSYxDxIhy%{kM>!EwrD1Kkg&W1|V_)U4IH4mJsZ +m&9-t`iNX0sdgDQquCh6f0QMRVcj7-59CQN%vE;XK{N5LId%@AOzOqtp%dsZd=U)wE|Pg!s_r3p-y{- +h^U>R*A@Ws)e1S^2XeVUz%lco^P@Vx?tGf3tccDu|dv9=|J*DmSy!Tsll2(dmjuS +A4$bSc{Zx8DO{O?Aip5zV)Xl5xt%9m89H}v}wE>>p>Z38`jw7Y@pP$dvToMf}}Xb~FlojrgQOsL<#Y} +hY6|7X-Th=H+1`UYzkaj!Wqka5Kh`gixjhnO1LSgIOC%Dqn~M9q?=(1D|WoQ6nW^ShzCI0oq3n;ih7y +h%dJFhl?+bXlQ$X<@>M`9sHVYu52VN`z)36}SW>$xQLGmpOEAR{<<%Yv>hASu}*PuzP-mO*IYw|9T +Vm3g~n6_c46r79~Pm7uxE8LB~PPaDg*xr(Z@GFmJnp9}DK8L&BVb+3B9=3*Qjvl^Er$@=T*Terx22aF +8}M<`Padj6dP;ED*=OkmgU8*)J+;-{lRK|+-8pq}>dZJQ&!@p;qh5Gs?9G;~)E#ha_J#1X#YvKJmJss +`n7bOBw`-+u&-AJX_YUee +aqxeLm0v^~2eC==iyqJhp(ca>5ZpuKVm|HU-;iPej7j1-gqBVu;eAQqbKjjPkPboqjayyr<-)#A6M-TWELdBuhXL)oawSGCbh=;Ac}0L-;z%$ylSSHUZf2$ +{=$u*GawY*g9^lHD~bXmH)^(-)!l3|TuDP+M!~@8d?|?-IWCPQdZw{@#FE<^YHEx_} +0LcdP3*mAiirK(Zbdqi)piWF?ETW==kEk_IMc7SF`E)w&S+F8t- +-SaL~ZNY%Cb5H))Ca;N|(ccOw^nfgBSbJgS4lT|a_tbA!@~QFZ@EF&@#?SgKY +1>GP;EoPWaM?s +YUz91^Lwz`UC(xpQNtUt)gPjVJ;JvKFn*kFpTTT!Y~mLqske&;eu&F>hox1sLeaeI*!fxJEp|j#@e&a +bky|Qi&MGF;H|_wp!q|Qua6MHTC{*DsOp9@@xZBk!^etr1E33I-hF@8K%#Mtt$C=b*kWHY!gwZX{(cXtbRQJid}V~}Zh6ihM!C(8mbT>UB +fU3{A!~Qg>Ru%Mw;`@W#W~3pKStJ7N8KKv8+ePK50j}$QkwL#A^d$Unc=ZSSVq25)e3;}czl*~x`@?| +r@`{bMi&F?pqYO8N?V8aKATrw;d6_TQsNZ*m;~0Jk#%03rYY0B~t=FJE?LZe(wAFKBdaY&C +3YVlQ8GZ);_4X?kUHE^vA6U3+ueHn#tNXZjsbdZw0JNp;%YKGNDdNo-Fu+efx>?`E&-@uf*fVoi}MLE +2HE0}vQQ;h5aq43apB=*&ho~0kpf4V3w9*1`{xSUQXl#A`*W%g%jjfhTs7~&WPpbk@(2mVZwn8gvb$Fj +}62E@Uzg7yzkw;~zOZbdGNzUNk4F*0dJ5K201+;ozJ`E%cG|s{Efh4$3XTLROknL0t +8$xPU|&qpnUAL=@9JH5()~fPn=LQsuBwmrU%4(dgvIFaV4 +D_~W|=I~p=%tZa>_T>$xLZFVG~tgcp3GyoOF2bJ&vc}?SF!kK}c^K4&=3u@$;m=dGRu_yGxw}(LR0x( +A}016Oagh_<9z@W_4bzvMp6@K7N1Wb9z|g?9B~qyU +7)>zi*y6>K!M&p1$B;9iy^3u5ePHlMW!D0UB!SNMu7aF00{$HN=pggh|kLf^lhw@gt0{E30TZ6Oc|@j +WM1#z2~ALB+$*!URBxwQd=Nr*_T2+F07HwH+7;^2O5`6qbHh%zur8=L5pm*Cue7J_2}tB(Mxt#2#>PAx5^Nbu!=1 +Ysn30W2ipn|C3Y0Xd4IjdtSfC?~5d;(tq{_~{I*iE6=?|8B#$JNzoZv4V%j*9IzPy +w5>!>T_p*IQb*)U9{ogvLaKR{-%4ich;u$)57+CzDm0!0IZpl`;aR~fbIe5Ga0AOoi&x|kjdaFq^izl +U><35GzuK^XM{;CAFWJsy%yt!_nP*z!Ge2Kx7>m5mJk%09n)rzi*Yft{Vs@!PYnj*rQ=Ckkuf5BM5z= +?H*EZ@n`scM8Rx9A98T9r#nWTfZg07@(z(^2?9Ir1> +%pB3~h@2&9=AFv$`1tsVL5A@4S0)*P6#0+m#yU@oa!>1gd10*cb$OkOvon)^U7Vel)XCX6OymB5P>;r +)E8gYjU95X?ai0Erf&V_?UsMw$KB5M%`kAElnf&JS+QdGWRsSxD{at?ZkJ`lkA*+5NiG3lz`LZ^#FJ; +xQB(bmLH(%E#_O-0~OcHx0zjV +2w(db~oD$d~n?2KwO>M^i(WVYV4fKc#OSI*DP5L|u$=82RPJ>)vPN)ifSK$a|Z3zRM0pb`AVU7{F}qA +&l;ri5`Xu9*>jB^B-RI&3Hl(f1;W1()ybA=YMQJ5%Mwg{GO;7*5hgc&)oJ>@A?<%+>_H&h;EOL$S3lb +7xI^n1cn@GG18fc-ucMBQNwrg69z^P9;00{NdP8em@a@15wivIJD)Z};FzD_VQelk7JP(QTI4SjmSKc +NK0Lws!qe^e?7WWlP`F_d_ZS^}PUwe$rK_15+$Ip8$2;c}C=H0H(tG0h%f67iW#=AH +0avx)%+p8l +9(>D#mG}UZewa3XsvaRu`KtapDSNp<6|1M)MbEwX&eY +yu79V3hJnWPHeQu86}=XUusb_*)_OmgBxu_o4)f@z|`>y(faj)?4K68_VQAMBA8iF$2ls=5>bT+h-!= +ycOZ(z>8R#!7s*<*cCqA)CZ8wvyaHHW-|uPc2FxyNr?Hri2(=XSxkItL%&d^ctNn5ByhVB6iYkhy?+> +mFg*@<)}_P|mnawX;;2|7GdhviG$eIkJi4bS#`R(pKZfZbMd!B}C=iRjolvj>S9ke3crM@@PpIiCp5q +)~1~|0htUqtchN;lASj>KSbV5@9on|x1*c*;wk(CW>Og)J1_sMy-rj@6L2^toM5fn{H4gK)e_6fu+o_ +z~iW61>`ON$T=bXqbQq@*UeN{8bKHX~qO;Jr;)ato}(Oc1WijHpg5g?t?0&`^GTS +%|uEFr^$+BbaK4>*q#aoV!BEQKraU|D9Uf(Wod3;JBy{b#4-Wz0ndF4YAAUdVklgO(+Ps~KLSR3+~0e +As_sxteCB{yH2EVa2$)k&PHCq~C3N)^Va;Hjg5>u!7y`#m09=F4tQB+Q`!6~zi+gh&6stW2-=UOI;sd +ij&dPHAHAb8A*-+FeAB&?+_1H5VQO7tWjvBcDG9Qzxk0Ml6c-(cW%k;+Zj6ZV-q;9EpRSdXJtWq3U#& +6(L!FT9YN}O(vnlzoOnJ})`((zADiha&;?x`MYlyrZC%B=(hiiU3|8~}sKS%qnipsq1riP2u8XwIymk~DJ^N-NipdG=9R^AtmRms^ZXgC-HNz6>(H7ALi)b;)$cM!G)2 +(wP**QRv7-(4-RO84|v~z?!>$ou6K-6KmzOKEK9UP;O`dyzvV~6-=81)KPf2DP>3l40$1v2wWdxDI5q +AibX&{aTgMst&uXSt{-L_1kl?HuEX5P!DW6PTpZ$N1pbfAT@7%uneH8cHW?IGSNwU)<%|7sx{IQ8;|> +p!Tvu+}36%_w*k!|)t<_WlI;2<_4p%Df19CgBbVN@}BQ!will@@a(kedWnoo1 +0PN!nKg2yQJ2MV!ujhzP^#;B@?1kpxlyr=LqZcdN$0dp#kmnSTlTURK18`;=CQRR5#(1I}w~i!oogR@ +iBUgaX)TZWbOYjD1DemCSIX$K8!fBipH=^;JUPkHL#gu@XKLNkylKC~kEPP|>>WXP8?(khh5J0-Od-1{w8BM<2m()knmWqN5lfFb4HLp(rU +mnBzJ`%{I7{GRo)!~vhe#U0L|-Hg^dvXmtiw0_+W0G{HzkHSd+M4&LKCPQQ-9iDUUYiQST{=N{QRU~h +U^#26LqvPc6q}1NLMwUgyg{+a8dXmlHWaw`zX5vP|H#C!JInJbm;U%HpYkUxKke35Wxe{M4mR^P>I=Kxde$U4`)4Nq4@MFsB2cB$qH!E6^t?OB>-*Lgal@g_GKm$nq= +UZ5}8K&}@jLtNM%uEl18K9YU6``&sT6e;NGj@gHSu-IqV4>5fkH{vv^;Jaw9D!{i$Tm^|6_Js9KaR022IgvMs%Y(G$HGkG(tN +g989nHi)68yy=DC?Pk8H~sz!so1qK6YY4p*NRiV;Xj4f>6&*1J-|N*2q$T3hOz@zXtUJK$_O +o358+`vlX7l$lnO?kU8yY{buB3taJT+Q4S%8*8|8-o;}!rXtK8*nu-$JVh>93te@#Y*wk5;;NY)O`j) +&0Y3)IGHr4-tTTuR#5f0TK26bJ+*fOr#5B@1M}N9RjJi8tC`PDVi%?2Kf=x-K+_}MUqHq#tkG +ag5u=aWnn8kSIVP#d-y8C!;@#U>hgY5z)W{W?hPRP}hJ)LS+pI}0!>ePfdt|R0w78WvahD9H%4;U2QBn&Crk=Eb&@3 +SAN0wjowL-GLO8#NRv^s=Jr6MX0vtA1NEKF{F`W`G5tfrLPT{x({ZlHF0ua^&+kXpN#uutjVb}^w{Ol +Xcx?P3Di&SJunz-di%R^yQ~bZnlx;07ufVoH})jn#C{muTK{V%%JGT +RE<+Y-U?l>i1;{AvdNaGm~qYBP{N3oAlhw8pY;b{uP3reQNAg`Q{<49$f5EYShf;IpbQm6R^J|BSbD4rAN6822Isoy!#&JvPai-8e!suP}>G(Ofo$+Kj +QChEPkRNnt6W?Q6+p0ons#5(rF@pu*tzvcdWHjmm_ZrR>r7s?-iBBEP)q;TryWR%i|olVVN*B1V)9NBOK-@Q!8Zp`7Y%5L=S7+Hy_@FYw*@z +=r2ZI-7p3QK*>UM-m9wRr|Sv(&wyNXE^I+QVS4g1R=qt}7p?~x(buGo^Eyb_k=avke&5ZbsY)mcR7p! +RlA-^|C|RI;c-tgxy@Rf$&CYf#XI)}R_0UNZ*8xQgwCK^?AaPdV?lv{=u?H4{w@X-h2k)a +ZhxH79X=^ClQC%>D1M-7?M~O`W`am2}GTl~zyV8cC1P$F}~D}KiNqfj_ufUfV6re?*Z=s-*D%}{e^~`dii{0otDLa`VpP@;4C}Rk9V`EN~utyLU +{q1-yRUWGrc;Xj_KTv^ZX;O7lAI}?vw$)s~CRdV#}oQF!G!c-{j)Kj}fs|dU$*f?pX+smwjy@CAe0}z +DhJ|Y)5y=Z%DhhKJI{e$JJ~%rW%d# +rhm?XEp=e%^G){kL8@kFdCi|KgwO;t)x7$F)#HPoI;dhLn<)>i3G(KN64QDiz1kxD^-9U%&$&~Z!)iu +OU7Xg>t)I`=6W9;;me`_w2CzXxS&(w8)Kf~e=f66SC(m?UPD#{xEG-RedY(=(I0+{?kr^UEJ)m3YunTTyJqIzA +y1Iquy6kO!Zc$=xtJ^}nAcgW?DvYWp9TbMU;vtDUke%EP2dbwqt1kXK%i8^#4`DHN()6DH`(yRJ*6dr +eZ^G=sqS6D~*JS(~w)w~F?RVkjv3=0^bdj#kR)VrisOBrbs*SDRTbJLP%Zz<+5BwDboYL*I)vVV~G;z +vNov^3jVB#(NsTaeu={ux%h(td8rjadi=HD_aWFWzIc#< +v3+k#RJCjl9bD%*XE_LZK&D?SQk)W8&``gzL~9i83wZ4_NHd)w%WS@L-r(6jC8I0-ZQkKhi`cLch@UM_|n)fo(-UVI_8-2N+*VnB|G4saFXWLWh+{r2vTm*boc#RdGd7CWpDQW7w#fwphMd6QB96{} +tbZ{4Y9v2qV);P4Y2r2*yV{NrxdnUpFQ1DN_5dkd&m1pJTDlwxYZ#mOd9X$N2e&$1CsGe1~~WdFP89k +Y|OriJ-jgrc-HO)swt)ol3e$>heO{*$6yUTBgwJ_Pjl*kjZBW3U#LE`4!4g6O9af>i6*sXuewxLoKQa!E>)W2x6o*2X*)@+GfAz5rjKolK{;IS +AU19dh@eX?5Vb(GA`s1A+ur6fV8JNbWxyqP(?%=XT?SkrsVd8WMiRTd4EX3h^G+n6*5q;8?9&eMtc`3 +Iy&Jj+R5ra1SlM^)X_8(6+BQkw!sKr7tV)tTuef#A0T)II8(s%&A*O|x7GmBHh*@aYk1adg>ST4E?al +09)#Vhu#-5(^WYleb#uqvN;pOGB>8Z`>R~|3QPUhmYlnW2aq;;iSmLskgKvG(hShnmY2zfiwwWVJPQK +i;tp%(sH;qA8ct_kxJ4L*i<(+teS2lz>&I3ovrDwavLhhGWV`|PQ>ROI@)%vfdF^xOwt?P>3`Yjf$6* +XT;RE9pztV!w~nw~$QiYL^5DfI +5+uG)Ml_-%viVg}@yHJ3>u=)Pl37J8{31+(I30f9!9TL=vGRB@D|||hwc+CHmN(c+WP|Z_vZAIpBi9^ +F<5MG|PG!JHU+H?VRKZs8C9?pd_7nmCQAbb-nsUhV|4>T<1QY-O00;m`Rt8f(ni>Z01^@u!6951n000 +1RX>c!Jc4cm4Z*nhabZu-kY-wUIUv+e8Y;!Jfd7W2#Z`(Ey|35&!!$Cn&DN~e3i(w1q0c#p#?a-}2Qx +t<6sI)}eY-Capsn~AO@4h=yZ`)})CxC46?sxA8XN6iq9M4Kq7CeqYEOS*DU{kHK((u^*4j~oyLJK8BD +5XeL%HvEJUa(9b9vmLb2*qr2u4{ldDlId9gRkh^6r0P#1Hg}^OjMTf#2}oG*feQ)Se^?xKRn>;gy#lc ++1J-ap^81+Tbz|DxyYmVp377fkrwNk%<#2|NzD-6{`-um#f-jH=oQs^nMK?boJZFgUvg<~@H;RKDW9= +2GqF8HEi=t4&ep!2ILX66#Ix7r|DhQD#WBk=jOPd@Uhmio!F$8aLZvT-&Y4LTyr6eNzbjGfoEiR>34G +Ox$B(DGhd1g9Gay$&8e(1ASdUp^RI#ZZ+x$7%pyNzQ9ye&(yLI{=!P|_PnJShVq%u&^V^D!OpydGnWB +Kxzv$GR?jK|{u^g@+c3R6xf2`lFd1Jezd1;=-pT4C6=iQtulIhT~OcqBBeDlTAh19>H!AWBVA-`|W+& +VMHF3ZsEb%z1jPW_1C8HU+-XA?lVakdSy^Z_u+cf*X>6|5J_%XqEP1kEQfMqU1+%iCGvGR5IdAygPaSpZE?#3Q@Ozf|PrZ-Mf!@;q;0W?fhH<=1g=W)?kBH_ +36-uj`nAvb(?MrQ#Ot>Gf!?uuuPL>|Q2M&_+wDPYo3n)FW@c~-6Febk|J)M?w9esALQpNmKXkGG)vuD +4yU>$wSA5xe;Olw{|pmft=uI&RmNoB^qEJa<$*y{R4{{Zn#QC4-BWT*@b3(ikPVrt)cp7#=eI)`0=&U +qp*N%KPGXocYVl7NWv9|=MVBbc$B_>ONh`kG<8#{9^HHk6DU(^VB<$~;3|=p<3{9+4<8$^k909SA&k5 +Ly9C60U9DQ#DY*VYDbUvB_ga3YVv<5OX0}=0Hf`Nha>_{q!?7WGFZe5veWdk_;t0jE?h#m7Ah-ww&?R +_hw2_Cz-Os3oh~T#rqc2EE9Wr3!@R(lN#)gy6-s|uv!SL!-PpHYIFnYQ7G&NR-c-b8LZkVPPV9VevOA +DI0?zYaqLr_hR*ttA9T|^rr2Fegt4KX+_p#9K|r!4ju)p9y3Hb7_Ie@JR3|kZgHPTJUDQIFb6>bb8*D +s#;=8g4oP-d>4w9j^5l%^*iM~&Km5xEs=j-J6cVRlFc43G#8aT4cA8ZwB#-*hTD8}?-4>{39btFgdH| +A+Scyn&Zwni+bch`3=L7jp~+7g95=wJ)9O^7OLA*dD3k`bw}L&6JH%5*EwTV`u)w?XV?sMeZJqxev;9 +E*CZhC-MMX$KTdt4a$S8BK%YQ!V#zN3In`Rn)qGIBOYBOqsamSy9kfrQtzdA{TaO=oOQVm9z>lm{Oo +{3X}V{!agv6f=1m&-2`g9*GsWMD%M%pT>xivM5R6uF(}oJ9#(jJx}&b^YH;EPT@ZKsO6MKvO{e}qlR? +p774PS->wP~vjx58_S>LJT-A<`D%6DpsyOj}mQ`t08we^s49I$rYuDB>tiL(zUr48--t9KW0uj%=mKw +R3vv=mut?>eO5U|IGl32XH#)qFD2zNe;kL5p19{i;aq`Q+?2fZs=Owx%7&E_}VC-|W$g@%RCV^9JH-2 +yxy((6a1YLniE7=zF%{u35y`MzE0mv-SBIe&8qYd-|PJng?)<-<}i2_WbaB`kYjn)>ET$SuM>)&)p!Q +x=%DuU#ie8m_o~^241s!7Q!pIlWG+Z-=DNBV~n*?xg(r%YK$q4R*^7TgYaqu3Rfb#8Q|)l&^AyzX4h@ +TpSfr47e{l&Fx${IU%z#ex5w|(p6@IP;g}V4T{u3zThX^cfp=Dd_VSF~gQjd8hxU5yd*0SEK@6aWAK2mnY{22&)jG;6a+004?n0RSN +Y003}la4%nWWo~3|axZ9fZEQ7cX<{#9Z*FsRVQzGDE^v9xeS32oIg;o96EWX`>%BSKx0cPf#OBWIsU_ +Lgv@Cff+3oJ>zCN%DB&$`dqUxcCcjvnwP_O(YfFh}$zKETeXp5N%AdyHU5{U$Ap2VxpFTc$5EKlSwzj +T7tI!>~Vn5A);XY!Zw_p6Q{+y!Y6N3S}06nL>Oe+h$3CL&D#_=i9Kp(>STNf0eG;N;?M=g*z*5;p1M})SePU;O8l;>#BrxUc7nPfg+uXJ5_Ne7LHo +?ykswu_hAspJsD*|_GqwJ(RGe4I~850q%-?|_7yP@b>Q4$#Cr|52s)C&;U(J +-bUtk5Qy}3+eENw%j-$t3VxEuELBigzXQJoKaIS!X6YRcXaS7Q;|KgVHZ(h(7(>pI +&I9!}K)DUUm;2UfSJmsQ2b^@&vAwKogm&A8AKKjVzklzF|CBj1V8Z#XT+AIM4P{xl+?4jH69F_(35T8 +C4_leEOU!mJe&C3rYoz7G|pUMV#%)c)qFby7=qPySQv3o_FR!D3yMunH2ukmUgMVqqylrYMOWEaUMaP +kyxn;cN2;xeWFO9_V&8C2sD>emB^H>Qq0SAFNjo4{+^n%#(eShJk~0k2_-XS_?li*>88f#sSM|PUKpp +!1n$j4vDk~7il6UVF;>#il{h-D$`%4>G$9EV%WQ9^=F*MiR_~;)SGT9jl%jb4m**R_z^eZ?muaSK|K3 +U@H#^n;A5?$m#rx|IXB|J5sUBFIRcXF1mW_21donG$tV<=ir&~*3zij7i7cSCxC9|cjRfcJ!_I921nC +ZHoAkuo2w5_+K+BscBv0e;2&;R|+UtB8eYSo<5?zTRWy1=yFu?cO(p4J9g>F0lX;fqYXOrM*xT`{hwk +#lt1>=vu!Qqqvw>^GB)YojTo7IAb=tjSt5X9`kenR16x$|gr;_w-e%>khV!|+&6OLh2RM& +z^Vreu^OkP?S&wpEh+1rFriFnk(o@@ETGig3Lu@m;L@%o7y!h$a%dPEbd-~6|^L%?VQCv2W{@xPdwPOzbn@ +z1XZTTWyDo;;s5d7H79@j1Y!}Gnp=h}QdKfXA+{^8@v^Vez%o0*Fp^kF8{@>;TA>6C6scfpHSD{1UOp +&S2FvA?&c`LBr`Eu?F;>-$Yj6eTw{0~=FERkR(gu$CTfVZcy{EwvOYknY;ks_VSid+Cx}A*A0Tw&zL( +N}(0nP>lU9>_aP0?!lft$5AS!4y2jb*^2BU>q2+`sY;E>yn}GrLR9zP+&M~1?d>Uh9y9%}Vo_B93g)l +F6qL2?x`P7d|3d78wW*qFb7Gy%J=xP~X`~!fY+}cu+@-#!RpsbLpaKi1Dw!&)PRC-;_zQd +(@mFKz}*A}-99o|wC(_y>!r3`~8lF2e#g{+n@U%qq-OtB?aORUp!ZEn79vD{%woH%NSuAAIT;+QM3hI +aJ4N_E(6CX1cdJV;Ut?sn8`#o9q_Pd2G7O8X{??^_fgN#aPxdAdQROpf$ew0b>S9;GrhLzZr0u +C&uI(hL;^(MMs9xSYmi^ +ZkPDRR=eluaMNCPAMY*IMl9VEiS~h&g^`)=X|@#6o>Halqos@MSu7udG}8;ml43W~?eYDF5!zH;qgs1 +9KB{cT>apn5Q#4JDj!;gTi1f@>y(v5KA->zz=9AR9sc97yp*p7|K9snP<7SjkH&u{2YjDc1)XsF<0I| +LmKh7&DVq~(a4Ev$}Jl}~CeWq0G&07r<;~`e7V-Sh3uDo+ymhXtPqxvrIO9VCQ>w^nVRMF7wZPWsM-8 +qhR1UA>P(ClBfNVC-0r_^q#c~fm%oOk)m6bLn8m&W;@dmbt3MmEnj>snph8mPMUEROf2NJ8^u%rbkal +xkZU$DM*2EKEI8-W`D$S7VPVW^P +skK^t7l~LeJG~+*s1}pDiB8!>M}!MC5whiK6Bqs8iHrU}#l_%v;$rYma4~#6d>R+SHZF!w;KE)8cQAI +K%1<_RQS3D5*UKQS{SWN&Qq>d1_T&V#W$4_yYb&IGwWy|{TQa)(6qev(ytL>~}-_6M~@O`PbMe97>`6FOyrIGg;r~l#P|QKF;P`)koHqr+`XmEnM0?Tr+B;wV8`LS8W3m+f2;Xjy9EN@r|;GjeMPru`chhr`_Iu`?&IkH#Jr&Pqli +xI=|I=mpW9h|82GwxN0nY`yg%0@fumyWE<-n)3`^UJUAS~b>}HDx$M(` +}nno}vU#kqOMUZ7gh>amYrggHK_?oQk`v$S$+tDV*?Gx$B_(|K&o@=H1nlEI_4ws*EF_P#@Q{rpE1lm +asVw4;%SS!B-ULZ))|)QR^qQSSSB&SRdWteb{(74y(8OB6f|{`r20IE}Cpt+URU6r(P1QGy3a^JQro! +c~eJc#trBuQ+V+X~kyaGe*Kj`O9tccZUpsClx~7fJLX*F1QTeq +qi3p|2Nd=@x9IaL=e5YyEGw{^re_`}_OX_k-7QvUt;*OeSw03Vd@WC9>rP@`E=Y6hZNy4`)rG^lsrU( +eHMvgEC=gU`{h&)jQlDAM5e{3dMiR3nyatl>F`&BO!n~BQBX$y_NjbP~}sI;eGIoM7TZzpL +H9+ITnNeWZ4jY>qyDv9E7;h}9rv@UDf%3xn?ka$h%%hcOUkp)>bBtO5@N~wdgBFlc8UTzt=**1Kz*rw +F6zfFJSY74Wt?9_PaRY|}%v_sF)c0=umho +>B+7tvxT^rqzmKk;M6WE7@u#w#w4qN+2`kGH+8#KiJkUKe*dQv906&HuaIKncUW&c_2eS-8zBh!6Hw# +>DN4tw-2aZ5Vn!%o!Krl+xcGhwh{EV5e&8w47U-Cwh@fC5$N2p?Ic@ezuZc&-F*xA==`g`OCif-wO}O +{vZK8FDhFKeA@i&WlB;zb$$HZppL|_hZPSG_;oZDa+tf~wHMdT+V;9B1T`Mj5J$m)uaMxdV +RQSK +qQArVcI`c^5_1_=#-wiSh)@#+_JPNWR=A{8272s0=XvoU&V`&C!#w*}e0c6bAolZ-xjr3&Hx1SR-sFwylnj-|Cc%TIDt~_{e{&*TQz)Z_G +Qka@uI#LY5%FX3W=~pDJ$@{I06Jak$CPUPRrL!9vS8JquZzEHWZ8Z>jWj{|`>_1o5CyA1J6cL1q|kT1 +3(5+pR6-sWz?xjYqau@~)c4{vW%>Jk`I{3p!YqGsQI@d#z%K<<{`|i9NhIk_rN&TIdUI65%=*$BM#a% +lzKXp}+?A42zb>j@HG!U$G+z3bF;eTo506ZSDzKBM3deM?g4rrqAcz4;K +0>qJJc`epD*ClOkL%ML|*l4hw)qruRBovHURN0(@D3sYszucanNnLwNtxbS}E4{{4c$#|roZ0M}AvUY +wUEto(UY{NyBlo)E#Y`a$T@Bxzd}>G$E%lCFfF5Y#L%R~6-z<*H$CYgkRJrI3drK|5Q+EJIr(QqQT1P!OeBTH!%Zuywofp-Sp!SF>;H +wTgbS3)dm#=sQiI>EoSwRKdrGjdyhtdUPm!fvVJGYuHb*eBHEg-uTE!}ojwgl}|xOC&NY~jRhF1iZ8D +dO@)L48JC#p#V=+TCV_^t{AE#qVu}%2Qk@RRXpYE3E7b{krC>`nOd=!HhEB)W^^-nlg=KT!pL2kw+W! +`$d$#>V&Z}Q!l^i-0E6%(nzj6jRYaq)k45aF`Bg+0nqNzNeJ-QsMCt{>S$r=OPiISFE9H +9@uM3J_!kXk|Rp!%ry{$}MlhkF-iaXNjhE_@fMehfTAahcd7)7#_567)k3fT}M$Ni$OT6^Nef8i;`Dg +J$3^zX&XmxTs-)%&cDt`r*ZubuZwoaN(w)$r&C4yyMJRE1JG6q><;2!`-QH2}+AGZxD}{W+jNhxF%&{ +v4}uD>|eTs5YubX*HVdWqGL7?ELPE^!H+82ipip11W;3O|!I^&FD`Zj&3R}f~MpM>ri8}f7g*P;)L>E +%4l`tUvO?R{a{C*+s|Lszlwc%^YdY~ZvHM$6Plk@lo)= +O@n8T{mDL@X#cI70?)6z2MPTh&p!U?-fzl465|TDNUaVFQGN@&+lb0%Jd?3=pV^o~3#2P3>?iY?f(uh +2Z*P(cP_Noj(sAU#S`CZ`4Cl;xbx>y-sKk!@l59wp%0d;+D$;nmMA|z7dx<~@wpjzc&7Oacq`I#T2+B +Tv<>Pf&0mDl@N5#5ia^lr*O6{|v33>7UCqYgFNYLAX1R2)~>VoLLw+A{%NbF68I2UyWtgE$Hwp;UQSc +9$|u_?$^Xl`oH?pVVq~q~2ae_i!G2IgWlg$9Lnj2|uyA_-L3qT&L;o)Bvlr(H4+Pp6$pjbg(>UG$x}I +t5D~J6m->+QM?Nr$n^X2rR(CMYkg_BTr_^ER*heZY{TZcoTa#v#S_u`%YN}a$4hG}ltByX*>^>ZHJL76hr_e8&e +_TJ^~q)DZRh*b>-U}a7Z=~vjrRLZqrRVi#zB|1Bba~RbIB_W7ajWtT4tiAkNm={;aZ4Y6wEDw)r9FO=1$JlM7Vn25H<66Qq7p2;^gE55bY00fhW~mIab{(O~du+*>R;T2g3uZt +(+A-&DQnh;qghqKARQ;s6iOdqxcQ +$DO9|+p6$E%Y^!&EPi}?cyf7CSj@uJRXg~TYW*=h^)Q=iKYdaDBUbD3-*uc8f9Tx6Y3002s-bq14^P6 +N$a(+i!5cUGgaSIX!5=s+b)fLkt4xQTI)MH{9PCUS+!J=uRKLY-;AoOd24Gi$qhUev#2L&85;M9VCtM;U6D +AHxqY;O$6Wp)fDCK8^viZrb$w2o5q49tZQ{V=`Hw{%*J +)j(|?>rXN9|6v#O8M1pg`1B~>8T5a|X@YDfsjDfD_01$B$L>Y}WP0J%c&Zr25P>ysHVKvYXGpMUbR6e +dtJMTpI=`0^LPALuock%<12(i +T|Xk6x^XNP3)!f>+K~Y!q3IwHo-VM1L +EDJcKUbv=Cb7;@lu?X}Lt*M~6ug-#5L1Sy7Ul$$4gwn4D;mOPzmX&V5gu;`a18=fA=M6CLX@K@kA +sgX;E^-B$c9@QqWJ+8nk+g_7MQ`S}P+*@%V@6bujoH}TrZQUddTk`ExqU{(v|wnWxMT${Dq828wQvFR +`oiE%W5QwSJUM^YfQO|0C)e-GIS;c{!KOA4_mL9=JhkC`E$Kmvu#YsLQwJCvY8vhnbQ)oEa$+tpH1SS +we`$iJHoQDgqvZ`yY=_zF17e*$Ug@i!SIQ(O&B%}DGftx>u9Hpx9ov@npml5QpvB;1od)FMsil|ZDqA +GPzJb71PWd%`#0Ft7OM-kw69F2;EfP5OCuy*v#z=Kf&@Ui$KcT(pi~*-MR6iq5UUe7huJX_H?N(ZJsg +bv+N;DPUnkqv&{8oRl(svH1XArfHD5>&WYv6pqC5IvSQeD90Nuca}LeBDl4AEOAe}cbb&Lzp{-L(e(1 +%6KrfLN_yhz1-&O3APNZjuheQvWNRj6`@~ifto(fG5FM{qfK6ETFCJ2xiuMoI6 ++}Uqbt|y1dU!b7mJF`6-4Qr+L9ogP-Hz +t~-DvdC`syW@dP!JjQULORO-=Ln20nseVa|WDR(7&=t +Js1K4^oh9qt7*1LUH@uznpEPimdP3cPA1q7>fm2_2Z_1wShSX^`i?E0q$uBs^%{gu8u=Yx=LTcIK>wk +D*?$`J@2vilrhI4hpISO&Op~b3iWUFg;rG-4@PIP}#?QoR=0k^PfQg#bbsy5+&P4G6b#zw1JwC&D6a? +GO_*MbjqBBFVzyS&tz&a?PuK|5qeh7=0Jm55ZTimsgu&3)m{Gq`+;s9_ir^$|F@t9kU9&|Yi|!6b7TPHX1RVz8IJ&V>3K7@#c=5GkBc@#ig*Of +mz>)M*)BZLlVA(&p0DxV8-O7XV5sttZR^%v5dJB&&G=@kXeD}z_Q|8Afhc+r;)|iWkPH{L +?+Zrb@PfyTz%0Md(dhpQ(PKE5^XoGT!}EpSo7NXZPpra0;8|$P3wpW=n0o}Zxx~%S!cO;tC1IL(AJ}dm%H*_26ZhxLBOn=^^93J9@S +HEi5IZ^N3o#sVoulYz0-no-(6=UCB80tGO?JHuyqjI9qOC+a60?Oc&7|+%gyVL%q%-XhYSX#Ar_Ci@e +>pdYp0x5j{#G@3)361r!g(AOIOQ?`>kk4cV5?3l+8N(RqtVrpEVY&V9 +S~xTGsI?d}Z8Vt?@*5)38o;>&%bD2kIY?X)8=I9@t!X9YF`S8m!WqVCt!-n$qrbU$QDM<(&?PSTmlDUwOi2>CLQ_1{KHe+Uzl6gFR-k%Gmo=K1*pw)jx1_7zvFE`!2e*?l +q(dIhf?6Mx_HrY4F$A3xVIaNq8j(yt&--^yLXc_grW`_A?jX6{TzJomCpz?vk!4uY-ZBi!MnWFav2U4 +Lu8I*s!5RU|V%`YAPb58ODNh`Ga<>hzyj5{7g9CKgaw`go6`DT77YX(P7xaT|g8V&W0~A3XE`y0>eh` +0;0eS3_cwh;0u2i3JUYVA^wHw@c^F?3-(R$)P}RD@6ZE;^6wf6ftW8=ftTQ!CpiEYmM=|?%f(to?P+z +MO=nP$#$S*@hwR)7OEU!EbaHLg_X7hwwc)5pBLh5j!6DKtAkFWhiRk)c2AtYZW#~cYyKqcapb~Jd2fm +GjnAQLyC>s4y=+vmCOewn;j}t%Yr+*=Dst>twA5k%BoIjfCM|%GeQ$MMD9|1~gtoLM)@%b>L*{Pp9Qo +AF$R%Q~;p@41XoRfNfB<+~Z0BS7V`@+JR>$XMUFxmT_MP=A)2?R<{Xn;XxM*d|Nn~v9U!cId#zX%H~! +KUz|H77tKDGUkezP?scfS)nY)P$KDKq~h)HZUaSzgf(X>@L5BQ1t}1?w2R;s%@D>*whB{O*R;YOBOqy +0UWb6y9}O%#0)9%CEuM*GE_ejy#|d(rK`LZSLO8qHtx2SX7_Z4!Q|!*e>xn5!hJ9q#86O%RrJ +d*gr9dc(keSz_M`v$U$HSwjklNAhY8A1{W;>|9z9n74aYWHkBFj@3)b#Ah6LlBj`WlSQfDFTR0bwp(O)1k3BCa5@cFwEFiUHhzV7ZW!2%CRjK_6#GcPWJuLszrVOVueL +psL#H-aU$JiH5#%vmGlK#^zd7W1RtrcK&eceQg*j#4tkzA$?{e0c_eM63})1#s>NGLquYh(E +%W=OZ#K|nDU?*_=DMVIPdtu>NySP56yhm9FV&Hh@K+Mk{|g77E%O$u2^jJ9y!RTnToa9={m=4~G^xCL}-gBua!!!p9uSP%jDS8#4G(7!@>DCg-T1#IE{6@^+a-3WYj2k +f5UukJ8J*u+Ey3V)>^W`u`|M3uAx5V&3suc;`oz_)xxhyl2V@HC9qbfX-W5rw125FmU6VfsEq$`!b&W +&+za9?0YcRw?wOXJCY&>JCiYjBpbZ=MherI10Og>rmlvNLTM}-LIE)Axh&u{;%%q7T +{wIu&l+>~h8(fX4-!AY_9Ts0n#M_E`v7U#8olPHtRsemThjf3Z=yhK9wL)F@X%)r!( +QMT85lBL3^z-fd{o>JYk06dI5dth(v*HaflEKKpI9~^wNqvXDG1?Q@d(9+n@^C1M7 +sbeD0hZDA0asbUVF|>VvU@2h3;>apnYlL8oSy{?56lM~)@ckmt^JYYg&-&j0SASNeb869)~Zo!2t^23 +j5UsDhjbYu=`FM;;4YxXLG*fz`V%a&>IhEn;hTP-ViE~eKbo=b-UR8wfGNp}KV!(GvSx1>N<^`xqAwWRIf#snOAaDqZ08^{jv$fs7~6L +wGA8^xP%oyzcc5NOA@6`G*2Oy-mI0IH9dX;|OaZr0qTSDHdtP*w+$q1TbY3BMrUXxIxaE+;bWi0fXc~ +tQ-K@>SOo3bGU8Zr>@t+usHlnb<(uieBr`P*vVRL%VrVCp&4W|cjn^XT%!tLRlQqp~Kpn2^R@ +-S9`OWK~t}*m@->ot{Pt!tVxBd0}sVgWz~t>sQ$!c%V!5O5LQxX+0x;8#DWD~#H}b=BVf;_Fe#qZgFo ++u`K(d?G59|PA`dbru6eWf8x8}WhX&@>jjCU$dBtI6v;P}`WC~&PsNr+k_re;kF=mc%&%i#wn>?_Z^F +YFg!Ot}_D`z~T0D`lK8;M56CArTUkCBftk2%~uT{O~@Ou-0=BC@iW1Ua8P<3 +Sz_gxkAvQ0-0hO4h2B&8A!K}`kVWzCzNjvSG!6SsWvz{UM|=041`2PN|VyZc0wkxv4Np``rx);Gz4CR +Qed?d43`V46IJURJNgsss(Je+c6P_vZZfkY) +q>Gv9c1y9=0*MGg-DgZW+11}se7UYEfsgaS#rz8T$`0}Ar8@WpH2@a>=o#%yU^=_CzbuJ+B2_YejBgE +w4#HJMUclmxtF=*#Lr0%EudHgy2;NpT3b`KDtB49$a-)NKOUslMr~L7}A}>xb_sX@v8(g&zpoW5J~mz +x{#m3dPs_F+-MRY3ZqG#>Y!0fzQIeTb4vNVO3hI(2~Ahe}|)CGMo*L%-}fa1aDtYlMBbo +HP0K0bwi)fTA20*jY-)A ++@RUpuno( +l@j6eyo8!&L@IUz^TXLSUmQp(#qL)_4O!X`J21DeqZjsyG57jTxcMZXJ(7O|m+3R5Yoo_d&f5jM4fl; +JXCz^Mb8P(B5uamfd-r2ZEG1t58xE)Q$zWdg6=;oHuVp3`?)(YesC-z~UrkJfaKwwmVkT$vWCaSU__HNTssn7ysSC5>)9?W?6w5Rs}o*JaxlIRMCiI#)O% +0%mHC$4ATd;h8zR!j2UK7Gb|18)D4G<_BjUF;+-5qnFEFyK_>;rKy6(1`+#A*kxlI4luIBDWdL5y+;X +`|mQ)9h;!G(jOA2B +Dq>eB=yn#x20=G3wh}r{Jn(lh!i{xU>aKu{{A{=mlGv1U&mzVq!w|O%x2@5OLT?BYLT^z#-grUYJG}P +!!gOyp6@e`BjcZup%M%;6<4Q3lzWz!mVVWrVqaeu&;|ykQ8C0!e0ptj7OmOz+!}2DHt4+aXF4+?~pXL +fD;-Me(P=plTDeH%ZP@ZPm;%YL%xGpw*z$!Jg=!m;JDFsLqL}mbj;X +MLU9w0w>HSg!32u}GV8XfW11!ZV2q`cG2lGpjU`%<4HDLsN$l&aP^8rJ_ZBl3-ZX;qid&xgALt3$v%K +w3|s)=F2SVSsEkK_V(`Ij>BU}pv4s-5h*{(~=z+h>9Ar$&yxfL*M?8DM*;EeC?&}>*fvTP!F5McSZUQ#YL9-agk>+%io)P^@k2|wNvK-5Zd +=+prQXM+?tz?c;rnFe~VR#pPR-=NvSaqyh(+rgX-Og$rg2{1I}8Q`f6Z(^VLF`YpD4>t@*&(7dMkU=! +3Glz+fMd+I~YNJhx6#UA*fCS>_p8%R6f_ycM?rKB`-+RJO;AouQ0=1ND&g{DQA+=!D?Ag=|-v{`*IhX +9>c|V-Dzz09d#{)_0ksid=hZBzi0qM&V@Q{NjMi&v3ggW*YAMq0sX^@UF-XaqZY7%>#N0 +q{^(fuQKve84bw&h{x65YnOpWenM$H*;YC(VHpMM$w0!$yZ`KxKRNOgY2iHL7Y{%Yv~ +Y4@+zI00|`yHMaD|H^w#Qomu}c>kZdf8AiTQ@@l1SZ +eESUk$lo?!}2@lcNfEQ{)<~w46Oav5;h4Y}0y`%~6(}dunjsZ&NEC)~v%fcYF4vlYD0jI*u3m5`UErJ +yD>|uTEi&qd03E8Wi2Mi9EReKCUVNPBIHj*O%-{x_(@Xt7O>Hwo7-DU75a}2**RUqc{^dCe|Gzu|3!; +pnX&rxH-HDNesYe*mCWnfb?flW=w-ZTZ*cF8{rl%et;11+IyxtSen +x}^TlLJfgza#L5mT^>Tg-AnEA9&QRMCF8no*17?gE?hbArCPmF8mT%=vA49&hXnHJQa~IC`7Z8mF$?Nctb3Q3V?wm&&LB<}ge1*$d2=`J!@0dpfoWkdQpc=NE;`}V~(I^K7GtxrVk +qHF3!w^%6;QMMJ|56+reTJ(zr-0>PW@3c5luj&0`d~`1VD*H-xqc!JBE95=tg$E-c?2<-&4G`iL< +;{s2=VzQ^k`Jru+J%z8~RjK4N247r2G#d!lo&&NQnLRk0k1p`s+c!D;RCCcL}mzYx#?0fzgif!nPq<5;xhznjF7F{ndxn&%pW`82=K>XrqQHQCxN~5-AM9|)#I7(Uv=94X9Z4;~5AZ+}g;zNg({uU=r_UC^g$E6n^im5)!5(ppV6eG1hI!m@ +Mp$CGu^k#%aK+)QS6`+i+cAP$hbs@i+%CYYS>Zm2EcS|D*MRn=Nb^Mc#!Fyi4OXTH1;eu>aX>Vz__R? +uKoDXspi>7JUKtx8*nY1`0dampBuin`1g$FHH^5T|9JAjclu{t*i7$9_puoU;dSZmtpeAc)U6$|a97bysEgkF1}}G&YdZ(G(BVGdDsMqV?=YX@C; +ln6=pB6bbM_Pe1e-$MF)S}?e<2zk35Z%g5e@gYBK9R^t9JZ2ugTaMGG>0AF>-wV0&ayx#=E(Q;SaL{t +71k8B7JxgATmCbI|K}S9MJ3mz7sb=Fg_G#cFcJG%|^@mS?Xc$o{bOn!rfI6AqA5tiWClD#I{ +v)@Ci@?BxPpp@c_f93>NGJwV@BbO>I6DG>A^@?c=q$kGTW*2Ht2!eI4mIvEI%4U8>3djaPT3e1GbFeF +UtOU;qB(*XA()nEH+o4y5dqe8L8yvN|+qYXLY){SZ9qD|G)TYS?5zrS6ez)O_aWB6p&!H~hp$M!5!!J +0bYh>hRmt_Yylc4=4|oK1Tz3ZXxxb-QWg;n5Qa5NkN})mn>~>>$ps5gR|wT@hektm923|0NudF&iSk=o7s#4Yk@fq8+nA5)9GIkBI&+f4?g~&Fqm@<>(d?I$U^mZ +?^&t>X#X8XSt+%SI1I!KtkHK0532SkH7+%!wAbTX +yGtu{Kn+9Wd&$np}r?0gX|hWuv83yd+pnZeypr{QylDVOJh8b0g~}i~74LV%kYo)zt{ +9W78Jy0%;FKuFs|*u}=*G)hFWDFGoV*a8Q3}(o>j3wCfWD`hui!qHFKmQ)X!5!ob#w=GG-e!nba +zQC|aCj3JacsEfK1UP`>KNhdAzQ$hyA%ktup?H8UB_z)&x}F-aLnJtg#iH{29%RO?gcWWvqu5Pw+c*W +5j{AfEfAD%ZlLDXu1YZA)CFCYz!?Wl9nfHb`RyRj1af0W2sb{Y0^&^IJ1QUqymN*Ipw1{@NNgR?lv5 +Ufm}q5Q^a#n-C}44l%97=~=aKCf(5y(tJUB9!2%6OQWKA2U>TPdR{eTGZ(WKK9#i5QNTeFo2BH>O +o+c>*g0Bpt1-2olH#ULt}$#yXEY+@2EV*0jpR!EMWp!CPy#k#KJLSc6k>YjI7c;5HYTO!TLB;b~!?q1 +nvGl67v-e{dMSWHSd;73Y4&KvM&j@M|+OKp?yiaDIz{gq84XGsH^5Zo-gF#?K!22W5D(hgdwxhG$k1; +nUUEKIDWY)&-gY;4qPM!r*Wjrq2-Y?A?eV@RkhL)m(=Gk2-8XVhYl`7oD8L=3N__{G-$EL23fDCxujizJ=RjWkA_w#@WEtYnQV4nApfkpOjGlqv_b~t)vCl$)PV>tTJ|gh) +1D0TyAHZ7hD+6Oh7#`LDaqP+fwqslwfc2(s=p_R|6ccW$C2R9!88n?|^@F+?Y4tGFmg5H!Km!NDuan_ +_Mi83M2}dMOjFE>9x?<=nFw5Q!o-jC`JTL@2Dty2Yn7c87)E{M&c;Yw|Yc^#)@IF$*?IUxU&X`=NCva +*FDf6iZ+o~aeaaVPKa9mztTg^|u!LswiZv?aDcn%nRb6|uu;T~{|aO4)d%=ds3z?dpM036$aA!D%iL( +lO91GZioPN5mW3y@6Ul0eGy20`pb-r$H~z(M9kn3+bDS10%EGvL$#t-d}s1(1HqhozByG%cQE38nEoLv@CN)fvkhguti&h76A9956 +PP7Xqc$!Y9tga7I^R*b{6F={!40Z}F>raEq`&z$wqblmLF`xDQTDfq;TTt02ofnsWrHB;0o{Ff8F_Zs +A}a86|;B&IKg*z?P!GU}I(}qcJQ=9}mS<$oLSlfgv9T)$%rX!`s3%spac|g>7FG+NX>DRy7@KLdcMR# +G1qBga8%G5>X@onO}LgN$@zzXXtu6g}3aO9`zB732X>Q^9<=mJg05U;8+BgjeMqW8xg99C~%+P5~fB# +I|32RzySkJT~N +{q2F3(RgtNIO8HT9?4j-ml?lU->hC@UxA**5En{X61#46;Mo>9Tpd|oqY;k@=30vKnNv3c#G#u*u;1_ +r0#zJ3(B|fpv`+VY@3tMQncrv*l|TRDn|XoL>Q*T +41$oEodYLusUXfl2bwFG6*W+XVfxd%RT}O+k!((i8+y^YIwoF3#VY24B^On00;4zxr-y4k}YK-o3Ft1 +A%CdA%wzb57DwXKkOv%*K2lzt-{ZJDHzx3I%!`%>9Nn_7QaJYgnF(NSFkcs|Yp*O$sw@hK6c*5_4aO% +Q`|uEJ5yq-iX`e%B&%Jvw959@0wTZ)#}=Rg<0XY{sgH;Z-%x?~>#}4mzXs&F4B)CsW^F(whyybRdd6Zx5L?aoY +bx-#U3MQ(7^@G*O4)t9l)`shTa+e^>*6BKuAD7ir208a8z800F^gOn;Hd+S3msHHV{fcVMBq0&-~pZ- +63k~DI(2|y2!MP3#nBNUSb{m+|IPV9D{yKYIwHncok(wT%>SMk;HeE~-xh_rowG~5;Be;rfioCr<4c* +Ifx(B87><2>5jfy)mXZCLZ+^g3yc)X!-^Q$Cu&Q_yXs6q}qd+ch1hPP+2XT2zG#fL= +)7KzcUSq3^_X@8!6Ip=4~20jD<9{NN_p;yY$3iJ!Y~!J~nEKn~@)xKrSVkv%`u$!#P4UMuZiqFjnIt +v$u>JsBrISlkp9M&wAv%Z#e6_NceR^2tnLoQau#qck%!MN6_F6D=>K6eMo^*7A +8Q7vBr$d=L4SlfPs0yZ=jCg`vebo+5=mr2mZ}xQl$s8>m>XKe3)e)eB%f7;qaBw2l&b>O}>Ya)pGy{R +u8lcZ-C)Dr4Q^W7gFs9yfi65=N_!ln;;JNT<~$shnVfG;KI;D!ny*L@h?}?s`FrtF36r8&IvfAdB{0$ +L%^_sgb_wN3dbA|e5OEb^AUGMz5$-P;IK6zm3!oqcOMWyPlNR+tGEs%M?cnIfnY#WHAy~?_yr=!Tz$0 +0o3!@P8VOR-N4D)kc;kT08E|Srzw!(uxTf-z2OMF)ggDMD +xj_w*oI<16ev_!iGsOS&|wUoGi^XzrWO4|XA0`s;lJ@S>al_~Re`_y^@Nsx7Tt$tXKgmPUB`3*enUcm +A_1*L~;~W!}DhdtBG0V1C(^ZWn+D-KzG(BLMa)N_v_?0s9qY?+}24Y7FQAR{@7L$RaKy3Y>Y^Z;P#U~Zz0E-{i`wawsTT@N|cvMl2kon`f?%-C1lt@^sSc?Hfo +xRS9;zl(6|PDR67fKqPFk)Ro(rXb+Ncc?&pbqEn96rUi_VU^>FXeqtMtXoe^80PKU-$m`kOaz?(gqk-w$5L$>L3KG +C6qjLAhwfe?FW&`&rfdOn;*3S=pQyiZtyUi%guwY5Kj|ovN+ri%+Rcs=r@;Qwi+Zvr7@A((l|aWznprC)fIl%8TZ&nt`& +&{{v7<0|XQR000O8NLB_@n2}4JMMVGrt<(VkBLDyZaA|NaUv_0~WN&gWXmo9CHEd~OFKBdaY&CFUa&u +*JE^vA6eQR^uII`&Xsha=5k!x?{T}g?P*?IWHIVbP&J(EYp-ba;>%S(%p%r!-7NXm+4YX190Hy#8)fC +MSoNzNTrDwar~0W=zo?na|~mS>CL;$pTcS2?-32;#*u%gZ3VEV5)(k_+~G6h!e&T*O&A3RY=6%_4G<# +3jkYr1<))8NgV^%gMzJNuw;E6!Av^XdRbV7YkB`(&~i2g&T +Wlq9H?OmB&lQjO7T{I}(jAZR{jlwdle&+gb1_j4_3dkA-WjH +^rezNyzxFFag@!+alB*$q<=6P7sse7NrX<4ALdO8uA%zS84ukTFRFG)7C2OM@qy|PA83U(00EjCJZ+%5l1Wf^Im8}>!gz^+p4sE9y_DdF +IgD1SG1eO!Z8d6#n2M}qaefcq5EE94lem{BgB)}yDr1#e(zMS9}teK0&gTJcsY%1?PhLTSnUuMzf$>Z +gd)2o=SHb54|;p6+uCy(#5SNE~py?qAYF&|m5y7${vSuPLn-(RoSll7xXme21W{P^RK_dh~~-{HS|L0 +AS4K7KzV;q==dAN&|yK6oN}PKki8vdiE|;~|qdDNpI|XYBV-dB;1kq%{C{NJX^3rHlToPdN~V=3qHdN +Lx~>LHMuoa1M1@DaxJ7FiwH|DVHAH#)_En2De`k5>l@fA&1I<$)EU*afSU?_5{(i)CpE>M)j= +B$|7VFQ1fhc?l%Z^<`UCFWx=>+nbkfPcL45c={T?Xp88FZ!aJG_}MNZ*D{gctSgKPD_2=`5s_&|_rxr +(q?h4rl}=$R66*w?1}|7i5)DC;5%1sw;4Uswvc9+&0+bP}0V5a%AchqD$%}Yesukfgqofpy-SI6}kv} +O#R~08z5L*;Ovp#&{8J*-9g_kKA%IYH~b4_0H@thj{RzFpnM}ZiB1!-SQz50w^>RD2F!0-9=)Q%G-+nk5nVKMR#2TUx$ZO;cgRN}I2%I=5PzN} +39wt_eH7;~9vp9Z6@ti~B*CS1hDbCCUcQh2gUG +o802Bh_9h}^L&mN{&ihH8)3rmn8%d|xif#-!qY4e!uK2hFh;w5DK+^CsA?$sypsEus6Hl*#B%2o$Pjn +(QU?%T}kC$9k5xZWIm(tklzHO-od%;|;0A369w*X~`fKOOE@O#NeDr`x*2GZSk1Bb!_HpdK|I#yT3fw +G)fUq95zfd0dcKhBGilYvG9qB1PO32TCOz1~*L*8}YcEK!3s;;ZKUjf-9TMw3d-VbeY)E8rLkTIK9q? +H|5dUI9a-?|uVNhje`xoVUof_Hh?s?32DmLC8Au0iB%kJpvpeq3(-a<#&yTG2xWAg1m40j0xh13UqdleMz=?7{EF< +LLCi@(%Av--%6wzc5SCofPXC-`%b@~-5p!Mrwbhp`HA1eYkClf4>h2+_r4A2Y;c7Z5!@N8V0h2g~O?0 +ZgpSS-VmQ;k+kTi10Wvtd>i0L +szGjSgrH4Ac~SYM@a@Q_VOQ0ZXSq%8^2pvy8{g8gv3Lbr2-6wwmv#*>DXym1nz0N5Q>iLQw>D?)7d|g +0MDQn3KgwhQSIGx=5o9Fl`t=dulE2)e{HK;j^TIl!a)JAqzl^JD&|M31z|LC%Rn^&RumV8Yx>Tp%9-n +>!QZxd5lFVLjcm%7)KpDHB!c<3n*A_r~p_VMsWeM$r0Tg$&6BWG`t +{R)L+gR)C@B!}Avu+l>XOp1OeXzx!`m0z?W?;=h)8wxlT8RD4dD-CokYAMC6(mPcg?&`4131v`Q;;i9 +wy4@S(cfQD(g{bkCob4ay}!+$~l|AjIH0HNdvf?z}_`~~1R*pRwlBYx0YP?}-_R3 +*OC-jh^$&s5X6_QOzSJgm_cP2V&{WFe;}`(j6W~I{L~UzUZMrIZRslPwyJ46X30?a`r@U0N_3t~S +tKk3`8X7LCFf<64%Osv6a+q=QFMy)`=A|r#T*KK&LnCye(eAI$G&9WG&H&u=-K8h(XAEk8q|jn6*XoD +`MKX|}<>ANKpn9uv*c1J1##YfNqSfZ&jSXqGHwBl9epxrd6I(q`hU5Un +R}bT_F`@b~ZjZmm*s2EiO3Aa17{gD?X(@R`?ffLG6t{SPR4kV0Cb4#Zj7~F_k0=%I<#qeShB$RwWiYk{kjbAH*;r>Cir*`!Ki%+Sc4M0doJT08*9|u9C8P ++XRkvq>-a4E$+K_CI)RNW=Lm5&9(Ru@!0^#XK;>di!SI{p8ZNv(Cp}rI!`vsE7*k=VG_^NMVMdH{dK= +_Y@EC(sG-47M^-kw-vV#w!o6+pZy9rK9r+3$`q(;RTE{cAS=`JAEu-cXVwJ8s(1x4~t-~X8lXOmvV?n +{EF5J7MC+E7fgFzjEy9qppqdK<7IhVGW6QlPHZK}+K=~PrL09$kgqT*W)hJDUQ%ih +H%r0d#9tk!Jcd%Bpv3QWc7NO*5eDE%uQgh;3b|RiBH^43V6i+;vH}$lHwyrs?x^;3dYxwLg +hX=^)paKLm>c;Zr7ZF$i=#1@+{a*-8&mBkhV!9uktK1oqPuwGrh}i~7&AQ7I>-3q@B6yl{vU8w1A25q +W&L0lr*Uybb+x#>R|GHLy;3y|j%TUyi#O8rt(qH|bSW#Dk}Fo3Q00rH(Xe7o2A!yFDh3)s+3#MNmYI0 +wY!W_U0p=RjcEWdM)*n^!kQZg)d#po2Df +{P(?V>Cg1;o(ZNVh?Q(7oPgLRlx))ZQnk^&f57OSGfgF?{&vyr3`XvD;@szHka#v}-iXUvms7L#biD# +{l4p(UDYM&^{x!#ql0PO}+ZS)1V5yEiaIQ1Ejw>?9%Ei|HP{nqGlNN(`roZ~|>wAelNmsUuSyFB}50Y +0uJ(J(3582zD0yUn&@%us2{{VyCJ>mTC~2d_&-KH)n*lB9pD$wcxWemo2^s3_GDXqT&mPA2_jtc5^j3 +EM_45Q#IxVUr_oPitbs`d$lUQN!HaW3)Co0@qtPe8zv_+3g})==s?O+tXazm8hnJ=l&QsdQq8c$^&WU +|FN$yO{lPeP$2h(mzESfn+Y#&N;D!dDzOHC|%{4Oz?)}(_E6D!WxdIcaxZxDO!->Js;Gh5e=XjNh0~U +Ni6>wo9K_4KMQ|tw_0xpL*A|YUhE}KPoO)8l5XBvTT+Ge8J;RQ^G--3L76#VwPff;syE5xIr{pesx&I~Yq#nsa*3W +;7UYsiYgRv_YD+I2-7vm8M;P56QgR#VFY_ +?@J)yMOKu_CZG=~p;IQGOoHJt=m>O2&)BhCjCzC$kF$Apx-wDd5e_2jz6(@wgO1^f&`A^&E4rakg=P? +^C+xeiJq*f-y?##j?)uBCrk6Q;`5{}^-+|!7UZ27QJcFNg=p>9<3(c~uY%4Skant3U56mXvqw10lp$Zdl}G+F&EZGvbUr>ojQ +j4>v`Tr3b&@=&d$qRN?obADd0d`b7>Yg4?WZNu<7v?Z39y9JZgb0896O#otU93Pn9kh;MzpFpth8J^~ +Q%;!3u2DI+e)2H`8JpIS%%YUAtZ~)?Yn9M2nFRvELUdX0gfygL`R|Rrmzy|xZG2j$4ZyVuhQ#ahf!J7 +gc%=-ei&B3OpBbCcvr<*&NF+Pf`c}z8^_ZCaOV>&gOfPON|{MTRo7rO*GS&&6W8)mm7OgGWPiUg8uUb +`UyA1a3boXzKOw+7fp4vzX!!Cie>FSz}TZp2iSzhX3_N$+4oy}%Uq;!QBMfhl>lCn!GH)S~?u?&JXb5CV)dp1QR?79$1#6FeF16%I50@;&hh5 +AlV7-p)&L{W>&oiPwt(c|8A)}3$!wb5A~|3>ok?NSsiBy81Va^^_WH#{phsoY6E|6(6PjwhcVgK470; +tT;MGRIs<*dZ3Ur52p#GIj%`QbT*;JoYwSd6@_Xw6Tvt%MJYNnfJEzP1jlA|mWo`8exBPkm4H>RrT3N +dfQ^N%-19T&^RPGFkp8M%8>>dnqv7vrFeW(o^8Qciintp0Z8PhXhS_}>_pevFr;mQZIfic4r*jc!9Nh +tXo)p9tu&>YbSC&nNC9X@c$<~S0@)-&dx8UwI3sjuzKnvXDvheAIAg;5Bw3PV +YY<0bxImTDEv%ftIaW^Cu@#=_IBrhu1=$VB^Eiq~I&t8VizK3hTxN2+GS#NoIFM`@p~h2{qrg>4K2qh +GqHr8>zcN=V%4v36L(1U5CuXR*4Y}Z(N9MQFh&UNLh%BPBy`;r!D`8FQG`Je&r +>vdsMZ+<6!(Qxc~1t=@13gW?7hyS!xH&4xXl`St2F!vf-=N0`SqO25QaEkLd)2a5IyvadzmO#FX2@aXVOx5`8@N +Q`?lQX*QN+*;`>l64J6!`V2rB_;Bpdtx&{E@#-f!=?TegO)xB70^LaxC6pL`W!HmYzgZL5$>|mrMtFv +?O2I_{Wx}Ekj2)m$uR5cDAkfNpM)&rEKL-bs1BZ0E$4z_THRq?~CPo1zSY}B)-OzQEM%e;x@4epyk13 +gckF@^k_|?1M$r1dv1GaT@vdosl6xaa7r?HR36vGs`$)XMd(~#pW7f#EmpsTFZn5=$%3fFMWdyLtun& +oWMZv*O#bFlF3n=1zn5Z)3J0aHOU4lWkTObSBSy##Sb62}GP!1WTch)cL|6&EHW}IhnwTn%MZ@0 +^kjO?&|EmvFV9>?WW2m7Xa`4c1V|Cd6;EG8e9NtRPh0(eBXk$8jL6)g1O@!0@cp*` +CDU?1&pu#dV7mewq$;S!=wE*29ICYF;<1m*_77lkLB>|Rb{&^19iq#}Gajll!D^Ip;p2c?0CL{#V6F~ +FQ8;YV-4Fo(aaLW(H*Wla@u4l*m@=#I%%-%TDI9>VQ`2j4w7I6SNb88B#I>}N+2u2`i3TPCPw6dGA)<-fz5(3dQIKy@SZ^`ogO5P;D9*YSf#jNuS}(v +C=X{oET~@7SMI6R>{F-c*-=V+fpCxPRT(v^*n6kb0l?v59)p@siI7gzcMrZl=stW63e}Lledr^iNEqu +!{-l*@RXk59@NK%ScnNX-}64u|XS7ibH6lVBKiQEt+~Ugx1gX0Z#~jeN`!@#%_vu5t?RGF$`!}JlTkSe}z` +I&dJounr=+GbgI_9xsutob|-chrTbvrJ*P{>yFu`PAj>~!5`$_Q#`HJ_XtsvOU=atu+;3iBisp=h|U8|8UmA-g@@!%w7hZ;{ByWjOcZuFCED ++Qs@^54Zh*vh!f6Q2Tif)!|mweSq1lFXgF$4B0u*Gr5HN9R=+PZ$##1S?EKr2by$ZK(XL|$g*+>9*r@ +PfkB1?5zkJ>Lo<)OI&uxT>T#|QC4(c^?vvLVPSb*Y1O8nefbHnW&Y!XR;%8gFDBaP!`ns4d74^GJI~z +wk!>f&gyPpi@?IgUkCT=Og@4VxooMbJWSB}||7&Cxgd=ox?ywl+-;D=EcRyF2D(X#b9cH;tHiOa~5XUdghT1!Q2NZKEGe(NxhoI|gs)n+~d$QaCWs6|iFf$%V`5K@K(bz;et!g3VQv2%}W*543r +$3a|{1T!hP`-S1ZF!C0O_#rVJNhlW~TDyBoCJ7DBEQzAY3P}J&ntx+oeA}QUkYGSi>f$?Yzxc#FdkB)7; +)3yMDsbX2h8BjAccmPvp&5q*EFHY|QWMMwVb9zTM|R8XEe~nrqSa)#j@BY##c2Yq!+eO=`I3zEbN%t6 +n28;MQ+WS~1jl=d%X{F#4&y#U8w{r{7Vl*n?dt!|ta6RxvPa`0>+r`ih8L0@ +HfmJ$2EnQ+y;fo$%A=T27f9B!Jk|aj(o9+%AH-M=7XiuFbK8JZX2t;_0+n34)Pj!=$`&Zamu{gRrLKk +RccGvP?S=*!PJVuJw$fq3iMOJrU1&Nb+@RuAp%_vMm7FiFI+2nVlOxxK$cikbm`w2-MaDnynvfaEVvY +^Bk(n$+R4C#;L=~}T>^hAr9+@Ed?D5W(W{E+Y2e +20dPcWA4wV{pf5t}5koR`ge<#Q7AkOa$F^#85{`U3`7&4)5cei`@W|wXiEgf=5H@NLdF`{Fwa-)>+zXzENg@KCvk*tvI?rc~N +8{f(MMz1DH8HYc-*+iW4qbv6N?(wNiiuIpqGX#eR-JcLqa0Z|om%x%iC@a{)hDekKcW;S)|;#Eou#d( +LMOXjLF=dYe9mH0(lj6D%knw+oUoP_~(Nd(;-Nwk1F}o*WMqY%t&J{fPwA987sHIDzz6EWRBM +QXv0|O@@sPScf9niS_N0M3SOLm3~) +NV~<+us!k`Oa#+uH!>JK3B^aDPidmYw(YFhsLRvII@| +egSf$^c*a`wyvVcVTe7BNQHvYQNk&Dc7}8b@ +S*X=)7Cc_&CAQfBeHay6s(wP%ZOx3`a`K+_mmU1y?a5~s<7*e25!-7`6CV(ljAmQoNhk~*xC2bs8JS>{ +5V(@LW?1M)OFhm;<4Dlf*W>9dE2biX#$czrrYZ_hss82sug1v)HmT0^ARR7o +r^R0bKYW40P6-6Wk!xD@~99Q70iTTqBCgGuR3<79WFH(>vD1f*Na^HfFs*TWwro;M6~N;tH5l9dWZ=( +3QX#f&4PayR(9T5a##pi$a4=C_+0lUUCcSCY#|PjG`PeUBVp2H_UWpRb7;x-4@&JjA+X +e*3r9@%_j+A4S)2yV)RAqJ-)YgmSW_!#I)vd{pYwdyM<8IBz47lFW&8Ni)(Ze(WkFXnCwTmPn)JBoRO +z5Ee9%ccAWNtL1mRG?7FHp4M_-8e$hj8vjo;$)4 +8yZjR&wN(q$mrPw71M-o~K;N`MP6Ue_tAThMQgQjnh}0y0fX~9nvS8KkPf*Vvv^Vy7!yspgw{DqfVFh<>eDg8HB4*uI*uQ6SbXP*krwd%v1}^>3;g|*+S}@!YM)s+lak* +_OTCp=${k$oS9rLKiyz$nhu*cGj1_0@Zl?$Sb6zRP(VBuo|*MJ+5uU2wcIYL_GBwOWE&!gqfD6ZE}jg +t14VVe2uR^%d_x5Wd>xa;s_GHKwSZQ>^cM}j9|;e8E9M2QhHW(=K0Cw8)~p3Q4Qr*>>291Ci*2{qL#2 +Yvj)s{`pHjY;%@rA+rxodO2u`_6s;xHwKD^0Ch;cSM$SBf^O>k8WbxEQM-2-u68gO +dkyv=#KgaK{%nC?!8#Mw&P~s`>P;Ua!SkyfQc2Zg`wf@2{#@lA*JRp&B;h4#j{nY-?4Pq~n8d$g3caA +V`eCkUQ;TKkHCL=?eRenqzogg_$N=5MFw$N@!kliJmv~S@33d{^#Irc|Jh&#Cb(Tj3rD!*<6py@mqrb +5opPPD5>32$Q$}NaR6{$h8&lN@qJ4hU5+Xu;#WH%aV~ +~h;xzwt!x5bTJgLG*&Y~9`Z!gn=~yv)6eoN^D{PZ>ZipGHl=9iPzyHM+Vm^6&HWc`G<1J37{rJiZ+r3_(^VqgJDF#zTMV9=EQXbaQIJ3ov!972Lh +m@Sbz_c$1@EoKht)@)Xtk0DQ}&h~@c9C$0-`!t&~NIR@`|7(w7{XztC=Po-Sg$~;&#+7yZBX-hrD4+QU&phPTxQM#esy<*w*x* +$$1Tg7Ic6BdJ64KJ0)^Q#g)=im*5VuskwIfe?iou2IlR~W>*x-|^mV&s<<-Zn5UGIj5CYDlnWOaF*o6V<|rhL_@x#3g#{T +I*kU6I0ds`kWPROG1eXiN{vP18p+xJzA4yh;OxA;C#>BBw5R~`$QBLPM&YEA6 +5R^7NOm}YvP+gz2413MA_W>n)NuoJ5My)ytN0dKO2l%P@h!p>g@86B{5GJ4rDq+V6#Qq|7Y)WiLM^UGP*4)fd6t-m#J=gAcXyt< +t?V}k5?=j(0a2Zao@sJEc)1YWgId#b;WsFq?W7i{a@iA-i3|bK(xQ3H=?kXqV~vHB4fhp+ +=fh96Vcy3@f|RxHsn%f=+kla2S~j>bXmYWr>*mUmk=0&cq@S_7;c_qzmM{W>J2tvX4I09x=e$ns`k0m +E7K^L_5)>D=ey>dq~5F!3<%uA!AoQm*u)PMdBE7W5%EgoL$`oX>4nE7yZNto43yl_xl2!G;E2^Ho)Hk +Y@uqY8|UoF8+60Lft_g@FqGWAE(JGB1cWEn7J2h?+PyyoDzSyGga;o+d{A9eDcgIxb<5VU-L<=u%@$DzMz&Z@uf{VvEs!%bV%lmDQi_^ +xf-sq9d0bvCY6d_V|F*bE)%dq-a>)YUW{`%mBoj~x_XOOh3~Wwr;sToa{)g|rH{pU$+cV6998@$EET} +|GJ5>Y}dJebjDMy}|v;v_9$uc<5pJpox>*p+6KF?NM;CbMv`A~n#Cbw4qaqz>T2|xkzsQ#t~;x9+gOD +BkL9q@nVhX6Ao*CA0b`A=Tf+v)B?njz!*tZ5XWG?DCKW90sT_u8%n)Nwszz|R%{WvtCX#$2$LdfBL+rOa%yd0a80uLfaPrxkWMX3J$3CQ+ +&K@kjr&qZA6By^iF`Zxwlj%%PN2oBAElfScMjqfQfV)VP95xdOXH1 +EIf!1;$KsvfCC6?(9X9Vv*xpf`9mEgr~1xC@dZ#58MsVrB9J1g1X>sM-k;=rmswfTuUsg1l3aaTk`Pq>6s(rudE^i`; +uI`lK&>v~#UWMVjCDkHo~#Kwl^Wz%yI@OZ26rBljn}3J{7j<(ZdP~DjOb>6djzR?W^Fw+5b8D!`^!_L +w-klM|9$-pBpuJvr|Fa;IBSUSD0$F0sAnfpn)t!0_nsp4XV;*(GU08x%nIh62kBSf2r-UmL-A}Q{P5s +bE^{ujOZKp|F!UjqE&e)z!&r1SqZLWYPsu5#UlR$3SHgo|7n{bFbxG*dv%SWtxgv381?hi +pZ`e}bLMRfZp&XCX&uT-Vhct^sEojh68a~LiYXxlY}HGp`ZhaL|DM~LReT^iLf~^Jba9jS}b_+&OsDNIxIS +sS9v+^g9r8k#xBsXh1ekM1+S@N+X_xvH+X4m)eZAu{gt+H&>3gwc+T%P3=wc6Ov~Yw4 +pSX@JGgJ_uXbyA^f;~tHut2-RrUq3E^3t7~IlrW2=sZJH|3>I!o72vHq92 +5AF~ReKl%Ljql{v(znH`^faL;YPv&Xqb-l**r!J)-&>n?q*B6ND?#mu=K|1*$wO)+ +cVb*zX53*f2web$@$gxd!ZMU9%;?tDJh|OM$k0^ZViJZj8nVq)j;G{Jx#iv1)GM=iy_KDmvAGN$GXHD +ncntUBZyU>=0-5iNb#qOVbUCY9ECzeO)g7TE00nY@Ljz!pB)b;vR`hyq?FN~sqQm10kA +%LVBqp0l;V$cA8iJ+XMOk4R$0B1{stAoBRyHt+OB#*@op-IY3~YKyIRlgFx8WB6MZkce`whjF*93lx4 +Ct)T#n^o2sO+9QjMTyOQ`VyQIw&&9G{L}&33r8T-jHUm`fD7TMHNjEAUG3bN>#7DS7b#R{(ZBMV${vt +ExBW+Ohy>&=*67WDD}SnL8lW7jHXc^s>HD|EjF@~cXo-~&qw^46#dcKN|p)DjXvsr1e|0El{`mVy{8m +SPahLMe4}W1}_C&<3_31kUTNjiC!z@B+2>A|8Yyi>R`uA$CltSRJksRGHMc1FEFLJ^#DQDSSB9-A2oz +VWLcRUvt}Cy-NKc_V@axF1CBv*ux~7vu}5>(<|A=n>T$c?1pV$z3%DUeJlTX%Qhpws%SiA5plE|>LiF6)3+oKdkY1Hv-foKR(`xA8IhHYKASKSzJZ7gX9QphX0r>p?30*BdJb&@@^yz&bOA>E$4}w2&C +H5Ym;-2Fw{Yh&`&(7D^c;_{9%fK3eXA3Md&y}1M{3=CU1Pk1^@Hi_;D2i6I8FMtr(qt1{p^cwnYi0t9 +vgryflHl!0f_0W(7d;JBw?<&S%nQB515u&3a3Ez&mB9A5;)pz&qJt^QTU+b(TD`ReCZYoFZjIUHE#{# +&hWWj}7pG&?X1sap9=b!3oRe9h(+8TDPrz^RG{e3=&#H``_1FvpEU!36W0VH)8Dyuf=;{HwuU^qF#kk +D8Di1SR1QV=?2=U|J{f>VFGrTCt)?j}7QRU+y)*sE;C^Ao$s@3UsljJ2mBsu%V~G#L}@6sAej9 +U_yMsB0Ms#xd`X+^qXc8_f?)P5PhwwY$|lKSVRrj@NKX$z&nr4+$vu<_ZnwM`e)PY5%#*7a%BK&Cln{McN}jhyL3PEi9e&e|( +XNQ^X`brE$`Fg=n=Fg6%-hrAlL&Y<4&mFJRlH+X4etcG))&S3u0qz_ZP6V(`cPB1z%9*^OL0gJeDwL^jSqtmsEN6(W>mjNq16Md~wIk*9UpwWm6r29uH`0xdTjE +vchDw_5?G9se`Kro%DwhO-9A&q)d4cg+Yj)@0G^o36m~74A#UUnC4pK3cZ7`~*eXnD#(J%S#UelIlJph+{9cFDU{|5c +-aP8B2?caS0N6u=K_F1pHmN?dB}}T>0yUwwjn|x9UmH{ValO?D;p6LYdtI`55Og|zsDc@D(gs_?|EwO ++k4-kU<+ZR)q_7${9*}f6K!Q^^QkEN*Kw(Wp4x^OZ&^;h0;q=N9li#Oj3_Aa)J{7J`3&rmO`cWC?8=5T#tG2IxJ)WZ~qv9LK&OeG?iO7}J%Q7*xo*4^%?vC#n9dVbYl(iTRl?mAITqX +BE-PdpGzM5i!@h+9FDQPn08k!5Bv#9dvLJfa}_I6^S1wK_pyoeKUc*#@&9i*Xw$dr1gmMk%&U2R!ANU&pkG{ctUIiIm< +%LjG0Y25O=&1_}XY*uT#>(X?WrEVk9jUqO|;xn)vRRh* +GN4Eve)5zp$S?Y+y+p#Pu{w%wez|zbndEpM)TjKf>>GO^bPHP98pMeZkNl7~HUEPsX!Q-uDhMAuJq1F450#Gh;4s&J@ML&+~X?!f9SjsQK(&K(YL8g*Nv$y>N()1`TT+jqMcP~%Fx +6HoaM+1l+R&m8wj(rZIj7T=VDkSlK7A(6VDqZ9T(!Ft`*ygAIAw{L!{z`zbDo}LCz(+$MD4~rm6gR5) +}X6u3y4$tn6ZqAAc;6H18Vdy?q671DHRI(;QYXHL;s~4CSP~7-nf^s^ScA2Mjr<=vu$D#ZWK2)pp2Oqi{&+8pa48 +1G>U6|&lJ#=A0P9+63`3#%qC?Mi(af)O5FB=n87ok4Av7piY+Ooj6KFIO&7LME}IJbCkLkGEIb48x5d +)q34A;{3H6tYEC!ZJEW@S6;J`F(w1*H}6o-7ak#d7~|23rhqbt`HQHXzaEQ1jKe#`v +6raXQ;jQC0t*pBVm_U1JjaJevLd7-;e%pz_Y3*o56&dZO2~+H2Mg%Y#gxr}KoSIoEqGTOzq${JyZ>kG +?bz8z`;bGWI!)%@lhj$^&fw#c#reU4BZb5An@hMagpQTXWt|#8fs`x-e>_RnS7kc+{N8eoJvk%r`p^$ +y_c`vX}Z%*kn6ZPS!Z@L*j?o^HR7qI@n)gtdoNo)&M{WGlZEhQ6RzbvHv +VHiI-Ba>XaBo4TYPxo$f$V(;NrTey9+G)F-z`d<`1DB&1|Fl1&cdg2tZ)Nwol~@lPxq{1J+#ByzIw#n +IXnj7FF<#`$?{3B(O~$KJ3o!yEr%|$1u5eNDO!1TH?``ID@L8K7=0!>h_06z8{2luf_bcDQC&PUT94E +vxI5M21d~7YOJ~wo_b9N1wQF6m~3ib5f`p(fi?E0Wz2!d*Zf+W2s@{h~dE&T$!pNs-B+RRjNo`@>%FYz4b|c?D^C +NMfo^p8<`rQM#zCeXH6X1Mg*VVYZyg*aD(0Z^L0N>D7!nBVTAR;+@6=zJ+bzZoE@v_FY|=w-z+x>2CnN~?1Ei)wVw+5`OB|YW=a6h?Spu(%VlQ&{j-^Lnns&b-TYxB+N`22H}b~~qHZQt2RmDyakv#K +pw26K7f8-q=fRIb2wxo4*`HD5vh_PxtYRQsO{?G(*5KHVM3x8~i`ZOYoFZ5IaR+H%~zNm;WKd)hG@y( +jd@>7Shc?|gi!6U)wc{;Mn}@jOLG%e1E@7mw$%l`!={Ea+LD{Yt1jSPsv${NP5`caDq84sjJ_g7_-gI +L~vg=75hn2>(1;Iayp17R}%Q|9^=8Kk5MI>RYVxSN#7w{9gn}!2V(y^mx(XBp1@QkMsoeJn{U3l$d;- +WmWfv#}n1#e>oyAUtQuHG^J&Cf}_OG65GpU?Medo)g}A0>;SM@Cp_sp{D*y}9Mxif4x@jsit^;|uPJ> +)e9RV!V3~wdNu$3F!laxho8=Ygef0Zl{NB=8S&25U)_Xx(^P9`65G-D;dknu@E2r@yEXg!WvK%Y3-_P +;4XXQo4j6m|io6IL9sVI$^%rHYl1>saaYXdS(;w5F4h;8`(KC~VA8I@*)5=0t7of&zCU)!r7j%erd_# +>-`NP5E__*7pkvy@T^8!yQP5dQd7JoW@t(D_zC;7^~b=}ng05Pcl%nfFklcs5(nB@xfBkb@wU7wqBB; +-L?iLN1*}xD;Tjhi}5xRnEYaR4T&WNEq-0yW#<0mSh>Hg;)61qsl6shP(>>`qZ<^WmV;wXO($X<+W#( +ysGlSqsly76$Oo2ubx%FrleGRi+go@)hu6L(ZxF7@VeF +S_R868MkNkb@g4i#UTF!5_Y{i6M0~#V2xrx+B+T{ob;6%^SYX96Q#)}&fBN=;N~0Xnr*V7YG^nu3FIS +Y4PDQoqcWY%PEoPVhMr)#c@QyubtB#vr&h}0G{=s9@OyZP;`8*G!m@*ou%AULyPuc)LxiC=yuqWOCz# +1hsF8Gcc*YBz*0-x#to+*KFLq(N1EGnR0{zxl+roD8gR=Ew|0HZ7vef8?q3nPd-I!O;z^>LPi1V+kt| +Lr;efmAw>)uSns4!$>YuDV@t>ut-FWop^?j*RCj*0#tS!o3yVHW||EDLeszHJZW7HI>Td$!yexNu5zW +&UO3*+L(sa$u-#&!~Ol>|4><&$(2;lo`q8;zsJ8qD&f9qY0E_-Z7*>;=8R%IWs2rb0;iZI*}+HKK^;p +u|ILxJ166&lLBEx?oacH$P|uBAz!AkU9WG@(m_r42mWJN#cLYy`z^gYugOg5|Mr`w`kJ{(($7O$lk1M +S4OkE|)R8YE=KZ>&fm3OZeiI>+j)BYG9o>FN&zFd{0uwG?m9rNu&aWil3Kwa;2HEQoOt?AL>dOdEg@u +;h;7916Fa_it7?RH3IT0!p00fcArNa8R$l +bjhp)XA#Pg%N3 +hfwW(BVjC`aNyuA6MaOj1bvX6E7Lw7a!P!ff0c +O6(RjLN#BcE!2Zida;x=%j>;3aHxC3a6^)|N9HVwVIYIc{r^wtn`H|+HDW_@*CY?mzW_RWvmnHar|dQmoA@tnlgw +ssY5+K}gWA8gvGK+07|HA#`U9(eH|_QbAOldSygjil7d&o(JEaP6l-BVU$KK*R*ci(Z&f>#{^BwY6N;Z>_6_- +=GJ?S+0iVI2(A*b1lRsSLfSP4F`lD8kQ#>@-@cwPaFO-XN;z6xEsx%zrQ}Nvb6<0SvlQOjW9{h>vTku +D*)MWw44+Hg1wxz@=19DvbvXp$zZzDoE{@qq8OIC~YHH-5BC9B`A*5v^849j{+h<$raiIL(CUxm@Stz=P=E+tKno_hbCDckkH9O$$g+GPvPKtV#v<>5PsqJoUA7OntJA7CmAg7`V*)q)z_q$UmU+7y_Ja|2?`Xg_D$1o +RR33rbm1Cz-{2-6e)m`A2qNaiX_Id!KAV+z56#VwP&YTC(7qMTR((lC?JUNF;rfer;U1rYXhYB#Go0= +2oq1szcJ4`TsU+uRqKTZjYj=?)S7!2?YAQt!KC=gO7eZ|T!Y4`sDP)h>@6aWAK2mnY{22($dQ!daD00 +1aI001HY003}la4%nWWo~3|axZ9fZEQ7cX<{#PWpZg@Y-xIBaxQRr%^PcT+cxrhX7V2pdJ<8sEL%>Kr +n2RmT%ukxlQfywx#q_9&=3hpsQD^E%2safzuztZzClTLJ99^mWQkntE_N6D0yIyefH=-POS6PI4&gx@ +B`KjZ5&2ol9QA!bJig+BN8x~EA$KE>IX+KWLVfZ0(HwEaJRUkL7J5-K6#Onnl(M_jN!KxZ{OIu`eJ_z +;Mrb8oo}`gwJ +3)a7)C1=M7+`E>IGkh!WSMDJ@aKJ5M=c0IsI`OFp`oZNk_s^=|<2fH+yo3-Q39p`ds8lDG2_Ot@4D$N +3|RQdY2f7Bd&w6l6jyYxsM_L%YFypL|cOORG0IIlZ=w2w-f&U~WOoeP&x17L4em1IVtdehbfgwxI6%e +wZFVBHJ(P^6u~uUL$xN!RtA^et_2tc)f(zkMMeRnO=t0&gf?FaxW8jXYiU|-i?632ndXTzz7J8fWYX* +&Im`q#|Zcsy@JEUHIdie_9UjeuHKEKS44i67^MTfv6`F^$M@IZQ{|lr(S_O2;8r(%K1V3`X_u|G)z-oXi +zJz*nDzwsrXhCU*EUTvnewcX3uZtOC0-3oa)Ofp;Te>Gq8)PKN4G4o`we`1BnI~j5`5;T$0SDG}4DV_y#eCQ*BxGaKTdBjr%RRn|K8q% +$Wc;VVHacfEgYGT3pu*btGho{R=3$&cW{Wx7#A5i*!ZZ)`$-~9u*C=E +I^4)g@eBwlLDstgkmUWgt5hPtQBGHjFx?v#^fQ<$u15dS(4+Z2#e9oCyNfD7FL^Y<#LX93v*cys7Dmk +tVKs*}0z=+V9sKezGQn2|q6J%qiWAO270-qr37YH9yA^!uOQh(!kJ$8L~GQLLPzwr5NSRxw!a{l3cA* +bXT#?q9)3K7JG>7~poI8cTl$|;rnc!UZ@w=VdS? +Hs%!`Fw~}j-!<`Q3&nc2_iW?nML0E^dvt0g+Dnth)+)r)QtoAwAU}Oltx&^Hy}~KQ%8xM`c6t0Vj?4e +9-7lxGU_2(`EN7PTs&Z}d4lxy>(?Y~J#T>MQzXj__8s~Pla=kWD6`PIfJnkS5vbuHW +NMWaCVS{K6v+yTHT+-e>6%cZ9mtF)>nY85v6h1T~7&C3sh)d-rlHdzHAOB4rzoE7ym8S^b)mNXU)pP` ++(Q2UbEnt*?xkT_OHqE~_;J*%hg!lH3zOr%cju?kK(OQ1I*)?rHT>b$hITZ?NDa3P`k2KZIg>J(F}!y +yZ4Lb(rGT7eU=lu8WQ8c7OLtyJ$<*rtbc6Ad7U@fzO&|sA+ +$8QEpum-INYPFS)gE#2fgv6wBM6*%RCPZzmBAS{m@kUdFCR81o!r=z}m@qWeBxj_Rql!iZ!=UW|yC6o +#7R;YxRKB@|h$X6-4mh}XI8-`{hTF(d;iaQ(i@`DZZOI_^Bv=n&QU#Gtfys!3phSZXtN8GGtNezHj@Q +>{hu7D`7SWbXl_X)Dd|L5l+FQwh=|y3$k;o_PkBld!(~AL%a%*IkM7J38W^74U9A;x*U*|6G`WhVV?U +IE?Y#us6nXW*2<jVc$ZL6uw(K?FtXq)NPd(s%JLnv@s^|CM)mp{X +3E_qx#hl(UK8Y)&3rW;M4N>^g9uaLZ4}Zde_3czC^sSfs1C+@NF#SCAjUkeL`;(gE}-qTr`}$RCL2^x +FrO2c4^Z92uGk_H>F!Kgs5yX|S!(iyDzrt03{%+%Y^+g6NN~t2pcVmU!zc-;4>`Z;qUN`C&?s(;3c*J_nDJFGLQaVd)OG-sN(d9=0KdAX;Ji6VOn4aeobN4*W0LvS?_d#4#lHId +X=*V34FhgNOB1iMD{7=1W4}Y1qh}%i=V*h3jj;0=)MhsOb%nRA#wJ%3%5Ghoq8G09tf5b)vs#LiziKB +OXW>#B|LuZt_aVi7Nj*E+}{jZ(`-2tkWj&jDhHYyX{z7LwD1}*?~449cIue=CBWxQAe6O2F*kXRG|%*Fk@2Zl|e*;Jz%;;$&FVQvb$Kib_X+8C^A47-@rAo)Yi!?* +{|HgM0cnwl6os2w+|ZFa4X`l(Y*shgOzx`j#ca1wu&6OPZp3`fHN$ykQ=tXg?WO&7a6W((t9d;*y;n2 +)W)6O7R`R0tzMbQ^-zRrQVpi$tjr~}p}CHlK~z;)v=F8UrRxIxIM9@3A6DI1w18^NOZ2Kw*zEh`6O5s +5+AJ2aDp9>(jaGH1suc+=B$j~i%IyjTGblI38!}X!0jBxT$rbyPe9hHzH!$Tb^hD+a$GqRbH>F-m~IfA!&rv6VqJuyE4lXA`9fg) +@23A9+HeIG6cg7JSYOy6bSV^3~#`FxM0dVV`5SgIwo9ID56=S?CG$tviLltK`bu{mm-tF91TMyd`J=y +nZIsZa#w8Gq=4#}s%##rgPP=jE0S;pM}%tgLR7;VG@t!P9a7KfqDgR1dftHE@gNHbo6ot+K^~AW$;=Q +`h+G%iXv?Hka%>VtrD*so1W@ph=S7=nm+Jp3NgL9eO-$sFGUfR*eAFYS+Csu=wSZ}`fzBCQ{0Mkq;Wz +854goV#KoerUja&(0*dg@PBW$amJbC-^&H2ARP03S%xr0WIWwv$IE_M_N1ruj8pS$lo>#Ae(GAHx>g! +IU#FMogk?hUbkOs}L<;%K8_eo0HL!EFb-(XzIeIbTlm%W*o4P;m?st8Hdv$`s8cN +w+40ZK>EvL$JsEB3OIb;r9N+}synTKCy30OVqO)tv7)EWdlQuFH${RSh>EjHmPl$)8jVbmt@Uuw`=*> +#Unftb_TxsjDPLy%txQ_E2h`;EO=I2kP%CIA>P}bVD9r`YF%=XclLI7KgW}C9zCHIS*NhV=R^Z6~41z +>!#9aV=pQV#mF>bPUjs+p!f;Mc+79y$N`_T!kmhNKXYsboTAu?(e{Zkos4fLsa=%pj{AL^8Ic^=Uo#N +YCh1HjTOIST6dIj~uXRj~L{7*}ZPrk{Tl~E4hC$rdRcE6AIwjri$!DU$`rY^;8l7uzc6k76B +%doXSA^ENpN~1hzxh+7Js5+NRe#vFCfxg@=yAL_TDQZ}9G;x>6JS5yGYAi}0&XeAM`x9KLL*V!4Yh!r +FOSP%~iM(p}(w;(qO?fC`RB<>ayB$9+FRQ1FqBkHtR8LRtWcHL)dYeoQUBGIndntHC52AzIyxGM*4f} +hW8E;TKk<|Qj8?jPgJ1JPFQx$W;JDW%5Q!=#fw$Rk0Ap=Brv668NGP&G;tacdQV!bmHtX^_WAfY)wkG}`?%^e}&ZaQfVt2X9YyJ5J31ud?0lLg3)3 +IIWZdo5cAh3M770`$f&VmNFaViH~%dx4*x+%rnYC^H4Sk5X*h$`(%Irso38?t#pQpdT+BW;eN)$ZRv* +LkJlvZ_ry9jcXAL&$A;p_$UTIkcqC6%Qj{<^T7-CTd2^{JQMC?BqK~PtQ~9-FSG!7?K#t%71{*tIojy +S$u%7AT{pOOWv*7`w4JtHJGYQXZ3%FgQj|tynucmz}U!T;LUDw88c^;~RDz$AQyo&Snx^rqcW#2lJUy +Q4tw^zu!j@`>awS(`KGw(>S3fMgGj#ujtd+ngR-1#g|ZEqN6IQ!l_L0KKU{Sdr7jBYypo}(z5+4Y0vJ +^4F~CUAwj)?xE4N9Nlo8mG^zbE)oDcAvFx&#iOUzp`a#-lLr5Kb4U`YOF60^F~{h$L_t)-O%>?edG41 +T9|6x;kukyU%!4O)6btjekLELXJ@bfF(toFr*Efkt@=(Wg5h~M?lFobBA9zXZ6a)SuW>dGZV_rSdBt`9z&Es4cxhkNC5f$B+ICP)h>@6aWAK2mnY{22* +Ygg4SLD0043T001Tc003}la4%nWWo~3|axZ9fZEQ7cX<{#5baH8BFJE72ZfSI1UoLQYHH<+DfG`Ze?t +}fI>@5DkUw9KKQ#&YBE8QmedmZ92fh3vkfS78jMu@=ssa7B^S)~@jk2~1-;>>zpBNQ%VQL=a5r +FQAh`_D&XY929)yw?Gy&|H|kdjL>N0|XQR000O8NLB_@4Xmkbt^oi59RvUXBLDyZaA|NaUv_0~WN&gW +Xmo9CHEd~OFJE+WX=N{8VqtS-E^v8;Q%#H8Fc7^5@*jrm#i8E7_EHGs&_fqWY3X5a%OYfZlnkp#MkBW +|rT@Je%Wj=)LdPddZ{Ej9GQl4~DbvL+pi)4`)`tkXVsE>MD*uW_SvO#kjs6t* +ln7j}^zD>j;%*gbX!4KumB>`$8bl1`WJo&jq73CA0wzop#^6!--7l4%*(5i`#}+R~C0V?7Jhn$oCByg +W(@k$Y2NDwHOL9SAtNlH-pN0(v>1J>H#r`ujupm_mY5r08mQ<1QY-O00;m`Rt8fBInW^`0ssIq2LJ#d0001RX>c!Jc4cm4Z*nhabZu-kY-wUIUv +zS5WiN1fE^v9hRZWZAFc7^5@*jqrY-&t@n)zZ1xeY>{`=1OBavN)w53$1So7xT& +C?TUy*q$X?J1r-Dg|^nIv+vrf-|Rxs{byap;rpzYyl@rwQEoris-co>s2cpM>>}36>Z~u8R$F`MLb6p +-;cOn4Zs}*tm9bkzm&l?UJ#|)>3}0*>j^;zc>MfDehO;6s*MhzD`mre{;*yFuLfJ5hb%>ufiTIYd>uw8Tc1K&fIryR1pX7R9P{8#Ut&o+<7fI#7J3OpJ@h@R|A*yN +&lY!gwX5?-h}`gy6Ns37q4*kq}IIkMLE9{CVPZ#Ij?qaz!IE&Eu2LEF*g7d^dOwX;#P5f{h!xO7;LoD +M6y;>cD1l!e&`f`wi=`r0@(-+T1f(z*KXEY9-MSAYUY5jY&fP +9@IEwtC;Gszj6fDeDm +H<@ruHG7ax<{tQ}BqN@6aWAK2mn +Y{22%h40006200000001Ze003}la4%nWWo~3|axZ9fZEQ7cX<{#CX>4?5a&s?VUukY>bYEXCaCrj&P) +h>@6aWAK2mnY{22)=uo>t2N006WD001@s003}la4%nWWo~3|axZ9fZEQ7cX<{#CX>4?5a&s?XY;b5{V +r6t`V_|GzbaZlQVs&(7b1rasbyC5K+b|Hl2mB9%FLA(__FCA(Lfc~tZJ~z{l(9Wd#LAM9##!92?~J`p +v(4@xgEW3`=IM>P7a|x}z*kpq*Vf3~MEg5H-BD$1qvQ4K|s+C-0qHDG +Z&QZfcP3!;fv871uXWOvCvQ0}J7-Wv-iKap~yHD8$B=S7}K_blq${L0khG>xq1PEP-b6W7W7O`_8NeA +msT2>1@u0Io;@j`&$v8Oa(L%?TbXrw|-K$g!G;duNOJROgqr6CE|pDOrGOEJN2g%SDzqtfMj4#IA5O! +Il#O1`ySC-?IVJPnjekp#~qG=HJUOQmZc4tfRZ2cBNqL1$y)YQ2Csux#Su!)(v~!$Fpc+-`Rztf`NO# +m~lJO7PV>nNXqAsFwYWtB-CtV@+)Th%@-lB(;zn^&&rAThH?`I&Wi&M6O#4X()rk6M3*6Zkko^GBj+r +koLKnlpmB_=XD&Q)K0E^;J}JFCGu{86th_R7;07ha>{UR`^xPAh1>xBp-@R4pl+uQGXEamNsX#}0|ax>A4;?J +pynNiH&9Ch1QY-O00;m`Rt8f-4y!#O0{{TZ3jhE!0001RX>c!Jc4cm4Z*nhabZu-kY-wUIW@&76WpZ; +bX>Mv|V{~6_WprU*V`yP=b7gccaCxOxO;g)25WQz6|3Tvp6MH74J%`c@g$@^bC^shK2zwo?*pjhiXol +gxS1ZYmojAsTPX5@B_x7z;N=lKFFia{{l@Nx6<%K8}q4!c`m4eW|&q>Ulm}G*_NyS+tVhA&)pro0cPN +tItqbzFYdny6HPA5@Dr6gA@)22RtUfAbgIh_!_JkPu4e<4zmtk>UjP?SU|EhVV)%bSbeKd*0o{aCMwq +8q}v5+ns06OoWuL{$!4X@46ihD5VqoT)JMC1lB*NKus$%n5J}m+?)V)8)M=fhv%MI(P`T{=KB-#`!Oa +O2Iu7B_4niskCc;U|;949`&2{A(~Fxx&e)=Rg^_9fnaBgpBGKw^Cm|f+!aBGaxvPJrP6e@lUHb+v5qY +eSokmz6_2%n-%Z0Lxe>e#qX6yz+^yQY5>dJeF}}~M9}_OMEfAJ*yz}tQTXij1;*2!-KZ=&>mTtVgD7B +r9!fCFfEHd$|+b%GUhH;Nmr!GnEn=sfuPTrTctSD# +KK%B810^J%y3rO7D%(RrX5nQRsI>#e4)R3yNC>AA>o0qFDtfqN5U1jeE7wBIeTH*Kdz~6Ac@O3uT&Tz +y%iqX1qEA5|<+dC`jq6^|iiZ)z3?UTp)QC7ER3|V(bSYbH +Pf^>qcn`P5ap<7?Wmx2)$cWoV(dWc-KfK_encgSaemInDrr@9YnyNfIYtD=lIU^^Z6!Xx*?*8G)VrXN +#?;;-E=!{D!t))HH00)XW+xlI$46>NK*x|Tr4MANK18C_`%m>-paxS~YeI1vwnR%_0W}J;n`O-*emGX +jRSEkpoIC$Jg{D}9+y}Fw}gJa*O{?Nf=+QXfjE2z`Pxl6{Pxr@j$})JPvhS~e17t&oqA<&S24t4nNj +u9+@LO^c*TZC)e9eJidXYsQr~ty=diJiw1iUcvJgi9rMTLn?~DYJkuH!2!E;dpq4{%7T1f=}2ahv5T; +^-RrgJpy@mu)r4X_y59({eM(YlpLYGF3X~Dn8AnWaoA%5_x^j$=(8-Z-lFfat1c=CAykqVc+w>74tw6 +*Yup7&@z2%Q=3G6B_?9Q!+obKC~NlU)p8cJyJedrj9`;_dE3j<%2L>ECw$2wPu)TA2IE +fnwIGOu&+S>YrTh2DFyTx{dngSBI6mIo)Io?NvD>#sV%JfwN{@9X*oSb;4 +4%lcPoGh%4q;yF=~|kQ+N+X~7xXg;x=G=dixJPd5<0+9o99{S{nm?5NL7`aU@ZANroU0DjxtWq=kH&|tQ+AgnYj4=ZybNqIR0e=_(Zc5Y0SRS4XA619xyl7U_v0MB3b!=1s4I;<-#zyfZxo +z2z!~YV_k#HIJ>ls>kJg`eu)>V2#zOoeQ$kO-pBLwQsqv>SfaWv&?a8h6HZA1hdixN{EOSpg6_?|>5q +q_Pw(3)HgU%bn9j|%jSrMPo@tIXq?t|>2Sc!DW5*-)QN@0GnP@3zwTL6#;Uovul8W*n6Y+JzHg7~|c8 +1=Zvj~qjVJqxz%wvd{0z8MZjE3HblSgf{otL0+B@^YiJWqfH=v9)sH?o%f6``qNJO4+u`GbQ +Ar$gR}8FxOYtS1aT)Zw4>9k@)obDl51#>|^7NVUoZ3m`qro$n=XnV>y32G8>7%A6~OwJyyITs6b=VH+@D4MDnSF4h;O8&5|>6|BS +gW2tFBOB4b&hT#btz%wC^p4FwS+4D8em9HQ7U*tjHiBC|`^J>+QZ+v`2FaD1MFXmZLG}%kikBcu>Zyh +L+=dDqnla);S@;@!s6@Z~QK4idLXk+2)8#PzkgVxjPj}6Nf~6RDC`HFC9_QMn98T+GR_yTm0rL1gX7|DbqS;=>7DVZvBYo#E5kP%k7Ht>F(S9c+^yfOeqqVkzGA;Ov2@J +zC7%{9*e?kF>7nJSTLe#lKr$cU9w83%(3H?#MSbd~~Ok=6T#lbPMN!yPVa)d3pGT5=Ilo#FG#Ywj^c& +3V<_&BIY&zI=JZ9wdV-u4po1(27yd8*-H8#_r;wOjg>*GMMQD(&BXt)wPX3>#dv?Sv3QPLl;9d0wtae +IAp2BF6Iz?esPEROvgbn4Y|o5ncUdOv_td29GT8HHe?1f8M`awo)$wH8C0;W!x~;nk#j4H9cF?IZeW^ +*e@8HXx&xd%td)T&kTuwE%?;QO*4x^G^8xq_1zA4^J_3jbQ8cu|w4opr`KM6oJL4`MS}VwVbPj`H0m= +&QH0cxL7dqqsO(0f|PgbfZ)C1sYx!Zdyy0$&T*S^o@<6@ynT#4hIq%0ve$?d2WBpRQcV9Hc+r`2{73# +jJZ#e*tj><|b~n+GYuy5?Cw>+o~I*oA!U5_q`=A9v;=YC8w9MIAw?Ekq?qaL +O2L30{7=f!$8+f(S0sj8UVJf3atJRV1KG8V@$ywYOqU1791ARO=R|TfH(wLfEg_U8uHHz9;P3v8MmrY +q1Y;@FI!HmHCFSV?bx-=L`u^q1dm9EqVs&yx92`E4Zw+vB5^M}ExgEH1`+>ABj?=m(3#}B}AT-#CN1{ +(WBHsr~@YC-P$QDJ8769A3g)}7xM{L20e6%52(nuERH9?)Yy}@7u$5G2@I%1{LxP(-ermEbM1mRrSo@ +#}h+9*3(pJu>733^PcaL!JheW>eH<-DYRRI8SBf|vZo%@toL9VwPETbLl#avlSQQ!F`HW9&n@PT9 +TnH_VfwEECJS$**=2&QUe7Y=D_Whr&vOt0D(>t9D?C0}B-QmBc=02Q?6v|jx2qU||z!F9R3Xaz)H2{!m4F@Q+?XamhOr&6<4Ye6zry?^;UllB0q6*-pExbO=Y9W`4;B!)^s*Bdz3kc +cT4|Iv1NYXB!zF*RMiO^sBiLS|0Cou!3x{>$vM`c#yTDm50p2QLzPu1FUI=`UvZ*+c1=T~%oeG*fL7a +B-s#0>reOsF0zPU1#&6QGK^91d4hNg=ZEVq2Wn_L#UDVMcIObhO*;UY%0)0Qq?}k2haW#_w$K!({UoK +l%OM)K71JrQ05!VWe*sWS0|XQR000O8NLB_@4Ub>JGZ+8>rECBIDF6TfaA|NaU +v_0~WN&gWXmo9CHEd~OFJ@_MbY*gLFLPmTX>@6NWpXZXd9^+Lm)o|HzvsRD53C$llD*P8Y1(^dd+p~q +Zkt!9_Vc<)d%GJMA|VM`ilhNhyYlV*?>7T}fy6+N(>uL%O%4XYU@)Hmh>k|1?|C74%rsA;mCU!%MsL$ +UreYa=J^yny*CL>B1{j~LKmkf2|vYRH8KS5Zv?G+kz@6q-SQNPo1?OBrRHC(7_;G#Y*Rg<;uZu +_|>b`C<`?ZIMe2qf>cWYQCu6PohNZgo2(Lh|WT!~w5hO=0(GzDgyMHiz#ixkE=x8wFv5FMSgchH;#7Rh6`Fs +*75XDjSu&wc1q%mSy5*3|TbnUeQa+D0f6Ts$AJX +2gxYMx9w249SEMB}TIXw1gblY`~Ur3|6OGSBwg5LS{%u2cw1@*qW0i#X*WEG3d4PML^2fU^vhi**Pi& +ExBjWv)Zy1R1?7wa&8uQp~a)3$Yor8$~FD1){`5M8zUFFtkuXlr)U7mU&qO(Y9OA$pBWuG+Rq?3|cCb +4xt6x;i1tbTr;r5A*@ws$`ZlSJS35c2u&(Dm=H#AL?N#6kj3EOar8`qjf_L$vl2W_z}5xP*2hUcAc6- +DA?0~kJyv-Zqzb~A0Lq55B-qvF>kz^>A%s7K5WWo|{4s>^rw~GDm?BiZ4avXAiZV1QA`2078NafZ)iq6xYGdG(^ZDtE9?Mt +f_+HXHtt0&N5H-A)FA8HE7-uvAP!9AUWtpLpYlt0yxMJPH47uh>Vc!D?`$|V<8%LkhOSHk}cDl)>0o( +x*nd7b2bu}GWXDBqpc*1!&n|CWTP1KuVN_*?J+<$_7S5zf?@g2r|dRX0ZA(N$Wae(!!n(F{AmvnjP$V +sm0msQW8H+24D1NBkMf8fmEQYoN)OFvz6MBMBi6?|0_lLU>!BSnp=i#Iz&b*Nz&#x!X$I{mq0I8kGf1 +^tA6mj`i!%Yuh1#f)UgoLK6bvwb%>5QoAnZmNghW1# +(oNBTVjQE3d9OXfrH57FWptiJpL>El&4EfSH?XE(3Fzii|V)7s(RAeVMF{dl+NOh}K*w)JGV#@ztpm>1dup2Zv7;-HI_y5@VLq;D`Aa4Oa +mlxVutd7N*Hl-tJKDqQ7psoauV;R2T|zQ%Oc&ZXL-lx4Bo!*U(>okI(UWVGN?3r_Tt%bq*zfj#YYWeH +e|L*>A_jmxKHt|2zVP)^u6S6v!|VLw!c;zpKj2oOjg&(#a>u +sWqYZ)iSm_p&L$W@&=M&+NeQE!d(H}Kr=2-a#y_&2zJh;d^5`v*bAVjFT7pymMN*0P-;LaIk@rqngnp +u1#T9G`>nae-$_gf+|!gw|xtdTcGp!a!}x&~4bLN0m1nVwMs&=e69SfA4&;2%pN33Ds?&V`{F@+p`EK +_!8dE0kqQghMLM30bBi@v{-g8`Vms#LphR3Ur3J9jEHe$+5blwX4qs<3T58#y(x7YO(36h`3GTIL5Fk(PU{BP8kW +{w(S@WDS#c`i!j&Mp8!Wk@Lnb;>^Tb!kNWBs9bw=rJO3NTFO&&-ev6xOJu*B#A5lS2`yCqE?cmWcg>k +Z-Hfbq0U{iXt11S@CYbsnUjpqX548l-$j_{Yk8QELf`7fdou@Lp-jb-Z!K*<(EV(4O^xppqu!O~gS5h!du|?`?D*|UZS_SR4Yzi?{Bu)Hm#kPV7noSvL`l^yj*L{UPhcffoUsL +FHH#xCd;LgIz9mV=<7xaw_!sY!LnS`zD1@wPpLK7s$!_KnUzNchXpktTRsj+YnG +h>BgjsP{G_U#oEhcR4AfV*DpnVM#y%$@efLsTt_FZO_)Ye5?Viw(=>zZT9B@}#qh^l5FX@;&s?=24K4y-(~BVmElil5 +ak=9!Q~~kBJN$;g4M$uEU;UE;{)+%&a{iKBDyqu9Ck$zmpTD{VPk`UF5Mfn~@@HtGlvLcsX%UsVrh)- +UEG!WDY)aT?DfVkQhhYPT;bufYicA2o+V6en3ROuvk*MWQ)8 +f9$3)g_75|zOT2q&A@zbSLz&}2W1B}9AI)+tyoR!yL4NFFB(RddhZuN|1@rIZ*?dpqmk2W+VT-Cky2B7ARlr2bz8pm# +k03A=Wjiy;k(R)#k%yKoUzbkEOXz^_UGkG$Fy@)PUYWf_at8A5)tMkhcWum28BTCEieD8*eXR-aj@`-mtlHLOJY@YNt1wLCGhqGU +CS?$(~mec0kg<3!xhqxWc7iWbh-;GPDsJ%%~f<^Ioc+%+jkoHh0Ob5`)jp;3?YjF>mwn%dbZWo&X>sY +G)(m&eN6bz5^_2Dncc*ApOuU|@>(+kW8|6xn{R?n39j*@i|ExE(xJ1%8;Y*NHDsp^E!Hz$gQ2Z~~)7T +v)XYNp2Qlh~HfaowDy)^*g^7MLR|v+7_9T?W9E;dw(B?jqwhFkNCcu;`*L7;Orwb0FZ> +YwTXn-QCk|cvQ*B=?Fp;8JD=wlMA{QM9*?W~hkn0=QOtpXcoVUx?kqB$J!M4!#>cpgXQxjQ_AK1kGb&#q(#x)u-3sY +j%xH?^g=1{qUJ6lTn;sN-GC^BtDejsgps}qqkv$Y1hGI8e|q)qyEo^rYH>CDj8S#1&jcAVub!umtF}g +?(RqD;kBT%Hxh%r)g>9pJ75x)W^*^i*t~@mR#xS`67v_jaWI{)4FWFqNgpDSUdMy?Lnq4d=il-}z@ai +jKs$_WC&e;%fs8UDHnF~5pv7eBf(cn-6O|*1I0S{H~`@%CCe5ire1)VvNgGAdV_Kbv*)%z<#XM=ZgzU +k$SGY{O`#308%qkxTc;}4|rB?vTrxx#8iK*eZ^A2YdjB<`^>WgzM8eNQ^CkY+OuanfFIz`Q}J;?WHRA +bnbwyuRrx@pjaSWZRYXg-z2LC|bYeh8&s->=-vKibFm3<+<(s<@tGJ#bVTOhopRNuZ2ua(gmK{cG~8^e6$X75)wYF>+GKlAOn2=PJpL`s_(9aQ{YB&>W~dYv<}&97 +m8MQ!u@T79Q=oMk0 +ol>KX0@7c+e*Na>SKlvQy?*uMtG~ZLKf!>uX|D1pSQW22#p;$S3>tXx{{6cjzWc}fSLgHSd=LdU?V)K +f_jD$v8*|ku_JLm_pydHmQ1Do+1iUWyZPVZs7jNIadHd?!`yXDN+XUJee|z<>pWnRu9!CJ(G`^uet1! +1IN2-C^#ZNC@FMc>*fPuFO1a4(nLm_@aVmv@)*_B10HFhBG{hOC>UY}zFZCe!u^ZLC3NA2aH3!I1kawo_4jPvuDxoQ~ktS3k#cy{!W=O+GnF**^X7nVEfDX8qD(a$>`ZjN +T{zPb7w9}`d42+di3;^IfOiAKFm&q24ZeUMBVvZj|M%8|fif%Rna_SQNim6g9FC=1R ++?r1JU45(kta`Z^;TzuEI@G5M4Q6mMI>&9f_iQ~niAJxoq7fQGn +0&!pQ2Ju!8sNEhv~@CxFg9q6iK{*Bv9oq~7})rGmMc61f$a5@}4{v_9>98~aIFpL!gjo{a*2ixIkf8N +f{qR~rpWwIU(+Abiqw<|iG*B$rlCQ50af`O1)(>21ta%8SxCJZwg8P(0Z!M$=(7G^4pAJ7w1XAVwXO{ +Tr51%LOVcP5lV-CCLtP#v?{{3?&Ms6sCz+g4XIUqnOAaKh962rk-ouUzb|mwAOVX|esov8?fp(3_}}0 +kF2OBP!0u@nN;$(0)$_6x6stHaK2wT{mJF?_%AHegLDIjoY9;23o)lp@oBF2=D7&^V{8}nqU*@dGkfx_MY_A~S0s%lu=2Y9~o4kaWV4s( +mm_dvvYe#Cxt}YojKAUwHR6mdY1rry&sjd_^-?6~BGjxB$9KPzrv}KaNB##U?%!EGJ=8`|aR3Sq?n1* +fy=7~rXK`u^JGvK6JFjnPqGP?M1dHd+m?DF=TN0+z%v4RKo=<-rsJp{9S%yM7zYuc{#>jM8?O{a5|F> +rCz$m)?;x362XRZVn9a90u@TJL>cw?7$Og1=rR=xs&>JzDMw5jG<;;c|23`Mi0U^tvHhbx@tatz{qsUBQ +T;LpV`_rMTb{4%|I`22GE8MYvb#?fb?)5gP%-FjhQpEbi=lMKoZo|9`CW;6*I!kGlgljC1nX3QUa*0> +J+s-YgF=!y&oJIOg**=1jQaLs%mbVxwz_WKpCoJ=}aYC@w9Ca)LX)Qb<6CHF;%PN?b$6hA)~!I-vAKd +3bWi`3E6)BEb#D7x=ULb`CdsWpsJt2pRjCe-FXbbxzt<(h{{rs{;^B-*tRazn#hXugFI*EhLyR^ZJZl +W}xkosI6R5j<4-VeF`Cvm3Me3bsi`wbte4eO0rwugw^3%<;rSB~a5|KH{Vm +RI`WXsF|Vp8Zv+pO3}+S$IIoMgmc((KQMnh`}!(+7>!P!jckkE6FDU3WgD*a@T^FGNTE*!db(DLztJ( +9?t87GV3=Yr{h5WK!RdgWg}+qBy2JYv{*0%_3!wF`d+DzjtXY}ql)!pUI?DFPrd~vBRZ>}DW??(00=kM$v)rU5`flvaxufE;py?kFO +Q#GJ(xU!=ew^=^Wnz30z5#O4HOWS4;OY%s=NtR54vB8EP*)y0_x6@p^ptMhJvZD-+Bb5Ekau{s1RAno +k~bT6JNWbNPmxwXch<77y-ltu^vAL$NjJ#6}&zfJF5##zp6-uo{VSX>BXZfn~Dqj==(`~JzRCZdfgpp +`kddmx&32kS)19Ym*}y7LwrphW&CR9H8sdE&fW{aSm5$7$wT>@+`9mYXS3Cd*`Kc--pTb6lr4F9|M|& +f@&NumH~(BtpWj_3|NZFXoBz3-Kd+xA@%E0xf46rcyW4K>iukV3cg5}9{kxO9OFSro;#(6peT+Jp@6aWAK2mnY{22-!+5=8w00086!00 +1fg003}la4%nWWo~3|axZ9fZEQ7cX<{#CX>4?5a&s?tXlZn1b8ul}WiD`em6T0y+b|4{i{)ax<&ifHBa1{jsDSCLsGJQ^*%*@5SG9_&GWsbIGsz6@q3Wb>1WWaalMY{33LR>>93zX`~Q;0Z>Z=1QY-O00;m`Rt8 +gNQb7!G0RRBS0ssIl0001RX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7VPs)&bY*gLFJE72ZfSI1UoLQYZB +k2112GW37xq7l@z5=3Jb5W?4?>}Wf(OBaNXYJvHZ+@wNw&2A-aNLYLQgxH`CgeUW9;1_g6yOX=sai)a +HIiRGJA9Zz8&>I1|cMv4{m6~;Np=$8VXx<)ufuN53;u!=c(pAk~_R_G_||=9PBY{$)pKDk+aHju}9O9 +1JzrKHrvqaN3EKxAby(i6yh9pr2e4sTQ%x;)WQ;`i^Nzh8tRU#jk;MpI- +6m%rcEJR!#Cwh4>!Q>j`mMo@_knA)qMdJLuG2c}3<92BXqD3a#R{rS?fGtMC)q%R_1kA+?T@OZa55#l +BKAxZ^x0hX0%r6yO#N{mN>BhZb#s=R93IM{cB9>7ylTk7HxB;nLdpSJbR1v3SKlP)h>@6aWAK2mnY{2 +2-D6%T-?j008L*001ih003}la4%nWWo~3|axZ9fZEQ7cX<{#Qa%E*H +Yuhjo{vODGI5Y}2cwzk-D5Inv^h48a4Xh;?quO$#)s~DTcMD_xeLBfb{F3hF;|w8sdG5Kp=T28BSwWf +>jcF81Q{YuCl>z#wW!V^(`h5gBf8m--5kVt(CUcgS+%QE;J)g~I1+qLbp?7?dNyR#vPitDS2ri9L`gP +RZ=JN9jKvp}dXl4tdNd)AsrWxxUg+0s$q0i +V-dcC$$C>_lrU{%uFtv;NT!ZAj0GhqdJg^+1D0aJrxULVjfqMi&FBAk|CiuU7mjKtcTMVFj&a1yTPpq +Ubv}{;7jiUEq51~N|MllC9UD$V8=^z~E=L;FMkFSjq^RaGS3X_V4(^|L=z33I}gko(QlUU@4IMo0b$L +s&3;HR{718kt<_(hDLSC{)rRuvOwoV=$!48uWL|DOCxW^Q(zByR_7G?1&ORNXw5JiE^A(Qmu3!|T1a? +S}Y2q(9NRrdk8Mfe+XBH}Mj_?5M%Ho6Gl|;dCJV!!Y^@6aWAK2mnY{22*hwr(jV6001Hf001Ze +003}la4%nWWo~3|axZ9fZEQ7cX<{#Qa%E*vT~R->j* +M&7FK8#L8U~hGfbp#Xt=b?#d48*luIFF%P%p_26=|21ABDNh&`d%&Gt)C%042FQ?0Dy%GsVc`&C)@tP +P5Tx(0gM8IwmV4d&vK6uc~jI=}<`{zDYnKx2qN>Xge3FE9c{zqaHlf>3jG@tMmCZ4j=O3!owL772s}r +5$Xv!q(=;nE$0&?np;Z=2b2i8Rp=0LRku4xjdnX;K)@b(R56Vu{D$ECJc@Q? +nVL3)M7$+~LYgJq`e*^oi_Ws&92RDFWC(6?~eM03a+H71Q-CK!Np4#Ds`4!t;tyJ#D;Q<&Oqc#tM{qS +WGs>16o$}Wru1Ge>Izw-l&fIStZd-LDn1clR$v!rACLV{LU#vNz7XdE;Lgx`peGgDU%g!%U77-Ya;mP-S=YeWbR3>#8r}`p6AWsaIQn6}O&mu +r08Q;a(^RIQ`k2g&~$*moRSe!lqYkk*wrhgJ@&g_ReDgCP2r(3_g;fwyi20~T1*FM5=_ad)h +@A?Z+O9KQH0000807zB_Q$!N^kY5D=0ALOP04)Fj0B~t=FJE?LZe(wAFKBdaY&C3YVlQ-ZWo2S@X>4R +=a&s?VUukY>bYEXCaCx0p+ioK_41Eufe;~YAtQ|N5^!8#P0n%*HF3?SZ-E?1y!j@-fj~b2SN*X(9(SI +*V&3G`KY@SQ3-4V +DPUpxT4=9yXQuQ(D2vbJ5DT +L6@2&&cOHH(BOoJ%D|GAy(@9PUp +$Yy&Ox5QsIg4GMDkNbR{TyvEw#^~%pOExugIwdC>(U|jIu-J5C5HI-P$eDJY}={8Vi3={(COmdcajkM9bOC?;%?C7!aRXG=UfWFB`cC?M*w +PobB#$|qo!uXl--g*j;sc832b!-6dKiu9o*lGl8ys{jWog87O*yT1%)F5rEC?T38VgCGRn<{hGHzri9 +JNY=Bp@LLv_VI`$5{rS6KFHZly_D+T2|P1k1K`8X1-@R{{^iXu;cA>4ec#(gW6n%7ZHK()1bWklQ+uV2TAS1 +foy*XJ_rWkCL#8Ppu|H2a=m%wvU~?KiwlGY~=t?&M_E}Kn+5BC2n#G=EhGz?yUe}&?r=bP6+Zm0aZPY +g598;=&>Xr6BQR|GiR(j-g(2~1>2r=7c&nKqCqi~RnkQd|W@lIlVH*2qH#+@X!>1c9l|ILg$k +kc#~=uKxsf+FR@cyukgAp6!A7paVkzG#|fLUx>st{NV{;fEcK=Uo~{vk(Ov!UhGjEJYkBh-uME#JWH&6k@`kjq%72NLxX|1Y%<6tP9gV##Z`qB*-xdLE* +rMaUMJtoZ7q7)^&9dZRykq6z+>8J9$cz<<_6)Usr|q1CwVzqYu)Q7Ss8|4Ssb`^!vTU)(448g1cMR!z +KLs9TyURJj|@#%pUA#!-2t>BabkSLTiP&QGb8f3Y5Ox3!vwdH<0$c^f9&>+trN_@+}DlH-Ch-)YbEA5 +cpJ1QY-O00;m`Rt8hAgdm6u4*&p$IsgDE0001RX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7Vs&Y3WMy)5F +JfVHWiD`e-5YCf8@cg&fcyuZg5ac9R^mJK5u>`|l1o|xO%NnTdw|b1=oKaLvRW!jZWP@$|GhIqa+lNi>Uk+P0v=D9;Wwo?ue7MmOTg8=sQ5 +Glbq~fNT`o)&lr7Y^mxEHwG{uK;m&!3jPirjDQqaNss_SN&tcM6wL15dr3U7IeVol5?Cp<@yx&ET +X^&9{d){PxZsS90ogACm^I@>gp(lN)dBNH{;eq%tGDkLY{lx9tct7LCW1b!YIyWej>VrR5=PFqq?TQFfTTD7FTMN`vPgu%wDCc +{FDrnd`KG#meVlHhQFys^euKJc;;c_e(hEhPvI{Q@{gdaB726l!CQH^F3q^$~p!UK!lUT9pfQZ^ZP;c +MkWKOYnxs3HYX3f(Zi_M^Hv$!6I-npJFs<<+!(4%W~le&hRz^)}>~f23E_eC>vmxmt_dcyFLb}U=REg +pw*h!vfMKT0Bb8Hj2P$X2#+;{xp85xwAsPu=6E0#0Ad(cJTV;9}&1{ +D*VjArn)!Ra#W0^3EyPoZm7(iX1QEmc(Up}DEe!FTQOrVZ+^dG| +ARFv9+Hi!tb+jiw3qR;GDfA9Nk&wnNA@#dG$lT$`EcJn=O+itYuf9a(rk+g)$em~hE#rP?>juaqZ$e$uUyH$lEBu4! +OsU@B0$;l*C%J>9~>Fdz6+dqYk8iKmPb5J7IdQ8aNQzz7b}v(6>SP1mHJF|4LL?kp8vXi9yt!B9NV=5 +J|M^#1{q>TArq3y)>jd;GzVVONn&McYto@9TawqNbZ-Z!KZh(|<$PZl`9ZvBOK@Lzj?rm)BD*| +cz;lTA4KJUmhVuExQL;pAd^KmbHczw$70jiyUd1lY&Q4CRl64N2UpgNE=RY@4SG~(ZusIN(<#Qx?>>& +0z3O@mNzn`3Vfl@&SUtGW7qkKCSlAuh%~z= +?mhM?@$3WAEI!~wrZF4c#1p+|@`kn0s^#Gk=gO-YZtacS8@9jdXcA{0gfhd92n}r>lDtRtokT<1y)uN +bV|IcjC9zELacC+2LGgYr1oj!T;x3pj~$LF$$!p_EoDaFcESH12fR0)HUp!P +>!aI0*&lGb#wmm`1<}XiCx6f-j!?h=Ofx_a(uP%U&yqZS_36xHVmnEU^{B3rNu~sLej_dr>Q2Z_e}hJ +LzvMQKu^B)myv}a0(2C?n)T!qSO+~!?vgng>k-QJ8YO~?UOEu5-a*|xHMn*?>>nhNR ++CZspgt?zL7?>?Q>L5Y6*sRvG*zn&1ICgr5_3xIJcFfStA%-TbQh~BH#$Ird3pMK*=Rv2OO&K#}d_$^+3_|vezQ*?G ++VT(lU3+slV{a9cD6^B(kQ}r#*Hsv#X99jdC;{M6z4y*_XwRj*ikNnrd=-Py+dlGTc4)813gD03lO0r +y!hvi{FOrXahC)9A6*@1jt# +(zw@rfArNo5awpOTAN}R{hhm^_?9no2ql0T(1+tM(*_RpV7PPmJDBe08h&$>>@1kt?YvMTp@02rG%wq +v69NLPXlkoY8Zw<63MY_{P@8~-}O*f=_4qqk^d7ko9k(?v8zYS>OZ*o%=nZ>P8$!JsF11BCtd7DgwcP +m=*5oj#5q-*$j_T0R5J13c|nT+rh<3_jl_apn}Z)WANDj!j3Y=4`9B<4I~Nhg0j$`3&`f?u0s%wI1X# +fBn7|#XGvtt6H&(kKjHuxE8Y$HiCusIF_s^<8s)#n^_ut@d3w9)yyzOme?)EDHfN9NJ#%g8Fug73+nD +sjTWbNe%Ro!TIAYi513^Yp}1hMl(okDp@KK|0!W&#F*SFu9kJNlUUTd_rvPpL)SJw#r^ZC=p*~&4?^B +M48{PbY9Y7e)4^-ffg(#Ot@S_OV)YHU{@s4#t7uHVm#3a+Rx-mOjP{J}OPhD +ddT8T97Uzp@g?|#R(tF5~HsLD&WE4d}*Bc2c$F^1CuH!xQX$$ix?j58I_s?u!C**$WT +JgQ>T+kKQq?g*xbH$3%&X@E~3|(g+wEne)RfC|%Gj!zQJA~1vjqMeur6e +)T23k>Q&G?p(QiO0mea;oaS3ClR*mz`*iN8tKnVo>`Nr6o^>YSkUC~S?RnhI7@B3U6DCRPBZ8&#u>9m +md?Ih5I4Ep&uxzS=>L@>)=uRX!Eq@&v2p+v}Nja#y^4~~Z360ZLcK8urVLZ6u;C=ZDlC*Igr~e7t$~w4#cr&sD+13<_i8n2L?s^+pIr3qEuG+YLtrmIPb>fbw3h +A6I0x5Ff`aa=Hw-I_R-(Mg6#6C53xpzYx0xoYyk~$GfChJg*iPeC69+;v@b2Kre2&-1!)PZgS{VFU%2D_CH{qZ0!0+^^g;Bs*Ob~}rK}^OCtz-P +p7Xy@O9KQH0000807zB_Q{5yS1rG=S0L&u*04M+e0B~t=FJE?LZe(wAFKBdaY&C3YVlQ-ZWo2S@X>4R +=a&s?aZ*4AcdCeMaPuw>6I}-oF4MODB5OsW+gm#rE(5>2ls=ZE|D2hztfY)4dvmM~3{rEk0?8Hu-+`* +SERqsgry!m=5Nf2GGDkc!VS``IJ1t9Pk-=>D_MJ@1cK`!uWMa#S09rBctl|UcV>N#T-J4R>^eO!zCszgWe4}Owkab=#Gf9YWc3FMMhW%e}YO1vLvOza>p~RTUbDIG+35Y{rS~=&Hxike6h +cB%m3QBq1k79wE}uv!UcA7h}B!to2`pp+yUvLDTA!U<7=tL=qyj$;B81rMKW^JpM-3Je7}!CYfKL*W{x)XZ2772-wcnSM)#yI2x$}6y$|;+l83H2)#T9{%LgwziCXm74)4B;{xD$KbY&Dq$fVxX&%415{RZc=8a_n<=K06h(=9Z0pTFl)9b;l_0 +k+awjDQTQP0OAe}7kSC#8XIS7!KYmB@ijlbzx+jeQjBQRwNm&{+lyql9m$vwLJkcRDH7JT5md_XQjRvj>OmTu9bRPbWEK!ea|GPtJ}@L+2W+h;NhNkU$~xdCKwFrnOP0zqDX)wrNJ +C7Dbapz6?&c#VSN06x|I_ctEPnrr0vnBt;|jw3m4W?Sd-b0gu=P2^IIHsqXDTe|$o12K7pDc-TV;}ofUrvuX9pejduV6Lu%48sNd;w6DW>R89YCHamZn=s&-T)!c|)%&O(- +ApYR$jvK(B*)u`Gg{sH5@hGPR}V5OHfJC4SFn?Y3raA-G_~Ed;FxGu8C3)&+$ulTj|4)_GmloMeX&m{ +ED}>MzrU#Hpn1RiF|&GwpIL`|GO0_wP0EWixDQ_Wn+C;b!Flt5e +B|2k6CoS4RsyWVvm7yAg%*|-W*QGS5Zq;eBJ)rdMrNg#!Co^bh!@yhU>cry&sO$nXAClv`P!X{ygiBR +#wc=^jo;OnYW(Ok?JV{4q5FOQ;=*FW`6^(-fw_5h~rE2&sPu@Chb^ipZN_-}8eiig@E!b#xt2fd^8KbPmh5FFil#)E|4(P2qCeZumbUeU|J>B#~=h)Qs-cXVN@@ +#>xL5%5c;Z@_<9=9I|I>~>#P})O3pPc@3jBrs@k4;Nks;V6Grg;ETr~)J3lQtbGh#@JOy8{F@jy^~fl +|-T>^049uV>Rv|H$vtSBo&YzaSHjKkJi$vfn1#bzDO>w&dyb7Ogp!T2O?}L`n*VBwr)=6g_x+J$B}fL +h3+0aq`ZOII|bZPgA-5GNqheBx`EZ{+U{CCf$jyoL!xMxj4UEB#S>jo-0*)jBOKut&TTo?YO{HC(!w|9f3~z6V7B6H9kr~FySy=mD6Xq36(tOaiapf +LNi)Z7-XPd#B;n7-uXg>Z2Gogkz+}UVg$cD-0``e)^b~(wkHxd)7hnf# +mN%w1XWViA~V +uO=L+s?T015Y38bNQKh&TaVB$ZF;{$^jx6nKPYdl?+@K9KmuH^KD+YCEI6fn`ugBy@f>0>u9XP)h>@6aWAK2mnY{2 +2-J=tITZ*004V4001fg003}la4%nWWo~3|axZ9fZEQ7cX<{#Qa%E*=b!lv5WpZ;bWpr|7WiD`e?HgTh ++_>>QK>hfFoZCM1gxpz{W`^_ +4nGwBen=L7d)n4pdRurV%?wVE*ddr(?FIb^|&&aa8D|y+}8QIt6qFJ({Dg|q4#UDR9A-h9W*5bvo;^Ih8rwh(N2Mi&dl0p#(c6)vSfdBy!>KT6YPs9#9_w}P(N!~hqY*S2Lp=t-HY5jy``L)XW +hf1+)$vrtp;xvP0a^gTwJw`z1o-6lC|kY#kQ;#_~>%GQ@>Clb-&tI6+WN80mZY~ +i^q=$TowYX8ATrepEp~13yOE~a{VSvC|@SC9ArXf5q(in&JApede7g2HyBp%LYa!9tV>Z8DQDFxliJL +BozCAjwS$5eLgod+05kX__F9tAF!DYG$P|a3T!wE2V-$Zy*Qw;tCG>4q$JK1+k}FsJJ5q9jI(RW$QL= +`BS9>s?EhfKCHey>PH<_gjmjDB=m_pN2W}KjWzN4*DC9)h+_F@B`i*CRH==vrEva0CXy!BkgHbc-|d{ +3FENhW}30lpjbr--U-q~y0#{`Hh6hX~d +hdAhxzPgIZqWm1CLzo3SSt>xp_FxU4j0oeBrBE{g7*EkP=8RR^n4!GjONQ#k70bEBke?idm6UTh9zv0;l}G5X<#f{p=#)(93_L=%Hekib8B2ZhTz0Jv&e@+&)Jaw@`16OGSr|8xx;(N#B8IL_c8!oX04l|`-KH1Y_u +<}=GS9JsUVPr|49C5=-$g`~G-@(C_kk +26NWn=Yyj{TW=TZcJ2P|FB8YxLn$fX370V+vCe9C)TR-{pZ3mukFL2I(mG^wx8ez?hz&=ko#;3?Agz! +41u_oJb(w@N?wJKwQIxhmOG`q2=%OIpF`3n)Z~s=?FoY1?HKeWfY9B+ueI{7~fz>5PnPeyC6nFJgq_c +tLxN*d!*%Si}st7>k++aL1>cAr=+H31+6?zSj5fI2%$RY1hkUeOqg)Xiv&e^%`pVG=K+*&eZGWP$-3{5gAgte>m>rC;N2z10@26mFLkV0|cNE#h +mWrDDioVgoq_c&)Hq5&BMb~UD?ETO(^0`1QU`P^ +dZ)P}c`v00gsLvr{(nF_szm8Unq%Fe^ap`#LM8G_z0=!j4Yeg*$pmNRAz&kbHTi@lkyPV@zp)pq;as$ +5(yA-^3s^qiMVpW_RYuw3-y$M0UhJHPtd`-_*!XmO`yj7|IW`Y5fw4L}**usqebArrpW6sDUIkkZ5~o +PbnGaG2x}j#w5_i*Dpcvx&Pt_tsaGZ5C_=+3xVZGn~y}ur2;YtL&a?F`AC6#UcFQYO&TIs>SjMM=ge> +%waGREt4W;YCJ$ReEj+1?N#yN-MgzKvf`VTuK#l@zxnXfpYf(&9cc>2#V@QaX;uD{wcXsnBHrEwvm!T +0=0k=?&1Cgji}|xiFSVf@E0-QV5p)Ucuzas=fXJ}Iaz?LnCj*76iExz9NI!A$JUuo)vx0DhX0$ +@>eKoS|7ANpOsZ|9c5<$Z5QuR#$3g$dD8D$cJ(CAX(1V4r1<#~(AK(3 +oe+?e*_p%c-B^Ton%Hq3>CeN|+4A9&mVJo1r8sXXcx5p7svSx#{z1NF<@)57NZCQF+OwNXI3i67?fV| +a5N`9d{ZV%UrLu=*>U_N&xFH^pesm+%Uhoi{DyiZuQe)V_Lmegf`%%ICPlKZ)%|)=I}VqxcNv!S+7`I +495D7pN39WbUIN*adhMr^GS!=2%4-oQ6)6>&%r4XxVbbtKesj59mfJ`BW$QKbo>m+dH=mZ(n{jI|h5qa&H)xIn8%oq-&eylD%kY!3!RR_`+GMAf3ybLpt08A{l`~-xjg@W>kxiVgDwH|v&3wAD^qUMF3Xy>hoj#~kA4I4xi%r8@^OwQfd9I!?u+=RkbuMD@;~n0T19;;H@{4s+J{XZaT);Dn +(+4N1Mf@&Md_15ir?1QY-O00;m`Rt8fJ-WPN{4gdf)I{*ML0001RX>c!Jc4cm4Z*nhabZu-kY-wUIba +G{7Vs&Y3WMy)5FJ*LcWo2J%cx`MhaCyBO{cq#8_4fezA6Nx}<kk9E`hdG6w+6er26ySYLz)S+M +8|4m$rGjIysrQI-s6Wvk(BIY-3+wnNRRJ}@0)ZG1nv_4Nd9@9N35&|y-pk!g-1BW +_sBiBd{c0t>BF=Vh|pfLy2!Qj`RxL`*-gKr&iv)hZwa!U!x$(o{2)hOM)jP;yRd`W8g^nN|fr93BKgf +Ebn)XfKYJP2E&1j!Cx0Z6$QU%e<*sEdQR7G<(Q+Ru(hT6j@THEY7o^2S;SH1?KDT(u`9Oa}Lth +EU8&~LX(7Xj)WBAM@gTz4=UDvC(F$|`EtrVT5~LPiJ(97td2z0r%|t^R63Xrut4zxp`d#jV3rtXx*9_kCH|yv4~1cKkhf +5C?#pTO_J+gDJumo*{rzhC*l30S0Si+a?#?!CpYr2&Ww^Mrr|{6=Mq`bY&fZLX#MNHdK1B$v{NYw16- +cDts;CsVPqk6wX?49gS5Cf==P6MPukG3PQ?;RrNOPsGEaFM7W{cz?&R1cA~wJ3UV6EY!Rl295p`HH-l&a_IV9y}N)ba=pwp?L6wEB$#~VU=g +DLumBItZX1aVYP-PhT(N!n=GQkxb~sG2R+CC&GkA62wgpkUlD7qr7Q>2B3Xf6+n~EMVos>n4Ms{<0OXhR(Vl)bYas3kDe};icopm|T5#Q^h5d0MPPl +$adT4G-i#3b6chC=i72O5LeO}*og22f{at!0tw>+bt6PEIiVoSeKIR$v4v&e5VbxzsevgDEr?slic}d +!&8Z778rWp3u%7j#!wiHRFr6(lFN*d`;_QrAyMbu#URoHWCn_p=4JX8qK)J7g&-g(CDrzn@#wQ$8y9r +T5!Gq0MTd(Mf<$#8Y20K@tcC*?o#{ru=6Uyub}DWMyY6iL?95|$pg(BX2gL=uEjcf?H17b)0wDalZbX8Gwf}Y9U0j~*{0 +`BcI_g$U|8299OCE6CagcGIjkITa5BfziKr?=*)AC&yrWF(qkfiXolR(i4h^iz5M0y4kH=a#^yDlJJE +TLF()rIslwETyDJMgG-t3d#O#05lRm96^eljX +J%x?_bUoIS^>}Au#k`&Ewr|nX?3 +dSVfKd!-fNr9z9MyfHdr3dRY`u{fDV|2?cBMBpb?4!HLlgGH^}Dx9O;F+KA^!Xg1@|%Iv+8`@P6&d~1 +ul`tc%gV}Aktq96SfDXVpRKwO;0>6Zc9{hI9e^;e3EV_AdJF`k!NxxNufcoavH<24jl-@qTgV`4&rK{? +*NmuD$e;8t0y$>izhG*-9qx&L*^^H0;XmyUrJZ*-`Sy!LFmGx7nc|oRkIySTenj!ITlrlX2#G|ex%Y5 +@9r3V4+R|Izzp%d9I8{AJ1npr>jl;!*E1Askm54;WscH}uUvvh_~;V4Zfl}YqDD|TZCpl2XYaz?9Ns7 +KL862;VwS~}~g5FK6kBhla~X&W4Utc|#GJD2o#z7{*tIPH1I#gOs#Mk_|B7FzZ0C~z!#^N!-MYdl}j{ +343WlsU~o>+QE><;74%L?-wW2XCI@-4w_}%)cY8UsaTdmr(AKjH +f!@SE4BdIu2u!m{p)Lt^gJ6xBP0BsBb==P^X}l_gd;&}H>_iC*MCnXDe$Ak6XLGC#y%82b2MqUYMJt- +%v3!V7dpPMHj10s#;0_Pd2e-c~e=ti*u&^!KW>)>?KSu6*+Fe-a^9ng^kvpYDwKf059G#M8LFj +o3f@5HPAsZrj|BZR^&}U_+($e&CXhi4pf&U%#@|0f9t!srH<~ur1y3_ij6lj>LAZHg7BRSk1|81C-TSyNmCh^Avs|17{T(4ccYf6pO`z#86+krI+ilKO9Y+5q&c2TYf{Eim-nn +NHK%GB@%$#hzXC&n#@#*zp`Vyg#{E78rJn@721T`pNW>CWUsR3oOBmqQOXp@VVe3u3)ib1EX5R{Rro$ +dr8nH?lqSNSMHDwoJH8dFWUiTc;U3&&#+g3l+^(vtat> +BGQjHwF;@-0rq5dbf;bWeJ5!C5&RH8i#z5w|^4FcX_2(mR?J`^W=(9xB%7^FDJp0vSm1JEGpr$v3yY6 +AC-c?`20c{S`7HUeM7gXTi2*kvbRO`KU|r2C9S5NRF}+o%WMnXe!I2-47_=KqNXMubI5RuWsc((k=c@ +0i=3C9?R+;T7;|yV`RLTpdqleI!oz(El`^1s2G`|6IW|F{}n%CsKqZ5q;1nBg5VQze{)4qJFGwfI7=f +F+co~M0_%Fb=U*^+kkfF94{MhCBvT73@l%9~K>WP!IJ#rDoa-Cw-B&h?SxbR=?d6%g4> +}>oelqG44uazKO%0_Q@GNAs-!L$}qoJWH(*9d)y=k93SlXSXoNz{$mFNf)6*A!nhamX@rgZl +Q@FK>iN3-iFbs9R?GVkG6@FV?PcKjljCt%PxPv^=N1+^_KBZ2QIclMB@+ouP=Uy +0Cx+4eHiO3X50@X~k3anQ)7$g-{l(udKE$uCt}b4Eh^2qN{q(gds34(#(}I&XbwNX7>{T~M;1qwoM$YV9!ydw$05e=*pIW0V_$!U}rgNf)3wF~b#W|D7q1CimGl&#`Z! +V152DT-$`|Ea*Rd`6Y?T*!7GtDLIs`S9E2rX9sfJZK3F+Q3nV%+n^7*pmEa_8^hpAw9Qx|Rgqf94f5}uA$ +6DR^|mh+kl0JkoVoDKaAeQ(UP7qin1@Cv32Fn0it?Ci*r*JI;TbbRZo%v_m2Yzj3G|vQ!(a~p ++a114a!%l=dFl^6tX^3giD#p)jn>z<&G#+K#g95?HS{IjZvY8qg +V)A>JM0=Z3G{-%~t|d!f +AaDZt9GAQ}EI>6ViAq5;WD$H4E`n7x3*nt&c~D2`yEhWS4k)Q>Ze`DDR86O5)=4=lcWZPfzCK5#WOfd +Clo48|M|qOgnzwvrMR5TRP~H4{#7>2Y0!M%}hwpc}n4tHv~9GbkHV)(vi6(qGX996p@lZEWrZk#x!ecFTx8yiG$h$$fJWiu +z}Ic=ch?@}WYtemLnc&*e@!4UOMpb~@E_A8wfXG!bu+VN6mT%o$h7@^hyOu@ejczrYQI-@gXSEf#rKcz@H0-e5N +Ch1Gaagmv=r;3#iYaA^9#i^FHGmacehp%Ez%rdU0kNrNq%z1E=oI_B_OB_tSQMOZatFXQo~q-hq`lq2 +zAfQa=pfZh9a62w{Iyk>C+tE-h90I4MKZQAiJ^U(|4*^G!vm2%dx5)bbjZmyA9@O?qmYIU~VWQZ>*r-cB*lu^wL +(KbdpYZ)ipJ#?=ieTL%gje{=43>jD^@fm-gcJ-ThtOSt*wh22=@Yx1boao~d=F-T6Gy@02);S*=g@J~8p2gC_ +@adLdw`SgeZ+J5Y+JR_RGsUbxBuPCk``_d@?CRoj4khxUFHaOw|QL`UA%0;%VL6TbZcSfw9s9zX#+;v +!ce(Mb3?-lK{fBX_Pyf_&4;mDmjQh#Q0vU%cuzCz0)8A|?wML>C*qN;{$N;UmTXIhaFe9wNZ-3;eu`u +F)=q3af)+MtPg6;FE1SfD$-3gZOb23b^R>v7z5V3l@Xs>h@&z4(kDpS=@wDo(4*s;8iN +yqSfueLL#6j&f|O`p(d)I%du+GrO}_1t6mV^@~)mRYH?XSGQkGPi8YrKFnrAHPyb?$nMJNXV05Bc^04M+e0B~t=FJE?LZe(wA +FKBdaY&C3YVlQ-ZWo36^Y-?q5b1!0Hb7d}YdEFVyZreuiZovPrWe$=WMQ9Jb5Ve6oRuZGJ9K=e~)G(w +Kxso=fNP)Yw;|Bis&g_F+KBYK06y-(xnD@-i%&t~xvL*BRDwA2t=5xZgyCju_E=3Y$lFjveNJ4(c1yA +B3$zmQPA)7~BvXn;R_~`g(g;c(5qfrRIhQt(r%+6>OB(ab*mcs7Fbj!q!222deUBbgj#I_9fheWN8H# +DUIFi_;@a~ee;+zGk%&MwCvE+*5NHzYWoeR?;+;cW7kTAI9_*xm8vg&o4r`Ro&pK1~0ZUVfZ?4hCS&Bf@B!16=EEW8YIXgEfi{%f(poSwmpHDB(CTbf+s_o*U0YOpiK90?@k=*W>so6BT*iAFF +m`b}GQO(SjQ5W{S-=CSuvr`Whsn{7oywT@`$NkFA0HKeMf`|%13Ul<6Ta?4`nMg*Iw8KEhIkP09kGa@tgr@loH8mIid?8=N#W$5;68c2-RXnC!bzGYsf*2U!6>s8>xwR~Sq#TfX%lNuZ5wjWon96LeRtfnSV)LLrXC +a{!2l8A**u2{-x#Q$NFf={bKG#2^lq3=AOcx7xnZy}Wwc0h#3lET}ae3;R-(K$~UdN?Fh$`lgDhKliq +M`6n1lUI&1%zC@mSpDr5?X)kZP}1&hqIYirO-4-Jk(zRXbAG7_N +}rg&s(=#KtAx!*9rN(s0nP9#RFI*5R4%}ynxaqYMz3uq=-DHQ@!4yO_g^nqZt?;%^}fl*HS +QOVTxF=Xcz@(Dp*s#EsKOya#t7-9SLZG>L8Gwy3LvEAQNq&n(g)cNA$a%3SEotr} +m;EcC#lBFi-M+hy-|6WoQBCkhZlm8KzEsV@z3Mf@Xi1^&o0iz^L8=s!$mdK*A`;x +-y2IwEV4Z+w2tA3@z+K-A>VOd!vag{>%3r%6ZW|l{hfI_irxHwqwh^?dnzV;&)_tp6)F9?n`t%2iksMd(lSf-t7QD*0Kl +8F3R(?IiS(;?lbL(12iTnR^Rzbv?3-6MLV)Ecwgv?!)ewu8bhrQjghV#PQl3sW-Iz%7-n2uVY^L_7$|wr1L)>6s?gQN#V(s%2H*v0VLTi=fF`8FpL6pgpB +LTX6xz4x7NN>;Ptml_p7Y`QX^RyE8)IF01M`MuXu(v&A|{?dxU`t8%+2>668`adID9g_4lX0Joyl8_1 +dwsAPZ@n0kv4P3+x^#D^*d2t}S3eB8H{=4YpVF|<;l+-oNBx#x^Y$|K>3|IP0TK6J%n0cT)@nhKBV;g(WU^n2Wzkr7<7<$rN&HBNywpgf+bXOjYFf-4`DnE9c(kY{ +^cfLz5dm&G(yfb7EsiKN44jKx}lQG$q6yV5l}5!PO|}} +5TkaiYh2BHXakkTTerTp4lC9&vean+qdq#vOAiR!oVZkndx9ZJ%^_%{$O+jEv?wS( +JYOh+19t~$i&eCon)m1lb=V>us&ZIc!J +c4cm4Z*nhabZu-kY-wUIbaG{7cVTR6WpZ;bWN&RQaCwzf+lt#T5PdiJAM|L0@COv~5FBp_?b=J$>9&+ +&6nnfYR=yy~VMFP^SCZw+I-4}rgD*2@j?P@9k~bB~va*vMhb%+1Y8x&QIf#aJ60-guqky(=1tzto;-j*U(pwSl$)&mj7>N+_iLZFlbJQ4N^+|#ZgZt5N69T)Tnlh6O;I!LVoO7 +sj;Q*S^L%7iC?l^E5^GJy1=r7;Eik)B#WtCbNHY?5$)AH`{w{&(p;^KD^&#Ysr0wuMe>-~o1~f!4?~BGTx4YWBvxsBp7R*V$tfrrRVo4*!ZO9gQdIZ-jO5kDZ6?qpy)k +{)4=Ulw6<{`u*p=^18ip$9b}B6x+9@cgCVfl@J=0M)hi5Tm7d+UcLiDCyc`iY;1_!+y8 +@fO=H?(>sIXo`E$vVX?CNzmy1LI`*Yn5&;u%eGfI2fG1&B8Evcjakd4YP0*xrY>^g;uk3H3FGPedloh +&sy5cwXJWTE{^?W{67WSWmHIDRnu4ZFB+U|Di&!h9gXnVtnGa-19-C2X?()aO~IC+Zkdu;=Oy)*787t ++-4!j)ke4M#bG7c4Qn$w#UVfBk#|O+BF(JP(9JZSJufXxb6%I)4FBO9KQH0000807zB_Q+X3t_xS_>0 +7?=704V?f0B~t=FJE?LZe(wAFKBdaY&C3YVlQ-ZWo36^Y-?q5b1!9da%E*MaCzNWQE%He5PlDk|G=s* +Ql|(_x2LQg&?Qb$ph=3%!!{V1z({AC2rUXE<;EEL-*-n*wrop|6JWqLA%HCE?z`_k-jR$ZTFqISPAXe +zNK+=}rP7x1d!uA!A+4WdmWzcjLKQKq3X!QCQYkEGF3s`L@zI1(G9@Ks7L7~;bE9+6kY9+*9v}HJ4J( +gQlu5dPB3C*w;<52ER*Pbq+GPoA^rU%v&kZzVNm^MUSK;O=uOwXZ%qqR~Hv+JHnrfKBW61^>Kgra*6c +WPVuWp6=66R1?8u~OZ>jzG$MrX%I46l{}#^^QQF+Nvw_ +Y-s7^h^AkFyJN+q6Bjwa^#*#ZVV%%-?)50b|gu1t%_U7Ds999*6*S-#$K~>DMew=a$)$rgh5qQ2J({y +*MgpFRJwv7_=xTWvol~N*H&be*tSI#r{nmJyDlCRMYqX&2G+`sOMr4hz7}QMK7Vl~cQRMK*)Wm^%GvpzW^a6;gu!h +p!^8P+yAFwES_CUV8$iQG_H2|h<`U9CxB%iXt!lMmhymKJ~hW0o?b&w(#8MjEMCerXG9T;lK*Ki~t!n +jvIGC*BGJ0CZSm;~M=w3R;#Pos^QT|f8=zedyxAP5s0kdxTOX)3z8kR^70f%#+NAz8%rMAfDB3ENIgX +V{nESZJkeQ!kr9bb-XRBz1pIrGX2%YllJIdr`1(Q^{?5%~h9^TOE?o@b^(n8FFiN=wcFD)Wyd@51^Zl +0Woe*uqRh28_u|K__ZX+ZW=^g>^uc_K!hviD91K|S{uNq}{`fNdDL4R@3}X*~vlDkq*kn&>he280OObuT#Vl@(nx(NYr_rZLcCVZLbRF +CCn=7Zmn(c0!&LY4?7I0J9Dt_V;Z){PR4&7I8JId@eEu=l^HhdRFcNo|)?*`f01{oW)cb~ZbZAys)IIK)$b;kLK}NN!}HS1Hj}QRa~%=02Ux~_Up9L-jvn=%hKX4 +%>G&@8CA`D>8M~1eezzBT_#wKd=(OFxzx5-VKhfo>6Mb+N6xO6W9drE +slu-#J%X7-D~t)rO!%L=Q9x2IF14gAz}&-;IBW)ARamolA9d6CyTyc+n1d0};vYfp8fDSPjf^!DSE)4 +PbBu+#mqTGnI2_>P_4xxn=6pDX7t4wsJJ|GR3&Lva)SplN=Isy)dbT%olG>eBR8smiW{#&gnX{$qSbH +vVks&Mxh;O)^y#%}_*wpB@l8?f`QC>o!0;O4XnqM5>HS6Z+2|&8Lqm_c4Xeh8T8zW=Easr;44QHH=v)1j{t&QZ3V4nKQ}q8=mQoO&s^4r86xVYn~?w{#e&sfQ(8EsWV2msbpJrt +U5JGOIuTQ@*(LSr+i1j$Gt$>`B~03gcS<0P+CSRzC1r8@ZlQ*-YRlm7OZ)MAISi=!aTJPnE*}zs!*C1 +8H?2Xgg`tLRIQW>XjMth5b0<}RmS;jPG;Z}U^I)Zvzeeuk@sb(qjLQ>VA@*1Q=};`xK7hZF|nBggsyp +fmN3Egtk4F)D`4=**$yCAYj1KkO&DoB~=rpyo_>+4 +Wv^5cZdaY6zrX+ifa{yzGudM$XC^87pLQ%_2>eOgNq$MH!cJ7%oXjCE0z+OG{q&rl|$bwW=tX;3Dfv{yjsMX`GZqX;V!xK34jq +EIe6id##djAOjoa{9!cLgnnb`3O8KS4-;0KwqaZwHpgAV_$8U4IMimbRc&Go3& +Q~>KO$Qr0V6+WN2{<09mSVi`BDF);$ +F-t>(pPkN)de)D3PC^_mDI<410`f+XyaEmt!E?O+nX{049vt61!x7`s2s)K`A +xMSDy;`!{DlgWSD8u3|lSUX3&pW3|~5}ia-IG8xw&sh;$e;ftk}%Id3*iWo7?;4{0o#dd)bJ-R^9UByIUKH#4cs#tFxJz-@&ygY}Om*F`WcjEOW)+6<{|xe+`I;WjIQ@AC%PnS2IxYz=DeWti8Pn$zTlqfYzL)rdOwkvN72A9x{bZ=0S7m +{fvYqoi>hdbC5o_E#$+qYx>9}@3Y@2>W3cs3Wg8jPbeI56s9QzmcD?M-S_@88vrYDmxfQCQUOHuq};Doc?6k(F49G= +}Ko1T+?0%jCC#oIyNbhf3CeE(u)u53;fybZxHvsn_|EFDa{~Hq++%;CO8f*f|`+__? +JY15`jAXRHc-IP=CYhO4+J?&xC<7SU#T=iJtOOKfox$9KjuO(lW*J3gt>~U_cbYJ)3D}3QJ>O9Xkik^ +<;va6*&4Ic!8mUA-!ajYXhzD{{1rA3>gG-fq520gz{J +&a5!+FmE+AxrWwX-T^kX`QMHxp1Tw#*VN_S55oh=QB_sz?%96|2Thl-{(72ADu-UDBxZQpWcgyY +O=6bo>_^`fO1;4IsC)nHuuONiG8wcXP<;-fm43_0&)xG}e!|H0g-Ry#wi03uT1Jl^dS`ShiCXKGwkBY +||ff?F4AEYIv4=h5*eL9H*(;9*t@h1~0<{rV#oAK=PbON@;m{_SvwlMTGilknk_71)jNrE|;M+7{QC( +sI>5F#ewKpwbsi-{6KJ#o1QC9wfG0zyXl7jXG;3-y3qiM^nncB@?CPMPqm#k25&Xt|1DY@SAh3_7hgc-{uMa +?x>J*8QV%?hWQXSEL)+I1uPb25}!CXZ0`dzCg%?%5y5l~C@t-RwVMkSF(F;;om#lWt;}A=T{D~hN6Zb +zVdL)4-@byW4@VNPvKkP}QnI&(E2aoNYws?EvCQcCz4Ov0Mjz6K*X-WLbnL^R&_g*pf=*{_VQa8nMYQ +xf{m +JdN>GI}+9yyHC-f3VHBRA)1Fl(O=idL~$VxI>qflB0@QgC)7wb03ei@_`)naLsP{(2&`*rSXGxQ`R9p +05+o7GR_@a_bSHaTt10U%wCS(U&;q-GMzX)dM~rY^);ld_eERf1?r>#1DIITtA1UFLwi3B&fY!S;_&V6YY4Ove9k=Z*LO- +h(=T<1|pnm92xKw}!Y{oBL(+ZaqeDh5fRA55^M&ahjwFfKUn`d(J+msfR$%;kcm@iCo+)W=X!EH2JuS +ijY?o@mN+=?@f2YJeidFI7##IYDvBs!oeG*o`q8Pv5xRoY*Ynm|GTd=`2$c(0|XQR000O8NLB_@E^_Z +5HUj_v@(ln082|tPaA|NaUv_0~WN&gWX=H9;FJo_HWn(UIdF@wSZ__XoeV?@daOobBXj#;O#KWW@!M2 +Bi1QIauuuhfR+_Xln6WNZSApSeoA6e5T9U2b^H1d$N_PJl@+9yt_sug7ON+Ix7T`2=7=T(6z6zHE!uo +;RJn((~3c>D5FsY)Fl1PhCs>$|@S4-ZP~)~stT7p>oqO)bdXyV~$dGI4ltcra}wx5m?=GI~lWEM6%`NH?y@todTEUc4NTH;0~VT(0c4Rw3lLB+V6lGvcmU(q^6g+(9HvS+!T&KzvIcZsG@a@s4&*@kJpkd@tGdlzg9v#JvRUK +e_6*6EEVP1u!*OxJ>&wizwlP{KADiz1^a+KTO+?L-ba`MH~u)7zc-A-7r7#}p_!PPXS!geb0)0|AcU4 +Pp&-WoVgjCd3*VNnoy*M6`2D1BMGQO9Y8uNpK1Euq^Il;l4_{dqOwDj^A`#YyQoO_INWwD9xlO7)aN}J3 +anaYH)c4!>zQj%VGeRv>D!>6^btR_rnNQ+;!*Gz%oif|i$B$1UPcIh59L>~tXj-Do$#6AGb-Dxfo +Wi%k*WD|S~hZSzYko;gEF=k2lmww!%E(g=A5>8TF^E+F`QLbxww}k;i6#fKIO9KQH0000807zB_Q_15HA<_T<0C)ia +02%-Q0B~t=FJE?LZe(wAFKJ|MVJ~BEZE#_9E^v8ekFjpTKn#ZWNO=cK41|hSs8hv&6ahmPmeL7zI1;B +i>CQg+%#gS5YpI}@+=_ZF$jh#KC +#nlnR8&m31n=<)NA8|Lyx4`?L}Csd9ypz@V;7oK*tz?SmC19jr%tR1YzZt-8|cW0Z>Z=1QY-O00;m`R +t8gR%LZAo3jhGKGXMY>0001RX>c!Jc4cm4Z*nhbWNu+EV{dY0E^v9(8f$OcIP!ae{s(UQK{gs2&U3Fp +vp_dZx7|zAMVl6jOAUjTX-ADL=}0M$EB3$N3`t3R`IT;QJp-{Lik$ZhXNH{ed`|i#nHM=PNixakgqHO +2BRw!q1dGe&vC5J-pD=3*XOACE5lVSqB-yM6^)XBGY@56*O8BLzp3_gVlGAL)M1!&(7{z&3(j}MhAPT^O=uCvaCX+-VZP1$-Pa9;nS1bdqOcD-yk&*>W|E}P1&azSv3S4J-Ndzqu +F-?Gg#dxzDnkJJIV6n(S4QrvMWReKrHopbXz<8D6Lo8X1Ye*y!q!OFpJ^|gcyY)yYub7Pr`IX?nnDko +-jU-DcnXoBwEvyq4HRdO1(CmnG085wjenCrBZ*IV@3{U7cFgo!SM+jXtFPY-hV=~YX^%#c6<~3xeuv!#e^F)kNMuKZuquT2B@-4#(U%Sd&RZ{!T8I@$G61dC7eKXOMIWDjJ +sRx)`B&5jmz`5w+ggG&ppl9!Da&T%RYyK};ALRDl%z^g4INp5hGD&4OnxDUdxlOmDX(ZMm_^(Lub4{= +07t_#so5r=fh5IYh;gfF+m?i_ps=%hu#bZ#Xu^O(vU{-C-JJUPipAF>3{o}GY9uZBv0NKAy8xw62nI& +Ns}wd2ag|po1f49sCu2rvQKT5(fN&)!qK#6J_ZMfae`te3a+G?(dkqdSdUuk^9S~V`(U(|O%^8P)Ruj +Ktil7O+P7JU@GP;E2^2VxT5=D-aKOr9!18H*2fH{aKz^>dcDaqI^`1lR*fy?tL(YsP8PS`t16DkbKlJ +XvTRC4(G7ojC7a*;^VzP66RS;4h^pljcC7qJ7dE~EF+Hi=%G$?x-M=x}EVUKxrJ2#vQio5-QYSbWQ?? +aZ+avZIF9Q;x)TIT*H4dI3KsC%m-DXQ`*wv&fro*ctkh`97quR|yxTxwjdbnpf}&%-&Jv**1BP`!J9j +P+qrNOG`x*mdV`2A`HxZ%=idB5ni0<8EYN0&fYFDmV9r2u}lo)=Xpu1?_K+qwxy+J3E!vc`vZQT$nVn +&{C+3Lf0f_oAKrQvyii{{i!l(RbXxt{-i1_A!jsum*(>v2%BH1KZ=*TK(_F6iJgygWtauH$W55_%tFA +web^mm%1hX2HVOJpz_E&MxC@Ms@R@#n(!xgJ4-YzV-{0mHp6sGvIRcSa;EF0Hc4};mJmx< +_XSjcqx^akUKH88NBfHCUT+Pmuc$DKT!mNq>K>oSEM*_~sVzI|I>@wqdJ%5RYN)qUkFCZo_jy^tmz0+ +X1587UP}8!8u(D~qLQJ^P2UVV-Gp5OZpyoP%bpkG>>jdx!`QMr>K&k=~JJ-OdbgEbIL{1{F>eBW4mpX +$0*@jDmy0mL`92cV-td7AEu@+VGhV{d);gannD8}!02ZQ*?NjG2x6hea|FT{8a0t*Z2k)2_;i*OgTtU +|HW&t>tGU;Fzf#?u7lD9i2+VC?N|6ZPk|3clnDt={#Tg4`Sz{TV9_7LuVGaDinTkdkY35xMxJ^g(e+= +7v%i1_oydV~4Ulz|_c@iXR(UOQStH_5NvmWfrQZYtlWAltS${y&uQ`)`-ABgy=FnJZ0x +7*|?m06K|UJSh;4aw8mVxBcE_EewcH!9K>f~z0rN2Y~QYz5!O?Ly=LH7w-2CfZ!j +Gn*}*CpvCWKVaCdkJ27la&1V8K*E(S2$KWNddT=gIj8qSK +WR(^B=;rF;->S{aaUOzXx#&pzx@eQef_GO_Wofe(QO!Tj!wL6^K@Qd2dplbb*OdnUvtzgAV!9Q +g#6E?-t1K8M*g}=iD-uWgFQg*&V*((&?jBKRqjm68K)a?h9c}~(in*~xJ1DM`hmD|-0dVoURfokqQ)V +HGaEeRUOkd@+pkdl17_^^M7chq7gz>8=7=AM+gt|tk{G@-t(*9@pxc~wGCC3va9%#>B43>4UzvSQszm +GQQo+HG~R42!zULWLxN=eKyCT+?!CfWbPreR@uI4-Tcx1iD@FZew5ljPPKp(?Em})e8J7`+o`vAYRT}=XBk4Eb#UktgMChf+e8|zM*1w*kVa$SZEH`DrDZwUI;Oi0AVehvjz~;W-mb@PkmB{2)snKLw45b__5f +!ZG#*O62{m@=Tz10lR6zB&%l74Zd9aVLy$gmn +x=iD=(=Zj3}%})(FGPG9)uTd$HO`bnOO;Sd}wjkO^#D8j@tmnQiVl!erEDv0K*>#@)YT`--2aQXIP+H +u#i4-K0g)D2gk`mL8Ca~|fug&epl1kgV +oYn+2y*=TGpN11p6rR^YMgjL}Q9#nu5o=YgF0=@6aWAK2mnY{22;B65n{e0003~U000~S003}la4%nWWo~3|axZCQZecHJWNu+(VRT_GaCw!TTd +yX$l9umV>iqyApM +|L*(me*E$O%wK=|{L9Zj{`l!%e)_?zsf0!{2=Rr~CT +(cm2If*V{jr{6Bl82hlU=RrCq^z3KUSrDrdC06mJHK+mE#(7WgZ^ttKPzS67P^a=VF^IB59(9N)K0Bs+-!~%33X-aqCw1{lc(j_xWmHjj*=(rp47l?U#8o=hfz$oGMN=w5&^(_0frcr +S^w^Uw`HsY3Ej=o#nNo7NKp%{i-V~yRu7Xuf~2|Z#Y(aFp?N}y_YgJ!&Wd{-VFEJAI|HAqP6?|VqWFG +Utig~Y{p&VtNGkDi7{ZJ@!kfv+SiwGt9C)JalGEl>Snej_i7J|;x{Mbbz79T8`?fGoGglT-==}`)m?Y +R-JE1|#`$)1{A|C@|I5A)&7k6_BjNOf^9b`-~ZZLKSuhx>`#Nf +ky+>CG?us{D=|5$&nha0-Q?5@BcTxD!V#p~Xlmu>xT#^6!_ +dow>eibrYf4?67H=jOS_j7GukCvNhqTW%o^PM$+pn%2u05^;t|RWBz|Y_p@GJNY +*B#db*Av(G>;2t@YlCaxckcCm&f_}Z8u(qpHSoKFYv6al?||O{zXN`Ezh3*N|I=6327HTa;P=4qf!_n +a2Y#Pl_3MG(1Ahdb2s{b>m(YI+_M7njB-n3?xQ6{E^k0Jf3GyfOUqb&S^j`wr1iTsVZ$|%R^j}8*W%O +T0|7G-FM*n5>U&i~JE3O-^JFW+=C$91S7QDX&{a?`k1^r(zeiih8LH`%@e*yji{004A(EkPfU(o*r{a +?`k75!h){}ugT(f<|wU(x>+{a?}l74~0Y{}uQv@K@lkz+ZvC0)GSk2K){98}K*aZ@}MxzX5*({s#OF_ +#5yy;BUa+fWHBMhy8cp@4(+-{~h+}T3z5?_WpsxUZ1?Ve4Ujh0G&{u%ILdG@hB|u +-H;TrZ5psxV@0r(^EN8pdZAAvste+2#r{1Nyg@JHB7#QPF~Kf+!j@FTuWh;LH@e#E;e0YBp1lsvA1KL +LLN{)G1>q5l!@Cd9i5@oqxAn-K3N#Jj2E{vG#+KS>?;hd)Uj_lG}8@F!_NzXSF)U|$3J9nkN9eh2Jpz +`h3X58xlbKY)J#{{a38{1f;m@K4~Mz(0Y10{;a53H%fIC-A@d-!Jpm>|gz7#(XwoKASP0&6v+-%xANY +xPQWR#@`q4EBFo99oGZb6W6c(m*F2W{6mI+$nXz2yp9hU{vik855ON@$A^sZI>*=brHt`9W4z87uQSH +${EXKx_k%nbuX99y#8-cnF`vqqPi4%fGUihm^QnyaRK|QNV?LEJpURj|F=1&>(r;Pbi#{4N`{**C)${lzyf6ACY5qi(p8wBsda034w%20wu-b?jjTtDhZ8*PQoBzl5m6Is1HYdIO@YuACCHP)Q6)!9 +QEO-4@Z4C>cdeVj{0!ahoe3m_2H=xPkngm!&4ug`ta0;r#?LO;i(T#eR%4_Qy-rC@YIK=K0NgisE +1nMJDAA$M^)JLE`0`(E7k3f9{>LXAef%*v4N1#3e^%1F$NPR@=BT^rc`iRs=q&_0`5vh+zeMIUbQXi4 +}h}1`y3)JLU0D)mvRk4 +k-1>I0tz>~-}QD_n%}66M(IZRPCizd-1}2#JJDLU|!ndcsOiSm_BXJz=FMtn`GHp0LsrR(irpPgv;*D +?MSQC#>{@m7cKD6IOb{N>5nn2`fEer6;WPgq5DK(i2vC!b(qA=?N=6VWlUm^n{h3u+kG&dcsOiSm_BX +Jz=FMtn`GHp0LsrR(irpPgv;*D?MSQC#>{@m7cKD6IOb{N>5ndJYkAo6X0;JiaFOqF$cvS6n9YkL3#c +9@lfLN^mP19KK`a0e^U=iJATy<%6L)6Lz%~YZc@f|P|QKG2j$SuIP`FzwuD0pO$I!Eg$+}e(E7%J`!&=6ZsrT|;jEkezxY&c^; +#)Z`zLn$RTRATF;NU9IqOb?Y#kX=??7?xd2ggMV9~bpAF6w7od@F}x4-Rwa#~k`GhkneVA9Lu(9QrYb +e$1gCbLht$`Z0%o%%LB1=*JxTF^7J*D7Pqwek@1z!F9Ro=3>^O@IE(}vlfLT$;}0=MWLQ=E@>?a^?Y+ +tYf-4@o6A~@LOtJH*jg0o`R3BrqEOE_7q=GW&<~fl7Uj@07q}KBU`)8VPP8b;ow-u9C>+D@cF5cepVf +{#x8u(3yfeMS-Qqz>$Fq(;Z5(^=#0B@b5QI-aROpA-r5}`WQ076oN +ul3altVxCJBxDYhkj>K4*k&YEXtuD`kh5N^h3Y1D2IONcNXQ)5B<)f9QvW(S(HOR{2a6>hkp1$Xi*OR +@RQJ@9Qxr$p+!0L!_PvCa_EO2h8E?}4?hhp%Ap_n(?vP-Lw~v`hkoc!7v<0o{pq3{`k_BvltVxCr;Bp +vhyHX?4*k%dF3O=F`qM=@^h1BTD2INY^{Tg3C6$*-LoXIJCebtKMRZi-T}^aUW?n;dR3 +u#+*76>&bR+o<m|m- +%5UzVj3&?E##LC8rYywedto_E8R(c2l<`kbdXcE;>H!wtLUhMc3t!V`V<{?dtTLf9#wnZi;lY9U1qrG +33R7m|1dW$eAT)hMd|G!DQI8wnQ))dMkR53f`ll`xo55sJ{yOr3hC6t}6YbM%rl01y +g~m>bVuqt-@7-t4jYB@>JPL{Z_>*>s>W&a5I9Yaid|}XwpN&bDN&qfvZbD9r{ryV8*vL0zkhE$vGhBk +emZ@4(V+`Z$o+;+>b(bf(m(~aKlWzUsL^ytuxG2|4!&(s(&Z?ck2C`c)zCfJTX2@jSmz0pVI$?{3?Do +H}w3m1qS!b&~tBYfT8E!$4(QXbC-+#Tvm5m#bXxnm{mMx5sz8LV;1q4RXk=9k6FcI7V(%>JZ2G(S;b= +(@t9RSW)Y8B#bXxnm{mMx5sz8LV;1q4RXk=9k6FcI7V((X3v3aOS-nV>yEG^svxvv6;xUVO%qkwUh{v +qrF^hQ2Dju_l$E@Npi+Ic`9@+>$%w1#>?P5;%%> +Z+auoginl%DZLfISBi{Ckw>{!*uXx)d-u8;OJ>qSzc-tf1_KLSX?A*)FJ?z}e&OPkhE8g~qx4rD&!~T +7e{0Z{wz3>V0C&`cZ#cK;Pk1fc2mi!s=D-QP=@?(9_#*kS!;V*+?-A#F#rYm_zE_;@5$AjD#N;dFuadt){wnzq|9i#%9`V1gk +{|xXH_6{1f0O(T@;AxfAb*qm4f4x>`3Cu$p`MLwbh4_CtEc)^2`C&)BWZ%YS+JFE9V);lI56mxurI@?ReQ%gcZH$&Jb+Km3=M|MKu(UjEC +&e|h;Y5C7%mzdZbxm;dtcUta#p!+&}CFAx9aZ^@6n(Y*YchhOuWA9>7={4M!!$gg>m$GpkQ4|>d-yyi +_F^Cqu(Q^33_Xx11m4sL|hu<9?xTX^&?U7Sj4y;HEzc-K;Lr4`Kr%2w||Kkuj0Mw3FAO +||0w2PaV5;F +a1EtnUSy%pe95T6$*u3$a6Xg#@L%iN-PwP0RZ6qgpP#}=)}7Obz9W<__POHRZ^rR@$Yh+B%@i-P#1^d +|c2Jhv!LD2Nlv`-Y?N*JVl`_{VZf9?av)Jw<pWH8#VWpw)u87FJXiKyF;A?TuT|J{l|5I?zpCuH +VxCoH&lUDuWzQA%TxHJ{^Ma~*L4`e6*>lA>UuDm=FHcP2WQs#?>Y}Y#3 +LY#?^*(4{Z|sXSNxCT3KO? +1RLP4mr$al2_=*oy#C%f#Y(|$Bx?ue^po=<%N==%W!b~R*I19mcGCj)jeWG4gP7e?3 +t=)ib2G+qtZgU%he2JB%dZ((5lbI6|#*vXKc3~aeQitdAWduZKkU|nnI#U7Y9>9g}SupTwk?*r>jqYE +#-gAaWNAFzj^@8ARLIYa$E(C!&_-blv`-!}!srkZ0KWbU%nwW=9>3?S2UlWgsqcNr?9u +t0KD&J#b-F0f+bzLU^q93OQpo#Za%U9RLdfn9fJJDZCdR$S50lkU-Y +HvC$kT8Eq}rD +IdnY7b;s9n=8jc#JXi6|ogjab{8$ILHJ;qaU%D0l+_2kQc6%f5=q|cHzF*vmbMC_PA*3ICceoYL+!gv +!JaglF!L7jhZqQE?u7>Bz&TrVcmL;BiS|-B2C)9Ee{*O#p92sDAWPmwxd1brCGHKCnuVMIA{Yy3}%w- +X>7`#=`t(Yiim1ty{VP>;_!4$KP)KunN**%F`Et6cEq-w~PvdT`foww?`Rp+g`ZpFkXyFG$&@YIW7#b +8RS4&HHrxvM(+#xZ$PlWXG&8Sh!JOgr-1znF;kaS?Q?2^{t!=ogp)7`@phPBL!&0^h2&r2OPiGHG!~H +6|Hkoh4ZKW_4>7W3^=LIMWp7&FXvcYSDv5;udCB>Wi@{Sh6tsWV~8 +1v<7M_ljy5()WED!SxII3Y~A1OP0f6U2ANvO*pba@(dYVdyxK{Ip20!pBX^WZ;hN;E77uSbt->_gZj* +K!;!C5r%5(%8Mjr}RTglj?Pv-$!{XG?xIi!6jk(y4A^O1~7yBb!fl(8&exF!aFu>%*w3Cwc>+Ga45dh +AaLW^f?;NJ(Wb=GY$<&7^}=l)hSKrS8v!t7Tg1cF(J2Uh4i(xLPKr?k|O_WoGL2E)`Q#x2In%b5r*s+ +LKEQh7k@{y-Xt*%e2D5 +0+?xqgB38-3I~mPTH#;~%#^iHTKjau!77;Pgu@Ys6AsqLOeY+C&!-a(M;v<9CSSGbgu@Ys6An)tUN}5 +)c;WEG;e~@|3r;T_o;bX4aJ-scI5>(<4P=wi>-56G;+h$RgVi-N2nWND8H9uNH8ThY3v8y2LgY++5rl +&!HdEo?WavC~6yju@IHPc|%4SC4V42N~!ofP58HIy|HZuwbD{ZC*#~=>P2Pfx)a|j2gGIIzAPnw-NX?Ai3Ifrl@l~Y +qVj?$?q97pZc6po{KY6{0uJvD{nD4&|banw&u;W!GYrf~2m-Kph-c@xJi9Gv6KTR1r3owsn@#BmD;rv +USP3cz}-sl~I&f~=|IVQ&WX`qJ}{9Ju^(5Ga44*)|u2&8@lGc@pzC4_wxS_omxcp5RGqSyj9k%5ENN`TwZM?BZ-m8C}LDHniz*vLySqry%`9K7C|@)aTMVw#8HH!5JwS?LL5ancr16rFTsaWvs*#L!x#!YOBwXzP^%eeMoP23)?C#iC#)*BzOqk&aRlKA#1Vue5=Rt{NE}f(B5_3Fh{O?vBN9gxj +z}C)I3jUG;YbvB&v8~ufJp!|e~|?+6JQoVW{}4$fSCZZ0A>Qr0+niP6azVoWl +2e>m)8KE|QbDj^a9r%UX=&B(9^lPU1R>>m;tHb~WxMj=M?YZsNF`H0~yjyGi42;<%eM?k0 +}AN#kzfxT~tI)e?us-NbP>Y1~a5caz3lCY~&UaB$pB8g~=N-K23haokNBcN53mq;WTK+)WyH6UW`8aW +`??O&WI-$K9lHH*wre8g~=N-K23haokNBcN53mq;WSz;)ue*0XZe%z!K6TR52PEU5r77p1~nJX$Vgo! +jp#Z#34Ls2u~cslZNn=DQ=cjjD6Bhh_8tQa?*gDI3Ont$cf`_(zu&A?k0`9iQ{h4xSKfcrlO82?5H9o +R*G9CZl$87!G(My087#C)O}c8-RgwbwJAmTBhKTTE!4y+X(dl7I;-*OkRNa?|(c8FOxUxJ7$5nntR=(HhKZx!L9 +063-iFlJmYjwPQ}%qHGDAKYsJJY1`8>2VdD(m*WSm%Smc?q~|+H?f7|oQFbplexkm-`iI*mJ!Jv=Zrs +SoJX_gp4nJjnR1R>n409>X=?csby7WEqN{+Up&x02O;YEq!@FV2)zNvUVwgb;s7N;DXx;adJTa+Zu@f +%rh<~GoavaL5CQfEn>`C0SI>eI|kX|F7M%P8x6C|(>s=Y50o!tcY^PRCv?Q}(VR2?`TF7G-V@(=;xn? +H^kQJd_{~k=ASLtuh~UQS=+;eJ)DboTGpCrT|aNx1_2UDl-bNs$MuUwy!E3)MGr@?%?gvx`QLxCXv2x +C*KaKo9wzB7NSqjSL3JhOLZn!V}jJBoa3W_cTprYOG3XC@^Ec`QS_dU!$PJcI5L>7ox=3A)SGfQt|KG +qee?Y5HvG+P_d9naFhcn$y}N6)!Yzb8HlDb}7r#5~=EhjG^8ZrHC^Q&fFYs$ZU50`_u9_%1 +bfag!iGi&0%KBqD*maawN6Q#ADwEQgfDkspiX^HD8Oe`_$3{E{ZtZ%3Eg@tdUj4HLE>j3%VQgja0Mt +yb_1hdQTlIv~;G!(^6{XJFVA%T?ZE +Wm@vd)HlI;kWg?@_Me*WLJId8I{D5Ojlx(rFw|7F%)ABd(2bd&7T_>~ql*`&iryV>k@21GkWcqrY7_e +IA4w`)L_opxpS(G9Ue^YjZV#;w)OW6_1YLB~_-3x`jeo>M*)LYS;Ran8G6AhmBkgbZt^kTEOvsx`v&7 +LB+&0+TDqBwDA2a4=WJ;vUux4s_!M=8-~UE87zacHB8t4w8qr>e`I_T+3lo%4mK5^Zx1i&uK^YAIV!d +rV~V)k6{I(DlwWS}ijKKaScNhrNwG`&ca{7}vIgao`!QibIPZd-kzfYIN+l*Nmfolv=#nLxLy{OEeZG +iNjtPQL<%Ph!T=lOM4r|iF4ddc0Cwdnw`&Lo`hR)6!Wc?71r181?y;u*1y+~^PSTs*UM#D9^dnROiyH +-7&Rir%6B{NWxmz2Tr_I-v05G@qjdjRna>=R@4s5SryobpG}=zo9AmXS_C_iH@hAa}?d?m>YFWv$C}V +T@mc%JMM`gg~)$?k}a!%n&nLntodJ_L +qZNSpD1-ldsm16aD6J1*56_v01LJXmxY7NUB{F{ibtH+q9{`YALk}AR042AIcDi6|wI}v2b1cp|s6mE +H>&};ndSca^5}=JS~risYQ3A9W093oTK(hpV6U9Lk-i@FC=@ +BG{wwaR;PNdla?4I3070DeIk?^SLh-S=?RXIfn9$Q;sUfj|wY}6t^mOZZ4Z^=)LSR10)A +(+aBAv1H+>1MD><=B#QQ=vz?V$i?XvXXTv(|$MG1?EJU2YQv1Wd{@efZhkyIu|L`A=V-Byqin7}Ijo< +t&f1~ZwFRYh29TVVS?Lj3N0b{s!y}Focq~Y_5(6G~5$CHuwm8gID{=5GNP)h>@6aWAK2mnY{22<>-*j +P6M0074a0012T003}la4%nWWo~3|axZCQZecHJZgg^CZf9k4E^v8;RKafJI1If9$UhJ%FtAa_hU> +0A~wmjwnG^cDoJ5;KVaS@KHqb_(prFDc2fo$2o6kVGQ+$oEKUwOTC~-{jw@L4cCZgE@iJ9?xi90j+2d +`i?KNi|O#xEA^g}t80Z)p!@cKmaWR@GuU3c<$_K}qa7Z5Pq-7XqiMU|BSl;6EIQO)cxg1p`wPO!*mk+ +dGUtW$RRO4*DlZ_OAHw+|hq5g55?(G`v375io@4*&(FLs4n{e@_b+8*N&r%B&IDT0HU&|KZd#2 +1ey9=e~dAVGySP%+pJ7YZ@B=I1WlQk{)_f8VaWcrhU3V7)|g&fde(9H^cMpv_c&8af}s`JO?0_bzc82 +AVo2k*#zpTqh&g7C3mcKhf{h5Q&~`k*&JG^q_p#Pt!`5>uy4NyT~-WUDt<9qTA;&C0Tr1-SlrA&rEe2 +^C?+Y!(53ilbNW!*M(xt-t)X)H??h~a*O5+j&Cng$!D;Yd|A4oWH%o}S?I1N_5i +RHZhaeLYL%%Bw^JvZIz@6C{UiIwkF+LsW-syQHrYWPG!d267AQDqeEUl7}O>X9{&TLC%Lh25G1l_Z6| +db(;7jzln7x_psg0Z|G+NGSQh;|*yRd)2^!ifdbJr0_@&fw-V( +xGE7NVA|Siu=E#5ontCBzY?K!X*;-@XV^>@fNLQ&<@v(PeAC-d;*Nr7GV|Fwau|chBr*PGtx~IB?66g +L^x*v`zXM*RZbo)d#gSYpK0X2S=8rV$|Do^^fo&x4yTa4OSD_r;{0mS^0|XQR000O8NLB_@q2U%u82| +tP7XSbNAOHXWaA|NaUv_0~WN&gWX=H9;FK}UFYhh<)Uu0o)VJ>iai;pi$Eh^5;&x?;&uvJhu)-zJ)<> +KW608mQ<1QY-O00;m`Rt8hVOvxmHm;eB8H3I-00001RX>c!Jc4cm4Z*nhbWNu+Eb#!wyHe_LRVJ>iaw +7uDn9m$pE`CdT%53H*0R;o)?74FyXrfyZW`}ONU0n_s`)q?_58a0(Gm8&wTwWkMUtlXF6z66=v_a!2@ +$C7|@BNI|Gx#Ysk7Y3OTHzK71^e2olDB5@I`~31P3PcKJKldBv=a?NkcFgS9-z@o;mzKS?T@gOa6~#Z!Q1cl7IR68?QY7y(RzSwPmlr_UOUC{_DT~=luUnm;CpoZ!A3gFH +3*o{@%jt|Knf&ucY?(?tw?o{pDZ$_{YC^{>``CYd`++k{>MjPE`5(p1<$;&R_n;bI-r9D)QFyH-Gf!>iB}kr9b-7<6n96`Q<-< +^R*>EeEk1C)AOsI?=AUGC}Tp|%nB$A0lG?BQGkL +2^;zvKS1G$@Xr6#pbBt~@DzHYiS>6h9XfSDzF=9~9S~6u%G@*Pj%>7!)_26u%S{H=h(Q3yRYx#Xk*-v +nRzb2gR)?#jga#`IF*TgW~p+;@5)W;z{xAffep~Qv60x?B)HlpxDd%&7j!J`>mkZ%X@iH?B)IQpxDd% +7eTR?_b-EDFYjLk#a`aO4vM|Je-ji-3$98Fu1X88N(-(^3$95Eu1OECNe{1uJ^V+HMt?ubM~gkm*TkC +C#G2H^n$*Oa)Wn+9#G2GnDaoXyB_(2QYhrC{1((#t+SWxEbAwh2ZzA7NW~zfVvy2D0{iEnL<~||peFqmWMY>x5n1L3+&C!q18z9kN${su2EY%wpG*Yh +BFRi7nHfnQ3xFSMqoCM}GjKFA5ohMzn&`pG{v<%TMd7D=_ogCmiRo{7@8-RCf^zTPymufd_u_u$4he&FY~#gMhdkhR73x5f6i#rC(w_P51iwMCw7a +j@EA^xNWKwZ%ZS#oo8Y0d0%DZ;KTwL_UScrx5uRBA-I!Q;2*DkxwD=DMUVn$fpqb6e6EO@74`To`~E-(L~c=!@2>9;g+SyN_4p3^{^}5j9HSoJS>In90+DCb<2&m +6>q8)Nje2}1eSc#JRK5}9yEz0Z-w5*E5(1TP1o>_Yfyy_6e0PLE79mFaL4OEjl#39d{a`QzGTKFm&wj8f1TyMHh|YdI@mffHtN$Rh+KE~f +gl9hpt#+a<5y9CHLaUu8(M7*t41}*H?-OsUX5 +yyZ)mkO%o^1o-_Vk3xHYOlzM&=6uxnI@d`Ci{@~uO@VDOq$+sp(aDBeY#jRb>;Z3~fx)tMbP25cpp6Aqh~eQqx`R`kruOGz8icn$TF-{Zp^ +84?}ydk?1HzR|sXI5$PyJR|xI7My8|G%kSeAAwX9M<(Fz#Na2=*{WMj3lX{VU7#i=H~){LbAy7{bf$9%0g+M*M%!~Bnup?$@l2=2ZO%g%q4<|#Q@93O>-L&KO++qd$MLc^iq%&6_#%SWN%(6DCI_J-r5&=J(| +X4LlW`=iiEYnU@?`v&+?=m=`KGiv+3@KI<>H0&9*A>YuHYxpxNe4BjkHz9z4LQ}4B&8YAK`Y@ce6tJw +J+g3nKp&8I#IW`@{JJ%y}mXCBHtKM(Ce5_hfY&-EhcP+cP8dVNa>RF{ajUf +&i1)g>aX*LQ?Kb%}`U^<5!QT_WOoeNPBfmx#Dt9}a=a7ZKO%`$M4eMa1>PeIZc!BI0`ePzY4Mh`3%q5 +(1SkBCgkug+S$ti0kzeAyD}u;(Gm52voj^f?hup0+lbKpx4iZK;?@l==G5hsC*Fwy*?HKm2bs=+k=r% +z=&2}p9p~r?lGd3AMOr88$W+EB>IhrR^I3hfw)RBqLm-62!U@fABRQ|G0TtFg#c5m(8MO%Y_O~J2K~A +=8-y%xgeF!)mN7z>H`a$hn+-yiH$t_|~6?ty9#tep}2@+~KPeIm%Bm+4fk`_of+~+w+sP$a;p~KG&0|9Z{tLK%l+-v8pu-t3sS7 +A9iLpVCA!&^ywWq!9J1imui9;S+R%L=;X@3w@%*W`Dr_-C*e>S_pdr~E<_5B7Ies=9X{fzIELgg|%oB +hdN#u@LC)egrx{4r{7`&I$sZzrP%U(5i%fRBf^Ss7hTnD^}|00i=PtSgOjOH?X?@{FkuY&xvn_a@5_d +l}5L1ag|0Dgxd+CnxC8wfj1oC@>iwdEypKkLf|L4pPUVWx7nYZ3xVv`tTepMzPT<0vTL(~aOchSA%NI +|utTM(%^HEuPr_#1^lkW)(Gd9V_(`Y~gdjh;5CU|@iy?sYFNMHM|C3Pq2tDUrKYm^xRQEJ-0Ta1*Tl{K5O_`890-Be#L +dAFcum|K3W3+e%~c`rnz*?-1YQ$2*Mz`p;^x{Ah*wxy*u0sSd~-`pD?(~FgSk-!y9lXmBd|uz3;PI3a +0staC&W%d`X~h1NYvO%NNqwl6IQUB@aC=%=$S6;B)qvN1Zs(~i}2=f2*hWsAo_N5e+a~9tiUU|c_0Kj +dcq;Oc_;*`VmKo=kAy%+Pek8t9t(lW7tyzyCqkg|MfC0FsSv1q5q-NEjvg9)t04MzGaNTG`c^5>qC+c +Jbh9S~q#Sl6-I7>A2#>l&(ao~!4#f&WcsJ=s^k8Dq>{dC*IyBlE!mAYCT>qY)^@9Cxfhua8g1Lg4lB> +4gw@eSCT`1YRGXUJ8NN$ETM=;Pvt8l@NG+e0ntmULT)c3xU_ir{f{;`uKDr1YRGXhCK`Mz)z<_;Pvro +;5sFU3f@`~0`$d|AwXa34FUQ`UkK1Y`a^*JF%SaukHHY2e+-2H{bN-K&_7m(0R3Z42+%*)h5-FzT?o) +W)`tN7V?zkgKY|ERqUYQbY`VI2AOtenC)jj#>re<}97?e1>ei7E$Y`Hn)77nGA&}8N!KSNQCqf{jeS% +F_w@!sXMxX?nu5O(PfsFPEHeKC17XlgW6T~8Jg_cKS5eZ@uw?dnvv4{k*h+Cnx(O5)+Sj4T+&giDA1e +>mIg%(E7#3psfH?%DpH%RJ`Z)jEOD&IQf8`_h)%D3U=dpoow4V5ou_qW5o*Um#4{I#CABOnA?a;SqsxA>3xE +A5yjgr)9;COy|?ih$Ie(4=cXDnUT%PH56KAeA5>btg3G8jwm5kh&9^bPY%)2uR%t&8!Bb5(K2~gl1L) +QV9Z5cS19(0jUH5sXL*W)qqrjfYhDP%xXX?K|tzGXl6Aal^`H>Cp5DfkV+7cx)YjN4M-&jNZkp|tOle +K1f=eSW>!zdCJ05{3C*mAq7sCn?u2GmcatU9O?D?VvwB)K!9KD(p_$cvWC?;$cS19(L8t`#$nJz@R`- +!5h)dlG&8+Stt0E$GcV!4fzE#Ac?qZr7`asRa_JOLpXRV4T)ZKv)h%T#$KiwS)f#|Y|$kW}`ArM_w5p +%k`HUy%}Dxyty*M~rKSw)=b?#2+PE)ik6yEz1^OT?D$ZV7?P7g43V+d`o7MLg;5ju5DP5lOncD+DTE# +E|ap34zKN(WAS=AyD}uZgh8l2voj^7~MS(0+lafMRyN{K;?@l(cL2YRKAD|-8~Tkl`mpK +cTa^t<%?+0-7_Ij`63Q<_gn~6z7=u#sul0@eHMxw`^Y{!7y|G3eHIEHr))nv90KqDeHMxzXKg<_8UpV +GeikaB;wAps@ep`d@Uu`A*yHxu$q;yl@Uu`M6)*kIPKUs|g`b6L!A7^w&W6Cp;yw$NgZ(z2oeu%@F%k +mkV>ATN$5;rUj|(AyJ}!m;`nVJV=;Lw-ppPpdfIhB<0Q$HV0_bBr1klGs2%wM25I`SOA%H%DfxC*+y` +QfLf!D|9;qZ+!zMuDo!0Y4lz7TkQeBK`duaD2e;T7k7KOYQ%q#>*7vEC}S#(Z{!Dyc{Qs=CRgs?n~hr +eCXS?5f(5eMVJ19aC-VUsMORT1Z;9T94*Ej3j+pEea=Vl)rJ&(=^xWUaemqyBGAp8e+2dRx$EQ`+A6x +?`-$hFw&fkxokwL=`O^2kEE2VN1CBA5lz)~Z)!c0oiFv4{#K#Cr7m_O)>MGHe$l8%`nyJ=PgOPbU{xg +1sOoRk^tWo7*f7#4)6A6GRGL{y21$uqHCvj&vL +#+?v!%6fi4WRrY35ChCN*=-eu+TP{i6r&9}U%qBxm=J9u%nQEpxPmqv!Z}&2-jmYg^D9WzC`_Yo_MM? +2+F}b-tVSh(Sno=9os(7E+xDrm<)yjip_s75yzuPoBa}zkhu6{_%+r>ZfTcoD}Z){o_~UC&UFwHF0@b +k#?Kv36iuTeKXZeH)$e0CDlV3X`(XJBNu5RO)b?tE@@RvZ)!~G@$D=VV@i+j5C~Ixe6NM%m@Il`>9nd +EsHz5xjXr)#K1O?xCZkU4+D3F?HdXtbs{KwiH*l)F|4Q`?*MNUP1%_GW5(Gqt^$+T +KiUZ>F|4Q`?)VdCkSdZ`Dyzzvo*7T?^nv@Q!>q-|(>r}oV9B3U7 +1dN=YECjey^=Ll_-alvH7A+QcC&^IIhneHndX(rG);J>`y(<<`JFXY-J0|})2u65s+~x85@e}19nEi& +rCKjdo1Liv%QAhcB|~(kOT3vnpqVb%W@^APHDH+4Opf#(^ilC#g-bKR*y)crADU}g)N7 +M{l(8Htro7O7OtgMsa28bc}uNQOAqX|)GD6TI +!dyv|G0d)uXO$w+b;`xpw|sJAba7KiAHmN22Xq`%tcZDAzueYahz1@{M^__Tl8(?sIMTxt`g~)oagnn +9X&V&Go*Fys5R(Y2ou#dt@!D$s+v=~ia~YP}CSZ8&)ct +F_LYN2eKnOM6zjmNP&3J(gBKIfOhD3AbV}bSlWbgf{%OTWT01|OF3r!GGVDa+)}q~+;TR! +NoGZ)m_!&ZCW}}{x%#%IX&p6$;mr%+53L4Oj$}(@q>W)US`lC^-le#F@WG${t#uitmi7Df1$=|J({9T +PVMX}BRqgXGob7dvJr(x$P*6bWntij?a)@X6`H~|trSMt4?>LblBn +qwQ6vvf4HNdg>sg)~{eq<#osk}xBT$y%?N?q(iR4GaEUD5mfqKfVch#nudr{}Prd3xh{LQS9%LMhNFp +$uq@5NyK63xskYx+9~|E|8Siy_(J&U0F%;ehqO(wGwI73hkoxlD}KmIeApqNqJO{#MzDNamnxL%4Ss8 +T_sVyDszdbUSewM>iM{`lKR!POV#xzR99B=cS~AjUE@TqO!EWRb=f_tYp}(YX)CYmWm>LGpKFxpt)gE+sl|)U~ZO%98nOusmv%q~&gu^z>$l44RRe`DRJ|nz78Mqo(eMa%C +mYm9*VvqHV2Na@9+-rgCoT4Zf~S`&P3=e$BL`ekJm2>KbR%Eb(%hB|ceGXN6I!+XG!$$#W%cP+HQb(~ +^-P)$_?wsyE|CX^H!gmW&{2iA_vX)kj()zqG_H$V%E@mZ)5^5;rX?`QEHVzgeL+C)11RqEeGFj2t`1f3B^Ef6RH4hA(Q}ZB~%64MyLj~olqTU2O;=deLD%k-|E{%2>w>zZbI<4`t}g&ItOcEB0k+ +WOsEaCk5B;=Cem-EMqlp`GWt3|$mr`JA)_y9N~zJ;VM0bT_ +t4nb&Ze?@vY2gW0H{323gw7Xd}%3-zu3}w@Lyttr8!krP~IglBL5a$EmKR1aR~3Q6m0henLQfjT~9Rp +CjPg)AIyyea0{m-Qm%6-}O2HuZJ)-y(3-^ +ZxHZJ>dg?~fY)*YUKu|p;Fa-90$vx?Zg9Zs2Hix16J9qq6G5lG&`ZecgBlwSc~Qf{AunoJCj0p=BHsG +aMP9kdf=>6zV5x)}qb`%UGOrP8RXE~BjR!}(=yu5*Yc6OhHD)XsQJcl#5^A&94LyBv@ma|Bh9W~nRflk5_^f2urMW{BYJgD4tMwlU3mp>_*nwDZ +*ChAuiW>}6T!W6WG&zbzd!)j9?&IJ@Pcm~AcDR}EfPL3UBQ>b1*Tj3a(KWuP7;Abb#a`K7yrl9#o+(m +I70;P)TIGJUOykR6kvcyh(JN42Kc^oRbUR+_Xf3Cc)mBNUz&?4E)s!;t`PAW`k2Nuc)fISUvBQTJ4FQ +dQ>-R}`|Z{cfyaA++9Et&x`i)?!y8^m;)qw!ZwPq(d`!bV+~JRDxQ91PxANt1hCd;RmiWSXNlZ5{xs0 +ogY$wxKRuU??z6}Xarmyr7D!IW8c}S+O3=k^0#0{xPrmqYUD!IoE*+{0ZtR_@)l^YV0OkY_`s7x{#+^ +(!ARC1vk(vnPH*+{75&b-LTeKVmFxkpCsTL_iNJu-6NMyN#Yk&*ijLM3vKjNEq-Dv^6+QQz#qERy4j;J|OT$xcaU6810Qe2r)GTo7=nNnPtQ8HbVsL4`X +nNc#`l&JYqT$xcaU6!aRQ(T!*GToP0GO=z~jFLwPmB_tfCcMDAvWdX)x~lKms?s*yVlCVB`iYS)b1>FV2(tyt}vkvrLUP08)bj +NHk-YgTSoX5>!xT@!P=G9!1g@0y$2l^MB{eJ`1UwrfW2WZyMIw<|MpC;P5Rx}$d8$erxFZc%k*M($+a +HC4AOGjb>UuGzX>nUOo$cRhXN%8cB}zL(6-+jS#%vhSL<+m#u)lYQ6B-LA~Yo$Pzb;&>bDsMFWTzL)G +9YGVm?`Wo5yMv2_9dOCd#_MKd_MNcUmwe?g?)W-Vl^fekx8f7<3L&?`@Flm%18JW(fuhC$lxz3|DlAT +XqqrpTIK)Es~`5Fx-dLTb)n|0)CG?-}OC|72bOoNFYT_4-CA0M7DMDBlWaF#HbX}R3!d +q_IFcEW2h7cV(vWk!9E9;jYXyK +Cw!#HX5>zmUDJuXG9!1g?3z*Bl^MB{ +W!I$QuFS}tEV~}6i`r&Y^%_}rO)VZhK5F#%@o}>3trEGnAop>y?0Q?AD}&s}$#ZX&$h`%*kCU<0>>AP +IqfLK)oQ!R&MD8ufeVmM~X60~Yko!0p+g6F(Taf!W8CyMx7`1aFcQUq`j@*?Qxs$QgL}aea$eoO>9`t +o(M(*Uf>k)ldX5>zuyQU_O+PRTCdG4B>JTiyOrpL*1*97HJJ2!GC&s}qrNA0$eJK1+lQ|`))+{wOcYB +yJAF)_!9+8ryE3C>8cZ~4x+^nErolvW^tdvkWExD$qWjC1;J=g^@cACYnG! +Dok8{Vg(_rKToVAgio00B?MP*qK^>1WTKxC_8d(N5W)vd3=+bgqls|vDN6RM7bPcH3KJin*hI*D|7Jq +w``;#HzJCiL^Zi>1neX35NOvU{CA-)Q69k@khmiS_1BA?%93*7E$79~ +6J3ln&qI8VrY>j)w9t)qm@w~i4q-+F{f+8ttwI%>MNxc@^hNdT}*P1x!B= +uVC9Gae_UTeaklhkXmcW8Q&dTq%O#iC^Id!hNTT$%X<>UdRMDlMv}?$qNnQ$kc!O{J-;m7H%Zu+e3DG +IX?xlCuki3Dr$fFRYo*qF!hsc9YZ#Yv${y7n;D`B=y3Y`AF)8CVn?by|8A!m3m>xxs=C8%oiRHuZ?#^ +Pga@J=i!y{QOVKs$ecY7?~#ulA3W!$v4iIv5j~-34w#46%SRn`UfK(Gc*uG|23I0vIBhWOS0|HIsIVjLHoJ +I#)}N&U{o)4sF$wC_xx_MO?&zO(hT@64a}o$aT6XYrKpEIB$@m>}rnd3gaN-}y)vl}BvJ@5q@$m{eXWy1uj!EPafAPd@KLu=LMwtrBP +yb%FmLu4nw@vM?y>tgsSAX-X(p21UIqV}c2xPSPv}S2nVXWdf;_G>O68jqGV5oLcgkR!_g9Imkj_)kB +(vw5+mN>?KEliX76W8G5bh6#dB(iAR-%*fx&OJ*Fok=0g~Ro=FcWR)9PZD +m=Nq!B3EkQH^uc8Qa1qLc5^WTkE7LX#B}q@27)sH?ARLY42*grsf6vx>#jZd*v=X?yYf&k$%1KUZdY^ +aMBO!prUE>h3=AqGykH74hcd<_4)`iTNl>ntNf)huC=&tem_;sO!AQge)f~2tnmE;X&Xskm+M)bWTbw +Y=i!W;LaGl)$lR6H@X|&1k7zxl`ap>|76NYzpY&IMMMUD>uFN!>0YVb@j(ZZXnt% +EyA`MUI&n|@49#@Fpk+?zJQ>}{_MUt9~%awK0H{rCF{z3Ye +@+B*+Z3pZz?qcUSf3u-exrjc#G8}kUkOO-xBcV>cOhF3HkQ+0|MSyJww2^x$hG2^Mcnx(C8*}1bZR^e +%Oi$_)c6Q&`l$aZW?KH(@3M6MjG8T(&(m(}}+IWe8*T%~Pyf$7T;I;880j~{ehlq^4LBMO{EdqWtUrs=>*Tg-VUB{LAO1?=ytvj9Ko|br +UaY-h|xTjD97{Mi2`VY?%ihxiHa`P7bwg6WT-;lV1z9IegJR|YoU0LI9Bvv6@8>0D{^ICSkDpg5+Utj>6<8Cz^1pgxQ317 +s`f-LzBhs-1dw?bN$!r`}CF^{(2fch^q+DcY%b)lR)cJLF$3`UMrP7G0Y{y?axrcWVmuZcU-yttr&IH +3iLU>B{7$5?5B@+hK0mqxMtti@P!j#>bH+2#sSn91m8Va{q|tA=L-e`-tIvJXm$s{R?TOC#3p-j+Kuy +tyie5?76l+r`vbp3U&oPT(OE!3bdM#Y*CGqSdu!&iEd?cWs=M-PGGqs0^T;n1iWde5b%Z}A>i#om4LS +kH3Hr&)CqW_&>-L~LX!Xn*&ZTZ$`OA%hFAW0-&7<1df!YV{(Rp{BmRC2sU!fH?1&@)n;nq|V6!8V0Ze +vGGFUL#k0UwJcM18q?K3eWKev6R!pH}9pGg?`$nG;$Mn1IrOpTEbTs>1~lzG%(_tGnUDMS^hI)p)`!zSSk(ZEn=}Wpty+T(tze-mPe7FEfH*UtxqU@hT&9iPspROT5kqUE&Q!(AUox +L0@k&g1+8j1brHZl&6Sv!0Y3e1iU_eMZoLh*95#i!VqDScUf~ua<30+_Ia03d4hdG&k^wYpq +7|>eNaopX-aB|xz`7^#Jo!wD#^Qqp_05yAT+^dB5H{^R!J=p_x4ds#K}r(iMiJYwL~1Qq?XwB`kU6;CgIZ$S>w{V%wlYym#6~7+iP*+OE~%chjME;PA(l)7Q_VCNr*Ln-|NS2j +@?*mfnAAWRX*fPbGkI`o_;*(bYNay0UCG96=)$Z;e+$%31P#``4K`f&?q77PdPtE2%A& +RUm#Qkx=0ASQ_f!^gl!t-jbk?<(>V4JGL2&|A=5a937N*RkC5(OX_aj3Yv~P3ajRxp&jCUjJ!~PmcHGe`#yL%P=E_V +5q*1J7_--Mb|5F0~lYc?LfAMu1G7!(dK_dosv^^N55kvEg#pbG}2ctApXr8gy+|u-5l*R~6{S}+5n;w +kP;Gn6$VsmTLgHak4H1(G&gE1JT;XqS=xiVORQ5pv{^_MF{_aCJJKvRFk<_f6?qttLU^;c}}j(RXkCS +Oy3#pcqe2cz2vmB_tmW+QJOSpwz1e;+w@?RW(%5a%#|6r(=)+gNP +waC$HbD^G6RqTjp{w=}g`+(NYO!59rDn%c~jLCIq@m}qJO4NpyV+cOfHJZ!9-J=xiTYn8cZ~`nJY7Lr@^Eow9?9r+-We;)Mjz3ZRAdaiKaGlWk&8am}qJHB7Doka_!fFaGSEfOymS)F_^H_sUdC4Z!yyOs6uIcFG$G5!Eb0t%!$5;K(bMW&YTnyEd +mu#rW^^R#*rdrRNx?LhK*)jk4-Wq;RCrFQPgQ4g2@Ok|BmKA=k9zN+tmOAf_VVX +>$?xSQzn7Q%o*sscA16o;pKF)wKWgj!z;V08v1}uI!|b`x2DeMp*T$LB`KkScv@;ZlqJDE_1)&J&IH4 +Hm5}^vvyM(+&{w7T1?vS5kesgVrPz`7up*qkzgc?BC2>F@)Oh4Dqo9UT>BZM-bQ-r*=o*C>Vlml%i)C +S@o^eUejqVGZ!b!O-yA+N8QRZu0lSc`sfiK~Zi95ZY7P!)aSm|1(3kZ&9_>$VZ90_`SL1G+-UH;$S0D ++x7#_7XzlSbu^L8pj4wGeWjA8>l}ZWIMBQ6(KZ^jfV)KaqQVa2#sTJ*ue{gY-bKplS0UL<}g(rA={b5 +YY0JKhe;_2+0Gn3MhN;kGDZmcIKcsAq0J +0rZ$f~nKPHkreH(n%w_sP92c3nJVgllx;jb-`npP14<|=vu9DTm*^!y?3xuGrajpjRHL;cu^c7~Kj7_ +4%nMrB@NR&7eW~z%#qQsd=YEd{yGV^X9A+N95Rn+y6C~}+Gt?`Q* +m3p@e;4$1mKs1}{3FsA&fCnM+e!%fI!7HAXKiNLq~M^kLIqG5u_&-;)Y(GgGWKP6wj3qo_0`$ +BlTZbS#!4(6b+%IL#s2KhR_YGeq21X|jScHdo$YTEg1&ZTB`r1t+5muWzyGhH~s@>VcZ$V +%CNtIZ2>g=apiRox(KXpB!>s95~g}2~*O>1&-QurpRf-S)0xjH7P94&8;Ax7|U~WD`=X91-iKv)UvTcH@AX58 +B26?E2)hl?ZjL!nEin-O)Hn5mCw`)Bi=xY}ZDp=E-J3yW^QhUxFpijoq-rNC_JTgj@ +?$4j(8bV*E$koH{{rOW|L+I-?&3v$ffBrPh=&&j{f0`^dcJa@jrZEMJgY#!7R)wAX^J7#EtP##%r4Eb +T{PR3-$5P?^HU2^9>l)1vuwpnrP7`MA>YpE{;RXwb^W)SVu(N-Docb5m59i0p6~XTQ`H2mLpf3&|V2A +(w1Whclnm9i}pNw7p^OIy?v8XsdNrO~wBIzUztS!z@ks-ov|M@Adp)4|0x+M~HKvv>hnXl~McXM+W{f +-@13t4BYm_&RJLl?2qqgNPLdQ5;QjLkY^0}-;N*lu6o$JCZxKqQ|T$p8)Faqf>=!Daq_8ur7~INu0)lR=MZX~?q5L&*-QIZ62iXB +!IOlL-n+k-5R!Wjo+gCU-h*cdA+>0KA0Z?b?e8aqWU~DOgpgTu@H`W5FzNRpBj(3HoN}-A?T}rH6iG$e+?m{ueF4XzSa>k`dUv2`s&|62>R +;ZNC^7s-$V%d>fcNV`s#n15cD-bf-)Cu4;&-}eGMEU1bq!0CIo$jOOX|m(Pw(#C?V);;20t3Yv4E`=x +g8vA?Ry>y1BV9CtMM&m@Gci1JupUy*UHa&CONZ17`_AUjyd|L0<#Zm(49Y1Jswz#oPnbmup5})R)cu+ +ym5?%|7)3>dQ4*cdwYtJ=6VL37L+yjSv(yME%=b0W?JY+gt%OM19U&0W?G%%Ul68w1p59HMD~e6g9Mq +km*=^2|-aq!yz&^c@OO;WDFs{WenjyLdFojL&z9H>cn-UFY3hRdhdSfb9G|~sbiU2zWb^7)Quse-eWH +N?x)^UH-?aUkGb=^pL$Q-7((g?=IZbMON30vBA3BjE;N898hWXfMZf09)x-2+YV_t7qaiX2<_@DF5{J +3LXn-2Mxl(9gCm~~XsXv(Oga*lVF_#GqlIvow5*nm_ZY~lUq(RMGBQ!{Znz=-1kSvk8LTHeBk-0!>};img3uvtb +8;xd4>D>2k*kf+;ml#OA_yJM?4`jEQw-VxRf$UG7g4)pkSg@K7!%fK+;ri)1S11`8O8?oF{~Ikz%Vgz +kYUxpA%-;rS2L^|xRznVz;z6p25w-O8n}^RX5c1S%)e8)v&wuY@@kP+*zpv +w#5J8{+zD;Bceg5%~h!6Vwd`CXDZTY|N5w2!7j-gCvQ|W~m%5+whVQk*yjuJze&ayI0%+Io>V< +>ZG;0(i>`I~GI;<|bB6hkQUo2$bNq0E`}D;YwWvwOMG#!zNwrTk4ObM`zppg1=_%S|taCg+E^TjL6iX +J+>9Cs=_#XLlT72z}1JbCO|fD#ZG%K%bq%Qw*WcnN8~%LZ6-0R~bT|bE5|sLZ6)@tg;I9IkSdkTY)~m +89&4j`kdL!U919q&YU^Q5c=%wk)MS=znS1RgiAl3nH%E~r(*QEp1VWRV}!unAu;`Pf}2SKea;OTWs_s +Q=ywIVnp8|p#<-f;Ww>o4A$Vt3CkSEJTDU{bA#9vD%ReZa^eZvKFmEh>o)9uSMnrJgMN9-41S>?4RXH +Kzzca29K|bXg5&xBOorrfq;|3A$fyPZD-T|%j5b^$Jg=K*3$t(l!eO6co-ubMs47~4IAsLvs3k92F6L ++Doactr)6abD*+=ZgNam`HU2ML+!JheC*epyYZYg{v~b&YGrCj3Id%h-fpD0ms0@CyYmV-tR%;AL#WF +BH6t5q@zVA|bCX=O2a$;TPu}67u?Tz9Av6FXtH&^7{HixP{UouP^5n67u?TJ|Q8mFXs^w^7?ZAAR(_W +=M56_`f|P?A+Imz2@>-9a(*D87OHXO+p%|udX+gJ@sGp&uGiWhcb_LDhrB8=qAxtBju8;yId$AHGjVG +22#D~UIz~Xm`F(_7>nX?^H_S|&x-tSHbi~FX%*3w_6EX{iD+!q$%G3-I5TQM}4q=D#d1@L6h|rd2hp= +cpM4htH?ZieU#-=OUlw2Ai&&_eKaOHU@VQ!jAGYh +X|QS*BU}NE=Mt|I5qu?#wcvMnz?+G5cIW?#EVT=Gwa3)VL|@v4nok^8U9;lhcbV14jG~^laIUT7bUrR +U`O5O33x +Y&aQ-9KWywu$o0d%u7qXaTSUS#@C_ni8F&>XECa8hD#-v_x*{16u%|0l1|2Cw`gdi>0Iv*5;FTc{yfU +PMSB7lx%1lCdg(fGwLX#F=p;z5YFB0*pd;U#EUU$#`f{54ME6bk?5%zaI>^(>*0wO0B`@0_Yk{!bSu7 +|y3=CQx)VK0pv*x&WAmmCMAka*ZjRv8&xANHOj)BqYGgu4=Y$tokE#KYc;gfbu+MUhwHVK0sI$nN^Em +&`oUOFZnIBxH6ay&EEAnRwVoei^d7KI|iJ9?2#i_R*XTnO=GOQ;b}%5Bq4SM7Gz5eKZ~--|NFZ@`#b~ +^LXXQF#4h?N@4U +xK44+=MZK~x`l4Q07=2N%ER4RWR~ANJqc7^Ph0zyHI18gM>dS@E7xm@B=!=}n!sv^-d13 +T5N(hU1oz!^hCikr(7F**n5DUiGydx9`@~K8(ZnA1kX&cFe^RAW*&BZ?a +}Y_o~LK3!82oQ87e)-gdTm^d%`_K4;Vvw+!--vP8TY(njUM|$J_(G?!zbO8Dm{fF4XoaJ&*X9;uM3tb%PH-#qnI!m~GeoN)b&YjFL!{%VMvwI!c-+mT14MOx@?4p|Bhxg?|6)@fSom|K^2oH +#bB`p@JF(CN`7^WNu`>&mUr*1NW;(LaTc{^Qrj3p*NP7)E&<-AOe`1&(Fb(wLqk1mXIeaGWvCk}6hq7 +K5o5p&1p>3$4iA@t4FEcc8g&!a_#b$_FQ6`hv3{*qxX;oq~R;|`AHN%x^HZ~*H8g?@51zLR@${LutMy +qe*LX+sH2h0GrFlKa-K#Lxe-!VhlriBMi>zA6bZR?|v!IW>hM{V~5H{(~4;o+aLXiYk|GT^escC+(csWdEoWVPR>2b%V; +JnFWj!ActZf)0U|EO@{*l`HgsX(YQA9(b1?FpXsR$*9j#HH~ENLOuBb(>(TwFq0npCm#JiX*P+1*Hz9 +*%n$INFl}Ss6Z1uuHc99i`72fXz@rwpkshc851it;VkM~u4~#7sz++ll(=hfwG5h>X(>}wFOOl54NeA +aIGR@FDdu|`h}`#Ax9tkxb%Q&AtxUjRsE4Tc;*CA(?U)?vQg}fRF +mL=i3J0~f5Nno(=zZ?YXN=Kj~@1p>bICiQtE+VO}VGT9o}5dtIRiwSQ%E9l_9*R3>Wh%^Lno-^D3_k! +y0o#nOAgE8Ez6(!C;*^QpsSYIa1MJtvOQJV6{0?;b6TvQt4pDIa2YM>mN(xgJ@rgfDr8~k&xG9Tp}T_ +%D6;AUYBu+guF6il@L~-W0errpkI1pDIu@T=a_g^F8w(Xug9M(eUp&a;_HZ6(%fj#?>lqV@a^S|XQ_g +|u{`%Gkp@Ui2$#6MS|Q|H$ugD_)^@Ir&92%+6w9YO&q&CkZuT-C<0Y>OR=a0+ZkP?zP&Awj*!`>^uO=Zi{3%9WO|L#!we2l} +jR0~jy|&ndV7>cgL0(<23c`hxg1o-oT>2cLwm~JIP!Oh%z*_Vh^d^>~Ut?nIAR}mwiC5ekzaZlE_8OB +K{tzSRjS0FJlV;aqIp*aL-z*0lYH7mzgo@J1HF`-Z +Y*WL#l$y-x2Vr!A^VpsQ{NPR;Tkq;*0+YG3fpGm2<_9Ww9Up5VP@lqFtc$)nAtcY%xoMHW;TupGaE;Q +nT;dD?8Xs+cH@XZyKzLIS71~k8?VEtL^xiJQHgZC8Y2}CmbfF84;Hy26%dxWBb5*qx?`0Pmbzn=5Ei> +*l@OM@W0epVyknIRmb_z?5Ei}T5(y#1Qz9X+&$vWF2o01-2xkaNB!puGB@*)be37O&NspXPZ59iEC@^ +zrk3N?s4Sg=rGsB`Vsc9WITn>5o}(B@|f$x7)>ny$>qovf55&U0l(?qsF(!XsDa<$h}o5${IaT1P~#3QKE+OnK6Ji7!ycUXgG29w +MZotz%=zw|kEeQqk725#-yw#|Wut>)8A8?cNiFRJ3*M_4s!0DMBjRI<|CtyY~zsZPImY-uQO!IYQc`> +lGu&F+wGRtQbLFBvc~EiV@^xLM4K%7(reoR3gZV5#%_b5=MthZal|fGT0@aW8HX;!(^~aJjc569EZtZmw1kK<2eqK!7lL}>*kF2Fbp<1W{ +Z9WoU2EPlIuNXb$g6Tl#G?>Z-=iDN=qKk@c0BFxpmZ);hg+M8Xf9JhGeVt#C2LX&iX#G)g{h)-8k#}$ +X1s)>viL-?;~4X;;h$=v%ZgPwcbSU${@6T>j;$y4U51xs2?@d{i+)WcOMzk5(l?#9Nc|mOiLWxy75@| +k##KbSnI}P-AC53#AB@+k98ke#}bdV4v+QQePkUq`LioS3)@H5QSW|F>!}eWSx3G5-IWO2jwS8+(a(;oo5CpuL_Z;x%yNB#{yYU@i6IeMEfB_+# +pBSWEqwS_GC-zdb_@Ju|u>k67=6cV$KwBWXn!K++PWrilt8t(H7j)%vAX^)=F +#+@hVPxtg7{(E4S1b%rY|drqxh)}wOCqLSZ>N`5ca@6F<}=hQr8amn8;>13Iih*3Bx!&d5VPhTd4@$M +86zrObEnYTk!EBW)-SN!pMB5FCZny!jvCZG3)cL*T?*oFgykN|AMK|)9Xw&4&VBmmoRm=F?xZ8$;*Hh +;rWLP!9%fuwKpd2cvQ2noP8oFIe*U>i;nLISW2q%@Pyd&6l$nAmJMLkKCGH=HGeeBK+vxmjlNd2cvR$ +mnZ?kkQvDA)~J`LPlQ~2pN4{BxLk;iICA3bqSMVd&3n%MqgJ68GT(NWb`#o$mnZ=kkQv9A)~J;LPlSq +_mpAUHN9~KA?R!4No(>Q3b%uM5;!7?+Ag9gjYG!7apGt)R|u*^*3pusXTje`cu%rp)fEHl$MXt2yov8e4~OLGPwXLb#AJynV_c=6WYjVlj*t-nYJ`a0cD2>M#ZpNtE7cb*9m(pk)3p-)B%iz^(b;p15cCyZ#O@F-z}`vUg)It3>=NDwi9hTlT} +x=bboNg+B1*AYT)&m&}`kO-o4=^a8yx4Ds;C^A3HuOp>kmjvC6>k#bWsqKW2i}esSY$SP@nc%w>I5li!W|+Oan-GraPGH +~&UDjmL@Az`{z+icSnlH9;bgq!3kn3SS+^p;n8pqnV3BmaG@>@u(JVf0R2_0sJsH1;bs2%UKy4Mo@x(m6Cv2p;DY*%a&xnVBM|4S5>Axk3Xowuj7)Q3F6Mb#@zhX4oatxj;P@`59)y +aEwE6ZVu4chTIIDy)@on&&b?nGQP;mFn5y1Y=m)V&(iRKFz(zGDFtEN*->(r5yqWeODzgv+_|;n>m!U +iyMgR7!nmDn{1)_e?JOZUHv^<)gmGtvNKgpl&K@N3B8=M^qc)E)?%ZlJUv<;Js5>BxJ3mAnwr-povH% +F<&TOW>j4&>xR(A;YaQrkO=xYZ}R1n6UeV0^;FmC5CwJ3yf=eLt5jWF)~34ROC%{A)g2;MHqMHDovOX#+@A`-_cwQu!=0UxftMW8ko(+027A@!5JN(){8K1= +WRyN*D-Qa5XS9Xrtd-+cWxy$Y=m*=CXN#F`kLE9y%J&E`4#+KUSIR;xrVTZtEg?5iviZ~4?03PVg#Q>LR;6NC6b_IDv2;+7(&>RL~+|FjQ*r^GoP)|V^cWxzhbA)l{S5d=875C8jYW^f(Z>oyXFkU@L} +xxvP8-6wv)d^mg)pvox5&)U8KRK`nHf5}_$@@*won)Y2^!{CGJ?LQ$aloHq}ko1W$a6u-$~NIzNDEoW +K)o*VRo1d2ev28?k6XpWg<7^+#prM>^17K$kou<#MMA&-bPA6wua6Dihi}sWRC_*r;41=12l +6J&?V!JftO@hG$&o_Ng!vI_hsc;Pe~p50$d@p?kKcklyiDDpFp+F(8_1N=9ceJEJrKL +ym;bT5`>iA)(Vt76ADX=FYHq5y>9sAH?K*9W5duGs4WOP$?;v?`BpF5rRFuc9anM*V-+FFnJi;P6+n! +>;NINQnZf{?BVVUgp55LCxrfWU>zY7{8~>4!LOrh388VY13gg*>D@W>HD=z&K!62b^KOpO?8f1S +}OLa>-qlZ0gRaFNDlk@jsuCCbKX-`uGygi4f+wY>Qi{5U+qKB}jlGj?SdbPk;(gc?5N%W?C&!;ngf<+yLA&JuzV>pMmWGwK1d16Yomp@q;C%W*T|EvF8lf34q42od4!!-U|3( +;{Aq<+zz$BttC6%^V=Zg5|iGgZ!3R-r>r_h>frevAol}l@KOP1N^s4#42>pQY^>K4vi9mQ@4#eC6?o6 +$p=fZ95=h0B#-5|*@HCbU^#B~BDE-U&GZ#A#8{4-o#1M~9uANUu^iV~b&3#7)F%ExvwX_mg-O$4vO`# +o>l~rhi{-cuZ9+-S3fYm+-%HI3*%2~_SRtc}JsrY|%TelISRv~irM7_;vd#%=*jOR!oLor=HaB#J5F*0im8uS5S!HwR;-**zn>$SU!ZO(0QPLNd!R +AiWcVQW9?le^c%V2Y3m1|~@Do{6L5ZOY8NW2ge8@uyCZG5GFoHXyh`hTqmd+W|iwSltOOpqF-+ +1>S03uFF|Nlk`-bJzC3JA}|z*)uRpY7} +cv&0X6YsRdw3ZFbXMLU2$vlX%UN+BPzvW=W0bT=uSQGS*m9o87yI5F8YqYhX!j_6%8MEUEFLdWt1A4# +K8bQkxy;YCvD(H1)-jns_`|QtPZBe;rF|;tXL)t+S7u@}}88Nw(4~sjZ=T2$s}j`idpB&gKz9a8S0A= +Zz(`&Q=;uu%y=6Nd^&1YMq_?3Bf_(S+rSF+e1phl3Is$P^VZ@>kRV`!a*6PF#t|L29wGkE+^mUQG3rlL^lVM4%GeLeWmej<%!YbF?dg{wq<(k__%?7JnbHmiXu*x-eggOycx#o^iv%xB +tI9^!gnmb9p3s$*gW{*{_If_W8SmhE=F6+AGD#a?-+$kCYu*xO=A6B{M&QfE;Dwp_wW|eD#zRRw1k)1 +cIT!S>UVU=rsH93@Km1`G8BCyIO4kcE(B+k<^z9zL^ta6DPidC-pcSv7Y{&Z*~`EBM7?6YLtu+a6*3i1%J(Dls(JxwMTY423oj7yLV95t5ig<_ULjJ!uklL>5WnRm^wx4BaxACFa1i6O3!hygmKb(~ +SkV*E5{J_+s?)FyfOT8vDgTBEBhnv5AO +p3SVp?;??xURw7Qb=M( +Z~9#DR_4dIGP3d6zyBtyv8u?b>O*k)l++1tXPv@OD*wynaTxNX9qy6wVB`Zq$pU+k3Uy>WBRof&kuTN +re=M;LUsSD0isab?igK6zdeQxq9Oyer!%ZBF +TbO@sKte3M)Vy;G^k;2D2KP7A!)uc=kQQT>`)5;k6aO&Z4Ls;`Ick-LN4RbPkmjto1fz8)arb^P^eB9 +J|~JlMB%kKggK|MKXU$B4DSzTQTx4R(*d7`utwdn7^Z8FCCVB(HVR?<8^+NlZof18RnmD6wzIeK-jdT +Y$a}O+%~4h~&%D#3IAa5HsSsH$be?wI;0|^L6*u5c7`Dm&c_pHN&_rb;HgQYk)~Tn}%^cQ^UBPzDs?1 +QtH_j#`Q$AJt_4p45NBti;rVePn^yF@)Xw-dwsq<#eWZzLdW>;C5G|egZ@tQw_{THr?to$bQHzvQC8^*ucHjH$OdE%F2{P!?V{PH6ICT5CXUX=Hsze`+C%oZKv-%J +dndSb@-mqhr*MFmb%s%XPuT@m{a|y{cjS_iBdm-}Cyr*GEHb3lm7kNM@KoI>z-(4P%*QVE6iowG89$% +MIi2Ya7Pj2mST)H()~P7<~gKl#bCiVE53yey%4bmX1+9F|l-v>WK|R_Xeb%HN&``b;G!x(BFX6vuPOD +Gc}Cs3H=R9JzIuxJ#)jjp3vW*)Uz;*>S-pJgIrH+Cb~Do^)%DZA^v;VPUIN>y~Hs7d(ht+`Fl0PNG{k +?YhjnupKG +##tGTex>CbSZz!-3ozzX1I7ER5#ykCwGs~R>+%sBh3dTe-dY>-&PFlrVxJ4v8sQ8O;D)H5@T>xm{KE; +e>OImY#D8^-l445ND1jmyjR#NH^!sGf+>sEdu=QI1hPu{}!NZ|sj!_Zx8^=YDfN>xOYXp+9lWu}8`=u +4if(*E2JW>j{%BZc5#_O!6K~wzwPEDW&cPwo0kHfxS}d8X)qet^qbMscV3^n7Rho!lc7EqGmdbV-u4O +;|(+9%J +KO^(qwG|iC9-_SHeu6%>BpFC7Ijs4`Ix@qhu57kW*v664d3>zY5?5EVl*iWg8v7b^GYd@(jh>V?VhrskNV67i&McF2;V!d(fYR8nDYrLk-yGq@e~xdo|RMnvj9K2mMJnA+;d`e$R#s_! +~^ffaQq@vxXBg6Efi6#C|8o_&1?H2{mLkWI*4LnUI0}y_#YC_nq@#V}^}aI=9EpE1lb8=atUwvGYou3GBSmIXV`YbdHX +_S2{<><|}nHu=&a{`gSZ^={y{puXG-cMJ>n3(a6pGR(>-xjK8mC7=Iu1Cr$_!#2n-AgRz!@zirl6_&s +DC&_NoDX*x)^?PP`Dvx6Fn$kOye99ZhVgqCa +>wL7v&tgxVaUB8@3jr%_s|Q^(0jRAT5=Ii)|CRZi&-!73-eXQn +Ljo>}FT_so<<-h=+6KbTcc=?`X=)Ak2)u^0WKHCK_w_qukz*mid6C}v4jI-glmmCk3DRHgI9`BQ}1eu +^-QQeiGIEUD`EVM$fojT}p=;!@;TQk5m@980P)N6)dODs%K4OR6$Q&nxxra;Y@B%catMid@pCd;|2y^ +Wq#!s$U+vx0aZ0YtIw8@v_LPHs;PzmAq=BMjThkt2Q>ok(In^99D^+vs&1VIkz_rv7V>TqQ2HXXB6L-Iidhr7TJp<{fYKM$ts}1QrUB +(zWdY@1HY-3Sva;(uDl5CvBeiM-+be`d0i`;d^`3s!uUYZwcm0T3yIt4OFA7#~5Lw3{GA=Vnc2P$gtmWd>ENSeJPew +vu<70p)ki`kD5CW)@I>*Mx;iz9?8}kzY33Gu6VS;mApUH&}0xUk+AW#Hmh$Fp#_(th&g%W~*k&yJoYd +>d3CWl>D-p%9PX@HteGIk2IK$3WW{3R6M7jZ9MZUWub+JtK +)sL%_%pL*M-6T%X7r7B>H73F*oto5$9&FqUd!V#9;d+YMdhudvBF{pQG5hiN#)&O)2?8G`0e|SZ+p?MsI%5a*yF*9t%PErErh%*eoVlB@-qbdCx4e +f{R#MA1R77k|0d9cB)G~c&^AID&?Z7Hpv{DGpf!ZrKx+vVK;b#7Hg<(ST(O#vX^-m&A=|}@^@J)w8we +#p8wpjlc_dyFKP2Ea@goBMfc}!-4G_w^w2*4I?pW>K9jhhXvD&TIReckrep>C;>#F|4sh=W}Bb}q_Kb +;zS)tgFc=v8kDsi7}Kb;=rg)igf-^QQ4pL&uirzX<_$DE}t{es}VJCg67_|1AOE=>Cp?*UEn-;I;CP1 +iV&$(8W2fF8-~1?V4sdXxDtR|F?f5;+6j&2zcfHM*?2?|BZm&RNStct>k1`5M8>rLYUb{PWGd&d6(Pu +1do$VscRmBcD;tj$Ai;m^otbHlf12Mhqd +SFAosG7>oN6RuHWP-#?Bt?B)$FUo9T$wxh`&~!BIg*_zU%%td1f +4u1NWAR@R@YemW33y-pZwPn;{~rkWvG_j{@HYNG5%3<;zkP>@x9`so@MGk633$`~p9%Oe@^1-vtNuL# +zM1?T0q-9DJpu0={X+<_52S~HZ&ncj?~}&_ycw+!@cwv0z&Fq;0YB@n5%A5nPQbgY4FbL~Hwk!se4l{ +V$A2N<_3>W`c&Gmd1pEN|j|99vLZ7^dy$|%?2zY%gCE)e(69Qfz&vx@L7yo*@0vku3qw0C(KTp6b{{; +eG`7aXi%72M~SN<{re!~1y0^Yv8Ou$c;Um@T-S6)I!Lu! +2dQ@JAk}Uiq}r{6RJ(PMYPSwj?bbo6-8x9O@JfO3%KuBi_mBTg!1s^;kAT<5gQKCBTi}w52b62M!0s7 +xMrtOiw~mmB>a8baqIxR{dCffR8z)o)8YP6FbMFB{2s-y3B7`Vx?-4?X!uB2`geYw92||d%_VM3BAgq +u776M^?{I?K=?F;8I1rB;Y?3*BjFl6sRLeN+5VM5SX?@>a~SMPB`&{ywCLeN+5DMChHX9yX6og-xQHA +2YfYmAW5*F{3mSMOy)&{ywOLeN()HBp3G9`=T}eHDnbJnVg!5cJhY-2vNt9`^MUg1-8wyW&vyL%I;VX +qf)BhLGuBYY9PLedy-ofiC{#a|KS1KkOr$f;iH{z72%XIQljcLgVOrn~>?u+X$J?ypxdW%zFr#&K%y~ +P&7?v-cQIhj&}%|#z9uEX&MKafTn32WCEI|agYgUn#MsUplKQh8N{a17g>j<(HB{VrqS1FLPlR?IGRR +ZWH_2eUt}GcrZev%WIA(rLr#&Fggp=vobF#iC3;GUuqA1F@I0Xmh-@`BBTWy|IF4}V^dKn;+mNQ|zVia1&}q8!slc9~>3-5-X7ok&Gc +)=cA_RTWEj&eL^hMnff!FE&)r6q0elp0J(H9xy%;<~!uFU9*Y$SI5O!t$G#Fn4wezK9+?=#)Mi4gSFz +nKv9)lZf+)7;esHt0+bknpfOXL^8a8Mfw350Jx!81(c2nK^9AnI0ft3_EhB2gm}JY{w~ZPDERaMnFAb>o*p2Bi8IpE17r^J5=)gES(Ay#jjYcSGO{N7nj2Y@9mtKW$qwX3)?^3 +r5?SX))?^!UBWtn^xsf&5hFoLZ1x_UYmYNz?2YyQ}4X2TROTup>^VRRjs^Tc}Z>gPOvEjGWIB^X50na +?zy7*h*^znnOG=5;&;=v$wX`DTNFi2e*M~@#2QkTZb;|GKPe{WyX97mEK`&Z`3u_qg=%&dsaJiJ3zWu +}?_fv%$&Nu#ATOV4PeIdLbr69mcT-XK8&1e-g!Z+&@(cVb`JIu1~U;tfAR38Fha+T-)XJK&jh9~d5}i +i*#_SllBc7YOPp{nrb$fWdRefBlLExF#ASlEGjQoe0`4jO^#_!k?bDqLu2dzfZ1fK^jG5yRa~Rz-L{I +Yktb;n)d(5mUI?sHbn!m8OC?M$qYB%l49sAlxK;+o=wVklG@_YV49Rk0r-r;CIFu^*aqMW0+TSFFBuHu`HI0Xp061UK|JRVG8n{j{t$yfJm(KH7{qh_2!Ux3&-pn9gLuv#WiW{6{4oZD +c+MYZFo@^;2?m3B&Yxs3i0Aw%27`FcpJvb)v4++tIJ9PfVHLe5tt%V_eYbFt!JzLJE-@JN-NI!CgT7n +1!eG#M3s)Ho`flMGgF)XdTxT%oyM?bA402&%k-;Dr7H%*Y%9*9!VR?z+2y*$1i{D3WrurXRJ1JZ`Jh1FDk5WF9ybE76YR{i$U}h +%ywaA)Xc2UwDwI!W__l$yDZ{Y&}|nQJzrS8R5ltX|rAM}mv+&wJ27_66Z9jv-E +WCDr!C+Ojc96kfRke1A!C+Ojc9_9nRke16!C+OjHpgJFs#-hBV6duMJH}wJs#-hFV6duMJHcSEs#-hA +V6duMJH=qIs#>FMTX0Ex2cBUtjORRqVLZ<=7{+sf!7!fZ7!2cip20Al7Z?oV`4xjDogJC?cG8o458iQdxuQM3NleUt4C6_=yKyLitUY2djOSwp!+1Vn(3tSX))YRrmY6&Dxl ++yA+St_A#-_Fwj*Yd{*m+=&Yg!A(#uAWro+gWG8lqG)vOaTUO!locSGLw~Z>`_HbL5q4THn6ojdM+F8 +_*hyr^Ipy6Uz+S-jW(MjV)zY7%wrq^-H|a=?bF@1G~cbEw){uVKQ}HAFn;OyWXhT#{HRCR$P)~(Q~ld +h2E$+jH8((#-ce%a--)HgP%##82y=icG9_KYqT@9eo;ynThPLB*I$#h-Gb;*V{l1QBWaVgiFam6Y7sZJh-*2SU17Y}>;vWVm2^-QI^ +DxYgcGQGfR!0&r+l3v&{O;%otoU!|Uz}tzTqJ6nmNM^^1n)XBrH#oZ+s}`t61e)D +;?oOXgx9(m3b@fxz*12GJtNlvsgED@$}Ob8toiw6Aab?c#^>;08cU42Hdp34k|<#opPCX|L*)fF0xlyYc&yER#r)?`&0$1%D>W1=oAW6@W}=aaV!t(uK7s +aDo>QCa&h%d|+U%3z3PMI=>g^rxkJcZFt6;}?w$Gc_3-hE1~bT5hgs{i3n8Oc+~Bxp913^LC-J+#Xxe +92-_dvh%(@s##mTR9hKW8&+;oTN$_W))=m7_2-UQ6xB4AGqqvmCbe-;xGOZqklJEhZN;dz@~E~JeLFY +eM>UOn-?{%js%iap+AynidH#rNT3Ot-#xH7(86n +NexSgrB(Mg9s?$2~+WL#Gmk6@{_(MjDfo;xy2k;{f@{Ov=lokMSo^WIu+`^uO*d}SQ+@Rj@Nx6=Y=*I +!c8wZKT5t}qT>R~So*zA|J=Ul}_}Up4VS;44F!=?ded^_CayN5-)3N5+uR6&k;2!8`z07%T$t4ufR?t}<8! +;9Uks0r(Ar;{d$JU>$(>8EgXZ0fTJ-J|xhE@qEN!7|+KHhVgvDU>MIe2E%whWiX8AGX}$WK4&nD=L-h +Ic)nyXjOQx`!+5@CFpTFL2E%y1WiX8AI|jpezGpCu=eGn}BV$v>3mZ{v0r)&y9LjVEOaYZ+VA#$tbDefua$;)!}>nL=G*yeSzaK0cM#6~ +@14lo(5ok@eiu$lAV)#y*bw$l5@3h4EHnG&Ytqqp{(>jK;>2b2K)VU89|Y5M1*!m;ROu<8@PQg`*xAl +XPwQesy9ze%KYpyPrAjjaM7rMi-Ufw(Ea~_DN&Dm~&#nNjI6R~j0Xo^Yj~ +>Ke2V-1QflbS*H>;B|%Z3aL&4@#@;)S4I>90qfe~*H(p^Kfwr7AZGpWDORP{_#}>H@F)U4DXJ8E!F6K +z;I-ui*Qv1-t*u8W>$Ej;%rYM99si1J8q-p@aW~o-Jev4RM*0l+dq#TN|B8{G>%V5CC-^@w(yu`Nk&% +A+@lTBOYmeVB(l0#znUQ|g@h^<@OOAhKq-XKJG1Alb3r2e0{yQT*S%1q&&(Plyf=3O1&qyDiKQPkA=S +xQV`23NPK0be9q>s;kFw)28&y4i(`A6(;%ZaoDDF08j^c7t<|uAAWsc%{Q|2h{H)W3Ef>Y)wZa8I*;)+w|C|=RR9K{= +2n4@?>3v(3jXJL-w^(@R$2pr5&s2j{tNE^&i=o-vXh#JgMC>qRB$QjI0Xc^2=2pP;#s2I#qNEpmf=oi +dUh!@OJC>P98$QH~|Xco*-2o}sys1@MoPnV>A*Iy&D-He_LoDdL2I3b{ja6-6vOWYOqJm6e_2Ew@j0f +ch_>Idfnqz}#o=pLL45Ir~-pm=aDU<<{$fV~sv0ya&Y3)m@fE?|4axqv+p=K?lDoC~;Wm2&|%t#U5lq +E*fX+_TELfNNGc7jVle=K?NS@%}altB&*SKGmIg0C5nWMN}l{t#bRhgr>Ta`JA +t5un!xLK7sii=g5qqtXl{tz_Rhgr>Qg;If@6bn4@^=iaCl$u9%~E-ikSjhpm{Sc+!eFipQ*&qj<)OIf@ +6Yn4@^QiaCl$tC*vBu8KK|hpL#Pc%q6qipQy#qj;8zIf@6Vn4@@#iaCl$sFw@@p&Q*H+4}t(0F|CBM3Xl3(3G$**ppUfn<`uWq1}S2u`H0Ca^)T6F^@t-67dR$Zc`RhKAf)g?+=b%~NzU81B_ml$fA +OO&eW5~ZrTM5(GSQL3s-l&b0yrK-9_sj4pd>3W=2oV)%mk?j^J_0}ayy>+QlZ(VArw?80CP<5#iR9&h +BRR?$KH;gD%)ul>Rb*WNSU8+=7mnv1&rAk$GsZv#4s#H~%Dpl2`N>z24QdM21R8^NLRn=umRdtzCRb8 +f3RhKDM)n!Umb(vCCU8Yo3XFNUJ6)Hj1WlB(WnG#f8rUX@IJO*F8q0&;_P-&@dsI*i!R9dPVDlOF+&$ +f1jN=9|Ysq?N-siAJD)KE86YN#73HPj828tR5h4Rubbq0V?lyDL;esB=mPbxsMP&M6_(IVFTTr-V@Fl +o0Bi5<;C*La1{}2z5>gq0T8G)Hx-DI;VtC=amrZyb?m4S3;=sN(gmc38Bs_A=G&#ggUQ;Q0J8p>bw#{ +omWDr^GXPHUJ0QNMh|n;D>c-4rG`2fJiFh^nZFh_GGwYpq +Mtu9wmtIL(t>R|LRM{}jOx?Jh4u26cbE0o^q3Z=KY!q8i3?YaAJ2kDxj#9db?an}_}+;xQ#cU@tKyFV +aGZ*_2$I3Wrpx;nT@oDhXlUR|M-S63+I)s;$lb)`~XU8$5;S1RSz!BygfD3$!`N+rL#QpvBbRPw7UmH +g^TCBM2-$*-MA9_x=P8fu2S-=tCaleDkZ-l>F){C +BM2#$*-PAX_bt5Iex{;D!-AKu=ZlvT_H&XJe8!7qK +jgIkeKD8<$flw#`#O0o3=rP%s`Is)qxCENN$$+kXG +vaL^)Z0i#x+xkQuf%S>fZhfMT!1_cTf%Sk}pV`b3GoK2@TxPt_4vpDN| +or%L(tsZxG@s+3=!D&^OwO8NDvQht4^lwY4J<=3Z5`Sqz%etoKxU!N-F*QZMP^_fzBeWsLOpDE?nXG; +0?nNogzrj%cwDdpE^O8NDfQht4=lwY4I<=1CQ`SqDneto8tU!N)E*Jn!k^+Ton`k_*O{ZJ{teyEgRKU +B)EA1dY750&!khf4YNL#6!sp;CVRP$|EDXeht5DBbzz`5y;I7s_e%NoUMaucE9KXFrTluY +lwa?a^6R})e!W-9ulGv%^vN_ +2`dlf$K3B@G&z17)bEW+HTq(akSIV!?mGbLzrTqF_DZf5f%C9e!^6Lww{Q5#EzrIk)uP>DH>kFm)`a& +tczEH}qFO>4@3#I(}LMgw#P|B|_l=ABfrTqFrDZjo@%C9e#^6N{b{Q6QUzrIw;uP>GI>r18l`cf&szE +sMuFO~A^OQrn!QYpW_RLZX}mGbLLrTqF*DZjo{%CE1K^6M+5{Q62MzrIq+uZOese;zZ`2k8I4V8}Rn> +tSAfh(4;1!G|Xd^+EWs#!w%H4^J8D!|>r5Lwy`RJZGp6#E0k?^^y4Sk|3B^A8^?nElqd-JuqE6`oMp9 +%}wkh|KSZoeds^DWvGw+hj$G1vH$R%p*}l5e9KUup5N_ds3*sF`xq*9*Z1W3?f^qQIleo{P*0BU4l&e +|bjbM=hc+EVYQf@YEvu!c>ds3s)`TD9p2nqj1k6j>0~RI12wP;wTKXh@ +)`OB96jB`|I3(#`G8q7cF7{Y_urM@X?|l1|u!%-$qBJ(OJ~!ENXNXH9CtLokfk#qDE&?qqC^dS=8t(Y +IGJgI*S^eMUBp)R%cPGI7?p1dBcq0b}~Y`HJWZ>nox! +yf3D24)crYo5THL`sT2|n7;Wy1}ujG#`MkMfH8e@SYS-w93B|cH-`zv^v&UdF@1B`U`*c}J{Z$ChY`l +~&EbSGM{^in%+VZ<7jra+<;5J$;dwDfbC_Pt(HyQ9b2NwT6-@$pVhww=_}%??^mOe21(Xc1&%BDJHQ=PJ@PT5qaY^qZ>)hV0mc%?dJQ@Mu8;l +4%Loa$`ObT(%?n=_rwna<`+XLA;0bM%P_7}FO%Ttr_OaS?st#6|Rl6&KMLUR*?9m~jz(GaWG4aS?st$ +3^soAs5jXj$A}vSaK14;mJkxg((-&7p`1HU)XXHec{VR9EJTBaTNYr#8DV<5l3N)MI41I7I74|Sj18I +V)2aQ^sonOEMfq>u_(+i$D$twcP#2(r=#L@7CD_oPG^zRS>$vUIh{pLXOYucEe7Fs~EL>jd*U!F-Tl{E4v6BKpERi|7mUETS*mvxvU1&m#K5Ka1 +!K11+L&t^)=OEut?xw1~bi(IWc7MGNRlYv0{}eN)#CWHaXqOuHyo;MzsG0^2Uil|l+JIgGm~A>hwN2? +2vHN(eY~Q9{6?ixL7JU6c?o>7s;yOBc^Lg&t#J)J1fIQx|cnkScQyuP*Li9hFk2Y^hVW)G1r)lr442m +O5oiowB7)*;1!$sZ+MpDO*}8`vau2sMJ|h>MSaCyegeVm0rWZzl##A(g{}S1gms{RXV{conVztuqsHf +=o1kzrf=Z!F?|D%kLeqDd`#cK<74^;9v{;;@c5X%BOS27c4PVmwj0wou-%xxf$hfh4Qw~2FKoAnzOda +Q`oeaL=sVKdNMO4$M+0w-IU0Ct%+ZlF1@gdKV@APSi#Q5vE#fGwwRjR3>tQeO)|df-x5i-}OO-hvOEW +Yd>!`q6`|cV;#wC3ck5r0?({BYl56M*80NjP#wIGt&2U!ARfLB_oCE1Ndc8A5 +^jrMzRmavJYz62aW85R`mgVGVTM|WZVaE$+!<-lA#Z1ef$66tyDP-Iqq=yaopjs4_CYQCppkvhiaxMjn^;$cF;rKj@d~!ex;MM3j5nB7qqt^cz0t2LjBlFi3gb75H-jX8+gMi +^x3d|f@yjKI10e~42}cv0fTh_K4h>7z()+W0r;4}B#h@12E%x+F&M`4DT84=p +D`H5^ErcIJYO&v#`7hEVLV?k7{>E8gJC@1Fc`-3ErVe^-!T}*^F4!MJilcyi0AFS3C@{xvu21DNW`xru`o-x!%>ihi+AySVS>W8|IPZ{dt_3VVchWdDY&umYPwM~=ioJw_0rSW@Wy23t2-ydPf7 +!#UQr%W2ZH@qv<$&%`1Np-TMI$2VkEU8YGR3}TOlO@y1lIdj0tPo^6O){M(nNE{Tr%7hTDANgI+*RBa +>eR@LDYF@BP7bXO8ETFVt@aN!$E=M>Gt}&|ru=58c{S9$8fsoyo1n&NMmfzWXLX>{j56N$*cEERIIF{ +*CXBTbYMdsFvpU@AQPf$>52wdZZ*{fT%ltLsg>*0%B^_kI<;~=Sm%1M&UNnPR_^6G_j0|O$#n+idNq?9Qc;uZbxa;#F5fhT-W +wD;4-1`#h29$!IvWeULn!nPq0s4C=yWY~x)#>>Sm+6<&>2+d3@UU66*_}Tok69}pi*a0srLq@&ZAQ2Q +K|E&)Ol3uy+Nt7snpq2>TD{l`K;1BuQbmq&GX9I16P{im1cOQ8D43ISDN9K#qdfqywVJ>G{Y;+@Jcg$ +q!~Wa3?FHRk2J$an&BhO@R4TtNHct-89vesA8Ce*k@coOZPjG5m +KDywD{Pe$!v6iZs|U>vUzyF#mGZPl!;nr-~Rf17;P!m{mN9sCVL>5otnLD`Q`8bQM!p)7*jkI*oJ{Ev +`}p!`S3M^N!2lmnuL=I*~1s%uC2V>^ndiPeu#89}iT*CwCEu+R1P{PO3F^|9LX_Yu|yYtOGDtdG{7Uq +@IUu08({VST*z{9}am0o(IW5!Od+&u=2E580l7jQAchGM_AO(3iNk`ZwgRfmYHs5(SkMb#nVDyj|=S>Jb@;huo&``5bv7)}8{V=R_)+7x!!^ +eBH9{IJRWuNdpUXma`lV?E*54;*2vr~Jn2=4i&-^+cK+J;_*~0h9O9Z|_rJaya_C7T@=XFgX5`xoeE|@!Nd1pRqoEo9Dh}tdHO93Hp;(y1V} +ps;(hDStd)-4+mM!yke{;%k=Fz#(J_$kDX_%C(HC?g!N?Ec)6Fc3fNJ`da``lcZ@NR<;5~%H9qq`V<5 +|E9s%IyO;2$0Vcju%#{G$v$Mom|V<5}&KE`^oY_7g$4DmZN$51*y!tU;z%xj(TAnO==Pvyb2AIq +ny%L?%a{FxJO!a^X2+h~FVjOvCJJQ!F03wdeHey=e8BViD2R=on+vNW&VhPZ%|_pvL5Z^%=vU#^(N4M +hzFX^yq?7!-t=7!F&t_3b-P#B$BTs#zNynOp7LIdD!jtrMeNp;a@nY8uvbTUcwf!thAj!irH +*Sf5#1HLVp^F|ulo42G1}Z!dGBKg-W16W25zr|Alfm1E_MXjay5ue|Y#s@$rX$3|;c7@yJZ3S*P9tt> +88MST9Zt*oA}Dx;SwtLLkc^_j-m=dRFLt5&sD(>SNxj;v^oO5-y}pXF4pIW~G}gl8uPjP%1%!bm@kr; +PODcg9FRZVwsh$7{z(KTdl_`tdnuq#u_HM*67|&nM98Z};E!)HNd-CA!T~9NDzmxk2!!H7jE*O-DOd- +TWOD#wYsIQIW)-IV#e)=6J`!`a3F&`JXnUCVJI%`opOE!$R*JV+pXAjP=~wc(so)9Kc>Yc#}bF!QL%0 +)>CZb{R754us4ih3$}MZV?EI(`(87KRoVVajA2!FV1=;}u+xn7tLpW$*BQgP>&1&vW3a6L=~8bSYk{3 +3414Iwp<9e$sdDHAV>m;@5y1ia#q-A*L_65x^5nm|$mnGHNH{cXFOF#P8%~#*l(1*9gPTc5;#jQ +S59dC% +rx)nvlleKu5Q+Iagkk$TImCl@#lP|PP +6n1#^HCtvX_2+@3U^#Wsv(M66QWb?^Y&NArclS`aQ(9I{8o)Q-9t><`z3HH`YyaK>6+{xFc7(j+GHvELIiLL!#S;scm0J^+YJF*ZZ +TVMaF`_YZ47L=#hHl%#FN{M;kvf18<#oVaguoJ#+ADaf-fr^2i(~%E!yWCib{t)qb$m6 +5;NrCI51Cy!6FhkqwG^>?#9ofwI49_HT04QuN+qK?5x{F>KgI4(SS&B=iC!jsp${J??X$s1nJ;l%LP-BV`?gcCe@!$ +ScM4R75%!IKou2G8a=DR4M=@|L>|M}XItc=E;>;H{-&yrRP?;H~8&JUQST@Z??ejYy|=OkkV@USHzn7 +w%7+ypO&QvG|r#6gQ|%zU3tku27qN%iChyp*G#mJ&F^^>$i9Vgd@o7w|EY#e;Dqw@=rzU=zhj)bIHNp0&T+>f<>|?&Ey%!|_c%y6r#wBuLo5 +y|Pml9xh?C0GlRRwWy++ehJi_AgwdrXNAZ}mVy7@5r^@!jZ9x8AF+w?SR3All6dWz=>T*0<}2lFQ_u6 +O;FS=$W(UFM@kf-dts)Z@mt=~<={u6&!$vk-(k-_}=oiHu9%rsrAY#I0}B^U>eo&WfeIJelF8O4F}+z +{XpZrdN3Oz-yJJmsruky>aXJcrT5c<0glBKE@4llf$g6;SRa!RbDgV +7P;wVo&<1@+-(1S#`*wmoaWz-yX2Khja?@+9li*glt)5l8=rZ+f9xL|I2Gx|Gt7fbWp+qn2`dXr}>T>Um(WZ4auzfEuPh>h#t)*o +={;sUtoZB}$}1>AI*GYOZ#O;>nVjBDVgcX`0ZMQ~e7m!l6w3Vy?s!EJEsk9ePl`{1Vcm`-ptx0Y`5&J +xDv^gj0*tj+bu90{14)B8M+!`_@e;3&c1oIYTx!Qz}gV1>R7mc4g*H3^?{>&8=7m|%5oEqxPxC4w0LL +~J9Mp74~6>*A(Qcw>zV3hZy!)GrTLmEEY#~5PxY?U#@@Yzeo5X0w( +7=!<`^z=G|SU5d>${5n{-HO7#JZ;msD0KPfGSTK;jnP(6YeDR7gMDQIqG`NUs=^gh +>a1qzi`%?@ehTk4$3}^H9a%<;2RmJbH#P%!0Qr&M}DC9ox$oVt4F3W5 +~Z-SJlh8@?C#9*LF)VV&7Uk$IS#)PQGRgR5`_28$9*%`W|B={2em|X65>89?oD$ZqD;a07G(fo{0iOa +&!JMW8lPDW@d2Ic7am}M{U>Na$3U3+&p)SF(O%tJ^qmAjx31Mld}vWO6MOj7D{_xGl-17$h3!( +xpnLP4F<;n$bo{FxpjNrK?bop`t~hjeH1tLa-zY^+`Phb63oo4+Xr}Zft%TZPZ&gQ-#&PW!C?SKAB*I +^%7Zq}>TX`;E{CDHd5u{GLvtgVBw%Q6vN>16(A?O^6Aui{jr}}H41++OxyB&U?FP?ua5Oh>l6X?W(cD +~$FdU*fz+=4&;&gGDK}?^w`B%6gOqVY+h=jX+g)yt~4nHA%un+?d94 +=&=yu|>v^WNW+(fSI_t#vFv1xcM|{3&z9S@1tLj70$EhL-F{?30~U6Xxx0ka~h1s&6gY|7>%2+nAI>E +H%{ImIa(mWXxv&p%B{g=LmMY~poHPLwR}7R5yN-9gn;9?wS0o7JXnsK?|JzI%W?DD=tKJ;&h|! +qgCH(1@K^!MaclV;M-iUmY#-ANmgCm)g{W6s{2@=yMX=X6$uk8^$Bonc`(ZlH_Vb(u({Xn20Aq;a8J< +O8I?fJqECSQo#%_6P3h*7AG)iLghvRu1s01^(#9dG0s(qq9rA8-qVOyTl=eKRUa_RD? +e|yTsZd{L$Iv2ty1n^HvP@=IeIxosb*5Vho=1(c-QcwWoXXh^9$(;8ZmmR-gH<_O;z0yfu29E7 +G&Q|7EEDPZmrzru^wLK#${eqz^mN2!plu~m9x9trSK|e-|%n_uX6T)hY5IU_dBOyQSvh;c +1cX^RTVu`yX5~{>iC|XFA~OJH<<`nu-eAJ5+*(}Z^$qOG*$ZACz^|BVfpC*56kc?KON*%75vJrJ6D5rVB4d3FKON;b!La;vnnxiRmY>crz2R4G-Fe8F55w})1ttv)%Z+9JP#Bhr)*6{{DXGl;}qy2)4{sEvDTj3KdaMPGb8cz +QEzP;5Ikz4_V{N;$a{Yf<*8a`Ue({T6bi?!i08mQ<1QY-O00;m`Rt8gUZ+z +P60RRA$1ONaZ0001RX>c!Jc4cm4Z*nhfb7yd2V{0#8UukY>bYEXCaCx0m-)q}25PmoKe>mJ1J9zHc(+ +K3DY+)N?TWHgVGD8J*J9|NSIOPSzH-buondrBB~?cRGVwHC%4zus7|S_i@x})j_d&Kuugp; +9ak@f#6PQe5|oIHb7Is?36N7Z24%`LytMEd52_J*T+#Dg`}dRNdvir^;;V*P(E<2WU)n)Cf}|;tu8)% +-K?%Jm#drg=O5pb43u_fia=_^WvhO^y@kaB-p%6(?9X#)nGhALcN3wNwNdA=m%X*B9Ii(LbsV7}3}=l +Vf^|aL)E#a6e}i(Te1qW2SiIC)=@*c%Am_dYTX24=av-CTbvevty93i($^v+jB;IgXpsIVU4V0<`CL1 +_tZVA8f7>4~TxNcz{N`*~TG6X$WxQ>%-K<|X5V1f_i)ntf!3m2kL2c`URKQX0nX(?VC_H +5M-=PV4bUk&VT-a|#o&e@&V_ksr1#b%noa^Xkul3C?J|pjKVa46nH0W^g-1c5emVlal8h({#tgdC%*e +K+8cW*4S%3xOeI3S7T%MU2h{k?;HxA&He&VO9KQH0000807zB_QFJE?La&u{KZZ2?nD@!dZ&dkqKuvO47)KM_dQ83cvFJ*XRWpH$9Z*FrgaCwzeO>d(x5WOSuA4b^=k+ +S&#k$S00q+T|an)DjEcwDSCcI1zw`S&}<0UTD5b~%9Yym|9@el&_X2M2v(&F}iV!IPIpSL15ADGQ)$u +~e>>V3~Bt;nGTl5PCkTzt&}u2!8GM_2#w@9lIv{L;%LOXu-EkgA1Q&juEMOCT|V! +D&T4YT$uLY4K&O*Eik13tu=O)Hd6E022wkZjIYN}isH&5&3X<(pxp+gs@b@~gA>8>j$QCO@xna&*M_a +J9rJZmSbZ=Ruufthw3EPBeP&8>=-gLtoah;5%HMtL{two@F`$vW;3Lxyc)^oJvJE-ed!VD4xCZ0OB5` +Jwrwo>Bs=vKunKu(E(=mcTX8A!4F$x$rsV)`82soj}EbUt=P05k7@ENVHbi<#nH6AKWH|H&~j)<0tA? +5Hs2yw|#!0{-x^v7f6P>IzIihM;ANzNcYUJSV>RoFV!?D)-O(Nu=vGDCf24gVnG|eAVZYb{dpt8 +j`qrHy)qdUx}anB4e2c8_k{m)wet`Bzat?(!+y~#oZaLQ06k!@7~Me4^T@31QY-O00;m`Rt8hRk*VAc +2LJ$x7ytko0001RX>c!Jc4cm4Z*nhfb7yd2V{0#Ecyumsd8JrwZ`(Ey{$3#e;pSq4lsc;H1Zi!=!-lj +SI&49TC0mE0P-vO9waJt#QfZv7`|Z1b9W>kLzv7Wu^2<9=10#D9?jEag==xlLlq +@LLbO^ZsRF@Pe5SH>%nu$FOEadpidNNv+{o&)O0!TMJfeS>e-2?nZ@pWdN|YKzVxGM7-@{}AL!yZ0fJ +EbiM}SWxRG>C%E|JN>FMWs;v2gR3L8d}nzeoh?<+0=)O!zrpROzO8ZLyYSd-Urb<#@QBQ}MfyuNC~LR +2oe)#b2grlI|hFsjbX-teE4wZti^;Gb!Qaow`EvoGOk^uUPB#p@&)EB$xGEw@}#Yl?0ITb;>0dN}`Dh +$WL?{^asyhWlQl97;#iI6)v9Y)S3qbFsN5%Qi*+hFqe+R>7okD+San@f3xqLtDzpMNa5fN2)R3O+_^3FjNNOhmt=gG?gg&t +b)Jd4}Q>(92BBAQSrIjZt^w<>E5T&H~n1%8xF!xLj2svJc~VCc^QJN&=mO8!fF^r1oHzQ%?G44{0rZx +mrdN@~}nJS5}!~)0mIpMWQpCV0TC(OqS$fBG4O>Czwcz@16;5DKQ9SoG2LtSd{O21sRo-#npRXuZ>7V +3X>{%Jnxs9`nH;hZvz#zy+)3R#nC+nW+g9Axbcpj@tUP9TCSU-A(K8?O9*Ims%-On>;Cgixzp@PhUGN1{(JkL*pXKmY8!Z~jF`MaB-xNzX^ku +;n%u}Xk1&cxVz@|NS(JEU}%15J7Q2h4*Uhp}~VrmxP*Q~rq6oTTSa6rqH;7s{ENt=;!H3&_8Ar_f5uA +3M?fpsDyy(FYp%k8e#Wx!!(%Yz-P)L6C~%p5ZFbivczZPE($m*}%I)X>?j($VeHW;=s{4~K^^>Q-iT( +)w#1z)t!<}Dq&$BZ;7=YkW+{aLG1t!e-he!$ezC)F|14=g`wTucLt_KyP!9UmIEzod-&eTR1Ij; +T_7>xfQk)Qees0-s@v7Fp31elX0^=P1n(97%w-#>3#DHZY`!Ro?Ge+^)}>JFL_*3^t_>nHj}3(9eUn$ +>#1}UoZo?e=jYGytK!*ZTEX`H_i%i&)ylzeyj4bjM{r($&{o@W&Ec$k_8BQv{xw)a92kj+^`0h7Ia4J +Nj)#3XJ;7p6VRU*ljg$se0UEh|vJ@5BYT>o^T`TT#BiuDXE(wc@HnuI?>N3=f``2A()fJ_o(`S_)ig@ +|(kUB3~W# +PWWdx3vHH?EDR$F&+jgYS8o6oaVT01Xch2~9GADs0EYwk&-3D#BHOh&(!g?8(-<9)svzKTh~>UvTRB= +M-_8~@Zic_zH?*uRVJh!Z?AGz5UFsM$KH3*Q0deMW6!QOLsD6#mc_J=!bONb;3t`9Pq3uR1$A` +1%c9qOZxB3PbU5U;kuz$9Rhn9A_y)EGlrnaxOE%=u +(=`8bQcivPA51%#fK61U6%mFJ@tE +Y+_+!Yc6nk?LBLA<3^6(QbVu8-?R*wpgZ|y}!$^l9)yF#f;B+oQHV?x5<>vv&#jPYPDKlfA# +fO(=?g0Ntp9InsY3jq`)Yq$8{1mGXR<#%$6n-**HSe +3o%f8}M5^T*lEjnQ+83PsO9#G>k9t3H`kf3T5*^q|E{L6KijG*;7^dTXudqyJJ_-3B6uVJUiv%CDk}e +(c@-*t^Q(=dY$?=ki&x#0k5m&$yfvE(K;A?+`t*@UM>r7V}dNlFz2giD~5D& +{w{D9eK@5=BukPwzD9jB34+fS#%P`*+ZSY7!LFz(D{(RxfgPLKR4K8bkL$JO6O<_HV(5vs3u9e}3}%@ +aPl-Q}G^4jAvn%70hcVqu=>B*ZYh56uKeGgP@)9*%U--IBK=@GNh@XHP$1%u!h1Enl^B3qfBR=PJOLj +xLELb0tL--nw4nWU}2mTWjAOR6j>gUG@Sy`ob!2--hGPrja`&lMuXJLlM(iE+rid>hULOE4ezRe2|za +8RlG3?yX5(&aJCe**$qe?G$7Gmj|KLN5DZiq*EA(c3>gPC3`?m}xasNxFlOO=GznQ2{gdyq-#&_#30h +BR0qB3JI?L&lr^n0Ltjpdl^LJAi)50!0y3Ny2{5($5c_>r{tpS+k!tyGhxq=4O&R|sOG8`45N9GIB`q +3}JhkrYH56JcD=HT61p#1#cjFH8KC#Oeyhr!E5rwEBHyw*ec(=*?V6;-$syUzrH^TPTssZIy~7sI +}(uT837S+=^m;ISaf%~_p^XNcl#%A;qHmNJNgCjd5X{vkB;}wU!Ota2j{0Bj)K=GZ%*jtU?3W$=)YVi_!a$7!qDu9nmj#aaL=52|O6(h8sor)NWt{yx>H^6_yEP8uILX0_Kp#qo?|eDSqfOK|CM= +*D=_KaZLW!@aIcnqoe9~nb8`n29Fw17iFC*O%O@y(8DjK^G^)7l!FHRA~j9pw5(tL5jZmxLDHbl`4Hc +r6(1LFbX0~8UKp=Z@DX>_UszoU4Zpd*=cyPl>vzKX~zFeTAMxt(_XQrD`{&XWYx9|qWoto<&(0{g7#I +rIt}anW59Hd&(Sker5J1+PAE&UR_g+Px6$<>>{Rg2R;vKs~Duma-7Yk1F4O3!VvB^YB*6T7ZHx#L-9~ +-qHHhgwmUWBcw|C8~_j)XsIY3h+k?nkfVEHz-r}GO3yTmW_+@%bs+p)EDI5pFfI2J%&)G2(Q3!6L_#; +7#+yRpO8PF7JRq4w{cU#~f(nst182H=MQ&6y^$q^i`r=z%_PooUbxhAw04F2ThVgvVWy!FwqXa^6bi0 +^E<0vO%1VWePBs?>I8IJE{EY#Bg6>_AHn@%Zejz;Sd$WG#pqB?FOZm}SU8p0bZZW@Tfg_wlU~$J#iGhbJ4L{9VN*O5JJFNzH5*S9mN&3$^bI*+Q6o%2py|woFdzH1JYkqEQ`` +0s-ud|xEG%^ylH?6l+vK<V>Zo4PBezZ$@+=`0Cz#@ZhM*BGRYqzg!6NAKSqyt4e`EvSI#vG&_APO>IdwxNoeFz +FRDC#-#bcF=(+QZj5yiEg%BpchGFncHu|!wTl%BDm&vZWoqJ1xpdn$mp;n6@3Sy#aTE;iw**hmI>fM4 +S?8T04}qe9EY;3`S3wKjjo;aF2!P|55nSC8ef%=3hyk6g@Y%wKcleCr}3 +5{)hK%%&7(Y{SPuCsS_`9G8>%Kf)blwHp~0<p8rIk~j%?34#n92GF{k*h5@Z=SA`7LI^$ZgU +xIsIX##goSt7iISosoyG|-{4^oRQ_BzBHAZG%=v4S$nnj}M3!{H +k!HOytX)@+n)=t*ME=vGd1DsqQsM28YpcQnnER>))y5Z0O_>tpJZL%^OTb5q{(FczAP}nm=Hv+t`&Bt +bx2-Pfjs~F{H)+0?16hY_z$=Ly{CD+67r!i|?GMOfNHNjQFJfs-`JqZZWcsR>37cEinq4POj8?u)|sy +RW3Rc3(*ATE6I*vm7S5D4|I4uC1p=p&@IDj%n$Z27ndvQ2~c0&;wL<6i2nNQt-=NppXVdxvXt7n)R&% +h*=b5x11HSB47yA!4uK_;FfQ_prmA2QdLikoZ>GFeY-0IUmWczSebHt$PxgAG|ANqDz7hv0F0l}31y2 +Ui-eN~m@m^5{4(%eX)?hFF^I&>xdur@1%<1R;ia4;2+`}~DKeuAoXKxE6vWt1gabJmo7=qI>FUt%y~J +IbwHDZ?4C0+hiz(wcyX%q>=hBzNDWmXO4?VtRCt@t`O3!lwVuMSSi$yfMgQ5E@0Xc?#$Z&=KMw&Ejd4 +q+yjn+5;{y_LOPiTUti`B@v2THCvhxj^07azI>!)|Kp4$Iu|)I{xgnWABxCn+$7Y_UnTHpSS^uR_Fsm +uB^ecNy1V6{rKZi=m~1k7E!aL@iC^W(KVhc&t&R!vkZP6ILi4*ym!^Vv&d`A*eCQe8Q#|vqVH{TXoce +m-QBxVA#e6+s1i^fdNZ1a<~G&Iq*O#-rH~;NYW}+LMq;`gjAT(N?2NnmC&T+t%Oy)cR2UskGOhefs^* +S5?t}#Ma=GOx2Ni8DTZ+N(qG>@^{vmpmk^YR&E{Z0P>LaZw;R{Zu4u2UKh&_nDV->8dr|7L!81ATy3j +&g0>c@mHvDv6o1TR#X+Cu-Ipd)Dz$q^MA1o+;I?C4Wv!90A)F0ez`&SusR+r@!tNXp)w#(A8oYsb2sB +ndFjamJ+3u(R<6w~f13YMN%S-gB^S5mGLlvO`{(rj7_dFK^-(-??5UqM^bi+f*TQFSG-M#d@YC|MF#lYk2*ARxa0;pp~Duif;O?@-4%)w +B+ERRA_8JY$MHZ^?{yr^ +qs9vUCkFYQjWsJ23PPOxKzT#@LN7!=6tB{-PP?2jN~{^YwXD_-^M}`)h&3a@Bn?@DoZ@jI6ktCsZL{+ +J^f*5O)`zxUs!)M=!*-dO>DsxSdH{~=Ca;kP}VE%yZ|E6$-A{c(GyJBw2fj(*QT_z8vWP14A{x0jZXsmIdc6@x$oq$R%40J9CHq>#UU&{sB_`QM;zIE}&oG2 +>$cu~A;NbJfkuP64+-uo4#x(oA-36)iSWXe}Akx19=fnO&~RW~YZc`IngAe}7vspRt*^_3~A#YjCt{@ +U8P(e+5^!{;XVmc5kjeS^=JBY#pI}-Su#Ie^k%mb%pk|!CrT#Q)BkZ +ss(#rcU`zE+55VRy*mYa2hZ-y-|Z&+efjD^4Bq~;GWbQo;O(vZGWcmD2A@2L!B79J4Bqp`OI4;-z_mC +xeLUl8ptfI&+D;SHCJ#hyrvkNuTGVVWN$-sZ`AG&2hqXA^R@=*-M|3s})Q%o5RF61f8>k&WT&Vv3d4% +fF4i%aRm5JI46Sfkw3Q;?>_fyW +lS1FfWf1MzjC*l$ +@N8{14xTr)iO(Ly$iedpPQIv~#&IU)|G2ruFKzZ;@Xn6Kn)A*c3EOeEv$nhcu!+SllcjbH*_@@e>*#0 +ciZJD2vx;9POYPXVe!=OBlhZ$B!Ao$?j@Z}X+!r8@e<0`TCltN_sruhqTeQT+SoAbFRzJ`21xVAE!7= +CHoM>D-i;C%yuH)n;W@7)xE^zm|TlCLH?zS{&s4HDAPr{()TSvO3-&UGrwE;!ew>Qkl$JqbA^l|P1LF +CFey8UU?7e}`XKE{j$q_p-KK*@)A6=r4nx_Z2z+I7LwHgrvaEb(y7B-~@xLemmS?_TUe%(KPH^hyJ#dHFg*t?E!IJC +0dW8!cFEB2SFQI)AiI^-rENOD3{Ev|(BsqJK6s=LzCvXJ|*Etv>ffb$a)b$({!K+3VyABbRa;o=4VDy +Ar&U#dlSXR*TU;pjz?Vw3-i<5cPH27;3+_%p7p$JO2x +W4k<);D!aO4I2+66)t#Nbs;Cj!SDpA!3%CPu)m-WbQ#BKAiqd_#!e}lIl*X##d0~*M_Y|WPc3 +iGFLpw}8*X>DwEwM-a9)2<1VzKfAxPF-?$DIJ_BldP(%LN!Ds_`gTFta!V%%%{OltJGKhSgD_K^35u! +*gtveCEJUqRrlzZGfxr*c^;Wg*!%O&;g#dY@FFi=Pn_TqX&#G)>x4x+t~&It#%QGEm^vCcb!(@N7L4} +;^({|$fd+9HC+k4Am&v@4RjxcgV6UtgFq#_P^5l;?yoVdIyqzF-$?GEE+3BqdofZ(#hXQ1t^NPbqW_d +cu=ax5f!b%8hm>f~gbJj>h#xNB%iDg&AlSyBb#R)oH=yN%M&Fr$?;LJ?9jn9!e7`{p_>72{1qlLq@6& +{L1LR{^!*Q1#~UeN1%)v@85jRXS4bgys)oom8YZ1iZ|gY##LD +qU(R?yiCQ&u@T9GH#V`e4MBfw<*<1-3bRg+gPaUWZUb-!XOJEd-?$??)#d~!=PUP|AAto3Snb#*uh%l +QTCLE*mr5U@R!Q;Zo#A_uyR!Jd&i&M(*MG5>a03@cAW_DF9h+0>cIMxcTy!IkQBuf(-YI$R$uwG-hge1(^c9|6mph5fODw+9a}z

  • !`2YW_t+s%qfhXV)K)*RC$jy?%ino2Ca@fj)?JAL%o_(`2@v@VT`W|U_izL_0((WF}*0RMStZgmzGdSL9a{1DG8M&hYBM@ODIaHR1y4QQ+bC<8sO? +!KJKZ`7ZcC*{a5OUP|kK1wI1CV$pKIDa3;}ccUZ2cr$Rg +>FxlMU)@)uUN_%(DVlN*#!N0`}kT_X +~W}AD=6G~JPKdZNm4uwQ%~vfF|?%-`cP6|?!OYW9~N5U8)(T6e}fYmF9`Yt0`%d;M*E^9< +KlT7Frdiqi9dn@zoK=ZzfSPU4(jf{Q4=$dWAS<2kg*U2o~d`k4NSQ=iF?Wri<5U1DpRO2=He(B< +2e7Kun^7N&qG`>y=(WG@KfaL$9k)1KfpZL~v&Sx$rd_6t-?i%W^g7wND1m{G2w(!_*VL)~n+hWh!qDM +AC2qxH;7!<)~;y!x;2>g2NxVx;;c=;LwYjh6qhIY&O5I8l^X;Y?ZKGFi +2I#huOTsOm1Qf4mLHGb98?%nk<<+(C{F_K*PQXHS>KioAO!rmvKcog8(wa{H?gq?^F%f&U7Z*Xuh&vv +taOp;qErbryA~=!fXNe|F3=u>*LC|9s=z(12j@FkSO@M|JviSXyE<^s>%n=Co7KVFsexA?IjmMY88bZ +Zve!QKHCuT=JJMg@scffGYa08+FDhU+3Dao!ZC1BJGb%PnY6hsBh*;n2(S{9UKY(xGXuQj`1d6Tx7Jc +mMlO3DPKI7MnO!_G0s<{lJ1h*B?zR!v+hKd~mCT+r@v0FYgadkl$Tka*E*32Th;2P?|A-uqGqN7rg_s +Vsy8qZwja5Hy=6`P>^J!<*p9{fwE+&d}xpje`L9I+0b`cr2<^gw)Hy<9ZGbUw7)?zUbpu6gnLrWS@eqw;FPI( +5(ahkOoAEb$Dns;97t^`V^!+3ug1X!BF-H+}`=T+&=o;+&*5(?L&x*8Zle<5M$}`bysc|sBt4begejw +?dvZ4)WP7Bny7ZxuUEvHTG3CQqBf1%>~gI@4H}p^k0k9mlBAuFF}BvwN4uIEYwv8#;ft=Pv1-9>EU|k +l;NANSGOQx^5ni6ET`3;tk;q2u6{pVzdzSqXnuB|Zrz +XKnrQrvYr^A}zOzqy6yXZ6AVJ-YuvHR8d)|HPgJqRhA7u)c?&EpOma5h>4kvRWw^7e1RhqF`ovwwc_`tax!H^sgG) +d>>?+;T}xoVu2a9V^7Z-vg2esQW)qO9KQH0000807zB_Q$rLBMCq58A#4F3Xe?=!CNtRP>je +l^#ki;A{t}eK5iyD{&%49x@i5+~qGq(_A-P8|*lA0wO&=20+CZt>P9h^z$bzKJ(Ts=qbKr*_=dT&cdONzxN)c4w`tY&oml<9A{ed(2Ge$vuSfk-WRUk-30c9B +U0rj0K~fd{P#lO-D>ig;{!s>mu@}KuMdO2w#+>w+``Vij;|F26UGAiC7Qt6F4A62LNhUpugzL@C(*JH +@ZZnv}A84BV0#Hi>1QY-O00;m`Rt8gKc3QFg0000A0ssIZ0001RX>c!Jc4cm4Z*nhiVPk7yXK8L{FJE +72ZfSI1UoLQYO^{1##4rqm?}7XWv3qGDgwlHtee9vI^pes`DaK8djlqtBY!~wH>o^bOiQd)(;ivvCF=f=506C&>&`beSf`tG +?#7Sm>Cm=H9W1rIFUDjcwPc~Vra=t76U?uK^yDqvM7t5$w94@{-qd{0^=s)0C!~p=_MY+K&(`?gO;Y5 +&20#Rp-Xu}Z_Neq(K*etAi)GH9g&Stf9owWxnK57o7gl0p5S}+oQ{Z7b&z9>U^!UmBU-S%r9fLJL5@~ +}iEAwMr!4*fP)h>@6aWAK2mnY{22MmPUu| +J-d2DHJb$BjtdF@(RbK6D|es`7s!z^4$z$^qFq9j`tIgw?_W-TQ-mYt)tD%21dk`v(y4pU6Z|9!jX05 +AY0(VOhs(uqv~J>T^7-3{_6O|pXcNfT6zPrC-#S>zZmsvzW&Vx +G~R?h!pD-u84X#xpc^D?iUW36fC1fk~!ppMlASHrXh34Ws3aJ&>`Z+({^DDYFx=iKKkF32LIaT(H#7i&@^2nGw|g3sY{$q3E-B`-h>WyVM{7x%I<#uE^ +B$NaLOvycHk$&-i?pXMxg;5%6{#0nT3BjPbo6eL*3G~zxD!!?<$L0mLI7=&!{C&g0lgE(*?pXIqrJ}h +}ok{A$ZnueU=1)1`=ymQEE$^9iMfdjGpbb3Z4s{8bbAJRN09^5bH^~-#yy~A;$Mk3%c(B>6xHuEo&k)aB3P6dcwwlDBXvhO&NBbulmh3dklB}n8$B$)M^~G@v( +hy7K31Bgi@L)PQoT!Wgx3yMY0=aj7{AP0EU3@%$b^4whk-^=;*?4b!GTyI9PNv6KSGyy4I5h9=kKx{^ +y7&6{!|`Z(^~xX=H_ydQO?mF^KHrDy-Rk;k_r(ASYKr6c=Wt^@sBZl7eBTrM?Q5blB=U}5P0o+6{v|j ++I~#~ko51_&^!=Wo80_6caW=g?{@}eme*aq1;b)9=k}_a(;(0vgh3DBh3+Ijy^C7t3^=w_R{H8~CUXq +I>W(_UjsxnTge{0X9;t+jYkny-^P~oS@)~!2YJuN7(gFCrs`?d@cE;xjr^FJ7(_IllQ8OzHM_7fg|4Z +AtSb=@0)QUEnmj@YDy!R~yP>X76zG`ZCBP{S4EH-|5XwB^ErG!L?ya?1ghxlDEj_|Qkd5{7t<}Ne0&DW ++Sd%W+?*i+`FUR_^ie<=hcN9Drps +hL--a;XmT(@@MZOh}OAPVb96VjZdA28x%V-t?NK4C#KGN0TKpq2?Sd5^(5oWJ7D-bVve#x@xl +ysl%%Ce?EJ>2G*oH#dt;evVDUca-6?H*MvtVZ)CQzzFIRjO7OlC|J(|*B%x|RNk&w*haR4hm_dju5^@_SE +9mJV24Y|X4Ht+M@wlePj#y0_WRNU*7zlC{YY6d@ggh@WIMOiGt8y2CpnZp;c;%2q2@$g1Xvr5#25ZcX +BDh;X5OyHh8F$7xN{Wtr17 +0IWcq*Yse=Y767jvnDGbi&2;kW#5|%4a +7($-~+8gh{6~p_M}fSJx;EbzSFm)Tl1hJg`|fI@ohOhp(+H4|jBGuZA7(G9JOnar70k2)s~^e-dRXEn +rgS_;-@KY6rIk_pI>@1Iq6u2PsoB15M4rc);!oush;ov6{QDr&E_qif=$eAsA4IHW2b#7OpiMm>_6&m +L<0gxXM5Vc`lSIW>ByTXmPQ|t;!rM7W@Ns2dOm(Bl9Fc=JWXY=uVFdPj0J}9uSLEUuyY&tyH+nElZ?{2D~n!>TIlPmAd#YayV6y%w!$>jx9QPSB +#9z;nT#X&6Tw0ds+yry(!wRxNiduz^a|amNJ^&&o +pAbycmmc@k&HsfSwMa2CNQ{X}^vW}h}tftf*fDIB)vx^5@DcIrZPmPfrw80!|!1c@@7K>_|{-lTk?g= +LzKnoLVlL)a7X7KJSHhKU4D~h9uSh(?yuMV%t%y1(h;#pF)N};%6tx~0O#re&N6wsIeC|ELp4% +!$S$}J8r155c(TOzfx#6@O4ZyJLI@CzY>kt +LBE|X-qcJNgM(C38?p5mBbvdD{2W2O$Mfnbw3f$p=wu|KY;kUyZb5SHoiMYBbTjv&J`@B@CW-^&>Yy%xQcl<)&m|im3npK!fXNHN7Zp?3tpP@WbNWn*ipFbk7za`h579_)f2=_&p+$^Buw7M_h +)NOm!UA2=%wR1Z6ita11&h+c-Q=Rm%sljh9NKVf@;e@9xUM?+!)|MCvWQDj%QvG5?956{VM_e +I2eoeIrE*Op28$XW=r+OVoCZb1dPMs0_b$5e?V*^Jt5e?xso|(B6>P0kO+oH +M8xz*EBGBXuYK9a2-OjGZbKxH|h?ME(jFwvrd^pRvllA5g2lI~OSlI7176$$~z36grUZi>hjY*x<%4zE`DL0wuBr)z2l6c++ZX +GOzq(G}svphGTa)w0bVNVj!gD&hFmx=k00(2aOOlBV@_WEc^R+--+(y<@I3a#mzr^|8~VKJe0K(PX{JSrCTt}94Gc2Y9jYrBaI6*xStP|F`6{=_S51~2lk9yN7d&D=OLi +I$dz~<~Gj!m8!{EW-?8seYNtxP~V8-h8oYrmR&gv=K{-OGrf*WR~=TlHN+I=;RH7))D3paztJzihQze +#=XNXM2(G$#y?d16lw63#G5zdL_E^=Ftw404~mxO@~qWGhvuCeX6rr51Pv>0fAHoAz*n85QsK9L=D@({aoK6^Xd5I$WZ$kvtOe0yvs5V +n8Sg816DW(mGA|^@^{&p{0x21sjR?8cO1U8i6I|$p@&y^2%2vjJtNNM)Ja=9q7LCx)%{B;BVrn5>Z*% +f7YV4FtLSm`vk(T37w{tj6;LCsKHJyVB)_-%?!-fx9q*snTTn;dV~)c8Y?)?)cCCBYtjqa|whGhGf+# +GvVNZ8mfj+>(HJTB)ybT2Y_Z2yOM%%BW`-o7JEsXR5*kf>?{c$f>KIKR?5NmMee#V1H|D)LP3sl`5dg +XziFBw%NzenzIn!EntGo7zBUl`Ymw@u@HcIZPmQ3hlwG?Wq(cTRN@dLH<_1Xgv;Fy^QI6%6C3mH2G-^ +Fo|N6jMb`%QO&6joyBc(B5k4x$8WW`XA5cpJ1QY-O00;m`Rt8g|7jY^-2LJ$~5dZ)q0001RX>c!Jc4c +m4Z*nhiVPk7yXK8L{FJEnSb8KvBZgqGraCx0qTXWks7Jm1P|HCQsP_mKX*s+~dv+A^+I%`d{iL>$Si> +#p`5|mJrAQu1=DVhBDJqO@bcHPc0&WHqX?%%l}2!iX&Yq&VS08>|0sVzG^I6S!7NCQQg*0}&#RJkaGv +Ig>UEfcUMq{52Cg3Q2f1SGZAxS#Jp*GfsXhU=Z(lnSIdJjkWG7b);Hm&#ZuYLicZs}w$Oxc`JtLL2Fh +Lv2Kg8U#T=YM^M;2$E7+v9-BebxT>T5?hwJ=~l{0Xl_g0FZ6p_bg#zhIRq+IU5N17x|kZ?K)s~r$;@()3v3Aosbq_%vO3kM_O@+-%XLaC;(v9>bPyU;yHEizMy#4rR~z0Jy6rA|v$ +>h&?&d%Wf=dG`D`*_<)qiP^>$d1t48$(Rx1K70}RPpfy0C+LwWLL6ofVmg7~Sp21vw;<-$r;Fx|BP^Bd=uqXI7mh8hLxVoAL(F9 +H+V*Bx4R**?daRfTTxZue~D&e!U$;mE^V`n@A5j*_8DRqHqoRu0dkJg$OO>o%oaP{D@L0A!t9z876E# +gz9h$chTCX=X86?-kKxXdymtXC5`qXFj2fP>R);$==eHw@d?{s28EQwW1OK37*)0funiJop~IKnG3WE +J8=2IxkO;R?@OD`6-cq`0$}8|KOg6{B#264f(~{9{IP0kNDYgIfBgZVt4sha1pr?gZ(OA3xZ@@`k1t$ +z$GkF;SbL6s14sUctcL4RK+4AbcMV{9|m$|BTZtRcLi6qT8~2?Pte2F27@r6cmGE#)FBXL{zKKpqq*X +O^Z(@v!A7F?pt)L$u$A&0(L5k)kl(0?y93qW2ar*JbZnP*=sw0;e4#PU+u=i#9MGZx->2<*)HZss7I^ +to2wu9xe}|cVeG|{$-CX|rdJlpWBdm7ff$6RC1C53uN4OZ4nN%cYiVef}k=D#8VyL#P!r=Br5RG&Ts0 +jqF_+ShM9RH4OEw*~9QG@e`>0gp_V<7$)%KDG5Kw;l$+c+2e4|FMUYrduGzV|wxYkN&C3(MM8^`Pl?O +>Zc#?S9xwu=>nkp&ar^c?ebGo~U2*Oqr>2X&p@%MdUBF>`HttROq%M_y?Qp*JKc$lv +4C`mI33bpY3*l}0Ji2A>2aAvAd$No=*%YthDaOsh$mPz#GxTRzu(dAhDm?I82C>u(A*j`j(qy@h?S#X=p*UlKXaQp`k%$66cd%q3`kRPVo +W{gtr^U-Au)~IZaSv5hWK+?=P9gE)v +^$FL*jCIFQ(JhK8KBNJPWt4FXD@{LFTqVIbo;lv|s+MG4Z--+Hai-d-Kp|oY4luH@nk%`F6gR=uOmKM{I`|{hZ~nmkJ&)Btp>Iq^=9|x`7oF%{6bTX_aYfJaM9R#Y% +d@1X6T|vNPAGyaFg|r?g7^kp@jr8*Vvou5`nQPHay4c^CNfV2UC7O47qaL4@9jX=GG^0bVC1!v(Zip3 +~3g0R>og@A}aac;;)Vve+dQO0uM>FB92i@5yvpYW);T;Dj3Isv!&8}UGOOygP#1<7LookjY*%$bsGhW +7-d(x&d8B%7{kVGhyc!Jc4cm4Z*nhiVPk7yXK8L{FJE(Xa&=>Lb#i5ME^v9}lsjv~Fc5(EK> +owwEDkuPbPTl6M+$+EC84WO?X#_-NUkIo^6x9j4)v>pLx)1;K}f#)PC9>h0mp)A$QVbkb6QajoFynUr +cj9#6_^6SpgL)gnO{7VG_ljJ4vXLwm1a`RXA8KEZz7-~^>L4f=Sxrw2SmTQ+dggfhfPya_&p`1sp0kr +3QMV(k?QP#nN*O-Q_0aFq-ZGPI-bwmlW?jvY(>TdGw0*E#PxgzcBGgAA(o>E2{rO;39Eb1Mm6unJ~j-7ZkcOb=@6aWAK2mnY{22(sD_+OX`0 +037a001BW003}la4%nWWo~3|axZXUV{2h&X>MmPZDDe2WpZ;aaCxm-UvJ#F5q}Sm@4zSzmS9E5U3+M- +$OhQt_6|lJ+qE5|=wcx-64$GXR@70H9oKO`duRAZA|%bUXq`a*D;}0uIS!h{A!63a>1AyJU;rcJh@ +n!6)QC+IG0*Ds32I)n?iDNhXOZTkc{u@l1WB#CTUSN$@6E=pB1~B3*f4mpg`OYG~HueQVIOKZlJk?$& +KJUBJb*=x+Z3PQQgJl11(GXxdeNDEixH{<_-KlttFU7%a~lYHBNr2l&MBeUDQc>!>XK%q`s>uIHDID6 +7rH%{7*U~CvSfG+w*4xetzTPmWn*qZ@&xP*5>)R05604X+w_9&>0A1%dZ(zTDXJftZ7Txn4y2PT(bN^ +3Rv9?{fh~HDn4?-%s``NmsRe)Uq~t>j6|JDNmH||*c7m-){vv>hL%NsVJBkJJtAA0g6q#!2v1j$#HIt`kb1W0EO(Pu2x`fj5e +=h@|p8e5k7yGm9%Myr*3#f%a$#L;3Art)q+4jEGh_DMN4*ynB2l{Z8#j1n6wSLtl9Fm%pr<5cyu^9Tb +_|$UcX5kN&UhT9{JAYb=*TxC=IBTY*#}F5E!8321USWqLSpCHPr!xep{=pEaVfK4E+D7I-~-MfdXLiX +NJDWdj;SSNW#9tfdc)TB4;L(Xfe(fPV08Ub7s~CB}tkVRUy+fY*@L8wXrii!x4G)4u~sDtfCxf677$< +CPlfuD2NCnNpZug!z`F=MTLW5FDLQ^K)QNb!X|iouOy_YGAT{r37waw58;8DdN_#Q1+Hj_rUJJ5-)yg +JS~Lvol}DH!(S5LcxKXa}t{|%L0EFnoX!gH|_l=Kyso;no7P&bHw8_DNEA+Jfe<^vjI6qpvKVHI?hbF +g%L1De5jtwMjb!i2WCisRV_QFW(JGliKpT#2`NW;DKMQI6}7Bb_bO9@so+OX-#M>%(J3&V+>o`kCoPU +MtIQ!;@%wXEI*Z^owaCedSfy+L|=^_Rc?;>${US<^IjKIxQsg08dm3A#NSw})##F(h=Jwfam!6w0=ia +A`E8WY;vib7ZiuQqX<@$n@$iT^t`RE|wP%fcGY)S+C7V=Tv)_c^;b)e>W`A$YnD!j*}6w^_xp<$411{ +{%~2RDLO$v&i~LK+63IKAkHGg*|4O$&p9PcOaO$*S)b92kCSUAL%Dhl7d!rrB9#QLmgg5orzh!$k7r& +Mn9t|1Ti^7feR|hDz3rY}bWi{3={hnz5Ne%|i}SxP&(l*}#a&a8;k8#(mZ?x@u48!0PDqE^9%q@W#ql +T1YA)Ujg9EjKH+B#lL_XKkZpD<|ulWA-^jKL1nggJ>MsN}M3Z0|jHs5|D?vf7O(dF_Z5GuGEI=qLZci +>^qd>(n6Pw6fZnmf85pDoUpCtCL~h!O-pf;CMDv**rSG??oaUHVUq_u&CWy8i&42@$QCKf7FjmOPF*DuyMrN4& +$s)$=5%!o;nBx_p|Yzon+GpDP)l`0Xy)t3SRqXRs&e1Izq!F2Bhy<$wlQk9KT7mL6Xb$Ib5x!^k{iyZ +*sTSm6@h5=Rwk~Ra=7?j;#7+I}Qtu?vb7TFdna-e_>#abEebAr;XwA(Ecx~6t1fTfU#h2YNtlaVo +IRM(SSSyw1Mm2(hACgFGX$TsY;wReI>qWD|OyT!!;{#-Wah|K5YCx3P%L#2P$_mPn|5r~6`=>&8e<;Sg!Y3#tYLK`_=rQ3-#Drk?WP;R@`<~>;$Iok^6VS_4kIvj4;>#GM#fqsko^49b< +^cAM)yG788Zr23eh+<;WE43$^DXEUAXuoTRoghEuY=V1S5?woL@eqvXR=1~IJ%e7va0j~9jB_GD0y9~ +)F{ug;Lx)CU@`zuD=j9qPn&no%)D@4OT&>n?tBp!*y0h11x5jbN4_*A@UD$W0^qQ_-{j^SKU9&0=_c_ +CJK;+#J-!BD!5{T#nyVX4~7mv5zbFIL2GEWTdjvjjaGLdEz1z$qCwvZI97(!$ +zTTAxLqtj6&tRb6p0;6aV!|-FFgk?4(?wkJNgHQ%!^M&)jef`KLm+hMF}>Z`chPwRdFOS4YHEmu(Hc& +ir@u^hD=3E*9&&p!?_{?T&JrdCEnaLk)L|(=PPUV#J7b|8Rw@MTMgYP7&zrB^pW4|u-zMb&5gT=Sl^A +=aK=;mzPEU?7de}f9q(uAyGm$}OK7`QfM0YA%bgrGm1bnvdjixk#bEoM4ucum!(cj^4!s`PL*5%S?3@ +i4CK|O4kKZ)iv(~=G*W;bFNj;qIUScrZ9$ix1^@vuppVyuwuYEWpZg}L%(84$ulw|`tKomvj!0Zj=^`L@A=R)s3bi{{@`aPogDm9S&fFx^$!09t}YDq14#+oMayw$%LYWL0oM^D +~F9;h5Bf+&1&K=(*b+B(ObcOj%}r47ju_Vl7Mo)Qgm{tHk`0|XQR000O8NLB_@NOWJj1_uBD))D{!B> +(^baA|NaUv_0~WN&gWaA9L>VP|P>XD@PPadl~OWo>0{baO6nd5u?XbJI8w{@$7X2jPCGJqaGT;bxd{r +I^%k8S-{c%FAVfM~#)#oMRhFq3xCa_uG~HB0B*t{ZLEVeRlWJYPC8c^XpiUb(}B)|DzpEND{ADDp*8z +X~Z~@*NjNUw}NDAHSlt}f`3CT)+O&57jc#zADxij+$UW^(@4p^o?VneE}1c=CWnK*Kc4x8#Z=AlERz} +>T;MFvL@YCYhk)yhlU26O6DApnn51zc+~cF;qj;NVT!M7v_-!x)Iu*u!F~GN +Ff4bkDzCF*RLG25iG_i|-E^kBZN~8P6tsl<^WiJ((T5fCed;%fLh@cQrkIVcWoYhgLA*yTBALfEh#G**|b)#20&{IOoM+nUCj(^~6KMk7 +xzbz4g_e>CV%hLb|`MQhnBJbBfcKR%jXj{EaVZ$40}iUaF$=vuHixbVmG!J7f@{`dk`E3&>5yUse>fN ++4TQmTOf+Qk{LE0m6rx^~l;qkJ2CBOes&CEH5rmZ5L{$9dp2)==Rx7{Hu5CpNbhpUJXgIkp979(68*A +qFuA!yt(A3S|}!2Q%bHV>Z0fjCb@>X?6y(cD4VV>g?JE91>9`0eAo>kyBx*sNVa*45$hrBcDBcQu$jQ +4>#mE5kk&&A%9(ve?5WH_#Fp>x8q6R_r00#)JQ}Bt=IolA+-+S|HrElCu1bO<>_bAG+2v|yzwVvhxHj +Vu?~`I2smBxC5zLUI%a2(-Edt*UnpD>^6U*wuv7z!A*VMX79AuqEY)XIzYoT%U9gMeRU1(5!G5TD2Ld +-2+EA`wQk7qCg@ATKw^tD*qD!Fu%N~(#ghYdRV;R@%1owzVzll5dLz1YVkfT!L$ZC^D)!xCzm<+Q!TW!oB$f9jxk8xe9Z8Vjc6cFun5w~M9D?xBdGV^ItLj)_Z4*OF)VP{;aNx5<{{#WU60%F|R@c}8VMT{g)hB2;U|M&Kx^lWCps`?wngx +=jVD^ee$=zdU3%9J%7?{0I^X|vb6X@+OCOy)pDH14|fKK9?LG#Ih(=B}HZGc`gg&s2n?3)-TnGcSEEigpRifry6bL2(Wd-64f{2O%~cYZL&6ez;X(QP+ctZ7!m^OBs1tw|VSs)eB~ShDV#F^8Jd9&+oD$1lk^OIeXB%4>dSjPM7_ct!mn +1^1zyTAis*xYZRS4XkYgv*b`m*%ZUy`c$VZ!#0xuf!6(j_!i7fvK3T1{l7z=FCXy-t4HpcyA_*<(I(#A`JP4N5BzMNJgK0_d4<7L-*VcA@Ar6#=oq5w{&gn;j6R+g)3bpMo~7Yz~`*(l!>ONl_j!RI +xW1a2=^L~HN3u!p~A03+Zej|nBnRMqKsItm;MwZ<&O@~tUVA@QF>H`fuTWkj|~K +v0&PWHxg%s+x>N0*&rQqr1^)v<`x|^Rx=4X(j^rf3k|QAWLJBS0V{kc_PZ7o{OLs<)R9TDSdgmjAHoT +3vMm25*F*hc8Z_J%9P;xg0U4XJt{;3>;7>#j>cTr8HEs6~*7I!d!DynE;FopiA*$~uT9vHp9y%Hz5yvPwTm0dMLsHd%Id{U~e}|@nfW5{X +K{N@b>`8CRD>!4Qx=T#!+76X&hzg&*I`r%>_4hQTr_D}mr&;~$B99*lYHGu_VWe +}4DQ-}B`j9fm+<@ZUw1XvSWEWYhC82z2=LyJ!Cc4-^`I!DrC~=!oJPTF#4vUnF80TwK8Oi;I3Gvgsf=Ize +(}KZ^wX{2=O8nOC&tpcyW;QdA=DR0`V`<+P5Mm7vCH)*nU@nmSq9!zuq(?0S=`Ad@4~jQlm8R8R>wVNOEuoh(`)!5Fy3uIZ#Zv +g2pM$!QdHVdUUI(>eu`a#jGJLk_qqFl680jUtOYfRXkE&TRX2z6-q!m8k +9l)5VFm(Irl3t}#K~i)^UK1!J^H+b@Eufd5C}W#!pMwpq{9c-}xmX}E!Rmc*nuZkwCRzFnN=NqUu{L) +n-)%jltbV|bg|AzR?uz(lRkcujY=^SA4bZ7I%8kL)Mja()>@w-(j;^MqSxD5@|?A`i0XI_@ +5zEO2C!hGGM@KwfI-s|WD(aIbua+nr;O4{wEc}~@6`1vM#24$_~q06-ayf&1TMT_lE_iCIMla?BNW0} +S*(`*p5kn85JKy8*&htIw3hCox3PuLLGT8EingM+Y_0SWqBs`I+MEHGGUFA{V>L7<7;Q|qs{`HI%$V9 +!KmJGq8c*((pL%u|n8`L)Ko=6&tXTCC8O*J@DlI;hWK@FQLFKqVj<5K>?{%!0hrr*;{Z?SqNYHc_b$CqUCh}Z5G*}z5h +f{EldqgHF+E5HX+TRIAr2b%USFe(H7zy)j6#)O|0%kxjRK6J=y4R4VZ7ov=9y#X#Te;WmEC;%z7_WQP +YIkR-7Vgh}CBxK;u*p&`!(697kYk?B_}^RRL)jCfVGT4FF1;}jOd+}(}|jk!IPu#$oliv(mrflXG9O~6Dlxj~$k@hYuNXjw5! +W8S^OfI3EtJ9Bg8IGW7jM3`2_lA!&b-0Euq^xB@kMp-3nPCbol*rdU>Fezv|&5Av(0412k#N;F4+uCT +Gz|4JPvk_uwf^=elL*LiKG2)6#eNCQWps$G@pyxJq +b-R%VuFT5e`?Kk@~%8th84KKD8*IWmbW6Y8mFB<@B|x%mQ``*>z(#CZ +m8L5}Wnge$r4_87hzJynwfPXe51w&g;b7MKQWrJZQ@>(6L#R=RYwh-%BZ0S2TH5oyom;|e(vyRE!ty2 +)z83_1XLt9qMBt2KKlVCf2g!%{~l+|=C5SrwjS@1=1(eBnwXKuM9m3yvn1RuH!Twfo);^ebR|P;sWlA2>V_w`(>N*XIml(TjAGP$paB-?QCwt2PA?Zxd5N(=c9w?S0HYwzii*}B +xVzv!5%rR;N+B+CY(??sZcu1Um^Bf)GmA(9OY8f{M)@A^Jxtcot>vA26r +A;DIR~4?sNN>7RN-Z2IH#AHI9_=6P_9Urzt_^3@N|pFMr^Tw9`M*Ni6H7*cKts~i=&=RG?(cS2Z$H^vv9Az_2l0VbItUx!0M-I#nRk`j5#k??;p#m@3Q +*1>dlQ2pMzRh-1^|vp(UM9;?FO>v1d4m5Sb!c*l-rYw4|o9ZQ-LSzgjCNF_wOZK80;Nm=nJslG7MtTv +RfF$>e3ziu$obTO_}5y10j+Y4i%BGNm7<&fm4Vn1EW@&h!UACJ9nc$Mkc+EUpqRiFwraMD`0bPZ)}`3 +`xS6{yf;p5Ez#N;?Sx=E;IReGliRcIYcTC|c=i?WIk^F!yLSki5@stc^?QY#oW5y3GE${_+Svikye)6 +a3Mud0PbDzv%)D@2M$4){Rn9yjjE|NgA%=l{F(F&rbBEc+y|&IXKsDjstuYek>C`i&jD;2k*-{c@x|? +6z(fnn6?f9DTeQfIEZWpuv#Ji_`i#zuOGkEGv&^RWvXC+S#957ME(GoWZ=q2*L1Z<-ZT2fH^4^dnz*O +PrqnIERC%cfWXjA~Ay1)Qub^K7sVI8BudF-|NPV>W=8xFYlH)b0fwMQoxsMBcp~Y;M3$jeyuVScnKsF +>m!!ndzoEKxwgQ6~lf%nwh()2HFI?Xz(wy74Bq^fnfeOlxbsg%tKiJ$>T9j9D;d;xf#qPPF(ZMM;n5* +uTY;ea7|$gLD;>M-oESF5B)BDmsice*pFUw@(?AHKsR!x5DHE-%(ueYq1V~KU+*lH{T$XR*`O}b1i?s +P;!F0|vLcSWLa8?s97|W*s07nv)V`Nuo>SaN!vs{6xaI@ZU?F!AIOWh+dM^mw>7@N>;iRZ-R3$%39ol5^L{d&a1b9j^iR>vA9;F +*+VX5Fel5{Vym?sp3``opWSA0_nZNs244Q?yvxCl8YH9?||@A{triGF@g_Jt_j*Ax)l7lkUKT6P +8o~i5o8CqU4+!C#nEC#}S#kdDU-Q_P1J9Pr)Ks;HBRsBkhmn`;gzX2 +0t2UBWqEuN-`On-QgugeqZ^gFoPoe!`1mitF)nhF|G1##Q1-qXnbRFtx)@K>;ORGOyHw(lGbHZZd#`U +f9Z-%d17e|Uwo0YU1C8EW(UEu#;?%_n(~wrcUS3El!5Pr?$0F6^=-J5F*(8GF@^*pD}^VmIQ;R;_)+) +>zZp7QrHWBDp +L1YCrhikuP^+DHQ1u{9M|wgT2Z{8K@S5heANNfA_j2&cnQ#$cXgn#s!9 +v7)-zCj@p^xJ~UNOWh#uL2$}KM_Dq()CFfcEK=y$OY?7_taBz6I5y`dT&^Zr8V_77-k@~xRa;Et#*_9 +5I*heUy#TaoL5KJ3jFxl(Kcc(Z0ME=AWPk|MkIpF~`2! +tr^8L*+N;701?u)x=HQA*?ygQKY0<<}C3({SH+c;rQvCELUG-rFz$|3{mG0#)vo%FNUPm&NVK-xq;!w +^fQg`lpG>%mFE6vl--m3W2%&KARk<(&{W9g}#$REWgkM~g>iBP5~UNYUWD&6C$?qE=*xD2!31i +KsZX^s@k1nnv3DOu*J~lvA_G1;|`;7iW|tfUwL3;4jW>!c%=x*7hnJB8Gkjw0ubWfjXu03_UinYkGN7 +#n3&;0Y~N*txI`glICXCwJWUDlN5r}b#`U0*`#-gk${DbV +aH024NNo7dgJ-h!sbhLl^oLmB9%-a~st;q%ZoMKA!IDwQ;eoa%{eGs`1wmT#r>}%j(em4qUn2`^8BE% +jno*cb=^)^_dQmgAGf2I|yJ}?vAK%A^-3V4|lSWGG}Y?Y_@fLRplklzjJG$Q7gWW;2@tny)Ron$UexprsQZz`+FR!Hofy +9{^^wlZBn+99#uneeSvB@>XZT%fKg+Ois6SM20Z9WP#;iQSllZbvo#;wC|<8ZN1A?&YOx3>-+C*@6lZ;x+QJPg6nlX3&XN5BIGU;ISkeaL_?b@j-_2tSe=r)+PF^Ymca +VO<>^i=LyIvCOkQrbvIS(@oU%4(gi%;i=w#1G#tF%s^a?15ezC3Hzv2C{S@idDz)#u>+?T-<*sS(_D$ +$oO2aNo-)d|<7pe8#5Pb>Owtnfz92VY +(wvAzO_1wZeGHS>U}P>=>sQfX7_k_RUHYXBZq&CJrh)zV#RYKMhOdUliH)1uM%+*auZ|rqa6Y=1bj>R +xX%^JMys4EWFpI&8{f2)u%Qj*Hjh8V@ossiuE_{-nroLbI0j3K`lgf8 +YFJVC%qTxqifWbBqvpB12aoSir>NscB`gfK7g~a%%DY)Kr~+@YsDv?N-r{bAM+m+jUR5@#sa1V58{ZT +5l|AJpXJa%urTpIDkadpPmtj+{-Zuh=QNFQaOB`zjKfbJa@i6HFVwyZj0JimeuscM35aB-){HKiZUv! +sLg${H2%s6`{rsm8HgEF1X%_l}#F{73KQ_V5LVR?`s_zTVS(5QNF5d2N>@soQlR9N@)D;KO{e&^Y77A ++=86nq#3A7Ft}E@po)BnSgFy#{8tg-0L;&DoMkP_;W1@Z=1SW|~)L$H42+c{ir?h&mZBn_dc?)4WpKL +Oj*br?LhTDK+Uogo)`L_s)YuSS}y>$HTw}{3vMS>Eq{CpbnWwzL6T9$~EE6O+1g%ynooh8sPkT+ZdoF ++}8D;H==GAxm220-(7WMy^U|N-$hv?{3~^~=`Zl1ESNv?>L=jYUIrq<4S>FZWNs33s_g-;ZL#KJs$>K +$e->*(N)WOlkTm0fCCbEqXg{F2PGar&)nS`-q+lIas?sFiA3@b)WSmH)J?st0&r@yBZ9+NF(M=l=!sS +cono3$$O|l~H^=+PM6OIK5z>ZU1OT;mV$SoUSrl$}RBP_ss=DXu{PL5gKH*5k?Y)n9lK|7Vf_ZB3j! +3|f1>wt5bD*CLrgMdlrQYub)BNH0laB7t;Z6BQfUdoqR3a(wq^t0=g4rNMLg-7rS12k1aBM1Wr{JynA +8Hf!7B+)He$-iUJGwJT})m9qCesPPEma2HA#aJ#|X*!Lag(l5@dzYDPl#YYIz|+RJ0L_JYZ5F7st1xt +lB9)rLtPEb`yk6KlwuOuqYDg3O7+!E4CO_mCDB}i`7iu! +!hLN%ZFJ{Wi?H_{iAD+H9n&*qi422ryvH`k{r9C3C)QgHxdQfOkZ1PP?veJ*7Yon+Hg*|^{CtNQ)f)g +>~_g>?i^UWLWy0YK9(i^_+XQ3BpSbGVFsMrXK!Y`K&$rx4~`hJ5iyh+f`%pS-9CfBKv8A7mVWo^TdeW +xtA_NOsP}6g`Qgq*l~(O8w5!kH+p=irTi(9!TI5<**5!Eg&82kjm-NF*F;~zsl#Ep*_r9!rDO;NGa6# +%{W`a@cG+t#l3 +KD9^ieNp<8q@$~G8m_`4>=p@VX)R`{}c1qXix_g__I8YB=LuS((7a_fkcxym=|jj7C>XI%02@O)n)>1 +h9}1|1*)}>tis!P4%Di*ymQLNJ2XAu&up-S%#y3-6z;sSGed`^&Lvv+n(~3P4aNy}>nD5UnWa-t*W&z +&kE@|&f577!bAcYjVp(t0W$DHawK-4qc%p0_Z7SQ^=$e&=I|5sQbcRbIPW|bgh)-o}XFK{~R{Ps-oxZ +tx&VOjp&(WfWuj{&q&P4?(zNKs_7om1bXB;RN@$Y@cDZvH?|s$a%xSXjw5-N;e{2N +{-8NvzDfFM^+j_Qrt(GI{ +EDWm{_vohIU~P+)I{NM?n$G-mvdz%E2H94k +266bW5oIJ!J9bnPo<$+mi(5WN8walvepxsMzJGrAllutL>INJVNQzc%mDN2p=!Hcp|M9GF_8F$sV-pm +Vvvc#LWD7u&aoodK$uhuOgk-<}o(%%X{o%Q1Pml!pg1Ho==@6aWAK2mnY{22+2`$jr47002fm0012T003}la +4%nWWo~3|axZXUV{2h&X>MmPbYW+6E^v9>8*6Xe#__uW|Az&I!aU=SmUOP;3YJ^hmJ0;==cV_m%U6OZ_(xgQz*mpc~c6N5&GrQbiCznF9xyU$!@3cuWmWdh9B~MwCr@UhIC1*9 +S7LpZn`mt9gGx++Tz{c!HUP)2pj~{KY?Pzn%k~}52H^=V`P{fWor<)%R-tK=q-q$ncbgwFkS|JA#Dqa +>+)J3&Kg!7_evtm(Zyyh(Bbs{nuJ%04~kyw;PRkN(PxDfe;{at2B4PzGehFnVbdRF9f0pRnhSTJ=v6X +($aRwQy>E%iON|MwFh^WgAfeEjMC`-8vlAL}vovV{2zSoru6gTHrTR*%)?`>Obr=f~XqJ^Wr>vRB)Qnpn1euF?4br%T=Kk5P@=|a>g +0mel>j!8QYY;ClJksonG_?Jbpp(lEU7rCd@cnT9~hxnESefMNZVDTATASmiFYHm{|gwcsFw_ka`rPB1 +0n=dN-|k6*_5acJdGSe)ny#Ztf*xi!&*;+7zD>FARRt|`$3$X3;H%TML%%f;LKo1`5dr_T-0$KN}kP0 +{07SoBPjzOePuAw&k>uvW*>{3YYyTcqKI5VfHLP-loi*YrK9cum;nIca}|J@7@*8$A9MqJA&2GQ5`iZ +mH-3dKK>|;K{XF9f5Gp#Y1Z<%`1>v8uYmsH_9G+e!S(5;p83$j&YO&xh=K3VaOevMSOB)m4g%T+9Vb4-U5EJZ(yv)Ww+=*Pz}NP +Zp%v%>w*J)Rhsk9pr;wVc1iIaVzR^N2sHbQOCalRVA!97LYM!o#oCpq|&UL!7 +(L(2*|%ogYmdm=$yd8Zdu=#!8~Br8OuBQH;1yxHWq)II5sUQpbr2!Gq^ +7aEO4ankU6Cq`BNY=#r;|$Z7+g#U|64zA(<1UM-I#fVgCw~au#zi?d@04tkVh90`&=%klNB +jj!YZf;H}(W8Q|1FX!bwX{L?2U?f%iU>&QwC7a0xBqEFj|jhghpeMP>K37wdQ%Wf5fIQp9BA~0{?lC+PRDH9Wq>X#kP*m$6^mb~n^?qQJ!AB-#ehbxsc0#{JQMs^2Kgr +$q=7{j18N09P%jqA!OZ+jtGUH#aDjrPMeQIz-Pt}H8MhDGi|Ouifm6-kvYE-}@MTztxnOL-x)vE6E#r +6lZ$5n+sAX%c5WOIj%x6Ud*&gL;NnJx?wFK7|0E)V(lgt1ZpSHn%5;>%R67oPU^Cva=Vlx34bYV*uVu +)52bn8SOC7~k2{6nGRH#N8wOS*>e&=KcsnZu26K*ub(4|&6};w_weT<8H$s}!WzALo(ZkP4)6 +weOBCsOBQmJypm0)!dR8PU3zW|f`Z^xD7=GgIxI|(>1*tvz^!9i{>$Q^5*$(QoGoF$JaQHk=!+9_(zi +Qds8jB?WNT;A&aagxGZm-=@UulK6U(KwyUy%>DHgWX%q(|*u8LvvCcj^T)~zeh8EK<9L$>kAvh|KPFAIE +E4|f0(`}oWfO}# +TqXDyel`-hf+Ps606?wMAUd2qz1vcCuPy6Bo;u`xY4JiM_79)_*N%Rgn)dw~{!F4>r2bk!YE)a}L-HD +$dSHAOxh4G$(v4Evh9-7hp_x1|EuvDfhsrMUJ$h$HJms7cJ-fLQos1n=k3wzheQdMR +ho&dY>lE5c#~o)WR&x@6VwtXpOy>o49x>Ke4s5n5z`0A5GxNip;L#bajf<&EVW&y+FaizJ}45OiTaFz +(PX2wt0*eCo^oU2MPS5xZ&!IFS@17>DC#z&8;#1n{3^`(jQ1$`u1!Sg5el${!A?I7=4 +%0&9kw;rqQU6Z{XGnjp+;3>nzZY5wbhuN7U3n!0_gHViUSo1VUF7PTZlpvMJkc$8uSi3-@ +zMp>cSB_IZnwDdZ7qm!|0g_kW*&EtpLu#X6fR8^^mPBsSwLU(9=>Vv{%}DSb<(v~rMU579Qa^i=BG+Ppr?8wWQ8co +pfVP(*{qu6z0j*aF)v9Dl@i!=!eb>Lj$#=R=UhqUbL{E!_e(pMhHXXfQPn!*iZYb=9!O378eE7OTk47 +E1M(!2|HR@^s9`@fZF7F!=9^&R7m&>QCia$RPn5 +`bnxT7dS1^Mg%d`1u0aqkXx82HE3KEK(Wx=mit;8m_^T}JpoA88#;KV7vf8IFLfQZ9`r0PY6A&$8A~Y +i6;|e33v--xxu|UxGjN8dvq@q%!1c(M7}G%l^lo(uKO;L9JA* +uw*E~q?93QZQ!))t0~BmDm!Di +_HlJlae*>rmXn!=8b-YPr9q2ME{qPe(aTbt4LUk4ZTS}o|$Bjy;%Z&;NS1+bA>Ul$Vr&}!RJN9?6JzNPTlHfNy_ +Qq=$nYM+EiO@?di%!Jt99(|(a!+bMfE58@QHwB~;7L~-)DM^#n<|GElt}4n(;Ap)Yu0>0aXH8Y%5j@K +hkDDfBQ+SBSj)Am?{!r3a?Xf?{PWok4n-JhB+c0S54+^OEbr(q)(xC)rZs#b4%8W4 +tGU*)N#w87AN101Av#3^WKq=#=Ax1v7%41>trd>%igO!))oY-iUF002&+D1)jbr*!vxF!|JI+*YLzN&{&DJ +K6@~$fN(s;}b}Lhw5I6FuXKyWR|w~NtC!}ty4>F# +7M)OW=-9wt|=;)d_IKteFG>wl?n_X#C~`mW!5ZeW#eGX` +B_9STtNp7|mSaj<;Tn*>uQ=@b96X82^4RYcg~{+@kRB_p~bfQl{3m?geI@fSsXR;~ZYzY|;&O_Wd$&P`3K2qvWBMmey}dE}qQc1OAN9K^Vs0>koD`wMZo|y0 +fy4`Hm6)eg-F6WrXvueCfz +BlfQ%f#(=(0Xy2wBe@V{kSf9uchB--VwHw+ZFXxmUn-KtS{+q2JtMA(UGXXKAH$mc!`^9pv%8C30%}S)#~%!Oph%hKYT>Ni%W=ij+04z=-d&Jokz=Q- +fE-v96}~B>pfzqSH+Ro~{NtjK)ZD=wxz?{HC%O0Xj63wMt#`X`p#kZ%it7s;*X$ht?P^#@VVq&!xeZ& +axOh9;e*p6@P)h>@6aWAK2mnY{22+Dh1Z(33008h10015U003}la4%nWWo~3|axZXUV{2h&X>MmPb#! +TLb1rastygVt+DH)oj>LbMI+d^+W6@lylaO*!Us5Sj#8sEH{XjY7*b8iHyuRHvghcu8H?v;92HHe-s1 +2~5*Joy*8IIt3#T6`g!T{gnJWL?r5la<|Ax~o_L9ZBSCf5qY!hCs~g%Q487GgepVN!9C_IpQg5{!HZ) +7Z$pyZTTH1#rcfIlR1he?GZ7w;oe-CWX+2aSTDSOen5}+>ybBkPwM=mN3mAW;*1F3i`c%kFPT!HA?q; +3n|v1cNtHYV2?jaamUgtrhS;O?e#9hFt2m8;=|_@WAz~ll_onzInd!!mAcpA(uXXRip}x3l-LwWmO>Z +lCQNv2Q*U6<@4d@;5~C3~D|}6jDUe{?qag2sX4*I<)36& +~i__|^&`G|0)DCT|%lboGPDWo^<_W)mx;}jp>V)TUyY)Qw|fRtbxVhac?w-33(OhBzfp2Udmf*L@;r} +Iy6dh&bFu=psBtC%bO%s3S3JR=V37VN$s;gX6?GO)~f%({Wiqc9aIj|_fu$(HOEp_MG4UP5oKbf&|3F +gtcU>N1<&7%u~Z8>%0q`T7Q}uXBt~=@3i4<$47n#C*v$`kNa$YLWf+-EZ-+=LWAl#}+MS3uuL9aH2nB +Q(74Q0qvSw;EswrivM)@gQZ{^l0tqn`pqW3A+#}qJ$VNzzj2)*_7A!8f<$bYbiG1PGM&rRTn4x8+b?V +$irroZ*S?Q!t5^4dy4z%gAG-A~vq9Y*D8A-NXo_oMlVse)Em!a{|rY`JEs4mx4*1qQ)WEG}9r6De+)jhJ3q?i%0dWvC%Wu)nl1@M8w0Cg7Zv7s|t)lGhlQGFV0}pw +yISSW*JLkcj4SOWglJ?9bVY{s||X2Jb5=R>W=X84C^D2$>|Ocg3&-x^{bp4@s=L(Vo?{ya22!76Fs5ieXZ2;b1CrlmyOk~aP28ELQnE_eBab*xUzj-B5l+fOjHbKGsEWP>~I +Us@~fd#zuz+)R7~A2zg1jS4s5?(8>aYmHXB#DQ7)zF0E1V=JFXLKSBcYP3+!s_{k?;^eUlf?#r{$3vR +jQC69H^y53<34+zh-i+?P@hPHn9Mtuk>|eVmSmr?cW|9I6#ljb}Kj@>J7g@TxSe5M_%E?VkGZ=6}v^D ++OM9J6y%0=!jpYR+3j0yUG%-i>YQ~9>R&ug9PTJfeJ=(I+~RoOk1cO_U7W6F_F{2K)jiXLbF7gEkla$ +EYikWg^x3zJA_Q_0&O5UtwSBHke>dYe%pSFgZb>$OFM6klut6BCl~Mk{BZ8k)#zM{pWs>}*$k+KS0io +Ac>jiB!mxtn{jCRBfzs>QqeeJ8gEx&hi;brR?a8eB*kD1cNM}E?Ak{V+^AbHal^-MYtnO=fU~`>A+-m +<1RLMaNB7ZD-faXPETzaZ_qFHw@c8(|%po$@8QG=vaw#)EM$YP=Ahtfksz8<=O= +W$kHWy>ZH|meb(k$9FS+;2M*Bsek!hI}=>v6EWYY`!d;Tp%*EjEH7erhPpN<0o2zT??5tQzUrUWi-0) +Bd4yh`BmHP4@C~k;i~oU_kDXq@Aw4Ur^f-d}RI@E}3F3YOcDX(+|Zy@P}Ya)l%fFSp%`7~q?VcBJ`|IA;_G=6qP@Y%mmO9KQ +H0000807zB_QyzBeM-~tO06{wd03ZMW0B~t=FJE?LZe(wAFK}UFYhh<;Zf7rcWpZgtJY45Jp#7@)1*YRA=@x;0GgX~iv5|UX{Bnwiul{o+Xb{7B%fRt=EnYqrXotOd_4; +G7k!vfl3@6OYT&C*OT_?s+w#}j+!W +IRuZ?&p(NhA3huLQv!D!(#J%dbcW}=aHE6I=vL_d%p$g5PC8J<|Y%i$Hq +a)WhH_k3$*C*@ip{|S60Jee#x^mv0C0AygfO5b2K@A@$TKh+oRwNarHO?$Eg6F<}%*QCALE6V=0b{s( +vLdvBB@F5VefPAd(@hcYOx^PL|WxMa;8Jy;2k}bOQ1&?ZXEQK7Cvb)hjiodd834&3tSwhVCoX-60@ed +dYg(aM103_Hz_P`Wv>wOkXa_e~J}M)8;>I6m3k8(Cci-T-o3Lyjuwm%HDV8En5FN%zXFY?bl>+0890EH&IA-5}zt +g;@K{5h2ZIL&xTCPT|-s&l6PK?+>MIf$YQKG}My-&BZGZ_m4|D*YO2!r=LTZ#iI3mN)Se*tq;#Lb+pI +0Ur<0@#o(D`;cYd9ln;`ZEk^g%N^1(RS#`&!9mr>yrzy8f{Wy?2Uw|PLs(wug)ivK_#*oX%g60R|ZH# +BoU&A24RaGlM1s7Axc`$1k^r_c?~i7Lt3SIRr5R+9~gw;_`-D3!L&-RDPk&Htr%#Tpg?ZumU$*%j;mq +`K^PR0gRoX85`I~L1bL#A2_$(gc*17OoGg%m)llqhCz;iHibGbQHjlt2Ey)EmbF^%0>Y$-HMEiCY1L` +_-WFC%ON2}#^RMdx<3M@npqA=pJ2pUs$$?sKio|9{Bs-g<85e_k2yZ{YgRtI +qptna(?+{+}zxBVWYEt-3zHZ%{QSOrx4vb%OFykqe7QHLmgddr0O*q?U@*mQ7Two5YI}8U_wPXwS^-6 +(W2XxMB%jN&#sf26cB{=+)U&K>UTsEQRvg82GQ!EQ1#;@PMRPfU`}E|IWpA%@+JOh{{bW4P-ZrWt*(%@DL8h*lt&+WcyLK~}w6ZlqFgR1rArp$sHP +P3W9k`EH1L);{usDh*z6x$A1)NilT&67Yh7xe&1ht$FG`8q}tkYNNeogfOlxEL2Xxc{Z_I%9|NX!i`c +4|aB+e)s)f|MvGEe)M~90bxQ@&6f)y)7Z+{UCLD|VBdnU(Z?BtkiidDSQDT~qdxlJn}9u`UhS?zxR)zoVIsIqn3+b1w=@h#6O(b=E^hp@TLR?zVhI;lPeZ;o-8%9Nb8vf_8FVZwH1CsMUdio0Sq!$QKP0F-iBt5T#Lw1x)n>O +2L8VAEKst(Tbb7|<|bN`P+|BAufo04Qw}A~sz?V5yfS6&@QfN(D%u(7~UV*-R-B>{-ENOL1Po{%%2a* +_vNKLP6EV@?sA9Top?hiz;&L%i;jSMq1!Tx7i)mk1N>Q6;#JGmE4lY7krLMOqNb1mn+Q?+3AQ%pJbU> +bUf>^&OYll6@;v~5^@lRqmFq*AWNXUO6&6h=&7G@ZZVY+q2p{5|HR>(ESYzbijaqTLZd_I^Ld~MrY66Mm@qL+aK_o=}mki;~R3~H|WG~;MI)| +-eGtTxZFt3dT&1y9i&NjpV=1Wo2++cAmMF;ILUX-*dD_-olae)@12y*FbE3&+<;7OWcWt_@~1tr!B4n +eRFDSUO9z0yUs7uq4SK-T;NQEhGIrRs%3)((r%#Zc67_vdGL2Cv=%xj1qp`FtmDjUoH{}&l*X{Mym=M +S2;@VGh|8rVV^5Q#%C*Ty|)nA;17t&z(a`?;9o3{rqU+4rFvF}kyXl1g+-eVORb0KtSCah|IT%k+DeP +F@LR6z7?J1%%lAzbY(XM@2U8MplL`^BE{6g!7+7^ +P^bKmjEA8I2hMCy=OdY@At7w40S*+o&>$SLPHWzS32tBwXxDGJfaHO?<0!ueB7u4Kym=aUR_${H#(bA +ql9T9+PM)AUOS0ew6~FX@s&g}^yQg};h@6rJNY}8^rZQ<$_FAGY;`&yF#Jbs<+MM1^xGXyQ8L7l&6NO +fojWn?4o8QBVY$O12ZhTMjT3?yFYiVsmQSA;TK9x|a^xa;n12-80%O&R3R^-th+wmNelTFToPc8N5N4 +^JoyDm#|K){Hj--7tM^Q0{xxGeDbO7da&@abrR8@)$E6TKSrz;hjkmrdFM>huwUpYDfg8+aZ{Hyxh4nuTh`_k0-Dzf*am|o&DEWG_V^Veq +Itx2qn+*Dut#a{H*H1br5$+wY!?wGl>ldihrE?v=^LL$lkKx0?D{RI3ZasxrfS!Cq4s{YeYa9uSOy+Hs3nB;(7D54D4P!*dudl-A2CO1m85e +>uym0O#jDS1?C$Q7_!{ZzRLxHm!!@f!;&!Sn?M`ZF`t)yZ^y|&L3_}D8bvqSg1-BDvJ@Ck!i}pxLO42 +=cMq{}W`j{O{RK_y2B!{-&G*qA+6jku_RuxYTo_E@(FL6ceqyU`9K)$ThAPBGAZ$`f)Ngx#vTp +2)MCTa1Od7WmOUkaBPCcxH#ActK#5)HKjz@s+$6^*EB14dC)jSN$m^7uk&Inu=ic>EY|Hze9o>C-x3m +voofmQ_1dyfA<&h#^B)@36q9&BN?q!Mc6b*E}Qxocf^oT~35DpaaiA#uw9sv+EJ$+7KXihHalFO- +)&*${&_W$Y24LgyMW6zlW{zj`Lc+Q&kN}VS!Qo-%xns_g;X5~XzYz@=EGd*Tovy%FwC)sBZM}17eDef +vBTO1?(LD{EmAB|*1u^KBJ-K9!7O_K$0d%Q>lKYts;ii(Ti!-l(^%Dwe!i`Rav0PK)XQ2rbA;=RN^bBDl3c?y!tN3JMz#~HcG$-~iFYmYS`mES$ +vJmezl~)C+e1iuZ(yRQH$?AT6!Cr6fX3F5!v5h{zs`D0dwg`5P(BCUp=r@)Y~Tc%%RpX==9w)-@Z*Ey +_LnD$ZkxTpq`J8vqTwrNzW&R*17zYljf=VD<#~!f<5;;}k9s?4s4n@*IlKlnGS`?KCbJ@++wZPal1hG +bmR<{oYjbcat~vTFe+7FY3VsGD?y-E^ +v7@jIj#BFc=2+K;GfwcPNO21QkJW)H$1Dh+5lFn?EM0;@fKnms^fIB06WEQkx4Z<1&TE4fa!ej!6DG_ +^vION{Aio(PC?M=$VHi1rbRhg>$c88#p?L0~)Q2QM#GUJ@W|l{@=rxI9Duz1tLHF9;b~qtC?#lzEDd8 +1QY-O00;m`Rt8fKnOFcn1pojH4gdff0001RX>c!Jc4cm4Z*nhiWpFhyH!os!X>4RJaCxm)TW{Mo6n;1 +0|3GLcBo(%TJq#O+`JwC5Zdh03Vc1I$2((DsY$Q@8DSJkc|Gsm0(Jf7uhouje#B<|!zVneylH`|8)Qn +KlYW0OzmUKqQ1G%}rAwPWoBRMK{Z;It2Ns{G4G_BH>PAm`JFL4xuH{K+B^xenv^#6swp^QNcf(t#>^CUvJ*{i5qa734DpmJ(mNzYYTao4-e#$o5RjO&Jj +aDB-%|9#qL3NT{YpuZEs-`Y$MH$u#8zS4dt>VD#CwSyKD@#cmUY0o;Ef6k#y#%5c*yel=aK8*lC{qAYj>Q)xS!@BE0BxHR-vSR=CFpuO603mbt +IF&@{SVQpkg+wDEnW$jw*u0lkUq&DYukCY;@|OIcio@2kZ^*(Q{NkSSZv}mL9M@LB2B8hKtH(1k8`9B +8qyr#@Gb$Ty>831eexTK*ETeJ$T$ZsI+>XMbHk^-~*XiAqNj}8Dn3Ac|rOuq>BWq79;DZ4IGj43$H87 +%#xts*T=F`bX&;}o=d_ecdG^)khx3#KhZL&On8Vka}D^O3z0-j{*H}ExB_siMCV^{>CE`;2xEd2=7@W +hQ)8}bJ6Kz}DNuHW)hw4yDAG!|Hzg~e&8-~A#;A-g~!q6&c-he>BJ-U4v%Lpm73 ++gP4nGg2{jxOnI^tf9#(n=3$MErFC_kK^&9m0)lari<8&jrg?q{r4V&+EiY9MLTLj*Z=kUo`5(!=$aI +en_n0(~cS!A?risql3T~G?w9koZHVzLTt}z_Y&#a4`_G*EqQVNG1^Pg@75tmipit!hYN<=PEUhpZUcV +NH39O>?=smOX`j{m-p&N%E-v7;Bum5RH5MK#o04*V!}9Ee5g5^PJ`gc=uP=9_2LGpM`1ZNmqK56pn`7 +}#A3Chy#;s&|v?(ZKB@K%KjC4RE%HWaL?KXLb#}%A1N?+i>la>mJ^E5qu5^#Ar{Cd3;lIjae<$4!W(8 +>xWx2b{fytKOGaV1FZ7EU$8UlHAx@1mRkKf3Dd+93lkYKqUV&;i&PWhr5yiL9D?Dz +p2qZAOIJ~uK{#3NN?g~p^lxYM#!_Ybv!P@ecqrlLTB8eOw#Es(bfH4Nh26=!T>R=W01k4wXNA)T|oy$ +q4eP@H1f*Dy&5F;J4j%;ZiM7vTgW-nOKE`8eS-rZeo<4vBlAKrK9r4{WW0Xh?uq~aj5yvc+$T>D!l5C +=`H#H)+1h&t)3J95IQI_P=2REVu?TFEt9dh1?`#xkEPd`X5FqFm{)0l||4hgh&WWc!Jc4cm4Z*nhiWpFhyH!ovoWn*hDaC +z-m-EZ4A5PuJl|AEsGNXA@c*)VL#;D;_twqofTU;*}!6#^sDHXE5#Nh*n3^nc$SMe56zlXY$PuOYtTo9|sbtMW$DubUFrx)bAqwmR5sT-5-??q9x +zb96u(jY`HN~#TSCWLB+g<33tzxh^Xe{ZhU3I=IO?UDo*GZzJ)l2=MdLJcW#&~TY$WkumRO?3_G=iHd +Ud_s$Yvx|E2RBNSM&b4XAmTKI@i$P2AGYDDTFq~?r--xoN_Vj743Z=oGz`!NFE_jC1m7Z5crYc)jwk2 +PIfr0q)#q;x@d9E0D`b`1fRgo2nvOv$}Z4J2R_z6Ew<*m?4uDERE-f3EvTsOvF@Dd#JEBM91piyDeau +%>p7u=r1kn1GNBwg_=n;_Bt9^-S8V?<wk*d-Wc-6F!>TN=tG^3*#bY909P?fN$zN0?#ytXLF@{et7$C6pLS!^-2ZqV{%~Psgu45!l`=+C3o9 +iX-Ec1b}0p0fXocFm|g0Mzc&oSKo}@K=6be<0RA+-f816(t!Ch8UsjS1PixqvUiWIP!x(+woN3u)7lf +NL_+ZV#ByVD@8&Z}{$wpZ9RqBv?!vK~84eA8R1NH}EtS`M2W{;1mT^Qpd=0rk51pT0rGJ`cLiBl-~>E +ca18BwZs{^lA!(OE$9z!0Xmk+CI9ddrEI3OZXZ4VWB_g7c8-t_9BOZs)u+A_q*YJ7j_!W9B%FqZh6XI +Z3EGzXya3sOUHcz5|tZLNgSGI5@DK)a^na;V$2aQ(-bc{D*c41BV!3bzjlHvP#qjjf>l5&s<2T6~jEN +k1MHOS+7F+*@c*t0JI8;NE?kGyqq}ge$S3H+ZljOah3}Td11)$ +*vjyhQo%X#ut^bR~PV0y7(`kYALw4FRal7pKpq(}`@XMa_j0qi(DA(amBm_xIGbM4eoZ^prs6Cxv3F} +>CI~k`c$;fsg0}wmq@VPMV@?|^U7+U>`S_;TCw- +(8~qs5oxR2nQOcFD<#N+4ExO&m{7p~!H8Je;*GuI|8z1JUkna#h#9C$I(;JAT=f5IFEq0A;vde1ePd*4}aM>>BfIU6hlJ%b(x)+Kq<6;*;i?yrqR0N{zeFrQW +-VgQS+8a3S!O$d;J8Wd?2BeH$6Aey`u9IxC^v<*$mL`p`Fof*}pxjX{AyAX}5BF2*#6qaLqr#8G17AEw(2!ZuIi72EE!MvZ7EQs9xO8epiwQFZKkN4))YZ#>xjr7tM2OIZH{rS@z +-+1r3gIO|WU^OMf6)T3edq!$XJ!kt)47^Z{|`U_ESAr6nL9E@IQCdQsu;zy*HkaO$CDN*T+jU+nz%#% +)Z*({_5vi*gn{B`^(pVoK*qTCjbyD1}lE!ZGMzE2jx=W*kzDrX$y;Nkh;E4>FpxQ>!}x({pL51=vFY! +6ZJZeOXdtz?V?E7tk7HJlS4tw{ZF>08Wj;&`C3_3($^ja^x>ikuSr%Mw{Q(gurFEjgLwE*ZtA;)z_62x#XLHE}w0{FoO9KQH0000807zB_Q$x +peGMNSd0Cf-m03HAU0B~t=FJE?LZe(wAFK}gWH8D3YV{dG4a%^vBE^v8;S8Z?GHW21>CZP +KPvSvPSNaLu!l;fxW7e+fD~qW@DJBUC;IGm6x(6KD!hQHtsVM?(slIY8NmC`)g)nrY1|3XMF@>KN;7rii=B(>1b~CXb--6S#3#5KLcxN{h@z-TnFP{;?HHZe>+O9p|R$bm0%^x8R +yl%yq?gvw^A`_koAeT39k;{D?+ZwDc&Rxg^I)xKqnpUpcsr!a>E`kvd8&3U|92gD2f6F%W3qTKX^7H@ +Cz4yZPd3ayOaI(DCP;YxO>(@$F;)7d?Pi@E+4h9-5%u$D{H40lafopLm|D^`Y6#bUK+1;H@*7^7PQ+Y +Kn<+HM}DuKl?%~&=;qng^g9{|Z +tp$|$m|X}zpre2S)0Gm>Gu^NrS>!y=;gS0qMzZ1 +I;it%X*EJ6hPUkejWxCjn)|wsX`Aeh};n3=7eWZ&2aX0w_ +gF^+&S2R`4z@QhuD%aBKs(#xGVBM@YS=7Dm;t(L5g>B4!e@?_&66j*V}na;k2E7{h{^iKhhuKRh&$;u +yaAhL^0cc=B|Bij6OJ2E#Pv#IIWzxDFoDyIxiWm)0~O_tbTenQ=?7wMfHfqu$Vb>`#MYI|7Z& +kw3-{k$SU)z!05X@#v23n+FxWEiPGf81E7dYM)30w81?pxoG%lJ8<{uvX+Xal}`QSzah(F%QrrHKA;^ +0o)cnK+&VNa_q{9tAhUEQSu{fqG>9qAogI5mj@;-il_~vDqA81ir4UZ@$RBgOVG!NI4hB?+7N4{{|## +@DM*YvFNi1Xz<6H}D0Y_T=7{u+r9@I>+_H%5JUxS)GY;+NxX}=4PO +ZSu*3}ECdaf=I2~Pd{y(kuz7v@0N^fYtjbHMr%95_x$O-6}l*6@+G?>1dM+(l9<=K=hX)1~fry5A1gB +t%SDMwI{u+P4h14;9X}NKv4dv?#_63ih;lin^cQemQz;!Lq&=UjsAane38{K> +;91pNCMq85K|AUY1){ecX&sBqSlD7v}Fgxcm}$gVbyW!moYy_tslI4#L;FDu(sUXtH;dT+BP#MxdD;m +*7-4W!5i{dy1QbyFy>cc8mpDcs!$7yiuer@co@ +_YCC`Veavt(1+iL)Jh6;g}VI-jRdeO|@6aWAK2mnY{22(i}EzyMm003D8000~S003}la4%nW +Wo~3|axZXYa5XVEFJo_QaA9;VaCxm$O;5r=5WSP|A139%M@mI%3?br4i5dkH@nj4sbQTudow{Ab{(Cz +u5Q2c1Fum+dciwyRI<+k8IWbdG&^&WtOkw01Ig)`iiDg;Ef(#?l24*p-brL6WvA|CW?mXoO2pT%%vC+lB=#D=Rqntu);Ir_R6rMa+! +UPAY^|90%&0MQoK|r2g5g8>0F>agEJdG)%V?Ef`e*#zbaPKjJm#!-V?2+lxT=}-_YeSb}}b7kQQDy|Ket3idP5DbL@EdLlDfQRJTFiX-;sOZycL-(R_zNc%Do^LQUJQv6 +SgZ|UwW@!s%V?{EwNJh@^3zbjWVPyG#L3<(okn#giO9KQH0000807zB_Q@HPtnb-jU0F?v)03HAU0B~ +t=FJE?LZe(wAFK}gWH8D3YWNC7AZ*OdKE^v9xQo(N9Fbus1$UmsL#K|)4J|MubA_EEx=%G6dyQE4?H( +YGVpctgt*N?Om+s*m`vk#`J_a6C3Vbca;6XJA&Da6o+``{T~xyD_eVrbe5Yv>d4aAK_cUm)^>z#q(De +ZLHHHiwP#?geN%DWU45OQd4)c{(xj%x1HD0>@xncNWOe#RvlS93(<72U*BuB`c)F=F+VDZf$ccH7*wU +kS@81J{N?#6hg{W=v`fp&yz(<4HH1YyjfVp9f)#s@a +!Fp4>E;B^lzWAFxL3SCCP&V$1?Z@!=qc)ZZyBhzWmY8(B>1{yUeXrpjbX)}ZKVV%2ERRPILG1#vchW +PM!U8az?9eI7dZ}u0q-?VySt<3y9&tNT}J*~8e%thf*uaXSx?CRs2hOdOy@j)?4xp7~`ZFjm>0gdeMf +WAm2^nYkH?*rqdnOd~OhqsB!OTC+#o~PNe$$=9|Do#-8@7B8}P8mh)79uVbN&W}6o#GHt>%71=%8*cCC)H8YZh%Y +2BBibAPBN>$Ae%MgnDprnowgv)A{m3*d2er$(J#Hu^pJ&`C1;1h8ZL-K6GFGzN`qurEVKTB!@y*F_2<5w8etB0?|& +(lD{l5GV-1BWfX{u!|FIW}>;4z#*mME638M#|#YfkW$p`&%#fVH)Tc9+3l!ovx>-FxzeFt(wv@6TQm8 +UW@{25LYW+6_HT)&KoW`Ghk)FeP-Hfk3$PHn)wm{P|F;Ai5kcr_{T1Fx +SEQ@8dC(DF}wk1{|Yqq?C>8(q_-q3Ct^DrelV+pIKY-q^6fir2NL_|;y7DeekZZN7=rD7+_{RqWc&?d +9JhFZkhVLOCHLVvl{HQ#Sip|?&2MTt>~&E8;6)0{mbhm`vshp0LH0zd-FSOXlm+BMj$LdwbJrnI7E@w +BC6)l?7WOAsU=z+-Cz33J+w$x9nxB$dcCl~0tZkq%=)YU;DjL6d`o8~MnswuK92(m;!X9UWMHaE5Y9Z +>_u6M^hLWl*sKH3M*oU*vO2)Sy8Se4qZcxyPU3Fwq9KMcaxRNyfs^{7QcFz?j`F@*7)6X*qsOWz4aMZ +tR{2+6I)!d$@~-ht2e)_PsaV*a^^Oz!-CX#Sg +Z=p3G);<1!+N?R2sHwDLY)`|Nr#yL2&l;Z|rfZ*nnni;bc1bT;vB9CkUmnS6Au@B&~fyR77TckS9CY& +*gKsqZc36&lPoUCjLzMjcpS<<|&z-r9B8WaX{NSXZkBb|H&HVqvi$H+NfLbSUk!!%F&nyLJ!EF5Srtk +ZZzdE*@72k#aLC#$0B+8nRU}Ku$41fb^9nh|QFT**-t_)t(Otm56a?tndTwfG}M#ev5f6{&Du*t9@Rd21sM;?^>qmY-fMd?~j{A); +)S%yc&UXaqVw}d~zx#n$%14voE0*$U}$06R&OrME_4S+-K2|F*wh77$RM=K$!f4+v-ZXZTYfGM;R{eJ{6 +AL>m*zQsw1EydWVU|;7C+#)1!RkOTe*C%cq%r8)YY4eXQw(WFjgJc8(8lu@pbX8Dt~VSbls-G^sN)B{ +ro+H9{iomTGRYGnPVG$`#B!pws~k}lS|t)a}s*AptM7o;L!2vW^O{W=ap?H8cZ~?XxOR7iVSUlEz_Vu +7qqCnol)CYbfNX_scslctD>6QeL5-?9FJwUX#=~__u9N>2_J-_T?`X^ZX;78&YyX-H{m8lBIu8HeY%> +6oc@$>^f?-d`%Ef6D#v)-@8*Nis7yeOluGV~?#v9jWa{f;=eh)z9b9cOCoUwOXv2V#7{NdTvxV{u~A7CFjN0W0;mho2Ex(oX5IR>N={mDBh$8Az;(zGs4To*R^2Csh9 +?i2wX2~=^+_((B-Qb&;!ZzSJA?bf;CLN;m5qrWQF}A&#<|SjDv#fscRP9etGVY}Sskgn-^#6>OOEHI> +dCS +6TKO(esp3;Q#s83PQW-rE)nuR9%Vc>We-cQ0YM&j;vW?8xcn4wS69#pHx_g0#mvzV3;%HO9KQH0000807zB_Q;MJ94⪚0Q(6502%-Q0B~t=FJE?LZe(wAFK}gWH8D3YZDn*}E^v8$ +R$YtZHWYmiddZH)GRa13zC!ESjm=}6;I-%1%H*W +rP=gF&no*pONdSiMt*lxVNzT0jX{?cw7dUdF0Y|_!Hp!s$K-&+_x`y9+3tTpxr{^E82Wy+&91N%pfN7 +Y02f0X|GMwaX{uI;y*uu+4)90IXvH?aJmFUlG{fcDw$*~)PQ>*gphBGbPk(Z9*Q2V1dklL9Z?bikUl; +GD3N)?jmhPT9a=4&Y%Jajjy*6Aogm7!BTtbiC}hLU)n9LWV%NtHS^l7EyNdq&34ReVWn{vBa#&g|1KF +|G-bMTnH@(5TYcb?WThx6QhO5I1lQn7&3#H!1*#UY2lm0a8Xh=*6R=GfGt!qmV +38o$|PFr*=FjhSzm@Mh7+Cv^dhPh4sfbi;?v*ei?X%pB?c!(z!3RQZMqNu_?}QK~YyX%?N&AhZoETYN$$YMb2mF#0_s)c|Xa-5rU)d&QF`T +z(1T7AoM#ZZ1-YjRi;!zoZIPx!k)(DGl0`n^3(B-qEW!^><_%MKH!&D49v4R?{sbB2CwcJCLZY>oC(X +}WjEYx*x!do%Pqba^}ceve|>hv+yl>l`0ZluFA`YP`Om&#FDs&FU?RN7&P)h>@6aWAK2mnY{22)hJyoAL +J002ET0015U003}la4%nWWo~3|axZXYa5XVEFL!cbaByXEb1ras?Hg%t8#ng50sjZC^FgDR+{Kl#c0mO^7v>`$u +-HO(CmXQ_%-2ng&SE@L6@vLK61lSxgAm^CDkjxoL(#Z8I$*t3qlns@k_H!t!m8+w^h~GWhFDFRD*2Av +YRxcMG?F2vesp-WAggq*~>Q@Pc^|>zf>hj*r}Y(lW9J%KRu7X%BbmhEkPPywoYOfl$&E +$VpQ&Czr=Z1U{1|G`nFUeTlNB`;T6tEdN=DdogGYdfHz_Al%G<9Ru&duUy$61 +lXIy(3ycgYDVP2T63xQ$g3P{ +bjvu4g7%)2gHYafd263o|VRl=N<69l@1r0h5Z%^Be3vKk%SavZC4vr9o7-9Fr;_btifrb0BvljoF>iY +c3Y2;W&&7gzK6_=x#lQWztUa6)HVF5d6B0o=SmN>Epnbw%@mAMnAK>$6aB|qC&JkqKcZ}%Lu}pV^A}Q +pIYAhxXv)NfS5lay-q>EeM8ZR2(w5t!N1n3PO3y4*&(?AU`Xz*4BIiO*l#sc;0b|5Uch9j>MC73a(T!Yw{?x23%)3hC)@cj +4wFMK-EBDVF7-(VP@VXHN*yjmBj|VNW!W@X2v^7Er2zHVF{ZAu{;pup`qm!({%+!iNGpF7PV$%!zzW_ +<)TNOW5O}qsK-@130D9j8psAeCK!RN(gtj| +Si)M=-z{)FXfrq+upze{kkSi%j8KhYQ*-#{{r2y+)@DF4mD{{UA(--7H^2ilX{F)bB?-KG_k}dOKL}L +*dRR$W}NSzZOPHP|Yy0Qu~GWao^X4kU%6h&5n!8m!+Zs%A?om4 +TW=YNN{lecXe6H6{O;hxFaSAh=LGXQ9QmrcpVQ4ap2MRlmxM(2eyHw!p(8!)pomP5glX4H^(`z{nsO7^`C+K!7$NFJw{3EvRPZH{U0aK4utPv5JAnsB{ +JM3XBoc8WY-QKy(j`$>v|Rtk*Dyf!Qm;(#(uJx3WeBqNylHKTDPgnFQi!6Z<4-XxSQCEDb{vwz9gx?} +0I++?{WELX5;PoEiIU5t65}0(hkqx$@@gMHSe1Lrgb216feg0W#1l(%{BX3S +e44A~j^e_tDqFn%`c2LhY13lzp8wO)uk%OxV97Jm$mhkq3WPgNKWpbVaLmV9ITnsM>z%Pp(kx=+mJoi +z=A97kOLHafrk`96(%Tx>t`aNR)96fX|S@2^nTH2xEP0L{fNTQc7igF1jA}l5#VyYAblUl3at{5wvR6 +%?TzDyx3VW3r=6Oj|aS&mV7v4eP?-GGc0aWHM5H84@Ulo-IFr`kGP1sNu1C73P~+0GvWY+0{jlO$%da +65i3PYhJ@?>YG)dGPQgCMQ5_U1}){b#fIhzgR(Z$t4ho=FLS`)H(CS+Kkm6J-`7+(>#98v@6GyJ1ZzO +nckrg!9t`5uwZ2v`*V92g_oh>TO; +C)SnFt(rZjJnAs%~u2WU)K13IvHYmyBg)7Cw%oXr5|Pg%z~xS>bI>~0%N+RYzcf${RPUF17 +7z1S1#ts|NFT;X=@X#SRVZAFIb@%js213$mUq3xNBT=tuj!1##Vd00+3J@Q +wAa3M*$-&Dpfu%7?%$HgFRIhAq^C`(ZuVL3l2+N6$+epVT!72#>MclVUnJan_0;X>e$P#eQmjWWf4CxZ2!{RVo!%SK@Zl&i@L4o5j7 +@@s$e>WYGq_-T_}mC*Up${&4PM5AgNZp5`My^)V4e3!Hqn8L68sp&ZzBW<+9iA@b$+!+QrPC!e^DJ@+3oV{{2Fc;h})dt=U&O!Hg9zt=+! +m*S;Y?B1R1ugAlf7lk01q4|!qOxMf)%HI==>FedC;oexMNY@cp7yM1Zn2a_p*q&L>sY(Uv64EJA7?2i;AJ +Glxw#(t5@C9B@rUZkk`|38uo^DSr`vtM4ppqGoX)hHz;FPUq`v^5$=A=i54+AJ08mQ<1QY-O00;m`Rt8h&R=40S0RR9*0ssIh0001RX>c!Jc4cm4Z +*nhiWpFhyH!o>!UvP47V`X!5FJE72ZfSI1UoLQYjgh}@#2^gDcO>4y%5sTxx(Ddgr9=1I5lNg2ZNSJT +?d9zYIePu04n;@-$DjT4N7FRt*$L!rLDoLn;niLcdK~O32x&$b9pjLQw~|ExWRp|0X@-dyj0+|*4JbL +dX{FUUOTJZ-nbu_lNp=G(fIg=E5J|-KOKw46-^k^Z*^cz?!Ua5y>w=QI+oV-t=yNp;BY1b26F47<(@C +qQX|$S{JWyr2{VL4CdOU*drNoc$LN4rm%b|(}3JI@ias8*RM!XG~oy$0hw+F2tubOiEWX*|DXyrV5se +8W^QArB1qC~a5kmBpA=4qxJZh}M|uo-s!_{IvMcX@%DTV}j?Mc!Jc4cm4Z*nhiWpFhy +H!o>!UvP47V`X!5FJEbHUvP47V`X!5E^v9>T6u5V$QA$JfWN~)!GcsOGn*`mF5GGXXCpUm5-0FFShR} +ErNognG09;$q-AZ8@80+3;_%Sn#7&C|K`fDP-uZjS3^^DKE|*LY{FmMG8ydr35)o0&@`BH)5TxW}9wi +AWmz2yZmc(Qh&2MNHlO^XjA~-xaJUA-}z02vm6vu}LN91RorV$V%ETbeUuEnvW4YMevBQob%S@0x`S% +Jip?3NWgOKDb;+o)jCETPDT$j`gUyFWhtQ~3P-+3OeK^OK)nP0nA8fwWi$IcHgUEI@3VSdoSAPA*@L^ +Qc@3;tQHbMO1=)D^@NE7lFJ%^C9ZssPu$oc~u4<1<#}sN95DsW`#x!j>)GW2tI#Ci!Z3C5^b)#npdS+ +ZdMnxtconWjS}F13=R(lgTdj!qTngHPWX(lH0MPrzw68DO2X_t^(RbuTqX4IKwnj2^banauhKltO#d> +hK5`7PAUS@qONyGSSXL!V>YwYHaalxjit$GuJR%Z13zkp>P*F*?0G@fl@^VBF0>9~nbDn|@zad@5OY% +F*k3_TpGzd;s6nU|1o>Vad=}RC39gvV^*K!eZ!S8?jQQ&vCTWz3cMBAJ7nC<&X$g685AHE@6gXtNB0C=v`w=VdPHhu!2!5bz7#Bhf~k?b6(ORDHqc4J$ivXam{I?=Gq%zu>Gz~MisC{M9>XmVB}-LRILy?1~g4xtR34gsVJ4@rm!Y_ZaWv0GPLuW-O{XH9$0$~Z%{**{ +UGB-I*$bP&4p+t@UPWlsEx7g_E}u3u2<_+F?3L*Q=a7oU9dZU+GFz7kc`Lm&)Qd^oH9ZUL*H@DDTCO{ +!2(jTJCIl>JR&DC4l+k&L2Eo4QH;u=M#2RK59pSv{7bbJ1HB}aZBsgfswpM7rls%o%o(2c0tC)o?0}2 +jL3+xIP$5$RY+b@YLsJ^J8JUVl^MkPSODyzG6XjM(AM8j +2C!qg;aO|s8LxKbr+T#TDdy=Sf?E<2n+hJ3bI6mAXca0YDsD0gC7^{Iux|TQY)OCJd@i7nN3`l@hI8K +N>)c>^M%wykm;3aKBn5E&p;~-U0`K3k`rufSBgHcN`^Mqf^DibpnC$&W3#HH9rP;SO?4(Y^N@2FrFNX +Q)7La+rA&keakt_2zv*$K-M4c(^dd+B8m5pLaa2YfkkoTq6}X$TdOuFhaaMns2RJ=v!NR)PF0rR-o2$FhWRuC-(ewHE!liYu(!M8nB2(8>FYs3u-gOspg +@=TmQ}s=*qtz)D!*DLptmq1l2Ok^!5*<6cAys`%H?{;M5y(erz>{UhpwgxyFdL3ZApv$tlA@0Y>oPFW +J_H%_D&e+71FOyp{AbFGO0&P<38o~QLtZry=U{9j6FxQ7X0zk&UdTn +J=5Gtr~9P(ebX904^SQO^B=4bzrLJIUdw&OW%%ag;??8=Tx+%)C$BFiColf4Z%b0o+1tfTl=6U0&f!A +3>vxvB)PT;q_zvI6b*q@$ +0h1mO~XPF)Te#sZ~{-hdja2t_E4WpI#J8<2#Y)-m%M^DkVeG~28Gpa3B4<|sn1b1N^~PJboA90_=cA} +oGval%EE0-&9kKk7ciwW|RNEcpr2Ch8_k`l-^D` +`l6B2PDoORJ=0FvdB|{|rMIKG$OlMxj#BWpvAURRqLqcS7%1mL%%=q%*?@8?`g%gOfW*gJezMVlB6Ub +E|v<+3rR`;cwf_+r>%a$hoFj=St1&P$$ERaMyOr&dMWLW5(8%bS2N%gq6^V +fLQ@|BWzE0oJK`Wjc;UHu81%z6Rbw}sQ?x@8 +n-1|>yy*_9{%x<$D7-e>s)pJJlpg6Of9~^35wUPRw!>3K#q1H70^*OLT5571T?wt?L0=YN%5)ke_?i4 +sn{NEofODn(4DW`h48osE~bX{*E`W~>Jb9|I|4RGQW4CC|`5>UY$lT<4TOQdIN)_>*Y`%F|h&e$}5n^ +VZ$O(IS?Zq`a#VAv`_)ds+LSPB!QkK2Pk2d}HDLY(}6zrD_u{w%quy7`6p(E)9irae_`0Lnb;(VSrN> ++>sYTwBl>c8c7l0B?CRcX;sf?b}!3`N^BfFJSm5#nRiBsGjbX-mW>t682r&@nB&eAsyNcm$#*MYpuQZ +xdvYv!FX<*t%qZLs2dg6w?pz5`<9UeGhC;4u&WxJQyPn{ug?uM4BVFw4BzQfT}2#v_x7_Zt4_UKzL=7 +cI-tFZz0YnkzEYLwn0zM&xtVda1~}L6#6&B(iICSdnjHtyDGuZ)bFsvu=t78UIyvl +%C;XyDYeu3-Jxd9zUS++4rVvsJR8rP!Ucbo&mEBYQ)w*sO#bqpvsJJ$c_KQie{yIkZ)#rU@-fwd^`Kp +q({d?7{WU+ir%{i1Y#2c`ei%jv&YIkaPS@7f^c!Jc4cm4Z*nhiYiD0_Wpi(Ja${w4FJE72ZfSI1UoLQY)V=F^8%L5 +L`rqH@cc>=E2Y>{YB)dJ+BQM>nmek$au_dk~+tWjFfdWtqk4^d8G3SCvKS;c`1;xd-j +`y>9Gh@R^%-*GVXDl&6`D4Up~%u+vUk0KKustjJc)Wi>BW*|J)d0KVQ9Rb4K!n`(Pi)$n}7(8pQRy4Ih5((Pu8sx9Z+rrl@TtFkSdWu9ekuS)ZVH +=eBguz|*%`>`(8W%ubP+a|jz-j@iXYT?!G75t}#hFuB&7F!ugHl040PRH53y4l{#Zm!_L`?AHiU`R9| +dR1P6p;jG?=2??L`)t>hS<~gzqS=(tpla$agRa*_U2S$NfMy5w=_fR@Y*xZT!&tfwhEUbe7(0Wh0Brh*7`O!El>P5Dz@gab44`4bkw%NRZJy~@Em^N +rttt(olVoyB*w4bUCzeK>O`}wZhHtX!{`C0bG!_Q&SibdJN8o)|s-EOmK+AU3RI=w8nlSQ#DhNJ29(@ +)mr_NrOrf@+_BvTOm$WU}0CcWpVDWYwBpEoNP_+HK1Tf5(>c1SV_$Zrq<$WB=S%>(YK{-Jje2rgSgBo +N!9|yXi}QxhdKj_RPJ~Z438wxvK$Lo0WUKxxU7DDY2KbOKEG@@JUs^AE +669YM7{y@a!-`w7}*r`fAF@`jmMwaF*%%X)!_T=u#85I@Y}*L>Z)heIIEcI^uGX$*&cGsfc~OK}W`YJ +=E}_z#ouaEnW58x4CSy3ytE#@{mOs3H@%H)T&39*KuU@}}3 +BGN2<-wJ&;k37a5ayhYvrXH~irH!paN!hZe|hpe4f@YODoVR@Zgha@HSSZ>P4G|qoJpkF%Z{ed0fKLn +D8v82lIH7T4wpp)B+E+kS(K|{k5pIIvYcN5o$l6MuKV| +T$R`tA>B;24N+w=|HG0|9Zi#Z!=Vt%KBS!Onv@13nSfQ@J0TXyapNX8l$z(XtNNStpaGiBPSO%lJG)7 +WTEh~8CZtdIh#~s|(>$2Y7@r`xSUJH2qzsUqHf?~dcV*y0}lxQIlP^=2NEs$Mqm6F +&QLBsff!@#Q;FYio7yf_+3CwQFC{`R>&6>qN^fHm8}IfiSHH=VBa0#0&M! +{JzzOJZfbdzq|mc`mWn6JvB-fbq +fLG;Q5kT?D@00U#0z}?gqbn8usK1A;jrAUH87RvmNSK0&+u-#8K4J<-pL^!=9D7-B3e8*2px-9JPFsY +7-3l0}{g18*}lvq1J@^S(cWwGjcW*9TI^SGdRB>?~Q``~#af>0eKvDyY|Gn!QGb=fF!%@E5EU*7o9IhAa#TeuIuIo2g$%Jd5i{E(mj8#-PK5X@1g3U0m*m;Z|^# +k%A2JXu;~>;*?^s$S6f;UzJ80WE^mP9Ci)so +DI(HSl3eb(T;VD~$`6W{<>+m1omHhigeV}?vZEw=5h%x=-%EMP-`*=}0Y7OnRB#xT&3asaL%1Eo=t8z +2J!Omfo+;eJEC0#mr1Uk$Obv;)3ujT?tI7NR`q8;}dM<2^mijuu6GQ`JX`0QvWl8q$Dz{c!SG2mc#np +Jl^AKFI$L{K9a;kLZ#cj?N!{elY@BF28J>-Ddd3Xyo9@_4R$R+IhmI`5e=D%_axqfLq=pCO9y|0|R~$ +_j_)b&_5MjHFpXiBj3OxXg=ftaXlGvJ@{Y_QmGM&27-ozG5oo!C*8cQHe38gC%J9jSHQxU?;7f0ED%t +aqZupHn12Fk*Z%<-bNg<@l-VZ%8%N{wnMCg=!=c6R3sdK{X0FZqiI_-y+pO*L`hC?lHOef-iZqjLvl8 +HjNw7Q{f>Qi5e(hnM?EsNE^cIxHlVEu9=ZKxQU}#&)WwBdrCv%W1OFf6PwgN$Fj{@O}q%RsvM!aMxcs +5WI#jbwfX}sZCyp|5XEovZTZuHMQOu%+l!o#o6aZAq_8`P3oDM_Fp^cBINbAoTmiGSMEOQ4hlO!o!y( +VrU-)9paMtC1|$&vDJDXS;X;(sloXRnXC!9xv^!8n7aGKN#x9CrZQ#yjy^n+SOyu*AsBZ=2s(AsEg7p%C=Dl1Wp~Z(GwQ3@s|>Q*de=NesZg +!G{)iF;SxqItX0G#B);*hpGv`92S{&YcC>+a%0*ev%OgBoz~t0hi;;i%%jDUMH*XQ%pI*Ft^7?P~>zh +~Ky?*-Ketr7a=THCb)pu|056_-||NNU*XY$>3esm_J>?k7vcnOCVZZZF0i1xcJFL9{!(}PvhT<nI~Lr>!PBg_~d<2tqM3-&wS$h=_eOj)DCFV&=N1jHb0qlL~XKJ(UrtjEy@kDKlOZ1*D?b@^?TE +HA~EB;ELH(i+#-Wtu9l-%n8lw#5)h@A)i8p{gzwQ?Idc0WJ?emb4ue@lI)W!?&w<-?u|KKs5Uy6hx)} +~|E>JpV4UxS>LRWTM*T{04Z!8N4?ESVP8nAFLI_Mr9!1Lz8Wnk($@zo9Z=LGE+D;AEAex`N6d-NCN1Y +I}KLBP{d*v@-0+iU0+2k`S^shcrRU7rx`8JlqwTGl|&EDNDp$pL!GUGIujfQorf&%nbYJFI#S +*k!c}?g1_$Y`DiqXvxdJ64kNi~cRR9w?h)1$J9mIiJyA4Av&$gSv*5Qx06nuF9-* +at;OM~M0F3O&M?A(=q)>`@L`&6@?)#Uc(QhRQw-FWY>|;pFAvieb2$q~he|qWTI6y=!HmJ^^8+s6p2= +7`Joqc$t|0K#c!71Hxol1A!Vb>DG0X+1stwmFI^V=7DCka_UKJf}fN_5 +!(X?2c5#ZE2cBsr8*IQZ{emS?VN}uW1q1(Y3*&DVpkpU{nJ*YiBhdZL)I1gdk>zgo__BDr#7R1V-k-1 +3egR1FKfM(QMI0+wnigtcA6vvamUiI_fthp)Mv!{c{*$1Ro3BeZs)cS(=Uf;z|ZjSq7m#c(oF!W$Yxg@^00S;OI+ +Z@%zb$u>eOIDtF78-Paq{sOtBRROgBC_5TwN0X!AH9?;vV>4OgpDj*4>yFTDk~p;W*5EH7=xFTy@Cbg +0UrFL_(C(w`F1XUL#b_x)mY$7wXn_M+7q^&KJ!5sJ*tp2C21#THYCx!`3YG@ZIA6=v_|#oDB1OBit4q_`%n?iT8R4<(SS@piTSq6OFS;vz~%7{IK#|DOb{6}-fUJGZoZFeQ^by@ia&I-u_Ue0*>r8_%JGUx*AVwGzu^ +O6XIuUI4ta!4ZBn7XU04|^aLWfzhP7>}754us5=ju-NNv+Yh#?Q@08$5~l)Fp8opBi@U>IgO6g8m)<% +q8Y2Ob{_zYj}I7H?QYo#@H=WKIMQ37n8}RvwRX(pnz(O#Uo^#kj6_lCAUes0FClT8!D275Dq%|6K7rC@LGHjNBlzRX7sW>V_I{XCC+ZVboy +<C(qR733+DGnJ$#pUD*O$@$cBo9c-by=|0HiQ*^%UBXI+{dpn_eu_m2Ho7k_V@Lx&8SZderXd?XhHx +67OT?lcQlyl090H@}GA{37Io`{y9clO;w#GttP8m*iA|68putR2xO0v0-=n_RhyfULZ#CPw_ssa+n2~z&jaj-*`x6gaGqAihl +H0{1WoG_Jp~FmsK13li+B2S3R^3U_klMKVqr8SVA56Jg6Q~*+n3N+N5(@(bDRZ;E}JxEl42XC3cv`A4Z2t8#nD`M+O7ALptNhxox5--_VN$(K_;i +9In}2Z){(iBo^IX!Xz(yk->mxM0mW{rrj`H +sL&ngXj~lWmIE4h_pNLkX045{Wf5NblU>m5zL&FRycY;)#*F#e6yU!l%#iJ>XqW(IX5_f92RGLn(dYAYgVUL> +JVku0yq8=DC}z1#2NUCK++nb>U8KICWAizt)EFeO!6kDE!Y27Jka$9fg7F~Q?IvVU?6$`K&>4SDO>gvTvwcQ(Boo_-w+Z?AWqwPGqvsbRy<|V)@3GhW(5 +{0o9b;?S>#kY7$9w2USThxJFx2`(@}Na*tXOajk1_y)gKQ*hPYJhJ5DHUZHO$OV4P&4Smcb(%=5f%OP +^LYL@Q5aPn!vb`B}t6z7KNTQn?0#ikh6l|g&+x4=EtK;DwffVk*%z4CyznJi()VKU1Q5K2NMW +HA$4xyi?Hs)E7!NTIxMMAh>XK)rXd>FDyqEG(4}l2o>m10AkO;F^-oe&4zi~G(c&ty2Ii9zmK+hd4dJ +t(zO4z`MPZqbcoR8KV$|T>4eNXgGqP$KpT{L3U!@`}15`wot+Aokf7jkaE_jC9%LX(5ho|*BHJCpUQ4 +u7Cp8>YwNQ>3N~(eImy?|?6JVl#C(tq$=V)fEk%Y5KMN$kalsAr`N4+K6c%V4oxwmoh+Z%i-$F$@eQ# +Cz!TDVj4hv)AUu_Rlq&_^iug3NaNuo5K0mDNY*3d1Tk5%jWv;_)agThhBrQoyD38thBR*ZQ{U=q +6JiZ$wBi|^A&j)lzaL*n0q?H(9N~;knK}X&RJPkwwFT9ZCe6*wx#Mx$EsXUg^|-_4K&%}UfvW?E5!ACm)YPQhUZ0`3^I0=%Y+hM!Ct3EGi0w;a2fiu^lDX!7<4^mwQ +nq;3ofxhA4h{79(<+kAfbHLt(mbsKv~v$5=1u1N+T+}M@$vPK5&s)EfE9-&Dp&GZs!N?_#PU`Z;SG^cbJSuiPet|SXh&1mVDWizRmcPf;Qnp}XLXP2FGN +~J96k3(ke3N1ynMeO(MPF_{<#3NqcEhpCk3+T_rT;?7Mw~-1#jfH6DH6{@2uN`}>%xS!+uX^uVmkTs3D+4?D}C?7MC@iw$KEVrLQ~c6 +n`=X%$aJkmN-}BAiy9{~BZV+3AGLlY4n5by`wAy5D`!KHr&I0nf;-S^%@j*?-WBkjPli1dBa%F3p$a_ +*!r)z-*HQ1Q8h@ZPC)t5xNyS~6ST9-#lV+NAUI3d|SeS;=lH*>qD6yMB54cT@EJJ&N*yFZ`+y{(py0S +SS)mUYJ0AZS$)mfPV1ev4s)*&U78)^Aotv7{f~)NKKVMhz-A@sNOa3>W0E12kqHnj2ZAxD*#zh-h2nO@K!g4*+)_xTnx#) +=8Z!%@9{OSP)%Gh53(#u0TM@U%A;AckQpOfDG^W_h7kRY2lpo<#=i<(VFjHhS4m0MEgauGA#AiLCy9-?|ql0X#$> +ux3_!mS;bE&5S_4=TS;tg@DN8=`a8?9S{2jvNqo)*s!;i-2a65U%6F%=nDCSaH`l=9dz +`r2+JZ(B2W*Y`O?X>X7o9bLItEC*LPMc{0}sy{^+ygzu`$N{dE4q^4JwkDuf0RkP_h%gYh&(NWel*|L +c6u~4a_5(Gz{p(EOl{)!+XT*PnOW+%wKL_r?>QAm2>irRHoHoVE>Ga6HxBw$AH1C1vEQ$ix9(XJA~l$ +#*rO%x9B6~~E1hat?wU~Tq^S{U@&-c5fY0>2pWmGnf_5Mt)MAOoki7Z1 +AZh`V)enMa<4Z+=EE1sp&k^2V%W-a?+^Z#U@pKNzPW*%q5skd}fv~=L9qG+4x2zSl +1x&_T+1xjcsM2%8J?+i4_NZyuoq7iGyfXJV>vrQ4yZC^UM?XO8+3kQ{b&5?sm#Qr-I6Lc;+@TEm35iP6dSYGPz7tj +$&wE^dbuaXzMFX3a|0|GZdju8LW?CAoW1w!P5v&ZaH%mVwLM_^K9vc};FH^Ayg3Az!_QnvW3iu<`vs*7mh+e`EbM$NvqL2;C_YFrEsTX2rY6psrEqlwmSt{pSGRi1cEMlK&o7nR@$c2Y={e4f +zSYrst1s~1WX~_b%I7`z-X7zo#C8DX^2?vBkc7kTQ{$cBrf6;6Hor>1=0dUHu7D+UL2$+!^&&ue2scUef#P~Nd +t@)A*5b3<36U>pHxgEl?z$mPzltr_;Kz{j9FkKtQT&7tg(Uk{b0b^l;-4JiGD_~4Xf +1wpA#z{7{?0h>yixVQQUyn*eK-853x6g8QCphLl?}Txq%;hJScC7w1+xEzVmDk^rK5i^(3`Ts9+4EcK +-Nc9Qnm?QLM3NxscxyknETNmU|o~-Sr0vxH-9=%qCtulyxJ*L5wr*c%12|KzS?Jb{G8TW|e4SLlV^_g +Jw21(VKN2mZ5>qo4kwvR0hgLDg*ep#U|kj(Rg-ox;e^qL6Is|BE>z$&1<8Xb+0+(52F-dQqRKx5liQf +S#gQljxCRnt>l4|n>Q@?`)idq#|YC1@QMuP+zs6?%c9bbx>8w$oEelagG%nxM>A-26MP@HV;G+1AH}M +eo7yGAp9fs<-7mKwh>7jdP@-)Vx&eST8nC8Air6wT5ccUTO^`PdSWvQ(| +gEl8w8%*mPG-!jRoi1Ua=Ncapw9;gmL(U!zhiqI5OMl1}@I!Ucq2L#0bkO={`@@NfY~M)IUEQZ9j_f +yQYd4ktmiY@#a5hnk+if9y6-UXyys<_E^P&~D32lm<7L63oFem}N%}7E0=pYPZ8OzszC%24Cd=JeW>L +$^qlRXu`50`1xD2Baa_dJr8vQYUPJOc^+WQflOe;@qg?MhU9AB>>NBGL06oIe3RV+GJh|W5b$XK9T#~ +^RepnY_SI1)i>S>q3B!;TaC1>dCb)7ah7Jdkh?-YPH)BlU7TnRP9Mj+_DxHSvPt!G6mkdSfaik2VT>2v-Q?&p;o*fqn(FyF +5N^dt{`aYrV1|_)SvnUKC)P#xew8@it*wu*Uo>rTLwcZBFh=N4ZtpPxU?t#+tc*GKS{PEIM(VhrJ1-8 +pbM(*m)q2IdlbgHMSqqK7b0X^hxslJhZiG&lrvOXu(lEuzGh_;&jjC-tcOiRC)?%(^@-iqH=BFmTT>0 +svp|1`{=}Gt5`CMoSS>}S`tAAbwpE9AJ>U^{w5tWHNEAV`w$TvVjeW+oAm##41h!tZE?(&53bVT7s3d +<_MbS~-T^j|v+G^%3KIs|c1aBCVD31=&K0x{fP9lVNEfD>Tk4cbl3^+m}BOrSAIBY1fmZO{zE)d +<56Avj5X430W?fgqSe$y7BDaSt2VkQF=GF!IFBRlghf4iTtSCSUyhfD4jqL%L7s>l6$A6x*iFu{;8-J +SMOiz%;Jr>&<|Y_u_mw7|Gj!UI#KIdMa;(*qx&{gd^qJ;c*4zhw14~Ib>AmIr1K==c&(Oh>nGvZA8$7 +Kh%T2RJps-BQ2ZK!l=Sw2QCr!_6OkfXqvLoku61Lx3xpvuXt$O$0(1e|L8j>pt^!@ioH(N=++oZKRag +trVcz{li9U!BMnF*`r8q(kgkZVfnZX>kL_rj{F^sV}PTVx3IyioO{+;q0`4MXRY^A)=d{k6J@mbJBYJ +zlHCEu+-)R`wZ2NpGVjXwo7IObaPjK%QQ4piEyL4{foTij4cdD +(t8|&Atz-k&}X;%FfddCg4zfiGS#*%4q0)Me3!o7ii0}s~l-fn(V~2A%kk6m{?W+v>CrW@jCJ=4!ksF +G~|5HvI6nSeRmnMAYEas4m+HzfN8*UJoBnh;|IAF@#$fI70T$-n4RQO9l^zcDgDV(j8A8t2dW8JR#Ph +4%N?=viGWz~V!HKAq}TA{5&_`2c69~fLf{i37ebHwnK$Y%wB2$bWXlbi72$Kv6UP-l9Hp4BMLaQj<#+s;u!u-d!F!{|`)%<9=YbgWKz=O_)cD4bb=9JuRqi +j(&Sd&kN3$1d(uctfmwFK)Cg2^0f4R_4wiIUA?W=WmJ-?SG4UOb5ft4s&WDt3M*_X +HD`cQFZFO1I#cD!J;zLTf^+{APd5Ah-Es*j +9FiLhV`O0(>H)_FN;rF@-e9C!?qrk!{^$ej}M2FY>>@E=ndhr9v==`Kh4frP*Za{B)IY4l|Lt)1_H%l +-SSCl!n~yt38kJe_J{x;AZ=SPBd;RMtO({9Tt^>NeQVjcFDz5O%_dW3F7*b@IM?mg% +r|*8m^kJuSdE=*9E#Oj^i*g+P;wl`Ya~<&JE@PX|Cb+GjPb%-h>P4>g>H9lw*P9i*jUjzEEr@IT10V|BSm8ih(zn2^4G +Yn0~XC8*9cu#EPbCXz-l#xIY36h%Naag0lffNFUh9)?^y86fIaMW{M&B)muV`w;46pALiNo48KuvEIp +F3?6}#uq7QWkax*N=-?g0=NP;SC<5caY{49F(qOtO9t^w=%V2d#MvLWhH*EnfOM>Nw)0iP*|Fi&3|Xg +xDr|{-3cXWY`=6=}Lj9d~4q|6q%9{aH{wN2SM;Qg_O6D3_pRuld>4oKK=Gs|KX?18A8?1Jl9qnqk299 +_{mzRT1bNObodV{s3I|@OxnbA1-S8gI#hu%6edzMv6oI`AD)GzC(3HnkJZFcOGj5%d$ACkO3mVCVU<( +gUK%d$qRjj1rh*GnU(7ZX|5yG;gSb=e+tmYZ*X)%{^$Y>Sp|UIt-maQq!;%20#%&sBCm8=L@b7>u)fH +28xh4JNZax-(9=aKOYKlA-E5R*^qf60n9A&XO;2??z<(#7)uGlU>~v%W@K|iZ`|RRW0$It3d`yA5GA- +YhX(XpmS_Bk7^1W8WIhq&$aV~_~1*qVTF{&MBluJh2Xq&h@B>32Fc)<4^5E-hsWIxhyftx1K3M)7i%^ +p8kslj(uQt=yV_J%#p`8@@&2f0X!J6eM0#di7+G2wzbP_;6+2jow)zP(F&BGOb?4a+h15UWby}@~cC+ +ldZf@Mr6?snh;=L(V%7-#L{^DQ0r14T<%A(w&Yw7`42S9m~DtAzC2d?-wo&!~UyTQ!-bOWCcZZKB09b +AO!#beBa`2##`FyF?eY^gGVgfz&3Us1{@94;IAfGo+jhOjt?e7WA}0B8>0gz+ISLM*_{M0(;nV$$9w0 +>lT9Q;or@W3kzkn%n)h#^TIG4iJ+WH*+)x5A|~DYpnKv!eto@)t`zaQVrS5_O(Eg)~S6OQ@bN*woAWDZIdo@%-t&AIwp}pODyTcgOGtlqfvx!Fk$TnA2%U+osbbbsIC~#IMaxry-1pjENCO?@GM<+2+C1M~As)PWIlyCZ%6{CE +yK9FiMdFzF+k|pw(L6zc#`Z*NvyT+j#yqH4DtxJH`sv=Deu#eWofTEuryrC!P&KrPRIj`L&MStC8L^#j-UvE_X#+fNWC+aYKFG*<1{AL$fQegb;dN +dZ;~o*=)39QDWP8!`4_dHN~X4zYW$GI0eF(PR +M{vMAPD_Rmf0qg5z0fNboWy74JKqeC~7qM&;Tf?;ALNNl-WSO$TdiSKFcVC_{ZuuK+iMEWHJOS;2GGS +qSd#*h9Aa!4PukEeSO}U3mZX?WWqb_JcJMF_oe8+NE^Gf>4Jxaw6=t7t098=2!HqJ#*YFt`k2hB0`fv +VxgxTz%swZa`}btdh^K>A(nFnKjmfehKGRcX?F#a=d^Yq_6qh{y6(Kq0EFj;J9cuOv?6+Ce%3XgV#nn +AIOnv|Td}tFh5EQVh_jE65S#;QWa3wo!R=lXWPGO=RY0o3O1f?_WV-9Hy$OedmjZ-{cYi=B}}U=_pe< +wXKzdHOP@TyyzGwhU`TP$u9GCmOL`EFbD#(d_P5zU*IkZ75p^e8KHz66+cQv|n>STSa?&- +BkuWGg{*)cYX*%t!+R_appD{lOStiK@iYJdM}3{2QUi-REqab@^cc##eD2!`$J%UsXTxJ( +Y-MVw>RB*tJmpHeq7l2Sb(}6eU>P{zVN;S+$VY)G%yJk&Qr;1s@JFT(f)-dk`I7VKby)k?G*F2JKg&l +LYME8I$gcN0Wgls!i!V@ew8mYw{|zkr0cIz_=4$kjX%TutzQc!Ys?Sai;jbQ)b=H8kekw#GY+Kx^x5J +r0nL!hH2zlFAr=vUfXr?TDIm_3)FJsiJ(M-xg6U$*?9d!3z&NsTanG02Jc#vEzg(^B_7?Nd>jmC;1J`fSEn*b2Ph9eZ;0i}Sx% +>uMu-Rj_6d1w)CFI@emR*x>P8C&?Oa=M3(LSnm4nzL?NTdNKB=TEHPvGx<8!o`m+#>9D_7XTpBb~Vod +&^$xb|sWVfz$^0u>y`n!TfZ828oJ1IiKT2vh_anc1o737@HXw3dv#$fEfj)>s +ZinZ7!p|m1*#bvRdX-y-mLXt0h|VS8;jtoUgTcttmZY7QWZW#L9dI!5!poLYqeu-de6_b{&&lb|{Z>u +>r~S6ww1-FA8M2hD~G3IDFHu)#BG=I-nd^GFf=f|G%9038X`B4>M!TBx)`Qe7?gA%Jb$>_FxNNxs3ml +eOTcnmXxPYzUe4V&3jIWneFZhrmV^9X!2%nhkImKmQ_n3N{@+h93sTb%wRb!5Y&taqw0eK_%>vND{<->P(1(O<#Vs +_WK`Idft`zR{E-^bHT;X9n}T_802L=0~>7SAl +KxszB3(u{Gh(7NkEKh0~vqsB@adCrqa1WU6=VyBb*__-{WL_f5ap`F +u83@k}g(-!ux#Vx{gWIgLb(kvuNKGR1SY&Y_8vyhI7YI2_X$RRraHFbZjIIUgbfg{c~8)wL2wT~U|~l +1Zs3OZCQ#*Q+-~{OLRx(M8%-VddsclFK-IIQBNTgF9$Dq)m-x*^6>&JQ3~OZhJ|tk8M>zs$gvCI}i$lm~7-CMfS +kr{g3PVjDxj%Dhx9eSqnU%X22=VchdxGw0*MAaQfe3db4s`P20b`=BeGQ(tL+0(_+-ukUstDob*>Yv$ +TIakbrUy2lS5bTGutwrN&f&K28AYFIT7%K8DiY(wz#shXddA5Yj>TZ-|J` +PU;Bnb+?KDoR^XF@7wZO^PdWEqT3u)U~VISXq53kumgiU}4&=@w(br(^c#a4%S{g2`A{}RFzX$#yKMEwfXXOSEnb<1vhYeF;qbu>MgylZ{{n*0^#da7C +Y|QXgq?jbdxKh6ZZ%)1oATSQ1g;qj7x6V!55HbI8+@u&Jtm4ZKZ#Wu+9Ob8<7|zoe43O^}a!~Ks<0t& +w$CsDp_o3IwSyjwW&SS91-dF8*SFDt?YhUW)KD&p{;Urs!>@V?i6F?1MxQ6~-dym3U(PK{9EQ#nlY2&Q~pZarrgpc!k77Q-TTAlY|c5x4=mMXULjVY{w!r(=ZEalVqk +&cFuc$cvs#wn!MdonivCnGXf<=NCgTcq29bb9ho23S0yLjQKhPKV3fbrbk8hXq7AJyjDqOO*>F6p8cK +UEt%h}0yT?W&b6R;Si6joQ?DNc(=hqVKs&%z0TBCu1L5Q-}`jEQUNm+}xXkfc+Q6#*r7Wp>#XXE2-j)wPm<0HVD8>F#}>9`s*z{G5_!arDVBP<#-Cad8NLV-tnqKq>nmMNa1o3 +%E-9BMb&L!xx5?$wz`obO0@HtCoIfzKppSPheGomKlVQb>35I;T_VYq6rt|ph +3ACM9go8&({0(zU9u|4jNbe@`;21!rF^edIrw9=sXERms#(;zvh`>boZY{eIgO8nywI|6DDHvi@2IC1 +PeuJM!m@xsJgUvzgb~B#|KQ{TGO%0CJg`iS)pzOr~`B@|q@o=#gDgBYRoCHNEpoh$+X>xtij2~X`5d< +ATt{1*X3&1a(uMAIXTdR~otOa<|b+F1kR&xoVCbg1TQoIyD>nNcl_cHb{!PkGOZKr%3vftG9z>WvID4 +R?9dN87*pm=wBf>0^eC)bcRUAbE{C(AC7#O%g-xO?A@=PnCxr9y+;ciaN=9@dI_^!P%AzX)gk!MkF6O +>E152mWpY-R#{i$=U2H7{~IFM|X024F$;k4|LoNJ=s#0!aJ(8X#L`mj5zShRs*Spt%YVQT-utz`X2t#infmdTK2*1Fm(w;}8qXIL +L@Ye8Q$p-jgm?kHR^$@i?EYE3xGO-?aquU_a5#MIn6@_FuflGDL!SzU!#m1iT8e?uwGxLZX`>D0w<^6 +hbweIfUEDDff_w?U|4l<-}gwwH*+>iIO;X`ulICj4D{5xM?<3 +B?SJ6=Eg$~fa`s3nP3H8_WwI(xWdvD9Xb20^6!5T%ROxZ<@tHxN?kkI@)p5jh%TTCA{R_7r@k7whdCz +83s+Y*)<-W%h$alSDprW+#J|ms8K*2k)TzXlbq_s&8%AX%uV$QllSUzj*t6^5(mrL5jH(a7(Wr&*WQywH*>!Bcq#v6hW5UGd|2g2; +q`gu+yuxV(=lIA^vUA3)4(9Ld(sZA)G1L}3w*Qit7r0$sfp9GOY%MpG6T~0&Pv7H`pBBU{NIb}8(@?e +to9!JlkcQGj&zZ*Q;5lP{>jkBRbT>EDT&g`nr{$c>Q>4b}zEDF?)=qb!mRT2 +I>b-+(jbbL=9_XXPBJpUIxWw|^z&<*v&4HI~zwHSfzFWeJ1BSaDiaPyCM8EkxNgG@DFnQLe~JVPoL|L +c34DMtSjryjBO6fGNvR?_K|}d$;@7&(w2*r|}=e6(sv4{}mLSeX?w;vR(ii7^EV{k3sdr`QrNMhZssM +YKhcaS?fXy!8}FFYgh5s)X%lAtCXd#sA4SA_+@8)`@Lj9$*mL|IXk)@@hEWXLS9KJh}_#=Kg3aWTN)U +ufaqUT56SOVH)N-Gr_PW0RI=*dOFvB2R(v+yK0CdI_PW=c{YP!NH3EgT!>NtxWf^%u%PjoSocb?Du~V +T~5Q=yjvT1ar`YeF{i@Q$JNEled5!x+$|-l~A`V@Vkd{!)Ae +>XvJry`h^kPLZ#|N$vczVulFHH412Hy`IKM8BR`6T@Kdpbnarp|z=*$hV)35>D3 +2yNlteL%f?!a!)z?3_|cKag%i6~HMw)?;iFX6^9Exw +P9=VL%#40_dL)MIqa;73P&a19_~cSLW)KMk4yvDr=Czn~nhym+wBz@c$Qs{xE9_*9l%%fs?_)Nj-_9pY0y};^$an`vdwTQ->6U`Q6 +(;pZp=vwX-{PPYD6PX_T|J7ft_>LD7b$p1~X~X$!oQQdHFvH!>JpUg~sq2Z-r$_6!}1n+;_UK})gsSo +u9F^2)w}FVSxU&1l#_Ei-y1PS{&1R7@Fgqa-!=rfi^$v&2i7{VVk2BxV#?_hIDNX#tGRA71!C;C)??j +e4a{b#>`e+nqZ>r|L}V)%>nT&&hHTZb7uFLa39gdh)((Pd0n}Z@HaQL4(^D%m57(*!E}Tq&hmd2sy^3 +I(wFbo0_|z;Vsvc+Qjg}@_pWXHAContFvJ!mu|ZRnm@Vih#*{MH4YEoa6f@MJ#yM>2w(eYAySQ3t59Kt&s$(#TmLJaS@*uDMJPbNUTS0Fr*xeG20bhv +`6b>h`*#{$M6L7)Eo%gE)qvRo`bweul%cAO;uc-}LTZE~7uH)X$&pi(%wLt_mkmW|LS%)YnP#x +E#PG1gK}N|TD|=!LO?Dt2Go_ePvo$LWpLMH +vdjCqVr?BH=M50HqZRnw>$&noYysQ`O`7+3Fu2ut|1HHK_fvP0Th~i +;|336qt-Aj~6pHl8~u3XE`8Eib@EKt!oG#Cs?aAtQ4$FZ%D{kV!WkqHaM`*HXYNW;1#OX$==3hH;_!YMxK()iOPJ_@qQsk|82m_72Au-so +0Uqj!gc|jzh{9bAX_G`j+?6hwM8!RWFaduWwCZs(+ne;ez*=;3*iP)59Ht`5_ZLdq&e^{4em1AmCId% +;LkGu!f(zXerPgE7Dv +9n?hrS1qUkaUMR8Hs=$;E*Kld!Hqhh2Reke!my4;_t6~Z%zf0n0crVfdSscv$2=H?bIkD`1n>m8SbwcwGFvzfKo{b=FweD=1+D|08x +03?X2)yJ>~J{?an3Z4=mzU%bL^juH9;j8Hc1b&VIKu#(_a>|LgbzG&tCLEkE0pKidNLd)f1SF=8f{x8 +A-Mpo`7}5!O)1!AS(6UYu6r`w%M@ZD^6471tuX_<*q`SGyI7hQ@Klz4I(|zvfx62Y60mb%Zt +l$xhWL2(>NeUYL#Zpqj(Wy7V5+;Y!96>b25^K9(@Ex;T%Rh+`_OB9JS>#XA!^dwghHOh8Z(2neIb2N; +75VwBMz-@E*5WsrAcJn8lrAii>gEZ{H@)4^>V0%w<(41YkxV9v1a}xGpwBdTg1*aRzLn*;>kOuXskox +y6+iJZzoN>33Rvg?y&_((^ZoC0_3(@}|K9gY)&cc;4aw8cG7I_a(JL|Eqpj{?ujS_{t!J`LcOdN%W9C +88)#sKMS|e=997S%FAAiE4Ct|uWdCPm}+ZQ$9bN-iYEv?5w#lo^dytcPT&rxe=6H%GQs01(0nB#Dr2k +?F@^KSypQbtaHOudh>k`m{$z`;qEt+ixK(XnWOXZ+VnQhz^RxhcUF)PIhZCarhREYXF`Nz0Y!ykeu3@ +h3?UQGQlF@H*Ft7sf>P<2W{&Du=vTjGL=C+sjwaU%rjpzl||X1=mtQ2W3^5m7>jCuu|iu*k_N!L_@8Od +50_V=mtaGVXclNAv&5>KmTV{9xisDvvW+DM3Zurp|w49$iqTOV-cEm6qa~Svmp{o1B_(~ag65nF!HRV +0CK09f#zRj-F3AICKpxe75a-})l6($8L7g&t?^x5<8pMUe +}Z1U{Io3~?+jN-=C=s-$A_H12Dk_={mv5W~XCpBeLh0YKiyya(`h)mj)wg>C`;q@LSalqeC>7P6*>~4 +^tio62d+O4Wu6|x9#rt~b03{UJ0$NAZdX>4Gm_(LR=;!wsZ7?&j$+@jLJu){$&?lIk^$fG4U!L+7sU0 +c8Gl4HP2ky@Vao`j{DZ`HUV(wl`blPWAvUz4FVqX!9((UafHBO%eDgqoDqoM{$Q&iN73{f&T +2g4-l`9EfUN{q`lKCLRMi&}NQqXAK>-lY)<-Vi~uB)%-9%xMr|&0M-OU#N#a_Jw=OS=r5IlEaCTW +fn#|4?#K4Z`@1@g)tch&o1MUf&5DhH<7Gq@?#ku|`;A7Kk()7`3^05@Q^dKyx~#4(CZc2OIpq2*sDWT +>qje2i7srCs){Qxbbbu86ZVF-YVAmw;_qt0Q`W46E4%ESPS$e_*s!6Lf5wFE+dGDtYOadEpO&jHje0<1by#WjZuufh(S2dM9FFxUq!MZj_wms +WHQ1iT^n}SquoVJ6SVsT3@tTi~Z4#`4|DWv1rS+Pl=ahi!=fThIqHL{-)1{@I%vH7cKc}An4;SKYvK~&+(U!zUWK9(;sjmJ?FY-* +HUD9m6bRNWeJ$F9S*8`>E|B8%&+7HQPp;ihqI-tf~u4my;vFZ2(7VA+=(R&4RpP>Jz!~Z?-Ji)9i2>5 +hj{rM)*NP;4=6W>2JYZaOoWRDq_e5WvL|*uMxH2gM#VJL<&6yoVQY&zFKBT|t^Fu^_ZE~jEicryQgy~ +L4c-Z^oa1g=2o2{Y`mqk9(8hb;hAvM;>yGf#Xz+kFn=3y+=`Px-XSmMz!fW +_Jgu;rsuor@ZTY@xcHL@!BByGR5a5jz<3{bIth><{-W4MfCHI&4b*2-;QNJfsyrTYU>bD%9nK)U!D=4 +p6zHRg?H^>uE@6IB-oqF1=H<+#-PF>xdnDsMD!FEHJ^jKmdNoSgH@4np^Op|5tvH*&#;!*8gyGS}4_> +4FX&-(NEw_@PI7XhN3M2)-aFxWp81shzz$JwizDm+_8%&09YJm6YIfyI;KqQxF-e?WECqgeDdeRL2)u +b)~7<2ULj`*d4}RF&}%M5Di3t$qtu2Rey^>tkH#f<8U_g25bO&BM}bfc-CRt$98w4>0*6uYZr;3)1h%J5&E=9 +ozB_yM=Ec8fpFe!~2lP0|o)XM4>Wt<$i){GC$f)5uBkZlw-r$_Kx;*<1D^l}gGm~OQVaUT#b_2p1UWF +vCkzH_Att+lZ+6BaI14$5>JQ~bA2ZEQ +{N>)d!VpVo&2;H&7)5F4KH~K1o|Rj)NY|8a<&tbvL@$v&D_XPqoRqR>;c3+qZpr9vIP%95d7b_G +@$6JIThG9hF)LmO)H%54LHO^%LJDIQ*=u|`^gR2OdotNTIuGt!xoSCrP$g-w0&JMI!e**~x3#eCT6AK +xPe>;wQGK7vQ%%eZ6WzAjO*t^(KES_##!%oUn{&ymBh{5C)A5fxG!J@l8BQ=q@MH3B;a5ms@8$fbfC1 +8+KvP~l9$CaVqEwbP^B$9}KATZ(DSG>X47&3hA_1}GfI6Kkb#RMb03uIbKKV9iEhoor$ncM6{|!;&>+}D0a&dp85 +7P-ggNrHgh#qhD*nomWdzx4%NRz+L?}yHve{|o?-{xN!;Mkn<{QKvx|Mcq3^Xz})mluC|`Reuar%&EI +hX!`GDjXA)gJ*C)whnP$u?ej$#mbK83B;vXV2a)fm37SEkol1Lk;5^WTE#2+UOal;>2NgWq>KC~8m~R +UOz;dbj5l+rfZSSc>ga_O2NN3xzI5b`BaG%30^@sZ>tIP~puPp|XkKU%p``BgE$oCRY}@eHu#v?18uv +=Kuy8&Sj&KMLa|556Ou%+J<7oW81CkqcL?f^&`D3iqY-y-#oB+ZTNkYlaKkF_AFs{K^LyD$uqz!+{h= +~2pe@705#i9MPc7$utNdYZSd#y6(W8M%D(8*(coZTzhOCYcJ?p@#DUk8-g^p;Zt*tBbI9AlKCYU{sJZ +F{TNF>cF9sMH(ujsNl}g2g;k;EZ6O6)kM=wq%}wiXEWFW+4^J%0&*WGU|77Gd9W$nt+s>F-i4`a<4;&_dz|rNJyf&nwfro1_1u({UF?8o5YNQ1I_8OV{zI$FlU5X0m+zN@e6=B9p*?+m46o#}dHE7KL82i +XxXMxYx0OSu(C5PF;V9e1?#=941;o}r3T4w=ygYGHT!F$yiuekeKUl}dHNy*uSMrx_+wx@46zzk#=H* +ZANhHz5k1^cV(<6Yp4;ErYm0{{!fr8C`YK#65C10jmD*|k_~ +vLO%VVny3Yd?m{6yd?Ut{%BJw;<(KD?s{$sQ?f<|jIxD2gAHuT4j^JTAIH?8e7>jJZ>nME^2txx(bt9|6Y^NFCAmdy~M=6@=mrtc#_AX9xKh4R7 +lnN+$_k?h+IG+h&3TJEYut>rAfd3xo;6uX?({ubXxKLuf6oEHSSn-rdcMAP+4>)JM6X^@j9ly%O#{Dz +S-h$@-=n38V?+F&~O2{ATY5MN}AUmx)3R#{Ue~paF;l`zuTt5nByTkEj2VtHy{W) +ey|JKu9d*_^9P4TlLtI_kjOKH^H2x9We4y-uoeX +sdh&*2$}aussedV!AY>I%rvXb#ZN4fK*ZVvcxp-HyWN8@EX|BaiO?*Xv +#BY~YBf_C|lL;Lz5u#X(t*9V7I{@4%AgzP)Hh?c`BWqJ$>~b3tK2bYX`Bs!c&JJ#@mJ+#G2i!& +|C{fvyO2(5_6@!DeaQBK5W?^<;8Ho&Bt|j@(1|P^2EeTmAII4bS6pFDa$zV=cU#V{>S|6XUSu*N^E@y +k*+RdpAH(?X-~axhV@>$Ltl-#T^|fm>xNJSckv&XvJUpzHhrduRL`^s@LJ23c!pX7Up#gJMY`u|e34{ +o#olT;LidUSXMAmIYy=1F(hZ7=?d2C$S)D2X?IeXJME7DClSgo>4)L=-`6|yC3K)9AH0;{Dpxr8!_2* +k2fjW)gOTEa$jy=^b0kO6YHHy{k=3*;ZRLr+hJpTW=i(EgGQf0iD88Bsveti+LtN&_v1viAXRP{UMYp +VIX&HZ>MGxgZ55=}Qj33q}R&I5Kj1F7+jbgLgIZ$(bMc6H*$nqqs3efHMb6ODT`m?pH!0`rQ+OkibDfP(epODs3Kh*1oRtxP>q%+a@)mX#A5W}uoQ +&Ck+GyuDNoI$f!fNChz}?H%VPN60c#iHN$wJS4hW_!9z073FM1BpgVcxa4!#X-+2;<*4Xxl7TZ;5U<4 +q5Y_I^2F2i*@hXn{+?`b3;C{OJj1RR4N!72f|xLVR}e2uynRsg!+BeHZ}Rzu8a1XvW3(O(L!eA2~zJp +IPPUKzoJZUc1a-gZILbWj`tXO5QwsqbuTcK&9cxj +ox`};3{=lV49Qul_angLq|0GT*->zASs#Po+dZ>L&;PjAERt;W^u+pSnLed4&m!bXH)9mUOVO3S6q*_ +P}x|H&f*k_FCi?zp>`X?hKB#U*zVr6WJ1LM_V%3?Ai^4EmNvy70I0{xSc5;AX~HsKI3*v@;Yd$U) +|>Lkro33w;mtnqbA^%c-%(hvvOmhjE^_SC)^jO;)=`3^y2-lT{7MMXvMeZx&1bFk0H#ez9NxsvmYcY4 +>|vfFE}hkv^}(cf!@oIvW$jzJVB2j$x4U18$LLj8m&}WDcIZ^$yiwNFx@@1x5fuXCs~y863sihCC%nC +Co86_@dU$Xuv!BEQPXBoRJACBKv7Kq(7J9tMF`?}2It+_1;gnb9WLJChn*i$s6rWL7S1XjbNzIbt +*h=t+i7$CSV<;;gmDZiz5;6{>Kgv`2D2YV?BK&rR=X_GNJHYQQZ@5QVoFgM@a#M4BdXvA@KiQG^|?9j +v`ZqPlLnVsLMCiAi+m4X;m@Fs9H1z3$`7ov6@jR!%HhLW_dM$`Q*{VJ|TaiKOMHUB{1$gxAP|h^s?BY +A&%e1$LM(l~ZF39fb}gCY@12jOk5`K?_sW9kB0{#)O)gAWA0#{4t@#F!?O~#Y+KH0va3;jby-uYhRtras^;_#Nz5c-=23CLkep$A&jn@@op|Qrd{LYN!D6OMZ +-=|^WhVY)fjQ|n1Csn1o~7*&?rxCC0&5dO$PK)(KGFZC_hQOKe%6#)1OKoT(Xl87UXu8c5<9VQy+Wd$ +dtjn4xeo@?Ur?277vqFJq1;eDO5cIR4b$jYWT8?X&1#+W0-=Tz>6NS!`9o58$xc#5?y<=&@@1eY{ +WVJ0Bnb3V3Sw4isxk3QF`f#GgykHIGmU=J-%;xi-Q#Fyw*$vPS`yhgN|~K(mEJKztdGEL71|<+o7Jw} +XUwO~n@(}q)2X**S{vQJf?C2n$5(Ntc1=68}c4d~XP+$A4YLfzWxZx5?s +Z6LS*sFY~UM^Jp(37#v(^l5Ib%}QFVI8Kqm8o*2xl@`-TK*R-?-BY{;x_$BFfQg}ipj2{^m|Y(jt`BXZt!0?$#$m-49-^8)Z4o&kgznMon2TQIiTNDcJakh2V@yh7L`(&P0*CQD1qIl +ANZ-Bu=J}g9#xsVg#SAObQSMGebnCW{1N8`Wa^r?-Sw_50h_9GN7onVQ9$)lqQEIsa=q>6h^Pyz~q@7 +<|Mc}-tm#ZKu(|jc5Gt!UYhAy;3qdI6+r<(;BnZU%mP2(e`aPXAqQ9n%0t>>(4R7&*trIhQ|##G?wHn +8)2z1)1Af8}b87Ty1HCQjM&k*&$3Hjk^O33Z|w`Ta$VE{gmhxM>awVI +SK>D6Hk5W}z9r!uMv=6&_Nsq%UIDt-f;4q~%0IkwfS4O&ajMI0vzbsXkffp+X_WyWshyR!IrG88EYl> +%Zvwnu;Q++&)}hBl`>;B9)B5sYVy5>Es*%g+w*I_y8t_+qCyQ(q@lJtpHO*FboxYlze!zP +kDHCyIHrbwmb5Kr2;>DAb3T62|EKcUdDv;tc6ad&z +7qFy#y5$nV+KL6LUC_Nu?^? +V}FN4Z3T_|>_|oft2knL1?R@60=_v%oK!8(1?DEKdFs5 +pDEV(f2<=u>wP0j6Zx(yS+E5myUlp=+%o?p>2FijO%(4I~WHcb2?`EPKa%XefM}~ZtsGc$C;~+49mV> +MFHhH+WFh(Z=XDS^7e@kr|19k`Tq>la1PJk4aXOl;%NA8k>8{INW=5-yV1qy=n!o%u^PxuSZ!lZwvt6 +gLVp%TW)m75cCt0_qdV%LzE@QvjhsskI%4~sqFd*>s8*~TZgZLtM&HwSGzS}?>ANnRLjP*^xibErbU~ +hTM*oTS%dHdqq5JHR| +}lWhW@B*tv=clX2F#+9EkO)JHeAbAnn$9sFT3)9QO7cd4?_V?(E3t&$=|WMar2br@;V{d0~W7oeP6iE?3PA*!gr>m17Q#;w!xEww!VlP@^!(4B^<{1FXCUOSM +4hRKH8Qb}g!Arp%zKR!Zp-!>}|~_TV*&d;oPfq_L0kchDAp7n_Wcoq{CL7OB +N@^3OiMPj7`=f^WsE)H|aARjC|9W?`hEVGQtIH +@8R8ow|MFV$VsUXbdQH!HoGoyq)s}@8*0^%C6Z+K?WN%FLi>r(9?p>t&@rHKt`9(TKR$7rt@7|ZJLOR +=!`N$c2vu!pnXkTN-+#$L9oi87Stei5b=WGp~n~OdJxoYB{G%snodS$}+96GnX%3i*Dn>~H<%{STGzr +J{refRR&tEb<6`~2nG=g%@=!T_C6=v>ESl!@_0++fcFNX9<}ostbFy-m`zi +jofN>ypUNuC_^t46o*=Xzxu~WZOpRgkpyv4%GJ_9FEwh*C??gGKKyu$yX>xzFde4el^ZUT>1c=l?)3a +l(1|s0`NgrdE)6Umoe6DqGdyh@)VOtB-5(IX;FBs+g0jkJHFtd<6HDdLI}neQliMLF+f;HN)H-js|qK +>DO0l3AUDd>C^yQXEFbACGjY-neUklP_jqx2W{#)pjW+{5uHTZRgCUsmMi!1P%uN&VJoLCcRZ4H5+e0 +J5{NJ5gwCE+6M%NlZAu};U3ZfP8bDTZFG{C6ZlmdpI1+0uBuXC2_zRze~<8L^3H*YGGoLNTB3BprTQ%V`flZni-dSj~%^T|O$&k3#K_s*Mm+{ +=VV}J;A%;&*;Pcacnbw_{?20FRe(jUYT$%+lHm*nwgrFajr!#1ZtQq3w8w}XSZfHW$Vq8-B;&XMo9&Vh?Ss*C~1R>HCqR +6#my>`^O&rVv>{iG#OUStO$kJT_~kPk8U@n6`&FN1BM+frrh%eEDU}8De3@=d{}?MInp1X0yW3XT3l8 +$Sa~1%65R%aBB?Z?J-F$bX{)>z!XFMo}-jGok}YZK}pOZiDLu%pf;UcL%5kqgfToimtQVSl3!fnl3_} +R2Byg*VPUhJ2+JbMK~;Inu@oW#o-|m-lb+?7lgX$!alPkYV19E}E{$u1C_#9H42}m*Wgqqf%7Kr^o3j +-c@`7FsJfY%%8xmKu7CID2rmQreeYv{3fGr`hP+8ND&Z7iWk??qQ5w($aW^7c{l7@6psTkr&+}2H&#L +`EPIoEJPE=@0kJp^etoc`SC8sh*-jyQ+z;UEX;-nu^-Nfn+ifzqD-by-w;@04TMSD(*VkBCc +azf9!CgE7S*B)^jen&zbohSL7Hx%fFp1cJFh-=-Q>h6If;Mr`XsyJF8~`C<%Tt3A+>1&SAG6I6Yp!f| +TXZy>fuNe~R_4RAKhEAD4W0A3^70IEEl#qGk8L76)PaEf4a^IBYodAb3VYi_p9rpnaWrdKgTJ%tYF_L +(R(nr!yQhbw8Zubmg`(Y^f~DPIuBBu9wv>SZ^3$$>4Q2Ckvy?O_Mr}wwzi3MhT`W1{Jf_$}jg8eZ`fm +@9*!2qV5Ix>5;chtfmSZd<)c5W&b2)!6+G5`1wp3B}DF4Ege}#RT6kW9_hs)lIT+5!aPbP+wJNxenm? +^bcS7_+0ekzBTr&fb(FOxU0IdC5ML1va6t?I=k68S~Bs%9ng{(MaX(^Z~y#rtwG5@Ki?pJt{g*0=nNA +rF`5gNZa6{C|yIO>e?54E-xe99AT>A24YLcH_oj(oPcr+DZ}7O=W=g?`J>TsgnZQ4f>I$Y2vTg?|lM` +1;6#=vv9~ex3dx@Y6hNOu2mI9`k19!vHH~u$0&9PP~5D;oBdSWzDI1LEtbP-@GSw8S1jWWlCs!3dsSzV< +M>c8q4Ff(yBY`u-Arh7k~LUm*36M73q-(E5g3?zrMllXExNbvy=jQkP;ENAhH@WMaqDzSOa9xM|LO$U +mB|hSVgZY;$aFHRKqA>PdVC@&;TpukLqJ3P$->GArSznv?)?aurC$#iR53hBY}!f<_Pl?N0seuulBVo +J6(jqa~2^=WfmazkkXer4y#iSbSf(uHE40BAJE1Lo-M6!#M$AbmKxH5>OzVC$zX?lav|(?ytP6Cz)td +^#?@s-)PS=&`YkZ=tC3MmZT2RMB>#5A|=04W|D5h`ousUfZ%s+4i=T0N@vUBm&(`M7=nj{3Z=1QY-O00;m`Rt8fdwy#T70RR9=0ssIf0001RX>c!Jc4cm4Z +*nhiYiD0_Wpi(Ja${w4FK~G?F=KCSaA9;VaCucx!EVAZ482$CKX~E-5^L9WLkPiCf>*2plm%bcF8cQe|bwW*^i#(e +;6{)du^I(om^6ApN!D{g89g}Q-oiZqlt<8_WP +&os8gMtBUtcb8d9_ztPuoZoen;X +z9F>PS)J>sMUsk1CHyC1Vuvyz_Rc$59*pp-y-*#p~l7GKv#+NwG1*mod0%zu&?_B4b<1-lM#hTICT!0 +q!;lst{2e_pQxG8q#DC<>}|slQaB`9GBT8_Ty%&l?~H*bbQAOxp)0-E++L;7Kxw`1$pw1=kDb(DhdxF|UBDg_-3*Yc!MXX00hauh7W*k%c;vM=+2z9=tD7 +ZY)3DBzpzYGCzGPo^wSAqb`mv0x+!fTl&x1orV0mE9xTk7fe*vq<3gMkUIVgs3In=#YOFq}q-{K?F@n +y(gbzl8CU{PebheYA+%*sh0V=LE|DOjGdkcw*Kw(1n{FI+MsEUy`5(?Iz1t)h!g@WwP7-rvqMViIpl3 ++`4a?q@MXl9gDdD=4nFqe@(7PXFr2s^<`S2Gt}Y+``u)Sl1UJ2r^L@|OyjKE7Nys@^*_38f5G{ZB8`I +UVy`l+0r8-8r#M3ZX(k#xGxN&&__2&#c^c=lk6d7OWkNrqQ^jpHLChd7D2Agku`#7!8 +meEys%8vxO`zcH$HI{J?uwW_I+ToW5L?{HS5>~7A +>!facSkf4~dpFi9$1+RhQ(KizOCi-7lE=%8b6M6MS8Cx_i8~VVqmHiDYGEBn7ON6VD_4!Z{u)#Q3@HIK+w>f!upJl%g)3!cxx5s$UeN1C&Il4!26mp2%|^92+bxikYrOb^S1DeMV!d +&SBw@ixtW?^jDnyxwR!76T?dE#{av%6aCwUoshKo+R*^b<+7^FPoy`N0oTYvP9obZk1C8JdoI`CgMjg3HR8m}qwE$~6|_kvfnQ&Q+&T0)n5C@w)>!%7V +)5%9CHth1#nNx;Ng}271V8Ifvldf6z2XeR$2y)S94P)P9v$2k3MYJ>{YDzHw +$VACUxKyt<=MMpmNc)vGOvIKp?aD35u=H9NCNi?mC+-aE}?^$^#r*Hc#O%Uj6yf^U}?yRyFg=+@<+-Z +bS#rxbGhS;@JFiakYIQ=>vzy +x*R`?^!-$$T37jP*25KS^~;NkqZ5Rh3=YmE +^0i58?ymFF6eDSfPGz(HGswR%mx%BfCVq`bwtIWEPiIjj5WU6NkAc-?JAjtbOuN+EC**yT>SIne={X2 +o-wv{7?^*_QxadEBRUbT>GbE7uXiB&(3eF<=f>y@Sw%Nf|Cr%2#?9gvsocA=Ny!2?Bk*XJHA5U8Q^|} +7EXAt@MKN;m;P)h>@6aWAK2mnY{22)CW{k{FY1L&sNiqZKZhvuvWC&v#@ +U_etgPdS_sd=Y#`Ied7jettY(1~l%>(&b~s3iz0?CFPu^3FIjhoM;#*TWPSwa1q)d2_d9BXY``T3FHf +;<7t#%1&lzHGIUr8qO4$9$_ZT2d;z#EY+B?H5dsKV5JoOmFlRxMlTd?_fhzVLu7+uEl2#(bvqX+yPAdOuc_^1_H#~0TA`f%4 +&A1_=a;;_KXhYuG|1O6TP_DM4$21WN8;#DVDNUNsN|l-dt#3#ZIyDuk_ENEGYrgGAIh@yWsglRA`r@Q +1!nf*`)m;Q^jw4=F82(kaS1%?UfHRWht?=-0cOmVpquph6d-KayA>F>ZY19SvaUb<~pBFgmxsX+pSjp +QWef8_#NZ;-v?NT<2?@Rjn_ifVGFE&WcWX8x6GZVyH*`twY?M@a4Iex5`9i9g)=i=kkY+TW#QSXs#5p +cqhzgb|;G$>H~%hKL7%6lX+Cp$3`f}TGp^Sv_PDf3d9ZVItDvjU=42VuIB@=i{%AjkVYVJGrTyym5P$A#)fl3hH0UU@(?XSHkR(lg*TW>u9~imhOfqrQu|I_ +3RepIFY`zVeb#<>E@X?qYqwd!7}naBt2F1L;8Y?W!?7$D95DA8WcYPx9&6Rf+wzvnK&{sQJK4%G)rfn +?MBAEu|9ZrY={1oZy4kQ*o!6S__RSZvJKlV$%vZ{MEzR4~{HDww%KX{bh~+JUaCNU(W#j&+%umYvTx- +t108mQ<1QY-O00;m`Rt8gHiufM;0ssJo1poja0001RX>c!Jc4cm4Z*nhia&KpHWpi^cV{dhCbY*fbaC +x1S&u-f|5XSET@(zRM5W7%}ZBL7$yC@^mHUU}kl5&coC=A7-Y@)U(kW{ic?!UG`55)rQ=KcC49a46@K +?*FeI(S5KzTbQ^HhltTkI!JKOI5F)LF=lsAN|hp(ecp<#JagLYPELITlV4U4^N)LSZTA;7A_l}uQdwuzW$PrkHR7zU+;w3jRN7#|jR!T +YX|$Dat=t-L+eh6xsH6mxtfi5cH?T5A?PM82WAv3OWr@JT;fI9cQeR0=THbY3Yp0enSO|5SgW3I2)5y +Yrs-f6yyi!R!*tM2VD2Qy7za*Rj6ENf11sl>KJR$Ddq75cgQ(QSpF)MAF({8LJW`rX$JIyra6d-=lsjrI +faCg^Mt}E;~quQS0H9IW+aKwH>YtPA!7fAk~m8R{bi0g%z+`9lCd|!gLJnHjZH`{GOWw7Juy#m-^Pe% +Q%Ex5ox@yEtU+?()i^`6EF#oVG|0Vgk_Jn0&a%`CuyoFe_mR?Z!p1a>sn=#fkY^kd=VB*_fN&;!(QKa +Y29*UYVp7^c!}R?;&`WSZISyh<0(f-)+Zgz3Q5n63#iD9mYvf`9YTIbzpsf|&BDvToCrz=jyTrl%oYj +@8@hpOw(JLdZ^|0fk<)*L}Z(57LUDMlV^`9Ld0Y0Uypl!;+$)1&)YQOCtzaCs0<&T^R#{*TD^4EU<0l +C!Ib#Gtyf3l6l`(5MT#C^VS#q#X~cC#+5wD1%3PT@3y)35*h)`j1@@JAQ^?80ANc-MvZUHEVs>imb(` +mWMO)}7IAr+>&%c)C?}Z=ZVxPa?e5ZkN8Z4v+V+e*gQEIKW@4H}{4xQ?eHZ@tyv^-hK=X-G1)E-(C39 +g-^cAZ%|7E1QY-O00;m`Rt8hwo8(au0{{Sz1poja0001RX>c!Jc4cm4Z*nhia&KpHWpi^cb8u;HZe?; +VaCwcB-;UEp6vppY+IKhtRoS3yTDYnZRLnY)WF*H9Gm}LKP~?rr$*7LU8r%Cv>NB90UT|4K9)KI5QYr +1b@FbiWC+x1cSzq|GXU_LK-;qcBOEKI74<$Y-C?C@u|vcI>#cN+pzURb?c)o`%r!o8pF-h+`gc4 +;a&R=AP;sw;SK+CDy7t`nX27iRqndYyf$>{?fqHU-oMno9MsQF+mWHMYx8lOlywSGCnAO|76_A+9o6e +U?}SnXy=K;Xq|$OH(O0)Ab5)*{Nx2$dm%KtdvzJ7qGNRQLD5Er8TEIRVf0K8gB}c6LYE@Y4L5M!qj@9 +d<5UOybQbBDod4E&;=xU?wndx{q1OSOhLp$`Gjx^Ou&@K$810caEl0hzXc>3_#!fssbVN8>Vss(zk{t!%Y`~(CH?qkzWZ2~r0P)Zp+Wb&zRKV&kZLiCXVQ4I +8$MiRu>ajqmgq7X826jB(*+@VPH6vUJUjD$VR&1oQeh}b=$C5R(IAI}hnH83C(GIBwIm`sb9z+u7#xumoqK7_;0lCXfA|EM`ERLK3TT4z{j);yzHlk5Loj3Ld8FMV0i +LIdrgfroq#xuD+sMuo>6VVpxPv4yfX7MQ~$Ag#<4<26sHTv$e$gEkze4aIRW7T{PdR-b@LsMw{iqt&U +wX#WGZ43LmeO6?;z&(f9-&^EKRe>mVQ7C(`+w0!n-vhkLRZ^)69>PKAu+xLi^XKmVo4ftk>9#d1{D;p +Mjjix|cnH!qp6qr~l|fUcNv#elm1jGsyEK-%wZ1M=bq+s3t_ok&Z(DXROa1DZ)mzH8;F*5Wy*j~EtKA +S={ceV5oiDHdcG17Q{@BWgR^E4BTt!WcDQ?30qjkS(<@Z+pyv_=Bag$sx|MBjZpIZ61mCx7SK3ykI^| +{J#fZqJ)?VEP;&AV3q^77_?FK=7fwB0wKU8Ap1O9KQH0000807zB_Q;K8$goFnG0Fx8|03ZMW0B~t=F +JE?LZe(wAFLGsZb!BsOb1z?CX>MtBUtcb8d9_$=Z`(K${vIIzVOITM`JgDVv%9x2ngG3SdM&P-7D;Y{ +Ls4jHiLtqrMJ-7s>s|DI`#ls%S+O^Nb9eNET{%2BJa|S#S=N3 +ZXW1Ap?LTsVGs=$bm)$?67?1H0#mDWxBeLY!&c;rR{~TMfQ4}2>T=Cx;u8pSGH#hI;t(YsO)`tiG9{O +0Oj$UYDmE$TMS3(a;7{eU>pINcc>HPeh#HyCckn +)Fg>x$00#dN_)G+~!^1;bw`ZkNEk@%FOR5bsjm`imZ&#-$C*AIR

    bppjyd=VyhJtq>@#O2 +5OD06)MeT1&d6}c%vF_l}*!>VAlJ0*EfCTYql;WE1+s2aua{z>&e;Zo|O%sj1t1@)W1&qYW6LwrS6$# +zvrU0>)7rl~^pzjN*KZ3!eOy=4<)~uPUfPS6rAsKshT#&MVDl^rB`t^qrs!ZTk +I9!zX>PI#x5P^E2Uj>xY4F28E@jpFol)zVyMkSB)cNq!mw>)f9j45oVv4;>Kcc|qtr(ZjM98_3mxM(XY^rI@cYq6qG$9 +oSK@9Rh!E=42nbck3uOtXY(ZQIllmA2$F+q2iWS#M$0e(BjR>_s;VSVzoCRDb-=`BFVB(HSUhSeWrn5 +7e&br9jvy0W%8RF52H7scH(!$&+V1087a)08A{VG*LNLuy_+xc=II4iD_zp)v;`e8D8iqWQ!c4wTpXS +?m6TbglDFDQ0?Mw4_pNhb+CZy&$kx|>#*wSTg`Y~@Cid^Suxv4>^sn-poqI&|LcK2Z1@g+&(%K6P +CtH$`)gry_UwiiU^q=r(kT$6@9=Q4A@;DurwJihO~)3Rct8hiQ2bp>bmGtArH5fS>b?q*Vb=dUI6K#D +-NeEI8&8MfnR?5ggy(bz9?g3gC?+(gi;%D5=wGs-iX%aweAv1+pqEtCW!#WYqH#!*na`7IT^rk2)XR0 +;f7wlcFuk`6g(vB0`mNjBu +XP;C}nD$LlL0)Ed1ji!NQtZ6Or@`FRYtSyRp$;IB*hP{@ZW9i_HYVP+Vyy<>E0A7cNOJYHqTu9J2d#g +$21aS_M_*MmuAgZ~r7FYVRa|E>Ubr8s`kIyBbWyGVNX65M3l;wYry7Ra7M+V|rDpKa+1$=fQpY9o~OT +C`{b?dB7CJ->;DFFgtXKghBZ*gRUh+_cMz}RZ-<;u6?e$$>z6my2K6oxdm=K{=0Tsif +(!t3bz71+XH$ +Gs24mj$P3vG*$a9hXl$3a+w>vKxvE&1HpVuq&L>0fcl+k@$9K2aUFcS_TieTey#_FjAew??Jp)E_NnCvJl(Kp!*q8Mqv$8L;NAL47U)cNrbS!ObPc?DLhmt +!V5~0uR^Y=dz4N?7+=gbrlXa(D0T +KUlc;HLRHA*I$HrABJX7Ghg*h`d)5+xJmhTThn;&>^$#XH$lMYO5L-A@3X+rtoK`OV9wEMGeb_FTVff +6ID{N5n^#Rcrc!Jc4cm4Z*nhkWpQ<7b98erUte}*a&u{KZeL$6aCv2qO>4t +242JK4{0Bh~*(I*$K|2P6ja>@6l+oP^iR-A1X%c7t80+YdpFh%lFvN?bR}Yds15%P70r}gPB+q7Wz5+ +OxUgQihd|msoFGCJU;iE2tYLar2w?odeJgX`zZHH9__fTkjb7-t7g5RJKT8>t#Ax+mO+o}Opo6Q>bO2 +f+ad@v-!PIn1vtsTwXe12I)H1fu_*eGF$R1tnhZN9fM!WM)^f9GV^M3;V0#uk(Ympgw}C-@nM0c{H}D +D7wQYl=OW(+hfhI3`R7iYR@EWG4jc!Jc4cm4Z*nhkWpQ<7b98erUukZ +1WpZv|Y+rSBX>4;YaCv1?v2NQi5Zwjn9}eyiyRbE6Dg9Y= +#Dp5MEBeD6NM?&A)mX_bDi!Be;U6e?v=7OT~&EG+)@=*U%#(u7uvq4A^!R~G-?Jc00F&5LT$K|m;)q4 +QdjGTH(4f$NMp+4&@0(PixcV=j0Suqiz70u!$UYNEa-8V&^lN<&v4J0DgGd +H->8v5TRi9zfsZ_EItd{4(J>hZahyIYUC~3A^2;u=undg1~DSGTYO55l;$Hs)RT*`PgVnhO=>Y64n10 +oeKblQEbLstW3PCi3$8o3{rUO+9=u*;kr~)xo?#ElVRgJv{7@bHrL-fVB_4e^vo2c@9^(malEzrY!Qm +NT=@#5St6dVSB`UQEc!Jc4cm4Z*nhkWpQ<7b98erVPs)&bY*gLE^vA6JZp2?$dTV&<^M1 +zeIU7^5Zn79Db~_evaG#TooqRhoK&6Tf``OVoCpMXcu3Z6ZGL;Ydmfk>3@FKVa+ST~*2*N%)6>(_?`d +G49ej7dmSrZ3s~KyX)xjU}%F`!LpX}}JJ$+J%|7k_t)Cte{wh>kR^vU1D4+3AVWz9Ba*5(4R6%Cg~&6 +>4fO~s3PTUHG-7~nE**ttf`+FHP+l4W8g3-R;`FEX});fMlJ6=K=QvZxbH(bFe#L-@)%+45#>f7j*mS +~Li-s>%)9%59R~h$1Viq^5aNHW!tL!e3#1<$^i^A{JV9qH!BBd1 +r&@c5CZ<&xutKl8jb!!?2g42;NobC|o!asvjz+=UrP#ine*!NfEbSU3ZTnqkq5UmyQ?`2O@VJvvNZy? +^`q^f-NYc=^+miO*oXmQDJz+-^mdqOqx`PoJ>J=SnoGD3lph_G1nF2V;jeMQmZsX}w&FjY#DxEriHKw +gVGyo3*t!TQ=|(}G>H+6SvX0qzvFY~+o2EtgHlsPbBNkY=6wa@v&X +-?-Vd$PSGIy_g1?K6Qm}YK3WF!Sw0WW6C;xowAeShUYT7K;Citlc3+9pYFn_K>^D02-#=Wkb+RirQ$U +34n+)RohgKa(u$s~l8XdgO{XkpNB?y}BxyhL^oimbJ0SpCipC84*oPl~$Ez!ll<&T~zQu2oS+}l=OYk +xt8^(KYWL<-x0XCE@kUQMW6{strW_7#8d6T^fF3U*wyR);?SEm5L=IlqFquRK)E>8aY816iO)?Ge7zC +1rUzJROGeAx8zy<8tvg5DK#zaNd^?4lz|sJ*GYP4~YLeIy6BiFie?~Z_Ob0Rm-&BHc=EQ<2Ym}7EaUD~3BPzMNn_pI~2xV(2Dg%X@ +2OOtgE^)Ak3z)D5X%=95M5bdLrWlc+WVaRQkuhB6h9Mb0GZ@J?hTIuLp3R82vri%3AA$=nKe2^C!GLu +m)&Qqk`HYQA(r2De*k9HHg0u?bFLjqGG6PSNdfw#rKm`-NVJ%_^~)eH!a<(99p~5zwERTNCz!{#FB5ns%EDcNxvyR&=YNnQ$V4># +bHvg}2yaNo)h!AujR(4q2Az*a?Qhp=BPoe#GX+lg3jO$@;k@aOmYlnvvg;-x_ +nIzA_Au2@@x7(b)IC7VGDizwow`Bu%H3we`!kO6s!KnNa1J<7106a&yPmpeGO(MeTtytosfRkcVCl8; +?7&{C}jj?lVQeg2Gt<0MP*gFI_29LDExD`YHJH>Jo{wq<4isuJvr(yPL#g~Fc*~|%|nwtDFu#a-CrHY +vuH)@8zE^wNfEhq_4OF=b5e)r69H&enWqf_g=m3giZwsp&M*f0=)C8!%P1raxnjk-`ou^;po&_0C(#8 +8kyP?(k_NPKOVZiENaDRq)QW&etHvw*SX%5cod3u%aA0SEV(^wDkj=UV3H|@qj>C8qpir*l^X +-zoXM+n1H%&17f*)Wx4Dr%# +YCG2Fy?#h;7?8~k|?%JGJ#zwO8Mu`Sq$fQ~e$xDY!8NOSg=i;41@8W$F4KSjgK2Zqy$w(e@WeZgC1CM8&b5w-60%tanAW19zN`=DtzJ~B_HtXE`%a<<=xn?8WTQvk!w&0@m2LqZ8g1Zo=pp +v+D$dSuT-JQNaz*XIO5aAADBTDDJ2-@{5a^+%X^EnHU&wS*#z{DG&5}xt1DFrYpFr9RR +AmJ4c#F0?W2sYf60tXe1Q@aR5e)%=HqpDw~KBfS|(AyEU;n3S+?8ymW4KGE9=xsFM`ezhWso>n%upSv +ipHurAhcryRX{Ji8=N-n0FRPHF*9|zoNP<#Ph|hjOHJV{Ek6*M@_M3t7RTLog%2Qv#p~y{S|oiescc +Z?R6SqFRVpA-aGKYZv5zb-3wd3OZJFj0yW;r(1jZDV8LEgFw;2_D$kM2cM22UXgtwh7=IdRYLe78$@C +J1|p|Gd)#M2 +jmpg-J}Kg5j3Ex2ot=B_M{VS@-kJ&&o-{Gpl~s4)GdS`lQcsvZ>RoU7BGkxPMnCXB)BG>`zI+Nx?oot +ipzL0dt&k~bhdgyc7)yR@2_tqk0$rD +k~p~6uW|PkP-#d1x*=|Esf>)(WY5i)2ux=g20VO!h_MAa3Yn$^M!Y{i?ZcV+`){o1jw_GU))l!!@tUO2HfrjztOKT=#6+{q-9 +nNhlx9#5=itu0z0VGFz)V)-Kv&0MQ*ac^^|5^74*`1ATqv0+#~3Tk`pW0bZ1$sv2!|WE-6&!w^8?5Dl +Y1t@&BoulnVCFsQ1+zve_e)O5_8J!1=_l~R%uOzS@B&U@PM)M?CAPnvXE5Vquy*^t1vnSP=FA3QU +vg5r3bbZ5{jyMjw~FU!c5W +ek+B1-dVBWvcoc)|$Wtp%?t&*~bA$2wXkD=N})z*kG +3Xr|5G>u0mXBOJdsLK|Nka=7Qtk!IZO +i^$BD5>f-eApp6m+bAsfd99c=C+H(ueV<9m@4^3s%wocW)AnR|}up~Tys|2q- +2XB|?j8Taq6jU1E>SpwWP&n>T;b(|Jpd1Sp&n=zx+B0X1)*&GaY%ciOfB +n_(q%e#vxgg2%?JL(xF_vjol=c>iY&S`cJ7!kX>U`U2aq#IpB`%p6O5`+9UU!v_vJrk!-KBn% +0?B6oqcAHmkH1JDR>sTNjUIMScwr^P7m-bTYjM+s}V->SvQRSb{F4saCA=VJ$$1wr${`ZtlSUI(pU`LO;LKXbK+q&(uGt?8cL$*^xFiM)q3k5{uj$j4Uw2TK+;K$j!EV)93&&Tlvgy$ +7I*3dIa2>PIAw%4*oBT%F2@ZwJOZGoQYLsA#@QCn4_X!1MVV?;P>dq8BW>$uN|2@E+}W4Cte)Y5b%M4 +>=%T&QrHj_RSxMV~JqtCh<{I(Hhbk|*kTs(1FQir*Gn$31O6h~YsKuu*)Ut7i*(=%`VVo37p{`?}%RM +(`od2S#_csY7u>ffn0{8XJF5P{p(k-B*R{#@)EG@UQSY|BOax>z@t-FQiOHjWr?j6?lp2clg93Jr9lk +N`Q>@Iqrq$Fe1G^fT)I}FN%yL(416X1WYJqyB&Y^gSF{S{}BOyF2qbaU*l{PAMnlB2%%&89A& +Nco0kVU^H=0+I4yhQ!^xAgTHerY418!a<&y;z`p3DvvtZ$8auyEQp7cB(?xmc+leQdt0WTwH;3g|~Pj +c#}7+(zFoR_y;%W(W@iU@y%+#-e3&c#W9*N7pv(LLQliD8`c^FRS`oJJItG3!S!v!5qf7h!p8r;%P1T +4~V8wBQ&wDgcvWU+_KtjSkrZ_j)P2!wBc8BiMSxaR|oPOeWbX? +|y18b`%`Y@sh^xzN>m6iON&YnhiY#ltgH{Q`D@TiYjLCl_OqQ2FmW&i&$nTzQ+{V*Bp9`mtJ@T-mL@! +{J%X6CQL9)Ew$iMNl^KmPu0JZARLD!k+8sxVA+)Bm81cu%OWpBC;v_>Q96E8V(z(?&Ndp|Gv?8yWUfwFje(5S>Xz%axTew#dTYz)MVV1Q0-b&U +8%IaV_8gpw;;LDXP{IvtSJ@w`##bx;M +K}EJ`#z+9um))gTH`JDNP)Xgy;78yEk#O`_iVSFgtprdzuWGZeio$)u~ +ISEGTo+)>RxU|5oz|rkLpS@W+YKR2jVKgG)LnRzl$0;chzwK&NLAQ+Bg6W!HL9dKu1_{AlL%5b|?%f7 +H)O|Zz};3m!*$Jx-*d7FGo-+#{)EWQ}tYIIh*Xe`jo4VVe@6GTt&?*ALvgS3Np(;P1pAo{kY_Ch$ +e6(jH0qVd*lJJw4x2X%A8b@feRK) +we8Iaj>-p=eo(*_<{x1*-ra#X7wkpyhbdM17wu{#$Z +12CPsa#m&UzRXx^BBRP)OS}WU(0$j*Zp<#wEnZ?u%@OX;yDxk_(y3=1$do^fJCrzW?19kU7YG?bzIRu +c;?svCIn@*MOI!p*xfwjT;4CJ7Buter{;qh4B$y20t-R6Nip$n%b~|ZQ92(Do6^~vQfu(Rk_H)_%Q4b +g0LHtOngN~P-PxM1_>AC5-OIeOI5|Y#j06ho<~JgeA%=-XZBT60|@Q*t8>o*iKNL~nb0CS_Fk4|UX+k +fOgo{lxC*6L(H*?b3IoC2IhCH!d$(LH?)D={(+w29E{lf8j6d-A?Dg3UY|N%469e(PL^XPrJez@S5p4 +g(AD=&udikJPwVS*DTDu{?<1t6mdSAA=Yt`N>;f6aaJC=UUWX=oshNtRazG^D-g0q1=#BEztFbJQPcH +jHLZk{JEUQj7H=Z%OZUW!gL!9%DV5a|aAbnvpr;qt?mp?CIV7`3}6F(hNl?y2)m$jJlL@2hqS;wF0BR +q!g8=FEZi@5?sLg%49r;k`ibUUjjM51A>N74$5sE_+k@5_B5dijMB%`v*`<0|XQR000O8NLB_@nebf} +b_M_dp&0-G8vpunrY19ouXq(IcsN?y&v#okLQV<#x|?dTfW%m +hKoMRbhB%dn?5;1XtQQX;WzzN}Y9KXXj=v^u%y&4d;?cm+}T>n!Go6gXcO5`!AO#e^af$3G5+aonZF#pUqy;{3}0I2jxs`_<*eRXA{YT`dlWM<=` +_4RL%tI2#NHF`Khu40##B^{cbTn9;oHz72t;W|kr;vJM=}#4Yur3z?)Ks4>`*a5rqiXklq#l?#~{#UE +KXy`)JmfFeswTN<-&NYjE;Sn){xU9a7RVk|S<&EyT%HhBLk+bWYrDqT1c4_flpsG?!XA-uf0I45aZic +G!}Bg%lw1>s%%XI)biDe8X;Jj*y44lm)vA6x2~OG=`Lr6)?=NW3eZ19#;1UJAYL!y{#L;P10Lj$4Juu +@%)2?Rrfnj_De+_>aaB2kxJ7-0^u|W>%6a3t0G)1+=e3+N+U{ditpa*eixF`Se3sCcsmp3Q`9V=I=btQk{^;wkWZdkD^)dFa18MYM^$#ljijN?9B?Rgzo*DM93*s6b~y!pjwDDm|1HD{JM%4ccC2GAn7aL6mmd%$br|Zip6558ZVI$AM^)N}%o8<-TohDTknB!fI?RQ%OS>pTZ&5Fq%q6AvVT5CDB@`ylr7 +j4L^O!hgyB^d?<2Z`jB)o7L;ce(rHxVl}EVl=#cp +;U&V=)u4Rub)cef=IoiAKFHr`$V5wMBjCXzV8Zs?jukMC22i4#<8G$auJK;p)Rp*9nRQ0hKxvivqNb0 +`=(kFGCmix8ON25S(T`aH8CXIjRHYg!lH>Xp(V$QhRgw#A7n5_-v2Ha)%jE=Q_62cqlYK0R+-@pa);* +_B-J1}pq7+q85o{jL4w-Ku}p-;fVj`H4LWjSr!OWmVW(?yDI4JE5JqK@dVZG%?+>zfm$aeC8}3T3ls| +~fAeRKTSfiQIR8lATTxJ=YBJ-T^A=|SBq-tJpF~oGO;**E#@~?uA?1;+^lR=V67R|?f$!Paxn0I<`I) +)+BvI8ol+Ldc +!tWo(=8uOMWBCh-yb#`z?UeH)d^YenW-0%&>>0$zkuF+ScbMMM8+8r*7;b~O!-XmUZ^qTYa{>qrW-yF +5;k(|Qcc6)x-+&@)^vYtw|AX +bWX|0=Sha*FsshHG}TmBQ!y1icSKfS~ykR;#jyWl2Xz}5}#s8b)QyE=8en@?sHp~Ej15ir?1QY-O00;m`Rt8 +gXih?hW3jhERDF6T*0001RX>c!Jc4cm4Z*nhkWpQ<7b98erVRdw9E^v9}8f$OcM)JD>|A##p0m(+9A5 +GH0h)}qRdkxw;f#c$G$OXhoT1gvITy}S9*)4j%{bu$-E=kF9t~dmDrxp&yotd5Y%+Bh7jJ_I?shII%a +Y`yZA3eu6`+NI)gTY{bPqJStrnE|FrI-7A{|r9}bhYG)ti-I!8Bi%S<%J@8$q2%;Lh~urTohzUi&@U3 +B6A_hr6=k(+27-CUBCkqK4o*%RWHC(oX+0t9C +DdO8)Wl4=i7$-Jaeu^~}f4h=Rum>oR0xaLfGcy_BLo$AcJ&a}urQR#s*UT7u@nrD^fd5r`>1!(4mWo_ +CmAj3vB<3M}~LNbXgzkG9Xb$)S`UH$#-dG`IA%b&AfE`K_|c=_hl`71Ie5iQHyY5nkav|g`Apz_rSq_ +Seqq7IFpfBEU^b@uk`@(R&cRjxT~tPc&^jA|O;w)=ZCHYXWqgHJOwE;1fh3T0b?u1c!ZTF6;)y1z%@1 +BSh1x{}}ylzfL+_@ttybVi1bj$~l88CsXmqMQvZ8m& +ynsL8ixak_H(XSbz?WIInve<5TQgEHHZ$ZU1Ez17#|Lr~Hl-j5DVZq2kEvwfe)%2niQ96W4m>b?M&zg +JnkwpE`IW0BkTs}F27i>ufdcNgf{?(l07sf$gKy3hvHr#n(6TrX#3BYkl&aW8(ER_-yUu&M{z$7 +rW5;q_P5c@hkg8wWopOZp>`znY%O^YcD%+h$THw=D!h@|)sH}8$%E<`c1D8$ooEEIy|qF6AA%&4kFC$ +;SOh$e#$G0P`;n=@bpGsF!uLCc7kd}32g?nD*cFylU!RQ&s2*5FIcwE~eLRjv-%}uvZ*M$!3y`S9C} +QEc?f;Br!z;I;M1+eIxR?S9JoHs>@_l+GPkuwQg6ReRzkS1k+E${oYo5m++9B83eM}Y%>;R?cL@k{R+ae}Co@Ce2-b!(MXB@5Nc3Zv8z;Ez2`aoZ +Mkkn?A(SKa_S9}4iJ}k;C?0-X;d58n@BV#3Gbf{BU2umhM_Ur<+8q5GILefQ-W@ceB3o~wxJE?gSx8l +Hv1cW`3*~~~7N^<~pO=0bIyeNamY0iIx%Kiznp@1?JaM<9w76uMsCQ$nM(pQYZk#bhMj^YLz`XIyOPpAT{QiR6L)-@eWsH@;^E)`&fxRs9ZNX3ZkU3A!1ay;XCc?o&##^aPRMd~FL?c&LN5+RPQqZ +!>1r~ga7tFHg6v}5!HrauIZ)bZ0K_J>9XXzZb9Te>mH^?7jzes~`>#ju&fmR@I{czPu4Ye-g5uk5^tv +sA9`&}@oQ9uU5c;-mP0ds(G==}TO5jF1DeMnhL*2QxN{Blsp#?p!p%MB@c=yBECPCGbeo;YT|KkTs>Y +3ds>FDI?vn|rX%9j=xSoj=~J;%GHetLYeMQT|2(ozEppCh&BILu8y;#AKj38IE%cG@7tgGcK0ks6RkB +*vvA=_Iv={N|+Q81AOS1M>d;dpoOW4o>8b>A)I=28nu=u*}TN5?dfYMvbwE1+-70Il>d3B5t-ksw78~ +@xsk0zJEzho*h3U>m{EqK`o-nXNDU%q_I`$M`mU}BsH$LP0e@6dV|A3OA$2y>?rR#S};hD!JWRvGjba +=RLj{hrhcPx_&+jQZ@0XroNn)Y&rWIr2HTf;WT0ibAIw&bozISwaPg$Qc+xeyE8-hVWYbCQ?$E+63iN +Y)Z^hf$g$3xv^oHi9h)0i(p5LM}?ne*0VmGh3A3oV0tO`XjpgUmj+993d-*^`}q0&myVkTDc^OiBDzM +qO)xnH$X!iUr2XCIT61s_2eGTG4H=s-s(z70lsI8_@rNbddp5hdXGp*9DI)*q={iD +Pi9%WEdy<|r0p_xyS8&^@ZBO1&sbLnA@6fH+``E9WoAm8OYLJ>v{ia3A7O?Gftd?sc7Q98FA2$IyERF +~0A*k%>_>uBX+NiPPy-EAvW}GN;a}F2KNWXR9x1N|uHsg{_wV&e*$yZ$~;lXV20mbzIr_3x2lsu;W8)B*q_VQn_ENiuGRnRYmUm*TKwF6@ +V>raYj1q(5|tyQaOw(f*mDWDbMJL7;MmboL@8NmJi+_XRFrpJpv^z!h!HpSFNX~c^GMelc1rONmCPfx +N%WLyR`0oGFjm|&F6_5PhE#p9z%T9d7k)BnG||{4#pp9U2C2AVsP9r*mIyBU5Z-)avvXHUmwz( +3qc6HAjdfGlu3HR9-}P)e9J!m(cTgod{A?biC!MmehD=Xypag-3;?FW%2c)ifY5W#s*p+Cvp2qIIblU +0;vbMbJm|gcyy<2D7(xzC=@;nro-A-Al$(e>~JE=4?7pc9<@Y-JSPcaEV325KzK+B3h3owyAvz6PI2r +=*F25fmH!{74MqXJVdsHr(4^CKC1xiiE9gvTI6>e6W!C|8zh!bVhoA;NW}2zE$Xbg5kQ^Unh>2QVrY5 +INvYc;6h?4Rz!O1x-Jcd_I=%$&$~Fs9iDje|jbSFHlPZ1QY-O00;m`Rt8hlppMr&0RRBw0RR9U0001R +X>c!Jc4cm4Z*nhkWpQ<7b98erV`Xx5b1rasRgk|<4@-GR;3+E~O6DY3>Vua`3)a-NuqGKu@ +^+!NUeI=4jAKQIXymbRuOuO9^DV~1m^7csvm8e_ +Cw?I3aDR_W=LXN>+~u7$sAp7`e6MN}7|$qtd7yXs(m=9%Z3S7 +j(-^fi6HAjje2uCa{PrY-2n-&<5eEl8!xs^&4jBEQ)R5EJR3XXStFom!+z)>n_FQ@hu%@Q(Y|yJPl6Y +UVn$PGg!`s(A6HgdIDWVD11~7*Km!e_XyOTe2WDrPgPJg0&gruv0m4OaTyvw9m|=w2;6}-NZoLCU2%E +6F)YllAAo+>oSODvlYn%lTDKl;&-P$uA$$V>A9K_WrWZBny)nwno0iqWtP3;gM<^7{RVvpd-gRR+4_U +jdp;UnNL+t~#<*5?n+HR~mC(^4vbLkY5RLaFlsCu4a`tLh!6@#W +Y&Ny7M%gIe6J@>QAbLx1F+;j6fNZoWlm4HtLb1o$n(BO_gSqW|q)>DMfkDjgmW>W8b;2pAD1M%!KjBb +X&R3!OQ*rMsZnhtft1*y8=tYHqR1-C8&yUVL?ITE_(eqQTZ#HAu{(KBB;Lqxpz)B;*L +LMHG5QNt&?y#|YSJa2EDUG{2?=Mmfy +75RU4IWycuKo@w9bA-;uq;lwx3W-0|XQR000O8NLB_@I37uVOVaA|NaUv_0~WN&gWa +%FLKWpi|MFJo_SYiVV3E^v9>T>p>TxRw7sK>i2b1Ov%AkGAdo(z-D2cDLZopiBC2evG6Jj%$D +OG@Jyi@m?S?+Zzhlg*5Yxg!e2G~-FGOJ*ZF==#GXIv)N%3rnShU-@lMISq +*nQ{ko!)&qJ}>z)vB;-+3$A8l^Y;(?T^O)iaOUCW-spFvS8D8^FbEf%n$;3eXsJp7P(kE(VMPo+`C=1 +s|sudZh*hvxJKG7ByoKwdDE-r#4gK^dzcc}R)Jhm%(r<#{aaN^h0_$RQqZS9GRE%FAnL(6V1wy7uLe;-*6_BlK7(*L^baARi9zd6KU|oxc3S_&L-3lJA%)@lHre%?+_D +bSt9JmvpHmcpLD(@st{X^R`Rd~7qy*i3R4$B~V%#R_tMxot;#7dA(-5xTx`<7p57AvrUs#~p6P}SSH< +=UzWey|~1DY3MTag)adbo%k9sJ%PxB|Nt_k{Ws$JeXy=02ZacAmjswP(ObiAd_A-1I>8QT??9iV$k&D +jqK{Si0QBftbp0GB#DxncNS9tKLuVz)uZ7r#CM=VmI`YnsgF#wc=z^OQL(Oq16ATKFPo%8)uw0Pw7&F +T7=;?1z|f}c?2ct-Q*f1Of@2eZAu%s36`%YoTFFtARY^;zI#dvFiShv~nav0+7}nmYzThRY-Gk1t=?& +yiOrQp_Z%w1czUPJB%!Oz=l(HBfp1u#o#q%1aofAuQ`w +$|-o%ciKJMDgg*{I`ulGd%tw>$77L*q@LOO;;r&4X0cZG}PPlr~=Cv83vp_z1S%wR^m&dty$ZLOE@Odssp=iKLL@l=!zyt8A +gjLXQQ>@xSbZN`qBVp9>OW%}+r|2!wi*< +Z$*LHq;IaT-@KMu1K62DTN&(h)!er5YrtDsmL2Z$?mxv>xvl`m)} +Z6yz*e*(Bzx7)VYm=~y{!ArvFrMH&YA_lZF;nGm}=hc7O5Biof_P4>v%8aTtd7KApJr%J15nZ+*QHRr=kRwZPH)W$Kr^yPH*q8>rKi55EF{oePha`V8`YLQq=k^dd1;Oa;-+ArXzB|Zb1vf9=B|+Ef +Hy#-F=(^G1Yv0XhROHVjCZzvQg^B2%A@(c+yaE*8dgBae#i+ +fEd$ic@!}Z@7^F$82}1tck`^0f9stWB#qDJpF#6%?(=}GrL=OLVht +`FQ4iE;wUz2MVs_FoerTw>f1vPz6yJe6LlYq3X4-tInxgJYX?M>FK37{E)PXkWpR#)T@NCmlA_6y4z0RpS{U_A0dLcO42b +~}fw1vVm871!%Ry39?pv>iVkATenQe_)t=Hdv|Hf;heORymRMpaZ>ICwxsVf4qN^biaHrCS-$`@HX;m +f`%?!it$`8?PTAX_z9Xb9JPiEYYMHEBs53rys;tq+;% +u;IYv;+jXrzO{?jCi2utQpj?tsf*V3!^sHLMzsBf}_30r|5|XH$t&%AhiQNDoCYmBn#4mK%`ENHiFGb +Wl@6Ji3DIMz1vR!?J>n)HSMbG0kIWezB=_|Gz-(KFqvxo|H`9Zs*gyUz45nCN83mF_*}evd4E9Bp4=( +@|9z(>h5o|%9nbcei=Ls4by46f!JMjXgLM?N>$NB-EICR%DPIsM?Jq4)s{*HWT(gM}k4VhtxERDqzRlRHG+C$ud@s +5oH63Vx21s(C=o7sBHLm}Fjn1$9$=J9YjU4}p +dpoLiK1B#81mHG!#QUf2aLD&3@2_3oXos2DqZ@4IeAIZa*v)nj4?oNP+I4?!;X1h`6ZiIcrAEkTQoA0L_@sIni3H +k(Jys^tPs#IL$7r8VP>^#HAaYL$gsDrIED{A4ti!esrG`(?hoR@c&ih+b3|0mFDD +OwzUsI&~!-q!jyZCjtv@XxDRaWyGwJ*RWQVeQXXMivJbp%p=3yp=*weOSc@zUXAP3u~6-ffxioDM7r> +@3EXfaRESOPnaCOpUyStLV$Ij`#QJ)(1OE$6S_0cv64UY!)Z{B@<~B>7W;+6ONWuiRID=YU1pH2-qTJG@Ks8;K(5H6R#}f?!6Zm@vt|HjFB=8!@?szEP}1tA_Qv$hGwDh1gmf4VNFW1W|RSI%5I}W|#4i=`XE`Jf#9M3&pG_#5{2UA(FNm^|@q?_ +1hYckdMRR2$c#!wzjrz!-^I9K#-(=Lg;#0=#%oZ8MdE_NAfCV{is^XT6WSq<>9-GxltKDyjf4)wp=Hk +nAYp*>c@<#nkN+Ce7R&6m9A_lfqU?exp;|%hv^ztMK_Y@c-D7DbCKvSSDnVq=cC!cS!F|Onp{kEfM_{ +1j}CE$j*%D4l!WM1$Ta!e-!2kZMlz1FkGL_vqH;n2zSR1C3ASV;Mo1P8s +k($=X-2qhTeAhS`k@R40~M7zt_IM5q~+l-m6jrJyg|)${m>pjqzQ7eyj0FM@GDuyrAox5m^#iF8(9PZKH22HYVm@`n()< +%1D`u-gGcba;7k~#a@#kAKo@+D*vR0Bp~s7D=tB-1@EdnT`LCwsxQ}``Ng9;YW}LTe@23xT#1vRQk!+ +93wD{pfEAI*hr?g9!1Mp!{j`#EIvTHe&hcJxV@~>^MD0|DVS1edy#(s8RyqtHRYt1(<>^+Rr^WlfRz| +<9x&pX8DT>i(~$N*jeYX{75z^K7~o31C_aW6SkR7R*wzvgO0&2ZfoFQfjpwH;h35Bc+}leY##_mK;#!|ae44r8y3!l!>aNdF$jwd{2$=G4$#I23vbm +$Vt8Ax)jl@)Dd`NF7axidxZH`EHBNL<>By}&u_c8%LR?57_Sjz8DG{eear$)jb%}E#PJWHFV3PNna?G +|3LWvAg`D0bv+DTeIlgS!N6TJ04}ei#P3@Y19m@Pw?&+jmps<&AjCwSeJ1oVlNG*26bG8obWxuKN+D! +PhPasP&b*%QiR17!{oqu5mU4@My|4#{zyW)4W{cs?$lr)c6J*-t%ZMYN(@o=`!uir8>OyhBp{B0Y+F< +E-om2_n0(W9dwd_!^!X|1bX) +x9*T4E*wAP=jE^P9W?iMYnR%Y}|T+uJyg_$#K)7R5?snB$iP-kaii#HA%_Y&c~JRVwAy3+@}5SKcM1O +N4L&sSViqgX*WY;x~P`)`@}$q)Jfgw*$6Jdo?i +yi1Kn{eujlva5KX?DCIS`Bo-3XHOsoBCv)O9Y=zAX*?<_50>!EJuqdw@HmZYqs&NHvK@qWwLVH#|2$69llk3_OGe|s&IAn +?cxUl<))C{3_5*?9a`Z~)85nW8UU2A!9&kyp1S3^KC8e8e0gjLRzaOlmlNGO4y +ap#QV?WbKi4gjM}F+lGaYG4#-OBIk&xtoRDQSCI$}LbDDqLH`W-)H^@XWeWE)i?@}dGL922DkOnao7# +m-m^$Hh69*`H0`+<;;5VLU(*ZDm&pWBM(!JZ}I#_BxcK)&~J?-%}BD?@W6wA|ZTYB2+9qDikeS)5v=W +H(a%>p$^%D!^`bj71h8BUN!;y8JV#8GkxqG|F&+ta_%LYGuXCXo#%zOIfY)pK$ivlE&><-tPLabyNx= +CLz4G%{~LMo%r}5$?y3WKddzJgqv7fXA1R`c|91n*<+&2?Agw8+Dd8b<=3tRQ&O0EaD$fO9KQH00008 +07zB_Q$GY))>i}o05J^!03rYY0B~t=FJE?LZe(wAFLGsZb!BsOb1!9hV`Xr3X>V?GE^v9BS50% +9&g37Nhf9I9)XgoSQI~$KI{#&8*F2R=+ORtP}O8JYHF0Uz#IiXN?gIigVM@BJWL$ss~KDf +yLR+-@wDNm7)|4NOQuTX>N_694;$q(cx<>HDP+lSuB6J~Bbensm{OYpdqj*s!yi!7C8a4R=W+Rb2A_? +_>CS;;Ogdii@xUSkaR3`u4^h<$CM_t9h&Dc$gdF%~50n;nuJMxzC%m^U%nVw(X}5+0A@1cA*E(`Tm4KulYh>?d=4XafYzD0x4>qAVSeLiCC$u4z;>XIBI#gv0_xgmO@l$MKM~r__y +p=#YhJcBV2@xAuVk|mQ1khxlR%oSPT(w5fu{1u5~Tee_bPZYmp~3l+^pBarx|Z-&oWk%ZB(8^%0Ix;_ +^dX(B7wMi{4t=6Ns~hp%j0gAkNDi9kgY?Yb^A)vYh_HdSFc&Dz^Q~A9rJb@k~J*V48)HI-D+?V1Fq +7y8pz1E1H+%lOHxPo3lRI#)UH}#1pZ#65k&>W9}%ldz55P@_Tgp45LvVHd^=5?v!)XlX;q9AqFJrZ;@ +9xaEOJtUGRblChG(;?@OSb@VA?CPDPE~r);F_*C^+Re6)!wryR;Htca;DU>(FkVhsz0`Kwvcpgk+ehc +BJd&J>uL4b4D+dxhmA4cMTfzefkR$(Xc@fvw+qk#j%lihnf#y;D>cXfjKAV^%o&XJ6OyE{l)10U$Wwl--qLwaP>vF=DTeb@5eeQdfsgw1+uAHaKDQlWS2f10R6jmY6zT9Yw8>OXK=J?LY)6lJ|a-T@Ykb5W +M>AT5LcXYbOCNWwPbWG(9d=plag&Vm1y5_e@^W9a;RQ(>DTB3<#|Vqj8Hx38NA1m)J$+@hcs +_t#fJ?y!<49b?kOdF@%%r5ZgD)8Z4o3=0SXnY=yj>Iuv^GcuYGpdy4s1@>3pE_ZC?p+{~l5Qg&+di`h +hg)^f^To&Urpv@TDYuCM()gj&DO&=yh|r{0C4=0|XQR000O8NLB_@uKT))p9BB^2oL}O8~^|SaA|NaU +v_0~WN&gWa%FLKWpi|MFKA_Ka4v9pwO4I#+cpsXZovQH<{^*`c>2&`7!bf;R%Atj)iIKGUxq=DE!t)x +lNw1SQ49a~-ANQhz1a1DE@%^*yqAY}5ATkX$>gI<@|3|w3KMg|#SYf_4iw9zGO%Z9#?(~L!pUSZ9&MF +8Koo6rlPeZQzz?NVrnoS;DmI2oF&>RacH>FY*IFu#%~q;|ThN7WKI@e9N(ULw!svyGM5@{YPegorH&< +!O*FQT0VZ9Ndpwfs5vBnw?^-s>U(e)}RZ1%Cz%tQi_#VP;76f9sV1*1))itF(R@aL3kQcsu}kJ#IWWd +?4`vYJ-nBCDZ2Nl4jHeC+~U$c%|HMkoC^8Rv;s?{V$%@sHNpMirTnI~8a9!fmirGi%8@wW_T(U +zLtB9wlrG5$>k!z>q;ifa%N{Le4xeq>6wT6239;%FQ0kp24axdnur;2eC-NrY0oYWMie|3r`rVrBxO2 +ft8Z<#Y%(Ro;GU_ohyYaX#px1MDq}S$279Hn1ttmYVWQ3yYScW7vKapO_4Nog38F(fV>CS5Lnq@4uQo +$tEFaebl^YjxrX{)z^hcx8rH}_-y0oeRji6@-~zH(8NSKWSj`DQrsO(ar$uEgkxYxgly(4b65Lf5@YA +i5qOym_DsLxRdqGcpSA|yZWu7MJ9IezmFtRcwb5lldBQZpnCB2U`R)=&WG5F*RjcevEu?}5|9YOq5E} +UeTmAHBwsbj>qJ>5kUU(2Mvhx!f=c(Oa>g7jEA?1F-Rk^SIGhvmNju3(B)3ms*JD=l1XWq636q?$LzF +8n!RjEnDSY@_7NJLdL;pkgVDHU4^GDg1!P-f|JAeuU0PDF~k=7t_D`xQBJ-7vN~y`uiIhz55QF0r(N# +OHwloK481h=Y2Le@Bi}a>hk&Ue-_U|zKWo}S5@6=N>*F5RkwzX9-9JBdGV|I2PT+`4J$XWZY7*$kCO$cAT +xdfyzk2b`)@+ynNMDf;ZkAn_;6p%?#;N$M+O)I{AhaQonE6i{NE$`%(iZu&xkO$NmYFcB^BlGZGDCws +TM~T|uqnHTF844M;m3`7}D?t>e)u`=Gm&8US(iMh&RerT_PWITed4HH;qxGD1U=hTGj!iH2x&?_lyw(@bzs#+ +)iYmL53dT9D11h>Z+O4rcI6vPDBC%mFPCc^WFtpBg5X{O-eLMG{j1nqCDQC_Wrd}k@1hTPc7 +^jk7_CL1Y80lCNJ*$!>+jWXMBG1pf4dJ!|Hk=DuDa&|-_?HiTp^20@l*L~E@y1&nSL +k5r|R9~{jzDGrRbDi(_xYyGCi#tGEPv~XQO9U=*1_M<12=X1WC1cH=9-Nk>6270{}jNX4qH5WNTT9|k={MRP8J9ts``g$7!0BFUOe;$qxQcQ+Oh``bIaNi>QB`FgYS-kX;(EM^PHWX?qy +L1T)=i$#3T_eP_U?qHlW8ZQ&@@WG8ILGT1nZ6S?!JHvaFiEIF@{yVjH8#l$bKZrC`b`5Lj~Z1vSTvbV-PqLbthPXd`~@p0vdI@}v=#XsoAc+vCMGf +9T3WOfzHDq_Z6)Nye%wNrLI%+>mHK9smFUaA|NaUv_0~WN&gWa%FLKWpi|MFKusRWo&a +UaCz-L{d3$lcE5YZ{{w1`yIkFhcI+hQDyQ7}{L$zxi9MFwo2l#BrIuKUCoZ`qxsugP`nUIe0R%vBC0% +c(?MyY3SR#Q3;NjtYKX6YS>>sFQofp;lq1twvUJFwSCauwD@{Y9 +rNG1;@PUIzMiSBR@qfubSnW)lS)7D&2`aT6ctRS(OC`?&u-K*+qPQ$Sl5@^&6}pF8+Co5 +E7f*cm1j+^%HpiSg}^$YXQux3`27q)x7+1~%IM*n^_k9dovZgZ-9=rc>O^aG(RG{l@X3>N7;<}-F6;G +^O}lzxkou%(TlnS4^KX9pBDD)P3vAOOuQFWws;Sp%Q*6@tm9Fx-Nw-Z2Q+l4R3SGhyq@&>UEk4}Ysnn +agwfLa%^nAT7yJC|y-F#IyYuJWt2aIi3l%_xQY4sf5>}apQEcK=<>dHRfkoelfoQ +9B5c7{Jps`523jMH;PFdT+s9r&=nBS$?`(4vza=suClVoKYsXere0lat4p1~5gOwT4B!Ey&fb!s^gLAWn$L)3vfcT*d@u!n_Kbvjg>COuP+EiINN0PB~?&^7!Au{KHZWwH)wpBr7ShVx3T^5 +A_;B}Lwo&%Y;bEM-cTWdxi?gOI~Xl|zPh}9jEWg*LJY2Ndk2yPDByMZM@10EMS$^25^%wZk#vS?vbn6 +7z0;8Ati3Zw#7G;e{Y=yoP^`DuRO|{mV-OfbdgvH`AUj)|;#gR`x0@%j~R#>HL&!H +i#I0&U64r2@utk+luB!zqi2LI{J;z@eCI_Q)f4ww)1W0o114iAK+yg8lP|E +pO*Ry{@I_EtlPG89M>Um_=7n1p*jU2(CSXrDy&es3hmSx1d4 +6*IuW#VRS5MVG{O{S<4<8(#zWLz4{N}4?2=(EEWtp|Dkq+kM|5SV_s)Qx;^pNNI2f#PXvb*6wfjz<@f +MW&3m9)BCnRfUCQu~iUMmY<5Xr+L^pg0Cenu5=u7JBjnrrmDV3T`(!bV#cXzFSz*A8 +y*BbPOyvO`pMVGsR$Dk^BKutsr1)%rU_h +0DL!T*>Pzp4nOd(qYxIMgQ2i+D}LYu00z*rhok6@jk{z2b@$H!;3r|ke7s8d;UR&;o3JhVGg$r_F96e +z@ +8JR-UiJ3y_cP3ZqK6=4cJQ$DNn8``H#S>65#>h=e8FvSSD8-t+n?!_DK$r=8xd;!%C0f^S!neG`+(rb +7x)|lW~Ayf}oMOFa){YzGE+3ZOky`>#l18 +iI$2=1!_tk3k(G+;&pj_7cOTL*cK`$S{l%wU{MaaS6r?_KHZx!9sUH?t38Tnw@J%db0P +J4JnBLJ_=O5{d7Ua+p$BJbbO}Rb9 +A>Q9S2TMd@oAVkEXH&zzjPha})8}g}6nC)YD_A5Bf8`!<6l>F?K7v$x~yd%a3-j@%yu +IpMp?gTuqsJfX)Jh>?v_3P+R>)d`afa2m6oG9CtQ&d=Y&-m}Wv8WkyVKpsh#`7#21}9UZA>BU`ru({q +LD_0{k_v4kGP^PM1Oyq+kSfg~Gto$OGmqpcR>JAP=!wXz#*C6A)&)l_K+&oQ$_g+==766d3JBFAKl{utoG-8_2MA>e1u +&(L_B~2|e>Un|~T`@R{{4h1A>6?-N1#Y&MT@Fk;;>$ZO<928_@EZ(q(P(s2g$QK*Io5*JXeTL4mQ$)bUCz8#8j#?|EQ0Ef${=(3nX +L29xviVu6S?M!`&x)n@}|*I?y2)C&AvZOUvJAQSz(hwS{t^Lp!qRf6)(XzIqoLk?&;*wi0^JzKnEj(L +Xz5c@F$!2ADkY+!idP+?pL7+{r`z*SIn#FH~4lX%Lb`@JOg;}cM$UeV(}WerZ+#D@aBlErK+beD=rj0 +6;kmcm)KyyUmELS+Ika7?mu?HpS8>+rDWSKojCy>Z=}xTO0Eo`Hn6VPL~hCjb2AbOP+(L^$!^v!Y7t= +KRUI?t0&-z(|Ej^jcBK6VxXF`T2XxmR#NsLZ&LZ`J9=)&-$1j9HCA{VG#Mvs5JZ+(WU%OS`hsjx{r|@ +r4jD8{3;t{;39_QcRU>Q>-5m&|6spyU{UGyrn{lNYH3f%U&7-!;zKmKPoG`a2!dsk#rH588<|`4UtV* +1)VDY6?Ffu#>|y@)`pCn+7thgll0UsZ{LC4MBCB>7j=EY)N-Pt$M$R9v$QqVir&@! +rb_^JZQTvUMZuOr`_rRE}2j+E==m>K3&F{Ly=}Rl$zXJK1x8Z|Mzj+#|(dHt&tylo0jwmI +Bd7M7Y5l!(omLDf>qYj$tE5%f+RMSp3eO&&TcO?@8TsaOSCK=8Q*TMHnxpzlU3~+W7-&q1 +i8?NqE>XN=R!lXFaY6F(#A5OCtXRDuxn*SBA~F1Q9f0-qTY=@S50>rHSAraTrS$afap4M$mwm +m?e(jrD1C>iG+(IUe_N}=k;_zC>vMr40j^GaoKlid)}uC|zhuC +K$tl1gXjA~ux4AJSi%lmQf9t0S1gQ$hf}>$%mrFWv?{jmXkPqQK +=<_D)npWdY3#zwR%k-Y{?2VSQ%g#x4rS%M7dC^{8FUyk;&yyf%UkhuC+*kA-fc?9-XTFahn43V&MF

    gb~THN3(xX|PUn8^!}MLLx|4L{az-amz`Ms>OUhSCi}$P~VYd3O%pu<`SbtnFvoACXa@Dky*e&{^{e%sXDKNla +7O@Wbw)r>IO5nPQ5y5faQ58=6QaJ_7d +9wO@bBNhD#|w1*&`Vd{PS`sbHX?=XcKK60ijH5lwLju_iylln)@%(C6PZ8Q4qygde~`f^Fr^(q1`vMV +il1q%6tMopYpo~!mi=fCJlgL*JAM__hmWl`nj4F}34k@pDz0^V1Vg3x;1vzGm8r0^8gvfSpZ5A~sSla +Go%hqKAV*9hRaY$&;3Jx{;*GrKL(e*?_;$+KtAUw9<#ggpqm(TKeDKDadClfW=SD4iH3lx@i4VInZ|A +q?yf<?aVN*+2R|70f4kzeo!quU +(0T~Ug5$jfxX-aBfi#kq1CQ>}`74aGMr_D#xiIZO-I|fJtBvcgTYiSmky6T>WNQ{Ya$ZFk8F$Y1oGwM +6zd{)q4n(TaymhMqsxFLjK>4{m&u-F1m&>(}2cuQYmknHO;YtH?6t>mBZ6axU7ARnpZkpLl5`Q_<9G` +}7Khv!b2P54UHd)-`Qn^xV_TaXiAJ=5uf!}rV6Uw{8-8i!8*rs(<4x?~_0==90C8YH{$3cZ?&MBCbvV +2OzL+c4xU^l?M0Bq3nd_z3i!;n_T{#Pa}iz1HeF6L$p|LCE;~!YGbgwjITYC5|cV&6H=kaPbtrJb86| +Oa%hjk~DSJ41urK*Db{dIY8lN=wsOh!Cux4X0y{QT)<}MG4vYgV> +38tBF9#sys;KX@UfFff&GCR{{>Wc^R$Aj+gu&(W7f0&PaKltu`yaM^p1_w}rarF|uE8P*dE` +^))z2FTz6qhxI#U>MfC+K}JN9pvNBQ^QMWB>~uvm)bwK^`~`1bF>#NETwVU{QZWXCHH7a{M8+*@N2-$ +>z=_304&EOjI-s9h280)WENi*65co32ql;UjHa$yD|LBeIT=_8G&mGI^duCz!bqX0@Rdn(7*QqYXspa +WJgk(Ux)C=^f~dz@2o$t@tlD?vfd5n6j#f+?ZE8CFAy1cL@*1iC|(|Lyk7ymw~(ORwtyhP2?S+>Zf3%Kv3bjP2?P1#WNGm!7^4%Wy9ZG;|?=VfV;QlqY(Odm)c{OuYMlNI+@v>)Z$WC=Op}Xc{JCtMRzGV1v-(!@|Uo@0w~V>9jon+;nxlro+ +k(SZq=2uGSJ!ysl0=ePM76`MbGq>;tlklfHB0uzvULJqKYd8`jC;BBn>-QB8*vl^5ml;ng;6&4WUMZ3 +4?BTf=BmPXwH6(!H~$5t(Xp5#CHjQ;=wASQ^Z7=)$VTF@jKb1cPD%az-WC7Eio@vD~%|^=(jj;Br;_T +HeNb#3fGC_g93zPfRS7+czO%I`GafKV3}n1S@fAK#`~_#V8L~vpoF?0*cgAlr%>18*YVoj8@=oiN6uc4Ab>l9v0_}LOTgvdz_PtBRvH +KI%8e$E1lx>*arpAh*mr~X^p +bT%`c!rXR3vuF?UJR9dZ}x-pf&z#rI;<`6x;ZjDrp*SpV7-B|viif% +`XY=eyZ5ffGSLhJ7Rlc5ok%d%=j?N7~!2I6`%kn7^lP$bK-7oz6Q(BI(miR(R4F=1A%09@j}^<-rn;J +f8bv*<*LuUgqxIIhE|w5D|_`nq($PB3RM~*lyA3UY+Yu@HXO;O`@jGp8B}KOWKIkShZS}TD-k9LL4f~ +OuF!DK2NsuQ#Ck2Z!qJ^2f62Semj;|6#H>X%=^*cy2wsVudtY6Mkm>d@I2cP8vmHc_I0 +Ei-qCxhVFYnJeElg*|r?Lkz1fr^?FMYaQg%-dCU$y2TPd3KY@pa%n8XkZFHZ&%7Qm5QQx?+>>Q(N94~ +Pe<9IBxiX%4wjFSN2&!x6yLcnb4{uysQ_s-r;)ywJz#MD`fFDfpFcbpWk`o$vHN|7-o+8nRC$`rKU55;jbX8=}Cd<%Qq9p@ij34@d+DL=xBuBgv#+(U1l4g4}MRx6U+6pOQECrxsMlVSwIzoi1_|lV`7Pazk*&#nxL8;YU~DqG6`u`@mb^1hMgSlVF_8Oev9S89^)f(jOItk1 +cw+oW7{;Hs&Dmzk4)V`>bi;TF-KCUYA+OIluYBrb;bTivUo4vJnr12cWSCY0SZMTG3S9;FPiCmxfAjolk8qnTD^0%uFoX0>-f&UEih3YHEt86cQ<@j>4FF5jmq9M?V8;yv3}zN5};pz5DZ{0TD2u5iEO!$ +*)XdvMtZx5-Bw4L$JG*lHl4VxfnEC!%VquyI5m(G%;g17u~hS%a;N9CTT|y=Yhz?otrgHffq8#zV^qJ +ZU#fpxh}hG8aZ0#x=T}`&S_1c{kB`9Y0%WLD7*?MHXtxES1IDt&xLhd_VHFldqL$RS<~7yEn%kW*k4> +hqcv>zEmLwqGAeJ)4P +H2Y{++`FIc<)2EHfc4vde?=jz)WJF&XO&iO?ilW%)1OesZ +u5t`|05lUKyl-u_sbBQ#TpVpJfOuBdWdtJV)8X|shqQ$Y2Nd0dAto@WZP6O$*aHLL#P`01f2r2bq@p&Mx{7CeSr$mroS{pXNQ#uo$}WurBhH)zf9D<@&Sr +JaNvrnvffn}naT=5n;!V-zsyQuhvA|K@{dut%a|JluCB?cZch9Q2`VZaI|G#bjx3;Z_%MrXXiSwc{1D +)?7azvGqd|8u9G&&rC7gm#e_3TAoh%~&0TkgC1JCM%c6084_4Sh3JjNfKFXl7{p +5T3X_K=b)c@NeL104-HIrtsgy4ySPghR?=EWKl!YTDo%ILTWVQeWO&1eJ@SAtjA4#4L-K#^m}IuW8Ni +Z^qaeCLXbwt4TMjd`kw&zhNSVIC9OG`zNu0%h@LgW=bqx8|4;Zu2$x5pf{xei!wCL%l-s9*w5IsgO!6 +poJ&s!M#=&PA{OU$Q8B!SUi+Hoh*Bl0|ZOp0PqO8teuSY%yHe6^l&Fu4n%{SsbDA5UscK3ZUxa9F3PF +l4CL=%QG8*RFx7)XxKqSh4|WX$qGnR;;SF>oZL*1-^Bq=;Rg@cT!pI4wQ;Cl1M|P=`$crQSuH-`T;=H +cwrja3E@t$+dr3&*-${hyW7nPkJ!Dib@+ImDKCW$>Ypk6{<1fuDVpG49A{&GHmwcjn(K{tF~rShAs8# +%HEf-|6A=bT;ly7;8hJW2eH7ptn6W003{KnIUs?`llcC!QdgOF_o`5Ifn%Qf{9Axz%KoY~8OW8{gS;w +n@S`i3N*=A$e`$V{1F0o6>yz_iG*;Q81J)eE$noV|b34HvbO*~vCmN_sJ_tXi;(-pp`zqw5RIPQ=X_nG5grQNPu6@pHe?Wngrk+!IKxP@qLD@mgdsx$o* +DWh)0i1LNU-o=o#EVXz`6%?ttTaYKs1Q*a0p0Y&}4>9d7qm)EJq420=u9a!0nxQCU=d9!{?s1>dxaD@ +ypZEY=7oRwHza;xr@u9mhv~hvNmJOvNR$r&ZmmOgS62TdNuwD+WaXn-7u8_WeW*lVdZ`2T{v#xpt)B;7*|v{9)?-QDr`8LcohS;Xea$LRP*g`o1_vu6vEuvKQGFuk$BhlE7)V)pbb((Xj{}cO$NoT|_J#G +PJ5}x`j1{WIt!Ud1Ndr`c8_mta>Pm#n7{{Sl}k`n_PF~z;r#nj`0s0W0n&`E&t5@KT4=r~ +TNxYHPmASU4*9dFGwnR1cK0sF8}H!>5MFxU1V_G&Y(F<~g8pD;EE4IXvNk!=qdW%Kf9_V{h1MGldBSj{oKM$y9L%sjeHUB5i%F5MRwy>{134{)c$>jiX0S+w@v27{^fO;e9<_5 +Xv8=+KQI^gEPF1SxjdGNI6OWl-%j|q{eP1&4%IulF(E5$_KQu^qtbFJNCfVHd*SK{hlJ&pvwk%-#WHc +nJ93De&10l|9f73o4KbSW_u>kv?I*9lg>LLtO0$p5<8PRO8PuQ2&KYpIl*|ITzN!C@%*gph&E4j~L;J +J2=RBUJrsF7sc6p~gA2z~btuv|A@%;Mg`@x6O@REoAm;uXA6>b6vyRW}}_FH(w-r(n)%F^NqldHe=my +KR_A-s>LZx3F~)By-eIMN5tVM(uvKMOmN{BR^%wkNBW^s!?Uu^Ok{N?hd)%La=>Jrh~S)v3S#n#gdD@GAcsD5GAG!?#w_W_mk(UgE!F}SdJw_qn6U)(5{_wX|9%{@_j|5 +EJzLG=j!TDE8OB8v#gm!F{TXXzY~_ulAdj~8C;;Vyr^rgfx#GmDUrIOG1=U5 +A!2IyIsZLS8L23q#|Sd5X0DDu;fH*_5Op}d;ZPy7r~~+8LZ72cQ7} +8Au;~khZpXI-mmYL3vXjkf+K|)NxwT9I#Pb8zIpmI05>3~?c{N*UU9~fC^wH=6I8_Cqq&nfyC#;NY=zPOQ4F%T&EXH%{lc!A +(lEV`8&<$hUpw_`yz8ElgF8B%XU;wO*RsrE}0nJMMKp6+|gi>sk`O +3}usVvv2#^iBxFt`(Pbh5>B1MW3AP%)@6aWAK2mnY{22-WK$1QOI0074V001BW +003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCPaCv1?O^@O*480@qAFSC!+F6v#-jL9oclSCsG^#> +ls6|M+b_&yfKMpM;SbT~6@t*DH(5X+HK}K0FmWvD@t55h^oq2($rp8kUZm1fS@nm2aucK& +?XOjv&9gicftBny%SG?Q9o0Ofc903ZMW0B~t=FJE?LZe( +wAFLGsZb!BsOb1!pcb8~5LZgVbhdF?%Ea~rvp-(BVZfQ&zooTXa!*qb=4p{rzBlhLlPmE`QE92eOlL8 +(X0ZcZPPnJMSD@4JA;(Ujy#_NtAN8k20`0eJU&ctAf=d*AM7;e55D>4n}-i7{ +d249rcP>I*I8NA4$FHOv`UMm+UTmz>PFSN +X<&G0zo^Q(?gmxEMOj{Dx*n^vZ7#5` +%d{O2(Ob{1nn!Kx)y3)&6*`mP(QnLi7VH(WN(>hzs@TXbbRHn(ITyN6GG~xy1+4)#y^=z|!I#z9!|5R +oLeb}T`t;gz3y4e5*IuNvK17rB`fr3B&^{2F&VXN5^CN@_3b5o^@W+q)`O*w-WV|5}ZJfz?M1%F!W>O +z}_BQqAzOy_ne={8pHD-CngOZ&sCqaO}Fygr+q9KAX|IXXO>y*_?(d}g3r0#plRsaI*6H#7Qitd>~~2 +w7axchg~(6^*WnG@rFi){PI-FVZHv)`(FDBVnT}y`5b{=RCu?*#UJ5G^@MGzJGUec4}bg&kMcrNGymw +E6X=&u|3httO6#0{>KFX&z7f)OTA9V>hQ8HuJrP#U^zLesuJ*dSi%yD=9PAS9d)b%3uqTjTXl3iOly5 +y)VioMTq1Lp@YOQi06uKfAJ5L-AMkG-L=OovcNCSf(X+P7#_B>hGhJL~RawkLRQSh2Hx+a0Sk;$hn=f +bQ+f7>6wi!diw#3iGVZfhb7~m!=pn196K78OUFKyZy*lF16D4m@e@M)?pfCvR@j7G0Pqhu@moNV&6!P%?nR1IM5Prn;X7&}e1ohSt(ZktO0@HBb$Py7J?Hf9<#nueN^ +j!8hrtpG`FL6pRX%!cZq0R}&B8%=yeaF5a|&vd1l%e3eQxpN5m^6&wmNy|icG-Q2<{{y0kIuPAE6qRe +f1Dmks?tVah1Mq-prjBYH$&jFqUujt58i);wd;@&7sWL>h03cc$oL5GxH0c#S17%PmaS%^}>q`xo+T_ +_HvjzgG2DoXu?r24@rD>2A@|EVEsN=kj5>BT)p_eKd5!lGm9aNjUc|KU_eqdpKxyFniy>Ku$e8g&>w(d+VBD`;Lb ++krhqK0+@BiIb=!Hon|sl~px8ORvjpsnQ&zfmX!OkS`sYEZd6c42@F!O~kwaxHAGbRUfZNW`>~Bu1Cn +)!HS^ox*m>@me+=1J~0^V2T-t+h3McORB8b1BeeKrmM*06TZD>(jJrjjn0iABYx(a`8Sg<$kd+{qwK^ +}sy|UgPO1D5%1Tir)BySV`U+4uqM|_^^McURHbbhB^cp9k3e;I%o-hk!TgNge1=?jUy!zSvy(kV1Obw +^WR+`%Epw}~V4fyj&B!^l^Fb?-p}YGl|AJ;JGc{IpvLs8f$th(_@q+;Q-6(;ZrCyl=uIO@a6F(PI@M-&S7AeFGAn1+CbqbaW;PmkLSS>D7v?1jC +7v%SwDJ3D4Wfes)&>ciO&&!ra(MV<@!kUXgf4rxzPmpJC4tcbkCDdc6X7@)>*2PSOkuxnm +trK-#Pnz?MOZjdqdxHr+k0?2ku33!2nd1=o|bH=q!i`fk)635h)I!BWQ!axJVSxD1;Tc8B1fiSQKEso(jVeDOa!rJea(}Q0 +Cz*1Sb@?Gt=~>uLB9ud)*Z2TC3ko9SHX4NiTlSs@4*96?39Mmg`-QElOx6`b=RQ9dOT +?glJ2@#+R4a2=UF^0YvKoEfNR~fAAe5+1=I8?v;?&&|XbV8dejH!(C^PE%GiyK4WO)Q+08y?n +A%*#Y(%N2MFOnCCD?`0sY#m!6TE9jtBHnyWH*}C)V^Tp=bctvgkX>+|Q=kiyGJO7cOdL6ig#x=cgbF~ +`gRp^x`Rv(K0XpiZ>JKS|kwyl1)jzTckt7RI28f4AJW?HK0+vKdPy;j(XYHWf3Unf=(==M9&u{os+|? +{j#W?i4^N+5V%UK~Ad*~&F$Z3io3T`62BMC8H3g)zfa_+!)(yp)5{b_S5q9amQl_k4EYj6j^$~TM4NX +dC50hh^NldJxKwy4&veXboh4gdQyxP}dg*Lq4b8&j5{Y?~=!Zyc|@`{P)2iYjER7P^uyCci7+wwVUx8 +bLIJeEY2=%VsFf7xmXUj2F7Vhzr&YGHx2whzFA~O`PB!|1L2_jUF}P`%3GL!l3VpJV=uv(2}sUz$X@77X30DrQ3 +}*gu8X0IjFEaFY_?86cM{q+UYdc29_F}*gCkg*M*r@YFAkjt3Fbaij>vr2qp@}Pr$VZbD^3`T#00RUg +11!2^m2|5A8g7pPkP9_$V=e*9;jk~tZ#&=T`=09X~f_tm@Q~ObPAq4fBYOmQEv5Y6yXtM22#p5lNV*?@5_IZaJ6?65QMwevI5t2c%e=5p4l|>RItyq(Pqjg=Cy`yF`x-hKNRMD62I +voyWu+if8mh{VsWud>2eKb`9gj04y<0ol=5@u}={xtev=nK?DqN&?Zk8dT9DgBPhWXbRJKlZ=fFS8T9 +fyF^#0j1=@tXkKcYs7w8(IY6P`FL{weSsq8KjhP&bnb}nfgZgKi`un#`x<$hD{qgidMg&ru(n*uP@wB +)!=_V<_NI?alt$(M2aQ3&}E9N9dB5T2b_n8DNEKmRR7U>glR;DHl{A>&1)Mz2N?Qs@R2*@I^*1jN}!2 +FC8*6Y)_*#X1ZfI%r|vkP4wCOSoGkRegO)byfPj&A@%&{LL_gC&{Lm_~bwXAua%;?K$q|?r-YVzVSl# +v$}>4`%fM}e-0D9RceM;%xlY9E?_m6wTrKNnXurGS +V1F>LxuXSIHou&0HyXd6RcTSLbhT$}U&w{{hv{aCYhktiI6dp=rLRdYY|71$KktlwqQ1B}3F~ +pgck|b+cGrl#?P(eV-aH@JiG;jj^8XkLnmzj(rB0$jn;#R)XLs;>vQ3nm)HWlTsFxojjQ*oT@*6z06opPuJ>Znr}@J|i240X}#fFaZ0 +LX%PhzMF-*~OiSEh$HrlxX>vZDcNn>X1_)}XdkDXe1KJe=txG#4(N-n?MOqJ~&!`DdWIZa+X%SGgk5U +clc}hm{T5zoxcP!NkD7c5ITS=J5=D1sZZ{%Q0F(St4naDecva4*_ACg5_|G7s+_QWB6Ox`m|YJl*1oq +_&v$jDWVQDDElK>gDHL{bp^&yF@GF(Lo%XeC(*{%1!UN><>1c04y348N`>{3DU-A}dL4^tTQ39dWCO_ +V~9$g}W+t91+}8;e8H>K(9MzAisv{tQ#q8$UngXi`p1^)Co*aF8bYcT7Q_ +Yg9*O4w!@2v^92vE;XhzG4W4U0=I%?AA;(V?DjGKnJkCh`_Coiya3SH6R0c7SauV!_$|+~3*M{Z9RP> +0Xp(@NH{jf3BFi8DhAO8(-_;o^Njqpa7@)p(GdA8%!q+%N5-;_*;x2ap^#|S_ho@0_6CZ^;+DtK|NgT +iw5{J$&?QsHaaIedu7A~E9CQP_f)FZnpOgGFA09%Hj0(ACoYGH+h-EZ_4_sP$|CRAIe*4f2nL!OmXnx +Sif^3c-W0J?2vrp{292d0+OpiGbA8ZI;d)HPLGQ)M5iQRQ26iv0zQWBl&O3|jw*Ml%3pLq8NSG +2`v{BIYtYZU~~o0X}TaNg(pLz%s6~i32qLhMR2V1}6j>T +)l}_J>jyNl*)L}CshN1-h^s8b|{R__cfm|Ib%LL-WK +uZFJJ!ts>ZajJ3(IdD%$s<&6RM{HV1B7l{2PY*M#b5%Q*rT_GcF0@8(tm0`_}fTT3T~VDjfodw=Wo=2geeI +@f19?%dVia$UX}oS+xHOCyVzVNt=n9gHVyPClZ7hh_(by4b&L*7<(5T41vGQO!Z@-fxp<$up0b*1K^Ng43yF{A#UdC6$LDtz}-nnX1 +8$M+dJ40nP2S2Y)y^8~BraRf48Z8^V~Np;pTh4@K0Qtm&#N>Ll^X#(uzh)v+S#KYjdN%^Nf@Kpj_U?O +w;KY;j}aZ(GBu2E)7LI6}RDcY20)MJfx}o>P81g?T=a8E1_YjpK~Hb&Nv~Atr48@{g=Cg^`L3TgF#K@u&=h#DWg5Go7cAV-HiC&oW7v^|ISTlo($5a9zXu{!c5^^#f68ohx)mZ6J}L7M`EFPCfK(#EFdHF@2{Veki?8Vn_=-|J)2<(LB|9GQRA3mmuM +V6qSD5HMHAykD!QfL(kflkCr(fdoP5CmH6j6!b42)=Ga~rO`Ws#!Z*$i?`Q&Q<*(@uvW;P?eKl0Kyk0 +$DX9NfLEJJIA8xMRLg{67%Wb8QpjSmo-$nGml}=EB2>Q~K~?&V3}{&g$M?reynj`S4=y$TN9cof{gQ6 +Z7}}0Ol(xaXRor#HQWkRCe8+Vw9cOro87$!Z}IM7y1A=hYp2nK0JZGQd2I<9H(GEqnu(iY4#Q{=n%t6 +zLf`*w;+Gvg0qPltjaPuPpiq(C*M6MCgaUVicZm2aN9h4{N%gAmr=M@plB=RnCh1AzxK|N#bCh$dUTC +>SzC!}ivE3aa#6p3$}lFS$B8hLYRfx@coa1CNcc4ziD?{<%_)msn}TXQQbK{C2M!cISQTU_7=8_N&Zy +Utr=>`+@$6KzR2KO@M4ggscN{Sj4);rAFwCmSTqbKDr1^9FEMv3n=>2c +EKeB2GMeeM-(6!`6!ZlmI#FvcY|br9U1-_5vuY&8eF?O8dZpDCoexUo33jjB981tRcjo~F!v)%O52m)a +&X!p6GJKOYZ$`|jo+Lo)cSXKs#Gu2|VTbb&fE_#|6!3vybSlNW5gRP>47tc9nJ@!6XQw1V7)UfEICY& +%7Y`D4r@am}Og4HwqBPAJD$%hTT2pnUA;tz!SryKb`u8fwH|ghW-L6&9uFrMlRj8R#3>wS{A1FZ6%R0 +6odA82*451bGutsXG?2%z|NO9vU;8JonmyT#y2ZIl;CjG~z4Vwfrdipp-g=eDyC%aBz`;$U*?`6#F3H +ueBkTBI{k5GGXf=nr8asg*~8d0oEX!NqyfwITYNmGFnj!FY4rWW?ZM%gwLs!-zGZl=p#dVnZU&El87qm}yqHsUc_5;Ea0XNjM{o1FJ>j3&p7Xh_G0%JU`rJJjHSIgsKm6s(CipRJ!cR^NtYy^Ju +wJP`Yt9JbKXmM8O$sGp0#;!iq}Upo>X+;X744}N$t_lwC7I~-uqTvhxT1*hjF_SNSOb;4h&@IW~L6cIp0v6z6M{1^%2N +M^YUqEFA{*QWDmY^)EbkivoKZ>WqtL|K~-SF%Y)^M}wcERi`DtV;d&>>R7mN~F04MSSbI`Dp3Oqf1!p +GB%^I0}ko%*w)voodez0L3Uf7^-%`m^pCIj)j9!30Dkd@auiD+P1z1zu34Zz<2-tm$L!jEeUG{0)}!m ++s5o`Mfk+j%G6J}B<*H{$}8A-hasoV@WE5`Yij$Pz6C8Xz_1PQOYr=sx-9H-{1QBu=yck9CO5Iwr*3T +P9kuScW+Yr92NpX<;D=*31PS{iiGtp8xriZh*hAQJ>Ptbmat!6BJ?G04dlK0MfG+&_AYV`>;93VJJ#9 +1hBZaf`L`42*=)vF2LiihVL(h2^!VtIdPQ^r}A6#SQ9en9S3U`)T7ycsUDl)C^5DoDcRV?1a0>+lc=J +(O6$@YD*Boo(xGY4uqFcOH`gcz7C>-a=gh>0*US<*d|Wr>#*NUi~3;U|AIO6u>w%J3-r{vlr?K%_u0@o1vUya9hKZ&Y +6L0nnQibcPWG&aW8!#zqoEYlZoii|m?L$VV#F3Rc<>$Ag}#^lCC6O4y_N3;6IPm3cH$-^-^OR1o38T? +YE5-707#9EjS*xb=0lx9U?)Num12d{Ue<A498Suv*L|z4zB}@Y +`uo*rwv{>uqa@8EjrTE2(x=oA}2KhKP*Sy-x8l{R|M1kFUZw-EMU +o72Jx0eVkmQpW{+eiMm1F?VLjr7~oL1488lhBSi9lrFAYVKPzOZQ^5pyD6#fam(1?tLXnVflA=ZxrG1 ++i&Ch04k8FQ;J@wPNX|rwJUzPEok}M0**g_`)8TpM7hd +w^&CqBk?zn1(5XNN!jg8}|GG5}KMzAUiRxhAjs-gf`h(d(nLqyJx)zZcJph+EA#MJryBK|g*4z4p5EV +8gQa1lIi%1DJX2N{&qJKbJ+~ZC3c20K7VgGQN1apU4>5WjSu7V6`)-uB9}5jSh{Q%T2hB{ +=};VO_Bc|u_Sh*u9NpT(6g9hrYPa{0e2Xm^yS;T+Qr&xngQcfiqB;r!?ql3Q?@rz#>RUQ*!KXjd1>XB +vG~h*US?{DKMH4gsBlU+CFYZe(QY=$oXmZEpf-ZYN%3&(fuWX2KTemO!h^`H!n;QOzwWf47X*l>(*%F +JCg_&W%nsqYhsUT=K-I+^s(bS5rp$;wxq_m@X--p~KE7wgMh=Gf(riNH_^Q#?>nb1AS=r#l#(BD-OQUhJ{^Y{Bcb!eAp6RMRUWzlZq-$D)_tCl@5G?!>!jj+!Kxg5%njk(1p*P7~|0`DZusc7w&(BI5fq!qDxR`*eg1NTs!>UUBD +CGOj`AsuaW#n*mltlus^w69o<~!bPg^6IK6CFE52f#-eFbIiMWh-qtak7q^pmYekub`0nb*fnD +~VfLvwS>$V#JF{6M;#Stc1l89oN2M3=@Hy_e%P^akWQT?QU>CD&~tI8@Ey#hk&doWV7yBpi-esOZ8eJ +_@=jnNcv?ZEg(hZjvwD9S@?By~Y)<6&a3vJW`q2tvt5lZqQ^#x0FojvXbFFxGK1fmg{ybtA%&p@u}d` +rraExSA6tdl~{XLNfspgu_4A~XSFXi?s}=W>_0eh!L3^kfiv5EIlR=1D}FhTQ(n&JA+ZE9ldA~pa&6z +rQ}_D2x7;`YJj$2YFX(KIt&Rm>mV^tL$Qj;e2)`6DlCQWR(i~FvTi$&=z^&IH#20SCpuhGAzYE5HBa8 +#oybh-Kp3LYNK0$~r+H27Mq8DX1+9k7pTL%&y--2Na-9uo+2nz@c<7=+ElRZ>FQC7|`J(ri@pFO%VEb +ByO#Uwd7`nL~9r)Q_L!-Lt&4{u++J_6#DRScs03qc&hEZy$C<-K@Z0ohPI&#pm7R=*^&6EQcY=<#L!Z +bl&cLnn&|zmt*P{^&_g)c9TK7Ar4&iuB4aCM=s+^FpH;D!Rd{p?Z;QXc)9{s?9}jtms(gehF0v2og799SvT +FEFfu*h&n(b4GCvUJYjC(WaAwQy5}4a{f>nRcsPE7IsiWi#qgtCxymmMgzt8N_B{QUNLdk3 +e+sofg)z5rXJQ5z%YB%cz}aC=>zy{`yYILr3;dD`Th9`UE{U+RR!Vb<|6aNWNO9KQH0000807zB_Q) +Ot1(wqhW0M`%z03-ka0B~t=FJE?LZe(wAFLGsZb!BsOb1!prVRUtKUt@1%WpgfYd3{&SZW}icz8mm65 +D5ZFiLJ=8>^KIJOPiuai?(QTivyb4r6k6?-G(+z^pYv<{BZ_pUz%=ZP_$v)wsaZi< +675tG{wDdwuiS3@v=`YR$(M8+EHi-BrPW{p>byg)PO>g^lQ%M*CwLSMClz3)O0It9=VT0GF)5p_{l&D +6fU85LZscci=9~UODQ6H+47H*J6&jLY>1M4>Nnz#K2lG`4;J#IRutB1%F>hB>{5(Jg|orfPLGZzNBzVJPPBI>6dTAo*?1R +R;i@*r%n<`Klu>q_Po0w9jV^8)XKcu(=CrEwMh53(<#3%C#c0YMdB0}PX1W_hqVw=Scu%r~zmjzAT#4 +UTm=f^+-+%tfRMEi-uT{`xGHcF>ES*g0Pao#{^%&nFN{E6BhpNXE%_Fb?ViO$x!N9MCLoP +uELkY)u7G6m=4ZRLYeRgR0I4EYL?l!HZCT1$sSN6AAU2@!)5 +WDUH$GJ0n@g$0g;$$!7iXU?%5Ay@a9kLCY0XQ<~m^2_nbI|+ +HQh>V+lL*76$sVe4Z-a5*&1r&p>S$kFLonnZx40`b(^LC5pvD}+e@?# +nSQ4>etK|MBcKfT)UrUJ-@%#9`3R`{ +C>v#4mUIH;W{W=f=G=J+;V^X(p_EgRqF4-fHUp@SX~Ai5Yl>{m1o=pu!pSSvUR7n`CRPH{uY|Q%v``3 +n1*Qu#V%?}RGQ`~sW%}sGTz*|@Ozd)#rbTenX>ysQ8fAfEhDTnNj_nOn6m>vBiA-x^O_{4 +cL;u|7AB1RL0!ZTFBO~ZuoHeDjDZTr~lVKlThG#8ZLC{#KLbMwUBOinzsS&-42K8*0Z>Z=1QY-O00;m`Rt8flV+b;d1ONag3;+N +k0001RX>c!Jc4cm4Z*nhkWpQ<7b98erb98cbV{~LHHA@4owe$8%U*ETGV(tTva>xb@;E{xY3RCue78(} +}^y2CcK1b*3p?W6(|~Ur!$vcq^QM9tUnK9MpP`2L9}5TH5JkZS;=xDusoUy0Rd5wKfiZHzgV@-^#)rV +OgPfHF33&kD{*0+&O$~oLDJr-x)rg6iQeNH^Sok%3@`ulTXNO=@31;oKAoqGUpl!f-hu`hdeJyB+nO0 +J|O7T7qT*AdKP)Vs}=6Bau$SAutT@iCH-HOpldWD_@mbWo##Wvc@DzD9!ZjZSxsUIROtGU=NViptAk) +|^ph-+4;2S-DIM-CVLCgmiQ)kdq}d?gKQ-BiB{&T$^m9DAAn-#n6e +{5H}`JkIq5bjMaYS&z*(NpJLlMdls^Q}E`BlnVb5{|1%|yOFA7~bA*+ywJ_{_K!EfaJ6Ph_t37G?V<; +hwR8(16_7oSKQ{E2dTdD+Fwdn-%0dbT`f1JGS^T@+ZmO!0R2@P%B$dVzAohN@8Lexhszm4+8)O5Yk>PIlhAy+Q&>(n0mmR3)xu<&A3_zO#}JV7Z +i)bjxLGQLX1d_KB-cx}tzzTs-WB+1PGU@WY^N>7Avzh-Ot>K6348SUEFA;$A5qUBSQqjaWl$Qa+A!4^ +5?px63f8J|A!{BfXg&xPbFhF=nuZ7sA&>EjvS)*FN +9ZmU+%ain+0cc}sSB~~Z2`YTefQ)2kgi1a8XX# +k^c&-m(|c=8gUFnqaAP}PBs037gaQ0LxT6xZT*ruRzeu6z6r)Y4(l}aBHl~-L*7vAXF~|b;siFrjH1$ +7*JDQE!N+4NHPxJny`;X9w%b9eKc2&Aop+BvePPlE;vSF6j+?X%$tUfO%O1>g +|tT3^s{hVqnpi^_Nk#gV|5smmdQk;9N)g!?fBj@n2Sq}?{b-u+AwH+kYbbgBw7(I@kuaWXKkg3_#045 +0|XQR000O8NLB_@ulMP3S|-*Zm>19nuG9A`wm9Ird^I8|ghQR~>Q9?7P@S#Vt>2Kfr06>7`NJ_W6XIH0&L1{AzD!E7$m2SdVp-(ZNi +0i&t&{3b%N2WCR7g>mG8Q*Ub(v)1P(lCvGxIu1%N*Ox>r~A1Lde@n7Fm?S9J8`sED>6uC +inSg$zquo70zGIO6zM@LBBManny`)KITosLYj|dKD&}t^R+7Knf3Lq=~T(Za-O73*PE!w0DVo1pA*$! +UgQgyO`1xD1=Zkv5z9ixuM-@C8ZDD$5MIkH&WoTfQkeP_{ +tPgX!iBL$_K+MF|V6s%4laC6GcXLVku{)yn(3`k#L$;P0n8uVCtcJn9FFxT|D2!kvVtnRk@0L+nQtvv`{i9Oylt%)Wb|H(fx?3X5qzspLJ3SCWnO!GEo-Jfn*@L;yzmJwn06T2flV3q2v +rqK(Q&fcbI*c1C379PFqG`MbvpmR3nUx75*&?Yx-p=IGp*w}DgX}s=llbKD?MQ$Aju1Q&Z-8#Uhtu>U +)AUH3WQDv*vN)Qi@|!%q+b@c|7$OP2+dn?s3*Q{P-9Hi&;d?$m&ihu!1vpaW53aD&w4J1sxFKRiZ=zj#Ov-dMcY1eo}_gfGvY?fiX1E5Ue95^abG173A!=A +`e7T3s;RuMggR3xf|vVi0`FlT4wkkU_ZwYWIu5=& +XDSuk91*M>9k<>@suG$#GH3;pu=Muj|Kyb66;?6yP?xbrXbvKp$v@DH9@-|!FS$dulCPVW@&OMpHu ++}AQK1Sf4!mKA9DH3E$2X?$7q>qJUHV1MkY&ff`uok?|PVz}CbE+>ZSWPZX +T>I*oxO#o_+9;nCj&uvz@{`p5T&uSX&S%7D}PGx6xkg|oDvOmMw=MSrPfarZ-%)^h*WZ{wk7b@%^y)@ +;4u>E?O=a^3?~7D%jc2%~h7BMVP&qC5B|G~e&c-gGjX^RJz|p1mH2=HDT=_eT$tntlo=BFifW_poE;k +sj!gWV=R81TZwal(Q>wjq4U9{)mBBms04{DuBd*$l*J1j2Uu0X~Xeerui&NUwI1IApEF1J;j60{Y8|; +^n8{B(U!|Ri=CZW6SFAGvjjSEhoE`a-ZAI9VvgQdHnpz-Vs6yh0C6`GmRrp(fpG!W=I-)n?WHvwDjXo +cLCRiosbh7@7?&uSA@qp7mhor_? +!kYC6v@qwGS8i!`5#5>?}D_Lxb0&2SW$7999obT9_ejJCo@KqErO2p=8{d$7`rkv@`ciy9?im=J!7n$ +DoSA2vWow1+?pM5pDY=7tuGePIf_ +bYOK8(Rt~s}w@UhsA%oaA$S`Z3F%V!J&~sMYtcl0X^vZe(3UewmKi9phyaMY09$i$I0OTDo0)8=L85%Bg)M7! +gnTXAv`Q)fuV*fC7X4HbqW9t5(_qh<@^?kkb06wnljCNg?d-v;2+=yWDN8jFcmO4ouoPYwmWFrfdk|M +&Dw;&+_+)FU+$(O;rtfI>2afe_qgk|!xBX#g^O?(GBFvtl~n5he@&9MYhStJ?B7g~m3&?DsDnIz@94G +FC6c~UG`5wFl2Kpk&D!OKC{fqcIq1%;1R!6U@ryrxoS8auFL^i~jJgr5X5F#0^g +Ld>j$mqr)VAKGE}VIbHY6WzhaMPv#Q2OxPrt5yPX5fgw_XnZqbCjk3PQ7X(VO_4bla*ZW +Ao#layTA{?X?9OhZ;R=3G$uxc7IQDRtJivZE$I@5YAQ;B)+0-4k>76hPdJ;@y2|I`jEb|2ryiAH1D5I ++BB%|x025krJoJ*M*QfY%jg&9oxC{6EBS4KGnyqYK_mgTHSmKESKO+ZmBD^S;HbC7B~fHFA7z~1Ss#( +Cyd;tCb88$Or%D8qm-vmAvd*c3=1Kg$BiByIytC3?sd70fQ{>;Wbr*Gxo1c0- +uH66*8Z1LUU@@SY+DmpG5DG|A|3hQJ@xtq-9+&DGP(u~12pn}W#oh;1)o`I7fzSZnh^*NpjN^Fz$Uu; +uK)4?oBD9Vwuk*KI^s!F?!9B9uMQ`mFf9zG;h8$itNJgaqDqJy3uGZU_jMw_eEJD^NO|u1 +UE2Z=Sk$q>_**R#_Z7~IIbLDzRcBe=dGAe)jlv)K~t_026TVZwAAA?xpV7?cXvm~+oH)M}b08N2fF*|v5M|P&&r+6`+1 +Q7JE-)zw^K+XpYHi0t=JhN>Xs|9iY-Xo^`2dF3mIbFc1-ymb@CNFx<5w!?$yZy}uFwCELoY?9SP)a=b +s;Po-KGJ=+&wG@|7VEl4i9FEcjAl|q;V(2XNQ4Ly2un0v%96u3crr;GYdeJabQ|c6^guiRRHGZ^3P_j +HPeP{7Ko>7zqOf_hq(WV@yy9312`?VG>}qwkXzOzh;cHC=aAKEBv7cR_1$hY?2>e@Wm>NfxcYh>yL5) +j5-;y0PvE^%oE-7e=H3Itf>M_hVbVE?2niZRO!>Pv +Xu_@6KAjtWXz-kVna65F-XKV1z-miy*bDzG#14|BBw*VMHt6sfKf5K1Pt^z09`)|#<~@wa68#s**pQqxktCfzeYJY(d=e_$wNvTQ19>azBg@Ts3S2TA3&SQ}O?mgL3fL=x2D@ +pGIM+9P2OlvbHEwh58YT_aHkUN*cTGL@l&(AKuPK;5$g+6kL`Ux=Fm)DB_>*LkA+W=P9;T7aKdq{gHe +_9jn5o`EpAiL$DZOWd;*iz`?Xh=>_>onf#Ec}6oVu@g`L7Rf9`XIOiNcyJJ42s|N9Wn0;{Uj;O4-|Q! +@#b*=^hmOJLj=@~@k4?r2Rz5!R^KPad?GnD|-=w=lrm_9rK3%IGnXv@=OXb{x4sDZ?bg!+_kKfi!WIK%B6`rMlp0>~=%{;7HSDqz;Ca8#WTa +8-kJMJ>#*hZohiZFAFjtLj%^+s!!eTNXYwiDU&tA18M6p<&@^&$OTaj +4zHoe>tY<_H;)X3J?O|$?1@YbXIyp&5Eto~M*qHgseTY2yuweD61)$ZN;r?4>ikHz%UX|2NOWfz`aBdCqn~8AMzK4$6uk +Hu5l6Za_nEw;U#3g8)*Z*fq?j2rdP+G(BiYEN;itw~W=X?&<#2fVJERw3MFO#c$<3<)&Kg;?;_$E);v +c!i0ADvESYUl8f6M)7m1;rszXifp*^iCYU*%RBF&tHfmP74;>CdJ_(WB2>4P9wlCjf2^?i)g)+@GZgv +#!xA9l5-tWiRlF;oFgT}FI5^5#sQOTmezo7N`Q^mnLx`xh+X#)r=l?c)PW*YUU+P343^|NcMx_5R)*< +Csf-hbu}ESN_t5n({0?_mM-8JIRH6vIKNR(vV_K$ifi$S7(6m-fZt~))WP^2#IpyPzyalDyJMfV$OCl +!Pd^HQe0EX$JLM-aEO3>?GB@3Jn@Us~x7DDIFV!nD(n$*!I@8p>5Ga?{!Y%r@0AVfth#xpp^<@h6F{G +;Jm$Q`<5;#K*T%tm6MFiG$Rnq8doYh*U|d(XJpj&oMUQQ2gOO~#8e4G~?y{*6ejH#?#*7uTqaDWvHHe +Y4mxQrUtB9-HJnrK&Skao<3*8N$ri9sfZV1W1PMfa7|qI4XKZao>=Jx|!eZ`O<6wnxBrhNIRy_>sw>` +;tkpTP!`W4mZ7~_Y)jxFe=D +MLmZQ@hhqmMrX9M)44>}oym_8a21NVwq`VxU>#LXyFupC*)DSU=Mc8&f9^yfXjUv?~kYSUO@?LzCNTi +Q|?*UXi0Sc}+1Yv_i<1;fj31Y=3n!o`1co;t%Qv-yd(xGz}KB83XJE6`hQJ$;(3nmb~uwQy!jY(4e`d +iK?4)#BGJ`wl>=aa^F&u83KcFpCIm9W>>0GNF|7sSodOCCmMO({m$hm+`Jjs+qHgw2pkr4hNrYV(CiX25z +Tz8OXF^TpIFsThtrL74bfU+@(Tb1Q-3FnIxMbix +C4TX=LSf-soBY@^W5hq~L$b-G{88Fc^V6H)RTMe{p4asMP4$TzrhD +0d}tGw(@kVR*egGy8oL15A``q*I7CemPoiNwjfJ>{3jIAA9#z!TAFa7@dv?h~DdX9LqI@g}`>D%1Q1u +g5gQP|_Xg%JVrIvt)6$GR@Xhk`&`E&TCBmDS>HTYI%vHn`dg#nGFC=j;`X +_l~znrFA{sy`A<>NKJ2qk`qXwICCKYTu*#5wY$U2oeo-nIReesVI0@k*-pFa(=~F&sosag^fub5xvXGWGeCL(3hv3L%|Qn&gDE~D{ywA@&c*goja1n>$TU3QD +SD$;SuE9z_dYSpR})x*-IZWppp`Uu^T9Ic=>bXe#V#0L9&mg4E#;CWCGwRJ#W#0$|Iy&D8iB+UdB} +N#oB&OHbyLGyl`}&|e5Z`(r>^n{o>C3P2|VS|0Y>N_~6Z60$aTW#+0cU#ou~D=fY{KK>y1zB|dnwzyE +>#x{-jh>Jem#5GB}WNnDQF99&c0w?T+_*=vSPadsvG7%pTWM+|TPh6eb@6pP!nxnj8k+*R+!O{Em7caj2!}^wIcL$M`CM=jXK9zZPAE-64%h@z1$8XkOP*xTSJ~GRmJ>UNKu}Ocuxw +#1vS&hGE{D9g+36&vVu!Rj!>6|`gb5h*gX68rKN +pL6B33ap9rMx0Al-CX0*xni4zc&z%joJ%o?r-803Q~Zvo(j?^prfGZLX@i4elwAwuTtJUWnO4jPHHz_ +80no)agBEf8J^BRrS>k1Ev|dlP+TkC(F0}bp?;8LQOY$K8uZz)8aR6EaFSsO)Fw+`7yY2G>P~>QVF$D +xFwL$3SKNg&w3PN~RL5mr@`VF61sk5;Lp1Q5=P#j$HB;vuUu{&$hDtr9d@da6rTwzAR(^g84aeB{3kR +?j)TEaa=dH_v6)`gh)+FxtX(#MUAqANd16hNhNGMQ+g41TcpphIZpq5z(1Dm914M?$H?ORS{Y&^SLfj +PaZ0#Czcxy5!M<`{sb*`Pk7oe22QjAXnR?U-uU_4FIfgkCKEzqJx7#C~J?sHhIVTZ6s1duTfhXPew3= +8HfJdW$RSruSX|RQwut4S=Z8N2Y5xkNbpor|*JCYpFUwWWmzQexvD2Uw(F`b^nw`Vg+MW6FdgYwP&Zj +&KjC_JY)2-&U8>yoE#q5?l~Xjz#i%SFHW +1q_*CkEnNFN36v(yU%|suJ{Hc*1KLv&#aDtoWBBvGsh|(%%)SSy?DuAe`dSl#}uY>hr378Vm19=?%ma +aoM@S2#cB_T3j;h8C4?%R{+FiaI`%>Z!Mv}5Rug%cEy5VF@kQ-2YP9pP~|%Fb~a>=B7n%eNLuuLC^@a +dx~BkSY$YGyg?beQq+1cPbeqDrBCjvdBdt%<21A_P%_t#NGuftI7T)7Uu#|q0k#sKkmH<59+=E9@c^S +GR4O2n9su)!`<^ +vVXsp>oWMnMw^S#`Wmr9U?v)QD2sz5=Dw~OfrGQ5n1krh8#j_|sZoxbQC9ANTTDD5t<=~lF^Nd!mc-~$DWP;a3ikAetGo@TPrhhU# +WZDTd#lH5AvnnBOf)*rTmt>-U-tze4uq0sGP0c~z7Q0~dZ{JYlCOqPfwyyiz00|T +sTIJ@$_$y;xvYF!2iKo7w(%K&@BrZg5j#lAo~3CS?{Zrw64ZK%@GBjLw+g$ehn7s(t1J{-Ek65byPQq +7V8h~Mi8xvpG=1oM`}rLPBS9$RrQccS#RA*nsip((k_6X~)Wx3_}TaipHx+uOkUe}{PR{t0#k%G`d|5 +T~Yv?JvI!^#3;>X1{8GHgD(ZPyAi@TjJ$Qv9m4K1+RErY&l4=5t&taCw>~vlDN=AsO|5}{Rc^PT_~W={6SsE!p?e+@pgG +0a`ma}s7SCtL`P8e9mlzFhX*aa&iF(jQEuI9zo?^z$EF3vy8W}&b0Kx5^0ulXohHziVhiOQeSQVx^vL +ersju4To&{|7EMUd{_tQX2X?p~bJaq4;Bx95cEr5x49a;%Uwg*pE?V4kTIE1JBdB`91XH-Vs1MTX|$Nek+>SS$q1wzUYc(`3}{~d(00ktuuu{=*7;tb9KY4TZY1c!I6+pmDal8v&;}-ENUD|SXJJ&Jn@fK+$H$&WyI4zZaA5G^d-|zX$t +c(puc~N~sC?r%z)}l*)vzX{d*$9L>mZ{21%k~K4?b +GpzL*GaY?WZLI(PhQeI(pZJ*_DMUyVx~lgqRS1*e%ttHH``JZ28c`NVw4G0&9?F<7%AcZMlqhj+_YK} +3svxm3O&BkZ_79u5^eV)M8O@d#VrYZ&avVczb&`8GAH`;dIqfWp;S9>i1vRCmPL!Ka~gwPq!~CvG|2H +izhF%f5*V7=!sDN@QOJ8Z&~U!^7V5i>?}NkTzYOSB#%;| +CT(5HT8~Z~>bE1B8o<6~8PL=3#H7}I?Eu88THL(>*jV9JJRG7n2In;LO2KT8PR>5Q3XVlzGH=g +UrdFe%XCCKnLJ|9=|j)IO2*cJ@&1+dAD4fNV~CN)~w%X)*uJ^}#EPJKVOX6W2+X(Tz_Ps*%CxVB5D5p +J$~j5T!i_SAG~Stm|$HM_{P<@%EIs7|X8%hiQo0%sM~9hwaIme{%uVh>iIV3k{IE`#f0RSLAAN_YhN5 +y1r=P~r521EE-P`3K +2iaFKU+A&o=SaGp@S^(Dji;XdW-re&zV?T2(EwWq4e(m(M!#CvQopvt7k<(tyDB?(56!0P8~Vz=z}7m +6t;3$Fc=20kF%~b;wi|B*L9j6gZe9hSmh$4&SAfZMLJz*8WYWnXSpaS}@a}1tL{FS~1LKx)uaa|eo72NBv%@!+Iy{X=wX;p39ff_(uO*Dc)0bY$pM-y1g@7fCPV +9pYP4=vN@$DhUUk4BxF1Dsf`-?xm11*7&;J*q1-pf~nq~Wi2^PVhPE3x-ck?3yHY;&vcpNL6!>PYHUQ +5I_7W}ZK=)h=>O&If+!78p=_j+qzlYRmw^%z>}$>SKjecSM0TVwO`TWCYqF6@<`5>_SX;4C`wC`DbB< +VDRS8?dGPZYD}7Hji`f~>>23I=?Zq4Nd;jW6G92pQ?ad{?O4xtEJeJ +*!qgo!Fe}ykn&2y``dFo%BO!RNtkiI1RAQMzL9TKwOK`fZzUZ5^Nzs&}<5+gB4TiN=MSG2@K(A9d|HqAJfG0x&SU34?9C`pZ6?m>v_+@;Nj|pZ9QMTFx*7iC +XDvcuqqxx)M#+0MZ2YrpMkQ+j#0zKNpy?J-SotK{-FJ4MWeNZ^&rKiq|_Po>ufd#eL{O>AdW ++gCh87t~{5r#9?0@jBrWeTr7MRdqSca^89_hzqP!B2g#*p^cB7ethgSGj9LRmrJLsSN&)R_pdFo*0!_ +Vzcz>4>Ku5b)BxWGz8tdh9wze;IWFa2paCdv)EkSJeM<=x5jiEu$BT&(6!*HLiK#+=jUhrKSh*McM)8 +Z+`kJ&!Ce;i@wQK5UV&<4y?C5ztmngJc}MCZElb~#_z8bQYXs~ux>#_6jcuMem}p$%x{ +1EonR;=y;d^d&4bHpouW#gIGXM3tMcd~@5@zc+s1s%}(Lcp`MNllDyM!H4D +tH;qG5&1UF^_kFki@2E*^9rldov6sEIN%&T4T;Ef5p}ek2{EagF;RT&yVai#8_Qe+j1=c~hBee_Ep}n +{JyYIs9_IF?JABKnf-|qiM_|0FB_uVUnQ}6F*XGf>-pW^H+JAeAS|NQ&!a6Wy#kB$#J_YtXYVfWNTLL +m^V0z(of)m``E-w(3DAF)zr&nc6TlvXhir>eNR#-~gnb-f*$fL^*pE(QEf-@oAFy%_wcts7X!$Y$ +bKkTFb2CDQMVu2@T@9Kyv05MW*W(X_={sAtje>XlbTvO|-Zqy5#YlW`D4o=^qf7(GS?f{A(lJmOZoldu9(DZug)Ad}17CgY*UE +cAVtCsh#kTrIPytUz@4)ga0p3O9KQH0000807zB_Q!+YQ{onuq05bvr03rYY0B~t=FJE?LZe(wAFLGs +bZ)|pDY-wUIUtei%X>?y-E^v8mkTGk+Fc5`zf&PQ=)Ic!F)Tu*~cF0ytwo-&*UqB%1+&jg;KQdNrCAK +!b_wKm&X>FX-T79S=DO7xg);$ng^ja6&q#dpt$kgGRu(u7+!l;1J^_g)r3>9=pArsHOM$eW5_;515v5 +g5Zxr-eY{M{`<8`FG3e{;4D&^+hwAzu1LgtVfc+YXuS)xnB+LHq>h2Ip4_DYD*?MzJg3%Y88zQrrkzR +d%U7E87mm)GL*(dd2ZSN{PfYn&X*6UYRO9KQH00008 +07zB_Q>pxk(EUX=G(`b1ras)mY1p+cp& +42gpAVvx#Tm_yeX00;5gZSu`k`pjld!bY-(ckt#`zu^_u`D&(Neq2a0(EvlO>n5_6!Jb_|7T~2%5Xz;0;g$giUzi_)*C4D}fgk|_QT4>3E* +@RLa?2bEjJ^`AKKAd&>)<$vmq(|AZ6Mq8oR2XIlXoB{E8)sP$<_PUB^C12(Tv%Wqw0r0W>4ii4&KPY{ +n2z8MgMkhVs0JpmqMO@*@92b9cFPvn@Vih5LC@(;0U`#u9Wg|lS#2D9go{V(=&?=NAW@0DcYDlmMS<ktD<*vt~rsm6I2SFDE-ub#B&Df$SYe)f8?7D65t&{%pO`0b(AN>~digm*pM$b +zzNGofM5@XW>p+sj$xIo^Xqqh`_Dj^fefJpNhQ0P$V*|YUx)t?|>9 +iXGD#;hbUm9q{Ns~Ep=21>z)Ocv>~>oBA4ZlJ8?J95z97nw5jLZmWiCTXoRw=K4=9foH{uQY)Bybp|w +lbeyw3Oz6gU>YI7WeW`rJnp$coq&_^3NwuHn+>BN5p4OolovHFAxT}K<7owo(*4>r7&^ur;sIygCE&tj%L+>%Q5(Wt#`qYV`^yfzoGe1 +&tfIl$YIYX+h7`WvW6Uh4YDH{P3gynpsRx+g(;2|1AQqZEgf*Y6oW~p<5<`6p!G|Ep6Pg2X@40*bJ2Y +qMPg@_k9HLq5Z#EH!2Uiqw))N9In||o~?mujLYT-K1THlRZt1xJ +g8H}AQ$SdF*;iR~bHXb;n{yvlXraPfty`vFsKPR4@fyhmXSZM`-(Lg$(DH839xEsU)v5Me>vP{ +_6UHTg_{%%XCu5_I~}F6ewaZOFwHV=pHLzBbXC==!ypT*SBVEfnNx6~4+1DTu`P&>&UBE%9fqH`p_c% +SgKRQWjEHJMReZI|?#IQ-A&Zaf_TA%RAt} +O>@G+D5{QR;gETaD_(0Qr<7%BiqGj4#89j`yYV#TCZFOY3yT!KY_NE*9Sf)=aTJE*8Z`%c(xCjTmald +C)!5ID5OF~IgHR&NPw-I3ktqC>a}hrdVk?bwW)h3xg)p~vscOR9Gv?YO80Ae(zS9yxY +Z4nIJCbmHx#=fNbq^bZk!@4m)=<6WHh1d9kD~Ulmero?t#UL_I~1{{^n_L6P +PdOG-+h!*GkozxRG}*1$pHs76-_sq~Ou17)`bDvMQMyJehxf#@ +NvNk!CE1Bw=XlK<;CMh_vG>-7HDnv%sy1Q@|fLwXL8Hhyt2{$<-#eVSxaTwa;Xtd^nhRL+O=U;|l?WR +h3VSJS7ntf6ZzaNk33e{{m1;0|XQR000O8NLB_@+((i9?*RY+c?AFfBLDyZaA|NaUv_0~WN&gWa%FRG +Y<6XAX<{#OWpHnDbY*gLE^v9Bl+AA2Fc5_A0rCzDpB%u|1NhkXoB}~|j#lK#W=oSQN!fLtzC%%tWY^_J(jR7K`siwxQt@klgg;{jja_;jQ}DOrBB8?VwHN_KNdoKi~AgW@AN=lk#So7yelHCwl +tKGZ|8EO%1I($k7#Hhl8N*p!GCFBoUYFB^Pa@%s~xQ}J;~=91N5>jw1e0Ek4I94VcJXgauspHGIkEwi +k_lSU3#o19BPlgrvtl`XI&;st#NG43ujpMStmvh{(tyu&BtCTM6Kt~>Nsh#2A?BX%_%UeSr*q6~cSL +6~=2SXrgWrMmaC$h5elG9o`t9&EtbV0{w<$ue=X+a>Z{xA+;@B+Ro67M8d%qg*RPm**Y%W_L&smgdj0CayE7?z-aWFKazS*H4|_5 +2vKoG*qH7*0Hx1$KSPiz>N5+9Tt^df&{+K_>7aZ{Qs~_qAj?nK++u^ +o7bo^Z6^vK4aYtfcxS`TC2(B49vwtYyP? +yA-rJtVzXjE4?=Oh!MnTnWw%c*l|@@I*x)z0Gbr`1ImH;#Fneo+R+L`?6!rG1rq}_`wsS&)vz%yW3lOVmD|~IQfB +99#e6qykniUAue~=vD0r#e2;|E*{41$DDoEQ6M1U@f!!*BJ;au1Mg01|{bXs=%zXK|H5b-07LIreJz| +|c{-wDC?jn>XlzJ{M;1F{I8OHkkHR6aPI_=MVi?3x;tno~;3Vc={(4piKjHt)!eIiTAShU)Dw5()3}p +tyta7#D?E+V^F3l&=&Gj{&GDYG4GJq0>-h*1jgda)cd2$!*|pEC-_PhCwDvw(wR^b>|*twPy_*7!}!d +ARM0B?$V+(ydS#vmyVy@V8RR!69g!3#+njokE9mVXmoIQ?Vzq>IvOsx1OwX8G70z?eZABBTG<(%aM +T$RA}1RAgMK%auRCbm6gk)z#X7Bo)Uj@#tVM17BB}@8MXrF{Tb%h0wX7I3C`nC?$La`d-t(JI`7^QeC +Gp7*-kijhODxS(R)Iz$ZFH|6h?|Q@ib(evOGY}078V1cOq)V0=LBbD`^*vCtFAF!i6A0Ws%a8p^b-V5 +z;LYH`nBml^guEvTz9m)rwZ&?}$x8k1(S*HEFl+BGHT5^%w243zLRlzPL%^B;8MN1vk2)6eJ1X +K?MKcDB%f*cRq>h3laTpulEl`a@M}VQt5$vHgPJGwW_ub*38!KC@yagYc0D@1fI^p8@pvc(>K@A4>xu +QZSGj7}sYF(4^01LX5ynY_;nkW%Am16iud4jLo9yn^+#ost?lkORRlN*H6cXw}Q@Si{jgk+f9gk-Bk^ +9MfNgqWwXz$bn8{@Z)kNE_N)FlC>m1jhu>q>>NDLMdoiNJ@(Uk+%IPj@y=Aw)wdMM}L48u&2<*iXrSA +>^nrtlNAIj46faUsVH$2%HHAz&Ul!(t1TE~M+>ZwbWXIid-HLB$Vhi{g-t^1h!o_=7HPHEAdy1EC#TE|3Wc>!c#@`2Uzes3~VHrCWSY9R +9Mzz&RZ%hSHlFDIYiw&Pptd-&8n24rHcvUs4w(v9M^W(U_HlH{C^g7#tQMMa6*+$q +t!>rAlNfYK0NJc{MoJ`%%riegHw>_r^3Vf}oMr!o +8GCsUPji}EoZXlTiF^riM8%a)D4`Zid1LZdDX}$EQy;McrIEijvlZt=poVql#={N?V +=3!^ydgoylOP-kL|rn2cbz?vPsQy!{uQvaXwwp+A&>1Bqk-+~+cjXsCr=1*7$LKZOo}mvh*7v&}`?7- +U>aIL>htWqs^%ggWtO%-B8~O`DKok#psm>nwncw^^uVHATb)Edv}`dTY&%?6?-YUsoM3k65xTreo;bf +qm*fogauzsKiOy|Meur~R@nk%Y#uy~VHqNccm`_^dK-;$M +06p80`W(UM4f*a!A*fUl#ig0v1M}M;cVAY+C{uozr4@w=TU16frlqrtvU&x92)M>AqD@08#;frRTLi! +^uuwRtA{bRUN|hPSp)Usflqcb_n1Zo2Ly6df$d>g*2)3n;Lp-IQV=@G_Q&#BddT* +DsYR{7ctfS$bgT!b)7$xP{@?8}b3Pz)NipoMG)1w)==v_tiA4C;-rwwND7xwkDW~0$!oW`HU%2*tVHM +d?`WOcF1NzC`V@_rs~@K6$>91+&!2iIfCD}mK+Bz0VKhU8+M{{Uv6$<*4*iA#n0qE|KX>(4}Xh46z~v +Q@&;photXj~Mi^NBhCY_<5X}190=5?gLP%DFI3nwQ21a&RY_V0&WBEw278pp*zZK-KfMY-tMGWhTgPk +wD7+WRbDust0Qi$w~kX;*2Q>rXEV1ia +NlcM+rD13y*Bj1v-34Qky?IWEq}r?S{6xBjQM-iZHR7UcGz +%d^UHf$^Y~IqD$IVM}#o&L|iuA4(86^5^n|6O8;8W#U0&aIO!k8+Hfi?}6{$=zBeSyb#R(s6|!{En8$ +MK*$_y1j)zCku@L-RQYoz*|#G9u_3>^i2?dYdhWD5DrU=+IN;pFgOtCS0r9D6lgV(V10ZPXAtb;%lcK +cl>AJ3Z_PjgMc570nK_(OJL1YDVq~smL@Co>q1oY?}_f96WMfuq(66o5dMt*r(tE|jpCh<4pc|ey()b +U_y)?VYpL_D($k3Td3i06`f&SjRTQ}KdD8gEzI**nY0`_-&_;!DenkCHgW@{CoW@Ag=L6c@P=dvpP;y +@vpaV0*&0)eC%ZVJ`vNM?(==G +X9%7@yB8(0{l`mAoidpi^Sm&x?LW^;`S{O2lKM8!wQs*9$^9#>=;e8E|M_xyMk@R{T*#m&KEa`rxvcP +2eVa`^_)k9Z6;Mk|7oOi~>u1)2{3oxCR}$lShB(X>S0&3OL$4GRUL>2>(&zq@S-)oVC!S_cG)#mw;W+ +LaD=f+buMz}L?au9>!nbe8pXl&b1sLdH4ne6nHkb!9NG`~qpZyyhJ=lmvjwVkDC_{!$@$qngsJzdHva$MW#43SD2bZYr_F{eHeZO)r`kh%r{|GbqY@Zh8k^2c+_@^z3A0Q_t!|78RAz +n;uk^&;3R<5$Ro7zlK$aDBHe!|Vh5KjmKXt{akli;qF5lDjFg`-fEio7d +Mm|HHmAK63c5~=VxJ~Y6@*BS>g=vaVnX^^2$g6$G>rVlLF=rskM{}-5rh!@v2s#z^F%GVqJcNl(K7f& +odwqJMLvyJ!Ku$d24~FN_WaKE5EAg-p3o^8^lTS@2#^m6SjAI^^EU@3g91%8Y&&S2bJ8{u_^00!(A;Qkv*^i+rqcqEq@(`lNJP`?;t#n+VJX7WkN4rkwtKEh-%4IR +Q%J()O6}DJyxPOl5R1%cCUWEK?|*%Ai2ljZJG%fN!!H*MwKII>!qz#9F#)t*^BYUJ|>@x!xrI@w+__(4MQ?)Lfd$dFL;B4jN>`bV21+bG-s`zA%XQ<`=ptlRNlzI(^u_@ +9#8%S#1*)(XHhnLKL5-nXTqjhuOw$U1v}mHW!wVR2=X<~P9Z`U!AS+PkE!8QFsUQvBtEu7m{HwlN&294~{qA{`R*~t@aaEjN*HGcrXA~#bkHUgXaTU5 +??w{*b^OWr)K9K%VwnJ9%68haxgJj8!|xCBe!0GQuded2)@Ezv0hf|)NRUs+(Vd2RGHA-sKn0H{mIz*Qx#s>iwZ~T83bHeY!goZaV&lw}sY+U2c^AL +yY~dGHC52Y5X%~R)4uoBuvvCYJA*cn{2``byX(Ze=kgI`Jy8bN<1YSEe98@(9ifp3wgExR6?=HvbU9> +EOZ=g0dC3oSjEaBpn&bolPi=I|^Py@$>7;p~@V33XahwQc;qf&9=H-P89C|D%@Rg%f0``; +9O34wM1VXWlDmr%7KU>i0`p8;6toHId5NbaGr#OWXTcWLd3PIJMTg14afw$C0DRhk;E>%|LR>!R*wV4 +;1*o=VD}m)qSauQ=R)m&rS9sg)z~Pwm|=T<1QY-O00;m`Rt8hgWJVxj1pokF6951q00 +01RX>c!Jc4cm4Z*nhkWpi(Ac4cg7VlQ)aa&=>Lb1raswODO$+cpsX9w7hW=6#VcRm1i%SPE?2It;^#p +((lmMWD#iDP}E^8cEsFg8cU#Nl6wZ*>Km+FalXTKKJ(A@fcRsLTX?dr52)8mlu~8g%lOUnW*ZNR-8a4 +$`Uip1XoGA&WzaQMOLOt!Ou+63g*AcwB7__{gHl!SC9o4k7}wZ~=$j0^^gwjgc}@w=*LsA8+0M6x1dyOR +=|tAPUeMrd(28+WI|0Z%|V7d_sg1fm!xuPEN$f$=qJqExQotEo>v$9enHMVhVhvg7A5UDMd`Enji4dgk+k>ehWoA?M?P!w?~h^$!7*a*;CkurtwJ}n#kMM@!qRfmj^Olh^6l|+vd8#|4K +$7<#SE#4g~tRXNHsY4hfR$_-oBk|TQQ!qMnZLb+Z5m8%E#A|L^4SxXQo$C))?pul)(|))n&UM;-|4e? +11)kKR4ki{Zd0N~Zht*4*If*-jSx;cc1!)6-J)h{S7Ye&r&`qe2@_cO9ps=`GP;x{C#rTyI0KFHER3; +%qpnE9uc|)~e7B^{KBUgsPCZj`{r_}S)Qev972j5~(na6((T<9|^;CE~t|MhV~Qvwa?=Mv+vS&_oagK +h7YezW#yDHTDsVP!6nA8$evqX*9PJ16Vr(=se`n~|9@{jQS@e{}A6H9hyOrp$1y&PDT!KOD~hzmZg`h +5UmkKvW1O<3ilM@9}3xd<2z~h#<`<*GfowGIYw-^&7!)J_B?cs0VdVQ-L0cB>Kmhh|^#sx+fBUdiKv* +iS>z7el>Od=_~mh!QpSXiYX0Ms)MC=(7U~~Hbi1^(HT5in~Vo*8t0CiZB_$6kmR}tJ>Vq?7yKWTB1tG +xJ+hgQZf|d|;LlcX1c&eA&mT`lrg-l=i+lwSs7;X_@W<#{wv*D@+0$yI<->L~1L9+bwI15ZwAajPp|g +lE!o`xY!*rZ&C<1|>O`13&->sND31|Qb!Z18*z^nvy)N2T~ZgiQ&$qo^I6YYFH*Vv&NjjFSz*3Kr?RK +a&&z^udc+l#!#OB(KwqDzCLAxGwQM!~OgM@)u1ObSPR+O`$^Jc6F-g3bJnDzF<5jT{_!PyASUNG +QXl3#8C3yCO^CfqPiV`oe6ud+|NwMa#lO%$YhLXTjHm{%~U2xtWI6lt7X8q +wti`q4O%Gh#~c%7CCXAZrS4SrrfJ$>1nSngHHO_$YAC=-(M%EPaY!LoOz5a>%RBVsWsl@>lW$}qc@;< +4@TIisRDP|Wa~q;@Pf_fbb|1Vzauev%R749$%S?w88^{w#9#%I54G%(xzezBoU9P5@8P;jzaE9&UF#b5T9raEDs}oe~)_$)HR%jVxa5{k`}AWDgrfQ;1HeihSgfss=)X`)0|XQR000O8NLB_@000000ssI200000DF6TfaA|NaUv_0~WN&g +Wa%FRGY<6XAX<{#9Z*6d4bT40DX>MtBUtcb8c>@4YO9KQH0000807zB_Q&u@VjAsA<0HOc@05AXm0B~ +t=FJE?LZe(wAFLGsbZ)|pDY-wUIV{dJ6VRSEJZ)|L3V{~b6ZgXE@Vq-3Fd5e!v%*lz5SFlxxR`N|OD9 +Fr9SJF{X3QjF7P0dSARf^^1;^itSs@6aWAK2mnY{22;Zqo?8bL0034=001BW003}la4%nWWo~3| +axZjcZee3-ba^jdUukY>bYEXCaCy}{?Q-16jsIQccjz1aIF5GAm7P@XZW8USY|Fb!962Rv*R|(TF+H3 +fP1_uDmLD3;sq_GOuDsX*=nr-`$SmMu-Brjc-jN;$D&#`oc+kltk`tMe}i27mR;~I`? +1L8q67#8dM{IvS7OeZ9A2_|DcH%Hr|^#+nXsRMqAYUuDn4Qn0*v&~X#9Ibu`L?50=#)qv!)V&Lso1dG +r`1ND%LfVIZKPxI+Hw41>4AaNpv)p7;*ffaTK$f12~5XYj|3?fQ;9g>1AE7r{8|N*=%A?%*I7|^KGWU +s&C()y*WLj^*QnW!pOieDR9!eVDz#@E15%4fi4#y6}e8NVq7jMW7)yeVZZ^P35)ibcKQr9d= +uS=FVSHMJ)%!yc^K1ptZhoQ+N{+1cfYy*{}-yPP1Bk7rjue)#E%eLT6iI61#MJH2EdF4&t7=Woxh&OV +&O(>r!@{tNrl+4 +;d|6)-qQG?`q0cm{d$v|fpNS;w}(E&SC<2=jQ +tdUv$0=B&rb-86ov=%o~R3uTsD+w}bDiCC>iNp$0)1ZfB#$gNC0T{xsd{cQh7SBr_@WkN!s}YB)}4JDfS!wtcT~Fz+qI@HOxHv@Fuj`6f!ZuZuB +4SzVh@RJxUBb*7y(+RDC;C$;z1bj(}gNlGjME=!om4bUi+y*H5ckB$%>Asdf~t~EIqLfF+_&47e5M8lTr57 +!%Yv=KYx@IpiScAUm*{F??~Q6;Aj;X2m-82CF$bMLM>VYq2xNH8)^g3 +}1x6JI6xtf+1@R$8xkZ`tdBM;bFu7o;auvfUjRKRLLMV~n%CgAOOekJETiAI33|8?1NlsWTa-Pb13p{ +~H9ZKR(q!3L4Q!BxcNyI|z!F-@p!J2N786tP9BlW_PnXt`Lrc0RGWDxd3eD(zwhFE}Sh)*FGu+uPXFC +hK{Rt=ZON+uvwJP@Z$-sA5 +(0KHf0qMGKo^kz%*$wGUymkihP)rw@f%P}jO)kGpCuQf{+VZ2{v`8uG*-C3uVajkvw}m2ITs6cNJoo& +$}Ijdd+}ZSq?fwpRkahv;hdK@)f9-I7#dT2`BZLnOpmozEWB2)vPj; +_aAE$|NzHl@xdqHznNAVgM)sSy$W;cjj~zR}__oil&vav*lAYD}u5#l*=$#sMh)nM5nbA5nLljBxZr; +pH+0J&W!aagwAN2s=sOSI$==NghaEMhtdSCTui>pGpJdiC;GP6q;TR&ohOACqC@f9D^Pi_^=W-lLyamLH8>qHl{6b)A)7s?pOpmxw9E} +~;#~s}Vlmyi0x$Z3&595)~J-i!c-kvM%p0p}TcB^zWYSq;!w8JY(bwqLVXyQm01w#85aHMoj%Y&xTKB +>dBdPi1+tm00%g2u(Gi2az6ZAJ8?c}|)fbLh}yiR40-6{Xk_{hHb!Mh7`+3PdcGz{uVYci@-iLQ6{l; +VD4aFwoV+(Fv-T9XXzsWl{FJsna|Kf7fK3Y-NcBfP|k+*1M%{q1zzel==%G62VAbLlY +&C#HAU2u@3`{+P6Yf(ny*phO=#x66ZAlKR|->8ny@i+k~jvU$`$qxB=!DS}q!I47vdVp00FLI5>LO$I +bc39e{`wsDk>yf+L`fT_!&ml=I+ig}a?7o5u)#9#xWdBt3M8*x~FDQ7GLZLtQ%t;u#bYxi +~L_}2n}!5^^G!y35E;}Alt@!u&)gWHKCtBb1b^?iw*2fUcWIy@|1W`VXW706G!8cC5HUu-a%Q2ES}0V +!9@W1JXnNH>oKfu3$G3!Rt5mj76=f~77?TI#54YKJ9QJ5mbwJ@D#%4@Y3)vArCEjo5&`3cWh6!OV7C#LijDPuN+lzt}lT`wTO!^&LBBwH~9p> +LJtIiO{-^e-OUVi5Rp`b0&{SPWu&u9sSGWvi3cV;Vbkw`{Qxk&Qlku-OnH`74dqtAICRA+=m@Cs=Y<$ +w^Rb9+B_90A&W07mSRauP`Z%|F*k(@XEiD(apTpXB1ijp>|{aeG7Sh#wtD6s5`GMnrPNIVVLd?9ILUW5{o9on&7usLCitkE3R*azKE@ +=7E%&c5yK8kngyxb)HZx;&`^UR_}{rFjW{+JAJZv_MkxGrIqQq*@KAAoc!CG5GLR)PNUQsvVX_$1{^sTr}0Pdlp@roD15OL&Hrl0urNv05UTkl^W()VB|v ++Vo#cRs*i&R?e}30d*bZ#BXj&af9_Dy71>E+Fu_YhXcA +?rv)fcjbqQzxCNVSSiW_U*H?t;;D8wH{^3b9Td%6FophCcpL^4uOKf#*qcp +|SVtwrlcu!hr*Z?9LPV=PMK7MceWt}TBGiX)-OeTxyFu21HukhQNc8{3w`uEV>)CCuHwof3zV6}&i^J +3?Pv%4*nB03c5X1w)(%R0fMc +7%xn{)krvIJP2}>CTzDV4UR5Y9>mW{c;Dm1c#b{J`v!NyCexQ#$IJ*x68xcbxzE+rVTIrjt=|-XXDqM +B^w?WJt|{tL1{cuAl`igMcXeSc+T2()?j49n&M7?&3$z6aWVu7rsG&9iail`$6k1hD_51=eUYJ +!HmQ10`BF656%N|ga-c-J;Dz2~To3dMf5d1FHt|m;mB9MHyJH$=$`uL#&o<(gSqmZ@_rVd`*X-CQWHk +(RWJrw}61RNif^v>G`gXRc7h4DqCb~+b#pa$x6JN7P+%~fsREaS7-H$!FGUjMcSbjNvqL*BD1Gru8px +1G4KgXUr#kLm?!Z>n)nUy=mZaCQ8H$UznAqWt9FBC3j-uB(OTkUFYD;?6~&@83;?lOHRbc-S7Jw^HZs +@%@9dOPeKrc!hUUfgq@vlgP`!X$)wS7PjL)v7OZV<-QiAE%iqDlGu|H^-XnX(N^>}J2}b-St!J6q+^L +v=l0|X=e5-Wps-&<6gAD-+&=swsZ|d2P4*fXdqb>q^v%*StfUhQ(1%rrF6y8EH>Rvz{Je0@NVV8R`WwPnJq~sIaHat<`U8Wk$EH6-s17&E( +HR6xH=?$A<0CyI{+TyrGVgDv3fp+XB*e#q_mVOw#6rUKTtVxZftNBPyx)2yVh`g9>@6C3n8+Q+ML(nn!n5g%>;;zwT>)r}tWQYd=92+4Qg$a(@<6qzg9gFsZRcPrv4V?O2s>-oi#(V{%jdz3eohx$*QuP?rugxWM9qJFr6! +GWq9YbT~Dm=C_Yy^USRM}ELBx&Y4i*rTbFex?sDbmG?f##n5fH>qL2T~d^?ZLqK;9N3ZT*$4iL0Naeg0OX;e7fZItYJ=UG +XGHluIyDsaNAKauX5J*p7YDbO(^((JcE_gYB=tbxuTxU2KzwE3_8m$aBc7TIku;A?frdxCLGySk>-Z@ +iLPy#258`%eZ*VRtPs18RRS@rPeg9Zm$7}jVBOortFI3~bSxE?3;r6rl*ms;MMHDoxw9j`$%>4f!ntp +4warCo#J{?p6re~5(q9)MvZ8Z;f9&Wz~e?yabu;D}NH7fF9}cpF0=`_Nv!te5Iyq)WWfecO23*O$-^K +a4#NJE4_>Te$$he($km3bqg4Ef08HYQ9%rIF$GmYUDZE347(HTM#oMb7sw5rn}+X=nwaL7A(j#S3FE@ +Z~DP~k2XF0UbBXUx_kNwujuqq3z@Qe_2c0?-4IU&%e7p`$*sueMH!n9ljDTrCvdW`|Mx8J7jZT(J^k& +`tI65Rqa*W$u=*ZN3wjv4hTDjl!5@6d_WarZ08mQ<1QY-O00;m`Rt8fWV|?wv1ONa|4FCWj0001RX>c +!Jc4cm4Z*nhmWo}_(X>@rnUtx23ZewY0E^v9pSKDsmHV}O`;C~o6FH)c=X<8Jh3m0`Ri*>toir7i82m +*nYM>e+#sU@j6ZqX0uclF0Qq%M|iCC#D_)q^8yZf6b;FFAm7nXMIHEDfBVeDy8-{g>e=1i~3A&Ejt7V +AtcPJS7jfVB(X^+w4*=a|Z93O66+L{`C?4d;`~P4ew<-M@1gi=>v~ZXq-bX=p&dV!Z2eo{V6R0+!7)# +1)PQ_;8}-WY3ljk+Z1b=!z1Mt(m<|}a&Qd^PZ97r#>@a05X;9buGE{_Os8E_*~)EX3i&bgvyXKP>?0gBm-V5V#ZQUO +~iB%6^I@JeyR#UcPL6SHCpZJIgPM)6s0w)m>xQP)ieVvGss4JR;~^x$kb8BGG4{CWu=Q5?Mgf8OA)}F|{TZIa?qs< +P$1E9YKcbk!w4nngGsinv_3s!;Jg1*B2G)cBrUS29jLFM#@yzU-WwOZq^wU=ynn%AA#w$h`EHyFGb93 +CTGle7Di7f=2C?Q1&g^^SDr&-@G&#LFhRK08v@Rl#%nZcy(#{h`-LnkyvcV`;Qm)n2jHBg>4bTH&NtiOp~CPmp<&2zrRmX$;?Ag8HOuH3L(n_!ErGG93K8g3uB?{=%HBqaP$L=rNFYHeRNKe^Z +c-`Tm||K9|9A)yj6bNm$f(@mB?d)NAUm$wj}m}FN9QR$LC8@Itib?Fq6|aH)F1}b=|HMY=(*0Z$1pe{ +l@Pr(jOig;fK2fG;J;Xlx9a$m9W1Z!_`Gv>4+?Mzra+|cwj5DXgc{&o;gXcy#yOtjN`a~W(_o4=FaK9r{AYtgXIQ$q-t<)8Z +T4vRiov{OChX=80W6+>sxII`I+_M?7NfGmx|T>gc?nRRkTODTSy}kZX!Lut!AosiF+w)(UMpE*-3 +?I8*^^&Y2&S#n$Crcj;DyW{z%}niCDq6*MhbnbFS2H-hH+e*4@B8jJR!|KHZjUYGl;hN9{Eu(NU2Z +81(~`^3o1(L192?y!D~hF)2HC0pBle-ZF6l2hxoBy%CU$qt3NZGwbMV+~~e+IpLG;fAGM3p4HvfVa +2{grkxR8RIwUfy^04$cZ)=%$;POGNSds$b;(IoLS6@yB{hY<6t~G`yO7Ef}?~>C +O@e1`LmM)yRw^-!4)$?Z4c}(PU+_*T(b(Ul*rH!!h>G(6s5%sN=cjGHVBbsDgCvU0sMu=awTCLj-clO +L2C;7KX{5tl~53`R1Qju#KA4Bxj?{&6tx2`AytJIoXHen%iX5-)3St~<88~KR*n&z(E&}X^*#`oYejF +I)05$FyWNJY78@#)4@CpB!`Jb}bhenDlHm5h8!6CQF!-_LhJsyHz%&U;*@|QewgqK?Tn!SbJrxcpZOO_+#>K=`xh(Mz$xb+paB +rx+df*0H|S8pA}MUI|7Bd5wZu$BGt?kY`m-smaF5*F;i@_%sR*O8txKxH9 +WKu?{tN!>!0dI)8><#$|T##!A(el?uRTyVSb9B7!ZC-a8bZ>;D#G*o;Its^);hf(XmKL%ZCsQ7LZG@B +^8;IkuO>z)9QkAP+N1o*`fu<3wb!jI=)PSET4Ka^WMv>(ehdit)agCO_~ogXzV6Rb-is?(1pqb$)41J7J{zlX{~}eOp_f&5OmnKG3)5B%)kQ6F~8EQE!i$PInzOeyPu)l4L-H(Wj +dcYbmsGq(B8w6%~*;?G?cQdI|6P?ZFQ_mz`QGC0jS<9ee(_sIXw$X@J07=#}bm%W`cBK;RoO9KQH000 +0807zB_Q}ar@^ZEh+00ssC0384T0B~t=FJE?LZe(wAFLY&YVPk1@c`spRbY*fbaCwzf(N5by5PXl6e^ +?nIh!hNkst>7JDGi~dMWISU5kkni*lXun`_8>vVRO2>nvT{Np=a_>RU( +t>8F3f_(=Fd@Ja@cN9xqLBYIA6I2$NLt02>j(`s_mIP8ktcx<2Ld6KRq(!sGvxJW0#&hU735E+EDETS +f1cD$hdO=k74iD?P4n-q2)FwO3S1@~cF&YfV)8PRN_5)WcM{B|0uSyy=J70n*Sx_wIEHf81XagcM$UV +|daBU<>m36`Dlxktn(InE6k@JeS@>yJJ(`_gUd`aTKbiE$v(a!0ml +GIVj!#Fk(dC$*&Y(ZOfv=w;yw6K8s~szN|z)iO>9%dEeWY!NwOuzGL^H}!<6>OAd1@RF`MB6xgG8FpcFj2l`dHnNi6Pp)R@Y +2S9bubG-xTP@6aWAK +2mnY{22-xn60F4n004;v0015U003}la4%nWWo~3|axZjcZee3-ba^jgWoB=3WiD`el~i4C+CUI|N8&% +MoQDL-G3kf;K&48A&_WwQg-N3*imbz4n``Ym_wF3Ws`B5vXCEqrhqe|1d3SbpW_;!ZCbDc4&$b3$U%Y +w;pR1gsfGZ}x%c|cyIqFNjtr*Oh%4L1je{)V>Z(+q6n8`drMFiLV73V3lEw7nIzD_urM +VRFI=Fx{3-Daa8@#s^jjW&`ULg%s8i|8zNO_KcPcfDTxPVv|WzLz15o&I>?HA!jfR!XDW*d^3vl^rVmMNs3 +kydc6xZxu6LCe(COrfPoxHgJ!Dszz6P9C}52T)>6fHw|d9(r&&4(Fk7Np9!s+45!$x8v1nyjahtAuLx +gSuU>T>-lm)cOPNAxPveA#gz|8Y0(~hDwTCWGPz9>lQy;x@jyZ$! +t0yZ=Xi}9?<3Bxy&+0ZL5E!U!hUWgynfi!qJZPdkLnnLCOFlI*VJPJ5v@GX^RX-xT^-s|?I)DCQJ@tr8*;lDkO%hLkb!Q{U&_uS +lVeyYhumbn>1gRK2pZegvc!Jc4cm4Z*nhmWo}_(X>@rnVr6D;a%Eq0Y- +MF|E^v93RZ(x-HV}R{;Qw$?USdFDx^CEmiwSnq|xx2y@K-qh`LZTdJ`yWQ^JmTwee6oBo65ylL`SJ5-dDO4T;$mN(!A +IRMKyoeL5BvIPN+Qv+;u9f)WaP%lbeNz7xImz8GI#R#nABBbFJPU#b@7E^p@3*7~wNa3=GWuo!4k>AaPj(jV39NglcG1E^v`Lo`{zd=$v}< +RO-82hPdJ*3T1f5W|ycIp;=X?5fGB|$Vb +O65=0yu2;I*`WZ3PHzTf4b_(Zd$fUr!G7`D#$n&Oz87fQJ3d~|+NKHP=&>CXEJx2ngKk9fcXLktE@PP +@6aWAK2mnY{22*W&T)pK2005E&000{R003}la4%nWWo~3|axZjcZee3-ba^jsVQ?;Rd2LkfZrU&u{g +1>uT%~`YqM`fHelT?srEIWOVIp)@HBHDkH^H5;GuvsJN7!@i#de*fAGAb>VBdSsJ?Hr1BbeyCG9ueLc +zgQh1N{5z1S*Aaj>ab#W-J?Y3YrV@B%oRs7uveZ7Yzt6-*Of`&4D(xu?2EGAGW ++61>n80U=BtBnlc2Qd;_!Q1c@dOjg&LqY$ehp2P`JHl1kL1B?FgoRW{1ia^%I}i%E-sMs-#Su#3wsoH +tCG;3KEr#AWM(KgNgM7*i08W0=PQoR8yqJn$gb^V +Mu|wSw#MaygDx^Jxr=B}^94#e6kiM6~)0aAD0s8P2=O|^00#15YpRZe(yG;J5*>gM}&J(*1>#Qw{u ++W~qW(zga#(pGdk38r8rV&1dJwMy&=QmL8i!^tOzw8FMJ2!iHE?301~We&57;FyC1C95pCgS_zcYXcZ +$;*S0)s@QEv3Jp=)p;qFdR(?Y1<5dz5R!H{=6Wyc862H6TI}*KfWSt;L +n=EePGU{d<6Ca$%xUzQ7>}uIFmo?WBYXo{|KVRYTDb@SJEx(kD9x|n|n;R#W67M3hGhSdYkEOx;)2d? +F7->1`S_=AupgO@Cg%dk-Pe^kUCcV@Q~8bx9YWR&e}H})qhY+0|XQR000O8NLB_@^aoX(!UX^T${GLw +9RL6TaA|NaUv_0~WN&gWbY*T~V`+4GFLGsca(OOrdF@%|$$-N`m|3pJqIiUHZ +86l?FcfFFAjWT@D+tV3qi4p%os*NrV&SpeSWoHcNmPg>CocxQ{&3VkMuY8wmonl>0-C=|g+^z`8L$`) +3fLIUjMxH{1~%0k=ZqqQ7g`u0r)@}7XckO!f|-cKXfaMrEncO0=(Q_=5n~b>-4P5%4S3QW4MuGOc{R8 +?zj%2Cuez6)-Qm@sKZ1)(=v@q-4z30lL;Us(y2ESueK36529D7}J^Vwg$pbV~P$c;z58H_InhBvQOiE +%Nh)@LRnw+L=%3-SBb1l&$h`FANgcwcGzzM-bVlE6b`OunQsAzu=l`3t3jf3i$LSi3H79(;l7uql0)% +72Jzjxm6p&!3>_V)lkp;mKO{k{g}%SQL%W98Q4ydqKo<>Tt#%vxGO%2(SC3* +Rr6EMx#5%u(QAb%|o$(LXj^5nlpSVc;cZfLDI5c35MB)b8cp8QcO&E2)-Yn5x(C{coep+@`P;aK%;JC +qXoymz*wos+C!fhEhrTgco^vlP7$RDwDhR}u>*+QaIkPN)e@>X7o=5JqV&!>WVT(0%+_?2H28&x-%@eewUx +L4G=gz;*!!T5mJ7FLwuR=nakt ++_DNh&vVzl#{8(J?fBNDTCp6Lu8cVHX-vn5SA`>D^YMfMW|v5WMAos*X=3BFg3#K$EwOF77o)bh`p)W +SXO$Y;J2RW+E$6+MO{Ec0omlKGzf=0{PIO*9%cvB*v8>QuVS+-a!5u`H`sHF6@R=OEB{>s_G? +4U^cK0Zk{=f3}Uvjv8|r8cQ}fpTcE4=EmpIrw2a=XZ>^+(vcU)8_!gVB9=hH?iZ&m+`c7ekg-xMMno} +)o)XBt|Ukqg|jodjV+N{t2*6XF4Eki7ju~Z&0K0tL+%yLe5y}0+a#@C4%e(Z*1Fx +b(tz7{*PU`Be*-3`@Ob%7km+eU=hAFUm3o4#H5Qe*yR`V*H+MO;TTl3n?(?=03or7*s> +LUH%ID+^exrJwtz>-cH+0;=;-Ytf3z*3gW6n^dC->dtLpFQmEXtu1bVrp%66d`4V0XS@_zE3MPl-icH +zjb>^Sdpw>AZ(ma|gdXLzBOJMIRoRlGQ+p8#AVHpMK8J|43u+7d8^ceigdV&wL25&IudO9KQH000080 +7zB_Qv^4C2D1YI07eS{02}}S0B~t=FJE?LZe(wAFLY&YVPk1@c`tKxZ*VSfdDT|Iaw0bny}Qc)&^3E8 +DLnQ%sT@doD=cPXHd{CaY_@!w5;HxZl#nKp#=tHAkUz|qq+2uK!GPDP#D{ROk^1%P*RM4i-ayY3Yb$4 +S4_B9OzyIrxKd&yY-onRHNz`y7^lzp-J$ds41|N-^mjZ5uRc5&t|Mr~zdC1#HMn!`-JAh@7}f>wDG2*TH*=iV2ci;LxQ8H*q`HgW*PBj^KTFJQ%l` +^%xsYD?u(j706{3>HdJxle``xb#B0D{yjl5pOllx!#N$< +AbBk4bNPEUY78~EI!$m$H<&#hTNA&WS9K%E&Id(!8fJi-X94b7U!a2Ypk28rbCg?`VkIW+KyaoEX$*MqxIV4F+5|ZT1p~~BIfJjv0SWyR}U +5QZ(&fme*7*)&RPeB4Ug(^O>d`o9|wnVh;sN$&B#2Yo3sB~#HY(hjpfN&j{d8YE50Qj1st%5hxD99N*(Zro=J=UH;1Uxf9OyWRBNP5>7YqNjs(H`F8n8&KM3XOTjfy-dDIlPKhx +zLbwV-i6Nmozco`RlJCg*4kijRPlD;z40Hsd07iQe&o#^OrEAHzm@fD1EED}o>x;}8!bB{I=-s7Tpi7 +WIjdyuB2vtFgZyK8#;uQm){v8aJebh()F4JWlf>d-zOt`CsBf_II)_=y7Q_=vb8?q;aOGk5ORTQ3(_y +eyOK_#q#u*KXZ?@6aWAK2mnY{22&UX +RF$>^005i_001HY003}la4%nWWo~3|axZjcZee3-ba^jyZ*p#7WN&w6VlHrbl~!$U+cpsX9w7hWpnQ= +6Rmt*Ymv5qioQFpw1p68CFFJLN*O7U!C;O6?5Uty +MWjTQXO#8-KKc0nKb%wrT96DUQ33d{y!Qm~kQT8j@)s5F-XZh~uYt%K8=9Pf7e>BqXag_a~ngh4I_6(3ksU0eMexX1-&t*7+qcMcDsPpU<0YLtGsd3S +D)w8*&>|1Bf#x~N0Fn}pzvGC6_H(6zzPBsvo*oY*$$)vmMJ8gkrwbyal=LCgO;h;F@-iv!nIL+U791k +I(Wpo??8$%0nQ|ZdFa6HB%Fu74f18a{CNMkgfEkahsk0&pM`M$0Mq-$-F!K}U(oD5Ocu}ZX}-Ag0ZA> +&gRh0M3_7Z~%D$3#tJCCd;p`5l!YMo^&Ia&0TB31DKw +?#eHc|46$x}?@|y51a_FWqjrC8PF_ub}w6CzwBqp>J4dB8 +8z!C6FNQHJj-BABhJJTCx2{g*rK&BeloKcA${o*a&WayT18RvOS+bqlvIv?b2Bp(2O=qDqU|VXRc3$`%%<}lH2^8gu;AC +x%yNb(ybqw?v#SAwur8cNZ_l9SfjT)fVL6yu)itK8z6&x6r(>K^?5_RS1Qpg{1;G50|XQR000O8NLB_ +@yrCrHqX+;1;u-(|8~^|SaA|NaUv_0~WN&gWbY*T~V`+4GFLz;SbS`jtwOP$_8@Um_yUOo?q7NZuYDm +c|ACyvRxw0I~*_Bd~9LMEyvA`in!kPhQ0gNf`Y2P7lm?z2C!2Ey|MR~LK9AwU*(dce;fBoZU>{J&kBj +*dp4u=OX|NHxY4u^*a>}{Dzq1Y>~KI`)7lV=-b?5(zolCwA5WO{jT{lz~1{J^gGioMaHU>htaQn8@KxHe(hBg9md#`)n7B(t;h0n`)p?Og +u2R94(k=Ws6_n7dFvmKF9)@9sGQLdUkPrwhw`;4ewM +YtYt?0T1o?DCo9Ga2$b>(#LW1TX~X#32&_3x0xykpQqB9!>X}<|BZ$pZT4&^>bZhpi#e>!{0W8K9>x{ +11`E`f=G`c>&?h}*u=QnRI-`%kHqpPdY#m)KIHM_iGru>N5dr;T&;S7} +5Y@O0uMwdb3>%u_nSy2a#+cQaO_;R8#Yk&jp+7FTyBj#0p_@Y3W8S1e_9^Oy<&Y=Re)k3kN-Yf6LTqS0#af~yM81 +tyxNxwx6sqDFcmX49;XmtJnmYN&H8XM2sO4O(J||+i`eP0{lBqUrn#%+p{IZZ^+|ytpH3Tonj8R}8kG +W*@Q%2eNF_QEH=JJ@#p30dQ*Zqw^A$%`SM*N7K|ZdZ+mDGqMN&LNINu^B94j=Ojlv9pMevZ9nUe+HX` +^l7U{cD=?IZMTnX_4`Qu4=^Aza0a$4*YzFghqwnUuI^2l|-Q7HuuC<|s3HC#Ky-k;qrxM?SHFnA}xjb +LKZOp6gOMk5n>;62dJ7P+9<$f+xgo6ihA^&Z$jK+>V1scw1j+B_i#-^sS8S#BW&V_b0tCwo`l3JsT+q +)@|2!+i0qVReuS#0&Fu#0Mj7IL=ceNX_sBLnxX`9b$dxeIJ|$60h9jdO@>_$U1cuSAm?|F+#{%t*8@c +=*AK@2glijugx~EXIcT+}_)+>-zReMWv|}8IJ@~tehTjnlzx%!p6G`-(b;DayT}6-WvF=vE_P28A-M` +ct#J1h)X;=d*q&J@V4V2`z!}yka+iNM0+pO_OC<2@;4BKw~F7_?+?p-(9eiHre`HXc(mCA-(TkN+$Nw +wX2w8Gp{Q(ci$bZ)URMfLWDXQl8S5})t8o@$MpDdrg1b!iLw4D>Lm-$5Z=7RbAyMW7PSYKVlZkU7dU&# +ag1_OW&E=|`_5?;}|=DgbL$b|=wcyyvl$8#l1{q?-NBLE<{_kH}kvDS!*_2%~m&G*X77 +NvFT*(BrhD*3ijAss&)MCA8zyi|@)kvH@;PB$FVZ}ixbuhFZ*W^g&4eQhP?n{mKNv=!Z1daGx%HvjlC +QZEA%a+V_P=v@AWs<>(?BorVIMmX__%&1X>!`A*x&Kx)ek0wg6t9{$vb8aGl%B`a}qTC=X;7G#Yb4Y` +sI`7IvBmsUY?EyzVOW4=+Xp9i%B2iilyljcqo7yANYQoX5rxbZ~M?S~SkuQ)=Gwf37iY*s{#v{!n1KR +1*q@v+hD*lDbtYHablc=!QVksy?P!bPx_K*%PKC9iz74zJu{ +a5PXDe&R1zvo=^{gTz7c?y8e$?=P0ZDuRyRqS$Ap1+|YL$tZz%FmBQGEdkUA*&KqHpP#nHn)CAOHiF^13|!N>Y> +~Zs$ZArSl)UJ?jU>2wSg^j__IwRq@en!?n`jmB-3*mSGlGQp+U1f9t`->COqfAX_cTG9T1@%aFZ^i6D +fQ{3?dEoD-HG`)qY3-gPP%4GZ$hyO-o}#eeEIkeu6x@^W|?8LS?6)!64KYztnAKCp`W|}zKS?H +(e#J}}60o_1ds*{O;`Kt#7X+)#*sbFCaPPALm(?JsR1(I>2{nl&=>7*#O9KQH0000807zB_Q@TM;e6I +ii0PFw&0384T0B~t=FJE?LZe(wAFLZBhY-ulFUukY>bYEXCaCu#hJqyAx6h-$!|HI`us8E6*vqPZ;1F +;>9L#I$}g+SVbSM~R6tdq;TZ@7nhh$x4%ZV6qV6hg~W&w_~3Bu$KIoihdptjPJcn<&Hgi5dGvfAd{mG +JB?V?7w=RtRKG&ZN*0H30R=0QPmn{AvN?-o}pw_;*?jHT&YGtl^WOQ5OzNv7cG_4O$4QmDtGn+f(_y` +F?OEC!w=31GYi8G!Se%uP8p)?e5W*d15ir?1QY-O00;m`Rt8g{E102U6#xKES^xkX0001RX>c!Jc4cm +4Z*nhmZ*6R8FJEwBa&u*JE^v9(JZp2?IFjE}mH&XDKS-L$8ryMhcU0%CavWt>wT_dDm3`SNE=55$Cls +k6sn}kx|NC_}01^N}N=hbscdANckwCw@(cJ(UEmvt)u)N5k}o5XZqR=JMy8C@kckZ?p7YJh|lR_qgDhHuC+dh +|(m8<D}wm$Lja{vx)q%42tm9FH(OV6ugL*+`b;CVGz4+BJY(sC!FS +FXi~*}i!gxLB?ALW#1gI7uKl{u8^ehNJ=W&qV4%pO<2qs)-8BdD%f!zklJmzy2?d|&r2w{vb#*;BJBVk#@5Z^W=L$g(K7;o#fF8XZ~3a9 +hH5QfKlVlR@E6F%kQp4&wI1951FboNeB`PkhK3U!5nltgh+@vt*)KdS1Zo_Kye?LQj05(bxFobi1m)e +_pyEXPyJyl4Lx+s$_&UN-ze)OQFl1*x9~`!@Astc*4~OkxbY}UAhYS>*-sLPzlP^3ga5?v`Gca>jqzp +qqpK(}6F*>XCKmdp%T%Y57z|MKm$w6x@PjONXi&7jd9*8BxE5(+Dz)TKuTgBjYU4f;~WSGGrE3%^lIl +jQ9;e=h{cehtjF8E6mE{2e$fS>|~tEn2)#nc2=SXgnEJVl@V^BDX2gVlMiX4LS9LDrL=036AT%g~ +&pv~+21lWFHr+#tYxlx*iPEZF|I8nL`hxBWDvPq(Z3wRjbq>~t`i-L;QYcu)*)sjY@d}3dBv=Ze^y+B +ARX~+Af6I9!^#X!IY)Tyd1X~1xVo)xGr$IHT$R0FF;RiK=TFJsI(8RR5$?rqHD%d!g^LvU&*2EbQ{JF +XWK}mo*A!D@$qv~IrutN)Xnt(5@c^wMaUu`9HfILC1_@osdZA7yuGJ7Lp^jz~dSYs)SSm*D`X0I+*{N +xbWlQ}^jmYF#%6_vnY5L9JIV`M_ +oL~tOkA&Y@43$|(uz-b*R#7Eg6J*a6r-%hcUO+Pk>AD!8?dVAoEpCzrObdKJN}|H|yE%^+wC1A&7%RZ +LbwO1z!vthMP2z{@F3v(c=r}wFzA$6x_N)PmIJiNzzs2varRVW-sUu(*ukW%*Pc#Y0E!^apUxx1?6Uk +w>r}IZBstk*M!s-Ug^)guD5eaSk3(eeySr>&f-u?Z +@~37+qAkMF|u14vQHPbPE;fjKk`}?{RgBuviryFN6>R0ST;rDU{8b0y6`PfyEJqtTEN0h1x|}f0NVd{D$=oN+4YfQF?FmEf2n{0Js|NA5JZA17G}189EF>IF86^*D)GO{JQ!)z48(=Rc~q%HCUyqE+PiJeWJRj^N1V*eSXGcC}6& +DABIYH(KLONbi*-b~*^=P;kND+pU@|b|3MNr{A1k!!9hV*K|&mR*8AYW@#E%c2Lz*rw0cwx3daN(jbO$gpGd#K} +(Ulk;BkMv|zVW$*KPzED4EH{58`i0LXE_4S6QWtJ-{|th`31$XxbXSxA)o%@#t|fMzi?SVxZQ?jx7?w +*evr<0(^yEKS9N0t<#^F#nNwD@d{?Ckd?ArlM=$ksyR#4Xz8^rb$!ba-{!vK+^77v1ifZLVZV +*@;t8iaSAvH +t+$h$bFPu-1P14;#W->z$Ags1^So?XC1nF0bAtx*rm05@|eUQk9lfQ&1zPSEzQ$J_H0>7dsmR&C!%Y$NY^0M7U7mO4 +um|rk55LCUdwY#Mu4-c$ERu4<~6H-+3kfYamYSk{YiI*!gTc89GWH#Ltoug$RpjeSiug)Z_QlWLRzTihTw1w$Iarh%dPP*(L`l*L~T$$ihK{taSx +?)w|F*uXY}s5MrL7a(adx8zgPli;)l^ndp>l1@BQ%^+WdLb;>Mc7Kr8*VpoWO3EV#8wCUNE8XiiR$^n +6}a;`>|x(479eqVI4Gv6%-_Il<>TW)HgF1a>7B@E>AE%{R<+fN822(ek>Ju0Azam~aePqvd(?I+7kS` +DGK13BI70JS!w-V8O`aH^MM`-J+at`fFA`)cPzD)+>>iQ6}&w3M}_w8S|<8;orHUj3qwjP +6v$jXB^gK?jvjv2;tLiZ|G#|+sQrm#Cm&Cb2l1zV$dsc} +Te>7xMA4F`PK4b>#kMuI0V$^(U=i{eD-UXk4UFbc4^z^X!J?a`uY|4b;e~fbt*Zj^1jtq)(Rc)4Ppr6 +$Lar7+9UazVO|)s{voP46%PXsfr#q=IP@nM(N2lUjoqRYDT}TUxlu7xemkYM^3> +aw5-reAJ=}!E%w6`61JQz%LZ?mkPR*wgY0a38&5Exu<|ECR|5vQ}s|^&}y9IaldC6v#e)k}c;GCpgjY +!fv(aDVpfD6lMjt%A@5lJ9lu8Ied1%kmW2ZKS~@T$Zg#8r+uHVS?K +!jJT7qZG(!5_{pm&a*v%sg%?B#9&M8+6xEgkNU^xZeW6aFZ3Nv#a1n +KS!CfZhm2w)EV^aj*J7!qzJfz^a11`4pj2%E~@(72nwim1Lia}i2MMQ2q_tLd%c{)SOut$~a-~&0xH~l5RP~u!vlWIOUl>aTUa(asG<&6PPj_V8S#G*dhio#5 +O=jUV4cj~hwG$(nkPDQTmO0fGz7y}e3tI|YH6PWmhKjo7prIWC6tWFgf^z@QwpqZ{%roF-f}0hxUA1j +L!UR@tn~T$0Kz9jzSaG2DemaickC3N4_zH12v-PI|=y;tb03ZY(S15lLGSH6!jj2ukwgMy(3j`pjjGqbO +}ysT>_(`?+f(p4L1tsJFEsJS1LNdl-5>TGh>=xI;t{jM<=XvyMg0!A_m<4ZbunEC1IT6b|}H~oPqf3n +ARxUMx!k}nnoZy*b$#m(to1`VH`lhwf55iA#rMB7GHWpM;ge?`@X0dhNtm(8qN=z@zaFy~N73gZ0NXe+ +F*4fFl@w0V|d)nmxZi_Erd`q~~MPo^S`*IXN&hBDsVW|ZA6fb&c2FHaqBa)S0!?@ +baN~sc%+9X-Eu+?m3Xvcj02ZgMhTsy7iu%kGkt^V+vAU4dj4)zj=Qc=LCsa($Y^SkqwB2}0P!XWwmNg +_CIyGVPltC(09ECRYLdfANpb+jq9A$bhU!S2d>9mN%LlZvXbi={b}@VFhO8%uRIEhuFle +DD?X$!Gs(eBm`|{UGgpUpTAn!QHWN9R)Z%^Dv+D#Re<>SNw`y>!BfxskPcOrZjg2HQS*Xjc7UPG5_3w +0v!;gJAt6i|l1de^y6?dqG9Ou%wyvtY`rbe0B*QBEL*svXR4m +Ep@x^^3XK*cBu?jYL=tn)5iqq5?#yCD}=NY^v?}Pi-x-u6UgeD8{^2(^AX(A{wTa%B$}~3N7X!`znMC +B{x%3GAAXMFO*=d^Nvb19j{|d?N=za=>?U+5~uI6)A^jy^I-SUa=m25T?)%lkp-G9tB<5St)mmgXw9rM!&K+R=s|t@!cw#i4b*S`OND^Qd_N^#^*r&jR=*HrRkQhcW_1B9kwyPYTx3UGN +{I`v%D#K7Ikgbtx|;f($<>##&m1FXxiLp=PF97!_+%Yl_(nE*Zb!_s+mVcZST9FQM{U!V10Q3K*s- +1N!A?xRbr&y{&USs1np(u38tYe2(A^ux;M!%O5vcx?Xi_QBZq>DN0|{7Dk8dk(tghKM+k&M=V8O)~(bUN=wjlplkPq +lvdz8n0?GZd3$6^Z{uLKTMSSylO{p$HQ!P}bvlQZnD;VXC~i|WO9zH)l)4SS8QZ%%4mYs;bO>=%Gud* +NQ!gjH{uJ0A-nZnr9W5j-ZG3XS6#Et3KjeRRUbl=5AGx~$6A4K@nPnpV#q-0;j{!w0&>m~U`#nc9)rez)J}a_?wq_O)J!wIZp%Oep&Dw^k-VX9htk0_m+5lK=iUah +G+nRtO`b0(rqsJoYbx8_^oW>+6$Z)=tnFm5Y5oa(BvC0rhyaBi0>xMOWk$pQYc5xl*n-HPQB5-&W3B5 +XojT|^lkMZd*E`t{c3%c^Oq52XE=C|Te>OcI#eFm~0Tk7nOWa1aEdRbxvOu9%4kitrEBGnnqY!?Rw=q;=-V!o|~!VY*JlTTA=wKsd5 +^yhcMOhC_K%dyF?;FB4IUnUkE){Az>u33nzQr8JP&%w^#b`bYf#sjt91itndVlImRw*84LT-XrMyiFJ +bl=jf6a_?~*Y@ig_(@d`a1<9ywP#N9n$|BVl6%c1z=)vKeQU(u6k-B-VRd3;2JX)j-LWiD`etyWua+cp$_H{kz3u +rFdG2~x5(jR4O`J#`J%Bt_;9+cZJeEIQF3ivmRjvzPt%@v5tfV!-rZlSkhWGTZe-(!&P +D?*vv+pW|F-EW2qma65EltOsS%N?1|v?j&#*-n^bhbk{0jluP=pTcDIAzb}Zh_#+L{*1Ox3ZJz|ZeOKj`n>^z(Bh+PNU*v95rL@M>sC*K;_XSCr(0c_!_t6#q4)D +C-;ymy?_ewrP;e3{?Z*IfnAxwT+KY#_G(26`ijy&foemUoV7cmOB$oW>uh8yd5S1!)b5{h%VnGfMF!V +Gq|WMAb4wkQ;kYv+CH>|$#fB+K3&y@p7ty}P-as`&=*cfkAA`h@+<*79Wsz59IoE_@iG6{lS1%!!v=$ +9su<9zg3C*G3S4S0FDLe#BJa&jc^aKu0?OFD07jRP$`5TfO87VgvOEhp{~Ot5)z|6|cRLX&Wk>kN2zV +k0M*ci24as!0)fu;S^^gx8#|BfAbG=EKdgi;&ah?JlyN_^S=wpzjRQw4d%54{HwQb9iQu64q}AeE?R@ +Q-S)j4C|JLt7fY~$zEXIaBiY4dx2tiFTv>idRN#D;;T_nE21*!7Oov-i5&$S$3nJirr0u;!BF#iGFB^tO!WwTUuq`F^NO)|DCXxJhG +L^j~%f|=l7bjfMZzi4~UgMHK_mPsOsx74CB79p#EE0R*Fy7FXVFlT8+cKi}nhkA(r^k2#@*3Rnjtn}F +C}s^UWh$)kY1)9$wl#aUAWVBI|l7{E>fS$n42)4&t64JwE4h}7-=CwQ +ohx~*Q~g2&z#ZO{+27na+wt-;qg5Ob*@TRyl&IXD6}QWV9b2XI$6axjf+$DWprtut!wA`5mrHS44YUm +AA3XxjGbH+I|4{pva0TmM@@YAU45|D`XCkEci|oBbb{ju^0kyeuU1QY-O00;m`Rt8fvc>Wb+1ONaa3jhEg0001RX>c!J +c4cm4Z*nhna%^mAVlyvaUukY>bYEXCaCwzhS#RSw41N!g|3J7X#u;S&vTp?nYWmuh%P3&BjPGz`k)ia65yt`oYQ?q10W6rEAEr*IG +5*b_sQ(kWSWt^?k36TY|S}pf(M5l^T{Hy9!!ua6Zd8Qf+h#Le$=QgF-;lvKPpW)yg{*@jRc+XIJoy4l +1mrR}PfkZYd({2v1P}r$N5zrh@EAPr_RJuF7C9O-Hu2n9oM|(R(^4i!q-BK5C!5Quh&(6A30oQsVa#d +nO?{7vdF-WmZNDkpw~{lLK|kTry<3KTVnyX_i6ud@ek=3&s=PsD#V~2>hq?x>8ptTH>tfrE#Lxrmdut +$$=ASWhb}9*XYAR9;0XpE2F;;)2ZjvMNosbCj#*JB40*Xvhl7_WcbbFA-Pt*px9AE5kF^kjyi6%`of1 +3wQE|`eu7!Yv4?Szn?TH8>k-9mQPRR#xGQ2_l4xeUjm567SEPX49$MTc&Yv7>5Ar29 +nA$A@(gPL(C$Nu5I|rGg@#&w&22%lc-^Bh~Xex}$O&WJgsw0ZFt^!oc2i8mXGjvZ{o2#J>^T^haQC^w +^}U8Y?#nMS3=q;@xhWEK)Hh!Ep~v!qYZm@q#HG-(^7{pF_5}5S?tPCX0e@GQQFL=)LBP;5eVr+<=SA< +_cd?-6q&ye*O9D?~9u;2cAy+;~rHXW{H7szA0bSz(716Bs@DBhwc4g2Du$V#4g~g#o8-&j6fA`dlO=p8wnpv`T{69u$Tyd7o6|8rSg%+ucE9ej_%W*gK_Bz5Ll@LwWI3bFDU=48<@Do!sJdB~jCOBe +%CRGi@Gfny*fe=Vi(S)wT$R}9YX+kj2%kqU9ZcHg&liK?qsuC^J)efd7yyd56g+=!pwT*O+D>Ql>B-w +Q?ZOJ2GVc|NKki&&ji`G(#3!5z#$7vq7#~9~SPeDI3Qp7veg~H|8T~!e$5@HLzwSl7j4O~+v!x;%C)H ++$mro4ipDBzPq*@5p1?oxHWZP@BgskEYa!x9S?KDOSvk6d`!_f#-S&3B5f4}p)0N0k+9EFO +(woK&f5hAgTB^MqENnICx`xe=_BokPTiVjxRv7P@XVHWBJS^Jc40kzFaJx1h7a_3U{7F52r`!|O;1E9 +EJB8PrEWhoz9o|V`%@=6p*GXPVhg)ri+aokyZX?cr8PO3lOlf*NX}BGI37!DFbF +F6E(Hx{(rGzNyzX4E70|XQR000O8NLB_@)CoTm91Q>fj4J>DBLDyZaA|NaUv_0~WN&gWb#iQMX<{=kU +t@1V?GE^v9RT5E6HMi%`ZApc% +`<^=wayX=9d%OBzOXAF(`@ZMi(Yh)&Y`I)Fs;R_s$>gRiD#iG!F0w|6rTP5)+4E&!35XB1*;H>t)|pRZ$(G_8_Hz5p`Dyp5lqy`0>XgxgR}{u`~ +7|<7!=0n?+rUM6P9W&VDW|k(%)>ml>T#64Mb$#_QUUw1?6w6>BI>yB*H(7>_ +#p-v+70t}IR{dk7qP|}^eN844^X5ZUY-I|OxV7%0=4-)HoVm#4y?_cWCAPbh$sOC^1AS4mrevxxRBmo +Id|%5y#LW#OiD_*nh+?rLpW{1zz$d{n!E3dsET@YGteOogaVYY1KrMFOabEI@ZP%!}ga0*{#8<>}NGz3gS=0hyohKf}@3*1?w{jyG&&hNfCTk|PPzkuT(c~#+-fg|P@o7fdSat`*uMib1!JDnl4_wocE->__@f1A2YN(UfSg#Z +{GzAd=gmXfApi*5bGS~!hGXPKpxv$o*`vmp-vsfojZ}B71Dky6&%Iq{R|$zfFlZo8X1JvLUi~uWjRH5 +r>{kxl2_=rWJ5$b%fL?DQQAp$*Rd0%6zu>Pp>T0olo2u1UmUEu!isBbCK~;P@W`gh?E_!vdMNaq8AtL +Mv)*X(Ar8=a;gJF%yBBpWSJ}akF{;_iT9M)mMJQ~Flc)Wi;i;icV6aH@tJ!&+YBVk1iwukJ*K9{=D9x +wJvWZ~qb2+Ca8+m)D*d5;rGI%RcNKYpo<`NIniAP(ujpvoPm3dCYt+#Y?1rD(WwLA*snXxzhXbQAWuc +cPbLt2Dq5oZySy1IDBvs%o2{owdvP;xkVpDt2PUMORU^ASt*5>;<&5QVA8PDSBKS34o14W83-J;?YN! +!XVW+&Ianjm9yv23d9-%T!#}0tiU~N13uCQVph}{yCHP*w1weDo|zMW>gt;^~Refr-}>jgd?wnLt7}v +@uVZuV<>@JS5Fi53>Bq3u(FVZ%XD=_;W3s1Qi0>=AW#Y&0vm@W2S%k@lpz?S4%!St1CrCdnr4urO48S(L%{(RmXt;NN*JImJz +mTG9}@RW--h8hQMblCYd=YHaHN8FyXbSqU;&gm_IqC~|_nR +4Yry0`WT@KJy?$;P!RDoVzrL1t7~w_D$JGBHS?7efxpf+vI`e&}%~d7cPuYD4bhyG!=%>u+Ahtc%am0 +b`1THKbC8VoQ(w2c7~w61##YmRNYM)=A6xuQdsEbK@0y$X_2I`d;URU4?IJqf|PaD<={YUmBi`A7X?n +2pp2i9Y}9AJcXW08MAj_)gx_*`Sj7OnOvt37f|8hS`R1Vb7e=U4@ERg=Kpkd}^iZZ)?>97sUoiB6}8a`=<+i@Mkd1GZO|R#w!iW%A9NH!k +jw*@Cd04c3sv(e#s0i_4<8mts0+({)i?tavp&wi*UsB<<=I|Ks9Iv)_*_znW4gpolC`kZ#r8lyK7Y+v +&){Pv3ubG_TEL$HttsCyea7vuf8(Lq1RS4C0ftE7Uw#x&f=C>*b4MI2 +d~fZ7otEoD_MX;OoWJijfdpL6y}j#KM#x=P7ZuF%EOXiq1zhNF#$w&Y_r%yt?9@{@HEJw;V!Gk$Mpll +F0KcyQNDIC!Hitz%I&%K5Q`~gr*=N683Xkp&I`x|XVa7;{v9}kYV*~r^XsPpPdt7C`*uxzjGE=iBS4u +fpx-AczO=*&Lpr25U7H=T_@al$MR>Jk~8*8j!J5Qj4qFWR +A_R6_Yb<}4q%}L*Y$|+#}?#=9(B-Aj5b}p?8O}QcwWlFR~#td!YRY2gL3no=?Smq>Gg~)-mvW)@^1i$ +n4LhIL7@&Cs~FRxSCs(D&(n4);C1WoHOP0BVqE|cfD!s^NYD&m&2E7>a_d$*>f`<)4!U{M3#qe9;~uZ +RiVdD4R{CzhsOiu^GL;of4vnw5gl5x5xXDym$t@u)qmXz=7I+TUst!7=NI`5@G|;x#Q(t3-Uev`A3OB +H*fi`-CJ{52vH6@8xczstC7PTH(c16H*>gj0eK%ML^zPob2L;m|HJ>%%sxG*vqyya4#3O$VAOItZ+YrNyOvX%YUuq +iprFdKB%ntg;5&%Lf5u0?A`mzZtD(NX3-Bh}i^Mj|Q0C_OK^Jx!5g4nOD;!gFyl{V;@{;Z{;8V4pMXk +dkX1c0gPiZIZq*s5C(9TQXCT;6jtX<@{@b!oj*kP)cl)YiELg1qZYo8(1*f>p7v&x$>#qVhxpx6eD9e +YKVb|Z?($UYp|m5OE9{k{8dX>(X;hB$uy5V`T94fjol(x%-*`eMMRC?6G9_0ME$Kn)^-ifWlPj|nw(H8PHDvn)MnOhN?hs +r@7D>AQ^}c)T`LP-O{LZWPF#2i1kd)~YDp8JJUf6d0MIF7UXA|~mmvhtG4coqcGtv!Djx +=izseI +vHp^$%mY|`~I|ER7igy$iA&2U?ot0(dk{AHBs06R?Lo!Ms%$KtaIs0-k$zVQy~B@do?<^4lkBt5Q)Xm0YF7j(_WK7A9vj{joqCi7GWAK@6SbA66E +6a8bOj!~o-wLe@TZ=z;zD~qOf{36-gpID(m23a+!A_Gqk06_o%03QGV0B~t=FJE?LZe(wAFL +iQkY-wUMFJE?La&u{KZZ2?nQ&tGc$ShV!%gjkt0MZ3T`6a2zC8;S2WtoWz&i*d0{^5SELBYIS@$qG;M +a7xc!Jc4cm4Z*nhn +a%^mAVlyveZ*Fd7V{~b6ZZ2?n%{={c+qjj#XD0sxDqqgjuFQO1n^n)#Dz>umtk+I$XEVJtr704UxTXj +#L0Zvmw|{%zdjLoPq#P&HnLF*wh6I6!hxh#+0MARYVo@}&tGeV-#In^|log9-Qsi~TBlZ69!NUjoOBz +?a%2vE-$i-rj6^rIw$mX$Zdr6Vp@Tz&;#AN}4pz9nFP+!gDyevh@V#zM4d5W*S9T5*vS8dlT)qvGEv}6k=yj;;HIAvz9wjFj}_vVc+6hEc +=FT$s;evy*QNNhHG`6p1JNUDi=@n>L=+YOM9Zwo?HJ)4iPbvGdCE3fwPcrC7JBOGAy}SoOo-8oJ^*wLaAjb8f38Q0@5`s0K`j{~w6;h;8_O$yuDHAW*DYW|CC`{^hKQS6l*LIEW +3)fR1s&W?YkmryA3im4+l@EU*$~mc;o8j0kE|%CdspJ{vaBZh0wDhcu+fG(?fh^BA=;GjI#MQF(dgj=@E#zI+J}S-UY)`tFM}w;rAHBULVTkc*uG@Rfo_ +NF*8$qr!I-^#GGg>B;pN4P=f7e1J@$Ud3(^Jw+Cgpt^`%~;^HS<_8|O7ga~^}BqSu-O?u$B)OQ1dwff +sR+aD)|PFoa87W&)NxA{YQ+9%nh=JTzcoI+)|1zh^6;)KY>Lf_>zqq)AML`9DEla{O{!8e?AU|N)46ny51}ByxE8e5_F@;gTbKn8Yu&P8xxrEH4;5k&MkCdTr=<$47}BCmIC# +`Ac5SJG0LQ@$~vjg+#t*u=mrD=sXV}S#8ox}idI{e$6HHkp$Itp|62Wkr(LDQ~Rd0lU72C4uR>z8 +o}L(A|J#&S&>yy1R9XfN9>!pTuAumo12Xthh~X +oFhVucaLf!j?qeCoA-0ob7hlNV@+R5p?G&~x(P9fD(q{TX1cK_IuSxS^QR2wS?E00 +DpB^9uYV18=z$C8lAq*{pcI?uNUztnNAG%OVu`B6qSqIjcP){-3DR!;yyV@9!_B2Esr(ei!}GA}uZ2Dp|5 +0Fs!#Suo9-wP#b20=m@JhoD7SahvBQ2GL}NQHS{_@Vk{FY{Jn2b(V?0iYcn*vDjm{3?go83f&t$G83~X8DFxO>Krlox7TOkChYb`kgXRQcWT +_BWpzY-aS;)a35`Pp8k+OPi7+}Z>BOOVrXd%Z*1p-QOhvh!vq!d!>U`f>m76cOjNe-Bz!ULs$sR67Uh +CB4N?t{mZZ?=!|K@}ZJ$~ +H_AI#VfzL)=qmBsFG8{2jQ+nwGqf#Z{{3G8-zg0^&Men<=$XVF9#Iog)Mi=-I_(++=iG=N59sGoHm8e +OPC*ahQwV_j);D%KvNVBo->^wg<(&?5!xHV>?-HCTm5stf`td=2I=&2FrgauL^jNa +TuFj9o$Lw|XiK#BSs*G#H04U{a^e&>XW&>6<35$bEGmAgTk8yd^$%tY_ypq?H=k>sg>q>y0b5!9cHgz +zTF191pI!3w)|DT`rLZ3a5+tTl!zlnNBMp(9u{xL31(LMOOOq +X$1K)RMSBu48eT;$%@ARJ5XG8J7_IVD$T_WWk)`FL2K}xa$g0zX6gGOl~_ +vZNgiJy5+sX9^1LmJpq)L%I}X5;F*_K?k>BdaH6sk-#lGJhY4d6jR;O-be>L{(9DRCw7(*&(UmTpF&< +p+!_WH0@XW&cnAOthlDIl-PwQm3VATKQQD@k7f9iotk>voCEI@__xm|N*dMX|LHIL-!NDqC2Y@d)s|r +hblD5Kv4w}Q^aI{M{kd=2xz)YmuLp0Ndgp%I8x{LmVii%%7vY4-K+{JV0cidx>Tal$~CDLrZRXc#Z8I +I*Q+8zVH7#fs^yQb1lVItPsz*Xe|RwoW($g-<}VQt_^z)j11l)M}4qe1$jr<*?v-XYitfyJmT0^b*zS +_)1X(Ugu|t|2i9{GACh@68Ik?7;uE>@ZmMFf>Y*bpd($S2EU7*+~vIssNLE%S{D15vvs#aXncBUL|Xo +ok=&v!zoqztnp&JNJOeis;ROF818dMmEs+UbXW{2tRq;eIlAFnNlvi}KHQ(VA8qd3Wa}uV?UAU#^pFi +y(}8Ikw8Lpq4}eaU1{Ct_?nwAk0*L +WMvQg7RC+!R{pK%ao1KKLgDM6s1yWx1-iFail59Yt_n^--6Y3IdxSPR&NngGz-5e;Y4{+}eIir+Tna( +Y22XH$LZP#derau_yWXm^fBqxZ!1Z3UwWCZfLnT6HZ=5zywNuv~#oCCyygXF^b$w^H1uGaAw2wVrZ^! +exNZOs|=Fo?A^5Xid_Tpr_8P}ZoXbyN$YfOhhdK;rOEBo8yDt10t=SH0N?kN@LbCt^|D{Jw;Z2X2 +Zli_n8^Q(IgEN6j-B0tJ9ms!K?tccGD}eZj2hgnnwYh%)F_vsBJN`8N3TaiPc(4m +xd)0Nt%hV?ePwPbqz*l{hbY~CF1aXDa#~y)b77>-6tl@yv}GbyC1Ov*@4_cK0;B@-CZqiVEKA$q$fz` +RdsP30VETkBehXQEr%X31PgOEHL(b(n9fwN5rPv3(oP#EK?r2xleGm7uTT7|~p=Q5$!}s&t?1 +UUmFRr4C>0jQRTuhJ6xt0w$uUrZnqVi}3`iLVf>gqHXMlDMCaEnR<$HG$vLPjSHwh<(kb`>x4u8ReLFO?@Nh@=WW+wO&T+(`vj_ffv^w_8>1VCQs&}v)9t!-*tuEMjz+aJvWb-T?!el +<7yf5PiuI^tRyhsecR$XbNlUXK-gs`EENFQulK3!?%}CF`v`zOP?LBy($7|a9XqdgIxIijgcW`?Vaxw +#%j%|MPn6Op*EY_Y05pb9c?1Syy#de@I@?HJm9>L&!vJTaz%Mr)fq9b}19TZUCp!B8H@rkkDNuKRs9p +J2IsT02V)K8tKhQ!w2N5Z=v^{Oo9$CQJirPiju9prMplIUhMGtphWZZq8oq-poT^)sPOsLoQj=Z=hKI +sB*#8gHYi$!8S_fH##l}I708?ygq)|q4nJ!`awiMhfP;zQrIX@AZ-P{-xfp=?+uk^~rC;Q +2AbScw`0)-?E+zkl^J!rbG!31=&_WT_ch`!-gT=T#2i|TC~5RiNC&s{SF*A7JLsmmPyOV9=PZ<(pCyA +6OaYYoc$a~0Aa#3!Vby=w_lsXXHFceZIZ>WK9AfZM*ZNFwXpm3#LoTq0{V7s+g$Fj=Uzb2z}5|YENG+ +4u8Vhl@tr%;R^;z3sME)eLwX|%Z(3IduF5USBA$I_=am;a(fdF#7C4j7}kNzgS5 +QQid1YQ3$&+@`rP~T@Wr=oRnMb5o%OUmzWdo!lzoqWpI+pg{cb&y)a|s4C2e<`OC+GRK%u4>N_g5OE8 +l|}-gHL=-5^i9Hm~gAkqH|Q(Y<(L1A!W4IZn9f^}&hTPEC~9h1BLhotV%EX%Zrbb^Vtb;L^YlSdKrGhm5Bd%^xg_rMMO|^a%jFnf+|A59-gGmR)l0= +fXM;_QIU*v8DG*%-D^T0Sb5#MOwvvI^=<2W&@f|h@b#si0{Kgy8sHHkAZGSIvY`kRW!&Mowwm3yA)`2 +XrXw#7t4FQ;GHUX3^EK*5&&9&%crBl4LSxv>7^aWaQn=oBM99cutLX}`VNM3eHFL>65f+~e$GtbZ$~r +!2G&hhGOrNJqq)O-8`mxh*CRFuLr;gGVxSw|&LaZiuy|LjPm^4${dIyu-3v%BRl+cb$P};l(Iqd0ZSD +@^E=l||^qqlckaRDw=(S+YES8~z!56QO*t3^UU+VfR6;{WOA1}bP)HA$=`B=+d^KXVFTKV|JcYnBZ8p +J;ZrO9#`4hJSLF*4AT-Q%y%IN|TEOUll8x5>Mf9H7{Y`t=d8^@4+L_~l+&x`$~wv`mYsa=N2v+Wyj)l +_~cG7_{lrt)aA=7*-`^R~gtoo{c(e)g<)xoVvlIq8I(Hs$ZkH#d@6g!P?B*4Xb&a~%I@LOP>v{QAJ0%Q&hGokN%5vD6?bHmmh@v29 +=L+kl2|J|GPcK!s1pH+BKU?ZdIUB7)zy!UsjVwT +jh5-Vl8L%KhU6vAs0=2D8#f03l*YX4eaE!J4r(ClJK*vp5|MXIN~xusV}RmUrqvYFDc$7!T+x^zI&v4{c59+M39~BbtB4T^R#ss+7h|9GwwN74bb-s{J)#wUoyzWBGeI$# +}g+|Ni!9@s$f9%`V*+r(7s2@Cv@8 +Ka?hu+~XCq#Ke=>+<>KP?R240U+T6JfiS{wGSYD-{$oix;$g9R*GV=_eXSS+b))^j#6;?5hVz$N+VeE +W^=h@X&eL@M4#hitAO^xE+|rEc4{$(JJLV`ixA{Wra7+7lXv|G$nQN%t86H@+hlTYQnqEGAs-XO&gPZ +PPujS!`e*sWS0|XQR000O8NLB_@HjA%?Rww`fHkAMXB>(^baA|NaUv_0~WN&gWb#iQMX<{=kV{dM5Wn +*+{Z*FjJZ)`4bdF?%EZyU*#-v^lg&`ZG(>7`nhon$6LG=bOF%n0j4YsoPdlL6Uc7s*~VyO};DF+ujX? +>p+QYFbVPK^6#MSY&r~9j{)!`@LQ^^(L9kmVMVZVm3>P&9-j3B)e(rvhT!g#^9*}c-;$g4kIUtdNqo}E +G>7i+N*aB-rRBUJCLx13jH-HQCOu1kCF=bLS}^Dq7=vRpKmS<}J>I5+CHd$rE0mB{VO7gZ}3fDa$UpL +)@DpLplw%{TQT!}_~G1EG(a}c6u@fIH>JA$u_>p?OjL_H7qc#FR${ +Q}tg4$$Ru+F2w9iOn+$%#5LZ5Vhn&5K83~nJodT8)yC1;67vy?C4O(30cM-tE!dUyk0MKhkw5Wi-#2{ +xL&7R#(JJ7`(o{15Elm@a?))j@)XTwVl(T9g3R#GEy}JLbESQ$8o7(Z~?OgdbQU)h$a4KY$d~WjjsUe +z67s@Dr!h`gESUL=Xp=Cs~!d4X(TvDNY!>NmGDlCr;&rkcc@g5v282RO3mK*8+jxnGHx_s$0=4MAaSf +ZYI2&$;S^Dt?u!DUBJYPHtAcj>|uwN$*%4j)fQ0iPBhegbqGKEEp4FNajtu_Mo29v@>eF-ur=@MO7JB +*DzC~GE?Ost@8x0%oVKWnZZ;dYqFhcBi8C;RGwfqx0E6?!1d +)xrwPNLDYZEBaed5F;U%3>L;gPT9-k3inaN)8Pg>UZfVX}_g6mnL#0LX`wreB+?DfK#@0%(aJ!xqwXH +Oalm?zC-lsrks-EJ%RsZ<@2saes~3_5X#gKZUi2E@zW0}s{?0sEcd$o%atE9IpdyDJ)cf&AEp!U2*l9 +OC<|gbnLz81jfWFROO2xDKXPWMyJld@wtD)Se!X;;%&?Z8}rfgibXcWszHlyQRaL7j1S^!i?5}nxq3A +ZQ)2OYID7YBL)Au3)8pgyD&6jYtyO8=td1A_{TSK7@i$p^lYR62MBFT%9lt4Vj#-w2_6Oz +jUq1)(c2mL?pno$GEAiO;g}$ZdFMoJ>@ot*PU!UH8eDnH0-n@VHpMT{UG5HF~(b)Yt^*C=p3$ND=@S& +qNTMC~D5^;o6$w;m?;S?x*w(-v;3Q|aMa(bvkVBy>>`j(7Ex=ND~EI2Fi>Ct3L5Qb-ps;f<3ik2dL0j +mnY5|8fA{n;SeTeG>uqxYHFAicp2MQH%g8U{fO{q~ujl|_nRdVkBT;Gh=^dtR?Hm{be=C%@{lu5aHI6 +`hM4!9*=bs)tzF((Re3Cvx9&wVM*w6n0K*mqVy72S9DtF0!-Q2?2 +sFF)U{p5Mdr-gtZfcPMoixtpYnuTii!UvZWYpfAi!_hiVfz%3+G-tjoGGugBUfdOVpV>TIP5>Zh`T_u +E$+E1o4uJ%~5Fl(!CA$Oxt|h>$p%`xjqm7jIrlvEL8bB1S0M69{IP+4&6F7KT+`M23NlIpOIm)U)K^w +SnvH@6_-j=XUM=QX9iiuU%pk#w=TFIGmT5X!VTp~bmYS0i6tY366nRV9MD+LP0$TDT*hksdlpY0G~24 +={jlD{RJ?4bn?X>G~7escp8fEC+R6>zxC?vSDacmeE$(E?9kn!JUz;*BafF=B~*xRMPBctuBq2mk;Qb +WfZeR(r8b=5wNv^Ep1;3Oc346{a2(?fKjnK>cA33+S_hK5t-40cJbCfpP`h#2M#6;15j9w?CS`>GVcL2CeEqdFn*;w +iQ0hIrVP#iHoSoj)>aX@#&yCRGhrvMg48Lm*KrzuBSqLaGc+Mok3qpU$WhM4S;=*0dQLo$7>D+m9dKf +Yi6H`!ZK3O$e2PcDsQ2PKiLl%1|~GA?a6yfXohWvkEl*W*(ivECljP7>0H$Yj(UMo|J64e`5COH#7XA +iT&}9PoE;n%x>@PS>-wD-@pwt_u*Zo3BX=3MZkmAu7d$B4&w2`=`i@bhb-l9t~ta$-TCS9Ct%I}%>vSt6%(}(`%o&qg>)bNaIFrmHeSQA@%a3oaXV({R&)R)!9H-@-ClF|E5MJzGUkbo{B!G!A@*CV9VUWBRs!QGi7k2xrDyh_oX&mv< +jofaucS?{4?>iWF^U*sRk)3Ei;mU)WZ{R2nlUu2M8Zu@0NGZn9iwf50+s@?v!Q9xo`TB-+~@U`#9t8% +Azoqxa6zZgig>{GC#j3B1&g+^J6y^y68sw0?1W=`D^qM#v$_r|F#D|NWMpojQp9+iTyTdYnG4l8rT?` +%*K-h4VsMd4F_*`G~m*0-;s7RaVK=W7N{0C$c0;=!Ytzgn&NCoEfzOD8RL> +|-)upLZc}gdRlk|peEKYT7HOebTX-eVpb6Bnt(J?;2@i;J}LWFA`$^ +~vbdQPlePv*g4yDeF~A=s9-RHUd4mAq0krva)7LyW=PMlU*f`%oKz0X3s3$;x}G{r%!E!ah9gOO4}SZagYVQ02*1QdUn2 +L*V+6Z;7DbT>i(%9IbC8@V8X*wDor<95ppaQ7sx7yl_oaVBbhy&GxH$Lay}=CWIiXQ3P!dFFd?-)gI3 +PZQV;PnXve_Mm9$&{g}ToQ6pEKMYa58Zz#ed3Xayo?ko}}Da2j)I>Uulpih>dUMB5hL#*V!XBdbU4bv +}4RB`O;`KVe_vQ)wIHi4`G}V|mZ;YJ(UtDTEi7yzBrsL2GBr{wdN?BN<5X#FI|Qu{n@FW|wY5~FnmoGN`CL!GzQ9~IP0>m!S{Qzr!G9Xyt9kq{{XqT3OV +UJH|`CjAPiq2y`CN5#^T;Sd&1SKeBuylMqWoZ-tx{U~C4sN5cc+WPMl9&?N``olKH55NRR^a9~+cSQN +=!K!Vh#O)s0iW1G}2){$+GmCk7p6A?^O($JQ*WDKMNL=Kc6QPqz121`L2KsM$bZ9kC*rT=0xDLyCKh66%E~6KGJr&YI}Muv_bh}bDp{hp4sBzNEM|z0ge=2VTlmyj76K1TB`Tsia3`7_5F;P +}FwY!D-%UZYsze$Pc8{0%@_E2l(vWHnFpKYDe}n#r6e#PLxFcNx2vp^=5hNwZb3+0rojO3oTl?o&>(Hc>${q5VcV{Ef0u0>Y1dJJ1)HnPjIyfBL+@Ar6knN7S&TTe5H2mDU}TY05Yx5afuQ +a2`NxUeuc{DDgK%S#-Op-=TInVUSUtlzFe#j#%FNs?7!SSQ*f{=z2i +xopkKLGryPm4TnM45q=ck12C~{#Nl?(9Gt+9KXNc4j`E^tJtJ&RGt3i&PBL!P1(egDv=o8jtlI +6*BQ{rpgDbKd*bdw}G{N96SS1HGIDX*d!uXDn>#Z}}6?Z`1MULoM5HJ@QiHLF!@|R&X4m1w +fF!NT?&|jVPpI#6lH(Pw6*K$dAVOCxq-PTKcwb+X8i2yR()-QBi2{)f)x_`%qDCbSzVNV$g#Mr$U3T2 +HIOxS(&0)tP<=l$!nJQl}u7E9U>`1U_c^o?I4(978oc8&?RcSO&4CQZ+;0dx!YjbaMLL)XZdDE7YJ+oa4|@ +_-`Z$*m+62D1Oy;lEQSuVRAKHxzg-ch8v!wM_glY%tV)2f>q4fGC~`A|Gd=lyYW+ +*Vn4B8yBe5T9K;>#hA2?ZQ`lAJ5}68(l5YlyCa3h`Y2wYpxf56OC*aJDv8u}aYz0zb71!F7b+x75hDf +}d-^lkZ0yk282R+T5)obfz8}@64USLjfot!{Y=`=Vmx7awWyI&@#pU@VVG|F5gafyW*6ywps{-~E5Ef +-jifOH-_U38ptT?rzBDGP)4XV_H)R`kjb4acaqF4b`V=Bq%LKG-pn~VlP(~j-+SuQQ{-f8jH7daX%1w +KOQzIcDG(xhTXObU=lbT`@J{nhcCqUs;~b_7^zbbj^Y$LrVcKfR+Z@%N!NGU=3?Uw(Lh{r>vD;b~ZQ# +devx;<5Zu5D{9Cj6_o`Zo3qAY2DwXMSTn+=IZ$Qvy)#PpPU?@{PsA@F}kS-h#nQyk#@lzWk)(b`lxOn +j&4O$iSh_;9|z|^ND-DI1ghjOA-ttco5A +j`I2ab?mfW|(s}E)pve=6Y84F3Gzeq@*+F907iT%sPYwvj2okchyFsC?gx`YYBWoT*0W^nCP)eZ?8B9G(=--I{9L54)bb@wpXJ4p{cU?-&?nMNud&ORL?4Ysz0#Skc>t~PiAkg-%s3_(-If#gEt^NZ6e +E0abns6?Jz;eWo%|Sq=V=%r4a7j+W~&>Rho2$7|Vnwpd{Hg5|X{t?DwLu^%r8rsd1NgFnQ3kSbvi+|2 +JQ{GUv5(lXcfEi8{uDDEWGHG@>MU+#%0?ccx=PuPIuz!{ +;8o|juJ_nEtRG&uwe&FUXx9|ihi7Fh9($~mJ>)&(zB<>=yKu4JRq1e#pJt%&(s*$FN$`XbsCrjldj~c +(W#;6x29y9cD`Kon?)GI$*nealgWn@wF&&R +B=h^;--PZ3>JFlnHFKY&dm=K#Qcb^+Kz=BKMpQH#<`f!_9=5>#`auph_-qFxS}Wcy4d|ob4?*X|_`e6 +67DY6ln+~!F_ZK&(RAM~qFCBe3OltCdA5VU6e9)e_RIn|peoSz4g@#E +d}<7CkF?n6r8Z=&;QPb*^)$JBe}(^lg#SdhNGFk=fe?7zUc<6Jet1Jtq{L;*0+Uc0MbHf%F0Y^0S(g! +_iYix%o$3Ue!<-6AzEsEp$_pTf`gODGL_7C{Npzfbq9GB;O1j1;kG%)k!htrlBT_KE1ZQjRVR9o)-=s +I9ajb8+d7-RyGG^WPtE%nz-$>ZHQz>h1*(3d2mrIoJ=5vG=2 +xC-11Z}09*^KT_>5?E)DU~sP=yS6%b(T_^%G{0|jV0u}{gFR4FVK8Ml_Nl{Yb_w9k!5g{6S|lcQ1yB& +hQd)B91ym_-9!%P>$RB9Uez*pS&yv(K4>(yr`9sqj~D7gm$K{KvI`Bb3$vveHZ)G;mpw11(!^^5WUa6#jeet@g#U7kK#_{O{XInw(>S3%&eB4QMZ2$2@OmEjni2(Qid?jg9N;>(;FyGYDwZRBlkM!f +Bx4i8GWom0L}F{Q#p%exgo##B1_@c$f>nFQav2AX;kYbs;I?_RiK3fv{qwm!CeUvco})6I{G1uiat`p +s3Y<4tTqs#dp#ld$QKhJ(=~%YV7%p6%eI4K~RVk~g2B&vYAD&a)TX+yi&9y}gYvRN)=|dkQ5ofHIoqH +my-dOH>v1+rJk=F%RWN2V9-jACM{KV)3e;=l;2 +OWa5G(T#5^fYOkTXX(l6phX;UwO?Na=6f3XTFo=mxNM8wqT6qca%1EQJ7w0X%$F_Pn+*(D{ydKQMl44 +T-jCTEFH1V$ba7)c`FMP-u#nG2*^!}}7IHcir;9V4}8o~D^ag`u$(pAcI$7D{ksGSubo#i5lt%2_Q~b +4V7ONc^&&k26s->Y+zg(3t3c9J`ffX<_@*L884_D9Q-!8rdTjRSX109suW=)S@{XiST5%s>YrydL=bi +`&s5N0GVT{r5ra{0lFoc`*E8y%>a2qL*aCrsAtrf!Zk#^s?s%ClFY$lH;TY2DQQ3JpUX=hIi +ZYF&o)vd{{*w7augB|I@Ic40;BYql1(lHE9qm9#0xWC6`G<$CY9pcsd|6Sq*l3FHn7mUL48rJmbk==rxU4&ar`kjU`siXH^I~m+=>S%0 +qj;R#P%#YJHdGGo}%mYaaK`>RSPa#N^~^V+|Kp%yrBd5)J0*T*vbLyLacWt}#=??v0F~C~no;9jjxKR +AZ?wQ?De!VxO2sLu7^P&vX(S>(+JkTTuZg*+G0b+CxZhZKSLD`|S(#!<-R_AAWsA?|wWQ+QOTT52t3r9nniJim%Dyl)r>HLX_(9wk02z +|dR1xzj(>RihFvhEHa-C0Y3Z0ui$Z|&SQ2fnWWV7N~k2c!YijfW_z(}@|mEG166-(z* +WNzWlyd7;+zJ8s#Wj8R6kKZD_i>ws`&hE*$(&tB8REzn&y~_8(i?-_-M7fq2h(yj?^^VXjivQ7`_+|k +E1-|q~k|b(6$qBWs-%zhi}okX|R5Wst_8 +bhoDMmO;9F#a={WAE0@aQlTdU+mm|t?_YD>qXyjRl53P=XRYB6xH6y3NKSdEVL^5|aP} +j!n`W_3u8A^WHLr^t@qV>8Fo4PeYhxG=n +)KVxPQ`^zDC0^t?lkj5O;s$S+br@<8TE<1&_{i&s9En$>}@vc6M5~B?l6AYf2WKcuNhDNdCe6ud^#r6 +OIkZGfei0~QpDXzFDj@;eWMI$m3Zi^(q^0sm+slT$hLSn{L^IYh9mn6p7@$Je(?CJN5(<0dKr#kZ&&0 ++L=(<{A1t}3#x!pw8joIa4I-7_O68g1ZVIx)7thGSw80fSnB~+&0q)XtRQM_p!TAqrjHXoY2I~++sGO +6uLe9{>ZeT>I>M(i1*n}72r1YwV}Bz7~R!rt +h-l{#Kx)4(bjq#jLK$z&IFWB%2KW(LiLh`b7A{YmbE+<;bn4Q8GGx1}K6I7{du)r82L`ceg-Xpgx +Xd2H!34|F37M@-kI6%wRYZv0AghG24m^;myF^+ZWqHA#&CRH_N8mRO)6C~OW +Pf>yv?lC3g7o8>c+^JI9&X^o#?Xg(#&+VG_0P>gIle9|1sQcK!8tqQ|@jt=PRJ7K)0`h209v0rx>ma? +LEq-8z4<=e48ukQ?qiL;n$9G#05-f92%Vl7Aki2^^>m%l@{JV<_YLpy$N+adYVtdy^&BJLxjH#q)4I( +QKEgVtj=;5xV;Qgko*1p|j)JbMwL5mH|{OGZDOUypo36VFuJ$3Y!_sb--ie4BH|bLyP|<1a-*B6w%NSI~Ql={S7XVDK`*E6g +>+YBOPx~mG7Vr}cQo+!)OkYh%ag^z=5x7upyIAXvjsJxo2v`~eN(-lc+M|oozL+CX7b6v#k$Ek^! +g58NNvYnD|7M1GTIwFD}x0q`EVh{JS;$Kryybm#e9A%cI3rE`}usn;E6jw8}X-B{>)~0rL~qLFUrGKu0zaY!^Q&p4@&V)6Rz +s(~}hU3wa&s>zD|WffH+bSgOwzW%7)^Zp|XAO7`%qkS(pQV16$#3|H^-asUb^`tN>cu1mZ07`?N{w?n +;4cxX)^}v{T79R29CUY|+KWPMQX!fE1V&G*Q(f4WiX$w(qAaTXd<)C*Wctc0IVw@8)PDC%x;{A9a!%z +4wDuqmN2-R1wg+?nheW~xUHi%)W4StV|q>xRHrhfXpNz}v6K_Ri;q`T`4Bno9M1>9}?Fumw>M?ct~r{XocPE;bUcB452iC4M<1}v{ +2Wm52V##R_vp7T{&S%rv;zWwA~L3?Xbwt7L>`A=1y~i6v@}h0ae1gIQAro2V@(xpbkKFUArUkmafhW? +O+aI~IsBMSSO>5)j2&=hOWWig8SfMYQl%Yu_votY4xXPYw4@4V5-lxZLq<`$5pgzRUI&lhD{*wMfcRN +M$-}a=v93&{LQK5ZY7`qCFN;10jLbciq*{Bz4%_77@)Y^R@PW}-FM|tJP>g!5yp`qe#23aw-JEx(rAn +yMQdR7{b!7`YaE5=Z2uR-p!9EOArPbQWzw4b~{`;nFY5yPcg0QQ9^c%u{^{W?yuL!eiOt`YIdeMq|6% +hD!zuD}})o9STb`ys*0bRIGk?Bp@3=*&XzUz7#5=%)D)D6i^F`p-{#9nXCt*Arv%_bul!k2 +2grW$&tuNdOY>?bRV| +59EFvF@`W*o($BBHXR%!-`CAT>&TMh=nVRvG80rEexzJLCiaymb*sv42o@BAjj?jcoXR=QJ(P)(cxya +rP_(lQp)+%SX;o$bRX?O~dX20FtGEKHv>5j25q2F^=et*} +6Hepiq${7Q(^!5&o_g2~a!a@d)?UbI-iS5(I*>GR*D4_LYvMc(|VZ?lz1*WIQZ>hl0Zr6UC|EE-Cn4G +9%~VrADZsut5TgbZx{#686#KEM8o>X?~)1`6<#iD6ix`u09B+xmpK~iOwxH}mVgGoa2Xw#YHch-J>ivAnb-Et={WvMyhen?g+<_jw? +VE%x5dAylonp`3aK`Wvxz2*0h>4Hv{AjXRddX;P>QBbDdP-0NiFS=S68M6xTn7)Qqn_~G9OyQ;aUtAZ +6mxAa<^)&NA#%q!cmIEh-%`9p@qMcf)Z-+lL;9qi&`*r9wK$f~YaASPe}&*8*3bWRRH$k59i +m1)8tUM)L8l00%cZFFM5T}P-U)Z^6fME2#OecL}hsdVn%SQpdicVbfEZAFSpI=<&mAJ)4Q*VT5x#Zsk +U6sz$=@^$@Wy?2qScu^F`i9FQuq`1FNP7T0c@j-;~^op+XokbT9P5xoPMn%#;Rlae4O}JO!W(=yJUW$ +(8k5pQ-d^22Z2EfMUYU3k-3uO-#C((W1%zTu|l|hK9HJT2(r|f9la`zrsvLN=ufN>7;GPkY39SpZ4(z9`knQ&!cV5>-kQ%C6h@&YkDq{`$plHI5T +qwo^ENl5aht8=}hP<)59*PKgTm=ckj?qZ1k4s7iMd&-y=5O9KQH0000807zB_Q$nV<`Tq$309+ve03i +SX0B~t=FJE?LZe(wAFLiQkY-wUMFJ*XRWpH$9Z*FrgaCyC1TW=e?5q=Ml|3D}(A{UAhobwV0#{qH_r3 +v6Tfn~RkGPO%dtar&R$(1Y=?QieQkldSAvfPInuq`dgxqLI6+pdhx*8 +_fR`mQl(cROj>SrVVqpa1h8W(E;Gx}Hx;?joF!T*ks$UbFND!g`}0`3jc5>MPUAhm +!LA`+d7^Hk=g(>ZrC0gd1 +kT6@WH74)BKGt5%)5_+vfN6_TV8yNlUmeL6KlszFa3=U!Gj +g18wVXf6yv&&@^QACMukcnGDZtAu#TAi?bR3nQ6x{Hf?(>A*wLP^CvJ;2K@|z|k+vMRta1$Z_{XVWwP +;^Hd)v99zd1$e+APgGF1ct#R7{+4g2|KGGXvY;ku$^12{LxaS!eo0SSGj9I<-AbniCs}R>4rnU23GSQ +B0>BKHwxAUV4@1&2ri{Nui%Ac!zqu+_QG6JDr)e)Y|?F7xIf~y0(dL+YwM;PuGS(w+gl*yi6?0t1!K& +sYvC*er(h9sZ;pS3Tr0K#4(BJCfXUVGOkgvmOKg<>3XMUI!Q`ns~bJ#E};-erJdihlUW9}7% +Z@*25cwbs*?w-SDOM@PvDR4^G^?;Um6aDtKB-s{^)u7>0x|u8@uXvISrd%M(i9P?qcH6fJK>MY)ORNA +{Z%m;AB|v!wF%w!t_Ueh%10v8{kxf!zdl=E-begcaA3;kOGlWaP2LcT&j&Q(s@P@jFBKFzPdtXoAvrO+G6`l}ann8h@zC?l;JE6i6#zv9&3cgWxrmq +`lAD065Cq)001lJm_Er|a&o|Pdc)gMRfjM@`41&^*Pqn*+C7eg#(Yq^D8=+IIXr8`HGU)XTs|L9>NcwcLd2ud|!k?f2XvgE${jZUd~+&Td{?A`2pRlk(&YRx)KsN{`~}Qm#W&|6yq;n`-$OHzWBz&>S4 +-z8u~NV+ZRj5Xk)`lq^9+rDW!#||UuXaT8T_2xUC-ash(fECT$jcN8|ljrzJG+M>9EzQk63^B4#(>958Nd21TZ3LlVZ}>7_H!e!-z)9CkGyX>U0l+eDl-gX_zT +wqBl_b4|s%GFiw`D2(dxO0rl8Vv8gGK@;~^d+rgs{$c6a#_MPhiVc!1wV|l%==Q^%xx)p&#g10i6l82Ag9&uHHIu3~XqsIZqrcUDK{84TME622_K6$h3JsQ2fKf5G?4}7V +Ylopym$`P*6C@vj@L#tD)aZ01t|sP}D%G)f9y-UQz2{35Q6CSdSm}b@!uFj8;c=;RtkKz!JxHGorw-pA9L1CfCA$gLEJlyX`}f#};ztXg2Q^)h+2d?FUQWKy=6u+%L2p$_ +fw-S$AAb&;v2aT&$m%yf=XYnlzF!GE?5%LViZDuym7oRBNd&%yG`d`G!gG9?72Yp2FgW#|nyxPn5`g% +#v(M!o-*v*3SomwdDEz?@y^Gn=*bCYOhb0zuFPUDC<9lwJ#^#vlPe*%rglT&O8qjq}7dSCI(o0`7_R( +Z<0-=#YVIYGR9t5ilx-`3nJVfIxL*6SRc2cEShGPYI9LMmYhCw~7OPov5*fNfWX-s7Hq4)DN0mLPcJT +T#Z0Z>Z=1QY-O00;m`Rt8hrcP2(N3IG6=A^-p#0001RX>c!Jc4cm4Z*nhna%^mAVlyvhX=Q9=b1ras- +C65y+sGCEA0Y2AU=WDRN@H8zpn+Ny^*YXOfH)13EZV|0s1Z4m7*iydGmNdK>9hBoxsbzKHdz!~phAF< +!#Ur%UuL{1^F0Z}O(m<6hapM!MP5q6)*??U$wU4A@WI0e<_q7mB&C&1QciCA_rRAcD4Pm`z-L%QeJ(J;%v +zV8m(kd)B(R_Y-;%i@hWNF2F?)ZtP1uw~_$|7_gs-mF?%VP3Nl}iq8K@+B&Xmp~`xr8TeTd=zZ1D`X= +w+9q2$#_hXjJ$nuPEd;4t8Z8-lc-8rnH~UuZ>kho_P_?(N=!+ +b-0FxRsH~t6A&CIkG|lgXCR!;;PMf9pc9UJX8Lep?_xD?I={0B~c!8BkVvxC*XD=Sr&0$i8IBLLDxyf%7cpGz~P|Lc-Pn0<#jd*T3IA8{g$MfJQhpuvG6A3F=@qnvsso0hqNK5JkugE +5haP|Q1JVR7n1ltYkOR%eHUe!gSpMxb6&3n9rS_;;b5hL_t>jc)>1fzKwG}~$9zG^Bl6}TcMz%bQF~h +9JNA4;^aoo52#Zh`0Two2VjGK?=UNo?Ras$X_E<2p75rcYBI`;zpbbPjc!Wc9s?n_cPM@M}VQo+~O1X +64<|j*Vb>Gv!&4MElX}qKM=rSAr^#1bI)9~%Zn^$M&7nc{W-@gNpPkMXhU027)^SQTx$H(yh)Y|SXFH +#mkXS(*tT_ShzaP3i->gl=ENr$Ru~{#DkTco^{Wd!Wa-|69w#=)-KMroJ?+dJ +hK(_<{8s+-}8jJ6sdjIn1hdjyrEBMYu#Gq?y9)U8AenyqYIMZ8x5PooDjNwF`sc1$7Dyp?__3K7@uYi +n?0@%);{(ggiioALp_#d`@)g@P2oWJGeElT;kWE(S0HIu-}F`dupA6B7KSH%U_)N8(D9}|!gdTvyeso +nr315Cdzfd|N2Ye*>x2e|baIYDYCkh{}}ZlOR_YntSG0D-|n^<_cv^^r~vFrr$VK`OKVqgkZWI;QIXq +5v4=06DsbOv*u-rV9Qpvx(g_TB8OEZ8t~1G@$4*{{qajhJ)B0D`PRkq64R?b&@6$9Wjv8Akk>1YtSf# +5d`|rFOVnlMZ(kA&(|No*}ddHBN$6I)36MHG^Hja>pVU^+|G>!#M%uicL5sBvY-Ck&%Rw9}W$kIWkVQSK!|QYyDz18bqz5Y}0MS5VZ?Kry&ZSZl=wv3U +7xoc+h#ZPFcRrGk2R2dU(09&~j8>L*(W+3PwW}0!t-U_fs7q%UZ6^7L^!FqHAsIi}g3mfS+3vPd{`9X +fT3&^BN2yH#)=i?vnM$UsRGqX72L1@evM(p{$@r2|r4qNvhCu%Fgr~?CKh{t{5sp$wF~oto{vsF-_K| +RPhnitvM^QfnuGP2R*XpX*?71fU%774HXvry{ihmixOCqpbu2gKJgak>-Yhm2B{fRAV*>BGR4H6PtIO +HSF7lLB@qR^>KDut$M5dd7e+O?e0h06R97+T-0gVDRT9>p5W{9NZLqN5^Lw@js>Q-p9e{jtNOzo9LodjM03Sx+objQlR&x-w~t)%~Hd +K(AKytD%JpMtIXbWKCEP(7vnD(tQcq;+qqegVgGUCC^N?l)mbMWZ;a2zgEos^&>CKBvu`LH-7FKJ8gZ +buj8bEL-$}GQKNT5IXuAO@4sxOxB6tYMg+Bk-GY3f|hvOi*pj+zH(24y+#q&e}O@bm#i0O_4K6h2@IOYs22;mOZ5c&GIU^ +Ua+1Rmj7-Gp(1S-E?x1=@Ic2E094dLK``Ka$%yJj~&NQo0%|$x13H7FRb;nFc4Sd|9?+AFJc;o~fF(d +ewXwkShE7ttz0dbej0VdmT&NZxLpTP?ugqIc8wfCWYK*9`jhcgJkHyr1e#BAneuYn8Iy}p*s8PI*v-*R;~ +kpe~j^hf!I_ZbaX0`R&;i{aOVhF&lzzOD*8Ownm{^BsR@{6GnR}-{3Y-g1Mv+uhVsQoEm|-9-CGHu)+ +a8K>rM`sUlr!q8yv}cA@ANHxEB^p>mR7)u4x9E?8KE|(!C0C?o#CnQmFG?StG$xl9ct~*heW}=@}|yJ1RRB6*-p3hr+Q2e`dHoLl2aOo?+zUO +Rp7a{#5nmiqNWHtgf0O|<<03QGV0B~t=FJE?LZe(wAFLiQ +kY-wUMFJ@_MWpHnEbS`jtl~zrU+cprr8}NS^C|E!$Y!zvMpm4F5ASvKOnxg5YNCKIXMr(E{Q6;I}^(D +VOGZgi)vb)&`Fd&h`H#2YE3|$#rLzY#Y>kMWY$hy_Wfyk{^ox`mEUa!`xu#?M1*h0#2q0l83cKp=zS} +~~91K69BL%aXLAbuyUB&yO5+kfGI9a{H0FAf&1C~n0Kjy6_4EVoix8@6-CzBkgLf$I=asBBdnFpjXG^DIa3G*TO|stv2jpo1GTKt +X{~I!08JN=!#YJckeD9vLCNYAYnygPG^MWNm#>qgKOie;w9h^L^PgWrgiEBs8Kq2V$C1a40)jy*Ycm> +6`7=|QFGHsDf3a{j?u&9p#GWjLL(q4`ZEuq@3h#aFTBhHEl9no8L43P`y98WEL|LGGpp=BY@vm%T$*M +oQD-S3K|3JfuCyq9D~kR)c9Hcpd%^_X36g398;nk^@zevhl`mq +0ck=LgP%*gTT)JQjXV9;NCPJ}fSxnC#|ADT_Yq35|d92+5@~{%`%#6iRFj7F5J?w}Y4I>mk3VO8fT_L +6oI*Qa4=LXO#83+(RtkPw_ +K2K_)#o_?Un&CH=%tZx`>U!efOox4)vEk;<>P|V?3ga@eu1n#>7}n6@*MH~+&qVOfp9kTir=sxeAR-Q +j{`lcQ)u16ql|vYv{LD_a>;>NM-@oGsYvD#(cVr{#5bKPPw~t(eQBTQD?Bi+kyiz7hXG4}Vcl$6TTP! +4DkgzC;hEnav=`<_vrBaY1++gEnU-mgqNI{HKKnh!J-EyoS&gXi`lQ`UR@ie?g*VGPNmdg|T>?*oEe! +e%bhe%R?4aRvKZMRqUv-#Xaa0T%IpG*Ser@u2}KA41)9yT@Dl;rV9t%}CR!sC0KOC{aud+6L{4j)|RPAx9|T*%M+dvXfTXbi@eDULk5E^v9hJZq2JMv~tR_&@Y12uAYBbZi +HExd9ZwH|r!uY{&38yI6P)iXvO1&QPS5P0oz6$bY|jbh95MXU5*!6JT3ncUM=}yQ+)cHuYYl>9%XTMy +9Dy`$OHdBHQS?>{^-f?}rZ_J}@JBU6wL$RbA=+*|w|lwysO}q^vJ4RCR&twzxx*dh6^a?`1Blwmkl-W +pP$lZKf*O*adlARXqOyknNJ6K74q0){ne{ktIDlk00XfbaPB~zLR@tfy$d)9s(TZ4<88lysq;MpZ_H* +JNdF{>SoCUZ?c>BvTct3vv*B>b37-&zgCx3eO-+W=nKe>-^g}X7urthT2&?o#98#rwNy+If@-8rcWrw}+YScGU}1 +w~aT~yoU>FdxgM?@b4^SM^G%HIGw3YZcLsq@XDmY#MSmmns?CvbS}o@6OLbPu#H(<|9%AX7XkE +mRZC1B$ltrtP0028T@R>$)jsECLT7d?j9#r68#gFagIz)?9RZP!BEIU51?L%0g_U0HR!}LKdQ}VFZ4o +cI=sGOQBkg;P9bdin^gGT~)}YJOa5q2J1w8*eQ@WEjrEMU{JSBmxKBrdd +&d=ED*=Zr7W4_t9v13a_Tm#aw+PqfWZV*>5Rvb%4~a{vyS6hZ` +^~r&u+B(U5>GOCo>R7bCLpB4OnuZ@$4-;mCXSLM->d_ +fGk8XX$o7KQ_y-TaHt@ZnJObD0Dx(+H5SB~4KfS~0YaOVce(|W50eEO11&d$)t>{X@q-5$W9C-@m2ul)ETacItGW(FL{vqyBMfYqL%3hl3$!5LFH&~?@nKtudWzD>^n3b~#$f^Z +uSZWiJ(vhRX@R3Wz_g}<_{Yzf_}ZuVIT8qv&2Cz0q&VZ5|Oa&c-HIh*m9vqomE^sKP}Gl=4u*`>lPCD +8(Wvj#0TuYQr|H60Zh`~HVB@%+EP`_mExtlbe;m|-HT3W4GR9tRkmY55o|fULA?)s;*b6v#{?fnajU9 +Hf8`rJ+BUX1E)g<-~|~H(rWC;VDq8vWf#Ydy+)F+KOY{iEE{IB#OHXB%Sx$$X#}&z&0N)1$!d5Qbc^gfjjV5wM@~>2z-wh_yU-~0wrZQ}`wx2<-Q3=vy;egufb+Za=3=72 +lynhGp}Z&-L6U^7sM%u5C*jV*}@F&GMNbqY~5YkH&$Ux{;tUg=tjeb*w(5p{ROB?Lj2l6E+NgsDawQ) +^GRiVuJomnonc~qGw52UxNn6ODNb07Vr}{-5vo145SJbjkKl59%31ii%PN?29mW+R%ze}& +=VbKEg%m59n?~`#>*hLP?>;<^O8@ng1ihyo`)F~*JDvktQ*im;RpEFZ2#PeoL?1~IquH +9KAFU}Ukx;}%jw=3Owbms!3OUZPB9PFp(%u>c8;hr1>zJ6Z%dJm~-)fx9T{4IJVq(4$j^sH#GNNA2io +%~EM|06-!3q(It#8qP3G=aJ&kA1o6 +b2zZpAhgFn8vXQa9pNPs+0P|;-hCGe8rIPH&|CBudoDUM~TZlB +TwPbuDZ`wd772*>~m23H_e(nf2A(s^bgMR;1u%=%7N93B}ea6Ev_7?K}om+RxQ +Px=FYe0h`Y!BC%`+6Vag{`>E3sRjhp7p1#l+|OVrFmjcF^`$5ol_HwE~bQ*l +W05*~X&6@I>=DmGeih{0TK(AY)_sL17jgJ6~O1d#ld530VBO`|w}w@FEW|ESF!ABa|uc$D~2ADa7|22 +#n!FvMq^@$UFIT5#hrP~PMzCK&wfDl5&SjSq@>3I?zb@|ha%d5a&iQd1<*$j42lFqE&*nYVx%I4$9>7 +Y5kT(g3|mUK5)QethsHrQ&>jF{lraAp7X{t_MGTu>b&nuKn6%Xbr(Z_`6$-KychHc;L)4swR*LOV)`m +mXOx3*U?EJOoX7tma?&I?f=G1aRAc+^b6mvdSx>5J*lr>)z0#kV;f`XVQmodhQ4tJr$th9t=JsJDS|s +)dkSrDBi12V!1x`q3c>7+{SdF{OpvO`s_3&iqgI8i$d_~ZO#M!GPss#hjZ&e(xJCOu@;{$~z&Rp2A~c +f6#j{pZwk=Xi){Pe>9JssK1*L=?uu#HTj}VWLOp*mQ)Cw#nVu|=G)^#90e(~;=tuw*CS&r&!;(JWgBbx~PYb0L56&|aCG`znMnVAca8C`fRZYQ`+t +d_)_fV`NV8Y8)~0e1+C_P@sM{X4H{(&VPsBYPT)WyNmp+k{7t;+myz)7Wyx_Bjydm-|DDWlSt^W9$j> +DJvebuYk0N3RH4Wp&|+kI=aA~keXjB7{-J!+ir1*svd7kb+PlWFu{Lrx|Wlne*Q-K2Yt+isV(MasKTY +ZO%a{%c0R&8d}Jh$Y&Ll4DQIF5=IM$Vx)eseP&RM3@jpr|6@IC4M^*5di6l4vc3`k^23ct=Gf+HER+{ +JtdH}HyJS$vo?-e*Z-CtbbXFY{yctyWnCwAwa7=(al2qS;7m(2y>&=*;46n>%s%+PwR*ACQG)+kbRq( +PJlP+10iXyh9xH3#MdC}D*vFb7^RYxEbT4yhj70E!=meFyW|+nVd~`XF<)RRDe#>F21qPKg|;N4~lDe +KEN{D0v!V12%z7Qc+xve=)If|}JambWUk +UBh}kaewn~P}}IbC(zfi=xX0#3AN>1J}CBkic0rzApgpbuK043)>ARo__nbt6?au^)oIdl+CcwKiHO1 +;D%sicSlw7zALI)tZTxjHBC;od)`Eb~b%L@6| +_rxwVLaD2?Bwah;W-J|n}}B8B%q8~>V(FSa1Ad*R)rp_3pnrXqPL`pfwqidcd#!pVw*u+gBgj +|F|!F=B7{!i`F25jI?ymbR2FF!K(V-)$1Y`Q#EuX0z!Z^+*%$WSUT3PJWMUe5jGZ`FhHKv{e6O%*I5p +eVayu_8Db{Z=Vq`o~ykovj(G^Q%tKS^>{4B9g7OX*o^IKpSFW9VbLU_dbf8$!tL%lKq1EyO$Kh`GAzU +?s{eaM01&RvGfiXRrROV#h0!9jqHXP4RwD7(T(`)Qlw#0U#ZIXDtE5R5P3Qmgn`a%c7}fDe7w +U$DN^Q+SSI0o0{44wNgN9SA?)Ui7Whpq**oS&zL8HbA3RM;xBihF29x1l=~Pt+gIEe7%UEP4SH?802* +!nxMoIgIXh{gZwEGz%IO5EsuTc0mWLJ)pGX}+aHWgOvjcu`{nK1muDY5TTIR2Q*w$WEb@iiXg*Pa92U +vF=ZM1{t?7{|b2~1*@8%xKCwLNHQH0hDc&-W{yWu2HCO!gGM#uSOGd#(|zZhVqr5o}c$u+DNRO`^1PQ +9WQA6}m$&+s2F_ygWJSnrPQuCAV8JV}P8bMG5qArM>dONoW^jN2GLhdHLt*Iv*3$Ev?BALCkFuI#}jU +$$P|B=7$AjQf9pI0Nn_Q+=GHIo(6ABHur(qv8+xG;$(L#L&6`^ +5#}td(?eSF%l@q_ncxYJ_+K+Y~ca8E>=!Wc)Nf4+yxL!xT+Oz=JQFnLm3KPbRf_iuPjui?FRCWN?92x +~$=HPv13Tqo@;FRW?MzvoOeQ`>09}8lGtDs(hSdRE~7rfmkTnivxUVogKE^>{p%Jfw;V|V>|mb?CVpr +?Jyq3z2G`YtROJL!`>iFq(`lVokj-A=O`-lymTD5*4e?^cHAfx3`>xOF1|z@1=h0|eltdVs#SVw$C;s +p?V=YP5u7O5Ol-@pLeIJ}g}g=8XGI3pc!&J7-%1qh*G@+!zXQz-+y^(r`j5rmEI0>rJiw5}o`U<{kPLrhV32@+~%a?4%&ghA+wRrqJ{5^|8sd3A8$MO6sOYAZRfIYX-x0rgbz0SR!tU-h +6|KM9NAPK)=3`B2q*vQ6PwdPxIj>7zYOB=#6EH2J&Rb^kXTbe8n2W<3e>@X?~$Gzx5;vP(P*KqLYMH) +;zYd=@zl9;JwjerQT_o*GriE{00KUg!ctEFy@nwX9xbIXS7NtyU! +~)o-8g$DQarIV!VMfef<$3(jqTIo%-(8HF%bz#s5xL+2W!E4_dZ~RFy4D?5sjI!nD2QVKDKNMN60oMy +-fvE!(`GB$_vX05@q}>2SK6}<8_Y|Y;N8Spb9VrkP9rpQfne)xRA0i=d5FNV#+L}{bT%Cg!aIFykf$) +!pf}`vbx363S6$x +@yUS~&bG4^OzyEpfkClV7i5*$Y@dLu#OC7siC+Y#+xVTZ8o__euNU<_@8F&fX3dMKVhefHh3$@wW}VW +je3&>{WouEqGzMpa4OT#Pn2^WwO@`DwJx^INykckI)Ad*|{N3>d^flYQ9ZXg(k5!VVg%!+drbX(ZZx? +^)1)?H&h>*fE4#2R+jlPnfbnD>63T#IeS29$lP938f;UK=hDA1Ro$!Ih8<|T(lE%;ICh|c>jYGjnX&Qv +9hcZJxVs!-n3b62so~zg;dlg5QXN14OkN@+J(G{M+_N%;Sqig!N8@%_+=YyroNr@knjY3)fXkH0*MVR +Qo(@WAp{U&&PFA#jX7y>^HSkfIZ{`KrXm+lwtRSfUV2!p}IYaRa&eVN0~+==@3EeE^Doh*LFn)2_wg~ +iWk|IxpKjaS@fq`U_PzqtJW*%!tTA%?=|H$M2zV!GHZA&j)DYVNW&M;ptY+Qv$1Dd(&B7u5Mp5ztSyA +KKY+rTB$Q+dg2w^CC;LrRavYa{8$*-y5< +YnKL#$BQ3!b@6aWAK2mnY{22&zs#9Ffl000XZ0015U003}la4%nWWo~3|axZmqY;0*_GcR&wadl;LbS`jt# +aLT!+eQ|C50L+Gz&=QF73QID0*t~)ytRQkHX0ZE5Cqal99dJ79F`nfRs;X{{mxv-5hXP#7Mm3eOPo1# +=3KsW0Ng?d&cK}ND1WJBAHW*Kr>$P#+ftN^#!0`llemJ2E@^C>m$YUB8chG +zD(rGh3urC0a&cetj+^-7m_TIUNAW#;II#JqhYN*JYHq^t}R-PQU@WsqknH{wY_Ko+tPkE(p2l`aZhl +G#-t&}-uBsCLSG0h|85Os*`a?MAIO_7I;5O5FX<|1R#|U7b{+6We=Olsm$YEb`Qpl%cK4bIP +@Tq_$!aJp!w$)>W;9k-aBbI$;`~IC3~Grb!j8;VcUXbfak1wXY2Jx@T`HqQuKtoW!2|*aW!=m=ZR +{Ftq{!Sd`OlSG;R{r`%G*pr1q1s)jo_InwPh|5z8W{zfeE{a9uy+A=O&-4T&WO9wnr=aP4K5k^bo(5UYKfrpENG;w?gP`Y~$nf8nJ$628jQ3AY +`$wNm`URYbo*0ex@@OrVDOw&T-t+sqjraSEywq)(iTZ%?+1caPJxyjrKhVXpF|}Bko}v~CNcy17bx4p +hyR2(nM+#cEyz|LaJ{uo~`>7eyxRT-1Y%(|xAgG%3wxmUuc|+<=BjQPHY>y}Jx{#;Ypg!uI!utk<*8Y +O|<2H72S1$N-gWy0d*v|*U%3y(QUGaj>Gbi$4C`Z;&DICy@Q4`e +o^H7?IA>sTx*iR=E@qR21;F;kYkE*@`@Z%NqH?q;zd)B}!Bodn2d8f~$F>Oe?r{Av#eva!HNh)Wfxit +3=2={LvK?a)@V8n=*DAClrkUZhZadGR<})OF~^lyaR%+j3Qh1+`Ltf)RyUqJ)8C;93u)P4CKt9x9qSS +3;EDt=?Q0ho1O=}wN)kS@g#Oe51CI%kOphXXPm-!Osdv_A#7{&#*s`l+sA(`(S3R9LqiOLtrvM8?Cz_ +FyZ_!t{64Emgpa(D-!2uq;D0LCL6pb#_`e5y6wiG>(KBNb97?+wMyRdZ+}8VH*On_>e9-z#Nt)FuyaH +jN0D(6vIdEKGsN%X6gA{qX7O;n{n0`N*g@Z4Lga`^W^>Q4UasYZ~k%1&2rytKL`8lB<`xbxfX!kh8U< +*}r&%p^VwY=?9WTnLpoZHHo`q7n<+gK+&gO&|ZZJ(dxb}Ak=KaADu0e}xOXu6?H%F#~ +(f;kR(Qdi{qw5-MpA?F-t(`n<+x8ws8CjFtdu2uq93>iEieWmqD@Tcg1QIS2aWl;1iL9QB&Kw8eRFB8Iw3~}gVh6EbTrH(NSM;UxM_G6+>p*@u|j85NMLn7x+eW0aoS+#qE&oof9ama{f;y6(0nfk`2;>iNZaw)Vqr>^eM{1>Fh?4*pVz8J +Vrf?^ZCgxXiM&sZSMXV7vD);Iypuwm}WpgFeP0=^AlVzs16r31FTF*{0>ZATyi +qX!Oq6;mtifGV&G0&i2vhhjAT&xviA}DIQnChjAw1E8x)&4cQJ_L%*f)$`Ic#@MRS`G&riR-J)u=-= +DU{W(A!4^jvtRq8YksDA;v)`c_MEmU#-r`k&_1o4#@iC5W+(r|2Rbyu=m*_F%EseF;?*97<{Aiq3QMu +lSAUKwB`S-ss0a8O9KQH0000807zB_QyEVH*P0yw0AO(d03QGV0B~t=FJE?LZe(wAFLiQkY-wUMFLGs +baBpsNWiD`e?LBLA8#j{QUFH9PqbnscrY4e{OZK$UCblfEwP#x{NjdM8<06L~kep~Z!<~m{?uYyB*AH +L@z+i^*ZZ3Bpj+Myb4A4L~8jXHJi#%J4$z)NLRW2tJ5wAB{UW#y5WJy)Z34ee5=})n1)N4t7b(uzmuhIBJ(`UMOcU%dVGOjy$#EFEwid@n%?pQoMxHtP +9HxK@KKFtc?R=%{78PD%S|aR=_$ch?YMzB6YsKAA_y8Wn9Q>zk@GUn(n7;}eS7==6q)VE#RhPYFk^x(^?)_{j~|UR&YN(42di +IWT*ROCtE!BXAXmWj`?#1aHj{ah6#y2$=?0TBo2+0$xLUx4uy%?UgQYCrz^^hNP9|x%29_RSqsNctNm +vv>y+sn1l8KlPv)Qk(a3jSCkzBx5h|{>7OooL_7WxH#@Ov;>#CcIpfX86S+q{zQ`zS0!cr_ag+&3^3y +ud9IL=qPWa4F#7kmlD64+w;1nX4I$1tMY2BbTsKQ=ys-Yv8J-39K1M!NS>@u;PVCvyvVgPmNLVjfQA+ +aA@K0ThGkEcStiZAsS?RdZxzH@7kanNHPLy_6=cnix*Z;2dIB_Uxi5_yBexxW`W_On=x9`KuI9t0ci- +JFPg+2ZaSoAZ=e5l^5Mnh?Ttf-Hbvb9b9jbPb)YQbG)xj7BoB3(z4zz8#~ZcrZh(2EvHKX(tIO+~+sW +IDw=XWPq5ly-y}EsUajl--z~f^jUp_p4^XBTq>D{!Tmu&N-L0w6I;+L#5?%u0i*9Bxkauo5~uz>PwMb +OD~@qNGe>b(i!zo&FQ|cE67dqY7$NNUK#4rlm`f2HdM`r?bCS`7Orp`Gz&3s2dD>W+H +`0Gj(~vE^b0|4@@!6UZ2T8VRjjzVXta!x^UT99jYu?xWz%bALVf%5%`Kc^ +smyVYhT7W1!!`iCH7VL?TAWKAHGa?&zvH<#z+-99?riFJX1}&TCb +3q7o8=y0wvevDske^7Xc?1ecx(H27-zxd725r+4T#NZJDFHGjA0)HdZZU1-M3*L6^HZM*caOv&Vy4L= +XAO%%T2(ib6^fS6|<_O9Z*}&)9EaWcGD>kfuScEY^R9m!0QyjzCVoQ8lP-el6TN_it7s9K;CU+Uha^H +s3@QX69_m=G(1Kf-zG&S+!-UR_q%eH0V9_%0-y!k7Y>wscTk8^Sc%hVoJ|=Ne5(>Urn$}G_=3sIQS^Y +yB7x=+`gc(9gb^t#EZ_8=SN`oxd<0YDYwHwBm!w2T1*xnUln}g&<*<94ZKN95e2_Hc120X9*Si~ie5 +axR?Q*4e%f*zDq3QIjhmVa_qEybqFH(3F~SX@G110w3ZTToa9tE4Wnu>bQ|76yv9w2<^v?_xfc0E@Cl +=D(^u0givBJz8Md1lRa;YVaw3w!qyuuwHuRlfPB6DBZcp6!4kXOFv1-foDwZf*aK5BR3GB#Le%}D@IO +5CDLHT8R(2Mhl4?^E13E^EX`B%K1?c!`%0hD>@QSIF;*rDZka$_X)2Cc`)8Y|eaxO;y}P}5cRP7`aen +oZ?f8!@-AU6U89VsYBghS8Td+cTfov<_QLA$v&r7SJBWKQNzndzlDH`0jt$phbx +XH4_ebzX&-8buE={{21!hP)L+cWZNS-pwtCm^9$S=5HjZ((1#ufnR#*gb5b+&GjJH^pEp;b}CNL$zFE +$CK~1U{qV+9`&1nSlf_ry4djbxURp5*#r#^`o(#xI0jy;-<CBlKv#c-+LSg|$4d{FaV5|EtBO<3($0K)cqnHna&jA||MsG>*!;0c;R>3P0 +VB1}hUO>u(Oj5+8JvsH#$s5+pJ`>&<)qwgByciKW#qTP6-n@)HZ^hA!%O_fePu+n&J0Q5OjFyrJh +cH*qh;Y5^mk|RL_7z}Yzqf)q0hfLhma17a+lB%1j<`lEMUpCZt|luRn?>ID{tNc(=E!@`j`0xU}{@m? +N1CJ4F`JAS|P$iYA_0tY%4AA#S`%|OAkq^OH1pCB3ipsPi$aul4`DmK(m6tGH^K3>b4io3TKiyR8b9A3 +VP?}m#FZwDx2Xy5ft%Z0ZUZFd|ap15kM20z-w7F3Bpg0P?rDamjgZ2*bi7?A6=$@c;$Hk_n8_{eYf-z_r2%{ +ckn?>w*Q?^wD>GE17GhS%V$B(=F!W}VdntuJh?Ez3OdJOqECKy1)u_A`7g;^wh-Zc7$@kO>zZ+G2DGKP_BnAATb2W8 +)zj|)Fj-$IGM>1~6LMNM6DQ}`0%pwHGfbUCDY6txbVPziH}eLA;mDKmJv%1VzyXiKqy_LvQq(d|d*X1 +3tPn>7(UQY6xxi2iI#Nh)Ohug}7PLDj*)jmBgF=4TeoJd{t>>FJL;%z&*628QP%;fVz}#wmSO;r>^rb +=^{*#}HbCVr1$}El|&~dv!!8W>nbuPX?d3FRq=HglKEI0``%8r1)U{;E{WIAL*#pxPMVToGvKJ5giSJ +U&}7wG1lXr9>m<4@hJ%dPj&!@lY#S<&Kaj=p{$*dax{|+xtW1XfpwkiB0TSP*B_O +H{wGTD?~ozG)CV(QFKHpR1QDF?1s>k%_{f>3CK;%aGht-St<<`xRLmiIQFbGu?KlVt;EWX9g3fUvuZj +(6g4c-!L`)8O38hT+dxrl_*F3)h_8g}YcW3_{AyvXFbp=?#*IQNb?ZNHpo@+Sn;K3#JFX-C-pKnO#8C +%L9jK-Xc8=kIytYLsBv8`wk~)^hmTwG9PB=reHEcA{4U4f@aiXo-)EIG6fItlxOn!d%@9(ZYyzAFYwH +FV0%dmCYdTJ-(Y7j>bZs@@$T(n>-cv}@@W-8|c#XmyjU@@rLm|Vk?qi@mS{p|=%oa3V-@q>0c0?|+4I +}Ks#2^2AXeewL|z?o~k6h|HdOf#LUkmwk3zwIo`!)il2yY}^=hbf*_r6g-jqL2^sU6;o31o)$rr&@^C +9vr2~Fx60Y;^H61K+m)QjprWZ35Kb(lr{n0)B^jYd93}JHT*suXP+CXR^v6!AkX|NPit6hO}jCX?%d4 +Ug=&TFUBK?XPnvkX+tf3Z>B{=(-_KIJxBRck=j9vC5YqmhFwq)zCwbakKXnTH8W~^m&Obs}hN7l2xwy +W*x}KcBd49u@s^R68vR0csMuqCWz?dK8Q-GjdRaYKgL<2s_8z^=eIemXMM+fv%yWuDcvvK95*x-(*wP^ad8+Jn;L?&9B6au5MX+c3&PJ +i11gCE$*8D!#&qLRI=yjsY4^oDQZE3*s_BMZ-nOqtZ#|nJxV+2J>Ym)k(ls%c0K$XfgsZxZS1q{CR~< +mA0#wUpsWO6gZa-4)QL<3$qnx%aj$ecLR=HUn;r4JK!FkoK=fHcAO1dA*iziq2E)dpOZyqwr;}oKe<& +}H5Ey8Oy;O+?xW&4HUg!83&#DuOvHxsftrG+vB0Pnn%ucO)Yh)@sx)BkL30bilo+G$hVuYxkVf$o+6O +2k5*gmHxUrI3R`GJBI)$6q@^4<@oZ@nZW^O~2hF8 +})W;#6Elk|gX_9$?r34_wLmpF%M +U^}7uyLM`=|~%+yuv6DG=hy+fs`3_P{#D{`lPK*okAf4sKr+IZ{*4c0l|XQ|x9ayR?QU2%pns&})%UW)#c?Q`lfXeDn7CIZ>B0sMy1wOQK*T5Q{$BQ%`*;TZH? +cS%;zJTDP3=qwaNK4-&Iy;ke^E*~cnQ9HRAHZ`eG$lPR2GIi5-|{WkU#a0Di<1qd>BxyrLGOnO@z2;q +GeN6hIYn>H?#D%GY)oa~&bTJpdy67*M2(WGGTC?nxL6a3Ba&A`TE+8wEW4=T>hP`B0nK{o-9%uV!^7 +j7%9^!T$h}l<|gCuLp%OPT0*xQBwoi!m?Nqx=G2uT%h4v**g5}tn~5-rVx(7Sr1dlj(5IN|ZOw2xCHh +RK)*Po(OIcExsDPzIrPr{X*VTMwFlVV49VZtcYiKWb#B?CXMInm27*94w<(2c0<%x(gS)e>zgJOri?t +-)#nk3>wajH5Mgq8M1l_WO2dYL`Q^xBpGWKuPL_9s>wld*{3JC_0_0?Ql~oFp5mz(uIVDZ0xM`6?+}^ +0es(#EDC(myYY_)Rzh?dXgQk)grF5?*pR}K!0M&V+PXW4%}rGXjN&cstN+PtN>RKaE#LmOwcU9YsJO5 +$P)AOVPpWn5&!8_)m<7h8D4?B%sJi!BTuKmoJQe;{$Q+X)UnfyWF^CY0V@S`%Aj^Mwop}$g+?)?_<0w+m!PmyqR8>Tj>XB*@rfAXbWXqdW>zhWfPn>BzWgRGib@`Tck*w4 +7{TaW@VImc#TqDib01mJ^$GHt;!ISJOLlZOJdk?%nv3=n0TD8QpvoW|p<`KV?yU7QU#tCFMSrw_>PYL +ZwW(J6r!stdqMb(`=b-*L+5P6H%ZkQN!+4l65iL2m!$+YG?lrEVpHS7<%eB)mNw#P^HkI_2XusbT9`4Q?|b<7b>twZmn$v<}*w_A$blqb(Uj-W*@!Vuufjmbh>*h}Wa(H +55=;%Whnw!^_RwV3;B&;_I))@poQpq|@+tu8Z`tysq5CNE|DhbTo5pIDn?eZmN(Gvp+#$EMr6Y*6J81 +zcoeWw4lmaXh};Lhd|hRwB;61#^Y~fn4q)6z!x!5)WsOIBligs$FB0Kg>1=SEs|_y2mgs8GNB93Vd&kRfP7+^puoUWAjEQ9k!(nUj~qOPHnNg?e+DWyT8_%^smmDaG$6xx*9DH+;Dv= +7@I$b4kF2XJ3M@b~INZ@tsjfG7bWdH{s~5-7Q@Fx6SGiu!TV3p%>mp9)y(;|8RSjngHWdK#Kh8T36FM +!djoZ9xU*&<#^x8b}Tth)xt~&Ag;)Jfv-G~%e3q`Nq%7MC~*3;9t2MtBzBCL|O-qKYbWc3<(u(Ne|1F +I~%i{-DO5%iA1{P_|ro(ScU!mE=|!>R8CX<)L2)>nwcCrO7^?DN3y_vb&vk8e()sljyxkVYsl@aU@}@ +z;SoXe?bUVh?f3}5PKU)e-HGaVObm9#o_7)AhLKhOt)8J +v|-EcN`+M|DGi9AdxE^LI_R%PB1RuP0CR4>7jrfLQ9th1T-ztp;8yvH56SHNFQB<3+FG?+ggkzWn0%X9yDi0KAK?G3r@e1E+ILUs`*=LXaAqkC`2q +h{#W`O10zwwA{ZTjmIBt}dY;}2>>u1VL9SpPIc2G2QEVUec05hku8lPdq5oSfZIqGWKN4-2d`*_+w`2 ++*vFJ3rxE6c$5TSWbdSeQIs=u|Z)pkr-~1P;GKIqMCxaj#0RLopvxpM54T)v1sc5J9#i!`T$yK?ef#R?eA{U=u!udWHefu-iM~FduBCHoKk*h& +jA9>PWm&vLj~-r2-jvOUJoUQv-|WrZ@Sb1gk*Kq%__A|Pfv9nvLAQTP$By)OGLVf3h6-ZfV^hqkX84c +Fv7Qb72omFX^zq0@j_#<-wJut~G6;kNoxl5n8i*&lWJcH4;UZJ2PaVYt%WMX_An?t==lZrPag4jql_b +GQ+6%vel}gmYH{P_i{wEc`waQ$2qhz{Ey%v)qT*yhCMPQK8o7icLO8<7~hz8V?5sApG;Q09Y^_y2hMT +lkEB$Oa9FHyVi&!BT2QT@Sf>LAUP7k;FXw)=*4pB&<-d#FQWdggl80{72P_IIQB!!U(|hawMv?Vntbd +tiMzTmHTTsLfV$-maohp92p_%lU!~oZhUPFzrve@#Wn7e|F99?QQN)_NcRjujMw!q?ppsE9n+dBvg!+ +4_|@FE3Y$&*>_l>;+bU2cy6EGjdAZSSJ(Nt?w``|S8_DQ3uScbI&_}}!%jJI(7Ms+RQEeTIO~{Vly3F +&$dAJVo%4)T*IzU7p+=V!HYIvpYA5Nbeta)=VI;c`*@u`~dMDoz>`^a0*f$RzoF${Lgqrca>Z(T7LCyCMXH&BKh?K{&M`_f$94(&uFM)Gn&w*WP)sIxV4eH)GI!r*>%0hadctk*KajQdswwc +)J@Ivp0>)9d^z0}Tm$3ckPQyLBkF?LYmQ0rGe4SN8F0X_#%50zdkmUMJk_ACv&?2_ZQ_>9dYpCqDtyZ +2Ch@+{P2^?mY$Y^91W~&Xw~Y8Rz}({m#tA=g_83f3Ir?eUR#!tvYlHF#p1!0pQo`c)2RS*03;f!UrXh +Wr6AhmImKrkQPi^l>6y!8@il|A9uN0HEKOX?MUnTOJ5RKFUhfF9NA4D-Jo)*qw1IZm-Mv0Br0|VPw%F +ApbcptNF`aZchmATZ?FWvh%q@pr_7^zNK-w)xgK<&-?7WkNkg;5zrKY1-LiG3ze~0coZ?_4;s4Rd>LT +~$;K8!`9}&Hc8oNZPw&(t%Yju9%!`V(-rKlBIg|`8d&KMV_mZrO3F3kIexp#9xv(>A<>#wssp`NQc)rG;J`+5Ef# +;l?nrq0Ai!*xP8Qqbnq=?W@q$8&qHC-MtiopJ#_!tiCZny!J`hx1G7tCVLMG8+tnU$ZzJ@_a|)DR=&O +^a*~YzU{7v#^-t-ACFIgw!MeL=WbY-4MkA8VKk(yTY75sJ3`bp0H?y-E^v7R08mQ<1QY-O00;m`Rt8g|aKs+I0RRBu0{{Rq0001RX>c!Jc4cm4Z*nhna%^mAVlyveZ* +FvQX<{#5VQ_F|Zf9w3WnX1(c4=~NZZ2?n#gjp6+%OP@?*{*eL6>e|v7whjAcuuGA%|{*vuzKh7)P45M +P!YT#%}lDSK91uz1z@he3LZ#=IOn$5F*buy$&#t8V!Q;G--u#jjav91PU1?1NR6k=b*Bkwg_k^?a2;k +xyUn-FnOj2N1lN~TI89+E$Gm+&f)&j;_5ojAbuWs^fsLtlC7Cn0Qs~ZWvKb~+rTEYRz1`26FBOqF=+^ +(w|mL4HeLZgTB~gb+U|F^+6RUXE&7%qY4(pw$nn;it!#w2)qBrV +~2WYGMIOa6|s2ZJGM!N6Wi))Ouv(4j%X>}Z70(`c0az+e$o3PAQKUtpVzNnR&jK!l#zh3m4EhDKF%M4 +iDx(oml4DHX|329AnaugatPlU0{t%wvdmVvfZj<{x79_*kv)AJ$b5i&w&4`cqYZtLo<>^&0|XQR000O8NLB_@P-{}3hztM#R4f1hC;$KeaA|N +aUv_0~WN&gWb#iQMX<{=kV{dMBa%o~OVQ_F|Zf9w3WiD`ewOZ?M%}4V!BO@B^g~czk=@ +Pi1Ya4pc__^dP{54NOC8m8zU(PS*d}Ao6f=Lsomj3cAX`3?niG_VET$DYCz3 +-E+))^hz-QO$Nw1kAN%|f8MvmBgo1tUAU4+M&fgG3nCpsz-4(y3P=+vz%mFDxHi!a2WXTe~-YcN3MF! +kZe=x1G_stE68|`VH-P|0qn2OY57w>4r!C*{!A&bw)v-9}t^?7{#`Q2a6m)G%+%jMPO#anVpj_RsmN6 +6<*L@w62h=QX6AGtX|jeN-1JqO<7(UVQNHd9W(LUYC1B0!}EfK*DWbUd;!Xn7w2C7PNj42ojSH%+A+K +p-}hkpMuTG_ORFv!W(Wc`)$=>(F?ll64%fn;MK8$2KF=l@wW1vsiyeO7n&b_c0TjjS4{X{f<@ze3y=I +6tVk+nSeD!Q*C#&?|D^;YJT|svz~88KGFLxtgiMx)m0^Gk%-J6yS~2kzb<*sz$kWT=0quJatn5ok>vM +LE_U!q#}vG&q^m_R_N(B9n`Sbtef>p${lviZsr0C12}(LHsLmHN=7^e_XFv%$9k{`l_WN4PZz+sAmc| +Zy<&Tn{DlDx)UrB%?Gh$DI9G`_b+~zSo-LkjkJ)Jo{`ldYC<>;slQulon&Owi +*D%rqTgEuQ(nPZ8Th}DHKQpJYm-N8RUj;HKw*}uIuGca>(VJ9`>lS3$(R5g-&RhS1iHus8Z4NM07h;o +g{K4LoCY(2&Js6RS~cCb_KwKoC&=V0)D1EL&oWX`j+LzF*4vg#Z=j*`lJWO`)m6O$-BF%l+KPNCn~Ce +1<)yNU&YCa@RO`vgbF|R90l{=l@$PkSvS1(D2k(BB21udEgn-Vc718p);%yraIuql&zsa@P +gNIJUxggS+<^gT`QoORdRuu!W+hH$6051M>C!R%HxDpHD6;mvBeVVtVd%Z7F!MePR%)x+D}k9R85cGpMN;}dbw-|5kQ7jMgkGqh +@Ne7|UBAE5PD-Ezf_Z6@B6Qq&-X>I+?G){#R^^#uV#2&e+X(XVq+J9xcsE0ONQ6!5%+Q8A6pLvLdpKQ({nPL38*4{euy^sXef)EXiBv)Owoevupe! +-BkQy{Vy?Wkr(eqk+anW&R=rZ#c!Tzs*_s~(LJZzf^jI(s1@YF*W +T4~WAh-j9Ow1-;?Qo%-;Ee`H;yBGtLsrftWnciEH26IZlz)M4>opv62Z^Sx>ah9@FRURzZthZA6+ZP- +6EwF|BFA60er1U|~P$4I^5G143hE1@L!OncjW~W^%JhNQlb10d%GjVIp{jvrnXfnXTG&j<6fTCDp>U# +AuUny~+Z|c+&XON!pS)XZJo5$(HR(&t-3JjG8ZMpb{c!GSF=! +Qoi_dA*+mx=~mZC%>q@`;^o-TdbDBW~^qw_)wCuP@&n0`(Ey?cu>A7BxYpdy)UWIa9%Ow`unwIt8DS? +*LKX+3HHz16IJUE%qvePJ&)8HMOr6af>vN%MQ<7A$Fy&r+{{(ctL)IYz@7ujfg*YOL&|Ky?YLeVoR<_ +m)~H@)Qb&>z|Q-?Auq_gElY0IhTbhzxnmih6u1pnA6Kr?!LcZuV9yV7m576+5d?g2q2Ni_wvdEvd;_h +;g1mY8re^{^W|O!elaJ@iiSH`=beorh!-DQM;zd$1-1rQXg2Rd~rD<6gEy?F3#+$B%?sxl`%zAQw4#} +2b2LxBhc)G`&zTfOQ&vMvDx0LR)y_HgCjFmB|qHO_QL?3-Eo+|15^Xa(mp}b)L9B(d +k6dWt!Ofxa`X)co_M-I^3I61RBE`ybPd}|aNY`nYTC)7+Bk?5pglfA+Q}@;qb9x$$EgkCzJxOmR}Nd9 +hw{cVvqJ#57Va@|Ik1O4lOI?QhSSsVJBZzWxT^i`0A7PlR1d}Dczp_1AIIqf7@Y+hujt5NI#3D^44Ic>nS29Ds?<2cCnk@eG}?A`vNOM%vUOhZL5;)^tB7tEN^xSI7 +=qFaWC9>FYN)UHd9(ljC6*Kst6*i?)7|@i4~VbYiHFE%js;76eea%MuE=QD)Lpxl)$(ule`xdcU+!EN +CIuta_sGmb@kX!E8S>d$FsI*R4~Jq*Z9{Q%l$}u=)tEOK#1b?j7Dt6h~-$)&7d60>JF_WqPu0id&Wv* +n-ojg^(UCz+Q$vw3ns9ywsT($ui@h=)BX5*;$ds#niD62nXEIwcIb*jLj#JDzlD#3k{;;fOnet{k(sn +I1O=ofj7RXxw%T2F$V0c?nHGvNAu?`2Np}wz{C64P|EN_5FN@B|Gwq<#dhZFEPUQ +9+t$hvSuC65+IFIdi3TSFP>qX?f#|s7je)CABa@u5d6qE+M%)p=9*b}|ET`{Z?2eG0UZ!+w?j#?9<#l +1}QJ+S}2C*wYJsbil9_VpV`2TexZve0+b*t$bgHFP#su^paf=y+DA!bAnRGRxu6#p*G1O7?`?7Y_wJP +O=LG2h;7h9sJk>bPb#CF?#nN&-1M#SIypVo2hSH@Fx#3haR)D%ArlNc3b$@H?yGeu+5`IggQL1=k<7j +=6~!;1JY*j+Tf`fp^MARCR_3-F5r812+;Sttd3HSke0s@-+s*3W)HYeq%sAiW@`=+G?RfNGP)h>@6aWAK2mnY{22ccFF3;lROH;XC6Y_d?&CMl_a78PuL2lBlnWg +9LL=ZH?=ZnS1>rFMd!jgmH1R(~m_9t?vy}`qAFv0nhGtK?&D7wle(TQ%21YH&uu +P{(3S#Xs;LX4qo{Go|L2Qt@?3QfD?4mNmIkTphmElg4_ZGfBb_QHI8{OfVB^?9*^M_B?>92k_EE08NN +2_mbHTUgAPby;R9&2g3kJtLI_fxHHuU-^4iGlO$qDvEJpOpIeHz^u)HQ5fbd!<@2{`+HH!vXJYap$KOT;?QHi#0ZEZPTt4AFr=oVpnmfr1n%vV?yA{QnMN1CTA-%{ +s8vGMKBxPcF^m|AQ6dv(8|rv`NgIsNJdlno59hGR_a&z~+qR#c;7M30ZmkO^jnLCjV0(@}%glZ_wR(6 +TQA9c^?2Ovxpt*<%yGcOTxLTwLAW(k#URR#IimIKGNar)1@FRn8H-3rUG50!i>DzThE81{x--pLrP}Ox|_*efQ5*s=o>p5)}}{y +JriVYhSYTuCFh%vGRnbHN2B{#CxA<+jx`ma?01o0-pbq +qbZq(vO+wU?-q&B<8o04m%#!Uq1mvBb?5hL@r*JhmC+J)%@s|}6&$ +ZHpW476BUOS@0Jr@C;3ypeOHlp*)+LHmPfAvmJoin_EYmTiZ=WA(1rD+-08k^tXoHs50J3?mTf~3QNZ +LvXwO$2*44To3rvk!}llf^uIKfk^{Ih&7m&|BA9huvXXc%P~)YhX-iPrrE^cgYCqlpJ|y^M&Vk!w6T{ +S$3_YN|;sav5`;!Fj4rZYmvkhk61)zgl}Y6I~t9j!d>|RZI~%7?g~0Bug0q9s`)C>7vfW2+*zI!R~n~vT;*G^2&Yj1=4f2*DUktFU`=6@}bgN8pTXCJ+;rC%ZyB9`V(ax1|K;3g6KSL@V{ +w!^lt!?@G4I}=R27hqyjvW01P)h>@6aWAK2mnY{22*AUDo*7^Of|TsL5BE@#3JLV|^n3a-?Dcv_&+aa#Y!eqNHockz$9b7$QWb0=6}# +CME19xaKD&@fA~&p9iCnNa%?nkAMJ&^t#S6BTCELg{iH@En@x5S$WDi2ci!DRcp-@Gz&t=$01YI-(;xg3|D;bq +q1tAJTq>Tewg|V5(|BF3*{T29|9-$0O0HS9m4GPE6TKvkQtpDj@=pw)i>!}Syp8~J^AWjPz7^s=n4%(A7Yo!0^*<6782 +(r+*Sc1{7Ic~<@-re1hn80XqpONjMkmewBnWASg&StYbE<{-7gSiKp%&9Vd;1V7L_=d8?e=h@8CJR4Xyh21;Ft_5ucU#RU+f5N|bP0?Ymm}x~b5b=(lZB`;BqqO$Rw77 +G|m8iX+tolsb5=s=sXCT3Kz?)WFhmcHy{=hw09!KChiv=*tFwIF?TR6TLW0z$H +5ocw^cSKJ`01*vKT7mgt|haE?x{&GQ`MkSOdsfz+a4r}UVJ?*znXD9geM3wgVNlM15a`Z&)-h{7CJPU +gt6i6&UUsVL_dqdMhw81HM1*FnNYkm;nt+0XawnMo#qsB<5V2!V@Si^30qec_mLB9{=`m(_O)y!P`pu +LKPKWCL~)C)L9`wJ}TL&(%8w^wE6n%bVNlyX*7o%K#!)aDF)%U)>@8Ab_X{ZZ~2ddmRD2H()*SxlnNU +28g+O{s|Xq|_Ks&-WM(;LbvHKs!rUQ)z%~b=Wn4k5D}*|Rqz{f +)z!z9Ygh-fEt~z4>&{`lSUB-rPbin@}UcC<9jql!GzZvKy9b$NV5&E>d@Zq$%h91>R{z)oc;7J1ES$V +J=#8@G~9bWhPbdp&R2OEGAgu&Odj<+L+j}5YD%_open;DDfFj1O?(}QFgm@g#EtZI|iMD`ToUOj=hS6 +@Zkt8U_6ImCe-=0yay8-Q~-II%pc>=99)k8kgStLv+A(?otA-%c)mp%;S|@b%5zronUIys%K^=ZesS0 +z)lqN)&HQMUj@7e4;ur+R=3p|mAP(B9`3cbFCAubwxRk$mjD!3N6hkeiz5yYn(Qz4#PW{u_?`buE;Bmq+NdPFBL{K-|n0_dq_Jz%5t30X7ZgXoWKq>e>ohtQ +9I8@+aP^}Lj@&%MCLso>TU?0w`ObnWf_%fOo))cF@Gh*8jNb85k0b91xHKx7W6};{Fk&*3FOJDcw7@C+P#V}U +K*u29Wm8u>*3cO4~38e@+)#UBcrP+HUikRqA|5K@^~Ht|HT* +-D({Pwgeni+rp^Jw~9D-ts+w(q)@ozaQsrHG4`Hd2c?@T5KcDm1`GY;NZ`B3QUBTWd@=#jpj!0V2T}E +Z2^_))a#9z^VhpwX@S~;iA?ED;-No^b%tPk2^i7P-fwi0Mdkq +;}0IgG^;4F=%@o$3Ku0`F8MX0<+*sp{(q0q-rrP$xX_%#~(W*>Ff24E;_6qb~4a;g+7ykVu!7(=2`(DXJWS;;1^czkL5X~UO=j%S>h8F&>6#5uquBnVepEzeTVsM`F$&RIk$l@`;j^Yr3=2QdDTGDq~ +kkBLYvG#h^EhBp?X!Hn#@9?!!#hLY$z=7?0Qx{1atKLBOABLopjCH%>otFw07)^iw#_ +Ct4?o~hj?GZYDwHgjdQ^ZYnciOcRXpzvdnjV0#|_(h8+>ZFF*4h+>IxG9besS##l&j@!1*@!O`L<=aU +roE=Di-U6IW%9ntbi*Uau?`$3OPWt}QZkV8@gphX@_;q@W_g%Y=Zu0fNv@aQ`zoG*8`LNZlZV{(A6{DLwo_3I_}l4jTt)NXSDaa}tw_O$o?eIiw<0=g7=hYH=? +yu|zp%n)^2M4mWNi*K0M02M({4lQ=0C9B&&W0#vYoJc@KOJ-cF6Ug@*5{Jt4u0XC5|;DP|@#l`d7Vq5 +SW6$SAx1rV8`Y^sq%ziZT^>lh-fPKEtFnJpz&es_k^_o*k>nD7vIl`>z+;zE`*1+2AjCBbaL#H!r})^ +Si0Vx1LR$Vc+R-*MWHpBj5Pxf^Z!rqn|+QSCpdA-bB`gBAh@=FcJ(iYLhC-8K^qM3f6qG&GOA)dOQtiknkxPHWRSE{sPHJmnFa^VCSwh{a +ghP!xLp5h96nZI+z}xw)?Pkz%y3mz?HsS+$s52C8$`HH~mhN>2N& +rwn|xXhIn(P&nZoCGY<{H%k-&rQ+Xb`I=<5>DJnVSST!No8aopKR67M78n$Af{c}At=M^G@z_;J0JW4 +1@XK${iI-FO8n52=}q><|Y0zTv~5K~eEg>Tgj0IumY|2NgmI6p +`t;_b>D)7Ox-Fa)VO>9ok&wAe?x5IvSzm+^Pi=SO(RnGFHJAG;Ql%azR}jwcW^p+29j836`vgHbeN4im%_;nGv)?_f);f0(r*xXkeG=z +S3^y8z&J^WnaSyC-Um2Ipg7ROHcyYRoCE~Ux??+ci77B`Xn}HX4At!*C___0HhoO!t3n4a{|s3*+zKE +^1tO6}Ldjd&sH-HjY&o) +{5@=Nxk=&%<_oL=)?RI=Vp8Pz16HMQ}zk752;p)GMuY){%fD`9d@MsSQ7|f}^zTpsBF!Cw@_8c!7G+Y +WeG`AGIey3hY&=#b3N4JuAgm+4mzSV{ABJ7;>`cGrg@#{Ud*(jtgr+7sXFEJIzP%A|F3OtVS5|f9bx< +_AIALq}tpXRmDw4<7Dk7OdwGP|IW*@BIft4BmkgL@kY+v)E&>|*lQcVjd*oP@hq+JwD9nO7}Kc!) +)>?jFSLs=zhUD)72g=66@~Rd3YSI2tC>?MqcU?!xYiL3>^FV8XD!Y^db|E`@DaY*HBeoX +06wTXe_uh$<*oU}nPHb>LOqT-)z$gk$6GxV}3D`RvTG%>sv#?lsMcxF414tU*ER{l(;ReD}-EI5?YLL +3YvTIXhVY(Q3Y#0YY!cJkRUlk2YkAO^%*M;oIp2z<7QC{Hi~|f4Y&JB?T=VcFa789%tP@T|Jq0{K49_ +tq>&(8OrJ!pWdZ;dgwkw8)=u}eAhA@-LCj*hiaCjUund2|IxW_{P?}+ntqGXSp6DmckJ*j^7uC~IzD} +xI9gh8Na;d5&{0TtRW$v?@RM#;S9hdG-PfZVy(`~=c7bf8t9`F+x7VdVOc-A?UbVUOV%vzSCqlEC!84 +mNjmg(bA<54mh+|CG2x+b2X`a|$l3*LJ5Ng3gVf*|$S1x9=n$&6wjgT78NrFFBbMHq#sSRcwWJlNb1x +~Pb?!wxapV|fz7N1*JVy$!=Nc-(|(wua=tt|SbR^1|Rb+rDZM{vcqX3(HI7$xj}6&mxMGEJenkqsDiS +a-dmCf*rzHDrQ#EtsdV9N<7-g$>Ye_HnWI((T83PwELfsQT56<4?}x9hTd>?`7Nar5AX6@AQ78^`8dJ +QiH~d{@X4M0`ogi3kR9dYFCfV%{!OuAP(bf1-5HjzxrD%X2&`G)+-H#X8;EH%LpH>mm2ErjMG4216Y5 +mu2?jt{kt@4@BoLGcB=-(iR6)Gh{m~&35Fcw*a#;G_yzf`zFJ6*={~ri>X@#nmo&QD{my)s4Kglwx?`jev9O3WRJ8(zNk7bQ@VyqLwcx_3!JM#((De$y31xF6v +2XU#pBeP0zA<2xEEW;D)3iU8>yllQ>ZzIgvU)1HCj5kJ&U!eb&K%bmd-bL+jhLvIfs5DV+sUi(z57-FUKD?^s_Px+T_l65aTGuXZ8B`i;9L~ +@W@o$G*uz}gtA5?M29$Or5hknmDskwsUAw??%&)cEPBec~%DIPhs{a~Kc8#@F5j58yh@Yv}bOCArT5* +N0T3RR?JJ>}=IdB&5RG(ki{z8nd>Yq@iAL_6gG3@KIqB)X2zoNg@1Czj;A$$9&#LiPYUQcLsHKY2R<4 +?p-3=CmDT?pU?`Sw=Hy(m?tiyHF}frT%{ejI8VxsQ^vUO3!Sifw2(}DfWK?P)h>@6 +aWAK2mnY{22-T}$l3WN0010{001rk003}la4%nWWo~3|axZmqY;0*_GcRLrZgg^KVlQ)LV|8+6baG*C +b8v5RbS`jt<$Y~)+c?tT_f+LSV3pd6oRPV4JTuRfZ@jyTEGMdQET5%pZfYoCHPmAjR}*B7%n?ii24!MUj>uHo44lktWbD98bhHDwgS{5XDlyd;#x7x) +3l%x>`rE%sfFuz<)s^q7~v3B!H3(3P4rNWEfx{=qzq_04NbRL3khB0jyCnmtV2xDt(BOJ46ARrz;8I0 +l9}0@7-~l9WSH1rOX5%^&qnz3^ONabGZmMaUsTQnM@{EFJBbP07$Zo);Xc9um|u-T8J=M(=-A1d5VLn +0T6w7n#4P?4R(k^Ci66YkO+_G1<&q~UJQ?k19J`HLS~AT&|_0XFgIw6xUI7k23u)lgW~0jvI#>(npCT +qBiU%ZzDRn-D=1*zCJCWy31+p)3z}Ib|JNqUq_ZSJ@j8F`LU&z9YfXqCj?=B2^Gw~ILIU9cK*>3(Q2YH^y}9Rnp+4iTWIqJoF8q*$hFNt4HAx*6Vx@4h{Sg^FV#A7oN&0I{7Q(hEiTd? +T5)b0B0Pw#z77zI?HU<%2(fMz9E(EN}+YAxx7Zi)O%0^Gz&0@qUAd(|Y7f+$M-+4|^t(rb3JIz`O~Z3 +o|4xM1Tv13ZnvG#kcU^ryw-81H8JAV1d)c%NOq8lhpG(T0R&r$Rv}L*PVKD!=A`tWSBVb9O9QK`MV5Z +;Nt8II|=wvZ`B6%)L>pt3{+?a;J#Xu +AQ_b67J;EBsLQ+qv=L7pZ(z4~)n=S@6E7KPd8(H!Wnc{WTR=&xA{qG7hvWWrq%N1;q$U6AXC; +EFH!9JeFX?R~P$sk?tGLTG#=!Qq)-P@Dzj^Y2luO=^kMCShMb_TzDi7fuCd)@#W0&^=_fII?&-og45a +^4e*Op=remqB(1`~lbp$xf_+;7IRT5kv`6CSYJ8puwv^!eLQtgN$bqayWlIuY_0#}6ys0WS0cte{;zmB6-#KS0X0E4KcgvycA*mJixkBs&J-AH9^nM@k`++81vB6;nj13(NKHUp$$ylE)uO9c`4S`^awbzz;?_~qG^Up!CO +N9q2v!9*Fb?fb+bdD|I24od<@8Jcb|^*@adSKV+vs9=Av*mD{N3q^FQe(l@#m?47PtND>Ca+(Df-txi +=Rf<7dkr~RbiN)>#ntH3XbO<0V;WV#8x1E2@zd~j@Dcv( +zaL$Vra!CoyBtlg5&Gr$R`kV9|8_bWe7@@6ikr{3H{;0=5V-)@*Q4vpTNrBiX?Q*LU}$(IhJS-U#N=c +D>I%nWfc?)fnOj6x48}J<-;O?foQjX*tBWB#d_M#n`|q!YJRZz(aMd4u>WPc~r~Zc_^%?^xHC1fO$ci +r?hxi;v?!$kB>1ce7vl@)Ar?>ED4<>y(E&G2NO@=+uza32gX*JW^F${!shHhhm1f8#k3<}r4UMgsUe} +A3~9jq>f{VRYvK?r6KSn&>8go_Ng+V>Y5w6=V|)FU&}#?t4%kv-HKR15NJ0kW@pM8-z>r_8b>E&ob$Z +yglN^6z{JNd)MGRb-p6dQ~hzUZb!mpKN7tpUDLxN7Dc+#q+@ym;={q7}3}AH3(v@2MGi9 +5dZLC+;Z-~^V9KQeC2~G^9R7b*Hhitca`BRBBh_}zJoNAm#D`AmT+XEMa8cAZ2;S^XoUwt!jHk8i8a7 +3FQX6ASZ0Nvq&|>0&D4hQXPYRV`$e4lo8&%8w}~GHegKwOp<9I5Ey%K9=i`|E8q7-l3J3~I94=5~CU@ +LQzl_sh?uT&%VxM7vLWvUnwvdP(sAK +BV=K(dd$O;2{vba3#*0NYJ>I{SPZ0EVMl=-9LG3i)KzRBfU&j&&BI +KfC(OgDxi93%C<((${f}(B>(~WpMI<(c|b|!ta&orQOMVwYj^CrHhP%0YL+6J1o!&R#p +d5A(1rgTe+~2AbqOwV-EtCM2ryB-A3l<`M$zGzLciEK;sSUa57QZRwC(yE-J!}65O6S4+IwbJWXB~Xb +VI%E&@YZ=YQIQ3;?~X5Y?p5*^Oj_6sb&>|6K|#gIpO8XfF^}@N)vxE3|X9PQflvG(&*sZKXDNAS+_v{kkYF9BLNdigYK-lggMOhgM@m}O8cr@IL7{xul88Tctt$nGT29kAaf2#y?cnW69 +rkUZLIM(ZAi^uv`SGfyf5(Y--37}FUonN%jER%390i+(Ro+!fPJf1;tDNeq1XpzaPs~A`)c +>8AO<*0WI<~_e2tbM_yOs0h@lPufL;uOl)yu +u$JVM04==MHsCquCqasc;;rEs{n!(`9NPO-I6S}<0fJY)-lEvDAd3R_><2tp$STRW$&Bn+e6D>IJo#Y +#Q&f4eKg4Y{&3>I`|f-H!{Cz-b|y@}cTnGxcR%R90Opatx6dB+_Ss_^_=0f`|Gj(b-;A$*J~@5+-GNv +i-2b_A{-@1Yk2!chc-HC~eGg{~)$w42jlPHS)05}f*7Hy{eDWOIdLG6*PZ;i5vwQB`o;9-rHo^hC|D1 +jHoIO^~5}N*SNaI8Ce**cRvt$SJ#-8<6Zo>N@SEh&7Srp%uXQZ%8&lFVd!@E;d)nI`psMpEn3k2FL7* +2l0=Mx;r6Lb5+yxCCI}WoF!H4)&So+H|^yhc^nuiQ_igFW>2yV}rx~d%^xEMLZT@F6Kc|WL>aP +z`?WW-#I!{9%sXpe}NvJ-Tqe~gL3EV>~RlGGz#x}hqO9+4AIjA5j}k}qNh(n^zPib;5cgO3X0B>C%F_ZJ>*zw +2c#DPQt);m`6$#Ys<$qmMdb%e+UvZtY@Z& +N_3PTK<8*C`RPCkeBONM|)uLqW)ZN##k$jf{Xu>BETD8=a_R(ecb +tIu{IQG%JC+0yB__NJof%ymY2ROiY#nPv0^jbqV{Ndfn!luJf)f;0mHUO9n4rPphKa92`Rb-X{i;NOVpNOjtvmlo +hJ~n($2Mep_idfKu7X$hFQ5(Mn=4q6QyktKM~uc~Y$2q9TusypnDbg!9${Z_$|Oyu`q?(c{ +M&;!Y1mUyZ3EES?1FM}LM<8#<|u7-S>n-v$wgjx;0Q?i$qXX-G#cP-v1tl$Tsabm-E-kT>jq=#Q>JM< +Eaj?R1Vz8dOwTsiW%i`dS?uB&m1ht%eFrWio|$dXb4Y70@G(gkmtCXEM*b&GRt!t@vi|h)4BD;c}DQ` ++4*a35Xcf^D-GekT~sXkx*gPM22$dzd1L6HZD2^NXsVs*SG?_Zx!1&;2=(OIsX6(MlLpSGl&@sa&K%0 +KGNx$TycaEZWmR(Z=|OV6YbY918$)r;KbnQ>hRM)oC}lH8m8C0(TUO8*SH+@yo8TuvbV#@aEjZ@eR(s +w8Mcu2QL^@DWj>D84>y%bjVDdSlDh^!{h_|Go-TjC;Bn1+3|ZSkl{&oTCu+Ti^be*kI7{8A2K0iug3y ++?zAS+P!D|TY*BGVNrf~YdPHhwT=j&}S-8ica6nIV%b3U+0WPt}({~zn_*{+Z|dN54kcFk?>6KJN?fj +;jpy4DigP>=rUdY{(ti5j2U{^+3eFW2-m%D-CGyWN$-V&a&J5+2QIg&I0h^GYE~Th8epb@l|lF`&2N1 +ELu8JtbP{b5=fU8ogc-LGlfslV%cA`|1E!8*oD(@GuY04^t39t0*s14T;Y%-^FWII*L_)sn9+%xb**i +{m6uNoBvyBMweFrd15A@%HG}f|FAB!G9B)vT;m47bJHzmgrNJM12B-0G6^vyA&wp-9jMdUJ%~W@Veop ++l8X;%Ri3g;YC18bG>@Rb45Fe;SqKCM8#w!0WlaMR%w?q{fR$XOnEH!(>0+xAMLf^jEaCrV!Q2x+%V6 +1~zOQr65=k?7{2t!Dd|`X%njLf~2S|gVh+AlL2}pG9)_6{Dn+YxbzhENa+N5& +v}eOQ)ou`yu&_fq)mwAJG=bMiZI8V6=TVcE=kcA)xw2i=lD1tQJYXuxLe+i92d;}a#ypqkZbJ#^N}H? +7l>~;MTn@~Ls9^m=W@%+&&Qav+{D&HQa0c_Wm~ +Uo_ylU~bGztG&Wx;AjT?Gkjlg!uzq>_9vpV=frpynPZcRTHPomVRq9G)YVsLUj%>|qKzY;+=`-MqEhFRyQSJvWhNytKSkVfGX{Pe+)*wV+tyET5wX1?fAz6GZg0L{wg4i>_ +^~94+OasC!t8JjO?8}rvL3 +CNlJ(}n93yEB$&(??@Vx1oW#pz5?X8nroQS*)ge{pAf_Zy4kVMt1Q?n0Bs3gb#-3uk6=5uToi5Iko+I +Q+ViLW(j)Be&LD|_-4X~el&fLoPZ;6Qnt7Ai^NLh5II)Yx`mGygE4TTG&IRT}kA%6BGnZcrfPy^2!9f +a~o8zc)IY1`7&*c6+*NLM6B=c&wby$C_9xk>J*xJIFck^^g1@`Aj0RZJITIi;~{cv_F^gf{X{Rpw$@* +N7$(M$_;?g86qVuG?s&ouU&)Kur^6K#wPwFel!?%#*H_?RIq3ePcf`ZOI&oZJRu>M8gErb>{=MqD>sa +BBZC0>NN~pbu)f-bKbj*!%>Z$f_R;j=bE`PI?BJF#9;lao9n8J)@cg$HYzI@y6o2IFjUXVc9>{SQJ1F +t`)-B!z9>Q4df-HEEK*bpU?Jda*>+;<7KVjJSAju+S|EN2&mZ6&tdfuB#w*#7#?W>fWx-UZHMBouLqOvG0k$@xTKtKLzKeZ*8Gg}r_V))LmikBekT4n?}$H&t_i$n1_>XD2=EGDq!lNiT%rM5N7t}m@Qen +Ry(8Paas6@BU>x|8m@9^L>BvM0Rt_$~0ig7Ehdm8G0z(W%S$iE4%OI6Z);(&qvO$8xF-V>8$i$Y+i`w +Q`t>hdlQ*BEt23s2)sdI7C!`ONBfQBg+t;o!7$IsAh?5X$UYrF-N(j=T4_HVi_kmtGBgKCGmoha(`aE +Q0<9s~XcU3DODf|*t0!B4}fc81}Rk9B#&+a8VKf~uKSD#n^qngvOXZU=WeRzTr2c_V|l4MsHxr9bAl` +pg)%@OAbD%icDbOV%*iq7=M@oxzb9su95oF?2&WYg?|Uia#&P(L?XH!Nwm-<4M8%v0C*+C!2LB@VRK% +fM!!zn}tW`(-xaoMQ=6toHvrGH%rm8uz}PC%`5Q%ol`Nm>=SJE(LlX|x5~CB4vcjqL^LM`Y$4TDm*)y +8ckuCjPZcns#>?htNfutYM0n2x``*Hs6C*j9Vi4Q~$Oedx_3#x4YLoS_APXZCn@kM)Ip>#REl7dI({y +a*LW}MatTA%(N|8fQBILa)ei7vN$S9F=a8Y*XdUOLhjGCDBWwQ>+hK-%Lr>dAEIGyYK76{1Dd4Spb`c-&!%T^+>LaGGO?j&^^3FKCMu +YDWYn0X#a~GIa(<2=irdFAdX=^oIE-UasRW!P8BGl{DTcv)&QFTGCgKF&9-?3Fgz_Yr|6`xLSXDFL#X +({WYxQdYJ{eIjrqF>J-)1Fr7Y;S9O3a^zkC3l17!p$~GYBHsA#Wx}0!k*eiC>o`oaVz+yaMR>tA^EG2 +U#p5q5xDi=oiIvA0P=*EXxROBo$OY;fr6HwXakq_$u6J8)nG0DN^oFk1Of0ZE5m8vlSmjcWUzJ_-0td +`9*s!H{Ayb5IRT9!VY0pfB_qIu)nNdX{NC>R<&hc=&iG{$3_{lSaoBcz_2LZV+hE1UpAi1EVrma8CDqS-O0~-dL3AL&aRSMFJ^#%=E9@&CRwYn@GB*YD{scM>l7 +K5;yz5MA&8m`W9y_oh4f|gr(+Nf;cVw% +i`QM{LstxBwN`MR#CZN&T;3&iRi*SaZW#N#*z>`gkTaS9(xf5ybVu?T?@rWwS!!>exUS6f1xnH;Gz2| +A^_Aoiw4%J@+Ps;x0du~N8@^A=3h;y8ydjmIv{0FY2o;_g1iGjFRlcv=ia|EPk2%J;+&Vv^QQik07Lh +Fw+5av5LI6^qB<^%c7ANnaG@eFqD&$wIz((g=>uWr(IILyXe;33YkEZYLo9xt3?qUG~BUjOuui-*#0uqZeg8Qi+*5JgHZx)HSq0y&xX;d +_Pgax@bb%`)$JVxn$(L;I$uQiN~_R|5B(-nAm7o=$=;-(08HDd@7D%~8)3`PFeO#c&kiDEUeemZIO}a +bx-qnQsAU08{{;3+-dNm2_pbx~v$Z`z{J}S{Z@u$M<^b1MxS2!y6YKZE;!`+>$+YS+F7~9f}(sCG105RcM(%dIuHeE}u@tXsgN#D%j4?EqeBTwa4s3ED +>O5ng5uPw=L07Dt9)uQBZi6R>q69P-xY&Mk&$KH+SzA)aQ*zlbpJ(wfXJj-pQIQrw +>{kVCC9{y*X1x8eTGX0_n^f7@y3*?xqwARo=Nr**&ET&NCz$Q+LLZvZa#LV3OKmefox`o;3ldTpFzsi=aor;Q%cK6skK&}!|0t^<>Om6?N#TCTS)wy%x8L6K4Qm +p$K*7OSJQ`~-Y0*}Z(TJN4oiw0i9xnf+nq5eAy}Neb23#JQ&YqrpakiJOz;1S%V{EB);zlvc6UtZcr@ +^~^in?z1c`coWrcbCd!@&87@RSM5PgmzutYhz&&)MA)MP8C(!KNX|UYV^vawHGs-nKjxJ8Nlt;!*yna +PvcS2F)^!=ek@%<=ydh(mGGZ1SUjtV%#gd2A79XDF`2|$XUPO&)4wn!T8%-GlK`1Vqrl$1^HgR9$fdh +eg=RWKyf;QbhS$9WW-c(g1;hT;mvUYG%h(5jh365HeHVlh9QNXK@(f3d~2aH>0ethQ!-aql4H&R9zcr +~<^_cmp89&`AQ4nlawbAI=H@0**h)~<@=gSve=w1BJi3_h*BAP6fpqYkb~8_WBFAiorWZ;7!nr4#+|! ++vQq{lC9WTvds`9eoEX{KdV-{#Ys=vr6c9Qw!RxCjdplN-T4*0Gn6X4$!M`m&@sWM8_loPt#vPw`PZ} +b`NJbi;-kP$2E93WlGETFSmFgG?Um2+xTDwJQ>**cy&-eYYsJ)Qxnr|NK;IUuQ$7&!L5!wclEVM9cBcab|go*^kBmtLCCrlkmgRl6A8aull}t5q?)iIF4AFQ7#i2Wv({194F?iueJ3&2~blFzYL7(A7YW8e +o(`+cp^QVQM!;)(XVfz56c_F;IR=j-Vdz&|JO-9RE2PhdQ>SCu|yTI*Z@+YIN>_BZ~Cly_Lt_B(s42* +YYkc=xbBXk^laZGABwv9S^N0Gw7#5RAGQU7h0Y+svJAfx4qgzeR>g7FiyPFTFmkwOx`sgdvw%&zn$L$ ++jT5$*eAaW_CC-D12%7*Z2`=;;rBvg7|~!jFE1XfM|1ovf +oz+(3VvbEfN?AiK-qpEvhg{H<1lqbmHWevLQaRzd#iJ1P7zQxhDOo0o8Oc?e0SNL>p9mK&ff)i*R>ot +%7kN{%Wzaz};%P=T?cfQr7g?&5x3LW5 +rd9EZxlT^S{F!PcI^p#71mQCYu!oap*>j@*|#;D?O1~l#jnQ&F-p>=4{lbYE^)XS2!Pb}2D?(#;o8C4lNvN)G#3Vx(LY^>>W{&82DJJiS7n*OVR$ybwo;R38PyZ>iQ2rz`noopm*E +1&cEu)l}=jE7);^;qBDF9sc*vqub#{WlAYwNVSh;v+MEoupH5VUz@f&cEi*inTxv`M=#CSI&OqZd1JX +A3Ny*oB`OM5uOwr}LUW9FDf^$++f@s->OU9DRk2T1 +%q7DvSj}a~&8a4B>eM2dn8*&e*I+7YjjS*RsOIroK#S$Ng(95pHEl8 +#m*wraVO+BI0W%qM+&iRLwS)J8~J*ydtQ)I>u(oOa7i?ugrwk-=n9a0!3QuP8}zaTV0a#hx7I;f)0)< +>kEOjf3NNVv9I>&9Mx#(GO(U_fZ9zt31NW5D*wO`P(!xl_$Q)>{-R=BMY&ZU68;R>Aqc_Wm-7Fl>09v +)GU@pHoWM>p?>I>fbQ=l}PS$-1Kz@yKH}jsG?70`SyiS0dHf4mH9E?AWHmVv4Z}JG-^i@v5uX69zLcs +&<(-UBU8f%8b;Z>Big=#W|du~l?xH5?kj!@{K(U9?-)kXa#r3#Y12nSQxMt +C-eW}cjvSK`nBk;P8!SK+8YzWxf14&n4DHsjul)Tw1I5f?V#(U7=QRDs>aLH-LB*=eit4|bi;CW?N?qSmsBP!Tsxr7dnoB->H{Q)tzdX#qlo)l)P4N&D +1WX^lq2Oo1-JP8KGF{}gHg>fg?+t!kH#D~DYC}HA=X4mVZzhXE>N5jMWL3snBy;1Y2ooSmT&lCC+?sq +Dt$KXmjX9B#x)xgBJsa>{t2&R;{)mu55Uk~!;f?t2+tV^&fKnD#ATaM&Qb_W}Iwg&3?^|eGYu7~NrYy ++U^x5E)Y?-?h57)NlK`s{ri27HQh5qVx1(1ro@zhaNw-lOb0qfW)>UD3Gx39Xz$Byh*$f4U&5}^&A*K +`R%18eo%x0S8j_^RbLf~rCDy#&Vl#v2N%_sw?|)ZW{-7BszXzQ3Sq*?5xy{#d6kocwB$_T`KJ0#Hi>1 +QY-O00;m`Rt8hVZy9}Z2mk<)8~^|$0001RX>c!Jc4cm4Z*nhna%^mAVlyveZ*FvQX<{#PZ)0n7E^v9R +8EcQ*MDTm0{D+mRisY2|C|nV;8U*S?RETmSR|-OvZ(~m`RvoYLt}l%c|DBnA*y~3^Tj~!Ndw1S5JCD5 +wYEvdYNwybhodd!Yif4snWhL%2nTdkuOx9&7D#f;7=gmJllL7GM}Kul?L%@VSj&MGhQ%wub?0%Ol?Gj2RrxPm+35qMgZf_+4-rkY~6%VHqvI1D;vVEDu9?%fPs?~rnw +>1*2cyR|LvelSvfvdU#8M2p(@mz}Oq>Cl;TpZB&mV<+ji1D&D$?Yu}_824G)fZaCD;`~5hGBSnJ8vOJ +0f%spjlvb|g#z~M)p`u?@$MvIE)sP9DZCEaYYA1s_ZSiAJXUfk528w?ju_P#R?{Gr!;lpyQ-u06MmEE +EsUQ)1w0a6DQ-am9ag!DieTB5_ina=_$Yck^l%fv_9Yswbji?BrD10Q^8pYsBD}#0yd97WGY*SHy31g +De*h&*ajqYu10Z+->NuFU)EM=BrDD9x2!%_goBl;EjaHO@zv%|;`I;5<(noxLoeSf75fry +{iO@S=%GEa>kyivcCg^UauGRuF9%djM+dF|-j;wr9}nc?O(L4jD%L1`jgo&bai@vy@x_iH~d?UBO>9N +EKOMW8#732E*xH6tVKCcA}siG%{<$Dri0X#8Ch4d%e5d38{kfx;;Ln8`%gIvH{sDJDrSa%f^&-)Gi$r +&9}y_LS+f3aA{nqX?=p)I>etiPTxgO9It^5bjH^48pqq3vPc|L#-jYpH#)K*Hs1uUs-rn%4o0(^PT+k +4rD8Aj;`6E!RTu2Qs{&0doxqx77#cLRpLBrFprkWfB*!-f#bG!KA-&AZ8a>`CJ%NcBS7I=hT~ehW%J8 +si!`gh#U95@HlmQm>$fZsmEKd=WB_!Dvy_DxzE^K`W8GC`bWG^bBiz#Za$u7|AFrc?ns+8y)SKKO01} +IP}5*cRV5{k=5;aA^;?=PuxR@o-pseS%HTX|`#q(^eTXx|5xvwL&%Q}Ij!8B?2{2!e7(2XYw2=UOjo| +BV8wY!9MVE$EF|Xy}HaJ5Q7j4bqP{H`h;_)rJ0Ew+kbfr4acz_BHMdD9~aJv; +aKd>^#j6=_JJoB48lRMNMF?{ooL3)ivc! +G*<@#1#})bH;GN6O!40(ou59TxQ*L?kJ5Ivwp5t%J@(CI2?im7ATF7x^+8IT(Iox2`j}fqo3l~e>y}2JUHlcNDK`(}M-5JZlBjKad%y2n +)>g6%P9!jkP_RzHo*~|uHqt`<@4~8@z4r9|I^uu#ZL30AG$>h&nt%RPU56qmJ(G<(?GMo<)Xa_mgs|} +XSOi@3um!!c4bg~gee#E}!NK3XncL`zy;ecq$GtsIb?1ErU0C%DhDjMo-O8cq3tn&x5#9U4$+8ro@&W1qra$=raZiZ)k +z#&|h(k)JGlh4CD9oS1mIl;&{Bl<=awqCMWjGEc$Wvdufb2*AM`-o`HR@gkhVb=^4#km$`%^dVAo7Y}0-B3rEhxx%bBxVx8MVP)r%dy05S?i_C{#Xl$GzP^J#!&KwncwU +34!oh3vj~1$hprG1gim@5SiJ-4iOt~FD4Q7aRTNK0Yy#v*|clf(2S#O$*x_0RGf&SnaIn(O)aTHEZhz +%Tl_2HT^Flx_DpOhQ)qUJ#LLG(BmX=iv_-u=Tc3H{l>b9w7rJ~sV(w=jPF7~I2jnS@Bsh +Z=1QY-O00;m`Rt8f500002000000000u0001RX>c!Jc4cm4Z*nhna%^mAVlyve +Z*FvQX<{#5b7f<7a%FUKVQzD9Z*p`mUtei%X>?y-E^v7R08mQ<1QY-O00;m`Rt8hI&O6T-5dZ-AMgRa +f0001RX>c!Jc4cm4Z*nhna%^mAVlyveZ*FvQX<{#5b7f<7a%FUKVQzD9Z*p`mVrgzMn8E^v9xTzh +ZZI1>NAK)(aei$!gm>)1(?rp4uOj%+u^I<{du%@&J=ASd+73tYEIBXSmj_Kn`phIg~^J4Tu%Kc<@ii$i{ZJ#3WC926darwf2PV$PRu9ZRQ5VglY?Z2~9T)$j)bnw +{MozihR2AO*inEA9l!UPssx_9kQoBB{moA`Rvi&QW8R)!=Bzd^pQHGKOaM7v50^Y6WD1GP&c$q$9dt* +hGZ?l)W*-ZZ-DaU9dLU-4Z|1m;In|kcw+u{@??km&f7QA5fIWbltw*h#XvZQ +@WxM3wb{ule-0i!7u)y+PJT$_wH@@k=bV%PN)MfsGMFH8;O^DLj69@$Wd;0)#e6n%O|Jv>Y2uJ_;rtR +?JDCm$8^U(k^BtA6*=C%b9hY;*p|rrQ3!tG;J~aCX!3x9i$RPHX2@uZaq?j1y$78fO1FtX-m?SJ3Q>t+;I>`lyP#ae#g> +5^x$b{cE5s6%zhe&cL9`U2XlwIsl!OBIUULJ%#BCEn;j$2*3pCg?UfSrgD%e90#V{0Jq}WfSnTmNnBH +`nsZW=NK^(dxdx7?Sal%onq-Wp^c(qQJ +RP*j@YnfbxLlIiLdYB6&L`s`RE(#_CXCWHQM|I3hb%|Mhi6=?-na(^?#a@5?1zs71jo1# +_5Y`R*&XB&)OtP=kF@p9NE{l$0*prxgY87M+O1KW&C0_N#Zh{86|n+izq`))bRpc)MO6Og(@2`UG)cm +qc!98k!5y^BH^4%Ta8A23EB90TS+Ct8bd3=di3Ic5lX^B`H}Kqf1jGPeyT6WL_O=x%DPzRid7dp;XaS +Hp$;vep~Mob=-*q{#;+u(g+b+HQDuntVCTeMmk6`@eJ@CZCZ`8fmnT%-(XTEzN*V6vB8Qt=Pekyq04N +TF&V_+v}_!sk>!@4>WYOH46A=|Bqb{ATO7?(Yc!cx}gkDvUgbHV>xP1mCpdp|%rvjvY +1|okpwG;#f7de~@Oc+a{MS4J33k4p5Zg9|09`{z`h?&Lz)TE`6dtpG_pl+nBh>&q3$sppi2FOq!}v0N +>V7{MP%uC8)XKZ{KX`4yH5ac5Q7Pr|Fsp$k|{!3wqsk?_%wl;XVco0X_jroM>P~;y^6ucaR}57jK~bF +hhTL*^^6I#rqSvwjanc^7$-_T6@>F_)MwSgV|e1p7;b$$ROK@#sh%lQUz^j +2s0pxKpb;J9g1VF;OwMy*;e`Im1vgUMN5H&e>ncDtTR#?>IAfyMGbiF(w3$l+YKo*%vwC4L53OO(1q> +qQ(nt0dZ)zoyHo_s6JgxsEz?BlZFJn4`o7DqR(=#?pwAZd!dQ(0A?;{Qt>fTmDs6#igSNIEF*0M+G$7 +!(fo(EnE|oFGC&M<(TCbGQk}(w5!O+(@S@JG?G7;iWMm~tb32tx_lXZ4+SyI&C$;Mzl9pQ9VOri=j=@ +bo_vZ|Eo9E$3^C~4_6q+4pDdXaho*J$i+=`%0EOo=5+m_oS^Ll-8%7@b#1QpL)F31NGcq|%f=Q`q-?^ +CeSu8-?b^p+!{#F)74W(18X(*#$-8fxs&4h)WlcF|Oi)rzy%>93jlfEHh-jph%^?eKT7wVHygn +iK}?%5%SRZtUN4hrEbVpi_%u&l59OMY-PQZrBdr!y1#OGlA2Vh0Xga#vRzQ>^*iwp| +$%N=CIFyr-@uEoiQ&mr|)(}8u@0Tx|qd?~eKi29_VmHP1(m#5)Nl?LRMS>YLfjvVtL$O+02feYp)_94 +)(8wbQt2{RJgE!Vo7%zYLzi#fWY<;1W(Sf(u_JEXZP2FteRu*w!lhCu}Zm*G<0L~$T1MoR7=a>62K^6 +Hs~%mqG5O49yvEa`YkY3!<8xV5T@; +;2)}$^xobQe{|W#41r$AS}%oG28EzKEuVeXo)y~#p^niLFuGlXejcg=I#O|g*>UcIQi@R%WU3rFj +ioln(b!fEp#49c^dM>}{d&z;y2{{5LmZ3UEno+18==msp3tb- +8L!vus2;z8bCrPykLa`hbMbgu&A}w{MVpM5Dky8Y{U+V5E>F4PFc&kPkA1WBJtcA1J*OG&jgm*u65`ZS1&)xI{9WSRO=xMxsh)5>;8b7Y(=j>1tT*GHB_*paBv +La_u(D(hA8FklDBAyb&G{aJCLNUVqC#&oyA-uzyHpg>#7RZk1{tk;=X(8UXIaJls$@c#T)U=V`qfiN5 +vyw1~tqn>n_WKN^Q1{Z&F8&s?XEBjj=6UznH23844nGI>+3T6b_`nG6wmDo`>5*EX$-EW63ZjJn9W@H +W+ihFtn!z-hs*n&SA8b6BZFZSo_!s3S=OCMOskFN!5oer15T;kiT;2(e#@2Y&22QfeF;#g3%56czT%# +pA#!=a3C7S(cOx@%SBMO|LKnL4M1j@EZAm{}-OjUc;Bh&5NvpDG3ZQABtkhNyyKSY?x_qat5{Yxrn7Q +EqyvqR6*ZyhoUCvaHZo0QsWL3k?>zVHu_}wM8P-O#yy@Q4>Hvmjkh +ipBe@71}hdFZyNpJ6)K_)ypw!2Qx@>LOS(kK_wNH)nzwDQv(*#bYLQ5BGC6TkdKGPe>!`#}FD>N0l=j +M0MP)zQphhuj$Yy_zR`z@l`g0u~q{GAr^LdIVFd2yb_k>L@-YI;y44iBXP8w3o8`Uy!V7jQRz;``LRM +`eoI(s<+eYDaD-0F;3;#T4}e`Zxt!0Jx4 +2xf~@#9P6>&A7w1Ss=Iqp>K|RZ=;VN)7SDtMo?MRFqO{WC{Uo%jMdrg0Ss}(hjeuwIauT@~GLGjGGjz +#ji*J};?GqJ}IR1-2c)T%A%dmeSSMUj#blbfR-msfM3=X{ulbK4HNh0YP)`iU&&<3NkH8&cIr(|4h)+ +Uf1P5+?(?GhyZ|0#ON`{47!zf^*5d7=7BBSBu@M^xk1Tm3a|$lpej$SYLUusMmogy)XGAFmz$rxqL2m +WNOoMX?c{zq-ozSq*d>@YV*Y-DL&@NjWu$gps8;Zw8|ewi2J~{YvYS1n?LCjZ#WwAj{5TsO12C#ExuZik5342l3wSG&`0Xxi-r(iO}@j2A+hP)A$C}x2?6)I`HEZfIo-j!BA-5eQ +oUd}HuzoMnpNBY%}&kuTC?IeYKbk1`S5m0{}WNyqEc^4q3nQ+-A4%it&mYr4+*BHv>H{VbjtK0(N2L_z57Z2! +Wng!)AY;mfdKsy8dc_IA>mH|$N@}iJ(h5-;iEch7vnEJlHt@i#C^hveu9^V3A!&vxT)}E=g{9? +fNix2&47sdSNd5S5Uve4eW2#VfCo}$0Os1*V(ba=nw@q&R{Y-tQ2*+)$Ze{C#lKB+ZhS8Rw$-I{eMdH +C$vK9xdyAF|Z|{2QN+#Cm(0eRnQ@O0{5&pOQ$DQCRvW4=)%>fggG*8AhzXodN02VNa4m +i!W+@r9QO%NIXjdii?kz-Ak2V%Hm_d5Jm=^HM~DPKfO3pX>eB$+JcWng5XSzR3H$+Q7GwD+5h8P!H_3 +yJohB`RP4ojHa?8o;}d6` +zgnsgdPc9OXId9?{f0y^Og)K&MXC04pyPrlC9 +0isE$FN3g8P`UpwLTnetvt_VeNB?3+pnueGW0$i@t7n}?Z3FL$05nja*bIKav3FH`q|Nt%kzA!uPJs{ +d88FH^v;fiOWs`{>v6>HQWVsP-a!dYsijC&yNe1q-qu^F$0kVrUO= +p5_n<7rPxabrDEz_|kvZy7cxZcJ6@Ao`NijwU#nb|KJpp8V4hv)qwGa8K!58mnZvr^ruQY=+jD`Uk%8 +__J4n5w)r>e|T4*4i{;R#fw%y0Piu!QsL6QduRWQR3sKS?WqG+A41fU0GNw8~Re|M$8MN@}}Gfqe|JR +xo9*2Ulun@x9{o43wwAlYuzH1D{H0M;LID<;OrGXueG^N1?;;KMU|KB9JUwhr7o42!QM-u7ozmU93Gr +$qu%PanoGn=tN$w89k5FL6ilrf~`s +rnO&6`cFY)GL5+GtS1pK3S2bmrY)rr>RWA46d)291?ov7Zj79XkLT|NK#&4?3c{2$0--x%P=_?UMm+2F&^#ht1E`EJTY}^!jnra}AlmVX+4vts&GnjRI_V%yW;$M4NRf6uL#GNbQ*&Wv^G!LB@uG^X2F(`%RkaH^eCp3Qc;xZxV3MgW8haH5m34u=j@{dE?{PNrk-9OeF}n#j<~l6Qq%m@nl(u=O7uhL-fh&36HgZ;~Q-mD}h4y-gOnAwaEU +ZGGf#EcrPT!py17?m%kt|Bcp|+&AKypAXA9SU72y%$yo<;D4Ex{40EH{UIB(q1$4S$XX?@py=8>1FON +8+r8k?^)MjsT3i$6`SVAd_<3-@_os#Cyt-T^(|L*BVNOYb9%(wOIo3hd$QU +or~7t**%7A9w^;voGL9_kSp3~7t9j&qF(oOmcBnLjYRCls*%sv1OWknML-m3$gfPFQ0nAcM^=;?`h>A?w3~ +Pq0j-0u)4bwRBLmw|MK3tz)CBfbEnsEr)j?}%L{%6%V7yl9Lq7&NyiOKz*yXUw;XWXtpBRaB3cV_Kkq +2l<&&fJNTzb4O*Uc8JoNLg@B4zXRSv!2TN{UGm( +9YqioqnQ%=J6MV+4A>f$@YEZziA)IsAJl|dr883Vg#Mp6_W+E}@)D+;;FjCAwN&8 +fDfW3wMFYbuxmg;$rr;MKAssLk>g9qz5R)1mTh?F$Si!SSPQ=l1tQ5GL*K+U%G%E{?@Reo?4`(0+lE- +^&AlxXYERIg1i@`ufU$+g1i5>1`u!~!CpDP?p4qEs!nc8UrEg +m7UotZ-=&y44(X2=XxBEC7Qk=A2ODOaZBHQ4PK86W|JLRbm;4YTLCEo?6HhK^iJ_vv7V#67Z{gNkLmn +nX6FlSM&P0Lee3{-D-PXzT@tC3wjUi)tosD#XU6uy?W5i{ +K{52>6b=LgWvAOJs!XLCS8dDyaTP%v4f~2ig4lionkO|d?027T^?trQnk +TciDCbN$=X#NJ_u{jiln13^5+6fQKlKi2cSK3A3$-wcy%d|@Oo7(%Ko?VI?F`7n^wMB#O$VTK1v%_mR +t>_NYj&K##1MXU4jN#Og~ruuVBaJvWn*A=sas$l;*{<+@jkiCreO!E`x1qd!jkoHX*LWVSBm4pwP_V0 +FK#HDbp0z#C}K}Gbo=TXI5w>9_o_UkkqGSKP14Vowg~Jd60R7tJ@h_3#T<)>X7fjAf}F`gc_2j>Zu)< +i9Ble)gI%{6R?jK*jJL#-ijZR3#@i2imK5!SX~oT#@(#^Kb(_>ss#u-hDt83LEJVH_FktrI-At60?+T%P3> +rcccQA#iDUMM7Jpl0r%!bsquN{*6!I=HHqqlfZ&a;vFX6es|0_iRhp>sAocH)#tR}Cz-c)GBa8GWk2z +cg(Haw|8Jcr%U%zyK(14RPLYDFeG$L>|^kN!W$Oiw+wlyt=iB9WKLH(0ElTQ{)42rQ!nWG9~8|4{WWT +g4}MjJ539H({5cyCMy8Ksjw^W_M);Q_3&6z{gic4mMFPb5I(9o6lfgNK{MbLWJ!sr%iK_9Kpi!?Xb;Y +6fYMtF3%o`?tBr0*#g>t3F-1fOouL54wes+!y`}u(JxMva*4-@&^B#(D0zA0Y(0>65;D%`KdF +i`&UpEk<5yqDyQ{@uP~QXb>iFvzk$&&=pR#ZPHS{xAM5B;8@tlP7!*q2!$LA!(2hW|1l#58ir}Q@ve9 +{9m2_%kFhg65YXCnquy$-?8X-!WIMPpUDNc-ClJZfZc-j`Y~#CFfyItA&G#m8lyyW>e<^hCS`#Zp|5P +WlpfH +v5WM9$JO?=o<@Qw^`XR04#!5SWqL>cl{U++7WWn^^SnqkTV`gTD#4+fZb7=bNN+P__nu(iSWx6Yv;`f +N665N$)A82redWuLjf3pxt+>ccc89fc(uo9pT+wknL;17Qsio?3sD%<6y7x+7ZTZdxo#d1Lp$1uFo9G +jfF|=9oyj%Q0x|_0oV-4|}#?AcJi>qosHm`Ajf6NFb1#Xn=#NGWJ@rfX4*qibm{Cb=dM|yu4SSJrSp& +ALKfrcM5`vyDUSo9u0)3ZIoH5c@Z)p24EE4GXX<%S5heO(gmZCUZ`)4kuG3+pW~cm49O*5)u2kF6oG~ +3%eY&mYO&N%Jb`}T8TWDLQ5aTWt}M}hH;{~S7wk`4_L75*?CgdUTd*096Z*{Ww=D|DYQCZDA&%duB!) +X4)YNpOcF$f^5uY{}2(h>2gm)Ph?+h+J&%mMKMWOCq!d7`+>?sxFpB*JB=DfR3{YYmcZ=Fgi^mk2FWp +Phg_Xqax!orT-w-5OMi{9nJKB5qUK!L`7V6F$m2{2%N`6~p=@CipMG-Jc37w +>VlX&P!2c~naEW1g**;$mE0BW(olBu|yj`7&8>5h$nrX>aE%7PwtdB6e!oud8$aRG{HqR9iq`&>f~P= +bXKe#!CP6C9H}VEmMm<64(R4o=$P1i_(^w<@Hoq>`jX*spg*2VG5w?}$AS=MrzHR;~g?mFZ<5-+3z*@T`aJ6Hk~w=k6B*&t;% +6k+qGMNdXcpu%6o^==4y2yG}OKza$hgpyCrjvZ_+=_L?+8QPXnvj7o7fpqzOY=6mmSpee^DQjEq5rBK +qt-;i9G46^9F2$Qc_CqFxiN-E{a%~pa?x~IL6nS_%c=?&}MStTw>`q73)j@>Fu?Sa$o(%Em`LAc%JR{ +B7>$<49Fk`xbIQ}wgE9nMZr$H8`O~~VjF*G(dL&!z0fE+xD_*qSrpFvnIS&ob +xZ1FAgT0#RM8g*;ZzClKUqQ#lJyo1;(VpnSsaJ)-JdspuL=*qhuK+|h(HRAiAaB!9VbX3Z;BQW8iTYQ +1Dbai3R8A+;@nmJXREBc7juj!6v3@@z3;z)zlgy>5a{(U=$bfsNy-lTR`20OXBO@ZsiSN^b2oe1>w~% +!e|9#7{QT%fWyiK1=W3mlV)lMH`a0+c#V8K&7`*Q9677+_OF{=b^rvq%*9<3(90yEsgK29_!UVISHLe +k8o$<+%6Dih8p(*Wu?mb-!%2(?;IT{aC&LQY)Fh2W}DBIwc$W>!qW{2K2LW4##-x&NazIPd4kqmzZl5 +$?YH>E!w%4v8UN?9C5EUt1eA4O*3PH5lM+kh%YO@@$WDwnP`@5>x91J2yCqxTwxVVTY*(UDu{!&l$al +dV(RNQ@KV3aq%crobCb+E;!d7a?x`5hm_cYJlr%sOxvr?Bmtt_2t>+yX+t5SJR8j4^vRTf4fovqTSt# +(XmL5CgO#cH;w*?qd!i>Q5XeSchBAE^Zsb+$6pM_U%2rfas2ycYEodsRJIA91Y(mloc^ZDk86UrShY^zG;cf0` +?sn}TmPbrjH)ACGv-%DS6$ZQi6c7*sO)>+9E}$pGwR53F&3l9j4*e`*46;TRT4&(NlsZ?yOG|B1iu*# +7k|^k+zewUaFlP5j1V@fUpTRqnk0@ZkReP)h>@6aWAK2mnY{22-0nr^r+Q0040S001Ze003}la4%nWW +o~3|axZmqY;0*_GcRyqV{2h&WpgiIUukY>bYEXCaCu8B%Fk7Zk54NtDJ@Ekk5|adEyyn_QAkWG&d(_= +NsWi_dAWGG(m*QpUVP|D?FLP;lE^v9pJ!^B@IFj +GDYX1XHsi~2&3NNuUb9brn);Nwc(XC%6JCm8qrf7s7c!S=mc)Pl(=+%7ujw+(vM`B7n2Ci<$ym&2EL_s98+zaC{z7d}ApKawn-;-2QQr1J}$7w# +ce#X_b{NyeJYgsl*3`gRs4N%~E6|%RwJPFA}RHrE;4!b~GO!j>YBq`>Rif(_=BY6c^L;pC|8*--*HDC +A<&D;?v~n!}-T60T9!}v#Vdl`FnAA_N(~Y1POm0MA5RXa;^O1<;{5U +$n0N=2&n9Q@r%=%G>G9c>0|i5xIQ|)4#N~&>lM^fr$sK-#YD|$)addw1>vZzdhb!^n{N&v+B)&Zc{tn +-s9CK+~_UJ3Kx7>6oz2fe=*^A(>6_>BBLmVyTDl@91iBeuniqIzPLb!s{5SJH0BgKTR%=$K +r50xkSmlpPrvmO;NglbWU^t_3W4lq2%ig1R(hS@$$H&Dc&6)o&dQ^L{@dx@X*MA{?lME_?U+gs2^y@9 +Mt1Jg=bRAK!Q$qV3`Ff2zZ(`m|`sUNCo!+5%K3K7=Y`}i@Zps>xyuRIt9v`B@tMg%kK!Oa^^+s=SdP} +Wl9JRte%p9ZhLu>mI>Lq%FNd*nIS>f^$Or9g;Ed1pyd$74>OhYj~0E3SZ@Q3~^zHvz!fIfux@rbiRaP~Jct@0p2=lKyh?_w&@{|*2Ve +F-AojhCTEPKp4J^bZz^54gb_oercg@4FD|4Bxf?BH?cWs=i+Srt(d-}LayD&QPp))hQj_%GrR71&k>s ++uZ&POPsYFNgMCI^Y-6Ji{($IQY%G^9CbT5&U~5{*w*Bi|)O3hP_TE_V<8#o;ej*B^9X3aQI66@PpWY +H4-nxE5)ZFAf`LXzXf-mfG?j#61#_m2c93LnaqbI3-K=p`u$?w2C(*-@qb2qr#c`1iIWG7$N?<-+h<}Pm4r6aAjUpyo3CiPs$UbB47v_pJ(mnD1Sih5*pEgvJRT84jT{(0yUL~#> +A910O9^l!quOTr+-o>Yk)!EPYf29Me#lgedsS3(6SyKn`aq0_YKNV5-0!EVwuD$+L2!(4^HHtSpgz9~ +IwS{8-bo~IC#2B)lKRoiZ8RA`_G1|PA!KR~;vwQ&Jm{m`JwH5fT1nMPe|eV~yD#8LK0-{RabS1Xa3PS +OJ9#Y}nM>u(HDs7*~5wQ@CGtCC{FHZ`G9OP{elOTpobNlc;lY4n{J6$qe64P9;UM60d&RY7CM9kqC;< +d#7z1>kO&U#K5`gl$v18^kuXJ6F1Ne(F`Jz0RmhGze2me}~n@P~!7IA;BO99Q# +bt3b@7f7Cx`^Qr%upgDU&b-fC(10Y`Z(!|z;%52pZm;pGbsXQUZYl%Wnk9Ev5tLZ_B5R0M~yO;Sd4;e +&$~meEia;&+@BQ8A38iBWgXg#%h7F+Ri2{o?py|K*;ThcSkFNPwe02jX~b(lea6Swis`(K9(5Rg^<$= +R2N9iz4Wp!;A*pYVb#MF$}kF)T+~{p)N|sZeIY^0BZo<#>suG^3m7)F?==Jk*3+k;yH|yBP5oY +g`$hp8R#ZUAe$1`%KcpN`xh~A2JpW|hOx3BH8+^T0IMbwzV(T0^32!U}{H~(rZHHJ0AsyMRxjqJMHwNX2k?rt@sjWn=|ZvQ6NwzxL+!b}SG7?eL8kYT +HlT9AOg$4M@*811}FWvE4Ih_p+6Xx7{JGMan$pn6EiUS(r2_2|0RV|eZ$RG?%ugi$j?LBcGIaa!1yLo +#k7ByY|ZFk$%L8iZQ!8+=1P4JQz0?#lCTy_>%FD0>C`n=+j|EkKP!p7lnAKsP=O; +1p5EoCf&>fKsZ-cj#Ug2~F;UWWm>`DbDPp#R%?{GIID8w(|V@&T9dzO6)~me`n4~)VNf&U3gg;sCP6X +1+QrR5WLoO6@hUItY0wGw&k*velNllSByea$S9n$FO)%zzQp}Pf_0`a +ZYK0^-^k-gHrkS<(Q|aZhZmZZjU428%vH{L%*}i-LZ`kYv8`i-FNORL=)Nx=BUG#{f9^{bKO>()+%&} +1|z`M!GJE4(+Ryre8&{{zw2Nc)ROK(wJsx1|twt)r{XpArh{w2-aPR?<_RC@^%{J%^YjqL>HVF +WcN1U=)AFS7#D!TC@|1*4uih#5-EfO8N0PK$Q^r2%QaGQBR&!+2HXJWf>H^03ilJ@xM05{@6b$w9fXg +&ugqPNb!`>KB~?z@IJ}U@886fKW+R{m-dI^|%3lW+zZR&H4x=MXrPd5%LnEU?KtW_bN?%nRN>w_2L`3 +p%ETRAb!}aJ=_;7!?sW_r-KJ-DEpdvILe@M2!DhAfF~_{8})KAth3hHB9ag8??sXm{wiEa78s`E17~% +in`YWD*)EiinM~h>J~ff~>?ctqX-c}ATZBJ>@r=gR&)ecMiyFIn+XnyPD=Ns~k8p#vDFFQ0tYiS?;yR +9GY4o_L2Bi6FQ86_SSL4jAycO6_;(2(p!c|Rjv`6%Q!S6N!PRX207Q?GOGkC +b&;h;>wEwN9ndk=5G8XDeVZTe>$fJ2`m9U-)q_sS`e~05v=%Z4i%@+`Z1T|4~X4Fxv68eDz^ +dDM44Jy_;Eb@E>kx_CR%HO<{WrQ#2&EN&Ioho}1iHwy69~;y6sH8~rc!C^ +hpW?*i}H=|SKX?s_>V#;Lo;2gJ?B_a<0Ot}ZTRddHg4PN4 +?R_|;0}=%+~?qm{6c#3hZ6>PWVW`0hpe*rkWu8_1)B +9x)v!yQ*gX&2qS~2olih&XCNT<<&(;DR9-JW?n|s_yoa|o9RsG9R_3SJIR#o}FV(wNS +WT}6*H4i&q~eb?rp;juu9spSQfzp|KN=rX`_b}Sf>%Ep4PJZEAflihg&DVJ?Nt-068jrE4w#5~C>uHM +*CgN}N>YE3B&uh~Z;vqL$V}0k7?2xiE|<79vucMUN@=%D4YFUvw{>02Z=a@(^CVVU_r82m6q2rX%hq9az?&^w!UUfB?Y413|8OCNBBJl#}L48uOrHD9bZ0C-$HG6~F +JSA7qf6tzT9PNY+Krqx +wy_;RJe@nu6S6ma}(a0GJeOKfnep1;*ynE^8OkL^xAA#qtq9WS?xrlcyR$=Z~urX%Tg!06tP2xSo#-h +e>6S03P6)4FVS9^A-EVU|(lhXCB$a!f=k47zO{r2NwM)O~)zW?#ofD~exM~S~XK7e@3-P +|AHn@zR@dWt(`!@JGy;7cP!=h(GysI7) +a<3lSdD~Twl?rv~aYNz*i@I3#xXjv0k5dVLE6cy4CM$|9T~_Ij&}KkPRx-Bhqb{a<`5J~iQRE^8*`d> +$Xy6)E@FWVjy8+vG58$o#u!#VG?P(%A5x9~0Doq~3j$mgK`c&obRQq$1+`ACB9d-GYO*GIgy_0hf#x> ++sK6r5u?b;kjY$^j{=*fh5mFa>v1ngQpKz~?2i4|HycxqiPz(?FCX>fY;b%=Zl^Mz`)sq~`dZbT1fS) +O`+4pj{O!y;cixt?E#5;fY_A*HMMF2v>YuTsaaM2af;71??+fy*aRzzOW95qM%1jy!)MpGxFoOgoLAE +K72wPjp=XmP&Y&cf0jW0Om*1i;LAx=n(5Rd*;WqrWgC`r;)&Jb?wg9xSw)20yHe5#s4VWwU1>YxAOP9 +Es1o*-Cg0?mXWtY-!=AD=)KW*7l*sX-(~3A8G$!tUys3ct~ClbGPjJwvsDs*)p==6(h`Yt#v?*;5USo +@Vo`HdPM3-TvL7XxTtL9IGX>mU^d#i&2;9K$G>#65ZEYQlzq@%b{s#N#U~+c@(K^{0sxYW>!{)XXFL+Cz9P4Wv(dvX$rSql{!Gmoxmv5x@J<-{e$KkNSQBp{N$s*RCjU;|mzu4 +5>Z@2E)WH5!G}RBdYFiOVqNT)yV7@w#L>zNTF8^w=D%zTekT;UwxGGc~DN%mzy!ljvpVjf2-U7m&VsO ++iQQf#t-ltt6D;-vRDx=;EvbJ8^^kMF&=KU#LvVk%`Cr~vTij%80A2SU{w&E3&LGc^L&>nWpP<_h>fM +R`d5ki^pINF##e!xjtpsk@wI+~o$%UplK&OX#YL`L25VN>9At4)@8p_`L(3?M8WX?yHsziY&veKvQ>eRZmZlMx{`W>aw6djNtO0uXM>X1h|>jOaOtJu=%jc +RDh)pet&x+01$!tj7rJku+-P}wo|-~m7r$?%umh#IJgT)EM1Tm{qZC(zT~%5^rx(>b2s-dkNIkP94wB +9Kv&0Bttef`o#z6A5X%;YJz^W+#DjZd;IGusVsmP~Kw=O=+?wq3#3P0CNX5c&<)22Itpti2ZI9%0`Tq +``eYth6FP#OHoq)w%&4#v)Gjn +)@-*AB2w&EAB>I28Zj-)~G;MWC)9`L`akUH}zPYXZSU}2o^;dVnl4;C_a&U~Vi;PNCd` +F}`z&4c$8$&tjm*u&(fqbC-j3yueA#5dR4S~-!Zo@=#s=wMssKn84oc9*db(VUm*hU)o?BZ#2X?_I#b +AJ4L;YAp$H`7#1Z~5}{^Q$l7hXe7-+|JI}Q##0FDsSXNMbb!I4caITU;;b!a(_|o{4}7?k|u&qY2Y*t +o!OGV?bv$^TLX))@6d7$@=Sb6(pxX(=n2nf&%HSCB8U^k4Ad}#@Nlu&0MvoU!^}zLD)M|eOb37e`(P~ +a2OdVW)s|pfnfcyIR%TV&3_r9igwl)(DH9mZY>Xsn02!ZOeyR5sr9BL1#e6KnF3e*7Ijm4k8!699!2N +`Hdi}8cbRNH#U5$~zsAff#wX|%hG98`Ru&o}eq3IISY9471+uOop?4b1HGm6=kia%x4Yq25glv4`V!mszF-w$cIjuIr*%1ly}RuldOk44G67;MG7;;Wa&nOnz +zRpgRySil=Ps`Uq_cBED~*EBmMJWti(j?iV}96Y@bUo}LbX&f-TGuH#jR)a9lJy`*&z!PL6lA6M)^rhm1vs&)3Z +fAIjPAuC0W8HJUtJ~jv1q?H3XO*nz1T$Q6!sg8KASv?t8HxaJS8jIhx&pcq +c+-j;243zpC-w07VsL`S*QE$G7S;V3wi_G}hQ~8Zv<~r1O!%}ztOTAch{HM>Y2kGo{3LZ4$*0wZxzpl +n*e=&#a}KtG^ha$dI%&6AAyr%l_~B3)AjndBLs3kdfwoWqs8^_430D8jQt92+ +xb5K-I)W&SjjMr%gTKdvh6VCY5d%cj^sIfdZABHS*@*!Wn-N+RkPVseY-(%4VAWS#7#$ZhOE}goBV%a +g)XPcynWe7@ZR$;)2smnph|__Pko9oqj|4lWSQmg`8*}PA^)k>z(Z{(3b8tep9+h=%|#^=+9m~Jz-ua#xh97t89U_Ai2+l5;o +bZa7RVT?67%}`J>G+i1)RDgiZ!-Pj0?ly(oT^&5 ++S{BtyFHK$`JFo;Gy8)F-usZr47rklE!7+2MFnVRj=8qCUC8uov#PsXN>`iyI1%wDh1ShIUjiCJ^2AI +2p=>bIg;M!X~hs{m^`#*9pf)rPKS<7mbRuLYqE7ejmk|4u_HV=G;`l@v47-V7QHd7{LvMv2PrrhHtwz +WOV{nW0tW#dMw98}e2&{H9HWf%LG$fOu=^-C{9x@TVqLmOu)RAzvi#WJEXis%=B+>Q;3TE^TS0>HZ9_ +F~MCD)v@!+P5OB3y1_hWGQCP1xp9A!)<)b(a__oBKg#swL3sE*9ft`ifhCl%&vfwNuK#9>_>=_o^_tN +2rU@RF9N-E0;=Au(>ROr-wvi#B=cy-r^zp-c^z%y)IrV&_Yjg2w(YYQ{T{W(Xjw-2La^0mZco2&05I3uy!nWL=js_&x_is~CLi;X6Wijx}}BpXuRC9e@@g +i3MPazU#mj3zdp-jHpYLkwq39b8Atw2j?xodl=iLa&Ci#it39oNc#X9SD4fdZq4eJlt0(uzLd>VCANC +NN{bkj!Nv+v+3L6|qp8J4kie=bW7<&6H_{HXaXABbFevi*i+>;JxuF9+xnyc>BJmcC=g+g}&-I>i<8I +sXwvzY8+-50_7vfqCYp=zNzpD8@y20ccO1d_{@D)K+dW+`%8w;>Wk5+6=ikv_+&AhT3ZK&?pys_85c5 +cNgF97b_RnjpD*P1LrD}!Df4W^$H7icN?%1Z;^Rg-aU)!-sq-r|!mhF;^Q +0N@SuFLQb74*EvTV!=0ned(=J|>lJLUPJBRMJGx24wqM8*kcfOd~(B)oG3nFX(QQ-JpUFi=&rtbMh0q +s*>4y0qwPu$7*6-U=6*@Y4`9`6cb%d$@`-ew0yBCTb1{}teubbFxxcmBh*l1)q=ODxg5UJ-rdvloo;p +E;PAE)?(oB*ifgK{heB&07RGeRv8LpbIk@P~;F%-9OcZqAVYz0;G(Vb&`+5ooHGcthsP0n4&v;g}Cr1 +;YGWKNEoQX25h^{4>+t7>_ +6jN1-D>3L>m0q(@x+BV;UJq;ywiRwX7u#pc3C?0OWf`Ce2wgB_hT1aO9w-XpWl^4gMKHlm@HX{Gd!Da +&1snfB(JsaVqIl3);zu-2)R5 +q3FAe#Nk7q)CD+mS(h6`@pa#Jdf8^V~rnU!n7YPU)gAh66F)gE4q#1U +<8You*xiZBMxJh%|Lti0jrv1X|eV!!<$8JQUn{GL&bOiNt-!QQcw)A2;S_Tj2Ot|VeMyR*`Li)E@ +7n5LFV5@nI)-jqZfql|b6~TxkUA)O+6XGRAoU5}J7X51j!}8 +l=(8~V-P)h>@6aWAK2mnY{22%h40006200000001%o003}la4%nWWo~3|axZmqY;0*_GcRyqV{2h&Wp +giLVPk7>Z*p{VFJE72ZfSI1UoLQY0{~D<0|XQR000O8NLB_@SR2nERRRD2yafONG5`PoaA|NaUv_0~W +N&gWb#iQMX<{=kaA9L>VP|D?FJfV1YjAIJbaO9lVQXb(X>4UKaCvo9U5nc=5PUcIKP)N-cAJ=%QYgpy +p`i%`O7FOj2Kti7@w!oCONS)a4W<3P +A3#nC7(LENd#4z~xVuV2$%sM3K9m3r~?*xbUu-PcF-J|aFHyfzX1al~agjyjqOz(hllVK#7Y^uYcq1- +YF*h-`tfdVNE0RhJbBKwJ3op_b3vXy~}Fes#`I*?y}XlKh?t1=97ETG%fx)T+J0hpn%EGAHi98)661h +Tp)P#RU~SHW=t$emB!R7xS0pKxqS#<87+L455PTY2`Uvj;>m>Pf4+b1nIhoTak@xX?zRj;y>GZc_NuY +9x*QWaO`}XosZC`1*~OHO{TZh%o+(mMm}g23Hay!OH{42{dMxK+94zn8yUJpyi*$v14l_)Am$STL&0# +30%k=OzuOn4}m#TE7a#XBvlH{&btHyISFTsQ+cpF*?9@tw4^3<^ug1dY}diwv{HJYFLD9ckiLfD?|LM +1E#QIiu`QCmSJn~>WhvhEmHIvbrocA9pyn}_!gMU<2HswPGm(6rFFrked0fw)7K{1)`eFHfK0=YECmg +<6oUeoR@z$uhBf=b%Hs)3es#=+J^b8~(IEcEI-1>8V9_9w;BbGaEx0#u*bHBQyOh$&Jb$W2md?hSL&v +}%~pwM%@q*23@@NIa~dzWZe26TzmT7Pb>_r*covc})qkv^Kg=e*h3N(pK +M;7RBT +9IOJyojrB0;oQtO=UQ+f4wqCM+nLIw$ug(m^?E>_J$qUc0(N21~&W#?!1t#7bnviJTXh(%72u0o&&s? +{{E#U;OlOsy;pL=78gIN)YjV%I2$c?6%pr#advT$ZB?^Co9=54k&ijj$3%&ETMiO7ffx$5ltZZD&jPu +zZG@inrIernfE!G&%!)may>=ye>fATLN|5>E$hCaOWRc2&z-&yW#wwdv(Rm%2xCVtFb6BnE*SCL`M53 +LVqO(@=hs(FUe@_4&~98AC`wV;P)jzPSL2Q8-g`=u2mIgwOQcEX*!My9u<9eU>>7r+*Yclqj!#C%jVe +o-dRantBwO=kIkH>ExC#FNP)h>@6aWAK2mnY{22(2rFrdQ-007Sx002S&003}la4%nWWo~3|axZmqY; +0*_GcRyqV{2h&Wpgicb8KI2VRU0?UubW0bZ%j7WiMZ8ZE$R5ZDnqBVRUJ4ZZ2?ntypbO+(;7sj>LZ`8 +AW)=U;^A`bE9J=M93~u7Pye?MVHNudh9mS!PBu6h+%x+gm$ySz0-v_j6? +^SA`_}Ug}KNWTET&<_#4pmD)%W#=T0*jQ?9ml)QXSN&`Tpp!6aqW!VCyjTJ?KO>hMeI% +sQSd~to+Xt|N~S6QDH&a;MM%U)SQHDExNawd9X_K%YvMpQy}y;6`^>S9eZv(m#$G$(1n1AIW4+(@Yy +S#@iNWdQ8E-G5m5rU8y2VMV*Ao}V=~e+{AKdDY&b3KDg7I+i%V_+5&LP-uU#n@lZ~>w3O)v75~)IE^AG1L}6smxX|I+Gpw`}(pdIhi{V7Kw%gk*!9m +fIP3o?E=LKS#nBdiOgHGK#9zq70S$I)|XM0s2gh%i_#Y`CVwIMyx<5MGV^l9*L*? +s2<&yW9dqOEBi9vPKs9nCao*JH;B6fsNwAZ3|aGJ6SC~yO&%s5>ZuBXL0oDKgs9kA)92;LzaNSNXeD5 +^EkTK`}+|ZMcoDG;qt(UZ-(kP>2Lo_;pz**fKbb);suAw#Z0<3$jA|91nP9ZiQPcZhwwJ%Jqjvo?UGa +!>c2Rjm0Y}Bt~}w~S|-w@qLOj&>8N$IkppjCg$_J+fp3GW+n{15sZo_M3nl2ttJP}nyOk!$;zFbnHTn +0wXiP5XD51T?LR)n{^A(w^Um%VF+io>VAq~ogKbV)k0CK*x#eDKDV0@xejycZ=6n=-iK`^onfRt1h&t-Ebz4rCTJh``m$NT{upqrMZr0MNTNtymM +=&_!aVrV)h0=LR|DgDI+^0JB1Ak8}LZtBwC!R#Yq)Gv?0xYVoM!k?l^;9U9xV38 +jq`i3x*gM)s2A7N9F`^t4i1?}@*bTzRo0Do(&$~x2{KojLyPI|wycp73l~%#8U5)vr0*fdwjsp8QvI4t8NIKKK6mlCfIuV!n17%sdi(>rOCTWQg0Yd#HhZE@gy-3u!B5+CTJSBI}0aYNJ_W0nP%6 +J&8-@)$3(G0hOiK4IK4H)>3;7{5KbzJ@@k%^`!!svAGtJtar1m&j1M(g-m`8I%yl8ZLN!_Zn8{E*Tp% +YGwwuxj!zrA*Y+(H^&>_27ySI{h?;O5iamhX52cXjq^-f$^LOv3SJ&? +^t}ajC@arx0)H{ +a7pQ4kWt?;r1Je=8^zZT%u|rs7s +oBX)u^;9h|c8iviQ^j2=ePs+5D@xlRc${n$@BPn=3zbbV9gab=ZtkKN)iJF>DUsaPZ(Cms9>#bL;KjC +k-5N;&O@0P#g`5Hac5dFS%IU$@Q4NGLzV6I6E1j#zV-~kb+Q&qY;L +|dB@Z|BqU?*9n$3TneSZ|(@birz7gEPz%ZPrW!w3EETbKN{%{_bAtHR*tuTxD!!!-BIMD&c}?k +gAH6a4VK5LL|I0md!z(P;QVfPXc2$3AxW`8&W5;11zEt2aOe*`Ho~e&Q!}-pWXog0JH=E03-ka0B~t=FJE?LZe(wAFLiQk +Y-wUMFLiWjY%gD5X>MtBUtcb8d2N%yj+-zLhVPN`4pw?;CA4p_2bxlC)F?r~9(o!%Fl?*<_Kr=`efyd +O4`j2PJl{Wz?SIBK=~e*Lz3T}9gldf@C*;BErgsRpc)3_EzKP$O#6gy)i=@hmQnyArbx@6RACWMm&#J +<)fEDg2o#-uA!fA0paYVwW<)R+;dn$F;Vd<3a!iLJi1fe2r3|1kX@V!UtLT>(Nlfp|9|4Opx8l>^hZ8 +DNP60JNT98p$Cb}C_w?kvvss|}Qmw)1TqeT0N;2!dj5!`6IXE`1w!$ +szr_Pm^7qZzq%|>(?wPvUr!L`>Ys+Pzeey=Kosj&fz~d0%YmLmZeb+Ko(MY!gC25wYC_&CHIaBj2S;svwvl8J`T<(wC&ZtctPB5 +Lq~Z;RHPo8?!DSUq;f0jLVekNC)A-SR4%V+mn^)syR&%50@RQXnP0li!`_J&c$BeI|!O>_C$KD8DKX# +0zxt$EZd~GoB=z}Wd0r)KFrL6GMgIKX1$!){VXxRYwRPv_IGN@^rr9F-P2y(=JY>MO9KQH00 +00807zB_Q$DWBHnRr+0L~Kt044wc0B~t=FJE?LZe(wAFLiQkY-wUMFLiWjY%gPPZf<2`bZKvHE^v9BS +Xpl)HxhmyVE#ivM&OpAwC7qU5HJtU#FH31v5m2ueF%aU)lEr^DYBPjOQV0kRXqBT}68A- +yj20f4vHZpuZCU|&UTWoxSfsNBt81=SLh*vDtuR`(Cu>$dvlVZ}TJbII?U-DFs(9|8+Kd_|z(xg +I@fFKemGIwBu7i=b?_4TaeIXE4pN$$KyNfxZp3pw!6 +@N$o)sTDZGltn+#O1TF-4vozZ!bKQ@kFes7IcI#FCorPb(FDBqaX5&f^k>mhzkE6w)6T6fYS9~i?MT? +2RzX%5LyQ7pVjpt?nQh87Fbj)4P~;KW%zLV2(}?A;oVy~}?9>k +u!@R3rq$Xqef9;}+|;5sv3@T~#Q?X$*wLnqDbJWJDUYLJZA=>_P`lndcMnoyO;riR;x?G;#Vw%k~fQwdvY`~?j_!+*YeOyz6$*6|Hgnp<`55b|3dE~f}EDcn8}{83_F +kSx1`$U-R5Lc^_{M2N(U#aI=;e7XPW`b+Wi&FAZq)qJ6}{IXsCG +`T~g*j%8bgf>r!%Ga_A$E`?rWCMg1Z$4m7B9s+^GHG#`4B1J8*nj9axj7%aj2r&-f4 +Z?_Bd;X(;gmfY;7V(o1n4+;^&ne^`SR<`nDiit2cTh8ZgQY5^^;hm6Ne9~Re~bS0$vyno*@+|{ +4QMHtHgn9Aads!%Ivf2l8uy6PTktWRGNQ%~lTnMA7fY{aD0J6;tlCWP +qLMJOFnMMtTFK5WT}Br}Q%u24`<_?JR5Y(zegSkJoR*+^81jfTzQNeS8u{o#oJalpzA6P6bg)~ws7&+ +$-~HKX(I?D+&FTW|?fcYBJ4(->c^RwMY%8#JE8BqmX<@q}nY1D=AekW!9%1u%)Rn51Km_n-b*JkGw}T +>sXG6I+H8ZeL^Pt&Mhi0wq%zedHC~V;D_T*xiV;$<1sbCXM)G8m!Dez`7Yz1*J7%maP!7}AZ>N&idz=)m#heC|2W%gN&R_(;)^-(HeLq?HYij*p| +A$53RJ9qcBs;-S8?5#iB0P5>opYU +ng;3dOTjq4COeSUdy_`o`Vt0{`pTbBeaQvE^vKtT+vWVK +nsYd&J6!-XAU#c5*Pn~0yT^x{Phm7{TNh0zL4=>0T*B$kNH+NNp#{m$4XIFGjE5SZsT-#j +{_cTic|*C2O;HVztt!eKp{zL&4x>P9Pv@2&)cR`+jMH)vdK@SmMU?EZ$vO0AX){+*H3W&`0()keob~pkDINx_W&v`f+OAy!iXLGef~2ejam6&x$aK<60au~-TP8b>PiB56A0qb +_!^<(mJC|=JE6n|O*I@&!dhppaFPUF6PzHNz$NAtFHAe`MqQq1mk1|5+UuXFkd}j)C&oK5_ +Aao(X(0k(;>jJh!m3*buZW6`-eij{ydHQDLNuz_RA(8If?)N*2~4`0RJ*!z1oPy*C}+1LFRP1VnjhaL +hvM2e@h(o3o=!=kVRk=J649qxVoW$^@`QvEvR;d}j%UO9KQH0000 +807zB_Q?>_aSp)$900;sA03iSX0B~t=FJE?LZe(wAFLiQkY-wUMFLiWjY%g(jWp!mPaCvP|PfNov6u% +ex9Ukn`fdnmH9PH{zLD@k(mC(G5hiP8g*QogEP1??NxIiHM{uAl~4Jt=BM +Q*068iqfsRXKyKhLT@V#J(6HapbId%Y_QNM{4DnFBg$(SYS4sSE;In-&PLhvx&lkvIG$3J#d{l3bg*e +o%AJ(ln=OfhPJWXqt{qblA*zs`K3I9X#IO-sL@I(K;;xPh@$LRMS*7y|>FogBBX{G**Jn-avCA^2v~z +Ot4@7nKGErYMVR$LVBSeTv8nReY)$c0mnG_%v9&t%Y+b5D`>>-`VgfYSTA&Kc}B(z7bapm;Q`qSxwyf&&Pcz4yFn~sk;z*;`luBStJNwvaTAi +_wS|BGM(c@B&)6NLG5G|Mb(eX6~!YdorR?~Kw8lV&q;${OSG=x*O*=81Cv4+kq2(oi2R%`iAh(cM9-@ +4(krSY$rdIZ^N*5nE2&Dm&a{b~+`}+f7__v-FK*?*>XLwsE@3ScIc +LjcMH<@EQL%9=twyEEGP(He`HS)EsH3$dLl`}s(J6~8!kvM&DUsuZ*}v8%t%K-E(Bs3}$Zw)bn@6NJI +PSH8Eb>4OyPW)GGd$4f{~5DW1V3Rai;Re`&gg7H&12+O?ooAiC-Nf0$)s7}=gpfpyTu=rJ*ydpWgQ(0 +mkag$<@iN3XfeJA#nPKVFDREG^jd=OUW?HC@rV`OHp8auXzVm;hTf@?ADwp5+%`w>z21_`N2-=KrIpG +$p%Uy`GvsU!0%?A#cHF#S_7end0$qrX5ML(4Xv}5ZJwFIFS^OzZ=W1xh>#*Fv^gh$64o*WG@@#L1*kX +mZB*C=80m9xkyhUjF6h^s7&T3Tv0YwXb-C7i>Yh8_|&|>O{1CZ@_*F3EL2pD0^=lfx;R%HHhk&MOz2?#`{eX}Nm#n%F0+xazPBU_4#vHgY8d$GTvDy9t*L6(%Rqk!x=S(m?b_h +fJEm`h|m;aCVab+52*c-s2{3;+pC3^-FHD1G)H9e$A_~od{7pcn8Hx7>KUNUL`8 +~yEl*t>cr{Rj0^*4wm;^R#hff&n11t1p`OWQ{1;G50|XQR000O8NLB_@`+e)$Zv_AVAr1flBme*aaA|NaUv_0~WN&gWb#iQMX<{ +=kb#!TLFLGsbaBpsNWiD`etyfKN8#fTW8}NT1l!GO}UI+ChRbdov;?zDQH5xZ4P!uw?J1eo?B^8n@MJ +W2;`-bFxSw@Tk-HWwL&IjMTc|(`h)MBwH8{b%3EQG2z#(E)_&Qy)3MSMR#IzB3SC)H9_8Ch$rYxjO=D +z7%u`dht~R!QwYD^p3YjDANY<^EKB@>u@KG`f&>|IzsO{4*|guCt83=Con6&h-t+g6y5NPU+P(N9{Im +Wd0~u!ZZwSQ2sNy;V!_A=F@y-b +On2?ZmfA3cx5#S*l#gs)yn%L9vtj?Ac^uD*sm^s*HJbwUZ(@+DoNT?0Y?XF@%G@AbV0j*Rh`)64-aocqAo0iLpm{kRNvNaCX@i#_F +Or6a7Mu11Mez6Wsw%Eijo5ojn&=pr<_nu1G%Cz#ZFFSi +qL5y~xbHwfGO(LJ>G&=lD@X1d$pnw%u#V?#&z%i+|1k8jqZ2T~Fo9Lzzhl%=l@s%kgz_gY6i14>9jzk +8=-s6Pw(WtT)@_~+%N4a_09BjXw1Z^ea=?eQ?Nm&cdrv^sc{_XR`8hjCtQq8QDj6s +l0u{6a#tZjiWE87UhHFV~QJP6_m9adV`0cS_LaHGIszX++K_e17M0o`;KyE2x7`)_F3k`!{&m!-`(G% +J5mc~eO{n!2Be<{>LLKR;h7ziyUUZtC+SFx_9pquHImdHs4=7}y(D1hvuz>|v^urmD=2PyZaWXWzPQj +dt?Gmy^7bsshET4@q96o9y%(;GVt=%53a;@cL(4^uA*RQXe(ffeIfEq-nCfOn* +2FImfoW9DdIl7+T06)+Y=D4*V}rO9KQH0000807zB_Q{mdEx%n0V0O?f#03iSX0B~t=FJE?LZe(wAFLiQk +Y-wUMFLiWjY%g+UbaHtvaCzlCYjfL1lHXnB|1d>gN}xl*l$>lftmP%LM8~SNBbP+4@5<)`hvZPi8U%Q +Fh*DhM-+tXa56lbz%9~QY?DB&I0y90+kLfqK%CilNqE%H^xribbZ?;)pGCnV|q$))uzdwHT_>p=dHat +$uw>_ZR +MSZ7PP>{WS!{{*6n7e&d-s=%vUEaM!Q5GC1yBa?a`i4+o(>BZT_2)My+j@eGI1y5m1o9srwR^x(6gha +x4x+IJi2`>t^i_0}zBE|qxELkSG4|qgpV#N`=G0WzEhh+n`l$;n5gWmw4puF3P{z&5X^ES`6AZT~;>r +$-P+2rNPyVuj`&1CxO;w*aq>U=tReSS3!7D=I};YZ7Kh!;V9pGfwuKyDn+qG1=r0|tm%$RMgc56~`75 +;hl#HcRB)&2cyk*}Fpc@K0X9zIZ=5Q|nv-__QcNLVE)j=$Tp)DED3>VGZclmmnflo-)9@YYC(;k+BQK +un;927$>}tDH90w(TwX;lUZ0%)^uf_Hy#(;%^h +P&c&~0?LR`6yH{(7bvFq*1$;k89z@jtB^dK{9I=nW<3~S(*$nw|85zS6)cU=zRX*Jbq%qKh?)7?f8(f +fd6<=35Z7|dfpYX+6T1Li!t(z^lE=ZN@Q5$R$M|2G;PLks#n)xIg +-LG#;2WND^fMHwvHI_v8|lix1e_Nthax!mWimY&umij~diG4*@(p+l;Ue2SJLv0OUgQk))uEch0&w4e +z94OR9xp{f-inPSen&j}KBE8p9tN5`V@aH&VZYx+^tj;j1gJj0V_Og$JZZDpOP&;BHhajO5J>uD;CGq^Z<>rQtt2$#t0T;$Tvlxvq6<4Kb3LN*0=y(s;uybCe+97e!Ut0yyw+oM$tlS +(K%bIcsV{8yVFRwFvnt+p!H#@AM;J2b`CL)QON1LZl#-lR7(`uEFgmZmWTkeZWXKOOn<4j +4Afa6@*5Ka;YSJzc;g^`)kd&2=%F-&X?cv{2*AeKavH({e`EpTXD{`8=#BzJTt?5ljclmS+GvI`rb8b +ZsmRenD@7+HnW7b6l_GaYwc{?W6p=uu<5yc1$nhI~2Xq2CEkLCmiq#63_`9UY&FmJLL^$VC72(HbP8d{QNTX*XxW)zALXRoHyx3v-^+lS#OMtmT^OTnHGj|TAP82>!SKR +>X{I-Adje||X9{9&h5BJ_0vHwSsM2+$^VByLJSZa5H$<;MwFUYpl;LvBPQgOS5oua*1UJEkw1<8KnyyMo~Gp<<=#FIw +`ZMPu5dYvupBY>y_S{3*xIWDo3xQ1C=Q1XKkZf%~72W_v^b)+5L*T} +0u4b7=4=A+4O5VW?w2*V)9++g#klSycd)NE8evB;S;(g9RnsA|P9-w8May;lip4@b$gG5QDy>@c2*Rk +VK{{ChU?IUoDMn(@{u~0lgx?d64pAt4d#92#U^3O1Y_l$(yiw6y{j&edQc@C5kizXyS?TY*s@}MB&FYGA`j@e +WA`?(GnNC|4Q_=O#^qdGpL6G>YtLR6WEp-V5M_Aop`(gQ(8t14ZLW+ClqX6?__lpluS5bW(RJf^?j!Q +U_pKN`BK&A^f>>Q4pwxo{kkEWNfv3=rJZRS_>vPXBuG@@4eqs~HZ4MfQ-gaO@INQcW;*r)-EFsarz>02wUh1G@_}F>OB`TyOwAY4SK@De)!eXfE<4lzkwwAS +$5qd59i*yTWg#}SDnsYL4%POZjnqeBJh#iCws53$xHk$oIidvj5Ks|%qn}q}&cdfJ*LeKK0E2@@N%od +9(hvh<<=#n!*nc#HToJ^bP?1NkNX{a&DTc9#lct|@(q>rLCRn{!#DS?#&M&o6|>hOg0mmH7Q(J>BnDy +)X&9J$NM^zy%>lb6%UWpp(?nZCPFSqup~zcem}?~6*J%&COAmnKZvt>B +tz7hn9_D7TxcAFsivQleqkm_U*OTc4uP)wB&o6$x!prI9$>{`ZemJw=vRBs`@HgB%nJRnN!FOlNAHF+ ++@bkOl9|!FF;q#Aet6N-k7w(o1pJ;pY)z)TuClwGqkAG8TP-+OQyuRaefnD<_=-5)vvlN?};qm6435J~PZ +y3{%(-1U=;766J<{%O5z+2^@tq^4<9{5-sfCyhp+o86n;Dwzq&Sf!5#+4NovivQy%{8*TV*sJ)+*`&Ee<6Iz+POL7%*zHL9R)Kp*SLecKsH#V#Q41H$}IJG-lad~YNcxWWF~( +m|J5;dO??Dg=UWa;tKfT+Jg=rjfPGXNzD~OscTKag +^lo}nv{z({aD%R$e$PE}*Z|b&YTa`z1}$=kAJbNdCJ9C2j$Ow$5SoEhqhw+N0lk9Vnq$IUK7#+|LVq;wugS-|O!fo-Akf@Ng!pg +-?(2A;Y!nhYLa7b2nap7!9Vc&2pLU3BzJyBS;=L7E%bzmsalN=ilob|lvceX%-k;LvOLLCuBExzY{E* +X9v&MhNo59gZU=d0Yae)E=8pC6i3`>5TQPP2#~(X{+FRG)F(Kz)TC%9f`9l)W7gQSuF|UStjW!PBR5x +H+Uh(*clRYx9BW6bNTa*%_y@ZdUga1ztLjN&%Z~VtT#a3s9aQ8q4Bvk!`j>=6RgN<(+9N%91*nhox~| +hLk?hAmX}%1tbI)X!uLI*Gl&*!4z{TgA<%a6t{cYJWnkyP*8Zf5P?CHB(vXK5NW9fxjjh6sACQ;(BJz +c&bWzsEZF6!-a2e6XcE`Vucsy1b+{Ds>N@D1X6X&+BT>_js#+uVP0kMg6aMxrhFQxs#lm`MyABZGMcV +dZ?n6qB%ifp3vMS-kPGoRpdfh3XnVNEnO{Y5691kPYTtj^*lTdzw@+6NZlJj-uvJGDV}~99{%| +8Te$d-Ugz@oQPnP>8frm%U_Guym&<{uh$C5c7|Zu_LLe+`p6DQ-{?-B1Y}*6Wt_5jLN&`7>(F8s9wI# +0)iUJ&hZ`ePz{D&VunjIj2-rj+|NZE07H&57!Y*l+VJw-@qMhVp4P!&@L2sn`ODz>xe_0)@0K|T(D7% +-b!O7i3uR0FPo3ql8RR2#rpXP;JV=2RS3mbMGn4xPMNnxm`zL{y$2(n(|*_&Ue +^G2u!4&bIO*Rg>gS|ym8Lb579Hn2lVVaju2!Rh(P+Os>I5X4TJ_!gu=LpD{Pd+=(4P=OpHR5{usNW;l +;aig_-k{V)mq2-}Ne5Y;fQBDdu^Z;9Mb`n!m6_IEu+tBbObdklv!0|oYt?SWX0DbwB>(}=zT-2;4vF{ +@>NYfQ{RoD#)=0ya*vk*i?b-YldqAROhHM&7`JDXYJhIMUSyZ5t4=r_uK4vH()OA)$Uji~9;Bc|S|A% +h#QSG|0QEo?PJ+ES$p^sp+v&Bqf>(a>w35q{4Lws+vRECM$XCJC^5Swe@D4OOx~&nCQ@rmLhs{6yG!=$7Mu00vj?-i^md2xWHWgh3?(H0eTh4a$x;$DK)MmA!f +eoWo=&E80Anv6@`dZAusF^z3L-7Oke;SOwcV7)-=dqD6Q%}Snww-sNZrV$&`$wi*=r)OX8DyF0SD?sGfNYVB)fibRD<`jrWb+*OdDW%vIii_=L?53V( +UqBf;rfEIt)f&QZB9yT!9+z6|1)tlDDJ%~EVdx+HW~7VlL{$|9+jGI@f8=*(4kL77bAy`(;l9fV)F?RTT~E7ZM;S8*5u!n* +ZS8iGu~TAB$9OVs3pqjfUze>)l!jEuaU!bNIlF!UuP-1d!F&*IvUz3*jQb7je|}zrg?{JU^3TbW}_} +04RaWsaL<~92q@zk7qy$1y3_Gg8iZGJ +Te9dBGEKXm+w7Q@?}wlW$33lk%NF&>WpK@ymMMHOH=U+kLt}7E1S@+3l^KVA@j&S29HMf*GUpPQKiMiDM!R;?{8FC^hi{y`P|hD+!L8ktKtaU)M=jrCgjsBBsm9B}W)6C5`j=;Bjm=KX+rC6o*t5E +7=|i@?-huN1AATq&WnR9}jpDq}F~kzB4EngMDoNazYDW@NBp5P4MW_&)N~-ONB&@z0)8vABMk3>G@Ci +G!UVo`splvXW&X0Fe40?|SQD~|0z9|OcL+(}gI4zb_k+B_JA +^ltW$hlX_1rh&YC^Rxr|%UC}5yJ5i)kpq@+zZER6zv6D4*1K(bPs9|!8_9Jr^%PjL=3HKt +xwljDiLY~iJ8sPLatu^+>aZlK}Hm}qnEaaZEl}TDEnNQ+R_=?i^!y%||A4aS1j0#<*ES$Fh_vRdIsyR +Q{7XC8%b#i%r8ci-QFD_en)g}pzcgC$5j-K7m;hMZ^c$3rJ`@VWRIX!==Hy+AH2ip-Jd)sYkIQbgh)} +4(`qvN-}ckRAG6zFgm92cV`luz6g%6?aimu@34%VY0hizOZJYbo5*O}MX-@SYw*i*Kz>gl+wUy5FwZY +UkI74C0{^7Byl|QqIoneE~VlHsMkGJF*jyM7ghET3sOLmR~HrW~_+@=*S;@Qylb1vJr$9F#GJc1Qq6? +`zj0mhG7&nLcI1sw)YwPYxGYeehDPIKi6*8mw{pQXWMIn`~Ht$5z9Xt@9B%x7xUTin!adRRrlqWFMSJ +OU!OSG-nedp8jzS$N28mbjX>Gr3vCMV0lkW6qmK!6d^>MEZb>gTl&egdURpnzV`m1Fm>13VKK%S!}fFRl!q$~+@|6YOlVX)@__WuD;O9KQH0000807zB_Qy#XCZ^jk?00>0@03ZMW0B~t=FJE?LZe(wAF +LiQkY-wUMFLiWjY%g+|*P?1RIS;qu&i?UgRqlMe|L$DMS=8xmxE%$@om=>82Ese*X5GZ@)2 +bmMb1t54oy-s$Kni%Zp5A3mBLq`kBnQilwy3m*P2<^IgC;nfYk|ms~AVIkRx9Ef9iFrA>8Gn*W0kl79GP +xcVMP9&TuOeX0<43*_)$&R{+jpBHwWB}W4~C=b(PR>h@BaIJG`PCD|5J273K%?Izz^IJpdaR0DV|Gq$ +!_vY(9lD{GetP;;K#uv0utf*15XtUj3?3Em)pD14S;*2lhN?={pjI-a5K5Py??0R4X*EQqPzRshuh)p +^#sQM+*y|8`s6e`?feC4IbxsqTCs8|SSqqbxn%jAah6EfY01hQey?+2(JU2oT5P__Vmt=6F7jj(1Jhu +#RAvSd{qe)+n_={6a0l!Ce16u~=X&w3Z_f7pMc +i;g+U8IE<1>eCGU_|u<^9)ZM{QtNl6zqRA4!bt)pd#Utz0<63Wjm0#5G60mqKn!eu#W$}6TEU;W<2#!zbKf}`P4nkuVGDmB|jb%H1RVmP{g0Hyh_&*S^iRlv3dUq>pB9|i168w55cNK#kBiZxf +tuHTNsYNtEax)rv%6in1U)+s-?#6=s&({mJ+VSvjR7 +G~XXkK`KGZ>q>dn2lltBwjYg_$mC_tElRJ@J=9?xDrTo*bTUXMpN4=?Py1$vLrc}WiM>yq}q3=@tlasA{l;JA> +yG&Ca=EdSw1Et5mmbV(B|TG677SYp9Das_TJ)XUu5K8*S# +ho|9r<6toAfGtBjn7|;8E%UpjHZ} +aWA_H$@+U4+1OW1`Mjr;BuOFh}`0nHA9*ttB-wFSk%ghADf3EFmhXmm*FcMhb3HDwLKMvsU**ovmff) +zk5mozyLkr`03#d1AKN+-5H^bi8?>I_>KP2{tKsx*Z5E47^7mD^@GWAa24o3A;o(Kr3VDM1%ps~a$cz +b7VH=m)^>Am9j&uTI$K8|E2%P8t9k?ainSEstVRK!k1cp*@J)B!m>r?$3wG +bD4_6^B=|TWdLg>EP#I`Y0eWOF^ZQQR8OCbT{d6_ee2OU#Mz%O5@>$4l3>`H=Gq1-O9;mqmqz%E>Vr$x~1%qX4SYYh4 +ye|0kBkS-)mI=8}}Fxwb99g!rSo&y +mgmBK(bTEts}KmXo!2fhYyWz+*pwBU7x?f^1Wyshpi_3jvAXTrR+YW(&&j_!?}@x{%<$(;fM?72h%K; +@gm+q=EL(T)>rySSxjzvpLWdvM>1tre|!kUKAWG2NfkUv4xXs!)FW}=JLjp!UUD4Pl5^+A^Xr0aUzS` +$+NMa*>ecyFaejG7y)9~zl_>JY+E!S<{*R!J!1EtCG|jRB0)m6SRH2SxU2vM$PB_FnV7O-pZ)yqmrkhdheuZBM2^@`0?ZUJM`SU`dAA2l1gnD?$9i{u_HS&P7a#>#O5L5 +2pemJNBlkrk|_?xT-{ZR8oD&I>0(^Qd>p7(!n|T#Xd$e{b?0I+ +Y0H_z>ck9;Z}nsIeMT4s6}d!G+c%P_FNaVt1>w_V|X^!0*IiMriL5_;tOVg@!1SQ1WDf0EZd#rY-gOc +PG@#(TG58NcL~lDmzy3UTb6Gi9uX2Vb~eS}XA06%)En6fvkSYW6dm$TZ$=-5Q3Jc1`Z#px2|BWI;NgaxI?pDg +UN1pI@L&Wr!0N(He}r<#V<1mSJ)n=wjSpuPO^3$o)NtjMeAOgmBIwkzmsqqXfG6P#Y%b!HA8Iw8!>nIX6T!j +K7oK)o~X0tUQ5E4X_d~PKOs;x#@*NK0cy&-Me2|N=JI$+XC6fWgri2+cx4pq`;dAY2S5p%Q{p!8q?pL +`TgQ`3c*v)i@EFflb%13!eWw9wh)PfJ0*3-$wVHnPz4H~~DP+{e_b&4!cOx!0(sEe?L_Q((lwS$|^6h +!y1q1Q!xcjSuM4_?^bnbQ&~Qcft0Zk;JTwzGmPA`-}ZpODuragJt9ovMe3w6+L*ei +hUoH(4XOElp<@d{g$YYAr80c6$gu6--z#%g&g#f-Lei0e8pXV9w9!uAd4aW~2^cuJkC%R%(beoa(YOyVn2=-GY{_Q~_Cyc7l__fc-4>6^~kUFpCM$PizI1cF2!pxf8OY1-JKzXnCtBZq?VyGz9 +_`fbqXf;7tlAbf~y$E#SVRZmk9%X+MGBf-8x^Uy(mzXbkd7jin3=1!$9&yjaaQDZNQJW*GTK?W6z4rQ +57+u5^wW7f9+8HTl5&fX!VgyIKY^{k5piUDU?wdDa^=!c^(l@P7Ph#2`4~SXV%sZPouYeD~Hx)RhoD3 +wCt=?q7Q^5a1}cI~zps=`=ip^G$al`<#IvR^(B|^E#EWgy1?rzk{NW=8x_WNem%uVZh))qldDiPd;40lvRD` +$KK!!1&ur?`*SB;aXFLcME8O0}e!V9gEgx($LiJRA{ytVWFA*$0h5+X?-8Ci=OWJ+I(U!%!BL#&p)cp +Kf)901Wuc(cc2AgMXY5$7)HFmWqOelldqu8H-h8#+A-oeIpW*PYJ%<*1GZHU-1 +8e4BZv9e`xT=_Wy)oV!&fV>(2U0o;>lVzi_}STxD2`vg+^HXqnB^iY%2)sMWuc9O|{W&$G^jFQPjlHG +6{pp>zANG+-rr;8QIhT?X`=i+#=;YH#$k;%2}sF21C +fZFnk5xBa6qYet}bnC@ocg;C3{5Qwl&dj+pQ{!(q{t0{KR4dJ)tNmZ{R`xVY^#`p@M+CtZGzqaDGd$5 +jtaX!@RJrD!izhExO{jr5p7MQk4U0q9HI;>3=QoSFPLd1!=xmm@+(CWo7(7IiV-XDKLvsQc931yc;C4 +Ct2hf6)B&1ArXtk`36|_$I+MT+aEuSuSahJ>-WsXO!A(#7s$GmQvTs>4|L^Gu9;Y^%bmF+P%|CGv0)T +|JRm&6_7Pcyo*&`r*QUFjtI*I)h5L?2W5=)3cys&h%Gy|4hn@ATW83_O^K!L-nHy`_ +$E+s2$SjTtn3GIy-C>wBR1W3V_i9tYBFrDeS+k%c=9TZpg-3LY|P1-CZ@%y^k9@M97DqdU-nS6R|O{Q +8o)5~FWedHV}oT21Yyrjinhx?J9@PT_qh^ao&TGB74M+NV|yC7*89YOgHeQ{iXr8D0ZM>90Q$pyh>aP +*kShmdl%gh*SJ9TXR+zprt-0o7$%fLH%iMyQa27hXtq%<0|UF=TDHC02w%d*0aV2X4G0hj|1(6q*rtB +fz#--Y2j9wG8k@E$BJlTTI(S7*?CCqRgWEM7?*T{!-2~=g`wdJvPubF1r7)ig0)9QQkY{pCZoEyG27X +Y^NnK1!vWnxnWub#?u5wIS5urmjZ+_~9Y*`znV!ElS52JF-L6d-ZQoflYH#egIm!WUVGpMTqdAI~S#~ +%2#B_U%Y9?f?;kibKbkz!DugwqzqSWP-*ID7wT^j(0>d(6ESAV-=8Y)J0AofQh+Pf=$5j1w|F6hP0+x +!~=ywdMJc@9TmT0EUJx(RQvd-Appeg{V0P7ulfjdAEoEX3hWt$65)WLs)$9PwgtZxATki +<{3Np^-zwI%rCS~TU>Bk#eA{2T>r~DL5W_>Hgli|DaWUQH-QxZ4lB9Ey=X2~tg0qvkO&aWEYejUmqB1 +1J1DkGh^+;)tHhOj@>iv3Ia9}QmhEz2X#Y&=Op(2!N!U|+Nfb2F+NY<>@+$Y +~#WgXP1h7(60ZrpG{A+T7u0SW9o(;Q6&0?GF2AET!3+V0)q->aySmC#hz~Y&U6U(*YUkfE%IX$a3k%F +V2kpKjE(Q`(-W*z)h+~=j{H&@cg@r_iUoC3u5J6BLUDzWzx; +mo1~x|BVrI`xi;i<8$i9fG=!J&Y6l1S)&AzP!yO(Q;b#fb1>|)1NJN6nH)%aI-%i1$<>O9-ZV+H(^Wc>#jrfXz0gl? +K+;U-7UTB(iadvNO&S!`hxBFCY@8G2`84MXOH!cx?(!;U2!K7Oe{!e#;4q?WVjUGY~%vXlbD)7U%fg8 +$cQ-@z+#tfiV33vGrPO6RExC`rBP;{VlJvzI>JS(6`k_P%HVeiqYtQVaN!&5;anB{5(Bvh!*ALb?LN@ +Q}=IEx2lb!It`=$Dj^4#?s)|Ui>IzIc&4YaIdwjLMMhLr1vTnvTea19yR1QdxV^%DLE}YguDoKfKbQ>1W2#t!Ld;S(9(TM$W%3e1Hz_;xzb@$bCpgRVv}-=$59#V+eQL=51E2jjL7 +nS|hXbj|jiek|koaLfhE013rm3Tb6)Wj@a|jeGd=Og{9g4)~?svZUxjEkB00%a2#2H8II?o>oQx63BI34>Gc-rtR)iU2HQPu5D7q~cYQ?wH7>N@`UUeD!6*9y3-ME`=tZ<5tPX!+o}8C3`P)UExEPKLJtE>;v7f7C`ey +yz>73Iz7L>dH2Kp)h`!hL0-*p~4P+Z;woE1D%D@E`RfcDEK~-@ewPx$OAbf2B1!FmoJG@p93%KWI%LRUI#Y +dAk!+ED@Qc7)5WVo&|$J^U0A}gk>Iywo=>b`!fQPZph5oPWk0tF +7Ur;a5VD{>o^TRca&@N!bljQ>L_icEi(WP0aBNg6?>GqT_EYzqcKyG};Xj=q8TcX>fx4-n6~k65AE|D`^3M|lC|03!q3CB6_4@H-YD4l5 +TV0|Y!bX&T6@S+Sn`9GjDl<26$|rqV6sqM#*P{3HdN+Z+WwbgJlP!}65LwO%Y$%^c=v{=8ERvd~wtWB +H>>)==ntlGhpw;8-5T@f+%paFNLpxfjTi{K!hF4(C@l(^*6)YqOCfgc0eGuE;jb3FEbz?`pc~^Yc)fP@J(?ZH>8VH}{GHscnFoS9Z +GNhI~j0nX{t%Svm7#?k0x@i0?4SDR9Ss^u>c5Yeof(iN572XSpr|fV;H0J&~I9$9sd;Qg%tm^feDI4f +xkg0VNn#3XiZ^IVBXS*K!x}Y1qI18bngxaU%9}998RUKDx{zZ$LT_`2hbb?9$2UF7XV9B;^9NUk8E>j +fPrS0XD*>mOZ$qhxqNH4roZBE4MV3_tL;IGuf3KJ>=H#wo~fHZZ2VQY-Nh97^rKqpLFH#MiMz`oZ37~ +@9=4ja^((a{Y?f`*wA6!P|jNre7&2K0A_=F6bmVqH2rKEjaFu(yHKxi6jzem +VQ*tAc%~5wbnn!~7nXt7N8}Rqbx~04HJ;{N +LF0n(z!t02Ck!fdjTAGt>htl#&G1HTk$#!{^BPX_LI)N?bwyLeHUgsmk^#Oo(MfLW}{*-;Q{2Ufrf*2 +7;Y2C^9A{0jP*^gwuEe4Cf{9LzI}haOwZq6US8a#_;z6gYSFJ#KKz_dM%R}x*lN)xrZ(V|64JKTc1?9 +Clc>lxC5Y~a{OQtm&{e))TP4r^op%qnl&dL#S+@iwDsIrj@iK@Bl+0re!}T^hALhN-9=qZJi}Mp#pqq +wV;BDtvjz@NVw-+qo+ttkj3}+SKH)B^*r<)SpR_9q4Bk`#gXS}@>3BrC+3!DLagY(l}$uV^%8#j{=!u +X6DnA$hYRE((%tlo^6)?WBZrd(W7aM9>A?<5Hk0^ZIoumNc<#na1ygKJ2;s_|+IZg>W}r;J9v0$K$uh +P0?e3Y)BvnJk`zHo`fKF8(aI7{}gH2=de^rJn`b<>*>bmUd5mrYA!3NMmDo$zbR8A+y6WHWqXNwWEu| +FNFHY;uuhO7u=j>&?!+`bRFzrVH(0tfxLT0(V +W2~;8>u?2bR^y2RB<}SUsxwI`{^u@_gQ*v^j;rj?6nYRy;F6~pVz0@8+alEMI1VP=7f@SS7rDI_itzDenAZ`WxnVk()$A4&@I~r*Ba`D0KJNYFEv +W3&=^DX3&%cq6aiD;?ZrXM9!`q*h>D|TK@8)FS;Nk9Sc@eMS&F2#mFrz__ECk93h*{YRH764P(4g%@k +Rlm|b2AO=A2!zWiiC#84}^cSg*EPVYkXw2ZdGw>Jn+t9JT#fTad|Z7vS+7NwV3tschAtni8%s-WnenA)53W2KTt~p1QY-O00;m`Rt8fJ;{v+Z3jhEgC;$K +>0001RX>c!Jc4cm4Z*nhna%^mAVlyvwbZKlabZKp6Z*_DoaCy~QTaVkg6@Cve|ACmnAU2?ArkkeQsyo +G|Nj43T-2}*F`)Jb=ZL^U@tw@dIKJ>TuoWqMqS)NRGTcGtrVp-(jxqRo!S$As37K?Rn`c5nsOzv9M8O +B#y)x8l5`+WB3?2&t8~;`A8(N>TAkr)30sg@-_;=N)3Zm5mrgA=Os)PQN;8||ak%EA7DO-KsYV& +q$dbu5<9A%vd{vA5>`~cw9q_ckRX|`PfMwIo@G)cOVcU6T>EJ((dRcR=y`(dH^1L{E#Ncy&e(qkdQZ; +M2={t@J%5ftuaBnp$dBav@Sx`&GKxQ7yyjC4NwMx~6-sPmrGxivNEUvD^X +TEDeyQSJ)J)V2!-gT@M-4nNm&GuVaZkf6hT_-D{$-J@T_>8j8A1?f_;V<(lyYUM9-SvEa(oRw%a0riD +7{Ky|@w&Fm@{aF>5uHX{FPA^5Ml6>P=e|*k)7C>`&V2awH`!zmc{tL68Yvbe2*_Y~U4(NW07+1T+N}V +n)>@7?#+!;b^JF0mG@t=s^Ut&{emBtDOVAsrRAVGlw2+*z7Hfm3U=&@FbBqzY*5vF>gTD`?#2vV>4In +FJw%{~=i&rSJ$gN0>cVY-E86zh{76*C{*oA!QEyOxtG(;>X5pN+1iz<(7%Up~iXrKYZ%fcL5;gpSf%h +RzHlSYEQt3G!yywzK6-P%R0zD(;za?XBr&i!q2e!nwn;M=1!DFMKOq8JV_AtEt&f$E!$*p7Ms@q(Z +_WRXZ*5c|9*H2f!ub%zs>9^1Rc=cAUt^%2h`q|TGe_1Z_fgY;fN{09Z=a&%18)-z~1NAs)(JN1n4D0R +n0MjA{e}i3D!t8};@I-jm8+jL5f?QBR{N*-JuN~@L>^#s)4gSAo=aD_hNcR +~N!aURrq9|hThs=6lYhD`F*;K;UYjTXO@Zlx3FxtlzIH=7|K*1P$YQz3^d;2~ZFbOq3z+M;bGxnYo^H +{T&KYhUEsz$G_6ohbK6AC7A2%#6X9zxtePc_z>R*UQ%Jaj=A3c9gGB#lqNn6?8~k!ypo%muj&v+%fn0405M?N?YACS`VV14V2`MFE_udO%mKL|SS1r$}=ePX+bwWCL9oUF;GWyFnp2Hs=^Eu*hpeUS_eoBMV?AJZW9og{#7VK@GFwAFg%$*uGn_R{~y=W4?{6G;% +K3ackHh0{|kx^wjNOC5TE%g(6^zgUf4cJp&~$!5bof)!*cOE6z78pz#?MV78e6g&BjR5J0x$~(LWtozSy!vdEH@EVGQDFr8LWd1Afc0dj^t7AW{YQKY9ihXFw3C*snW5Y>KgaQI< +j@AM%qIwoYLmJN7&Q;M0x?nzr;NE8JjbXbUQqB3qc3j^?C2G*^XmJ +#)Om`UZNjt3|(Kv-tM8X`Qz)ebc?13kDk35rIe@Qb=JN~OPb+GhE85gdzU{C+wj +6UVU)7b{Q;)QhtN;7^`HcBy&DGj=4Bldo5XB%p0Tf~N!hctgAx#l>#jAr@JwW8haz)fRL@ypd{2w<{0xsE<#4`rJdLJ9ARC-;OXn +WDU)e9e*DzY(y*!Qg!;l3J38|_#wjJ#yuP-z5=9+*AQbYa*DbA#7vNFFJ=#jLm4n!AR9*UE`cVGHgIA +9@Xb&23R0_r*D-{4@R91LyBUrgNP>DgGY9yK(=adJjz-mb_PRRvRuqM#Ipc@B8KXmD;~xf5%p{d2G;`=!CoyqNSsTj+eVukk?u{hj30jK +L3IW*%M7q#_UB#7&76Z+Fs-Y1jd!o1z6hn;Sb$sIeg|P0puZ*R_(SIh^lqh6AWc~f~}+znZnoyT#y)o +x>a|*SoNLlO+%dVBhS>bwuE%EtLSPlKe`hvFxx^uJvdkbK?&yS{{L39+eB(HqAK83jQ*6bJ2tO|>cdIyM%-23*(;!LIyhS5IiCPLbim9 +nAC@|?2%jCS}w6nf)MBGr%k6!X)N2LJ#UjhXvfO)~)7(2xW`h8-OuZG4~z+$32c|nn9GmHXLc1K{Q37 +f~x$@<*)HVJgtll#E(y^H;aD7!e^_}+OMHUy8*>0+$hZ)p_gZad>kkMWEgKU83Zt1#9oE%7d5sMtcD9 +u7*=@vISd!?oHxw2 +o@fOACBzSIp7!P$*>jBcUV*>D^60K+|9EZD+e;Dqj6J44vH~vpnMwg&D9ylROhz6_VvH_|xzN)3(;-x +c`>kvDO<-Z;hNtfOUBfHbof7)Swjy?W_$^N4*^mSPQls1{FQq+4L9)!yQnC1XJWG}Fwt?cf}2%~_W0> +hUK=^AXdj@M1O=!t!HHvMJ#$-4XLk4G^VcnOBLFH?v-)>;I*JSFs1Ns}NK$G!BgETY#t{&{wt4FSoj( +&e|wl4qVpH0SZ$qCN`8Uawzas_AIz*nB9930L~wj9@kMZ%|7E1QY-O00;m`Rt8hFJ!Y}M5&!^?H~;`2 +0001RX>c!Jc4cm4Z*nhna%^mAVlyvwbZKlab#iPjaCxmedtci~lK^L5KIB@1ADeosqoD{%wZ~WQ2

    6iw-r%Awf3O@)}h~+pv^Bl;mqe +raL0W{`U3#O>Lio#)-Nx2J8TyFdB`0SA$5AaTM#rK+2eTDn=e4-Xnnev^Q0#cot{mj{<;l^T)5AV{z=y-ri-Y51c +u8w=boTqUdvElIr>-)8BecSq8J_C>U{eMbW1IIyophJASjcH&lp!99Z@4t(`58s0M +-S&6OuqSAjnF)h?l317FyFDcb2<4{v2sg$z1HcT26@W&VZf(MVV)Pwg@4O`N#BPMGBxbzA9{KE=Z2Dr +C8IHj$h6Du;O1z44S*c@v+x@<3;yWu=?n$c?I$I*_G=urTRHsvLZ_ +s0*jmm(C%eCl-Bvrh%_ZzMe~jI?jqc|6Wh^(_S#B+1DK%=)&qrBq=`7WYR$kl7d6l0>d2LsDweZ;a5g +stUl*LZVbSfNn+c_*HxT_OHN)&l6PX_(-{^0liVc|p$UcWi)zkT=q=x_gZ{Nd#E?C*o~i_72t@y~yK^ +hRT!Pj08dzdwhwD4ze3q*;FV<$m#Xf4W}(Z-X4uuI5b)w#JMH!~S<1MCR2!TV=vc +fJ0&T5WJY?35FH|qp$pUdeV1~4mCQfn>!Dy7CJhlSQ8(xh7YyCV67=(4h|0oAB$;T6M;g?HQ_#SOlOM +2*8cSnXbk%A+>--{FS@t=bbWO_Ty@ajHyHm6NEZi#cm1~T^~vnXkWh^T7y!WkgZnStdmY>OBt_p=vnF +Vo%Tn0gCah4%BN(N|6yEE>!JGbZ|NPwjlN7Eto%PLOK}YGhfois{!1Yk-!+n2xe&L>-4ld*}05fwneW +3rNDj`yn{&5FU6t%$AWGL5HP;Moq#PU&-S;Sb4WnJ2;G~Fek+Ny1|H}4Mym}#XvlY;%9_Z;mw%z!Rmy +NCVbqYo7(#!r9wYsa*HUhDqn9wj><$kEB$(*md=+ykpqdVp~57!a{xqVG$@DkTJZg{fl?A^(2aAN;ET +eLFaK_W@HmHJ}kfynRH$|BXX0g*-M1%`$yA4R{8^Mc!>$j#Ql?UEZOWLAskI`4}q)@+%3SCy=2=SrFZ +->=_een_Z@mqIxML$O#AVh~0T%PDp~7Gs&Z|b2G<+zy+o^HhUdI3JwXaN~yibXH!asfm@NT6IFm(;IN +=ILd3C`^7SABVbUOj1etl`ah&)l7$oI`w{IBcyS6qb@zZtFFwRmaguv7-H3}91ZzBH1b)^O2XB?*8@= +1ufhsnD(iUmr23};av!@{L8<~4Dtps5e4;zl1=>xG|SLbOPQQJm3(on~H=rC7O`U_Lsy;*AlYhwcF%D +%9kX1Sye2t~GQCu%`;AFot3U^{D`)8KgN>A12sZ7qw~GO(iQuOG;hRXqR-@C9QQyyspqpx6nmNp;*ON +^CSj#*`i!jB!gCOraYYJIslV+3l=?sa`cOr_~|;jAc&TuAPlmFs1LzT$zW}$COc@9!I-l!_{_l|z059 +liauXpVC++b~$6lz)VwV<(lX_3 ++zSjIkDMv0-5l9L6q8^$9BR@kLEyDUPuU`KfPUcq*2mTO +zd>=MO9v*g)G9%7-^`bjR@^1At&6;Cwtp($#e_1#P1Pq{*h!I1hcO+`*z?l|l~AIf?VgUx#I#3(J%4( +$@Xu#gx})0NxSav6;7^#T;?Q@4ayrE`(q8(5sU7B(NZw=UE0gSzHooa_X{Cp0OZPf+E#F@&Ft<>^e3Q +w9&zu+`hFbgrp@saj?Qhp%;CoMTC(sWK$1Zv;$(#;ydnJwyvK3y*HcVA%^4V!}$5_x8K+$*mWVD*};< +zJm`qy+mI56-pmWaUdL*|v}qyq;ic#;*os@H%Q|_=6Z9j_9AYULuaARhI!FS3zuf3nDcH8nIz8Nq2uV +8#e<<(Ov6jo$;Ppx|a$ar_$$g@?QV?5Wnt#%xO8=qzln};pqvA&BXbnVUhUP*H_Jzx%wkd4m6l3tzo6 +mV941&+*netmHGr{aPSZz=KNYp`r)>O;^@01&$uBwl~xjs#Spb-C66`S18li0##XlP{TPom`pRYRZ!v +pIL+?4Kgh2-HtIbIF>#xpd?jezEpHKwTG|Y6=q +4W&kUSGHABLO`a8e-%LP9N=CZR)Ji|4gnDUn)q0CWoC<_FlGtrFkRF +JdX!odg>*4~9&IDxGJZVM4LTu7-$t6a`rzo +E73zcL1{@pn&N-dZmnNQbN-=fFz6St~gZ8zGHV(A~S{!i=ci085)p-@&de6a2p}z0Gv!f?wF2+CSe}u +X~kp}C$LV#HkJnAlJ!Bfpo&8{H9VJzKpYzqlCpZGI>e{Cmt<5EENm^*IRAv48>|J2Ehw21*3tzz!c~E +p;kl$f4IvCL5T3e=-%_-BJSTZVPcJ7{KptK_;35Hq2<>tKB~yz^N0ZV*#3`W2z#!=>l2&OPAhD>;;zU +bLg_Ba`s5FSy39g8)ZAXkm+{BDmQU{{q*VV>QcMVc(JibjbbgG8&L*j#-0ZI6VA0jlPIz8WKm>bcFiY +DcXssnJ30F1}71ZsU8>8OHV%>PYi@24N2!-WWH!2g`clrDve_d@k!**~Uru9nvaY`?iQ7S=2>}=2LB~-W3yIP`0Y4)T#*rc&- +Tm&~KY`kIYdc-)t@j+(1;GH_9$K8NI)5)!D9?tv} +INNggS4QzCovthklwvh&qbqy$jt^)K+-J&NB-o<4=A}Q=xQtYaK_|G_D=ca~^zj`OLfv9BtZtKPtrMo{vwgNw%ShMO)T(T# +OEF=b?Oh}}#|MAQO@Mbw16D&HMCa)6TGsV}V^|@?PF#(E69QM3$(ICi*4lR=oL@PLp`dd3-2ES8W+K`)6@)z}Ds&=+ywYS +7U36f=#TVg35?MU*7TkD)##r}Hi>{QB9^ue@~Q>l0XQL4CXQNVwVQ%iL+nPVrks1OOqXAX7fog4cy32 +(GJwl;eWOR&x-6LG#QRSTIjBorc_727I^u#^dTCT_Rd>}|k9UrcPC#CHK^zto3;bQotUi+^fN5s3u#p +t|N%k%xgt$0`bvE5IvOp0axcnwRmryK5+eWJt*uR$r}TziA2`|Iwex4tKA9^mjOD +GP#610)>8f@4d>;?60U;kGM+Cty4v-F$GP<;alAkBlBXHTnKULPj-SQV20VnW%uheFLxaUN*UO=PnbB ++84+SW)+BD9=f4Q<>rTS`;UU7+8<(b<4#E-x-dUO*)>tR%5i{49%lOS6xZ=#8-&(hT)vznj7PgxSk#( +mNHQpdd2ixtaK_13)pNAGG>P>YGxutoFG6)Wsg=CD@++X=QY0^I(l@yf88xDpzt)Qn|iPZB-2elJW*= +ZI;W5;e)?)fY{vEjgeRr#(`Lc~=#d)H^D2ae?talbAKH`}h +?AJX33g5dsJ6k)gi83ZMt(gm0vM!daHXp5SYtyJ&JzevFJ=A_P>4}54cAP(xI4qTkc63ds|7Di3l!tN +`ja0M;(NO(j5WBL2=+kX*l=BQ_(;c|7wVGBy1S58_?)YdGB)N=?QA5O +q89XF<`^1al|C+W=!{*?J;S6)wwLi03r3O0*VeWgc`SLpZ6$d*#t!{I&HwI9mZjHP>K8e6 +5FD`Tw>otII%=q2@ud4T+L%|PO3tfs-&2R65v%R?rw*kC6`)q^L0KYPaa$pBSlpeUOS{dBxeTp=dw#v +6I)CT|nwG{vWAOHXWaA| +NaUv_0~WN&gWb#iQMX<{=kb#!TLFLz;SbS`jt)mT|?8%Gj;7oh)8OFfA6QnWTkuz=UxzyTb@*hDgH1% +3z&qB&DTcG%NB?(WfuKz@9x`j{Egl;XT)30pk6>g&3Xk#%FN2eek3^D`O0IaFBq;p^U=pHP_?@7# +wZ8rs1epnq0Z0F&%E8gJU;_^1yV0-%v80}#v3h*^D}%Xu<`IV9K5mC*vFW{XW_=o=Vz;xP-?Y;CHy+M +F(1YLj{ZphgRvh>vBQWsnZu-`Ussi}D~qB$_@T4Xgou3s-I0ug+P2U7@rO~q_W<&Im^#Xo09k{J +&RS6veUjRmf3+(PYqF|!CEn3op{`<~=-#6&MRPg%UTL*j7LLoG?4uM1q5K?bOC@;Zp=}hQi&tSDF(bA +z89;;SG6eV{E*chdr?>@KK;v9r71EWzyAITMxqCGu>MK3>R#s!Oc1j{1VhW=nx$wH#`&}~r%p||3mbO +L2OD-*z?yxisYfov +egARvYf&s2nhNYm>G<3D2=}KJ==kdQSmvk2BRIBi|rr^-%{WuAB=6XfDa~mKLWE3(Xi7QkW1OR)7c^g +!;R_Yu%)mm15>dzhUnMmJ48M3vAmJ-JGj*>XDK-dg@RwSd_XrJ{72+l$`aC_$n=IlNl*@=!svSvkl08 +oGDj_T6nbPWr&tP-trAiN^C)jNNUGa;2*a)1Y_D`%uPJg%s3cjnL|0hZcw=3RmBvU?m~0p21Q}dk$p; +4H`+_;??j$g)+gPmd*d(OiN2yEG5seSILQvXd+$qUJ!YdTc!@IrT5@c!6u`?TM?q$h=*jdpuXc>yCF&amQ5lWQgTcmzcDI@%JLPnX)2BMKvCwThSMArXjzKWH}wB44I=gz +jrAVaepzfRd^d`^b>5c*Px@)z)G3C*6X;VXEt_%_G$0(+8lx`b~Q|2~9DeR!Gx4|!+YcaKGm0$k!k*p +2)3`qL%FAJuazmRw>|2GE^r?8GthmyqP69uoesjFZUX&D)#1_a_7hRua5i#qpmqm_vzu=!WksD8d72`Kpk@{#7$ +L*uDXq^_(k_k`)YhkQ2BiF0r!b;!&m(8c0|0f%sFbLCIO>$tT9OefO^!=6KAZKYuZSr5rtb`*MEvgvmraUQ~rPEMlIZKC@^2{$j8wHVWPXy63ZeeqH_H5Vjr!032g2(sEiO%!f6@G +5zy->VoiF=yNxnxX6*RFUtU2&hKq>JVmhPKxYWgfB8dKAo_>r`6QMvjaF9gqHwON%qSJjGR%4A5tG++ +-x>E=nHDp=>b>pLr-Yx{{mL#_rGMB#vs-Rd={=gfF-@WVo>0Q(dtljFE9;^WQ~$~^D9_B6@TYJh3%NrTFX2{ojDVm=%#5&@^@;GVAxhzqXXB`UFx+aH%{ni7jGKlV^q +V;-oI2e^Ew_z*x^m!67rcxn40%mX==p(_;OSp@BtGAzKO2Pt_SA`j7)QPYAK7sAvc%;2h$iW9E~oNgR +ST%0ys{5#3dIVc#8e0&gGqVi}nR1h;sK@!HFH_EK3a~_$iuT)P&IlqqBxZ|8C>Dg<9N4v2gmrrvin#2 +#2r$J9oQKMjB_1-Dr>9D?v1zi>|ISo;7;u`ok`l$kPP?7#DmQF++w}VuQPc^K`WWv=ac7CP}@6aWAK2mnY{22)Z%hR8(=005~f001Na003}la4%nWWo~3|axZsfVr6b)Z)9n1 +XLB!KUukY>bYEXCaCz-o{chVv68}Fy-eE6BK?#)@C%N`qA`W0XN@JYk1d-F?E)J*_xw2SOOM$zztSb` +aH4eCYxjf0u><790kgX&=peQONvCN&Bot^p3#}2bYqAX7MML-HU8}8k`bMM~0yLay1A@EtTDdT!s=qD +eXkEJ@BCxXBqN?xtyJmVx;EK{~%TvD09ui1>qIU|#wpC6q4{396|i9|@sW17cW%Hz3|%i#WfyP+?ZEb +>=#DpwbNmS5a|GV#fnG4gtJ@btxq%(9%sOwuG3zSSg%vgJBYF6J^INA@duxclHCnXbt=S%5ZUx=gqsP +m_po!2)tPemWomgr6KAjZVjH>BcCtJX?@3oE5UjSs3C;vs{vbC!jP7(?qhIrUFUL5+$i+Q?gIIUH?1(fu|quka4zPgwq8BHeQ*J=I}I=p +nRc9~~c)lu0nYATlG0lSC9fdocVbViSz9WHBz%;lC=pENM;`MDGE#oV?MVG>?2kmNW1-7eGNcTeXSma +Zm`@2>~?GnxP`Y1aQVhB9kk&CEjnCzDVMSWp?w5eN`pRtVAX@~uLkBjBbY#VAchrc@jd3h@B! +0FoS#fh?PevuF>_3sc6STJ#a$5MleEu+NAY{qiP2}H#KCWK(Y)TN(0RXc)D +PLRTw`{o;vL2WqofqH$ik0Pcty1a?W#k>*FCx +6**3frF7?z%~FElK4y{Ia7xrEpHNLSm>)8JSTb6*?%lSv>R4_PoAT+=n#iJ +g$O0&9cj4*;Bx^VP0x=0*eZbuziVfNacwX8tQ%>W$09lqfa0IEy(18NtbckxQj9#v85bZiXq4WNCW6D +vOJOVh1qmmEHQ~gu$!cg897Y^KEaq`%{-&XFo|?$m&&)0h=M2u?*(ykY3BZYXF2@)KUHDW#EP=33RP|7@khv07lC;IyjUYatxIrS!hX +BKbv)FT}R2EbP`#MCL(9jUKtcMAoWfc!VfHg!cRaFkn(cLOsDm@^M>h`k(5Lnl^RgT3tlms|cnL~&XG +0%!L#!yv#fM1tWsR`TbR>U^}=LmN;cWi5sXSA$DIpoUzZ;{{~aMT0M_^o`np) +5r!jSi7HfXCeR^)5mAS|i8o6Z+!RpAO%0wmRsGG}B`r;$?FvZ1t|KUE@Gqzg&9CrJl(|QFp5@yHD?oH +k1(h;yTW0{>Q|Sq>-;R?OhZXO*)UnR>dFpLQK)%U6@=aT#{tW<8cCN6et?WSJHGm +#^A&J+Gvffu4ivqg?LEfA_8}6AdKm|<%W``gtdoxvz6oxA9(i6T<(E7==kSqvPfV%C$5rv*3iD;@biM +ijn=jagPW{gS{wrFsK0%UdHnyl18#X|w7ahgm^P<}9xeS|-bVTT4f@q;&~U{-KIr=iZ-1=gy3(gvW4Q +qVP#Vb~Kao#`Z1O9Boekis`PPTQ)s(oRA(Gw987w#?0D9ia9em)D+e#1jNnxu>}$1%968?6mG)4Y=o{CY%a=l9?4z8iFP4K0P-qM)|ubsVKXAS-Abz-()T8)&o0t}qqYTd1P)-P)_4 +2uX7(kheGW&aa<6gTJG$f@_n5+3@Yd;N2~vMksExsKf0=jX^+1Om&tUrt +;_q80Goea^$_xf<8nx}kFp+U?&U0B{OsAUjp<_pF_VVNO&=!9ZeF4&+n+q6q8XVSt3xz&EH3Q8TNm4$Rft`nBVopvm*SP(geN;?fMXT>EK>MmCIRo8Vxt?!q)MgKIV&om@>pjsz_7=%r +DbcwG;n)%0QVP*C-r8ByK@X|Dmk&T(DT2oG$(`-3x3f)`yt=Gfk^QR9>C +?ymV1@nk@xP|VYH1s4Y_qhs!^>g$<2F*FWr)rmz2pIcj=gZ?oPxm@%X=~2sM247Gk+Grs{Ie&!J$7!N@qNbK;EN*)KWcUte)+_rlA+Z@XsR#G +Zp-nFOMqfpHXO3Tp$3Z%w!U!Vy3yY<7+*&a4&o}3@QB;+yXr?!E`%w?QYy*UO~A?&$6=(}6n4!MT;Z|!WyQf)utGYa1D3dm5Tj2lMsm^@PRO+1QrnnjlZa7-m_K)!~}D +pnhMH>}|XvN{$v&3Z4ye4Eb!{f4~L<+rYBWm1(b>k~H%^=sFb>R^@777UtE+1BVb40M!K+n;P(Qa3c4 +f3VI`OY?p6|BHg4G3<0x+YRYz!`RZH{9UE2n@5cuEx>j+mquJ|m{oS+oZTK<%=XufuKIsaO9KQH0000 +807zB_Q+e`6jSmI@0Qw^U03rYY0B~t=FJE?LZe(wAFLz~PWo~0{WNB_^b1!URVr6V|E^v8`n!$3TMi7 +SYTea^naSpodxHKRnq^caUyFR4K$;CPBjY4QFjfFr3D3+5-o;`q&n3?_u;XSO?d}{vb?irdcKRi5q{` +C11tAF0*T4qVUQQ5Y9Oyjjo%gcrT=+E^#zsE;*{Sv!;hgCVNc2X5Aix2YgO0JUp_))0su3E8g*I$`86 +&_oEuord6Gxic6Ro3CDN@P~b75n<~{R=ClWY52Sd-~($iM{3p+sG&z&mSt4bxS|Bp{qeuyiT#A*7$wlb4NT**x%;IO}_e>{FP&Ypk$`tZ-X2$H%SB7IlneHza(Iy +dCl>*LI9_vOnkJ@{sezG<4~- +o(PR?F5&J#q#8rEdDj&qQYMGE-bK!upVPe0aXb;pwI$sl)qS$5C2uj}?o{RwNuKMFj7G1$Bk?m1<*gpxJc9b3wAXaAcE^i~DQ8OsQ$?}v%@)TKNMdW +Q4hj_n-ph9+XUC4KP7x$!o9jz#&ISTRG5s7zkNV4gV@kwt!L^>-uq&XUM_m0Hpd{6$R%2S+U0n_NsOlG4bFkuiyspK{2Cx=&n_JOH +ARK1y%*r^Lf~##(AHHr8YqeiSC=bLQ@F$eK(8l)5hx`GQQ_+L1Zd6f3rLrD$^|XUv0?P5^gdT%VeiAn +$cr0Qe?-#;N}<68ptyZQNJXP4e!|pATjL>4R95BXP7gEwp;TXm_2jk8M7@ZhBOGIL0}ES)*# +#(dN$5V6`lRZ85hg>Z9nO3Kl6#pMA4mz*;WCaBsthn(BsCz-i}*Xjt1Kd3#nrVdnX{?Vl_*jE)Yp`dGkZnmiZPHlOS +_F<-2gmf%RS$I9Hw#Cx)a@|4bjJZq^2X`y*rtA$pnkP!UU08|t;rwZF>4rMJfY@A+$BvZ{H5KY2ob +ov28Z#)4qsRrfyB2i%xbblcls4IN7fE+L5zed%m!bQxmE8V-Q}r@cIZe5afd(5nvF)L-R3fB6R9zA>C +nTMc+Wq9QmE27|>>1;Pn*o#hbov3Z6Og3BWeZd-9Urq-I}eR9%?Cd@8h-l2YAAR16w*Ye>vyz@qkf +A&nPXOqEd*HM+PiY>-;Isdj~TB2iNj#cb-Dr=_t-8r81gjiB{aYz6IVyMs1=%pvA$>*VjvCPyNCF{d_ +{Vt*QU=BH8?d?R1$2hT=(m>K4p{`d2z{{c`-0|XQR000O8NLB_@sY-ZDN&)}?R0RM4Bme*aaA|NaUv_ +0~WN&gWcV%K_Zewp`X>Mn8FKugVVPa)$b1rasy;H%C+At8kuf#u0sgRwDhIWrg9J;$4R-!$W9*|fRnP +dQO9ow~y09D;@uN}vvh*f*5BPEXg=8fk)Pru*q-Slq2j{~kzma5`%YxDZMU~3dMZoRxVQruVEqGX0S1 +h>OWnn(h*s`di&6frK9J{i8v=<*!!^@UTb9>JX_@{oH>mn(Z;A6_C^T4h8E)gltz!jV~N_qW3xOpMw#oN+~AKrT6Yy3o(ea>tVQD +rp$=%Mb;fIuwKQ=H;v!5Y?JSSjEP@QWy^-x!N9*;)pfB|a6Q5?rS(w0*5b|^`{L06FxR_!-GYu&STGN +~gesTkn)&J4Ex3E|&;6Z^@4zS|xO<4Q3;}$ktAowraJ5@8sOD4qWtWFgUknU}+Pl0Uq>25lkdzpUrZo +E*dc&N5OFJAl)gBsL^UKrTGvMd+D4^7FQWWz21nt&wQk_45=>H?j0=um9w54lz51Y9;((|gYW=qbmh|W%VqC~nRXVX)e3EtEZu-##!0%X-9Yj!LIO0S5YQ9o;I@4|=8EeIqrH2dQg +=6G}{E81lx;We=1f%Wg-kQaY-9g7W;{BpeO= +YQG_leMNu;@i-M%S@~!9Sx#P&rIXxbYl61M%SmuWvbBArN%y!hZTClXS>NikJ0|XQR000O8NLB_@LI9 +vP#RUKWJQ@H1ApigXaA|NaUv_0~WN&gWcV%K_Zewp`X>Mn8FLY&dbaO6nd9_!|Zrer_-5c;f3=#yS0! +ffuB(Vf|VZ{<)KyeDlSy=`#q(=5MG${;+w!G=0{gG}96lnL|rTvnAN$mN;yHxn4z&|=^D9V5U(B? +y}9K>^wHd+qh9|A`HyHu@8Sr}@QOxSJfs1M5;8=i*;yMUgrMp9$MMB%Ea?}7V;UxsU7NpT@g4HKWy(` +VJP*;kRm?a_BMO!fF9gdN4wFo@Yb4w-j=!d>HkzJ}#?x6F$OWQ}q$l7ike*qT5BiNV)%u)A0V8)L;y6 +5$@~myINvDXlFPD?!maydTU_q7$CkbKPW0)o+amhL$D^BC6jh1N?sJpkA-6U{%`}INGwT4PJXOF|&3M +1~8rIm;xMj5|_WCXmXT~9*ccJvyp7{0l~LlngjG53U7;=)pND$NF>F=H`voin-sS+3u{NxBYtlWVi~D +`Le9u@9PDvlHb5q|U;1WN13AZiMd$Iypglp;q?GF0z-Ct!3fWcM?Tg%Az@&M*=YC+p>chh$iSFj)*wJ +A0PQGWR`Sxx7pL*ek;P@+BuzlJU>Sdw<4vJ6%I(2%VnR=l&o#y1C18(L(=IVy*sM6lqPZK`1swR)87p +jVoswSG1Jq_^NW4-2;+=jnwbCn>!M%up7cg1j>1P2U`PcDg)B-ar}vaUc1wAH!?2tSqQc1~K)~SFOx$*{XtHSvU8uq_y4CC6|%m}*2A(U_xjEKjv6^=4-AQlqBT@{gCkWQgpwOZBK)Bz|! +dCrMzDfVe3RjT+ewF1))r;dslD&)3I2;WSf>Rq6Tfmp}6Alf7#_Nl}sBiz+{V)rl2}JlJB3-pn9pvuE +HPoCK6+Cj9!%CeiO2S?XiD$XH{P$Z0v*pa;rt(dhH1QmeL9Ovdm9`d)9oCY$Ly{r5hxRP^6>Mq^7&gO +Z6-L9#7CJvW%I^zPnO6Q}U*45YP +1-L==-Y(Z6tIynk2XoDgUIkV1)NqSBeN#p)}9dew0IVCO6LLz=usQ24B+d}*M0nXzFrJoLp6h^-V(0h +5Z~u;0}uEZuGleCi{YqHq{|P21TLCJ+zrHkK_wpK63~fzLuea?YqVr*#lcp?8j{h~1o>=R@nGFlm!WD +&waUYT{{c`-0|XQR000O8NLB_@#d)xMn8FL+ +;db7gX0WMyV)Ze?UHaCxPdTX)+;6h_~(R{q1_5NuP&SWc5zrUuenAW%xE3kBLDMl)w(fh-$ICJC4Rc6 +2-A`D*;og`W1A>)B^UT1$IEEp(LJjKwN92czAcR;#tUv%4dT=axcB>h7$67pyW$15GAMNY?}2Y=3x-( +?mq`WlVEQa+ybqL@bQRXC&?p%JmMK^#pI!=ZRyrSl*}6%`6{_Ge$8Scta81i)l13mYT|Clr+QRNKul} +SUjGd_e4e{o?M+>Je^($vq*)eTt;zLt=<@#bTJn}U{?7mr64HNT%@^JB~g(H1#y&9D&wpi+H{res%qH +X+1uMI(kfzcdVN*3saR%N-PWl`_N$;fW~IWl_M}cmvFV8@S+4S)pfp{i*`%H2X{7RYIYBf}N=0@_vvX +g~WIC72pjiZ__iPD+T!t}q-Cc{>|9{tJk+*iOT5(sd#Z{ux`rzY>D&v$3v)RMjZ>v#pOJqE`EDnU;wL +axK{Vhd$Ie5J#<8#`Uans{w{9pBbr;M?-knvjuekY +jb*e|Lw;?%olnUQ~4@XOP31-QDPQiBHUNus%YxeDb4yF3{Ty2X}wXNps3s@{HeHyE_n>^V +Gi_|V~p4j(yu?C^=hBg?s^#OV(mR+dwzl^ix}_MFT;Cv(rq+;cMboXkBZbI-}#b29fv4*L#Ehn?&_<* +;_x$=|d23x@X?S`2#(?=$Q(e9%Cv-D+Ao+|_0Hkl_JCkKur!&+rk0EvBucEu*cWEugKQEuF2KEt;*^n +BilFM+_5&PZ&OB_>AF{;W5J*!#TqR!zIJ#3|}x@F?`AJ6~hyTuNj^)e8X_f@GZkLhAG2yhVK}@XL!Nz +1H+FDKQX+t_v97RYlfd00)}50>~*wv(OyJ*3+)xO_wR;b#tEem? +m5;OB#%4}L!Q`QYb+pAUXM`1#=HgP#w6KKS|I=YyXQeiHm7_(|}S;3vUPf}aFG34Rj%B=|}2li(-8Pl +BHWKM8&z_=VsXf?o)JA^3&h7lL01ej)gU;1_~l2!0{>h2R&0UkH8*{1o^p@KfNYz)yjn0zU?y-E^v8JO928D02BZK00;m`Rt8htjO)#E0ssKb1ONaJ00000000000002Cfh_?50B~t=FJ +E76VQFq(UoLQYP)h*<6aW+e000O8NLB_@0yoD{#Q*>RA^`vZ3;+NC0000000000wt>t8003}la4&FqE +_8WtWn@rG0Rj{N6aWAK2mnY{22%jagHc-n002+|000>P0000000000006duy959LaA|NaUukZ1WpZv| +Y%gD5X>MtBUtcb8c~DCM0u%rg0000807zB_Qyla~{9p_K0Iw(j02%-Q00000000000Jecm1^@tXX>c! +JX>N37a&BR4FJg6RY-C?$Zgwtkc~DCM0u%rg0000807zB_Qz+I#U0MqO00k%j02TlM00000000000Je +eU5&!^jX>c!JX>N37a&BR4FJob2Xk{*Nc~DCM0u%rg0000807zB_Q=ZdihZYb305CTI03HAU0000000 +0000Jed19smGvX>c!JX>N37a&BR4FJo_RW@%@2a$$67Z*DGdc~DCM0u%rg0000807zB_Q#!PI#WfHB0 +RJ=q02=@R00000000000Jed}E&u>c!JX>N37a&BR4FJ*XRWpH$9Z*FrgaCuNm0Rj{N6aWAK2mnY{ +22;P&Z;SN+003+O000#L0000000000006duGd}KlXc~DCM0u%rg0000 +807zB_Q<<9bBQXg808ShL02%-Q00000000000Jec!KmY)6X>c!JX>N37a&BR4FK~Hqa&Ky7V{|TXc~D +CM0u%rg0000807zB_Qy_77@&E|{01p}f03-ka00000000000Jed|NdN$FX>c!JX>N37a&BR4FLPyVW? +yf0bYx+4Wn^DtXk}w-E^v8JO928D02BZK00;m`Rt8f%eSwlQ3;+N`F8}}@00000000000002Cfd*3m0 +B~t=FJEbHbY*gGVQepVXk}$=Ut)D>Y-D9}E^v8JO928D02BZK00;m`Rt8fLM#&m&0001p0000T00000 +000000002CfpuR10B~t=FJEbHbY*gGVQepBY-ulFUukY>bYEXCaCuNm0Rj{N6aWAK2mnY{22-;6EjQ^ +0005#H001KZ0000000000006du9bf<|s9smF#00000000000002Cfk$Wn0B~t=FJEbHbY*gGVQepBY-ulIVR +L0)V{dJ3VQyqDaCuNm0Rj{N6aWAK2mnY{22-wOBb90%003-q0018V0000000000006duVR8TfaA|NaU +ukZ1WpZv|Y%gPMX)j}KWN&bEX>V?GE^v8JO928D02BZK00;m`Rt8gxBrwS`0RR9K0{{Rd0000000000 +0002Cfe4WR0B~t=FJEbHbY*gGVQepBY-ulJZ*6U1Ze(9$Z*FvDcyumsc~DCM0u%rg0000807zB_Q!wL +7pqm5$0PYC@02u%P00000000000JedAk^lg3X>c!JX>N37a&BR4FJo+JFKuCIZZ2?nP)h*<6aW+e000 +O8NLB_@(CXWN76bqQjS2t&AOHXW0000000000wt-HU003}la4%nJZggdGZeeUMV{Bc!JX>N37a&BR4FJo+JFLQ8dZf<3Ab1rasP)h*<6a +W+e000O8NLB_@pj-@HU;qFBdH?_bApigX0000000000wt?}?003}la4%nJZggdGZeeUMV{B?y-E^v8JO928D02BZK00;m`Rt8fE?fN-e2mk;b9RL6y000000000 +00002Cfs)by0B~t=FJEbHbY*gGVQepBZ*6U1Ze(*WV_{=xWiD`eP)h*<6aW+e000O8NLB_@lGm=-GXe +krQ3e129{>OV0000000000wt*_!003}la4%nJZggdGZeeUMV{dJ3VQyq|FJowBV{0yOc~DCM0u%rg00 +00807zB_Q$Ahdd*}lI0KE$U03-ka00000000000Jede-2eb^X>c!JX>N37a&BR4FJo_QZDDR?b1!3WZ +E$R5bZKvHE^v8JO928D02BZK00;m`Rt8h0VV`vG2><{LBme*>00000000000002CfyChe0B~t=FJEbH +bY*gGVQepBZ*6U1Ze(*WV{dL|X=inEVRUJ4ZZ2?nP)h*<6aW+e000O8NLB_@b+&m5y9fXP!x{hp9{>O +V0000000000wt@KT003}la4%nJZggdGZeeUMV{dJ3VQyq|FJxt6b!RScc~DCM0u%rg0000807zB_Qxa +O-e5(Zj0N@h<03rYY00000000000Jeef^Z)>GX>c!JX>N37a&BR4FJo_QZDDR?b1!6XcW!KNVPr0Fc~ +DCM0u%rg0000807zB_Qz$V>TfYMU05uB$03ZMW00000000000JeeK`TziMX>c!JX>N37a&BR4FJo_QZ +DDR?b1!CcWo3G0E^v8JO928D02BZK00;m`Rt8hD-HR{m0ssKF1^@sa00000000000002Cf!O{40B~t= +FJEbHbY*gGVQepBZ*6U1Ze(*WXkl|`E^v8JO928D02BZK00;m`Rt8g!SO_%p0RRA%1ONaY000000000 +00002CfdB#l0B~t=FJEbHbY*gGVQepBZ*6U1Ze(*WXk~10E^v8JO928D02BZK00;m`Rt8f$EO;R51po +jz6951o00000000000002CfiDCB0B~t=FJEbHbY*gGVQepBZ*6U1Ze(*WX>Md?crI{xP)h*<6aW+e00 +0O8NLB_@K9;td1RDSVQ*HnNApigX0000000000wt-d(0RV7ma4%nJZggdGZeeUMV{dJ3VQyq|FKKRbb +YX04E^v8JO928D02BZK00;m`Rt8fCPxGfu3;+N+F8}}@00000000000002Cfte)%0B~t=FJEbHbY*gG +VQepBZ*6U1Ze(*WY-w|JE^v8JO928D02BZK00;m`Rt8f=TOmqc2LJ#|761Ss00000000000002Cfg&; +i0B~t=FJEbHbY*gGVQepBZ*6U1Ze(*Wb7f(2V`wgLc~DCM0u%rg0000807zB_Q^^?GN&X1{01zMm03H +AU00000000000Jed@IROB0X>c!JX>N37a&BR4FJo_QZDDR?b1!pfZ+9+mc~DCM0u%rg0000807zB_Q? +bp7Kcxf!0Q?OA03!eZ00000000000JeeoLjeGAX>c!JX>N37a&BR4FJo_QZDDR?b1!vnX>N0LVQg$Ja +CuNm0Rj{N6aWAK2mnY{22(vy9CjlI007+>0018V0000000000006du-$(%faA|NaUukZ1WpZv|Y%gPP +ZEaz0WOFZfXk}$=E^v8JO928D02BZK00;m`Rt8hNYU{N`0RRAR0{{Rg00000000000002Cfj>|I0B~t +=FJEbHbY*gGVQepCX>)XPX<~JBX>V?GFJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_@E6!b_8v+0T+yn +psBLDyZ0000000000wt>h|0RV7ma4%nJZggdGZeeUMWNCABa%p09bZKvHb1!0Hb7d}Yc~DCM0u%rg00 +00807zB_Q(_W8_(}l)0Gk2;04M+e00000000000Jec7Q~>~RX>c!JX>N37a&BR4FJx(RbaH88b#!TOZ +gVebZgX^DY;0v@E^v8JO928D02BZK00;m`Rt8hI5bZDj1ONba4*&op00000000000002CfwWZt0B~t= +FJEbHbY*gGVQepCX>)XPX<~JBX>V?GFLPvRb963nc~DCM0u%rg0000807zB_Q>-@D5Agv20Ko(R03-k +a00000000000JeekS^)rXX>c!JX>N37a&BR4FJx(RbaH88b#!TOZgVepXk}$=E^v8JO928D02BZK00; +m`Rt8g%)Rti)0000W0000V00000000000002Cfhb)80B~t=FJEbHbY*gGVQepHZe(S6FJE72ZfSI1Uo +LQYP)h*<6aW+e000O8NLB_@$_gbhC>8(!14jS=AOHXW0000000000wtMd?crRmbY;0v?bZ>GlaCuNm0Rj{N6aWAK2mnY{22;p8u$!170001y001Qb0000000000006du?Q;PD +aA|NaUukZ1WpZv|Y%ghUWMz0SaA9L>VP|DuW@&C@WpXZXc~DCM0u%rg0000807zB_QyT{9*8K$l0DT$ +&03HAU00000000000Jee1mH_~8X>c!JX>N37a&BR4FKKRMWq2=hZ*_8GWpgfYc~DCM0u%rg0000807z +B_Q`GLVv!)FI06;AO03!eZ00000000000JebvodE!FX>c!JX>N37a&BR4FKlmPVRUJ4ZgVeRUukY>bY +EXCaCuNm0Rj{N6aWAK2mnY{22&~baixTY;!Jfc~DCM0u%rg0000807zB_Q|5qZ7%d6_0LdQ!03`qb0000000000 +0Jed&vjG5bX>c!JX>N37a&BR4FKlmPVRUJ4ZgVeRb9r-PZ*FF3XD)DgP)h*<6aW+e000O8NLB_@uH!H +G8v_6UUj_gG9{>OV0000000000wt*zR0RV7ma4%nJZggdGZeeUMY;R*>bZKvHb1!0Hb7d}Yc~DCM0u% +rg0000807zB_Q^N(mt0@8i09Xb903rYY00000000000JedA!2tkpX>c!JX>N37a&BR4FKuOXVPs)+VJ +}}_X>MtBUtcb8c~DCM0u%rg0000807zB_Q_Tu%cm4c!JX +>N37a&BR4FKuOXVPs)+VJ~7~b7d}Yc~DCM0u%rg0000807zB_Q?V|toOK2O0O=F}04D$d0000000000 +0Jec2%>e*#X>c!JX>N37a&BR4FKuOXVPs)+VJ~oNXJ2wbaHQOE^v8JO928D02BZK00;m +`Rt8g?y@0RV7ma4%nJZggdGZeeUMZEs{{Y;!MjWnpq-XkT+004sr001Wd0000000000006duWB368aA|NaUukZ1WpZv|Y%gtZWMyn~FLPyVWn*+{Z*E_3a%E<7E^v +8JO928D02BZK00;m`Rt8fBM~Gq71ONby4*&oq00000000000002CfrI-20B~t=FJEbHbY*gGVQepLZ) +9a`b1!sZa%W|9UvPPJXm4&VaCuNm0Rj{N6aWAK2mnY{22=MpepcuN001!#0012T0000000000006dun +*ag;aA|NaUukZ1WpZv|Y%gtZWMyn~FL!8VWo#~Rc~DCM0u%rg0000807zB_Q`c!JX>N37a&BR4FK%UYcW-iQFJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_ +@t7^wRz6<~Wxh?03HAU00000000000Jeb!J^}!6X>c!JX>N37a&BR4FK%UYcW-iQFL +iWjY;!Jfc~DCM0u%rg0000807zB_Q^X>+qR0XO06PZ&03QGV00000000000Jeb&L;?VCX>c!JX>N37a +&BR4FK%UYcW-iQFL-Tia&TiVaCuNm0Rj{N6aWAK2mnY{22%h40006200000001Na0000000000006du +4@Uw3aA|NaUukZ1WpZv|Y%gzcWpZJ3X>V?GFJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_@XfzNxeFgv +kNEHA8ApigX0000000000wt-Ve0swGna4%nJZggdGZeeUMZ*XODVRUJ4ZgVeVXk}w-E^v8JO928D02B +ZK00;m`Rt8g2$BnNQ3jhF7DgXc?00000000000002Cfe%gs0B~t=FJEbHbY*gGVQepNaAk5~bZKvHb1 +!CcWo3G0E^v8JO928D02BZK00;m`Rt8gzN^Q@p8UO&FV*mgn00000000000002Cfn-?%0B~t=FJEbHb +Y*gGVQepNaAk5~bZKvHb1!gmWpH6~WiD`eP)h*<6aW+e000O8NLB_@000000ssI200000DgXcg00000 +00000wt-M|0swGna4%nJZggdGZeeUMZ*XODVRUJ4ZgVeUb!lv5FJE72ZfSI1UoLQYP)h*<6aW+e000O +8NLB_@$&Tsf6#@VNumk`ADgXcg0000000000wt<>-0swGna4%nJZggdGZeeUMZ*XODVRUJ4ZgVeUb!l +v5FKuOXVPs)+VJ>iaP)h*<6aW+e000O8NLB_@z2*;r>H+`&#|HoaF#rGn0000000000wt@F{0swGna4 +%nJZggdGZeeUMZ*XODVRUJ4ZgVeUb!lv5FKuOXVPs)+VP9-zXJKP`E^v8JO928D02BZK00;m`Rt8f+v +8tK<0RRAa1ONah00000000000002CfiQXk0B~t=FJEbHbY*gGVQepNaAk5~bZKvHb1!0bX>4RKcW7m0 +Y%XwlP)h*<6aW+e000O8NLB_@(U02hCbYEXCaCuNm0R +j{N6aWAK2mnY{22%)=2<#yO005%}001)p0000000000006duXMq9$aA|NaUukZ1WpZv|Y%gzcWpZJ3X +>V?GFKKRbbYX04FJ)wDbYWs_WnXM%XJKP`E^v8JO928D02BZK00;m`Rt8fbjl%N81polK5dZ)x00000 +000000002Cf!Tus0B~t=FJEbHbY*gGVQepNaAk5~bZKvHb1!Lbb97;BY%gqOXJKP`E^v8JO928D02BZ +K00;m`Rt8g3tF8-^A^-qAb^rh=00000000000002Cf#rz;0B~t=FJEbHbY*gGVQepNaAk5~bZKvHb1! +Lbb97;BY%h0cWo2wGaCuNm0Rj{N6aWAK2mnY{22&8EDdeOB003MtBUtcb8c~DCM0u%rg0000807zB_Q_(1c2uBeB0IWX%03iSX000000 +00000JediumS*ZX>c!JX>N37a&BR4FLGsZFJo_Rb98cbV{~tFb1rasP)h*<6aW+e000O8NLB_@woZ)u +8595j5Jmt19RL6T0000000000wt*zV0swGna4%nJZggdGZeeUMa%FKZa%FK}W@&6?E^v8JO928D02BZ +K00;m`Rt8hTr-vb-Apigce*gd=00000000000002Cfq2#e0B~t=FJEbHbY*gGVQepQWpOWZWpQ6=ZgX +^DY-}!Yc~DCM0u%rg0000807zB_Q(}4IOLqtW08$c!JX>N37a& +BR4FLGsZFLGsZUvp)2E^v8JO928D02BZK00;m`Rt8fdsfntX1polp5C8xm00000000000002Cfd~Nu0 +B~t=FJEbHbY*gGVQepQWpOWZWpQ70a$#d@WpXZXc~DCM0u%rg0000807zB_Q)siK(astG0D@lt03rYY +00000000000JeeN1_Jc!JX>N37a&BR4FLGsZFLGsZUv+M2ZgX^DY-}!Yc~DCM0u%rg0000807zB +_Qvd(}00IC20000003-ka00000000000JeebAp-z#X>c!JX>N37a&BR4FLGsbZ)|mRX>V>XUtei%X>? +y-E^v8JO928D02BZK00;m`Rt8h=hJO(G0000#0ssIY00000000000002CfiNNi0B~t=FJEbHbY*gGVQ +epQWpi(Ab#!TOZZBeCb7d}Yc~DCM0u%rg0000807zB_Qvd(}00IC20000004o3h00000000000Jec=B +Le_%X>c!JX>N37a&BR4FLGsbZ)|mRX>V>XY-ML*V|g!MUukY>bYEXCaCuNm0Rj{N6aWAK2mnY{22-h( +nQbZKvHFKlIJVPknOa%FR +GY<6XGE^v8JO928D02BZK00;m`Rt8f500002000000000l00000000000002Cfh#xz0B~t=FJEbHbY* +gGVQepQWpi(Ab#!TOZZC3Wb8l>RWo&6;FJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_@Vg84_V+8;JIu +!r_D*ylh0000000000wt;#$0|0Poa4%nJZggdGZeeUMa%FRGY;|;LZ*DJgWpi(Ac4cg7VlQH0b7d}Yc +~DCM0u%rg0000807zB_Q=1;U%RLbQ0L4oH05Jdn00000000000Jec9J_7)7X>c!JX>N37a&BR4FLGsb +Z)|mRX>V>Xa%FRGY<6XAX<{#9VQyq;WMOn=b1rasP)h*<6aW+e000O8NLB_@-^JqwVjBPer)vNJE&u= +k0000000000wt=!w0|0Poa4%nJZggdGZeeUMa%FRGY;|;LZ*DJgWpi(Ac4cg7VlQT4V{~tFc`k5yP)h +*<6aW+e000O8NLB_@JX!>$83q6VGZg>;Hvj+t0000000000wt--40|0Poa4%nJZggdGZeeUMa%FRGY; +|;LZ*DJgWpi(Ac4cg7VlQTIb#7!|V_|M&X=Gt^WpgfYc~DCM0u%rg0000807zB_Q)E2rriu#y0I4AW0 +51Rl00000000000Jee7aRUHwX>c!JX>N37a&BR4FLGsbZ)|mRX>V>Xa%FRGY<6XAX<{#Ma&LBNWMy(L +aCuNm0Rj{N6aWAK2mnY{22;x-u`5FZ003QbZKvHFLGsbZ)|pDY-wUIa%FIDa&%>KE^v8JO928D02BZK00;m`Rt8g6;y}*$1ONc_6#xJ;000 +00000000002Cfi-~x0B~t=FJEbHbY*gGVQepQWpi(Ab#!TOZZC3Wb8l>RWo&6;FLGsZb!l>CZDnqBb1 +rasP)h*<6aW+e000O8NLB_@-=A2?-V6W$D=Gj0F8}}l0000000000wt;?z0|0Poa4%nJZggdGZeeUMa +%FRGY;|;LZ*DJgWpi(Ac4cg7VlQ%Kb8l>RWpXZXc~DCM0u%rg0000807zB_Qvd(}00IC20000003QGV +00000000000JedtlLG*7X>c!JX>N37a&BR4FLiWjY;!MPUukY>bYEXCaCuNm0Rj{N6aWAK2mnY{22+E +`Tvp%#0012X000{R0000000000006du=#v8gaA|NaUukZ1WpZv|Y%g_mX>4;ZUu4;ZV{dJ6VRUI?X>4h9d0%v4XLBxac~DCM0u%rg0000807zB_Q{)uV+n@j +d0Q~>}03QGV00000000000JedJqXPhNX>c!JX>N37a&BR4FLiWjY;!MUVRU75X>DaLaCuNm0Rj{N6aW +AK2mnY{22&AWjUM;}008I=001HY0000000000006duVx$8AaA|NaUukZ1WpZv|Y%g_mX>4;ZWMy!2Wn +*DV>WaCuNm0Rj{N6aWAK2mnY{22<{}Dgnd;0003D001cf0000000000006dunyCW-aA|NaUukZ1W +pZv|Y%g_mX>4;ZWNC6`V{~72a%^8{Wo&R|a&sc!JX>N37a&BR4FLiWjY;!MUX>)XSbZKmJUtw}*b1rasP)h*<6aW+e000O +8NLB_@SLZr?76JeOwFCeF9{>OV0000000000wt@by0|0Poa4%nJZggdGZeeUMb#!TLb1!9XV{c?>Zf7 +oVc~DCM0u%rg0000807zB_Q*QaK?@9sy06GK!03rYY00000000000JecqvI78cX>c!JX>N37a&BR4FL +iWjY;!MVZgg^aaBpdDbaO6nc~DCM0u%rg0000807zB_Q>k}dXZHvI0J|3e03iSX00000000000JeeKv +;zQeX>c!JX>N37a&BR4FLiWjY;!MWX>4V4d2@7SZ7y(mP)h*<6aW+e000O8NLB_@a{NbJX#oHL6$1bO +AOHXW0000000000wt*480|0Poa4%nJZggdGZeeUMb#!TLb1!CTY-MzLaAk8YaCuNm0Rj{N6aWAK2mnY +{22;9sjD$x8006!W000~S0000000000006duxW5AcaA|NaUukZ1WpZv|Y%g_mX>4;ZXKZO=V=i!cP)h +*<6aW+e000O8NLB_@lHhfo7zY3V)D!>!9RL6T0000000000wt+dt0|0Poa4%nJZggdGZeeUMb#!TLb1 +!INb7*CAE^v8JO928D02BZK00;m`Rt8fjCuGXA0RR9_0{{Rm00000000000002CfsD)p0B~t=FJEbHb +Y*gGVQepTbZKmJFKKRSWn*+-b7f<7a%FUKVQzD9Z*p`laCuNm0Rj{N6aWAK2mnY{22=lnMIQ$a006%+ +0015U0000000000006duip~Q7aA|NaUukZ1WpZv|Y%g_mX>4;ZY;R|0X>MmOaCuNm0Rj{N6aWAK2mnY +{22-r@2-nvg005X@000{R0000000000006du&D;Y3aA|NaUukZ1WpZv|Y%g_mX>4;ZZE163E^v8JO92 +8D02BZK00;m`Rt8f|uidJR0RRAC1pojY00000000000002Cf!+E80B~t=FJEbHbY*gGVQepTbZKmJFK +usRWo&aUaCuNm0Rj{N6aWAK2mnY{22(2i=Pw-u006BE001BW0000000000006durThZ`aA|NaUukZ1W +pZv|Y%g_mX>4;ZaA9L>VP|P>XD)DgP)h*<6aW+e000O8NLB_@NF&H%odf^?{R{vA9{>OV0000000000 +wt)Zv1ORYpa4%nJZggdGZeeUMb#!TLb1!gVa$#(2Wo#~Rc~DCM0u%rg0000807zB_Q&!fmQ>y_00D1% +f03-ka00000000000JeeJ1q1+aX>c!JX>N37a&BR4FLiWjY;!MgYiD0_Wpi(Ja${w4E^v8JO928D02B +ZK00;m`Rt8hWgk&#R1pokK6aWAx00000000000002CfyV~~0B~t=FJEbHbY*gGVQepTbZKmJFLPydb# +QcVZ)|g4Vs&Y3WG--dP)h*<6aW+e000O8NLB_@3weAzjSK()R44!dApigX0000000000wt-{~1ORYpa +4%nJZggdGZeeUMb#!TLb1!psVsLVAV`X!5E^v8JO928D02BZK00;m`Rt8fN9}RyB3IG5SAOHX#00000 +000000002CfiM~b0B~t=FJEbHbY*gGVQepTbZKmJFLY&Xa9?C;axQRrP)h*<6aW+e000O8NLB_@=O7Q +WED8VsWh4LqAOHXW0000000000wt;sf1ORYpa4%nJZggdGZeeUMb#!TLb1!vnaA9L>X>MmOaCuNm0Rj +{N6aWAK2mnY{22+=5rsGWm002h^000{R0000000000006du;VuLKaA|NaUukZ1WpZv|Y%g_mX>4;Zb# +iQTE^v8JO928D02BZK00;m`Rt8g4;ZcW7m0Y%XwlP)h*<6aW+e000O8NLB_@>_RFUGXVeqV*&sG9RL6 +T0000000000wt+A}1ORYpa4%nJZggdGZeeUMc4KodUtei%X>?y-E^v8JO928D02BZK00;m`Rt8gH>{_ +St0{{T>3jhEc00000000000002Cfu2DG0B~t=FJEbHbY*gGVQepUV{k-1ORYpa4%nJZggdGZeeUMc4KodXK8dUaCuNm0 +Rj{N6aWAK2mnY{22;qlI(fDQ0043n0015U0000000000006du%vuBhaA|NaUukZ1WpZv|Y%g|Wb1!XW +a$|LJX<=+GaCuNm0Rj{N6aWAK2mnY{22)+-S6b7^mGE^v8JO928D02BZK00;m`Rt8g7=Gsn07ytmiT>tMtBUtcb8c~DCM0u%rg0000807zB_Q +wdWFYJC&{0IX*K02TlM00000000000JeczjsyU3X>c!Jc4cm4Z*nhTaByU4a&sc!Jc4cm4Z*nhWX>)XPZ!U0oP)h*<6aW+ +e000O8NLB_@bQ{R#fBgUewMPU17ytkO0000000000wt*GP1ORYpa4%nWWo~3|axZXsaA9(DX>MmOaCu +Nm0Rj{N6aWAK2mnY{22*wqSNb&}007&H000sI0000000000006du%E$!(aA|NaUv_0~WN&gWb7^=kaC +uNm0Rj{N6aWAK2mnY{22(py{zK{j004pk000&M0000000000006duFX{yVaA|NaUv_0~WN&gWc4cm4Z +*nelcyv%p0Rj{N6aWAK2mnY{22;eEP1dmh0021w001Na0000000000006duP3r{!aA|NaUv_0~WN&gW +V_{=xWn*t{baHQOFJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_@eR*jI2?78BNCf}@AOHXW000000000 +0wt+zG1pshqa4%nWWo~3|axY_HV`yb#Z*FvQZ)`7LV{K$EaCuNm0Rj{N6aWAK2mnY{22)tEZ$Y31005 +U0001KZ0000000000006duhwcRcaA|NaUv_0~WN&gWV_{=xWn*t{baHQOFJWY1aCBvIE^v8JO928D02 +BZK00;m`Rt8gs-nr;H0RR9@0{{Ra00000000000002CfoSvv0B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY +%gPBV`ybAaCuNm0Rj{N6aWAK2mnY{22(5te|;7K007hi001HY0000000000006du;q?UoaA|NaUv_0~ +WN&gWV_{=xWn*t{baHQOFJo_QaA9;VaCuNm0Rj{N6aWAK2mnY{22001Tc000000000 +0006duH}?eqaA|NaUv_0~WN&gWV_{=xWn*t{baHQOFJo_RbaHQOY-MsTaCuNm0Rj{N6aWAK2mnY{22= +N9hCBlV001=#001Wd0000000000006dus0RiBaA|NaUv_0~WN&gWV_{=xWn*t{baHQOFJ@_MWp{F6aB +yXEE^v8JO928D02BZK00;m`Rt8hywf*A01pokT5C8xs00000000000002Cf$$3k0B~t=FJE?LZe(wAF +Job2Xk}w>Zgg^QY%geKb#iHQbZKLAE^v8JO928D02BZK00;m`Rt8gEhzcSq2><|Z8~^|%0000000000 +0002Cf$|Xs0B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%gZgg^QY%gPBV`yb_FJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_@M?iqf83q6V%MbtnEdT%j000000 +0000wt*KN1^{qra4%nWWo~3|axY_HV`yb#Z*FvQZ)`7PVPj}zb1!CTY-L|#VPj}zE^v8JO928D02BZK +00;m`Rt8f^!W2br0RRAb0{{Rn00000000000002CfqNqc0B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%gP +BV`yb_FLGsMX>(s=VPj}zE^v8JO928D02BZK00;m`Rt8gk2OV0000000000wt=-J1^{qra4%nWWo~3|axY_La&&2CX)j-2ZDDC{Utcb8c~DCM0u%rg0000807zB_Q +x_bfz0f5B0EzVj03HAU00000000000JedZCI$d-X>c!Jc4cm4Z*nhVWpZ?BW@#^DVPj=-bS`jZZBR=A +0u%rg0000807zB_Q`}7uaj^sd0AvdQ02=@R00000000000JedlOb7sQX>c!Jc4cm4Z*nhVWpZ?BW@#^ +DZ*pZWaCuNm0Rj{N6aWAK2mnY{22?y-E^v8JO928D02BZK00;m`Rt8hOdAjE3IRF5ddjJ3*00000000000002Cfk +;*e0B~t=FJE?LZe(wAFJow7a%5$6FJftDHD+>UaV~IqP)h*<6aW+e000O8NLB_@vSdz!M*{!=3<}BB>(^?00000000000002Cf%20G0B~t=FJE?LZe(wAFJow7a%5$6FJow7a%5?9baH8 +8b#!TOZZ2?nP)h*<6aW+e000O8NLB_@$#=vmh6DfrY7YPaDF6Tf0000000000wt<432mo+ta4%nWWo~ +3|axY_OVRB?;bT4CQVRCb2bZ2sJb#QQUZ(?O~E^v8JO928D02BZK00;m`Rt8h@f}$YL2LJ$v6aWAu00 +000000000002Cfls0c0B~t=FJE?LZe(wAFJow7a%5$6FJow7a&u*LaB^>AWpXZXc~DCM0u%rg000080 +7zB_Q{!<=II;x*0A3CN04V?f00000000000Jec(st5pZX>c!Jc4cm4Z*nhVXkl_>WppoNZ)9n1XLEF6 +bY*Q}V`yn^WiD`eP)h*<6aW+e000O8NLB_@O5runV*&sG)C2$k9RL6T0000000000wt-o&2mo+ta4%n +WWo~3|axY_OVRB?;bT4CXZE#_9E^v8JO928D02BZK00;m`Rt8hGLZGB@0{{SU2LJ#f0000000000000 +2Cf%mcq0B~t=FJE?LZe(wAFJow7a%5$6FJo{yG&yi`Z(?O~E^v8JO928D02BZK00;m`Rt8gF`{91g0s +sKX1^@sY00000000000002Cfv2_z0B~t=FJE?LZe(wAFJow7a%5$6FJ*3ZZF4Sgc~DCM0u%rg000080 +7zB_Q^e&_WkCf10Micu03ZMW00000000000Jedyxd;GoX>c!Jc4cm4Z*nhVXkl_>WppoPb7OFFZ(?O~ +E^v8JO928D02BZK00;m`Rt8hTMpy(J1^@sADgXc+00000000000002Cfi1rX0B~t=FJE?LZe(wAFJow +7a%5$6FJ*IMb8Rkgc~DCM0u%rg0000807zB_Q~5N<5H$q=009pG03rYY00000000000JedG#Rvd!X>c +!Jc4cm4Z*nhVXkl_>WppoPbz^F9aB^>AWpXZXc~DCM0u%rg0000807zB_Q};P(w|f`>0If9u03ZMW00 +000000000Jeem$_M~(X>c!Jc4cm4Z*nhVXkl_>WppoPbz^ICW^!e5E^v8JO928D02BZK00;m`Rt8htz +2vw+0{{R62LJ#f00000000000002Cfw1EU0B~t=FJE?LZe(wAFJow7a%5$6FJ*OOYjSXMZ(?O~E^v8J +O928D02BZK00;m`Rt8hjI*5-tIsgC&eEc +!Jc4cm4Z*nhVXkl_>WppoPbz^jQaB^>AWpXZXc~DCM0u%rg0000807zB_Q}WGgCyFNk00vS303iSX00 +000000000Jec9B?$mc!Jc4cm4Z*nhVXkl_>WppoRVlp!^GG=mRaV~IqP)h*<6aW+e000O8NLB_@G +%6+4Jp%v$2nPTFBLDyZ0000000000wt??V2>@_ua4%nWWo~3|axY_OVRB?;bT4OOGBYtUaB^>AWpXZX +c~DCM0u%rg0000807zB_Qw5CVh_ey^05UfK03!eZ00000000000Jec`PzeBVX>c!Jc4cm4Z*nhVXkl_ +>WppoSWnyw=cW`oVVr6nJaCuNm0Rj{N6aWAK2mnY{22+3#-w-S?008!70015U0000000000006duVPg +pZaA|NaUv_0~WN&gWV`yP=WMyc!Jc4cm4Z*nhVXkl_>WppoWVQyz)b!=y0a%o|1ZEs{{ +Y%XwlP)h*<6aW+e000O8NLB_@L{oCOf*1e*A(jCEB>(^b0000000000wt@D=2>@_ua4%nWWo~3|axY_ +OVRB?;bT4dSZf9q5Wo2t^Z)9a`E^v8JO928D02BZK00;m`Rt8gC0g@&z7XSeFk^ulF0000000000000 +2CfxF%b0B~t=FJE?LZe(wAFJow7a%5$6FKl6MXJ}<&a%FdIZ)9a`E^v8JO928D02BZK00;m`Rt8hLB< +Jst8vp=|q5%LY00000000000002Cfi3n40B~t=FJE?LZe(wAFJow7a%5$6FKl6MXJ~b9XJK+_VQy`2W +MynFaCuNm0Rj{N6aWAK2mnY{22-JO5caX<=?{Z)9a`E^v8JO928D02BZK00;m`Rt8hecwy7A8vp>WqyYdV000000000 +00002Cftx!D0B~t=FJE?LZe(wAFJow7a%5$6FKl6MXLM*`X>D(0Wo#~Rc~DCM0u%rg0000807zB_Q>? +Rsf@&N90O^MT04D$d00000000000JedSR|)`dX>c!Jc4cm4Z*nhVXkl_>WppoWVQy!1b#iNIb7*aEWM +ynFaCuNm0Rj{N6aWAK2mnY{22);En8vLJ004^>001KZ0000000000006duJ#-2HaA|NaUv_0~WN&gWV +`yP=WMyaA|NaU +v_0~WN&gWV`yP=WMy001Zv001Tc +0000000000006duotX*%aA|NaUv_0~WN&gWV`yP=WMyAWpXZXc~DCM0u%rg0000807zB_Q`-5-6pIZ20QE5d04M+e00000000000JeeEqzV9VX>c!Jc4cm4Z* +nhVXkl_>WppofZfSO9a&uv9WMy<^V{~tFE^v8JO928D02BZK00;m`Rt8hlQl3yz1ONaa3jhEi000000 +00000002Cfu^zw0B~t=FJE?LZe(wAFJow7a%5$6FLiWgIB;@rVr6nJaCuNm0Rj{N6aWAK2mnY{22;&* +xxl~x008>{0015U0000000000006duHn$1@aA|NaUv_0~WN&gWV`yP=WMyV>WaCuNm0Rj{ +N6aWAK2mnY{22-qsq7(!G00062001KZ0000000000006duGPnu=aA|NaUv_0~WN&gWV`yP=WMy?y-E^v8JO928D02BZK00;m`Rt8he=YS}f1ONaJ3jhEm00000000000002Cfp@qH0B~t=FJ +E?LZe(wAFJow7a%5$6FJo+JFJow7a%5$6Wn*+MaCuNm0Rj{N6aWAK2mnY{22%h40006200000001Ze0 +000000000006duQoRZQaA|NaUv_0~WN&gWV`yP=WMy0000W00000000000002Cfwj;I0B~ +t=FJE?LZe(wAFJo_PZ*pO6VJ}}_X>MtBUtcb8c~DCM0u%rg0000807zB_Q~2@)F_!}X06+==02}}S00 +000000000Jedu(Fy=?X>c!Jc4cm4Z*nhVZ)|UJVQpbAVQzD2E^v8JO928D02BZK00;m`Rt8f;f}y;D3 +jhEYDgXc@00000000000002CfqT^o0B~t=FJE?LZe(wAFJo_PZ*pO6VJ~5Bb7^#McWG`jGA?j=P)h*< +6aW+e000O8NLB_@I>Kjvi~;}v%Lf1eA^-pY0000000000wt+q23IK3va4%nWWo~3|axY_VY;SU5ZDB8 +IZfSIBVQgu0WiD`eP)h*<6aW+e000O8NLB_@QuSTsT?GIDv=smV9RL6T0000000000wt)%c3IK3va4% +nWWo~3|axY_VY;SU5ZDB8WX>KzzE^v8JO928D02BZK00;m`Rt8g%r)VAg1polg82|tu000000000000 +02CfuZRN0B~t=FJE?LZe(wAFJo_PZ*pO6VJ~-SZggdGZ7y(mP)h*<6aW+e000O8NLB_@Fy##4WdQ&HT +mk?99{>OV0000000000wt?F33IK3va4%nWWo~3|axY|Qb98KJVlQ7`X>MtBUtcb8c~DCM0u%rg00008 +07zB_Q_hmT&vh#R03fCS0384T00000000000JedA@d^NNX>c!Jc4cm4Z*nhWX>)XJX<{#9Z*6d4bS`j +tP)h*<6aW+e000O8NLB_@h?96aIWPbKuFL=c9{>OV0000000000wt*}i3jlCwa4%nWWo~3|axY|Qb98 +KJVlQN2bYWs)b7d}Yc~DCM0u%rg0000807zB_Q`S~0l=2h+002_}02}}S00000000000JedmO$z{UX> +c!Jc4cm4Z*nhWX>)XJX<{#FZe(S6E^v8JO928D02BZK00;m`Rt8g4JBQt8GXMY)&;S4*00000000000 +002CfzDzJ0B~t=FJE?LZe(wAFJx(RbZlv2FKlmPVRUbDb1rasP)h*<6aW+e000O8NLB_@{~v!|ix2<+ +WIF%=9{>OV0000000000wt;e%3jlCwa4%nWWo~3|axY|Qb98KJVlQoBZfRy^b963nc~DCM0u%rg0000 +807zB_Q-*j{lK=()0D2Js03HAU00000000000JecTrwagZX>c!Jc4cm4Z*nhWX>)XJX<{#JVRCC_a&s +c!Jc4cm4Z*nhWX> +)XJX<{#JWprU=VRT_GaCuNm0Rj{N6aWAK2mnY{22&D*?$5{z0062h001BW0000000000006du+tLdFa +A|NaUv_0~WN&gWWNCABY-wUIa%FRGb#h~6b1rasP)h*<6aW+e000O8NLB_@(=~cK$P@qo2Soq?9smFU +0000000000wt?Z^3jlCwa4%nWWo~3|axY|Qb98KJVlQ)Ja%pgMb1rasP)h*<6aW+e000O8NLB_@uz=P +Am$?7{0D1uc8vp005o=02=@R00000000000Jed(tqcHgX>c!Jc4cm4Z*nhWX>)XJX<{#QHZ(3}cx +6ya0Rj{N6aWAK2mnY{22*@G&+iCH005H@0RS5S0000000000006dufOic5aA|NaUv_0~WN&gWWNCABY +-wUIb#!TLE^v8JO928D02BZK00;m`Rt8hd&^q#}82|vcUH||d00000000000002Cfx*BH0B~t=FJE?L +Ze(wAFJx(RbZlv2FLq^eb7^mGE^v8JO928D02BZK00;m`Rt8fX7!JlaumAu6U;zLd00000000000002 +Cfv4IH0B~t=FJE?LZe(wAFJx(RbZlv2FLyICE@gOSP)h*<6aW+e000O8NLB_@`x>(RXTSge0EPho8vp +c!Jc4cm4Z*nhWX>)XJX<{#TXk}$=E^v8JO928D02BZK0 +0;m`Rt8gC*JL`$0000O0RR9g00000000000002CfnIeF0B~t=FJE?LZe(wAFJx(RbZlv2FJEF|V{344 +a&#|WUukY>bYEXCaCuNm0Rj{N6aWAK2mnY{22)K;?%vk{008s@001Qb0000000000006duZgvjc!Jc4cm4Z*nhWX>)XJX<{#5Vqs%zaBp&SFLP*hbZKlZaCuNm0Rj{N6a +WAK2mnY{22=gq|Ci} +rSpWb7bO8V;00000000000002Cfx)&90B~t=FJE?LZe(wAFJx(RbZlv2FJEF|V{344a&#|rVRB|^Y-K +KRc~DCM0u%rg0000807zB_Q@A(TBu@eW0ILK503ZMW00000000000Jebx5fA`yX>c!Jc4cm4Z*nhabZ +u-kY-wUIUtei%X>?y-E^v8JO928D02BZK00;m`Rt8g(hP*mT6aWBuLI40E00000000000002Cfshjr0 +B~t=FJE?LZe(wAFKBdaY&C3YVlQ85Xkm0^cx`MhaCuNm0Rj{N6aWAK2mnY{22+w+$+9#b008%a001Na +0000000000006du87L3{aA|NaUv_0~WN&gWXmo9CHEd~OFJEbHaCLNZbaG{3Z7y(mP)h*<6aW+e000O +8NLB_@r#U!pavcBww=)3%A^-pY0000000000wt;u8P>9smFU0000000000wt+!q5CCv#a4%nWWo~3|ax +Z9fZEQ7cX<{#5b#!TLb1rasP)h*<6aW+e000O8NLB_@B(F4Uvq%5{ictXoApigX0000000000wt;MH5 +CCv#a4%nWWo~3|axZ9fZEQ7cX<{#9Z*FsRVQzGDE^v8JO928D02BZK00;m`Rt8g;kxQLLMF0S;)Byk^ +00000000000002CfnK!`0B~t=FJE?LZe(wAFKBdaY&C3YVlQZPZEQ7gVRCb2axQRrP)h*<6aW+e000O +8NLB_@KaNu_&=3FsC_exIA^-pY0000000000wt?jO5CCv#a4%nWWo~3|axZ9fZEQ7cX<{#PWpZg@Y-x +IBaxQRrP)h*<6aW+e000O8NLB_@ZViIgUH||9asU7TCIA2c0000000000wt@5u5dd&$a4%nWWo~3|ax +Z9fZEQ7cX<{#5baH8BFJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_@4Xmkbt^oi59RvUXBLDyZ000000 +0000wtc!Jc4cm4Z*nhabZu-kY-wUIUvzS5WiN1fE^v8JO928 +D02BZK00;m`Rt8f500002000000000e00000000000002Cf#?ts0B~t=FJE?LZe(wAFKBdaY&C3YVlQ +TCY;MtBUtcb8c~DCM0u%rg0000807zB_Q(r2cR?7hZ0JH-D05$*s00000000000JecJ5f +K1zX>c!Jc4cm4Z*nhabZu-kY-wUIW@&76WpZ;bVQg?{VPa);X=7n*VRUqIX<~JBWpgfYc~DCM0u%rg0 +000807zB_Q(1xew449{04f0h03-ka00000000000Jeco6A=J#X>c!Jc4cm4Z*nhabZu-kY-wUIW@&76 +WpZ;bVqtS-E^v8JO928D02BZK00;m`Rt8f-4y!#O0{{TZ3jhE!00000000000002Cfh`mf0B~t=FJE? +LZe(wAFKBdaY&C3YVlQTCY;4?5a&s?iX>N2baC +uNm0Rj{N6aWAK2mnY{22*N;ilXrb001j0001li0000000000006duTN@DoaA|NaUv_0~WN&gWXmo9CH +Ed~OFJ@_MbY*gLFK=*kX>V>}Y;<8~b1rasP)h*<6aW+e000O8NLB_@4Ub>JGZ+8>rECBIDF6Tf00000 +00000wt4?5a&s?pVQy)3X?kUHE^v8JO928D02BZK00;m +`Rt8hA=MqHy0RRBx1ONak00000000000002Cfe?_CaA9L*E^v8JO928D02BZK00;m`Rt8gNQb7!G0RRBS0ssIl00000000000002CfmJ;b0B~t=FJ +E?LZe(wAFKBdaY&C3YVlQ-ZWo2PxVQ_S1a&s?VUukY>bYEXCaCuNm0Rj{N6aWAK2mnY{22-D6%T-?j0 +08L*001ih0000000000006du5I+$BaA|NaUv_0~WN&gWXmo9CHEd~OFLZKcWnpAtaCBvIb1!FQZgXg9 +E^v8JO928D02BZK00;m`Rt8gX8mC}U0ssIa2LJ#l00000000000002Cfw(~t0B~t=FJE?LZe(wAFKBd +aY&C3YVlQ-ZWo2PxVQ_S1a&s?pVR$ZZc~DCM0u%rg0000807zB_Q$!N^kY5D=0ALOP04)Fj00000000 +000JecoL=gaRX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7Vs&Y3WMy)5FJE72ZfSI1UoLQYP)h*<6aW+e00 +0O8NLB_@uY@3o3l9JQhB^QMDF6Tf0000000000wt@Lc5dd&$a4%nWWo~3|axZ9fZEQ7cX<{#Qa%E*=b +!lv5WpZ;bVqtS-E^v8JO928D02BZK00;m`Rt8huBpd|~2mk=gBLDy>00000000000002CflFBt0B~t= +FJE?LZe(wAFKBdaY&C3YVlQ-ZWo2S@X>4R=a&s?aZ*4Acc~DCM0u%rg0000807zB_Q=z1*%xww)0DCh +404e|g00000000000JedkVG#guX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7Vs&Y3WMy)5FJ*LcWo0gKc~D +CM0u%rg0000807zB_Qx4u2bUY3K05&@S051Rl00000000000JecuY!Lu(X>c!Jc4cm4Z*nhabZu-kY- +wUIbaG{7Vs&Y3WMy)5FJ*LcWo2J%cx`MhaCuNm0Rj{N6aWAK2mnY{22)c)c2Tni008k8001li000000 +0000006du-g*%LaA|NaUv_0~WN&gWXmo9CHEd~OFLZKcWp`n0Yh`kCFJE72ZfSI1UoLQYP)h*<6aW+e +000O8NLB_@tiQ%ZCn0068R001rk0000000000006du&yW!SaA|NaUv_0~WN&gWXmo9CHEd~O +FLZKcWp`n0Yh`kCFJ*LcWo2J%cx`MhaCuNm0Rj{N6aWAK2mnY{22&=1Z>`e;002q}001fg000000000 +0006dur00000000000002Cft8>U0B~t +=FJE?LZe(wAFKJ|MVJ~BEa%C=Xc~DCM0u%rg0000807zB_Q@ZgHV!k5)0C24U02}}S00000000000Je +dAtPuckX>c!Jc4cm4Z*nhbWNu+EX=H9;WMOn+E^v8JO928D02BZK00;m`Rt8h-tJqjK0{{TW2LJ#a00 +000000000002CfppLj0B~t=FJE?LZe(wAFKJ|MVJ~TJbaG*CXJvCPaCuNm0Rj{N6aWAK2mnY{22-Ko7 +D*WZ000*N001BW0000000000006du=F<@XaA|NaUv_0~WN&gWX=H9;FK}UFYhh<)Uu0o)VJ>iaP)h*< +6aW+e000O8NLB_@#7xN~ftUaQZ#4q|9RL6T0000000000wt+p=5dd&$a4%nWWo~3|axZCQZecHVbaON +|WMOn+E^v8JO928D02BZK00;m`Rt8gUZ+zP60RRA$1ONaZ00000000000002Cf%$9_0B~t=FJE?LZe( +wAFKu&YaA9L>FJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_@mOqay7XSbN6#xJL9{>OV0000000000wt +*jS5&&>%a4%nWWo~3|axZOjXK-O-YcF4RWpZc!Jc4cm4Z*nhfb7yd2V{0#Ecw=R7bZKvHb1rasP)h*<6aW+e000O8 +NLB_@z>%rk4hH}Li5LI?8UO$Q0000000000wt;DJ5&&>%a4%nWWo~3|axZOjXK-O-YcFMZbS`jtP)h* +<6aW+e000O8NLB_@{SZ=%Zyf*t$(R5D9{>OV0000000000wt=sB5&&>%a4%nWWo~3|axZOjXK-O-YcF +PDY;0m-V{0yOc~DCM0u%rg0000807zB_Qc!Jc4 +cm4Z*nhiVPk7yXK8L{FJE6_VsCYHUtcb8c~DCM0u%rg0000807zB_Q)G5pvHSo4015&C03iSX000000 +00000Jeb%m=XYRX>c!Jc4cm4Z*nhiVPk7yXK8L{FJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_@=i+68 +pbh{44K4rxBLDyZ0000000000wt+;M5&&>%a4%nWWo~3|axZXUV{2h&X>MmPUu|J-d2DHJb$Bjtc~DC +M0u%rg0000807zB_Q=%7fDnADR0HP5903!eZ00000000000Jec8s1g8hX>c!Jc4cm4Z*nhiVPk7yXK8 +L{FJEnSb8KvBZgqGraCuNm0Rj{N6aWAK2mnY{22)w+ZdGvs005u{001Na0000000000006durLPhIaA +|NaUv_0~WN&gWaA9L>VP|P>XD?rKbaHiLbairNb1rasP)h*<6aW+e000O8NLB_@JR%a4%nWWo~3|axZXUV{2h&X>MmPZDDe2WpZ;aaCuNm0Rj{N6aWAK2mnY{ +22)6MU%Lhe007n!001Qb0000000000006duF1!)|aA|NaUv_0~WN&gWaA9L>VP|P>XD@PPadl~OWo>0 +{baO6nc~DCM0u%rg0000807zB_Q?nzWg+Lns05E(203!eZ00000000000Jed5!x8{+X>c!Jc4cm4Z*n +hiVPk7yXK8L{FLQ8ZV`*k-WpZ;aaCuNm0Rj{N6aWAK2mnY{22+2`$jr47002fm0012T000000000000 +6du_}&r#aA|NaUv_0~WN&gWaA9L>VP|P>XD@VNXLBxac~DCM0u%rg0000807zB_Q-e+fYvTm~0Pqk10 +3HAU00000000000JeeX@Dc!UX>c!Jc4cm4Z*nhiVPk7yXK8L{FLiWjY;!Jfc~DCM0u%rg0000807zB_ +QyzBeM-~tO06{wd03ZMW00000000000Jeb#_YwebX>c!Jc4cm4Z*nhiVPk7yXK8L{FLq^eb7^mGE^v8 +JO928D02BZK00;m`Rt8gg(Kls!0001p0000U00000000000002Cfm;U?0B~t=FJE?LZe(wAFK}gWH8D +3YUtei%X>?y-E^v8JO928D02BZK00;m`Rt8fKnOFcn1pojH4gdff00000000000002Cfe{E30B~t=FJ +E?LZe(wAFK}gWH8D3YVs&Y3WG--dP)h*<6aW+e000O8NLB_@dIZ&2-30&upBMlD8vpc!Jc4cm4Z*nhiWpFhyH!ovvY;SUGZ)YxWc~DCM0u%rg0000807zB_Q#lq +b(S-p309gb802}}S00000000000Jed28WR9;X>c!Jc4cm4Z*nhiWpFhyH!ovvZE#_9E^v8JO928D02B +ZK00;m`Rt8hJ?~s|;0RRA%1ONaY00000000000002CfioNv0B~t=FJE?LZe(wAFK}gWH8D3YWNC7AZ* +OdKE^v8JO928D02BZK00;m`Rt8f~K8=Pn2mk<{Z00000000000002Cfwv|T +0B~t=FJE?LZe(wAFK}gWH8D3YZDn*}E^v8JO928D02BZK00;m`Rt8g4y1azN3;+NMO|a&Kd0b8|0WUukY>bYEXCaC +uNm0Rj{N6aWAK2mnY{22;4qG*b2o006El001li0000000000006duML81yaA|NaUv_0~WN&gWaAj~cF +*h%1ZeMV6Z)0V1b1z?MZeMV6Z)0V1b1rasP)h*<6aW+e000O8NLB_@R}TwErE~xQ)w2NrB>(^b00000 +00000wt?y-E^v8JO928D02BZK00;m`Rt8f +dwy#T70RR9=0ssIf00000000000002Cfo-`H0B~t=FJE?LZe(wAFK}yTUvg!0Z*_8GWpgiZc{4F%Z*6 +d4bS`jtP)h*<6aW+e000O8NLB_@*su60e+B>msuKVJAOHXW0000000000wt)w`698~&a4%nWWo~3|ax +ZXlZ)b94b8|0WUukY>bYEXCaCuNm0Rj{N6aWAK2mnY{22)Fa%FRKFJfVGE^v8JO928D02BZK00;m`Rt8gHiufM;0ssJo1poja00000 +000000002Cfvv_90B~t=FJE?LZe(wAFK}{iXL4n8b1!3Wb#8QJaxQRrP)h*<6aW+e000O8NLB_@-<#x +75d#1Kj|Bh#9{>OV0000000000wt?lz698~&a4%nWWo~3|axZXlZ)b94b8|0qaA|ICWpXZXc~DCM0u% +rg0000807zB_Q;K8$goFnG0Fx8|03ZMW00000000000JecM%@Y7{X>c!Jc4cm4Z*nhkWpQ<7b98erUt +ei%X>?y-E^v8JO928D02BZK00;m`Rt8i0zGSrv0RRBU0RR9a00000000000002Cf%ViA0B~t=FJE?LZ +e(wAFLGsZb!BsOb1z?Cc4cyNX>V>{UoLQYP)h*<6aW+e000O8NLB_@b?5!69|8aXas&VXCjbBd00000 +00000wt+#`698~&a4%nWWo~3|axZdaadl;LbaO9XX>N37a&BR4Uv+e8Y;!Jfc~DCM0u%rg0000807zB +_Q;MzcpSKhM06|s&03ZMW00000000000Jedp*b@M7X>c!Jc4cm4Z*nhkWpQ<7b98erVPs)&bY*gLE^v +8JO928D02BZK00;m`Rt8g<@Ld*m1^@t|82|tq00000000000002Cftu|T0B~t=FJE?LZe(wAFLGsZb! +BsOb1z|VX)bViP)h*<6aW+e000O8NLB_@af*U3jtc+)5Geov8~^|S0000000000wt-3X698~&a4%nWW +o~3|axZdaadl;LbaO9Zb#!PhaCuNm0Rj{N6aWAK2mnY{22<3aj@LT@0083w0012T0000000000006du +5dahbaA|NaUv_0~WN&gWa%FLKWpi|MFJonLbaO6nc~DCM0u%rg0000807zB_Qz3Pr2lD~|07(b{03HA +U00000000000JedL0TcjmX>c!Jc4cm4Z*nhkWpQ<7b98erV{dJ6VRSBVc~DCM0u%rg0000807zB_Q#c ++;f8-MY03%8O03QGV00000000000Jed)1QY;pX>c!Jc4cm4Z*nhkWpQ<7b98erV{dP3X=QURaCuNm0R +j{N6aWAK2mnY{22(!-Sk_kr001!!001HY0000000000006du*BBH4aA|NaUv_0~WN&gWa%FLKWpi|MF +J*XRWpH$9Z*FrgaCuNm0Rj{N6aWAK2mnY{22-y4x{03z000OO000~S0000000000006duZXFZ=aA|Na +Uv_0~WN&gWa%FLKWpi|MFKA_Ka4v9pP)h*<6aW+e000O8NLB_@Ox`k*fB^si7XttQ9RL6T000000000 +0wt+_?6aa8(a4%nWWo~3|axZdaadl;LbaO9gZ*OaJE^v8JO928D02BZK00;m`Rt8h9SbGB^CjbC4i~s +;000000000000002CfdV8H0B~t=FJE?LZe(wAFLGsZb!BsOb1!XgWMyn~E^v8JO928D02BZK00;m`Rt +8h0zsD_c0RRBU0ssIY00000000000002CfnZA%0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgfYc +~DCM0u%rg0000807zB_Q)w+5O>Q9o0Ofc903ZMW00000000000Jeb-O%wodX>c!Jc4cm4Z*nhkWpQ<7 +b98erb7gaLX>V?GE^v8JO928D02BZK00;m`Rt8gLXo}LD1^@up5C8xq00000000000002CfxB)L0B~t +=FJE?LZe(wAFLGsZb!BsOb1!prVRUtKUt@1%WpgfYc~DCM0u%rg0000807zB_Qz~N!GKmBL0459o03r +YY00000000000Jeddbrb+_X>c!Jc4cm4Z*nhkWpQ<7b98erb98cbV{~c!Jc4cm4Z*nhkWpQ<7b98erb#!TLb1rasP +)h*<6aW+e000O8NLB_@GCErQ-~a#sGXekrA^-pY0000000000wt@Gd6aa8(a4%nWWo~3|axZdab8l>R +Wo&6;FJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_@sr-r20tWy9f))S(BLDyZ0000000000wt*L;6aa8 +(a4%nWWo~3|axZdab8l>RWo&6;FK}{ic4=f~a&sc!Jc4cm4Z*nhkWpi(Ac4cg7VlQ%KaBp&SWpZ;aaCuNm0Rj{N6aWAK2m +nY{22=N|ddVIV001II001KZ0000000000006duimVg>aA|NaUv_0~WN&gWa%FRGY<6XAX<{#OWpi(Ac +4cyNE^v8JO928D02BZK00;m`Rt8hgWJVxj1pokF6951q00000000000002Cf$6>!0B~t=FJE?LZe(wA +FLGsbZ)|pDY-wUIb98cbV{~&aaCuNm0Rj{N6aWAK2mnY{22%h40006200000001cf0000000000006d +uio_HEaA|NaUv_0~WN&gWa%FRGY<6XAX<{#9Z*6d4bT40DX>MtBUtcb8c~DCM0u%rg0000807zB_Q&u +@VjAsA<0HOc@05AXm00000000000JeeC#1sHc!Jc4cm4Z*nhkWpi(Ac4cg7VlQKFZE#_9FJo_PY- +M9~X>V?GUtwZnE^v8JO928D02BZK00;m`Rt8hU7oJ-O6aWBLNdN#K00000000000002Cfr!Nv0B~t=F +JE?LZe(wAFLY&YVPk1@c`sjIX>MtBUtcb8c~DCM0u%rg0000807zB_QyXJ^?Z5;808R}603ZMW00000 +000000Jee6*%Sb9X>c!Jc4cm4Z*nhmWo}_(X>@rnUtx23ZewY0E^v8JO928D02BZK00;m`Rt8g-BHST +_0{{Tr2LJ#b00000000000002Cfy>?$0B~t=FJE?LZe(wAFLY&YVPk1@c`sjebZKmJE^v8JO928D02B +ZK00;m`Rt8h^O1ty=0ssI81^@sZ00000000000002Cfs5i40B~t=FJE?LZe(wAFLY&YVPk1@c`spRbY +*fbaCuNm0Rj{N6aWAK2mnY{22-xn60F4n004;v0015U0000000000006duz~vMGaA|NaUv_0~WN&gWb +Y*T~V`+4GFJfh8Z*pZWaCuNm0Rj{N6aWAK2mnY{22-qvMZRqV006EB001Na0000000000006du!RQnK +aA|NaUv_0~WN&gWbY*T~V`+4GFJfh8Z*pZ{b8Ka0a4v9pP)h*<6aW+e000O8NLB_@ZF^k3c!Jc4cm4Z*nhmWo}_(X>@rna%FUKc`k5yP)h*<6a +W+e000O8NLB_@1UG#KvjYGCMhgG{8~^|S0000000000wtAjaCuNm0Rj{N6aWAK2mnY{22&UXRF$>^005i_001HY0000000000006duc=;3naA|NaUv_0~WN&gW +bY*T~V`+4GFLZBmZee6^cV%KOaCuNm0Rj{N6aWAK2mnY{22;GDB;%t90080|000~S0000000000006d +uZv7MhaA|NaUv_0~WN&gWbY*T~V`+4GFLz;SbS`jtP)h*<6aW+e000O8NLB_@x;M1&9RL +6T0000000000wt-6q6##H)a4%nWWo~3|axZjmZER^TUtei%X>?y-E^v8JO928D02BZK00;m`Rt8g{E1 +02U6#xKES^xkX00000000000002Cfi(ve0B~t=FJE?LZe(wAFLZBhY-ulFaA9(DWpXZXc~DCM0u%rg0 +000807zB_Q&Po5OWgwi0L%&i02lxO00000000000JeeB92Ed?X>c!Jc4cm4Z*nhmZ*6R8FJE$HE^v8J +O928D02BZK00;m`Rt8fvc>Wb+1ONaa3jhEg00000000000002Cf#o0-0B~t=FJE?LZe(wAFLiQkY-wU +MFJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_@)CoTm91Q>fj4J>DBLDyZ0000000000wt_Gqk06_o%03QGV00 +000000000JeeSG8F)DX>c!Jc4cm4Z*nhna%^mAVlyvac4cyNX>V>WaCuNm0Rj{N6aWAK2mnY{22;jfW +uB@Q003P}001EX0000000000006duVKWr~aA|NaUv_0~WN&gWb#iQMX<{=kV{dM5Wn*+{Z*DGdc~DCM +0u%rg0000807zB_Q#OmQg;pp405+8X03`qb00000000000JecjN)-TbX>c!Jc4cm4Z*nhna%^mAVlyv +eZ*Fd7V{~b6Zg6jJY%XwlP)h*<6aW+e000O8NLB_@LZ-L*{|Nv9Tp<7eApigX0000000000wt?ev6## +H)a4%nWWo~3|axZmqY;0*_GcRR$V`Xr3X>V?GE^v8JO928D02BZK00;m`Rt8hrcP2(N3IG6=A^-p#00 +000000000002Cfgybr0B~t=FJE?LZe(wAFLiQkY-wUMFJ@_FY-DpTaCuNm0Rj{N6aWAK2mnY{22(g-# +tx4I008O<0018V0000000000006dukB1ciaA|NaUv_0~WN&gWb#iQMX<{=kW@&6?aBp*TE^v8JO928D +02BZK00;m`Rt8hgPR;g>761TCPXGWS00000000000002Cfm@3e0B~t=FJE?LZe(wAFLiQkY-wUMFK}; +fY;9p~VP|D>E^v8JO928D02BZK00;m`Rt8fdWyD&u2LJ#I7ytks00000000000002CfheLC0B~t=FJE +?LZe(wAFLiQkY-wUMFLGsZb!BsOE^v8JO928D02BZK00;m`Rt8fUPXO1N9RL7eaR2}x000000000000 +02CffuS30B~t=FJE?LZe(wAFLiQkY-wUMFLGsbaBpsNWiD`eP)h*<6aW+e000O8NLB_@000000ssI20 +0000CjbBd0000000000wt?=)6##H)a4%nWWo~3|axZmqY;0*_GcRLrZgg^KVlQ7`X>MtBUtcb8c~DCM +0u%rg0000807zB_Q=)Lh9=-tp0O11w05Jdn00000000000JecO$Q1x^X>c!Jc4cm4Z*nhna%^mAVlyv +eZ*FvQX<{#5VQ_F|Zf9w3WnX1(c4=~NZZ2?nP)h*<6aW+e000O8NLB_@P-{}3hztM#R4f1hC;$Ke000 +0000000wt+&*6##H)a4%nWWo~3|axZmqY;0*_GcRLrZgg^KVlQEEaAj_1X>MgMaCuNm0Rj{N6aWAK2m +nY{226dM+4&^^033<{04@L +k00000000000Jedg@)ZDZX>c!Jc4cm4Z*nhna%^mAVlyveZ*FvQX<{#PWn*=6Wpr|3ZgX&Na&#_mc~D +CM0u%rg0000807zB_Q^ao>eR2o@0FfL303-ka00000000000JeeQ7#09c!Jc4cm4Z*nhna%^mAVl +yveZ*FvQX<{#PZ)0n7E^v8JO928D02BZK00;m`Rt8f500002000000000u00000000000002Cft4W^0 +B~t=FJE?LZe(wAFLiQkY-wUMFJo_RbaH88FJE(IV|8+6baG*Cb8v5RbT40DX>MtBUtcb8c~DCM0u%rg +0000807zB_Q@74L&lnK^0Qg1#05|{u00000000000JeecAr=5|X>c!Jc4cm4Z*nhna%^mAVlyveZ*Fv +QX<{#5b7f<7a%FUKVQzD9Z*p`mVrgzMn8E^v8JO928D02BZK00;m`Rt8gGl43A^5dZ+-Hvj-R000 +00000000002Cfm<^c0B~t=FJE?LZe(wAFLiQkY-wUMFJo_RbaH88FJE(IV|8+6baG*Cb8v5RbT4dgcV +BE}c4cfXaCuNm0Rj{N6aWAK2mnY{22-0nr^r+Q0040S001Ze0000000000006duFhv#saA|NaUv_0~W +N&gWb#iQMX<{=kaA9L>VP|D?FJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_@3%-|iVIcqjpNjwhBLDyZ +0000000000wt>n;765Q*a4%nWWo~3|axZmqY;0*_GcRyqV{2h&WpgicX?QMhc~DCM0u%rg0000807zB +_Qvd(}00IC20000005Sjo00000000000Jec_XBGf(X>c!Jc4cm4Z*nhna%^mAVlyvrVPk7yXJvCQVqs +%zaBp&Sb1z?CX>MtBUtcb8c~DCM0u%rg0000807zB_Q&=0%AXNeY0K5eN05Sjo00000000000Jed?XB +Gf(X>c!Jc4cm4Z*nhna%^mAVlyvrVPk7yXJvCQVqs%zaBp&Sb1!XSYh`9>Y-KKRc~DCM0u%rg000080 +7zB_Q&L-(TC4#80Jj4G06PEx00000000000Jec+Y8C)+X>c!Jc4cm4Z*nhna%^mAVlyvrVPk7yXJvCQ +b8~E8ZDDj{XkTb=b98QDZDlWCUukY>bYEXCaCuNm0Rj{N6aWAK2mnY{22(2rFrdQ-007Sx002S&0000 +000000006duXlxb$aA|NaUv_0~WN&gWb#iQMX<{=kaA9L>VP|D?FLQHjUu|J@V`yJ!Z*z2RVQpnEUuk +V{Y-Md_ZggREX>V>WaCuNm0Rj{N6aWAK2mnY{22-m$4WHft006WE001Na0000000000006dui**(NaA +|NaUv_0~WN&gWb#iQMX<{=kb#!TLFJE72ZfSI1UoLQYP)h*<6aW+e000O8NLB_@KCa3(vj+eG&JzFtC +IA2c0000000000wt=g6765Q*a4%nWWo~3|axZmqY;0*_GcR>?X>2cJZ*Fd7V{~b6ZZ2?nP)h*<6aW+e +000O8NLB_@=4V`_W&!{Jd?X>2c +Wa&LHfE^v8JO928D02BZK00;m`Rt8hH2WVLY0RR990ssIZ00000000000002Cfk%QC0B~t=FJE?LZe( +wAFLiQkY-wUMFLiWjY%g(jWp!mPaCuNm0Rj{N6aWAK2mnY{22=j+oD2H}0062G001KZ000000000000 +6duih~vaaA|NaUv_0~WN&gWb#iQMX<{=kb#!TLFLGsZb!BsOE^v8JO928D02BZK00;m`Rt8i1ee2q91 +pojc4gdfo00000000000002CfyRjz0B~t=FJE?LZe(wAFLiQkY-wUMFLiWjY%g+Ub8v5Nb7d}Yc~DCM +0u%rg0000807zB_Q{mdEx%n0V0O?f#03iSX00000000000Jed5j}`!MX>c!Jc4cm4Z*nhna%^mAVlyv +wbZKlaa%FUKc`k5yP)h*<6aW+e000O8NLB_@9=48e#ufko2t@z@AOHXW0000000000wt=sv765Q*a4% +nWWo~3|axZmqY;0*_GcR>?X>2cZb8KHOaCuNm0Rj{N6aWAK2mnY{22+TDQfMFu008hC001Ze0000000 +000006duv%MAoaA|NaUv_0~WN&gWb#iQMX<{=kb#!TLFLQHjbaG*Cb8v5RbS`jtP)h*<6aW+e000O8N +LB_@4&ws4*9!muA1D9-BLDyZ0000000000wt*SO765Q*a4%nWWo~3|axZmqY;0*_GcR>?X>2caX>Db1 +b#yLpc~DCM0u%rg0000807zB_Q?xy1vA_}l0FO8T03QGV00000000000JecK(iQ-4X>c!Jc4cm4Z*nh +na%^mAVlyvwbZKlab#iPjaCuNm0Rj{N6aWAK2mnY{22;BBd3DqW006ZW001BW0000000000006duF69 +;gaA|NaUv_0~WN&gWb#iQMX<{=kb#!TLFLz;SbS`jtP)h*<6aW+e000O8NLB_@Qb2~tMGF7`sVV>fBm +e*a0000000000wt+zH765Q*a4%nWWo~3|axZsfVr6b)Z)9n1XLB!KUukY>bYEXCaCuNm0Rj{N6aWAK2 +mnY{22*+RMvV^!008Mn8FKl6A +Wo&aUaCuNm0Rj{N6aWAK2mnY{22-g@cuPtG002}4001Na0000000000006du7XKCiaA|NaUv_0~WN&g +WcV%K_Zewp`X>Mn8FKugVVPa)$b1rasP)h*<6aW+e000O8NLB_@LI9vP#RUKWJQ@H1ApigX00000000 +00wt=Dn7XWZ+a4%nWWo~3|axZsfVr6b)Z)9n1XLB!fWpi|ME^v8JO928D02BZK00;m`Rt8hWd9bdq1O +NaS5&!@y00000000000002Cfu;u+0B~t=FJE?LZe(wAFLz~PWo~0{WNB_^b1!&bb#rBMUu0!wX>Mg?E +^v8JO9ci10001t0gM45d;kER3>N?Z00 +""" + + +if __name__ == "__main__": + main() diff --git a/scripts/build-windows/kcl-doc.go b/scripts/build-windows/kcl-doc.go new file mode 100644 index 000000000..6e0296fa4 --- /dev/null +++ b/scripts/build-windows/kcl-doc.go @@ -0,0 +1,59 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ingore && windows +// +build ingore,windows + +package main + +import ( + "os" + "path/filepath" + "syscall" + "unsafe" +) + +func main() { + // kclvm -m kclvm ... + var args []string + args = append(args, os.Args[0]) + args = append(args, "-m", "kclvm.tools.docs") + args = append(args, os.Args[1:]...) + os.Exit(Py_Main(args)) +} + +var ( + python39_dll = syscall.NewLazyDLL(findKclvm_dllPath()) + proc_Py_Main = python39_dll.NewProc("Py_Main") +) + +// int Py_Main(int argc, wchar_t **argv) +func Py_Main(args []string) int { + c_args := make([]*uint16, len(args)+1) + for i, s := range args { + c_args[i] = syscall.StringToUTF16Ptr(s) + } + ret, _, _ := proc_Py_Main.Call(uintptr(len(args)), uintptr(unsafe.Pointer(&c_args[0]))) + return int(ret) +} + +func findKclvm_dllPath() string { + kclvmName := "python39.dll" + + if exePath, _ := os.Executable(); exePath != "" { + exeDir := filepath.Dir(exePath) + if fi, _ := os.Stat(filepath.Join(exeDir, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(exeDir, kclvmName) + } + } + if wd, _ := os.Getwd(); wd != "" { + if fi, _ := os.Stat(filepath.Join(wd, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(wd, kclvmName) + } + wd = filepath.Join(wd, "_output/kclvm-windows") + if fi, _ := os.Stat(filepath.Join(wd, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(wd, kclvmName) + } + } + + return kclvmName +} diff --git a/scripts/build-windows/kcl-fmt.go b/scripts/build-windows/kcl-fmt.go new file mode 100644 index 000000000..d221e5c65 --- /dev/null +++ b/scripts/build-windows/kcl-fmt.go @@ -0,0 +1,59 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ingore && windows +// +build ingore,windows + +package main + +import ( + "os" + "path/filepath" + "syscall" + "unsafe" +) + +func main() { + // kclvm -m kclvm ... + var args []string + args = append(args, os.Args[0]) + args = append(args, "-m", "kclvm.tools.format") + args = append(args, os.Args[1:]...) + os.Exit(Py_Main(args)) +} + +var ( + python39_dll = syscall.NewLazyDLL(findKclvm_dllPath()) + proc_Py_Main = python39_dll.NewProc("Py_Main") +) + +// int Py_Main(int argc, wchar_t **argv) +func Py_Main(args []string) int { + c_args := make([]*uint16, len(args)+1) + for i, s := range args { + c_args[i] = syscall.StringToUTF16Ptr(s) + } + ret, _, _ := proc_Py_Main.Call(uintptr(len(args)), uintptr(unsafe.Pointer(&c_args[0]))) + return int(ret) +} + +func findKclvm_dllPath() string { + kclvmName := "python39.dll" + + if exePath, _ := os.Executable(); exePath != "" { + exeDir := filepath.Dir(exePath) + if fi, _ := os.Stat(filepath.Join(exeDir, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(exeDir, kclvmName) + } + } + if wd, _ := os.Getwd(); wd != "" { + if fi, _ := os.Stat(filepath.Join(wd, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(wd, kclvmName) + } + wd = filepath.Join(wd, "_output/kclvm-windows") + if fi, _ := os.Stat(filepath.Join(wd, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(wd, kclvmName) + } + } + + return kclvmName +} diff --git a/scripts/build-windows/kcl-lint.go b/scripts/build-windows/kcl-lint.go new file mode 100644 index 000000000..652cecd60 --- /dev/null +++ b/scripts/build-windows/kcl-lint.go @@ -0,0 +1,59 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ingore && windows +// +build ingore,windows + +package main + +import ( + "os" + "path/filepath" + "syscall" + "unsafe" +) + +func main() { + // kclvm -m kclvm ... + var args []string + args = append(args, os.Args[0]) + args = append(args, "-m", "kclvm.tools.lint.lint") + args = append(args, os.Args[1:]...) + os.Exit(Py_Main(args)) +} + +var ( + python39_dll = syscall.NewLazyDLL(findKclvm_dllPath()) + proc_Py_Main = python39_dll.NewProc("Py_Main") +) + +// int Py_Main(int argc, wchar_t **argv) +func Py_Main(args []string) int { + c_args := make([]*uint16, len(args)+1) + for i, s := range args { + c_args[i] = syscall.StringToUTF16Ptr(s) + } + ret, _, _ := proc_Py_Main.Call(uintptr(len(args)), uintptr(unsafe.Pointer(&c_args[0]))) + return int(ret) +} + +func findKclvm_dllPath() string { + kclvmName := "python39.dll" + + if exePath, _ := os.Executable(); exePath != "" { + exeDir := filepath.Dir(exePath) + if fi, _ := os.Stat(filepath.Join(exeDir, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(exeDir, kclvmName) + } + } + if wd, _ := os.Getwd(); wd != "" { + if fi, _ := os.Stat(filepath.Join(wd, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(wd, kclvmName) + } + wd = filepath.Join(wd, "_output/kclvm-windows") + if fi, _ := os.Stat(filepath.Join(wd, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(wd, kclvmName) + } + } + + return kclvmName +} diff --git a/scripts/build-windows/kcl-plugin.go b/scripts/build-windows/kcl-plugin.go new file mode 100644 index 000000000..39b228de5 --- /dev/null +++ b/scripts/build-windows/kcl-plugin.go @@ -0,0 +1,59 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ingore && windows +// +build ingore,windows + +package main + +import ( + "os" + "path/filepath" + "syscall" + "unsafe" +) + +func main() { + // kclvm -m kclvm ... + var args []string + args = append(args, os.Args[0]) + args = append(args, "-m", "kclvm.tools.plugin") + args = append(args, os.Args[1:]...) + os.Exit(Py_Main(args)) +} + +var ( + python39_dll = syscall.NewLazyDLL(findKclvm_dllPath()) + proc_Py_Main = python39_dll.NewProc("Py_Main") +) + +// int Py_Main(int argc, wchar_t **argv) +func Py_Main(args []string) int { + c_args := make([]*uint16, len(args)+1) + for i, s := range args { + c_args[i] = syscall.StringToUTF16Ptr(s) + } + ret, _, _ := proc_Py_Main.Call(uintptr(len(args)), uintptr(unsafe.Pointer(&c_args[0]))) + return int(ret) +} + +func findKclvm_dllPath() string { + kclvmName := "python39.dll" + + if exePath, _ := os.Executable(); exePath != "" { + exeDir := filepath.Dir(exePath) + if fi, _ := os.Stat(filepath.Join(exeDir, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(exeDir, kclvmName) + } + } + if wd, _ := os.Getwd(); wd != "" { + if fi, _ := os.Stat(filepath.Join(wd, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(wd, kclvmName) + } + wd = filepath.Join(wd, "_output/kclvm-windows") + if fi, _ := os.Stat(filepath.Join(wd, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(wd, kclvmName) + } + } + + return kclvmName +} diff --git a/scripts/build-windows/kcl-vet.go b/scripts/build-windows/kcl-vet.go new file mode 100644 index 000000000..e823e1f2d --- /dev/null +++ b/scripts/build-windows/kcl-vet.go @@ -0,0 +1,59 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ingore && windows +// +build ingore,windows + +package main + +import ( + "os" + "path/filepath" + "syscall" + "unsafe" +) + +func main() { + // kclvm -m kclvm ... + var args []string + args = append(args, os.Args[0]) + args = append(args, "-m", "kclvm.tools.validation") + args = append(args, os.Args[1:]...) + os.Exit(Py_Main(args)) +} + +var ( + python39_dll = syscall.NewLazyDLL(findKclvm_dllPath()) + proc_Py_Main = python39_dll.NewProc("Py_Main") +) + +// int Py_Main(int argc, wchar_t **argv) +func Py_Main(args []string) int { + c_args := make([]*uint16, len(args)+1) + for i, s := range args { + c_args[i] = syscall.StringToUTF16Ptr(s) + } + ret, _, _ := proc_Py_Main.Call(uintptr(len(args)), uintptr(unsafe.Pointer(&c_args[0]))) + return int(ret) +} + +func findKclvm_dllPath() string { + kclvmName := "python39.dll" + + if exePath, _ := os.Executable(); exePath != "" { + exeDir := filepath.Dir(exePath) + if fi, _ := os.Stat(filepath.Join(exeDir, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(exeDir, kclvmName) + } + } + if wd, _ := os.Getwd(); wd != "" { + if fi, _ := os.Stat(filepath.Join(wd, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(wd, kclvmName) + } + wd = filepath.Join(wd, "_output/kclvm-windows") + if fi, _ := os.Stat(filepath.Join(wd, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(wd, kclvmName) + } + } + + return kclvmName +} diff --git a/scripts/build-windows/kcl.go b/scripts/build-windows/kcl.go new file mode 100644 index 000000000..6ad9d3b65 --- /dev/null +++ b/scripts/build-windows/kcl.go @@ -0,0 +1,59 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ingore && windows +// +build ingore,windows + +package main + +import ( + "os" + "path/filepath" + "syscall" + "unsafe" +) + +func main() { + // kclvm -m kclvm ... + var args []string + args = append(args, os.Args[0]) + args = append(args, "-m", "kclvm") + args = append(args, os.Args[1:]...) + os.Exit(Py_Main(args)) +} + +var ( + python39_dll = syscall.NewLazyDLL(findKclvm_dllPath()) + proc_Py_Main = python39_dll.NewProc("Py_Main") +) + +// int Py_Main(int argc, wchar_t **argv) +func Py_Main(args []string) int { + c_args := make([]*uint16, len(args)+1) + for i, s := range args { + c_args[i] = syscall.StringToUTF16Ptr(s) + } + ret, _, _ := proc_Py_Main.Call(uintptr(len(args)), uintptr(unsafe.Pointer(&c_args[0]))) + return int(ret) +} + +func findKclvm_dllPath() string { + kclvmName := "python39.dll" + + if exePath, _ := os.Executable(); exePath != "" { + exeDir := filepath.Dir(exePath) + if fi, _ := os.Stat(filepath.Join(exeDir, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(exeDir, kclvmName) + } + } + if wd, _ := os.Getwd(); wd != "" { + if fi, _ := os.Stat(filepath.Join(wd, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(wd, kclvmName) + } + wd = filepath.Join(wd, "_output/kclvm-windows") + if fi, _ := os.Stat(filepath.Join(wd, kclvmName)); fi != nil && !fi.IsDir() { + return filepath.Join(wd, kclvmName) + } + } + + return kclvmName +} diff --git a/scripts/build-windows/py39-libs/include/Python-ast.h b/scripts/build-windows/py39-libs/include/Python-ast.h new file mode 100644 index 000000000..dfa0b1aaf --- /dev/null +++ b/scripts/build-windows/py39-libs/include/Python-ast.h @@ -0,0 +1,697 @@ +/* File automatically generated by Parser/asdl_c.py. */ + +#ifndef Py_PYTHON_AST_H +#define Py_PYTHON_AST_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_LIMITED_API +#include "asdl.h" + +#undef Yield /* undefine macro conflicting with */ + +typedef struct _mod *mod_ty; + +typedef struct _stmt *stmt_ty; + +typedef struct _expr *expr_ty; + +typedef enum _expr_context { Load=1, Store=2, Del=3 } expr_context_ty; + +typedef enum _boolop { And=1, Or=2 } boolop_ty; + +typedef enum _operator { Add=1, Sub=2, Mult=3, MatMult=4, Div=5, Mod=6, Pow=7, + LShift=8, RShift=9, BitOr=10, BitXor=11, BitAnd=12, + FloorDiv=13 } operator_ty; + +typedef enum _unaryop { Invert=1, Not=2, UAdd=3, USub=4 } unaryop_ty; + +typedef enum _cmpop { Eq=1, NotEq=2, Lt=3, LtE=4, Gt=5, GtE=6, Is=7, IsNot=8, + In=9, NotIn=10 } cmpop_ty; + +typedef struct _comprehension *comprehension_ty; + +typedef struct _excepthandler *excepthandler_ty; + +typedef struct _arguments *arguments_ty; + +typedef struct _arg *arg_ty; + +typedef struct _keyword *keyword_ty; + +typedef struct _alias *alias_ty; + +typedef struct _withitem *withitem_ty; + +typedef struct _type_ignore *type_ignore_ty; + + +enum _mod_kind {Module_kind=1, Interactive_kind=2, Expression_kind=3, + FunctionType_kind=4}; +struct _mod { + enum _mod_kind kind; + union { + struct { + asdl_seq *body; + asdl_seq *type_ignores; + } Module; + + struct { + asdl_seq *body; + } Interactive; + + struct { + expr_ty body; + } Expression; + + struct { + asdl_seq *argtypes; + expr_ty returns; + } FunctionType; + + } v; +}; + +enum _stmt_kind {FunctionDef_kind=1, AsyncFunctionDef_kind=2, ClassDef_kind=3, + Return_kind=4, Delete_kind=5, Assign_kind=6, + AugAssign_kind=7, AnnAssign_kind=8, For_kind=9, + AsyncFor_kind=10, While_kind=11, If_kind=12, With_kind=13, + AsyncWith_kind=14, Raise_kind=15, Try_kind=16, + Assert_kind=17, Import_kind=18, ImportFrom_kind=19, + Global_kind=20, Nonlocal_kind=21, Expr_kind=22, Pass_kind=23, + Break_kind=24, Continue_kind=25}; +struct _stmt { + enum _stmt_kind kind; + union { + struct { + identifier name; + arguments_ty args; + asdl_seq *body; + asdl_seq *decorator_list; + expr_ty returns; + string type_comment; + } FunctionDef; + + struct { + identifier name; + arguments_ty args; + asdl_seq *body; + asdl_seq *decorator_list; + expr_ty returns; + string type_comment; + } AsyncFunctionDef; + + struct { + identifier name; + asdl_seq *bases; + asdl_seq *keywords; + asdl_seq *body; + asdl_seq *decorator_list; + } ClassDef; + + struct { + expr_ty value; + } Return; + + struct { + asdl_seq *targets; + } Delete; + + struct { + asdl_seq *targets; + expr_ty value; + string type_comment; + } Assign; + + struct { + expr_ty target; + operator_ty op; + expr_ty value; + } AugAssign; + + struct { + expr_ty target; + expr_ty annotation; + expr_ty value; + int simple; + } AnnAssign; + + struct { + expr_ty target; + expr_ty iter; + asdl_seq *body; + asdl_seq *orelse; + string type_comment; + } For; + + struct { + expr_ty target; + expr_ty iter; + asdl_seq *body; + asdl_seq *orelse; + string type_comment; + } AsyncFor; + + struct { + expr_ty test; + asdl_seq *body; + asdl_seq *orelse; + } While; + + struct { + expr_ty test; + asdl_seq *body; + asdl_seq *orelse; + } If; + + struct { + asdl_seq *items; + asdl_seq *body; + string type_comment; + } With; + + struct { + asdl_seq *items; + asdl_seq *body; + string type_comment; + } AsyncWith; + + struct { + expr_ty exc; + expr_ty cause; + } Raise; + + struct { + asdl_seq *body; + asdl_seq *handlers; + asdl_seq *orelse; + asdl_seq *finalbody; + } Try; + + struct { + expr_ty test; + expr_ty msg; + } Assert; + + struct { + asdl_seq *names; + } Import; + + struct { + identifier module; + asdl_seq *names; + int level; + } ImportFrom; + + struct { + asdl_seq *names; + } Global; + + struct { + asdl_seq *names; + } Nonlocal; + + struct { + expr_ty value; + } Expr; + + } v; + int lineno; + int col_offset; + int end_lineno; + int end_col_offset; +}; + +enum _expr_kind {BoolOp_kind=1, NamedExpr_kind=2, BinOp_kind=3, UnaryOp_kind=4, + Lambda_kind=5, IfExp_kind=6, Dict_kind=7, Set_kind=8, + ListComp_kind=9, SetComp_kind=10, DictComp_kind=11, + GeneratorExp_kind=12, Await_kind=13, Yield_kind=14, + YieldFrom_kind=15, Compare_kind=16, Call_kind=17, + FormattedValue_kind=18, JoinedStr_kind=19, Constant_kind=20, + Attribute_kind=21, Subscript_kind=22, Starred_kind=23, + Name_kind=24, List_kind=25, Tuple_kind=26, Slice_kind=27}; +struct _expr { + enum _expr_kind kind; + union { + struct { + boolop_ty op; + asdl_seq *values; + } BoolOp; + + struct { + expr_ty target; + expr_ty value; + } NamedExpr; + + struct { + expr_ty left; + operator_ty op; + expr_ty right; + } BinOp; + + struct { + unaryop_ty op; + expr_ty operand; + } UnaryOp; + + struct { + arguments_ty args; + expr_ty body; + } Lambda; + + struct { + expr_ty test; + expr_ty body; + expr_ty orelse; + } IfExp; + + struct { + asdl_seq *keys; + asdl_seq *values; + } Dict; + + struct { + asdl_seq *elts; + } Set; + + struct { + expr_ty elt; + asdl_seq *generators; + } ListComp; + + struct { + expr_ty elt; + asdl_seq *generators; + } SetComp; + + struct { + expr_ty key; + expr_ty value; + asdl_seq *generators; + } DictComp; + + struct { + expr_ty elt; + asdl_seq *generators; + } GeneratorExp; + + struct { + expr_ty value; + } Await; + + struct { + expr_ty value; + } Yield; + + struct { + expr_ty value; + } YieldFrom; + + struct { + expr_ty left; + asdl_int_seq *ops; + asdl_seq *comparators; + } Compare; + + struct { + expr_ty func; + asdl_seq *args; + asdl_seq *keywords; + } Call; + + struct { + expr_ty value; + int conversion; + expr_ty format_spec; + } FormattedValue; + + struct { + asdl_seq *values; + } JoinedStr; + + struct { + constant value; + string kind; + } Constant; + + struct { + expr_ty value; + identifier attr; + expr_context_ty ctx; + } Attribute; + + struct { + expr_ty value; + expr_ty slice; + expr_context_ty ctx; + } Subscript; + + struct { + expr_ty value; + expr_context_ty ctx; + } Starred; + + struct { + identifier id; + expr_context_ty ctx; + } Name; + + struct { + asdl_seq *elts; + expr_context_ty ctx; + } List; + + struct { + asdl_seq *elts; + expr_context_ty ctx; + } Tuple; + + struct { + expr_ty lower; + expr_ty upper; + expr_ty step; + } Slice; + + } v; + int lineno; + int col_offset; + int end_lineno; + int end_col_offset; +}; + +struct _comprehension { + expr_ty target; + expr_ty iter; + asdl_seq *ifs; + int is_async; +}; + +enum _excepthandler_kind {ExceptHandler_kind=1}; +struct _excepthandler { + enum _excepthandler_kind kind; + union { + struct { + expr_ty type; + identifier name; + asdl_seq *body; + } ExceptHandler; + + } v; + int lineno; + int col_offset; + int end_lineno; + int end_col_offset; +}; + +struct _arguments { + asdl_seq *posonlyargs; + asdl_seq *args; + arg_ty vararg; + asdl_seq *kwonlyargs; + asdl_seq *kw_defaults; + arg_ty kwarg; + asdl_seq *defaults; +}; + +struct _arg { + identifier arg; + expr_ty annotation; + string type_comment; + int lineno; + int col_offset; + int end_lineno; + int end_col_offset; +}; + +struct _keyword { + identifier arg; + expr_ty value; + int lineno; + int col_offset; + int end_lineno; + int end_col_offset; +}; + +struct _alias { + identifier name; + identifier asname; +}; + +struct _withitem { + expr_ty context_expr; + expr_ty optional_vars; +}; + +enum _type_ignore_kind {TypeIgnore_kind=1}; +struct _type_ignore { + enum _type_ignore_kind kind; + union { + struct { + int lineno; + string tag; + } TypeIgnore; + + } v; +}; + + +// Note: these macros affect function definitions, not only call sites. +#define Module(a0, a1, a2) _Py_Module(a0, a1, a2) +mod_ty _Py_Module(asdl_seq * body, asdl_seq * type_ignores, PyArena *arena); +#define Interactive(a0, a1) _Py_Interactive(a0, a1) +mod_ty _Py_Interactive(asdl_seq * body, PyArena *arena); +#define Expression(a0, a1) _Py_Expression(a0, a1) +mod_ty _Py_Expression(expr_ty body, PyArena *arena); +#define FunctionType(a0, a1, a2) _Py_FunctionType(a0, a1, a2) +mod_ty _Py_FunctionType(asdl_seq * argtypes, expr_ty returns, PyArena *arena); +#define FunctionDef(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) _Py_FunctionDef(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) +stmt_ty _Py_FunctionDef(identifier name, arguments_ty args, asdl_seq * body, + asdl_seq * decorator_list, expr_ty returns, string + type_comment, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +#define AsyncFunctionDef(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) _Py_AsyncFunctionDef(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) +stmt_ty _Py_AsyncFunctionDef(identifier name, arguments_ty args, asdl_seq * + body, asdl_seq * decorator_list, expr_ty returns, + string type_comment, int lineno, int col_offset, + int end_lineno, int end_col_offset, PyArena + *arena); +#define ClassDef(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) _Py_ClassDef(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) +stmt_ty _Py_ClassDef(identifier name, asdl_seq * bases, asdl_seq * keywords, + asdl_seq * body, asdl_seq * decorator_list, int lineno, + int col_offset, int end_lineno, int end_col_offset, + PyArena *arena); +#define Return(a0, a1, a2, a3, a4, a5) _Py_Return(a0, a1, a2, a3, a4, a5) +stmt_ty _Py_Return(expr_ty value, int lineno, int col_offset, int end_lineno, + int end_col_offset, PyArena *arena); +#define Delete(a0, a1, a2, a3, a4, a5) _Py_Delete(a0, a1, a2, a3, a4, a5) +stmt_ty _Py_Delete(asdl_seq * targets, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +#define Assign(a0, a1, a2, a3, a4, a5, a6, a7) _Py_Assign(a0, a1, a2, a3, a4, a5, a6, a7) +stmt_ty _Py_Assign(asdl_seq * targets, expr_ty value, string type_comment, int + lineno, int col_offset, int end_lineno, int end_col_offset, + PyArena *arena); +#define AugAssign(a0, a1, a2, a3, a4, a5, a6, a7) _Py_AugAssign(a0, a1, a2, a3, a4, a5, a6, a7) +stmt_ty _Py_AugAssign(expr_ty target, operator_ty op, expr_ty value, int + lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define AnnAssign(a0, a1, a2, a3, a4, a5, a6, a7, a8) _Py_AnnAssign(a0, a1, a2, a3, a4, a5, a6, a7, a8) +stmt_ty _Py_AnnAssign(expr_ty target, expr_ty annotation, expr_ty value, int + simple, int lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define For(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) _Py_For(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) +stmt_ty _Py_For(expr_ty target, expr_ty iter, asdl_seq * body, asdl_seq * + orelse, string type_comment, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +#define AsyncFor(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) _Py_AsyncFor(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) +stmt_ty _Py_AsyncFor(expr_ty target, expr_ty iter, asdl_seq * body, asdl_seq * + orelse, string type_comment, int lineno, int col_offset, + int end_lineno, int end_col_offset, PyArena *arena); +#define While(a0, a1, a2, a3, a4, a5, a6, a7) _Py_While(a0, a1, a2, a3, a4, a5, a6, a7) +stmt_ty _Py_While(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno, + int col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define If(a0, a1, a2, a3, a4, a5, a6, a7) _Py_If(a0, a1, a2, a3, a4, a5, a6, a7) +stmt_ty _Py_If(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno, + int col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define With(a0, a1, a2, a3, a4, a5, a6, a7) _Py_With(a0, a1, a2, a3, a4, a5, a6, a7) +stmt_ty _Py_With(asdl_seq * items, asdl_seq * body, string type_comment, int + lineno, int col_offset, int end_lineno, int end_col_offset, + PyArena *arena); +#define AsyncWith(a0, a1, a2, a3, a4, a5, a6, a7) _Py_AsyncWith(a0, a1, a2, a3, a4, a5, a6, a7) +stmt_ty _Py_AsyncWith(asdl_seq * items, asdl_seq * body, string type_comment, + int lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define Raise(a0, a1, a2, a3, a4, a5, a6) _Py_Raise(a0, a1, a2, a3, a4, a5, a6) +stmt_ty _Py_Raise(expr_ty exc, expr_ty cause, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +#define Try(a0, a1, a2, a3, a4, a5, a6, a7, a8) _Py_Try(a0, a1, a2, a3, a4, a5, a6, a7, a8) +stmt_ty _Py_Try(asdl_seq * body, asdl_seq * handlers, asdl_seq * orelse, + asdl_seq * finalbody, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +#define Assert(a0, a1, a2, a3, a4, a5, a6) _Py_Assert(a0, a1, a2, a3, a4, a5, a6) +stmt_ty _Py_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +#define Import(a0, a1, a2, a3, a4, a5) _Py_Import(a0, a1, a2, a3, a4, a5) +stmt_ty _Py_Import(asdl_seq * names, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +#define ImportFrom(a0, a1, a2, a3, a4, a5, a6, a7) _Py_ImportFrom(a0, a1, a2, a3, a4, a5, a6, a7) +stmt_ty _Py_ImportFrom(identifier module, asdl_seq * names, int level, int + lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define Global(a0, a1, a2, a3, a4, a5) _Py_Global(a0, a1, a2, a3, a4, a5) +stmt_ty _Py_Global(asdl_seq * names, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +#define Nonlocal(a0, a1, a2, a3, a4, a5) _Py_Nonlocal(a0, a1, a2, a3, a4, a5) +stmt_ty _Py_Nonlocal(asdl_seq * names, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +#define Expr(a0, a1, a2, a3, a4, a5) _Py_Expr(a0, a1, a2, a3, a4, a5) +stmt_ty _Py_Expr(expr_ty value, int lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define Pass(a0, a1, a2, a3, a4) _Py_Pass(a0, a1, a2, a3, a4) +stmt_ty _Py_Pass(int lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define Break(a0, a1, a2, a3, a4) _Py_Break(a0, a1, a2, a3, a4) +stmt_ty _Py_Break(int lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define Continue(a0, a1, a2, a3, a4) _Py_Continue(a0, a1, a2, a3, a4) +stmt_ty _Py_Continue(int lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define BoolOp(a0, a1, a2, a3, a4, a5, a6) _Py_BoolOp(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_BoolOp(boolop_ty op, asdl_seq * values, int lineno, int col_offset, + int end_lineno, int end_col_offset, PyArena *arena); +#define NamedExpr(a0, a1, a2, a3, a4, a5, a6) _Py_NamedExpr(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_NamedExpr(expr_ty target, expr_ty value, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define BinOp(a0, a1, a2, a3, a4, a5, a6, a7) _Py_BinOp(a0, a1, a2, a3, a4, a5, a6, a7) +expr_ty _Py_BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define UnaryOp(a0, a1, a2, a3, a4, a5, a6) _Py_UnaryOp(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_UnaryOp(unaryop_ty op, expr_ty operand, int lineno, int col_offset, + int end_lineno, int end_col_offset, PyArena *arena); +#define Lambda(a0, a1, a2, a3, a4, a5, a6) _Py_Lambda(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_Lambda(arguments_ty args, expr_ty body, int lineno, int col_offset, + int end_lineno, int end_col_offset, PyArena *arena); +#define IfExp(a0, a1, a2, a3, a4, a5, a6, a7) _Py_IfExp(a0, a1, a2, a3, a4, a5, a6, a7) +expr_ty _Py_IfExp(expr_ty test, expr_ty body, expr_ty orelse, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define Dict(a0, a1, a2, a3, a4, a5, a6) _Py_Dict(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_Dict(asdl_seq * keys, asdl_seq * values, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define Set(a0, a1, a2, a3, a4, a5) _Py_Set(a0, a1, a2, a3, a4, a5) +expr_ty _Py_Set(asdl_seq * elts, int lineno, int col_offset, int end_lineno, + int end_col_offset, PyArena *arena); +#define ListComp(a0, a1, a2, a3, a4, a5, a6) _Py_ListComp(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_ListComp(expr_ty elt, asdl_seq * generators, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define SetComp(a0, a1, a2, a3, a4, a5, a6) _Py_SetComp(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_SetComp(expr_ty elt, asdl_seq * generators, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define DictComp(a0, a1, a2, a3, a4, a5, a6, a7) _Py_DictComp(a0, a1, a2, a3, a4, a5, a6, a7) +expr_ty _Py_DictComp(expr_ty key, expr_ty value, asdl_seq * generators, int + lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define GeneratorExp(a0, a1, a2, a3, a4, a5, a6) _Py_GeneratorExp(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_GeneratorExp(expr_ty elt, asdl_seq * generators, int lineno, int + col_offset, int end_lineno, int end_col_offset, + PyArena *arena); +#define Await(a0, a1, a2, a3, a4, a5) _Py_Await(a0, a1, a2, a3, a4, a5) +expr_ty _Py_Await(expr_ty value, int lineno, int col_offset, int end_lineno, + int end_col_offset, PyArena *arena); +#define Yield(a0, a1, a2, a3, a4, a5) _Py_Yield(a0, a1, a2, a3, a4, a5) +expr_ty _Py_Yield(expr_ty value, int lineno, int col_offset, int end_lineno, + int end_col_offset, PyArena *arena); +#define YieldFrom(a0, a1, a2, a3, a4, a5) _Py_YieldFrom(a0, a1, a2, a3, a4, a5) +expr_ty _Py_YieldFrom(expr_ty value, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +#define Compare(a0, a1, a2, a3, a4, a5, a6, a7) _Py_Compare(a0, a1, a2, a3, a4, a5, a6, a7) +expr_ty _Py_Compare(expr_ty left, asdl_int_seq * ops, asdl_seq * comparators, + int lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define Call(a0, a1, a2, a3, a4, a5, a6, a7) _Py_Call(a0, a1, a2, a3, a4, a5, a6, a7) +expr_ty _Py_Call(expr_ty func, asdl_seq * args, asdl_seq * keywords, int + lineno, int col_offset, int end_lineno, int end_col_offset, + PyArena *arena); +#define FormattedValue(a0, a1, a2, a3, a4, a5, a6, a7) _Py_FormattedValue(a0, a1, a2, a3, a4, a5, a6, a7) +expr_ty _Py_FormattedValue(expr_ty value, int conversion, expr_ty format_spec, + int lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define JoinedStr(a0, a1, a2, a3, a4, a5) _Py_JoinedStr(a0, a1, a2, a3, a4, a5) +expr_ty _Py_JoinedStr(asdl_seq * values, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena *arena); +#define Constant(a0, a1, a2, a3, a4, a5, a6) _Py_Constant(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_Constant(constant value, string kind, int lineno, int col_offset, + int end_lineno, int end_col_offset, PyArena *arena); +#define Attribute(a0, a1, a2, a3, a4, a5, a6, a7) _Py_Attribute(a0, a1, a2, a3, a4, a5, a6, a7) +expr_ty _Py_Attribute(expr_ty value, identifier attr, expr_context_ty ctx, int + lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define Subscript(a0, a1, a2, a3, a4, a5, a6, a7) _Py_Subscript(a0, a1, a2, a3, a4, a5, a6, a7) +expr_ty _Py_Subscript(expr_ty value, expr_ty slice, expr_context_ty ctx, int + lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena); +#define Starred(a0, a1, a2, a3, a4, a5, a6) _Py_Starred(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_Starred(expr_ty value, expr_context_ty ctx, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define Name(a0, a1, a2, a3, a4, a5, a6) _Py_Name(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_Name(identifier id, expr_context_ty ctx, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define List(a0, a1, a2, a3, a4, a5, a6) _Py_List(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_List(asdl_seq * elts, expr_context_ty ctx, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define Tuple(a0, a1, a2, a3, a4, a5, a6) _Py_Tuple(a0, a1, a2, a3, a4, a5, a6) +expr_ty _Py_Tuple(asdl_seq * elts, expr_context_ty ctx, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define Slice(a0, a1, a2, a3, a4, a5, a6, a7) _Py_Slice(a0, a1, a2, a3, a4, a5, a6, a7) +expr_ty _Py_Slice(expr_ty lower, expr_ty upper, expr_ty step, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define comprehension(a0, a1, a2, a3, a4) _Py_comprehension(a0, a1, a2, a3, a4) +comprehension_ty _Py_comprehension(expr_ty target, expr_ty iter, asdl_seq * + ifs, int is_async, PyArena *arena); +#define ExceptHandler(a0, a1, a2, a3, a4, a5, a6, a7) _Py_ExceptHandler(a0, a1, a2, a3, a4, a5, a6, a7) +excepthandler_ty _Py_ExceptHandler(expr_ty type, identifier name, asdl_seq * + body, int lineno, int col_offset, int + end_lineno, int end_col_offset, PyArena + *arena); +#define arguments(a0, a1, a2, a3, a4, a5, a6, a7) _Py_arguments(a0, a1, a2, a3, a4, a5, a6, a7) +arguments_ty _Py_arguments(asdl_seq * posonlyargs, asdl_seq * args, arg_ty + vararg, asdl_seq * kwonlyargs, asdl_seq * + kw_defaults, arg_ty kwarg, asdl_seq * defaults, + PyArena *arena); +#define arg(a0, a1, a2, a3, a4, a5, a6, a7) _Py_arg(a0, a1, a2, a3, a4, a5, a6, a7) +arg_ty _Py_arg(identifier arg, expr_ty annotation, string type_comment, int + lineno, int col_offset, int end_lineno, int end_col_offset, + PyArena *arena); +#define keyword(a0, a1, a2, a3, a4, a5, a6) _Py_keyword(a0, a1, a2, a3, a4, a5, a6) +keyword_ty _Py_keyword(identifier arg, expr_ty value, int lineno, int + col_offset, int end_lineno, int end_col_offset, PyArena + *arena); +#define alias(a0, a1, a2) _Py_alias(a0, a1, a2) +alias_ty _Py_alias(identifier name, identifier asname, PyArena *arena); +#define withitem(a0, a1, a2) _Py_withitem(a0, a1, a2) +withitem_ty _Py_withitem(expr_ty context_expr, expr_ty optional_vars, PyArena + *arena); +#define TypeIgnore(a0, a1, a2) _Py_TypeIgnore(a0, a1, a2) +type_ignore_ty _Py_TypeIgnore(int lineno, string tag, PyArena *arena); + +PyObject* PyAST_mod2obj(mod_ty t); +mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode); +int PyAST_Check(PyObject* obj); +#endif /* !Py_LIMITED_API */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYTHON_AST_H */ diff --git a/scripts/build-windows/py39-libs/include/Python.h b/scripts/build-windows/py39-libs/include/Python.h new file mode 100644 index 000000000..613453db4 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/Python.h @@ -0,0 +1,172 @@ +#ifndef Py_PYTHON_H +#define Py_PYTHON_H +/* Since this is a "meta-include" file, no #ifdef __cplusplus / extern "C" { */ + +/* Include nearly all Python header files */ + +#include "patchlevel.h" +#include "pyconfig.h" +#include "pymacconfig.h" + +#include + +#ifndef UCHAR_MAX +#error "Something's broken. UCHAR_MAX should be defined in limits.h." +#endif + +#if UCHAR_MAX != 255 +#error "Python's source code assumes C's unsigned char is an 8-bit type." +#endif + +#if defined(__sgi) && !defined(_SGI_MP_SOURCE) +#define _SGI_MP_SOURCE +#endif + +#include +#ifndef NULL +# error "Python.h requires that stdio.h define NULL." +#endif + +#include +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#ifndef MS_WINDOWS +#include +#endif +#ifdef HAVE_CRYPT_H +#if defined(HAVE_CRYPT_R) && !defined(_GNU_SOURCE) +/* Required for glibc to expose the crypt_r() function prototype. */ +# define _GNU_SOURCE +# define _Py_GNU_SOURCE_FOR_CRYPT +#endif +#include +#ifdef _Py_GNU_SOURCE_FOR_CRYPT +/* Don't leak the _GNU_SOURCE define to other headers. */ +# undef _GNU_SOURCE +# undef _Py_GNU_SOURCE_FOR_CRYPT +#endif +#endif + +/* For size_t? */ +#ifdef HAVE_STDDEF_H +#include +#endif + +/* CAUTION: Build setups should ensure that NDEBUG is defined on the + * compiler command line when building Python in release mode; else + * assert() calls won't be removed. + */ +#include + +#include "pyport.h" +#include "pymacro.h" + +/* A convenient way for code to know if sanitizers are enabled. */ +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) +# if !defined(_Py_MEMORY_SANITIZER) +# define _Py_MEMORY_SANITIZER +# endif +# endif +# if __has_feature(address_sanitizer) +# if !defined(_Py_ADDRESS_SANITIZER) +# define _Py_ADDRESS_SANITIZER +# endif +# endif +#elif defined(__GNUC__) +# if defined(__SANITIZE_ADDRESS__) +# define _Py_ADDRESS_SANITIZER +# endif +#endif + +/* Debug-mode build with pymalloc implies PYMALLOC_DEBUG. + * PYMALLOC_DEBUG is in error if pymalloc is not in use. + */ +#if defined(Py_DEBUG) && defined(WITH_PYMALLOC) && !defined(PYMALLOC_DEBUG) +#define PYMALLOC_DEBUG +#endif +#if defined(PYMALLOC_DEBUG) && !defined(WITH_PYMALLOC) +#error "PYMALLOC_DEBUG requires WITH_PYMALLOC" +#endif +#include "pymath.h" +#include "pytime.h" +#include "pymem.h" + +#include "object.h" +#include "objimpl.h" +#include "typeslots.h" +#include "pyhash.h" + +#include "pydebug.h" + +#include "bytearrayobject.h" +#include "bytesobject.h" +#include "unicodeobject.h" +#include "longobject.h" +#include "longintrepr.h" +#include "boolobject.h" +#include "floatobject.h" +#include "complexobject.h" +#include "rangeobject.h" +#include "memoryobject.h" +#include "tupleobject.h" +#include "listobject.h" +#include "dictobject.h" +#include "odictobject.h" +#include "enumobject.h" +#include "setobject.h" +#include "methodobject.h" +#include "moduleobject.h" +#include "funcobject.h" +#include "classobject.h" +#include "fileobject.h" +#include "pycapsule.h" +#include "code.h" +#include "pyframe.h" +#include "traceback.h" +#include "sliceobject.h" +#include "cellobject.h" +#include "iterobject.h" +#include "genobject.h" +#include "descrobject.h" +#include "genericaliasobject.h" +#include "warnings.h" +#include "weakrefobject.h" +#include "structseq.h" +#include "namespaceobject.h" +#include "picklebufobject.h" + +#include "codecs.h" +#include "pyerrors.h" + +#include "cpython/initconfig.h" +#include "pythread.h" +#include "pystate.h" +#include "context.h" + +#include "pyarena.h" +#include "modsupport.h" +#include "compile.h" +#include "pythonrun.h" +#include "pylifecycle.h" +#include "ceval.h" +#include "sysmodule.h" +#include "osmodule.h" +#include "intrcheck.h" +#include "import.h" + +#include "abstract.h" +#include "bltinmodule.h" + +#include "eval.h" + +#include "pyctype.h" +#include "pystrtod.h" +#include "pystrcmp.h" +#include "fileutils.h" +#include "pyfpe.h" +#include "tracemalloc.h" + +#endif /* !Py_PYTHON_H */ diff --git a/scripts/build-windows/py39-libs/include/abstract.h b/scripts/build-windows/py39-libs/include/abstract.h new file mode 100644 index 000000000..bb51c668a --- /dev/null +++ b/scripts/build-windows/py39-libs/include/abstract.h @@ -0,0 +1,850 @@ +/* Abstract Object Interface (many thanks to Jim Fulton) */ + +#ifndef Py_ABSTRACTOBJECT_H +#define Py_ABSTRACTOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* === Object Protocol ================================================== */ + +/* Implemented elsewhere: + + int PyObject_Print(PyObject *o, FILE *fp, int flags); + + Print an object 'o' on file 'fp'. Returns -1 on error. The flags argument + is used to enable certain printing options. The only option currently + supported is Py_Print_RAW. + + (What should be said about Py_Print_RAW?). */ + + +/* Implemented elsewhere: + + int PyObject_HasAttrString(PyObject *o, const char *attr_name); + + Returns 1 if object 'o' has the attribute attr_name, and 0 otherwise. + + This is equivalent to the Python expression: hasattr(o,attr_name). + + This function always succeeds. */ + + +/* Implemented elsewhere: + + PyObject* PyObject_GetAttrString(PyObject *o, const char *attr_name); + + Retrieve an attributed named attr_name form object o. + Returns the attribute value on success, or NULL on failure. + + This is the equivalent of the Python expression: o.attr_name. */ + + +/* Implemented elsewhere: + + int PyObject_HasAttr(PyObject *o, PyObject *attr_name); + + Returns 1 if o has the attribute attr_name, and 0 otherwise. + + This is equivalent to the Python expression: hasattr(o,attr_name). + + This function always succeeds. */ + +/* Implemented elsewhere: + + PyObject* PyObject_GetAttr(PyObject *o, PyObject *attr_name); + + Retrieve an attributed named 'attr_name' form object 'o'. + Returns the attribute value on success, or NULL on failure. + + This is the equivalent of the Python expression: o.attr_name. */ + + +/* Implemented elsewhere: + + int PyObject_SetAttrString(PyObject *o, const char *attr_name, PyObject *v); + + Set the value of the attribute named attr_name, for object 'o', + to the value 'v'. Raise an exception and return -1 on failure; return 0 on + success. + + This is the equivalent of the Python statement o.attr_name=v. */ + + +/* Implemented elsewhere: + + int PyObject_SetAttr(PyObject *o, PyObject *attr_name, PyObject *v); + + Set the value of the attribute named attr_name, for object 'o', to the value + 'v'. an exception and return -1 on failure; return 0 on success. + + This is the equivalent of the Python statement o.attr_name=v. */ + +/* Implemented as a macro: + + int PyObject_DelAttrString(PyObject *o, const char *attr_name); + + Delete attribute named attr_name, for object o. Returns + -1 on failure. + + This is the equivalent of the Python statement: del o.attr_name. */ +#define PyObject_DelAttrString(O,A) PyObject_SetAttrString((O),(A), NULL) + + +/* Implemented as a macro: + + int PyObject_DelAttr(PyObject *o, PyObject *attr_name); + + Delete attribute named attr_name, for object o. Returns -1 + on failure. This is the equivalent of the Python + statement: del o.attr_name. */ +#define PyObject_DelAttr(O,A) PyObject_SetAttr((O),(A), NULL) + + +/* Implemented elsewhere: + + PyObject *PyObject_Repr(PyObject *o); + + Compute the string representation of object 'o'. Returns the + string representation on success, NULL on failure. + + This is the equivalent of the Python expression: repr(o). + + Called by the repr() built-in function. */ + + +/* Implemented elsewhere: + + PyObject *PyObject_Str(PyObject *o); + + Compute the string representation of object, o. Returns the + string representation on success, NULL on failure. + + This is the equivalent of the Python expression: str(o). + + Called by the str() and print() built-in functions. */ + + +/* Declared elsewhere + + PyAPI_FUNC(int) PyCallable_Check(PyObject *o); + + Determine if the object, o, is callable. Return 1 if the object is callable + and 0 otherwise. + + This function always succeeds. */ + + +#ifdef PY_SSIZE_T_CLEAN +# define PyObject_CallFunction _PyObject_CallFunction_SizeT +# define PyObject_CallMethod _PyObject_CallMethod_SizeT +#endif + + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000 +/* Call a callable Python object without any arguments */ +PyAPI_FUNC(PyObject *) PyObject_CallNoArgs(PyObject *func); +#endif + + +/* Call a callable Python object 'callable' with arguments given by the + tuple 'args' and keywords arguments given by the dictionary 'kwargs'. + + 'args' must not be NULL, use an empty tuple if no arguments are + needed. If no named arguments are needed, 'kwargs' can be NULL. + + This is the equivalent of the Python expression: + callable(*args, **kwargs). */ +PyAPI_FUNC(PyObject *) PyObject_Call(PyObject *callable, + PyObject *args, PyObject *kwargs); + + +/* Call a callable Python object 'callable', with arguments given by the + tuple 'args'. If no arguments are needed, then 'args' can be NULL. + + Returns the result of the call on success, or NULL on failure. + + This is the equivalent of the Python expression: + callable(*args). */ +PyAPI_FUNC(PyObject *) PyObject_CallObject(PyObject *callable, + PyObject *args); + +/* Call a callable Python object, callable, with a variable number of C + arguments. The C arguments are described using a mkvalue-style format + string. + + The format may be NULL, indicating that no arguments are provided. + + Returns the result of the call on success, or NULL on failure. + + This is the equivalent of the Python expression: + callable(arg1, arg2, ...). */ +PyAPI_FUNC(PyObject *) PyObject_CallFunction(PyObject *callable, + const char *format, ...); + +/* Call the method named 'name' of object 'obj' with a variable number of + C arguments. The C arguments are described by a mkvalue format string. + + The format can be NULL, indicating that no arguments are provided. + + Returns the result of the call on success, or NULL on failure. + + This is the equivalent of the Python expression: + obj.name(arg1, arg2, ...). */ +PyAPI_FUNC(PyObject *) PyObject_CallMethod(PyObject *obj, + const char *name, + const char *format, ...); + +PyAPI_FUNC(PyObject *) _PyObject_CallFunction_SizeT(PyObject *callable, + const char *format, + ...); + +PyAPI_FUNC(PyObject *) _PyObject_CallMethod_SizeT(PyObject *obj, + const char *name, + const char *format, + ...); + +/* Call a callable Python object 'callable' with a variable number of C + arguments. The C arguments are provided as PyObject* values, terminated + by a NULL. + + Returns the result of the call on success, or NULL on failure. + + This is the equivalent of the Python expression: + callable(arg1, arg2, ...). */ +PyAPI_FUNC(PyObject *) PyObject_CallFunctionObjArgs(PyObject *callable, + ...); + +/* Call the method named 'name' of object 'obj' with a variable number of + C arguments. The C arguments are provided as PyObject* values, terminated + by NULL. + + Returns the result of the call on success, or NULL on failure. + + This is the equivalent of the Python expression: obj.name(*args). */ + +PyAPI_FUNC(PyObject *) PyObject_CallMethodObjArgs( + PyObject *obj, + PyObject *name, + ...); + + +/* Implemented elsewhere: + + Py_hash_t PyObject_Hash(PyObject *o); + + Compute and return the hash, hash_value, of an object, o. On + failure, return -1. + + This is the equivalent of the Python expression: hash(o). */ + + +/* Implemented elsewhere: + + int PyObject_IsTrue(PyObject *o); + + Returns 1 if the object, o, is considered to be true, 0 if o is + considered to be false and -1 on failure. + + This is equivalent to the Python expression: not not o. */ + + +/* Implemented elsewhere: + + int PyObject_Not(PyObject *o); + + Returns 0 if the object, o, is considered to be true, 1 if o is + considered to be false and -1 on failure. + + This is equivalent to the Python expression: not o. */ + + +/* Get the type of an object. + + On success, returns a type object corresponding to the object type of object + 'o'. On failure, returns NULL. + + This is equivalent to the Python expression: type(o) */ +PyAPI_FUNC(PyObject *) PyObject_Type(PyObject *o); + + +/* Return the size of object 'o'. If the object 'o' provides both sequence and + mapping protocols, the sequence size is returned. + + On error, -1 is returned. + + This is the equivalent to the Python expression: len(o) */ +PyAPI_FUNC(Py_ssize_t) PyObject_Size(PyObject *o); + + +/* For DLL compatibility */ +#undef PyObject_Length +PyAPI_FUNC(Py_ssize_t) PyObject_Length(PyObject *o); +#define PyObject_Length PyObject_Size + +/* Return element of 'o' corresponding to the object 'key'. Return NULL + on failure. + + This is the equivalent of the Python expression: o[key] */ +PyAPI_FUNC(PyObject *) PyObject_GetItem(PyObject *o, PyObject *key); + + +/* Map the object 'key' to the value 'v' into 'o'. + + Raise an exception and return -1 on failure; return 0 on success. + + This is the equivalent of the Python statement: o[key]=v. */ +PyAPI_FUNC(int) PyObject_SetItem(PyObject *o, PyObject *key, PyObject *v); + +/* Remove the mapping for the string 'key' from the object 'o'. + Returns -1 on failure. + + This is equivalent to the Python statement: del o[key]. */ +PyAPI_FUNC(int) PyObject_DelItemString(PyObject *o, const char *key); + +/* Delete the mapping for the object 'key' from the object 'o'. + Returns -1 on failure. + + This is the equivalent of the Python statement: del o[key]. */ +PyAPI_FUNC(int) PyObject_DelItem(PyObject *o, PyObject *key); + + +/* === Old Buffer API ============================================ */ + +/* FIXME: usage of these should all be replaced in Python itself + but for backwards compatibility we will implement them. + Their usage without a corresponding "unlock" mechanism + may create issues (but they would already be there). */ + +/* Takes an arbitrary object which must support the (character, single segment) + buffer interface and returns a pointer to a read-only memory location + useable as character based input for subsequent processing. + + Return 0 on success. buffer and buffer_len are only set in case no error + occurs. Otherwise, -1 is returned and an exception set. */ +Py_DEPRECATED(3.0) +PyAPI_FUNC(int) PyObject_AsCharBuffer(PyObject *obj, + const char **buffer, + Py_ssize_t *buffer_len); + +/* Checks whether an arbitrary object supports the (character, single segment) + buffer interface. + + Returns 1 on success, 0 on failure. */ +Py_DEPRECATED(3.0) PyAPI_FUNC(int) PyObject_CheckReadBuffer(PyObject *obj); + +/* Same as PyObject_AsCharBuffer() except that this API expects (readable, + single segment) buffer interface and returns a pointer to a read-only memory + location which can contain arbitrary data. + + 0 is returned on success. buffer and buffer_len are only set in case no + error occurs. Otherwise, -1 is returned and an exception set. */ +Py_DEPRECATED(3.0) +PyAPI_FUNC(int) PyObject_AsReadBuffer(PyObject *obj, + const void **buffer, + Py_ssize_t *buffer_len); + +/* Takes an arbitrary object which must support the (writable, single segment) + buffer interface and returns a pointer to a writable memory location in + buffer of size 'buffer_len'. + + Return 0 on success. buffer and buffer_len are only set in case no error + occurs. Otherwise, -1 is returned and an exception set. */ +Py_DEPRECATED(3.0) +PyAPI_FUNC(int) PyObject_AsWriteBuffer(PyObject *obj, + void **buffer, + Py_ssize_t *buffer_len); + + +/* === New Buffer API ============================================ */ + +/* Takes an arbitrary object and returns the result of calling + obj.__format__(format_spec). */ +PyAPI_FUNC(PyObject *) PyObject_Format(PyObject *obj, + PyObject *format_spec); + + +/* ==== Iterators ================================================ */ + +/* Takes an object and returns an iterator for it. + This is typically a new iterator but if the argument is an iterator, this + returns itself. */ +PyAPI_FUNC(PyObject *) PyObject_GetIter(PyObject *); + +/* Returns 1 if the object 'obj' provides iterator protocols, and 0 otherwise. + + This function always succeeds. */ +PyAPI_FUNC(int) PyIter_Check(PyObject *); + +/* Takes an iterator object and calls its tp_iternext slot, + returning the next value. + + If the iterator is exhausted, this returns NULL without setting an + exception. + + NULL with an exception means an error occurred. */ +PyAPI_FUNC(PyObject *) PyIter_Next(PyObject *); + + +/* === Number Protocol ================================================== */ + +/* Returns 1 if the object 'o' provides numeric protocols, and 0 otherwise. + + This function always succeeds. */ +PyAPI_FUNC(int) PyNumber_Check(PyObject *o); + +/* Returns the result of adding o1 and o2, or NULL on failure. + + This is the equivalent of the Python expression: o1 + o2. */ +PyAPI_FUNC(PyObject *) PyNumber_Add(PyObject *o1, PyObject *o2); + +/* Returns the result of subtracting o2 from o1, or NULL on failure. + + This is the equivalent of the Python expression: o1 - o2. */ +PyAPI_FUNC(PyObject *) PyNumber_Subtract(PyObject *o1, PyObject *o2); + +/* Returns the result of multiplying o1 and o2, or NULL on failure. + + This is the equivalent of the Python expression: o1 * o2. */ +PyAPI_FUNC(PyObject *) PyNumber_Multiply(PyObject *o1, PyObject *o2); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +/* This is the equivalent of the Python expression: o1 @ o2. */ +PyAPI_FUNC(PyObject *) PyNumber_MatrixMultiply(PyObject *o1, PyObject *o2); +#endif + +/* Returns the result of dividing o1 by o2 giving an integral result, + or NULL on failure. + + This is the equivalent of the Python expression: o1 // o2. */ +PyAPI_FUNC(PyObject *) PyNumber_FloorDivide(PyObject *o1, PyObject *o2); + +/* Returns the result of dividing o1 by o2 giving a float result, or NULL on + failure. + + This is the equivalent of the Python expression: o1 / o2. */ +PyAPI_FUNC(PyObject *) PyNumber_TrueDivide(PyObject *o1, PyObject *o2); + +/* Returns the remainder of dividing o1 by o2, or NULL on failure. + + This is the equivalent of the Python expression: o1 % o2. */ +PyAPI_FUNC(PyObject *) PyNumber_Remainder(PyObject *o1, PyObject *o2); + +/* See the built-in function divmod. + + Returns NULL on failure. + + This is the equivalent of the Python expression: divmod(o1, o2). */ +PyAPI_FUNC(PyObject *) PyNumber_Divmod(PyObject *o1, PyObject *o2); + +/* See the built-in function pow. Returns NULL on failure. + + This is the equivalent of the Python expression: pow(o1, o2, o3), + where o3 is optional. */ +PyAPI_FUNC(PyObject *) PyNumber_Power(PyObject *o1, PyObject *o2, + PyObject *o3); + +/* Returns the negation of o on success, or NULL on failure. + + This is the equivalent of the Python expression: -o. */ +PyAPI_FUNC(PyObject *) PyNumber_Negative(PyObject *o); + +/* Returns the positive of o on success, or NULL on failure. + + This is the equivalent of the Python expression: +o. */ +PyAPI_FUNC(PyObject *) PyNumber_Positive(PyObject *o); + +/* Returns the absolute value of 'o', or NULL on failure. + + This is the equivalent of the Python expression: abs(o). */ +PyAPI_FUNC(PyObject *) PyNumber_Absolute(PyObject *o); + +/* Returns the bitwise negation of 'o' on success, or NULL on failure. + + This is the equivalent of the Python expression: ~o. */ +PyAPI_FUNC(PyObject *) PyNumber_Invert(PyObject *o); + +/* Returns the result of left shifting o1 by o2 on success, or NULL on failure. + + This is the equivalent of the Python expression: o1 << o2. */ +PyAPI_FUNC(PyObject *) PyNumber_Lshift(PyObject *o1, PyObject *o2); + +/* Returns the result of right shifting o1 by o2 on success, or NULL on + failure. + + This is the equivalent of the Python expression: o1 >> o2. */ +PyAPI_FUNC(PyObject *) PyNumber_Rshift(PyObject *o1, PyObject *o2); + +/* Returns the result of bitwise and of o1 and o2 on success, or NULL on + failure. + + This is the equivalent of the Python expression: o1 & o2. */ +PyAPI_FUNC(PyObject *) PyNumber_And(PyObject *o1, PyObject *o2); + +/* Returns the bitwise exclusive or of o1 by o2 on success, or NULL on failure. + + This is the equivalent of the Python expression: o1 ^ o2. */ +PyAPI_FUNC(PyObject *) PyNumber_Xor(PyObject *o1, PyObject *o2); + +/* Returns the result of bitwise or on o1 and o2 on success, or NULL on + failure. + + This is the equivalent of the Python expression: o1 | o2. */ +PyAPI_FUNC(PyObject *) PyNumber_Or(PyObject *o1, PyObject *o2); + +/* Returns 1 if obj is an index integer (has the nb_index slot of the + tp_as_number structure filled in), and 0 otherwise. */ +PyAPI_FUNC(int) PyIndex_Check(PyObject *); + +/* Returns the object 'o' converted to a Python int, or NULL with an exception + raised on failure. */ +PyAPI_FUNC(PyObject *) PyNumber_Index(PyObject *o); + +/* Returns the object 'o' converted to Py_ssize_t by going through + PyNumber_Index() first. + + If an overflow error occurs while converting the int to Py_ssize_t, then the + second argument 'exc' is the error-type to return. If it is NULL, then the + overflow error is cleared and the value is clipped. */ +PyAPI_FUNC(Py_ssize_t) PyNumber_AsSsize_t(PyObject *o, PyObject *exc); + +/* Returns the object 'o' converted to an integer object on success, or NULL + on failure. + + This is the equivalent of the Python expression: int(o). */ +PyAPI_FUNC(PyObject *) PyNumber_Long(PyObject *o); + +/* Returns the object 'o' converted to a float object on success, or NULL + on failure. + + This is the equivalent of the Python expression: float(o). */ +PyAPI_FUNC(PyObject *) PyNumber_Float(PyObject *o); + + +/* --- In-place variants of (some of) the above number protocol functions -- */ + +/* Returns the result of adding o2 to o1, possibly in-place, or NULL + on failure. + + This is the equivalent of the Python expression: o1 += o2. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlaceAdd(PyObject *o1, PyObject *o2); + +/* Returns the result of subtracting o2 from o1, possibly in-place or + NULL on failure. + + This is the equivalent of the Python expression: o1 -= o2. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlaceSubtract(PyObject *o1, PyObject *o2); + +/* Returns the result of multiplying o1 by o2, possibly in-place, or NULL on + failure. + + This is the equivalent of the Python expression: o1 *= o2. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlaceMultiply(PyObject *o1, PyObject *o2); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +/* This is the equivalent of the Python expression: o1 @= o2. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlaceMatrixMultiply(PyObject *o1, PyObject *o2); +#endif + +/* Returns the result of dividing o1 by o2 giving an integral result, possibly + in-place, or NULL on failure. + + This is the equivalent of the Python expression: o1 /= o2. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlaceFloorDivide(PyObject *o1, + PyObject *o2); + +/* Returns the result of dividing o1 by o2 giving a float result, possibly + in-place, or null on failure. + + This is the equivalent of the Python expression: o1 /= o2. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlaceTrueDivide(PyObject *o1, + PyObject *o2); + +/* Returns the remainder of dividing o1 by o2, possibly in-place, or NULL on + failure. + + This is the equivalent of the Python expression: o1 %= o2. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlaceRemainder(PyObject *o1, PyObject *o2); + +/* Returns the result of raising o1 to the power of o2, possibly in-place, + or NULL on failure. + + This is the equivalent of the Python expression: o1 **= o2, + or o1 = pow(o1, o2, o3) if o3 is present. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlacePower(PyObject *o1, PyObject *o2, + PyObject *o3); + +/* Returns the result of left shifting o1 by o2, possibly in-place, or NULL + on failure. + + This is the equivalent of the Python expression: o1 <<= o2. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlaceLshift(PyObject *o1, PyObject *o2); + +/* Returns the result of right shifting o1 by o2, possibly in-place or NULL + on failure. + + This is the equivalent of the Python expression: o1 >>= o2. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlaceRshift(PyObject *o1, PyObject *o2); + +/* Returns the result of bitwise and of o1 and o2, possibly in-place, or NULL + on failure. + + This is the equivalent of the Python expression: o1 &= o2. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlaceAnd(PyObject *o1, PyObject *o2); + +/* Returns the bitwise exclusive or of o1 by o2, possibly in-place, or NULL + on failure. + + This is the equivalent of the Python expression: o1 ^= o2. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlaceXor(PyObject *o1, PyObject *o2); + +/* Returns the result of bitwise or of o1 and o2, possibly in-place, + or NULL on failure. + + This is the equivalent of the Python expression: o1 |= o2. */ +PyAPI_FUNC(PyObject *) PyNumber_InPlaceOr(PyObject *o1, PyObject *o2); + +/* Returns the integer n converted to a string with a base, with a base + marker of 0b, 0o or 0x prefixed if applicable. + + If n is not an int object, it is converted with PyNumber_Index first. */ +PyAPI_FUNC(PyObject *) PyNumber_ToBase(PyObject *n, int base); + + +/* === Sequence protocol ================================================ */ + +/* Return 1 if the object provides sequence protocol, and zero + otherwise. + + This function always succeeds. */ +PyAPI_FUNC(int) PySequence_Check(PyObject *o); + +/* Return the size of sequence object o, or -1 on failure. */ +PyAPI_FUNC(Py_ssize_t) PySequence_Size(PyObject *o); + +/* For DLL compatibility */ +#undef PySequence_Length +PyAPI_FUNC(Py_ssize_t) PySequence_Length(PyObject *o); +#define PySequence_Length PySequence_Size + + +/* Return the concatenation of o1 and o2 on success, and NULL on failure. + + This is the equivalent of the Python expression: o1 + o2. */ +PyAPI_FUNC(PyObject *) PySequence_Concat(PyObject *o1, PyObject *o2); + +/* Return the result of repeating sequence object 'o' 'count' times, + or NULL on failure. + + This is the equivalent of the Python expression: o * count. */ +PyAPI_FUNC(PyObject *) PySequence_Repeat(PyObject *o, Py_ssize_t count); + +/* Return the ith element of o, or NULL on failure. + + This is the equivalent of the Python expression: o[i]. */ +PyAPI_FUNC(PyObject *) PySequence_GetItem(PyObject *o, Py_ssize_t i); + +/* Return the slice of sequence object o between i1 and i2, or NULL on failure. + + This is the equivalent of the Python expression: o[i1:i2]. */ +PyAPI_FUNC(PyObject *) PySequence_GetSlice(PyObject *o, Py_ssize_t i1, Py_ssize_t i2); + +/* Assign object 'v' to the ith element of the sequence 'o'. Raise an exception + and return -1 on failure; return 0 on success. + + This is the equivalent of the Python statement o[i] = v. */ +PyAPI_FUNC(int) PySequence_SetItem(PyObject *o, Py_ssize_t i, PyObject *v); + +/* Delete the 'i'-th element of the sequence 'v'. Returns -1 on failure. + + This is the equivalent of the Python statement: del o[i]. */ +PyAPI_FUNC(int) PySequence_DelItem(PyObject *o, Py_ssize_t i); + +/* Assign the sequence object 'v' to the slice in sequence object 'o', + from 'i1' to 'i2'. Returns -1 on failure. + + This is the equivalent of the Python statement: o[i1:i2] = v. */ +PyAPI_FUNC(int) PySequence_SetSlice(PyObject *o, Py_ssize_t i1, Py_ssize_t i2, + PyObject *v); + +/* Delete the slice in sequence object 'o' from 'i1' to 'i2'. + Returns -1 on failure. + + This is the equivalent of the Python statement: del o[i1:i2]. */ +PyAPI_FUNC(int) PySequence_DelSlice(PyObject *o, Py_ssize_t i1, Py_ssize_t i2); + +/* Returns the sequence 'o' as a tuple on success, and NULL on failure. + + This is equivalent to the Python expression: tuple(o). */ +PyAPI_FUNC(PyObject *) PySequence_Tuple(PyObject *o); + +/* Returns the sequence 'o' as a list on success, and NULL on failure. + This is equivalent to the Python expression: list(o) */ +PyAPI_FUNC(PyObject *) PySequence_List(PyObject *o); + +/* Return the sequence 'o' as a list, unless it's already a tuple or list. + + Use PySequence_Fast_GET_ITEM to access the members of this list, and + PySequence_Fast_GET_SIZE to get its length. + + Returns NULL on failure. If the object does not support iteration, raises a + TypeError exception with 'm' as the message text. */ +PyAPI_FUNC(PyObject *) PySequence_Fast(PyObject *o, const char* m); + +/* Return the size of the sequence 'o', assuming that 'o' was returned by + PySequence_Fast and is not NULL. */ +#define PySequence_Fast_GET_SIZE(o) \ + (PyList_Check(o) ? PyList_GET_SIZE(o) : PyTuple_GET_SIZE(o)) + +/* Return the 'i'-th element of the sequence 'o', assuming that o was returned + by PySequence_Fast, and that i is within bounds. */ +#define PySequence_Fast_GET_ITEM(o, i)\ + (PyList_Check(o) ? PyList_GET_ITEM(o, i) : PyTuple_GET_ITEM(o, i)) + +/* Return a pointer to the underlying item array for + an object returned by PySequence_Fast */ +#define PySequence_Fast_ITEMS(sf) \ + (PyList_Check(sf) ? ((PyListObject *)(sf))->ob_item \ + : ((PyTupleObject *)(sf))->ob_item) + +/* Return the number of occurrences on value on 'o', that is, return + the number of keys for which o[key] == value. + + On failure, return -1. This is equivalent to the Python expression: + o.count(value). */ +PyAPI_FUNC(Py_ssize_t) PySequence_Count(PyObject *o, PyObject *value); + +/* Return 1 if 'ob' is in the sequence 'seq'; 0 if 'ob' is not in the sequence + 'seq'; -1 on error. + + Use __contains__ if possible, else _PySequence_IterSearch(). */ +PyAPI_FUNC(int) PySequence_Contains(PyObject *seq, PyObject *ob); + +/* For DLL-level backwards compatibility */ +#undef PySequence_In +/* Determine if the sequence 'o' contains 'value'. If an item in 'o' is equal + to 'value', return 1, otherwise return 0. On error, return -1. + + This is equivalent to the Python expression: value in o. */ +PyAPI_FUNC(int) PySequence_In(PyObject *o, PyObject *value); + +/* For source-level backwards compatibility */ +#define PySequence_In PySequence_Contains + + +/* Return the first index for which o[i] == value. + On error, return -1. + + This is equivalent to the Python expression: o.index(value). */ +PyAPI_FUNC(Py_ssize_t) PySequence_Index(PyObject *o, PyObject *value); + + +/* --- In-place versions of some of the above Sequence functions --- */ + +/* Append sequence 'o2' to sequence 'o1', in-place when possible. Return the + resulting object, which could be 'o1', or NULL on failure. + + This is the equivalent of the Python expression: o1 += o2. */ +PyAPI_FUNC(PyObject *) PySequence_InPlaceConcat(PyObject *o1, PyObject *o2); + +/* Repeat sequence 'o' by 'count', in-place when possible. Return the resulting + object, which could be 'o', or NULL on failure. + + This is the equivalent of the Python expression: o1 *= count. */ +PyAPI_FUNC(PyObject *) PySequence_InPlaceRepeat(PyObject *o, Py_ssize_t count); + + +/* === Mapping protocol ================================================= */ + +/* Return 1 if the object provides mapping protocol, and 0 otherwise. + + This function always succeeds. */ +PyAPI_FUNC(int) PyMapping_Check(PyObject *o); + +/* Returns the number of keys in mapping object 'o' on success, and -1 on + failure. This is equivalent to the Python expression: len(o). */ +PyAPI_FUNC(Py_ssize_t) PyMapping_Size(PyObject *o); + +/* For DLL compatibility */ +#undef PyMapping_Length +PyAPI_FUNC(Py_ssize_t) PyMapping_Length(PyObject *o); +#define PyMapping_Length PyMapping_Size + + +/* Implemented as a macro: + + int PyMapping_DelItemString(PyObject *o, const char *key); + + Remove the mapping for the string 'key' from the mapping 'o'. Returns -1 on + failure. + + This is equivalent to the Python statement: del o[key]. */ +#define PyMapping_DelItemString(O,K) PyObject_DelItemString((O),(K)) + +/* Implemented as a macro: + + int PyMapping_DelItem(PyObject *o, PyObject *key); + + Remove the mapping for the object 'key' from the mapping object 'o'. + Returns -1 on failure. + + This is equivalent to the Python statement: del o[key]. */ +#define PyMapping_DelItem(O,K) PyObject_DelItem((O),(K)) + +/* On success, return 1 if the mapping object 'o' has the key 'key', + and 0 otherwise. + + This is equivalent to the Python expression: key in o. + + This function always succeeds. */ +PyAPI_FUNC(int) PyMapping_HasKeyString(PyObject *o, const char *key); + +/* Return 1 if the mapping object has the key 'key', and 0 otherwise. + + This is equivalent to the Python expression: key in o. + + This function always succeeds. */ +PyAPI_FUNC(int) PyMapping_HasKey(PyObject *o, PyObject *key); + +/* On success, return a list or tuple of the keys in mapping object 'o'. + On failure, return NULL. */ +PyAPI_FUNC(PyObject *) PyMapping_Keys(PyObject *o); + +/* On success, return a list or tuple of the values in mapping object 'o'. + On failure, return NULL. */ +PyAPI_FUNC(PyObject *) PyMapping_Values(PyObject *o); + +/* On success, return a list or tuple of the items in mapping object 'o', + where each item is a tuple containing a key-value pair. On failure, return + NULL. */ +PyAPI_FUNC(PyObject *) PyMapping_Items(PyObject *o); + +/* Return element of 'o' corresponding to the string 'key' or NULL on failure. + + This is the equivalent of the Python expression: o[key]. */ +PyAPI_FUNC(PyObject *) PyMapping_GetItemString(PyObject *o, + const char *key); + +/* Map the string 'key' to the value 'v' in the mapping 'o'. + Returns -1 on failure. + + This is the equivalent of the Python statement: o[key]=v. */ +PyAPI_FUNC(int) PyMapping_SetItemString(PyObject *o, const char *key, + PyObject *value); + +/* isinstance(object, typeorclass) */ +PyAPI_FUNC(int) PyObject_IsInstance(PyObject *object, PyObject *typeorclass); + +/* issubclass(object, typeorclass) */ +PyAPI_FUNC(int) PyObject_IsSubclass(PyObject *object, PyObject *typeorclass); + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_ABSTRACTOBJECT_H +# include "cpython/abstract.h" +# undef Py_CPYTHON_ABSTRACTOBJECT_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* Py_ABSTRACTOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/asdl.h b/scripts/build-windows/py39-libs/include/asdl.h new file mode 100644 index 000000000..e962560bc --- /dev/null +++ b/scripts/build-windows/py39-libs/include/asdl.h @@ -0,0 +1,46 @@ +#ifndef Py_LIMITED_API +#ifndef Py_ASDL_H +#define Py_ASDL_H + +typedef PyObject * identifier; +typedef PyObject * string; +typedef PyObject * object; +typedef PyObject * constant; + +/* It would be nice if the code generated by asdl_c.py was completely + independent of Python, but it is a goal the requires too much work + at this stage. So, for example, I'll represent identifiers as + interned Python strings. +*/ + +/* XXX A sequence should be typed so that its use can be typechecked. */ + +typedef struct { + Py_ssize_t size; + void *elements[1]; +} asdl_seq; + +typedef struct { + Py_ssize_t size; + int elements[1]; +} asdl_int_seq; + +asdl_seq *_Py_asdl_seq_new(Py_ssize_t size, PyArena *arena); +asdl_int_seq *_Py_asdl_int_seq_new(Py_ssize_t size, PyArena *arena); + +#define asdl_seq_GET(S, I) (S)->elements[(I)] +#define asdl_seq_LEN(S) ((S) == NULL ? 0 : (S)->size) +#ifdef Py_DEBUG +#define asdl_seq_SET(S, I, V) \ + do { \ + Py_ssize_t _asdl_i = (I); \ + assert((S) != NULL); \ + assert(0 <= _asdl_i && _asdl_i < (S)->size); \ + (S)->elements[_asdl_i] = (V); \ + } while (0) +#else +#define asdl_seq_SET(S, I, V) (S)->elements[I] = (V) +#endif + +#endif /* !Py_ASDL_H */ +#endif /* Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/ast.h b/scripts/build-windows/py39-libs/include/ast.h new file mode 100644 index 000000000..a8c52af78 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/ast.h @@ -0,0 +1,35 @@ +#ifndef Py_LIMITED_API +#ifndef Py_AST_H +#define Py_AST_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "Python-ast.h" /* mod_ty */ +#include "node.h" /* node */ + +PyAPI_FUNC(int) PyAST_Validate(mod_ty); +PyAPI_FUNC(mod_ty) PyAST_FromNode( + const node *n, + PyCompilerFlags *flags, + const char *filename, /* decoded from the filesystem encoding */ + PyArena *arena); +PyAPI_FUNC(mod_ty) PyAST_FromNodeObject( + const node *n, + PyCompilerFlags *flags, + PyObject *filename, + PyArena *arena); + +/* _PyAST_ExprAsUnicode is defined in ast_unparse.c */ +PyAPI_FUNC(PyObject *) _PyAST_ExprAsUnicode(expr_ty); + +/* Return the borrowed reference to the first literal string in the + sequence of statements or NULL if it doesn't start from a literal string. + Doesn't set exception. */ +PyAPI_FUNC(PyObject *) _PyAST_GetDocString(asdl_seq *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_AST_H */ +#endif /* !Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/bitset.h b/scripts/build-windows/py39-libs/include/bitset.h new file mode 100644 index 000000000..6a2ac9787 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/bitset.h @@ -0,0 +1,23 @@ + +#ifndef Py_BITSET_H +#define Py_BITSET_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Bitset interface */ + +#define BYTE char +typedef BYTE *bitset; + +#define testbit(ss, ibit) (((ss)[BIT2BYTE(ibit)] & BIT2MASK(ibit)) != 0) + +#define BITSPERBYTE (8*sizeof(BYTE)) +#define BIT2BYTE(ibit) ((ibit) / BITSPERBYTE) +#define BIT2SHIFT(ibit) ((ibit) % BITSPERBYTE) +#define BIT2MASK(ibit) (1 << BIT2SHIFT(ibit)) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_BITSET_H */ diff --git a/scripts/build-windows/py39-libs/include/bltinmodule.h b/scripts/build-windows/py39-libs/include/bltinmodule.h new file mode 100644 index 000000000..868c9e644 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/bltinmodule.h @@ -0,0 +1,14 @@ +#ifndef Py_BLTINMODULE_H +#define Py_BLTINMODULE_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PyFilter_Type; +PyAPI_DATA(PyTypeObject) PyMap_Type; +PyAPI_DATA(PyTypeObject) PyZip_Type; + +#ifdef __cplusplus +} +#endif +#endif /* !Py_BLTINMODULE_H */ diff --git a/scripts/build-windows/py39-libs/include/boolobject.h b/scripts/build-windows/py39-libs/include/boolobject.h new file mode 100644 index 000000000..bb8044a2b --- /dev/null +++ b/scripts/build-windows/py39-libs/include/boolobject.h @@ -0,0 +1,34 @@ +/* Boolean object interface */ + +#ifndef Py_BOOLOBJECT_H +#define Py_BOOLOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + + +PyAPI_DATA(PyTypeObject) PyBool_Type; + +#define PyBool_Check(x) Py_IS_TYPE(x, &PyBool_Type) + +/* Py_False and Py_True are the only two bools in existence. +Don't forget to apply Py_INCREF() when returning either!!! */ + +/* Don't use these directly */ +PyAPI_DATA(struct _longobject) _Py_FalseStruct, _Py_TrueStruct; + +/* Use these macros */ +#define Py_False ((PyObject *) &_Py_FalseStruct) +#define Py_True ((PyObject *) &_Py_TrueStruct) + +/* Macros for returning Py_True or Py_False, respectively */ +#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True +#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False + +/* Function to return a bool from a C long */ +PyAPI_FUNC(PyObject *) PyBool_FromLong(long); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_BOOLOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/bytearrayobject.h b/scripts/build-windows/py39-libs/include/bytearrayobject.h new file mode 100644 index 000000000..9e95433f0 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/bytearrayobject.h @@ -0,0 +1,46 @@ +/* ByteArray object interface */ + +#ifndef Py_BYTEARRAYOBJECT_H +#define Py_BYTEARRAYOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Type PyByteArrayObject represents a mutable array of bytes. + * The Python API is that of a sequence; + * the bytes are mapped to ints in [0, 256). + * Bytes are not characters; they may be used to encode characters. + * The only way to go between bytes and str/unicode is via encoding + * and decoding. + * For the convenience of C programmers, the bytes type is considered + * to contain a char pointer, not an unsigned char pointer. + */ + +/* Type object */ +PyAPI_DATA(PyTypeObject) PyByteArray_Type; +PyAPI_DATA(PyTypeObject) PyByteArrayIter_Type; + +/* Type check macros */ +#define PyByteArray_Check(self) PyObject_TypeCheck(self, &PyByteArray_Type) +#define PyByteArray_CheckExact(self) Py_IS_TYPE(self, &PyByteArray_Type) + +/* Direct API functions */ +PyAPI_FUNC(PyObject *) PyByteArray_FromObject(PyObject *); +PyAPI_FUNC(PyObject *) PyByteArray_Concat(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyByteArray_FromStringAndSize(const char *, Py_ssize_t); +PyAPI_FUNC(Py_ssize_t) PyByteArray_Size(PyObject *); +PyAPI_FUNC(char *) PyByteArray_AsString(PyObject *); +PyAPI_FUNC(int) PyByteArray_Resize(PyObject *, Py_ssize_t); + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_BYTEARRAYOBJECT_H +# include "cpython/bytearrayobject.h" +# undef Py_CPYTHON_BYTEARRAYOBJECT_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_BYTEARRAYOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/bytesobject.h b/scripts/build-windows/py39-libs/include/bytesobject.h new file mode 100644 index 000000000..5062d8d12 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/bytesobject.h @@ -0,0 +1,82 @@ + +/* Bytes (String) object interface */ + +#ifndef Py_BYTESOBJECT_H +#define Py_BYTESOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* +Type PyBytesObject represents a character string. An extra zero byte is +reserved at the end to ensure it is zero-terminated, but a size is +present so strings with null bytes in them can be represented. This +is an immutable object type. + +There are functions to create new string objects, to test +an object for string-ness, and to get the +string value. The latter function returns a null pointer +if the object is not of the proper type. +There is a variant that takes an explicit size as well as a +variant that assumes a zero-terminated string. Note that none of the +functions should be applied to nil objects. +*/ + +/* Caching the hash (ob_shash) saves recalculation of a string's hash value. + This significantly speeds up dict lookups. */ + +PyAPI_DATA(PyTypeObject) PyBytes_Type; +PyAPI_DATA(PyTypeObject) PyBytesIter_Type; + +#define PyBytes_Check(op) \ + PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_BYTES_SUBCLASS) +#define PyBytes_CheckExact(op) Py_IS_TYPE(op, &PyBytes_Type) + +PyAPI_FUNC(PyObject *) PyBytes_FromStringAndSize(const char *, Py_ssize_t); +PyAPI_FUNC(PyObject *) PyBytes_FromString(const char *); +PyAPI_FUNC(PyObject *) PyBytes_FromObject(PyObject *); +PyAPI_FUNC(PyObject *) PyBytes_FromFormatV(const char*, va_list) + Py_GCC_ATTRIBUTE((format(printf, 1, 0))); +PyAPI_FUNC(PyObject *) PyBytes_FromFormat(const char*, ...) + Py_GCC_ATTRIBUTE((format(printf, 1, 2))); +PyAPI_FUNC(Py_ssize_t) PyBytes_Size(PyObject *); +PyAPI_FUNC(char *) PyBytes_AsString(PyObject *); +PyAPI_FUNC(PyObject *) PyBytes_Repr(PyObject *, int); +PyAPI_FUNC(void) PyBytes_Concat(PyObject **, PyObject *); +PyAPI_FUNC(void) PyBytes_ConcatAndDel(PyObject **, PyObject *); +PyAPI_FUNC(PyObject *) PyBytes_DecodeEscape(const char *, Py_ssize_t, + const char *, Py_ssize_t, + const char *); + +/* Provides access to the internal data buffer and size of a string + object or the default encoded version of a Unicode object. Passing + NULL as *len parameter will force the string buffer to be + 0-terminated (passing a string with embedded NULL characters will + cause an exception). */ +PyAPI_FUNC(int) PyBytes_AsStringAndSize( + PyObject *obj, /* string or Unicode object */ + char **s, /* pointer to buffer variable */ + Py_ssize_t *len /* pointer to length variable or NULL + (only possible for 0-terminated + strings) */ + ); + +/* Flags used by string formatting */ +#define F_LJUST (1<<0) +#define F_SIGN (1<<1) +#define F_BLANK (1<<2) +#define F_ALT (1<<3) +#define F_ZERO (1<<4) + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_BYTESOBJECT_H +# include "cpython/bytesobject.h" +# undef Py_CPYTHON_BYTESOBJECT_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_BYTESOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/cellobject.h b/scripts/build-windows/py39-libs/include/cellobject.h new file mode 100644 index 000000000..f12aa90a4 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cellobject.h @@ -0,0 +1,29 @@ +/* Cell object interface */ +#ifndef Py_LIMITED_API +#ifndef Py_CELLOBJECT_H +#define Py_CELLOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject_HEAD + PyObject *ob_ref; /* Content of the cell or NULL when empty */ +} PyCellObject; + +PyAPI_DATA(PyTypeObject) PyCell_Type; + +#define PyCell_Check(op) Py_IS_TYPE(op, &PyCell_Type) + +PyAPI_FUNC(PyObject *) PyCell_New(PyObject *); +PyAPI_FUNC(PyObject *) PyCell_Get(PyObject *); +PyAPI_FUNC(int) PyCell_Set(PyObject *, PyObject *); + +#define PyCell_GET(op) (((PyCellObject *)(op))->ob_ref) +#define PyCell_SET(op, v) (((PyCellObject *)(op))->ob_ref = v) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_TUPLEOBJECT_H */ +#endif /* Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/ceval.h b/scripts/build-windows/py39-libs/include/ceval.h new file mode 100644 index 000000000..0f372e204 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/ceval.h @@ -0,0 +1,166 @@ +#ifndef Py_CEVAL_H +#define Py_CEVAL_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Interface to random parts in ceval.c */ + +/* PyEval_CallObjectWithKeywords(), PyEval_CallObject(), PyEval_CallFunction + * and PyEval_CallMethod are deprecated. Since they are officially part of the + * stable ABI (PEP 384), they must be kept for backward compatibility. + * PyObject_Call(), PyObject_CallFunction() and PyObject_CallMethod() are + * recommended to call a callable object. + */ + +Py_DEPRECATED(3.9) PyAPI_FUNC(PyObject *) PyEval_CallObjectWithKeywords( + PyObject *callable, + PyObject *args, + PyObject *kwargs); + +/* Deprecated since PyEval_CallObjectWithKeywords is deprecated */ +#define PyEval_CallObject(callable, arg) \ + PyEval_CallObjectWithKeywords(callable, arg, (PyObject *)NULL) + +Py_DEPRECATED(3.9) PyAPI_FUNC(PyObject *) PyEval_CallFunction( + PyObject *callable, const char *format, ...); +Py_DEPRECATED(3.9) PyAPI_FUNC(PyObject *) PyEval_CallMethod( + PyObject *obj, const char *name, const char *format, ...); + +PyAPI_FUNC(PyObject *) PyEval_GetBuiltins(void); +PyAPI_FUNC(PyObject *) PyEval_GetGlobals(void); +PyAPI_FUNC(PyObject *) PyEval_GetLocals(void); +PyAPI_FUNC(PyFrameObject *) PyEval_GetFrame(void); + +PyAPI_FUNC(int) Py_AddPendingCall(int (*func)(void *), void *arg); +PyAPI_FUNC(int) Py_MakePendingCalls(void); + +/* Protection against deeply nested recursive calls + + In Python 3.0, this protection has two levels: + * normal anti-recursion protection is triggered when the recursion level + exceeds the current recursion limit. It raises a RecursionError, and sets + the "overflowed" flag in the thread state structure. This flag + temporarily *disables* the normal protection; this allows cleanup code + to potentially outgrow the recursion limit while processing the + RecursionError. + * "last chance" anti-recursion protection is triggered when the recursion + level exceeds "current recursion limit + 50". By construction, this + protection can only be triggered when the "overflowed" flag is set. It + means the cleanup code has itself gone into an infinite loop, or the + RecursionError has been mistakingly ignored. When this protection is + triggered, the interpreter aborts with a Fatal Error. + + In addition, the "overflowed" flag is automatically reset when the + recursion level drops below "current recursion limit - 50". This heuristic + is meant to ensure that the normal anti-recursion protection doesn't get + disabled too long. + + Please note: this scheme has its own limitations. See: + http://mail.python.org/pipermail/python-dev/2008-August/082106.html + for some observations. +*/ +PyAPI_FUNC(void) Py_SetRecursionLimit(int); +PyAPI_FUNC(int) Py_GetRecursionLimit(void); + +PyAPI_FUNC(int) Py_EnterRecursiveCall(const char *where); +PyAPI_FUNC(void) Py_LeaveRecursiveCall(void); + +#define Py_ALLOW_RECURSION \ + do { unsigned char _old = PyThreadState_GET()->recursion_critical;\ + PyThreadState_GET()->recursion_critical = 1; + +#define Py_END_ALLOW_RECURSION \ + PyThreadState_GET()->recursion_critical = _old; \ + } while(0); + +PyAPI_FUNC(const char *) PyEval_GetFuncName(PyObject *); +PyAPI_FUNC(const char *) PyEval_GetFuncDesc(PyObject *); + +PyAPI_FUNC(PyObject *) PyEval_EvalFrame(PyFrameObject *); +PyAPI_FUNC(PyObject *) PyEval_EvalFrameEx(PyFrameObject *f, int exc); + +/* Interface for threads. + + A module that plans to do a blocking system call (or something else + that lasts a long time and doesn't touch Python data) can allow other + threads to run as follows: + + ...preparations here... + Py_BEGIN_ALLOW_THREADS + ...blocking system call here... + Py_END_ALLOW_THREADS + ...interpret result here... + + The Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS pair expands to a + {}-surrounded block. + To leave the block in the middle (e.g., with return), you must insert + a line containing Py_BLOCK_THREADS before the return, e.g. + + if (...premature_exit...) { + Py_BLOCK_THREADS + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + An alternative is: + + Py_BLOCK_THREADS + if (...premature_exit...) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + Py_UNBLOCK_THREADS + + For convenience, that the value of 'errno' is restored across + Py_END_ALLOW_THREADS and Py_BLOCK_THREADS. + + WARNING: NEVER NEST CALLS TO Py_BEGIN_ALLOW_THREADS AND + Py_END_ALLOW_THREADS!!! + + Note that not yet all candidates have been converted to use this + mechanism! +*/ + +PyAPI_FUNC(PyThreadState *) PyEval_SaveThread(void); +PyAPI_FUNC(void) PyEval_RestoreThread(PyThreadState *); + +Py_DEPRECATED(3.9) PyAPI_FUNC(int) PyEval_ThreadsInitialized(void); +Py_DEPRECATED(3.9) PyAPI_FUNC(void) PyEval_InitThreads(void); +/* PyEval_AcquireLock() and PyEval_ReleaseLock() are part of stable ABI. + * They will be removed from this header file in the future version. + * But they will be remained in ABI until Python 4.0. + */ +Py_DEPRECATED(3.2) PyAPI_FUNC(void) PyEval_AcquireLock(void); +Py_DEPRECATED(3.2) PyAPI_FUNC(void) PyEval_ReleaseLock(void); +PyAPI_FUNC(void) PyEval_AcquireThread(PyThreadState *tstate); +PyAPI_FUNC(void) PyEval_ReleaseThread(PyThreadState *tstate); + +#define Py_BEGIN_ALLOW_THREADS { \ + PyThreadState *_save; \ + _save = PyEval_SaveThread(); +#define Py_BLOCK_THREADS PyEval_RestoreThread(_save); +#define Py_UNBLOCK_THREADS _save = PyEval_SaveThread(); +#define Py_END_ALLOW_THREADS PyEval_RestoreThread(_save); \ + } + +/* Masks and values used by FORMAT_VALUE opcode. */ +#define FVC_MASK 0x3 +#define FVC_NONE 0x0 +#define FVC_STR 0x1 +#define FVC_REPR 0x2 +#define FVC_ASCII 0x3 +#define FVS_MASK 0x4 +#define FVS_HAVE_SPEC 0x4 + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_CEVAL_H +# include "cpython/ceval.h" +# undef Py_CPYTHON_CEVAL_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CEVAL_H */ diff --git a/scripts/build-windows/py39-libs/include/classobject.h b/scripts/build-windows/py39-libs/include/classobject.h new file mode 100644 index 000000000..1952f673b --- /dev/null +++ b/scripts/build-windows/py39-libs/include/classobject.h @@ -0,0 +1,57 @@ +/* Former class object interface -- now only bound methods are here */ + +/* Revealing some structures (not for general use) */ + +#ifndef Py_LIMITED_API +#ifndef Py_CLASSOBJECT_H +#define Py_CLASSOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject_HEAD + PyObject *im_func; /* The callable object implementing the method */ + PyObject *im_self; /* The instance it is bound to */ + PyObject *im_weakreflist; /* List of weak references */ + vectorcallfunc vectorcall; +} PyMethodObject; + +PyAPI_DATA(PyTypeObject) PyMethod_Type; + +#define PyMethod_Check(op) Py_IS_TYPE(op, &PyMethod_Type) + +PyAPI_FUNC(PyObject *) PyMethod_New(PyObject *, PyObject *); + +PyAPI_FUNC(PyObject *) PyMethod_Function(PyObject *); +PyAPI_FUNC(PyObject *) PyMethod_Self(PyObject *); + +/* Macros for direct access to these values. Type checks are *not* + done, so use with care. */ +#define PyMethod_GET_FUNCTION(meth) \ + (((PyMethodObject *)meth) -> im_func) +#define PyMethod_GET_SELF(meth) \ + (((PyMethodObject *)meth) -> im_self) + +typedef struct { + PyObject_HEAD + PyObject *func; +} PyInstanceMethodObject; + +PyAPI_DATA(PyTypeObject) PyInstanceMethod_Type; + +#define PyInstanceMethod_Check(op) Py_IS_TYPE(op, &PyInstanceMethod_Type) + +PyAPI_FUNC(PyObject *) PyInstanceMethod_New(PyObject *); +PyAPI_FUNC(PyObject *) PyInstanceMethod_Function(PyObject *); + +/* Macros for direct access to these values. Type checks are *not* + done, so use with care. */ +#define PyInstanceMethod_GET_FUNCTION(meth) \ + (((PyInstanceMethodObject *)meth) -> func) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CLASSOBJECT_H */ +#endif /* Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/code.h b/scripts/build-windows/py39-libs/include/code.h new file mode 100644 index 000000000..b9e23eb81 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/code.h @@ -0,0 +1,20 @@ +/* Definitions for bytecode */ + +#ifndef Py_CODE_H +#define Py_CODE_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct PyCodeObject PyCodeObject; + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_CODE_H +# include "cpython/code.h" +# undef Py_CPYTHON_CODE_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CODE_H */ diff --git a/scripts/build-windows/py39-libs/include/codecs.h b/scripts/build-windows/py39-libs/include/codecs.h new file mode 100644 index 000000000..3ad0f2b5a --- /dev/null +++ b/scripts/build-windows/py39-libs/include/codecs.h @@ -0,0 +1,240 @@ +#ifndef Py_CODECREGISTRY_H +#define Py_CODECREGISTRY_H +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------------------------------------------------------------ + + Python Codec Registry and support functions + + +Written by Marc-Andre Lemburg (mal@lemburg.com). + +Copyright (c) Corporation for National Research Initiatives. + + ------------------------------------------------------------------------ */ + +/* Register a new codec search function. + + As side effect, this tries to load the encodings package, if not + yet done, to make sure that it is always first in the list of + search functions. + + The search_function's refcount is incremented by this function. */ + +PyAPI_FUNC(int) PyCodec_Register( + PyObject *search_function + ); + +/* Codec registry lookup API. + + Looks up the given encoding and returns a CodecInfo object with + function attributes which implement the different aspects of + processing the encoding. + + The encoding string is looked up converted to all lower-case + characters. This makes encodings looked up through this mechanism + effectively case-insensitive. + + If no codec is found, a KeyError is set and NULL returned. + + As side effect, this tries to load the encodings package, if not + yet done. This is part of the lazy load strategy for the encodings + package. + + */ + +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) _PyCodec_Lookup( + const char *encoding + ); + +PyAPI_FUNC(int) _PyCodec_Forget( + const char *encoding + ); +#endif + +/* Codec registry encoding check API. + + Returns 1/0 depending on whether there is a registered codec for + the given encoding. + +*/ + +PyAPI_FUNC(int) PyCodec_KnownEncoding( + const char *encoding + ); + +/* Generic codec based encoding API. + + object is passed through the encoder function found for the given + encoding using the error handling method defined by errors. errors + may be NULL to use the default method defined for the codec. + + Raises a LookupError in case no encoder can be found. + + */ + +PyAPI_FUNC(PyObject *) PyCodec_Encode( + PyObject *object, + const char *encoding, + const char *errors + ); + +/* Generic codec based decoding API. + + object is passed through the decoder function found for the given + encoding using the error handling method defined by errors. errors + may be NULL to use the default method defined for the codec. + + Raises a LookupError in case no encoder can be found. + + */ + +PyAPI_FUNC(PyObject *) PyCodec_Decode( + PyObject *object, + const char *encoding, + const char *errors + ); + +#ifndef Py_LIMITED_API +/* Text codec specific encoding and decoding API. + + Checks the encoding against a list of codecs which do not + implement a str<->bytes encoding before attempting the + operation. + + Please note that these APIs are internal and should not + be used in Python C extensions. + + XXX (ncoghlan): should we make these, or something like them, public + in Python 3.5+? + + */ +PyAPI_FUNC(PyObject *) _PyCodec_LookupTextEncoding( + const char *encoding, + const char *alternate_command + ); + +PyAPI_FUNC(PyObject *) _PyCodec_EncodeText( + PyObject *object, + const char *encoding, + const char *errors + ); + +PyAPI_FUNC(PyObject *) _PyCodec_DecodeText( + PyObject *object, + const char *encoding, + const char *errors + ); + +/* These two aren't actually text encoding specific, but _io.TextIOWrapper + * is the only current API consumer. + */ +PyAPI_FUNC(PyObject *) _PyCodecInfo_GetIncrementalDecoder( + PyObject *codec_info, + const char *errors + ); + +PyAPI_FUNC(PyObject *) _PyCodecInfo_GetIncrementalEncoder( + PyObject *codec_info, + const char *errors + ); +#endif + + + +/* --- Codec Lookup APIs -------------------------------------------------- + + All APIs return a codec object with incremented refcount and are + based on _PyCodec_Lookup(). The same comments w/r to the encoding + name also apply to these APIs. + +*/ + +/* Get an encoder function for the given encoding. */ + +PyAPI_FUNC(PyObject *) PyCodec_Encoder( + const char *encoding + ); + +/* Get a decoder function for the given encoding. */ + +PyAPI_FUNC(PyObject *) PyCodec_Decoder( + const char *encoding + ); + +/* Get an IncrementalEncoder object for the given encoding. */ + +PyAPI_FUNC(PyObject *) PyCodec_IncrementalEncoder( + const char *encoding, + const char *errors + ); + +/* Get an IncrementalDecoder object function for the given encoding. */ + +PyAPI_FUNC(PyObject *) PyCodec_IncrementalDecoder( + const char *encoding, + const char *errors + ); + +/* Get a StreamReader factory function for the given encoding. */ + +PyAPI_FUNC(PyObject *) PyCodec_StreamReader( + const char *encoding, + PyObject *stream, + const char *errors + ); + +/* Get a StreamWriter factory function for the given encoding. */ + +PyAPI_FUNC(PyObject *) PyCodec_StreamWriter( + const char *encoding, + PyObject *stream, + const char *errors + ); + +/* Unicode encoding error handling callback registry API */ + +/* Register the error handling callback function error under the given + name. This function will be called by the codec when it encounters + unencodable characters/undecodable bytes and doesn't know the + callback name, when name is specified as the error parameter + in the call to the encode/decode function. + Return 0 on success, -1 on error */ +PyAPI_FUNC(int) PyCodec_RegisterError(const char *name, PyObject *error); + +/* Lookup the error handling callback function registered under the given + name. As a special case NULL can be passed, in which case + the error handling callback for "strict" will be returned. */ +PyAPI_FUNC(PyObject *) PyCodec_LookupError(const char *name); + +/* raise exc as an exception */ +PyAPI_FUNC(PyObject *) PyCodec_StrictErrors(PyObject *exc); + +/* ignore the unicode error, skipping the faulty input */ +PyAPI_FUNC(PyObject *) PyCodec_IgnoreErrors(PyObject *exc); + +/* replace the unicode encode error with ? or U+FFFD */ +PyAPI_FUNC(PyObject *) PyCodec_ReplaceErrors(PyObject *exc); + +/* replace the unicode encode error with XML character references */ +PyAPI_FUNC(PyObject *) PyCodec_XMLCharRefReplaceErrors(PyObject *exc); + +/* replace the unicode encode error with backslash escapes (\x, \u and \U) */ +PyAPI_FUNC(PyObject *) PyCodec_BackslashReplaceErrors(PyObject *exc); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +/* replace the unicode encode error with backslash escapes (\N, \x, \u and \U) */ +PyAPI_FUNC(PyObject *) PyCodec_NameReplaceErrors(PyObject *exc); +#endif + +#ifndef Py_LIMITED_API +PyAPI_DATA(const char *) Py_hexdigits; +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CODECREGISTRY_H */ diff --git a/scripts/build-windows/py39-libs/include/compile.h b/scripts/build-windows/py39-libs/include/compile.h new file mode 100644 index 000000000..98adee3d1 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/compile.h @@ -0,0 +1,116 @@ +#ifndef Py_COMPILE_H +#define Py_COMPILE_H + +#ifndef Py_LIMITED_API + +#ifdef __cplusplus +extern "C" { +#endif + +/* Public interface */ +struct _node; /* Declare the existence of this type */ +#ifndef Py_BUILD_CORE +Py_DEPRECATED(3.9) +#endif +PyAPI_FUNC(PyCodeObject *) PyNode_Compile(struct _node *, const char *); +/* XXX (ncoghlan): Unprefixed type name in a public API! */ + +#define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | \ + CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | \ + CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | \ + CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS) +#define PyCF_MASK_OBSOLETE (CO_NESTED) + +/* bpo-39562: CO_FUTURE_ and PyCF_ constants must be kept unique. + PyCF_ constants can use bits from 0x0100 to 0x10000. + CO_FUTURE_ constants use bits starting at 0x20000. */ +#define PyCF_SOURCE_IS_UTF8 0x0100 +#define PyCF_DONT_IMPLY_DEDENT 0x0200 +#define PyCF_ONLY_AST 0x0400 +#define PyCF_IGNORE_COOKIE 0x0800 +#define PyCF_TYPE_COMMENTS 0x1000 +#define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000 +#define PyCF_COMPILE_MASK (PyCF_ONLY_AST | PyCF_ALLOW_TOP_LEVEL_AWAIT | \ + PyCF_TYPE_COMMENTS | PyCF_DONT_IMPLY_DEDENT) + +#ifndef Py_LIMITED_API +typedef struct { + int cf_flags; /* bitmask of CO_xxx flags relevant to future */ + int cf_feature_version; /* minor Python version (PyCF_ONLY_AST) */ +} PyCompilerFlags; + +#define _PyCompilerFlags_INIT \ + (PyCompilerFlags){.cf_flags = 0, .cf_feature_version = PY_MINOR_VERSION} +#endif + +/* Future feature support */ + +typedef struct { + int ff_features; /* flags set by future statements */ + int ff_lineno; /* line number of last future statement */ +} PyFutureFeatures; + +#define FUTURE_NESTED_SCOPES "nested_scopes" +#define FUTURE_GENERATORS "generators" +#define FUTURE_DIVISION "division" +#define FUTURE_ABSOLUTE_IMPORT "absolute_import" +#define FUTURE_WITH_STATEMENT "with_statement" +#define FUTURE_PRINT_FUNCTION "print_function" +#define FUTURE_UNICODE_LITERALS "unicode_literals" +#define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL" +#define FUTURE_GENERATOR_STOP "generator_stop" +#define FUTURE_ANNOTATIONS "annotations" + +struct _mod; /* Declare the existence of this type */ +#define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar) +PyAPI_FUNC(PyCodeObject *) PyAST_CompileEx( + struct _mod *mod, + const char *filename, /* decoded from the filesystem encoding */ + PyCompilerFlags *flags, + int optimize, + PyArena *arena); +PyAPI_FUNC(PyCodeObject *) PyAST_CompileObject( + struct _mod *mod, + PyObject *filename, + PyCompilerFlags *flags, + int optimize, + PyArena *arena); +PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromAST( + struct _mod * mod, + const char *filename /* decoded from the filesystem encoding */ + ); +PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromASTObject( + struct _mod * mod, + PyObject *filename + ); + +/* _Py_Mangle is defined in compile.c */ +PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name); + +#define PY_INVALID_STACK_EFFECT INT_MAX +PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg); +PyAPI_FUNC(int) PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump); + +typedef struct { + int optimize; + int ff_features; +} _PyASTOptimizeState; + +PyAPI_FUNC(int) _PyAST_Optimize(struct _mod *, PyArena *arena, _PyASTOptimizeState *state); + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_LIMITED_API */ + +/* These definitions must match corresponding definitions in graminit.h. */ +#define Py_single_input 256 +#define Py_file_input 257 +#define Py_eval_input 258 +#define Py_func_type_input 345 + +/* This doesn't need to match anything */ +#define Py_fstring_input 800 + +#endif /* !Py_COMPILE_H */ diff --git a/scripts/build-windows/py39-libs/include/complexobject.h b/scripts/build-windows/py39-libs/include/complexobject.h new file mode 100644 index 000000000..9221f9c51 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/complexobject.h @@ -0,0 +1,69 @@ +/* Complex number structure */ + +#ifndef Py_COMPLEXOBJECT_H +#define Py_COMPLEXOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_LIMITED_API +typedef struct { + double real; + double imag; +} Py_complex; + +/* Operations on complex numbers from complexmodule.c */ + +PyAPI_FUNC(Py_complex) _Py_c_sum(Py_complex, Py_complex); +PyAPI_FUNC(Py_complex) _Py_c_diff(Py_complex, Py_complex); +PyAPI_FUNC(Py_complex) _Py_c_neg(Py_complex); +PyAPI_FUNC(Py_complex) _Py_c_prod(Py_complex, Py_complex); +PyAPI_FUNC(Py_complex) _Py_c_quot(Py_complex, Py_complex); +PyAPI_FUNC(Py_complex) _Py_c_pow(Py_complex, Py_complex); +PyAPI_FUNC(double) _Py_c_abs(Py_complex); +#endif + +/* Complex object interface */ + +/* +PyComplexObject represents a complex number with double-precision +real and imaginary parts. +*/ +#ifndef Py_LIMITED_API +typedef struct { + PyObject_HEAD + Py_complex cval; +} PyComplexObject; +#endif + +PyAPI_DATA(PyTypeObject) PyComplex_Type; + +#define PyComplex_Check(op) PyObject_TypeCheck(op, &PyComplex_Type) +#define PyComplex_CheckExact(op) Py_IS_TYPE(op, &PyComplex_Type) + +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) PyComplex_FromCComplex(Py_complex); +#endif +PyAPI_FUNC(PyObject *) PyComplex_FromDoubles(double real, double imag); + +PyAPI_FUNC(double) PyComplex_RealAsDouble(PyObject *op); +PyAPI_FUNC(double) PyComplex_ImagAsDouble(PyObject *op); +#ifndef Py_LIMITED_API +PyAPI_FUNC(Py_complex) PyComplex_AsCComplex(PyObject *op); +#endif + +/* Format the object based on the format_spec, as defined in PEP 3101 + (Advanced String Formatting). */ +#ifndef Py_LIMITED_API +PyAPI_FUNC(int) _PyComplex_FormatAdvancedWriter( + _PyUnicodeWriter *writer, + PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, + Py_ssize_t end); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_COMPLEXOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/context.h b/scripts/build-windows/py39-libs/include/context.h new file mode 100644 index 000000000..4e5007089 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/context.h @@ -0,0 +1,81 @@ +#ifndef Py_CONTEXT_H +#define Py_CONTEXT_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_LIMITED_API + + +PyAPI_DATA(PyTypeObject) PyContext_Type; +typedef struct _pycontextobject PyContext; + +PyAPI_DATA(PyTypeObject) PyContextVar_Type; +typedef struct _pycontextvarobject PyContextVar; + +PyAPI_DATA(PyTypeObject) PyContextToken_Type; +typedef struct _pycontexttokenobject PyContextToken; + + +#define PyContext_CheckExact(o) Py_IS_TYPE(o, &PyContext_Type) +#define PyContextVar_CheckExact(o) Py_IS_TYPE(o, &PyContextVar_Type) +#define PyContextToken_CheckExact(o) Py_IS_TYPE(o, &PyContextToken_Type) + + +PyAPI_FUNC(PyObject *) PyContext_New(void); +PyAPI_FUNC(PyObject *) PyContext_Copy(PyObject *); +PyAPI_FUNC(PyObject *) PyContext_CopyCurrent(void); + +PyAPI_FUNC(int) PyContext_Enter(PyObject *); +PyAPI_FUNC(int) PyContext_Exit(PyObject *); + + +/* Create a new context variable. + + default_value can be NULL. +*/ +PyAPI_FUNC(PyObject *) PyContextVar_New( + const char *name, PyObject *default_value); + + +/* Get a value for the variable. + + Returns -1 if an error occurred during lookup. + + Returns 0 if value either was or was not found. + + If value was found, *value will point to it. + If not, it will point to: + + - default_value, if not NULL; + - the default value of "var", if not NULL; + - NULL. + + '*value' will be a new ref, if not NULL. +*/ +PyAPI_FUNC(int) PyContextVar_Get( + PyObject *var, PyObject *default_value, PyObject **value); + + +/* Set a new value for the variable. + Returns NULL if an error occurs. +*/ +PyAPI_FUNC(PyObject *) PyContextVar_Set(PyObject *var, PyObject *value); + + +/* Reset a variable to its previous value. + Returns 0 on success, -1 on error. +*/ +PyAPI_FUNC(int) PyContextVar_Reset(PyObject *var, PyObject *token); + + +/* This method is exposed only for CPython tests. Don not use it. */ +PyAPI_FUNC(PyObject *) _PyContext_NewHamtForTests(void); + + +#endif /* !Py_LIMITED_API */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CONTEXT_H */ diff --git a/scripts/build-windows/py39-libs/include/cpython/abstract.h b/scripts/build-windows/py39-libs/include/cpython/abstract.h new file mode 100644 index 000000000..0f1304d26 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/abstract.h @@ -0,0 +1,384 @@ +#ifndef Py_CPYTHON_ABSTRACTOBJECT_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* === Object Protocol ================================================== */ + +#ifdef PY_SSIZE_T_CLEAN +# define _PyObject_CallMethodId _PyObject_CallMethodId_SizeT +#endif + +/* Convert keyword arguments from the FASTCALL (stack: C array, kwnames: tuple) + format to a Python dictionary ("kwargs" dict). + + The type of kwnames keys is not checked. The final function getting + arguments is responsible to check if all keys are strings, for example using + PyArg_ParseTupleAndKeywords() or PyArg_ValidateKeywordArguments(). + + Duplicate keys are merged using the last value. If duplicate keys must raise + an exception, the caller is responsible to implement an explicit keys on + kwnames. */ +PyAPI_FUNC(PyObject *) _PyStack_AsDict( + PyObject *const *values, + PyObject *kwnames); + +/* Suggested size (number of positional arguments) for arrays of PyObject* + allocated on a C stack to avoid allocating memory on the heap memory. Such + array is used to pass positional arguments to call functions of the + PyObject_Vectorcall() family. + + The size is chosen to not abuse the C stack and so limit the risk of stack + overflow. The size is also chosen to allow using the small stack for most + function calls of the Python standard library. On 64-bit CPU, it allocates + 40 bytes on the stack. */ +#define _PY_FASTCALL_SMALL_STACK 5 + +PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult( + PyThreadState *tstate, + PyObject *callable, + PyObject *result, + const char *where); + +/* === Vectorcall protocol (PEP 590) ============================= */ + +/* Call callable using tp_call. Arguments are like PyObject_Vectorcall() + or PyObject_FastCallDict() (both forms are supported), + except that nargs is plainly the number of arguments without flags. */ +PyAPI_FUNC(PyObject *) _PyObject_MakeTpCall( + PyThreadState *tstate, + PyObject *callable, + PyObject *const *args, Py_ssize_t nargs, + PyObject *keywords); + +#define PY_VECTORCALL_ARGUMENTS_OFFSET ((size_t)1 << (8 * sizeof(size_t) - 1)) + +static inline Py_ssize_t +PyVectorcall_NARGS(size_t n) +{ + return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET; +} + +static inline vectorcallfunc +PyVectorcall_Function(PyObject *callable) +{ + PyTypeObject *tp; + Py_ssize_t offset; + vectorcallfunc ptr; + + assert(callable != NULL); + tp = Py_TYPE(callable); + if (!PyType_HasFeature(tp, Py_TPFLAGS_HAVE_VECTORCALL)) { + return NULL; + } + assert(PyCallable_Check(callable)); + offset = tp->tp_vectorcall_offset; + assert(offset > 0); + memcpy(&ptr, (char *) callable + offset, sizeof(ptr)); + return ptr; +} + +/* Call the callable object 'callable' with the "vectorcall" calling + convention. + + args is a C array for positional arguments. + + nargsf is the number of positional arguments plus optionally the flag + PY_VECTORCALL_ARGUMENTS_OFFSET which means that the caller is allowed to + modify args[-1]. + + kwnames is a tuple of keyword names. The values of the keyword arguments + are stored in "args" after the positional arguments (note that the number + of keyword arguments does not change nargsf). kwnames can also be NULL if + there are no keyword arguments. + + keywords must only contain strings and all keys must be unique. + + Return the result on success. Raise an exception and return NULL on + error. */ +static inline PyObject * +_PyObject_VectorcallTstate(PyThreadState *tstate, PyObject *callable, + PyObject *const *args, size_t nargsf, + PyObject *kwnames) +{ + vectorcallfunc func; + PyObject *res; + + assert(kwnames == NULL || PyTuple_Check(kwnames)); + assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0); + + func = PyVectorcall_Function(callable); + if (func == NULL) { + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwnames); + } + res = func(callable, args, nargsf, kwnames); + return _Py_CheckFunctionResult(tstate, callable, res, NULL); +} + +static inline PyObject * +PyObject_Vectorcall(PyObject *callable, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ + PyThreadState *tstate = PyThreadState_GET(); + return _PyObject_VectorcallTstate(tstate, callable, + args, nargsf, kwnames); +} + +// Backwards compatibility aliases for API that was provisional in Python 3.8 +#define _PyObject_Vectorcall PyObject_Vectorcall +#define _PyObject_VectorcallMethod PyObject_VectorcallMethod +#define _PyObject_FastCallDict PyObject_VectorcallDict +#define _PyVectorcall_Function PyVectorcall_Function +#define _PyObject_CallOneArg PyObject_CallOneArg +#define _PyObject_CallMethodNoArgs PyObject_CallMethodNoArgs +#define _PyObject_CallMethodOneArg PyObject_CallMethodOneArg + +/* Same as PyObject_Vectorcall except that keyword arguments are passed as + dict, which may be NULL if there are no keyword arguments. */ +PyAPI_FUNC(PyObject *) PyObject_VectorcallDict( + PyObject *callable, + PyObject *const *args, + size_t nargsf, + PyObject *kwargs); + +/* Call "callable" (which must support vectorcall) with positional arguments + "tuple" and keyword arguments "dict". "dict" may also be NULL */ +PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict); + +static inline PyObject * +_PyObject_FastCallTstate(PyThreadState *tstate, PyObject *func, PyObject *const *args, Py_ssize_t nargs) +{ + return _PyObject_VectorcallTstate(tstate, func, args, (size_t)nargs, NULL); +} + +/* Same as PyObject_Vectorcall except without keyword arguments */ +static inline PyObject * +_PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs) +{ + PyThreadState *tstate = PyThreadState_GET(); + return _PyObject_FastCallTstate(tstate, func, args, nargs); +} + +/* Call a callable without any arguments + Private static inline function variant of public function + PyObject_CallNoArgs(). */ +static inline PyObject * +_PyObject_CallNoArg(PyObject *func) { + PyThreadState *tstate = PyThreadState_GET(); + return _PyObject_VectorcallTstate(tstate, func, NULL, 0, NULL); +} + +static inline PyObject * +PyObject_CallOneArg(PyObject *func, PyObject *arg) +{ + PyObject *_args[2]; + PyObject **args; + PyThreadState *tstate; + size_t nargsf; + + assert(arg != NULL); + args = _args + 1; // For PY_VECTORCALL_ARGUMENTS_OFFSET + args[0] = arg; + tstate = PyThreadState_GET(); + nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET; + return _PyObject_VectorcallTstate(tstate, func, args, nargsf, NULL); +} + +PyAPI_FUNC(PyObject *) PyObject_VectorcallMethod( + PyObject *name, PyObject *const *args, + size_t nargsf, PyObject *kwnames); + +static inline PyObject * +PyObject_CallMethodNoArgs(PyObject *self, PyObject *name) +{ + return PyObject_VectorcallMethod(name, &self, + 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); +} + +static inline PyObject * +PyObject_CallMethodOneArg(PyObject *self, PyObject *name, PyObject *arg) +{ + PyObject *args[2] = {self, arg}; + + assert(arg != NULL); + return PyObject_VectorcallMethod(name, args, + 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); +} + +/* Like PyObject_CallMethod(), but expect a _Py_Identifier* + as the method name. */ +PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *obj, + _Py_Identifier *name, + const char *format, ...); + +PyAPI_FUNC(PyObject *) _PyObject_CallMethodId_SizeT(PyObject *obj, + _Py_Identifier *name, + const char *format, + ...); + +PyAPI_FUNC(PyObject *) _PyObject_CallMethodIdObjArgs( + PyObject *obj, + struct _Py_Identifier *name, + ...); + +static inline PyObject * +_PyObject_VectorcallMethodId( + _Py_Identifier *name, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ + PyObject *oname = _PyUnicode_FromId(name); /* borrowed */ + if (!oname) { + return NULL; + } + return PyObject_VectorcallMethod(oname, args, nargsf, kwnames); +} + +static inline PyObject * +_PyObject_CallMethodIdNoArgs(PyObject *self, _Py_Identifier *name) +{ + return _PyObject_VectorcallMethodId(name, &self, + 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); +} + +static inline PyObject * +_PyObject_CallMethodIdOneArg(PyObject *self, _Py_Identifier *name, PyObject *arg) +{ + PyObject *args[2] = {self, arg}; + + assert(arg != NULL); + return _PyObject_VectorcallMethodId(name, args, + 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); +} + +PyAPI_FUNC(int) _PyObject_HasLen(PyObject *o); + +/* Guess the size of object 'o' using len(o) or o.__length_hint__(). + If neither of those return a non-negative value, then return the default + value. If one of the calls fails, this function returns -1. */ +PyAPI_FUNC(Py_ssize_t) PyObject_LengthHint(PyObject *o, Py_ssize_t); + +/* === New Buffer API ============================================ */ + +/* Return 1 if the getbuffer function is available, otherwise return 0. */ +PyAPI_FUNC(int) PyObject_CheckBuffer(PyObject *obj); + +/* This is a C-API version of the getbuffer function call. It checks + to make sure object has the required function pointer and issues the + call. + + Returns -1 and raises an error on failure and returns 0 on success. */ +PyAPI_FUNC(int) PyObject_GetBuffer(PyObject *obj, Py_buffer *view, + int flags); + +/* Get the memory area pointed to by the indices for the buffer given. + Note that view->ndim is the assumed size of indices. */ +PyAPI_FUNC(void *) PyBuffer_GetPointer(Py_buffer *view, Py_ssize_t *indices); + +/* Return the implied itemsize of the data-format area from a + struct-style description. */ +PyAPI_FUNC(Py_ssize_t) PyBuffer_SizeFromFormat(const char *format); + +/* Implementation in memoryobject.c */ +PyAPI_FUNC(int) PyBuffer_ToContiguous(void *buf, Py_buffer *view, + Py_ssize_t len, char order); + +PyAPI_FUNC(int) PyBuffer_FromContiguous(Py_buffer *view, void *buf, + Py_ssize_t len, char order); + +/* Copy len bytes of data from the contiguous chunk of memory + pointed to by buf into the buffer exported by obj. Return + 0 on success and return -1 and raise a PyBuffer_Error on + error (i.e. the object does not have a buffer interface or + it is not working). + + If fort is 'F', then if the object is multi-dimensional, + then the data will be copied into the array in + Fortran-style (first dimension varies the fastest). If + fort is 'C', then the data will be copied into the array + in C-style (last dimension varies the fastest). If fort + is 'A', then it does not matter and the copy will be made + in whatever way is more efficient. */ +PyAPI_FUNC(int) PyObject_CopyData(PyObject *dest, PyObject *src); + +/* Copy the data from the src buffer to the buffer of destination. */ +PyAPI_FUNC(int) PyBuffer_IsContiguous(const Py_buffer *view, char fort); + +/*Fill the strides array with byte-strides of a contiguous + (Fortran-style if fort is 'F' or C-style otherwise) + array of the given shape with the given number of bytes + per element. */ +PyAPI_FUNC(void) PyBuffer_FillContiguousStrides(int ndims, + Py_ssize_t *shape, + Py_ssize_t *strides, + int itemsize, + char fort); + +/* Fills in a buffer-info structure correctly for an exporter + that can only share a contiguous chunk of memory of + "unsigned bytes" of the given length. + + Returns 0 on success and -1 (with raising an error) on error. */ +PyAPI_FUNC(int) PyBuffer_FillInfo(Py_buffer *view, PyObject *o, void *buf, + Py_ssize_t len, int readonly, + int flags); + +/* Releases a Py_buffer obtained from getbuffer ParseTuple's "s*". */ +PyAPI_FUNC(void) PyBuffer_Release(Py_buffer *view); + +/* ==== Iterators ================================================ */ + +#define PyIter_Check(obj) \ + (Py_TYPE(obj)->tp_iternext != NULL && \ + Py_TYPE(obj)->tp_iternext != &_PyObject_NextNotImplemented) + +/* === Sequence protocol ================================================ */ + +/* Assume tp_as_sequence and sq_item exist and that 'i' does not + need to be corrected for a negative index. */ +#define PySequence_ITEM(o, i)\ + ( Py_TYPE(o)->tp_as_sequence->sq_item(o, i) ) + +#define PY_ITERSEARCH_COUNT 1 +#define PY_ITERSEARCH_INDEX 2 +#define PY_ITERSEARCH_CONTAINS 3 + +/* Iterate over seq. + + Result depends on the operation: + + PY_ITERSEARCH_COUNT: return # of times obj appears in seq; -1 if + error. + PY_ITERSEARCH_INDEX: return 0-based index of first occurrence of + obj in seq; set ValueError and return -1 if none found; + also return -1 on error. + PY_ITERSEARCH_CONTAINS: return 1 if obj in seq, else 0; -1 on + error. */ +PyAPI_FUNC(Py_ssize_t) _PySequence_IterSearch(PyObject *seq, + PyObject *obj, int operation); + +/* === Mapping protocol ================================================= */ + +PyAPI_FUNC(int) _PyObject_RealIsInstance(PyObject *inst, PyObject *cls); + +PyAPI_FUNC(int) _PyObject_RealIsSubclass(PyObject *derived, PyObject *cls); + +PyAPI_FUNC(char *const *) _PySequence_BytesToCharpArray(PyObject* self); + +PyAPI_FUNC(void) _Py_FreeCharPArray(char *const array[]); + +/* For internal use by buffer API functions */ +PyAPI_FUNC(void) _Py_add_one_to_index_F(int nd, Py_ssize_t *index, + const Py_ssize_t *shape); +PyAPI_FUNC(void) _Py_add_one_to_index_C(int nd, Py_ssize_t *index, + const Py_ssize_t *shape); + +/* Convert Python int to Py_ssize_t. Do nothing if the argument is None. */ +PyAPI_FUNC(int) _Py_convert_optional_to_ssize_t(PyObject *, void *); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/bytearrayobject.h b/scripts/build-windows/py39-libs/include/cpython/bytearrayobject.h new file mode 100644 index 000000000..569b0cd03 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/bytearrayobject.h @@ -0,0 +1,20 @@ +#ifndef Py_CPYTHON_BYTEARRAYOBJECT_H +# error "this header file must not be included directly" +#endif + +/* Object layout */ +typedef struct { + PyObject_VAR_HEAD + Py_ssize_t ob_alloc; /* How many bytes allocated in ob_bytes */ + char *ob_bytes; /* Physical backing buffer */ + char *ob_start; /* Logical start inside ob_bytes */ + Py_ssize_t ob_exports; /* How many buffer exports */ +} PyByteArrayObject; + +/* Macros, trading safety for speed */ +#define PyByteArray_AS_STRING(self) \ + (assert(PyByteArray_Check(self)), \ + Py_SIZE(self) ? ((PyByteArrayObject *)(self))->ob_start : _PyByteArray_empty_string) +#define PyByteArray_GET_SIZE(self) (assert(PyByteArray_Check(self)), Py_SIZE(self)) + +PyAPI_DATA(char) _PyByteArray_empty_string[]; diff --git a/scripts/build-windows/py39-libs/include/cpython/bytesobject.h b/scripts/build-windows/py39-libs/include/cpython/bytesobject.h new file mode 100644 index 000000000..f284c5835 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/bytesobject.h @@ -0,0 +1,118 @@ +#ifndef Py_CPYTHON_BYTESOBJECT_H +# error "this header file must not be included directly" +#endif + +typedef struct { + PyObject_VAR_HEAD + Py_hash_t ob_shash; + char ob_sval[1]; + + /* Invariants: + * ob_sval contains space for 'ob_size+1' elements. + * ob_sval[ob_size] == 0. + * ob_shash is the hash of the string or -1 if not computed yet. + */ +} PyBytesObject; + +PyAPI_FUNC(int) _PyBytes_Resize(PyObject **, Py_ssize_t); +PyAPI_FUNC(PyObject*) _PyBytes_FormatEx( + const char *format, + Py_ssize_t format_len, + PyObject *args, + int use_bytearray); +PyAPI_FUNC(PyObject*) _PyBytes_FromHex( + PyObject *string, + int use_bytearray); + +/* Helper for PyBytes_DecodeEscape that detects invalid escape chars. */ +PyAPI_FUNC(PyObject *) _PyBytes_DecodeEscape(const char *, Py_ssize_t, + const char *, const char **); + +/* Macro, trading safety for speed */ +#define PyBytes_AS_STRING(op) (assert(PyBytes_Check(op)), \ + (((PyBytesObject *)(op))->ob_sval)) +#define PyBytes_GET_SIZE(op) (assert(PyBytes_Check(op)),Py_SIZE(op)) + +/* _PyBytes_Join(sep, x) is like sep.join(x). sep must be PyBytesObject*, + x must be an iterable object. */ +PyAPI_FUNC(PyObject *) _PyBytes_Join(PyObject *sep, PyObject *x); + + +/* The _PyBytesWriter structure is big: it contains an embedded "stack buffer". + A _PyBytesWriter variable must be declared at the end of variables in a + function to optimize the memory allocation on the stack. */ +typedef struct { + /* bytes, bytearray or NULL (when the small buffer is used) */ + PyObject *buffer; + + /* Number of allocated size. */ + Py_ssize_t allocated; + + /* Minimum number of allocated bytes, + incremented by _PyBytesWriter_Prepare() */ + Py_ssize_t min_size; + + /* If non-zero, use a bytearray instead of a bytes object for buffer. */ + int use_bytearray; + + /* If non-zero, overallocate the buffer (default: 0). + This flag must be zero if use_bytearray is non-zero. */ + int overallocate; + + /* Stack buffer */ + int use_small_buffer; + char small_buffer[512]; +} _PyBytesWriter; + +/* Initialize a bytes writer + + By default, the overallocation is disabled. Set the overallocate attribute + to control the allocation of the buffer. */ +PyAPI_FUNC(void) _PyBytesWriter_Init(_PyBytesWriter *writer); + +/* Get the buffer content and reset the writer. + Return a bytes object, or a bytearray object if use_bytearray is non-zero. + Raise an exception and return NULL on error. */ +PyAPI_FUNC(PyObject *) _PyBytesWriter_Finish(_PyBytesWriter *writer, + void *str); + +/* Deallocate memory of a writer (clear its internal buffer). */ +PyAPI_FUNC(void) _PyBytesWriter_Dealloc(_PyBytesWriter *writer); + +/* Allocate the buffer to write size bytes. + Return the pointer to the beginning of buffer data. + Raise an exception and return NULL on error. */ +PyAPI_FUNC(void*) _PyBytesWriter_Alloc(_PyBytesWriter *writer, + Py_ssize_t size); + +/* Ensure that the buffer is large enough to write *size* bytes. + Add size to the writer minimum size (min_size attribute). + + str is the current pointer inside the buffer. + Return the updated current pointer inside the buffer. + Raise an exception and return NULL on error. */ +PyAPI_FUNC(void*) _PyBytesWriter_Prepare(_PyBytesWriter *writer, + void *str, + Py_ssize_t size); + +/* Resize the buffer to make it larger. + The new buffer may be larger than size bytes because of overallocation. + Return the updated current pointer inside the buffer. + Raise an exception and return NULL on error. + + Note: size must be greater than the number of allocated bytes in the writer. + + This function doesn't use the writer minimum size (min_size attribute). + + See also _PyBytesWriter_Prepare(). + */ +PyAPI_FUNC(void*) _PyBytesWriter_Resize(_PyBytesWriter *writer, + void *str, + Py_ssize_t size); + +/* Write bytes. + Raise an exception and return NULL on error. */ +PyAPI_FUNC(void*) _PyBytesWriter_WriteBytes(_PyBytesWriter *writer, + void *str, + const void *bytes, + Py_ssize_t size); diff --git a/scripts/build-windows/py39-libs/include/cpython/ceval.h b/scripts/build-windows/py39-libs/include/cpython/ceval.h new file mode 100644 index 000000000..e1922a677 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/ceval.h @@ -0,0 +1,38 @@ +#ifndef Py_CPYTHON_CEVAL_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(void) PyEval_SetProfile(Py_tracefunc, PyObject *); +PyAPI_DATA(int) _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg); +PyAPI_FUNC(void) PyEval_SetTrace(Py_tracefunc, PyObject *); +PyAPI_FUNC(int) _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg); +PyAPI_FUNC(int) _PyEval_GetCoroutineOriginTrackingDepth(void); +PyAPI_FUNC(int) _PyEval_SetAsyncGenFirstiter(PyObject *); +PyAPI_FUNC(PyObject *) _PyEval_GetAsyncGenFirstiter(void); +PyAPI_FUNC(int) _PyEval_SetAsyncGenFinalizer(PyObject *); +PyAPI_FUNC(PyObject *) _PyEval_GetAsyncGenFinalizer(void); + +/* Helper to look up a builtin object */ +PyAPI_FUNC(PyObject *) _PyEval_GetBuiltinId(_Py_Identifier *); +/* Look at the current frame's (if any) code's co_flags, and turn on + the corresponding compiler flags in cf->cf_flags. Return 1 if any + flag was set, else return 0. */ +PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf); + +PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int exc); + +PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds); +PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void); + +PyAPI_FUNC(Py_ssize_t) _PyEval_RequestCodeExtraIndex(freefunc); + +PyAPI_FUNC(int) _PyEval_SliceIndex(PyObject *, Py_ssize_t *); +PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/code.h b/scripts/build-windows/py39-libs/include/cpython/code.h new file mode 100644 index 000000000..cda28ac6e --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/code.h @@ -0,0 +1,165 @@ +#ifndef Py_CPYTHON_CODE_H +# error "this header file must not be included directly" +#endif + +typedef uint16_t _Py_CODEUNIT; + +#ifdef WORDS_BIGENDIAN +# define _Py_OPCODE(word) ((word) >> 8) +# define _Py_OPARG(word) ((word) & 255) +#else +# define _Py_OPCODE(word) ((word) & 255) +# define _Py_OPARG(word) ((word) >> 8) +#endif + +typedef struct _PyOpcache _PyOpcache; + +/* Bytecode object */ +struct PyCodeObject { + PyObject_HEAD + int co_argcount; /* #arguments, except *args */ + int co_posonlyargcount; /* #positional only arguments */ + int co_kwonlyargcount; /* #keyword only arguments */ + int co_nlocals; /* #local variables */ + int co_stacksize; /* #entries needed for evaluation stack */ + int co_flags; /* CO_..., see below */ + int co_firstlineno; /* first source line number */ + PyObject *co_code; /* instruction opcodes */ + PyObject *co_consts; /* list (constants used) */ + PyObject *co_names; /* list of strings (names used) */ + PyObject *co_varnames; /* tuple of strings (local variable names) */ + PyObject *co_freevars; /* tuple of strings (free variable names) */ + PyObject *co_cellvars; /* tuple of strings (cell variable names) */ + /* The rest aren't used in either hash or comparisons, except for co_name, + used in both. This is done to preserve the name and line number + for tracebacks and debuggers; otherwise, constant de-duplication + would collapse identical functions/lambdas defined on different lines. + */ + Py_ssize_t *co_cell2arg; /* Maps cell vars which are arguments. */ + PyObject *co_filename; /* unicode (where it was loaded from) */ + PyObject *co_name; /* unicode (name, for reference) */ + PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See + Objects/lnotab_notes.txt for details. */ + void *co_zombieframe; /* for optimization only (see frameobject.c) */ + PyObject *co_weakreflist; /* to support weakrefs to code objects */ + /* Scratch space for extra data relating to the code object. + Type is a void* to keep the format private in codeobject.c to force + people to go through the proper APIs. */ + void *co_extra; + + /* Per opcodes just-in-time cache + * + * To reduce cache size, we use indirect mapping from opcode index to + * cache object: + * cache = co_opcache[co_opcache_map[next_instr - first_instr] - 1] + */ + + // co_opcache_map is indexed by (next_instr - first_instr). + // * 0 means there is no cache for this opcode. + // * n > 0 means there is cache in co_opcache[n-1]. + unsigned char *co_opcache_map; + _PyOpcache *co_opcache; + int co_opcache_flag; // used to determine when create a cache. + unsigned char co_opcache_size; // length of co_opcache. +}; + +/* Masks for co_flags above */ +#define CO_OPTIMIZED 0x0001 +#define CO_NEWLOCALS 0x0002 +#define CO_VARARGS 0x0004 +#define CO_VARKEYWORDS 0x0008 +#define CO_NESTED 0x0010 +#define CO_GENERATOR 0x0020 +/* The CO_NOFREE flag is set if there are no free or cell variables. + This information is redundant, but it allows a single flag test + to determine whether there is any extra work to be done when the + call frame it setup. +*/ +#define CO_NOFREE 0x0040 + +/* The CO_COROUTINE flag is set for coroutine functions (defined with + ``async def`` keywords) */ +#define CO_COROUTINE 0x0080 +#define CO_ITERABLE_COROUTINE 0x0100 +#define CO_ASYNC_GENERATOR 0x0200 + +/* bpo-39562: These constant values are changed in Python 3.9 + to prevent collision with compiler flags. CO_FUTURE_ and PyCF_ + constants must be kept unique. PyCF_ constants can use bits from + 0x0100 to 0x10000. CO_FUTURE_ constants use bits starting at 0x20000. */ +#define CO_FUTURE_DIVISION 0x20000 +#define CO_FUTURE_ABSOLUTE_IMPORT 0x40000 /* do absolute imports by default */ +#define CO_FUTURE_WITH_STATEMENT 0x80000 +#define CO_FUTURE_PRINT_FUNCTION 0x100000 +#define CO_FUTURE_UNICODE_LITERALS 0x200000 + +#define CO_FUTURE_BARRY_AS_BDFL 0x400000 +#define CO_FUTURE_GENERATOR_STOP 0x800000 +#define CO_FUTURE_ANNOTATIONS 0x1000000 + +/* This value is found in the co_cell2arg array when the associated cell + variable does not correspond to an argument. */ +#define CO_CELL_NOT_AN_ARG (-1) + +/* This should be defined if a future statement modifies the syntax. + For example, when a keyword is added. +*/ +#define PY_PARSER_REQUIRES_FUTURE_KEYWORD + +#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */ + +PyAPI_DATA(PyTypeObject) PyCode_Type; + +#define PyCode_Check(op) Py_IS_TYPE(op, &PyCode_Type) +#define PyCode_GetNumFree(op) (PyTuple_GET_SIZE((op)->co_freevars)) + +/* Public interface */ +PyAPI_FUNC(PyCodeObject *) PyCode_New( + int, int, int, int, int, PyObject *, PyObject *, + PyObject *, PyObject *, PyObject *, PyObject *, + PyObject *, PyObject *, int, PyObject *); + +PyAPI_FUNC(PyCodeObject *) PyCode_NewWithPosOnlyArgs( + int, int, int, int, int, int, PyObject *, PyObject *, + PyObject *, PyObject *, PyObject *, PyObject *, + PyObject *, PyObject *, int, PyObject *); + /* same as struct above */ + +/* Creates a new empty code object with the specified source location. */ +PyAPI_FUNC(PyCodeObject *) +PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno); + +/* Return the line number associated with the specified bytecode index + in this code object. If you just need the line number of a frame, + use PyFrame_GetLineNumber() instead. */ +PyAPI_FUNC(int) PyCode_Addr2Line(PyCodeObject *, int); + +/* for internal use only */ +typedef struct _addr_pair { + int ap_lower; + int ap_upper; +} PyAddrPair; + +/* Update *bounds to describe the first and one-past-the-last instructions in the + same line as lasti. Return the number of that line. +*/ +PyAPI_FUNC(int) _PyCode_CheckLineNumber(PyCodeObject* co, + int lasti, PyAddrPair *bounds); + +/* Create a comparable key used to compare constants taking in account the + * object type. It is used to make sure types are not coerced (e.g., float and + * complex) _and_ to distinguish 0.0 from -0.0 e.g. on IEEE platforms + * + * Return (type(obj), obj, ...): a tuple with variable size (at least 2 items) + * depending on the type and the value. The type is the first item to not + * compare bytes and str which can raise a BytesWarning exception. */ +PyAPI_FUNC(PyObject*) _PyCode_ConstantKey(PyObject *obj); + +PyAPI_FUNC(PyObject*) PyCode_Optimize(PyObject *code, PyObject* consts, + PyObject *names, PyObject *lnotab); + + +PyAPI_FUNC(int) _PyCode_GetExtra(PyObject *code, Py_ssize_t index, + void **extra); +PyAPI_FUNC(int) _PyCode_SetExtra(PyObject *code, Py_ssize_t index, + void *extra); diff --git a/scripts/build-windows/py39-libs/include/cpython/dictobject.h b/scripts/build-windows/py39-libs/include/cpython/dictobject.h new file mode 100644 index 000000000..e33a0d156 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/dictobject.h @@ -0,0 +1,92 @@ +#ifndef Py_CPYTHON_DICTOBJECT_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _dictkeysobject PyDictKeysObject; + +/* The ma_values pointer is NULL for a combined table + * or points to an array of PyObject* for a split table + */ +typedef struct { + PyObject_HEAD + + /* Number of items in the dictionary */ + Py_ssize_t ma_used; + + /* Dictionary version: globally unique, value change each time + the dictionary is modified */ + uint64_t ma_version_tag; + + PyDictKeysObject *ma_keys; + + /* If ma_values is NULL, the table is "combined": keys and values + are stored in ma_keys. + + If ma_values is not NULL, the table is splitted: + keys are stored in ma_keys and values are stored in ma_values */ + PyObject **ma_values; +} PyDictObject; + +PyAPI_FUNC(PyObject *) _PyDict_GetItem_KnownHash(PyObject *mp, PyObject *key, + Py_hash_t hash); +PyAPI_FUNC(PyObject *) _PyDict_GetItemIdWithError(PyObject *dp, + struct _Py_Identifier *key); +PyAPI_FUNC(PyObject *) _PyDict_GetItemStringWithError(PyObject *, const char *); +PyAPI_FUNC(PyObject *) PyDict_SetDefault( + PyObject *mp, PyObject *key, PyObject *defaultobj); +PyAPI_FUNC(int) _PyDict_SetItem_KnownHash(PyObject *mp, PyObject *key, + PyObject *item, Py_hash_t hash); +PyAPI_FUNC(int) _PyDict_DelItem_KnownHash(PyObject *mp, PyObject *key, + Py_hash_t hash); +PyAPI_FUNC(int) _PyDict_DelItemIf(PyObject *mp, PyObject *key, + int (*predicate)(PyObject *value)); +PyDictKeysObject *_PyDict_NewKeysForClass(void); +PyAPI_FUNC(PyObject *) PyObject_GenericGetDict(PyObject *, void *); +PyAPI_FUNC(int) _PyDict_Next( + PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value, Py_hash_t *hash); + +/* Get the number of items of a dictionary. */ +#define PyDict_GET_SIZE(mp) (assert(PyDict_Check(mp)),((PyDictObject *)mp)->ma_used) +PyAPI_FUNC(int) _PyDict_Contains(PyObject *mp, PyObject *key, Py_hash_t hash); +PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused); +PyAPI_FUNC(void) _PyDict_MaybeUntrack(PyObject *mp); +PyAPI_FUNC(int) _PyDict_HasOnlyStringKeys(PyObject *mp); +Py_ssize_t _PyDict_KeysSize(PyDictKeysObject *keys); +PyAPI_FUNC(Py_ssize_t) _PyDict_SizeOf(PyDictObject *); +PyAPI_FUNC(PyObject *) _PyDict_Pop(PyObject *, PyObject *, PyObject *); +PyObject *_PyDict_Pop_KnownHash(PyObject *, PyObject *, Py_hash_t, PyObject *); +PyObject *_PyDict_FromKeys(PyObject *, PyObject *, PyObject *); +#define _PyDict_HasSplitTable(d) ((d)->ma_values != NULL) + +/* Like PyDict_Merge, but override can be 0, 1 or 2. If override is 0, + the first occurrence of a key wins, if override is 1, the last occurrence + of a key wins, if override is 2, a KeyError with conflicting key as + argument is raised. +*/ +PyAPI_FUNC(int) _PyDict_MergeEx(PyObject *mp, PyObject *other, int override); +PyAPI_FUNC(PyObject *) _PyDict_GetItemId(PyObject *dp, struct _Py_Identifier *key); +PyAPI_FUNC(int) _PyDict_SetItemId(PyObject *dp, struct _Py_Identifier *key, PyObject *item); + +PyAPI_FUNC(int) _PyDict_DelItemId(PyObject *mp, struct _Py_Identifier *key); +PyAPI_FUNC(void) _PyDict_DebugMallocStats(FILE *out); + +int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *name, PyObject *value); +PyObject *_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *); + +/* _PyDictView */ + +typedef struct { + PyObject_HEAD + PyDictObject *dv_dict; +} _PyDictViewObject; + +PyAPI_FUNC(PyObject *) _PyDictView_New(PyObject *, PyTypeObject *); +PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/fileobject.h b/scripts/build-windows/py39-libs/include/cpython/fileobject.h new file mode 100644 index 000000000..3005ce1f0 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/fileobject.h @@ -0,0 +1,24 @@ +#ifndef Py_CPYTHON_FILEOBJECT_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(char *) Py_UniversalNewlineFgets(char *, int, FILE*, PyObject *); + +/* The std printer acts as a preliminary sys.stderr until the new io + infrastructure is in place. */ +PyAPI_FUNC(PyObject *) PyFile_NewStdPrinter(int); +PyAPI_DATA(PyTypeObject) PyStdPrinter_Type; + +typedef PyObject * (*Py_OpenCodeHookFunction)(PyObject *, void *); + +PyAPI_FUNC(PyObject *) PyFile_OpenCode(const char *utf8path); +PyAPI_FUNC(PyObject *) PyFile_OpenCodeObject(PyObject *path); +PyAPI_FUNC(int) PyFile_SetOpenCodeHook(Py_OpenCodeHookFunction hook, void *userData); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/fileutils.h b/scripts/build-windows/py39-libs/include/cpython/fileutils.h new file mode 100644 index 000000000..e79d03e24 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/fileutils.h @@ -0,0 +1,165 @@ +#ifndef Py_CPYTHON_FILEUTILS_H +# error "this header file must not be included directly" +#endif + +typedef enum { + _Py_ERROR_UNKNOWN=0, + _Py_ERROR_STRICT, + _Py_ERROR_SURROGATEESCAPE, + _Py_ERROR_REPLACE, + _Py_ERROR_IGNORE, + _Py_ERROR_BACKSLASHREPLACE, + _Py_ERROR_SURROGATEPASS, + _Py_ERROR_XMLCHARREFREPLACE, + _Py_ERROR_OTHER +} _Py_error_handler; + +PyAPI_FUNC(_Py_error_handler) _Py_GetErrorHandler(const char *errors); + +PyAPI_FUNC(int) _Py_DecodeLocaleEx( + const char *arg, + wchar_t **wstr, + size_t *wlen, + const char **reason, + int current_locale, + _Py_error_handler errors); + +PyAPI_FUNC(int) _Py_EncodeLocaleEx( + const wchar_t *text, + char **str, + size_t *error_pos, + const char **reason, + int current_locale, + _Py_error_handler errors); + + +PyAPI_FUNC(PyObject *) _Py_device_encoding(int); + +#if defined(MS_WINDOWS) || defined(__APPLE__) + /* On Windows, the count parameter of read() is an int (bpo-9015, bpo-9611). + On macOS 10.13, read() and write() with more than INT_MAX bytes + fail with EINVAL (bpo-24658). */ +# define _PY_READ_MAX INT_MAX +# define _PY_WRITE_MAX INT_MAX +#else + /* write() should truncate the input to PY_SSIZE_T_MAX bytes, + but it's safer to do it ourself to have a portable behaviour */ +# define _PY_READ_MAX PY_SSIZE_T_MAX +# define _PY_WRITE_MAX PY_SSIZE_T_MAX +#endif + +#ifdef MS_WINDOWS +struct _Py_stat_struct { + unsigned long st_dev; + uint64_t st_ino; + unsigned short st_mode; + int st_nlink; + int st_uid; + int st_gid; + unsigned long st_rdev; + __int64 st_size; + time_t st_atime; + int st_atime_nsec; + time_t st_mtime; + int st_mtime_nsec; + time_t st_ctime; + int st_ctime_nsec; + unsigned long st_file_attributes; + unsigned long st_reparse_tag; +}; +#else +# define _Py_stat_struct stat +#endif + +PyAPI_FUNC(int) _Py_fstat( + int fd, + struct _Py_stat_struct *status); + +PyAPI_FUNC(int) _Py_fstat_noraise( + int fd, + struct _Py_stat_struct *status); + +PyAPI_FUNC(int) _Py_stat( + PyObject *path, + struct stat *status); + +PyAPI_FUNC(int) _Py_open( + const char *pathname, + int flags); + +PyAPI_FUNC(int) _Py_open_noraise( + const char *pathname, + int flags); + +PyAPI_FUNC(FILE *) _Py_wfopen( + const wchar_t *path, + const wchar_t *mode); + +PyAPI_FUNC(FILE*) _Py_fopen( + const char *pathname, + const char *mode); + +PyAPI_FUNC(FILE*) _Py_fopen_obj( + PyObject *path, + const char *mode); + +PyAPI_FUNC(Py_ssize_t) _Py_read( + int fd, + void *buf, + size_t count); + +PyAPI_FUNC(Py_ssize_t) _Py_write( + int fd, + const void *buf, + size_t count); + +PyAPI_FUNC(Py_ssize_t) _Py_write_noraise( + int fd, + const void *buf, + size_t count); + +#ifdef HAVE_READLINK +PyAPI_FUNC(int) _Py_wreadlink( + const wchar_t *path, + wchar_t *buf, + /* Number of characters of 'buf' buffer + including the trailing NUL character */ + size_t buflen); +#endif + +#ifdef HAVE_REALPATH +PyAPI_FUNC(wchar_t*) _Py_wrealpath( + const wchar_t *path, + wchar_t *resolved_path, + /* Number of characters of 'resolved_path' buffer + including the trailing NUL character */ + size_t resolved_path_len); +#endif + +#ifndef MS_WINDOWS +PyAPI_FUNC(int) _Py_isabs(const wchar_t *path); +#endif + +PyAPI_FUNC(int) _Py_abspath(const wchar_t *path, wchar_t **abspath_p); + +PyAPI_FUNC(wchar_t*) _Py_wgetcwd( + wchar_t *buf, + /* Number of characters of 'buf' buffer + including the trailing NUL character */ + size_t buflen); + +PyAPI_FUNC(int) _Py_get_inheritable(int fd); + +PyAPI_FUNC(int) _Py_set_inheritable(int fd, int inheritable, + int *atomic_flag_works); + +PyAPI_FUNC(int) _Py_set_inheritable_async_safe(int fd, int inheritable, + int *atomic_flag_works); + +PyAPI_FUNC(int) _Py_dup(int fd); + +#ifndef MS_WINDOWS +PyAPI_FUNC(int) _Py_get_blocking(int fd); + +PyAPI_FUNC(int) _Py_set_blocking(int fd, int blocking); +#endif /* !MS_WINDOWS */ diff --git a/scripts/build-windows/py39-libs/include/cpython/frameobject.h b/scripts/build-windows/py39-libs/include/cpython/frameobject.h new file mode 100644 index 000000000..36a51baae --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/frameobject.h @@ -0,0 +1,84 @@ +/* Frame object interface */ + +#ifndef Py_CPYTHON_FRAMEOBJECT_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int b_type; /* what kind of block this is */ + int b_handler; /* where to jump to find handler */ + int b_level; /* value stack level to pop to */ +} PyTryBlock; + +struct _frame { + PyObject_VAR_HEAD + struct _frame *f_back; /* previous frame, or NULL */ + PyCodeObject *f_code; /* code segment */ + PyObject *f_builtins; /* builtin symbol table (PyDictObject) */ + PyObject *f_globals; /* global symbol table (PyDictObject) */ + PyObject *f_locals; /* local symbol table (any mapping) */ + PyObject **f_valuestack; /* points after the last local */ + /* Next free slot in f_valuestack. Frame creation sets to f_valuestack. + Frame evaluation usually NULLs it, but a frame that yields sets it + to the current stack top. */ + PyObject **f_stacktop; + PyObject *f_trace; /* Trace function */ + char f_trace_lines; /* Emit per-line trace events? */ + char f_trace_opcodes; /* Emit per-opcode trace events? */ + + /* Borrowed reference to a generator, or NULL */ + PyObject *f_gen; + + int f_lasti; /* Last instruction if called */ + /* Call PyFrame_GetLineNumber() instead of reading this field + directly. As of 2.3 f_lineno is only valid when tracing is + active (i.e. when f_trace is set). At other times we use + PyCode_Addr2Line to calculate the line from the current + bytecode index. */ + int f_lineno; /* Current line number */ + int f_iblock; /* index in f_blockstack */ + char f_executing; /* whether the frame is still executing */ + PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ + PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ +}; + + +/* Standard object interface */ + +PyAPI_DATA(PyTypeObject) PyFrame_Type; + +#define PyFrame_Check(op) Py_IS_TYPE(op, &PyFrame_Type) + +PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *, + PyObject *, PyObject *); + +/* only internal use */ +PyFrameObject* _PyFrame_New_NoTrack(PyThreadState *, PyCodeObject *, + PyObject *, PyObject *); + + +/* The rest of the interface is specific for frame objects */ + +/* Block management functions */ + +PyAPI_FUNC(void) PyFrame_BlockSetup(PyFrameObject *, int, int, int); +PyAPI_FUNC(PyTryBlock *) PyFrame_BlockPop(PyFrameObject *); + +/* Conversions between "fast locals" and locals in dictionary */ + +PyAPI_FUNC(void) PyFrame_LocalsToFast(PyFrameObject *, int); + +PyAPI_FUNC(int) PyFrame_FastToLocalsWithError(PyFrameObject *f); +PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); + +PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out); + +PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/import.h b/scripts/build-windows/py39-libs/include/cpython/import.h new file mode 100644 index 000000000..c1b47121f --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/import.h @@ -0,0 +1,50 @@ +#ifndef Py_CPYTHON_IMPORT_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +PyMODINIT_FUNC PyInit__imp(void); + +PyAPI_FUNC(int) _PyImport_IsInitialized(PyInterpreterState *); + +PyAPI_FUNC(PyObject *) _PyImport_GetModuleId(struct _Py_Identifier *name); +PyAPI_FUNC(int) _PyImport_SetModule(PyObject *name, PyObject *module); +PyAPI_FUNC(int) _PyImport_SetModuleString(const char *name, PyObject* module); + +PyAPI_FUNC(void) _PyImport_AcquireLock(void); +PyAPI_FUNC(int) _PyImport_ReleaseLock(void); + +PyAPI_FUNC(PyObject *) _PyImport_FindExtensionObject(PyObject *, PyObject *); + +PyAPI_FUNC(int) _PyImport_FixupBuiltin( + PyObject *mod, + const char *name, /* UTF-8 encoded string */ + PyObject *modules + ); +PyAPI_FUNC(int) _PyImport_FixupExtensionObject(PyObject*, PyObject *, + PyObject *, PyObject *); + +struct _inittab { + const char *name; /* ASCII encoded string */ + PyObject* (*initfunc)(void); +}; +PyAPI_DATA(struct _inittab *) PyImport_Inittab; +PyAPI_FUNC(int) PyImport_ExtendInittab(struct _inittab *newtab); + +struct _frozen { + const char *name; /* ASCII encoded string */ + const unsigned char *code; + int size; +}; + +/* Embedding apps may change this pointer to point to their favorite + collection of frozen modules: */ + +PyAPI_DATA(const struct _frozen *) PyImport_FrozenModules; + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/initconfig.h b/scripts/build-windows/py39-libs/include/cpython/initconfig.h new file mode 100644 index 000000000..0a256d4b5 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/initconfig.h @@ -0,0 +1,462 @@ +#ifndef Py_PYCORECONFIG_H +#define Py_PYCORECONFIG_H +#ifndef Py_LIMITED_API +#ifdef __cplusplus +extern "C" { +#endif + +/* --- PyStatus ----------------------------------------------- */ + +typedef struct { + enum { + _PyStatus_TYPE_OK=0, + _PyStatus_TYPE_ERROR=1, + _PyStatus_TYPE_EXIT=2 + } _type; + const char *func; + const char *err_msg; + int exitcode; +} PyStatus; + +PyAPI_FUNC(PyStatus) PyStatus_Ok(void); +PyAPI_FUNC(PyStatus) PyStatus_Error(const char *err_msg); +PyAPI_FUNC(PyStatus) PyStatus_NoMemory(void); +PyAPI_FUNC(PyStatus) PyStatus_Exit(int exitcode); +PyAPI_FUNC(int) PyStatus_IsError(PyStatus err); +PyAPI_FUNC(int) PyStatus_IsExit(PyStatus err); +PyAPI_FUNC(int) PyStatus_Exception(PyStatus err); + +/* --- PyWideStringList ------------------------------------------------ */ + +typedef struct { + /* If length is greater than zero, items must be non-NULL + and all items strings must be non-NULL */ + Py_ssize_t length; + wchar_t **items; +} PyWideStringList; + +PyAPI_FUNC(PyStatus) PyWideStringList_Append(PyWideStringList *list, + const wchar_t *item); +PyAPI_FUNC(PyStatus) PyWideStringList_Insert(PyWideStringList *list, + Py_ssize_t index, + const wchar_t *item); + + +/* --- PyPreConfig ----------------------------------------------- */ + +typedef struct { + int _config_init; /* _PyConfigInitEnum value */ + + /* Parse Py_PreInitializeFromBytesArgs() arguments? + See PyConfig.parse_argv */ + int parse_argv; + + /* If greater than 0, enable isolated mode: sys.path contains + neither the script's directory nor the user's site-packages directory. + + Set to 1 by the -I command line option. If set to -1 (default), inherit + Py_IsolatedFlag value. */ + int isolated; + + /* If greater than 0: use environment variables. + Set to 0 by -E command line option. If set to -1 (default), it is + set to !Py_IgnoreEnvironmentFlag. */ + int use_environment; + + /* Set the LC_CTYPE locale to the user preferred locale? If equals to 0, + set coerce_c_locale and coerce_c_locale_warn to 0. */ + int configure_locale; + + /* Coerce the LC_CTYPE locale if it's equal to "C"? (PEP 538) + + Set to 0 by PYTHONCOERCECLOCALE=0. Set to 1 by PYTHONCOERCECLOCALE=1. + Set to 2 if the user preferred LC_CTYPE locale is "C". + + If it is equal to 1, LC_CTYPE locale is read to decide if it should be + coerced or not (ex: PYTHONCOERCECLOCALE=1). Internally, it is set to 2 + if the LC_CTYPE locale must be coerced. + + Disable by default (set to 0). Set it to -1 to let Python decide if it + should be enabled or not. */ + int coerce_c_locale; + + /* Emit a warning if the LC_CTYPE locale is coerced? + + Set to 1 by PYTHONCOERCECLOCALE=warn. + + Disable by default (set to 0). Set it to -1 to let Python decide if it + should be enabled or not. */ + int coerce_c_locale_warn; + +#ifdef MS_WINDOWS + /* If greater than 1, use the "mbcs" encoding instead of the UTF-8 + encoding for the filesystem encoding. + + Set to 1 if the PYTHONLEGACYWINDOWSFSENCODING environment variable is + set to a non-empty string. If set to -1 (default), inherit + Py_LegacyWindowsFSEncodingFlag value. + + See PEP 529 for more details. */ + int legacy_windows_fs_encoding; +#endif + + /* Enable UTF-8 mode? (PEP 540) + + Disabled by default (equals to 0). + + Set to 1 by "-X utf8" and "-X utf8=1" command line options. + Set to 1 by PYTHONUTF8=1 environment variable. + + Set to 0 by "-X utf8=0" and PYTHONUTF8=0. + + If equals to -1, it is set to 1 if the LC_CTYPE locale is "C" or + "POSIX", otherwise it is set to 0. Inherit Py_UTF8Mode value value. */ + int utf8_mode; + + /* If non-zero, enable the Python Development Mode. + + Set to 1 by the -X dev command line option. Set by the PYTHONDEVMODE + environment variable. */ + int dev_mode; + + /* Memory allocator: PYTHONMALLOC env var. + See PyMemAllocatorName for valid values. */ + int allocator; +} PyPreConfig; + +PyAPI_FUNC(void) PyPreConfig_InitPythonConfig(PyPreConfig *config); +PyAPI_FUNC(void) PyPreConfig_InitIsolatedConfig(PyPreConfig *config); + + +/* --- PyConfig ---------------------------------------------- */ + +typedef struct { + int _config_init; /* _PyConfigInitEnum value */ + + int isolated; /* Isolated mode? see PyPreConfig.isolated */ + int use_environment; /* Use environment variables? see PyPreConfig.use_environment */ + int dev_mode; /* Python Development Mode? See PyPreConfig.dev_mode */ + + /* Install signal handlers? Yes by default. */ + int install_signal_handlers; + + int use_hash_seed; /* PYTHONHASHSEED=x */ + unsigned long hash_seed; + + /* Enable faulthandler? + Set to 1 by -X faulthandler and PYTHONFAULTHANDLER. -1 means unset. */ + int faulthandler; + + /* Enable PEG parser? + 1 by default, set to 0 by -X oldparser and PYTHONOLDPARSER */ + int _use_peg_parser; + + /* Enable tracemalloc? + Set by -X tracemalloc=N and PYTHONTRACEMALLOC. -1 means unset */ + int tracemalloc; + + int import_time; /* PYTHONPROFILEIMPORTTIME, -X importtime */ + int show_ref_count; /* -X showrefcount */ + int dump_refs; /* PYTHONDUMPREFS */ + int malloc_stats; /* PYTHONMALLOCSTATS */ + + /* Python filesystem encoding and error handler: + sys.getfilesystemencoding() and sys.getfilesystemencodeerrors(). + + Default encoding and error handler: + + * if Py_SetStandardStreamEncoding() has been called: they have the + highest priority; + * PYTHONIOENCODING environment variable; + * The UTF-8 Mode uses UTF-8/surrogateescape; + * If Python forces the usage of the ASCII encoding (ex: C locale + or POSIX locale on FreeBSD or HP-UX), use ASCII/surrogateescape; + * locale encoding: ANSI code page on Windows, UTF-8 on Android and + VxWorks, LC_CTYPE locale encoding on other platforms; + * On Windows, "surrogateescape" error handler; + * "surrogateescape" error handler if the LC_CTYPE locale is "C" or "POSIX"; + * "surrogateescape" error handler if the LC_CTYPE locale has been coerced + (PEP 538); + * "strict" error handler. + + Supported error handlers: "strict", "surrogateescape" and + "surrogatepass". The surrogatepass error handler is only supported + if Py_DecodeLocale() and Py_EncodeLocale() use directly the UTF-8 codec; + it's only used on Windows. + + initfsencoding() updates the encoding to the Python codec name. + For example, "ANSI_X3.4-1968" is replaced with "ascii". + + On Windows, sys._enablelegacywindowsfsencoding() sets the + encoding/errors to mbcs/replace at runtime. + + + See Py_FileSystemDefaultEncoding and Py_FileSystemDefaultEncodeErrors. + */ + wchar_t *filesystem_encoding; + wchar_t *filesystem_errors; + + wchar_t *pycache_prefix; /* PYTHONPYCACHEPREFIX, -X pycache_prefix=PATH */ + int parse_argv; /* Parse argv command line arguments? */ + + /* Command line arguments (sys.argv). + + Set parse_argv to 1 to parse argv as Python command line arguments + and then strip Python arguments from argv. + + If argv is empty, an empty string is added to ensure that sys.argv + always exists and is never empty. */ + PyWideStringList argv; + + /* Program name: + + - If Py_SetProgramName() was called, use its value. + - On macOS, use PYTHONEXECUTABLE environment variable if set. + - If WITH_NEXT_FRAMEWORK macro is defined, use __PYVENV_LAUNCHER__ + environment variable is set. + - Use argv[0] if available and non-empty. + - Use "python" on Windows, or "python3 on other platforms. */ + wchar_t *program_name; + + PyWideStringList xoptions; /* Command line -X options */ + + /* Warnings options: lowest to highest priority. warnings.filters + is built in the reverse order (highest to lowest priority). */ + PyWideStringList warnoptions; + + /* If equal to zero, disable the import of the module site and the + site-dependent manipulations of sys.path that it entails. Also disable + these manipulations if site is explicitly imported later (call + site.main() if you want them to be triggered). + + Set to 0 by the -S command line option. If set to -1 (default), it is + set to !Py_NoSiteFlag. */ + int site_import; + + /* Bytes warnings: + + * If equal to 1, issue a warning when comparing bytes or bytearray with + str or bytes with int. + * If equal or greater to 2, issue an error. + + Incremented by the -b command line option. If set to -1 (default), inherit + Py_BytesWarningFlag value. */ + int bytes_warning; + + /* If greater than 0, enable inspect: when a script is passed as first + argument or the -c option is used, enter interactive mode after + executing the script or the command, even when sys.stdin does not appear + to be a terminal. + + Incremented by the -i command line option. Set to 1 if the PYTHONINSPECT + environment variable is non-empty. If set to -1 (default), inherit + Py_InspectFlag value. */ + int inspect; + + /* If greater than 0: enable the interactive mode (REPL). + + Incremented by the -i command line option. If set to -1 (default), + inherit Py_InteractiveFlag value. */ + int interactive; + + /* Optimization level. + + Incremented by the -O command line option. Set by the PYTHONOPTIMIZE + environment variable. If set to -1 (default), inherit Py_OptimizeFlag + value. */ + int optimization_level; + + /* If greater than 0, enable the debug mode: turn on parser debugging + output (for expert only, depending on compilation options). + + Incremented by the -d command line option. Set by the PYTHONDEBUG + environment variable. If set to -1 (default), inherit Py_DebugFlag + value. */ + int parser_debug; + + /* If equal to 0, Python won't try to write ``.pyc`` files on the + import of source modules. + + Set to 0 by the -B command line option and the PYTHONDONTWRITEBYTECODE + environment variable. If set to -1 (default), it is set to + !Py_DontWriteBytecodeFlag. */ + int write_bytecode; + + /* If greater than 0, enable the verbose mode: print a message each time a + module is initialized, showing the place (filename or built-in module) + from which it is loaded. + + If greater or equal to 2, print a message for each file that is checked + for when searching for a module. Also provides information on module + cleanup at exit. + + Incremented by the -v option. Set by the PYTHONVERBOSE environment + variable. If set to -1 (default), inherit Py_VerboseFlag value. */ + int verbose; + + /* If greater than 0, enable the quiet mode: Don't display the copyright + and version messages even in interactive mode. + + Incremented by the -q option. If set to -1 (default), inherit + Py_QuietFlag value. */ + int quiet; + + /* If greater than 0, don't add the user site-packages directory to + sys.path. + + Set to 0 by the -s and -I command line options , and the PYTHONNOUSERSITE + environment variable. If set to -1 (default), it is set to + !Py_NoUserSiteDirectory. */ + int user_site_directory; + + /* If non-zero, configure C standard steams (stdio, stdout, + stderr): + + - Set O_BINARY mode on Windows. + - If buffered_stdio is equal to zero, make streams unbuffered. + Otherwise, enable streams buffering if interactive is non-zero. */ + int configure_c_stdio; + + /* If equal to 0, enable unbuffered mode: force the stdout and stderr + streams to be unbuffered. + + Set to 0 by the -u option. Set by the PYTHONUNBUFFERED environment + variable. + If set to -1 (default), it is set to !Py_UnbufferedStdioFlag. */ + int buffered_stdio; + + /* Encoding of sys.stdin, sys.stdout and sys.stderr. + Value set from PYTHONIOENCODING environment variable and + Py_SetStandardStreamEncoding() function. + See also 'stdio_errors' attribute. */ + wchar_t *stdio_encoding; + + /* Error handler of sys.stdin and sys.stdout. + Value set from PYTHONIOENCODING environment variable and + Py_SetStandardStreamEncoding() function. + See also 'stdio_encoding' attribute. */ + wchar_t *stdio_errors; + +#ifdef MS_WINDOWS + /* If greater than zero, use io.FileIO instead of WindowsConsoleIO for sys + standard streams. + + Set to 1 if the PYTHONLEGACYWINDOWSSTDIO environment variable is set to + a non-empty string. If set to -1 (default), inherit + Py_LegacyWindowsStdioFlag value. + + See PEP 528 for more details. */ + int legacy_windows_stdio; +#endif + + /* Value of the --check-hash-based-pycs command line option: + + - "default" means the 'check_source' flag in hash-based pycs + determines invalidation + - "always" causes the interpreter to hash the source file for + invalidation regardless of value of 'check_source' bit + - "never" causes the interpreter to always assume hash-based pycs are + valid + + The default value is "default". + + See PEP 552 "Deterministic pycs" for more details. */ + wchar_t *check_hash_pycs_mode; + + /* --- Path configuration inputs ------------ */ + + /* If greater than 0, suppress _PyPathConfig_Calculate() warnings on Unix. + The parameter has no effect on Windows. + + If set to -1 (default), inherit !Py_FrozenFlag value. */ + int pathconfig_warnings; + + wchar_t *pythonpath_env; /* PYTHONPATH environment variable */ + wchar_t *home; /* PYTHONHOME environment variable, + see also Py_SetPythonHome(). */ + + /* --- Path configuration outputs ----------- */ + + int module_search_paths_set; /* If non-zero, use module_search_paths */ + PyWideStringList module_search_paths; /* sys.path paths. Computed if + module_search_paths_set is equal + to zero. */ + + wchar_t *executable; /* sys.executable */ + wchar_t *base_executable; /* sys._base_executable */ + wchar_t *prefix; /* sys.prefix */ + wchar_t *base_prefix; /* sys.base_prefix */ + wchar_t *exec_prefix; /* sys.exec_prefix */ + wchar_t *base_exec_prefix; /* sys.base_exec_prefix */ + wchar_t *platlibdir; /* sys.platlibdir */ + + /* --- Parameter only used by Py_Main() ---------- */ + + /* Skip the first line of the source ('run_filename' parameter), allowing use of non-Unix forms of + "#!cmd". This is intended for a DOS specific hack only. + + Set by the -x command line option. */ + int skip_source_first_line; + + wchar_t *run_command; /* -c command line argument */ + wchar_t *run_module; /* -m command line argument */ + wchar_t *run_filename; /* Trailing command line argument without -c or -m */ + + /* --- Private fields ---------------------------- */ + + /* Install importlib? If set to 0, importlib is not initialized at all. + Needed by freeze_importlib. */ + int _install_importlib; + + /* If equal to 0, stop Python initialization before the "main" phase */ + int _init_main; + + /* If non-zero, disallow threads, subprocesses, and fork. + Default: 0. */ + int _isolated_interpreter; + + /* Original command line arguments. If _orig_argv is empty and _argv is + not equal to [''], PyConfig_Read() copies the configuration 'argv' list + into '_orig_argv' list before modifying 'argv' list (if parse_argv + is non-zero). + + _PyConfig_Write() initializes Py_GetArgcArgv() to this list. */ + PyWideStringList _orig_argv; +} PyConfig; + +PyAPI_FUNC(void) PyConfig_InitPythonConfig(PyConfig *config); +PyAPI_FUNC(void) PyConfig_InitIsolatedConfig(PyConfig *config); +PyAPI_FUNC(void) PyConfig_Clear(PyConfig *); +PyAPI_FUNC(PyStatus) PyConfig_SetString( + PyConfig *config, + wchar_t **config_str, + const wchar_t *str); +PyAPI_FUNC(PyStatus) PyConfig_SetBytesString( + PyConfig *config, + wchar_t **config_str, + const char *str); +PyAPI_FUNC(PyStatus) PyConfig_Read(PyConfig *config); +PyAPI_FUNC(PyStatus) PyConfig_SetBytesArgv( + PyConfig *config, + Py_ssize_t argc, + char * const *argv); +PyAPI_FUNC(PyStatus) PyConfig_SetArgv(PyConfig *config, + Py_ssize_t argc, + wchar_t * const *argv); +PyAPI_FUNC(PyStatus) PyConfig_SetWideStringList(PyConfig *config, + PyWideStringList *list, + Py_ssize_t length, wchar_t **items); + + +/* --- Helper functions --------------------------------------- */ + +/* Get the original command line arguments, before Python modified them. + + See also PyConfig._orig_argv. */ +PyAPI_FUNC(void) Py_GetArgcArgv(int *argc, wchar_t ***argv); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_LIMITED_API */ +#endif /* !Py_PYCORECONFIG_H */ diff --git a/scripts/build-windows/py39-libs/include/cpython/interpreteridobject.h b/scripts/build-windows/py39-libs/include/cpython/interpreteridobject.h new file mode 100644 index 000000000..67ec58735 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/interpreteridobject.h @@ -0,0 +1,19 @@ +#ifndef Py_CPYTHON_INTERPRETERIDOBJECT_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Interpreter ID Object */ + +PyAPI_DATA(PyTypeObject) _PyInterpreterID_Type; + +PyAPI_FUNC(PyObject *) _PyInterpreterID_New(int64_t); +PyAPI_FUNC(PyObject *) _PyInterpreterState_GetIDObject(PyInterpreterState *); +PyAPI_FUNC(PyInterpreterState *) _PyInterpreterID_LookUp(PyObject *); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/listobject.h b/scripts/build-windows/py39-libs/include/cpython/listobject.h new file mode 100644 index 000000000..74fe3301a --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/listobject.h @@ -0,0 +1,43 @@ +#ifndef Py_CPYTHON_LISTOBJECT_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject_VAR_HEAD + /* Vector of pointers to list elements. list[0] is ob_item[0], etc. */ + PyObject **ob_item; + + /* ob_item contains space for 'allocated' elements. The number + * currently in use is ob_size. + * Invariants: + * 0 <= ob_size <= allocated + * len(list) == ob_size + * ob_item == NULL implies ob_size == allocated == 0 + * list.sort() temporarily sets allocated to -1 to detect mutations. + * + * Items must normally not be NULL, except during construction when + * the list is not yet visible outside the function that builds it. + */ + Py_ssize_t allocated; +} PyListObject; + +PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *); +PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out); + +/* Macro, trading safety for speed */ + +/* Cast argument to PyTupleObject* type. */ +#define _PyList_CAST(op) (assert(PyList_Check(op)), (PyListObject *)(op)) + +#define PyList_GET_ITEM(op, i) (_PyList_CAST(op)->ob_item[i]) +#define PyList_SET_ITEM(op, i, v) (_PyList_CAST(op)->ob_item[i] = (v)) +#define PyList_GET_SIZE(op) Py_SIZE(_PyList_CAST(op)) +#define _PyList_ITEMS(op) (_PyList_CAST(op)->ob_item) + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/methodobject.h b/scripts/build-windows/py39-libs/include/cpython/methodobject.h new file mode 100644 index 000000000..7ecbfe3b5 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/methodobject.h @@ -0,0 +1,35 @@ +#ifndef Py_CPYTHON_METHODOBJECT_H +# error "this header file must not be included directly" +#endif + +PyAPI_DATA(PyTypeObject) PyCMethod_Type; + +#define PyCMethod_CheckExact(op) Py_IS_TYPE(op, &PyCMethod_Type) +#define PyCMethod_Check(op) PyObject_TypeCheck(op, &PyCMethod_Type) + +/* Macros for direct access to these values. Type checks are *not* + done, so use with care. */ +#define PyCFunction_GET_FUNCTION(func) \ + (((PyCFunctionObject *)func) -> m_ml -> ml_meth) +#define PyCFunction_GET_SELF(func) \ + (((PyCFunctionObject *)func) -> m_ml -> ml_flags & METH_STATIC ? \ + NULL : ((PyCFunctionObject *)func) -> m_self) +#define PyCFunction_GET_FLAGS(func) \ + (((PyCFunctionObject *)func) -> m_ml -> ml_flags) +#define PyCFunction_GET_CLASS(func) \ + (((PyCFunctionObject *)func) -> m_ml -> ml_flags & METH_METHOD ? \ + ((PyCMethodObject *)func) -> mm_class : NULL) + +typedef struct { + PyObject_HEAD + PyMethodDef *m_ml; /* Description of the C function to call */ + PyObject *m_self; /* Passed as 'self' arg to the C func, can be NULL */ + PyObject *m_module; /* The __module__ attribute, can be anything */ + PyObject *m_weakreflist; /* List of weak references */ + vectorcallfunc vectorcall; +} PyCFunctionObject; + +typedef struct { + PyCFunctionObject func; + PyTypeObject *mm_class; /* Class that defines this method */ +} PyCMethodObject; diff --git a/scripts/build-windows/py39-libs/include/cpython/object.h b/scripts/build-windows/py39-libs/include/cpython/object.h new file mode 100644 index 000000000..444f832f5 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/object.h @@ -0,0 +1,554 @@ +#ifndef Py_CPYTHON_OBJECT_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(void) _Py_NewReference(PyObject *op); + +#ifdef Py_TRACE_REFS +/* Py_TRACE_REFS is such major surgery that we call external routines. */ +PyAPI_FUNC(void) _Py_ForgetReference(PyObject *); +#endif + +/* Update the Python traceback of an object. This function must be called + when a memory block is reused from a free list. */ +PyAPI_FUNC(int) _PyTraceMalloc_NewReference(PyObject *op); + +#ifdef Py_REF_DEBUG +PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void); +#endif + + +/********************* String Literals ****************************************/ +/* This structure helps managing static strings. The basic usage goes like this: + Instead of doing + + r = PyObject_CallMethod(o, "foo", "args", ...); + + do + + _Py_IDENTIFIER(foo); + ... + r = _PyObject_CallMethodId(o, &PyId_foo, "args", ...); + + PyId_foo is a static variable, either on block level or file level. On first + usage, the string "foo" is interned, and the structures are linked. On interpreter + shutdown, all strings are released. + + Alternatively, _Py_static_string allows choosing the variable name. + _PyUnicode_FromId returns a borrowed reference to the interned string. + _PyObject_{Get,Set,Has}AttrId are __getattr__ versions using _Py_Identifier*. +*/ +typedef struct _Py_Identifier { + struct _Py_Identifier *next; + const char* string; + PyObject *object; +} _Py_Identifier; + +#define _Py_static_string_init(value) { .next = NULL, .string = value, .object = NULL } +#define _Py_static_string(varname, value) static _Py_Identifier varname = _Py_static_string_init(value) +#define _Py_IDENTIFIER(varname) _Py_static_string(PyId_##varname, #varname) + +/* buffer interface */ +typedef struct bufferinfo { + void *buf; + PyObject *obj; /* owned reference */ + Py_ssize_t len; + Py_ssize_t itemsize; /* This is Py_ssize_t so it can be + pointed to by strides in simple case.*/ + int readonly; + int ndim; + char *format; + Py_ssize_t *shape; + Py_ssize_t *strides; + Py_ssize_t *suboffsets; + void *internal; +} Py_buffer; + +typedef int (*getbufferproc)(PyObject *, Py_buffer *, int); +typedef void (*releasebufferproc)(PyObject *, Py_buffer *); + +typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, + size_t nargsf, PyObject *kwnames); + +/* Maximum number of dimensions */ +#define PyBUF_MAX_NDIM 64 + +/* Flags for getting buffers */ +#define PyBUF_SIMPLE 0 +#define PyBUF_WRITABLE 0x0001 +/* we used to include an E, backwards compatible alias */ +#define PyBUF_WRITEABLE PyBUF_WRITABLE +#define PyBUF_FORMAT 0x0004 +#define PyBUF_ND 0x0008 +#define PyBUF_STRIDES (0x0010 | PyBUF_ND) +#define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES) +#define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES) +#define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES) +#define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES) + +#define PyBUF_CONTIG (PyBUF_ND | PyBUF_WRITABLE) +#define PyBUF_CONTIG_RO (PyBUF_ND) + +#define PyBUF_STRIDED (PyBUF_STRIDES | PyBUF_WRITABLE) +#define PyBUF_STRIDED_RO (PyBUF_STRIDES) + +#define PyBUF_RECORDS (PyBUF_STRIDES | PyBUF_WRITABLE | PyBUF_FORMAT) +#define PyBUF_RECORDS_RO (PyBUF_STRIDES | PyBUF_FORMAT) + +#define PyBUF_FULL (PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT) +#define PyBUF_FULL_RO (PyBUF_INDIRECT | PyBUF_FORMAT) + + +#define PyBUF_READ 0x100 +#define PyBUF_WRITE 0x200 +/* End buffer interface */ + + +typedef struct { + /* Number implementations must check *both* + arguments for proper type and implement the necessary conversions + in the slot functions themselves. */ + + binaryfunc nb_add; + binaryfunc nb_subtract; + binaryfunc nb_multiply; + binaryfunc nb_remainder; + binaryfunc nb_divmod; + ternaryfunc nb_power; + unaryfunc nb_negative; + unaryfunc nb_positive; + unaryfunc nb_absolute; + inquiry nb_bool; + unaryfunc nb_invert; + binaryfunc nb_lshift; + binaryfunc nb_rshift; + binaryfunc nb_and; + binaryfunc nb_xor; + binaryfunc nb_or; + unaryfunc nb_int; + void *nb_reserved; /* the slot formerly known as nb_long */ + unaryfunc nb_float; + + binaryfunc nb_inplace_add; + binaryfunc nb_inplace_subtract; + binaryfunc nb_inplace_multiply; + binaryfunc nb_inplace_remainder; + ternaryfunc nb_inplace_power; + binaryfunc nb_inplace_lshift; + binaryfunc nb_inplace_rshift; + binaryfunc nb_inplace_and; + binaryfunc nb_inplace_xor; + binaryfunc nb_inplace_or; + + binaryfunc nb_floor_divide; + binaryfunc nb_true_divide; + binaryfunc nb_inplace_floor_divide; + binaryfunc nb_inplace_true_divide; + + unaryfunc nb_index; + + binaryfunc nb_matrix_multiply; + binaryfunc nb_inplace_matrix_multiply; +} PyNumberMethods; + +typedef struct { + lenfunc sq_length; + binaryfunc sq_concat; + ssizeargfunc sq_repeat; + ssizeargfunc sq_item; + void *was_sq_slice; + ssizeobjargproc sq_ass_item; + void *was_sq_ass_slice; + objobjproc sq_contains; + + binaryfunc sq_inplace_concat; + ssizeargfunc sq_inplace_repeat; +} PySequenceMethods; + +typedef struct { + lenfunc mp_length; + binaryfunc mp_subscript; + objobjargproc mp_ass_subscript; +} PyMappingMethods; + +typedef struct { + unaryfunc am_await; + unaryfunc am_aiter; + unaryfunc am_anext; +} PyAsyncMethods; + +typedef struct { + getbufferproc bf_getbuffer; + releasebufferproc bf_releasebuffer; +} PyBufferProcs; + +/* Allow printfunc in the tp_vectorcall_offset slot for + * backwards-compatibility */ +typedef Py_ssize_t printfunc; + +struct _typeobject { + PyObject_VAR_HEAD + const char *tp_name; /* For printing, in format "." */ + Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */ + + /* Methods to implement standard operations */ + + destructor tp_dealloc; + Py_ssize_t tp_vectorcall_offset; + getattrfunc tp_getattr; + setattrfunc tp_setattr; + PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2) + or tp_reserved (Python 3) */ + reprfunc tp_repr; + + /* Method suites for standard classes */ + + PyNumberMethods *tp_as_number; + PySequenceMethods *tp_as_sequence; + PyMappingMethods *tp_as_mapping; + + /* More standard operations (here for binary compatibility) */ + + hashfunc tp_hash; + ternaryfunc tp_call; + reprfunc tp_str; + getattrofunc tp_getattro; + setattrofunc tp_setattro; + + /* Functions to access object as input/output buffer */ + PyBufferProcs *tp_as_buffer; + + /* Flags to define presence of optional/expanded features */ + unsigned long tp_flags; + + const char *tp_doc; /* Documentation string */ + + /* Assigned meaning in release 2.0 */ + /* call function for all accessible objects */ + traverseproc tp_traverse; + + /* delete references to contained objects */ + inquiry tp_clear; + + /* Assigned meaning in release 2.1 */ + /* rich comparisons */ + richcmpfunc tp_richcompare; + + /* weak reference enabler */ + Py_ssize_t tp_weaklistoffset; + + /* Iterators */ + getiterfunc tp_iter; + iternextfunc tp_iternext; + + /* Attribute descriptor and subclassing stuff */ + struct PyMethodDef *tp_methods; + struct PyMemberDef *tp_members; + struct PyGetSetDef *tp_getset; + struct _typeobject *tp_base; + PyObject *tp_dict; + descrgetfunc tp_descr_get; + descrsetfunc tp_descr_set; + Py_ssize_t tp_dictoffset; + initproc tp_init; + allocfunc tp_alloc; + newfunc tp_new; + freefunc tp_free; /* Low-level free-memory routine */ + inquiry tp_is_gc; /* For PyObject_IS_GC */ + PyObject *tp_bases; + PyObject *tp_mro; /* method resolution order */ + PyObject *tp_cache; + PyObject *tp_subclasses; + PyObject *tp_weaklist; + destructor tp_del; + + /* Type attribute cache version tag. Added in version 2.6 */ + unsigned int tp_version_tag; + + destructor tp_finalize; + vectorcallfunc tp_vectorcall; +}; + +/* The *real* layout of a type object when allocated on the heap */ +typedef struct _heaptypeobject { + /* Note: there's a dependency on the order of these members + in slotptr() in typeobject.c . */ + PyTypeObject ht_type; + PyAsyncMethods as_async; + PyNumberMethods as_number; + PyMappingMethods as_mapping; + PySequenceMethods as_sequence; /* as_sequence comes after as_mapping, + so that the mapping wins when both + the mapping and the sequence define + a given operator (e.g. __getitem__). + see add_operators() in typeobject.c . */ + PyBufferProcs as_buffer; + PyObject *ht_name, *ht_slots, *ht_qualname; + struct _dictkeysobject *ht_cached_keys; + PyObject *ht_module; + /* here are optional user slots, followed by the members. */ +} PyHeapTypeObject; + +/* access macro to the members which are floating "behind" the object */ +#define PyHeapType_GET_MEMBERS(etype) \ + ((PyMemberDef *)(((char *)etype) + Py_TYPE(etype)->tp_basicsize)) + +PyAPI_FUNC(const char *) _PyType_Name(PyTypeObject *); +PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *); +PyAPI_FUNC(PyObject *) _PyType_LookupId(PyTypeObject *, _Py_Identifier *); +PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, _Py_Identifier *); +PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *); +PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *, const char *); +PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *, const char *); + +struct _Py_Identifier; +PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int); +PyAPI_FUNC(void) _Py_BreakPoint(void); +PyAPI_FUNC(void) _PyObject_Dump(PyObject *); +PyAPI_FUNC(int) _PyObject_IsFreed(PyObject *); + +PyAPI_FUNC(int) _PyObject_IsAbstract(PyObject *); +PyAPI_FUNC(PyObject *) _PyObject_GetAttrId(PyObject *, struct _Py_Identifier *); +PyAPI_FUNC(int) _PyObject_SetAttrId(PyObject *, struct _Py_Identifier *, PyObject *); +PyAPI_FUNC(int) _PyObject_HasAttrId(PyObject *, struct _Py_Identifier *); +/* Replacements of PyObject_GetAttr() and _PyObject_GetAttrId() which + don't raise AttributeError. + + Return 1 and set *result != NULL if an attribute is found. + Return 0 and set *result == NULL if an attribute is not found; + an AttributeError is silenced. + Return -1 and set *result == NULL if an error other than AttributeError + is raised. +*/ +PyAPI_FUNC(int) _PyObject_LookupAttr(PyObject *, PyObject *, PyObject **); +PyAPI_FUNC(int) _PyObject_LookupAttrId(PyObject *, struct _Py_Identifier *, PyObject **); + +PyAPI_FUNC(int) _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method); + +PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *); +PyAPI_FUNC(PyObject *) _PyObject_NextNotImplemented(PyObject *); +PyAPI_FUNC(void) PyObject_CallFinalizer(PyObject *); +PyAPI_FUNC(int) PyObject_CallFinalizerFromDealloc(PyObject *); + +/* Same as PyObject_Generic{Get,Set}Attr, but passing the attributes + dict as the last parameter. */ +PyAPI_FUNC(PyObject *) +_PyObject_GenericGetAttrWithDict(PyObject *, PyObject *, PyObject *, int); +PyAPI_FUNC(int) +_PyObject_GenericSetAttrWithDict(PyObject *, PyObject *, + PyObject *, PyObject *); + +PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *); + +/* Safely decref `op` and set `op` to `op2`. + * + * As in case of Py_CLEAR "the obvious" code can be deadly: + * + * Py_DECREF(op); + * op = op2; + * + * The safe way is: + * + * Py_SETREF(op, op2); + * + * That arranges to set `op` to `op2` _before_ decref'ing, so that any code + * triggered as a side-effect of `op` getting torn down no longer believes + * `op` points to a valid object. + * + * Py_XSETREF is a variant of Py_SETREF that uses Py_XDECREF instead of + * Py_DECREF. + */ + +#define Py_SETREF(op, op2) \ + do { \ + PyObject *_py_tmp = _PyObject_CAST(op); \ + (op) = (op2); \ + Py_DECREF(_py_tmp); \ + } while (0) + +#define Py_XSETREF(op, op2) \ + do { \ + PyObject *_py_tmp = _PyObject_CAST(op); \ + (op) = (op2); \ + Py_XDECREF(_py_tmp); \ + } while (0) + + +PyAPI_DATA(PyTypeObject) _PyNone_Type; +PyAPI_DATA(PyTypeObject) _PyNotImplemented_Type; + +/* Maps Py_LT to Py_GT, ..., Py_GE to Py_LE. + * Defined in object.c. + */ +PyAPI_DATA(int) _Py_SwappedOp[]; + +PyAPI_FUNC(void) +_PyDebugAllocatorStats(FILE *out, const char *block_name, int num_blocks, + size_t sizeof_block); +PyAPI_FUNC(void) +_PyObject_DebugTypeStats(FILE *out); + +/* Define a pair of assertion macros: + _PyObject_ASSERT_FROM(), _PyObject_ASSERT_WITH_MSG() and _PyObject_ASSERT(). + + These work like the regular C assert(), in that they will abort the + process with a message on stderr if the given condition fails to hold, + but compile away to nothing if NDEBUG is defined. + + However, before aborting, Python will also try to call _PyObject_Dump() on + the given object. This may be of use when investigating bugs in which a + particular object is corrupt (e.g. buggy a tp_visit method in an extension + module breaking the garbage collector), to help locate the broken objects. + + The WITH_MSG variant allows you to supply an additional message that Python + will attempt to print to stderr, after the object dump. */ +#ifdef NDEBUG + /* No debugging: compile away the assertions: */ +# define _PyObject_ASSERT_FROM(obj, expr, msg, filename, lineno, func) \ + ((void)0) +#else + /* With debugging: generate checks: */ +# define _PyObject_ASSERT_FROM(obj, expr, msg, filename, lineno, func) \ + ((expr) \ + ? (void)(0) \ + : _PyObject_AssertFailed((obj), Py_STRINGIFY(expr), \ + (msg), (filename), (lineno), (func))) +#endif + +#define _PyObject_ASSERT_WITH_MSG(obj, expr, msg) \ + _PyObject_ASSERT_FROM(obj, expr, msg, __FILE__, __LINE__, __func__) +#define _PyObject_ASSERT(obj, expr) \ + _PyObject_ASSERT_WITH_MSG(obj, expr, NULL) + +#define _PyObject_ASSERT_FAILED_MSG(obj, msg) \ + _PyObject_AssertFailed((obj), NULL, (msg), __FILE__, __LINE__, __func__) + +/* Declare and define _PyObject_AssertFailed() even when NDEBUG is defined, + to avoid causing compiler/linker errors when building extensions without + NDEBUG against a Python built with NDEBUG defined. + + msg, expr and function can be NULL. */ +PyAPI_FUNC(void) _Py_NO_RETURN _PyObject_AssertFailed( + PyObject *obj, + const char *expr, + const char *msg, + const char *file, + int line, + const char *function); + +/* Check if an object is consistent. For example, ensure that the reference + counter is greater than or equal to 1, and ensure that ob_type is not NULL. + + Call _PyObject_AssertFailed() if the object is inconsistent. + + If check_content is zero, only check header fields: reduce the overhead. + + The function always return 1. The return value is just here to be able to + write: + + assert(_PyObject_CheckConsistency(obj, 1)); */ +PyAPI_FUNC(int) _PyObject_CheckConsistency( + PyObject *op, + int check_content); + + +/* Trashcan mechanism, thanks to Christian Tismer. + +When deallocating a container object, it's possible to trigger an unbounded +chain of deallocations, as each Py_DECREF in turn drops the refcount on "the +next" object in the chain to 0. This can easily lead to stack overflows, +especially in threads (which typically have less stack space to work with). + +A container object can avoid this by bracketing the body of its tp_dealloc +function with a pair of macros: + +static void +mytype_dealloc(mytype *p) +{ + ... declarations go here ... + + PyObject_GC_UnTrack(p); // must untrack first + Py_TRASHCAN_BEGIN(p, mytype_dealloc) + ... The body of the deallocator goes here, including all calls ... + ... to Py_DECREF on contained objects. ... + Py_TRASHCAN_END // there should be no code after this +} + +CAUTION: Never return from the middle of the body! If the body needs to +"get out early", put a label immediately before the Py_TRASHCAN_END +call, and goto it. Else the call-depth counter (see below) will stay +above 0 forever, and the trashcan will never get emptied. + +How it works: The BEGIN macro increments a call-depth counter. So long +as this counter is small, the body of the deallocator is run directly without +further ado. But if the counter gets large, it instead adds p to a list of +objects to be deallocated later, skips the body of the deallocator, and +resumes execution after the END macro. The tp_dealloc routine then returns +without deallocating anything (and so unbounded call-stack depth is avoided). + +When the call stack finishes unwinding again, code generated by the END macro +notices this, and calls another routine to deallocate all the objects that +may have been added to the list of deferred deallocations. In effect, a +chain of N deallocations is broken into (N-1)/(PyTrash_UNWIND_LEVEL-1) pieces, +with the call stack never exceeding a depth of PyTrash_UNWIND_LEVEL. + +Since the tp_dealloc of a subclass typically calls the tp_dealloc of the base +class, we need to ensure that the trashcan is only triggered on the tp_dealloc +of the actual class being deallocated. Otherwise we might end up with a +partially-deallocated object. To check this, the tp_dealloc function must be +passed as second argument to Py_TRASHCAN_BEGIN(). +*/ + +/* This is the old private API, invoked by the macros before 3.2.4. + Kept for binary compatibility of extensions using the stable ABI. */ +PyAPI_FUNC(void) _PyTrash_deposit_object(PyObject*); +PyAPI_FUNC(void) _PyTrash_destroy_chain(void); + +/* This is the old private API, invoked by the macros before 3.9. + Kept for binary compatibility of extensions using the stable ABI. */ +PyAPI_FUNC(void) _PyTrash_thread_deposit_object(PyObject*); +PyAPI_FUNC(void) _PyTrash_thread_destroy_chain(void); + +/* Forward declarations for PyThreadState */ +struct _ts; + +/* Python 3.9 private API, invoked by the macros below. */ +PyAPI_FUNC(int) _PyTrash_begin(struct _ts *tstate, PyObject *op); +PyAPI_FUNC(void) _PyTrash_end(struct _ts *tstate); + +#define PyTrash_UNWIND_LEVEL 50 + +#define Py_TRASHCAN_BEGIN_CONDITION(op, cond) \ + do { \ + PyThreadState *_tstate = NULL; \ + /* If "cond" is false, then _tstate remains NULL and the deallocator \ + * is run normally without involving the trashcan */ \ + if (cond) { \ + _tstate = PyThreadState_GET(); \ + if (_PyTrash_begin(_tstate, _PyObject_CAST(op))) { \ + break; \ + } \ + } + /* The body of the deallocator is here. */ +#define Py_TRASHCAN_END \ + if (_tstate) { \ + _PyTrash_end(_tstate); \ + } \ + } while (0); + +#define Py_TRASHCAN_BEGIN(op, dealloc) \ + Py_TRASHCAN_BEGIN_CONDITION(op, \ + Py_TYPE(op)->tp_dealloc == (destructor)(dealloc)) + +/* For backwards compatibility, these macros enable the trashcan + * unconditionally */ +#define Py_TRASHCAN_SAFE_BEGIN(op) Py_TRASHCAN_BEGIN_CONDITION(op, 1) +#define Py_TRASHCAN_SAFE_END(op) Py_TRASHCAN_END + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/objimpl.h b/scripts/build-windows/py39-libs/include/cpython/objimpl.h new file mode 100644 index 000000000..b835936db --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/objimpl.h @@ -0,0 +1,145 @@ +#ifndef Py_CPYTHON_OBJIMPL_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define _PyObject_SIZE(typeobj) ( (typeobj)->tp_basicsize ) + +/* _PyObject_VAR_SIZE returns the number of bytes (as size_t) allocated for a + vrbl-size object with nitems items, exclusive of gc overhead (if any). The + value is rounded up to the closest multiple of sizeof(void *), in order to + ensure that pointer fields at the end of the object are correctly aligned + for the platform (this is of special importance for subclasses of, e.g., + str or int, so that pointers can be stored after the embedded data). + + Note that there's no memory wastage in doing this, as malloc has to + return (at worst) pointer-aligned memory anyway. +*/ +#if ((SIZEOF_VOID_P - 1) & SIZEOF_VOID_P) != 0 +# error "_PyObject_VAR_SIZE requires SIZEOF_VOID_P be a power of 2" +#endif + +#define _PyObject_VAR_SIZE(typeobj, nitems) \ + _Py_SIZE_ROUND_UP((typeobj)->tp_basicsize + \ + (nitems)*(typeobj)->tp_itemsize, \ + SIZEOF_VOID_P) + + +/* This example code implements an object constructor with a custom + allocator, where PyObject_New is inlined, and shows the important + distinction between two steps (at least): + 1) the actual allocation of the object storage; + 2) the initialization of the Python specific fields + in this storage with PyObject_{Init, InitVar}. + + PyObject * + YourObject_New(...) + { + PyObject *op; + + op = (PyObject *) Your_Allocator(_PyObject_SIZE(YourTypeStruct)); + if (op == NULL) + return PyErr_NoMemory(); + + PyObject_Init(op, &YourTypeStruct); + + op->ob_field = value; + ... + return op; + } + + Note that in C++, the use of the new operator usually implies that + the 1st step is performed automatically for you, so in a C++ class + constructor you would start directly with PyObject_Init/InitVar. */ + + +/* Inline functions trading binary compatibility for speed: + PyObject_INIT() is the fast version of PyObject_Init(), and + PyObject_INIT_VAR() is the fast version of PyObject_InitVar(). + + These inline functions must not be called with op=NULL. */ +static inline PyObject* +_PyObject_INIT(PyObject *op, PyTypeObject *typeobj) +{ + assert(op != NULL); + Py_SET_TYPE(op, typeobj); + if (PyType_GetFlags(typeobj) & Py_TPFLAGS_HEAPTYPE) { + Py_INCREF(typeobj); + } + _Py_NewReference(op); + return op; +} + +#define PyObject_INIT(op, typeobj) \ + _PyObject_INIT(_PyObject_CAST(op), (typeobj)) + +static inline PyVarObject* +_PyObject_INIT_VAR(PyVarObject *op, PyTypeObject *typeobj, Py_ssize_t size) +{ + assert(op != NULL); + Py_SET_SIZE(op, size); + PyObject_INIT((PyObject *)op, typeobj); + return op; +} + +#define PyObject_INIT_VAR(op, typeobj, size) \ + _PyObject_INIT_VAR(_PyVarObject_CAST(op), (typeobj), (size)) + + +/* This function returns the number of allocated memory blocks, regardless of size */ +PyAPI_FUNC(Py_ssize_t) _Py_GetAllocatedBlocks(void); + +/* Macros */ +#ifdef WITH_PYMALLOC +PyAPI_FUNC(int) _PyObject_DebugMallocStats(FILE *out); +#endif + + +typedef struct { + /* user context passed as the first argument to the 2 functions */ + void *ctx; + + /* allocate an arena of size bytes */ + void* (*alloc) (void *ctx, size_t size); + + /* free an arena */ + void (*free) (void *ctx, void *ptr, size_t size); +} PyObjectArenaAllocator; + +/* Get the arena allocator. */ +PyAPI_FUNC(void) PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator); + +/* Set the arena allocator. */ +PyAPI_FUNC(void) PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator); + + +PyAPI_FUNC(Py_ssize_t) _PyGC_CollectNoFail(void); +PyAPI_FUNC(Py_ssize_t) _PyGC_CollectIfEnabled(void); + + +/* Test if an object implements the garbage collector protocol */ +PyAPI_FUNC(int) PyObject_IS_GC(PyObject *obj); + + +/* Code built with Py_BUILD_CORE must include pycore_gc.h instead which + defines a different _PyGC_FINALIZED() macro. */ +#ifndef Py_BUILD_CORE + // Kept for backward compatibility with Python 3.8 +# define _PyGC_FINALIZED(o) PyObject_GC_IsFinalized(o) +#endif + +PyAPI_FUNC(PyObject *) _PyObject_GC_Malloc(size_t size); +PyAPI_FUNC(PyObject *) _PyObject_GC_Calloc(size_t size); + + +/* Test if a type supports weak references */ +#define PyType_SUPPORTS_WEAKREFS(t) ((t)->tp_weaklistoffset > 0) + +PyAPI_FUNC(PyObject **) PyObject_GET_WEAKREFS_LISTPTR(PyObject *op); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/pyerrors.h b/scripts/build-windows/py39-libs/include/cpython/pyerrors.h new file mode 100644 index 000000000..9c87b5397 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/pyerrors.h @@ -0,0 +1,200 @@ +#ifndef Py_CPYTHON_ERRORS_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Error objects */ + +/* PyException_HEAD defines the initial segment of every exception class. */ +#define PyException_HEAD PyObject_HEAD PyObject *dict;\ + PyObject *args; PyObject *traceback;\ + PyObject *context; PyObject *cause;\ + char suppress_context; + +typedef struct { + PyException_HEAD +} PyBaseExceptionObject; + +typedef struct { + PyException_HEAD + PyObject *msg; + PyObject *filename; + PyObject *lineno; + PyObject *offset; + PyObject *text; + PyObject *print_file_and_line; +} PySyntaxErrorObject; + +typedef struct { + PyException_HEAD + PyObject *msg; + PyObject *name; + PyObject *path; +} PyImportErrorObject; + +typedef struct { + PyException_HEAD + PyObject *encoding; + PyObject *object; + Py_ssize_t start; + Py_ssize_t end; + PyObject *reason; +} PyUnicodeErrorObject; + +typedef struct { + PyException_HEAD + PyObject *code; +} PySystemExitObject; + +typedef struct { + PyException_HEAD + PyObject *myerrno; + PyObject *strerror; + PyObject *filename; + PyObject *filename2; +#ifdef MS_WINDOWS + PyObject *winerror; +#endif + Py_ssize_t written; /* only for BlockingIOError, -1 otherwise */ +} PyOSErrorObject; + +typedef struct { + PyException_HEAD + PyObject *value; +} PyStopIterationObject; + +/* Compatibility typedefs */ +typedef PyOSErrorObject PyEnvironmentErrorObject; +#ifdef MS_WINDOWS +typedef PyOSErrorObject PyWindowsErrorObject; +#endif + +/* Error handling definitions */ + +PyAPI_FUNC(void) _PyErr_SetKeyError(PyObject *); +PyAPI_FUNC(_PyErr_StackItem*) _PyErr_GetTopmostException(PyThreadState *tstate); +PyAPI_FUNC(void) _PyErr_GetExcInfo(PyThreadState *, PyObject **, PyObject **, PyObject **); + +/* Context manipulation (PEP 3134) */ + +PyAPI_FUNC(void) _PyErr_ChainExceptions(PyObject *, PyObject *, PyObject *); + +/* */ + +#define PyExceptionClass_Name(x) (((PyTypeObject*)(x))->tp_name) + +/* Convenience functions */ + +#ifdef MS_WINDOWS +Py_DEPRECATED(3.3) +PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithUnicodeFilename( + PyObject *, const Py_UNICODE *); +#endif /* MS_WINDOWS */ + +/* Like PyErr_Format(), but saves current exception as __context__ and + __cause__. + */ +PyAPI_FUNC(PyObject *) _PyErr_FormatFromCause( + PyObject *exception, + const char *format, /* ASCII-encoded string */ + ... + ); + +#ifdef MS_WINDOWS +/* XXX redeclare to use WSTRING */ +Py_DEPRECATED(3.3) +PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithUnicodeFilename( + int, const Py_UNICODE *); +Py_DEPRECATED(3.3) +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithUnicodeFilename( + PyObject *,int, const Py_UNICODE *); +#endif + +/* In exceptions.c */ + +/* Helper that attempts to replace the current exception with one of the + * same type but with a prefix added to the exception text. The resulting + * exception description looks like: + * + * prefix (exc_type: original_exc_str) + * + * Only some exceptions can be safely replaced. If the function determines + * it isn't safe to perform the replacement, it will leave the original + * unmodified exception in place. + * + * Returns a borrowed reference to the new exception (if any), NULL if the + * existing exception was left in place. + */ +PyAPI_FUNC(PyObject *) _PyErr_TrySetFromCause( + const char *prefix_format, /* ASCII-encoded string */ + ... + ); + +/* In signalmodule.c */ + +int PySignal_SetWakeupFd(int fd); +PyAPI_FUNC(int) _PyErr_CheckSignals(void); + +/* Support for adding program text to SyntaxErrors */ + +PyAPI_FUNC(void) PyErr_SyntaxLocationObject( + PyObject *filename, + int lineno, + int col_offset); + +PyAPI_FUNC(PyObject *) PyErr_ProgramTextObject( + PyObject *filename, + int lineno); + +/* Create a UnicodeEncodeError object. + * + * TODO: This API will be removed in Python 3.11. + */ +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject *) PyUnicodeEncodeError_Create( + const char *encoding, /* UTF-8 encoded string */ + const Py_UNICODE *object, + Py_ssize_t length, + Py_ssize_t start, + Py_ssize_t end, + const char *reason /* UTF-8 encoded string */ + ); + +/* Create a UnicodeTranslateError object. + * + * TODO: This API will be removed in Python 3.11. + */ +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject *) PyUnicodeTranslateError_Create( + const Py_UNICODE *object, + Py_ssize_t length, + Py_ssize_t start, + Py_ssize_t end, + const char *reason /* UTF-8 encoded string */ + ); +PyAPI_FUNC(PyObject *) _PyUnicodeTranslateError_Create( + PyObject *object, + Py_ssize_t start, + Py_ssize_t end, + const char *reason /* UTF-8 encoded string */ + ); + +PyAPI_FUNC(void) _PyErr_WriteUnraisableMsg( + const char *err_msg, + PyObject *obj); + +PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalErrorFunc( + const char *func, + const char *message); + +PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalErrorFormat( + const char *func, + const char *format, + ...); + +#define Py_FatalError(message) _Py_FatalErrorFunc(__func__, message) + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/pylifecycle.h b/scripts/build-windows/py39-libs/include/cpython/pylifecycle.h new file mode 100644 index 000000000..eb523b82e --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/pylifecycle.h @@ -0,0 +1,72 @@ +#ifndef Py_CPYTHON_PYLIFECYCLE_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Only used by applications that embed the interpreter and need to + * override the standard encoding determination mechanism + */ +PyAPI_FUNC(int) Py_SetStandardStreamEncoding(const char *encoding, + const char *errors); + +/* PEP 432 Multi-phase initialization API (Private while provisional!) */ + +PyAPI_FUNC(PyStatus) Py_PreInitialize( + const PyPreConfig *src_config); +PyAPI_FUNC(PyStatus) Py_PreInitializeFromBytesArgs( + const PyPreConfig *src_config, + Py_ssize_t argc, + char **argv); +PyAPI_FUNC(PyStatus) Py_PreInitializeFromArgs( + const PyPreConfig *src_config, + Py_ssize_t argc, + wchar_t **argv); + +PyAPI_FUNC(int) _Py_IsCoreInitialized(void); + + +/* Initialization and finalization */ + +PyAPI_FUNC(PyStatus) Py_InitializeFromConfig( + const PyConfig *config); +PyAPI_FUNC(PyStatus) _Py_InitializeMain(void); + +PyAPI_FUNC(int) Py_RunMain(void); + + +PyAPI_FUNC(void) _Py_NO_RETURN Py_ExitStatusException(PyStatus err); + +/* Py_PyAtExit is for the atexit module, Py_AtExit is for low-level + * exit functions. + */ +PyAPI_FUNC(void) _Py_PyAtExit(void (*func)(PyObject *), PyObject *); + +/* Restore signals that the interpreter has called SIG_IGN on to SIG_DFL. */ +PyAPI_FUNC(void) _Py_RestoreSignals(void); + +PyAPI_FUNC(int) Py_FdIsInteractive(FILE *, const char *); + +PyAPI_FUNC(void) _Py_SetProgramFullPath(const wchar_t *); + +PyAPI_FUNC(const char *) _Py_gitidentifier(void); +PyAPI_FUNC(const char *) _Py_gitversion(void); + +PyAPI_FUNC(int) _Py_IsFinalizing(void); + +/* Random */ +PyAPI_FUNC(int) _PyOS_URandom(void *buffer, Py_ssize_t size); +PyAPI_FUNC(int) _PyOS_URandomNonblock(void *buffer, Py_ssize_t size); + +/* Legacy locale support */ +PyAPI_FUNC(int) _Py_CoerceLegacyLocale(int warn); +PyAPI_FUNC(int) _Py_LegacyLocaleDetected(int warn); +PyAPI_FUNC(char *) _Py_SetLocaleFromEnv(int category); + +PyAPI_FUNC(PyThreadState *) _Py_NewInterpreter(int isolated_subinterpreter); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/pymem.h b/scripts/build-windows/py39-libs/include/cpython/pymem.h new file mode 100644 index 000000000..79f063b12 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/pymem.h @@ -0,0 +1,108 @@ +#ifndef Py_CPYTHON_PYMEM_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(void *) PyMem_RawMalloc(size_t size); +PyAPI_FUNC(void *) PyMem_RawCalloc(size_t nelem, size_t elsize); +PyAPI_FUNC(void *) PyMem_RawRealloc(void *ptr, size_t new_size); +PyAPI_FUNC(void) PyMem_RawFree(void *ptr); + +/* Try to get the allocators name set by _PyMem_SetupAllocators(). */ +PyAPI_FUNC(const char*) _PyMem_GetCurrentAllocatorName(void); + +PyAPI_FUNC(void *) PyMem_Calloc(size_t nelem, size_t elsize); + +/* strdup() using PyMem_RawMalloc() */ +PyAPI_FUNC(char *) _PyMem_RawStrdup(const char *str); + +/* strdup() using PyMem_Malloc() */ +PyAPI_FUNC(char *) _PyMem_Strdup(const char *str); + +/* wcsdup() using PyMem_RawMalloc() */ +PyAPI_FUNC(wchar_t*) _PyMem_RawWcsdup(const wchar_t *str); + + +typedef enum { + /* PyMem_RawMalloc(), PyMem_RawRealloc() and PyMem_RawFree() */ + PYMEM_DOMAIN_RAW, + + /* PyMem_Malloc(), PyMem_Realloc() and PyMem_Free() */ + PYMEM_DOMAIN_MEM, + + /* PyObject_Malloc(), PyObject_Realloc() and PyObject_Free() */ + PYMEM_DOMAIN_OBJ +} PyMemAllocatorDomain; + +typedef enum { + PYMEM_ALLOCATOR_NOT_SET = 0, + PYMEM_ALLOCATOR_DEFAULT = 1, + PYMEM_ALLOCATOR_DEBUG = 2, + PYMEM_ALLOCATOR_MALLOC = 3, + PYMEM_ALLOCATOR_MALLOC_DEBUG = 4, +#ifdef WITH_PYMALLOC + PYMEM_ALLOCATOR_PYMALLOC = 5, + PYMEM_ALLOCATOR_PYMALLOC_DEBUG = 6, +#endif +} PyMemAllocatorName; + + +typedef struct { + /* user context passed as the first argument to the 4 functions */ + void *ctx; + + /* allocate a memory block */ + void* (*malloc) (void *ctx, size_t size); + + /* allocate a memory block initialized by zeros */ + void* (*calloc) (void *ctx, size_t nelem, size_t elsize); + + /* allocate or resize a memory block */ + void* (*realloc) (void *ctx, void *ptr, size_t new_size); + + /* release a memory block */ + void (*free) (void *ctx, void *ptr); +} PyMemAllocatorEx; + +/* Get the memory block allocator of the specified domain. */ +PyAPI_FUNC(void) PyMem_GetAllocator(PyMemAllocatorDomain domain, + PyMemAllocatorEx *allocator); + +/* Set the memory block allocator of the specified domain. + + The new allocator must return a distinct non-NULL pointer when requesting + zero bytes. + + For the PYMEM_DOMAIN_RAW domain, the allocator must be thread-safe: the GIL + is not held when the allocator is called. + + If the new allocator is not a hook (don't call the previous allocator), the + PyMem_SetupDebugHooks() function must be called to reinstall the debug hooks + on top on the new allocator. */ +PyAPI_FUNC(void) PyMem_SetAllocator(PyMemAllocatorDomain domain, + PyMemAllocatorEx *allocator); + +/* Setup hooks to detect bugs in the following Python memory allocator + functions: + + - PyMem_RawMalloc(), PyMem_RawRealloc(), PyMem_RawFree() + - PyMem_Malloc(), PyMem_Realloc(), PyMem_Free() + - PyObject_Malloc(), PyObject_Realloc() and PyObject_Free() + + Newly allocated memory is filled with the byte 0xCB, freed memory is filled + with the byte 0xDB. Additional checks: + + - detect API violations, ex: PyObject_Free() called on a buffer allocated + by PyMem_Malloc() + - detect write before the start of the buffer (buffer underflow) + - detect write after the end of the buffer (buffer overflow) + + The function does nothing if Python is not compiled is debug mode. */ +PyAPI_FUNC(void) PyMem_SetupDebugHooks(void); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/pystate.h b/scripts/build-windows/py39-libs/include/cpython/pystate.h new file mode 100644 index 000000000..f292da1d3 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/pystate.h @@ -0,0 +1,263 @@ +#ifndef Py_CPYTHON_PYSTATE_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cpython/initconfig.h" + +PyAPI_FUNC(int) _PyInterpreterState_RequiresIDRef(PyInterpreterState *); +PyAPI_FUNC(void) _PyInterpreterState_RequireIDRef(PyInterpreterState *, int); + +PyAPI_FUNC(PyObject *) _PyInterpreterState_GetMainModule(PyInterpreterState *); + +/* State unique per thread */ + +/* Py_tracefunc return -1 when raising an exception, or 0 for success. */ +typedef int (*Py_tracefunc)(PyObject *, PyFrameObject *, int, PyObject *); + +/* The following values are used for 'what' for tracefunc functions + * + * To add a new kind of trace event, also update "trace_init" in + * Python/sysmodule.c to define the Python level event name + */ +#define PyTrace_CALL 0 +#define PyTrace_EXCEPTION 1 +#define PyTrace_LINE 2 +#define PyTrace_RETURN 3 +#define PyTrace_C_CALL 4 +#define PyTrace_C_EXCEPTION 5 +#define PyTrace_C_RETURN 6 +#define PyTrace_OPCODE 7 + + +typedef struct _err_stackitem { + /* This struct represents an entry on the exception stack, which is a + * per-coroutine state. (Coroutine in the computer science sense, + * including the thread and generators). + * This ensures that the exception state is not impacted by "yields" + * from an except handler. + */ + PyObject *exc_type, *exc_value, *exc_traceback; + + struct _err_stackitem *previous_item; + +} _PyErr_StackItem; + + +// The PyThreadState typedef is in Include/pystate.h. +struct _ts { + /* See Python/ceval.c for comments explaining most fields */ + + struct _ts *prev; + struct _ts *next; + PyInterpreterState *interp; + + /* Borrowed reference to the current frame (it can be NULL) */ + PyFrameObject *frame; + int recursion_depth; + char overflowed; /* The stack has overflowed. Allow 50 more calls + to handle the runtime error. */ + char recursion_critical; /* The current calls must not cause + a stack overflow. */ + int stackcheck_counter; + + /* 'tracing' keeps track of the execution depth when tracing/profiling. + This is to prevent the actual trace/profile code from being recorded in + the trace/profile. */ + int tracing; + int use_tracing; + + Py_tracefunc c_profilefunc; + Py_tracefunc c_tracefunc; + PyObject *c_profileobj; + PyObject *c_traceobj; + + /* The exception currently being raised */ + PyObject *curexc_type; + PyObject *curexc_value; + PyObject *curexc_traceback; + + /* The exception currently being handled, if no coroutines/generators + * are present. Always last element on the stack referred to be exc_info. + */ + _PyErr_StackItem exc_state; + + /* Pointer to the top of the stack of the exceptions currently + * being handled */ + _PyErr_StackItem *exc_info; + + PyObject *dict; /* Stores per-thread state */ + + int gilstate_counter; + + PyObject *async_exc; /* Asynchronous exception to raise */ + unsigned long thread_id; /* Thread id where this tstate was created */ + + int trash_delete_nesting; + PyObject *trash_delete_later; + + /* Called when a thread state is deleted normally, but not when it + * is destroyed after fork(). + * Pain: to prevent rare but fatal shutdown errors (issue 18808), + * Thread.join() must wait for the join'ed thread's tstate to be unlinked + * from the tstate chain. That happens at the end of a thread's life, + * in pystate.c. + * The obvious way doesn't quite work: create a lock which the tstate + * unlinking code releases, and have Thread.join() wait to acquire that + * lock. The problem is that we _are_ at the end of the thread's life: + * if the thread holds the last reference to the lock, decref'ing the + * lock will delete the lock, and that may trigger arbitrary Python code + * if there's a weakref, with a callback, to the lock. But by this time + * _PyRuntime.gilstate.tstate_current is already NULL, so only the simplest + * of C code can be allowed to run (in particular it must not be possible to + * release the GIL). + * So instead of holding the lock directly, the tstate holds a weakref to + * the lock: that's the value of on_delete_data below. Decref'ing a + * weakref is harmless. + * on_delete points to _threadmodule.c's static release_sentinel() function. + * After the tstate is unlinked, release_sentinel is called with the + * weakref-to-lock (on_delete_data) argument, and release_sentinel releases + * the indirectly held lock. + */ + void (*on_delete)(void *); + void *on_delete_data; + + int coroutine_origin_tracking_depth; + + PyObject *async_gen_firstiter; + PyObject *async_gen_finalizer; + + PyObject *context; + uint64_t context_ver; + + /* Unique thread state id. */ + uint64_t id; + + /* XXX signal handlers should also be here */ + +}; + +// Alias for backward compatibility with Python 3.8 +#define _PyInterpreterState_Get PyInterpreterState_Get + +PyAPI_FUNC(PyThreadState *) _PyThreadState_Prealloc(PyInterpreterState *); + +/* Similar to PyThreadState_Get(), but don't issue a fatal error + * if it is NULL. */ +PyAPI_FUNC(PyThreadState *) _PyThreadState_UncheckedGet(void); + +PyAPI_FUNC(PyObject *) _PyThreadState_GetDict(PyThreadState *tstate); + +/* PyGILState */ + +/* Helper/diagnostic function - return 1 if the current thread + currently holds the GIL, 0 otherwise. + + The function returns 1 if _PyGILState_check_enabled is non-zero. */ +PyAPI_FUNC(int) PyGILState_Check(void); + +/* Get the single PyInterpreterState used by this process' GILState + implementation. + + This function doesn't check for error. Return NULL before _PyGILState_Init() + is called and after _PyGILState_Fini() is called. + + See also _PyInterpreterState_Get() and _PyInterpreterState_GET(). */ +PyAPI_FUNC(PyInterpreterState *) _PyGILState_GetInterpreterStateUnsafe(void); + +/* The implementation of sys._current_frames() Returns a dict mapping + thread id to that thread's current frame. +*/ +PyAPI_FUNC(PyObject *) _PyThread_CurrentFrames(void); + +/* Routines for advanced debuggers, requested by David Beazley. + Don't use unless you know what you are doing! */ +PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_Main(void); +PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_Head(void); +PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_Next(PyInterpreterState *); +PyAPI_FUNC(PyThreadState *) PyInterpreterState_ThreadHead(PyInterpreterState *); +PyAPI_FUNC(PyThreadState *) PyThreadState_Next(PyThreadState *); +PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void); + +/* Frame evaluation API */ + +typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, PyFrameObject *, int); + +PyAPI_FUNC(_PyFrameEvalFunction) _PyInterpreterState_GetEvalFrameFunc( + PyInterpreterState *interp); +PyAPI_FUNC(void) _PyInterpreterState_SetEvalFrameFunc( + PyInterpreterState *interp, + _PyFrameEvalFunction eval_frame); + +PyAPI_FUNC(const PyConfig*) _PyInterpreterState_GetConfig(PyInterpreterState *interp); + +// Get the configuration of the currrent interpreter. +// The caller must hold the GIL. +PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void); + + +/* cross-interpreter data */ + +struct _xid; + +// _PyCrossInterpreterData is similar to Py_buffer as an effectively +// opaque struct that holds data outside the object machinery. This +// is necessary to pass safely between interpreters in the same process. +typedef struct _xid { + // data is the cross-interpreter-safe derivation of a Python object + // (see _PyObject_GetCrossInterpreterData). It will be NULL if the + // new_object func (below) encodes the data. + void *data; + // obj is the Python object from which the data was derived. This + // is non-NULL only if the data remains bound to the object in some + // way, such that the object must be "released" (via a decref) when + // the data is released. In that case the code that sets the field, + // likely a registered "crossinterpdatafunc", is responsible for + // ensuring it owns the reference (i.e. incref). + PyObject *obj; + // interp is the ID of the owning interpreter of the original + // object. It corresponds to the active interpreter when + // _PyObject_GetCrossInterpreterData() was called. This should only + // be set by the cross-interpreter machinery. + // + // We use the ID rather than the PyInterpreterState to avoid issues + // with deleted interpreters. Note that IDs are never re-used, so + // each one will always correspond to a specific interpreter + // (whether still alive or not). + int64_t interp; + // new_object is a function that returns a new object in the current + // interpreter given the data. The resulting object (a new + // reference) will be equivalent to the original object. This field + // is required. + PyObject *(*new_object)(struct _xid *); + // free is called when the data is released. If it is NULL then + // nothing will be done to free the data. For some types this is + // okay (e.g. bytes) and for those types this field should be set + // to NULL. However, for most the data was allocated just for + // cross-interpreter use, so it must be freed when + // _PyCrossInterpreterData_Release is called or the memory will + // leak. In that case, at the very least this field should be set + // to PyMem_RawFree (the default if not explicitly set to NULL). + // The call will happen with the original interpreter activated. + void (*free)(void *); +} _PyCrossInterpreterData; + +PyAPI_FUNC(int) _PyObject_GetCrossInterpreterData(PyObject *, _PyCrossInterpreterData *); +PyAPI_FUNC(PyObject *) _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *); +PyAPI_FUNC(void) _PyCrossInterpreterData_Release(_PyCrossInterpreterData *); + +PyAPI_FUNC(int) _PyObject_CheckCrossInterpreterData(PyObject *); + +/* cross-interpreter data registry */ + +typedef int (*crossinterpdatafunc)(PyObject *, struct _xid *); + +PyAPI_FUNC(int) _PyCrossInterpreterData_RegisterClass(PyTypeObject *, crossinterpdatafunc); +PyAPI_FUNC(crossinterpdatafunc) _PyCrossInterpreterData_Lookup(PyObject *); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/sysmodule.h b/scripts/build-windows/py39-libs/include/cpython/sysmodule.h new file mode 100644 index 000000000..1802b5b30 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/sysmodule.h @@ -0,0 +1,24 @@ +#ifndef Py_CPYTHON_SYSMODULE_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(PyObject *) _PySys_GetObjectId(_Py_Identifier *key); +PyAPI_FUNC(int) _PySys_SetObjectId(_Py_Identifier *key, PyObject *); + +PyAPI_FUNC(size_t) _PySys_GetSizeOf(PyObject *); + +typedef int(*Py_AuditHookFunction)(const char *, PyObject *, void *); + +PyAPI_FUNC(int) PySys_Audit( + const char *event, + const char *argFormat, + ...); +PyAPI_FUNC(int) PySys_AddAuditHook(Py_AuditHookFunction, void*); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/traceback.h b/scripts/build-windows/py39-libs/include/cpython/traceback.h new file mode 100644 index 000000000..837470c3b --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/traceback.h @@ -0,0 +1,22 @@ +#ifndef Py_CPYTHON_TRACEBACK_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _traceback { + PyObject_HEAD + struct _traceback *tb_next; + PyFrameObject *tb_frame; + int tb_lasti; + int tb_lineno; +} PyTracebackObject; + +PyAPI_FUNC(int) _Py_DisplaySourceLine(PyObject *, PyObject *, int, int); +PyAPI_FUNC(void) _PyTraceback_Add(const char *, const char *, int); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/tupleobject.h b/scripts/build-windows/py39-libs/include/cpython/tupleobject.h new file mode 100644 index 000000000..1565f2a5c --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/tupleobject.h @@ -0,0 +1,36 @@ +#ifndef Py_CPYTHON_TUPLEOBJECT_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject_VAR_HEAD + /* ob_item contains space for 'ob_size' elements. + Items must normally not be NULL, except during construction when + the tuple is not yet visible outside the function that builds it. */ + PyObject *ob_item[1]; +} PyTupleObject; + +PyAPI_FUNC(int) _PyTuple_Resize(PyObject **, Py_ssize_t); +PyAPI_FUNC(void) _PyTuple_MaybeUntrack(PyObject *); + +/* Macros trading safety for speed */ + +/* Cast argument to PyTupleObject* type. */ +#define _PyTuple_CAST(op) (assert(PyTuple_Check(op)), (PyTupleObject *)(op)) + +#define PyTuple_GET_SIZE(op) Py_SIZE(_PyTuple_CAST(op)) + +#define PyTuple_GET_ITEM(op, i) (_PyTuple_CAST(op)->ob_item[i]) + +/* Macro, *only* to be used to fill in brand new tuples */ +#define PyTuple_SET_ITEM(op, i, v) (_PyTuple_CAST(op)->ob_item[i] = v) + +PyAPI_FUNC(void) _PyTuple_DebugMallocStats(FILE *out); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/cpython/unicodeobject.h b/scripts/build-windows/py39-libs/include/cpython/unicodeobject.h new file mode 100644 index 000000000..17db79cff --- /dev/null +++ b/scripts/build-windows/py39-libs/include/cpython/unicodeobject.h @@ -0,0 +1,1221 @@ +#ifndef Py_CPYTHON_UNICODEOBJECT_H +# error "this header file must not be included directly" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Py_UNICODE was the native Unicode storage format (code unit) used by + Python and represents a single Unicode element in the Unicode type. + With PEP 393, Py_UNICODE is deprecated and replaced with a + typedef to wchar_t. */ +#define PY_UNICODE_TYPE wchar_t +/* Py_DEPRECATED(3.3) */ typedef wchar_t Py_UNICODE; + +/* --- Internal Unicode Operations ---------------------------------------- */ + +/* Since splitting on whitespace is an important use case, and + whitespace in most situations is solely ASCII whitespace, we + optimize for the common case by using a quick look-up table + _Py_ascii_whitespace (see below) with an inlined check. + + */ +#define Py_UNICODE_ISSPACE(ch) \ + ((Py_UCS4)(ch) < 128U ? _Py_ascii_whitespace[(ch)] : _PyUnicode_IsWhitespace(ch)) + +#define Py_UNICODE_ISLOWER(ch) _PyUnicode_IsLowercase(ch) +#define Py_UNICODE_ISUPPER(ch) _PyUnicode_IsUppercase(ch) +#define Py_UNICODE_ISTITLE(ch) _PyUnicode_IsTitlecase(ch) +#define Py_UNICODE_ISLINEBREAK(ch) _PyUnicode_IsLinebreak(ch) + +#define Py_UNICODE_TOLOWER(ch) _PyUnicode_ToLowercase(ch) +#define Py_UNICODE_TOUPPER(ch) _PyUnicode_ToUppercase(ch) +#define Py_UNICODE_TOTITLE(ch) _PyUnicode_ToTitlecase(ch) + +#define Py_UNICODE_ISDECIMAL(ch) _PyUnicode_IsDecimalDigit(ch) +#define Py_UNICODE_ISDIGIT(ch) _PyUnicode_IsDigit(ch) +#define Py_UNICODE_ISNUMERIC(ch) _PyUnicode_IsNumeric(ch) +#define Py_UNICODE_ISPRINTABLE(ch) _PyUnicode_IsPrintable(ch) + +#define Py_UNICODE_TODECIMAL(ch) _PyUnicode_ToDecimalDigit(ch) +#define Py_UNICODE_TODIGIT(ch) _PyUnicode_ToDigit(ch) +#define Py_UNICODE_TONUMERIC(ch) _PyUnicode_ToNumeric(ch) + +#define Py_UNICODE_ISALPHA(ch) _PyUnicode_IsAlpha(ch) + +#define Py_UNICODE_ISALNUM(ch) \ + (Py_UNICODE_ISALPHA(ch) || \ + Py_UNICODE_ISDECIMAL(ch) || \ + Py_UNICODE_ISDIGIT(ch) || \ + Py_UNICODE_ISNUMERIC(ch)) + +Py_DEPRECATED(3.3) static inline void +Py_UNICODE_COPY(Py_UNICODE *target, const Py_UNICODE *source, Py_ssize_t length) { + memcpy(target, source, (size_t)(length) * sizeof(Py_UNICODE)); +} + +Py_DEPRECATED(3.3) static inline void +Py_UNICODE_FILL(Py_UNICODE *target, Py_UNICODE value, Py_ssize_t length) { + Py_ssize_t i; + for (i = 0; i < length; i++) { + target[i] = value; + } +} + +/* macros to work with surrogates */ +#define Py_UNICODE_IS_SURROGATE(ch) (0xD800 <= (ch) && (ch) <= 0xDFFF) +#define Py_UNICODE_IS_HIGH_SURROGATE(ch) (0xD800 <= (ch) && (ch) <= 0xDBFF) +#define Py_UNICODE_IS_LOW_SURROGATE(ch) (0xDC00 <= (ch) && (ch) <= 0xDFFF) +/* Join two surrogate characters and return a single Py_UCS4 value. */ +#define Py_UNICODE_JOIN_SURROGATES(high, low) \ + (((((Py_UCS4)(high) & 0x03FF) << 10) | \ + ((Py_UCS4)(low) & 0x03FF)) + 0x10000) +/* high surrogate = top 10 bits added to D800 */ +#define Py_UNICODE_HIGH_SURROGATE(ch) (0xD800 - (0x10000 >> 10) + ((ch) >> 10)) +/* low surrogate = bottom 10 bits added to DC00 */ +#define Py_UNICODE_LOW_SURROGATE(ch) (0xDC00 + ((ch) & 0x3FF)) + +/* --- Unicode Type ------------------------------------------------------- */ + +/* ASCII-only strings created through PyUnicode_New use the PyASCIIObject + structure. state.ascii and state.compact are set, and the data + immediately follow the structure. utf8_length and wstr_length can be found + in the length field; the utf8 pointer is equal to the data pointer. */ +typedef struct { + /* There are 4 forms of Unicode strings: + + - compact ascii: + + * structure = PyASCIIObject + * test: PyUnicode_IS_COMPACT_ASCII(op) + * kind = PyUnicode_1BYTE_KIND + * compact = 1 + * ascii = 1 + * ready = 1 + * (length is the length of the utf8 and wstr strings) + * (data starts just after the structure) + * (since ASCII is decoded from UTF-8, the utf8 string are the data) + + - compact: + + * structure = PyCompactUnicodeObject + * test: PyUnicode_IS_COMPACT(op) && !PyUnicode_IS_ASCII(op) + * kind = PyUnicode_1BYTE_KIND, PyUnicode_2BYTE_KIND or + PyUnicode_4BYTE_KIND + * compact = 1 + * ready = 1 + * ascii = 0 + * utf8 is not shared with data + * utf8_length = 0 if utf8 is NULL + * wstr is shared with data and wstr_length=length + if kind=PyUnicode_2BYTE_KIND and sizeof(wchar_t)=2 + or if kind=PyUnicode_4BYTE_KIND and sizeof(wchar_t)=4 + * wstr_length = 0 if wstr is NULL + * (data starts just after the structure) + + - legacy string, not ready: + + * structure = PyUnicodeObject + * test: kind == PyUnicode_WCHAR_KIND + * length = 0 (use wstr_length) + * hash = -1 + * kind = PyUnicode_WCHAR_KIND + * compact = 0 + * ascii = 0 + * ready = 0 + * interned = SSTATE_NOT_INTERNED + * wstr is not NULL + * data.any is NULL + * utf8 is NULL + * utf8_length = 0 + + - legacy string, ready: + + * structure = PyUnicodeObject structure + * test: !PyUnicode_IS_COMPACT(op) && kind != PyUnicode_WCHAR_KIND + * kind = PyUnicode_1BYTE_KIND, PyUnicode_2BYTE_KIND or + PyUnicode_4BYTE_KIND + * compact = 0 + * ready = 1 + * data.any is not NULL + * utf8 is shared and utf8_length = length with data.any if ascii = 1 + * utf8_length = 0 if utf8 is NULL + * wstr is shared with data.any and wstr_length = length + if kind=PyUnicode_2BYTE_KIND and sizeof(wchar_t)=2 + or if kind=PyUnicode_4BYTE_KIND and sizeof(wchar_4)=4 + * wstr_length = 0 if wstr is NULL + + Compact strings use only one memory block (structure + characters), + whereas legacy strings use one block for the structure and one block + for characters. + + Legacy strings are created by PyUnicode_FromUnicode() and + PyUnicode_FromStringAndSize(NULL, size) functions. They become ready + when PyUnicode_READY() is called. + + See also _PyUnicode_CheckConsistency(). + */ + PyObject_HEAD + Py_ssize_t length; /* Number of code points in the string */ + Py_hash_t hash; /* Hash value; -1 if not set */ + struct { + /* + SSTATE_NOT_INTERNED (0) + SSTATE_INTERNED_MORTAL (1) + SSTATE_INTERNED_IMMORTAL (2) + + If interned != SSTATE_NOT_INTERNED, the two references from the + dictionary to this object are *not* counted in ob_refcnt. + */ + unsigned int interned:2; + /* Character size: + + - PyUnicode_WCHAR_KIND (0): + + * character type = wchar_t (16 or 32 bits, depending on the + platform) + + - PyUnicode_1BYTE_KIND (1): + + * character type = Py_UCS1 (8 bits, unsigned) + * all characters are in the range U+0000-U+00FF (latin1) + * if ascii is set, all characters are in the range U+0000-U+007F + (ASCII), otherwise at least one character is in the range + U+0080-U+00FF + + - PyUnicode_2BYTE_KIND (2): + + * character type = Py_UCS2 (16 bits, unsigned) + * all characters are in the range U+0000-U+FFFF (BMP) + * at least one character is in the range U+0100-U+FFFF + + - PyUnicode_4BYTE_KIND (4): + + * character type = Py_UCS4 (32 bits, unsigned) + * all characters are in the range U+0000-U+10FFFF + * at least one character is in the range U+10000-U+10FFFF + */ + unsigned int kind:3; + /* Compact is with respect to the allocation scheme. Compact unicode + objects only require one memory block while non-compact objects use + one block for the PyUnicodeObject struct and another for its data + buffer. */ + unsigned int compact:1; + /* The string only contains characters in the range U+0000-U+007F (ASCII) + and the kind is PyUnicode_1BYTE_KIND. If ascii is set and compact is + set, use the PyASCIIObject structure. */ + unsigned int ascii:1; + /* The ready flag indicates whether the object layout is initialized + completely. This means that this is either a compact object, or + the data pointer is filled out. The bit is redundant, and helps + to minimize the test in PyUnicode_IS_READY(). */ + unsigned int ready:1; + /* Padding to ensure that PyUnicode_DATA() is always aligned to + 4 bytes (see issue #19537 on m68k). */ + unsigned int :24; + } state; + wchar_t *wstr; /* wchar_t representation (null-terminated) */ +} PyASCIIObject; + +/* Non-ASCII strings allocated through PyUnicode_New use the + PyCompactUnicodeObject structure. state.compact is set, and the data + immediately follow the structure. */ +typedef struct { + PyASCIIObject _base; + Py_ssize_t utf8_length; /* Number of bytes in utf8, excluding the + * terminating \0. */ + char *utf8; /* UTF-8 representation (null-terminated) */ + Py_ssize_t wstr_length; /* Number of code points in wstr, possible + * surrogates count as two code points. */ +} PyCompactUnicodeObject; + +/* Strings allocated through PyUnicode_FromUnicode(NULL, len) use the + PyUnicodeObject structure. The actual string data is initially in the wstr + block, and copied into the data block using _PyUnicode_Ready. */ +typedef struct { + PyCompactUnicodeObject _base; + union { + void *any; + Py_UCS1 *latin1; + Py_UCS2 *ucs2; + Py_UCS4 *ucs4; + } data; /* Canonical, smallest-form Unicode buffer */ +} PyUnicodeObject; + +PyAPI_FUNC(int) _PyUnicode_CheckConsistency( + PyObject *op, + int check_content); + +/* Fast access macros */ + +/* Returns the deprecated Py_UNICODE representation's size in code units + (this includes surrogate pairs as 2 units). + If the Py_UNICODE representation is not available, it will be computed + on request. Use PyUnicode_GET_LENGTH() for the length in code points. */ + +/* Py_DEPRECATED(3.3) */ +#define PyUnicode_GET_SIZE(op) \ + (assert(PyUnicode_Check(op)), \ + (((PyASCIIObject *)(op))->wstr) ? \ + PyUnicode_WSTR_LENGTH(op) : \ + ((void)PyUnicode_AsUnicode(_PyObject_CAST(op)),\ + assert(((PyASCIIObject *)(op))->wstr), \ + PyUnicode_WSTR_LENGTH(op))) + +/* Py_DEPRECATED(3.3) */ +#define PyUnicode_GET_DATA_SIZE(op) \ + (PyUnicode_GET_SIZE(op) * Py_UNICODE_SIZE) + +/* Alias for PyUnicode_AsUnicode(). This will create a wchar_t/Py_UNICODE + representation on demand. Using this macro is very inefficient now, + try to port your code to use the new PyUnicode_*BYTE_DATA() macros or + use PyUnicode_WRITE() and PyUnicode_READ(). */ + +/* Py_DEPRECATED(3.3) */ +#define PyUnicode_AS_UNICODE(op) \ + (assert(PyUnicode_Check(op)), \ + (((PyASCIIObject *)(op))->wstr) ? (((PyASCIIObject *)(op))->wstr) : \ + PyUnicode_AsUnicode(_PyObject_CAST(op))) + +/* Py_DEPRECATED(3.3) */ +#define PyUnicode_AS_DATA(op) \ + ((const char *)(PyUnicode_AS_UNICODE(op))) + + +/* --- Flexible String Representation Helper Macros (PEP 393) -------------- */ + +/* Values for PyASCIIObject.state: */ + +/* Interning state. */ +#define SSTATE_NOT_INTERNED 0 +#define SSTATE_INTERNED_MORTAL 1 +#define SSTATE_INTERNED_IMMORTAL 2 + +/* Return true if the string contains only ASCII characters, or 0 if not. The + string may be compact (PyUnicode_IS_COMPACT_ASCII) or not, but must be + ready. */ +#define PyUnicode_IS_ASCII(op) \ + (assert(PyUnicode_Check(op)), \ + assert(PyUnicode_IS_READY(op)), \ + ((PyASCIIObject*)op)->state.ascii) + +/* Return true if the string is compact or 0 if not. + No type checks or Ready calls are performed. */ +#define PyUnicode_IS_COMPACT(op) \ + (((PyASCIIObject*)(op))->state.compact) + +/* Return true if the string is a compact ASCII string (use PyASCIIObject + structure), or 0 if not. No type checks or Ready calls are performed. */ +#define PyUnicode_IS_COMPACT_ASCII(op) \ + (((PyASCIIObject*)op)->state.ascii && PyUnicode_IS_COMPACT(op)) + +enum PyUnicode_Kind { +/* String contains only wstr byte characters. This is only possible + when the string was created with a legacy API and _PyUnicode_Ready() + has not been called yet. */ + PyUnicode_WCHAR_KIND = 0, +/* Return values of the PyUnicode_KIND() macro: */ + PyUnicode_1BYTE_KIND = 1, + PyUnicode_2BYTE_KIND = 2, + PyUnicode_4BYTE_KIND = 4 +}; + +/* Return pointers to the canonical representation cast to unsigned char, + Py_UCS2, or Py_UCS4 for direct character access. + No checks are performed, use PyUnicode_KIND() before to ensure + these will work correctly. */ + +#define PyUnicode_1BYTE_DATA(op) ((Py_UCS1*)PyUnicode_DATA(op)) +#define PyUnicode_2BYTE_DATA(op) ((Py_UCS2*)PyUnicode_DATA(op)) +#define PyUnicode_4BYTE_DATA(op) ((Py_UCS4*)PyUnicode_DATA(op)) + +/* Return one of the PyUnicode_*_KIND values defined above. */ +#define PyUnicode_KIND(op) \ + (assert(PyUnicode_Check(op)), \ + assert(PyUnicode_IS_READY(op)), \ + ((PyASCIIObject *)(op))->state.kind) + +/* Return a void pointer to the raw unicode buffer. */ +#define _PyUnicode_COMPACT_DATA(op) \ + (PyUnicode_IS_ASCII(op) ? \ + ((void*)((PyASCIIObject*)(op) + 1)) : \ + ((void*)((PyCompactUnicodeObject*)(op) + 1))) + +#define _PyUnicode_NONCOMPACT_DATA(op) \ + (assert(((PyUnicodeObject*)(op))->data.any), \ + ((((PyUnicodeObject *)(op))->data.any))) + +#define PyUnicode_DATA(op) \ + (assert(PyUnicode_Check(op)), \ + PyUnicode_IS_COMPACT(op) ? _PyUnicode_COMPACT_DATA(op) : \ + _PyUnicode_NONCOMPACT_DATA(op)) + +/* In the access macros below, "kind" may be evaluated more than once. + All other macro parameters are evaluated exactly once, so it is safe + to put side effects into them (such as increasing the index). */ + +/* Write into the canonical representation, this macro does not do any sanity + checks and is intended for usage in loops. The caller should cache the + kind and data pointers obtained from other macro calls. + index is the index in the string (starts at 0) and value is the new + code point value which should be written to that location. */ +#define PyUnicode_WRITE(kind, data, index, value) \ + do { \ + switch ((kind)) { \ + case PyUnicode_1BYTE_KIND: { \ + ((Py_UCS1 *)(data))[(index)] = (Py_UCS1)(value); \ + break; \ + } \ + case PyUnicode_2BYTE_KIND: { \ + ((Py_UCS2 *)(data))[(index)] = (Py_UCS2)(value); \ + break; \ + } \ + default: { \ + assert((kind) == PyUnicode_4BYTE_KIND); \ + ((Py_UCS4 *)(data))[(index)] = (Py_UCS4)(value); \ + } \ + } \ + } while (0) + +/* Read a code point from the string's canonical representation. No checks + or ready calls are performed. */ +#define PyUnicode_READ(kind, data, index) \ + ((Py_UCS4) \ + ((kind) == PyUnicode_1BYTE_KIND ? \ + ((const Py_UCS1 *)(data))[(index)] : \ + ((kind) == PyUnicode_2BYTE_KIND ? \ + ((const Py_UCS2 *)(data))[(index)] : \ + ((const Py_UCS4 *)(data))[(index)] \ + ) \ + )) + +/* PyUnicode_READ_CHAR() is less efficient than PyUnicode_READ() because it + calls PyUnicode_KIND() and might call it twice. For single reads, use + PyUnicode_READ_CHAR, for multiple consecutive reads callers should + cache kind and use PyUnicode_READ instead. */ +#define PyUnicode_READ_CHAR(unicode, index) \ + (assert(PyUnicode_Check(unicode)), \ + assert(PyUnicode_IS_READY(unicode)), \ + (Py_UCS4) \ + (PyUnicode_KIND((unicode)) == PyUnicode_1BYTE_KIND ? \ + ((const Py_UCS1 *)(PyUnicode_DATA((unicode))))[(index)] : \ + (PyUnicode_KIND((unicode)) == PyUnicode_2BYTE_KIND ? \ + ((const Py_UCS2 *)(PyUnicode_DATA((unicode))))[(index)] : \ + ((const Py_UCS4 *)(PyUnicode_DATA((unicode))))[(index)] \ + ) \ + )) + +/* Returns the length of the unicode string. The caller has to make sure that + the string has it's canonical representation set before calling + this macro. Call PyUnicode_(FAST_)Ready to ensure that. */ +#define PyUnicode_GET_LENGTH(op) \ + (assert(PyUnicode_Check(op)), \ + assert(PyUnicode_IS_READY(op)), \ + ((PyASCIIObject *)(op))->length) + + +/* Fast check to determine whether an object is ready. Equivalent to + PyUnicode_IS_COMPACT(op) || ((PyUnicodeObject*)(op))->data.any) */ + +#define PyUnicode_IS_READY(op) (((PyASCIIObject*)op)->state.ready) + +/* PyUnicode_READY() does less work than _PyUnicode_Ready() in the best + case. If the canonical representation is not yet set, it will still call + _PyUnicode_Ready(). + Returns 0 on success and -1 on errors. */ +#define PyUnicode_READY(op) \ + (assert(PyUnicode_Check(op)), \ + (PyUnicode_IS_READY(op) ? \ + 0 : _PyUnicode_Ready(_PyObject_CAST(op)))) + +/* Return a maximum character value which is suitable for creating another + string based on op. This is always an approximation but more efficient + than iterating over the string. */ +#define PyUnicode_MAX_CHAR_VALUE(op) \ + (assert(PyUnicode_IS_READY(op)), \ + (PyUnicode_IS_ASCII(op) ? \ + (0x7f) : \ + (PyUnicode_KIND(op) == PyUnicode_1BYTE_KIND ? \ + (0xffU) : \ + (PyUnicode_KIND(op) == PyUnicode_2BYTE_KIND ? \ + (0xffffU) : \ + (0x10ffffU))))) + +Py_DEPRECATED(3.3) +static inline Py_ssize_t _PyUnicode_get_wstr_length(PyObject *op) { + return PyUnicode_IS_COMPACT_ASCII(op) ? + ((PyASCIIObject*)op)->length : + ((PyCompactUnicodeObject*)op)->wstr_length; +} +#define PyUnicode_WSTR_LENGTH(op) _PyUnicode_get_wstr_length((PyObject*)op) + +/* === Public API ========================================================= */ + +/* --- Plain Py_UNICODE --------------------------------------------------- */ + +/* With PEP 393, this is the recommended way to allocate a new unicode object. + This function will allocate the object and its buffer in a single memory + block. Objects created using this function are not resizable. */ +PyAPI_FUNC(PyObject*) PyUnicode_New( + Py_ssize_t size, /* Number of code points in the new string */ + Py_UCS4 maxchar /* maximum code point value in the string */ + ); + +/* Initializes the canonical string representation from the deprecated + wstr/Py_UNICODE representation. This function is used to convert Unicode + objects which were created using the old API to the new flexible format + introduced with PEP 393. + + Don't call this function directly, use the public PyUnicode_READY() macro + instead. */ +PyAPI_FUNC(int) _PyUnicode_Ready( + PyObject *unicode /* Unicode object */ + ); + +/* Get a copy of a Unicode string. */ +PyAPI_FUNC(PyObject*) _PyUnicode_Copy( + PyObject *unicode + ); + +/* Copy character from one unicode object into another, this function performs + character conversion when necessary and falls back to memcpy() if possible. + + Fail if to is too small (smaller than *how_many* or smaller than + len(from)-from_start), or if kind(from[from_start:from_start+how_many]) > + kind(to), or if *to* has more than 1 reference. + + Return the number of written character, or return -1 and raise an exception + on error. + + Pseudo-code: + + how_many = min(how_many, len(from) - from_start) + to[to_start:to_start+how_many] = from[from_start:from_start+how_many] + return how_many + + Note: The function doesn't write a terminating null character. + */ +PyAPI_FUNC(Py_ssize_t) PyUnicode_CopyCharacters( + PyObject *to, + Py_ssize_t to_start, + PyObject *from, + Py_ssize_t from_start, + Py_ssize_t how_many + ); + +/* Unsafe version of PyUnicode_CopyCharacters(): don't check arguments and so + may crash if parameters are invalid (e.g. if the output string + is too short). */ +PyAPI_FUNC(void) _PyUnicode_FastCopyCharacters( + PyObject *to, + Py_ssize_t to_start, + PyObject *from, + Py_ssize_t from_start, + Py_ssize_t how_many + ); + +/* Fill a string with a character: write fill_char into + unicode[start:start+length]. + + Fail if fill_char is bigger than the string maximum character, or if the + string has more than 1 reference. + + Return the number of written character, or return -1 and raise an exception + on error. */ +PyAPI_FUNC(Py_ssize_t) PyUnicode_Fill( + PyObject *unicode, + Py_ssize_t start, + Py_ssize_t length, + Py_UCS4 fill_char + ); + +/* Unsafe version of PyUnicode_Fill(): don't check arguments and so may crash + if parameters are invalid (e.g. if length is longer than the string). */ +PyAPI_FUNC(void) _PyUnicode_FastFill( + PyObject *unicode, + Py_ssize_t start, + Py_ssize_t length, + Py_UCS4 fill_char + ); + +/* Create a Unicode Object from the Py_UNICODE buffer u of the given + size. + + u may be NULL which causes the contents to be undefined. It is the + user's responsibility to fill in the needed data afterwards. Note + that modifying the Unicode object contents after construction is + only allowed if u was set to NULL. + + The buffer is copied into the new object. */ +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_FromUnicode( + const Py_UNICODE *u, /* Unicode buffer */ + Py_ssize_t size /* size of buffer */ + ); + +/* Create a new string from a buffer of Py_UCS1, Py_UCS2 or Py_UCS4 characters. + Scan the string to find the maximum character. */ +PyAPI_FUNC(PyObject*) PyUnicode_FromKindAndData( + int kind, + const void *buffer, + Py_ssize_t size); + +/* Create a new string from a buffer of ASCII characters. + WARNING: Don't check if the string contains any non-ASCII character. */ +PyAPI_FUNC(PyObject*) _PyUnicode_FromASCII( + const char *buffer, + Py_ssize_t size); + +/* Compute the maximum character of the substring unicode[start:end]. + Return 127 for an empty string. */ +PyAPI_FUNC(Py_UCS4) _PyUnicode_FindMaxChar ( + PyObject *unicode, + Py_ssize_t start, + Py_ssize_t end); + +/* Return a read-only pointer to the Unicode object's internal + Py_UNICODE buffer. + If the wchar_t/Py_UNICODE representation is not yet available, this + function will calculate it. */ +Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode( + PyObject *unicode /* Unicode object */ + ); + +/* Similar to PyUnicode_AsUnicode(), but raises a ValueError if the string + contains null characters. */ +Py_DEPRECATED(3.3) PyAPI_FUNC(const Py_UNICODE *) _PyUnicode_AsUnicode( + PyObject *unicode /* Unicode object */ + ); + +/* Return a read-only pointer to the Unicode object's internal + Py_UNICODE buffer and save the length at size. + If the wchar_t/Py_UNICODE representation is not yet available, this + function will calculate it. */ + +Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicodeAndSize( + PyObject *unicode, /* Unicode object */ + Py_ssize_t *size /* location where to save the length */ + ); + +/* Get the maximum ordinal for a Unicode character. */ +Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE) PyUnicode_GetMax(void); + + +/* --- _PyUnicodeWriter API ----------------------------------------------- */ + +typedef struct { + PyObject *buffer; + void *data; + enum PyUnicode_Kind kind; + Py_UCS4 maxchar; + Py_ssize_t size; + Py_ssize_t pos; + + /* minimum number of allocated characters (default: 0) */ + Py_ssize_t min_length; + + /* minimum character (default: 127, ASCII) */ + Py_UCS4 min_char; + + /* If non-zero, overallocate the buffer (default: 0). */ + unsigned char overallocate; + + /* If readonly is 1, buffer is a shared string (cannot be modified) + and size is set to 0. */ + unsigned char readonly; +} _PyUnicodeWriter ; + +/* Initialize a Unicode writer. + * + * By default, the minimum buffer size is 0 character and overallocation is + * disabled. Set min_length, min_char and overallocate attributes to control + * the allocation of the buffer. */ +PyAPI_FUNC(void) +_PyUnicodeWriter_Init(_PyUnicodeWriter *writer); + +/* Prepare the buffer to write 'length' characters + with the specified maximum character. + + Return 0 on success, raise an exception and return -1 on error. */ +#define _PyUnicodeWriter_Prepare(WRITER, LENGTH, MAXCHAR) \ + (((MAXCHAR) <= (WRITER)->maxchar \ + && (LENGTH) <= (WRITER)->size - (WRITER)->pos) \ + ? 0 \ + : (((LENGTH) == 0) \ + ? 0 \ + : _PyUnicodeWriter_PrepareInternal((WRITER), (LENGTH), (MAXCHAR)))) + +/* Don't call this function directly, use the _PyUnicodeWriter_Prepare() macro + instead. */ +PyAPI_FUNC(int) +_PyUnicodeWriter_PrepareInternal(_PyUnicodeWriter *writer, + Py_ssize_t length, Py_UCS4 maxchar); + +/* Prepare the buffer to have at least the kind KIND. + For example, kind=PyUnicode_2BYTE_KIND ensures that the writer will + support characters in range U+000-U+FFFF. + + Return 0 on success, raise an exception and return -1 on error. */ +#define _PyUnicodeWriter_PrepareKind(WRITER, KIND) \ + (assert((KIND) != PyUnicode_WCHAR_KIND), \ + (KIND) <= (WRITER)->kind \ + ? 0 \ + : _PyUnicodeWriter_PrepareKindInternal((WRITER), (KIND))) + +/* Don't call this function directly, use the _PyUnicodeWriter_PrepareKind() + macro instead. */ +PyAPI_FUNC(int) +_PyUnicodeWriter_PrepareKindInternal(_PyUnicodeWriter *writer, + enum PyUnicode_Kind kind); + +/* Append a Unicode character. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) +_PyUnicodeWriter_WriteChar(_PyUnicodeWriter *writer, + Py_UCS4 ch + ); + +/* Append a Unicode string. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) +_PyUnicodeWriter_WriteStr(_PyUnicodeWriter *writer, + PyObject *str /* Unicode string */ + ); + +/* Append a substring of a Unicode string. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) +_PyUnicodeWriter_WriteSubstring(_PyUnicodeWriter *writer, + PyObject *str, /* Unicode string */ + Py_ssize_t start, + Py_ssize_t end + ); + +/* Append an ASCII-encoded byte string. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) +_PyUnicodeWriter_WriteASCIIString(_PyUnicodeWriter *writer, + const char *str, /* ASCII-encoded byte string */ + Py_ssize_t len /* number of bytes, or -1 if unknown */ + ); + +/* Append a latin1-encoded byte string. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) +_PyUnicodeWriter_WriteLatin1String(_PyUnicodeWriter *writer, + const char *str, /* latin1-encoded byte string */ + Py_ssize_t len /* length in bytes */ + ); + +/* Get the value of the writer as a Unicode string. Clear the + buffer of the writer. Raise an exception and return NULL + on error. */ +PyAPI_FUNC(PyObject *) +_PyUnicodeWriter_Finish(_PyUnicodeWriter *writer); + +/* Deallocate memory of a writer (clear its internal buffer). */ +PyAPI_FUNC(void) +_PyUnicodeWriter_Dealloc(_PyUnicodeWriter *writer); + + +/* Format the object based on the format_spec, as defined in PEP 3101 + (Advanced String Formatting). */ +PyAPI_FUNC(int) _PyUnicode_FormatAdvancedWriter( + _PyUnicodeWriter *writer, + PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, + Py_ssize_t end); + +/* --- Manage the default encoding ---------------------------------------- */ + +/* Returns a pointer to the default encoding (UTF-8) of the + Unicode object unicode and the size of the encoded representation + in bytes stored in *size. + + In case of an error, no *size is set. + + This function caches the UTF-8 encoded string in the unicodeobject + and subsequent calls will return the same string. The memory is released + when the unicodeobject is deallocated. + + _PyUnicode_AsStringAndSize is a #define for PyUnicode_AsUTF8AndSize to + support the previous internal function with the same behaviour. +*/ + +PyAPI_FUNC(const char *) PyUnicode_AsUTF8AndSize( + PyObject *unicode, + Py_ssize_t *size); + +#define _PyUnicode_AsStringAndSize PyUnicode_AsUTF8AndSize + +/* Returns a pointer to the default encoding (UTF-8) of the + Unicode object unicode. + + Like PyUnicode_AsUTF8AndSize(), this also caches the UTF-8 representation + in the unicodeobject. + + _PyUnicode_AsString is a #define for PyUnicode_AsUTF8 to + support the previous internal function with the same behaviour. + + Use of this API is DEPRECATED since no size information can be + extracted from the returned data. +*/ + +PyAPI_FUNC(const char *) PyUnicode_AsUTF8(PyObject *unicode); + +#define _PyUnicode_AsString PyUnicode_AsUTF8 + +/* --- Generic Codecs ----------------------------------------------------- */ + +/* Encodes a Py_UNICODE buffer of the given size and returns a + Python string object. */ +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_Encode( + const Py_UNICODE *s, /* Unicode char buffer */ + Py_ssize_t size, /* number of Py_UNICODE chars to encode */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* --- UTF-7 Codecs ------------------------------------------------------- */ + +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_EncodeUTF7( + const Py_UNICODE *data, /* Unicode char buffer */ + Py_ssize_t length, /* number of Py_UNICODE chars to encode */ + int base64SetO, /* Encode RFC2152 Set O characters in base64 */ + int base64WhiteSpace, /* Encode whitespace (sp, ht, nl, cr) in base64 */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) _PyUnicode_EncodeUTF7( + PyObject *unicode, /* Unicode object */ + int base64SetO, /* Encode RFC2152 Set O characters in base64 */ + int base64WhiteSpace, /* Encode whitespace (sp, ht, nl, cr) in base64 */ + const char *errors /* error handling */ + ); + +/* --- UTF-8 Codecs ------------------------------------------------------- */ + +PyAPI_FUNC(PyObject*) _PyUnicode_AsUTF8String( + PyObject *unicode, + const char *errors); + +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_EncodeUTF8( + const Py_UNICODE *data, /* Unicode char buffer */ + Py_ssize_t length, /* number of Py_UNICODE chars to encode */ + const char *errors /* error handling */ + ); + +/* --- UTF-32 Codecs ------------------------------------------------------ */ + +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_EncodeUTF32( + const Py_UNICODE *data, /* Unicode char buffer */ + Py_ssize_t length, /* number of Py_UNICODE chars to encode */ + const char *errors, /* error handling */ + int byteorder /* byteorder to use 0=BOM+native;-1=LE,1=BE */ + ); + +PyAPI_FUNC(PyObject*) _PyUnicode_EncodeUTF32( + PyObject *object, /* Unicode object */ + const char *errors, /* error handling */ + int byteorder /* byteorder to use 0=BOM+native;-1=LE,1=BE */ + ); + +/* --- UTF-16 Codecs ------------------------------------------------------ */ + +/* Returns a Python string object holding the UTF-16 encoded value of + the Unicode data. + + If byteorder is not 0, output is written according to the following + byte order: + + byteorder == -1: little endian + byteorder == 0: native byte order (writes a BOM mark) + byteorder == 1: big endian + + If byteorder is 0, the output string will always start with the + Unicode BOM mark (U+FEFF). In the other two modes, no BOM mark is + prepended. + + Note that Py_UNICODE data is being interpreted as UTF-16 reduced to + UCS-2. This trick makes it possible to add full UTF-16 capabilities + at a later point without compromising the APIs. + +*/ +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_EncodeUTF16( + const Py_UNICODE *data, /* Unicode char buffer */ + Py_ssize_t length, /* number of Py_UNICODE chars to encode */ + const char *errors, /* error handling */ + int byteorder /* byteorder to use 0=BOM+native;-1=LE,1=BE */ + ); + +PyAPI_FUNC(PyObject*) _PyUnicode_EncodeUTF16( + PyObject* unicode, /* Unicode object */ + const char *errors, /* error handling */ + int byteorder /* byteorder to use 0=BOM+native;-1=LE,1=BE */ + ); + +/* --- Unicode-Escape Codecs ---------------------------------------------- */ + +/* Helper for PyUnicode_DecodeUnicodeEscape that detects invalid escape + chars. */ +PyAPI_FUNC(PyObject*) _PyUnicode_DecodeUnicodeEscape( + const char *string, /* Unicode-Escape encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors, /* error handling */ + const char **first_invalid_escape /* on return, points to first + invalid escaped char in + string. */ +); + +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_EncodeUnicodeEscape( + const Py_UNICODE *data, /* Unicode char buffer */ + Py_ssize_t length /* Number of Py_UNICODE chars to encode */ + ); + +/* --- Raw-Unicode-Escape Codecs ------------------------------------------ */ + +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_EncodeRawUnicodeEscape( + const Py_UNICODE *data, /* Unicode char buffer */ + Py_ssize_t length /* Number of Py_UNICODE chars to encode */ + ); + +/* --- Latin-1 Codecs ----------------------------------------------------- */ + +PyAPI_FUNC(PyObject*) _PyUnicode_AsLatin1String( + PyObject* unicode, + const char* errors); + +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_EncodeLatin1( + const Py_UNICODE *data, /* Unicode char buffer */ + Py_ssize_t length, /* Number of Py_UNICODE chars to encode */ + const char *errors /* error handling */ + ); + +/* --- ASCII Codecs ------------------------------------------------------- */ + +PyAPI_FUNC(PyObject*) _PyUnicode_AsASCIIString( + PyObject* unicode, + const char* errors); + +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_EncodeASCII( + const Py_UNICODE *data, /* Unicode char buffer */ + Py_ssize_t length, /* Number of Py_UNICODE chars to encode */ + const char *errors /* error handling */ + ); + +/* --- Character Map Codecs ----------------------------------------------- */ + +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_EncodeCharmap( + const Py_UNICODE *data, /* Unicode char buffer */ + Py_ssize_t length, /* Number of Py_UNICODE chars to encode */ + PyObject *mapping, /* encoding mapping */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) _PyUnicode_EncodeCharmap( + PyObject *unicode, /* Unicode object */ + PyObject *mapping, /* encoding mapping */ + const char *errors /* error handling */ + ); + +/* Translate a Py_UNICODE buffer of the given length by applying a + character mapping table to it and return the resulting Unicode + object. + + The mapping table must map Unicode ordinal integers to Unicode strings, + Unicode ordinal integers or None (causing deletion of the character). + + Mapping tables may be dictionaries or sequences. Unmapped character + ordinals (ones which cause a LookupError) are left untouched and + are copied as-is. + +*/ +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject *) PyUnicode_TranslateCharmap( + const Py_UNICODE *data, /* Unicode char buffer */ + Py_ssize_t length, /* Number of Py_UNICODE chars to encode */ + PyObject *table, /* Translate table */ + const char *errors /* error handling */ + ); + +/* --- MBCS codecs for Windows -------------------------------------------- */ + +#ifdef MS_WINDOWS +Py_DEPRECATED(3.3) PyAPI_FUNC(PyObject*) PyUnicode_EncodeMBCS( + const Py_UNICODE *data, /* Unicode char buffer */ + Py_ssize_t length, /* number of Py_UNICODE chars to encode */ + const char *errors /* error handling */ + ); +#endif + +/* --- Decimal Encoder ---------------------------------------------------- */ + +/* Takes a Unicode string holding a decimal value and writes it into + an output buffer using standard ASCII digit codes. + + The output buffer has to provide at least length+1 bytes of storage + area. The output string is 0-terminated. + + The encoder converts whitespace to ' ', decimal characters to their + corresponding ASCII digit and all other Latin-1 characters except + \0 as-is. Characters outside this range (Unicode ordinals 1-256) + are treated as errors. This includes embedded NULL bytes. + + Error handling is defined by the errors argument: + + NULL or "strict": raise a ValueError + "ignore": ignore the wrong characters (these are not copied to the + output buffer) + "replace": replaces illegal characters with '?' + + Returns 0 on success, -1 on failure. + +*/ + +Py_DEPRECATED(3.3) PyAPI_FUNC(int) PyUnicode_EncodeDecimal( + Py_UNICODE *s, /* Unicode buffer */ + Py_ssize_t length, /* Number of Py_UNICODE chars to encode */ + char *output, /* Output buffer; must have size >= length */ + const char *errors /* error handling */ + ); + +/* Transforms code points that have decimal digit property to the + corresponding ASCII digit code points. + + Returns a new Unicode string on success, NULL on failure. +*/ + +Py_DEPRECATED(3.3) +PyAPI_FUNC(PyObject*) PyUnicode_TransformDecimalToASCII( + Py_UNICODE *s, /* Unicode buffer */ + Py_ssize_t length /* Number of Py_UNICODE chars to transform */ + ); + +/* Coverts a Unicode object holding a decimal value to an ASCII string + for using in int, float and complex parsers. + Transforms code points that have decimal digit property to the + corresponding ASCII digit code points. Transforms spaces to ASCII. + Transforms code points starting from the first non-ASCII code point that + is neither a decimal digit nor a space to the end into '?'. */ + +PyAPI_FUNC(PyObject*) _PyUnicode_TransformDecimalAndSpaceToASCII( + PyObject *unicode /* Unicode object */ + ); + +/* --- Methods & Slots ---------------------------------------------------- */ + +PyAPI_FUNC(PyObject *) _PyUnicode_JoinArray( + PyObject *separator, + PyObject *const *items, + Py_ssize_t seqlen + ); + +/* Test whether a unicode is equal to ASCII identifier. Return 1 if true, + 0 otherwise. The right argument must be ASCII identifier. + Any error occurs inside will be cleared before return. */ +PyAPI_FUNC(int) _PyUnicode_EqualToASCIIId( + PyObject *left, /* Left string */ + _Py_Identifier *right /* Right identifier */ + ); + +/* Test whether a unicode is equal to ASCII string. Return 1 if true, + 0 otherwise. The right argument must be ASCII-encoded string. + Any error occurs inside will be cleared before return. */ +PyAPI_FUNC(int) _PyUnicode_EqualToASCIIString( + PyObject *left, + const char *right /* ASCII-encoded string */ + ); + +/* Externally visible for str.strip(unicode) */ +PyAPI_FUNC(PyObject *) _PyUnicode_XStrip( + PyObject *self, + int striptype, + PyObject *sepobj + ); + +/* Using explicit passed-in values, insert the thousands grouping + into the string pointed to by buffer. For the argument descriptions, + see Objects/stringlib/localeutil.h */ +PyAPI_FUNC(Py_ssize_t) _PyUnicode_InsertThousandsGrouping( + _PyUnicodeWriter *writer, + Py_ssize_t n_buffer, + PyObject *digits, + Py_ssize_t d_pos, + Py_ssize_t n_digits, + Py_ssize_t min_width, + const char *grouping, + PyObject *thousands_sep, + Py_UCS4 *maxchar); + +/* === Characters Type APIs =============================================== */ + +/* Helper array used by Py_UNICODE_ISSPACE(). */ + +PyAPI_DATA(const unsigned char) _Py_ascii_whitespace[]; + +/* These should not be used directly. Use the Py_UNICODE_IS* and + Py_UNICODE_TO* macros instead. + + These APIs are implemented in Objects/unicodectype.c. + +*/ + +PyAPI_FUNC(int) _PyUnicode_IsLowercase( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsUppercase( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsTitlecase( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsXidStart( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsXidContinue( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsWhitespace( + const Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsLinebreak( + const Py_UCS4 ch /* Unicode character */ + ); + +/* Py_DEPRECATED(3.3) */ PyAPI_FUNC(Py_UCS4) _PyUnicode_ToLowercase( + Py_UCS4 ch /* Unicode character */ + ); + +/* Py_DEPRECATED(3.3) */ PyAPI_FUNC(Py_UCS4) _PyUnicode_ToUppercase( + Py_UCS4 ch /* Unicode character */ + ); + +Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UCS4) _PyUnicode_ToTitlecase( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_ToLowerFull( + Py_UCS4 ch, /* Unicode character */ + Py_UCS4 *res + ); + +PyAPI_FUNC(int) _PyUnicode_ToTitleFull( + Py_UCS4 ch, /* Unicode character */ + Py_UCS4 *res + ); + +PyAPI_FUNC(int) _PyUnicode_ToUpperFull( + Py_UCS4 ch, /* Unicode character */ + Py_UCS4 *res + ); + +PyAPI_FUNC(int) _PyUnicode_ToFoldedFull( + Py_UCS4 ch, /* Unicode character */ + Py_UCS4 *res + ); + +PyAPI_FUNC(int) _PyUnicode_IsCaseIgnorable( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsCased( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_ToDecimalDigit( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_ToDigit( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(double) _PyUnicode_ToNumeric( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsDecimalDigit( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsDigit( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsNumeric( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsPrintable( + Py_UCS4 ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsAlpha( + Py_UCS4 ch /* Unicode character */ + ); + +Py_DEPRECATED(3.3) PyAPI_FUNC(size_t) Py_UNICODE_strlen( + const Py_UNICODE *u + ); + +Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strcpy( + Py_UNICODE *s1, + const Py_UNICODE *s2); + +Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strcat( + Py_UNICODE *s1, const Py_UNICODE *s2); + +Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strncpy( + Py_UNICODE *s1, + const Py_UNICODE *s2, + size_t n); + +Py_DEPRECATED(3.3) PyAPI_FUNC(int) Py_UNICODE_strcmp( + const Py_UNICODE *s1, + const Py_UNICODE *s2 + ); + +Py_DEPRECATED(3.3) PyAPI_FUNC(int) Py_UNICODE_strncmp( + const Py_UNICODE *s1, + const Py_UNICODE *s2, + size_t n + ); + +Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strchr( + const Py_UNICODE *s, + Py_UNICODE c + ); + +Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strrchr( + const Py_UNICODE *s, + Py_UNICODE c + ); + +PyAPI_FUNC(PyObject*) _PyUnicode_FormatLong(PyObject *, int, int, int); + +/* Create a copy of a unicode string ending with a nul character. Return NULL + and raise a MemoryError exception on memory allocation failure, otherwise + return a new allocated buffer (use PyMem_Free() to free the buffer). */ + +Py_DEPRECATED(3.3) PyAPI_FUNC(Py_UNICODE*) PyUnicode_AsUnicodeCopy( + PyObject *unicode + ); + +/* Return an interned Unicode object for an Identifier; may fail if there is no memory.*/ +PyAPI_FUNC(PyObject*) _PyUnicode_FromId(_Py_Identifier*); + +/* Fast equality check when the inputs are known to be exact unicode types + and where the hash values are equal (i.e. a very probable match) */ +PyAPI_FUNC(int) _PyUnicode_EQ(PyObject *, PyObject *); + +PyAPI_FUNC(Py_ssize_t) _PyUnicode_ScanIdentifier(PyObject *); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/build-windows/py39-libs/include/datetime.h b/scripts/build-windows/py39-libs/include/datetime.h new file mode 100644 index 000000000..5d9f2558f --- /dev/null +++ b/scripts/build-windows/py39-libs/include/datetime.h @@ -0,0 +1,259 @@ +/* datetime.h + */ +#ifndef Py_LIMITED_API +#ifndef DATETIME_H +#define DATETIME_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Fields are packed into successive bytes, each viewed as unsigned and + * big-endian, unless otherwise noted: + * + * byte offset + * 0 year 2 bytes, 1-9999 + * 2 month 1 byte, 1-12 + * 3 day 1 byte, 1-31 + * 4 hour 1 byte, 0-23 + * 5 minute 1 byte, 0-59 + * 6 second 1 byte, 0-59 + * 7 usecond 3 bytes, 0-999999 + * 10 + */ + +/* # of bytes for year, month, and day. */ +#define _PyDateTime_DATE_DATASIZE 4 + +/* # of bytes for hour, minute, second, and usecond. */ +#define _PyDateTime_TIME_DATASIZE 6 + +/* # of bytes for year, month, day, hour, minute, second, and usecond. */ +#define _PyDateTime_DATETIME_DATASIZE 10 + + +typedef struct +{ + PyObject_HEAD + Py_hash_t hashcode; /* -1 when unknown */ + int days; /* -MAX_DELTA_DAYS <= days <= MAX_DELTA_DAYS */ + int seconds; /* 0 <= seconds < 24*3600 is invariant */ + int microseconds; /* 0 <= microseconds < 1000000 is invariant */ +} PyDateTime_Delta; + +typedef struct +{ + PyObject_HEAD /* a pure abstract base class */ +} PyDateTime_TZInfo; + + +/* The datetime and time types have hashcodes, and an optional tzinfo member, + * present if and only if hastzinfo is true. + */ +#define _PyTZINFO_HEAD \ + PyObject_HEAD \ + Py_hash_t hashcode; \ + char hastzinfo; /* boolean flag */ + +/* No _PyDateTime_BaseTZInfo is allocated; it's just to have something + * convenient to cast to, when getting at the hastzinfo member of objects + * starting with _PyTZINFO_HEAD. + */ +typedef struct +{ + _PyTZINFO_HEAD +} _PyDateTime_BaseTZInfo; + +/* All time objects are of PyDateTime_TimeType, but that can be allocated + * in two ways, with or without a tzinfo member. Without is the same as + * tzinfo == None, but consumes less memory. _PyDateTime_BaseTime is an + * internal struct used to allocate the right amount of space for the + * "without" case. + */ +#define _PyDateTime_TIMEHEAD \ + _PyTZINFO_HEAD \ + unsigned char data[_PyDateTime_TIME_DATASIZE]; + +typedef struct +{ + _PyDateTime_TIMEHEAD +} _PyDateTime_BaseTime; /* hastzinfo false */ + +typedef struct +{ + _PyDateTime_TIMEHEAD + unsigned char fold; + PyObject *tzinfo; +} PyDateTime_Time; /* hastzinfo true */ + + +/* All datetime objects are of PyDateTime_DateTimeType, but that can be + * allocated in two ways too, just like for time objects above. In addition, + * the plain date type is a base class for datetime, so it must also have + * a hastzinfo member (although it's unused there). + */ +typedef struct +{ + _PyTZINFO_HEAD + unsigned char data[_PyDateTime_DATE_DATASIZE]; +} PyDateTime_Date; + +#define _PyDateTime_DATETIMEHEAD \ + _PyTZINFO_HEAD \ + unsigned char data[_PyDateTime_DATETIME_DATASIZE]; + +typedef struct +{ + _PyDateTime_DATETIMEHEAD +} _PyDateTime_BaseDateTime; /* hastzinfo false */ + +typedef struct +{ + _PyDateTime_DATETIMEHEAD + unsigned char fold; + PyObject *tzinfo; +} PyDateTime_DateTime; /* hastzinfo true */ + + +/* Apply for date and datetime instances. */ +#define PyDateTime_GET_YEAR(o) ((((PyDateTime_Date*)o)->data[0] << 8) | \ + ((PyDateTime_Date*)o)->data[1]) +#define PyDateTime_GET_MONTH(o) (((PyDateTime_Date*)o)->data[2]) +#define PyDateTime_GET_DAY(o) (((PyDateTime_Date*)o)->data[3]) + +#define PyDateTime_DATE_GET_HOUR(o) (((PyDateTime_DateTime*)o)->data[4]) +#define PyDateTime_DATE_GET_MINUTE(o) (((PyDateTime_DateTime*)o)->data[5]) +#define PyDateTime_DATE_GET_SECOND(o) (((PyDateTime_DateTime*)o)->data[6]) +#define PyDateTime_DATE_GET_MICROSECOND(o) \ + ((((PyDateTime_DateTime*)o)->data[7] << 16) | \ + (((PyDateTime_DateTime*)o)->data[8] << 8) | \ + ((PyDateTime_DateTime*)o)->data[9]) +#define PyDateTime_DATE_GET_FOLD(o) (((PyDateTime_DateTime*)o)->fold) + +/* Apply for time instances. */ +#define PyDateTime_TIME_GET_HOUR(o) (((PyDateTime_Time*)o)->data[0]) +#define PyDateTime_TIME_GET_MINUTE(o) (((PyDateTime_Time*)o)->data[1]) +#define PyDateTime_TIME_GET_SECOND(o) (((PyDateTime_Time*)o)->data[2]) +#define PyDateTime_TIME_GET_MICROSECOND(o) \ + ((((PyDateTime_Time*)o)->data[3] << 16) | \ + (((PyDateTime_Time*)o)->data[4] << 8) | \ + ((PyDateTime_Time*)o)->data[5]) +#define PyDateTime_TIME_GET_FOLD(o) (((PyDateTime_Time*)o)->fold) + +/* Apply for time delta instances */ +#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) +#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) +#define PyDateTime_DELTA_GET_MICROSECONDS(o) \ + (((PyDateTime_Delta*)o)->microseconds) + + +/* Define structure for C API. */ +typedef struct { + /* type objects */ + PyTypeObject *DateType; + PyTypeObject *DateTimeType; + PyTypeObject *TimeType; + PyTypeObject *DeltaType; + PyTypeObject *TZInfoType; + + /* singletons */ + PyObject *TimeZone_UTC; + + /* constructors */ + PyObject *(*Date_FromDate)(int, int, int, PyTypeObject*); + PyObject *(*DateTime_FromDateAndTime)(int, int, int, int, int, int, int, + PyObject*, PyTypeObject*); + PyObject *(*Time_FromTime)(int, int, int, int, PyObject*, PyTypeObject*); + PyObject *(*Delta_FromDelta)(int, int, int, int, PyTypeObject*); + PyObject *(*TimeZone_FromTimeZone)(PyObject *offset, PyObject *name); + + /* constructors for the DB API */ + PyObject *(*DateTime_FromTimestamp)(PyObject*, PyObject*, PyObject*); + PyObject *(*Date_FromTimestamp)(PyObject*, PyObject*); + + /* PEP 495 constructors */ + PyObject *(*DateTime_FromDateAndTimeAndFold)(int, int, int, int, int, int, int, + PyObject*, int, PyTypeObject*); + PyObject *(*Time_FromTimeAndFold)(int, int, int, int, PyObject*, int, PyTypeObject*); + +} PyDateTime_CAPI; + +#define PyDateTime_CAPSULE_NAME "datetime.datetime_CAPI" + + +/* This block is only used as part of the public API and should not be + * included in _datetimemodule.c, which does not use the C API capsule. + * See bpo-35081 for more details. + * */ +#ifndef _PY_DATETIME_IMPL +/* Define global variable for the C API and a macro for setting it. */ +static PyDateTime_CAPI *PyDateTimeAPI = NULL; + +#define PyDateTime_IMPORT \ + PyDateTimeAPI = (PyDateTime_CAPI *)PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0) + +/* Macro for access to the UTC singleton */ +#define PyDateTime_TimeZone_UTC PyDateTimeAPI->TimeZone_UTC + +/* Macros for type checking when not building the Python core. */ +#define PyDate_Check(op) PyObject_TypeCheck(op, PyDateTimeAPI->DateType) +#define PyDate_CheckExact(op) Py_IS_TYPE(op, PyDateTimeAPI->DateType) + +#define PyDateTime_Check(op) PyObject_TypeCheck(op, PyDateTimeAPI->DateTimeType) +#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, PyDateTimeAPI->DateTimeType) + +#define PyTime_Check(op) PyObject_TypeCheck(op, PyDateTimeAPI->TimeType) +#define PyTime_CheckExact(op) Py_IS_TYPE(op, PyDateTimeAPI->TimeType) + +#define PyDelta_Check(op) PyObject_TypeCheck(op, PyDateTimeAPI->DeltaType) +#define PyDelta_CheckExact(op) Py_IS_TYPE(op, PyDateTimeAPI->DeltaType) + +#define PyTZInfo_Check(op) PyObject_TypeCheck(op, PyDateTimeAPI->TZInfoType) +#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, PyDateTimeAPI->TZInfoType) + + +/* Macros for accessing constructors in a simplified fashion. */ +#define PyDate_FromDate(year, month, day) \ + PyDateTimeAPI->Date_FromDate(year, month, day, PyDateTimeAPI->DateType) + +#define PyDateTime_FromDateAndTime(year, month, day, hour, min, sec, usec) \ + PyDateTimeAPI->DateTime_FromDateAndTime(year, month, day, hour, \ + min, sec, usec, Py_None, PyDateTimeAPI->DateTimeType) + +#define PyDateTime_FromDateAndTimeAndFold(year, month, day, hour, min, sec, usec, fold) \ + PyDateTimeAPI->DateTime_FromDateAndTimeAndFold(year, month, day, hour, \ + min, sec, usec, Py_None, fold, PyDateTimeAPI->DateTimeType) + +#define PyTime_FromTime(hour, minute, second, usecond) \ + PyDateTimeAPI->Time_FromTime(hour, minute, second, usecond, \ + Py_None, PyDateTimeAPI->TimeType) + +#define PyTime_FromTimeAndFold(hour, minute, second, usecond, fold) \ + PyDateTimeAPI->Time_FromTimeAndFold(hour, minute, second, usecond, \ + Py_None, fold, PyDateTimeAPI->TimeType) + +#define PyDelta_FromDSU(days, seconds, useconds) \ + PyDateTimeAPI->Delta_FromDelta(days, seconds, useconds, 1, \ + PyDateTimeAPI->DeltaType) + +#define PyTimeZone_FromOffset(offset) \ + PyDateTimeAPI->TimeZone_FromTimeZone(offset, NULL) + +#define PyTimeZone_FromOffsetAndName(offset, name) \ + PyDateTimeAPI->TimeZone_FromTimeZone(offset, name) + +/* Macros supporting the DB API. */ +#define PyDateTime_FromTimestamp(args) \ + PyDateTimeAPI->DateTime_FromTimestamp( \ + (PyObject*) (PyDateTimeAPI->DateTimeType), args, NULL) + +#define PyDate_FromTimestamp(args) \ + PyDateTimeAPI->Date_FromTimestamp( \ + (PyObject*) (PyDateTimeAPI->DateType), args) + +#endif /* !defined(_PY_DATETIME_IMPL) */ + +#ifdef __cplusplus +} +#endif +#endif +#endif /* !Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/descrobject.h b/scripts/build-windows/py39-libs/include/descrobject.h new file mode 100644 index 000000000..ead269d1d --- /dev/null +++ b/scripts/build-windows/py39-libs/include/descrobject.h @@ -0,0 +1,108 @@ +/* Descriptors */ +#ifndef Py_DESCROBJECT_H +#define Py_DESCROBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef PyObject *(*getter)(PyObject *, void *); +typedef int (*setter)(PyObject *, PyObject *, void *); + +typedef struct PyGetSetDef { + const char *name; + getter get; + setter set; + const char *doc; + void *closure; +} PyGetSetDef; + +#ifndef Py_LIMITED_API +typedef PyObject *(*wrapperfunc)(PyObject *self, PyObject *args, + void *wrapped); + +typedef PyObject *(*wrapperfunc_kwds)(PyObject *self, PyObject *args, + void *wrapped, PyObject *kwds); + +struct wrapperbase { + const char *name; + int offset; + void *function; + wrapperfunc wrapper; + const char *doc; + int flags; + PyObject *name_strobj; +}; + +/* Flags for above struct */ +#define PyWrapperFlag_KEYWORDS 1 /* wrapper function takes keyword args */ + +/* Various kinds of descriptor objects */ + +typedef struct { + PyObject_HEAD + PyTypeObject *d_type; + PyObject *d_name; + PyObject *d_qualname; +} PyDescrObject; + +#define PyDescr_COMMON PyDescrObject d_common + +#define PyDescr_TYPE(x) (((PyDescrObject *)(x))->d_type) +#define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name) + +typedef struct { + PyDescr_COMMON; + PyMethodDef *d_method; + vectorcallfunc vectorcall; +} PyMethodDescrObject; + +typedef struct { + PyDescr_COMMON; + struct PyMemberDef *d_member; +} PyMemberDescrObject; + +typedef struct { + PyDescr_COMMON; + PyGetSetDef *d_getset; +} PyGetSetDescrObject; + +typedef struct { + PyDescr_COMMON; + struct wrapperbase *d_base; + void *d_wrapped; /* This can be any function pointer */ +} PyWrapperDescrObject; +#endif /* Py_LIMITED_API */ + +PyAPI_DATA(PyTypeObject) PyClassMethodDescr_Type; +PyAPI_DATA(PyTypeObject) PyGetSetDescr_Type; +PyAPI_DATA(PyTypeObject) PyMemberDescr_Type; +PyAPI_DATA(PyTypeObject) PyMethodDescr_Type; +PyAPI_DATA(PyTypeObject) PyWrapperDescr_Type; +PyAPI_DATA(PyTypeObject) PyDictProxy_Type; +#ifndef Py_LIMITED_API +PyAPI_DATA(PyTypeObject) _PyMethodWrapper_Type; +#endif /* Py_LIMITED_API */ + +PyAPI_FUNC(PyObject *) PyDescr_NewMethod(PyTypeObject *, PyMethodDef *); +PyAPI_FUNC(PyObject *) PyDescr_NewClassMethod(PyTypeObject *, PyMethodDef *); +struct PyMemberDef; /* forward declaration for following prototype */ +PyAPI_FUNC(PyObject *) PyDescr_NewMember(PyTypeObject *, + struct PyMemberDef *); +PyAPI_FUNC(PyObject *) PyDescr_NewGetSet(PyTypeObject *, + struct PyGetSetDef *); +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) PyDescr_NewWrapper(PyTypeObject *, + struct wrapperbase *, void *); +#define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set != NULL) +#endif + +PyAPI_FUNC(PyObject *) PyDictProxy_New(PyObject *); +PyAPI_FUNC(PyObject *) PyWrapper_New(PyObject *, PyObject *); + + +PyAPI_DATA(PyTypeObject) PyProperty_Type; +#ifdef __cplusplus +} +#endif +#endif /* !Py_DESCROBJECT_H */ + diff --git a/scripts/build-windows/py39-libs/include/dictobject.h b/scripts/build-windows/py39-libs/include/dictobject.h new file mode 100644 index 000000000..c88b0aa0a --- /dev/null +++ b/scripts/build-windows/py39-libs/include/dictobject.h @@ -0,0 +1,94 @@ +#ifndef Py_DICTOBJECT_H +#define Py_DICTOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Dictionary object type -- mapping from hashable object to object */ + +/* The distribution includes a separate file, Objects/dictnotes.txt, + describing explorations into dictionary design and optimization. + It covers typical dictionary use patterns, the parameters for + tuning dictionaries, and several ideas for possible optimizations. +*/ + +PyAPI_DATA(PyTypeObject) PyDict_Type; + +#define PyDict_Check(op) \ + PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_DICT_SUBCLASS) +#define PyDict_CheckExact(op) Py_IS_TYPE(op, &PyDict_Type) + +PyAPI_FUNC(PyObject *) PyDict_New(void); +PyAPI_FUNC(PyObject *) PyDict_GetItem(PyObject *mp, PyObject *key); +PyAPI_FUNC(PyObject *) PyDict_GetItemWithError(PyObject *mp, PyObject *key); +PyAPI_FUNC(int) PyDict_SetItem(PyObject *mp, PyObject *key, PyObject *item); +PyAPI_FUNC(int) PyDict_DelItem(PyObject *mp, PyObject *key); +PyAPI_FUNC(void) PyDict_Clear(PyObject *mp); +PyAPI_FUNC(int) PyDict_Next( + PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value); +PyAPI_FUNC(PyObject *) PyDict_Keys(PyObject *mp); +PyAPI_FUNC(PyObject *) PyDict_Values(PyObject *mp); +PyAPI_FUNC(PyObject *) PyDict_Items(PyObject *mp); +PyAPI_FUNC(Py_ssize_t) PyDict_Size(PyObject *mp); +PyAPI_FUNC(PyObject *) PyDict_Copy(PyObject *mp); +PyAPI_FUNC(int) PyDict_Contains(PyObject *mp, PyObject *key); + +/* PyDict_Update(mp, other) is equivalent to PyDict_Merge(mp, other, 1). */ +PyAPI_FUNC(int) PyDict_Update(PyObject *mp, PyObject *other); + +/* PyDict_Merge updates/merges from a mapping object (an object that + supports PyMapping_Keys() and PyObject_GetItem()). If override is true, + the last occurrence of a key wins, else the first. The Python + dict.update(other) is equivalent to PyDict_Merge(dict, other, 1). +*/ +PyAPI_FUNC(int) PyDict_Merge(PyObject *mp, + PyObject *other, + int override); + +/* PyDict_MergeFromSeq2 updates/merges from an iterable object producing + iterable objects of length 2. If override is true, the last occurrence + of a key wins, else the first. The Python dict constructor dict(seq2) + is equivalent to dict={}; PyDict_MergeFromSeq(dict, seq2, 1). +*/ +PyAPI_FUNC(int) PyDict_MergeFromSeq2(PyObject *d, + PyObject *seq2, + int override); + +PyAPI_FUNC(PyObject *) PyDict_GetItemString(PyObject *dp, const char *key); +PyAPI_FUNC(int) PyDict_SetItemString(PyObject *dp, const char *key, PyObject *item); +PyAPI_FUNC(int) PyDict_DelItemString(PyObject *dp, const char *key); + +/* Dictionary (keys, values, items) views */ + +PyAPI_DATA(PyTypeObject) PyDictKeys_Type; +PyAPI_DATA(PyTypeObject) PyDictValues_Type; +PyAPI_DATA(PyTypeObject) PyDictItems_Type; + +#define PyDictKeys_Check(op) PyObject_TypeCheck(op, &PyDictKeys_Type) +#define PyDictValues_Check(op) PyObject_TypeCheck(op, &PyDictValues_Type) +#define PyDictItems_Check(op) PyObject_TypeCheck(op, &PyDictItems_Type) +/* This excludes Values, since they are not sets. */ +# define PyDictViewSet_Check(op) \ + (PyDictKeys_Check(op) || PyDictItems_Check(op)) + +/* Dictionary (key, value, items) iterators */ + +PyAPI_DATA(PyTypeObject) PyDictIterKey_Type; +PyAPI_DATA(PyTypeObject) PyDictIterValue_Type; +PyAPI_DATA(PyTypeObject) PyDictIterItem_Type; + +PyAPI_DATA(PyTypeObject) PyDictRevIterKey_Type; +PyAPI_DATA(PyTypeObject) PyDictRevIterItem_Type; +PyAPI_DATA(PyTypeObject) PyDictRevIterValue_Type; + + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_DICTOBJECT_H +# include "cpython/dictobject.h" +# undef Py_CPYTHON_DICTOBJECT_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_DICTOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/dynamic_annotations.h b/scripts/build-windows/py39-libs/include/dynamic_annotations.h new file mode 100644 index 000000000..0bd1a833c --- /dev/null +++ b/scripts/build-windows/py39-libs/include/dynamic_annotations.h @@ -0,0 +1,499 @@ +/* Copyright (c) 2008-2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * --- + * Author: Kostya Serebryany + * Copied to CPython by Jeffrey Yasskin, with all macros renamed to + * start with _Py_ to avoid colliding with users embedding Python, and + * with deprecated macros removed. + */ + +/* This file defines dynamic annotations for use with dynamic analysis + tool such as valgrind, PIN, etc. + + Dynamic annotation is a source code annotation that affects + the generated code (that is, the annotation is not a comment). + Each such annotation is attached to a particular + instruction and/or to a particular object (address) in the program. + + The annotations that should be used by users are macros in all upper-case + (e.g., _Py_ANNOTATE_NEW_MEMORY). + + Actual implementation of these macros may differ depending on the + dynamic analysis tool being used. + + See http://code.google.com/p/data-race-test/ for more information. + + This file supports the following dynamic analysis tools: + - None (DYNAMIC_ANNOTATIONS_ENABLED is not defined or zero). + Macros are defined empty. + - ThreadSanitizer, Helgrind, DRD (DYNAMIC_ANNOTATIONS_ENABLED is 1). + Macros are defined as calls to non-inlinable empty functions + that are intercepted by Valgrind. */ + +#ifndef __DYNAMIC_ANNOTATIONS_H__ +#define __DYNAMIC_ANNOTATIONS_H__ + +#ifndef DYNAMIC_ANNOTATIONS_ENABLED +# define DYNAMIC_ANNOTATIONS_ENABLED 0 +#endif + +#if DYNAMIC_ANNOTATIONS_ENABLED != 0 + + /* ------------------------------------------------------------- + Annotations useful when implementing condition variables such as CondVar, + using conditional critical sections (Await/LockWhen) and when constructing + user-defined synchronization mechanisms. + + The annotations _Py_ANNOTATE_HAPPENS_BEFORE() and + _Py_ANNOTATE_HAPPENS_AFTER() can be used to define happens-before arcs in + user-defined synchronization mechanisms: the race detector will infer an + arc from the former to the latter when they share the same argument + pointer. + + Example 1 (reference counting): + + void Unref() { + _Py_ANNOTATE_HAPPENS_BEFORE(&refcount_); + if (AtomicDecrementByOne(&refcount_) == 0) { + _Py_ANNOTATE_HAPPENS_AFTER(&refcount_); + delete this; + } + } + + Example 2 (message queue): + + void MyQueue::Put(Type *e) { + MutexLock lock(&mu_); + _Py_ANNOTATE_HAPPENS_BEFORE(e); + PutElementIntoMyQueue(e); + } + + Type *MyQueue::Get() { + MutexLock lock(&mu_); + Type *e = GetElementFromMyQueue(); + _Py_ANNOTATE_HAPPENS_AFTER(e); + return e; + } + + Note: when possible, please use the existing reference counting and message + queue implementations instead of inventing new ones. */ + + /* Report that wait on the condition variable at address "cv" has succeeded + and the lock at address "lock" is held. */ +#define _Py_ANNOTATE_CONDVAR_LOCK_WAIT(cv, lock) \ + AnnotateCondVarWait(__FILE__, __LINE__, cv, lock) + + /* Report that wait on the condition variable at "cv" has succeeded. Variant + w/o lock. */ +#define _Py_ANNOTATE_CONDVAR_WAIT(cv) \ + AnnotateCondVarWait(__FILE__, __LINE__, cv, NULL) + + /* Report that we are about to signal on the condition variable at address + "cv". */ +#define _Py_ANNOTATE_CONDVAR_SIGNAL(cv) \ + AnnotateCondVarSignal(__FILE__, __LINE__, cv) + + /* Report that we are about to signal_all on the condition variable at "cv". */ +#define _Py_ANNOTATE_CONDVAR_SIGNAL_ALL(cv) \ + AnnotateCondVarSignalAll(__FILE__, __LINE__, cv) + + /* Annotations for user-defined synchronization mechanisms. */ +#define _Py_ANNOTATE_HAPPENS_BEFORE(obj) _Py_ANNOTATE_CONDVAR_SIGNAL(obj) +#define _Py_ANNOTATE_HAPPENS_AFTER(obj) _Py_ANNOTATE_CONDVAR_WAIT(obj) + + /* Report that the bytes in the range [pointer, pointer+size) are about + to be published safely. The race checker will create a happens-before + arc from the call _Py_ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) to + subsequent accesses to this memory. + Note: this annotation may not work properly if the race detector uses + sampling, i.e. does not observe all memory accesses. + */ +#define _Py_ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) \ + AnnotatePublishMemoryRange(__FILE__, __LINE__, pointer, size) + + /* Instruct the tool to create a happens-before arc between mu->Unlock() and + mu->Lock(). This annotation may slow down the race detector and hide real + races. Normally it is used only when it would be difficult to annotate each + of the mutex's critical sections individually using the annotations above. + This annotation makes sense only for hybrid race detectors. For pure + happens-before detectors this is a no-op. For more details see + http://code.google.com/p/data-race-test/wiki/PureHappensBeforeVsHybrid . */ +#define _Py_ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) \ + AnnotateMutexIsUsedAsCondVar(__FILE__, __LINE__, mu) + + /* ------------------------------------------------------------- + Annotations useful when defining memory allocators, or when memory that + was protected in one way starts to be protected in another. */ + + /* Report that a new memory at "address" of size "size" has been allocated. + This might be used when the memory has been retrieved from a free list and + is about to be reused, or when the locking discipline for a variable + changes. */ +#define _Py_ANNOTATE_NEW_MEMORY(address, size) \ + AnnotateNewMemory(__FILE__, __LINE__, address, size) + + /* ------------------------------------------------------------- + Annotations useful when defining FIFO queues that transfer data between + threads. */ + + /* Report that the producer-consumer queue (such as ProducerConsumerQueue) at + address "pcq" has been created. The _Py_ANNOTATE_PCQ_* annotations should + be used only for FIFO queues. For non-FIFO queues use + _Py_ANNOTATE_HAPPENS_BEFORE (for put) and _Py_ANNOTATE_HAPPENS_AFTER (for + get). */ +#define _Py_ANNOTATE_PCQ_CREATE(pcq) \ + AnnotatePCQCreate(__FILE__, __LINE__, pcq) + + /* Report that the queue at address "pcq" is about to be destroyed. */ +#define _Py_ANNOTATE_PCQ_DESTROY(pcq) \ + AnnotatePCQDestroy(__FILE__, __LINE__, pcq) + + /* Report that we are about to put an element into a FIFO queue at address + "pcq". */ +#define _Py_ANNOTATE_PCQ_PUT(pcq) \ + AnnotatePCQPut(__FILE__, __LINE__, pcq) + + /* Report that we've just got an element from a FIFO queue at address "pcq". */ +#define _Py_ANNOTATE_PCQ_GET(pcq) \ + AnnotatePCQGet(__FILE__, __LINE__, pcq) + + /* ------------------------------------------------------------- + Annotations that suppress errors. It is usually better to express the + program's synchronization using the other annotations, but these can + be used when all else fails. */ + + /* Report that we may have a benign race at "pointer", with size + "sizeof(*(pointer))". "pointer" must be a non-void* pointer. Insert at the + point where "pointer" has been allocated, preferably close to the point + where the race happens. See also _Py_ANNOTATE_BENIGN_RACE_STATIC. */ +#define _Py_ANNOTATE_BENIGN_RACE(pointer, description) \ + AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, \ + sizeof(*(pointer)), description) + + /* Same as _Py_ANNOTATE_BENIGN_RACE(address, description), but applies to + the memory range [address, address+size). */ +#define _Py_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ + AnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description) + + /* Request the analysis tool to ignore all reads in the current thread + until _Py_ANNOTATE_IGNORE_READS_END is called. + Useful to ignore intentional racey reads, while still checking + other reads and all writes. + See also _Py_ANNOTATE_UNPROTECTED_READ. */ +#define _Py_ANNOTATE_IGNORE_READS_BEGIN() \ + AnnotateIgnoreReadsBegin(__FILE__, __LINE__) + + /* Stop ignoring reads. */ +#define _Py_ANNOTATE_IGNORE_READS_END() \ + AnnotateIgnoreReadsEnd(__FILE__, __LINE__) + + /* Similar to _Py_ANNOTATE_IGNORE_READS_BEGIN, but ignore writes. */ +#define _Py_ANNOTATE_IGNORE_WRITES_BEGIN() \ + AnnotateIgnoreWritesBegin(__FILE__, __LINE__) + + /* Stop ignoring writes. */ +#define _Py_ANNOTATE_IGNORE_WRITES_END() \ + AnnotateIgnoreWritesEnd(__FILE__, __LINE__) + + /* Start ignoring all memory accesses (reads and writes). */ +#define _Py_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ + do {\ + _Py_ANNOTATE_IGNORE_READS_BEGIN();\ + _Py_ANNOTATE_IGNORE_WRITES_BEGIN();\ + }while(0)\ + + /* Stop ignoring all memory accesses. */ +#define _Py_ANNOTATE_IGNORE_READS_AND_WRITES_END() \ + do {\ + _Py_ANNOTATE_IGNORE_WRITES_END();\ + _Py_ANNOTATE_IGNORE_READS_END();\ + }while(0)\ + + /* Similar to _Py_ANNOTATE_IGNORE_READS_BEGIN, but ignore synchronization events: + RWLOCK* and CONDVAR*. */ +#define _Py_ANNOTATE_IGNORE_SYNC_BEGIN() \ + AnnotateIgnoreSyncBegin(__FILE__, __LINE__) + + /* Stop ignoring sync events. */ +#define _Py_ANNOTATE_IGNORE_SYNC_END() \ + AnnotateIgnoreSyncEnd(__FILE__, __LINE__) + + + /* Enable (enable!=0) or disable (enable==0) race detection for all threads. + This annotation could be useful if you want to skip expensive race analysis + during some period of program execution, e.g. during initialization. */ +#define _Py_ANNOTATE_ENABLE_RACE_DETECTION(enable) \ + AnnotateEnableRaceDetection(__FILE__, __LINE__, enable) + + /* ------------------------------------------------------------- + Annotations useful for debugging. */ + + /* Request to trace every access to "address". */ +#define _Py_ANNOTATE_TRACE_MEMORY(address) \ + AnnotateTraceMemory(__FILE__, __LINE__, address) + + /* Report the current thread name to a race detector. */ +#define _Py_ANNOTATE_THREAD_NAME(name) \ + AnnotateThreadName(__FILE__, __LINE__, name) + + /* ------------------------------------------------------------- + Annotations useful when implementing locks. They are not + normally needed by modules that merely use locks. + The "lock" argument is a pointer to the lock object. */ + + /* Report that a lock has been created at address "lock". */ +#define _Py_ANNOTATE_RWLOCK_CREATE(lock) \ + AnnotateRWLockCreate(__FILE__, __LINE__, lock) + + /* Report that the lock at address "lock" is about to be destroyed. */ +#define _Py_ANNOTATE_RWLOCK_DESTROY(lock) \ + AnnotateRWLockDestroy(__FILE__, __LINE__, lock) + + /* Report that the lock at address "lock" has been acquired. + is_w=1 for writer lock, is_w=0 for reader lock. */ +#define _Py_ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ + AnnotateRWLockAcquired(__FILE__, __LINE__, lock, is_w) + + /* Report that the lock at address "lock" is about to be released. */ +#define _Py_ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ + AnnotateRWLockReleased(__FILE__, __LINE__, lock, is_w) + + /* ------------------------------------------------------------- + Annotations useful when implementing barriers. They are not + normally needed by modules that merely use barriers. + The "barrier" argument is a pointer to the barrier object. */ + + /* Report that the "barrier" has been initialized with initial "count". + If 'reinitialization_allowed' is true, initialization is allowed to happen + multiple times w/o calling barrier_destroy() */ +#define _Py_ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) \ + AnnotateBarrierInit(__FILE__, __LINE__, barrier, count, \ + reinitialization_allowed) + + /* Report that we are about to enter barrier_wait("barrier"). */ +#define _Py_ANNOTATE_BARRIER_WAIT_BEFORE(barrier) \ + AnnotateBarrierWaitBefore(__FILE__, __LINE__, barrier) + + /* Report that we just exited barrier_wait("barrier"). */ +#define _Py_ANNOTATE_BARRIER_WAIT_AFTER(barrier) \ + AnnotateBarrierWaitAfter(__FILE__, __LINE__, barrier) + + /* Report that the "barrier" has been destroyed. */ +#define _Py_ANNOTATE_BARRIER_DESTROY(barrier) \ + AnnotateBarrierDestroy(__FILE__, __LINE__, barrier) + + /* ------------------------------------------------------------- + Annotations useful for testing race detectors. */ + + /* Report that we expect a race on the variable at "address". + Use only in unit tests for a race detector. */ +#define _Py_ANNOTATE_EXPECT_RACE(address, description) \ + AnnotateExpectRace(__FILE__, __LINE__, address, description) + + /* A no-op. Insert where you like to test the interceptors. */ +#define _Py_ANNOTATE_NO_OP(arg) \ + AnnotateNoOp(__FILE__, __LINE__, arg) + + /* Force the race detector to flush its state. The actual effect depends on + * the implementation of the detector. */ +#define _Py_ANNOTATE_FLUSH_STATE() \ + AnnotateFlushState(__FILE__, __LINE__) + + +#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */ + +#define _Py_ANNOTATE_RWLOCK_CREATE(lock) /* empty */ +#define _Py_ANNOTATE_RWLOCK_DESTROY(lock) /* empty */ +#define _Py_ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) /* empty */ +#define _Py_ANNOTATE_RWLOCK_RELEASED(lock, is_w) /* empty */ +#define _Py_ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) /* */ +#define _Py_ANNOTATE_BARRIER_WAIT_BEFORE(barrier) /* empty */ +#define _Py_ANNOTATE_BARRIER_WAIT_AFTER(barrier) /* empty */ +#define _Py_ANNOTATE_BARRIER_DESTROY(barrier) /* empty */ +#define _Py_ANNOTATE_CONDVAR_LOCK_WAIT(cv, lock) /* empty */ +#define _Py_ANNOTATE_CONDVAR_WAIT(cv) /* empty */ +#define _Py_ANNOTATE_CONDVAR_SIGNAL(cv) /* empty */ +#define _Py_ANNOTATE_CONDVAR_SIGNAL_ALL(cv) /* empty */ +#define _Py_ANNOTATE_HAPPENS_BEFORE(obj) /* empty */ +#define _Py_ANNOTATE_HAPPENS_AFTER(obj) /* empty */ +#define _Py_ANNOTATE_PUBLISH_MEMORY_RANGE(address, size) /* empty */ +#define _Py_ANNOTATE_UNPUBLISH_MEMORY_RANGE(address, size) /* empty */ +#define _Py_ANNOTATE_SWAP_MEMORY_RANGE(address, size) /* empty */ +#define _Py_ANNOTATE_PCQ_CREATE(pcq) /* empty */ +#define _Py_ANNOTATE_PCQ_DESTROY(pcq) /* empty */ +#define _Py_ANNOTATE_PCQ_PUT(pcq) /* empty */ +#define _Py_ANNOTATE_PCQ_GET(pcq) /* empty */ +#define _Py_ANNOTATE_NEW_MEMORY(address, size) /* empty */ +#define _Py_ANNOTATE_EXPECT_RACE(address, description) /* empty */ +#define _Py_ANNOTATE_BENIGN_RACE(address, description) /* empty */ +#define _Py_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) /* empty */ +#define _Py_ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) /* empty */ +#define _Py_ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) /* empty */ +#define _Py_ANNOTATE_TRACE_MEMORY(arg) /* empty */ +#define _Py_ANNOTATE_THREAD_NAME(name) /* empty */ +#define _Py_ANNOTATE_IGNORE_READS_BEGIN() /* empty */ +#define _Py_ANNOTATE_IGNORE_READS_END() /* empty */ +#define _Py_ANNOTATE_IGNORE_WRITES_BEGIN() /* empty */ +#define _Py_ANNOTATE_IGNORE_WRITES_END() /* empty */ +#define _Py_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() /* empty */ +#define _Py_ANNOTATE_IGNORE_READS_AND_WRITES_END() /* empty */ +#define _Py_ANNOTATE_IGNORE_SYNC_BEGIN() /* empty */ +#define _Py_ANNOTATE_IGNORE_SYNC_END() /* empty */ +#define _Py_ANNOTATE_ENABLE_RACE_DETECTION(enable) /* empty */ +#define _Py_ANNOTATE_NO_OP(arg) /* empty */ +#define _Py_ANNOTATE_FLUSH_STATE() /* empty */ + +#endif /* DYNAMIC_ANNOTATIONS_ENABLED */ + +/* Use the macros above rather than using these functions directly. */ +#ifdef __cplusplus +extern "C" { +#endif +void AnnotateRWLockCreate(const char *file, int line, + const volatile void *lock); +void AnnotateRWLockDestroy(const char *file, int line, + const volatile void *lock); +void AnnotateRWLockAcquired(const char *file, int line, + const volatile void *lock, long is_w); +void AnnotateRWLockReleased(const char *file, int line, + const volatile void *lock, long is_w); +void AnnotateBarrierInit(const char *file, int line, + const volatile void *barrier, long count, + long reinitialization_allowed); +void AnnotateBarrierWaitBefore(const char *file, int line, + const volatile void *barrier); +void AnnotateBarrierWaitAfter(const char *file, int line, + const volatile void *barrier); +void AnnotateBarrierDestroy(const char *file, int line, + const volatile void *barrier); +void AnnotateCondVarWait(const char *file, int line, + const volatile void *cv, + const volatile void *lock); +void AnnotateCondVarSignal(const char *file, int line, + const volatile void *cv); +void AnnotateCondVarSignalAll(const char *file, int line, + const volatile void *cv); +void AnnotatePublishMemoryRange(const char *file, int line, + const volatile void *address, + long size); +void AnnotateUnpublishMemoryRange(const char *file, int line, + const volatile void *address, + long size); +void AnnotatePCQCreate(const char *file, int line, + const volatile void *pcq); +void AnnotatePCQDestroy(const char *file, int line, + const volatile void *pcq); +void AnnotatePCQPut(const char *file, int line, + const volatile void *pcq); +void AnnotatePCQGet(const char *file, int line, + const volatile void *pcq); +void AnnotateNewMemory(const char *file, int line, + const volatile void *address, + long size); +void AnnotateExpectRace(const char *file, int line, + const volatile void *address, + const char *description); +void AnnotateBenignRace(const char *file, int line, + const volatile void *address, + const char *description); +void AnnotateBenignRaceSized(const char *file, int line, + const volatile void *address, + long size, + const char *description); +void AnnotateMutexIsUsedAsCondVar(const char *file, int line, + const volatile void *mu); +void AnnotateTraceMemory(const char *file, int line, + const volatile void *arg); +void AnnotateThreadName(const char *file, int line, + const char *name); +void AnnotateIgnoreReadsBegin(const char *file, int line); +void AnnotateIgnoreReadsEnd(const char *file, int line); +void AnnotateIgnoreWritesBegin(const char *file, int line); +void AnnotateIgnoreWritesEnd(const char *file, int line); +void AnnotateEnableRaceDetection(const char *file, int line, int enable); +void AnnotateNoOp(const char *file, int line, + const volatile void *arg); +void AnnotateFlushState(const char *file, int line); + +/* Return non-zero value if running under valgrind. + + If "valgrind.h" is included into dynamic_annotations.c, + the regular valgrind mechanism will be used. + See http://valgrind.org/docs/manual/manual-core-adv.html about + RUNNING_ON_VALGRIND and other valgrind "client requests". + The file "valgrind.h" may be obtained by doing + svn co svn://svn.valgrind.org/valgrind/trunk/include + + If for some reason you can't use "valgrind.h" or want to fake valgrind, + there are two ways to make this function return non-zero: + - Use environment variable: export RUNNING_ON_VALGRIND=1 + - Make your tool intercept the function RunningOnValgrind() and + change its return value. + */ +int RunningOnValgrind(void); + +#ifdef __cplusplus +} +#endif + +#if DYNAMIC_ANNOTATIONS_ENABLED != 0 && defined(__cplusplus) + + /* _Py_ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads. + + Instead of doing + _Py_ANNOTATE_IGNORE_READS_BEGIN(); + ... = x; + _Py_ANNOTATE_IGNORE_READS_END(); + one can use + ... = _Py_ANNOTATE_UNPROTECTED_READ(x); */ + template + inline T _Py_ANNOTATE_UNPROTECTED_READ(const volatile T &x) { + _Py_ANNOTATE_IGNORE_READS_BEGIN(); + T res = x; + _Py_ANNOTATE_IGNORE_READS_END(); + return res; + } + /* Apply _Py_ANNOTATE_BENIGN_RACE_SIZED to a static variable. */ +#define _Py_ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ + namespace { \ + class static_var ## _annotator { \ + public: \ + static_var ## _annotator() { \ + _Py_ANNOTATE_BENIGN_RACE_SIZED(&static_var, \ + sizeof(static_var), \ + # static_var ": " description); \ + } \ + }; \ + static static_var ## _annotator the ## static_var ## _annotator;\ + } +#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */ + +#define _Py_ANNOTATE_UNPROTECTED_READ(x) (x) +#define _Py_ANNOTATE_BENIGN_RACE_STATIC(static_var, description) /* empty */ + +#endif /* DYNAMIC_ANNOTATIONS_ENABLED */ + +#endif /* __DYNAMIC_ANNOTATIONS_H__ */ diff --git a/scripts/build-windows/py39-libs/include/enumobject.h b/scripts/build-windows/py39-libs/include/enumobject.h new file mode 100644 index 000000000..c14dbfc8c --- /dev/null +++ b/scripts/build-windows/py39-libs/include/enumobject.h @@ -0,0 +1,17 @@ +#ifndef Py_ENUMOBJECT_H +#define Py_ENUMOBJECT_H + +/* Enumerate Object */ + +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PyEnum_Type; +PyAPI_DATA(PyTypeObject) PyReversed_Type; + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_ENUMOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/errcode.h b/scripts/build-windows/py39-libs/include/errcode.h new file mode 100644 index 000000000..790518b8b --- /dev/null +++ b/scripts/build-windows/py39-libs/include/errcode.h @@ -0,0 +1,37 @@ +#ifndef Py_ERRCODE_H +#define Py_ERRCODE_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Error codes passed around between file input, tokenizer, parser and + interpreter. This is necessary so we can turn them into Python + exceptions at a higher level. Note that some errors have a + slightly different meaning when passed from the tokenizer to the + parser than when passed from the parser to the interpreter; e.g. + the parser only returns E_EOF when it hits EOF immediately, and it + never returns E_OK. */ + +#define E_OK 10 /* No error */ +#define E_EOF 11 /* End Of File */ +#define E_INTR 12 /* Interrupted */ +#define E_TOKEN 13 /* Bad token */ +#define E_SYNTAX 14 /* Syntax error */ +#define E_NOMEM 15 /* Ran out of memory */ +#define E_DONE 16 /* Parsing complete */ +#define E_ERROR 17 /* Execution error */ +#define E_TABSPACE 18 /* Inconsistent mixing of tabs and spaces */ +#define E_OVERFLOW 19 /* Node had too many children */ +#define E_TOODEEP 20 /* Too many indentation levels */ +#define E_DEDENT 21 /* No matching outer block for dedent */ +#define E_DECODE 22 /* Error in decoding into Unicode */ +#define E_EOFS 23 /* EOF in triple-quoted string */ +#define E_EOLS 24 /* EOL in single-quoted string */ +#define E_LINECONT 25 /* Unexpected characters after a line continuation */ +#define E_BADSINGLE 27 /* Ill-formed single statement input */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_ERRCODE_H */ diff --git a/scripts/build-windows/py39-libs/include/eval.h b/scripts/build-windows/py39-libs/include/eval.h new file mode 100644 index 000000000..2c1c2d054 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/eval.h @@ -0,0 +1,37 @@ + +/* Interface to execute compiled code */ + +#ifndef Py_EVAL_H +#define Py_EVAL_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(PyObject *) PyEval_EvalCode(PyObject *, PyObject *, PyObject *); + +PyAPI_FUNC(PyObject *) PyEval_EvalCodeEx(PyObject *co, + PyObject *globals, + PyObject *locals, + PyObject *const *args, int argc, + PyObject *const *kwds, int kwdc, + PyObject *const *defs, int defc, + PyObject *kwdefs, PyObject *closure); + +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) _PyEval_EvalCodeWithName( + PyObject *co, + PyObject *globals, PyObject *locals, + PyObject *const *args, Py_ssize_t argcount, + PyObject *const *kwnames, PyObject *const *kwargs, + Py_ssize_t kwcount, int kwstep, + PyObject *const *defs, Py_ssize_t defcount, + PyObject *kwdefs, PyObject *closure, + PyObject *name, PyObject *qualname); + +PyAPI_FUNC(PyObject *) _PyEval_CallTracing(PyObject *func, PyObject *args); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_EVAL_H */ diff --git a/scripts/build-windows/py39-libs/include/exports.h b/scripts/build-windows/py39-libs/include/exports.h new file mode 100644 index 000000000..fc1a5c5ea --- /dev/null +++ b/scripts/build-windows/py39-libs/include/exports.h @@ -0,0 +1,30 @@ +#ifndef Py_EXPORTS_H +#define Py_EXPORTS_H + +#if defined(_WIN32) || defined(__CYGWIN__) + #define Py_IMPORTED_SYMBOL __declspec(dllimport) + #define Py_EXPORTED_SYMBOL __declspec(dllexport) + #define Py_LOCAL_SYMBOL +#else +/* + * If we only ever used gcc >= 5, we could use __has_attribute(visibility) + * as a cross-platform way to determine if visibility is supported. However, + * we may still need to support gcc >= 4, as some Ubuntu LTS and Centos versions + * have 4 < gcc < 5. + */ + #ifndef __has_attribute + #define __has_attribute(x) 0 // Compatibility with non-clang compilers. + #endif + #if (defined(__GNUC__) && (__GNUC__ >= 4)) ||\ + (defined(__clang__) && __has_attribute(visibility)) + #define Py_IMPORTED_SYMBOL __attribute__ ((visibility ("default"))) + #define Py_EXPORTED_SYMBOL __attribute__ ((visibility ("default"))) + #define Py_LOCAL_SYMBOL __attribute__ ((visibility ("hidden"))) + #else + #define Py_IMPORTED_SYMBOL + #define Py_EXPORTED_SYMBOL + #define Py_LOCAL_SYMBOL + #endif +#endif + +#endif /* Py_EXPORTS_H */ diff --git a/scripts/build-windows/py39-libs/include/fileobject.h b/scripts/build-windows/py39-libs/include/fileobject.h new file mode 100644 index 000000000..6ec2994aa --- /dev/null +++ b/scripts/build-windows/py39-libs/include/fileobject.h @@ -0,0 +1,49 @@ +/* File object interface (what's left of it -- see io.py) */ + +#ifndef Py_FILEOBJECT_H +#define Py_FILEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#define PY_STDIOTEXTMODE "b" + +PyAPI_FUNC(PyObject *) PyFile_FromFd(int, const char *, const char *, int, + const char *, const char *, + const char *, int); +PyAPI_FUNC(PyObject *) PyFile_GetLine(PyObject *, int); +PyAPI_FUNC(int) PyFile_WriteObject(PyObject *, PyObject *, int); +PyAPI_FUNC(int) PyFile_WriteString(const char *, PyObject *); +PyAPI_FUNC(int) PyObject_AsFileDescriptor(PyObject *); + +/* The default encoding used by the platform file system APIs + If non-NULL, this is different than the default encoding for strings +*/ +PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 +PyAPI_DATA(const char *) Py_FileSystemDefaultEncodeErrors; +#endif +PyAPI_DATA(int) Py_HasFileSystemDefaultEncoding; + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 +PyAPI_DATA(int) Py_UTF8Mode; +#endif + +/* A routine to check if a file descriptor can be select()-ed. */ +#ifdef _MSC_VER + /* On Windows, any socket fd can be select()-ed, no matter how high */ + #define _PyIsSelectable_fd(FD) (1) +#else + #define _PyIsSelectable_fd(FD) ((unsigned int)(FD) < (unsigned int)FD_SETSIZE) +#endif + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_FILEOBJECT_H +# include "cpython/fileobject.h" +# undef Py_CPYTHON_FILEOBJECT_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_FILEOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/fileutils.h b/scripts/build-windows/py39-libs/include/fileutils.h new file mode 100644 index 000000000..12bd071c4 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/fileutils.h @@ -0,0 +1,30 @@ +#ifndef Py_FILEUTILS_H +#define Py_FILEUTILS_H +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +PyAPI_FUNC(wchar_t *) Py_DecodeLocale( + const char *arg, + size_t *size); + +PyAPI_FUNC(char*) Py_EncodeLocale( + const wchar_t *text, + size_t *error_pos); + +PyAPI_FUNC(char*) _Py_EncodeLocaleRaw( + const wchar_t *text, + size_t *error_pos); +#endif + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_FILEUTILS_H +# include "cpython/fileutils.h" +# undef Py_CPYTHON_FILEUTILS_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_FILEUTILS_H */ diff --git a/scripts/build-windows/py39-libs/include/floatobject.h b/scripts/build-windows/py39-libs/include/floatobject.h new file mode 100644 index 000000000..e994aa8f2 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/floatobject.h @@ -0,0 +1,118 @@ + +/* Float object interface */ + +/* +PyFloatObject represents a (double precision) floating point number. +*/ + +#ifndef Py_FLOATOBJECT_H +#define Py_FLOATOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_LIMITED_API +typedef struct { + PyObject_HEAD + double ob_fval; +} PyFloatObject; +#endif + +PyAPI_DATA(PyTypeObject) PyFloat_Type; + +#define PyFloat_Check(op) PyObject_TypeCheck(op, &PyFloat_Type) +#define PyFloat_CheckExact(op) Py_IS_TYPE(op, &PyFloat_Type) + +#ifdef Py_NAN +#define Py_RETURN_NAN return PyFloat_FromDouble(Py_NAN) +#endif + +#define Py_RETURN_INF(sign) do \ + if (copysign(1., sign) == 1.) { \ + return PyFloat_FromDouble(Py_HUGE_VAL); \ + } else { \ + return PyFloat_FromDouble(-Py_HUGE_VAL); \ + } while(0) + +PyAPI_FUNC(double) PyFloat_GetMax(void); +PyAPI_FUNC(double) PyFloat_GetMin(void); +PyAPI_FUNC(PyObject *) PyFloat_GetInfo(void); + +/* Return Python float from string PyObject. */ +PyAPI_FUNC(PyObject *) PyFloat_FromString(PyObject*); + +/* Return Python float from C double. */ +PyAPI_FUNC(PyObject *) PyFloat_FromDouble(double); + +/* Extract C double from Python float. The macro version trades safety for + speed. */ +PyAPI_FUNC(double) PyFloat_AsDouble(PyObject *); +#ifndef Py_LIMITED_API +#define PyFloat_AS_DOUBLE(op) (((PyFloatObject *)(op))->ob_fval) +#endif + +#ifndef Py_LIMITED_API +/* _PyFloat_{Pack,Unpack}{4,8} + * + * The struct and pickle (at least) modules need an efficient platform- + * independent way to store floating-point values as byte strings. + * The Pack routines produce a string from a C double, and the Unpack + * routines produce a C double from such a string. The suffix (4 or 8) + * specifies the number of bytes in the string. + * + * On platforms that appear to use (see _PyFloat_Init()) IEEE-754 formats + * these functions work by copying bits. On other platforms, the formats the + * 4- byte format is identical to the IEEE-754 single precision format, and + * the 8-byte format to the IEEE-754 double precision format, although the + * packing of INFs and NaNs (if such things exist on the platform) isn't + * handled correctly, and attempting to unpack a string containing an IEEE + * INF or NaN will raise an exception. + * + * On non-IEEE platforms with more precision, or larger dynamic range, than + * 754 supports, not all values can be packed; on non-IEEE platforms with less + * precision, or smaller dynamic range, not all values can be unpacked. What + * happens in such cases is partly accidental (alas). + */ + +/* The pack routines write 2, 4 or 8 bytes, starting at p. le is a bool + * argument, true if you want the string in little-endian format (exponent + * last, at p+1, p+3 or p+7), false if you want big-endian format (exponent + * first, at p). + * Return value: 0 if all is OK, -1 if error (and an exception is + * set, most likely OverflowError). + * There are two problems on non-IEEE platforms: + * 1): What this does is undefined if x is a NaN or infinity. + * 2): -0.0 and +0.0 produce the same string. + */ +PyAPI_FUNC(int) _PyFloat_Pack2(double x, unsigned char *p, int le); +PyAPI_FUNC(int) _PyFloat_Pack4(double x, unsigned char *p, int le); +PyAPI_FUNC(int) _PyFloat_Pack8(double x, unsigned char *p, int le); + +/* The unpack routines read 2, 4 or 8 bytes, starting at p. le is a bool + * argument, true if the string is in little-endian format (exponent + * last, at p+1, p+3 or p+7), false if big-endian (exponent first, at p). + * Return value: The unpacked double. On error, this is -1.0 and + * PyErr_Occurred() is true (and an exception is set, most likely + * OverflowError). Note that on a non-IEEE platform this will refuse + * to unpack a string that represents a NaN or infinity. + */ +PyAPI_FUNC(double) _PyFloat_Unpack2(const unsigned char *p, int le); +PyAPI_FUNC(double) _PyFloat_Unpack4(const unsigned char *p, int le); +PyAPI_FUNC(double) _PyFloat_Unpack8(const unsigned char *p, int le); + +PyAPI_FUNC(void) _PyFloat_DebugMallocStats(FILE* out); + +/* Format the object based on the format_spec, as defined in PEP 3101 + (Advanced String Formatting). */ +PyAPI_FUNC(int) _PyFloat_FormatAdvancedWriter( + _PyUnicodeWriter *writer, + PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, + Py_ssize_t end); +#endif /* Py_LIMITED_API */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_FLOATOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/frameobject.h b/scripts/build-windows/py39-libs/include/frameobject.h new file mode 100644 index 000000000..c118af120 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/frameobject.h @@ -0,0 +1,20 @@ +/* Frame object interface */ + +#ifndef Py_FRAMEOBJECT_H +#define Py_FRAMEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "pyframe.h" + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_FRAMEOBJECT_H +# include "cpython/frameobject.h" +# undef Py_CPYTHON_FRAMEOBJECT_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_FRAMEOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/funcobject.h b/scripts/build-windows/py39-libs/include/funcobject.h new file mode 100644 index 000000000..c5cc9d261 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/funcobject.h @@ -0,0 +1,98 @@ + +/* Function object interface */ +#ifndef Py_LIMITED_API +#ifndef Py_FUNCOBJECT_H +#define Py_FUNCOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Function objects and code objects should not be confused with each other: + * + * Function objects are created by the execution of the 'def' statement. + * They reference a code object in their __code__ attribute, which is a + * purely syntactic object, i.e. nothing more than a compiled version of some + * source code lines. There is one code object per source code "fragment", + * but each code object can be referenced by zero or many function objects + * depending only on how many times the 'def' statement in the source was + * executed so far. + */ + +typedef struct { + PyObject_HEAD + PyObject *func_code; /* A code object, the __code__ attribute */ + PyObject *func_globals; /* A dictionary (other mappings won't do) */ + PyObject *func_defaults; /* NULL or a tuple */ + PyObject *func_kwdefaults; /* NULL or a dict */ + PyObject *func_closure; /* NULL or a tuple of cell objects */ + PyObject *func_doc; /* The __doc__ attribute, can be anything */ + PyObject *func_name; /* The __name__ attribute, a string object */ + PyObject *func_dict; /* The __dict__ attribute, a dict or NULL */ + PyObject *func_weakreflist; /* List of weak references */ + PyObject *func_module; /* The __module__ attribute, can be anything */ + PyObject *func_annotations; /* Annotations, a dict or NULL */ + PyObject *func_qualname; /* The qualified name */ + vectorcallfunc vectorcall; + + /* Invariant: + * func_closure contains the bindings for func_code->co_freevars, so + * PyTuple_Size(func_closure) == PyCode_GetNumFree(func_code) + * (func_closure may be NULL if PyCode_GetNumFree(func_code) == 0). + */ +} PyFunctionObject; + +PyAPI_DATA(PyTypeObject) PyFunction_Type; + +#define PyFunction_Check(op) Py_IS_TYPE(op, &PyFunction_Type) + +PyAPI_FUNC(PyObject *) PyFunction_New(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_NewWithQualName(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetCode(PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetGlobals(PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetModule(PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetDefaults(PyObject *); +PyAPI_FUNC(int) PyFunction_SetDefaults(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetKwDefaults(PyObject *); +PyAPI_FUNC(int) PyFunction_SetKwDefaults(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetClosure(PyObject *); +PyAPI_FUNC(int) PyFunction_SetClosure(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetAnnotations(PyObject *); +PyAPI_FUNC(int) PyFunction_SetAnnotations(PyObject *, PyObject *); + +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) _PyFunction_Vectorcall( + PyObject *func, + PyObject *const *stack, + size_t nargsf, + PyObject *kwnames); +#endif + +/* Macros for direct access to these values. Type checks are *not* + done, so use with care. */ +#define PyFunction_GET_CODE(func) \ + (((PyFunctionObject *)func) -> func_code) +#define PyFunction_GET_GLOBALS(func) \ + (((PyFunctionObject *)func) -> func_globals) +#define PyFunction_GET_MODULE(func) \ + (((PyFunctionObject *)func) -> func_module) +#define PyFunction_GET_DEFAULTS(func) \ + (((PyFunctionObject *)func) -> func_defaults) +#define PyFunction_GET_KW_DEFAULTS(func) \ + (((PyFunctionObject *)func) -> func_kwdefaults) +#define PyFunction_GET_CLOSURE(func) \ + (((PyFunctionObject *)func) -> func_closure) +#define PyFunction_GET_ANNOTATIONS(func) \ + (((PyFunctionObject *)func) -> func_annotations) + +/* The classmethod and staticmethod types lives here, too */ +PyAPI_DATA(PyTypeObject) PyClassMethod_Type; +PyAPI_DATA(PyTypeObject) PyStaticMethod_Type; + +PyAPI_FUNC(PyObject *) PyClassMethod_New(PyObject *); +PyAPI_FUNC(PyObject *) PyStaticMethod_New(PyObject *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_FUNCOBJECT_H */ +#endif /* Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/genericaliasobject.h b/scripts/build-windows/py39-libs/include/genericaliasobject.h new file mode 100644 index 000000000..cf002976b --- /dev/null +++ b/scripts/build-windows/py39-libs/include/genericaliasobject.h @@ -0,0 +1,14 @@ +// Implementation of PEP 585: support list[int] etc. +#ifndef Py_GENERICALIASOBJECT_H +#define Py_GENERICALIASOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(PyObject *) Py_GenericAlias(PyObject *, PyObject *); +PyAPI_DATA(PyTypeObject) Py_GenericAliasType; + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GENERICALIASOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/genobject.h b/scripts/build-windows/py39-libs/include/genobject.h new file mode 100644 index 000000000..8ffd15646 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/genobject.h @@ -0,0 +1,102 @@ + +/* Generator object interface */ + +#ifndef Py_LIMITED_API +#ifndef Py_GENOBJECT_H +#define Py_GENOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "pystate.h" /* _PyErr_StackItem */ + +/* _PyGenObject_HEAD defines the initial segment of generator + and coroutine objects. */ +#define _PyGenObject_HEAD(prefix) \ + PyObject_HEAD \ + /* Note: gi_frame can be NULL if the generator is "finished" */ \ + PyFrameObject *prefix##_frame; \ + /* True if generator is being executed. */ \ + char prefix##_running; \ + /* The code object backing the generator */ \ + PyObject *prefix##_code; \ + /* List of weak reference. */ \ + PyObject *prefix##_weakreflist; \ + /* Name of the generator. */ \ + PyObject *prefix##_name; \ + /* Qualified name of the generator. */ \ + PyObject *prefix##_qualname; \ + _PyErr_StackItem prefix##_exc_state; + +typedef struct { + /* The gi_ prefix is intended to remind of generator-iterator. */ + _PyGenObject_HEAD(gi) +} PyGenObject; + +PyAPI_DATA(PyTypeObject) PyGen_Type; + +#define PyGen_Check(op) PyObject_TypeCheck(op, &PyGen_Type) +#define PyGen_CheckExact(op) Py_IS_TYPE(op, &PyGen_Type) + +PyAPI_FUNC(PyObject *) PyGen_New(PyFrameObject *); +PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(PyFrameObject *, + PyObject *name, PyObject *qualname); +PyAPI_FUNC(int) _PyGen_SetStopIterationValue(PyObject *); +PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **); +PyAPI_FUNC(PyObject *) _PyGen_Send(PyGenObject *, PyObject *); +PyObject *_PyGen_yf(PyGenObject *); +PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self); + +#ifndef Py_LIMITED_API +typedef struct { + _PyGenObject_HEAD(cr) + PyObject *cr_origin; +} PyCoroObject; + +PyAPI_DATA(PyTypeObject) PyCoro_Type; +PyAPI_DATA(PyTypeObject) _PyCoroWrapper_Type; + +#define PyCoro_CheckExact(op) Py_IS_TYPE(op, &PyCoro_Type) +PyObject *_PyCoro_GetAwaitableIter(PyObject *o); +PyAPI_FUNC(PyObject *) PyCoro_New(PyFrameObject *, + PyObject *name, PyObject *qualname); + +/* Asynchronous Generators */ + +typedef struct { + _PyGenObject_HEAD(ag) + PyObject *ag_finalizer; + + /* Flag is set to 1 when hooks set up by sys.set_asyncgen_hooks + were called on the generator, to avoid calling them more + than once. */ + int ag_hooks_inited; + + /* Flag is set to 1 when aclose() is called for the first time, or + when a StopAsyncIteration exception is raised. */ + int ag_closed; + + int ag_running_async; +} PyAsyncGenObject; + +PyAPI_DATA(PyTypeObject) PyAsyncGen_Type; +PyAPI_DATA(PyTypeObject) _PyAsyncGenASend_Type; +PyAPI_DATA(PyTypeObject) _PyAsyncGenWrappedValue_Type; +PyAPI_DATA(PyTypeObject) _PyAsyncGenAThrow_Type; + +PyAPI_FUNC(PyObject *) PyAsyncGen_New(PyFrameObject *, + PyObject *name, PyObject *qualname); + +#define PyAsyncGen_CheckExact(op) Py_IS_TYPE(op, &PyAsyncGen_Type) + +PyObject *_PyAsyncGenValueWrapperNew(PyObject *); + +#endif + +#undef _PyGenObject_HEAD + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GENOBJECT_H */ +#endif /* Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/graminit.h b/scripts/build-windows/py39-libs/include/graminit.h new file mode 100644 index 000000000..d1027b7a7 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/graminit.h @@ -0,0 +1,94 @@ +/* Generated by Parser/pgen */ + +#define single_input 256 +#define file_input 257 +#define eval_input 258 +#define decorator 259 +#define decorators 260 +#define decorated 261 +#define async_funcdef 262 +#define funcdef 263 +#define parameters 264 +#define typedargslist 265 +#define tfpdef 266 +#define varargslist 267 +#define vfpdef 268 +#define stmt 269 +#define simple_stmt 270 +#define small_stmt 271 +#define expr_stmt 272 +#define annassign 273 +#define testlist_star_expr 274 +#define augassign 275 +#define del_stmt 276 +#define pass_stmt 277 +#define flow_stmt 278 +#define break_stmt 279 +#define continue_stmt 280 +#define return_stmt 281 +#define yield_stmt 282 +#define raise_stmt 283 +#define import_stmt 284 +#define import_name 285 +#define import_from 286 +#define import_as_name 287 +#define dotted_as_name 288 +#define import_as_names 289 +#define dotted_as_names 290 +#define dotted_name 291 +#define global_stmt 292 +#define nonlocal_stmt 293 +#define assert_stmt 294 +#define compound_stmt 295 +#define async_stmt 296 +#define if_stmt 297 +#define while_stmt 298 +#define for_stmt 299 +#define try_stmt 300 +#define with_stmt 301 +#define with_item 302 +#define except_clause 303 +#define suite 304 +#define namedexpr_test 305 +#define test 306 +#define test_nocond 307 +#define lambdef 308 +#define lambdef_nocond 309 +#define or_test 310 +#define and_test 311 +#define not_test 312 +#define comparison 313 +#define comp_op 314 +#define star_expr 315 +#define expr 316 +#define xor_expr 317 +#define and_expr 318 +#define shift_expr 319 +#define arith_expr 320 +#define term 321 +#define factor 322 +#define power 323 +#define atom_expr 324 +#define atom 325 +#define testlist_comp 326 +#define trailer 327 +#define subscriptlist 328 +#define subscript 329 +#define sliceop 330 +#define exprlist 331 +#define testlist 332 +#define dictorsetmaker 333 +#define classdef 334 +#define arglist 335 +#define argument 336 +#define comp_iter 337 +#define sync_comp_for 338 +#define comp_for 339 +#define comp_if 340 +#define encoding_decl 341 +#define yield_expr 342 +#define yield_arg 343 +#define func_body_suite 344 +#define func_type_input 345 +#define func_type 346 +#define typelist 347 diff --git a/scripts/build-windows/py39-libs/include/grammar.h b/scripts/build-windows/py39-libs/include/grammar.h new file mode 100644 index 000000000..4b66b1e9b --- /dev/null +++ b/scripts/build-windows/py39-libs/include/grammar.h @@ -0,0 +1,77 @@ + +/* Grammar interface */ + +#ifndef Py_GRAMMAR_H +#define Py_GRAMMAR_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "bitset.h" /* Sigh... */ + +/* A label of an arc */ + +typedef struct { + int lb_type; + const char *lb_str; +} label; + +#define EMPTY 0 /* Label number 0 is by definition the empty label */ + +/* A list of labels */ + +typedef struct { + int ll_nlabels; + const label *ll_label; +} labellist; + +/* An arc from one state to another */ + +typedef struct { + short a_lbl; /* Label of this arc */ + short a_arrow; /* State where this arc goes to */ +} arc; + +/* A state in a DFA */ + +typedef struct { + int s_narcs; + const arc *s_arc; /* Array of arcs */ + + /* Optional accelerators */ + int s_lower; /* Lowest label index */ + int s_upper; /* Highest label index */ + int *s_accel; /* Accelerator */ + int s_accept; /* Nonzero for accepting state */ +} state; + +/* A DFA */ + +typedef struct { + int d_type; /* Non-terminal this represents */ + char *d_name; /* For printing */ + int d_nstates; + state *d_state; /* Array of states */ + bitset d_first; +} dfa; + +/* A grammar */ + +typedef struct { + int g_ndfas; + const dfa *g_dfa; /* Array of DFAs */ + const labellist g_ll; + int g_start; /* Start symbol of the grammar */ + int g_accel; /* Set if accelerators present */ +} grammar; + +/* FUNCTIONS */ +const dfa *PyGrammar_FindDFA(grammar *g, int type); +const char *PyGrammar_LabelRepr(label *lb); +void PyGrammar_AddAccelerators(grammar *g); +void PyGrammar_RemoveAccelerators(grammar *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GRAMMAR_H */ diff --git a/scripts/build-windows/py39-libs/include/import.h b/scripts/build-windows/py39-libs/include/import.h new file mode 100644 index 000000000..aeef3efd0 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/import.h @@ -0,0 +1,98 @@ +/* Module definition and import interface */ + +#ifndef Py_IMPORT_H +#define Py_IMPORT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(long) PyImport_GetMagicNumber(void); +PyAPI_FUNC(const char *) PyImport_GetMagicTag(void); +PyAPI_FUNC(PyObject *) PyImport_ExecCodeModule( + const char *name, /* UTF-8 encoded string */ + PyObject *co + ); +PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleEx( + const char *name, /* UTF-8 encoded string */ + PyObject *co, + const char *pathname /* decoded from the filesystem encoding */ + ); +PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleWithPathnames( + const char *name, /* UTF-8 encoded string */ + PyObject *co, + const char *pathname, /* decoded from the filesystem encoding */ + const char *cpathname /* decoded from the filesystem encoding */ + ); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleObject( + PyObject *name, + PyObject *co, + PyObject *pathname, + PyObject *cpathname + ); +#endif +PyAPI_FUNC(PyObject *) PyImport_GetModuleDict(void); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 +PyAPI_FUNC(PyObject *) PyImport_GetModule(PyObject *name); +#endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(PyObject *) PyImport_AddModuleObject( + PyObject *name + ); +#endif +PyAPI_FUNC(PyObject *) PyImport_AddModule( + const char *name /* UTF-8 encoded string */ + ); +PyAPI_FUNC(PyObject *) PyImport_ImportModule( + const char *name /* UTF-8 encoded string */ + ); +PyAPI_FUNC(PyObject *) PyImport_ImportModuleNoBlock( + const char *name /* UTF-8 encoded string */ + ); +PyAPI_FUNC(PyObject *) PyImport_ImportModuleLevel( + const char *name, /* UTF-8 encoded string */ + PyObject *globals, + PyObject *locals, + PyObject *fromlist, + int level + ); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +PyAPI_FUNC(PyObject *) PyImport_ImportModuleLevelObject( + PyObject *name, + PyObject *globals, + PyObject *locals, + PyObject *fromlist, + int level + ); +#endif + +#define PyImport_ImportModuleEx(n, g, l, f) \ + PyImport_ImportModuleLevel(n, g, l, f, 0) + +PyAPI_FUNC(PyObject *) PyImport_GetImporter(PyObject *path); +PyAPI_FUNC(PyObject *) PyImport_Import(PyObject *name); +PyAPI_FUNC(PyObject *) PyImport_ReloadModule(PyObject *m); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(int) PyImport_ImportFrozenModuleObject( + PyObject *name + ); +#endif +PyAPI_FUNC(int) PyImport_ImportFrozenModule( + const char *name /* UTF-8 encoded string */ + ); + +PyAPI_FUNC(int) PyImport_AppendInittab( + const char *name, /* ASCII encoded string */ + PyObject* (*initfunc)(void) + ); + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_IMPORT_H +# include "cpython/import.h" +# undef Py_CPYTHON_IMPORT_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_IMPORT_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pegen_interface.h b/scripts/build-windows/py39-libs/include/internal/pegen_interface.h new file mode 100644 index 000000000..ee4c77ec0 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pegen_interface.h @@ -0,0 +1,46 @@ +#ifndef Py_PEGENINTERFACE +#define Py_PEGENINTERFACE +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include "Python.h" +#include "Python-ast.h" + +PyAPI_FUNC(mod_ty) PyPegen_ASTFromString( + const char *str, + const char *filename, + int mode, + PyCompilerFlags *flags, + PyArena *arena); +PyAPI_FUNC(mod_ty) PyPegen_ASTFromStringObject( + const char *str, + PyObject* filename, + int mode, + PyCompilerFlags *flags, + PyArena *arena); +PyAPI_FUNC(mod_ty) PyPegen_ASTFromFileObject( + FILE *fp, + PyObject *filename_ob, + int mode, + const char *enc, + const char *ps1, + const char *ps2, + PyCompilerFlags *flags, + int *errcode, + PyArena *arena); +PyAPI_FUNC(mod_ty) PyPegen_ASTFromFilename( + const char *filename, + int mode, + PyCompilerFlags *flags, + PyArena *arena); + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PEGENINTERFACE*/ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_abstract.h b/scripts/build-windows/py39-libs/include/internal/pycore_abstract.h new file mode 100644 index 000000000..b791bf243 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_abstract.h @@ -0,0 +1,22 @@ +#ifndef Py_INTERNAL_ABSTRACT_H +#define Py_INTERNAL_ABSTRACT_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +// Fast inlined version of PyIndex_Check() +static inline int +_PyIndex_Check(PyObject *obj) +{ + PyNumberMethods *tp_as_number = Py_TYPE(obj)->tp_as_number; + return (tp_as_number != NULL && tp_as_number->nb_index != NULL); +} + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_ABSTRACT_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_accu.h b/scripts/build-windows/py39-libs/include/internal/pycore_accu.h new file mode 100644 index 000000000..d346222e4 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_accu.h @@ -0,0 +1,39 @@ +#ifndef Py_LIMITED_API +#ifndef Py_INTERNAL_ACCU_H +#define Py_INTERNAL_ACCU_H +#ifdef __cplusplus +extern "C" { +#endif + +/*** This is a private API for use by the interpreter and the stdlib. + *** Its definition may be changed or removed at any moment. + ***/ + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +/* + * A two-level accumulator of unicode objects that avoids both the overhead + * of keeping a huge number of small separate objects, and the quadratic + * behaviour of using a naive repeated concatenation scheme. + */ + +#undef small /* defined by some Windows headers */ + +typedef struct { + PyObject *large; /* A list of previously accumulated large strings */ + PyObject *small; /* Pending small strings */ +} _PyAccu; + +PyAPI_FUNC(int) _PyAccu_Init(_PyAccu *acc); +PyAPI_FUNC(int) _PyAccu_Accumulate(_PyAccu *acc, PyObject *unicode); +PyAPI_FUNC(PyObject *) _PyAccu_FinishAsList(_PyAccu *acc); +PyAPI_FUNC(PyObject *) _PyAccu_Finish(_PyAccu *acc); +PyAPI_FUNC(void) _PyAccu_Destroy(_PyAccu *acc); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_ACCU_H */ +#endif /* !Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_atomic.h b/scripts/build-windows/py39-libs/include/internal/pycore_atomic.h new file mode 100644 index 000000000..1d5c56216 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_atomic.h @@ -0,0 +1,557 @@ +#ifndef Py_ATOMIC_H +#define Py_ATOMIC_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include "dynamic_annotations.h" /* _Py_ANNOTATE_MEMORY_ORDER */ +#include "pyconfig.h" + +#if defined(HAVE_STD_ATOMIC) +#include +#endif + + +#if defined(_MSC_VER) +#include +#if defined(_M_IX86) || defined(_M_X64) +# include +#endif +#endif + +/* This is modeled after the atomics interface from C1x, according to + * the draft at + * http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1425.pdf. + * Operations and types are named the same except with a _Py_ prefix + * and have the same semantics. + * + * Beware, the implementations here are deep magic. + */ + +#if defined(HAVE_STD_ATOMIC) + +typedef enum _Py_memory_order { + _Py_memory_order_relaxed = memory_order_relaxed, + _Py_memory_order_acquire = memory_order_acquire, + _Py_memory_order_release = memory_order_release, + _Py_memory_order_acq_rel = memory_order_acq_rel, + _Py_memory_order_seq_cst = memory_order_seq_cst +} _Py_memory_order; + +typedef struct _Py_atomic_address { + atomic_uintptr_t _value; +} _Py_atomic_address; + +typedef struct _Py_atomic_int { + atomic_int _value; +} _Py_atomic_int; + +#define _Py_atomic_signal_fence(/*memory_order*/ ORDER) \ + atomic_signal_fence(ORDER) + +#define _Py_atomic_thread_fence(/*memory_order*/ ORDER) \ + atomic_thread_fence(ORDER) + +#define _Py_atomic_store_explicit(ATOMIC_VAL, NEW_VAL, ORDER) \ + atomic_store_explicit(&((ATOMIC_VAL)->_value), NEW_VAL, ORDER) + +#define _Py_atomic_load_explicit(ATOMIC_VAL, ORDER) \ + atomic_load_explicit(&((ATOMIC_VAL)->_value), ORDER) + +/* Use builtin atomic operations in GCC >= 4.7 */ +#elif defined(HAVE_BUILTIN_ATOMIC) + +typedef enum _Py_memory_order { + _Py_memory_order_relaxed = __ATOMIC_RELAXED, + _Py_memory_order_acquire = __ATOMIC_ACQUIRE, + _Py_memory_order_release = __ATOMIC_RELEASE, + _Py_memory_order_acq_rel = __ATOMIC_ACQ_REL, + _Py_memory_order_seq_cst = __ATOMIC_SEQ_CST +} _Py_memory_order; + +typedef struct _Py_atomic_address { + uintptr_t _value; +} _Py_atomic_address; + +typedef struct _Py_atomic_int { + int _value; +} _Py_atomic_int; + +#define _Py_atomic_signal_fence(/*memory_order*/ ORDER) \ + __atomic_signal_fence(ORDER) + +#define _Py_atomic_thread_fence(/*memory_order*/ ORDER) \ + __atomic_thread_fence(ORDER) + +#define _Py_atomic_store_explicit(ATOMIC_VAL, NEW_VAL, ORDER) \ + (assert((ORDER) == __ATOMIC_RELAXED \ + || (ORDER) == __ATOMIC_SEQ_CST \ + || (ORDER) == __ATOMIC_RELEASE), \ + __atomic_store_n(&((ATOMIC_VAL)->_value), NEW_VAL, ORDER)) + +#define _Py_atomic_load_explicit(ATOMIC_VAL, ORDER) \ + (assert((ORDER) == __ATOMIC_RELAXED \ + || (ORDER) == __ATOMIC_SEQ_CST \ + || (ORDER) == __ATOMIC_ACQUIRE \ + || (ORDER) == __ATOMIC_CONSUME), \ + __atomic_load_n(&((ATOMIC_VAL)->_value), ORDER)) + +/* Only support GCC (for expression statements) and x86 (for simple + * atomic semantics) and MSVC x86/x64/ARM */ +#elif defined(__GNUC__) && (defined(__i386__) || defined(__amd64)) +typedef enum _Py_memory_order { + _Py_memory_order_relaxed, + _Py_memory_order_acquire, + _Py_memory_order_release, + _Py_memory_order_acq_rel, + _Py_memory_order_seq_cst +} _Py_memory_order; + +typedef struct _Py_atomic_address { + uintptr_t _value; +} _Py_atomic_address; + +typedef struct _Py_atomic_int { + int _value; +} _Py_atomic_int; + + +static __inline__ void +_Py_atomic_signal_fence(_Py_memory_order order) +{ + if (order != _Py_memory_order_relaxed) + __asm__ volatile("":::"memory"); +} + +static __inline__ void +_Py_atomic_thread_fence(_Py_memory_order order) +{ + if (order != _Py_memory_order_relaxed) + __asm__ volatile("mfence":::"memory"); +} + +/* Tell the race checker about this operation's effects. */ +static __inline__ void +_Py_ANNOTATE_MEMORY_ORDER(const volatile void *address, _Py_memory_order order) +{ + (void)address; /* shut up -Wunused-parameter */ + switch(order) { + case _Py_memory_order_release: + case _Py_memory_order_acq_rel: + case _Py_memory_order_seq_cst: + _Py_ANNOTATE_HAPPENS_BEFORE(address); + break; + case _Py_memory_order_relaxed: + case _Py_memory_order_acquire: + break; + } + switch(order) { + case _Py_memory_order_acquire: + case _Py_memory_order_acq_rel: + case _Py_memory_order_seq_cst: + _Py_ANNOTATE_HAPPENS_AFTER(address); + break; + case _Py_memory_order_relaxed: + case _Py_memory_order_release: + break; + } +} + +#define _Py_atomic_store_explicit(ATOMIC_VAL, NEW_VAL, ORDER) \ + __extension__ ({ \ + __typeof__(ATOMIC_VAL) atomic_val = ATOMIC_VAL; \ + __typeof__(atomic_val->_value) new_val = NEW_VAL;\ + volatile __typeof__(new_val) *volatile_data = &atomic_val->_value; \ + _Py_memory_order order = ORDER; \ + _Py_ANNOTATE_MEMORY_ORDER(atomic_val, order); \ + \ + /* Perform the operation. */ \ + _Py_ANNOTATE_IGNORE_WRITES_BEGIN(); \ + switch(order) { \ + case _Py_memory_order_release: \ + _Py_atomic_signal_fence(_Py_memory_order_release); \ + /* fallthrough */ \ + case _Py_memory_order_relaxed: \ + *volatile_data = new_val; \ + break; \ + \ + case _Py_memory_order_acquire: \ + case _Py_memory_order_acq_rel: \ + case _Py_memory_order_seq_cst: \ + __asm__ volatile("xchg %0, %1" \ + : "+r"(new_val) \ + : "m"(atomic_val->_value) \ + : "memory"); \ + break; \ + } \ + _Py_ANNOTATE_IGNORE_WRITES_END(); \ + }) + +#define _Py_atomic_load_explicit(ATOMIC_VAL, ORDER) \ + __extension__ ({ \ + __typeof__(ATOMIC_VAL) atomic_val = ATOMIC_VAL; \ + __typeof__(atomic_val->_value) result; \ + volatile __typeof__(result) *volatile_data = &atomic_val->_value; \ + _Py_memory_order order = ORDER; \ + _Py_ANNOTATE_MEMORY_ORDER(atomic_val, order); \ + \ + /* Perform the operation. */ \ + _Py_ANNOTATE_IGNORE_READS_BEGIN(); \ + switch(order) { \ + case _Py_memory_order_release: \ + case _Py_memory_order_acq_rel: \ + case _Py_memory_order_seq_cst: \ + /* Loads on x86 are not releases by default, so need a */ \ + /* thread fence. */ \ + _Py_atomic_thread_fence(_Py_memory_order_release); \ + break; \ + default: \ + /* No fence */ \ + break; \ + } \ + result = *volatile_data; \ + switch(order) { \ + case _Py_memory_order_acquire: \ + case _Py_memory_order_acq_rel: \ + case _Py_memory_order_seq_cst: \ + /* Loads on x86 are automatically acquire operations so */ \ + /* can get by with just a compiler fence. */ \ + _Py_atomic_signal_fence(_Py_memory_order_acquire); \ + break; \ + default: \ + /* No fence */ \ + break; \ + } \ + _Py_ANNOTATE_IGNORE_READS_END(); \ + result; \ + }) + +#elif defined(_MSC_VER) +/* _Interlocked* functions provide a full memory barrier and are therefore + enough for acq_rel and seq_cst. If the HLE variants aren't available + in hardware they will fall back to a full memory barrier as well. + + This might affect performance but likely only in some very specific and + hard to meassure scenario. +*/ +#if defined(_M_IX86) || defined(_M_X64) +typedef enum _Py_memory_order { + _Py_memory_order_relaxed, + _Py_memory_order_acquire, + _Py_memory_order_release, + _Py_memory_order_acq_rel, + _Py_memory_order_seq_cst +} _Py_memory_order; + +typedef struct _Py_atomic_address { + volatile uintptr_t _value; +} _Py_atomic_address; + +typedef struct _Py_atomic_int { + volatile int _value; +} _Py_atomic_int; + + +#if defined(_M_X64) +#define _Py_atomic_store_64bit(ATOMIC_VAL, NEW_VAL, ORDER) \ + switch (ORDER) { \ + case _Py_memory_order_acquire: \ + _InterlockedExchange64_HLEAcquire((__int64 volatile*)&((ATOMIC_VAL)->_value), (__int64)(NEW_VAL)); \ + break; \ + case _Py_memory_order_release: \ + _InterlockedExchange64_HLERelease((__int64 volatile*)&((ATOMIC_VAL)->_value), (__int64)(NEW_VAL)); \ + break; \ + default: \ + _InterlockedExchange64((__int64 volatile*)&((ATOMIC_VAL)->_value), (__int64)(NEW_VAL)); \ + break; \ + } +#else +#define _Py_atomic_store_64bit(ATOMIC_VAL, NEW_VAL, ORDER) ((void)0); +#endif + +#define _Py_atomic_store_32bit(ATOMIC_VAL, NEW_VAL, ORDER) \ + switch (ORDER) { \ + case _Py_memory_order_acquire: \ + _InterlockedExchange_HLEAcquire((volatile long*)&((ATOMIC_VAL)->_value), (int)(NEW_VAL)); \ + break; \ + case _Py_memory_order_release: \ + _InterlockedExchange_HLERelease((volatile long*)&((ATOMIC_VAL)->_value), (int)(NEW_VAL)); \ + break; \ + default: \ + _InterlockedExchange((volatile long*)&((ATOMIC_VAL)->_value), (int)(NEW_VAL)); \ + break; \ + } + +#if defined(_M_X64) +/* This has to be an intptr_t for now. + gil_created() uses -1 as a sentinel value, if this returns + a uintptr_t it will do an unsigned compare and crash +*/ +inline intptr_t _Py_atomic_load_64bit_impl(volatile uintptr_t* value, int order) { + __int64 old; + switch (order) { + case _Py_memory_order_acquire: + { + do { + old = *value; + } while(_InterlockedCompareExchange64_HLEAcquire((volatile __int64*)value, old, old) != old); + break; + } + case _Py_memory_order_release: + { + do { + old = *value; + } while(_InterlockedCompareExchange64_HLERelease((volatile __int64*)value, old, old) != old); + break; + } + case _Py_memory_order_relaxed: + old = *value; + break; + default: + { + do { + old = *value; + } while(_InterlockedCompareExchange64((volatile __int64*)value, old, old) != old); + break; + } + } + return old; +} + +#define _Py_atomic_load_64bit(ATOMIC_VAL, ORDER) \ + _Py_atomic_load_64bit_impl((volatile uintptr_t*)&((ATOMIC_VAL)->_value), (ORDER)) + +#else +#define _Py_atomic_load_64bit(ATOMIC_VAL, ORDER) ((ATOMIC_VAL)->_value) +#endif + +inline int _Py_atomic_load_32bit_impl(volatile int* value, int order) { + long old; + switch (order) { + case _Py_memory_order_acquire: + { + do { + old = *value; + } while(_InterlockedCompareExchange_HLEAcquire((volatile long*)value, old, old) != old); + break; + } + case _Py_memory_order_release: + { + do { + old = *value; + } while(_InterlockedCompareExchange_HLERelease((volatile long*)value, old, old) != old); + break; + } + case _Py_memory_order_relaxed: + old = *value; + break; + default: + { + do { + old = *value; + } while(_InterlockedCompareExchange((volatile long*)value, old, old) != old); + break; + } + } + return old; +} + +#define _Py_atomic_load_32bit(ATOMIC_VAL, ORDER) \ + _Py_atomic_load_32bit_impl((volatile int*)&((ATOMIC_VAL)->_value), (ORDER)) + +#define _Py_atomic_store_explicit(ATOMIC_VAL, NEW_VAL, ORDER) \ + if (sizeof((ATOMIC_VAL)->_value) == 8) { \ + _Py_atomic_store_64bit((ATOMIC_VAL), NEW_VAL, ORDER) } else { \ + _Py_atomic_store_32bit((ATOMIC_VAL), NEW_VAL, ORDER) } + +#define _Py_atomic_load_explicit(ATOMIC_VAL, ORDER) \ + ( \ + sizeof((ATOMIC_VAL)->_value) == 8 ? \ + _Py_atomic_load_64bit((ATOMIC_VAL), ORDER) : \ + _Py_atomic_load_32bit((ATOMIC_VAL), ORDER) \ + ) +#elif defined(_M_ARM) || defined(_M_ARM64) +typedef enum _Py_memory_order { + _Py_memory_order_relaxed, + _Py_memory_order_acquire, + _Py_memory_order_release, + _Py_memory_order_acq_rel, + _Py_memory_order_seq_cst +} _Py_memory_order; + +typedef struct _Py_atomic_address { + volatile uintptr_t _value; +} _Py_atomic_address; + +typedef struct _Py_atomic_int { + volatile int _value; +} _Py_atomic_int; + + +#if defined(_M_ARM64) +#define _Py_atomic_store_64bit(ATOMIC_VAL, NEW_VAL, ORDER) \ + switch (ORDER) { \ + case _Py_memory_order_acquire: \ + _InterlockedExchange64_acq((__int64 volatile*)&((ATOMIC_VAL)->_value), (__int64)NEW_VAL); \ + break; \ + case _Py_memory_order_release: \ + _InterlockedExchange64_rel((__int64 volatile*)&((ATOMIC_VAL)->_value), (__int64)NEW_VAL); \ + break; \ + default: \ + _InterlockedExchange64((__int64 volatile*)&((ATOMIC_VAL)->_value), (__int64)NEW_VAL); \ + break; \ + } +#else +#define _Py_atomic_store_64bit(ATOMIC_VAL, NEW_VAL, ORDER) ((void)0); +#endif + +#define _Py_atomic_store_32bit(ATOMIC_VAL, NEW_VAL, ORDER) \ + switch (ORDER) { \ + case _Py_memory_order_acquire: \ + _InterlockedExchange_acq((volatile long*)&((ATOMIC_VAL)->_value), (int)NEW_VAL); \ + break; \ + case _Py_memory_order_release: \ + _InterlockedExchange_rel((volatile long*)&((ATOMIC_VAL)->_value), (int)NEW_VAL); \ + break; \ + default: \ + _InterlockedExchange((volatile long*)&((ATOMIC_VAL)->_value), (int)NEW_VAL); \ + break; \ + } + +#if defined(_M_ARM64) +/* This has to be an intptr_t for now. + gil_created() uses -1 as a sentinel value, if this returns + a uintptr_t it will do an unsigned compare and crash +*/ +inline intptr_t _Py_atomic_load_64bit_impl(volatile uintptr_t* value, int order) { + uintptr_t old; + switch (order) { + case _Py_memory_order_acquire: + { + do { + old = *value; + } while(_InterlockedCompareExchange64_acq(value, old, old) != old); + break; + } + case _Py_memory_order_release: + { + do { + old = *value; + } while(_InterlockedCompareExchange64_rel(value, old, old) != old); + break; + } + case _Py_memory_order_relaxed: + old = *value; + break; + default: + { + do { + old = *value; + } while(_InterlockedCompareExchange64(value, old, old) != old); + break; + } + } + return old; +} + +#define _Py_atomic_load_64bit(ATOMIC_VAL, ORDER) \ + _Py_atomic_load_64bit_impl((volatile uintptr_t*)&((ATOMIC_VAL)->_value), (ORDER)) + +#else +#define _Py_atomic_load_64bit(ATOMIC_VAL, ORDER) ((ATOMIC_VAL)->_value) +#endif + +inline int _Py_atomic_load_32bit_impl(volatile int* value, int order) { + int old; + switch (order) { + case _Py_memory_order_acquire: + { + do { + old = *value; + } while(_InterlockedCompareExchange_acq(value, old, old) != old); + break; + } + case _Py_memory_order_release: + { + do { + old = *value; + } while(_InterlockedCompareExchange_rel(value, old, old) != old); + break; + } + case _Py_memory_order_relaxed: + old = *value; + break; + default: + { + do { + old = *value; + } while(_InterlockedCompareExchange(value, old, old) != old); + break; + } + } + return old; +} + +#define _Py_atomic_load_32bit(ATOMIC_VAL, ORDER) \ + _Py_atomic_load_32bit_impl((volatile int*)&((ATOMIC_VAL)->_value), (ORDER)) + +#define _Py_atomic_store_explicit(ATOMIC_VAL, NEW_VAL, ORDER) \ + if (sizeof((ATOMIC_VAL)->_value) == 8) { \ + _Py_atomic_store_64bit((ATOMIC_VAL), (NEW_VAL), (ORDER)) } else { \ + _Py_atomic_store_32bit((ATOMIC_VAL), (NEW_VAL), (ORDER)) } + +#define _Py_atomic_load_explicit(ATOMIC_VAL, ORDER) \ + ( \ + sizeof((ATOMIC_VAL)->_value) == 8 ? \ + _Py_atomic_load_64bit((ATOMIC_VAL), (ORDER)) : \ + _Py_atomic_load_32bit((ATOMIC_VAL), (ORDER)) \ + ) +#endif +#else /* !gcc x86 !_msc_ver */ +typedef enum _Py_memory_order { + _Py_memory_order_relaxed, + _Py_memory_order_acquire, + _Py_memory_order_release, + _Py_memory_order_acq_rel, + _Py_memory_order_seq_cst +} _Py_memory_order; + +typedef struct _Py_atomic_address { + uintptr_t _value; +} _Py_atomic_address; + +typedef struct _Py_atomic_int { + int _value; +} _Py_atomic_int; +/* Fall back to other compilers and processors by assuming that simple + volatile accesses are atomic. This is false, so people should port + this. */ +#define _Py_atomic_signal_fence(/*memory_order*/ ORDER) ((void)0) +#define _Py_atomic_thread_fence(/*memory_order*/ ORDER) ((void)0) +#define _Py_atomic_store_explicit(ATOMIC_VAL, NEW_VAL, ORDER) \ + ((ATOMIC_VAL)->_value = NEW_VAL) +#define _Py_atomic_load_explicit(ATOMIC_VAL, ORDER) \ + ((ATOMIC_VAL)->_value) +#endif + +/* Standardized shortcuts. */ +#define _Py_atomic_store(ATOMIC_VAL, NEW_VAL) \ + _Py_atomic_store_explicit((ATOMIC_VAL), (NEW_VAL), _Py_memory_order_seq_cst) +#define _Py_atomic_load(ATOMIC_VAL) \ + _Py_atomic_load_explicit((ATOMIC_VAL), _Py_memory_order_seq_cst) + +/* Python-local extensions */ + +#define _Py_atomic_store_relaxed(ATOMIC_VAL, NEW_VAL) \ + _Py_atomic_store_explicit((ATOMIC_VAL), (NEW_VAL), _Py_memory_order_relaxed) +#define _Py_atomic_load_relaxed(ATOMIC_VAL) \ + _Py_atomic_load_explicit((ATOMIC_VAL), _Py_memory_order_relaxed) + +#ifdef __cplusplus +} +#endif +#endif /* Py_ATOMIC_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_bytes_methods.h b/scripts/build-windows/py39-libs/include/internal/pycore_bytes_methods.h new file mode 100644 index 000000000..11e8ab20e --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_bytes_methods.h @@ -0,0 +1,73 @@ +#ifndef Py_LIMITED_API +#ifndef Py_BYTES_CTYPE_H +#define Py_BYTES_CTYPE_H + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +/* + * The internal implementation behind PyBytes (bytes) and PyByteArray (bytearray) + * methods of the given names, they operate on ASCII byte strings. + */ +extern PyObject* _Py_bytes_isspace(const char *cptr, Py_ssize_t len); +extern PyObject* _Py_bytes_isalpha(const char *cptr, Py_ssize_t len); +extern PyObject* _Py_bytes_isalnum(const char *cptr, Py_ssize_t len); +extern PyObject* _Py_bytes_isascii(const char *cptr, Py_ssize_t len); +extern PyObject* _Py_bytes_isdigit(const char *cptr, Py_ssize_t len); +extern PyObject* _Py_bytes_islower(const char *cptr, Py_ssize_t len); +extern PyObject* _Py_bytes_isupper(const char *cptr, Py_ssize_t len); +extern PyObject* _Py_bytes_istitle(const char *cptr, Py_ssize_t len); + +/* These store their len sized answer in the given preallocated *result arg. */ +extern void _Py_bytes_lower(char *result, const char *cptr, Py_ssize_t len); +extern void _Py_bytes_upper(char *result, const char *cptr, Py_ssize_t len); +extern void _Py_bytes_title(char *result, const char *s, Py_ssize_t len); +extern void _Py_bytes_capitalize(char *result, const char *s, Py_ssize_t len); +extern void _Py_bytes_swapcase(char *result, const char *s, Py_ssize_t len); + +extern PyObject *_Py_bytes_find(const char *str, Py_ssize_t len, PyObject *args); +extern PyObject *_Py_bytes_index(const char *str, Py_ssize_t len, PyObject *args); +extern PyObject *_Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *args); +extern PyObject *_Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *args); +extern PyObject *_Py_bytes_count(const char *str, Py_ssize_t len, PyObject *args); +extern int _Py_bytes_contains(const char *str, Py_ssize_t len, PyObject *arg); +extern PyObject *_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *args); +extern PyObject *_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *args); + +/* The maketrans() static method. */ +extern PyObject* _Py_bytes_maketrans(Py_buffer *frm, Py_buffer *to); + +/* Shared __doc__ strings. */ +extern const char _Py_isspace__doc__[]; +extern const char _Py_isalpha__doc__[]; +extern const char _Py_isalnum__doc__[]; +extern const char _Py_isascii__doc__[]; +extern const char _Py_isdigit__doc__[]; +extern const char _Py_islower__doc__[]; +extern const char _Py_isupper__doc__[]; +extern const char _Py_istitle__doc__[]; +extern const char _Py_lower__doc__[]; +extern const char _Py_upper__doc__[]; +extern const char _Py_title__doc__[]; +extern const char _Py_capitalize__doc__[]; +extern const char _Py_swapcase__doc__[]; +extern const char _Py_count__doc__[]; +extern const char _Py_find__doc__[]; +extern const char _Py_index__doc__[]; +extern const char _Py_rfind__doc__[]; +extern const char _Py_rindex__doc__[]; +extern const char _Py_startswith__doc__[]; +extern const char _Py_endswith__doc__[]; +extern const char _Py_maketrans__doc__[]; +extern const char _Py_expandtabs__doc__[]; +extern const char _Py_ljust__doc__[]; +extern const char _Py_rjust__doc__[]; +extern const char _Py_center__doc__[]; +extern const char _Py_zfill__doc__[]; + +/* this is needed because some docs are shared from the .o, not static */ +#define PyDoc_STRVAR_shared(name,str) const char name[] = PyDoc_STR(str) + +#endif /* !Py_BYTES_CTYPE_H */ +#endif /* !Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_byteswap.h b/scripts/build-windows/py39-libs/include/internal/pycore_byteswap.h new file mode 100644 index 000000000..2b20fc6c7 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_byteswap.h @@ -0,0 +1,88 @@ +/* Bytes swap functions, reverse order of bytes: + + - _Py_bswap16(uint16_t) + - _Py_bswap32(uint32_t) + - _Py_bswap64(uint64_t) +*/ + +#ifndef Py_INTERNAL_BSWAP_H +#define Py_INTERNAL_BSWAP_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#if defined(__GNUC__) \ + && ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) + /* __builtin_bswap16() is available since GCC 4.8, + __builtin_bswap32() is available since GCC 4.3, + __builtin_bswap64() is available since GCC 4.3. */ +# define _PY_HAVE_BUILTIN_BSWAP +#endif + +#ifdef _MSC_VER + /* Get _byteswap_ushort(), _byteswap_ulong(), _byteswap_uint64() */ +# include +#endif + +static inline uint16_t +_Py_bswap16(uint16_t word) +{ +#if defined(_PY_HAVE_BUILTIN_BSWAP) || _Py__has_builtin(__builtin_bswap16) + return __builtin_bswap16(word); +#elif defined(_MSC_VER) + Py_BUILD_ASSERT(sizeof(word) == sizeof(unsigned short)); + return _byteswap_ushort(word); +#else + // Portable implementation which doesn't rely on circular bit shift + return ( ((word & UINT16_C(0x00FF)) << 8) + | ((word & UINT16_C(0xFF00)) >> 8)); +#endif +} + +static inline uint32_t +_Py_bswap32(uint32_t word) +{ +#if defined(_PY_HAVE_BUILTIN_BSWAP) || _Py__has_builtin(__builtin_bswap32) + return __builtin_bswap32(word); +#elif defined(_MSC_VER) + Py_BUILD_ASSERT(sizeof(word) == sizeof(unsigned long)); + return _byteswap_ulong(word); +#else + // Portable implementation which doesn't rely on circular bit shift + return ( ((word & UINT32_C(0x000000FF)) << 24) + | ((word & UINT32_C(0x0000FF00)) << 8) + | ((word & UINT32_C(0x00FF0000)) >> 8) + | ((word & UINT32_C(0xFF000000)) >> 24)); +#endif +} + +static inline uint64_t +_Py_bswap64(uint64_t word) +{ +#if defined(_PY_HAVE_BUILTIN_BSWAP) || _Py__has_builtin(__builtin_bswap64) + return __builtin_bswap64(word); +#elif defined(_MSC_VER) + return _byteswap_uint64(word); +#else + // Portable implementation which doesn't rely on circular bit shift + return ( ((word & UINT64_C(0x00000000000000FF)) << 56) + | ((word & UINT64_C(0x000000000000FF00)) << 40) + | ((word & UINT64_C(0x0000000000FF0000)) << 24) + | ((word & UINT64_C(0x00000000FF000000)) << 8) + | ((word & UINT64_C(0x000000FF00000000)) >> 8) + | ((word & UINT64_C(0x0000FF0000000000)) >> 24) + | ((word & UINT64_C(0x00FF000000000000)) >> 40) + | ((word & UINT64_C(0xFF00000000000000)) >> 56)); +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_BSWAP_H */ + diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_call.h b/scripts/build-windows/py39-libs/include/internal/pycore_call.h new file mode 100644 index 000000000..6b301264a --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_call.h @@ -0,0 +1,39 @@ +#ifndef Py_INTERNAL_CALL_H +#define Py_INTERNAL_CALL_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +PyAPI_FUNC(PyObject *) _PyObject_Call_Prepend( + PyThreadState *tstate, + PyObject *callable, + PyObject *obj, + PyObject *args, + PyObject *kwargs); + +PyAPI_FUNC(PyObject *) _PyObject_FastCallDictTstate( + PyThreadState *tstate, + PyObject *callable, + PyObject *const *args, + size_t nargsf, + PyObject *kwargs); + +PyAPI_FUNC(PyObject *) _PyObject_Call( + PyThreadState *tstate, + PyObject *callable, + PyObject *args, + PyObject *kwargs); + +static inline PyObject * +_PyObject_CallNoArgTstate(PyThreadState *tstate, PyObject *func) { + return _PyObject_VectorcallTstate(tstate, func, NULL, 0, NULL); +} + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_CALL_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_ceval.h b/scripts/build-windows/py39-libs/include/internal/pycore_ceval.h new file mode 100644 index 000000000..2affbf742 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_ceval.h @@ -0,0 +1,124 @@ +#ifndef Py_INTERNAL_CEVAL_H +#define Py_INTERNAL_CEVAL_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +/* Forward declarations */ +struct pyruntimestate; +struct _ceval_runtime_state; + +#include "pycore_interp.h" /* PyInterpreterState.eval_frame */ + +extern void _Py_FinishPendingCalls(PyThreadState *tstate); +extern void _PyEval_InitRuntimeState(struct _ceval_runtime_state *); +extern int _PyEval_InitState(struct _ceval_state *ceval); +extern void _PyEval_FiniState(struct _ceval_state *ceval); +PyAPI_FUNC(void) _PyEval_SignalReceived(PyInterpreterState *interp); +PyAPI_FUNC(int) _PyEval_AddPendingCall( + PyInterpreterState *interp, + int (*func)(void *), + void *arg); +PyAPI_FUNC(void) _PyEval_SignalAsyncExc(PyThreadState *tstate); +#ifdef HAVE_FORK +extern void _PyEval_ReInitThreads(struct pyruntimestate *runtime); +#endif +PyAPI_FUNC(void) _PyEval_SetCoroutineOriginTrackingDepth( + PyThreadState *tstate, + int new_depth); + +/* Private function */ +void _PyEval_Fini(void); + +static inline PyObject* +_PyEval_EvalFrame(PyThreadState *tstate, PyFrameObject *f, int throwflag) +{ + return tstate->interp->eval_frame(tstate, f, throwflag); +} + +extern PyObject *_PyEval_EvalCode( + PyThreadState *tstate, + PyObject *_co, PyObject *globals, PyObject *locals, + PyObject *const *args, Py_ssize_t argcount, + PyObject *const *kwnames, PyObject *const *kwargs, + Py_ssize_t kwcount, int kwstep, + PyObject *const *defs, Py_ssize_t defcount, + PyObject *kwdefs, PyObject *closure, + PyObject *name, PyObject *qualname); + +extern int _PyEval_ThreadsInitialized(struct pyruntimestate *runtime); +extern PyStatus _PyEval_InitGIL(PyThreadState *tstate); +extern void _PyEval_FiniGIL(PyThreadState *tstate); + +extern void _PyEval_ReleaseLock(PyThreadState *tstate); + + +/* --- _Py_EnterRecursiveCall() ----------------------------------------- */ + +PyAPI_DATA(int) _Py_CheckRecursionLimit; + +#ifdef USE_STACKCHECK +/* With USE_STACKCHECK macro defined, trigger stack checks in + _Py_CheckRecursiveCall() on every 64th call to Py_EnterRecursiveCall. */ +static inline int _Py_MakeRecCheck(PyThreadState *tstate) { + return (++tstate->recursion_depth > tstate->interp->ceval.recursion_limit + || ++tstate->stackcheck_counter > 64); +} +#else +static inline int _Py_MakeRecCheck(PyThreadState *tstate) { + return (++tstate->recursion_depth > tstate->interp->ceval.recursion_limit); +} +#endif + +PyAPI_FUNC(int) _Py_CheckRecursiveCall( + PyThreadState *tstate, + const char *where); + +static inline int _Py_EnterRecursiveCall(PyThreadState *tstate, + const char *where) { + return (_Py_MakeRecCheck(tstate) && _Py_CheckRecursiveCall(tstate, where)); +} + +static inline int _Py_EnterRecursiveCall_inline(const char *where) { + PyThreadState *tstate = PyThreadState_GET(); + return _Py_EnterRecursiveCall(tstate, where); +} + +#define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_inline(where) + +/* Compute the "lower-water mark" for a recursion limit. When + * Py_LeaveRecursiveCall() is called with a recursion depth below this mark, + * the overflowed flag is reset to 0. */ +static inline int _Py_RecursionLimitLowerWaterMark(int limit) { + if (limit > 200) { + return (limit - 50); + } + else { + return (3 * (limit >> 2)); + } +} + +static inline void _Py_LeaveRecursiveCall(PyThreadState *tstate) { + tstate->recursion_depth--; + int limit = tstate->interp->ceval.recursion_limit; + if (tstate->recursion_depth < _Py_RecursionLimitLowerWaterMark(limit)) { + tstate->overflowed = 0; + } +} + +static inline void _Py_LeaveRecursiveCall_inline(void) { + PyThreadState *tstate = PyThreadState_GET(); + _Py_LeaveRecursiveCall(tstate); +} + +#define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_inline() + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_CEVAL_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_code.h b/scripts/build-windows/py39-libs/include/internal/pycore_code.h new file mode 100644 index 000000000..8aee7ac25 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_code.h @@ -0,0 +1,27 @@ +#ifndef Py_INTERNAL_CODE_H +#define Py_INTERNAL_CODE_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject *ptr; /* Cached pointer (borrowed reference) */ + uint64_t globals_ver; /* ma_version of global dict */ + uint64_t builtins_ver; /* ma_version of builtin dict */ +} _PyOpcache_LoadGlobal; + +struct _PyOpcache { + union { + _PyOpcache_LoadGlobal lg; + } u; + char optimized; +}; + +/* Private API */ +int _PyCode_InitOpcache(PyCodeObject *co); + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_CODE_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_condvar.h b/scripts/build-windows/py39-libs/include/internal/pycore_condvar.h new file mode 100644 index 000000000..6e98673d0 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_condvar.h @@ -0,0 +1,95 @@ +#ifndef Py_INTERNAL_CONDVAR_H +#define Py_INTERNAL_CONDVAR_H + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#ifndef _POSIX_THREADS +/* This means pthreads are not implemented in libc headers, hence the macro + not present in unistd.h. But they still can be implemented as an external + library (e.g. gnu pth in pthread emulation) */ +# ifdef HAVE_PTHREAD_H +# include /* _POSIX_THREADS */ +# endif +#endif + +#ifdef _POSIX_THREADS +/* + * POSIX support + */ +#define Py_HAVE_CONDVAR + +#include + +#define PyMUTEX_T pthread_mutex_t +#define PyCOND_T pthread_cond_t + +#elif defined(NT_THREADS) +/* + * Windows (XP, 2003 server and later, as well as (hopefully) CE) support + * + * Emulated condition variables ones that work with XP and later, plus + * example native support on VISTA and onwards. + */ +#define Py_HAVE_CONDVAR + +/* include windows if it hasn't been done before */ +#define WIN32_LEAN_AND_MEAN +#include + +/* options */ +/* non-emulated condition variables are provided for those that want + * to target Windows Vista. Modify this macro to enable them. + */ +#ifndef _PY_EMULATED_WIN_CV +#define _PY_EMULATED_WIN_CV 1 /* use emulated condition variables */ +#endif + +/* fall back to emulation if not targeting Vista */ +#if !defined NTDDI_VISTA || NTDDI_VERSION < NTDDI_VISTA +#undef _PY_EMULATED_WIN_CV +#define _PY_EMULATED_WIN_CV 1 +#endif + +#if _PY_EMULATED_WIN_CV + +typedef CRITICAL_SECTION PyMUTEX_T; + +/* The ConditionVariable object. From XP onwards it is easily emulated + with a Semaphore. + Semaphores are available on Windows XP (2003 server) and later. + We use a Semaphore rather than an auto-reset event, because although + an auto-resent event might appear to solve the lost-wakeup bug (race + condition between releasing the outer lock and waiting) because it + maintains state even though a wait hasn't happened, there is still + a lost wakeup problem if more than one thread are interrupted in the + critical place. A semaphore solves that, because its state is + counted, not Boolean. + Because it is ok to signal a condition variable with no one + waiting, we need to keep track of the number of + waiting threads. Otherwise, the semaphore's state could rise + without bound. This also helps reduce the number of "spurious wakeups" + that would otherwise happen. + */ + +typedef struct _PyCOND_T +{ + HANDLE sem; + int waiting; /* to allow PyCOND_SIGNAL to be a no-op */ +} PyCOND_T; + +#else /* !_PY_EMULATED_WIN_CV */ + +/* Use native Win7 primitives if build target is Win7 or higher */ + +/* SRWLOCK is faster and better than CriticalSection */ +typedef SRWLOCK PyMUTEX_T; + +typedef CONDITION_VARIABLE PyCOND_T; + +#endif /* _PY_EMULATED_WIN_CV */ + +#endif /* _POSIX_THREADS, NT_THREADS */ + +#endif /* Py_INTERNAL_CONDVAR_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_context.h b/scripts/build-windows/py39-libs/include/internal/pycore_context.h new file mode 100644 index 000000000..884baa9cb --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_context.h @@ -0,0 +1,42 @@ +#ifndef Py_INTERNAL_CONTEXT_H +#define Py_INTERNAL_CONTEXT_H + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include "pycore_hamt.h" /* PyHamtObject */ + +struct _pycontextobject { + PyObject_HEAD + PyContext *ctx_prev; + PyHamtObject *ctx_vars; + PyObject *ctx_weakreflist; + int ctx_entered; +}; + + +struct _pycontextvarobject { + PyObject_HEAD + PyObject *var_name; + PyObject *var_default; + PyObject *var_cached; + uint64_t var_cached_tsid; + uint64_t var_cached_tsver; + Py_hash_t var_hash; +}; + + +struct _pycontexttokenobject { + PyObject_HEAD + PyContext *tok_ctx; + PyContextVar *tok_var; + PyObject *tok_oldval; + int tok_used; +}; + + +int _PyContext_Init(void); +void _PyContext_Fini(void); + +#endif /* !Py_INTERNAL_CONTEXT_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_dtoa.h b/scripts/build-windows/py39-libs/include/internal/pycore_dtoa.h new file mode 100644 index 000000000..4cf08f61a --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_dtoa.h @@ -0,0 +1,23 @@ +#ifndef PY_NO_SHORT_FLOAT_REPR +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +/* These functions are used by modules compiled as C extension like math: + they must be exported. */ + +PyAPI_FUNC(double) _Py_dg_strtod(const char *str, char **ptr); +PyAPI_FUNC(char *) _Py_dg_dtoa(double d, int mode, int ndigits, + int *decpt, int *sign, char **rve); +PyAPI_FUNC(void) _Py_dg_freedtoa(char *s); +PyAPI_FUNC(double) _Py_dg_stdnan(int sign); +PyAPI_FUNC(double) _Py_dg_infinity(int sign); + +#ifdef __cplusplus +} +#endif +#endif /* !PY_NO_SHORT_FLOAT_REPR */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_fileutils.h b/scripts/build-windows/py39-libs/include/internal/pycore_fileutils.h new file mode 100644 index 000000000..9636f2521 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_fileutils.h @@ -0,0 +1,66 @@ +#ifndef Py_INTERNAL_FILEUTILS_H +#define Py_INTERNAL_FILEUTILS_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "Py_BUILD_CORE must be defined to include this header" +#endif + +#include /* struct lconv */ + +PyAPI_DATA(int) _Py_HasFileSystemDefaultEncodeErrors; + +PyAPI_FUNC(int) _Py_DecodeUTF8Ex( + const char *arg, + Py_ssize_t arglen, + wchar_t **wstr, + size_t *wlen, + const char **reason, + _Py_error_handler errors); + +PyAPI_FUNC(int) _Py_EncodeUTF8Ex( + const wchar_t *text, + char **str, + size_t *error_pos, + const char **reason, + int raw_malloc, + _Py_error_handler errors); + +PyAPI_FUNC(wchar_t*) _Py_DecodeUTF8_surrogateescape( + const char *arg, + Py_ssize_t arglen, + size_t *wlen); + +PyAPI_FUNC(int) _Py_GetForceASCII(void); + +/* Reset "force ASCII" mode (if it was initialized). + + This function should be called when Python changes the LC_CTYPE locale, + so the "force ASCII" mode can be detected again on the new locale + encoding. */ +PyAPI_FUNC(void) _Py_ResetForceASCII(void); + + +PyAPI_FUNC(int) _Py_GetLocaleconvNumeric( + struct lconv *lc, + PyObject **decimal_point, + PyObject **thousands_sep); + +#ifdef HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION +extern int _Py_LocaleUsesNonUnicodeWchar(void); + +extern wchar_t* _Py_DecodeNonUnicodeWchar( + const wchar_t* native, + Py_ssize_t size); + +extern int _Py_EncodeNonUnicodeWchar_InPlace( + wchar_t* unicode, + Py_ssize_t size); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_FILEUTILS_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_gc.h b/scripts/build-windows/py39-libs/include/internal/pycore_gc.h new file mode 100644 index 000000000..012407a56 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_gc.h @@ -0,0 +1,179 @@ +#ifndef Py_INTERNAL_GC_H +#define Py_INTERNAL_GC_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +/* GC information is stored BEFORE the object structure. */ +typedef struct { + // Pointer to next object in the list. + // 0 means the object is not tracked + uintptr_t _gc_next; + + // Pointer to previous object in the list. + // Lowest two bits are used for flags documented later. + uintptr_t _gc_prev; +} PyGC_Head; + +#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1) + +/* True if the object is currently tracked by the GC. */ +#define _PyObject_GC_IS_TRACKED(o) (_Py_AS_GC(o)->_gc_next != 0) + +/* True if the object may be tracked by the GC in the future, or already is. + This can be useful to implement some optimizations. */ +#define _PyObject_GC_MAY_BE_TRACKED(obj) \ + (PyObject_IS_GC(obj) && \ + (!PyTuple_CheckExact(obj) || _PyObject_GC_IS_TRACKED(obj))) + + +/* Bit flags for _gc_prev */ +/* Bit 0 is set when tp_finalize is called */ +#define _PyGC_PREV_MASK_FINALIZED (1) +/* Bit 1 is set when the object is in generation which is GCed currently. */ +#define _PyGC_PREV_MASK_COLLECTING (2) +/* The (N-2) most significant bits contain the real address. */ +#define _PyGC_PREV_SHIFT (2) +#define _PyGC_PREV_MASK (((uintptr_t) -1) << _PyGC_PREV_SHIFT) + +// Lowest bit of _gc_next is used for flags only in GC. +// But it is always 0 for normal code. +#define _PyGCHead_NEXT(g) ((PyGC_Head*)(g)->_gc_next) +#define _PyGCHead_SET_NEXT(g, p) ((g)->_gc_next = (uintptr_t)(p)) + +// Lowest two bits of _gc_prev is used for _PyGC_PREV_MASK_* flags. +#define _PyGCHead_PREV(g) ((PyGC_Head*)((g)->_gc_prev & _PyGC_PREV_MASK)) +#define _PyGCHead_SET_PREV(g, p) do { \ + assert(((uintptr_t)p & ~_PyGC_PREV_MASK) == 0); \ + (g)->_gc_prev = ((g)->_gc_prev & ~_PyGC_PREV_MASK) \ + | ((uintptr_t)(p)); \ + } while (0) + +#define _PyGCHead_FINALIZED(g) \ + (((g)->_gc_prev & _PyGC_PREV_MASK_FINALIZED) != 0) +#define _PyGCHead_SET_FINALIZED(g) \ + ((g)->_gc_prev |= _PyGC_PREV_MASK_FINALIZED) + +#define _PyGC_FINALIZED(o) \ + _PyGCHead_FINALIZED(_Py_AS_GC(o)) +#define _PyGC_SET_FINALIZED(o) \ + _PyGCHead_SET_FINALIZED(_Py_AS_GC(o)) + + +/* GC runtime state */ + +/* If we change this, we need to change the default value in the + signature of gc.collect. */ +#define NUM_GENERATIONS 3 +/* + NOTE: about untracking of mutable objects. + + Certain types of container cannot participate in a reference cycle, and + so do not need to be tracked by the garbage collector. Untracking these + objects reduces the cost of garbage collections. However, determining + which objects may be untracked is not free, and the costs must be + weighed against the benefits for garbage collection. + + There are two possible strategies for when to untrack a container: + + i) When the container is created. + ii) When the container is examined by the garbage collector. + + Tuples containing only immutable objects (integers, strings etc, and + recursively, tuples of immutable objects) do not need to be tracked. + The interpreter creates a large number of tuples, many of which will + not survive until garbage collection. It is therefore not worthwhile + to untrack eligible tuples at creation time. + + Instead, all tuples except the empty tuple are tracked when created. + During garbage collection it is determined whether any surviving tuples + can be untracked. A tuple can be untracked if all of its contents are + already not tracked. Tuples are examined for untracking in all garbage + collection cycles. It may take more than one cycle to untrack a tuple. + + Dictionaries containing only immutable objects also do not need to be + tracked. Dictionaries are untracked when created. If a tracked item is + inserted into a dictionary (either as a key or value), the dictionary + becomes tracked. During a full garbage collection (all generations), + the collector will untrack any dictionaries whose contents are not + tracked. + + The module provides the python function is_tracked(obj), which returns + the CURRENT tracking status of the object. Subsequent garbage + collections may change the tracking status of the object. + + Untracking of certain containers was introduced in issue #4688, and + the algorithm was refined in response to issue #14775. +*/ + +struct gc_generation { + PyGC_Head head; + int threshold; /* collection threshold */ + int count; /* count of allocations or collections of younger + generations */ +}; + +/* Running stats per generation */ +struct gc_generation_stats { + /* total number of collections */ + Py_ssize_t collections; + /* total number of collected objects */ + Py_ssize_t collected; + /* total number of uncollectable objects (put into gc.garbage) */ + Py_ssize_t uncollectable; +}; + +struct _gc_runtime_state { + /* List of objects that still need to be cleaned up, singly linked + * via their gc headers' gc_prev pointers. */ + PyObject *trash_delete_later; + /* Current call-stack depth of tp_dealloc calls. */ + int trash_delete_nesting; + + int enabled; + int debug; + /* linked lists of container objects */ + struct gc_generation generations[NUM_GENERATIONS]; + PyGC_Head *generation0; + /* a permanent generation which won't be collected */ + struct gc_generation permanent_generation; + struct gc_generation_stats generation_stats[NUM_GENERATIONS]; + /* true if we are currently running the collector */ + int collecting; + /* list of uncollectable objects */ + PyObject *garbage; + /* a list of callbacks to be invoked when collection is performed */ + PyObject *callbacks; + /* This is the number of objects that survived the last full + collection. It approximates the number of long lived objects + tracked by the GC. + + (by "full collection", we mean a collection of the oldest + generation). */ + Py_ssize_t long_lived_total; + /* This is the number of objects that survived all "non-full" + collections, and are awaiting to undergo a full collection for + the first time. */ + Py_ssize_t long_lived_pending; +}; + +PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *); + + +// Functions to clear types free lists +extern void _PyFrame_ClearFreeList(void); +extern void _PyTuple_ClearFreeList(void); +extern void _PyFloat_ClearFreeList(void); +extern void _PyList_ClearFreeList(void); +extern void _PyDict_ClearFreeList(void); +extern void _PyAsyncGen_ClearFreeLists(void); +extern void _PyContext_ClearFreeList(void); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_GC_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_getopt.h b/scripts/build-windows/py39-libs/include/internal/pycore_getopt.h new file mode 100644 index 000000000..d045469c9 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_getopt.h @@ -0,0 +1,22 @@ +#ifndef Py_INTERNAL_PYGETOPT_H +#define Py_INTERNAL_PYGETOPT_H + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +extern int _PyOS_opterr; +extern Py_ssize_t _PyOS_optind; +extern const wchar_t *_PyOS_optarg; + +extern void _PyOS_ResetGetOpt(void); + +typedef struct { + const wchar_t *name; + int has_arg; + int val; +} _PyOS_LongOption; + +extern int _PyOS_GetOpt(Py_ssize_t argc, wchar_t * const *argv, int *longindex); + +#endif /* !Py_INTERNAL_PYGETOPT_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_gil.h b/scripts/build-windows/py39-libs/include/internal/pycore_gil.h new file mode 100644 index 000000000..e1c8923c9 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_gil.h @@ -0,0 +1,50 @@ +#ifndef Py_INTERNAL_GIL_H +#define Py_INTERNAL_GIL_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include "pycore_atomic.h" /* _Py_atomic_address */ +#include "pycore_condvar.h" /* PyCOND_T */ + +#ifndef Py_HAVE_CONDVAR +# error You need either a POSIX-compatible or a Windows system! +#endif + +/* Enable if you want to force the switching of threads at least + every `interval`. */ +#undef FORCE_SWITCHING +#define FORCE_SWITCHING + +struct _gil_runtime_state { + /* microseconds (the Python API uses seconds, though) */ + unsigned long interval; + /* Last PyThreadState holding / having held the GIL. This helps us + know whether anyone else was scheduled after we dropped the GIL. */ + _Py_atomic_address last_holder; + /* Whether the GIL is already taken (-1 if uninitialized). This is + atomic because it can be read without any lock taken in ceval.c. */ + _Py_atomic_int locked; + /* Number of GIL switches since the beginning. */ + unsigned long switch_number; + /* This condition variable allows one or several threads to wait + until the GIL is released. In addition, the mutex also protects + the above variables. */ + PyCOND_T cond; + PyMUTEX_T mutex; +#ifdef FORCE_SWITCHING + /* This condition variable helps the GIL-releasing thread wait for + a GIL-awaiting thread to be scheduled and take the GIL. */ + PyCOND_T switch_cond; + PyMUTEX_T switch_mutex; +#endif +}; + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_GIL_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_hamt.h b/scripts/build-windows/py39-libs/include/internal/pycore_hamt.h new file mode 100644 index 000000000..f6abb1139 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_hamt.h @@ -0,0 +1,116 @@ +#ifndef Py_INTERNAL_HAMT_H +#define Py_INTERNAL_HAMT_H + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#define _Py_HAMT_MAX_TREE_DEPTH 7 + + +#define PyHamt_Check(o) Py_IS_TYPE(o, &_PyHamt_Type) + + +/* Abstract tree node. */ +typedef struct { + PyObject_HEAD +} PyHamtNode; + + +/* An HAMT immutable mapping collection. */ +typedef struct { + PyObject_HEAD + PyHamtNode *h_root; + PyObject *h_weakreflist; + Py_ssize_t h_count; +} PyHamtObject; + + +/* A struct to hold the state of depth-first traverse of the tree. + + HAMT is an immutable collection. Iterators will hold a strong reference + to it, and every node in the HAMT has strong references to its children. + + So for iterators, we can implement zero allocations and zero reference + inc/dec depth-first iteration. + + - i_nodes: an array of seven pointers to tree nodes + - i_level: the current node in i_nodes + - i_pos: an array of positions within nodes in i_nodes. +*/ +typedef struct { + PyHamtNode *i_nodes[_Py_HAMT_MAX_TREE_DEPTH]; + Py_ssize_t i_pos[_Py_HAMT_MAX_TREE_DEPTH]; + int8_t i_level; +} PyHamtIteratorState; + + +/* Base iterator object. + + Contains the iteration state, a pointer to the HAMT tree, + and a pointer to the 'yield function'. The latter is a simple + function that returns a key/value tuple for the 'Items' iterator, + just a key for the 'Keys' iterator, and a value for the 'Values' + iterator. +*/ +typedef struct { + PyObject_HEAD + PyHamtObject *hi_obj; + PyHamtIteratorState hi_iter; + binaryfunc hi_yield; +} PyHamtIterator; + + +PyAPI_DATA(PyTypeObject) _PyHamt_Type; +PyAPI_DATA(PyTypeObject) _PyHamt_ArrayNode_Type; +PyAPI_DATA(PyTypeObject) _PyHamt_BitmapNode_Type; +PyAPI_DATA(PyTypeObject) _PyHamt_CollisionNode_Type; +PyAPI_DATA(PyTypeObject) _PyHamtKeys_Type; +PyAPI_DATA(PyTypeObject) _PyHamtValues_Type; +PyAPI_DATA(PyTypeObject) _PyHamtItems_Type; + + +/* Create a new HAMT immutable mapping. */ +PyHamtObject * _PyHamt_New(void); + +/* Return a new collection based on "o", but with an additional + key/val pair. */ +PyHamtObject * _PyHamt_Assoc(PyHamtObject *o, PyObject *key, PyObject *val); + +/* Return a new collection based on "o", but without "key". */ +PyHamtObject * _PyHamt_Without(PyHamtObject *o, PyObject *key); + +/* Find "key" in the "o" collection. + + Return: + - -1: An error occurred. + - 0: "key" wasn't found in "o". + - 1: "key" is in "o"; "*val" is set to its value (a borrowed ref). +*/ +int _PyHamt_Find(PyHamtObject *o, PyObject *key, PyObject **val); + +/* Check if "v" is equal to "w". + + Return: + - 0: v != w + - 1: v == w + - -1: An error occurred. +*/ +int _PyHamt_Eq(PyHamtObject *v, PyHamtObject *w); + +/* Return the size of "o"; equivalent of "len(o)". */ +Py_ssize_t _PyHamt_Len(PyHamtObject *o); + +/* Return a Keys iterator over "o". */ +PyObject * _PyHamt_NewIterKeys(PyHamtObject *o); + +/* Return a Values iterator over "o". */ +PyObject * _PyHamt_NewIterValues(PyHamtObject *o); + +/* Return a Items iterator over "o". */ +PyObject * _PyHamt_NewIterItems(PyHamtObject *o); + +int _PyHamt_Init(void); +void _PyHamt_Fini(void); + +#endif /* !Py_INTERNAL_HAMT_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_hashtable.h b/scripts/build-windows/py39-libs/include/internal/pycore_hashtable.h new file mode 100644 index 000000000..8fa411a15 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_hashtable.h @@ -0,0 +1,148 @@ +#ifndef Py_INTERNAL_HASHTABLE_H +#define Py_INTERNAL_HASHTABLE_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +/* Single linked list */ + +typedef struct _Py_slist_item_s { + struct _Py_slist_item_s *next; +} _Py_slist_item_t; + +typedef struct { + _Py_slist_item_t *head; +} _Py_slist_t; + +#define _Py_SLIST_ITEM_NEXT(ITEM) (((_Py_slist_item_t *)ITEM)->next) + +#define _Py_SLIST_HEAD(SLIST) (((_Py_slist_t *)SLIST)->head) + + +/* _Py_hashtable: table entry */ + +typedef struct { + /* used by _Py_hashtable_t.buckets to link entries */ + _Py_slist_item_t _Py_slist_item; + + Py_uhash_t key_hash; + void *key; + void *value; +} _Py_hashtable_entry_t; + + +/* _Py_hashtable: prototypes */ + +/* Forward declaration */ +struct _Py_hashtable_t; +typedef struct _Py_hashtable_t _Py_hashtable_t; + +typedef Py_uhash_t (*_Py_hashtable_hash_func) (const void *key); +typedef int (*_Py_hashtable_compare_func) (const void *key1, const void *key2); +typedef void (*_Py_hashtable_destroy_func) (void *key); +typedef _Py_hashtable_entry_t* (*_Py_hashtable_get_entry_func)(_Py_hashtable_t *ht, + const void *key); + +typedef struct { + // Allocate a memory block + void* (*malloc) (size_t size); + + // Release a memory block + void (*free) (void *ptr); +} _Py_hashtable_allocator_t; + + +/* _Py_hashtable: table */ +struct _Py_hashtable_t { + size_t nentries; // Total number of entries in the table + size_t nbuckets; + _Py_slist_t *buckets; + + _Py_hashtable_get_entry_func get_entry_func; + _Py_hashtable_hash_func hash_func; + _Py_hashtable_compare_func compare_func; + _Py_hashtable_destroy_func key_destroy_func; + _Py_hashtable_destroy_func value_destroy_func; + _Py_hashtable_allocator_t alloc; +}; + +/* Hash a pointer (void*) */ +PyAPI_FUNC(Py_uhash_t) _Py_hashtable_hash_ptr(const void *key); + +/* Comparison using memcmp() */ +PyAPI_FUNC(int) _Py_hashtable_compare_direct( + const void *key1, + const void *key2); + +PyAPI_FUNC(_Py_hashtable_t *) _Py_hashtable_new( + _Py_hashtable_hash_func hash_func, + _Py_hashtable_compare_func compare_func); + +PyAPI_FUNC(_Py_hashtable_t *) _Py_hashtable_new_full( + _Py_hashtable_hash_func hash_func, + _Py_hashtable_compare_func compare_func, + _Py_hashtable_destroy_func key_destroy_func, + _Py_hashtable_destroy_func value_destroy_func, + _Py_hashtable_allocator_t *allocator); + +PyAPI_FUNC(void) _Py_hashtable_destroy(_Py_hashtable_t *ht); + +PyAPI_FUNC(void) _Py_hashtable_clear(_Py_hashtable_t *ht); + +typedef int (*_Py_hashtable_foreach_func) (_Py_hashtable_t *ht, + const void *key, const void *value, + void *user_data); + +/* Call func() on each entry of the hashtable. + Iteration stops if func() result is non-zero, in this case it's the result + of the call. Otherwise, the function returns 0. */ +PyAPI_FUNC(int) _Py_hashtable_foreach( + _Py_hashtable_t *ht, + _Py_hashtable_foreach_func func, + void *user_data); + +PyAPI_FUNC(size_t) _Py_hashtable_size(const _Py_hashtable_t *ht); + +/* Add a new entry to the hash. The key must not be present in the hash table. + Return 0 on success, -1 on memory error. */ +PyAPI_FUNC(int) _Py_hashtable_set( + _Py_hashtable_t *ht, + const void *key, + void *value); + + +/* Get an entry. + Return NULL if the key does not exist. */ +static inline _Py_hashtable_entry_t * +_Py_hashtable_get_entry(_Py_hashtable_t *ht, const void *key) +{ + return ht->get_entry_func(ht, key); +} + + +/* Get value from an entry. + Return NULL if the entry is not found. + + Use _Py_hashtable_get_entry() to distinguish entry value equal to NULL + and entry not found. */ +PyAPI_FUNC(void*) _Py_hashtable_get(_Py_hashtable_t *ht, const void *key); + + +/* Remove a key and its associated value without calling key and value destroy + functions. + + Return the removed value if the key was found. + Return NULL if the key was not found. */ +PyAPI_FUNC(void*) _Py_hashtable_steal( + _Py_hashtable_t *ht, + const void *key); + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_HASHTABLE_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_import.h b/scripts/build-windows/py39-libs/include/internal/pycore_import.h new file mode 100644 index 000000000..4c5e0e5ef --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_import.h @@ -0,0 +1,22 @@ +#ifndef Py_LIMITED_API +#ifndef Py_INTERNAL_IMPORT_H +#define Py_INTERNAL_IMPORT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(PyObject *) _PyImport_FindBuiltin( + PyThreadState *tstate, + const char *name /* UTF-8 encoded string */ + ); + +#ifdef HAVE_FORK +extern void _PyImport_ReInitLock(void); +#endif +extern void _PyImport_Cleanup(PyThreadState *tstate); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_IMPORT_H */ +#endif /* !Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_initconfig.h b/scripts/build-windows/py39-libs/include/internal/pycore_initconfig.h new file mode 100644 index 000000000..855fb5505 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_initconfig.h @@ -0,0 +1,167 @@ +#ifndef Py_INTERNAL_CORECONFIG_H +#define Py_INTERNAL_CORECONFIG_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +/* Forward declaration */ +struct pyruntimestate; + +/* --- PyStatus ----------------------------------------------- */ + +/* Almost all errors causing Python initialization to fail */ +#ifdef _MSC_VER + /* Visual Studio 2015 doesn't implement C99 __func__ in C */ +# define _PyStatus_GET_FUNC() __FUNCTION__ +#else +# define _PyStatus_GET_FUNC() __func__ +#endif + +#define _PyStatus_OK() \ + (PyStatus){._type = _PyStatus_TYPE_OK,} + /* other fields are set to 0 */ +#define _PyStatus_ERR(ERR_MSG) \ + (PyStatus){ \ + ._type = _PyStatus_TYPE_ERROR, \ + .func = _PyStatus_GET_FUNC(), \ + .err_msg = (ERR_MSG)} + /* other fields are set to 0 */ +#define _PyStatus_NO_MEMORY() _PyStatus_ERR("memory allocation failed") +#define _PyStatus_EXIT(EXITCODE) \ + (PyStatus){ \ + ._type = _PyStatus_TYPE_EXIT, \ + .exitcode = (EXITCODE)} +#define _PyStatus_IS_ERROR(err) \ + (err._type == _PyStatus_TYPE_ERROR) +#define _PyStatus_IS_EXIT(err) \ + (err._type == _PyStatus_TYPE_EXIT) +#define _PyStatus_EXCEPTION(err) \ + (err._type != _PyStatus_TYPE_OK) +#define _PyStatus_UPDATE_FUNC(err) \ + do { err.func = _PyStatus_GET_FUNC(); } while (0) + +/* --- PyWideStringList ------------------------------------------------ */ + +#define _PyWideStringList_INIT (PyWideStringList){.length = 0, .items = NULL} + +#ifndef NDEBUG +PyAPI_FUNC(int) _PyWideStringList_CheckConsistency(const PyWideStringList *list); +#endif +PyAPI_FUNC(void) _PyWideStringList_Clear(PyWideStringList *list); +PyAPI_FUNC(int) _PyWideStringList_Copy(PyWideStringList *list, + const PyWideStringList *list2); +PyAPI_FUNC(PyStatus) _PyWideStringList_Extend(PyWideStringList *list, + const PyWideStringList *list2); +PyAPI_FUNC(PyObject*) _PyWideStringList_AsList(const PyWideStringList *list); + + +/* --- _PyArgv ---------------------------------------------------- */ + +typedef struct _PyArgv { + Py_ssize_t argc; + int use_bytes_argv; + char * const *bytes_argv; + wchar_t * const *wchar_argv; +} _PyArgv; + +PyAPI_FUNC(PyStatus) _PyArgv_AsWstrList(const _PyArgv *args, + PyWideStringList *list); + + +/* --- Helper functions ------------------------------------------- */ + +PyAPI_FUNC(int) _Py_str_to_int( + const char *str, + int *result); +PyAPI_FUNC(const wchar_t*) _Py_get_xoption( + const PyWideStringList *xoptions, + const wchar_t *name); +PyAPI_FUNC(const char*) _Py_GetEnv( + int use_environment, + const char *name); +PyAPI_FUNC(void) _Py_get_env_flag( + int use_environment, + int *flag, + const char *name); + +/* Py_GetArgcArgv() helper */ +PyAPI_FUNC(void) _Py_ClearArgcArgv(void); + + +/* --- _PyPreCmdline ------------------------------------------------- */ + +typedef struct { + PyWideStringList argv; + PyWideStringList xoptions; /* "-X value" option */ + int isolated; /* -I option */ + int use_environment; /* -E option */ + int dev_mode; /* -X dev and PYTHONDEVMODE */ +} _PyPreCmdline; + +#define _PyPreCmdline_INIT \ + (_PyPreCmdline){ \ + .use_environment = -1, \ + .isolated = -1, \ + .dev_mode = -1} +/* Note: _PyPreCmdline_INIT sets other fields to 0/NULL */ + +extern void _PyPreCmdline_Clear(_PyPreCmdline *cmdline); +extern PyStatus _PyPreCmdline_SetArgv(_PyPreCmdline *cmdline, + const _PyArgv *args); +extern PyStatus _PyPreCmdline_SetConfig( + const _PyPreCmdline *cmdline, + PyConfig *config); +extern PyStatus _PyPreCmdline_Read(_PyPreCmdline *cmdline, + const PyPreConfig *preconfig); + + +/* --- PyPreConfig ----------------------------------------------- */ + +PyAPI_FUNC(void) _PyPreConfig_InitCompatConfig(PyPreConfig *preconfig); +extern void _PyPreConfig_InitFromConfig( + PyPreConfig *preconfig, + const PyConfig *config); +extern PyStatus _PyPreConfig_InitFromPreConfig( + PyPreConfig *preconfig, + const PyPreConfig *config2); +extern PyObject* _PyPreConfig_AsDict(const PyPreConfig *preconfig); +extern void _PyPreConfig_GetConfig(PyPreConfig *preconfig, + const PyConfig *config); +extern PyStatus _PyPreConfig_Read(PyPreConfig *preconfig, + const _PyArgv *args); +extern PyStatus _PyPreConfig_Write(const PyPreConfig *preconfig); + + +/* --- PyConfig ---------------------------------------------- */ + +typedef enum { + /* Py_Initialize() API: backward compatibility with Python 3.6 and 3.7 */ + _PyConfig_INIT_COMPAT = 1, + _PyConfig_INIT_PYTHON = 2, + _PyConfig_INIT_ISOLATED = 3 +} _PyConfigInitEnum; + +PyAPI_FUNC(void) _PyConfig_InitCompatConfig(PyConfig *config); +extern PyStatus _PyConfig_Copy( + PyConfig *config, + const PyConfig *config2); +extern PyStatus _PyConfig_InitPathConfig(PyConfig *config); +extern PyStatus _PyConfig_Write(const PyConfig *config, + struct pyruntimestate *runtime); +extern PyStatus _PyConfig_SetPyArgv( + PyConfig *config, + const _PyArgv *args); + + +/* --- Function used for testing ---------------------------------- */ + +PyAPI_FUNC(PyObject*) _Py_GetConfigsAsDict(void); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_CORECONFIG_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_interp.h b/scripts/build-windows/py39-libs/include/internal/pycore_interp.h new file mode 100644 index 000000000..1023483a3 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_interp.h @@ -0,0 +1,192 @@ +#ifndef Py_INTERNAL_INTERP_H +#define Py_INTERNAL_INTERP_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include "pycore_atomic.h" /* _Py_atomic_address */ +#include "pycore_gil.h" /* struct _gil_runtime_state */ +#include "pycore_gc.h" /* struct _gc_runtime_state */ +#include "pycore_warnings.h" /* struct _warnings_runtime_state */ + +/* ceval state */ + +struct _pending_calls { + PyThread_type_lock lock; + /* Request for running pending calls. */ + _Py_atomic_int calls_to_do; + /* Request for looking at the `async_exc` field of the current + thread state. + Guarded by the GIL. */ + int async_exc; +#define NPENDINGCALLS 32 + struct { + int (*func)(void *); + void *arg; + } calls[NPENDINGCALLS]; + int first; + int last; +}; + +struct _ceval_state { + int recursion_limit; + /* Records whether tracing is on for any thread. Counts the number + of threads for which tstate->c_tracefunc is non-NULL, so if the + value is 0, we know we don't have to check this thread's + c_tracefunc. This speeds up the if statement in + _PyEval_EvalFrameDefault() after fast_next_opcode. */ + int tracing_possible; + /* This single variable consolidates all requests to break out of + the fast path in the eval loop. */ + _Py_atomic_int eval_breaker; + /* Request for dropping the GIL */ + _Py_atomic_int gil_drop_request; + struct _pending_calls pending; +}; + +/* fs_codec.encoding is initialized to NULL. + Later, it is set to a non-NULL string by _PyUnicode_InitEncodings(). */ +struct _Py_unicode_fs_codec { + char *encoding; // Filesystem encoding (encoded to UTF-8) + int utf8; // encoding=="utf-8"? + char *errors; // Filesystem errors (encoded to UTF-8) + _Py_error_handler error_handler; +}; + +struct _Py_unicode_state { + struct _Py_unicode_fs_codec fs_codec; +}; + + +/* interpreter state */ + +#define _PY_NSMALLPOSINTS 257 +#define _PY_NSMALLNEGINTS 5 + +// The PyInterpreterState typedef is in Include/pystate.h. +struct _is { + + struct _is *next; + struct _ts *tstate_head; + + /* Reference to the _PyRuntime global variable. This field exists + to not have to pass runtime in addition to tstate to a function. + Get runtime from tstate: tstate->interp->runtime. */ + struct pyruntimestate *runtime; + + int64_t id; + int64_t id_refcount; + int requires_idref; + PyThread_type_lock id_mutex; + + int finalizing; + + struct _ceval_state ceval; + struct _gc_runtime_state gc; + + PyObject *modules; + PyObject *modules_by_index; + PyObject *sysdict; + PyObject *builtins; + PyObject *importlib; + + /* Used in Modules/_threadmodule.c. */ + long num_threads; + /* Support for runtime thread stack size tuning. + A value of 0 means using the platform's default stack size + or the size specified by the THREAD_STACK_SIZE macro. */ + /* Used in Python/thread.c. */ + size_t pythread_stacksize; + + PyObject *codec_search_path; + PyObject *codec_search_cache; + PyObject *codec_error_registry; + int codecs_initialized; + + struct _Py_unicode_state unicode; + + PyConfig config; +#ifdef HAVE_DLOPEN + int dlopenflags; +#endif + + PyObject *dict; /* Stores per-interpreter state */ + + PyObject *builtins_copy; + PyObject *import_func; + /* Initialized to PyEval_EvalFrameDefault(). */ + _PyFrameEvalFunction eval_frame; + + Py_ssize_t co_extra_user_count; + freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS]; + +#ifdef HAVE_FORK + PyObject *before_forkers; + PyObject *after_forkers_parent; + PyObject *after_forkers_child; +#endif + /* AtExit module */ + void (*pyexitfunc)(PyObject *); + PyObject *pyexitmodule; + + uint64_t tstate_next_unique_id; + + struct _warnings_runtime_state warnings; + + PyObject *audit_hooks; + + struct { + struct { + int level; + int atbol; + } listnode; + } parser; + +#if _PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS > 0 + /* Small integers are preallocated in this array so that they + can be shared. + The integers that are preallocated are those in the range + -_PY_NSMALLNEGINTS (inclusive) to _PY_NSMALLPOSINTS (not inclusive). + */ + PyLongObject* small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS]; +#endif +}; + +/* Used by _PyImport_Cleanup() */ +extern void _PyInterpreterState_ClearModules(PyInterpreterState *interp); + +extern PyStatus _PyInterpreterState_SetConfig( + PyInterpreterState *interp, + const PyConfig *config); + + + +/* cross-interpreter data registry */ + +/* For now we use a global registry of shareable classes. An + alternative would be to add a tp_* slot for a class's + crossinterpdatafunc. It would be simpler and more efficient. */ + +struct _xidregitem; + +struct _xidregitem { + PyTypeObject *cls; + crossinterpdatafunc getdata; + struct _xidregitem *next; +}; + +PyAPI_FUNC(struct _is*) _PyInterpreterState_LookUpID(int64_t); + +PyAPI_FUNC(int) _PyInterpreterState_IDInitref(struct _is *); +PyAPI_FUNC(void) _PyInterpreterState_IDIncref(struct _is *); +PyAPI_FUNC(void) _PyInterpreterState_IDDecref(struct _is *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_INTERP_H */ + diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_object.h b/scripts/build-windows/py39-libs/include/internal/pycore_object.h new file mode 100644 index 000000000..15497007f --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_object.h @@ -0,0 +1,120 @@ +#ifndef Py_INTERNAL_OBJECT_H +#define Py_INTERNAL_OBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() +#include "pycore_interp.h" // PyInterpreterState.gc +#include "pycore_pystate.h" // _PyThreadState_GET() + +PyAPI_FUNC(int) _PyType_CheckConsistency(PyTypeObject *type); +PyAPI_FUNC(int) _PyDict_CheckConsistency(PyObject *mp, int check_content); + +/* Tell the GC to track this object. + * + * NB: While the object is tracked by the collector, it must be safe to call the + * ob_traverse method. + * + * Internal note: interp->gc.generation0->_gc_prev doesn't have any bit flags + * because it's not object header. So we don't use _PyGCHead_PREV() and + * _PyGCHead_SET_PREV() for it to avoid unnecessary bitwise operations. + * + * The PyObject_GC_Track() function is the public version of this macro. + */ +static inline void _PyObject_GC_TRACK_impl(const char *filename, int lineno, + PyObject *op) +{ + _PyObject_ASSERT_FROM(op, !_PyObject_GC_IS_TRACKED(op), + "object already tracked by the garbage collector", + filename, lineno, "_PyObject_GC_TRACK"); + + PyGC_Head *gc = _Py_AS_GC(op); + _PyObject_ASSERT_FROM(op, + (gc->_gc_prev & _PyGC_PREV_MASK_COLLECTING) == 0, + "object is in generation which is garbage collected", + filename, lineno, "_PyObject_GC_TRACK"); + + PyThreadState *tstate = _PyThreadState_GET(); + PyGC_Head *generation0 = tstate->interp->gc.generation0; + PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev); + _PyGCHead_SET_NEXT(last, gc); + _PyGCHead_SET_PREV(gc, last); + _PyGCHead_SET_NEXT(gc, generation0); + generation0->_gc_prev = (uintptr_t)gc; +} + +#define _PyObject_GC_TRACK(op) \ + _PyObject_GC_TRACK_impl(__FILE__, __LINE__, _PyObject_CAST(op)) + +/* Tell the GC to stop tracking this object. + * + * Internal note: This may be called while GC. So _PyGC_PREV_MASK_COLLECTING + * must be cleared. But _PyGC_PREV_MASK_FINALIZED bit is kept. + * + * The object must be tracked by the GC. + * + * The PyObject_GC_UnTrack() function is the public version of this macro. + */ +static inline void _PyObject_GC_UNTRACK_impl(const char *filename, int lineno, + PyObject *op) +{ + _PyObject_ASSERT_FROM(op, _PyObject_GC_IS_TRACKED(op), + "object not tracked by the garbage collector", + filename, lineno, "_PyObject_GC_UNTRACK"); + + PyGC_Head *gc = _Py_AS_GC(op); + PyGC_Head *prev = _PyGCHead_PREV(gc); + PyGC_Head *next = _PyGCHead_NEXT(gc); + _PyGCHead_SET_NEXT(prev, next); + _PyGCHead_SET_PREV(next, prev); + gc->_gc_next = 0; + gc->_gc_prev &= _PyGC_PREV_MASK_FINALIZED; +} + +#define _PyObject_GC_UNTRACK(op) \ + _PyObject_GC_UNTRACK_impl(__FILE__, __LINE__, _PyObject_CAST(op)) + +#ifdef Py_REF_DEBUG +extern void _PyDebug_PrintTotalRefs(void); +#endif + +#ifdef Py_TRACE_REFS +extern void _Py_AddToAllObjects(PyObject *op, int force); +extern void _Py_PrintReferences(FILE *); +extern void _Py_PrintReferenceAddresses(FILE *); +#endif + +static inline PyObject ** +_PyObject_GET_WEAKREFS_LISTPTR(PyObject *op) +{ + Py_ssize_t offset = Py_TYPE(op)->tp_weaklistoffset; + return (PyObject **)((char *)op + offset); +} + +// Fast inlined version of PyType_HasFeature() +static inline int +_PyType_HasFeature(PyTypeObject *type, unsigned long feature) { + return ((type->tp_flags & feature) != 0); +} + +// Fast inlined version of PyObject_IS_GC() +static inline int +_PyObject_IS_GC(PyObject *obj) +{ + return (PyType_IS_GC(Py_TYPE(obj)) + && (Py_TYPE(obj)->tp_is_gc == NULL + || Py_TYPE(obj)->tp_is_gc(obj))); +} + +// Fast inlined version of PyType_IS_GC() +#define _PyType_IS_GC(t) _PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_OBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_pathconfig.h b/scripts/build-windows/py39-libs/include/internal/pycore_pathconfig.h new file mode 100644 index 000000000..8f706fe69 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_pathconfig.h @@ -0,0 +1,72 @@ +#ifndef Py_INTERNAL_PATHCONFIG_H +#define Py_INTERNAL_PATHCONFIG_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +typedef struct _PyPathConfig { + /* Full path to the Python program */ + wchar_t *program_full_path; + wchar_t *prefix; + wchar_t *exec_prefix; + /* Set by Py_SetPath(), or computed by _PyConfig_InitPathConfig() */ + wchar_t *module_search_path; + /* Python program name */ + wchar_t *program_name; + /* Set by Py_SetPythonHome() or PYTHONHOME environment variable */ + wchar_t *home; +#ifdef MS_WINDOWS + /* isolated and site_import are used to set Py_IsolatedFlag and + Py_NoSiteFlag flags on Windows in read_pth_file(). These fields + are ignored when their value are equal to -1 (unset). */ + int isolated; + int site_import; + /* Set when a venv is detected */ + wchar_t *base_executable; +#endif +} _PyPathConfig; + +#ifdef MS_WINDOWS +# define _PyPathConfig_INIT \ + {.module_search_path = NULL, \ + .isolated = -1, \ + .site_import = -1} +#else +# define _PyPathConfig_INIT \ + {.module_search_path = NULL} +#endif +/* Note: _PyPathConfig_INIT sets other fields to 0/NULL */ + +PyAPI_DATA(_PyPathConfig) _Py_path_config; +#ifdef MS_WINDOWS +PyAPI_DATA(wchar_t*) _Py_dll_path; +#endif + +extern void _PyPathConfig_ClearGlobal(void); + +extern PyStatus _PyPathConfig_Calculate( + _PyPathConfig *pathconfig, + const PyConfig *config); +extern int _PyPathConfig_ComputeSysPath0( + const PyWideStringList *argv, + PyObject **path0); +extern PyStatus _Py_FindEnvConfigValue( + FILE *env_file, + const wchar_t *key, + wchar_t **value_p); + +#ifdef MS_WINDOWS +extern wchar_t* _Py_GetDLLPath(void); +#endif + +extern PyStatus _PyConfig_WritePathConfig(const PyConfig *config); +extern void _Py_DumpPathConfig(PyThreadState *tstate); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_PATHCONFIG_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_pyerrors.h b/scripts/build-windows/py39-libs/include/internal/pycore_pyerrors.h new file mode 100644 index 000000000..6140cf375 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_pyerrors.h @@ -0,0 +1,90 @@ +#ifndef Py_INTERNAL_PYERRORS_H +#define Py_INTERNAL_PYERRORS_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +static inline PyObject* _PyErr_Occurred(PyThreadState *tstate) +{ + assert(tstate != NULL); + return tstate->curexc_type; +} + +static inline void _PyErr_ClearExcState(_PyErr_StackItem *exc_state) +{ + PyObject *t, *v, *tb; + t = exc_state->exc_type; + v = exc_state->exc_value; + tb = exc_state->exc_traceback; + exc_state->exc_type = NULL; + exc_state->exc_value = NULL; + exc_state->exc_traceback = NULL; + Py_XDECREF(t); + Py_XDECREF(v); + Py_XDECREF(tb); +} + + +PyAPI_FUNC(void) _PyErr_Fetch( + PyThreadState *tstate, + PyObject **type, + PyObject **value, + PyObject **traceback); + +PyAPI_FUNC(int) _PyErr_ExceptionMatches( + PyThreadState *tstate, + PyObject *exc); + +PyAPI_FUNC(void) _PyErr_Restore( + PyThreadState *tstate, + PyObject *type, + PyObject *value, + PyObject *traceback); + +PyAPI_FUNC(void) _PyErr_SetObject( + PyThreadState *tstate, + PyObject *type, + PyObject *value); + +PyAPI_FUNC(void) _PyErr_ChainStackItem( + _PyErr_StackItem *exc_info); + +PyAPI_FUNC(void) _PyErr_Clear(PyThreadState *tstate); + +PyAPI_FUNC(void) _PyErr_SetNone(PyThreadState *tstate, PyObject *exception); + +PyAPI_FUNC(PyObject *) _PyErr_NoMemory(PyThreadState *tstate); + +PyAPI_FUNC(void) _PyErr_SetString( + PyThreadState *tstate, + PyObject *exception, + const char *string); + +PyAPI_FUNC(PyObject *) _PyErr_Format( + PyThreadState *tstate, + PyObject *exception, + const char *format, + ...); + +PyAPI_FUNC(void) _PyErr_NormalizeException( + PyThreadState *tstate, + PyObject **exc, + PyObject **val, + PyObject **tb); + +PyAPI_FUNC(PyObject *) _PyErr_FormatFromCauseTstate( + PyThreadState *tstate, + PyObject *exception, + const char *format, + ...); + +PyAPI_FUNC(int) _PyErr_CheckSignalsTstate(PyThreadState *tstate); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_PYERRORS_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_pyhash.h b/scripts/build-windows/py39-libs/include/internal/pycore_pyhash.h new file mode 100644 index 000000000..53d44d90c --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_pyhash.h @@ -0,0 +1,10 @@ +#ifndef Py_INTERNAL_HASH_H +#define Py_INTERNAL_HASH_H + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +uint64_t _Py_KeyedHash(uint64_t, const char *, Py_ssize_t); + +#endif diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_pylifecycle.h b/scripts/build-windows/py39-libs/include/internal/pycore_pylifecycle.h new file mode 100644 index 000000000..89f99a417 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_pylifecycle.h @@ -0,0 +1,114 @@ +#ifndef Py_INTERNAL_LIFECYCLE_H +#define Py_INTERNAL_LIFECYCLE_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +/* Forward declarations */ +struct _PyArgv; +struct pyruntimestate; + +/* True if the main interpreter thread exited due to an unhandled + * KeyboardInterrupt exception, suggesting the user pressed ^C. */ +PyAPI_DATA(int) _Py_UnhandledKeyboardInterrupt; + +extern int _Py_SetFileSystemEncoding( + const char *encoding, + const char *errors); +extern void _Py_ClearFileSystemEncoding(void); +extern PyStatus _PyUnicode_InitEncodings(PyThreadState *tstate); +#ifdef MS_WINDOWS +extern int _PyUnicode_EnableLegacyWindowsFSEncoding(void); +#endif + +PyAPI_FUNC(void) _Py_ClearStandardStreamEncoding(void); + +PyAPI_FUNC(int) _Py_IsLocaleCoercionTarget(const char *ctype_loc); + +/* Various one-time initializers */ + +extern PyStatus _PyUnicode_Init(void); +extern int _PyStructSequence_Init(void); +extern int _PyLong_Init(PyThreadState *tstate); +extern PyStatus _PyFaulthandler_Init(int enable); +extern int _PyTraceMalloc_Init(int enable); +extern PyObject * _PyBuiltin_Init(PyThreadState *tstate); +extern PyStatus _PySys_Create( + PyThreadState *tstate, + PyObject **sysmod_p); +extern PyStatus _PySys_ReadPreinitWarnOptions(PyWideStringList *options); +extern PyStatus _PySys_ReadPreinitXOptions(PyConfig *config); +extern int _PySys_InitMain(PyThreadState *tstate); +extern PyStatus _PyExc_Init(void); +extern PyStatus _PyErr_Init(void); +extern PyStatus _PyBuiltins_AddExceptions(PyObject * bltinmod); +extern PyStatus _PyImportHooks_Init(PyThreadState *tstate); +extern int _PyFloat_Init(void); +extern PyStatus _Py_HashRandomization_Init(const PyConfig *); + +extern PyStatus _PyTypes_Init(void); +extern PyStatus _PyTypes_InitSlotDefs(void); +extern PyStatus _PyImportZip_Init(PyThreadState *tstate); +extern PyStatus _PyGC_Init(PyThreadState *tstate); + + +/* Various internal finalizers */ + +extern void _PyFrame_Fini(void); +extern void _PyDict_Fini(void); +extern void _PyTuple_Fini(void); +extern void _PyList_Fini(void); +extern void _PySet_Fini(void); +extern void _PyBytes_Fini(void); +extern void _PyFloat_Fini(void); +extern void _PySlice_Fini(void); +extern void _PyAsyncGen_Fini(void); + +extern int _PySignal_Init(int install_signal_handlers); +extern void PyOS_FiniInterrupts(void); + +extern void _PyExc_Fini(void); +extern void _PyImport_Fini(void); +extern void _PyImport_Fini2(void); +extern void _PyGC_Fini(PyThreadState *tstate); +extern void _PyType_Fini(void); +extern void _Py_HashRandomization_Fini(void); +extern void _PyUnicode_Fini(PyThreadState *tstate); +extern void _PyLong_Fini(PyThreadState *tstate); +extern void _PyFaulthandler_Fini(void); +extern void _PyHash_Fini(void); +extern void _PyTraceMalloc_Fini(void); +extern void _PyWarnings_Fini(PyInterpreterState *interp); +extern void _PyAST_Fini(void); + +extern PyStatus _PyGILState_Init(PyThreadState *tstate); +extern void _PyGILState_Fini(PyThreadState *tstate); + +PyAPI_FUNC(void) _PyGC_DumpShutdownStats(PyThreadState *tstate); + +PyAPI_FUNC(PyStatus) _Py_PreInitializeFromPyArgv( + const PyPreConfig *src_config, + const struct _PyArgv *args); +PyAPI_FUNC(PyStatus) _Py_PreInitializeFromConfig( + const PyConfig *config, + const struct _PyArgv *args); + + +PyAPI_FUNC(int) _Py_HandleSystemExit(int *exitcode_p); + +PyAPI_FUNC(PyObject*) _PyErr_WriteUnraisableDefaultHook(PyObject *unraisable); + +PyAPI_FUNC(void) _PyErr_Print(PyThreadState *tstate); +PyAPI_FUNC(void) _PyErr_Display(PyObject *file, PyObject *exception, + PyObject *value, PyObject *tb); + +PyAPI_FUNC(void) _PyThreadState_DeleteCurrent(PyThreadState *tstate); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_LIFECYCLE_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_pymem.h b/scripts/build-windows/py39-libs/include/internal/pycore_pymem.h new file mode 100644 index 000000000..ba580bbe4 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_pymem.h @@ -0,0 +1,104 @@ +#ifndef Py_INTERNAL_PYMEM_H +#define Py_INTERNAL_PYMEM_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include "pymem.h" // PyMemAllocatorName + + +/* Set the memory allocator of the specified domain to the default. + Save the old allocator into *old_alloc if it's non-NULL. + Return on success, or return -1 if the domain is unknown. */ +PyAPI_FUNC(int) _PyMem_SetDefaultAllocator( + PyMemAllocatorDomain domain, + PyMemAllocatorEx *old_alloc); + +/* Special bytes broadcast into debug memory blocks at appropriate times. + Strings of these are unlikely to be valid addresses, floats, ints or + 7-bit ASCII. + + - PYMEM_CLEANBYTE: clean (newly allocated) memory + - PYMEM_DEADBYTE dead (newly freed) memory + - PYMEM_FORBIDDENBYTE: untouchable bytes at each end of a block + + Byte patterns 0xCB, 0xDB and 0xFB have been replaced with 0xCD, 0xDD and + 0xFD to use the same values than Windows CRT debug malloc() and free(). + If modified, _PyMem_IsPtrFreed() should be updated as well. */ +#define PYMEM_CLEANBYTE 0xCD +#define PYMEM_DEADBYTE 0xDD +#define PYMEM_FORBIDDENBYTE 0xFD + +/* Heuristic checking if a pointer value is newly allocated + (uninitialized), newly freed or NULL (is equal to zero). + + The pointer is not dereferenced, only the pointer value is checked. + + The heuristic relies on the debug hooks on Python memory allocators which + fills newly allocated memory with CLEANBYTE (0xCD) and newly freed memory + with DEADBYTE (0xDD). Detect also "untouchable bytes" marked + with FORBIDDENBYTE (0xFD). */ +static inline int _PyMem_IsPtrFreed(void *ptr) +{ + uintptr_t value = (uintptr_t)ptr; +#if SIZEOF_VOID_P == 8 + return (value == 0 + || value == (uintptr_t)0xCDCDCDCDCDCDCDCD + || value == (uintptr_t)0xDDDDDDDDDDDDDDDD + || value == (uintptr_t)0xFDFDFDFDFDFDFDFD); +#elif SIZEOF_VOID_P == 4 + return (value == 0 + || value == (uintptr_t)0xCDCDCDCD + || value == (uintptr_t)0xDDDDDDDD + || value == (uintptr_t)0xFDFDFDFD); +#else +# error "unknown pointer size" +#endif +} + +PyAPI_FUNC(int) _PyMem_GetAllocatorName( + const char *name, + PyMemAllocatorName *allocator); + +/* Configure the Python memory allocators. + Pass PYMEM_ALLOCATOR_DEFAULT to use default allocators. + PYMEM_ALLOCATOR_NOT_SET does nothing. */ +PyAPI_FUNC(int) _PyMem_SetupAllocators(PyMemAllocatorName allocator); + +/* bpo-35053: Expose _Py_tracemalloc_config for _Py_NewReference() + which access directly _Py_tracemalloc_config.tracing for best + performances. */ +struct _PyTraceMalloc_Config { + /* Module initialized? + Variable protected by the GIL */ + enum { + TRACEMALLOC_NOT_INITIALIZED, + TRACEMALLOC_INITIALIZED, + TRACEMALLOC_FINALIZED + } initialized; + + /* Is tracemalloc tracing memory allocations? + Variable protected by the GIL */ + int tracing; + + /* limit of the number of frames in a traceback, 1 by default. + Variable protected by the GIL. */ + int max_nframe; +}; + +#define _PyTraceMalloc_Config_INIT \ + {.initialized = TRACEMALLOC_NOT_INITIALIZED, \ + .tracing = 0, \ + .max_nframe = 1} + +PyAPI_DATA(struct _PyTraceMalloc_Config) _Py_tracemalloc_config; + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_PYMEM_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_pystate.h b/scripts/build-windows/py39-libs/include/internal/pycore_pystate.h new file mode 100644 index 000000000..2088d5111 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_pystate.h @@ -0,0 +1,138 @@ +#ifndef Py_INTERNAL_PYSTATE_H +#define Py_INTERNAL_PYSTATE_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include "pycore_runtime.h" /* PyRuntimeState */ + + +/* Check if the current thread is the main thread. + Use _Py_IsMainInterpreter() to check if it's the main interpreter. */ +static inline int +_Py_IsMainThread(void) +{ + unsigned long thread = PyThread_get_thread_ident(); + return (thread == _PyRuntime.main_thread); +} + + +static inline int +_Py_IsMainInterpreter(PyThreadState* tstate) +{ + /* Use directly _PyRuntime rather than tstate->interp->runtime, since + this function is used in performance critical code path (ceval) */ + return (tstate->interp == _PyRuntime.interpreters.main); +} + + +/* Only handle signals on the main thread of the main interpreter. */ +static inline int +_Py_ThreadCanHandleSignals(PyInterpreterState *interp) +{ + return (_Py_IsMainThread() && interp == _PyRuntime.interpreters.main); +} + + +/* Only execute pending calls on the main thread. */ +static inline int +_Py_ThreadCanHandlePendingCalls(void) +{ + return _Py_IsMainThread(); +} + + +/* Variable and macro for in-line access to current thread + and interpreter state */ + +static inline PyThreadState* +_PyRuntimeState_GetThreadState(_PyRuntimeState *runtime) +{ + return (PyThreadState*)_Py_atomic_load_relaxed(&runtime->gilstate.tstate_current); +} + +/* Get the current Python thread state. + + Efficient macro reading directly the 'gilstate.tstate_current' atomic + variable. The macro is unsafe: it does not check for error and it can + return NULL. + + The caller must hold the GIL. + + See also PyThreadState_Get() and PyThreadState_GET(). */ +static inline PyThreadState* +_PyThreadState_GET(void) +{ + return _PyRuntimeState_GetThreadState(&_PyRuntime); +} + +/* Redefine PyThreadState_GET() as an alias to _PyThreadState_GET() */ +#undef PyThreadState_GET +#define PyThreadState_GET() _PyThreadState_GET() + +PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalError_TstateNULL(const char *func); + +static inline void +_Py_EnsureFuncTstateNotNULL(const char *func, PyThreadState *tstate) +{ + if (tstate == NULL) { + _Py_FatalError_TstateNULL(func); + } +} + +// Call Py_FatalError() if tstate is NULL +#define _Py_EnsureTstateNotNULL(tstate) \ + _Py_EnsureFuncTstateNotNULL(__func__, tstate) + + +/* Get the current interpreter state. + + The macro is unsafe: it does not check for error and it can return NULL. + + The caller must hold the GIL. + + See also _PyInterpreterState_Get() + and _PyGILState_GetInterpreterStateUnsafe(). */ +static inline PyInterpreterState* _PyInterpreterState_GET(void) { + PyThreadState *tstate = _PyThreadState_GET(); +#ifdef Py_DEBUG + _Py_EnsureTstateNotNULL(tstate); +#endif + return tstate->interp; +} + + +/* Other */ + +PyAPI_FUNC(void) _PyThreadState_Init( + PyThreadState *tstate); +PyAPI_FUNC(void) _PyThreadState_DeleteExcept( + _PyRuntimeState *runtime, + PyThreadState *tstate); + +PyAPI_FUNC(PyThreadState *) _PyThreadState_Swap( + struct _gilstate_runtime_state *gilstate, + PyThreadState *newts); + +PyAPI_FUNC(PyStatus) _PyInterpreterState_Enable(_PyRuntimeState *runtime); +PyAPI_FUNC(void) _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime); + +PyAPI_FUNC(void) _PyGILState_Reinit(_PyRuntimeState *runtime); + + +PyAPI_FUNC(int) _PyState_AddModule( + PyThreadState *tstate, + PyObject* module, + struct PyModuleDef* def); + + +PyAPI_FUNC(int) _PyOS_InterruptOccurred(PyThreadState *tstate); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_PYSTATE_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_runtime.h b/scripts/build-windows/py39-libs/include/internal/pycore_runtime.h new file mode 100644 index 000000000..f0dff829f --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_runtime.h @@ -0,0 +1,144 @@ +#ifndef Py_INTERNAL_RUNTIME_H +#define Py_INTERNAL_RUNTIME_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include "pycore_atomic.h" /* _Py_atomic_address */ +#include "pycore_gil.h" // struct _gil_runtime_state + +/* ceval state */ + +struct _ceval_runtime_state { + /* Request for checking signals. It is shared by all interpreters (see + bpo-40513). Any thread of any interpreter can receive a signal, but only + the main thread of the main interpreter can handle signals: see + _Py_ThreadCanHandleSignals(). */ + _Py_atomic_int signals_pending; + struct _gil_runtime_state gil; +}; + +/* GIL state */ + +struct _gilstate_runtime_state { + /* bpo-26558: Flag to disable PyGILState_Check(). + If set to non-zero, PyGILState_Check() always return 1. */ + int check_enabled; + /* Assuming the current thread holds the GIL, this is the + PyThreadState for the current thread. */ + _Py_atomic_address tstate_current; + /* The single PyInterpreterState used by this process' + GILState implementation + */ + /* TODO: Given interp_main, it may be possible to kill this ref */ + PyInterpreterState *autoInterpreterState; + Py_tss_t autoTSSkey; +}; + +/* Runtime audit hook state */ + +typedef struct _Py_AuditHookEntry { + struct _Py_AuditHookEntry *next; + Py_AuditHookFunction hookCFunction; + void *userData; +} _Py_AuditHookEntry; + +/* Full Python runtime state */ + +typedef struct pyruntimestate { + /* Is running Py_PreInitialize()? */ + int preinitializing; + + /* Is Python preinitialized? Set to 1 by Py_PreInitialize() */ + int preinitialized; + + /* Is Python core initialized? Set to 1 by _Py_InitializeCore() */ + int core_initialized; + + /* Is Python fully initialized? Set to 1 by Py_Initialize() */ + int initialized; + + /* Set by Py_FinalizeEx(). Only reset to NULL if Py_Initialize() + is called again. + + Use _PyRuntimeState_GetFinalizing() and _PyRuntimeState_SetFinalizing() + to access it, don't access it directly. */ + _Py_atomic_address _finalizing; + + struct pyinterpreters { + PyThread_type_lock mutex; + PyInterpreterState *head; + PyInterpreterState *main; + /* _next_interp_id is an auto-numbered sequence of small + integers. It gets initialized in _PyInterpreterState_Init(), + which is called in Py_Initialize(), and used in + PyInterpreterState_New(). A negative interpreter ID + indicates an error occurred. The main interpreter will + always have an ID of 0. Overflow results in a RuntimeError. + If that becomes a problem later then we can adjust, e.g. by + using a Python int. */ + int64_t next_id; + } interpreters; + // XXX Remove this field once we have a tp_* slot. + struct _xidregistry { + PyThread_type_lock mutex; + struct _xidregitem *head; + } xidregistry; + + unsigned long main_thread; + +#define NEXITFUNCS 32 + void (*exitfuncs[NEXITFUNCS])(void); + int nexitfuncs; + + struct _ceval_runtime_state ceval; + struct _gilstate_runtime_state gilstate; + + PyPreConfig preconfig; + + Py_OpenCodeHookFunction open_code_hook; + void *open_code_userdata; + _Py_AuditHookEntry *audit_hook_head; + + // XXX Consolidate globals found via the check-c-globals script. +} _PyRuntimeState; + +#define _PyRuntimeState_INIT \ + {.preinitialized = 0, .core_initialized = 0, .initialized = 0} +/* Note: _PyRuntimeState_INIT sets other fields to 0/NULL */ + + +PyAPI_DATA(_PyRuntimeState) _PyRuntime; + +PyAPI_FUNC(PyStatus) _PyRuntimeState_Init(_PyRuntimeState *runtime); +PyAPI_FUNC(void) _PyRuntimeState_Fini(_PyRuntimeState *runtime); + +#ifdef HAVE_FORK +PyAPI_FUNC(void) _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime); +#endif + +/* Initialize _PyRuntimeState. + Return NULL on success, or return an error message on failure. */ +PyAPI_FUNC(PyStatus) _PyRuntime_Initialize(void); + +PyAPI_FUNC(void) _PyRuntime_Finalize(void); + + +static inline PyThreadState* +_PyRuntimeState_GetFinalizing(_PyRuntimeState *runtime) { + return (PyThreadState*)_Py_atomic_load_relaxed(&runtime->_finalizing); +} + +static inline void +_PyRuntimeState_SetFinalizing(_PyRuntimeState *runtime, PyThreadState *tstate) { + _Py_atomic_store_relaxed(&runtime->_finalizing, (uintptr_t)tstate); +} + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_RUNTIME_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_sysmodule.h b/scripts/build-windows/py39-libs/include/internal/pycore_sysmodule.h new file mode 100644 index 000000000..363eb873b --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_sysmodule.h @@ -0,0 +1,24 @@ +#ifndef Py_INTERNAL_SYSMODULE_H +#define Py_INTERNAL_SYSMODULE_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +PyAPI_FUNC(int) _PySys_Audit( + PyThreadState *tstate, + const char *event, + const char *argFormat, + ...); + +/* We want minimal exposure of this function, so use extern rather than + PyAPI_FUNC() to not export the symbol. */ +extern void _PySys_ClearAuditHooks(PyThreadState *tstate); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_SYSMODULE_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_traceback.h b/scripts/build-windows/py39-libs/include/internal/pycore_traceback.h new file mode 100644 index 000000000..274e2d0de --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_traceback.h @@ -0,0 +1,97 @@ +#ifndef Py_INTERNAL_TRACEBACK_H +#define Py_INTERNAL_TRACEBACK_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +/* Forward declaration */ +struct _is; + +/* Write the Python traceback into the file 'fd'. For example: + + Traceback (most recent call first): + File "xxx", line xxx in + File "xxx", line xxx in + ... + File "xxx", line xxx in + + This function is written for debug purpose only, to dump the traceback in + the worst case: after a segmentation fault, at fatal error, etc. That's why, + it is very limited. Strings are truncated to 100 characters and encoded to + ASCII with backslashreplace. It doesn't write the source code, only the + function name, filename and line number of each frame. Write only the first + 100 frames: if the traceback is truncated, write the line " ...". + + This function is signal safe. */ + +PyAPI_FUNC(void) _Py_DumpTraceback( + int fd, + PyThreadState *tstate); + +/* Write the traceback of all threads into the file 'fd'. current_thread can be + NULL. + + Return NULL on success, or an error message on error. + + This function is written for debug purpose only. It calls + _Py_DumpTraceback() for each thread, and so has the same limitations. It + only write the traceback of the first 100 threads: write "..." if there are + more threads. + + If current_tstate is NULL, the function tries to get the Python thread state + of the current thread. It is not an error if the function is unable to get + the current Python thread state. + + If interp is NULL, the function tries to get the interpreter state from + the current Python thread state, or from + _PyGILState_GetInterpreterStateUnsafe() in last resort. + + It is better to pass NULL to interp and current_tstate, the function tries + different options to retrieve these informations. + + This function is signal safe. */ + +PyAPI_FUNC(const char*) _Py_DumpTracebackThreads( + int fd, + struct _is *interp, + PyThreadState *current_tstate); + +/* Write a Unicode object into the file descriptor fd. Encode the string to + ASCII using the backslashreplace error handler. + + Do nothing if text is not a Unicode object. The function accepts Unicode + string which is not ready (PyUnicode_WCHAR_KIND). + + This function is signal safe. */ +PyAPI_FUNC(void) _Py_DumpASCII(int fd, PyObject *text); + +/* Format an integer as decimal into the file descriptor fd. + + This function is signal safe. */ +PyAPI_FUNC(void) _Py_DumpDecimal( + int fd, + unsigned long value); + +/* Format an integer as hexadecimal into the file descriptor fd with at least + width digits. + + The maximum width is sizeof(unsigned long)*2 digits. + + This function is signal safe. */ +PyAPI_FUNC(void) _Py_DumpHexadecimal( + int fd, + unsigned long value, + Py_ssize_t width); + +PyAPI_FUNC(PyObject*) _PyTraceBack_FromFrame( + PyObject *tb_next, + PyFrameObject *frame); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_TRACEBACK_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_tupleobject.h b/scripts/build-windows/py39-libs/include/internal/pycore_tupleobject.h new file mode 100644 index 000000000..10772fe38 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_tupleobject.h @@ -0,0 +1,19 @@ +#ifndef Py_INTERNAL_TUPLEOBJECT_H +#define Py_INTERNAL_TUPLEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include "tupleobject.h" /* _PyTuple_CAST() */ + +#define _PyTuple_ITEMS(op) (_PyTuple_CAST(op)->ob_item) +PyAPI_FUNC(PyObject *) _PyTuple_FromArray(PyObject *const *, Py_ssize_t); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_TUPLEOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/internal/pycore_warnings.h b/scripts/build-windows/py39-libs/include/internal/pycore_warnings.h new file mode 100644 index 000000000..23c50d66c --- /dev/null +++ b/scripts/build-windows/py39-libs/include/internal/pycore_warnings.h @@ -0,0 +1,25 @@ +#ifndef Py_INTERNAL_WARNINGS_H +#define Py_INTERNAL_WARNINGS_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +struct _warnings_runtime_state { + /* Both 'filters' and 'onceregistry' can be set in warnings.py; + get_warnings_attr() will reset these variables accordingly. */ + PyObject *filters; /* List */ + PyObject *once_registry; /* Dict */ + PyObject *default_action; /* String */ + long filters_version; +}; + +extern PyStatus _PyWarnings_InitState(PyThreadState *tstate); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_WARNINGS_H */ diff --git a/scripts/build-windows/py39-libs/include/interpreteridobject.h b/scripts/build-windows/py39-libs/include/interpreteridobject.h new file mode 100644 index 000000000..c8b5f9536 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/interpreteridobject.h @@ -0,0 +1,17 @@ +#ifndef Py_INTERPRETERIDOBJECT_H +#define Py_INTERPRETERIDOBJECT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_INTERPRETERIDOBJECT_H +# include "cpython/interpreteridobject.h" +# undef Py_CPYTHON_INTERPRETERIDOBJECT_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERPRETERIDOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/intrcheck.h b/scripts/build-windows/py39-libs/include/intrcheck.h new file mode 100644 index 000000000..a65bbb11d --- /dev/null +++ b/scripts/build-windows/py39-libs/include/intrcheck.h @@ -0,0 +1,33 @@ + +#ifndef Py_INTRCHECK_H +#define Py_INTRCHECK_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(int) PyOS_InterruptOccurred(void); +PyAPI_FUNC(void) PyOS_InitInterrupts(void); +#ifdef HAVE_FORK +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 +PyAPI_FUNC(void) PyOS_BeforeFork(void); +PyAPI_FUNC(void) PyOS_AfterFork_Parent(void); +PyAPI_FUNC(void) PyOS_AfterFork_Child(void); +#endif +#endif +/* Deprecated, please use PyOS_AfterFork_Child() instead */ +Py_DEPRECATED(3.7) PyAPI_FUNC(void) PyOS_AfterFork(void); + +#ifndef Py_LIMITED_API +PyAPI_FUNC(int) _PyOS_IsMainThread(void); +PyAPI_FUNC(void) _PySignal_AfterFork(void); + +#ifdef MS_WINDOWS +/* windows.h is not included by Python.h so use void* instead of HANDLE */ +PyAPI_FUNC(void*) _PyOS_SigintEvent(void); +#endif +#endif /* !Py_LIMITED_API */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTRCHECK_H */ diff --git a/scripts/build-windows/py39-libs/include/iterobject.h b/scripts/build-windows/py39-libs/include/iterobject.h new file mode 100644 index 000000000..8022a6ea9 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/iterobject.h @@ -0,0 +1,24 @@ +#ifndef Py_ITEROBJECT_H +#define Py_ITEROBJECT_H +/* Iterators (the basic kind, over a sequence) */ +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PySeqIter_Type; +PyAPI_DATA(PyTypeObject) PyCallIter_Type; + +#define PySeqIter_Check(op) Py_IS_TYPE(op, &PySeqIter_Type) + +PyAPI_FUNC(PyObject *) PySeqIter_New(PyObject *); + + +#define PyCallIter_Check(op) Py_IS_TYPE(op, &PyCallIter_Type) + +PyAPI_FUNC(PyObject *) PyCallIter_New(PyObject *, PyObject *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_ITEROBJECT_H */ + diff --git a/scripts/build-windows/py39-libs/include/listobject.h b/scripts/build-windows/py39-libs/include/listobject.h new file mode 100644 index 000000000..28273493e --- /dev/null +++ b/scripts/build-windows/py39-libs/include/listobject.h @@ -0,0 +1,52 @@ +/* List object interface + + Another generally useful object type is a list of object pointers. + This is a mutable type: the list items can be changed, and items can be + added or removed. Out-of-range indices or non-list objects are ignored. + + WARNING: PyList_SetItem does not increment the new item's reference count, + but does decrement the reference count of the item it replaces, if not nil. + It does *decrement* the reference count if it is *not* inserted in the list. + Similarly, PyList_GetItem does not increment the returned item's reference + count. +*/ + +#ifndef Py_LISTOBJECT_H +#define Py_LISTOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PyList_Type; +PyAPI_DATA(PyTypeObject) PyListIter_Type; +PyAPI_DATA(PyTypeObject) PyListRevIter_Type; + +#define PyList_Check(op) \ + PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LIST_SUBCLASS) +#define PyList_CheckExact(op) Py_IS_TYPE(op, &PyList_Type) + +PyAPI_FUNC(PyObject *) PyList_New(Py_ssize_t size); +PyAPI_FUNC(Py_ssize_t) PyList_Size(PyObject *); + +PyAPI_FUNC(PyObject *) PyList_GetItem(PyObject *, Py_ssize_t); +PyAPI_FUNC(int) PyList_SetItem(PyObject *, Py_ssize_t, PyObject *); +PyAPI_FUNC(int) PyList_Insert(PyObject *, Py_ssize_t, PyObject *); +PyAPI_FUNC(int) PyList_Append(PyObject *, PyObject *); + +PyAPI_FUNC(PyObject *) PyList_GetSlice(PyObject *, Py_ssize_t, Py_ssize_t); +PyAPI_FUNC(int) PyList_SetSlice(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *); + +PyAPI_FUNC(int) PyList_Sort(PyObject *); +PyAPI_FUNC(int) PyList_Reverse(PyObject *); +PyAPI_FUNC(PyObject *) PyList_AsTuple(PyObject *); + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_LISTOBJECT_H +# include "cpython/listobject.h" +# undef Py_CPYTHON_LISTOBJECT_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_LISTOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/longintrepr.h b/scripts/build-windows/py39-libs/include/longintrepr.h new file mode 100644 index 000000000..cc02f2b33 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/longintrepr.h @@ -0,0 +1,99 @@ +#ifndef Py_LIMITED_API +#ifndef Py_LONGINTREPR_H +#define Py_LONGINTREPR_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* This is published for the benefit of "friends" marshal.c and _decimal.c. */ + +/* Parameters of the integer representation. There are two different + sets of parameters: one set for 30-bit digits, stored in an unsigned 32-bit + integer type, and one set for 15-bit digits with each digit stored in an + unsigned short. The value of PYLONG_BITS_IN_DIGIT, defined either at + configure time or in pyport.h, is used to decide which digit size to use. + + Type 'digit' should be able to hold 2*PyLong_BASE-1, and type 'twodigits' + should be an unsigned integer type able to hold all integers up to + PyLong_BASE*PyLong_BASE-1. x_sub assumes that 'digit' is an unsigned type, + and that overflow is handled by taking the result modulo 2**N for some N > + PyLong_SHIFT. The majority of the code doesn't care about the precise + value of PyLong_SHIFT, but there are some notable exceptions: + + - long_pow() requires that PyLong_SHIFT be divisible by 5 + + - PyLong_{As,From}ByteArray require that PyLong_SHIFT be at least 8 + + - long_hash() requires that PyLong_SHIFT is *strictly* less than the number + of bits in an unsigned long, as do the PyLong <-> long (or unsigned long) + conversion functions + + - the Python int <-> size_t/Py_ssize_t conversion functions expect that + PyLong_SHIFT is strictly less than the number of bits in a size_t + + - the marshal code currently expects that PyLong_SHIFT is a multiple of 15 + + - NSMALLNEGINTS and NSMALLPOSINTS should be small enough to fit in a single + digit; with the current values this forces PyLong_SHIFT >= 9 + + The values 15 and 30 should fit all of the above requirements, on any + platform. +*/ + +#if PYLONG_BITS_IN_DIGIT == 30 +typedef uint32_t digit; +typedef int32_t sdigit; /* signed variant of digit */ +typedef uint64_t twodigits; +typedef int64_t stwodigits; /* signed variant of twodigits */ +#define PyLong_SHIFT 30 +#define _PyLong_DECIMAL_SHIFT 9 /* max(e such that 10**e fits in a digit) */ +#define _PyLong_DECIMAL_BASE ((digit)1000000000) /* 10 ** DECIMAL_SHIFT */ +#elif PYLONG_BITS_IN_DIGIT == 15 +typedef unsigned short digit; +typedef short sdigit; /* signed variant of digit */ +typedef unsigned long twodigits; +typedef long stwodigits; /* signed variant of twodigits */ +#define PyLong_SHIFT 15 +#define _PyLong_DECIMAL_SHIFT 4 /* max(e such that 10**e fits in a digit) */ +#define _PyLong_DECIMAL_BASE ((digit)10000) /* 10 ** DECIMAL_SHIFT */ +#else +#error "PYLONG_BITS_IN_DIGIT should be 15 or 30" +#endif +#define PyLong_BASE ((digit)1 << PyLong_SHIFT) +#define PyLong_MASK ((digit)(PyLong_BASE - 1)) + +#if PyLong_SHIFT % 5 != 0 +#error "longobject.c requires that PyLong_SHIFT be divisible by 5" +#endif + +/* Long integer representation. + The absolute value of a number is equal to + SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i) + Negative numbers are represented with ob_size < 0; + zero is represented by ob_size == 0. + In a normalized number, ob_digit[abs(ob_size)-1] (the most significant + digit) is never zero. Also, in all cases, for all valid i, + 0 <= ob_digit[i] <= MASK. + The allocation function takes care of allocating extra memory + so that ob_digit[0] ... ob_digit[abs(ob_size)-1] are actually available. + + CAUTION: Generic code manipulating subtypes of PyVarObject has to + aware that ints abuse ob_size's sign bit. +*/ + +struct _longobject { + PyObject_VAR_HEAD + digit ob_digit[1]; +}; + +PyAPI_FUNC(PyLongObject *) _PyLong_New(Py_ssize_t); + +/* Return a copy of src. */ +PyAPI_FUNC(PyObject *) _PyLong_Copy(PyLongObject *src); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_LONGINTREPR_H */ +#endif /* Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/longobject.h b/scripts/build-windows/py39-libs/include/longobject.h new file mode 100644 index 000000000..3ff911a19 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/longobject.h @@ -0,0 +1,242 @@ +#ifndef Py_LONGOBJECT_H +#define Py_LONGOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Long (arbitrary precision) integer object interface */ + +typedef struct _longobject PyLongObject; /* Revealed in longintrepr.h */ + +PyAPI_DATA(PyTypeObject) PyLong_Type; + +#define PyLong_Check(op) \ + PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LONG_SUBCLASS) +#define PyLong_CheckExact(op) Py_IS_TYPE(op, &PyLong_Type) + +PyAPI_FUNC(PyObject *) PyLong_FromLong(long); +PyAPI_FUNC(PyObject *) PyLong_FromUnsignedLong(unsigned long); +PyAPI_FUNC(PyObject *) PyLong_FromSize_t(size_t); +PyAPI_FUNC(PyObject *) PyLong_FromSsize_t(Py_ssize_t); +PyAPI_FUNC(PyObject *) PyLong_FromDouble(double); +PyAPI_FUNC(long) PyLong_AsLong(PyObject *); +PyAPI_FUNC(long) PyLong_AsLongAndOverflow(PyObject *, int *); +PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *); +PyAPI_FUNC(size_t) PyLong_AsSize_t(PyObject *); +PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *); +PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *); +#ifndef Py_LIMITED_API +PyAPI_FUNC(int) _PyLong_AsInt(PyObject *); +#endif +PyAPI_FUNC(PyObject *) PyLong_GetInfo(void); + +/* It may be useful in the future. I've added it in the PyInt -> PyLong + cleanup to keep the extra information. [CH] */ +#define PyLong_AS_LONG(op) PyLong_AsLong(op) + +/* Issue #1983: pid_t can be longer than a C long on some systems */ +#if !defined(SIZEOF_PID_T) || SIZEOF_PID_T == SIZEOF_INT +#define _Py_PARSE_PID "i" +#define PyLong_FromPid PyLong_FromLong +#define PyLong_AsPid PyLong_AsLong +#elif SIZEOF_PID_T == SIZEOF_LONG +#define _Py_PARSE_PID "l" +#define PyLong_FromPid PyLong_FromLong +#define PyLong_AsPid PyLong_AsLong +#elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG +#define _Py_PARSE_PID "L" +#define PyLong_FromPid PyLong_FromLongLong +#define PyLong_AsPid PyLong_AsLongLong +#else +#error "sizeof(pid_t) is neither sizeof(int), sizeof(long) or sizeof(long long)" +#endif /* SIZEOF_PID_T */ + +#if SIZEOF_VOID_P == SIZEOF_INT +# define _Py_PARSE_INTPTR "i" +# define _Py_PARSE_UINTPTR "I" +#elif SIZEOF_VOID_P == SIZEOF_LONG +# define _Py_PARSE_INTPTR "l" +# define _Py_PARSE_UINTPTR "k" +#elif defined(SIZEOF_LONG_LONG) && SIZEOF_VOID_P == SIZEOF_LONG_LONG +# define _Py_PARSE_INTPTR "L" +# define _Py_PARSE_UINTPTR "K" +#else +# error "void* different in size from int, long and long long" +#endif /* SIZEOF_VOID_P */ + +#ifndef Py_LIMITED_API +PyAPI_FUNC(int) _PyLong_UnsignedShort_Converter(PyObject *, void *); +PyAPI_FUNC(int) _PyLong_UnsignedInt_Converter(PyObject *, void *); +PyAPI_FUNC(int) _PyLong_UnsignedLong_Converter(PyObject *, void *); +PyAPI_FUNC(int) _PyLong_UnsignedLongLong_Converter(PyObject *, void *); +PyAPI_FUNC(int) _PyLong_Size_t_Converter(PyObject *, void *); +#endif + +/* Used by Python/mystrtoul.c, _PyBytes_FromHex(), + _PyBytes_DecodeEscape(), etc. */ +#ifndef Py_LIMITED_API +PyAPI_DATA(unsigned char) _PyLong_DigitValue[256]; +#endif + +/* _PyLong_Frexp returns a double x and an exponent e such that the + true value is approximately equal to x * 2**e. e is >= 0. x is + 0.0 if and only if the input is 0 (in which case, e and x are both + zeroes); otherwise, 0.5 <= abs(x) < 1.0. On overflow, which is + possible if the number of bits doesn't fit into a Py_ssize_t, sets + OverflowError and returns -1.0 for x, 0 for e. */ +#ifndef Py_LIMITED_API +PyAPI_FUNC(double) _PyLong_Frexp(PyLongObject *a, Py_ssize_t *e); +#endif + +PyAPI_FUNC(double) PyLong_AsDouble(PyObject *); +PyAPI_FUNC(PyObject *) PyLong_FromVoidPtr(void *); +PyAPI_FUNC(void *) PyLong_AsVoidPtr(PyObject *); + +PyAPI_FUNC(PyObject *) PyLong_FromLongLong(long long); +PyAPI_FUNC(PyObject *) PyLong_FromUnsignedLongLong(unsigned long long); +PyAPI_FUNC(long long) PyLong_AsLongLong(PyObject *); +PyAPI_FUNC(unsigned long long) PyLong_AsUnsignedLongLong(PyObject *); +PyAPI_FUNC(unsigned long long) PyLong_AsUnsignedLongLongMask(PyObject *); +PyAPI_FUNC(long long) PyLong_AsLongLongAndOverflow(PyObject *, int *); + +PyAPI_FUNC(PyObject *) PyLong_FromString(const char *, char **, int); +#ifndef Py_LIMITED_API +Py_DEPRECATED(3.3) +PyAPI_FUNC(PyObject *) PyLong_FromUnicode(Py_UNICODE*, Py_ssize_t, int); +PyAPI_FUNC(PyObject *) PyLong_FromUnicodeObject(PyObject *u, int base); +PyAPI_FUNC(PyObject *) _PyLong_FromBytes(const char *, Py_ssize_t, int); +#endif + +#ifndef Py_LIMITED_API +/* _PyLong_Sign. Return 0 if v is 0, -1 if v < 0, +1 if v > 0. + v must not be NULL, and must be a normalized long. + There are no error cases. +*/ +PyAPI_FUNC(int) _PyLong_Sign(PyObject *v); + + +/* _PyLong_NumBits. Return the number of bits needed to represent the + absolute value of a long. For example, this returns 1 for 1 and -1, 2 + for 2 and -2, and 2 for 3 and -3. It returns 0 for 0. + v must not be NULL, and must be a normalized long. + (size_t)-1 is returned and OverflowError set if the true result doesn't + fit in a size_t. +*/ +PyAPI_FUNC(size_t) _PyLong_NumBits(PyObject *v); + +/* _PyLong_DivmodNear. Given integers a and b, compute the nearest + integer q to the exact quotient a / b, rounding to the nearest even integer + in the case of a tie. Return (q, r), where r = a - q*b. The remainder r + will satisfy abs(r) <= abs(b)/2, with equality possible only if q is + even. +*/ +PyAPI_FUNC(PyObject *) _PyLong_DivmodNear(PyObject *, PyObject *); + +/* _PyLong_FromByteArray: View the n unsigned bytes as a binary integer in + base 256, and return a Python int with the same numeric value. + If n is 0, the integer is 0. Else: + If little_endian is 1/true, bytes[n-1] is the MSB and bytes[0] the LSB; + else (little_endian is 0/false) bytes[0] is the MSB and bytes[n-1] the + LSB. + If is_signed is 0/false, view the bytes as a non-negative integer. + If is_signed is 1/true, view the bytes as a 2's-complement integer, + non-negative if bit 0x80 of the MSB is clear, negative if set. + Error returns: + + Return NULL with the appropriate exception set if there's not + enough memory to create the Python int. +*/ +PyAPI_FUNC(PyObject *) _PyLong_FromByteArray( + const unsigned char* bytes, size_t n, + int little_endian, int is_signed); + +/* _PyLong_AsByteArray: Convert the least-significant 8*n bits of long + v to a base-256 integer, stored in array bytes. Normally return 0, + return -1 on error. + If little_endian is 1/true, store the MSB at bytes[n-1] and the LSB at + bytes[0]; else (little_endian is 0/false) store the MSB at bytes[0] and + the LSB at bytes[n-1]. + If is_signed is 0/false, it's an error if v < 0; else (v >= 0) n bytes + are filled and there's nothing special about bit 0x80 of the MSB. + If is_signed is 1/true, bytes is filled with the 2's-complement + representation of v's value. Bit 0x80 of the MSB is the sign bit. + Error returns (-1): + + is_signed is 0 and v < 0. TypeError is set in this case, and bytes + isn't altered. + + n isn't big enough to hold the full mathematical value of v. For + example, if is_signed is 0 and there are more digits in the v than + fit in n; or if is_signed is 1, v < 0, and n is just 1 bit shy of + being large enough to hold a sign bit. OverflowError is set in this + case, but bytes holds the least-significant n bytes of the true value. +*/ +PyAPI_FUNC(int) _PyLong_AsByteArray(PyLongObject* v, + unsigned char* bytes, size_t n, + int little_endian, int is_signed); + +/* _PyLong_FromNbInt: Convert the given object to a PyLongObject + using the nb_int slot, if available. Raise TypeError if either the + nb_int slot is not available or the result of the call to nb_int + returns something not of type int. +*/ +PyAPI_FUNC(PyObject *) _PyLong_FromNbInt(PyObject *); + +/* Convert the given object to a PyLongObject using the nb_index or + nb_int slots, if available (the latter is deprecated). + Raise TypeError if either nb_index and nb_int slots are not + available or the result of the call to nb_index or nb_int + returns something not of type int. + Should be replaced with PyNumber_Index after the end of the + deprecation period. +*/ +PyAPI_FUNC(PyObject *) _PyLong_FromNbIndexOrNbInt(PyObject *); + +/* _PyLong_Format: Convert the long to a string object with given base, + appending a base prefix of 0[box] if base is 2, 8 or 16. */ +PyAPI_FUNC(PyObject *) _PyLong_Format(PyObject *obj, int base); + +PyAPI_FUNC(int) _PyLong_FormatWriter( + _PyUnicodeWriter *writer, + PyObject *obj, + int base, + int alternate); + +PyAPI_FUNC(char*) _PyLong_FormatBytesWriter( + _PyBytesWriter *writer, + char *str, + PyObject *obj, + int base, + int alternate); + +/* Format the object based on the format_spec, as defined in PEP 3101 + (Advanced String Formatting). */ +PyAPI_FUNC(int) _PyLong_FormatAdvancedWriter( + _PyUnicodeWriter *writer, + PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, + Py_ssize_t end); +#endif /* Py_LIMITED_API */ + +/* These aren't really part of the int object, but they're handy. The + functions are in Python/mystrtoul.c. + */ +PyAPI_FUNC(unsigned long) PyOS_strtoul(const char *, char **, int); +PyAPI_FUNC(long) PyOS_strtol(const char *, char **, int); + +#ifndef Py_LIMITED_API +/* For use by the gcd function in mathmodule.c */ +PyAPI_FUNC(PyObject *) _PyLong_GCD(PyObject *, PyObject *); +#endif /* !Py_LIMITED_API */ + +#ifndef Py_LIMITED_API +PyAPI_DATA(PyObject *) _PyLong_Zero; +PyAPI_DATA(PyObject *) _PyLong_One; + +PyAPI_FUNC(PyObject *) _PyLong_Rshift(PyObject *, size_t); +PyAPI_FUNC(PyObject *) _PyLong_Lshift(PyObject *, size_t); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_LONGOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/marshal.h b/scripts/build-windows/py39-libs/include/marshal.h new file mode 100644 index 000000000..de9dfbbf2 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/marshal.h @@ -0,0 +1,28 @@ + +/* Interface for marshal.c */ + +#ifndef Py_MARSHAL_H +#define Py_MARSHAL_H +#ifdef __cplusplus +extern "C" { +#endif + +#define Py_MARSHAL_VERSION 4 + +PyAPI_FUNC(void) PyMarshal_WriteLongToFile(long, FILE *, int); +PyAPI_FUNC(void) PyMarshal_WriteObjectToFile(PyObject *, FILE *, int); +PyAPI_FUNC(PyObject *) PyMarshal_WriteObjectToString(PyObject *, int); + +#ifndef Py_LIMITED_API +PyAPI_FUNC(long) PyMarshal_ReadLongFromFile(FILE *); +PyAPI_FUNC(int) PyMarshal_ReadShortFromFile(FILE *); +PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromFile(FILE *); +PyAPI_FUNC(PyObject *) PyMarshal_ReadLastObjectFromFile(FILE *); +#endif +PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(const char *, + Py_ssize_t); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_MARSHAL_H */ diff --git a/scripts/build-windows/py39-libs/include/memoryobject.h b/scripts/build-windows/py39-libs/include/memoryobject.h new file mode 100644 index 000000000..3a7f2c209 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/memoryobject.h @@ -0,0 +1,72 @@ +/* Memory view object. In Python this is available as "memoryview". */ + +#ifndef Py_MEMORYOBJECT_H +#define Py_MEMORYOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_LIMITED_API +PyAPI_DATA(PyTypeObject) _PyManagedBuffer_Type; +#endif +PyAPI_DATA(PyTypeObject) PyMemoryView_Type; + +#define PyMemoryView_Check(op) Py_IS_TYPE(op, &PyMemoryView_Type) + +#ifndef Py_LIMITED_API +/* Get a pointer to the memoryview's private copy of the exporter's buffer. */ +#define PyMemoryView_GET_BUFFER(op) (&((PyMemoryViewObject *)(op))->view) +/* Get a pointer to the exporting object (this may be NULL!). */ +#define PyMemoryView_GET_BASE(op) (((PyMemoryViewObject *)(op))->view.obj) +#endif + +PyAPI_FUNC(PyObject *) PyMemoryView_FromObject(PyObject *base); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(PyObject *) PyMemoryView_FromMemory(char *mem, Py_ssize_t size, + int flags); +#endif +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) PyMemoryView_FromBuffer(Py_buffer *info); +#endif +PyAPI_FUNC(PyObject *) PyMemoryView_GetContiguous(PyObject *base, + int buffertype, + char order); + + +/* The structs are declared here so that macros can work, but they shouldn't + be considered public. Don't access their fields directly, use the macros + and functions instead! */ +#ifndef Py_LIMITED_API +#define _Py_MANAGED_BUFFER_RELEASED 0x001 /* access to exporter blocked */ +#define _Py_MANAGED_BUFFER_FREE_FORMAT 0x002 /* free format */ +typedef struct { + PyObject_HEAD + int flags; /* state flags */ + Py_ssize_t exports; /* number of direct memoryview exports */ + Py_buffer master; /* snapshot buffer obtained from the original exporter */ +} _PyManagedBufferObject; + + +/* memoryview state flags */ +#define _Py_MEMORYVIEW_RELEASED 0x001 /* access to master buffer blocked */ +#define _Py_MEMORYVIEW_C 0x002 /* C-contiguous layout */ +#define _Py_MEMORYVIEW_FORTRAN 0x004 /* Fortran contiguous layout */ +#define _Py_MEMORYVIEW_SCALAR 0x008 /* scalar: ndim = 0 */ +#define _Py_MEMORYVIEW_PIL 0x010 /* PIL-style layout */ + +typedef struct { + PyObject_VAR_HEAD + _PyManagedBufferObject *mbuf; /* managed buffer */ + Py_hash_t hash; /* hash value for read-only views */ + int flags; /* state flags */ + Py_ssize_t exports; /* number of buffer re-exports */ + Py_buffer view; /* private copy of the exporter's view */ + PyObject *weakreflist; + Py_ssize_t ob_array[1]; /* shape, strides, suboffsets */ +} PyMemoryViewObject; +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_MEMORYOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/methodobject.h b/scripts/build-windows/py39-libs/include/methodobject.h new file mode 100644 index 000000000..a90a2b5f4 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/methodobject.h @@ -0,0 +1,110 @@ + +/* Method object interface */ + +#ifndef Py_METHODOBJECT_H +#define Py_METHODOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* This is about the type 'builtin_function_or_method', + not Python methods in user-defined classes. See classobject.h + for the latter. */ + +PyAPI_DATA(PyTypeObject) PyCFunction_Type; + +#define PyCFunction_CheckExact(op) Py_IS_TYPE(op, &PyCFunction_Type) +#define PyCFunction_Check(op) PyObject_TypeCheck(op, &PyCFunction_Type) + +typedef PyObject *(*PyCFunction)(PyObject *, PyObject *); +typedef PyObject *(*_PyCFunctionFast) (PyObject *, PyObject *const *, Py_ssize_t); +typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *, + PyObject *); +typedef PyObject *(*_PyCFunctionFastWithKeywords) (PyObject *, + PyObject *const *, Py_ssize_t, + PyObject *); +typedef PyObject *(*PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *, + size_t, PyObject *); + +PyAPI_FUNC(PyCFunction) PyCFunction_GetFunction(PyObject *); +PyAPI_FUNC(PyObject *) PyCFunction_GetSelf(PyObject *); +PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *); + +Py_DEPRECATED(3.9) PyAPI_FUNC(PyObject *) PyCFunction_Call(PyObject *, PyObject *, PyObject *); + +struct PyMethodDef { + const char *ml_name; /* The name of the built-in function/method */ + PyCFunction ml_meth; /* The C function that implements it */ + int ml_flags; /* Combination of METH_xxx flags, which mostly + describe the args expected by the C func */ + const char *ml_doc; /* The __doc__ attribute, or NULL */ +}; +typedef struct PyMethodDef PyMethodDef; + +#define PyCFunction_New(ML, SELF) PyCFunction_NewEx((ML), (SELF), NULL) +PyAPI_FUNC(PyObject *) PyCFunction_NewEx(PyMethodDef *, PyObject *, + PyObject *); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000 +#define PyCFunction_NewEx(ML, SELF, MOD) PyCMethod_New((ML), (SELF), (MOD), NULL) +PyAPI_FUNC(PyObject *) PyCMethod_New(PyMethodDef *, PyObject *, + PyObject *, PyTypeObject *); +#endif + + +/* Flag passed to newmethodobject */ +/* #define METH_OLDARGS 0x0000 -- unsupported now */ +#define METH_VARARGS 0x0001 +#define METH_KEYWORDS 0x0002 +/* METH_NOARGS and METH_O must not be combined with the flags above. */ +#define METH_NOARGS 0x0004 +#define METH_O 0x0008 + +/* METH_CLASS and METH_STATIC are a little different; these control + the construction of methods for a class. These cannot be used for + functions in modules. */ +#define METH_CLASS 0x0010 +#define METH_STATIC 0x0020 + +/* METH_COEXIST allows a method to be entered even though a slot has + already filled the entry. When defined, the flag allows a separate + method, "__contains__" for example, to coexist with a defined + slot like sq_contains. */ + +#define METH_COEXIST 0x0040 + +#ifndef Py_LIMITED_API +#define METH_FASTCALL 0x0080 +#endif + +/* This bit is preserved for Stackless Python */ +#ifdef STACKLESS +#define METH_STACKLESS 0x0100 +#else +#define METH_STACKLESS 0x0000 +#endif + +/* METH_METHOD means the function stores an + * additional reference to the class that defines it; + * both self and class are passed to it. + * It uses PyCMethodObject instead of PyCFunctionObject. + * May not be combined with METH_NOARGS, METH_O, METH_CLASS or METH_STATIC. + */ + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000 +#define METH_METHOD 0x0200 +#endif + + +#ifndef Py_LIMITED_API + +#define Py_CPYTHON_METHODOBJECT_H +#include "cpython/methodobject.h" +#undef Py_CPYTHON_METHODOBJECT_H + +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_METHODOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/modsupport.h b/scripts/build-windows/py39-libs/include/modsupport.h new file mode 100644 index 000000000..799116267 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/modsupport.h @@ -0,0 +1,255 @@ + +#ifndef Py_MODSUPPORT_H +#define Py_MODSUPPORT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Module support interface */ + +#include + +/* If PY_SSIZE_T_CLEAN is defined, each functions treats #-specifier + to mean Py_ssize_t */ +#ifdef PY_SSIZE_T_CLEAN +#define PyArg_Parse _PyArg_Parse_SizeT +#define PyArg_ParseTuple _PyArg_ParseTuple_SizeT +#define PyArg_ParseTupleAndKeywords _PyArg_ParseTupleAndKeywords_SizeT +#define PyArg_VaParse _PyArg_VaParse_SizeT +#define PyArg_VaParseTupleAndKeywords _PyArg_VaParseTupleAndKeywords_SizeT +#define Py_BuildValue _Py_BuildValue_SizeT +#define Py_VaBuildValue _Py_VaBuildValue_SizeT +#ifndef Py_LIMITED_API +#define _Py_VaBuildStack _Py_VaBuildStack_SizeT +#endif +#else +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) _Py_VaBuildValue_SizeT(const char *, va_list); +PyAPI_FUNC(PyObject **) _Py_VaBuildStack_SizeT( + PyObject **small_stack, + Py_ssize_t small_stack_len, + const char *format, + va_list va, + Py_ssize_t *p_nargs); +#endif /* !Py_LIMITED_API */ +#endif + +/* Due to a glitch in 3.2, the _SizeT versions weren't exported from the DLL. */ +#if !defined(PY_SSIZE_T_CLEAN) || !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...); +PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...); +PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *, + const char *, char **, ...); +PyAPI_FUNC(int) PyArg_VaParse(PyObject *, const char *, va_list); +PyAPI_FUNC(int) PyArg_VaParseTupleAndKeywords(PyObject *, PyObject *, + const char *, char **, va_list); +#endif +PyAPI_FUNC(int) PyArg_ValidateKeywordArguments(PyObject *); +PyAPI_FUNC(int) PyArg_UnpackTuple(PyObject *, const char *, Py_ssize_t, Py_ssize_t, ...); +PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...); +PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...); + + +#ifndef Py_LIMITED_API +PyAPI_FUNC(int) _PyArg_UnpackStack( + PyObject *const *args, + Py_ssize_t nargs, + const char *name, + Py_ssize_t min, + Py_ssize_t max, + ...); + +PyAPI_FUNC(int) _PyArg_NoKeywords(const char *funcname, PyObject *kwargs); +PyAPI_FUNC(int) _PyArg_NoKwnames(const char *funcname, PyObject *kwnames); +PyAPI_FUNC(int) _PyArg_NoPositional(const char *funcname, PyObject *args); +#define _PyArg_NoKeywords(funcname, kwargs) \ + ((kwargs) == NULL || _PyArg_NoKeywords((funcname), (kwargs))) +#define _PyArg_NoKwnames(funcname, kwnames) \ + ((kwnames) == NULL || _PyArg_NoKwnames((funcname), (kwnames))) +#define _PyArg_NoPositional(funcname, args) \ + ((args) == NULL || _PyArg_NoPositional((funcname), (args))) + +PyAPI_FUNC(void) _PyArg_BadArgument(const char *, const char *, const char *, PyObject *); +PyAPI_FUNC(int) _PyArg_CheckPositional(const char *, Py_ssize_t, + Py_ssize_t, Py_ssize_t); +#define _PyArg_CheckPositional(funcname, nargs, min, max) \ + (((min) <= (nargs) && (nargs) <= (max)) \ + || _PyArg_CheckPositional((funcname), (nargs), (min), (max))) + +#endif + +PyAPI_FUNC(PyObject *) Py_VaBuildValue(const char *, va_list); +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject **) _Py_VaBuildStack( + PyObject **small_stack, + Py_ssize_t small_stack_len, + const char *format, + va_list va, + Py_ssize_t *p_nargs); +#endif + +#ifndef Py_LIMITED_API +typedef struct _PyArg_Parser { + const char *format; + const char * const *keywords; + const char *fname; + const char *custom_msg; + int pos; /* number of positional-only arguments */ + int min; /* minimal number of arguments */ + int max; /* maximal number of positional arguments */ + PyObject *kwtuple; /* tuple of keyword parameter names */ + struct _PyArg_Parser *next; +} _PyArg_Parser; +#ifdef PY_SSIZE_T_CLEAN +#define _PyArg_ParseTupleAndKeywordsFast _PyArg_ParseTupleAndKeywordsFast_SizeT +#define _PyArg_ParseStack _PyArg_ParseStack_SizeT +#define _PyArg_ParseStackAndKeywords _PyArg_ParseStackAndKeywords_SizeT +#define _PyArg_VaParseTupleAndKeywordsFast _PyArg_VaParseTupleAndKeywordsFast_SizeT +#endif +PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywordsFast(PyObject *, PyObject *, + struct _PyArg_Parser *, ...); +PyAPI_FUNC(int) _PyArg_ParseStack( + PyObject *const *args, + Py_ssize_t nargs, + const char *format, + ...); +PyAPI_FUNC(int) _PyArg_ParseStackAndKeywords( + PyObject *const *args, + Py_ssize_t nargs, + PyObject *kwnames, + struct _PyArg_Parser *, + ...); +PyAPI_FUNC(int) _PyArg_VaParseTupleAndKeywordsFast(PyObject *, PyObject *, + struct _PyArg_Parser *, va_list); +PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords( + PyObject *const *args, Py_ssize_t nargs, + PyObject *kwargs, PyObject *kwnames, + struct _PyArg_Parser *parser, + int minpos, int maxpos, int minkw, + PyObject **buf); +#define _PyArg_UnpackKeywords(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \ + (((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \ + (minpos) <= (nargs) && (nargs) <= (maxpos) && args != NULL) ? (args) : \ + _PyArg_UnpackKeywords((args), (nargs), (kwargs), (kwnames), (parser), \ + (minpos), (maxpos), (minkw), (buf))) + +void _PyArg_Fini(void); +#endif /* Py_LIMITED_API */ + +PyAPI_FUNC(int) PyModule_AddObject(PyObject *, const char *, PyObject *); +PyAPI_FUNC(int) PyModule_AddIntConstant(PyObject *, const char *, long); +PyAPI_FUNC(int) PyModule_AddStringConstant(PyObject *, const char *, const char *); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000 +/* New in 3.9 */ +PyAPI_FUNC(int) PyModule_AddType(PyObject *module, PyTypeObject *type); +#endif /* Py_LIMITED_API */ +#define PyModule_AddIntMacro(m, c) PyModule_AddIntConstant(m, #c, c) +#define PyModule_AddStringMacro(m, c) PyModule_AddStringConstant(m, #c, c) + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +/* New in 3.5 */ +PyAPI_FUNC(int) PyModule_SetDocString(PyObject *, const char *); +PyAPI_FUNC(int) PyModule_AddFunctions(PyObject *, PyMethodDef *); +PyAPI_FUNC(int) PyModule_ExecDef(PyObject *module, PyModuleDef *def); +#endif + +#define Py_CLEANUP_SUPPORTED 0x20000 + +#define PYTHON_API_VERSION 1013 +#define PYTHON_API_STRING "1013" +/* The API version is maintained (independently from the Python version) + so we can detect mismatches between the interpreter and dynamically + loaded modules. These are diagnosed by an error message but + the module is still loaded (because the mismatch can only be tested + after loading the module). The error message is intended to + explain the core dump a few seconds later. + + The symbol PYTHON_API_STRING defines the same value as a string + literal. *** PLEASE MAKE SURE THE DEFINITIONS MATCH. *** + + Please add a line or two to the top of this log for each API + version change: + + 22-Feb-2006 MvL 1013 PEP 353 - long indices for sequence lengths + + 19-Aug-2002 GvR 1012 Changes to string object struct for + interning changes, saving 3 bytes. + + 17-Jul-2001 GvR 1011 Descr-branch, just to be on the safe side + + 25-Jan-2001 FLD 1010 Parameters added to PyCode_New() and + PyFrame_New(); Python 2.1a2 + + 14-Mar-2000 GvR 1009 Unicode API added + + 3-Jan-1999 GvR 1007 Decided to change back! (Don't reuse 1008!) + + 3-Dec-1998 GvR 1008 Python 1.5.2b1 + + 18-Jan-1997 GvR 1007 string interning and other speedups + + 11-Oct-1996 GvR renamed Py_Ellipses to Py_Ellipsis :-( + + 30-Jul-1996 GvR Slice and ellipses syntax added + + 23-Jul-1996 GvR For 1.4 -- better safe than sorry this time :-) + + 7-Nov-1995 GvR Keyword arguments (should've been done at 1.3 :-( ) + + 10-Jan-1995 GvR Renamed globals to new naming scheme + + 9-Jan-1995 GvR Initial version (incompatible with older API) +*/ + +/* The PYTHON_ABI_VERSION is introduced in PEP 384. For the lifetime of + Python 3, it will stay at the value of 3; changes to the limited API + must be performed in a strictly backwards-compatible manner. */ +#define PYTHON_ABI_VERSION 3 +#define PYTHON_ABI_STRING "3" + +#ifdef Py_TRACE_REFS + /* When we are tracing reference counts, rename module creation functions so + modules compiled with incompatible settings will generate a + link-time error. */ + #define PyModule_Create2 PyModule_Create2TraceRefs + #define PyModule_FromDefAndSpec2 PyModule_FromDefAndSpec2TraceRefs +#endif + +PyAPI_FUNC(PyObject *) PyModule_Create2(struct PyModuleDef*, + int apiver); +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) _PyModule_CreateInitialized(struct PyModuleDef*, + int apiver); +#endif + +#ifdef Py_LIMITED_API +#define PyModule_Create(module) \ + PyModule_Create2(module, PYTHON_ABI_VERSION) +#else +#define PyModule_Create(module) \ + PyModule_Create2(module, PYTHON_API_VERSION) +#endif + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +/* New in 3.5 */ +PyAPI_FUNC(PyObject *) PyModule_FromDefAndSpec2(PyModuleDef *def, + PyObject *spec, + int module_api_version); + +#ifdef Py_LIMITED_API +#define PyModule_FromDefAndSpec(module, spec) \ + PyModule_FromDefAndSpec2(module, spec, PYTHON_ABI_VERSION) +#else +#define PyModule_FromDefAndSpec(module, spec) \ + PyModule_FromDefAndSpec2(module, spec, PYTHON_API_VERSION) +#endif /* Py_LIMITED_API */ +#endif /* New in 3.5 */ + +#ifndef Py_LIMITED_API +PyAPI_DATA(const char *) _Py_PackageContext; +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_MODSUPPORT_H */ diff --git a/scripts/build-windows/py39-libs/include/moduleobject.h b/scripts/build-windows/py39-libs/include/moduleobject.h new file mode 100644 index 000000000..f02e32bb5 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/moduleobject.h @@ -0,0 +1,90 @@ + +/* Module object interface */ + +#ifndef Py_MODULEOBJECT_H +#define Py_MODULEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PyModule_Type; + +#define PyModule_Check(op) PyObject_TypeCheck(op, &PyModule_Type) +#define PyModule_CheckExact(op) Py_IS_TYPE(op, &PyModule_Type) + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(PyObject *) PyModule_NewObject( + PyObject *name + ); +#endif +PyAPI_FUNC(PyObject *) PyModule_New( + const char *name /* UTF-8 encoded string */ + ); +PyAPI_FUNC(PyObject *) PyModule_GetDict(PyObject *); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(PyObject *) PyModule_GetNameObject(PyObject *); +#endif +PyAPI_FUNC(const char *) PyModule_GetName(PyObject *); +Py_DEPRECATED(3.2) PyAPI_FUNC(const char *) PyModule_GetFilename(PyObject *); +PyAPI_FUNC(PyObject *) PyModule_GetFilenameObject(PyObject *); +#ifndef Py_LIMITED_API +PyAPI_FUNC(void) _PyModule_Clear(PyObject *); +PyAPI_FUNC(void) _PyModule_ClearDict(PyObject *); +PyAPI_FUNC(int) _PyModuleSpec_IsInitializing(PyObject *); +#endif +PyAPI_FUNC(struct PyModuleDef*) PyModule_GetDef(PyObject*); +PyAPI_FUNC(void*) PyModule_GetState(PyObject*); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +/* New in 3.5 */ +PyAPI_FUNC(PyObject *) PyModuleDef_Init(struct PyModuleDef*); +PyAPI_DATA(PyTypeObject) PyModuleDef_Type; +#endif + +typedef struct PyModuleDef_Base { + PyObject_HEAD + PyObject* (*m_init)(void); + Py_ssize_t m_index; + PyObject* m_copy; +} PyModuleDef_Base; + +#define PyModuleDef_HEAD_INIT { \ + PyObject_HEAD_INIT(NULL) \ + NULL, /* m_init */ \ + 0, /* m_index */ \ + NULL, /* m_copy */ \ + } + +struct PyModuleDef_Slot; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +/* New in 3.5 */ +typedef struct PyModuleDef_Slot{ + int slot; + void *value; +} PyModuleDef_Slot; + +#define Py_mod_create 1 +#define Py_mod_exec 2 + +#ifndef Py_LIMITED_API +#define _Py_mod_LAST_SLOT 2 +#endif + +#endif /* New in 3.5 */ + +typedef struct PyModuleDef{ + PyModuleDef_Base m_base; + const char* m_name; + const char* m_doc; + Py_ssize_t m_size; + PyMethodDef *m_methods; + struct PyModuleDef_Slot* m_slots; + traverseproc m_traverse; + inquiry m_clear; + freefunc m_free; +} PyModuleDef; + +#ifdef __cplusplus +} +#endif +#endif /* !Py_MODULEOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/namespaceobject.h b/scripts/build-windows/py39-libs/include/namespaceobject.h new file mode 100644 index 000000000..35146e5c9 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/namespaceobject.h @@ -0,0 +1,19 @@ + +/* simple namespace object interface */ + +#ifndef NAMESPACEOBJECT_H +#define NAMESPACEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_LIMITED_API +PyAPI_DATA(PyTypeObject) _PyNamespace_Type; + +PyAPI_FUNC(PyObject *) _PyNamespace_New(PyObject *kwds); +#endif /* !Py_LIMITED_API */ + +#ifdef __cplusplus +} +#endif +#endif /* !NAMESPACEOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/node.h b/scripts/build-windows/py39-libs/include/node.h new file mode 100644 index 000000000..8db6298df --- /dev/null +++ b/scripts/build-windows/py39-libs/include/node.h @@ -0,0 +1,47 @@ + +/* Parse tree node interface */ + +#ifndef Py_NODE_H +#define Py_NODE_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _node { + short n_type; + char *n_str; + int n_lineno; + int n_col_offset; + int n_nchildren; + struct _node *n_child; + int n_end_lineno; + int n_end_col_offset; +} node; + +PyAPI_FUNC(node *) PyNode_New(int type); +PyAPI_FUNC(int) PyNode_AddChild(node *n, int type, + char *str, int lineno, int col_offset, + int end_lineno, int end_col_offset); +PyAPI_FUNC(void) PyNode_Free(node *n); +#ifndef Py_LIMITED_API +PyAPI_FUNC(Py_ssize_t) _PyNode_SizeOf(node *n); +#endif + +/* Node access functions */ +#define NCH(n) ((n)->n_nchildren) + +#define CHILD(n, i) (&(n)->n_child[i]) +#define TYPE(n) ((n)->n_type) +#define STR(n) ((n)->n_str) +#define LINENO(n) ((n)->n_lineno) + +/* Assert that the type of a node is what we expect */ +#define REQ(n, type) assert(TYPE(n) == (type)) + +PyAPI_FUNC(void) PyNode_ListTree(node *); +void _PyNode_FinalizeEndPos(node *n); // helper also used in parsetok.c + +#ifdef __cplusplus +} +#endif +#endif /* !Py_NODE_H */ diff --git a/scripts/build-windows/py39-libs/include/object.h b/scripts/build-windows/py39-libs/include/object.h new file mode 100644 index 000000000..b07337f6d --- /dev/null +++ b/scripts/build-windows/py39-libs/include/object.h @@ -0,0 +1,648 @@ +#ifndef Py_OBJECT_H +#define Py_OBJECT_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Object and type object interface */ + +/* +Objects are structures allocated on the heap. Special rules apply to +the use of objects to ensure they are properly garbage-collected. +Objects are never allocated statically or on the stack; they must be +accessed through special macros and functions only. (Type objects are +exceptions to the first rule; the standard types are represented by +statically initialized type objects, although work on type/class unification +for Python 2.2 made it possible to have heap-allocated type objects too). + +An object has a 'reference count' that is increased or decreased when a +pointer to the object is copied or deleted; when the reference count +reaches zero there are no references to the object left and it can be +removed from the heap. + +An object has a 'type' that determines what it represents and what kind +of data it contains. An object's type is fixed when it is created. +Types themselves are represented as objects; an object contains a +pointer to the corresponding type object. The type itself has a type +pointer pointing to the object representing the type 'type', which +contains a pointer to itself!. + +Objects do not float around in memory; once allocated an object keeps +the same size and address. Objects that must hold variable-size data +can contain pointers to variable-size parts of the object. Not all +objects of the same type have the same size; but the size cannot change +after allocation. (These restrictions are made so a reference to an +object can be simply a pointer -- moving an object would require +updating all the pointers, and changing an object's size would require +moving it if there was another object right next to it.) + +Objects are always accessed through pointers of the type 'PyObject *'. +The type 'PyObject' is a structure that only contains the reference count +and the type pointer. The actual memory allocated for an object +contains other data that can only be accessed after casting the pointer +to a pointer to a longer structure type. This longer type must start +with the reference count and type fields; the macro PyObject_HEAD should be +used for this (to accommodate for future changes). The implementation +of a particular object type can cast the object pointer to the proper +type and back. + +A standard interface exists for objects that contain an array of items +whose size is determined when the object is allocated. +*/ + +/* Py_DEBUG implies Py_REF_DEBUG. */ +#if defined(Py_DEBUG) && !defined(Py_REF_DEBUG) +#define Py_REF_DEBUG +#endif + +#if defined(Py_LIMITED_API) && defined(Py_REF_DEBUG) +#error Py_LIMITED_API is incompatible with Py_DEBUG, Py_TRACE_REFS, and Py_REF_DEBUG +#endif + +/* PyTypeObject structure is defined in cpython/object.h. + In Py_LIMITED_API, PyTypeObject is an opaque structure. */ +typedef struct _typeobject PyTypeObject; + +#ifdef Py_TRACE_REFS +/* Define pointers to support a doubly-linked list of all live heap objects. */ +#define _PyObject_HEAD_EXTRA \ + struct _object *_ob_next; \ + struct _object *_ob_prev; + +#define _PyObject_EXTRA_INIT 0, 0, + +#else +#define _PyObject_HEAD_EXTRA +#define _PyObject_EXTRA_INIT +#endif + +/* PyObject_HEAD defines the initial segment of every PyObject. */ +#define PyObject_HEAD PyObject ob_base; + +#define PyObject_HEAD_INIT(type) \ + { _PyObject_EXTRA_INIT \ + 1, type }, + +#define PyVarObject_HEAD_INIT(type, size) \ + { PyObject_HEAD_INIT(type) size }, + +/* PyObject_VAR_HEAD defines the initial segment of all variable-size + * container objects. These end with a declaration of an array with 1 + * element, but enough space is malloc'ed so that the array actually + * has room for ob_size elements. Note that ob_size is an element count, + * not necessarily a byte count. + */ +#define PyObject_VAR_HEAD PyVarObject ob_base; +#define Py_INVALID_SIZE (Py_ssize_t)-1 + +/* Nothing is actually declared to be a PyObject, but every pointer to + * a Python object can be cast to a PyObject*. This is inheritance built + * by hand. Similarly every pointer to a variable-size Python object can, + * in addition, be cast to PyVarObject*. + */ +typedef struct _object { + _PyObject_HEAD_EXTRA + Py_ssize_t ob_refcnt; + PyTypeObject *ob_type; +} PyObject; + +/* Cast argument to PyObject* type. */ +#define _PyObject_CAST(op) ((PyObject*)(op)) +#define _PyObject_CAST_CONST(op) ((const PyObject*)(op)) + +typedef struct { + PyObject ob_base; + Py_ssize_t ob_size; /* Number of items in variable part */ +} PyVarObject; + +/* Cast argument to PyVarObject* type. */ +#define _PyVarObject_CAST(op) ((PyVarObject*)(op)) + +#define Py_REFCNT(ob) (_PyObject_CAST(ob)->ob_refcnt) +#define Py_TYPE(ob) (_PyObject_CAST(ob)->ob_type) +#define Py_SIZE(ob) (_PyVarObject_CAST(ob)->ob_size) + +static inline int _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) { + return ob->ob_type == type; +} +#define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST_CONST(ob), type) + +static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { + ob->ob_refcnt = refcnt; +} +#define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt) + +static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) { + ob->ob_type = type; +} +#define Py_SET_TYPE(ob, type) _Py_SET_TYPE(_PyObject_CAST(ob), type) + +static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) { + ob->ob_size = size; +} +#define Py_SET_SIZE(ob, size) _Py_SET_SIZE(_PyVarObject_CAST(ob), size) + + +/* +Type objects contain a string containing the type name (to help somewhat +in debugging), the allocation parameters (see PyObject_New() and +PyObject_NewVar()), +and methods for accessing objects of the type. Methods are optional, a +nil pointer meaning that particular kind of access is not available for +this type. The Py_DECREF() macro uses the tp_dealloc method without +checking for a nil pointer; it should always be implemented except if +the implementation can guarantee that the reference count will never +reach zero (e.g., for statically allocated type objects). + +NB: the methods for certain type groups are now contained in separate +method blocks. +*/ + +typedef PyObject * (*unaryfunc)(PyObject *); +typedef PyObject * (*binaryfunc)(PyObject *, PyObject *); +typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *); +typedef int (*inquiry)(PyObject *); +typedef Py_ssize_t (*lenfunc)(PyObject *); +typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t); +typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t); +typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *); +typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *); +typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *); + +typedef int (*objobjproc)(PyObject *, PyObject *); +typedef int (*visitproc)(PyObject *, void *); +typedef int (*traverseproc)(PyObject *, visitproc, void *); + + +typedef void (*freefunc)(void *); +typedef void (*destructor)(PyObject *); +typedef PyObject *(*getattrfunc)(PyObject *, char *); +typedef PyObject *(*getattrofunc)(PyObject *, PyObject *); +typedef int (*setattrfunc)(PyObject *, char *, PyObject *); +typedef int (*setattrofunc)(PyObject *, PyObject *, PyObject *); +typedef PyObject *(*reprfunc)(PyObject *); +typedef Py_hash_t (*hashfunc)(PyObject *); +typedef PyObject *(*richcmpfunc) (PyObject *, PyObject *, int); +typedef PyObject *(*getiterfunc) (PyObject *); +typedef PyObject *(*iternextfunc) (PyObject *); +typedef PyObject *(*descrgetfunc) (PyObject *, PyObject *, PyObject *); +typedef int (*descrsetfunc) (PyObject *, PyObject *, PyObject *); +typedef int (*initproc)(PyObject *, PyObject *, PyObject *); +typedef PyObject *(*newfunc)(PyTypeObject *, PyObject *, PyObject *); +typedef PyObject *(*allocfunc)(PyTypeObject *, Py_ssize_t); + +typedef struct{ + int slot; /* slot id, see below */ + void *pfunc; /* function pointer */ +} PyType_Slot; + +typedef struct{ + const char* name; + int basicsize; + int itemsize; + unsigned int flags; + PyType_Slot *slots; /* terminated by slot==0. */ +} PyType_Spec; + +PyAPI_FUNC(PyObject*) PyType_FromSpec(PyType_Spec*); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(PyObject*) PyType_FromSpecWithBases(PyType_Spec*, PyObject*); +#endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03040000 +PyAPI_FUNC(void*) PyType_GetSlot(PyTypeObject*, int); +#endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000 +PyAPI_FUNC(PyObject*) PyType_FromModuleAndSpec(PyObject *, PyType_Spec *, PyObject *); +PyAPI_FUNC(PyObject *) PyType_GetModule(struct _typeobject *); +PyAPI_FUNC(void *) PyType_GetModuleState(struct _typeobject *); +#endif + +/* Generic type check */ +PyAPI_FUNC(int) PyType_IsSubtype(PyTypeObject *, PyTypeObject *); +#define PyObject_TypeCheck(ob, tp) \ + (Py_IS_TYPE(ob, tp) || PyType_IsSubtype(Py_TYPE(ob), (tp))) + +PyAPI_DATA(PyTypeObject) PyType_Type; /* built-in 'type' */ +PyAPI_DATA(PyTypeObject) PyBaseObject_Type; /* built-in 'object' */ +PyAPI_DATA(PyTypeObject) PySuper_Type; /* built-in 'super' */ + +PyAPI_FUNC(unsigned long) PyType_GetFlags(PyTypeObject*); + +PyAPI_FUNC(int) PyType_Ready(PyTypeObject *); +PyAPI_FUNC(PyObject *) PyType_GenericAlloc(PyTypeObject *, Py_ssize_t); +PyAPI_FUNC(PyObject *) PyType_GenericNew(PyTypeObject *, + PyObject *, PyObject *); +PyAPI_FUNC(unsigned int) PyType_ClearCache(void); +PyAPI_FUNC(void) PyType_Modified(PyTypeObject *); + +/* Generic operations on objects */ +PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *); +PyAPI_FUNC(PyObject *) PyObject_Str(PyObject *); +PyAPI_FUNC(PyObject *) PyObject_ASCII(PyObject *); +PyAPI_FUNC(PyObject *) PyObject_Bytes(PyObject *); +PyAPI_FUNC(PyObject *) PyObject_RichCompare(PyObject *, PyObject *, int); +PyAPI_FUNC(int) PyObject_RichCompareBool(PyObject *, PyObject *, int); +PyAPI_FUNC(PyObject *) PyObject_GetAttrString(PyObject *, const char *); +PyAPI_FUNC(int) PyObject_SetAttrString(PyObject *, const char *, PyObject *); +PyAPI_FUNC(int) PyObject_HasAttrString(PyObject *, const char *); +PyAPI_FUNC(PyObject *) PyObject_GetAttr(PyObject *, PyObject *); +PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyObject_SelfIter(PyObject *); +PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *); +PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *, PyObject *, PyObject *); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(int) PyObject_GenericSetDict(PyObject *, PyObject *, void *); +#endif +PyAPI_FUNC(Py_hash_t) PyObject_Hash(PyObject *); +PyAPI_FUNC(Py_hash_t) PyObject_HashNotImplemented(PyObject *); +PyAPI_FUNC(int) PyObject_IsTrue(PyObject *); +PyAPI_FUNC(int) PyObject_Not(PyObject *); +PyAPI_FUNC(int) PyCallable_Check(PyObject *); +PyAPI_FUNC(void) PyObject_ClearWeakRefs(PyObject *); + +/* PyObject_Dir(obj) acts like Python builtins.dir(obj), returning a + list of strings. PyObject_Dir(NULL) is like builtins.dir(), + returning the names of the current locals. In this case, if there are + no current locals, NULL is returned, and PyErr_Occurred() is false. +*/ +PyAPI_FUNC(PyObject *) PyObject_Dir(PyObject *); + + +/* Helpers for printing recursive container types */ +PyAPI_FUNC(int) Py_ReprEnter(PyObject *); +PyAPI_FUNC(void) Py_ReprLeave(PyObject *); + +/* Flag bits for printing: */ +#define Py_PRINT_RAW 1 /* No string quotes etc. */ + +/* +Type flags (tp_flags) + +These flags are used to change expected features and behavior for a +particular type. + +Arbitration of the flag bit positions will need to be coordinated among +all extension writers who publicly release their extensions (this will +be fewer than you might expect!). + +Most flags were removed as of Python 3.0 to make room for new flags. (Some +flags are not for backwards compatibility but to indicate the presence of an +optional feature; these flags remain of course.) + +Type definitions should use Py_TPFLAGS_DEFAULT for their tp_flags value. + +Code can use PyType_HasFeature(type_ob, flag_value) to test whether the +given type object has a specified feature. +*/ + +/* Set if the type object is dynamically allocated */ +#define Py_TPFLAGS_HEAPTYPE (1UL << 9) + +/* Set if the type allows subclassing */ +#define Py_TPFLAGS_BASETYPE (1UL << 10) + +/* Set if the type implements the vectorcall protocol (PEP 590) */ +#ifndef Py_LIMITED_API +#define Py_TPFLAGS_HAVE_VECTORCALL (1UL << 11) +// Backwards compatibility alias for API that was provisional in Python 3.8 +#define _Py_TPFLAGS_HAVE_VECTORCALL Py_TPFLAGS_HAVE_VECTORCALL +#endif + +/* Set if the type is 'ready' -- fully initialized */ +#define Py_TPFLAGS_READY (1UL << 12) + +/* Set while the type is being 'readied', to prevent recursive ready calls */ +#define Py_TPFLAGS_READYING (1UL << 13) + +/* Objects support garbage collection (see objimpl.h) */ +#define Py_TPFLAGS_HAVE_GC (1UL << 14) + +/* These two bits are preserved for Stackless Python, next after this is 17 */ +#ifdef STACKLESS +#define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION (3UL << 15) +#else +#define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION 0 +#endif + +/* Objects behave like an unbound method */ +#define Py_TPFLAGS_METHOD_DESCRIPTOR (1UL << 17) + +/* Objects support type attribute cache */ +#define Py_TPFLAGS_HAVE_VERSION_TAG (1UL << 18) +#define Py_TPFLAGS_VALID_VERSION_TAG (1UL << 19) + +/* Type is abstract and cannot be instantiated */ +#define Py_TPFLAGS_IS_ABSTRACT (1UL << 20) + +/* These flags are used to determine if a type is a subclass. */ +#define Py_TPFLAGS_LONG_SUBCLASS (1UL << 24) +#define Py_TPFLAGS_LIST_SUBCLASS (1UL << 25) +#define Py_TPFLAGS_TUPLE_SUBCLASS (1UL << 26) +#define Py_TPFLAGS_BYTES_SUBCLASS (1UL << 27) +#define Py_TPFLAGS_UNICODE_SUBCLASS (1UL << 28) +#define Py_TPFLAGS_DICT_SUBCLASS (1UL << 29) +#define Py_TPFLAGS_BASE_EXC_SUBCLASS (1UL << 30) +#define Py_TPFLAGS_TYPE_SUBCLASS (1UL << 31) + +#define Py_TPFLAGS_DEFAULT ( \ + Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | \ + Py_TPFLAGS_HAVE_VERSION_TAG | \ + 0) + +/* NOTE: The following flags reuse lower bits (removed as part of the + * Python 3.0 transition). */ + +/* The following flag is kept for compatibility. Starting with 3.8, + * binary compatibility of C extensions across feature releases of + * Python is not supported anymore, except when using the stable ABI. + */ + +/* Type structure has tp_finalize member (3.4) */ +#define Py_TPFLAGS_HAVE_FINALIZE (1UL << 0) + + +/* +The macros Py_INCREF(op) and Py_DECREF(op) are used to increment or decrement +reference counts. Py_DECREF calls the object's deallocator function when +the refcount falls to 0; for +objects that don't contain references to other objects or heap memory +this can be the standard function free(). Both macros can be used +wherever a void expression is allowed. The argument must not be a +NULL pointer. If it may be NULL, use Py_XINCREF/Py_XDECREF instead. +The macro _Py_NewReference(op) initialize reference counts to 1, and +in special builds (Py_REF_DEBUG, Py_TRACE_REFS) performs additional +bookkeeping appropriate to the special build. + +We assume that the reference count field can never overflow; this can +be proven when the size of the field is the same as the pointer size, so +we ignore the possibility. Provided a C int is at least 32 bits (which +is implicitly assumed in many parts of this code), that's enough for +about 2**31 references to an object. + +XXX The following became out of date in Python 2.2, but I'm not sure +XXX what the full truth is now. Certainly, heap-allocated type objects +XXX can and should be deallocated. +Type objects should never be deallocated; the type pointer in an object +is not considered to be a reference to the type object, to save +complications in the deallocation function. (This is actually a +decision that's up to the implementer of each new type so if you want, +you can count such references to the type object.) +*/ + +#ifdef Py_REF_DEBUG +PyAPI_DATA(Py_ssize_t) _Py_RefTotal; +PyAPI_FUNC(void) _Py_NegativeRefcount(const char *filename, int lineno, + PyObject *op); +#endif /* Py_REF_DEBUG */ + +PyAPI_FUNC(void) _Py_Dealloc(PyObject *); + +static inline void _Py_INCREF(PyObject *op) +{ +#ifdef Py_REF_DEBUG + _Py_RefTotal++; +#endif + op->ob_refcnt++; +} + +#define Py_INCREF(op) _Py_INCREF(_PyObject_CAST(op)) + +static inline void _Py_DECREF( +#ifdef Py_REF_DEBUG + const char *filename, int lineno, +#endif + PyObject *op) +{ +#ifdef Py_REF_DEBUG + _Py_RefTotal--; +#endif + if (--op->ob_refcnt != 0) { +#ifdef Py_REF_DEBUG + if (op->ob_refcnt < 0) { + _Py_NegativeRefcount(filename, lineno, op); + } +#endif + } + else { + _Py_Dealloc(op); + } +} + +#ifdef Py_REF_DEBUG +# define Py_DECREF(op) _Py_DECREF(__FILE__, __LINE__, _PyObject_CAST(op)) +#else +# define Py_DECREF(op) _Py_DECREF(_PyObject_CAST(op)) +#endif + + +/* Safely decref `op` and set `op` to NULL, especially useful in tp_clear + * and tp_dealloc implementations. + * + * Note that "the obvious" code can be deadly: + * + * Py_XDECREF(op); + * op = NULL; + * + * Typically, `op` is something like self->containee, and `self` is done + * using its `containee` member. In the code sequence above, suppose + * `containee` is non-NULL with a refcount of 1. Its refcount falls to + * 0 on the first line, which can trigger an arbitrary amount of code, + * possibly including finalizers (like __del__ methods or weakref callbacks) + * coded in Python, which in turn can release the GIL and allow other threads + * to run, etc. Such code may even invoke methods of `self` again, or cause + * cyclic gc to trigger, but-- oops! --self->containee still points to the + * object being torn down, and it may be in an insane state while being torn + * down. This has in fact been a rich historic source of miserable (rare & + * hard-to-diagnose) segfaulting (and other) bugs. + * + * The safe way is: + * + * Py_CLEAR(op); + * + * That arranges to set `op` to NULL _before_ decref'ing, so that any code + * triggered as a side-effect of `op` getting torn down no longer believes + * `op` points to a valid object. + * + * There are cases where it's safe to use the naive code, but they're brittle. + * For example, if `op` points to a Python integer, you know that destroying + * one of those can't cause problems -- but in part that relies on that + * Python integers aren't currently weakly referencable. Best practice is + * to use Py_CLEAR() even if you can't think of a reason for why you need to. + */ +#define Py_CLEAR(op) \ + do { \ + PyObject *_py_tmp = _PyObject_CAST(op); \ + if (_py_tmp != NULL) { \ + (op) = NULL; \ + Py_DECREF(_py_tmp); \ + } \ + } while (0) + +/* Function to use in case the object pointer can be NULL: */ +static inline void _Py_XINCREF(PyObject *op) +{ + if (op != NULL) { + Py_INCREF(op); + } +} + +#define Py_XINCREF(op) _Py_XINCREF(_PyObject_CAST(op)) + +static inline void _Py_XDECREF(PyObject *op) +{ + if (op != NULL) { + Py_DECREF(op); + } +} + +#define Py_XDECREF(op) _Py_XDECREF(_PyObject_CAST(op)) + +/* +These are provided as conveniences to Python runtime embedders, so that +they can have object code that is not dependent on Python compilation flags. +*/ +PyAPI_FUNC(void) Py_IncRef(PyObject *); +PyAPI_FUNC(void) Py_DecRef(PyObject *); + +/* +_Py_NoneStruct is an object of undefined type which can be used in contexts +where NULL (nil) is not suitable (since NULL often means 'error'). + +Don't forget to apply Py_INCREF() when returning this value!!! +*/ +PyAPI_DATA(PyObject) _Py_NoneStruct; /* Don't use this directly */ +#define Py_None (&_Py_NoneStruct) + +/* Macro for returning Py_None from a function */ +#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None + +/* +Py_NotImplemented is a singleton used to signal that an operation is +not implemented for a given type combination. +*/ +PyAPI_DATA(PyObject) _Py_NotImplementedStruct; /* Don't use this directly */ +#define Py_NotImplemented (&_Py_NotImplementedStruct) + +/* Macro for returning Py_NotImplemented from a function */ +#define Py_RETURN_NOTIMPLEMENTED \ + return Py_INCREF(Py_NotImplemented), Py_NotImplemented + +/* Rich comparison opcodes */ +#define Py_LT 0 +#define Py_LE 1 +#define Py_EQ 2 +#define Py_NE 3 +#define Py_GT 4 +#define Py_GE 5 + +/* + * Macro for implementing rich comparisons + * + * Needs to be a macro because any C-comparable type can be used. + */ +#define Py_RETURN_RICHCOMPARE(val1, val2, op) \ + do { \ + switch (op) { \ + case Py_EQ: if ((val1) == (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ + case Py_NE: if ((val1) != (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ + case Py_LT: if ((val1) < (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ + case Py_GT: if ((val1) > (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ + case Py_LE: if ((val1) <= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ + case Py_GE: if ((val1) >= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ + default: \ + Py_UNREACHABLE(); \ + } \ + } while (0) + + +/* +More conventions +================ + +Argument Checking +----------------- + +Functions that take objects as arguments normally don't check for nil +arguments, but they do check the type of the argument, and return an +error if the function doesn't apply to the type. + +Failure Modes +------------- + +Functions may fail for a variety of reasons, including running out of +memory. This is communicated to the caller in two ways: an error string +is set (see errors.h), and the function result differs: functions that +normally return a pointer return NULL for failure, functions returning +an integer return -1 (which could be a legal return value too!), and +other functions return 0 for success and -1 for failure. +Callers should always check for errors before using the result. If +an error was set, the caller must either explicitly clear it, or pass +the error on to its caller. + +Reference Counts +---------------- + +It takes a while to get used to the proper usage of reference counts. + +Functions that create an object set the reference count to 1; such new +objects must be stored somewhere or destroyed again with Py_DECREF(). +Some functions that 'store' objects, such as PyTuple_SetItem() and +PyList_SetItem(), +don't increment the reference count of the object, since the most +frequent use is to store a fresh object. Functions that 'retrieve' +objects, such as PyTuple_GetItem() and PyDict_GetItemString(), also +don't increment +the reference count, since most frequently the object is only looked at +quickly. Thus, to retrieve an object and store it again, the caller +must call Py_INCREF() explicitly. + +NOTE: functions that 'consume' a reference count, like +PyList_SetItem(), consume the reference even if the object wasn't +successfully stored, to simplify error handling. + +It seems attractive to make other functions that take an object as +argument consume a reference count; however, this may quickly get +confusing (even the current practice is already confusing). Consider +it carefully, it may save lots of calls to Py_INCREF() and Py_DECREF() at +times. +*/ + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_OBJECT_H +# include "cpython/object.h" +# undef Py_CPYTHON_OBJECT_H +#endif + + +static inline int +PyType_HasFeature(PyTypeObject *type, unsigned long feature) +{ + unsigned long flags; +#ifdef Py_LIMITED_API + // PyTypeObject is opaque in the limited C API + flags = PyType_GetFlags(type); +#else + flags = type->tp_flags; +#endif + return ((flags & feature) != 0); +} + +#define PyType_FastSubclass(type, flag) PyType_HasFeature(type, flag) + +static inline int _PyType_Check(PyObject *op) { + return PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TYPE_SUBCLASS); +} +#define PyType_Check(op) _PyType_Check(_PyObject_CAST(op)) + +static inline int _PyType_CheckExact(PyObject *op) { + return Py_IS_TYPE(op, &PyType_Type); +} +#define PyType_CheckExact(op) _PyType_CheckExact(_PyObject_CAST(op)) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_OBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/objimpl.h b/scripts/build-windows/py39-libs/include/objimpl.h new file mode 100644 index 000000000..3045213e5 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/objimpl.h @@ -0,0 +1,215 @@ +/* The PyObject_ memory family: high-level object memory interfaces. + See pymem.h for the low-level PyMem_ family. +*/ + +#ifndef Py_OBJIMPL_H +#define Py_OBJIMPL_H + +#include "pymem.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* BEWARE: + + Each interface exports both functions and macros. Extension modules should + use the functions, to ensure binary compatibility across Python versions. + Because the Python implementation is free to change internal details, and + the macros may (or may not) expose details for speed, if you do use the + macros you must recompile your extensions with each Python release. + + Never mix calls to PyObject_ memory functions with calls to the platform + malloc/realloc/ calloc/free, or with calls to PyMem_. +*/ + +/* +Functions and macros for modules that implement new object types. + + - PyObject_New(type, typeobj) allocates memory for a new object of the given + type, and initializes part of it. 'type' must be the C structure type used + to represent the object, and 'typeobj' the address of the corresponding + type object. Reference count and type pointer are filled in; the rest of + the bytes of the object are *undefined*! The resulting expression type is + 'type *'. The size of the object is determined by the tp_basicsize field + of the type object. + + - PyObject_NewVar(type, typeobj, n) is similar but allocates a variable-size + object with room for n items. In addition to the refcount and type pointer + fields, this also fills in the ob_size field. + + - PyObject_Del(op) releases the memory allocated for an object. It does not + run a destructor -- it only frees the memory. PyObject_Free is identical. + + - PyObject_Init(op, typeobj) and PyObject_InitVar(op, typeobj, n) don't + allocate memory. Instead of a 'type' parameter, they take a pointer to a + new object (allocated by an arbitrary allocator), and initialize its object + header fields. + +Note that objects created with PyObject_{New, NewVar} are allocated using the +specialized Python allocator (implemented in obmalloc.c), if WITH_PYMALLOC is +enabled. In addition, a special debugging allocator is used if PYMALLOC_DEBUG +is also #defined. + +In case a specific form of memory management is needed (for example, if you +must use the platform malloc heap(s), or shared memory, or C++ local storage or +operator new), you must first allocate the object with your custom allocator, +then pass its pointer to PyObject_{Init, InitVar} for filling in its Python- +specific fields: reference count, type pointer, possibly others. You should +be aware that Python has no control over these objects because they don't +cooperate with the Python memory manager. Such objects may not be eligible +for automatic garbage collection and you have to make sure that they are +released accordingly whenever their destructor gets called (cf. the specific +form of memory management you're using). + +Unless you have specific memory management requirements, use +PyObject_{New, NewVar, Del}. +*/ + +/* + * Raw object memory interface + * =========================== + */ + +/* Functions to call the same malloc/realloc/free as used by Python's + object allocator. If WITH_PYMALLOC is enabled, these may differ from + the platform malloc/realloc/free. The Python object allocator is + designed for fast, cache-conscious allocation of many "small" objects, + and with low hidden memory overhead. + + PyObject_Malloc(0) returns a unique non-NULL pointer if possible. + + PyObject_Realloc(NULL, n) acts like PyObject_Malloc(n). + PyObject_Realloc(p != NULL, 0) does not return NULL, or free the memory + at p. + + Returned pointers must be checked for NULL explicitly; no action is + performed on failure other than to return NULL (no warning it printed, no + exception is set, etc). + + For allocating objects, use PyObject_{New, NewVar} instead whenever + possible. The PyObject_{Malloc, Realloc, Free} family is exposed + so that you can exploit Python's small-block allocator for non-object + uses. If you must use these routines to allocate object memory, make sure + the object gets initialized via PyObject_{Init, InitVar} after obtaining + the raw memory. +*/ +PyAPI_FUNC(void *) PyObject_Malloc(size_t size); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +PyAPI_FUNC(void *) PyObject_Calloc(size_t nelem, size_t elsize); +#endif +PyAPI_FUNC(void *) PyObject_Realloc(void *ptr, size_t new_size); +PyAPI_FUNC(void) PyObject_Free(void *ptr); + + +/* Macros */ +#define PyObject_MALLOC PyObject_Malloc +#define PyObject_REALLOC PyObject_Realloc +#define PyObject_FREE PyObject_Free +#define PyObject_Del PyObject_Free +#define PyObject_DEL PyObject_Free + + +/* + * Generic object allocator interface + * ================================== + */ + +/* Functions */ +PyAPI_FUNC(PyObject *) PyObject_Init(PyObject *, PyTypeObject *); +PyAPI_FUNC(PyVarObject *) PyObject_InitVar(PyVarObject *, + PyTypeObject *, Py_ssize_t); +PyAPI_FUNC(PyObject *) _PyObject_New(PyTypeObject *); +PyAPI_FUNC(PyVarObject *) _PyObject_NewVar(PyTypeObject *, Py_ssize_t); + +#define PyObject_New(type, typeobj) ((type *)_PyObject_New(typeobj)) + +// Alias to PyObject_New(). In Python 3.8, PyObject_NEW() called directly +// PyObject_MALLOC() with _PyObject_SIZE(). +#define PyObject_NEW(type, typeobj) PyObject_New(type, typeobj) + +#define PyObject_NewVar(type, typeobj, n) \ + ( (type *) _PyObject_NewVar((typeobj), (n)) ) + +// Alias to PyObject_New(). In Python 3.8, PyObject_NEW() called directly +// PyObject_MALLOC() with _PyObject_VAR_SIZE(). +#define PyObject_NEW_VAR(type, typeobj, n) PyObject_NewVar(type, typeobj, n) + + +#ifdef Py_LIMITED_API +/* Define PyObject_INIT() and PyObject_INIT_VAR() as aliases to PyObject_Init() + and PyObject_InitVar() in the limited C API for compatibility with the + CPython C API. */ +# define PyObject_INIT(op, typeobj) \ + PyObject_Init(_PyObject_CAST(op), (typeobj)) +# define PyObject_INIT_VAR(op, typeobj, size) \ + PyObject_InitVar(_PyVarObject_CAST(op), (typeobj), (size)) +#else +/* PyObject_INIT() and PyObject_INIT_VAR() are defined in cpython/objimpl.h */ +#endif + + +/* + * Garbage Collection Support + * ========================== + */ + +/* C equivalent of gc.collect() which ignores the state of gc.enabled. */ +PyAPI_FUNC(Py_ssize_t) PyGC_Collect(void); + +/* Test if a type has a GC head */ +#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC) + +PyAPI_FUNC(PyVarObject *) _PyObject_GC_Resize(PyVarObject *, Py_ssize_t); +#define PyObject_GC_Resize(type, op, n) \ + ( (type *) _PyObject_GC_Resize(_PyVarObject_CAST(op), (n)) ) + + + +PyAPI_FUNC(PyObject *) _PyObject_GC_New(PyTypeObject *); +PyAPI_FUNC(PyVarObject *) _PyObject_GC_NewVar(PyTypeObject *, Py_ssize_t); + +/* Tell the GC to track this object. + * + * See also private _PyObject_GC_TRACK() macro. */ +PyAPI_FUNC(void) PyObject_GC_Track(void *); + +/* Tell the GC to stop tracking this object. + * + * See also private _PyObject_GC_UNTRACK() macro. */ +PyAPI_FUNC(void) PyObject_GC_UnTrack(void *); + +PyAPI_FUNC(void) PyObject_GC_Del(void *); + +#define PyObject_GC_New(type, typeobj) \ + ( (type *) _PyObject_GC_New(typeobj) ) +#define PyObject_GC_NewVar(type, typeobj, n) \ + ( (type *) _PyObject_GC_NewVar((typeobj), (n)) ) + +PyAPI_FUNC(int) PyObject_GC_IsTracked(PyObject *); +PyAPI_FUNC(int) PyObject_GC_IsFinalized(PyObject *); + +/* Utility macro to help write tp_traverse functions. + * To use this macro, the tp_traverse function must name its arguments + * "visit" and "arg". This is intended to keep tp_traverse functions + * looking as much alike as possible. + */ +#define Py_VISIT(op) \ + do { \ + if (op) { \ + int vret = visit(_PyObject_CAST(op), arg); \ + if (vret) \ + return vret; \ + } \ + } while (0) + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_OBJIMPL_H +# include "cpython/objimpl.h" +# undef Py_CPYTHON_OBJIMPL_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_OBJIMPL_H */ diff --git a/scripts/build-windows/py39-libs/include/odictobject.h b/scripts/build-windows/py39-libs/include/odictobject.h new file mode 100644 index 000000000..881e7ebcb --- /dev/null +++ b/scripts/build-windows/py39-libs/include/odictobject.h @@ -0,0 +1,43 @@ +#ifndef Py_ODICTOBJECT_H +#define Py_ODICTOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* OrderedDict */ +/* This API is optional and mostly redundant. */ + +#ifndef Py_LIMITED_API + +typedef struct _odictobject PyODictObject; + +PyAPI_DATA(PyTypeObject) PyODict_Type; +PyAPI_DATA(PyTypeObject) PyODictIter_Type; +PyAPI_DATA(PyTypeObject) PyODictKeys_Type; +PyAPI_DATA(PyTypeObject) PyODictItems_Type; +PyAPI_DATA(PyTypeObject) PyODictValues_Type; + +#define PyODict_Check(op) PyObject_TypeCheck(op, &PyODict_Type) +#define PyODict_CheckExact(op) Py_IS_TYPE(op, &PyODict_Type) +#define PyODict_SIZE(op) PyDict_GET_SIZE((op)) + +PyAPI_FUNC(PyObject *) PyODict_New(void); +PyAPI_FUNC(int) PyODict_SetItem(PyObject *od, PyObject *key, PyObject *item); +PyAPI_FUNC(int) PyODict_DelItem(PyObject *od, PyObject *key); + +/* wrappers around PyDict* functions */ +#define PyODict_GetItem(od, key) PyDict_GetItem(_PyObject_CAST(od), key) +#define PyODict_GetItemWithError(od, key) \ + PyDict_GetItemWithError(_PyObject_CAST(od), key) +#define PyODict_Contains(od, key) PyDict_Contains(_PyObject_CAST(od), key) +#define PyODict_Size(od) PyDict_Size(_PyObject_CAST(od)) +#define PyODict_GetItemString(od, key) \ + PyDict_GetItemString(_PyObject_CAST(od), key) + +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_ODICTOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/opcode.h b/scripts/build-windows/py39-libs/include/opcode.h new file mode 100644 index 000000000..f86c2f938 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/opcode.h @@ -0,0 +1,142 @@ +/* Auto-generated by Tools/scripts/generate_opcode_h.py from Lib/opcode.py */ +#ifndef Py_OPCODE_H +#define Py_OPCODE_H +#ifdef __cplusplus +extern "C" { +#endif + + + /* Instruction opcodes for compiled code */ +#define POP_TOP 1 +#define ROT_TWO 2 +#define ROT_THREE 3 +#define DUP_TOP 4 +#define DUP_TOP_TWO 5 +#define ROT_FOUR 6 +#define NOP 9 +#define UNARY_POSITIVE 10 +#define UNARY_NEGATIVE 11 +#define UNARY_NOT 12 +#define UNARY_INVERT 15 +#define BINARY_MATRIX_MULTIPLY 16 +#define INPLACE_MATRIX_MULTIPLY 17 +#define BINARY_POWER 19 +#define BINARY_MULTIPLY 20 +#define BINARY_MODULO 22 +#define BINARY_ADD 23 +#define BINARY_SUBTRACT 24 +#define BINARY_SUBSCR 25 +#define BINARY_FLOOR_DIVIDE 26 +#define BINARY_TRUE_DIVIDE 27 +#define INPLACE_FLOOR_DIVIDE 28 +#define INPLACE_TRUE_DIVIDE 29 +#define RERAISE 48 +#define WITH_EXCEPT_START 49 +#define GET_AITER 50 +#define GET_ANEXT 51 +#define BEFORE_ASYNC_WITH 52 +#define END_ASYNC_FOR 54 +#define INPLACE_ADD 55 +#define INPLACE_SUBTRACT 56 +#define INPLACE_MULTIPLY 57 +#define INPLACE_MODULO 59 +#define STORE_SUBSCR 60 +#define DELETE_SUBSCR 61 +#define BINARY_LSHIFT 62 +#define BINARY_RSHIFT 63 +#define BINARY_AND 64 +#define BINARY_XOR 65 +#define BINARY_OR 66 +#define INPLACE_POWER 67 +#define GET_ITER 68 +#define GET_YIELD_FROM_ITER 69 +#define PRINT_EXPR 70 +#define LOAD_BUILD_CLASS 71 +#define YIELD_FROM 72 +#define GET_AWAITABLE 73 +#define LOAD_ASSERTION_ERROR 74 +#define INPLACE_LSHIFT 75 +#define INPLACE_RSHIFT 76 +#define INPLACE_AND 77 +#define INPLACE_XOR 78 +#define INPLACE_OR 79 +#define LIST_TO_TUPLE 82 +#define RETURN_VALUE 83 +#define IMPORT_STAR 84 +#define SETUP_ANNOTATIONS 85 +#define YIELD_VALUE 86 +#define POP_BLOCK 87 +#define POP_EXCEPT 89 +#define HAVE_ARGUMENT 90 +#define STORE_NAME 90 +#define DELETE_NAME 91 +#define UNPACK_SEQUENCE 92 +#define FOR_ITER 93 +#define UNPACK_EX 94 +#define STORE_ATTR 95 +#define DELETE_ATTR 96 +#define STORE_GLOBAL 97 +#define DELETE_GLOBAL 98 +#define LOAD_CONST 100 +#define LOAD_NAME 101 +#define BUILD_TUPLE 102 +#define BUILD_LIST 103 +#define BUILD_SET 104 +#define BUILD_MAP 105 +#define LOAD_ATTR 106 +#define COMPARE_OP 107 +#define IMPORT_NAME 108 +#define IMPORT_FROM 109 +#define JUMP_FORWARD 110 +#define JUMP_IF_FALSE_OR_POP 111 +#define JUMP_IF_TRUE_OR_POP 112 +#define JUMP_ABSOLUTE 113 +#define POP_JUMP_IF_FALSE 114 +#define POP_JUMP_IF_TRUE 115 +#define LOAD_GLOBAL 116 +#define IS_OP 117 +#define CONTAINS_OP 118 +#define JUMP_IF_NOT_EXC_MATCH 121 +#define SETUP_FINALLY 122 +#define LOAD_FAST 124 +#define STORE_FAST 125 +#define DELETE_FAST 126 +#define RAISE_VARARGS 130 +#define CALL_FUNCTION 131 +#define MAKE_FUNCTION 132 +#define BUILD_SLICE 133 +#define LOAD_CLOSURE 135 +#define LOAD_DEREF 136 +#define STORE_DEREF 137 +#define DELETE_DEREF 138 +#define CALL_FUNCTION_KW 141 +#define CALL_FUNCTION_EX 142 +#define SETUP_WITH 143 +#define EXTENDED_ARG 144 +#define LIST_APPEND 145 +#define SET_ADD 146 +#define MAP_ADD 147 +#define LOAD_CLASSDEREF 148 +#define SETUP_ASYNC_WITH 154 +#define FORMAT_VALUE 155 +#define BUILD_CONST_KEY_MAP 156 +#define BUILD_STRING 157 +#define LOAD_METHOD 160 +#define CALL_METHOD 161 +#define LIST_EXTEND 162 +#define SET_UPDATE 163 +#define DICT_MERGE 164 +#define DICT_UPDATE 165 + +/* EXCEPT_HANDLER is a special, implicit block type which is created when + entering an except handler. It is not an opcode but we define it here + as we want it to be available to both frameobject.c and ceval.c, while + remaining private.*/ +#define EXCEPT_HANDLER 257 + +#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_OPCODE_H */ diff --git a/scripts/build-windows/py39-libs/include/osdefs.h b/scripts/build-windows/py39-libs/include/osdefs.h new file mode 100644 index 000000000..4ff50cbb2 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/osdefs.h @@ -0,0 +1,51 @@ +#ifndef Py_OSDEFS_H +#define Py_OSDEFS_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Operating system dependencies */ + +#ifdef MS_WINDOWS +#define SEP L'\\' +#define ALTSEP L'/' +#define MAXPATHLEN 256 +#define DELIM L';' +#endif + +#ifdef __VXWORKS__ +#define DELIM L';' +#endif + +/* Filename separator */ +#ifndef SEP +#define SEP L'/' +#endif + +/* Max pathname length */ +#ifdef __hpux +#include +#include +#ifndef PATH_MAX +#define PATH_MAX MAXPATHLEN +#endif +#endif + +#ifndef MAXPATHLEN +#if defined(PATH_MAX) && PATH_MAX > 1024 +#define MAXPATHLEN PATH_MAX +#else +#define MAXPATHLEN 1024 +#endif +#endif + +/* Search path entry delimiter */ +#ifndef DELIM +#define DELIM L':' +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_OSDEFS_H */ diff --git a/scripts/build-windows/py39-libs/include/osmodule.h b/scripts/build-windows/py39-libs/include/osmodule.h new file mode 100644 index 000000000..af5085029 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/osmodule.h @@ -0,0 +1,17 @@ + +/* os module interface */ + +#ifndef Py_OSMODULE_H +#define Py_OSMODULE_H +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 +PyAPI_FUNC(PyObject *) PyOS_FSPath(PyObject *path); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_OSMODULE_H */ diff --git a/scripts/build-windows/py39-libs/include/parsetok.h b/scripts/build-windows/py39-libs/include/parsetok.h new file mode 100644 index 000000000..d7778bf39 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/parsetok.h @@ -0,0 +1,110 @@ +/* Parser-tokenizer link interface */ + +#ifndef Py_LIMITED_API +#ifndef Py_PARSETOK_H +#define Py_PARSETOK_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "grammar.h" /* grammar */ +#include "node.h" /* node */ + +typedef struct { + int error; + PyObject *filename; + int lineno; + int offset; + char *text; /* UTF-8-encoded string */ + int token; + int expected; +} perrdetail; + +#if 0 +#define PyPARSE_YIELD_IS_KEYWORD 0x0001 +#endif + +#define PyPARSE_DONT_IMPLY_DEDENT 0x0002 + +#if 0 +#define PyPARSE_WITH_IS_KEYWORD 0x0003 +#define PyPARSE_PRINT_IS_FUNCTION 0x0004 +#define PyPARSE_UNICODE_LITERALS 0x0008 +#endif + +#define PyPARSE_IGNORE_COOKIE 0x0010 +#define PyPARSE_BARRY_AS_BDFL 0x0020 +#define PyPARSE_TYPE_COMMENTS 0x0040 +#define PyPARSE_ASYNC_HACKS 0x0080 + +PyAPI_FUNC(node *) PyParser_ParseString(const char *, grammar *, int, + perrdetail *); +PyAPI_FUNC(node *) PyParser_ParseFile (FILE *, const char *, grammar *, int, + const char *, const char *, + perrdetail *); + +PyAPI_FUNC(node *) PyParser_ParseStringFlags(const char *, grammar *, int, + perrdetail *, int); +PyAPI_FUNC(node *) PyParser_ParseFileFlags( + FILE *fp, + const char *filename, /* decoded from the filesystem encoding */ + const char *enc, + grammar *g, + int start, + const char *ps1, + const char *ps2, + perrdetail *err_ret, + int flags); +PyAPI_FUNC(node *) PyParser_ParseFileFlagsEx( + FILE *fp, + const char *filename, /* decoded from the filesystem encoding */ + const char *enc, + grammar *g, + int start, + const char *ps1, + const char *ps2, + perrdetail *err_ret, + int *flags); +PyAPI_FUNC(node *) PyParser_ParseFileObject( + FILE *fp, + PyObject *filename, + const char *enc, + grammar *g, + int start, + const char *ps1, + const char *ps2, + perrdetail *err_ret, + int *flags); + +PyAPI_FUNC(node *) PyParser_ParseStringFlagsFilename( + const char *s, + const char *filename, /* decoded from the filesystem encoding */ + grammar *g, + int start, + perrdetail *err_ret, + int flags); +PyAPI_FUNC(node *) PyParser_ParseStringFlagsFilenameEx( + const char *s, + const char *filename, /* decoded from the filesystem encoding */ + grammar *g, + int start, + perrdetail *err_ret, + int *flags); +PyAPI_FUNC(node *) PyParser_ParseStringObject( + const char *s, + PyObject *filename, + grammar *g, + int start, + perrdetail *err_ret, + int *flags); + +/* Note that the following functions are defined in pythonrun.c, + not in parsetok.c */ +PyAPI_FUNC(void) PyParser_SetError(perrdetail *); +PyAPI_FUNC(void) PyParser_ClearError(perrdetail *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PARSETOK_H */ +#endif /* !Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/patchlevel.h b/scripts/build-windows/py39-libs/include/patchlevel.h new file mode 100644 index 000000000..7e0b99db1 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/patchlevel.h @@ -0,0 +1,35 @@ + +/* Python version identification scheme. + + When the major or minor version changes, the VERSION variable in + configure.ac must also be changed. + + There is also (independent) API version information in modsupport.h. +*/ + +/* Values for PY_RELEASE_LEVEL */ +#define PY_RELEASE_LEVEL_ALPHA 0xA +#define PY_RELEASE_LEVEL_BETA 0xB +#define PY_RELEASE_LEVEL_GAMMA 0xC /* For release candidates */ +#define PY_RELEASE_LEVEL_FINAL 0xF /* Serial should be 0 here */ + /* Higher for patch releases */ + +/* Version parsed out into numeric values */ +/*--start constants--*/ +#define PY_MAJOR_VERSION 3 +#define PY_MINOR_VERSION 9 +#define PY_MICRO_VERSION 6 +#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL +#define PY_RELEASE_SERIAL 0 + +/* Version as a string */ +#define PY_VERSION "3.9.6" +/*--end constants--*/ + +/* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. + Use this for numeric comparisons, e.g. #if PY_VERSION_HEX >= ... */ +#define PY_VERSION_HEX ((PY_MAJOR_VERSION << 24) | \ + (PY_MINOR_VERSION << 16) | \ + (PY_MICRO_VERSION << 8) | \ + (PY_RELEASE_LEVEL << 4) | \ + (PY_RELEASE_SERIAL << 0)) diff --git a/scripts/build-windows/py39-libs/include/picklebufobject.h b/scripts/build-windows/py39-libs/include/picklebufobject.h new file mode 100644 index 000000000..5d0b0cfaa --- /dev/null +++ b/scripts/build-windows/py39-libs/include/picklebufobject.h @@ -0,0 +1,31 @@ +/* PickleBuffer object. This is built-in for ease of use from third-party + * C extensions. + */ + +#ifndef Py_PICKLEBUFOBJECT_H +#define Py_PICKLEBUFOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_LIMITED_API + +PyAPI_DATA(PyTypeObject) PyPickleBuffer_Type; + +#define PyPickleBuffer_Check(op) Py_IS_TYPE(op, &PyPickleBuffer_Type) + +/* Create a PickleBuffer redirecting to the given buffer-enabled object */ +PyAPI_FUNC(PyObject *) PyPickleBuffer_FromObject(PyObject *); +/* Get the PickleBuffer's underlying view to the original object + * (NULL if released) + */ +PyAPI_FUNC(const Py_buffer *) PyPickleBuffer_GetBuffer(PyObject *); +/* Release the PickleBuffer. Returns 0 on success, -1 on error. */ +PyAPI_FUNC(int) PyPickleBuffer_Release(PyObject *); + +#endif /* !Py_LIMITED_API */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PICKLEBUFOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/py_curses.h b/scripts/build-windows/py39-libs/include/py_curses.h new file mode 100644 index 000000000..b65aaa7b8 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/py_curses.h @@ -0,0 +1,99 @@ + +#ifndef Py_CURSES_H +#define Py_CURSES_H + +#ifdef __APPLE__ +/* +** On Mac OS X 10.2 [n]curses.h and stdlib.h use different guards +** against multiple definition of wchar_t. +*/ +#ifdef _BSD_WCHAR_T_DEFINED_ +#define _WCHAR_T +#endif +#endif /* __APPLE__ */ + +/* On FreeBSD, [n]curses.h and stdlib.h/wchar.h use different guards + against multiple definition of wchar_t and wint_t. */ +#if defined(__FreeBSD__) && defined(_XOPEN_SOURCE_EXTENDED) +# ifndef __wchar_t +# define __wchar_t +# endif +# ifndef __wint_t +# define __wint_t +# endif +#endif + +#if !defined(HAVE_CURSES_IS_PAD) && defined(WINDOW_HAS_FLAGS) +/* The following definition is necessary for ncurses 5.7; without it, + some of [n]curses.h set NCURSES_OPAQUE to 1, and then Python + can't get at the WINDOW flags field. */ +#define NCURSES_OPAQUE 0 +#endif + +#ifdef HAVE_NCURSES_H +#include +#else +#include +#endif + +#ifdef HAVE_NCURSES_H +/* configure was checking , but we will + use , which has some or all these features. */ +#if !defined(WINDOW_HAS_FLAGS) && !(NCURSES_OPAQUE+0) +#define WINDOW_HAS_FLAGS 1 +#endif +#if !defined(HAVE_CURSES_IS_PAD) && NCURSES_VERSION_PATCH+0 >= 20090906 +#define HAVE_CURSES_IS_PAD 1 +#endif +#ifndef MVWDELCH_IS_EXPRESSION +#define MVWDELCH_IS_EXPRESSION 1 +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define PyCurses_API_pointers 4 + +/* Type declarations */ + +typedef struct { + PyObject_HEAD + WINDOW *win; + char *encoding; +} PyCursesWindowObject; + +#define PyCursesWindow_Check(v) Py_IS_TYPE(v, &PyCursesWindow_Type) + +#define PyCurses_CAPSULE_NAME "_curses._C_API" + + +#ifdef CURSES_MODULE +/* This section is used when compiling _cursesmodule.c */ + +#else +/* This section is used in modules that use the _cursesmodule API */ + +static void **PyCurses_API; + +#define PyCursesWindow_Type (*(PyTypeObject *) PyCurses_API[0]) +#define PyCursesSetupTermCalled {if (! ((int (*)(void))PyCurses_API[1]) () ) return NULL;} +#define PyCursesInitialised {if (! ((int (*)(void))PyCurses_API[2]) () ) return NULL;} +#define PyCursesInitialisedColor {if (! ((int (*)(void))PyCurses_API[3]) () ) return NULL;} + +#define import_curses() \ + PyCurses_API = (void **)PyCapsule_Import(PyCurses_CAPSULE_NAME, 1); + +#endif + +/* general error messages */ +static const char catchall_ERR[] = "curses function returned ERR"; +static const char catchall_NULL[] = "curses function returned NULL"; + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(Py_CURSES_H) */ + diff --git a/scripts/build-windows/py39-libs/include/pyarena.h b/scripts/build-windows/py39-libs/include/pyarena.h new file mode 100644 index 000000000..97791bec8 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pyarena.h @@ -0,0 +1,64 @@ +/* An arena-like memory interface for the compiler. + */ + +#ifndef Py_LIMITED_API +#ifndef Py_PYARENA_H +#define Py_PYARENA_H + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct _arena PyArena; + + /* PyArena_New() and PyArena_Free() create a new arena and free it, + respectively. Once an arena has been created, it can be used + to allocate memory via PyArena_Malloc(). Pointers to PyObject can + also be registered with the arena via PyArena_AddPyObject(), and the + arena will ensure that the PyObjects stay alive at least until + PyArena_Free() is called. When an arena is freed, all the memory it + allocated is freed, the arena releases internal references to registered + PyObject*, and none of its pointers are valid. + XXX (tim) What does "none of its pointers are valid" mean? Does it + XXX mean that pointers previously obtained via PyArena_Malloc() are + XXX no longer valid? (That's clearly true, but not sure that's what + XXX the text is trying to say.) + + PyArena_New() returns an arena pointer. On error, it + returns a negative number and sets an exception. + XXX (tim): Not true. On error, PyArena_New() actually returns NULL, + XXX and looks like it may or may not set an exception (e.g., if the + XXX internal PyList_New(0) returns NULL, PyArena_New() passes that on + XXX and an exception is set; OTOH, if the internal + XXX block_new(DEFAULT_BLOCK_SIZE) returns NULL, that's passed on but + XXX an exception is not set in that case). + */ + PyAPI_FUNC(PyArena *) PyArena_New(void); + PyAPI_FUNC(void) PyArena_Free(PyArena *); + + /* Mostly like malloc(), return the address of a block of memory spanning + * `size` bytes, or return NULL (without setting an exception) if enough + * new memory can't be obtained. Unlike malloc(0), PyArena_Malloc() with + * size=0 does not guarantee to return a unique pointer (the pointer + * returned may equal one or more other pointers obtained from + * PyArena_Malloc()). + * Note that pointers obtained via PyArena_Malloc() must never be passed to + * the system free() or realloc(), or to any of Python's similar memory- + * management functions. PyArena_Malloc()-obtained pointers remain valid + * until PyArena_Free(ar) is called, at which point all pointers obtained + * from the arena `ar` become invalid simultaneously. + */ + PyAPI_FUNC(void *) PyArena_Malloc(PyArena *, size_t size); + + /* This routine isn't a proper arena allocation routine. It takes + * a PyObject* and records it so that it can be DECREFed when the + * arena is freed. + */ + PyAPI_FUNC(int) PyArena_AddPyObject(PyArena *, PyObject *); + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_PYARENA_H */ +#endif /* Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/pycapsule.h b/scripts/build-windows/py39-libs/include/pycapsule.h new file mode 100644 index 000000000..9387a1c76 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pycapsule.h @@ -0,0 +1,59 @@ + +/* Capsule objects let you wrap a C "void *" pointer in a Python + object. They're a way of passing data through the Python interpreter + without creating your own custom type. + + Capsules are used for communication between extension modules. + They provide a way for an extension module to export a C interface + to other extension modules, so that extension modules can use the + Python import mechanism to link to one another. + + For more information, please see "c-api/capsule.html" in the + documentation. +*/ + +#ifndef Py_CAPSULE_H +#define Py_CAPSULE_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PyCapsule_Type; + +typedef void (*PyCapsule_Destructor)(PyObject *); + +#define PyCapsule_CheckExact(op) Py_IS_TYPE(op, &PyCapsule_Type) + + +PyAPI_FUNC(PyObject *) PyCapsule_New( + void *pointer, + const char *name, + PyCapsule_Destructor destructor); + +PyAPI_FUNC(void *) PyCapsule_GetPointer(PyObject *capsule, const char *name); + +PyAPI_FUNC(PyCapsule_Destructor) PyCapsule_GetDestructor(PyObject *capsule); + +PyAPI_FUNC(const char *) PyCapsule_GetName(PyObject *capsule); + +PyAPI_FUNC(void *) PyCapsule_GetContext(PyObject *capsule); + +PyAPI_FUNC(int) PyCapsule_IsValid(PyObject *capsule, const char *name); + +PyAPI_FUNC(int) PyCapsule_SetPointer(PyObject *capsule, void *pointer); + +PyAPI_FUNC(int) PyCapsule_SetDestructor(PyObject *capsule, PyCapsule_Destructor destructor); + +PyAPI_FUNC(int) PyCapsule_SetName(PyObject *capsule, const char *name); + +PyAPI_FUNC(int) PyCapsule_SetContext(PyObject *capsule, void *context); + +PyAPI_FUNC(void *) PyCapsule_Import( + const char *name, /* UTF-8 encoded string */ + int no_block); + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CAPSULE_H */ diff --git a/scripts/build-windows/py39-libs/include/pyconfig.h b/scripts/build-windows/py39-libs/include/pyconfig.h new file mode 100644 index 000000000..0d5ac77fc --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pyconfig.h @@ -0,0 +1,687 @@ +#ifndef Py_CONFIG_H +#define Py_CONFIG_H + +/* pyconfig.h. NOT Generated automatically by configure. + +This is a manually maintained version used for the Watcom, +Borland and Microsoft Visual C++ compilers. It is a +standard part of the Python distribution. + +WINDOWS DEFINES: +The code specific to Windows should be wrapped around one of +the following #defines + +MS_WIN64 - Code specific to the MS Win64 API +MS_WIN32 - Code specific to the MS Win32 (and Win64) API (obsolete, this covers all supported APIs) +MS_WINDOWS - Code specific to Windows, but all versions. +Py_ENABLE_SHARED - Code if the Python core is built as a DLL. + +Also note that neither "_M_IX86" or "_MSC_VER" should be used for +any purpose other than "Windows Intel x86 specific" and "Microsoft +compiler specific". Therefore, these should be very rare. + + +NOTE: The following symbols are deprecated: +NT, USE_DL_EXPORT, USE_DL_IMPORT, DL_EXPORT, DL_IMPORT +MS_CORE_DLL. + +WIN32 is still required for the locale module. + +*/ + +/* Deprecated USE_DL_EXPORT macro - please use Py_BUILD_CORE */ +#ifdef USE_DL_EXPORT +# define Py_BUILD_CORE +#endif /* USE_DL_EXPORT */ + +/* Visual Studio 2005 introduces deprecation warnings for + "insecure" and POSIX functions. The insecure functions should + be replaced by *_s versions (according to Microsoft); the + POSIX functions by _* versions (which, according to Microsoft, + would be ISO C conforming). Neither renaming is feasible, so + we just silence the warnings. */ + +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#endif + +#define HAVE_IO_H +#define HAVE_SYS_UTIME_H +#define HAVE_TEMPNAM +#define HAVE_TMPFILE +#define HAVE_TMPNAM +#define HAVE_CLOCK +#define HAVE_STRERROR + +#include + +#define HAVE_HYPOT +#define HAVE_STRFTIME +#define DONT_HAVE_SIG_ALARM +#define DONT_HAVE_SIG_PAUSE +#define LONG_BIT 32 +#define WORD_BIT 32 + +#define MS_WIN32 /* only support win32 and greater. */ +#define MS_WINDOWS +#ifndef PYTHONPATH +# define PYTHONPATH L".\\DLLs;.\\lib" +#endif +#define NT_THREADS +#define WITH_THREAD +#ifndef NETSCAPE_PI +#define USE_SOCKET +#endif + + +/* Compiler specific defines */ + +/* ------------------------------------------------------------------------*/ +/* Microsoft C defines _MSC_VER */ +#ifdef _MSC_VER + +/* We want COMPILER to expand to a string containing _MSC_VER's *value*. + * This is horridly tricky, because the stringization operator only works + * on macro arguments, and doesn't evaluate macros passed *as* arguments. + * Attempts simpler than the following appear doomed to produce "_MSC_VER" + * literally in the string. + */ +#define _Py_PASTE_VERSION(SUFFIX) \ + ("[MSC v." _Py_STRINGIZE(_MSC_VER) " " SUFFIX "]") +/* e.g., this produces, after compile-time string catenation, + * ("[MSC v.1200 32 bit (Intel)]") + * + * _Py_STRINGIZE(_MSC_VER) expands to + * _Py_STRINGIZE1((_MSC_VER)) expands to + * _Py_STRINGIZE2(_MSC_VER) but as this call is the result of token-pasting + * it's scanned again for macros and so further expands to (under MSVC 6) + * _Py_STRINGIZE2(1200) which then expands to + * "1200" + */ +#define _Py_STRINGIZE(X) _Py_STRINGIZE1((X)) +#define _Py_STRINGIZE1(X) _Py_STRINGIZE2 ## X +#define _Py_STRINGIZE2(X) #X + +/* MSVC defines _WINxx to differentiate the windows platform types + + Note that for compatibility reasons _WIN32 is defined on Win32 + *and* on Win64. For the same reasons, in Python, MS_WIN32 is + defined on Win32 *and* Win64. Win32 only code must therefore be + guarded as follows: + #if defined(MS_WIN32) && !defined(MS_WIN64) +*/ +#ifdef _WIN64 +#define MS_WIN64 +#endif + +/* set the COMPILER */ +#ifdef MS_WIN64 +#if defined(_M_X64) || defined(_M_AMD64) +#if defined(__INTEL_COMPILER) +#define COMPILER ("[ICC v." _Py_STRINGIZE(__INTEL_COMPILER) " 64 bit (amd64) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") +#else +#define COMPILER _Py_PASTE_VERSION("64 bit (AMD64)") +#endif /* __INTEL_COMPILER */ +#define PYD_PLATFORM_TAG "win_amd64" +#elif defined(_M_ARM64) +#define COMPILER _Py_PASTE_VERSION("64 bit (ARM64)") +#define PYD_PLATFORM_TAG "win_arm64" +#else +#define COMPILER _Py_PASTE_VERSION("64 bit (Unknown)") +#endif +#endif /* MS_WIN64 */ + +/* set the version macros for the windows headers */ +/* Python 3.9+ requires Windows 8 or greater */ +#define Py_WINVER 0x0602 /* _WIN32_WINNT_WIN8 */ +#define Py_NTDDI NTDDI_WIN8 + +/* We only set these values when building Python - we don't want to force + these values on extensions, as that will affect the prototypes and + structures exposed in the Windows headers. Even when building Python, we + allow a single source file to override this - they may need access to + structures etc so it can optionally use new Windows features if it + determines at runtime they are available. +*/ +#if defined(Py_BUILD_CORE) || defined(Py_BUILD_CORE_BUILTIN) || defined(Py_BUILD_CORE_MODULE) +#ifndef NTDDI_VERSION +#define NTDDI_VERSION Py_NTDDI +#endif +#ifndef WINVER +#define WINVER Py_WINVER +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT Py_WINVER +#endif +#endif + +/* _W64 is not defined for VC6 or eVC4 */ +#ifndef _W64 +#define _W64 +#endif + +/* Define like size_t, omitting the "unsigned" */ +#ifdef MS_WIN64 +typedef __int64 ssize_t; +#else +typedef _W64 int ssize_t; +#endif +#define HAVE_SSIZE_T 1 + +#if defined(MS_WIN32) && !defined(MS_WIN64) +#if defined(_M_IX86) +#if defined(__INTEL_COMPILER) +#define COMPILER ("[ICC v." _Py_STRINGIZE(__INTEL_COMPILER) " 32 bit (Intel) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") +#else +#define COMPILER _Py_PASTE_VERSION("32 bit (Intel)") +#endif /* __INTEL_COMPILER */ +#define PYD_PLATFORM_TAG "win32" +#elif defined(_M_ARM) +#define COMPILER _Py_PASTE_VERSION("32 bit (ARM)") +#define PYD_PLATFORM_TAG "win_arm32" +#else +#define COMPILER _Py_PASTE_VERSION("32 bit (Unknown)") +#endif +#endif /* MS_WIN32 && !MS_WIN64 */ + +typedef int pid_t; + +#include +#define Py_IS_NAN _isnan +#define Py_IS_INFINITY(X) (!_finite(X) && !_isnan(X)) +#define Py_IS_FINITE(X) _finite(X) + +/* define some ANSI types that are not defined in earlier Win headers */ +#if _MSC_VER >= 1200 +/* This file only exists in VC 6.0 or higher */ +#include +#endif + +#endif /* _MSC_VER */ + +/* ------------------------------------------------------------------------*/ +/* egcs/gnu-win32 defines __GNUC__ and _WIN32 */ +#if defined(__GNUC__) && defined(_WIN32) +/* XXX These defines are likely incomplete, but should be easy to fix. + They should be complete enough to build extension modules. */ +/* Suggested by Rene Liebscher to avoid a GCC 2.91.* + bug that requires structure imports. More recent versions of the + compiler don't exhibit this bug. +*/ +#if (__GNUC__==2) && (__GNUC_MINOR__<=91) +#warning "Please use an up-to-date version of gcc! (>2.91 recommended)" +#endif + +#define COMPILER "[gcc]" +#define PY_LONG_LONG long long +#define PY_LLONG_MIN LLONG_MIN +#define PY_LLONG_MAX LLONG_MAX +#define PY_ULLONG_MAX ULLONG_MAX +#endif /* GNUC */ + +/* ------------------------------------------------------------------------*/ +/* lcc-win32 defines __LCC__ */ +#if defined(__LCC__) +/* XXX These defines are likely incomplete, but should be easy to fix. + They should be complete enough to build extension modules. */ + +#define COMPILER "[lcc-win32]" +typedef int pid_t; +/* __declspec() is supported here too - do nothing to get the defaults */ + +#endif /* LCC */ + +/* ------------------------------------------------------------------------*/ +/* End of compilers - finish up */ + +#ifndef NO_STDIO_H +# include +#endif + +/* 64 bit ints are usually spelt __int64 unless compiler has overridden */ +#ifndef PY_LONG_LONG +# define PY_LONG_LONG __int64 +# define PY_LLONG_MAX _I64_MAX +# define PY_LLONG_MIN _I64_MIN +# define PY_ULLONG_MAX _UI64_MAX +#endif + +/* For Windows the Python core is in a DLL by default. Test +Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */ +#if !defined(MS_NO_COREDLL) && !defined(Py_NO_ENABLE_SHARED) +# define Py_ENABLE_SHARED 1 /* standard symbol for shared library */ +# define MS_COREDLL /* deprecated old symbol */ +#endif /* !MS_NO_COREDLL && ... */ + +/* All windows compilers that use this header support __declspec */ +#define HAVE_DECLSPEC_DLL + +/* For an MSVC DLL, we can nominate the .lib files used by extensions */ +#ifdef MS_COREDLL +# if !defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_BUILTIN) + /* not building the core - must be an ext */ +# if defined(_MSC_VER) + /* So MSVC users need not specify the .lib + file in their Makefile (other compilers are + generally taken care of by distutils.) */ +# if defined(_DEBUG) +# pragma comment(lib,"python39_d.lib") +# elif defined(Py_LIMITED_API) +# pragma comment(lib,"python3.lib") +# else +# pragma comment(lib,"python39.lib") +# endif /* _DEBUG */ +# endif /* _MSC_VER */ +# endif /* Py_BUILD_CORE */ +#endif /* MS_COREDLL */ + +#if defined(MS_WIN64) +/* maintain "win32" sys.platform for backward compatibility of Python code, + the Win64 API should be close enough to the Win32 API to make this + preferable */ +# define PLATFORM "win32" +# define SIZEOF_VOID_P 8 +# define SIZEOF_TIME_T 8 +# define SIZEOF_OFF_T 4 +# define SIZEOF_FPOS_T 8 +# define SIZEOF_HKEY 8 +# define SIZEOF_SIZE_T 8 +/* configure.ac defines HAVE_LARGEFILE_SUPPORT iff + sizeof(off_t) > sizeof(long), and sizeof(long long) >= sizeof(off_t). + On Win64 the second condition is not true, but if fpos_t replaces off_t + then this is true. The uses of HAVE_LARGEFILE_SUPPORT imply that Win64 + should define this. */ +# define HAVE_LARGEFILE_SUPPORT +#elif defined(MS_WIN32) +# define PLATFORM "win32" +# define HAVE_LARGEFILE_SUPPORT +# define SIZEOF_VOID_P 4 +# define SIZEOF_OFF_T 4 +# define SIZEOF_FPOS_T 8 +# define SIZEOF_HKEY 4 +# define SIZEOF_SIZE_T 4 + /* MS VS2005 changes time_t to a 64-bit type on all platforms */ +# if defined(_MSC_VER) && _MSC_VER >= 1400 +# define SIZEOF_TIME_T 8 +# else +# define SIZEOF_TIME_T 4 +# endif +#endif + +#ifdef _DEBUG +# define Py_DEBUG +#endif + + +#ifdef MS_WIN32 + +#define SIZEOF_SHORT 2 +#define SIZEOF_INT 4 +#define SIZEOF_LONG 4 +#define SIZEOF_LONG_LONG 8 +#define SIZEOF_DOUBLE 8 +#define SIZEOF_FLOAT 4 + +/* VC 7.1 has them and VC 6.0 does not. VC 6.0 has a version number of 1200. + Microsoft eMbedded Visual C++ 4.0 has a version number of 1201 and doesn't + define these. + If some compiler does not provide them, modify the #if appropriately. */ +#if defined(_MSC_VER) +#if _MSC_VER > 1300 +#define HAVE_UINTPTR_T 1 +#define HAVE_INTPTR_T 1 +#else +/* VC6, VS 2002 and eVC4 don't support the C99 LL suffix for 64-bit integer literals */ +#define Py_LL(x) x##I64 +#endif /* _MSC_VER > 1300 */ +#endif /* _MSC_VER */ + +#endif + +/* define signed and unsigned exact-width 32-bit and 64-bit types, used in the + implementation of Python integers. */ +#define PY_UINT32_T uint32_t +#define PY_UINT64_T uint64_t +#define PY_INT32_T int32_t +#define PY_INT64_T int64_t + +/* Fairly standard from here! */ + +/* Define to 1 if you have the `copysign' function. */ +#define HAVE_COPYSIGN 1 + +/* Define to 1 if you have the `round' function. */ +#if _MSC_VER >= 1800 +#define HAVE_ROUND 1 +#endif + +/* Define to 1 if you have the `isinf' macro. */ +#define HAVE_DECL_ISINF 1 + +/* Define to 1 if you have the `isnan' function. */ +#define HAVE_DECL_ISNAN 1 + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define to empty if the keyword does not work. */ +/* #define const */ + +/* Define to 1 if you have the header file. */ +#define HAVE_CONIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRECT_H 1 + +/* Define to 1 if you have the declaration of `tzname', and to 0 if you don't. + */ +#define HAVE_DECL_TZNAME 1 + +/* Define if you have dirent.h. */ +/* #define DIRENT 1 */ + +/* Define to the type of elements in the array set by `getgroups'. + Usually this is either `int' or `gid_t'. */ +/* #undef GETGROUPS_T */ + +/* Define to `int' if doesn't define. */ +/* #undef gid_t */ + +/* Define if your struct tm has tm_zone. */ +/* #undef HAVE_TM_ZONE */ + +/* Define if you don't have tm_zone but do have the external array + tzname. */ +#define HAVE_TZNAME + +/* Define to `int' if doesn't define. */ +/* #undef mode_t */ + +/* Define if you don't have dirent.h, but have ndir.h. */ +/* #undef NDIR */ + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define if the system does not provide POSIX.1 features except + with this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define to `unsigned' if doesn't define. */ +/* #undef size_t */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you don't have dirent.h, but have sys/dir.h. */ +/* #undef SYSDIR */ + +/* Define if you don't have dirent.h, but have sys/ndir.h. */ +/* #undef SYSNDIR */ + +/* Define if you can safely include both and . */ +/* #undef TIME_WITH_SYS_TIME */ + +/* Define if your declares struct tm. */ +/* #define TM_IN_SYS_TIME 1 */ + +/* Define to `int' if doesn't define. */ +/* #undef uid_t */ + +/* Define if the closedir function returns void instead of int. */ +/* #undef VOID_CLOSEDIR */ + +/* Define if getpgrp() must be called as getpgrp(0) + and (consequently) setpgrp() as setpgrp(0, 0). */ +/* #undef GETPGRP_HAVE_ARGS */ + +/* Define this if your time.h defines altzone */ +/* #define HAVE_ALTZONE */ + +/* Define if you have the putenv function. */ +#define HAVE_PUTENV + +/* Define if your compiler supports function prototypes */ +#define HAVE_PROTOTYPES + +/* Define if you can safely include both and + (which you can't on SCO ODT 3.0). */ +/* #undef SYS_SELECT_WITH_SYS_TIME */ + +/* Define if you want build the _decimal module using a coroutine-local rather + than a thread-local context */ +#define WITH_DECIMAL_CONTEXTVAR 1 + +/* Define if you want documentation strings in extension modules */ +#define WITH_DOC_STRINGS 1 + +/* Define if you want to compile in rudimentary thread support */ +/* #undef WITH_THREAD */ + +/* Define if you want to use the GNU readline library */ +/* #define WITH_READLINE 1 */ + +/* Use Python's own small-block memory-allocator. */ +#define WITH_PYMALLOC 1 + +/* Define if you have clock. */ +/* #define HAVE_CLOCK */ + +/* Define when any dynamic module loading is enabled */ +#define HAVE_DYNAMIC_LOADING + +/* Define if you have ftime. */ +#define HAVE_FTIME + +/* Define if you have getpeername. */ +#define HAVE_GETPEERNAME + +/* Define if you have getpgrp. */ +/* #undef HAVE_GETPGRP */ + +/* Define if you have getpid. */ +#define HAVE_GETPID + +/* Define if you have gettimeofday. */ +/* #undef HAVE_GETTIMEOFDAY */ + +/* Define if you have getwd. */ +/* #undef HAVE_GETWD */ + +/* Define if you have lstat. */ +/* #undef HAVE_LSTAT */ + +/* Define if you have the mktime function. */ +#define HAVE_MKTIME + +/* Define if you have nice. */ +/* #undef HAVE_NICE */ + +/* Define if you have readlink. */ +/* #undef HAVE_READLINK */ + +/* Define if you have setpgid. */ +/* #undef HAVE_SETPGID */ + +/* Define if you have setpgrp. */ +/* #undef HAVE_SETPGRP */ + +/* Define if you have setsid. */ +/* #undef HAVE_SETSID */ + +/* Define if you have setvbuf. */ +#define HAVE_SETVBUF + +/* Define if you have siginterrupt. */ +/* #undef HAVE_SIGINTERRUPT */ + +/* Define if you have symlink. */ +/* #undef HAVE_SYMLINK */ + +/* Define if you have tcgetpgrp. */ +/* #undef HAVE_TCGETPGRP */ + +/* Define if you have tcsetpgrp. */ +/* #undef HAVE_TCSETPGRP */ + +/* Define if you have times. */ +/* #undef HAVE_TIMES */ + +/* Define if you have uname. */ +/* #undef HAVE_UNAME */ + +/* Define if you have waitpid. */ +/* #undef HAVE_WAITPID */ + +/* Define to 1 if you have the `wcsftime' function. */ +#if defined(_MSC_VER) && _MSC_VER >= 1310 +#define HAVE_WCSFTIME 1 +#endif + +/* Define to 1 if you have the `wcscoll' function. */ +#define HAVE_WCSCOLL 1 + +/* Define to 1 if you have the `wcsxfrm' function. */ +#define HAVE_WCSXFRM 1 + +/* Define if the zlib library has inflateCopy */ +#define HAVE_ZLIB_COPY 1 + +/* Define if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_PROCESS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define if you have the prototypes. */ +#define HAVE_STDARG_PROTOTYPES + +/* Define if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_AUDIOIO_H */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_PARAM_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_SELECT_H 1 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +/* #define HAVE_SYS_TIME_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_TIMES_H 1 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have the header file. */ +/* #define HAVE_SYS_UN_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_UTIME_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_UTSNAME_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_UNISTD_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_UTIME_H 1 */ + +/* Define if the compiler provides a wchar.h header file. */ +#define HAVE_WCHAR_H 1 + +/* The size of `wchar_t', as computed by sizeof. */ +#define SIZEOF_WCHAR_T 2 + +/* The size of `_Bool', as computed by sizeof. */ +#define SIZEOF__BOOL 1 + +/* The size of `pid_t', as computed by sizeof. */ +#define SIZEOF_PID_T SIZEOF_INT + +/* Define if you have the dl library (-ldl). */ +/* #undef HAVE_LIBDL */ + +/* Define if you have the mpc library (-lmpc). */ +/* #undef HAVE_LIBMPC */ + +/* Define if you have the nsl library (-lnsl). */ +#define HAVE_LIBNSL 1 + +/* Define if you have the seq library (-lseq). */ +/* #undef HAVE_LIBSEQ */ + +/* Define if you have the socket library (-lsocket). */ +#define HAVE_LIBSOCKET 1 + +/* Define if you have the sun library (-lsun). */ +/* #undef HAVE_LIBSUN */ + +/* Define if you have the termcap library (-ltermcap). */ +/* #undef HAVE_LIBTERMCAP */ + +/* Define if you have the termlib library (-ltermlib). */ +/* #undef HAVE_LIBTERMLIB */ + +/* Define if you have the thread library (-lthread). */ +/* #undef HAVE_LIBTHREAD */ + +/* WinSock does not use a bitmask in select, and uses + socket handles greater than FD_SETSIZE */ +#define Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE + +/* Define if C doubles are 64-bit IEEE 754 binary format, stored with the + least significant byte first */ +#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1 + +/* Define to 1 if you have the `erf' function. */ +#define HAVE_ERF 1 + +/* Define to 1 if you have the `erfc' function. */ +#define HAVE_ERFC 1 + +/* Define if you have the 'inet_pton' function. */ +#define HAVE_INET_PTON 1 + +/* framework name */ +#define _PYTHONFRAMEWORK "" + +/* Define if libssl has X509_VERIFY_PARAM_set1_host and related function */ +#define HAVE_X509_VERIFY_PARAM_SET1_HOST 1 + +#define PLATLIBDIR "lib" + +#endif /* !Py_CONFIG_H */ diff --git a/scripts/build-windows/py39-libs/include/pyctype.h b/scripts/build-windows/py39-libs/include/pyctype.h new file mode 100644 index 000000000..8f4bd79c3 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pyctype.h @@ -0,0 +1,39 @@ +#ifndef Py_LIMITED_API +#ifndef PYCTYPE_H +#define PYCTYPE_H +#ifdef __cplusplus +extern "C" { +#endif + +#define PY_CTF_LOWER 0x01 +#define PY_CTF_UPPER 0x02 +#define PY_CTF_ALPHA (PY_CTF_LOWER|PY_CTF_UPPER) +#define PY_CTF_DIGIT 0x04 +#define PY_CTF_ALNUM (PY_CTF_ALPHA|PY_CTF_DIGIT) +#define PY_CTF_SPACE 0x08 +#define PY_CTF_XDIGIT 0x10 + +PyAPI_DATA(const unsigned int) _Py_ctype_table[256]; + +/* Unlike their C counterparts, the following macros are not meant to + * handle an int with any of the values [EOF, 0-UCHAR_MAX]. The argument + * must be a signed/unsigned char. */ +#define Py_ISLOWER(c) (_Py_ctype_table[Py_CHARMASK(c)] & PY_CTF_LOWER) +#define Py_ISUPPER(c) (_Py_ctype_table[Py_CHARMASK(c)] & PY_CTF_UPPER) +#define Py_ISALPHA(c) (_Py_ctype_table[Py_CHARMASK(c)] & PY_CTF_ALPHA) +#define Py_ISDIGIT(c) (_Py_ctype_table[Py_CHARMASK(c)] & PY_CTF_DIGIT) +#define Py_ISXDIGIT(c) (_Py_ctype_table[Py_CHARMASK(c)] & PY_CTF_XDIGIT) +#define Py_ISALNUM(c) (_Py_ctype_table[Py_CHARMASK(c)] & PY_CTF_ALNUM) +#define Py_ISSPACE(c) (_Py_ctype_table[Py_CHARMASK(c)] & PY_CTF_SPACE) + +PyAPI_DATA(const unsigned char) _Py_ctype_tolower[256]; +PyAPI_DATA(const unsigned char) _Py_ctype_toupper[256]; + +#define Py_TOLOWER(c) (_Py_ctype_tolower[Py_CHARMASK(c)]) +#define Py_TOUPPER(c) (_Py_ctype_toupper[Py_CHARMASK(c)]) + +#ifdef __cplusplus +} +#endif +#endif /* !PYCTYPE_H */ +#endif /* !Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/pydebug.h b/scripts/build-windows/py39-libs/include/pydebug.h new file mode 100644 index 000000000..e34cf9078 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pydebug.h @@ -0,0 +1,38 @@ +#ifndef Py_LIMITED_API +#ifndef Py_PYDEBUG_H +#define Py_PYDEBUG_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(int) Py_DebugFlag; +PyAPI_DATA(int) Py_VerboseFlag; +PyAPI_DATA(int) Py_QuietFlag; +PyAPI_DATA(int) Py_InteractiveFlag; +PyAPI_DATA(int) Py_InspectFlag; +PyAPI_DATA(int) Py_OptimizeFlag; +PyAPI_DATA(int) Py_NoSiteFlag; +PyAPI_DATA(int) Py_BytesWarningFlag; +PyAPI_DATA(int) Py_FrozenFlag; +PyAPI_DATA(int) Py_IgnoreEnvironmentFlag; +PyAPI_DATA(int) Py_DontWriteBytecodeFlag; +PyAPI_DATA(int) Py_NoUserSiteDirectory; +PyAPI_DATA(int) Py_UnbufferedStdioFlag; +PyAPI_DATA(int) Py_HashRandomizationFlag; +PyAPI_DATA(int) Py_IsolatedFlag; + +#ifdef MS_WINDOWS +PyAPI_DATA(int) Py_LegacyWindowsFSEncodingFlag; +PyAPI_DATA(int) Py_LegacyWindowsStdioFlag; +#endif + +/* this is a wrapper around getenv() that pays attention to + Py_IgnoreEnvironmentFlag. It should be used for getting variables like + PYTHONPATH and PYTHONHOME from the environment */ +#define Py_GETENV(s) (Py_IgnoreEnvironmentFlag ? NULL : getenv(s)) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYDEBUG_H */ +#endif /* Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/pydtrace.h b/scripts/build-windows/py39-libs/include/pydtrace.h new file mode 100644 index 000000000..8fac20043 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pydtrace.h @@ -0,0 +1,59 @@ +/* Static DTrace probes interface */ + +#ifndef Py_DTRACE_H +#define Py_DTRACE_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef WITH_DTRACE + +#include "pydtrace_probes.h" + +/* pydtrace_probes.h, on systems with DTrace, is auto-generated to include + `PyDTrace_{PROBE}` and `PyDTrace_{PROBE}_ENABLED()` macros for every probe + defined in pydtrace_provider.d. + + Calling these functions must be guarded by a `PyDTrace_{PROBE}_ENABLED()` + check to minimize performance impact when probing is off. For example: + + if (PyDTrace_FUNCTION_ENTRY_ENABLED()) + PyDTrace_FUNCTION_ENTRY(f); +*/ + +#else + +/* Without DTrace, compile to nothing. */ + +static inline void PyDTrace_LINE(const char *arg0, const char *arg1, int arg2) {} +static inline void PyDTrace_FUNCTION_ENTRY(const char *arg0, const char *arg1, int arg2) {} +static inline void PyDTrace_FUNCTION_RETURN(const char *arg0, const char *arg1, int arg2) {} +static inline void PyDTrace_GC_START(int arg0) {} +static inline void PyDTrace_GC_DONE(Py_ssize_t arg0) {} +static inline void PyDTrace_INSTANCE_NEW_START(int arg0) {} +static inline void PyDTrace_INSTANCE_NEW_DONE(int arg0) {} +static inline void PyDTrace_INSTANCE_DELETE_START(int arg0) {} +static inline void PyDTrace_INSTANCE_DELETE_DONE(int arg0) {} +static inline void PyDTrace_IMPORT_FIND_LOAD_START(const char *arg0) {} +static inline void PyDTrace_IMPORT_FIND_LOAD_DONE(const char *arg0, int arg1) {} +static inline void PyDTrace_AUDIT(const char *arg0, void *arg1) {} + +static inline int PyDTrace_LINE_ENABLED(void) { return 0; } +static inline int PyDTrace_FUNCTION_ENTRY_ENABLED(void) { return 0; } +static inline int PyDTrace_FUNCTION_RETURN_ENABLED(void) { return 0; } +static inline int PyDTrace_GC_START_ENABLED(void) { return 0; } +static inline int PyDTrace_GC_DONE_ENABLED(void) { return 0; } +static inline int PyDTrace_INSTANCE_NEW_START_ENABLED(void) { return 0; } +static inline int PyDTrace_INSTANCE_NEW_DONE_ENABLED(void) { return 0; } +static inline int PyDTrace_INSTANCE_DELETE_START_ENABLED(void) { return 0; } +static inline int PyDTrace_INSTANCE_DELETE_DONE_ENABLED(void) { return 0; } +static inline int PyDTrace_IMPORT_FIND_LOAD_START_ENABLED(void) { return 0; } +static inline int PyDTrace_IMPORT_FIND_LOAD_DONE_ENABLED(void) { return 0; } +static inline int PyDTrace_AUDIT_ENABLED(void) { return 0; } + +#endif /* !WITH_DTRACE */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_DTRACE_H */ diff --git a/scripts/build-windows/py39-libs/include/pyerrors.h b/scripts/build-windows/py39-libs/include/pyerrors.h new file mode 100644 index 000000000..bd634e710 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pyerrors.h @@ -0,0 +1,326 @@ +#ifndef Py_ERRORS_H +#define Py_ERRORS_H +#ifdef __cplusplus +extern "C" { +#endif + +#include // va_list + +/* Error handling definitions */ + +PyAPI_FUNC(void) PyErr_SetNone(PyObject *); +PyAPI_FUNC(void) PyErr_SetObject(PyObject *, PyObject *); +PyAPI_FUNC(void) PyErr_SetString( + PyObject *exception, + const char *string /* decoded from utf-8 */ + ); +PyAPI_FUNC(PyObject *) PyErr_Occurred(void); +PyAPI_FUNC(void) PyErr_Clear(void); +PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **); +PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(void) PyErr_GetExcInfo(PyObject **, PyObject **, PyObject **); +PyAPI_FUNC(void) PyErr_SetExcInfo(PyObject *, PyObject *, PyObject *); +#endif + +/* Defined in Python/pylifecycle.c + + The Py_FatalError() function is replaced with a macro which logs + automatically the name of the current function, unless the Py_LIMITED_API + macro is defined. */ +PyAPI_FUNC(void) _Py_NO_RETURN Py_FatalError(const char *message); + +#if defined(Py_DEBUG) || defined(Py_LIMITED_API) +#define _PyErr_OCCURRED() PyErr_Occurred() +#else +#define _PyErr_OCCURRED() (PyThreadState_GET()->curexc_type) +#endif + +/* Error testing and normalization */ +PyAPI_FUNC(int) PyErr_GivenExceptionMatches(PyObject *, PyObject *); +PyAPI_FUNC(int) PyErr_ExceptionMatches(PyObject *); +PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**); + +/* Traceback manipulation (PEP 3134) */ +PyAPI_FUNC(int) PyException_SetTraceback(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyException_GetTraceback(PyObject *); + +/* Cause manipulation (PEP 3134) */ +PyAPI_FUNC(PyObject *) PyException_GetCause(PyObject *); +PyAPI_FUNC(void) PyException_SetCause(PyObject *, PyObject *); + +/* Context manipulation (PEP 3134) */ +PyAPI_FUNC(PyObject *) PyException_GetContext(PyObject *); +PyAPI_FUNC(void) PyException_SetContext(PyObject *, PyObject *); + +/* */ + +#define PyExceptionClass_Check(x) \ + (PyType_Check((x)) && \ + PyType_FastSubclass((PyTypeObject*)(x), Py_TPFLAGS_BASE_EXC_SUBCLASS)) + +#define PyExceptionInstance_Check(x) \ + PyType_FastSubclass(Py_TYPE(x), Py_TPFLAGS_BASE_EXC_SUBCLASS) + +PyAPI_FUNC(const char *) PyExceptionClass_Name(PyObject *); + +#define PyExceptionInstance_Class(x) ((PyObject*)Py_TYPE(x)) + + +/* Predefined exceptions */ + +PyAPI_DATA(PyObject *) PyExc_BaseException; +PyAPI_DATA(PyObject *) PyExc_Exception; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +PyAPI_DATA(PyObject *) PyExc_StopAsyncIteration; +#endif +PyAPI_DATA(PyObject *) PyExc_StopIteration; +PyAPI_DATA(PyObject *) PyExc_GeneratorExit; +PyAPI_DATA(PyObject *) PyExc_ArithmeticError; +PyAPI_DATA(PyObject *) PyExc_LookupError; + +PyAPI_DATA(PyObject *) PyExc_AssertionError; +PyAPI_DATA(PyObject *) PyExc_AttributeError; +PyAPI_DATA(PyObject *) PyExc_BufferError; +PyAPI_DATA(PyObject *) PyExc_EOFError; +PyAPI_DATA(PyObject *) PyExc_FloatingPointError; +PyAPI_DATA(PyObject *) PyExc_OSError; +PyAPI_DATA(PyObject *) PyExc_ImportError; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 +PyAPI_DATA(PyObject *) PyExc_ModuleNotFoundError; +#endif +PyAPI_DATA(PyObject *) PyExc_IndexError; +PyAPI_DATA(PyObject *) PyExc_KeyError; +PyAPI_DATA(PyObject *) PyExc_KeyboardInterrupt; +PyAPI_DATA(PyObject *) PyExc_MemoryError; +PyAPI_DATA(PyObject *) PyExc_NameError; +PyAPI_DATA(PyObject *) PyExc_OverflowError; +PyAPI_DATA(PyObject *) PyExc_RuntimeError; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +PyAPI_DATA(PyObject *) PyExc_RecursionError; +#endif +PyAPI_DATA(PyObject *) PyExc_NotImplementedError; +PyAPI_DATA(PyObject *) PyExc_SyntaxError; +PyAPI_DATA(PyObject *) PyExc_IndentationError; +PyAPI_DATA(PyObject *) PyExc_TabError; +PyAPI_DATA(PyObject *) PyExc_ReferenceError; +PyAPI_DATA(PyObject *) PyExc_SystemError; +PyAPI_DATA(PyObject *) PyExc_SystemExit; +PyAPI_DATA(PyObject *) PyExc_TypeError; +PyAPI_DATA(PyObject *) PyExc_UnboundLocalError; +PyAPI_DATA(PyObject *) PyExc_UnicodeError; +PyAPI_DATA(PyObject *) PyExc_UnicodeEncodeError; +PyAPI_DATA(PyObject *) PyExc_UnicodeDecodeError; +PyAPI_DATA(PyObject *) PyExc_UnicodeTranslateError; +PyAPI_DATA(PyObject *) PyExc_ValueError; +PyAPI_DATA(PyObject *) PyExc_ZeroDivisionError; + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_DATA(PyObject *) PyExc_BlockingIOError; +PyAPI_DATA(PyObject *) PyExc_BrokenPipeError; +PyAPI_DATA(PyObject *) PyExc_ChildProcessError; +PyAPI_DATA(PyObject *) PyExc_ConnectionError; +PyAPI_DATA(PyObject *) PyExc_ConnectionAbortedError; +PyAPI_DATA(PyObject *) PyExc_ConnectionRefusedError; +PyAPI_DATA(PyObject *) PyExc_ConnectionResetError; +PyAPI_DATA(PyObject *) PyExc_FileExistsError; +PyAPI_DATA(PyObject *) PyExc_FileNotFoundError; +PyAPI_DATA(PyObject *) PyExc_InterruptedError; +PyAPI_DATA(PyObject *) PyExc_IsADirectoryError; +PyAPI_DATA(PyObject *) PyExc_NotADirectoryError; +PyAPI_DATA(PyObject *) PyExc_PermissionError; +PyAPI_DATA(PyObject *) PyExc_ProcessLookupError; +PyAPI_DATA(PyObject *) PyExc_TimeoutError; +#endif + + +/* Compatibility aliases */ +PyAPI_DATA(PyObject *) PyExc_EnvironmentError; +PyAPI_DATA(PyObject *) PyExc_IOError; +#ifdef MS_WINDOWS +PyAPI_DATA(PyObject *) PyExc_WindowsError; +#endif + +/* Predefined warning categories */ +PyAPI_DATA(PyObject *) PyExc_Warning; +PyAPI_DATA(PyObject *) PyExc_UserWarning; +PyAPI_DATA(PyObject *) PyExc_DeprecationWarning; +PyAPI_DATA(PyObject *) PyExc_PendingDeprecationWarning; +PyAPI_DATA(PyObject *) PyExc_SyntaxWarning; +PyAPI_DATA(PyObject *) PyExc_RuntimeWarning; +PyAPI_DATA(PyObject *) PyExc_FutureWarning; +PyAPI_DATA(PyObject *) PyExc_ImportWarning; +PyAPI_DATA(PyObject *) PyExc_UnicodeWarning; +PyAPI_DATA(PyObject *) PyExc_BytesWarning; +PyAPI_DATA(PyObject *) PyExc_ResourceWarning; + + +/* Convenience functions */ + +PyAPI_FUNC(int) PyErr_BadArgument(void); +PyAPI_FUNC(PyObject *) PyErr_NoMemory(void); +PyAPI_FUNC(PyObject *) PyErr_SetFromErrno(PyObject *); +PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenameObject( + PyObject *, PyObject *); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03040000 +PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenameObjects( + PyObject *, PyObject *, PyObject *); +#endif +PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilename( + PyObject *exc, + const char *filename /* decoded from the filesystem encoding */ + ); + +PyAPI_FUNC(PyObject *) PyErr_Format( + PyObject *exception, + const char *format, /* ASCII-encoded string */ + ... + ); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +PyAPI_FUNC(PyObject *) PyErr_FormatV( + PyObject *exception, + const char *format, + va_list vargs); +#endif + +#ifdef MS_WINDOWS +PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename( + int ierr, + const char *filename /* decoded from the filesystem encoding */ + ); +PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErr(int); +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilenameObject( + PyObject *,int, PyObject *); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03040000 +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilenameObjects( + PyObject *,int, PyObject *, PyObject *); +#endif +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilename( + PyObject *exc, + int ierr, + const char *filename /* decoded from the filesystem encoding */ + ); +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErr(PyObject *, int); +#endif /* MS_WINDOWS */ + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 +PyAPI_FUNC(PyObject *) PyErr_SetImportErrorSubclass(PyObject *, PyObject *, + PyObject *, PyObject *); +#endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(PyObject *) PyErr_SetImportError(PyObject *, PyObject *, + PyObject *); +#endif + +/* Export the old function so that the existing API remains available: */ +PyAPI_FUNC(void) PyErr_BadInternalCall(void); +PyAPI_FUNC(void) _PyErr_BadInternalCall(const char *filename, int lineno); +/* Mask the old API with a call to the new API for code compiled under + Python 2.0: */ +#define PyErr_BadInternalCall() _PyErr_BadInternalCall(__FILE__, __LINE__) + +/* Function to create a new exception */ +PyAPI_FUNC(PyObject *) PyErr_NewException( + const char *name, PyObject *base, PyObject *dict); +PyAPI_FUNC(PyObject *) PyErr_NewExceptionWithDoc( + const char *name, const char *doc, PyObject *base, PyObject *dict); +PyAPI_FUNC(void) PyErr_WriteUnraisable(PyObject *); + + +/* In signalmodule.c */ +PyAPI_FUNC(int) PyErr_CheckSignals(void); +PyAPI_FUNC(void) PyErr_SetInterrupt(void); + +/* Support for adding program text to SyntaxErrors */ +PyAPI_FUNC(void) PyErr_SyntaxLocation( + const char *filename, /* decoded from the filesystem encoding */ + int lineno); +PyAPI_FUNC(void) PyErr_SyntaxLocationEx( + const char *filename, /* decoded from the filesystem encoding */ + int lineno, + int col_offset); +PyAPI_FUNC(PyObject *) PyErr_ProgramText( + const char *filename, /* decoded from the filesystem encoding */ + int lineno); + +/* The following functions are used to create and modify unicode + exceptions from C */ + +/* create a UnicodeDecodeError object */ +PyAPI_FUNC(PyObject *) PyUnicodeDecodeError_Create( + const char *encoding, /* UTF-8 encoded string */ + const char *object, + Py_ssize_t length, + Py_ssize_t start, + Py_ssize_t end, + const char *reason /* UTF-8 encoded string */ + ); + +/* get the encoding attribute */ +PyAPI_FUNC(PyObject *) PyUnicodeEncodeError_GetEncoding(PyObject *); +PyAPI_FUNC(PyObject *) PyUnicodeDecodeError_GetEncoding(PyObject *); + +/* get the object attribute */ +PyAPI_FUNC(PyObject *) PyUnicodeEncodeError_GetObject(PyObject *); +PyAPI_FUNC(PyObject *) PyUnicodeDecodeError_GetObject(PyObject *); +PyAPI_FUNC(PyObject *) PyUnicodeTranslateError_GetObject(PyObject *); + +/* get the value of the start attribute (the int * may not be NULL) + return 0 on success, -1 on failure */ +PyAPI_FUNC(int) PyUnicodeEncodeError_GetStart(PyObject *, Py_ssize_t *); +PyAPI_FUNC(int) PyUnicodeDecodeError_GetStart(PyObject *, Py_ssize_t *); +PyAPI_FUNC(int) PyUnicodeTranslateError_GetStart(PyObject *, Py_ssize_t *); + +/* assign a new value to the start attribute + return 0 on success, -1 on failure */ +PyAPI_FUNC(int) PyUnicodeEncodeError_SetStart(PyObject *, Py_ssize_t); +PyAPI_FUNC(int) PyUnicodeDecodeError_SetStart(PyObject *, Py_ssize_t); +PyAPI_FUNC(int) PyUnicodeTranslateError_SetStart(PyObject *, Py_ssize_t); + +/* get the value of the end attribute (the int *may not be NULL) + return 0 on success, -1 on failure */ +PyAPI_FUNC(int) PyUnicodeEncodeError_GetEnd(PyObject *, Py_ssize_t *); +PyAPI_FUNC(int) PyUnicodeDecodeError_GetEnd(PyObject *, Py_ssize_t *); +PyAPI_FUNC(int) PyUnicodeTranslateError_GetEnd(PyObject *, Py_ssize_t *); + +/* assign a new value to the end attribute + return 0 on success, -1 on failure */ +PyAPI_FUNC(int) PyUnicodeEncodeError_SetEnd(PyObject *, Py_ssize_t); +PyAPI_FUNC(int) PyUnicodeDecodeError_SetEnd(PyObject *, Py_ssize_t); +PyAPI_FUNC(int) PyUnicodeTranslateError_SetEnd(PyObject *, Py_ssize_t); + +/* get the value of the reason attribute */ +PyAPI_FUNC(PyObject *) PyUnicodeEncodeError_GetReason(PyObject *); +PyAPI_FUNC(PyObject *) PyUnicodeDecodeError_GetReason(PyObject *); +PyAPI_FUNC(PyObject *) PyUnicodeTranslateError_GetReason(PyObject *); + +/* assign a new value to the reason attribute + return 0 on success, -1 on failure */ +PyAPI_FUNC(int) PyUnicodeEncodeError_SetReason( + PyObject *exc, + const char *reason /* UTF-8 encoded string */ + ); +PyAPI_FUNC(int) PyUnicodeDecodeError_SetReason( + PyObject *exc, + const char *reason /* UTF-8 encoded string */ + ); +PyAPI_FUNC(int) PyUnicodeTranslateError_SetReason( + PyObject *exc, + const char *reason /* UTF-8 encoded string */ + ); + +PyAPI_FUNC(int) PyOS_snprintf(char *str, size_t size, const char *format, ...) + Py_GCC_ATTRIBUTE((format(printf, 3, 4))); +PyAPI_FUNC(int) PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) + Py_GCC_ATTRIBUTE((format(printf, 3, 0))); + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_ERRORS_H +# include "cpython/pyerrors.h" +# undef Py_CPYTHON_ERRORS_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_ERRORS_H */ diff --git a/scripts/build-windows/py39-libs/include/pyexpat.h b/scripts/build-windows/py39-libs/include/pyexpat.h new file mode 100644 index 000000000..5f5d381aa --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pyexpat.h @@ -0,0 +1,55 @@ +/* Stuff to export relevant 'expat' entry points from pyexpat to other + * parser modules, such as cElementTree. */ + +/* note: you must import expat.h before importing this module! */ + +#define PyExpat_CAPI_MAGIC "pyexpat.expat_CAPI 1.1" +#define PyExpat_CAPSULE_NAME "pyexpat.expat_CAPI" + +struct PyExpat_CAPI +{ + char* magic; /* set to PyExpat_CAPI_MAGIC */ + int size; /* set to sizeof(struct PyExpat_CAPI) */ + int MAJOR_VERSION; + int MINOR_VERSION; + int MICRO_VERSION; + /* pointers to selected expat functions. add new functions at + the end, if needed */ + const XML_LChar * (*ErrorString)(enum XML_Error code); + enum XML_Error (*GetErrorCode)(XML_Parser parser); + XML_Size (*GetErrorColumnNumber)(XML_Parser parser); + XML_Size (*GetErrorLineNumber)(XML_Parser parser); + enum XML_Status (*Parse)( + XML_Parser parser, const char *s, int len, int isFinal); + XML_Parser (*ParserCreate_MM)( + const XML_Char *encoding, const XML_Memory_Handling_Suite *memsuite, + const XML_Char *namespaceSeparator); + void (*ParserFree)(XML_Parser parser); + void (*SetCharacterDataHandler)( + XML_Parser parser, XML_CharacterDataHandler handler); + void (*SetCommentHandler)( + XML_Parser parser, XML_CommentHandler handler); + void (*SetDefaultHandlerExpand)( + XML_Parser parser, XML_DefaultHandler handler); + void (*SetElementHandler)( + XML_Parser parser, XML_StartElementHandler start, + XML_EndElementHandler end); + void (*SetNamespaceDeclHandler)( + XML_Parser parser, XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end); + void (*SetProcessingInstructionHandler)( + XML_Parser parser, XML_ProcessingInstructionHandler handler); + void (*SetUnknownEncodingHandler)( + XML_Parser parser, XML_UnknownEncodingHandler handler, + void *encodingHandlerData); + void (*SetUserData)(XML_Parser parser, void *userData); + void (*SetStartDoctypeDeclHandler)(XML_Parser parser, + XML_StartDoctypeDeclHandler start); + enum XML_Status (*SetEncoding)(XML_Parser parser, const XML_Char *encoding); + int (*DefaultUnknownEncodingHandler)( + void *encodingHandlerData, const XML_Char *name, XML_Encoding *info); + /* might be none for expat < 2.1.0 */ + int (*SetHashSalt)(XML_Parser parser, unsigned long hash_salt); + /* always add new stuff to the end! */ +}; + diff --git a/scripts/build-windows/py39-libs/include/pyfpe.h b/scripts/build-windows/py39-libs/include/pyfpe.h new file mode 100644 index 000000000..d5a52617b --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pyfpe.h @@ -0,0 +1,15 @@ +#ifndef Py_PYFPE_H +#define Py_PYFPE_H +/* Header excluded from the stable API */ +#ifndef Py_LIMITED_API + +/* These macros used to do something when Python was built with --with-fpectl, + * but support for that was dropped in 3.7. We continue to define them though, + * to avoid breaking API users. + */ + +#define PyFPE_START_PROTECT(err_string, leave_stmt) +#define PyFPE_END_PROTECT(v) + +#endif /* !defined(Py_LIMITED_API) */ +#endif /* !Py_PYFPE_H */ diff --git a/scripts/build-windows/py39-libs/include/pyframe.h b/scripts/build-windows/py39-libs/include/pyframe.h new file mode 100644 index 000000000..a25769022 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pyframe.h @@ -0,0 +1,22 @@ +/* Limited C API of PyFrame API + * + * Include "frameobject.h" to get the PyFrameObject structure. + */ + +#ifndef Py_PYFRAME_H +#define Py_PYFRAME_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _frame PyFrameObject; + +/* Return the line of code the frame is currently executing. */ +PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject *); + +PyAPI_FUNC(PyCodeObject *) PyFrame_GetCode(PyFrameObject *frame); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYFRAME_H */ diff --git a/scripts/build-windows/py39-libs/include/pyhash.h b/scripts/build-windows/py39-libs/include/pyhash.h new file mode 100644 index 000000000..fe42fa8c3 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pyhash.h @@ -0,0 +1,147 @@ +#ifndef Py_HASH_H + +#define Py_HASH_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Helpers for hash functions */ +#ifndef Py_LIMITED_API +PyAPI_FUNC(Py_hash_t) _Py_HashDouble(double); +PyAPI_FUNC(Py_hash_t) _Py_HashPointer(const void*); +// Similar to _Py_HashPointer(), but don't replace -1 with -2 +PyAPI_FUNC(Py_hash_t) _Py_HashPointerRaw(const void*); +PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void*, Py_ssize_t); +#endif + +/* Prime multiplier used in string and various other hashes. */ +#define _PyHASH_MULTIPLIER 1000003UL /* 0xf4243 */ + +/* Parameters used for the numeric hash implementation. See notes for + _Py_HashDouble in Python/pyhash.c. Numeric hashes are based on + reduction modulo the prime 2**_PyHASH_BITS - 1. */ + +#if SIZEOF_VOID_P >= 8 +# define _PyHASH_BITS 61 +#else +# define _PyHASH_BITS 31 +#endif + +#define _PyHASH_MODULUS (((size_t)1 << _PyHASH_BITS) - 1) +#define _PyHASH_INF 314159 +#define _PyHASH_NAN 0 +#define _PyHASH_IMAG _PyHASH_MULTIPLIER + + +/* hash secret + * + * memory layout on 64 bit systems + * cccccccc cccccccc cccccccc uc -- unsigned char[24] + * pppppppp ssssssss ........ fnv -- two Py_hash_t + * k0k0k0k0 k1k1k1k1 ........ siphash -- two uint64_t + * ........ ........ ssssssss djbx33a -- 16 bytes padding + one Py_hash_t + * ........ ........ eeeeeeee pyexpat XML hash salt + * + * memory layout on 32 bit systems + * cccccccc cccccccc cccccccc uc + * ppppssss ........ ........ fnv -- two Py_hash_t + * k0k0k0k0 k1k1k1k1 ........ siphash -- two uint64_t (*) + * ........ ........ ssss.... djbx33a -- 16 bytes padding + one Py_hash_t + * ........ ........ eeee.... pyexpat XML hash salt + * + * (*) The siphash member may not be available on 32 bit platforms without + * an unsigned int64 data type. + */ +#ifndef Py_LIMITED_API +typedef union { + /* ensure 24 bytes */ + unsigned char uc[24]; + /* two Py_hash_t for FNV */ + struct { + Py_hash_t prefix; + Py_hash_t suffix; + } fnv; + /* two uint64 for SipHash24 */ + struct { + uint64_t k0; + uint64_t k1; + } siphash; + /* a different (!) Py_hash_t for small string optimization */ + struct { + unsigned char padding[16]; + Py_hash_t suffix; + } djbx33a; + struct { + unsigned char padding[16]; + Py_hash_t hashsalt; + } expat; +} _Py_HashSecret_t; +PyAPI_DATA(_Py_HashSecret_t) _Py_HashSecret; +#endif + +#ifdef Py_DEBUG +PyAPI_DATA(int) _Py_HashSecret_Initialized; +#endif + + +/* hash function definition */ +#ifndef Py_LIMITED_API +typedef struct { + Py_hash_t (*const hash)(const void *, Py_ssize_t); + const char *name; + const int hash_bits; + const int seed_bits; +} PyHash_FuncDef; + +PyAPI_FUNC(PyHash_FuncDef*) PyHash_GetFuncDef(void); +#endif + + +/* cutoff for small string DJBX33A optimization in range [1, cutoff). + * + * About 50% of the strings in a typical Python application are smaller than + * 6 to 7 chars. However DJBX33A is vulnerable to hash collision attacks. + * NEVER use DJBX33A for long strings! + * + * A Py_HASH_CUTOFF of 0 disables small string optimization. 32 bit platforms + * should use a smaller cutoff because it is easier to create colliding + * strings. A cutoff of 7 on 64bit platforms and 5 on 32bit platforms should + * provide a decent safety margin. + */ +#ifndef Py_HASH_CUTOFF +# define Py_HASH_CUTOFF 0 +#elif (Py_HASH_CUTOFF > 7 || Py_HASH_CUTOFF < 0) +# error Py_HASH_CUTOFF must in range 0...7. +#endif /* Py_HASH_CUTOFF */ + + +/* hash algorithm selection + * + * The values for Py_HASH_SIPHASH24 and Py_HASH_FNV are hard-coded in the + * configure script. + * + * - FNV is available on all platforms and architectures. + * - SIPHASH24 only works on platforms that don't require aligned memory for integers. + * - With EXTERNAL embedders can provide an alternative implementation with:: + * + * PyHash_FuncDef PyHash_Func = {...}; + * + * XXX: Figure out __declspec() for extern PyHash_FuncDef. + */ +#define Py_HASH_EXTERNAL 0 +#define Py_HASH_SIPHASH24 1 +#define Py_HASH_FNV 2 + +#ifndef Py_HASH_ALGORITHM +# ifndef HAVE_ALIGNED_REQUIRED +# define Py_HASH_ALGORITHM Py_HASH_SIPHASH24 +# else +# define Py_HASH_ALGORITHM Py_HASH_FNV +# endif /* uint64_t && uint32_t && aligned */ +#endif /* Py_HASH_ALGORITHM */ + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_HASH_H */ diff --git a/scripts/build-windows/py39-libs/include/pylifecycle.h b/scripts/build-windows/py39-libs/include/pylifecycle.h new file mode 100644 index 000000000..2084b26b7 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pylifecycle.h @@ -0,0 +1,77 @@ + +/* Interfaces to configure, query, create & destroy the Python runtime */ + +#ifndef Py_PYLIFECYCLE_H +#define Py_PYLIFECYCLE_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Initialization and finalization */ +PyAPI_FUNC(void) Py_Initialize(void); +PyAPI_FUNC(void) Py_InitializeEx(int); +PyAPI_FUNC(void) Py_Finalize(void); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 +PyAPI_FUNC(int) Py_FinalizeEx(void); +#endif +PyAPI_FUNC(int) Py_IsInitialized(void); + +/* Subinterpreter support */ +PyAPI_FUNC(PyThreadState *) Py_NewInterpreter(void); +PyAPI_FUNC(void) Py_EndInterpreter(PyThreadState *); + + +/* Py_PyAtExit is for the atexit module, Py_AtExit is for low-level + * exit functions. + */ +PyAPI_FUNC(int) Py_AtExit(void (*func)(void)); + +PyAPI_FUNC(void) _Py_NO_RETURN Py_Exit(int); + +/* Bootstrap __main__ (defined in Modules/main.c) */ +PyAPI_FUNC(int) Py_Main(int argc, wchar_t **argv); + +PyAPI_FUNC(int) Py_FrozenMain(int argc, char **argv); + +PyAPI_FUNC(int) Py_BytesMain(int argc, char **argv); + +/* In pathconfig.c */ +PyAPI_FUNC(void) Py_SetProgramName(const wchar_t *); +PyAPI_FUNC(wchar_t *) Py_GetProgramName(void); + +PyAPI_FUNC(void) Py_SetPythonHome(const wchar_t *); +PyAPI_FUNC(wchar_t *) Py_GetPythonHome(void); + +PyAPI_FUNC(wchar_t *) Py_GetProgramFullPath(void); + +PyAPI_FUNC(wchar_t *) Py_GetPrefix(void); +PyAPI_FUNC(wchar_t *) Py_GetExecPrefix(void); +PyAPI_FUNC(wchar_t *) Py_GetPath(void); +PyAPI_FUNC(void) Py_SetPath(const wchar_t *); +#ifdef MS_WINDOWS +int _Py_CheckPython3(void); +#endif + +/* In their own files */ +PyAPI_FUNC(const char *) Py_GetVersion(void); +PyAPI_FUNC(const char *) Py_GetPlatform(void); +PyAPI_FUNC(const char *) Py_GetCopyright(void); +PyAPI_FUNC(const char *) Py_GetCompiler(void); +PyAPI_FUNC(const char *) Py_GetBuildInfo(void); + +/* Signals */ +typedef void (*PyOS_sighandler_t)(int); +PyAPI_FUNC(PyOS_sighandler_t) PyOS_getsig(int); +PyAPI_FUNC(PyOS_sighandler_t) PyOS_setsig(int, PyOS_sighandler_t); + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_PYLIFECYCLE_H +# include "cpython/pylifecycle.h" +# undef Py_CPYTHON_PYLIFECYCLE_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYLIFECYCLE_H */ diff --git a/scripts/build-windows/py39-libs/include/pymacconfig.h b/scripts/build-windows/py39-libs/include/pymacconfig.h new file mode 100644 index 000000000..a1eccea4a --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pymacconfig.h @@ -0,0 +1,102 @@ +#ifndef PYMACCONFIG_H +#define PYMACCONFIG_H + /* + * This file moves some of the autoconf magic to compile-time + * when building on MacOSX. This is needed for building 4-way + * universal binaries and for 64-bit universal binaries because + * the values redefined below aren't configure-time constant but + * only compile-time constant in these scenarios. + */ + +#if defined(__APPLE__) + +# undef SIZEOF_LONG +# undef SIZEOF_PTHREAD_T +# undef SIZEOF_SIZE_T +# undef SIZEOF_TIME_T +# undef SIZEOF_VOID_P +# undef SIZEOF__BOOL +# undef SIZEOF_UINTPTR_T +# undef SIZEOF_PTHREAD_T +# undef WORDS_BIGENDIAN +# undef DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754 +# undef DOUBLE_IS_BIG_ENDIAN_IEEE754 +# undef DOUBLE_IS_LITTLE_ENDIAN_IEEE754 +# undef HAVE_GCC_ASM_FOR_X87 + +# undef VA_LIST_IS_ARRAY +# if defined(__LP64__) && defined(__x86_64__) +# define VA_LIST_IS_ARRAY 1 +# endif + +# undef HAVE_LARGEFILE_SUPPORT +# ifndef __LP64__ +# define HAVE_LARGEFILE_SUPPORT 1 +# endif + +# undef SIZEOF_LONG +# ifdef __LP64__ +# define SIZEOF__BOOL 1 +# define SIZEOF__BOOL 1 +# define SIZEOF_LONG 8 +# define SIZEOF_PTHREAD_T 8 +# define SIZEOF_SIZE_T 8 +# define SIZEOF_TIME_T 8 +# define SIZEOF_VOID_P 8 +# define SIZEOF_UINTPTR_T 8 +# define SIZEOF_PTHREAD_T 8 +# else +# ifdef __ppc__ +# define SIZEOF__BOOL 4 +# else +# define SIZEOF__BOOL 1 +# endif +# define SIZEOF_LONG 4 +# define SIZEOF_PTHREAD_T 4 +# define SIZEOF_SIZE_T 4 +# define SIZEOF_TIME_T 4 +# define SIZEOF_VOID_P 4 +# define SIZEOF_UINTPTR_T 4 +# define SIZEOF_PTHREAD_T 4 +# endif + +# if defined(__LP64__) + /* MacOSX 10.4 (the first release to support 64-bit code + * at all) only supports 64-bit in the UNIX layer. + * Therefore suppress the toolbox-glue in 64-bit mode. + */ + + /* In 64-bit mode setpgrp always has no arguments, in 32-bit + * mode that depends on the compilation environment + */ +# undef SETPGRP_HAVE_ARG + +# endif + +#ifdef __BIG_ENDIAN__ +#define WORDS_BIGENDIAN 1 +#define DOUBLE_IS_BIG_ENDIAN_IEEE754 +#else +#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 +#endif /* __BIG_ENDIAN */ + +#ifdef __i386__ +# define HAVE_GCC_ASM_FOR_X87 +#endif + + /* + * The definition in pyconfig.h is only valid on the OS release + * where configure ran on and not necessarily for all systems where + * the executable can be used on. + * + * Specifically: OSX 10.4 has limited supported for '%zd', while + * 10.5 has full support for '%zd'. A binary built on 10.5 won't + * work properly on 10.4 unless we suppress the definition + * of PY_FORMAT_SIZE_T + */ +#undef PY_FORMAT_SIZE_T + + +#endif /* defined(_APPLE__) */ + +#endif /* PYMACCONFIG_H */ diff --git a/scripts/build-windows/py39-libs/include/pymacro.h b/scripts/build-windows/py39-libs/include/pymacro.h new file mode 100644 index 000000000..7bfee70e1 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pymacro.h @@ -0,0 +1,132 @@ +#ifndef Py_PYMACRO_H +#define Py_PYMACRO_H + +/* Minimum value between x and y */ +#define Py_MIN(x, y) (((x) > (y)) ? (y) : (x)) + +/* Maximum value between x and y */ +#define Py_MAX(x, y) (((x) > (y)) ? (x) : (y)) + +/* Absolute value of the number x */ +#define Py_ABS(x) ((x) < 0 ? -(x) : (x)) + +#define _Py_XSTRINGIFY(x) #x + +/* Convert the argument to a string. For example, Py_STRINGIFY(123) is replaced + with "123" by the preprocessor. Defines are also replaced by their value. + For example Py_STRINGIFY(__LINE__) is replaced by the line number, not + by "__LINE__". */ +#define Py_STRINGIFY(x) _Py_XSTRINGIFY(x) + +/* Get the size of a structure member in bytes */ +#define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member) + +/* Argument must be a char or an int in [-128, 127] or [0, 255]. */ +#define Py_CHARMASK(c) ((unsigned char)((c) & 0xff)) + +/* Assert a build-time dependency, as an expression. + + Your compile will fail if the condition isn't true, or can't be evaluated + by the compiler. This can be used in an expression: its value is 0. + + Example: + + #define foo_to_char(foo) \ + ((char *)(foo) \ + + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0)) + + Written by Rusty Russell, public domain, http://ccodearchive.net/ */ +#define Py_BUILD_ASSERT_EXPR(cond) \ + (sizeof(char [1 - 2*!(cond)]) - 1) + +#define Py_BUILD_ASSERT(cond) do { \ + (void)Py_BUILD_ASSERT_EXPR(cond); \ + } while(0) + +/* Get the number of elements in a visible array + + This does not work on pointers, or arrays declared as [], or function + parameters. With correct compiler support, such usage will cause a build + error (see Py_BUILD_ASSERT_EXPR). + + Written by Rusty Russell, public domain, http://ccodearchive.net/ + + Requires at GCC 3.1+ */ +#if (defined(__GNUC__) && !defined(__STRICT_ANSI__) && \ + (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ >= 4))) +/* Two gcc extensions. + &a[0] degrades to a pointer: a different type from an array */ +#define Py_ARRAY_LENGTH(array) \ + (sizeof(array) / sizeof((array)[0]) \ + + Py_BUILD_ASSERT_EXPR(!__builtin_types_compatible_p(typeof(array), \ + typeof(&(array)[0])))) +#else +#define Py_ARRAY_LENGTH(array) \ + (sizeof(array) / sizeof((array)[0])) +#endif + + +/* Define macros for inline documentation. */ +#define PyDoc_VAR(name) static const char name[] +#define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str) +#ifdef WITH_DOC_STRINGS +#define PyDoc_STR(str) str +#else +#define PyDoc_STR(str) "" +#endif + +/* Below "a" is a power of 2. */ +/* Round down size "n" to be a multiple of "a". */ +#define _Py_SIZE_ROUND_DOWN(n, a) ((size_t)(n) & ~(size_t)((a) - 1)) +/* Round up size "n" to be a multiple of "a". */ +#define _Py_SIZE_ROUND_UP(n, a) (((size_t)(n) + \ + (size_t)((a) - 1)) & ~(size_t)((a) - 1)) +/* Round pointer "p" down to the closest "a"-aligned address <= "p". */ +#define _Py_ALIGN_DOWN(p, a) ((void *)((uintptr_t)(p) & ~(uintptr_t)((a) - 1))) +/* Round pointer "p" up to the closest "a"-aligned address >= "p". */ +#define _Py_ALIGN_UP(p, a) ((void *)(((uintptr_t)(p) + \ + (uintptr_t)((a) - 1)) & ~(uintptr_t)((a) - 1))) +/* Check if pointer "p" is aligned to "a"-bytes boundary. */ +#define _Py_IS_ALIGNED(p, a) (!((uintptr_t)(p) & (uintptr_t)((a) - 1))) + +/* Use this for unused arguments in a function definition to silence compiler + * warnings. Example: + * + * int func(int a, int Py_UNUSED(b)) { return a; } + */ +#if defined(__GNUC__) || defined(__clang__) +# define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) +#else +# define Py_UNUSED(name) _unused_ ## name +#endif + +#if defined(RANDALL_WAS_HERE) +# define Py_UNREACHABLE() \ + Py_FatalError( \ + "If you're seeing this, the code is in what I thought was\n" \ + "an unreachable state.\n\n" \ + "I could give you advice for what to do, but honestly, why\n" \ + "should you trust me? I clearly screwed this up. I'm writing\n" \ + "a message that should never appear, yet I know it will\n" \ + "probably appear someday.\n\n" \ + "On a deep level, I know I'm not up to this task.\n" \ + "I'm so sorry.\n" \ + "https://xkcd.com/2200") +#elif defined(Py_DEBUG) +# define Py_UNREACHABLE() \ + Py_FatalError( \ + "We've reached an unreachable state. Anything is possible.\n" \ + "The limits were in our heads all along. Follow your dreams.\n" \ + "https://xkcd.com/2200") +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +# define Py_UNREACHABLE() __builtin_unreachable() +#elif defined(__clang__) || defined(__INTEL_COMPILER) +# define Py_UNREACHABLE() __builtin_unreachable() +#elif defined(_MSC_VER) +# define Py_UNREACHABLE() __assume(0) +#else +# define Py_UNREACHABLE() \ + Py_FatalError("Unreachable C code path reached") +#endif + +#endif /* Py_PYMACRO_H */ diff --git a/scripts/build-windows/py39-libs/include/pymath.h b/scripts/build-windows/py39-libs/include/pymath.h new file mode 100644 index 000000000..4d305990e --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pymath.h @@ -0,0 +1,238 @@ +#ifndef Py_PYMATH_H +#define Py_PYMATH_H + +#include "pyconfig.h" /* include for defines */ + +/************************************************************************** +Symbols and macros to supply platform-independent interfaces to mathematical +functions and constants +**************************************************************************/ + +/* Python provides implementations for copysign, round and hypot in + * Python/pymath.c just in case your math library doesn't provide the + * functions. + * + *Note: PC/pyconfig.h defines copysign as _copysign + */ +#ifndef HAVE_COPYSIGN +extern double copysign(double, double); +#endif + +#ifndef HAVE_ROUND +extern double round(double); +#endif + +#ifndef HAVE_HYPOT +extern double hypot(double, double); +#endif + +/* extra declarations */ +#ifndef _MSC_VER +#ifndef __STDC__ +extern double fmod (double, double); +extern double frexp (double, int *); +extern double ldexp (double, int); +extern double modf (double, double *); +extern double pow(double, double); +#endif /* __STDC__ */ +#endif /* _MSC_VER */ + +/* High precision definition of pi and e (Euler) + * The values are taken from libc6's math.h. + */ +#ifndef Py_MATH_PIl +#define Py_MATH_PIl 3.1415926535897932384626433832795029L +#endif +#ifndef Py_MATH_PI +#define Py_MATH_PI 3.14159265358979323846 +#endif + +#ifndef Py_MATH_El +#define Py_MATH_El 2.7182818284590452353602874713526625L +#endif + +#ifndef Py_MATH_E +#define Py_MATH_E 2.7182818284590452354 +#endif + +/* Tau (2pi) to 40 digits, taken from tauday.com/tau-digits. */ +#ifndef Py_MATH_TAU +#define Py_MATH_TAU 6.2831853071795864769252867665590057683943L +#endif + + +/* On x86, Py_FORCE_DOUBLE forces a floating-point number out of an x87 FPU + register and into a 64-bit memory location, rounding from extended + precision to double precision in the process. On other platforms it does + nothing. */ + +/* we take double rounding as evidence of x87 usage */ +#ifndef Py_LIMITED_API +#ifndef Py_FORCE_DOUBLE +# ifdef X87_DOUBLE_ROUNDING +PyAPI_FUNC(double) _Py_force_double(double); +# define Py_FORCE_DOUBLE(X) (_Py_force_double(X)) +# else +# define Py_FORCE_DOUBLE(X) (X) +# endif +#endif +#endif + +#ifndef Py_LIMITED_API +#ifdef HAVE_GCC_ASM_FOR_X87 +PyAPI_FUNC(unsigned short) _Py_get_387controlword(void); +PyAPI_FUNC(void) _Py_set_387controlword(unsigned short); +#endif +#endif + +/* Py_IS_NAN(X) + * Return 1 if float or double arg is a NaN, else 0. + * Caution: + * X is evaluated more than once. + * This may not work on all platforms. Each platform has *some* + * way to spell this, though -- override in pyconfig.h if you have + * a platform where it doesn't work. + * Note: PC/pyconfig.h defines Py_IS_NAN as _isnan + */ +#ifndef Py_IS_NAN +#if defined HAVE_DECL_ISNAN && HAVE_DECL_ISNAN == 1 +#define Py_IS_NAN(X) isnan(X) +#else +#define Py_IS_NAN(X) ((X) != (X)) +#endif +#endif + +/* Py_IS_INFINITY(X) + * Return 1 if float or double arg is an infinity, else 0. + * Caution: + * X is evaluated more than once. + * This implementation may set the underflow flag if |X| is very small; + * it really can't be implemented correctly (& easily) before C99. + * Override in pyconfig.h if you have a better spelling on your platform. + * Py_FORCE_DOUBLE is used to avoid getting false negatives from a + * non-infinite value v sitting in an 80-bit x87 register such that + * v becomes infinite when spilled from the register to 64-bit memory. + * Note: PC/pyconfig.h defines Py_IS_INFINITY as _isinf + */ +#ifndef Py_IS_INFINITY +# if defined HAVE_DECL_ISINF && HAVE_DECL_ISINF == 1 +# define Py_IS_INFINITY(X) isinf(X) +# else +# define Py_IS_INFINITY(X) ((X) && \ + (Py_FORCE_DOUBLE(X)*0.5 == Py_FORCE_DOUBLE(X))) +# endif +#endif + +/* Py_IS_FINITE(X) + * Return 1 if float or double arg is neither infinite nor NAN, else 0. + * Some compilers (e.g. VisualStudio) have intrinsics for this, so a special + * macro for this particular test is useful + * Note: PC/pyconfig.h defines Py_IS_FINITE as _finite + */ +#ifndef Py_IS_FINITE +#if defined HAVE_DECL_ISFINITE && HAVE_DECL_ISFINITE == 1 +#define Py_IS_FINITE(X) isfinite(X) +#elif defined HAVE_FINITE +#define Py_IS_FINITE(X) finite(X) +#else +#define Py_IS_FINITE(X) (!Py_IS_INFINITY(X) && !Py_IS_NAN(X)) +#endif +#endif + +/* HUGE_VAL is supposed to expand to a positive double infinity. Python + * uses Py_HUGE_VAL instead because some platforms are broken in this + * respect. We used to embed code in pyport.h to try to worm around that, + * but different platforms are broken in conflicting ways. If you're on + * a platform where HUGE_VAL is defined incorrectly, fiddle your Python + * config to #define Py_HUGE_VAL to something that works on your platform. + */ +#ifndef Py_HUGE_VAL +#define Py_HUGE_VAL HUGE_VAL +#endif + +/* Py_NAN + * A value that evaluates to a NaN. On IEEE 754 platforms INF*0 or + * INF/INF works. Define Py_NO_NAN in pyconfig.h if your platform + * doesn't support NaNs. + */ +#if !defined(Py_NAN) && !defined(Py_NO_NAN) +#if !defined(__INTEL_COMPILER) + #define Py_NAN (Py_HUGE_VAL * 0.) +#else /* __INTEL_COMPILER */ + #if defined(ICC_NAN_STRICT) + #pragma float_control(push) + #pragma float_control(precise, on) + #pragma float_control(except, on) + #if defined(_MSC_VER) + __declspec(noinline) + #else /* Linux */ + __attribute__((noinline)) + #endif /* _MSC_VER */ + static double __icc_nan() + { + return sqrt(-1.0); + } + #pragma float_control (pop) + #define Py_NAN __icc_nan() + #else /* ICC_NAN_RELAXED as default for Intel Compiler */ + static const union { unsigned char buf[8]; double __icc_nan; } __nan_store = {0,0,0,0,0,0,0xf8,0x7f}; + #define Py_NAN (__nan_store.__icc_nan) + #endif /* ICC_NAN_STRICT */ +#endif /* __INTEL_COMPILER */ +#endif + +/* Py_OVERFLOWED(X) + * Return 1 iff a libm function overflowed. Set errno to 0 before calling + * a libm function, and invoke this macro after, passing the function + * result. + * Caution: + * This isn't reliable. C99 no longer requires libm to set errno under + * any exceptional condition, but does require +- HUGE_VAL return + * values on overflow. A 754 box *probably* maps HUGE_VAL to a + * double infinity, and we're cool if that's so, unless the input + * was an infinity and an infinity is the expected result. A C89 + * system sets errno to ERANGE, so we check for that too. We're + * out of luck if a C99 754 box doesn't map HUGE_VAL to +Inf, or + * if the returned result is a NaN, or if a C89 box returns HUGE_VAL + * in non-overflow cases. + * X is evaluated more than once. + * Some platforms have better way to spell this, so expect some #ifdef'ery. + * + * OpenBSD uses 'isinf()' because a compiler bug on that platform causes + * the longer macro version to be mis-compiled. This isn't optimal, and + * should be removed once a newer compiler is available on that platform. + * The system that had the failure was running OpenBSD 3.2 on Intel, with + * gcc 2.95.3. + * + * According to Tim's checkin, the FreeBSD systems use isinf() to work + * around a FPE bug on that platform. + */ +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#define Py_OVERFLOWED(X) isinf(X) +#else +#define Py_OVERFLOWED(X) ((X) != 0.0 && (errno == ERANGE || \ + (X) == Py_HUGE_VAL || \ + (X) == -Py_HUGE_VAL)) +#endif + +/* Return whether integral type *type* is signed or not. */ +#define _Py_IntegralTypeSigned(type) ((type)(-1) < 0) +/* Return the maximum value of integral type *type*. */ +#define _Py_IntegralTypeMax(type) ((_Py_IntegralTypeSigned(type)) ? (((((type)1 << (sizeof(type)*CHAR_BIT - 2)) - 1) << 1) + 1) : ~(type)0) +/* Return the minimum value of integral type *type*. */ +#define _Py_IntegralTypeMin(type) ((_Py_IntegralTypeSigned(type)) ? -_Py_IntegralTypeMax(type) - 1 : 0) +/* Check whether *v* is in the range of integral type *type*. This is most + * useful if *v* is floating-point, since demoting a floating-point *v* to an + * integral type that cannot represent *v*'s integral part is undefined + * behavior. */ +#define _Py_InIntegralTypeRange(type, v) (_Py_IntegralTypeMin(type) <= v && v <= _Py_IntegralTypeMax(type)) + +/* Return the smallest integer k such that n < 2**k, or 0 if n == 0. + * Equivalent to floor(log2(x))+1. Also equivalent to: bitwidth_of_type - + * count_leading_zero_bits(x) + */ +#ifndef Py_LIMITED_API +PyAPI_FUNC(unsigned int) _Py_bit_length(unsigned long d); +#endif + +#endif /* Py_PYMATH_H */ diff --git a/scripts/build-windows/py39-libs/include/pymem.h b/scripts/build-windows/py39-libs/include/pymem.h new file mode 100644 index 000000000..a2c8c2557 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pymem.h @@ -0,0 +1,115 @@ +/* The PyMem_ family: low-level memory allocation interfaces. + See objimpl.h for the PyObject_ memory family. +*/ + +#ifndef Py_PYMEM_H +#define Py_PYMEM_H + +#include "pyport.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* BEWARE: + + Each interface exports both functions and macros. Extension modules should + use the functions, to ensure binary compatibility across Python versions. + Because the Python implementation is free to change internal details, and + the macros may (or may not) expose details for speed, if you do use the + macros you must recompile your extensions with each Python release. + + Never mix calls to PyMem_ with calls to the platform malloc/realloc/ + calloc/free. For example, on Windows different DLLs may end up using + different heaps, and if you use PyMem_Malloc you'll get the memory from the + heap used by the Python DLL; it could be a disaster if you free()'ed that + directly in your own extension. Using PyMem_Free instead ensures Python + can return the memory to the proper heap. As another example, in + PYMALLOC_DEBUG mode, Python wraps all calls to all PyMem_ and PyObject_ + memory functions in special debugging wrappers that add additional + debugging info to dynamic memory blocks. The system routines have no idea + what to do with that stuff, and the Python wrappers have no idea what to do + with raw blocks obtained directly by the system routines then. + + The GIL must be held when using these APIs. +*/ + +/* + * Raw memory interface + * ==================== + */ + +/* Functions + + Functions supplying platform-independent semantics for malloc/realloc/ + free. These functions make sure that allocating 0 bytes returns a distinct + non-NULL pointer (whenever possible -- if we're flat out of memory, NULL + may be returned), even if the platform malloc and realloc don't. + Returned pointers must be checked for NULL explicitly. No action is + performed on failure (no exception is set, no warning is printed, etc). +*/ + +PyAPI_FUNC(void *) PyMem_Malloc(size_t size); +PyAPI_FUNC(void *) PyMem_Realloc(void *ptr, size_t new_size); +PyAPI_FUNC(void) PyMem_Free(void *ptr); + +/* Macros. */ + +/* PyMem_MALLOC(0) means malloc(1). Some systems would return NULL + for malloc(0), which would be treated as an error. Some platforms + would return a pointer with no memory behind it, which would break + pymalloc. To solve these problems, allocate an extra byte. */ +/* Returns NULL to indicate error if a negative size or size larger than + Py_ssize_t can represent is supplied. Helps prevents security holes. */ +#define PyMem_MALLOC(n) PyMem_Malloc(n) +#define PyMem_REALLOC(p, n) PyMem_Realloc(p, n) +#define PyMem_FREE(p) PyMem_Free(p) + +/* + * Type-oriented memory interface + * ============================== + * + * Allocate memory for n objects of the given type. Returns a new pointer + * or NULL if the request was too large or memory allocation failed. Use + * these macros rather than doing the multiplication yourself so that proper + * overflow checking is always done. + */ + +#define PyMem_New(type, n) \ + ( ((size_t)(n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \ + ( (type *) PyMem_Malloc((n) * sizeof(type)) ) ) +#define PyMem_NEW(type, n) \ + ( ((size_t)(n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \ + ( (type *) PyMem_MALLOC((n) * sizeof(type)) ) ) + +/* + * The value of (p) is always clobbered by this macro regardless of success. + * The caller MUST check if (p) is NULL afterwards and deal with the memory + * error if so. This means the original value of (p) MUST be saved for the + * caller's memory error handler to not lose track of it. + */ +#define PyMem_Resize(p, type, n) \ + ( (p) = ((size_t)(n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \ + (type *) PyMem_Realloc((p), (n) * sizeof(type)) ) +#define PyMem_RESIZE(p, type, n) \ + ( (p) = ((size_t)(n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \ + (type *) PyMem_REALLOC((p), (n) * sizeof(type)) ) + +/* PyMem{Del,DEL} are left over from ancient days, and shouldn't be used + * anymore. They're just confusing aliases for PyMem_{Free,FREE} now. + */ +#define PyMem_Del PyMem_Free +#define PyMem_DEL PyMem_FREE + + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_PYMEM_H +# include "cpython/pymem.h" +# undef Py_CPYTHON_PYMEM_H +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_PYMEM_H */ diff --git a/scripts/build-windows/py39-libs/include/pyport.h b/scripts/build-windows/py39-libs/include/pyport.h new file mode 100644 index 000000000..49a67d900 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pyport.h @@ -0,0 +1,878 @@ +#ifndef Py_PYPORT_H +#define Py_PYPORT_H + +#include "pyconfig.h" /* include for defines */ + +#include + + +/* Defines to build Python and its standard library: + * + * - Py_BUILD_CORE: Build Python core. Give access to Python internals, but + * should not be used by third-party modules. + * - Py_BUILD_CORE_BUILTIN: Build a Python stdlib module as a built-in module. + * - Py_BUILD_CORE_MODULE: Build a Python stdlib module as a dynamic library. + * + * Py_BUILD_CORE_BUILTIN and Py_BUILD_CORE_MODULE imply Py_BUILD_CORE. + * + * On Windows, Py_BUILD_CORE_MODULE exports "PyInit_xxx" symbol, whereas + * Py_BUILD_CORE_BUILTIN does not. + */ +#if defined(Py_BUILD_CORE_BUILTIN) && !defined(Py_BUILD_CORE) +# define Py_BUILD_CORE +#endif +#if defined(Py_BUILD_CORE_MODULE) && !defined(Py_BUILD_CORE) +# define Py_BUILD_CORE +#endif + + +/************************************************************************** +Symbols and macros to supply platform-independent interfaces to basic +C language & library operations whose spellings vary across platforms. + +Please try to make documentation here as clear as possible: by definition, +the stuff here is trying to illuminate C's darkest corners. + +Config #defines referenced here: + +SIGNED_RIGHT_SHIFT_ZERO_FILLS +Meaning: To be defined iff i>>j does not extend the sign bit when i is a + signed integral type and i < 0. +Used in: Py_ARITHMETIC_RIGHT_SHIFT + +Py_DEBUG +Meaning: Extra checks compiled in for debug mode. +Used in: Py_SAFE_DOWNCAST + +**************************************************************************/ + +/* typedefs for some C9X-defined synonyms for integral types. + * + * The names in Python are exactly the same as the C9X names, except with a + * Py_ prefix. Until C9X is universally implemented, this is the only way + * to ensure that Python gets reliable names that don't conflict with names + * in non-Python code that are playing their own tricks to define the C9X + * names. + * + * NOTE: don't go nuts here! Python has no use for *most* of the C9X + * integral synonyms. Only define the ones we actually need. + */ + +/* long long is required. Ensure HAVE_LONG_LONG is defined for compatibility. */ +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG 1 +#endif +#ifndef PY_LONG_LONG +#define PY_LONG_LONG long long +/* If LLONG_MAX is defined in limits.h, use that. */ +#define PY_LLONG_MIN LLONG_MIN +#define PY_LLONG_MAX LLONG_MAX +#define PY_ULLONG_MAX ULLONG_MAX +#endif + +#define PY_UINT32_T uint32_t +#define PY_UINT64_T uint64_t + +/* Signed variants of the above */ +#define PY_INT32_T int32_t +#define PY_INT64_T int64_t + +/* If PYLONG_BITS_IN_DIGIT is not defined then we'll use 30-bit digits if all + the necessary integer types are available, and we're on a 64-bit platform + (as determined by SIZEOF_VOID_P); otherwise we use 15-bit digits. */ + +#ifndef PYLONG_BITS_IN_DIGIT +#if SIZEOF_VOID_P >= 8 +#define PYLONG_BITS_IN_DIGIT 30 +#else +#define PYLONG_BITS_IN_DIGIT 15 +#endif +#endif + +/* uintptr_t is the C9X name for an unsigned integral type such that a + * legitimate void* can be cast to uintptr_t and then back to void* again + * without loss of information. Similarly for intptr_t, wrt a signed + * integral type. + */ +typedef uintptr_t Py_uintptr_t; +typedef intptr_t Py_intptr_t; + +/* Py_ssize_t is a signed integral type such that sizeof(Py_ssize_t) == + * sizeof(size_t). C99 doesn't define such a thing directly (size_t is an + * unsigned integral type). See PEP 353 for details. + */ +#ifdef HAVE_SSIZE_T +typedef ssize_t Py_ssize_t; +#elif SIZEOF_VOID_P == SIZEOF_SIZE_T +typedef Py_intptr_t Py_ssize_t; +#else +# error "Python needs a typedef for Py_ssize_t in pyport.h." +#endif + +/* Py_hash_t is the same size as a pointer. */ +#define SIZEOF_PY_HASH_T SIZEOF_SIZE_T +typedef Py_ssize_t Py_hash_t; +/* Py_uhash_t is the unsigned equivalent needed to calculate numeric hash. */ +#define SIZEOF_PY_UHASH_T SIZEOF_SIZE_T +typedef size_t Py_uhash_t; + +/* Only used for compatibility with code that may not be PY_SSIZE_T_CLEAN. */ +#ifdef PY_SSIZE_T_CLEAN +typedef Py_ssize_t Py_ssize_clean_t; +#else +typedef int Py_ssize_clean_t; +#endif + +/* Largest possible value of size_t. */ +#define PY_SIZE_MAX SIZE_MAX + +/* Largest positive value of type Py_ssize_t. */ +#define PY_SSIZE_T_MAX ((Py_ssize_t)(((size_t)-1)>>1)) +/* Smallest negative value of type Py_ssize_t. */ +#define PY_SSIZE_T_MIN (-PY_SSIZE_T_MAX-1) + +/* PY_FORMAT_SIZE_T is a platform-specific modifier for use in a printf + * format to convert an argument with the width of a size_t or Py_ssize_t. + * C99 introduced "z" for this purpose, but old MSVCs had not supported it. + * Since MSVC supports "z" since (at least) 2015, we can just use "z" + * for new code. + * + * These "high level" Python format functions interpret "z" correctly on + * all platforms (Python interprets the format string itself, and does whatever + * the platform C requires to convert a size_t/Py_ssize_t argument): + * + * PyBytes_FromFormat + * PyErr_Format + * PyBytes_FromFormatV + * PyUnicode_FromFormatV + * + * Lower-level uses require that you interpolate the correct format modifier + * yourself (e.g., calling printf, fprintf, sprintf, PyOS_snprintf); for + * example, + * + * Py_ssize_t index; + * fprintf(stderr, "index %" PY_FORMAT_SIZE_T "d sucks\n", index); + * + * That will expand to %zd or to something else correct for a Py_ssize_t on + * the platform. + */ +#ifndef PY_FORMAT_SIZE_T +# define PY_FORMAT_SIZE_T "z" +#endif + +/* Py_LOCAL can be used instead of static to get the fastest possible calling + * convention for functions that are local to a given module. + * + * Py_LOCAL_INLINE does the same thing, and also explicitly requests inlining, + * for platforms that support that. + * + * If PY_LOCAL_AGGRESSIVE is defined before python.h is included, more + * "aggressive" inlining/optimization is enabled for the entire module. This + * may lead to code bloat, and may slow things down for those reasons. It may + * also lead to errors, if the code relies on pointer aliasing. Use with + * care. + * + * NOTE: You can only use this for functions that are entirely local to a + * module; functions that are exported via method tables, callbacks, etc, + * should keep using static. + */ + +#if defined(_MSC_VER) +# if defined(PY_LOCAL_AGGRESSIVE) + /* enable more aggressive optimization for visual studio */ +# pragma optimize("agtw", on) +#endif + /* ignore warnings if the compiler decides not to inline a function */ +# pragma warning(disable: 4710) + /* fastest possible local call under MSVC */ +# define Py_LOCAL(type) static type __fastcall +# define Py_LOCAL_INLINE(type) static __inline type __fastcall +#else +# define Py_LOCAL(type) static type +# define Py_LOCAL_INLINE(type) static inline type +#endif + +/* Py_MEMCPY is kept for backwards compatibility, + * see https://bugs.python.org/issue28126 */ +#define Py_MEMCPY memcpy + +#include + +#ifdef HAVE_IEEEFP_H +#include /* needed for 'finite' declaration on some platforms */ +#endif + +#include /* Moved here from the math section, before extern "C" */ + +/******************************************** + * WRAPPER FOR and/or * + ********************************************/ + +#ifdef TIME_WITH_SYS_TIME +#include +#include +#else /* !TIME_WITH_SYS_TIME */ +#ifdef HAVE_SYS_TIME_H +#include +#else /* !HAVE_SYS_TIME_H */ +#include +#endif /* !HAVE_SYS_TIME_H */ +#endif /* !TIME_WITH_SYS_TIME */ + + +/****************************** + * WRAPPER FOR * + ******************************/ + +/* NB caller must include */ + +#ifdef HAVE_SYS_SELECT_H +#include +#endif /* !HAVE_SYS_SELECT_H */ + +/******************************* + * stat() and fstat() fiddling * + *******************************/ + +#ifdef HAVE_SYS_STAT_H +#include +#elif defined(HAVE_STAT_H) +#include +#endif + +#ifndef S_IFMT +/* VisualAge C/C++ Failed to Define MountType Field in sys/stat.h */ +#define S_IFMT 0170000 +#endif + +#ifndef S_IFLNK +/* Windows doesn't define S_IFLNK but posixmodule.c maps + * IO_REPARSE_TAG_SYMLINK to S_IFLNK */ +# define S_IFLNK 0120000 +#endif + +#ifndef S_ISREG +#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) +#endif + +#ifndef S_ISDIR +#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) +#endif + +#ifndef S_ISCHR +#define S_ISCHR(x) (((x) & S_IFMT) == S_IFCHR) +#endif + +#ifdef __cplusplus +/* Move this down here since some C++ #include's don't like to be included + inside an extern "C" */ +extern "C" { +#endif + + +/* Py_ARITHMETIC_RIGHT_SHIFT + * C doesn't define whether a right-shift of a signed integer sign-extends + * or zero-fills. Here a macro to force sign extension: + * Py_ARITHMETIC_RIGHT_SHIFT(TYPE, I, J) + * Return I >> J, forcing sign extension. Arithmetically, return the + * floor of I/2**J. + * Requirements: + * I should have signed integer type. In the terminology of C99, this can + * be either one of the five standard signed integer types (signed char, + * short, int, long, long long) or an extended signed integer type. + * J is an integer >= 0 and strictly less than the number of bits in the + * type of I (because C doesn't define what happens for J outside that + * range either). + * TYPE used to specify the type of I, but is now ignored. It's been left + * in for backwards compatibility with versions <= 2.6 or 3.0. + * Caution: + * I may be evaluated more than once. + */ +#ifdef SIGNED_RIGHT_SHIFT_ZERO_FILLS +#define Py_ARITHMETIC_RIGHT_SHIFT(TYPE, I, J) \ + ((I) < 0 ? -1-((-1-(I)) >> (J)) : (I) >> (J)) +#else +#define Py_ARITHMETIC_RIGHT_SHIFT(TYPE, I, J) ((I) >> (J)) +#endif + +/* Py_FORCE_EXPANSION(X) + * "Simply" returns its argument. However, macro expansions within the + * argument are evaluated. This unfortunate trickery is needed to get + * token-pasting to work as desired in some cases. + */ +#define Py_FORCE_EXPANSION(X) X + +/* Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) + * Cast VALUE to type NARROW from type WIDE. In Py_DEBUG mode, this + * assert-fails if any information is lost. + * Caution: + * VALUE may be evaluated more than once. + */ +#ifdef Py_DEBUG +#define Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) \ + (assert((WIDE)(NARROW)(VALUE) == (VALUE)), (NARROW)(VALUE)) +#else +#define Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) (NARROW)(VALUE) +#endif + +/* Py_SET_ERRNO_ON_MATH_ERROR(x) + * If a libm function did not set errno, but it looks like the result + * overflowed or not-a-number, set errno to ERANGE or EDOM. Set errno + * to 0 before calling a libm function, and invoke this macro after, + * passing the function result. + * Caution: + * This isn't reliable. See Py_OVERFLOWED comments. + * X is evaluated more than once. + */ +#if defined(__FreeBSD__) || defined(__OpenBSD__) || (defined(__hpux) && defined(__ia64)) +#define _Py_SET_EDOM_FOR_NAN(X) if (isnan(X)) errno = EDOM; +#else +#define _Py_SET_EDOM_FOR_NAN(X) ; +#endif +#define Py_SET_ERRNO_ON_MATH_ERROR(X) \ + do { \ + if (errno == 0) { \ + if ((X) == Py_HUGE_VAL || (X) == -Py_HUGE_VAL) \ + errno = ERANGE; \ + else _Py_SET_EDOM_FOR_NAN(X) \ + } \ + } while(0) + +/* Py_SET_ERANGE_IF_OVERFLOW(x) + * An alias of Py_SET_ERRNO_ON_MATH_ERROR for backward-compatibility. + */ +#define Py_SET_ERANGE_IF_OVERFLOW(X) Py_SET_ERRNO_ON_MATH_ERROR(X) + +/* Py_ADJUST_ERANGE1(x) + * Py_ADJUST_ERANGE2(x, y) + * Set errno to 0 before calling a libm function, and invoke one of these + * macros after, passing the function result(s) (Py_ADJUST_ERANGE2 is useful + * for functions returning complex results). This makes two kinds of + * adjustments to errno: (A) If it looks like the platform libm set + * errno=ERANGE due to underflow, clear errno. (B) If it looks like the + * platform libm overflowed but didn't set errno, force errno to ERANGE. In + * effect, we're trying to force a useful implementation of C89 errno + * behavior. + * Caution: + * This isn't reliable. See Py_OVERFLOWED comments. + * X and Y may be evaluated more than once. + */ +#define Py_ADJUST_ERANGE1(X) \ + do { \ + if (errno == 0) { \ + if ((X) == Py_HUGE_VAL || (X) == -Py_HUGE_VAL) \ + errno = ERANGE; \ + } \ + else if (errno == ERANGE && (X) == 0.0) \ + errno = 0; \ + } while(0) + +#define Py_ADJUST_ERANGE2(X, Y) \ + do { \ + if ((X) == Py_HUGE_VAL || (X) == -Py_HUGE_VAL || \ + (Y) == Py_HUGE_VAL || (Y) == -Py_HUGE_VAL) { \ + if (errno == 0) \ + errno = ERANGE; \ + } \ + else if (errno == ERANGE) \ + errno = 0; \ + } while(0) + +/* The functions _Py_dg_strtod and _Py_dg_dtoa in Python/dtoa.c (which are + * required to support the short float repr introduced in Python 3.1) require + * that the floating-point unit that's being used for arithmetic operations + * on C doubles is set to use 53-bit precision. It also requires that the + * FPU rounding mode is round-half-to-even, but that's less often an issue. + * + * If your FPU isn't already set to 53-bit precision/round-half-to-even, and + * you want to make use of _Py_dg_strtod and _Py_dg_dtoa, then you should + * + * #define HAVE_PY_SET_53BIT_PRECISION 1 + * + * and also give appropriate definitions for the following three macros: + * + * _PY_SET_53BIT_PRECISION_START : store original FPU settings, and + * set FPU to 53-bit precision/round-half-to-even + * _PY_SET_53BIT_PRECISION_END : restore original FPU settings + * _PY_SET_53BIT_PRECISION_HEADER : any variable declarations needed to + * use the two macros above. + * + * The macros are designed to be used within a single C function: see + * Python/pystrtod.c for an example of their use. + */ + +/* get and set x87 control word for gcc/x86 */ +#ifdef HAVE_GCC_ASM_FOR_X87 +#define HAVE_PY_SET_53BIT_PRECISION 1 +/* _Py_get/set_387controlword functions are defined in Python/pymath.c */ +#define _Py_SET_53BIT_PRECISION_HEADER \ + unsigned short old_387controlword, new_387controlword +#define _Py_SET_53BIT_PRECISION_START \ + do { \ + old_387controlword = _Py_get_387controlword(); \ + new_387controlword = (old_387controlword & ~0x0f00) | 0x0200; \ + if (new_387controlword != old_387controlword) \ + _Py_set_387controlword(new_387controlword); \ + } while (0) +#define _Py_SET_53BIT_PRECISION_END \ + if (new_387controlword != old_387controlword) \ + _Py_set_387controlword(old_387controlword) +#endif + +/* get and set x87 control word for VisualStudio/x86 */ +#if defined(_MSC_VER) && !defined(_WIN64) && !defined(_M_ARM) /* x87 not supported in 64-bit or ARM */ +#define HAVE_PY_SET_53BIT_PRECISION 1 +#define _Py_SET_53BIT_PRECISION_HEADER \ + unsigned int old_387controlword, new_387controlword, out_387controlword +/* We use the __control87_2 function to set only the x87 control word. + The SSE control word is unaffected. */ +#define _Py_SET_53BIT_PRECISION_START \ + do { \ + __control87_2(0, 0, &old_387controlword, NULL); \ + new_387controlword = \ + (old_387controlword & ~(_MCW_PC | _MCW_RC)) | (_PC_53 | _RC_NEAR); \ + if (new_387controlword != old_387controlword) \ + __control87_2(new_387controlword, _MCW_PC | _MCW_RC, \ + &out_387controlword, NULL); \ + } while (0) +#define _Py_SET_53BIT_PRECISION_END \ + do { \ + if (new_387controlword != old_387controlword) \ + __control87_2(old_387controlword, _MCW_PC | _MCW_RC, \ + &out_387controlword, NULL); \ + } while (0) +#endif + +#ifdef HAVE_GCC_ASM_FOR_MC68881 +#define HAVE_PY_SET_53BIT_PRECISION 1 +#define _Py_SET_53BIT_PRECISION_HEADER \ + unsigned int old_fpcr, new_fpcr +#define _Py_SET_53BIT_PRECISION_START \ + do { \ + __asm__ ("fmove.l %%fpcr,%0" : "=g" (old_fpcr)); \ + /* Set double precision / round to nearest. */ \ + new_fpcr = (old_fpcr & ~0xf0) | 0x80; \ + if (new_fpcr != old_fpcr) \ + __asm__ volatile ("fmove.l %0,%%fpcr" : : "g" (new_fpcr)); \ + } while (0) +#define _Py_SET_53BIT_PRECISION_END \ + do { \ + if (new_fpcr != old_fpcr) \ + __asm__ volatile ("fmove.l %0,%%fpcr" : : "g" (old_fpcr)); \ + } while (0) +#endif + +/* default definitions are empty */ +#ifndef HAVE_PY_SET_53BIT_PRECISION +#define _Py_SET_53BIT_PRECISION_HEADER +#define _Py_SET_53BIT_PRECISION_START +#define _Py_SET_53BIT_PRECISION_END +#endif + +/* If we can't guarantee 53-bit precision, don't use the code + in Python/dtoa.c, but fall back to standard code. This + means that repr of a float will be long (17 sig digits). + + Realistically, there are two things that could go wrong: + + (1) doubles aren't IEEE 754 doubles, or + (2) we're on x86 with the rounding precision set to 64-bits + (extended precision), and we don't know how to change + the rounding precision. + */ + +#if !defined(DOUBLE_IS_LITTLE_ENDIAN_IEEE754) && \ + !defined(DOUBLE_IS_BIG_ENDIAN_IEEE754) && \ + !defined(DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754) +#define PY_NO_SHORT_FLOAT_REPR +#endif + +/* double rounding is symptomatic of use of extended precision on x86. If + we're seeing double rounding, and we don't have any mechanism available for + changing the FPU rounding precision, then don't use Python/dtoa.c. */ +#if defined(X87_DOUBLE_ROUNDING) && !defined(HAVE_PY_SET_53BIT_PRECISION) +#define PY_NO_SHORT_FLOAT_REPR +#endif + + +/* Py_DEPRECATED(version) + * Declare a variable, type, or function deprecated. + * The macro must be placed before the declaration. + * Usage: + * Py_DEPRECATED(3.3) extern int old_var; + * Py_DEPRECATED(3.4) typedef int T1; + * Py_DEPRECATED(3.8) PyAPI_FUNC(int) Py_OldFunction(void); + */ +#if defined(__GNUC__) \ + && ((__GNUC__ >= 4) || (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) +#define Py_DEPRECATED(VERSION_UNUSED) __attribute__((__deprecated__)) +#elif defined(_MSC_VER) +#define Py_DEPRECATED(VERSION) __declspec(deprecated( \ + "deprecated in " #VERSION)) +#else +#define Py_DEPRECATED(VERSION_UNUSED) +#endif + +#if defined(__clang__) +#define _Py_COMP_DIAG_PUSH _Pragma("clang diagnostic push") +#define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#define _Py_COMP_DIAG_POP _Pragma("clang diagnostic pop") +#elif defined(__GNUC__) \ + && ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) +#define _Py_COMP_DIAG_PUSH _Pragma("GCC diagnostic push") +#define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define _Py_COMP_DIAG_POP _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) +#define _Py_COMP_DIAG_PUSH __pragma(warning(push)) +#define _Py_COMP_DIAG_IGNORE_DEPR_DECLS __pragma(warning(disable: 4996)) +#define _Py_COMP_DIAG_POP __pragma(warning(pop)) +#else +#define _Py_COMP_DIAG_PUSH +#define _Py_COMP_DIAG_IGNORE_DEPR_DECLS +#define _Py_COMP_DIAG_POP +#endif + +/* _Py_HOT_FUNCTION + * The hot attribute on a function is used to inform the compiler that the + * function is a hot spot of the compiled program. The function is optimized + * more aggressively and on many target it is placed into special subsection of + * the text section so all hot functions appears close together improving + * locality. + * + * Usage: + * int _Py_HOT_FUNCTION x(void) { return 3; } + * + * Issue #28618: This attribute must not be abused, otherwise it can have a + * negative effect on performance. Only the functions were Python spend most of + * its time must use it. Use a profiler when running performance benchmark + * suite to find these functions. + */ +#if defined(__GNUC__) \ + && ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) +#define _Py_HOT_FUNCTION __attribute__((hot)) +#else +#define _Py_HOT_FUNCTION +#endif + +/* _Py_NO_INLINE + * Disable inlining on a function. For example, it helps to reduce the C stack + * consumption. + * + * Usage: + * int _Py_NO_INLINE x(void) { return 3; } + */ +#if defined(_MSC_VER) +# define _Py_NO_INLINE __declspec(noinline) +#elif defined(__GNUC__) || defined(__clang__) +# define _Py_NO_INLINE __attribute__ ((noinline)) +#else +# define _Py_NO_INLINE +#endif + +/************************************************************************** +Prototypes that are missing from the standard include files on some systems +(and possibly only some versions of such systems.) + +Please be conservative with adding new ones, document them and enclose them +in platform-specific #ifdefs. +**************************************************************************/ + +#ifdef SOLARIS +/* Unchecked */ +extern int gethostname(char *, int); +#endif + +#ifdef HAVE__GETPTY +#include /* we need to import mode_t */ +extern char * _getpty(int *, int, mode_t, int); +#endif + +/* On QNX 6, struct termio must be declared by including sys/termio.h + if TCGETA, TCSETA, TCSETAW, or TCSETAF are used. sys/termio.h must + be included before termios.h or it will generate an error. */ +#if defined(HAVE_SYS_TERMIO_H) && !defined(__hpux) +#include +#endif + + +/* On 4.4BSD-descendants, ctype functions serves the whole range of + * wchar_t character set rather than single byte code points only. + * This characteristic can break some operations of string object + * including str.upper() and str.split() on UTF-8 locales. This + * workaround was provided by Tim Robbins of FreeBSD project. + */ + +#if defined(__APPLE__) +# define _PY_PORT_CTYPE_UTF8_ISSUE +#endif + +#ifdef _PY_PORT_CTYPE_UTF8_ISSUE +#ifndef __cplusplus + /* The workaround below is unsafe in C++ because + * the defines these symbols as real functions, + * with a slightly different signature. + * See issue #10910 + */ +#include +#include +#undef isalnum +#define isalnum(c) iswalnum(btowc(c)) +#undef isalpha +#define isalpha(c) iswalpha(btowc(c)) +#undef islower +#define islower(c) iswlower(btowc(c)) +#undef isspace +#define isspace(c) iswspace(btowc(c)) +#undef isupper +#define isupper(c) iswupper(btowc(c)) +#undef tolower +#define tolower(c) towlower(btowc(c)) +#undef toupper +#define toupper(c) towupper(btowc(c)) +#endif +#endif + + +/* Declarations for symbol visibility. + + PyAPI_FUNC(type): Declares a public Python API function and return type + PyAPI_DATA(type): Declares public Python data and its type + PyMODINIT_FUNC: A Python module init function. If these functions are + inside the Python core, they are private to the core. + If in an extension module, it may be declared with + external linkage depending on the platform. + + As a number of platforms support/require "__declspec(dllimport/dllexport)", + we support a HAVE_DECLSPEC_DLL macro to save duplication. +*/ + +/* + All windows ports, except cygwin, are handled in PC/pyconfig.h. + + Cygwin is the only other autoconf platform requiring special + linkage handling and it uses __declspec(). +*/ +#if defined(__CYGWIN__) +# define HAVE_DECLSPEC_DLL +#endif + +#include "exports.h" + +/* only get special linkage if built as shared or platform is Cygwin */ +#if defined(Py_ENABLE_SHARED) || defined(__CYGWIN__) +# if defined(HAVE_DECLSPEC_DLL) +# if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# define PyAPI_FUNC(RTYPE) Py_EXPORTED_SYMBOL RTYPE +# define PyAPI_DATA(RTYPE) extern Py_EXPORTED_SYMBOL RTYPE + /* module init functions inside the core need no external linkage */ + /* except for Cygwin to handle embedding */ +# if defined(__CYGWIN__) +# define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject* +# else /* __CYGWIN__ */ +# define PyMODINIT_FUNC PyObject* +# endif /* __CYGWIN__ */ +# else /* Py_BUILD_CORE */ + /* Building an extension module, or an embedded situation */ + /* public Python functions and data are imported */ + /* Under Cygwin, auto-import functions to prevent compilation */ + /* failures similar to those described at the bottom of 4.1: */ + /* http://docs.python.org/extending/windows.html#a-cookbook-approach */ +# if !defined(__CYGWIN__) +# define PyAPI_FUNC(RTYPE) Py_IMPORTED_SYMBOL RTYPE +# endif /* !__CYGWIN__ */ +# define PyAPI_DATA(RTYPE) extern Py_IMPORTED_SYMBOL RTYPE + /* module init functions outside the core must be exported */ +# if defined(__cplusplus) +# define PyMODINIT_FUNC extern "C" Py_EXPORTED_SYMBOL PyObject* +# else /* __cplusplus */ +# define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject* +# endif /* __cplusplus */ +# endif /* Py_BUILD_CORE */ +# endif /* HAVE_DECLSPEC_DLL */ +#endif /* Py_ENABLE_SHARED */ + +/* If no external linkage macros defined by now, create defaults */ +#ifndef PyAPI_FUNC +# define PyAPI_FUNC(RTYPE) Py_EXPORTED_SYMBOL RTYPE +#endif +#ifndef PyAPI_DATA +# define PyAPI_DATA(RTYPE) extern Py_EXPORTED_SYMBOL RTYPE +#endif +#ifndef PyMODINIT_FUNC +# if defined(__cplusplus) +# define PyMODINIT_FUNC extern "C" Py_EXPORTED_SYMBOL PyObject* +# else /* __cplusplus */ +# define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject* +# endif /* __cplusplus */ +#endif + +/* limits.h constants that may be missing */ + +#ifndef INT_MAX +#define INT_MAX 2147483647 +#endif + +#ifndef LONG_MAX +#if SIZEOF_LONG == 4 +#define LONG_MAX 0X7FFFFFFFL +#elif SIZEOF_LONG == 8 +#define LONG_MAX 0X7FFFFFFFFFFFFFFFL +#else +#error "could not set LONG_MAX in pyport.h" +#endif +#endif + +#ifndef LONG_MIN +#define LONG_MIN (-LONG_MAX-1) +#endif + +#ifndef LONG_BIT +#define LONG_BIT (8 * SIZEOF_LONG) +#endif + +#if LONG_BIT != 8 * SIZEOF_LONG +/* 04-Oct-2000 LONG_BIT is apparently (mis)defined as 64 on some recent + * 32-bit platforms using gcc. We try to catch that here at compile-time + * rather than waiting for integer multiplication to trigger bogus + * overflows. + */ +#error "LONG_BIT definition appears wrong for platform (bad gcc/glibc config?)." +#endif + +#ifdef __cplusplus +} +#endif + +/* + * Hide GCC attributes from compilers that don't support them. + */ +#if (!defined(__GNUC__) || __GNUC__ < 2 || \ + (__GNUC__ == 2 && __GNUC_MINOR__ < 7) ) +#define Py_GCC_ATTRIBUTE(x) +#else +#define Py_GCC_ATTRIBUTE(x) __attribute__(x) +#endif + +/* + * Specify alignment on compilers that support it. + */ +#if defined(__GNUC__) && __GNUC__ >= 3 +#define Py_ALIGNED(x) __attribute__((aligned(x))) +#else +#define Py_ALIGNED(x) +#endif + +/* Eliminate end-of-loop code not reached warnings from SunPro C + * when using do{...}while(0) macros + */ +#ifdef __SUNPRO_C +#pragma error_messages (off,E_END_OF_LOOP_CODE_NOT_REACHED) +#endif + +#ifndef Py_LL +#define Py_LL(x) x##LL +#endif + +#ifndef Py_ULL +#define Py_ULL(x) Py_LL(x##U) +#endif + +#define Py_VA_COPY va_copy + +/* + * Convenient macros to deal with endianness of the platform. WORDS_BIGENDIAN is + * detected by configure and defined in pyconfig.h. The code in pyconfig.h + * also takes care of Apple's universal builds. + */ + +#ifdef WORDS_BIGENDIAN +# define PY_BIG_ENDIAN 1 +# define PY_LITTLE_ENDIAN 0 +#else +# define PY_BIG_ENDIAN 0 +# define PY_LITTLE_ENDIAN 1 +#endif + +#ifdef Py_BUILD_CORE +/* + * Macros to protect CRT calls against instant termination when passed an + * invalid parameter (issue23524). + */ +#if defined _MSC_VER && _MSC_VER >= 1900 + +extern _invalid_parameter_handler _Py_silent_invalid_parameter_handler; +#define _Py_BEGIN_SUPPRESS_IPH { _invalid_parameter_handler _Py_old_handler = \ + _set_thread_local_invalid_parameter_handler(_Py_silent_invalid_parameter_handler); +#define _Py_END_SUPPRESS_IPH _set_thread_local_invalid_parameter_handler(_Py_old_handler); } + +#else + +#define _Py_BEGIN_SUPPRESS_IPH +#define _Py_END_SUPPRESS_IPH + +#endif /* _MSC_VER >= 1900 */ +#endif /* Py_BUILD_CORE */ + +#ifdef __ANDROID__ + /* The Android langinfo.h header is not used. */ +# undef HAVE_LANGINFO_H +# undef CODESET +#endif + +/* Maximum value of the Windows DWORD type */ +#define PY_DWORD_MAX 4294967295U + +/* This macro used to tell whether Python was built with multithreading + * enabled. Now multithreading is always enabled, but keep the macro + * for compatibility. + */ +#ifndef WITH_THREAD +# define WITH_THREAD +#endif + +/* Check that ALT_SOABI is consistent with Py_TRACE_REFS: + ./configure --with-trace-refs should must be used to define Py_TRACE_REFS */ +#if defined(ALT_SOABI) && defined(Py_TRACE_REFS) +# error "Py_TRACE_REFS ABI is not compatible with release and debug ABI" +#endif + +#if defined(__ANDROID__) || defined(__VXWORKS__) + /* Ignore the locale encoding: force UTF-8 */ +# define _Py_FORCE_UTF8_LOCALE +#endif + +#if defined(_Py_FORCE_UTF8_LOCALE) || defined(__APPLE__) + /* Use UTF-8 as filesystem encoding */ +# define _Py_FORCE_UTF8_FS_ENCODING +#endif + +/* Mark a function which cannot return. Example: + PyAPI_FUNC(void) _Py_NO_RETURN PyThread_exit_thread(void); + + XLC support is intentionally omitted due to bpo-40244 */ +#if defined(__clang__) || \ + (defined(__GNUC__) && \ + ((__GNUC__ >= 3) || \ + (__GNUC__ == 2) && (__GNUC_MINOR__ >= 5))) +# define _Py_NO_RETURN __attribute__((__noreturn__)) +#elif defined(_MSC_VER) +# define _Py_NO_RETURN __declspec(noreturn) +#else +# define _Py_NO_RETURN +#endif + + +// Preprocessor check for a builtin preprocessor function. Always return 0 +// if __has_builtin() macro is not defined. +// +// __has_builtin() is available on clang and GCC 10. +#ifdef __has_builtin +# define _Py__has_builtin(x) __has_builtin(x) +#else +# define _Py__has_builtin(x) 0 +#endif + + +#endif /* Py_PYPORT_H */ diff --git a/scripts/build-windows/py39-libs/include/pystate.h b/scripts/build-windows/py39-libs/include/pystate.h new file mode 100644 index 000000000..d9f31e365 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pystate.h @@ -0,0 +1,150 @@ +/* Thread and interpreter state structures and their interfaces */ + + +#ifndef Py_PYSTATE_H +#define Py_PYSTATE_H +#ifdef __cplusplus +extern "C" { +#endif + +/* This limitation is for performance and simplicity. If needed it can be +removed (with effort). */ +#define MAX_CO_EXTRA_USERS 255 + +/* Forward declarations for PyFrameObject, PyThreadState + and PyInterpreterState */ +struct _ts; +struct _is; + +/* struct _ts is defined in cpython/pystate.h */ +typedef struct _ts PyThreadState; +/* struct _is is defined in internal/pycore_interp.h */ +typedef struct _is PyInterpreterState; + +PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_New(void); +PyAPI_FUNC(void) PyInterpreterState_Clear(PyInterpreterState *); +PyAPI_FUNC(void) PyInterpreterState_Delete(PyInterpreterState *); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000 +/* New in 3.9 */ +/* Get the current interpreter state. + + Issue a fatal error if there no current Python thread state or no current + interpreter. It cannot return NULL. + + The caller must hold the GIL. */ +PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_Get(void); +#endif + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03080000 +/* New in 3.8 */ +PyAPI_FUNC(PyObject *) PyInterpreterState_GetDict(PyInterpreterState *); +#endif + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 +/* New in 3.7 */ +PyAPI_FUNC(int64_t) PyInterpreterState_GetID(PyInterpreterState *); +#endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 + +/* State unique per thread */ + +/* New in 3.3 */ +PyAPI_FUNC(int) PyState_AddModule(PyObject*, struct PyModuleDef*); +PyAPI_FUNC(int) PyState_RemoveModule(struct PyModuleDef*); +#endif +PyAPI_FUNC(PyObject*) PyState_FindModule(struct PyModuleDef*); + +PyAPI_FUNC(PyThreadState *) PyThreadState_New(PyInterpreterState *); +PyAPI_FUNC(void) PyThreadState_Clear(PyThreadState *); +PyAPI_FUNC(void) PyThreadState_Delete(PyThreadState *); + +/* Get the current thread state. + + When the current thread state is NULL, this issues a fatal error (so that + the caller needn't check for NULL). + + The caller must hold the GIL. + + See also PyThreadState_GET() and _PyThreadState_GET(). */ +PyAPI_FUNC(PyThreadState *) PyThreadState_Get(void); + +/* Get the current Python thread state. + + Macro using PyThreadState_Get() or _PyThreadState_GET() depending if + pycore_pystate.h is included or not (this header redefines the macro). + + If PyThreadState_Get() is used, issue a fatal error if the current thread + state is NULL. + + See also PyThreadState_Get() and _PyThreadState_GET(). */ +#define PyThreadState_GET() PyThreadState_Get() + +PyAPI_FUNC(PyThreadState *) PyThreadState_Swap(PyThreadState *); +PyAPI_FUNC(PyObject *) PyThreadState_GetDict(void); +PyAPI_FUNC(int) PyThreadState_SetAsyncExc(unsigned long, PyObject *); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000 +/* New in 3.9 */ +PyAPI_FUNC(PyInterpreterState*) PyThreadState_GetInterpreter(PyThreadState *tstate); +PyAPI_FUNC(PyFrameObject*) PyThreadState_GetFrame(PyThreadState *tstate); +PyAPI_FUNC(uint64_t) PyThreadState_GetID(PyThreadState *tstate); +#endif + +typedef + enum {PyGILState_LOCKED, PyGILState_UNLOCKED} + PyGILState_STATE; + + +/* Ensure that the current thread is ready to call the Python + C API, regardless of the current state of Python, or of its + thread lock. This may be called as many times as desired + by a thread so long as each call is matched with a call to + PyGILState_Release(). In general, other thread-state APIs may + be used between _Ensure() and _Release() calls, so long as the + thread-state is restored to its previous state before the Release(). + For example, normal use of the Py_BEGIN_ALLOW_THREADS/ + Py_END_ALLOW_THREADS macros are acceptable. + + The return value is an opaque "handle" to the thread state when + PyGILState_Ensure() was called, and must be passed to + PyGILState_Release() to ensure Python is left in the same state. Even + though recursive calls are allowed, these handles can *not* be shared - + each unique call to PyGILState_Ensure must save the handle for its + call to PyGILState_Release. + + When the function returns, the current thread will hold the GIL. + + Failure is a fatal error. +*/ +PyAPI_FUNC(PyGILState_STATE) PyGILState_Ensure(void); + +/* Release any resources previously acquired. After this call, Python's + state will be the same as it was prior to the corresponding + PyGILState_Ensure() call (but generally this state will be unknown to + the caller, hence the use of the GILState API.) + + Every call to PyGILState_Ensure must be matched by a call to + PyGILState_Release on the same thread. +*/ +PyAPI_FUNC(void) PyGILState_Release(PyGILState_STATE); + +/* Helper/diagnostic function - get the current thread state for + this thread. May return NULL if no GILState API has been used + on the current thread. Note that the main thread always has such a + thread-state, even if no auto-thread-state call has been made + on the main thread. +*/ +PyAPI_FUNC(PyThreadState *) PyGILState_GetThisThreadState(void); + + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_PYSTATE_H +# include "cpython/pystate.h" +# undef Py_CPYTHON_PYSTATE_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYSTATE_H */ diff --git a/scripts/build-windows/py39-libs/include/pystrcmp.h b/scripts/build-windows/py39-libs/include/pystrcmp.h new file mode 100644 index 000000000..eccabdce0 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pystrcmp.h @@ -0,0 +1,23 @@ +#ifndef Py_STRCMP_H +#define Py_STRCMP_H + +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(int) PyOS_mystrnicmp(const char *, const char *, Py_ssize_t); +PyAPI_FUNC(int) PyOS_mystricmp(const char *, const char *); + +#ifdef MS_WINDOWS +#define PyOS_strnicmp strnicmp +#define PyOS_stricmp stricmp +#else +#define PyOS_strnicmp PyOS_mystrnicmp +#define PyOS_stricmp PyOS_mystricmp +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_STRCMP_H */ diff --git a/scripts/build-windows/py39-libs/include/pystrhex.h b/scripts/build-windows/py39-libs/include/pystrhex.h new file mode 100644 index 000000000..bccae77ac --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pystrhex.h @@ -0,0 +1,22 @@ +#ifndef Py_STRHEX_H +#define Py_STRHEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_LIMITED_API +/* Returns a str() containing the hex representation of argbuf. */ +PyAPI_FUNC(PyObject*) _Py_strhex(const char* argbuf, const Py_ssize_t arglen); +/* Returns a bytes() containing the ASCII hex representation of argbuf. */ +PyAPI_FUNC(PyObject*) _Py_strhex_bytes(const char* argbuf, const Py_ssize_t arglen); +/* These variants include support for a separator between every N bytes: */ +PyAPI_FUNC(PyObject*) _Py_strhex_with_sep(const char* argbuf, const Py_ssize_t arglen, const PyObject* sep, const int bytes_per_group); +PyAPI_FUNC(PyObject*) _Py_strhex_bytes_with_sep(const char* argbuf, const Py_ssize_t arglen, const PyObject* sep, const int bytes_per_group); +#endif /* !Py_LIMITED_API */ + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_STRHEX_H */ diff --git a/scripts/build-windows/py39-libs/include/pystrtod.h b/scripts/build-windows/py39-libs/include/pystrtod.h new file mode 100644 index 000000000..ba2e8d00f --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pystrtod.h @@ -0,0 +1,45 @@ +#ifndef Py_STRTOD_H +#define Py_STRTOD_H + +#ifdef __cplusplus +extern "C" { +#endif + + +PyAPI_FUNC(double) PyOS_string_to_double(const char *str, + char **endptr, + PyObject *overflow_exception); + +/* The caller is responsible for calling PyMem_Free to free the buffer + that's is returned. */ +PyAPI_FUNC(char *) PyOS_double_to_string(double val, + char format_code, + int precision, + int flags, + int *type); + +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) _Py_string_to_number_with_underscores( + const char *str, Py_ssize_t len, const char *what, PyObject *obj, void *arg, + PyObject *(*innerfunc)(const char *, Py_ssize_t, void *)); + +PyAPI_FUNC(double) _Py_parse_inf_or_nan(const char *p, char **endptr); +#endif + + +/* PyOS_double_to_string's "flags" parameter can be set to 0 or more of: */ +#define Py_DTSF_SIGN 0x01 /* always add the sign */ +#define Py_DTSF_ADD_DOT_0 0x02 /* if the result is an integer add ".0" */ +#define Py_DTSF_ALT 0x04 /* "alternate" formatting. it's format_code + specific */ + +/* PyOS_double_to_string's "type", if non-NULL, will be set to one of: */ +#define Py_DTST_FINITE 0 +#define Py_DTST_INFINITE 1 +#define Py_DTST_NAN 2 + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_STRTOD_H */ diff --git a/scripts/build-windows/py39-libs/include/pythonrun.h b/scripts/build-windows/py39-libs/include/pythonrun.h new file mode 100644 index 000000000..ecd35c601 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pythonrun.h @@ -0,0 +1,217 @@ + +/* Interfaces to parse and execute pieces of python code */ + +#ifndef Py_PYTHONRUN_H +#define Py_PYTHONRUN_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_LIMITED_API +PyAPI_FUNC(int) PyRun_SimpleStringFlags(const char *, PyCompilerFlags *); +PyAPI_FUNC(int) PyRun_AnyFileExFlags( + FILE *fp, + const char *filename, /* decoded from the filesystem encoding */ + int closeit, + PyCompilerFlags *flags); +PyAPI_FUNC(int) PyRun_SimpleFileExFlags( + FILE *fp, + const char *filename, /* decoded from the filesystem encoding */ + int closeit, + PyCompilerFlags *flags); +PyAPI_FUNC(int) PyRun_InteractiveOneFlags( + FILE *fp, + const char *filename, /* decoded from the filesystem encoding */ + PyCompilerFlags *flags); +PyAPI_FUNC(int) PyRun_InteractiveOneObject( + FILE *fp, + PyObject *filename, + PyCompilerFlags *flags); +PyAPI_FUNC(int) PyRun_InteractiveLoopFlags( + FILE *fp, + const char *filename, /* decoded from the filesystem encoding */ + PyCompilerFlags *flags); + +PyAPI_FUNC(struct _mod *) PyParser_ASTFromString( + const char *s, + const char *filename, /* decoded from the filesystem encoding */ + int start, + PyCompilerFlags *flags, + PyArena *arena); +PyAPI_FUNC(struct _mod *) PyParser_ASTFromStringObject( + const char *s, + PyObject *filename, + int start, + PyCompilerFlags *flags, + PyArena *arena); +PyAPI_FUNC(struct _mod *) PyParser_ASTFromFile( + FILE *fp, + const char *filename, /* decoded from the filesystem encoding */ + const char* enc, + int start, + const char *ps1, + const char *ps2, + PyCompilerFlags *flags, + int *errcode, + PyArena *arena); +PyAPI_FUNC(struct _mod *) PyParser_ASTFromFileObject( + FILE *fp, + PyObject *filename, + const char* enc, + int start, + const char *ps1, + const char *ps2, + PyCompilerFlags *flags, + int *errcode, + PyArena *arena); +#endif + +#ifndef PyParser_SimpleParseString +#define PyParser_SimpleParseString(S, B) \ + PyParser_SimpleParseStringFlags(S, B, 0) +#define PyParser_SimpleParseFile(FP, S, B) \ + PyParser_SimpleParseFileFlags(FP, S, B, 0) +#endif + +#ifndef Py_BUILD_CORE +Py_DEPRECATED(3.9) +#endif +PyAPI_FUNC(struct _node *) PyParser_SimpleParseStringFlags(const char *, int, int); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +#ifndef Py_BUILD_CORE +Py_DEPRECATED(3.9) +#endif +PyAPI_FUNC(struct _node *) PyParser_SimpleParseStringFlagsFilename(const char *, + const char *, + int, int); +#endif +#ifndef Py_BUILD_CORE +Py_DEPRECATED(3.9) +#endif +PyAPI_FUNC(struct _node *) PyParser_SimpleParseFileFlags(FILE *, const char *, int, int); +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) PyRun_StringFlags(const char *, int, PyObject *, + PyObject *, PyCompilerFlags *); + +PyAPI_FUNC(PyObject *) PyRun_FileExFlags( + FILE *fp, + const char *filename, /* decoded from the filesystem encoding */ + int start, + PyObject *globals, + PyObject *locals, + int closeit, + PyCompilerFlags *flags); +#endif + +#ifdef Py_LIMITED_API +PyAPI_FUNC(PyObject *) Py_CompileString(const char *, const char *, int); +#else +#define Py_CompileString(str, p, s) Py_CompileStringExFlags(str, p, s, NULL, -1) +#define Py_CompileStringFlags(str, p, s, f) Py_CompileStringExFlags(str, p, s, f, -1) +PyAPI_FUNC(PyObject *) Py_CompileStringExFlags( + const char *str, + const char *filename, /* decoded from the filesystem encoding */ + int start, + PyCompilerFlags *flags, + int optimize); +PyAPI_FUNC(PyObject *) Py_CompileStringObject( + const char *str, + PyObject *filename, int start, + PyCompilerFlags *flags, + int optimize); +#endif +PyAPI_FUNC(struct symtable *) Py_SymtableString( + const char *str, + const char *filename, /* decoded from the filesystem encoding */ + int start); +#ifndef Py_LIMITED_API +PyAPI_FUNC(const char *) _Py_SourceAsString( + PyObject *cmd, + const char *funcname, + const char *what, + PyCompilerFlags *cf, + PyObject **cmd_copy); + +PyAPI_FUNC(struct symtable *) Py_SymtableStringObject( + const char *str, + PyObject *filename, + int start); + +PyAPI_FUNC(struct symtable *) _Py_SymtableStringObjectFlags( + const char *str, + PyObject *filename, + int start, + PyCompilerFlags *flags); +#endif + +PyAPI_FUNC(void) PyErr_Print(void); +PyAPI_FUNC(void) PyErr_PrintEx(int); +PyAPI_FUNC(void) PyErr_Display(PyObject *, PyObject *, PyObject *); + +#ifndef Py_LIMITED_API +/* A function flavor is also exported by libpython. It is required when + libpython is accessed directly rather than using header files which defines + macros below. On Windows, for example, PyAPI_FUNC() uses dllexport to + export functions in pythonXX.dll. */ +PyAPI_FUNC(PyObject *) PyRun_String(const char *str, int s, PyObject *g, PyObject *l); +PyAPI_FUNC(int) PyRun_AnyFile(FILE *fp, const char *name); +PyAPI_FUNC(int) PyRun_AnyFileEx(FILE *fp, const char *name, int closeit); +PyAPI_FUNC(int) PyRun_AnyFileFlags(FILE *, const char *, PyCompilerFlags *); +PyAPI_FUNC(int) PyRun_SimpleString(const char *s); +PyAPI_FUNC(int) PyRun_SimpleFile(FILE *f, const char *p); +PyAPI_FUNC(int) PyRun_SimpleFileEx(FILE *f, const char *p, int c); +PyAPI_FUNC(int) PyRun_InteractiveOne(FILE *f, const char *p); +PyAPI_FUNC(int) PyRun_InteractiveLoop(FILE *f, const char *p); +PyAPI_FUNC(PyObject *) PyRun_File(FILE *fp, const char *p, int s, PyObject *g, PyObject *l); +PyAPI_FUNC(PyObject *) PyRun_FileEx(FILE *fp, const char *p, int s, PyObject *g, PyObject *l, int c); +PyAPI_FUNC(PyObject *) PyRun_FileFlags(FILE *fp, const char *p, int s, PyObject *g, PyObject *l, PyCompilerFlags *flags); + +/* Use macros for a bunch of old variants */ +#define PyRun_String(str, s, g, l) PyRun_StringFlags(str, s, g, l, NULL) +#define PyRun_AnyFile(fp, name) PyRun_AnyFileExFlags(fp, name, 0, NULL) +#define PyRun_AnyFileEx(fp, name, closeit) \ + PyRun_AnyFileExFlags(fp, name, closeit, NULL) +#define PyRun_AnyFileFlags(fp, name, flags) \ + PyRun_AnyFileExFlags(fp, name, 0, flags) +#define PyRun_SimpleString(s) PyRun_SimpleStringFlags(s, NULL) +#define PyRun_SimpleFile(f, p) PyRun_SimpleFileExFlags(f, p, 0, NULL) +#define PyRun_SimpleFileEx(f, p, c) PyRun_SimpleFileExFlags(f, p, c, NULL) +#define PyRun_InteractiveOne(f, p) PyRun_InteractiveOneFlags(f, p, NULL) +#define PyRun_InteractiveLoop(f, p) PyRun_InteractiveLoopFlags(f, p, NULL) +#define PyRun_File(fp, p, s, g, l) \ + PyRun_FileExFlags(fp, p, s, g, l, 0, NULL) +#define PyRun_FileEx(fp, p, s, g, l, c) \ + PyRun_FileExFlags(fp, p, s, g, l, c, NULL) +#define PyRun_FileFlags(fp, p, s, g, l, flags) \ + PyRun_FileExFlags(fp, p, s, g, l, 0, flags) +#endif + +/* Stuff with no proper home (yet) */ +#ifndef Py_LIMITED_API +PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, const char *); +#endif +PyAPI_DATA(int) (*PyOS_InputHook)(void); +PyAPI_DATA(char) *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *); +#ifndef Py_LIMITED_API +PyAPI_DATA(PyThreadState*) _PyOS_ReadlineTState; +#endif + +/* Stack size, in "pointers" (so we get extra safety margins + on 64-bit platforms). On a 32-bit platform, this translates + to an 8k margin. */ +#define PYOS_STACK_MARGIN 2048 + +#if defined(WIN32) && !defined(MS_WIN64) && !defined(_M_ARM) && defined(_MSC_VER) && _MSC_VER >= 1300 +/* Enable stack checking under Microsoft C */ +#define USE_STACKCHECK +#endif + +#ifdef USE_STACKCHECK +/* Check that we aren't overflowing our stack */ +PyAPI_FUNC(int) PyOS_CheckStack(void); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYTHONRUN_H */ diff --git a/scripts/build-windows/py39-libs/include/pythread.h b/scripts/build-windows/py39-libs/include/pythread.h new file mode 100644 index 000000000..739594a82 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pythread.h @@ -0,0 +1,169 @@ + +#ifndef Py_PYTHREAD_H +#define Py_PYTHREAD_H + +typedef void *PyThread_type_lock; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Return status codes for Python lock acquisition. Chosen for maximum + * backwards compatibility, ie failure -> 0, success -> 1. */ +typedef enum PyLockStatus { + PY_LOCK_FAILURE = 0, + PY_LOCK_ACQUIRED = 1, + PY_LOCK_INTR +} PyLockStatus; + +#ifndef Py_LIMITED_API +#define PYTHREAD_INVALID_THREAD_ID ((unsigned long)-1) +#endif + +PyAPI_FUNC(void) PyThread_init_thread(void); +PyAPI_FUNC(unsigned long) PyThread_start_new_thread(void (*)(void *), void *); +PyAPI_FUNC(void) _Py_NO_RETURN PyThread_exit_thread(void); +PyAPI_FUNC(unsigned long) PyThread_get_thread_ident(void); + +#if defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(_WIN32) || defined(_AIX) +#define PY_HAVE_THREAD_NATIVE_ID +PyAPI_FUNC(unsigned long) PyThread_get_thread_native_id(void); +#endif + +PyAPI_FUNC(PyThread_type_lock) PyThread_allocate_lock(void); +PyAPI_FUNC(void) PyThread_free_lock(PyThread_type_lock); +PyAPI_FUNC(int) PyThread_acquire_lock(PyThread_type_lock, int); +#define WAIT_LOCK 1 +#define NOWAIT_LOCK 0 + +#ifndef Py_LIMITED_API +#ifdef HAVE_FORK +/* Private function to reinitialize a lock at fork in the child process. + Reset the lock to the unlocked state. + Return 0 on success, return -1 on error. */ +PyAPI_FUNC(int) _PyThread_at_fork_reinit(PyThread_type_lock *lock); +#endif /* HAVE_FORK */ +#endif /* !Py_LIMITED_API */ + +/* PY_TIMEOUT_T is the integral type used to specify timeouts when waiting + on a lock (see PyThread_acquire_lock_timed() below). + PY_TIMEOUT_MAX is the highest usable value (in microseconds) of that + type, and depends on the system threading API. + + NOTE: this isn't the same value as `_thread.TIMEOUT_MAX`. The _thread + module exposes a higher-level API, with timeouts expressed in seconds + and floating-point numbers allowed. +*/ +#define PY_TIMEOUT_T long long + +#if defined(_POSIX_THREADS) + /* PyThread_acquire_lock_timed() uses _PyTime_FromNanoseconds(us * 1000), + convert microseconds to nanoseconds. */ +# define PY_TIMEOUT_MAX (LLONG_MAX / 1000) +#elif defined (NT_THREADS) + /* In the NT API, the timeout is a DWORD and is expressed in milliseconds */ +# if 0xFFFFFFFFLL * 1000 < LLONG_MAX +# define PY_TIMEOUT_MAX (0xFFFFFFFFLL * 1000) +# else +# define PY_TIMEOUT_MAX LLONG_MAX +# endif +#else +# define PY_TIMEOUT_MAX LLONG_MAX +#endif + + +/* If microseconds == 0, the call is non-blocking: it returns immediately + even when the lock can't be acquired. + If microseconds > 0, the call waits up to the specified duration. + If microseconds < 0, the call waits until success (or abnormal failure) + + microseconds must be less than PY_TIMEOUT_MAX. Behaviour otherwise is + undefined. + + If intr_flag is true and the acquire is interrupted by a signal, then the + call will return PY_LOCK_INTR. The caller may reattempt to acquire the + lock. +*/ +PyAPI_FUNC(PyLockStatus) PyThread_acquire_lock_timed(PyThread_type_lock, + PY_TIMEOUT_T microseconds, + int intr_flag); + +PyAPI_FUNC(void) PyThread_release_lock(PyThread_type_lock); + +PyAPI_FUNC(size_t) PyThread_get_stacksize(void); +PyAPI_FUNC(int) PyThread_set_stacksize(size_t); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(PyObject*) PyThread_GetInfo(void); +#endif + + +/* Thread Local Storage (TLS) API + TLS API is DEPRECATED. Use Thread Specific Storage (TSS) API. + + The existing TLS API has used int to represent TLS keys across all + platforms, but it is not POSIX-compliant. Therefore, the new TSS API uses + opaque data type to represent TSS keys to be compatible (see PEP 539). +*/ +Py_DEPRECATED(3.7) PyAPI_FUNC(int) PyThread_create_key(void); +Py_DEPRECATED(3.7) PyAPI_FUNC(void) PyThread_delete_key(int key); +Py_DEPRECATED(3.7) PyAPI_FUNC(int) PyThread_set_key_value(int key, + void *value); +Py_DEPRECATED(3.7) PyAPI_FUNC(void *) PyThread_get_key_value(int key); +Py_DEPRECATED(3.7) PyAPI_FUNC(void) PyThread_delete_key_value(int key); + +/* Cleanup after a fork */ +Py_DEPRECATED(3.7) PyAPI_FUNC(void) PyThread_ReInitTLS(void); + + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 +/* New in 3.7 */ +/* Thread Specific Storage (TSS) API */ + +typedef struct _Py_tss_t Py_tss_t; /* opaque */ + +#ifndef Py_LIMITED_API +#if defined(_POSIX_THREADS) + /* Darwin needs pthread.h to know type name the pthread_key_t. */ +# include +# define NATIVE_TSS_KEY_T pthread_key_t +#elif defined(NT_THREADS) + /* In Windows, native TSS key type is DWORD, + but hardcode the unsigned long to avoid errors for include directive. + */ +# define NATIVE_TSS_KEY_T unsigned long +#else +# error "Require native threads. See https://bugs.python.org/issue31370" +#endif + +/* When Py_LIMITED_API is not defined, the type layout of Py_tss_t is + exposed to allow static allocation in the API clients. Even in this case, + you must handle TSS keys through API functions due to compatibility. +*/ +struct _Py_tss_t { + int _is_initialized; + NATIVE_TSS_KEY_T _key; +}; + +#undef NATIVE_TSS_KEY_T + +/* When static allocation, you must initialize with Py_tss_NEEDS_INIT. */ +#define Py_tss_NEEDS_INIT {0} +#endif /* !Py_LIMITED_API */ + +PyAPI_FUNC(Py_tss_t *) PyThread_tss_alloc(void); +PyAPI_FUNC(void) PyThread_tss_free(Py_tss_t *key); + +/* The parameter key must not be NULL. */ +PyAPI_FUNC(int) PyThread_tss_is_created(Py_tss_t *key); +PyAPI_FUNC(int) PyThread_tss_create(Py_tss_t *key); +PyAPI_FUNC(void) PyThread_tss_delete(Py_tss_t *key); +PyAPI_FUNC(int) PyThread_tss_set(Py_tss_t *key, void *value); +PyAPI_FUNC(void *) PyThread_tss_get(Py_tss_t *key); +#endif /* New in 3.7 */ + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_PYTHREAD_H */ diff --git a/scripts/build-windows/py39-libs/include/pytime.h b/scripts/build-windows/py39-libs/include/pytime.h new file mode 100644 index 000000000..dde92ee0b --- /dev/null +++ b/scripts/build-windows/py39-libs/include/pytime.h @@ -0,0 +1,246 @@ +#ifndef Py_LIMITED_API +#ifndef Py_PYTIME_H +#define Py_PYTIME_H + +#include "pyconfig.h" /* include for defines */ +#include "object.h" + +/************************************************************************** +Symbols and macros to supply platform-independent interfaces to time related +functions and constants +**************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/* _PyTime_t: Python timestamp with subsecond precision. It can be used to + store a duration, and so indirectly a date (related to another date, like + UNIX epoch). */ +typedef int64_t _PyTime_t; +#define _PyTime_MIN INT64_MIN +#define _PyTime_MAX INT64_MAX + +typedef enum { + /* Round towards minus infinity (-inf). + For example, used to read a clock. */ + _PyTime_ROUND_FLOOR=0, + /* Round towards infinity (+inf). + For example, used for timeout to wait "at least" N seconds. */ + _PyTime_ROUND_CEILING=1, + /* Round to nearest with ties going to nearest even integer. + For example, used to round from a Python float. */ + _PyTime_ROUND_HALF_EVEN=2, + /* Round away from zero + For example, used for timeout. _PyTime_ROUND_CEILING rounds + -1e-9 to 0 milliseconds which causes bpo-31786 issue. + _PyTime_ROUND_UP rounds -1e-9 to -1 millisecond which keeps + the timeout sign as expected. select.poll(timeout) must block + for negative values." */ + _PyTime_ROUND_UP=3, + /* _PyTime_ROUND_TIMEOUT (an alias for _PyTime_ROUND_UP) should be + used for timeouts. */ + _PyTime_ROUND_TIMEOUT = _PyTime_ROUND_UP +} _PyTime_round_t; + + +/* Convert a time_t to a PyLong. */ +PyAPI_FUNC(PyObject *) _PyLong_FromTime_t( + time_t sec); + +/* Convert a PyLong to a time_t. */ +PyAPI_FUNC(time_t) _PyLong_AsTime_t( + PyObject *obj); + +/* Convert a number of seconds, int or float, to time_t. */ +PyAPI_FUNC(int) _PyTime_ObjectToTime_t( + PyObject *obj, + time_t *sec, + _PyTime_round_t); + +/* Convert a number of seconds, int or float, to a timeval structure. + usec is in the range [0; 999999] and rounded towards zero. + For example, -1.2 is converted to (-2, 800000). */ +PyAPI_FUNC(int) _PyTime_ObjectToTimeval( + PyObject *obj, + time_t *sec, + long *usec, + _PyTime_round_t); + +/* Convert a number of seconds, int or float, to a timespec structure. + nsec is in the range [0; 999999999] and rounded towards zero. + For example, -1.2 is converted to (-2, 800000000). */ +PyAPI_FUNC(int) _PyTime_ObjectToTimespec( + PyObject *obj, + time_t *sec, + long *nsec, + _PyTime_round_t); + + +/* Create a timestamp from a number of seconds. */ +PyAPI_FUNC(_PyTime_t) _PyTime_FromSeconds(int seconds); + +/* Macro to create a timestamp from a number of seconds, no integer overflow. + Only use the macro for small values, prefer _PyTime_FromSeconds(). */ +#define _PYTIME_FROMSECONDS(seconds) \ + ((_PyTime_t)(seconds) * (1000 * 1000 * 1000)) + +/* Create a timestamp from a number of nanoseconds. */ +PyAPI_FUNC(_PyTime_t) _PyTime_FromNanoseconds(_PyTime_t ns); + +/* Create a timestamp from nanoseconds (Python int). */ +PyAPI_FUNC(int) _PyTime_FromNanosecondsObject(_PyTime_t *t, + PyObject *obj); + +/* Convert a number of seconds (Python float or int) to a timetamp. + Raise an exception and return -1 on error, return 0 on success. */ +PyAPI_FUNC(int) _PyTime_FromSecondsObject(_PyTime_t *t, + PyObject *obj, + _PyTime_round_t round); + +/* Convert a number of milliseconds (Python float or int, 10^-3) to a timetamp. + Raise an exception and return -1 on error, return 0 on success. */ +PyAPI_FUNC(int) _PyTime_FromMillisecondsObject(_PyTime_t *t, + PyObject *obj, + _PyTime_round_t round); + +/* Convert a timestamp to a number of seconds as a C double. */ +PyAPI_FUNC(double) _PyTime_AsSecondsDouble(_PyTime_t t); + +/* Convert timestamp to a number of milliseconds (10^-3 seconds). */ +PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t, + _PyTime_round_t round); + +/* Convert timestamp to a number of microseconds (10^-6 seconds). */ +PyAPI_FUNC(_PyTime_t) _PyTime_AsMicroseconds(_PyTime_t t, + _PyTime_round_t round); + +/* Convert timestamp to a number of nanoseconds (10^-9 seconds) as a Python int + object. */ +PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t); + +/* Create a timestamp from a timeval structure. + Raise an exception and return -1 on overflow, return 0 on success. */ +PyAPI_FUNC(int) _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv); + +/* Convert a timestamp to a timeval structure (microsecond resolution). + tv_usec is always positive. + Raise an exception and return -1 if the conversion overflowed, + return 0 on success. */ +PyAPI_FUNC(int) _PyTime_AsTimeval(_PyTime_t t, + struct timeval *tv, + _PyTime_round_t round); + +/* Similar to _PyTime_AsTimeval(), but don't raise an exception on error. */ +PyAPI_FUNC(int) _PyTime_AsTimeval_noraise(_PyTime_t t, + struct timeval *tv, + _PyTime_round_t round); + +/* Convert a timestamp to a number of seconds (secs) and microseconds (us). + us is always positive. This function is similar to _PyTime_AsTimeval() + except that secs is always a time_t type, whereas the timeval structure + uses a C long for tv_sec on Windows. + Raise an exception and return -1 if the conversion overflowed, + return 0 on success. */ +PyAPI_FUNC(int) _PyTime_AsTimevalTime_t( + _PyTime_t t, + time_t *secs, + int *us, + _PyTime_round_t round); + +#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE) +/* Create a timestamp from a timespec structure. + Raise an exception and return -1 on overflow, return 0 on success. */ +PyAPI_FUNC(int) _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts); + +/* Convert a timestamp to a timespec structure (nanosecond resolution). + tv_nsec is always positive. + Raise an exception and return -1 on error, return 0 on success. */ +PyAPI_FUNC(int) _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts); +#endif + +/* Compute ticks * mul / div. + The caller must ensure that ((div - 1) * mul) cannot overflow. */ +PyAPI_FUNC(_PyTime_t) _PyTime_MulDiv(_PyTime_t ticks, + _PyTime_t mul, + _PyTime_t div); + +/* Get the current time from the system clock. + + The function cannot fail. _PyTime_Init() ensures that the system clock + works. */ +PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void); + +/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards. + The clock is not affected by system clock updates. The reference point of + the returned value is undefined, so that only the difference between the + results of consecutive calls is valid. + + The function cannot fail. _PyTime_Init() ensures that a monotonic clock + is available and works. */ +PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void); + + +/* Structure used by time.get_clock_info() */ +typedef struct { + const char *implementation; + int monotonic; + int adjustable; + double resolution; +} _Py_clock_info_t; + +/* Get the current time from the system clock. + * Fill clock information if info is not NULL. + * Raise an exception and return -1 on error, return 0 on success. + */ +PyAPI_FUNC(int) _PyTime_GetSystemClockWithInfo( + _PyTime_t *t, + _Py_clock_info_t *info); + +/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards. + The clock is not affected by system clock updates. The reference point of + the returned value is undefined, so that only the difference between the + results of consecutive calls is valid. + + Fill info (if set) with information of the function used to get the time. + + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) _PyTime_GetMonotonicClockWithInfo( + _PyTime_t *t, + _Py_clock_info_t *info); + + +/* Initialize time. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) _PyTime_Init(void); + +/* Converts a timestamp to the Gregorian time, using the local time zone. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm); + +/* Converts a timestamp to the Gregorian time, assuming UTC. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm); + +/* Get the performance counter: clock with the highest available resolution to + measure a short duration. + + The function cannot fail. _PyTime_Init() ensures that the system clock + works. */ +PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void); + +/* Get the performance counter: clock with the highest available resolution to + measure a short duration. + + Fill info (if set) with information of the function used to get the time. + + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) _PyTime_GetPerfCounterWithInfo( + _PyTime_t *t, + _Py_clock_info_t *info); + +#ifdef __cplusplus +} +#endif + +#endif /* Py_PYTIME_H */ +#endif /* Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/rangeobject.h b/scripts/build-windows/py39-libs/include/rangeobject.h new file mode 100644 index 000000000..d2105d026 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/rangeobject.h @@ -0,0 +1,27 @@ + +/* Range object interface */ + +#ifndef Py_RANGEOBJECT_H +#define Py_RANGEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* +A range object represents an integer range. This is an immutable object; +a range cannot change its value after creation. + +Range objects behave like the corresponding tuple objects except that +they are represented by a start, stop, and step datamembers. +*/ + +PyAPI_DATA(PyTypeObject) PyRange_Type; +PyAPI_DATA(PyTypeObject) PyRangeIter_Type; +PyAPI_DATA(PyTypeObject) PyLongRangeIter_Type; + +#define PyRange_Check(op) Py_IS_TYPE(op, &PyRange_Type) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_RANGEOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/setobject.h b/scripts/build-windows/py39-libs/include/setobject.h new file mode 100644 index 000000000..24ae8f4d6 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/setobject.h @@ -0,0 +1,107 @@ +/* Set object interface */ + +#ifndef Py_SETOBJECT_H +#define Py_SETOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_LIMITED_API + +/* There are three kinds of entries in the table: + +1. Unused: key == NULL and hash == 0 +2. Dummy: key == dummy and hash == -1 +3. Active: key != NULL and key != dummy and hash != -1 + +The hash field of Unused slots is always zero. + +The hash field of Dummy slots are set to -1 +meaning that dummy entries can be detected by +either entry->key==dummy or by entry->hash==-1. +*/ + +#define PySet_MINSIZE 8 + +typedef struct { + PyObject *key; + Py_hash_t hash; /* Cached hash code of the key */ +} setentry; + +/* The SetObject data structure is shared by set and frozenset objects. + +Invariant for sets: + - hash is -1 + +Invariants for frozensets: + - data is immutable. + - hash is the hash of the frozenset or -1 if not computed yet. + +*/ + +typedef struct { + PyObject_HEAD + + Py_ssize_t fill; /* Number active and dummy entries*/ + Py_ssize_t used; /* Number active entries */ + + /* The table contains mask + 1 slots, and that's a power of 2. + * We store the mask instead of the size because the mask is more + * frequently needed. + */ + Py_ssize_t mask; + + /* The table points to a fixed-size smalltable for small tables + * or to additional malloc'ed memory for bigger tables. + * The table pointer is never NULL which saves us from repeated + * runtime null-tests. + */ + setentry *table; + Py_hash_t hash; /* Only used by frozenset objects */ + Py_ssize_t finger; /* Search finger for pop() */ + + setentry smalltable[PySet_MINSIZE]; + PyObject *weakreflist; /* List of weak references */ +} PySetObject; + +#define PySet_GET_SIZE(so) (assert(PyAnySet_Check(so)),(((PySetObject *)(so))->used)) + +PyAPI_DATA(PyObject *) _PySet_Dummy; + +PyAPI_FUNC(int) _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash); +PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable); + +#endif /* Section excluded by Py_LIMITED_API */ + +PyAPI_DATA(PyTypeObject) PySet_Type; +PyAPI_DATA(PyTypeObject) PyFrozenSet_Type; +PyAPI_DATA(PyTypeObject) PySetIter_Type; + +PyAPI_FUNC(PyObject *) PySet_New(PyObject *); +PyAPI_FUNC(PyObject *) PyFrozenSet_New(PyObject *); + +PyAPI_FUNC(int) PySet_Add(PyObject *set, PyObject *key); +PyAPI_FUNC(int) PySet_Clear(PyObject *set); +PyAPI_FUNC(int) PySet_Contains(PyObject *anyset, PyObject *key); +PyAPI_FUNC(int) PySet_Discard(PyObject *set, PyObject *key); +PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set); +PyAPI_FUNC(Py_ssize_t) PySet_Size(PyObject *anyset); + +#define PyFrozenSet_CheckExact(ob) Py_IS_TYPE(ob, &PyFrozenSet_Type) +#define PyAnySet_CheckExact(ob) \ + (Py_IS_TYPE(ob, &PySet_Type) || Py_IS_TYPE(ob, &PyFrozenSet_Type)) +#define PyAnySet_Check(ob) \ + (Py_IS_TYPE(ob, &PySet_Type) || Py_IS_TYPE(ob, &PyFrozenSet_Type) || \ + PyType_IsSubtype(Py_TYPE(ob), &PySet_Type) || \ + PyType_IsSubtype(Py_TYPE(ob), &PyFrozenSet_Type)) +#define PySet_Check(ob) \ + (Py_IS_TYPE(ob, &PySet_Type) || \ + PyType_IsSubtype(Py_TYPE(ob), &PySet_Type)) +#define PyFrozenSet_Check(ob) \ + (Py_IS_TYPE(ob, &PyFrozenSet_Type) || \ + PyType_IsSubtype(Py_TYPE(ob), &PyFrozenSet_Type)) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_SETOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/sliceobject.h b/scripts/build-windows/py39-libs/include/sliceobject.h new file mode 100644 index 000000000..c41506046 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/sliceobject.h @@ -0,0 +1,65 @@ +#ifndef Py_SLICEOBJECT_H +#define Py_SLICEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* The unique ellipsis object "..." */ + +PyAPI_DATA(PyObject) _Py_EllipsisObject; /* Don't use this directly */ + +#define Py_Ellipsis (&_Py_EllipsisObject) + +/* Slice object interface */ + +/* + +A slice object containing start, stop, and step data members (the +names are from range). After much talk with Guido, it was decided to +let these be any arbitrary python type. Py_None stands for omitted values. +*/ +#ifndef Py_LIMITED_API +typedef struct { + PyObject_HEAD + PyObject *start, *stop, *step; /* not NULL */ +} PySliceObject; +#endif + +PyAPI_DATA(PyTypeObject) PySlice_Type; +PyAPI_DATA(PyTypeObject) PyEllipsis_Type; + +#define PySlice_Check(op) Py_IS_TYPE(op, &PySlice_Type) + +PyAPI_FUNC(PyObject *) PySlice_New(PyObject* start, PyObject* stop, + PyObject* step); +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) _PySlice_FromIndices(Py_ssize_t start, Py_ssize_t stop); +PyAPI_FUNC(int) _PySlice_GetLongIndices(PySliceObject *self, PyObject *length, + PyObject **start_ptr, PyObject **stop_ptr, + PyObject **step_ptr); +#endif +PyAPI_FUNC(int) PySlice_GetIndices(PyObject *r, Py_ssize_t length, + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step); +Py_DEPRECATED(3.7) +PyAPI_FUNC(int) PySlice_GetIndicesEx(PyObject *r, Py_ssize_t length, + Py_ssize_t *start, Py_ssize_t *stop, + Py_ssize_t *step, + Py_ssize_t *slicelength); + +#if !defined(Py_LIMITED_API) || (Py_LIMITED_API+0 >= 0x03050400 && Py_LIMITED_API+0 < 0x03060000) || Py_LIMITED_API+0 >= 0x03060100 +#define PySlice_GetIndicesEx(slice, length, start, stop, step, slicelen) ( \ + PySlice_Unpack((slice), (start), (stop), (step)) < 0 ? \ + ((*(slicelen) = 0), -1) : \ + ((*(slicelen) = PySlice_AdjustIndices((length), (start), (stop), *(step))), \ + 0)) +PyAPI_FUNC(int) PySlice_Unpack(PyObject *slice, + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step); +PyAPI_FUNC(Py_ssize_t) PySlice_AdjustIndices(Py_ssize_t length, + Py_ssize_t *start, Py_ssize_t *stop, + Py_ssize_t step); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_SLICEOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/structmember.h b/scripts/build-windows/py39-libs/include/structmember.h new file mode 100644 index 000000000..af01afe72 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/structmember.h @@ -0,0 +1,74 @@ +#ifndef Py_STRUCTMEMBER_H +#define Py_STRUCTMEMBER_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Interface to map C struct members to Python object attributes */ + +#include /* For offsetof */ + +/* An array of PyMemberDef structures defines the name, type and offset + of selected members of a C structure. These can be read by + PyMember_GetOne() and set by PyMember_SetOne() (except if their READONLY + flag is set). The array must be terminated with an entry whose name + pointer is NULL. */ + +typedef struct PyMemberDef { + const char *name; + int type; + Py_ssize_t offset; + int flags; + const char *doc; +} PyMemberDef; + +/* Types */ +#define T_SHORT 0 +#define T_INT 1 +#define T_LONG 2 +#define T_FLOAT 3 +#define T_DOUBLE 4 +#define T_STRING 5 +#define T_OBJECT 6 +/* XXX the ordering here is weird for binary compatibility */ +#define T_CHAR 7 /* 1-character string */ +#define T_BYTE 8 /* 8-bit signed int */ +/* unsigned variants: */ +#define T_UBYTE 9 +#define T_USHORT 10 +#define T_UINT 11 +#define T_ULONG 12 + +/* Added by Jack: strings contained in the structure */ +#define T_STRING_INPLACE 13 + +/* Added by Lillo: bools contained in the structure (assumed char) */ +#define T_BOOL 14 + +#define T_OBJECT_EX 16 /* Like T_OBJECT, but raises AttributeError + when the value is NULL, instead of + converting to None. */ +#define T_LONGLONG 17 +#define T_ULONGLONG 18 + +#define T_PYSSIZET 19 /* Py_ssize_t */ +#define T_NONE 20 /* Value is always None */ + + +/* Flags */ +#define READONLY 1 +#define READ_RESTRICTED 2 +#define PY_WRITE_RESTRICTED 4 +#define RESTRICTED (READ_RESTRICTED | PY_WRITE_RESTRICTED) + + +/* Current API, use this */ +PyAPI_FUNC(PyObject *) PyMember_GetOne(const char *, struct PyMemberDef *); +PyAPI_FUNC(int) PyMember_SetOne(char *, struct PyMemberDef *, PyObject *); + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_STRUCTMEMBER_H */ diff --git a/scripts/build-windows/py39-libs/include/structseq.h b/scripts/build-windows/py39-libs/include/structseq.h new file mode 100644 index 000000000..e67bfda7e --- /dev/null +++ b/scripts/build-windows/py39-libs/include/structseq.h @@ -0,0 +1,49 @@ + +/* Named tuple object interface */ + +#ifndef Py_STRUCTSEQ_H +#define Py_STRUCTSEQ_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct PyStructSequence_Field { + const char *name; + const char *doc; +} PyStructSequence_Field; + +typedef struct PyStructSequence_Desc { + const char *name; + const char *doc; + struct PyStructSequence_Field *fields; + int n_in_sequence; +} PyStructSequence_Desc; + +extern const char * const PyStructSequence_UnnamedField; + +#ifndef Py_LIMITED_API +PyAPI_FUNC(void) PyStructSequence_InitType(PyTypeObject *type, + PyStructSequence_Desc *desc); +PyAPI_FUNC(int) PyStructSequence_InitType2(PyTypeObject *type, + PyStructSequence_Desc *desc); +#endif +PyAPI_FUNC(PyTypeObject*) PyStructSequence_NewType(PyStructSequence_Desc *desc); + +PyAPI_FUNC(PyObject *) PyStructSequence_New(PyTypeObject* type); + +#ifndef Py_LIMITED_API +typedef PyTupleObject PyStructSequence; + +/* Macro, *only* to be used to fill in brand new objects */ +#define PyStructSequence_SET_ITEM(op, i, v) PyTuple_SET_ITEM(op, i, v) + +#define PyStructSequence_GET_ITEM(op, i) PyTuple_GET_ITEM(op, i) +#endif + +PyAPI_FUNC(void) PyStructSequence_SetItem(PyObject*, Py_ssize_t, PyObject*); +PyAPI_FUNC(PyObject*) PyStructSequence_GetItem(PyObject*, Py_ssize_t); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_STRUCTSEQ_H */ diff --git a/scripts/build-windows/py39-libs/include/symtable.h b/scripts/build-windows/py39-libs/include/symtable.h new file mode 100644 index 000000000..5f490df2c --- /dev/null +++ b/scripts/build-windows/py39-libs/include/symtable.h @@ -0,0 +1,123 @@ +#ifndef Py_LIMITED_API +#ifndef Py_SYMTABLE_H +#define Py_SYMTABLE_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "Python-ast.h" /* mod_ty */ + +/* XXX(ncoghlan): This is a weird mix of public names and interpreter internal + * names. + */ + +typedef enum _block_type { FunctionBlock, ClassBlock, ModuleBlock } + _Py_block_ty; + +struct _symtable_entry; + +struct symtable { + PyObject *st_filename; /* name of file being compiled, + decoded from the filesystem encoding */ + struct _symtable_entry *st_cur; /* current symbol table entry */ + struct _symtable_entry *st_top; /* symbol table entry for module */ + PyObject *st_blocks; /* dict: map AST node addresses + * to symbol table entries */ + PyObject *st_stack; /* list: stack of namespace info */ + PyObject *st_global; /* borrowed ref to st_top->ste_symbols */ + int st_nblocks; /* number of blocks used. kept for + consistency with the corresponding + compiler structure */ + PyObject *st_private; /* name of current class or NULL */ + PyFutureFeatures *st_future; /* module's future features that affect + the symbol table */ + int recursion_depth; /* current recursion depth */ + int recursion_limit; /* recursion limit */ +}; + +typedef struct _symtable_entry { + PyObject_HEAD + PyObject *ste_id; /* int: key in ste_table->st_blocks */ + PyObject *ste_symbols; /* dict: variable names to flags */ + PyObject *ste_name; /* string: name of current block */ + PyObject *ste_varnames; /* list of function parameters */ + PyObject *ste_children; /* list of child blocks */ + PyObject *ste_directives;/* locations of global and nonlocal statements */ + _Py_block_ty ste_type; /* module, class, or function */ + int ste_nested; /* true if block is nested */ + unsigned ste_free : 1; /* true if block has free variables */ + unsigned ste_child_free : 1; /* true if a child block has free vars, + including free refs to globals */ + unsigned ste_generator : 1; /* true if namespace is a generator */ + unsigned ste_coroutine : 1; /* true if namespace is a coroutine */ + unsigned ste_comprehension : 1; /* true if namespace is a list comprehension */ + unsigned ste_varargs : 1; /* true if block has varargs */ + unsigned ste_varkeywords : 1; /* true if block has varkeywords */ + unsigned ste_returns_value : 1; /* true if namespace uses return with + an argument */ + unsigned ste_needs_class_closure : 1; /* for class scopes, true if a + closure over __class__ + should be created */ + unsigned ste_comp_iter_target : 1; /* true if visiting comprehension target */ + int ste_comp_iter_expr; /* non-zero if visiting a comprehension range expression */ + int ste_lineno; /* first line of block */ + int ste_col_offset; /* offset of first line of block */ + int ste_opt_lineno; /* lineno of last exec or import * */ + int ste_opt_col_offset; /* offset of last exec or import * */ + struct symtable *ste_table; +} PySTEntryObject; + +PyAPI_DATA(PyTypeObject) PySTEntry_Type; + +#define PySTEntry_Check(op) Py_IS_TYPE(op, &PySTEntry_Type) + +PyAPI_FUNC(int) PyST_GetScope(PySTEntryObject *, PyObject *); + +PyAPI_FUNC(struct symtable *) PySymtable_Build( + mod_ty mod, + const char *filename, /* decoded from the filesystem encoding */ + PyFutureFeatures *future); +PyAPI_FUNC(struct symtable *) PySymtable_BuildObject( + mod_ty mod, + PyObject *filename, + PyFutureFeatures *future); +PyAPI_FUNC(PySTEntryObject *) PySymtable_Lookup(struct symtable *, void *); + +PyAPI_FUNC(void) PySymtable_Free(struct symtable *); + +/* Flags for def-use information */ + +#define DEF_GLOBAL 1 /* global stmt */ +#define DEF_LOCAL 2 /* assignment in code block */ +#define DEF_PARAM 2<<1 /* formal parameter */ +#define DEF_NONLOCAL 2<<2 /* nonlocal stmt */ +#define USE 2<<3 /* name is used */ +#define DEF_FREE 2<<4 /* name used but not defined in nested block */ +#define DEF_FREE_CLASS 2<<5 /* free variable from class's method */ +#define DEF_IMPORT 2<<6 /* assignment occurred via import */ +#define DEF_ANNOT 2<<7 /* this name is annotated */ +#define DEF_COMP_ITER 2<<8 /* this name is a comprehension iteration variable */ + +#define DEF_BOUND (DEF_LOCAL | DEF_PARAM | DEF_IMPORT) + +/* GLOBAL_EXPLICIT and GLOBAL_IMPLICIT are used internally by the symbol + table. GLOBAL is returned from PyST_GetScope() for either of them. + It is stored in ste_symbols at bits 12-15. +*/ +#define SCOPE_OFFSET 11 +#define SCOPE_MASK (DEF_GLOBAL | DEF_LOCAL | DEF_PARAM | DEF_NONLOCAL) + +#define LOCAL 1 +#define GLOBAL_EXPLICIT 2 +#define GLOBAL_IMPLICIT 3 +#define FREE 4 +#define CELL 5 + +#define GENERATOR 1 +#define GENERATOR_EXPRESSION 2 + +#ifdef __cplusplus +} +#endif +#endif /* !Py_SYMTABLE_H */ +#endif /* !Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/sysmodule.h b/scripts/build-windows/py39-libs/include/sysmodule.h new file mode 100644 index 000000000..388744258 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/sysmodule.h @@ -0,0 +1,41 @@ + +/* System module interface */ + +#ifndef Py_SYSMODULE_H +#define Py_SYSMODULE_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(PyObject *) PySys_GetObject(const char *); +PyAPI_FUNC(int) PySys_SetObject(const char *, PyObject *); + +PyAPI_FUNC(void) PySys_SetArgv(int, wchar_t **); +PyAPI_FUNC(void) PySys_SetArgvEx(int, wchar_t **, int); +PyAPI_FUNC(void) PySys_SetPath(const wchar_t *); + +PyAPI_FUNC(void) PySys_WriteStdout(const char *format, ...) + Py_GCC_ATTRIBUTE((format(printf, 1, 2))); +PyAPI_FUNC(void) PySys_WriteStderr(const char *format, ...) + Py_GCC_ATTRIBUTE((format(printf, 1, 2))); +PyAPI_FUNC(void) PySys_FormatStdout(const char *format, ...); +PyAPI_FUNC(void) PySys_FormatStderr(const char *format, ...); + +PyAPI_FUNC(void) PySys_ResetWarnOptions(void); +PyAPI_FUNC(void) PySys_AddWarnOption(const wchar_t *); +PyAPI_FUNC(void) PySys_AddWarnOptionUnicode(PyObject *); +PyAPI_FUNC(int) PySys_HasWarnOptions(void); + +PyAPI_FUNC(void) PySys_AddXOption(const wchar_t *); +PyAPI_FUNC(PyObject *) PySys_GetXOptions(void); + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_SYSMODULE_H +# include "cpython/sysmodule.h" +# undef Py_CPYTHON_SYSMODULE_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_SYSMODULE_H */ diff --git a/scripts/build-windows/py39-libs/include/token.h b/scripts/build-windows/py39-libs/include/token.h new file mode 100644 index 000000000..80c3e2517 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/token.h @@ -0,0 +1,96 @@ +/* Auto-generated by Tools/scripts/generate_token.py */ + +/* Token types */ +#ifndef Py_LIMITED_API +#ifndef Py_TOKEN_H +#define Py_TOKEN_H +#ifdef __cplusplus +extern "C" { +#endif + +#undef TILDE /* Prevent clash of our definition with system macro. Ex AIX, ioctl.h */ + +#define ENDMARKER 0 +#define NAME 1 +#define NUMBER 2 +#define STRING 3 +#define NEWLINE 4 +#define INDENT 5 +#define DEDENT 6 +#define LPAR 7 +#define RPAR 8 +#define LSQB 9 +#define RSQB 10 +#define COLON 11 +#define COMMA 12 +#define SEMI 13 +#define PLUS 14 +#define MINUS 15 +#define STAR 16 +#define SLASH 17 +#define VBAR 18 +#define AMPER 19 +#define LESS 20 +#define GREATER 21 +#define EQUAL 22 +#define DOT 23 +#define PERCENT 24 +#define LBRACE 25 +#define RBRACE 26 +#define EQEQUAL 27 +#define NOTEQUAL 28 +#define LESSEQUAL 29 +#define GREATEREQUAL 30 +#define TILDE 31 +#define CIRCUMFLEX 32 +#define LEFTSHIFT 33 +#define RIGHTSHIFT 34 +#define DOUBLESTAR 35 +#define PLUSEQUAL 36 +#define MINEQUAL 37 +#define STAREQUAL 38 +#define SLASHEQUAL 39 +#define PERCENTEQUAL 40 +#define AMPEREQUAL 41 +#define VBAREQUAL 42 +#define CIRCUMFLEXEQUAL 43 +#define LEFTSHIFTEQUAL 44 +#define RIGHTSHIFTEQUAL 45 +#define DOUBLESTAREQUAL 46 +#define DOUBLESLASH 47 +#define DOUBLESLASHEQUAL 48 +#define AT 49 +#define ATEQUAL 50 +#define RARROW 51 +#define ELLIPSIS 52 +#define COLONEQUAL 53 +#define OP 54 +#define AWAIT 55 +#define ASYNC 56 +#define TYPE_IGNORE 57 +#define TYPE_COMMENT 58 +#define ERRORTOKEN 59 +#define N_TOKENS 63 +#define NT_OFFSET 256 + +/* Special definitions for cooperation with parser */ + +#define ISTERMINAL(x) ((x) < NT_OFFSET) +#define ISNONTERMINAL(x) ((x) >= NT_OFFSET) +#define ISEOF(x) ((x) == ENDMARKER) +#define ISWHITESPACE(x) ((x) == ENDMARKER || \ + (x) == NEWLINE || \ + (x) == INDENT || \ + (x) == DEDENT) + + +PyAPI_DATA(const char * const) _PyParser_TokenNames[]; /* Token names */ +PyAPI_FUNC(int) PyToken_OneChar(int); +PyAPI_FUNC(int) PyToken_TwoChars(int, int); +PyAPI_FUNC(int) PyToken_ThreeChars(int, int, int); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_TOKEN_H */ +#endif /* Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/traceback.h b/scripts/build-windows/py39-libs/include/traceback.h new file mode 100644 index 000000000..b4466f517 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/traceback.h @@ -0,0 +1,26 @@ +#ifndef Py_TRACEBACK_H +#define Py_TRACEBACK_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Traceback interface */ + +PyAPI_FUNC(int) PyTraceBack_Here(PyFrameObject *); +PyAPI_FUNC(int) PyTraceBack_Print(PyObject *, PyObject *); + +/* Reveal traceback type so we can typecheck traceback objects */ +PyAPI_DATA(PyTypeObject) PyTraceBack_Type; +#define PyTraceBack_Check(v) Py_IS_TYPE(v, &PyTraceBack_Type) + + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_TRACEBACK_H +# include "cpython/traceback.h" +# undef Py_CPYTHON_TRACEBACK_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_TRACEBACK_H */ diff --git a/scripts/build-windows/py39-libs/include/tracemalloc.h b/scripts/build-windows/py39-libs/include/tracemalloc.h new file mode 100644 index 000000000..05b4cc16f --- /dev/null +++ b/scripts/build-windows/py39-libs/include/tracemalloc.h @@ -0,0 +1,38 @@ +#ifndef Py_TRACEMALLOC_H +#define Py_TRACEMALLOC_H + +#ifndef Py_LIMITED_API +/* Track an allocated memory block in the tracemalloc module. + Return 0 on success, return -1 on error (failed to allocate memory to store + the trace). + + Return -2 if tracemalloc is disabled. + + If memory block is already tracked, update the existing trace. */ +PyAPI_FUNC(int) PyTraceMalloc_Track( + unsigned int domain, + uintptr_t ptr, + size_t size); + +/* Untrack an allocated memory block in the tracemalloc module. + Do nothing if the block was not tracked. + + Return -2 if tracemalloc is disabled, otherwise return 0. */ +PyAPI_FUNC(int) PyTraceMalloc_Untrack( + unsigned int domain, + uintptr_t ptr); + +/* Get the traceback where a memory block was allocated. + + Return a tuple of (filename: str, lineno: int) tuples. + + Return None if the tracemalloc module is disabled or if the memory block + is not tracked by tracemalloc. + + Raise an exception and return NULL on error. */ +PyAPI_FUNC(PyObject*) _PyTraceMalloc_GetTraceback( + unsigned int domain, + uintptr_t ptr); +#endif + +#endif /* !Py_TRACEMALLOC_H */ diff --git a/scripts/build-windows/py39-libs/include/tupleobject.h b/scripts/build-windows/py39-libs/include/tupleobject.h new file mode 100644 index 000000000..19f9997e7 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/tupleobject.h @@ -0,0 +1,46 @@ +/* Tuple object interface */ + +#ifndef Py_TUPLEOBJECT_H +#define Py_TUPLEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* +Another generally useful object type is a tuple of object pointers. +For Python, this is an immutable type. C code can change the tuple items +(but not their number), and even use tuples as general-purpose arrays of +object references, but in general only brand new tuples should be mutated, +not ones that might already have been exposed to Python code. + +*** WARNING *** PyTuple_SetItem does not increment the new item's reference +count, but does decrement the reference count of the item it replaces, +if not nil. It does *decrement* the reference count if it is *not* +inserted in the tuple. Similarly, PyTuple_GetItem does not increment the +returned item's reference count. +*/ + +PyAPI_DATA(PyTypeObject) PyTuple_Type; +PyAPI_DATA(PyTypeObject) PyTupleIter_Type; + +#define PyTuple_Check(op) \ + PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TUPLE_SUBCLASS) +#define PyTuple_CheckExact(op) Py_IS_TYPE(op, &PyTuple_Type) + +PyAPI_FUNC(PyObject *) PyTuple_New(Py_ssize_t size); +PyAPI_FUNC(Py_ssize_t) PyTuple_Size(PyObject *); +PyAPI_FUNC(PyObject *) PyTuple_GetItem(PyObject *, Py_ssize_t); +PyAPI_FUNC(int) PyTuple_SetItem(PyObject *, Py_ssize_t, PyObject *); +PyAPI_FUNC(PyObject *) PyTuple_GetSlice(PyObject *, Py_ssize_t, Py_ssize_t); +PyAPI_FUNC(PyObject *) PyTuple_Pack(Py_ssize_t, ...); + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_TUPLEOBJECT_H +# include "cpython/tupleobject.h" +# undef Py_CPYTHON_TUPLEOBJECT_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_TUPLEOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/typeslots.h b/scripts/build-windows/py39-libs/include/typeslots.h new file mode 100644 index 000000000..29111f94d --- /dev/null +++ b/scripts/build-windows/py39-libs/include/typeslots.h @@ -0,0 +1,90 @@ +/* Do not renumber the file; these numbers are part of the stable ABI. */ +#if defined(Py_LIMITED_API) +/* Disabled, see #10181 */ +#undef Py_bf_getbuffer +#undef Py_bf_releasebuffer +#else +#define Py_bf_getbuffer 1 +#define Py_bf_releasebuffer 2 +#endif +#define Py_mp_ass_subscript 3 +#define Py_mp_length 4 +#define Py_mp_subscript 5 +#define Py_nb_absolute 6 +#define Py_nb_add 7 +#define Py_nb_and 8 +#define Py_nb_bool 9 +#define Py_nb_divmod 10 +#define Py_nb_float 11 +#define Py_nb_floor_divide 12 +#define Py_nb_index 13 +#define Py_nb_inplace_add 14 +#define Py_nb_inplace_and 15 +#define Py_nb_inplace_floor_divide 16 +#define Py_nb_inplace_lshift 17 +#define Py_nb_inplace_multiply 18 +#define Py_nb_inplace_or 19 +#define Py_nb_inplace_power 20 +#define Py_nb_inplace_remainder 21 +#define Py_nb_inplace_rshift 22 +#define Py_nb_inplace_subtract 23 +#define Py_nb_inplace_true_divide 24 +#define Py_nb_inplace_xor 25 +#define Py_nb_int 26 +#define Py_nb_invert 27 +#define Py_nb_lshift 28 +#define Py_nb_multiply 29 +#define Py_nb_negative 30 +#define Py_nb_or 31 +#define Py_nb_positive 32 +#define Py_nb_power 33 +#define Py_nb_remainder 34 +#define Py_nb_rshift 35 +#define Py_nb_subtract 36 +#define Py_nb_true_divide 37 +#define Py_nb_xor 38 +#define Py_sq_ass_item 39 +#define Py_sq_concat 40 +#define Py_sq_contains 41 +#define Py_sq_inplace_concat 42 +#define Py_sq_inplace_repeat 43 +#define Py_sq_item 44 +#define Py_sq_length 45 +#define Py_sq_repeat 46 +#define Py_tp_alloc 47 +#define Py_tp_base 48 +#define Py_tp_bases 49 +#define Py_tp_call 50 +#define Py_tp_clear 51 +#define Py_tp_dealloc 52 +#define Py_tp_del 53 +#define Py_tp_descr_get 54 +#define Py_tp_descr_set 55 +#define Py_tp_doc 56 +#define Py_tp_getattr 57 +#define Py_tp_getattro 58 +#define Py_tp_hash 59 +#define Py_tp_init 60 +#define Py_tp_is_gc 61 +#define Py_tp_iter 62 +#define Py_tp_iternext 63 +#define Py_tp_methods 64 +#define Py_tp_new 65 +#define Py_tp_repr 66 +#define Py_tp_richcompare 67 +#define Py_tp_setattr 68 +#define Py_tp_setattro 69 +#define Py_tp_str 70 +#define Py_tp_traverse 71 +#define Py_tp_members 72 +#define Py_tp_getset 73 +#define Py_tp_free 74 +#define Py_nb_matrix_multiply 75 +#define Py_nb_inplace_matrix_multiply 76 +#define Py_am_await 77 +#define Py_am_aiter 78 +#define Py_am_anext 79 +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 +/* New in 3.5 */ +#define Py_tp_finalize 80 +#endif diff --git a/scripts/build-windows/py39-libs/include/ucnhash.h b/scripts/build-windows/py39-libs/include/ucnhash.h new file mode 100644 index 000000000..6d1a29fbe --- /dev/null +++ b/scripts/build-windows/py39-libs/include/ucnhash.h @@ -0,0 +1,36 @@ +/* Unicode name database interface */ +#ifndef Py_LIMITED_API +#ifndef Py_UCNHASH_H +#define Py_UCNHASH_H +#ifdef __cplusplus +extern "C" { +#endif + +/* revised ucnhash CAPI interface (exported through a "wrapper") */ + +#define PyUnicodeData_CAPSULE_NAME "unicodedata.ucnhash_CAPI" + +typedef struct { + + /* Size of this struct */ + int size; + + /* Get name for a given character code. Returns non-zero if + success, zero if not. Does not set Python exceptions. + If self is NULL, data come from the default version of the database. + If it is not NULL, it should be a unicodedata.ucd_X_Y_Z object */ + int (*getname)(PyObject *self, Py_UCS4 code, char* buffer, int buflen, + int with_alias_and_seq); + + /* Get character code for a given name. Same error handling + as for getname. */ + int (*getcode)(PyObject *self, const char* name, int namelen, Py_UCS4* code, + int with_named_seq); + +} _PyUnicode_Name_CAPI; + +#ifdef __cplusplus +} +#endif +#endif /* !Py_UCNHASH_H */ +#endif /* !Py_LIMITED_API */ diff --git a/scripts/build-windows/py39-libs/include/unicodeobject.h b/scripts/build-windows/py39-libs/include/unicodeobject.h new file mode 100644 index 000000000..4213945b2 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/unicodeobject.h @@ -0,0 +1,1033 @@ +#ifndef Py_UNICODEOBJECT_H +#define Py_UNICODEOBJECT_H + +#include + +/* + +Unicode implementation based on original code by Fredrik Lundh, +modified by Marc-Andre Lemburg (mal@lemburg.com) according to the +Unicode Integration Proposal. (See +http://www.egenix.com/files/python/unicode-proposal.txt). + +Copyright (c) Corporation for National Research Initiatives. + + + Original header: + -------------------------------------------------------------------- + + * Yet another Unicode string type for Python. This type supports the + * 16-bit Basic Multilingual Plane (BMP) only. + * + * Written by Fredrik Lundh, January 1999. + * + * Copyright (c) 1999 by Secret Labs AB. + * Copyright (c) 1999 by Fredrik Lundh. + * + * fredrik@pythonware.com + * http://www.pythonware.com + * + * -------------------------------------------------------------------- + * This Unicode String Type is + * + * Copyright (c) 1999 by Secret Labs AB + * Copyright (c) 1999 by Fredrik Lundh + * + * By obtaining, using, and/or copying this software and/or its + * associated documentation, you agree that you have read, understood, + * and will comply with the following terms and conditions: + * + * Permission to use, copy, modify, and distribute this software and its + * associated documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appears in all + * copies, and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of Secret Labs + * AB or the author not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. + * + * SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * -------------------------------------------------------------------- */ + +#include + +/* === Internal API ======================================================= */ + +/* --- Internal Unicode Format -------------------------------------------- */ + +/* Python 3.x requires unicode */ +#define Py_USING_UNICODE + +#ifndef SIZEOF_WCHAR_T +#error Must define SIZEOF_WCHAR_T +#endif + +#define Py_UNICODE_SIZE SIZEOF_WCHAR_T + +/* If wchar_t can be used for UCS-4 storage, set Py_UNICODE_WIDE. + Otherwise, Unicode strings are stored as UCS-2 (with limited support + for UTF-16) */ + +#if Py_UNICODE_SIZE >= 4 +#define Py_UNICODE_WIDE +#endif + +/* Set these flags if the platform has "wchar.h" and the + wchar_t type is a 16-bit unsigned type */ +/* #define HAVE_WCHAR_H */ +/* #define HAVE_USABLE_WCHAR_T */ + +/* If the compiler provides a wchar_t type we try to support it + through the interface functions PyUnicode_FromWideChar(), + PyUnicode_AsWideChar() and PyUnicode_AsWideCharString(). */ + +#ifdef HAVE_USABLE_WCHAR_T +# ifndef HAVE_WCHAR_H +# define HAVE_WCHAR_H +# endif +#endif + +#ifdef HAVE_WCHAR_H +# include +#endif + +/* Py_UCS4 and Py_UCS2 are typedefs for the respective + unicode representations. */ +typedef uint32_t Py_UCS4; +typedef uint16_t Py_UCS2; +typedef uint8_t Py_UCS1; + +#ifdef __cplusplus +extern "C" { +#endif + + +PyAPI_DATA(PyTypeObject) PyUnicode_Type; +PyAPI_DATA(PyTypeObject) PyUnicodeIter_Type; + +#define PyUnicode_Check(op) \ + PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_UNICODE_SUBCLASS) +#define PyUnicode_CheckExact(op) Py_IS_TYPE(op, &PyUnicode_Type) + +/* --- Constants ---------------------------------------------------------- */ + +/* This Unicode character will be used as replacement character during + decoding if the errors argument is set to "replace". Note: the + Unicode character U+FFFD is the official REPLACEMENT CHARACTER in + Unicode 3.0. */ + +#define Py_UNICODE_REPLACEMENT_CHARACTER ((Py_UCS4) 0xFFFD) + +/* === Public API ========================================================= */ + +/* Similar to PyUnicode_FromUnicode(), but u points to UTF-8 encoded bytes */ +PyAPI_FUNC(PyObject*) PyUnicode_FromStringAndSize( + const char *u, /* UTF-8 encoded string */ + Py_ssize_t size /* size of buffer */ + ); + +/* Similar to PyUnicode_FromUnicode(), but u points to null-terminated + UTF-8 encoded bytes. The size is determined with strlen(). */ +PyAPI_FUNC(PyObject*) PyUnicode_FromString( + const char *u /* UTF-8 encoded string */ + ); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(PyObject*) PyUnicode_Substring( + PyObject *str, + Py_ssize_t start, + Py_ssize_t end); +#endif + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +/* Copy the string into a UCS4 buffer including the null character if copy_null + is set. Return NULL and raise an exception on error. Raise a SystemError if + the buffer is smaller than the string. Return buffer on success. + + buflen is the length of the buffer in (Py_UCS4) characters. */ +PyAPI_FUNC(Py_UCS4*) PyUnicode_AsUCS4( + PyObject *unicode, + Py_UCS4* buffer, + Py_ssize_t buflen, + int copy_null); + +/* Copy the string into a UCS4 buffer. A new buffer is allocated using + * PyMem_Malloc; if this fails, NULL is returned with a memory error + exception set. */ +PyAPI_FUNC(Py_UCS4*) PyUnicode_AsUCS4Copy(PyObject *unicode); +#endif + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +/* Get the length of the Unicode object. */ + +PyAPI_FUNC(Py_ssize_t) PyUnicode_GetLength( + PyObject *unicode +); +#endif + +/* Get the number of Py_UNICODE units in the + string representation. */ + +Py_DEPRECATED(3.3) PyAPI_FUNC(Py_ssize_t) PyUnicode_GetSize( + PyObject *unicode /* Unicode object */ + ); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +/* Read a character from the string. */ + +PyAPI_FUNC(Py_UCS4) PyUnicode_ReadChar( + PyObject *unicode, + Py_ssize_t index + ); + +/* Write a character to the string. The string must have been created through + PyUnicode_New, must not be shared, and must not have been hashed yet. + + Return 0 on success, -1 on error. */ + +PyAPI_FUNC(int) PyUnicode_WriteChar( + PyObject *unicode, + Py_ssize_t index, + Py_UCS4 character + ); +#endif + +/* Resize a Unicode object. The length is the number of characters, except + if the kind of the string is PyUnicode_WCHAR_KIND: in this case, the length + is the number of Py_UNICODE characters. + + *unicode is modified to point to the new (resized) object and 0 + returned on success. + + Try to resize the string in place (which is usually faster than allocating + a new string and copy characters), or create a new string. + + Error handling is implemented as follows: an exception is set, -1 + is returned and *unicode left untouched. + + WARNING: The function doesn't check string content, the result may not be a + string in canonical representation. */ + +PyAPI_FUNC(int) PyUnicode_Resize( + PyObject **unicode, /* Pointer to the Unicode object */ + Py_ssize_t length /* New length */ + ); + +/* Decode obj to a Unicode object. + + bytes, bytearray and other bytes-like objects are decoded according to the + given encoding and error handler. The encoding and error handler can be + NULL to have the interface use UTF-8 and "strict". + + All other objects (including Unicode objects) raise an exception. + + The API returns NULL in case of an error. The caller is responsible + for decref'ing the returned objects. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_FromEncodedObject( + PyObject *obj, /* Object */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Copy an instance of a Unicode subtype to a new true Unicode object if + necessary. If obj is already a true Unicode object (not a subtype), return + the reference with *incremented* refcount. + + The API returns NULL in case of an error. The caller is responsible + for decref'ing the returned objects. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_FromObject( + PyObject *obj /* Object */ + ); + +PyAPI_FUNC(PyObject *) PyUnicode_FromFormatV( + const char *format, /* ASCII-encoded string */ + va_list vargs + ); +PyAPI_FUNC(PyObject *) PyUnicode_FromFormat( + const char *format, /* ASCII-encoded string */ + ... + ); + +PyAPI_FUNC(void) PyUnicode_InternInPlace(PyObject **); +PyAPI_FUNC(void) PyUnicode_InternImmortal(PyObject **); +PyAPI_FUNC(PyObject *) PyUnicode_InternFromString( + const char *u /* UTF-8 encoded string */ + ); + +/* Use only if you know it's a string */ +#define PyUnicode_CHECK_INTERNED(op) \ + (((PyASCIIObject *)(op))->state.interned) + +/* --- wchar_t support for platforms which support it --------------------- */ + +#ifdef HAVE_WCHAR_H + +/* Create a Unicode Object from the wchar_t buffer w of the given + size. + + The buffer is copied into the new object. */ + +PyAPI_FUNC(PyObject*) PyUnicode_FromWideChar( + const wchar_t *w, /* wchar_t buffer */ + Py_ssize_t size /* size of buffer */ + ); + +/* Copies the Unicode Object contents into the wchar_t buffer w. At + most size wchar_t characters are copied. + + Note that the resulting wchar_t string may or may not be + 0-terminated. It is the responsibility of the caller to make sure + that the wchar_t string is 0-terminated in case this is required by + the application. + + Returns the number of wchar_t characters copied (excluding a + possibly trailing 0-termination character) or -1 in case of an + error. */ + +PyAPI_FUNC(Py_ssize_t) PyUnicode_AsWideChar( + PyObject *unicode, /* Unicode object */ + wchar_t *w, /* wchar_t buffer */ + Py_ssize_t size /* size of buffer */ + ); + +/* Convert the Unicode object to a wide character string. The output string + always ends with a nul character. If size is not NULL, write the number of + wide characters (excluding the null character) into *size. + + Returns a buffer allocated by PyMem_Malloc() (use PyMem_Free() to free it) + on success. On error, returns NULL, *size is undefined and raises a + MemoryError. */ + +PyAPI_FUNC(wchar_t*) PyUnicode_AsWideCharString( + PyObject *unicode, /* Unicode object */ + Py_ssize_t *size /* number of characters of the result */ + ); + +#endif + +/* --- Unicode ordinals --------------------------------------------------- */ + +/* Create a Unicode Object from the given Unicode code point ordinal. + + The ordinal must be in range(0x110000). A ValueError is + raised in case it is not. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_FromOrdinal(int ordinal); + +/* === Builtin Codecs ===================================================== + + Many of these APIs take two arguments encoding and errors. These + parameters encoding and errors have the same semantics as the ones + of the builtin str() API. + + Setting encoding to NULL causes the default encoding (UTF-8) to be used. + + Error handling is set by errors which may also be set to NULL + meaning to use the default handling defined for the codec. Default + error handling for all builtin codecs is "strict" (ValueErrors are + raised). + + The codecs all use a similar interface. Only deviation from the + generic ones are documented. + +*/ + +/* --- Manage the default encoding ---------------------------------------- */ + +/* Returns "utf-8". */ +PyAPI_FUNC(const char*) PyUnicode_GetDefaultEncoding(void); + +/* --- Generic Codecs ----------------------------------------------------- */ + +/* Create a Unicode object by decoding the encoded string s of the + given size. */ + +PyAPI_FUNC(PyObject*) PyUnicode_Decode( + const char *s, /* encoded string */ + Py_ssize_t size, /* size of buffer */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Decode a Unicode object unicode and return the result as Python + object. + + This API is DEPRECATED. The only supported standard encoding is rot13. + Use PyCodec_Decode() to decode with rot13 and non-standard codecs + that decode from str. */ + +Py_DEPRECATED(3.6) PyAPI_FUNC(PyObject*) PyUnicode_AsDecodedObject( + PyObject *unicode, /* Unicode object */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Decode a Unicode object unicode and return the result as Unicode + object. + + This API is DEPRECATED. The only supported standard encoding is rot13. + Use PyCodec_Decode() to decode with rot13 and non-standard codecs + that decode from str to str. */ + +Py_DEPRECATED(3.6) PyAPI_FUNC(PyObject*) PyUnicode_AsDecodedUnicode( + PyObject *unicode, /* Unicode object */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Encodes a Unicode object and returns the result as Python + object. + + This API is DEPRECATED. It is superseded by PyUnicode_AsEncodedString() + since all standard encodings (except rot13) encode str to bytes. + Use PyCodec_Encode() for encoding with rot13 and non-standard codecs + that encode form str to non-bytes. */ + +Py_DEPRECATED(3.6) PyAPI_FUNC(PyObject*) PyUnicode_AsEncodedObject( + PyObject *unicode, /* Unicode object */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Encodes a Unicode object and returns the result as Python string + object. */ + +PyAPI_FUNC(PyObject*) PyUnicode_AsEncodedString( + PyObject *unicode, /* Unicode object */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Encodes a Unicode object and returns the result as Unicode + object. + + This API is DEPRECATED. The only supported standard encodings is rot13. + Use PyCodec_Encode() to encode with rot13 and non-standard codecs + that encode from str to str. */ + +Py_DEPRECATED(3.6) PyAPI_FUNC(PyObject*) PyUnicode_AsEncodedUnicode( + PyObject *unicode, /* Unicode object */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Build an encoding map. */ + +PyAPI_FUNC(PyObject*) PyUnicode_BuildEncodingMap( + PyObject* string /* 256 character map */ + ); + +/* --- UTF-7 Codecs ------------------------------------------------------- */ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUTF7( + const char *string, /* UTF-7 encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUTF7Stateful( + const char *string, /* UTF-7 encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors, /* error handling */ + Py_ssize_t *consumed /* bytes consumed */ + ); + +/* --- UTF-8 Codecs ------------------------------------------------------- */ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUTF8( + const char *string, /* UTF-8 encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUTF8Stateful( + const char *string, /* UTF-8 encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors, /* error handling */ + Py_ssize_t *consumed /* bytes consumed */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsUTF8String( + PyObject *unicode /* Unicode object */ + ); + +/* --- UTF-32 Codecs ------------------------------------------------------ */ + +/* Decodes length bytes from a UTF-32 encoded buffer string and returns + the corresponding Unicode object. + + errors (if non-NULL) defines the error handling. It defaults + to "strict". + + If byteorder is non-NULL, the decoder starts decoding using the + given byte order: + + *byteorder == -1: little endian + *byteorder == 0: native order + *byteorder == 1: big endian + + In native mode, the first four bytes of the stream are checked for a + BOM mark. If found, the BOM mark is analysed, the byte order + adjusted and the BOM skipped. In the other modes, no BOM mark + interpretation is done. After completion, *byteorder is set to the + current byte order at the end of input data. + + If byteorder is NULL, the codec starts in native order mode. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUTF32( + const char *string, /* UTF-32 encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors, /* error handling */ + int *byteorder /* pointer to byteorder to use + 0=native;-1=LE,1=BE; updated on + exit */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUTF32Stateful( + const char *string, /* UTF-32 encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors, /* error handling */ + int *byteorder, /* pointer to byteorder to use + 0=native;-1=LE,1=BE; updated on + exit */ + Py_ssize_t *consumed /* bytes consumed */ + ); + +/* Returns a Python string using the UTF-32 encoding in native byte + order. The string always starts with a BOM mark. */ + +PyAPI_FUNC(PyObject*) PyUnicode_AsUTF32String( + PyObject *unicode /* Unicode object */ + ); + +/* Returns a Python string object holding the UTF-32 encoded value of + the Unicode data. + + If byteorder is not 0, output is written according to the following + byte order: + + byteorder == -1: little endian + byteorder == 0: native byte order (writes a BOM mark) + byteorder == 1: big endian + + If byteorder is 0, the output string will always start with the + Unicode BOM mark (U+FEFF). In the other two modes, no BOM mark is + prepended. + +*/ + +/* --- UTF-16 Codecs ------------------------------------------------------ */ + +/* Decodes length bytes from a UTF-16 encoded buffer string and returns + the corresponding Unicode object. + + errors (if non-NULL) defines the error handling. It defaults + to "strict". + + If byteorder is non-NULL, the decoder starts decoding using the + given byte order: + + *byteorder == -1: little endian + *byteorder == 0: native order + *byteorder == 1: big endian + + In native mode, the first two bytes of the stream are checked for a + BOM mark. If found, the BOM mark is analysed, the byte order + adjusted and the BOM skipped. In the other modes, no BOM mark + interpretation is done. After completion, *byteorder is set to the + current byte order at the end of input data. + + If byteorder is NULL, the codec starts in native order mode. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUTF16( + const char *string, /* UTF-16 encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors, /* error handling */ + int *byteorder /* pointer to byteorder to use + 0=native;-1=LE,1=BE; updated on + exit */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUTF16Stateful( + const char *string, /* UTF-16 encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors, /* error handling */ + int *byteorder, /* pointer to byteorder to use + 0=native;-1=LE,1=BE; updated on + exit */ + Py_ssize_t *consumed /* bytes consumed */ + ); + +/* Returns a Python string using the UTF-16 encoding in native byte + order. The string always starts with a BOM mark. */ + +PyAPI_FUNC(PyObject*) PyUnicode_AsUTF16String( + PyObject *unicode /* Unicode object */ + ); + +/* --- Unicode-Escape Codecs ---------------------------------------------- */ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUnicodeEscape( + const char *string, /* Unicode-Escape encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsUnicodeEscapeString( + PyObject *unicode /* Unicode object */ + ); + +/* --- Raw-Unicode-Escape Codecs ------------------------------------------ */ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeRawUnicodeEscape( + const char *string, /* Raw-Unicode-Escape encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsRawUnicodeEscapeString( + PyObject *unicode /* Unicode object */ + ); + +/* --- Latin-1 Codecs ----------------------------------------------------- + + Note: Latin-1 corresponds to the first 256 Unicode ordinals. */ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeLatin1( + const char *string, /* Latin-1 encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsLatin1String( + PyObject *unicode /* Unicode object */ + ); + +/* --- ASCII Codecs ------------------------------------------------------- + + Only 7-bit ASCII data is excepted. All other codes generate errors. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeASCII( + const char *string, /* ASCII encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsASCIIString( + PyObject *unicode /* Unicode object */ + ); + +/* --- Character Map Codecs ----------------------------------------------- + + This codec uses mappings to encode and decode characters. + + Decoding mappings must map byte ordinals (integers in the range from 0 to + 255) to Unicode strings, integers (which are then interpreted as Unicode + ordinals) or None. Unmapped data bytes (ones which cause a LookupError) + as well as mapped to None, 0xFFFE or '\ufffe' are treated as "undefined + mapping" and cause an error. + + Encoding mappings must map Unicode ordinal integers to bytes objects, + integers in the range from 0 to 255 or None. Unmapped character + ordinals (ones which cause a LookupError) as well as mapped to + None are treated as "undefined mapping" and cause an error. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeCharmap( + const char *string, /* Encoded string */ + Py_ssize_t length, /* size of string */ + PyObject *mapping, /* decoding mapping */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsCharmapString( + PyObject *unicode, /* Unicode object */ + PyObject *mapping /* encoding mapping */ + ); + +/* --- MBCS codecs for Windows -------------------------------------------- */ + +#ifdef MS_WINDOWS +PyAPI_FUNC(PyObject*) PyUnicode_DecodeMBCS( + const char *string, /* MBCS encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeMBCSStateful( + const char *string, /* MBCS encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors, /* error handling */ + Py_ssize_t *consumed /* bytes consumed */ + ); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(PyObject*) PyUnicode_DecodeCodePageStateful( + int code_page, /* code page number */ + const char *string, /* encoded string */ + Py_ssize_t length, /* size of string */ + const char *errors, /* error handling */ + Py_ssize_t *consumed /* bytes consumed */ + ); +#endif + +PyAPI_FUNC(PyObject*) PyUnicode_AsMBCSString( + PyObject *unicode /* Unicode object */ + ); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +PyAPI_FUNC(PyObject*) PyUnicode_EncodeCodePage( + int code_page, /* code page number */ + PyObject *unicode, /* Unicode object */ + const char *errors /* error handling */ + ); +#endif + +#endif /* MS_WINDOWS */ + +/* --- Locale encoding --------------------------------------------------- */ + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +/* Decode a string from the current locale encoding. The decoder is strict if + *surrogateescape* is equal to zero, otherwise it uses the 'surrogateescape' + error handler (PEP 383) to escape undecodable bytes. If a byte sequence can + be decoded as a surrogate character and *surrogateescape* is not equal to + zero, the byte sequence is escaped using the 'surrogateescape' error handler + instead of being decoded. *str* must end with a null character but cannot + contain embedded null characters. */ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeLocaleAndSize( + const char *str, + Py_ssize_t len, + const char *errors); + +/* Similar to PyUnicode_DecodeLocaleAndSize(), but compute the string + length using strlen(). */ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeLocale( + const char *str, + const char *errors); + +/* Encode a Unicode object to the current locale encoding. The encoder is + strict is *surrogateescape* is equal to zero, otherwise the + "surrogateescape" error handler is used. Return a bytes object. The string + cannot contain embedded null characters. */ + +PyAPI_FUNC(PyObject*) PyUnicode_EncodeLocale( + PyObject *unicode, + const char *errors + ); +#endif + +/* --- File system encoding ---------------------------------------------- */ + +/* ParseTuple converter: encode str objects to bytes using + PyUnicode_EncodeFSDefault(); bytes objects are output as-is. */ + +PyAPI_FUNC(int) PyUnicode_FSConverter(PyObject*, void*); + +/* ParseTuple converter: decode bytes objects to unicode using + PyUnicode_DecodeFSDefaultAndSize(); str objects are output as-is. */ + +PyAPI_FUNC(int) PyUnicode_FSDecoder(PyObject*, void*); + +/* Decode a null-terminated string using Py_FileSystemDefaultEncoding + and the "surrogateescape" error handler. + + If Py_FileSystemDefaultEncoding is not set, fall back to the locale + encoding. + + Use PyUnicode_DecodeFSDefaultAndSize() if the string length is known. +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeFSDefault( + const char *s /* encoded string */ + ); + +/* Decode a string using Py_FileSystemDefaultEncoding + and the "surrogateescape" error handler. + + If Py_FileSystemDefaultEncoding is not set, fall back to the locale + encoding. +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeFSDefaultAndSize( + const char *s, /* encoded string */ + Py_ssize_t size /* size */ + ); + +/* Encode a Unicode object to Py_FileSystemDefaultEncoding with the + "surrogateescape" error handler, and return bytes. + + If Py_FileSystemDefaultEncoding is not set, fall back to the locale + encoding. +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_EncodeFSDefault( + PyObject *unicode + ); + +/* --- Methods & Slots ---------------------------------------------------- + + These are capable of handling Unicode objects and strings on input + (we refer to them as strings in the descriptions) and return + Unicode objects or integers as appropriate. */ + +/* Concat two strings giving a new Unicode string. */ + +PyAPI_FUNC(PyObject*) PyUnicode_Concat( + PyObject *left, /* Left string */ + PyObject *right /* Right string */ + ); + +/* Concat two strings and put the result in *pleft + (sets *pleft to NULL on error) */ + +PyAPI_FUNC(void) PyUnicode_Append( + PyObject **pleft, /* Pointer to left string */ + PyObject *right /* Right string */ + ); + +/* Concat two strings, put the result in *pleft and drop the right object + (sets *pleft to NULL on error) */ + +PyAPI_FUNC(void) PyUnicode_AppendAndDel( + PyObject **pleft, /* Pointer to left string */ + PyObject *right /* Right string */ + ); + +/* Split a string giving a list of Unicode strings. + + If sep is NULL, splitting will be done at all whitespace + substrings. Otherwise, splits occur at the given separator. + + At most maxsplit splits will be done. If negative, no limit is set. + + Separators are not included in the resulting list. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_Split( + PyObject *s, /* String to split */ + PyObject *sep, /* String separator */ + Py_ssize_t maxsplit /* Maxsplit count */ + ); + +/* Dito, but split at line breaks. + + CRLF is considered to be one line break. Line breaks are not + included in the resulting list. */ + +PyAPI_FUNC(PyObject*) PyUnicode_Splitlines( + PyObject *s, /* String to split */ + int keepends /* If true, line end markers are included */ + ); + +/* Partition a string using a given separator. */ + +PyAPI_FUNC(PyObject*) PyUnicode_Partition( + PyObject *s, /* String to partition */ + PyObject *sep /* String separator */ + ); + +/* Partition a string using a given separator, searching from the end of the + string. */ + +PyAPI_FUNC(PyObject*) PyUnicode_RPartition( + PyObject *s, /* String to partition */ + PyObject *sep /* String separator */ + ); + +/* Split a string giving a list of Unicode strings. + + If sep is NULL, splitting will be done at all whitespace + substrings. Otherwise, splits occur at the given separator. + + At most maxsplit splits will be done. But unlike PyUnicode_Split + PyUnicode_RSplit splits from the end of the string. If negative, + no limit is set. + + Separators are not included in the resulting list. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_RSplit( + PyObject *s, /* String to split */ + PyObject *sep, /* String separator */ + Py_ssize_t maxsplit /* Maxsplit count */ + ); + +/* Translate a string by applying a character mapping table to it and + return the resulting Unicode object. + + The mapping table must map Unicode ordinal integers to Unicode strings, + Unicode ordinal integers or None (causing deletion of the character). + + Mapping tables may be dictionaries or sequences. Unmapped character + ordinals (ones which cause a LookupError) are left untouched and + are copied as-is. + +*/ + +PyAPI_FUNC(PyObject *) PyUnicode_Translate( + PyObject *str, /* String */ + PyObject *table, /* Translate table */ + const char *errors /* error handling */ + ); + +/* Join a sequence of strings using the given separator and return + the resulting Unicode string. */ + +PyAPI_FUNC(PyObject*) PyUnicode_Join( + PyObject *separator, /* Separator string */ + PyObject *seq /* Sequence object */ + ); + +/* Return 1 if substr matches str[start:end] at the given tail end, 0 + otherwise. */ + +PyAPI_FUNC(Py_ssize_t) PyUnicode_Tailmatch( + PyObject *str, /* String */ + PyObject *substr, /* Prefix or Suffix string */ + Py_ssize_t start, /* Start index */ + Py_ssize_t end, /* Stop index */ + int direction /* Tail end: -1 prefix, +1 suffix */ + ); + +/* Return the first position of substr in str[start:end] using the + given search direction or -1 if not found. -2 is returned in case + an error occurred and an exception is set. */ + +PyAPI_FUNC(Py_ssize_t) PyUnicode_Find( + PyObject *str, /* String */ + PyObject *substr, /* Substring to find */ + Py_ssize_t start, /* Start index */ + Py_ssize_t end, /* Stop index */ + int direction /* Find direction: +1 forward, -1 backward */ + ); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +/* Like PyUnicode_Find, but search for single character only. */ +PyAPI_FUNC(Py_ssize_t) PyUnicode_FindChar( + PyObject *str, + Py_UCS4 ch, + Py_ssize_t start, + Py_ssize_t end, + int direction + ); +#endif + +/* Count the number of occurrences of substr in str[start:end]. */ + +PyAPI_FUNC(Py_ssize_t) PyUnicode_Count( + PyObject *str, /* String */ + PyObject *substr, /* Substring to count */ + Py_ssize_t start, /* Start index */ + Py_ssize_t end /* Stop index */ + ); + +/* Replace at most maxcount occurrences of substr in str with replstr + and return the resulting Unicode object. */ + +PyAPI_FUNC(PyObject *) PyUnicode_Replace( + PyObject *str, /* String */ + PyObject *substr, /* Substring to find */ + PyObject *replstr, /* Substring to replace */ + Py_ssize_t maxcount /* Max. number of replacements to apply; + -1 = all */ + ); + +/* Compare two strings and return -1, 0, 1 for less than, equal, + greater than resp. + Raise an exception and return -1 on error. */ + +PyAPI_FUNC(int) PyUnicode_Compare( + PyObject *left, /* Left string */ + PyObject *right /* Right string */ + ); + +/* Compare a Unicode object with C string and return -1, 0, 1 for less than, + equal, and greater than, respectively. It is best to pass only + ASCII-encoded strings, but the function interprets the input string as + ISO-8859-1 if it contains non-ASCII characters. + This function does not raise exceptions. */ + +PyAPI_FUNC(int) PyUnicode_CompareWithASCIIString( + PyObject *left, + const char *right /* ASCII-encoded string */ + ); + +/* Rich compare two strings and return one of the following: + + - NULL in case an exception was raised + - Py_True or Py_False for successful comparisons + - Py_NotImplemented in case the type combination is unknown + + Possible values for op: + + Py_GT, Py_GE, Py_EQ, Py_NE, Py_LT, Py_LE + +*/ + +PyAPI_FUNC(PyObject *) PyUnicode_RichCompare( + PyObject *left, /* Left string */ + PyObject *right, /* Right string */ + int op /* Operation: Py_EQ, Py_NE, Py_GT, etc. */ + ); + +/* Apply an argument tuple or dictionary to a format string and return + the resulting Unicode string. */ + +PyAPI_FUNC(PyObject *) PyUnicode_Format( + PyObject *format, /* Format string */ + PyObject *args /* Argument tuple or dictionary */ + ); + +/* Checks whether element is contained in container and return 1/0 + accordingly. + + element has to coerce to a one element Unicode string. -1 is + returned in case of an error. */ + +PyAPI_FUNC(int) PyUnicode_Contains( + PyObject *container, /* Container string */ + PyObject *element /* Element string */ + ); + +/* Checks whether argument is a valid identifier. */ + +PyAPI_FUNC(int) PyUnicode_IsIdentifier(PyObject *s); + +/* === Characters Type APIs =============================================== */ + +#ifndef Py_LIMITED_API +# define Py_CPYTHON_UNICODEOBJECT_H +# include "cpython/unicodeobject.h" +# undef Py_CPYTHON_UNICODEOBJECT_H +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_UNICODEOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/include/warnings.h b/scripts/build-windows/py39-libs/include/warnings.h new file mode 100644 index 000000000..a1ec42525 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/warnings.h @@ -0,0 +1,67 @@ +#ifndef Py_WARNINGS_H +#define Py_WARNINGS_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject*) _PyWarnings_Init(void); +#endif + +PyAPI_FUNC(int) PyErr_WarnEx( + PyObject *category, + const char *message, /* UTF-8 encoded string */ + Py_ssize_t stack_level); +PyAPI_FUNC(int) PyErr_WarnFormat( + PyObject *category, + Py_ssize_t stack_level, + const char *format, /* ASCII-encoded string */ + ...); + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 +/* Emit a ResourceWarning warning */ +PyAPI_FUNC(int) PyErr_ResourceWarning( + PyObject *source, + Py_ssize_t stack_level, + const char *format, /* ASCII-encoded string */ + ...); +#endif +#ifndef Py_LIMITED_API +PyAPI_FUNC(int) PyErr_WarnExplicitObject( + PyObject *category, + PyObject *message, + PyObject *filename, + int lineno, + PyObject *module, + PyObject *registry); +#endif +PyAPI_FUNC(int) PyErr_WarnExplicit( + PyObject *category, + const char *message, /* UTF-8 encoded string */ + const char *filename, /* decoded from the filesystem encoding */ + int lineno, + const char *module, /* UTF-8 encoded string */ + PyObject *registry); + +#ifndef Py_LIMITED_API +PyAPI_FUNC(int) +PyErr_WarnExplicitFormat(PyObject *category, + const char *filename, int lineno, + const char *module, PyObject *registry, + const char *format, ...); +#endif + +/* DEPRECATED: Use PyErr_WarnEx() instead. */ +#ifndef Py_LIMITED_API +#define PyErr_Warn(category, msg) PyErr_WarnEx(category, msg, 1) +#endif + +#ifndef Py_LIMITED_API +void _PyErr_WarnUnawaitedCoroutine(PyObject *coro); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_WARNINGS_H */ + diff --git a/scripts/build-windows/py39-libs/include/weakrefobject.h b/scripts/build-windows/py39-libs/include/weakrefobject.h new file mode 100644 index 000000000..368908204 --- /dev/null +++ b/scripts/build-windows/py39-libs/include/weakrefobject.h @@ -0,0 +1,86 @@ +/* Weak references objects for Python. */ + +#ifndef Py_WEAKREFOBJECT_H +#define Py_WEAKREFOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct _PyWeakReference PyWeakReference; + +/* PyWeakReference is the base struct for the Python ReferenceType, ProxyType, + * and CallableProxyType. + */ +#ifndef Py_LIMITED_API +struct _PyWeakReference { + PyObject_HEAD + + /* The object to which this is a weak reference, or Py_None if none. + * Note that this is a stealth reference: wr_object's refcount is + * not incremented to reflect this pointer. + */ + PyObject *wr_object; + + /* A callable to invoke when wr_object dies, or NULL if none. */ + PyObject *wr_callback; + + /* A cache for wr_object's hash code. As usual for hashes, this is -1 + * if the hash code isn't known yet. + */ + Py_hash_t hash; + + /* If wr_object is weakly referenced, wr_object has a doubly-linked NULL- + * terminated list of weak references to it. These are the list pointers. + * If wr_object goes away, wr_object is set to Py_None, and these pointers + * have no meaning then. + */ + PyWeakReference *wr_prev; + PyWeakReference *wr_next; +}; +#endif + +PyAPI_DATA(PyTypeObject) _PyWeakref_RefType; +PyAPI_DATA(PyTypeObject) _PyWeakref_ProxyType; +PyAPI_DATA(PyTypeObject) _PyWeakref_CallableProxyType; + +#define PyWeakref_CheckRef(op) PyObject_TypeCheck(op, &_PyWeakref_RefType) +#define PyWeakref_CheckRefExact(op) \ + Py_IS_TYPE(op, &_PyWeakref_RefType) +#define PyWeakref_CheckProxy(op) \ + (Py_IS_TYPE(op, &_PyWeakref_ProxyType) || \ + Py_IS_TYPE(op, &_PyWeakref_CallableProxyType)) + +#define PyWeakref_Check(op) \ + (PyWeakref_CheckRef(op) || PyWeakref_CheckProxy(op)) + + +PyAPI_FUNC(PyObject *) PyWeakref_NewRef(PyObject *ob, + PyObject *callback); +PyAPI_FUNC(PyObject *) PyWeakref_NewProxy(PyObject *ob, + PyObject *callback); +PyAPI_FUNC(PyObject *) PyWeakref_GetObject(PyObject *ref); + +#ifndef Py_LIMITED_API +PyAPI_FUNC(Py_ssize_t) _PyWeakref_GetWeakrefCount(PyWeakReference *head); + +PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self); +#endif + +/* Explanation for the Py_REFCNT() check: when a weakref's target is part + of a long chain of deallocations which triggers the trashcan mechanism, + clearing the weakrefs can be delayed long after the target's refcount + has dropped to zero. In the meantime, code accessing the weakref will + be able to "see" the target object even though it is supposed to be + unreachable. See issue #16602. */ + +#define PyWeakref_GET_OBJECT(ref) \ + (Py_REFCNT(((PyWeakReference *)(ref))->wr_object) > 0 \ + ? ((PyWeakReference *)(ref))->wr_object \ + : Py_None) + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_WEAKREFOBJECT_H */ diff --git a/scripts/build-windows/py39-libs/libs/_tkinter.lib b/scripts/build-windows/py39-libs/libs/_tkinter.lib new file mode 100644 index 000000000..6bebff9a2 Binary files /dev/null and b/scripts/build-windows/py39-libs/libs/_tkinter.lib differ diff --git a/scripts/build-windows/py39-libs/libs/python3.lib b/scripts/build-windows/py39-libs/libs/python3.lib new file mode 100644 index 000000000..800a9c62b Binary files /dev/null and b/scripts/build-windows/py39-libs/libs/python3.lib differ diff --git a/scripts/build-windows/py39-libs/libs/python39.lib b/scripts/build-windows/py39-libs/libs/python39.lib new file mode 100644 index 000000000..e57eac455 Binary files /dev/null and b/scripts/build-windows/py39-libs/libs/python39.lib differ diff --git a/scripts/build-windows/readme.md b/scripts/build-windows/readme.md new file mode 100644 index 000000000..3ae0e187b --- /dev/null +++ b/scripts/build-windows/readme.md @@ -0,0 +1,17 @@ +# Build windows/amd64 + +1. install Rust and cargo +1. install Go1.16+ + - setup ssh key, keep `go get kcl-website` work well +1. install VC2019 +1. install TDM-GCC-x64 + - https://jmeubank.github.io/tdm-gcc/download/ +1. install LLVM-12.0.1-win64 + - https://github.com/PLC-lang/llvm-package-windows/releases/tag/v12.0.1 + - set `LLVM_SYS_120_PREFIX` to root path +2. install NSIS + - https://nsis.sourceforge.io/Download + - set `$PATH` +1. open VS2019-x64 command line + - `cd kclvm` and run `cargo build` to check env + - `cd ./scripts/build-windows` run `build.bat` diff --git a/scripts/build-windows/rename.go b/scripts/build-windows/rename.go new file mode 100644 index 000000000..a8505e28d --- /dev/null +++ b/scripts/build-windows/rename.go @@ -0,0 +1,29 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ingore +// +build ingore + +package main + +import ( + "flag" + "log" + "os" +) + +var ( + flagOldFile = flag.String("old", "", "set old file") + flagNewFile = flag.String("new", "", "set new file") +) + +func main() { + flag.Parse() + if *flagOldFile == "" || *flagNewFile == "" { + flag.Usage() + os.Exit(1) + } + err := os.Rename(*flagOldFile, *flagNewFile) + if err != nil { + log.Fatal(err) + } +} diff --git a/scripts/build-windows/requirements.release.txt b/scripts/build-windows/requirements.release.txt new file mode 100644 index 000000000..eb2ea3b11 --- /dev/null +++ b/scripts/build-windows/requirements.release.txt @@ -0,0 +1,18 @@ +pyyaml==5.3 +lark-parser==0.11.3 +yapf==0.29.0 +pypeg2==2.15.2 +protobuf==3.12.2 +pytest==6.2.2 +requests +schema +ruamel.yaml +toml +numpydoc +pygls==0.10.3 +fastapi +uvicorn +gunicorn==20.1.0 +parsy==1.3.0 +wasmer==1.0.0 +wasmer_compiler_cranelift==1.0.0 diff --git a/scripts/build-windows/unzip.go b/scripts/build-windows/unzip.go new file mode 100644 index 000000000..4ac8e41f3 --- /dev/null +++ b/scripts/build-windows/unzip.go @@ -0,0 +1,68 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ingore +// +build ingore + +package main + +import ( + "archive/zip" + "flag" + "fmt" + "io" + "os" + "path/filepath" + "strings" +) + +var ( + flagZipFile = flag.String("zip", "python-3.9.6-embed-amd64.zip", "set zip file") + flagOutput = flag.String("output", "_output/kclvm-windows", "set output dir") +) + +func main() { + flag.Parse() + + dst := *flagOutput + archive, err := zip.OpenReader(*flagZipFile) + if err != nil { + panic(err) + } + defer archive.Close() + + for _, f := range archive.File { + filePath := filepath.Join(dst, f.Name) + fmt.Println("unzip ", filePath) + + if !strings.HasPrefix(filePath, filepath.Clean(dst)+string(os.PathSeparator)) { + fmt.Println("invalid file path") + return + } + if f.FileInfo().IsDir() { + fmt.Println("creating directory...") + os.MkdirAll(filePath, os.ModePerm) + continue + } + + if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { + panic(err) + } + + dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + panic(err) + } + + fileInArchive, err := f.Open() + if err != nil { + panic(err) + } + + if _, err := io.Copy(dstFile, fileInArchive); err != nil { + panic(err) + } + + dstFile.Close() + fileInArchive.Close() + } +} diff --git a/scripts/docker/kclvm-builder-centos7/Dockerfile b/scripts/docker/kclvm-builder-centos7/Dockerfile new file mode 100644 index 000000000..eb46286fe --- /dev/null +++ b/scripts/docker/kclvm-builder-centos7/Dockerfile @@ -0,0 +1,89 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +FROM centos:centos7 + +# macOS M1 --platform linux/amd64 +# try fix "Problem with the SSL CA cert (path? access rights?)" +# https://issueexplorer.com/issue/docker/for-mac/5912 +# https://access.redhat.com/articles/2050743 +RUN touch /etc/sysconfig/64bit_strstr_via_64bit_strstr_sse2_unaligned + +# --------------------------------------------------------------------------------- +# Please note: The following steps are to install the dependency packages +# needed to compile CPython for centos7, see the +# [Python official website](https://devguide.python.org/setup/#install-dependencies) +# for details. When the version of CPython used becomes higher, +# please pay attention to update the installation dependencies. +# --------------------------------------------------------------------------------- + +# Some language environments and plug-ins related to development and compilation, +# such as git, CPython compilation, etc. +RUN yum groupinstall -y "Development Tools" +# Compiler and tool chain required to compile CPython such as gcc, make, sqlite3, ctype, struct, etc. +RUN yum install -y gcc patch libffi-devel python-devel zlib-devel bzip2-devel ncurses-devel sqlite-devel +RUN yum install -y libpcap-devel xz-devel readline-devel tk-devel gdbm-devel db4-deve +# Install the system libraries required by python3 for UNIX based systems +RUN yum -y install yum-utils +RUN yum-builddep -y python3 +# The python zlib module dependency package is required when compiling the python source code, +# in order to use the modules that require zlib, such as setuptools, etc. +RUN yum install -y zlib* +# The python ssl module dependency package is required when compiling the python source code, +# in order to use the modules that require ssl, such as pip3, twine, etc. +RUN yum install -y openssl-devel + +# install which +RUN yum install -y which + +# install wget +RUN yum install -y wget + +# install git-2.x +# RUN yum -y install https://packages.endpoint.com/rhel/7/os/x86_64/endpoint-repo-1.7-1.x86_64.rpm +# RUN yum -y install git + +# rust +# https://www.rust-lang.org/tools/install +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" +ENV CARGO_NET_GIT_FETCH_WITH_CLI=true + +RUN cargo version +RUN rustc --version + +# wasm +RUN rustup target add wasm32-unknown-unknown + +# clang7 +# https://www.softwarecollections.org/en/scls/rhscl/llvm-toolset-7.0/ +# +# 1. Install a package with repository for your system: +# On CentOS, install package centos-release-scl available in CentOS repository: +# $ sudo yum install centos-release-scl +# +# On RHEL, enable RHSCL repository for you system: +# $ sudo yum-config-manager --enable rhel-server-rhscl-7-rpms +# +# 2. Install the collection: +# $ sudo yum install llvm-toolset-7.0 +# +# 3. Start using software collections: +# $ scl enable llvm-toolset-7.0 bash + +RUN yum -y install centos-release-scl +RUN yum-config-manager --enable rhel-server-rhscl-7-rpms +RUN yum -y install llvm-toolset-7.0 +RUN yum -y install llvm-toolset-7.0\* +RUN scl enable llvm-toolset-7.0 bash + +# rpm -ql llvm-toolset-7.0-clang.x86_64 +# /opt/rh/llvm-toolset-7.0/root/usr/lib64/libLLVM-7.so +ENV LD_LIBRARY_PATH="/opt/rh/llvm-toolset-7.0/root/usr/lib64:${LD_LIBRARY_PATH}" +ENV PATH="/opt/rh/llvm-toolset-7.0/root/usr/bin:${PATH}" + +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime +RUN echo 'Asia/Shanghai' >/etc/timezone + +WORKDIR /root + +CMD ["bash"] diff --git a/scripts/docker/kclvm-builder-centos7/Makefile b/scripts/docker/kclvm-builder-centos7/Makefile new file mode 100644 index 000000000..9868ff39d --- /dev/null +++ b/scripts/docker/kclvm-builder-centos7/Makefile @@ -0,0 +1,32 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +PWD:=$(shell pwd) +TIMESTAMP:=$(shell go run timestamp.go) + +BUILDER_IMAGE:=kusionstack/kclvm-builder:centos7 + +# export DOCKER_DEFAULT_PLATFORM=linux/amd64 +# or +# --platform linux/amd64 + +RUN_IN_DOCKER:=docker run -it --rm --platform linux/amd64 +RUN_IN_DOCKER+=-v ~/.ssh:/root/.ssh +RUN_IN_DOCKER+=-v ~/.gitconfig:/root/.gitconfig +RUN_IN_DOCKER+=-v ~/go/pkg/mod:/go/pkg/mod + +kclvm-builder: + docker build --platform linux/amd64 -t ${BUILDER_IMAGE} . + @echo "ok" + +publish-builder: + # https://docker.inc.com/ + # docker login --username= + + # make kclvm-builder + docker push ${BUILDER_IMAGE} + @echo "push ${BUILDER_IMAGE} ok" + +sh: + ${RUN_IN_DOCKER} -v ${PWD}/../../..:/root/kclvm -w /root ${BUILDER_IMAGE} bash + +clean: diff --git a/scripts/docker/kclvm-builder-centos7/timestamp.go b/scripts/docker/kclvm-builder-centos7/timestamp.go new file mode 100644 index 000000000..96d8a87dc --- /dev/null +++ b/scripts/docker/kclvm-builder-centos7/timestamp.go @@ -0,0 +1,20 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ignore +// +build ignore + +package main + +import ( + "fmt" + "time" +) + +func main() { + t := time.Now() + fmt.Printf( + "%04d%02d%02d-%02d%02d%02d", + t.Year(), t.Month(), t.Day(), + t.Hour(), t.Minute(), t.Second(), + ) +} diff --git a/scripts/docker/kclvm-builder-centos8/Dockerfile b/scripts/docker/kclvm-builder-centos8/Dockerfile new file mode 100644 index 000000000..ff30e1451 --- /dev/null +++ b/scripts/docker/kclvm-builder-centos8/Dockerfile @@ -0,0 +1,74 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +FROM centos:centos8 + +# macOS M1 --platform linux/amd64 +# try fix "Problem with the SSL CA cert (path? access rights?)" +# https://issueexplorer.com/issue/docker/for-mac/5912 +# https://access.redhat.com/articles/2050743 +RUN touch /etc/sysconfig/64bit_strstr_via_64bit_strstr_sse2_unaligned + +RUN yum -y install make +RUN yum -y install which +RUN yum -y install wget +RUN yum -y install git + +# ca-certificates +RUN yum -y install ca-certificates + +# rust-1.54.0 +# cargo 1.54.0 +RUN yum -y install rust cargo rustfmt +# RUN curl https://sh.rustup.rs -sSf | bash -s -- -y +RUN echo 'source $HOME/.cargo/env' >> $HOME/.bashrc + +ENV PATH="/root/.cargo/bin:${PATH}" +ENV CARGO_NET_GIT_FETCH_WITH_CLI=true + +RUN cargo version +RUN rustc --version + +# clang-12 +RUN yum -y install clang +RUN clang --version + +# llvm-12 +RUN yum -y install llvm-devel +RUN yum -y install libffi-devel +RUN ln -s /usr/lib64/libtinfo.so.6 /usr/lib64/libtinfo.so + +# Go 1.6 +RUN yum -y install golang +RUN go version + +# /usr/lib64/python3.9 +RUN yum -y install python39-devel +RUN python3 -m pip install pytest + +# golang apps +RUN go get golang.org/x/lint/golint +RUN go get golang.org/x/tools/cmd/goimports +#RUN go get honnef.co/go/tools/cmd/... + +RUN go get github.com/t-yuki/gocover-cobertura +RUN go get github.com/jstemmer/go-junit-report + +RUN rm -rf /go/pkg/mod +RUN rm -rf /go/pkg/sumdb + +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime +RUN echo 'Asia/Shanghai' >/etc/timezone + +#COPY ./vendor-kclvm /root/vendor-kclvm +#COPY ./vendor-kclvm-runtime /root/vendor-kclvm-runtime + +COPY ./crates.io-index /root/crates.io-index + +RUN mkdir -p /root/.cargo +COPY ./_cargo_config-kclvm.toml /root/.cargo/config + +RUN touch /root/.cargo/env + +WORKDIR /root + +CMD ["bash"] diff --git a/scripts/docker/kclvm-builder-centos8/Makefile b/scripts/docker/kclvm-builder-centos8/Makefile new file mode 100644 index 000000000..ae29d16bd --- /dev/null +++ b/scripts/docker/kclvm-builder-centos8/Makefile @@ -0,0 +1,57 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +PWD:=$(shell pwd) +TIMESTAMP:=$(shell go run timestamp.go) + +BUILDER_IMAGE:=reg.docker.inc.com/kusionstack/kclvm-builder-centos8 + +# export DOCKER_DEFAULT_PLATFORM=linux/amd64 +# or +# --platform linux/amd64 + +RUN_IN_DOCKER:=docker run -it --rm --platform linux/amd64 +RUN_IN_DOCKER+=-v ~/.ssh:/root/.ssh +RUN_IN_DOCKER+=-v ~/.gitconfig:/root/.gitconfig +RUN_IN_DOCKER+=-v ~/go/pkg/mod:/go/pkg/mod + +kclvm-builder: crates.io-index + docker build --platform linux/amd64 -t ${BUILDER_IMAGE} . + @echo "ok" + +crates.io-index: + git clone --mirror git@github.com:rust-lang/crates.io-index.git crates.io-index + +vendor-kclvm: + -rm -rf ${PWD}/../../kclvm/vendor + + ${RUN_IN_DOCKER} -v ${PWD}/../..:/root/kclvm -w /root/kclvm/kclvm rust:1.54 cargo vendor --versioned-dirs + mv ${PWD}/../../kclvm/vendor ./vendor-kclvm + + +vendor-kclvm-runtime: + -rm -rf ${PWD}/../../kclvm/runtime/vendor + + ${RUN_IN_DOCKER} -v ${PWD}/../..:/root/kclvm -w /root/kclvm/kclvm/runtime rust:1.54 cargo vendor --versioned-dirs + mv ${PWD}/../../kclvm/runtime/vendor ./vendor-kclvm-runtime + +publish-builder: + # https://docker.inc.com/ + # docker login --username= reg.docker.inc.com + + # make kclvm-builder + docker push ${BUILDER_IMAGE} + @echo "push ${BUILDER_IMAGE} ok" + +publish-builder-tagged: + # https://docker.inc.com/ + # docker login --username= reg.docker.inc.com + + # make kclvm-builder + docker tag ${BUILDER_IMAGE} ${BUILDER_IMAGE}:${TIMESTAMP} + docker push ${BUILDER_IMAGE}:${TIMESTAMP} + @echo "push ${BUILDER_IMAGE}:${TIMESTAMP} ok" + +sh-in-builder: + ${RUN_IN_DOCKER} -v ${PWD}/../..:/root/kclvm -w /root ${BUILDER_IMAGE} bash + +clean: diff --git a/scripts/docker/kclvm-builder-centos8/_cargo_config-kclvmx.toml b/scripts/docker/kclvm-builder-centos8/_cargo_config-kclvmx.toml new file mode 100644 index 000000000..e268e03a6 --- /dev/null +++ b/scripts/docker/kclvm-builder-centos8/_cargo_config-kclvmx.toml @@ -0,0 +1,10 @@ +# https://stackoverflow.com/questions/31029095/disable-registry-update-in-cargo +# https://doc.rust-lang.org/cargo/reference/config.html + +# /root/.cargo/config + +[source.crates-io] +replace-with = 'local' + +[source.local] +registry = "file:///root/crates.io-index" diff --git a/scripts/docker/kclvm-builder-centos8/timestamp.go b/scripts/docker/kclvm-builder-centos8/timestamp.go new file mode 100644 index 000000000..96d8a87dc --- /dev/null +++ b/scripts/docker/kclvm-builder-centos8/timestamp.go @@ -0,0 +1,20 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ignore +// +build ignore + +package main + +import ( + "fmt" + "time" +) + +func main() { + t := time.Now() + fmt.Printf( + "%04d%02d%02d-%02d%02d%02d", + t.Year(), t.Month(), t.Day(), + t.Hour(), t.Minute(), t.Second(), + ) +} diff --git a/scripts/docker/kclvm-builder/Dockerfile b/scripts/docker/kclvm-builder/Dockerfile new file mode 100644 index 000000000..7c9482c8c --- /dev/null +++ b/scripts/docker/kclvm-builder/Dockerfile @@ -0,0 +1,72 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +FROM ubuntu:20.04 + +#RUN uname -a +#RUN cat /etc/os-release + +RUN apt-get update + +RUN apt-get install -y git wget curl +RUN apt-get install -y make gcc patch g++ swig +RUN apt-get install -y python-dev libffi-dev +# SSL module deps sed by python3 +RUN apt-get install -y zlib1g-dev ncurses-dev build-essential libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev + +# python-3.9 +RUN mkdir -p /root/download && cd /root/download \ + && wget http://npm.taobao.org/mirrors/python/3.9.10/Python-3.9.10.tgz \ + && tar -xzf Python-3.9.10.tgz \ + && cd Python-3.9.10 \ + && LANG=C.UTF-8 ./configure \ + --prefix=/usr/local/python3.9 \ + --enable-optimizations \ + --with-ssl \ + && make install +RUN ln -sf /usr/local/python3.9/bin/python3.9 /usr/bin/python3 +RUN ln -sf /usr/local/python3.9/bin/python3.9 /usr/bin/python3.9 + +# rust +# https://www.rust-lang.org/tools/install +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" +ENV CARGO_NET_GIT_FETCH_WITH_CLI=true + +RUN cargo version +RUN rustc --version + +# wasm +RUN rustup target add wasm32-unknown-unknown + +# clang12 +RUN apt-get install -y clang-12 lld-12 +RUN ln -sf /usr/bin/clang-12 /usr/bin/clang +RUN ln -sf /usr/bin/wasm-ld-12 /usr/bin/wasm-ld + +# golang 1.17+ +RUN mkdir -p /root/download && cd /root/download \ + && wget https://dl.google.com/go/go1.17.3.linux-amd64.tar.gz \ + && tar -zxvf go1.17.3.linux-amd64.tar.gz \ + && mv ./go /usr/local/go1.17.3 +RUN ln -sf /usr/local/go1.17.3/bin/go /usr/bin/go +RUN rm -rf /root/download + +ENV GOPATH=/go +ENV GOLANG_VERSION=1.17.3 + +RUN go get golang.org/x/lint/golint +RUN go get golang.org/x/tools/cmd/goimports +RUN go get honnef.co/go/tools/cmd/... + +RUN go get github.com/t-yuki/gocover-cobertura +RUN go get github.com/jstemmer/go-junit-report + +RUN rm -rf /go/pkg/mod +RUN rm -rf /go/pkg/sumdb + +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime +RUN echo 'Asia/Shanghai' >/etc/timezone + +WORKDIR /root + +CMD ["bash"] diff --git a/scripts/docker/kclvm-builder/Makefile b/scripts/docker/kclvm-builder/Makefile new file mode 100644 index 000000000..5fbabadd3 --- /dev/null +++ b/scripts/docker/kclvm-builder/Makefile @@ -0,0 +1,31 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +PWD:=$(shell pwd) + +BUILDER_IMAGE:=kusionstack/kclvm-builder + +# export DOCKER_DEFAULT_PLATFORM=linux/amd64 +# or +# --platform linux/amd64 + +RUN_IN_DOCKER:=docker run -it --rm --platform linux/amd64 +RUN_IN_DOCKER+=-v ~/.ssh:/root/.ssh +RUN_IN_DOCKER+=-v ~/.gitconfig:/root/.gitconfig +RUN_IN_DOCKER+=-v ~/go/pkg/mod:/go/pkg/mod + +kclvm-builder: + docker build --platform linux/amd64 -t ${BUILDER_IMAGE} . + @echo "ok" + +publish-builder: + # https://docker.inc.com/ + # docker login --username= + + # make kclvm-builder + docker push ${BUILDER_IMAGE} + @echo "push ${BUILDER_IMAGE} ok" + +sh: + ${RUN_IN_DOCKER} -v ${PWD}/../../..:/root/kclvm -w /root ${BUILDER_IMAGE} bash + +clean: diff --git a/scripts/docker/kclvm/.keep b/scripts/docker/kclvm/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/assert/assert_if/assert_if_0/main.k b/test/grammar/assert/assert_if/assert_if_0/main.k new file mode 100644 index 000000000..90bcccbb8 --- /dev/null +++ b/test/grammar/assert/assert_if/assert_if_0/main.k @@ -0,0 +1,4 @@ +assert True +assert 1 == 1 if True +_x = "good case" +assert _x == "good case" if _x, "_x need to be 'good case'" diff --git a/test/grammar/assert/assert_if/assert_if_0/stdout.golden b/test/grammar/assert/assert_if/assert_if_0/stdout.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/assert/assert_if/assert_if_1/main.k b/test/grammar/assert/assert_if/assert_if_1/main.k new file mode 100644 index 000000000..123910a7d --- /dev/null +++ b/test/grammar/assert/assert_if/assert_if_1/main.k @@ -0,0 +1,4 @@ +assert True if False +assert 1 == 1 if False +_x = "good case" +assert _x == "good case" if not _x, "_x need to be 'good case'" diff --git a/test/grammar/assert/assert_if/assert_if_1/stdout.golden b/test/grammar/assert/assert_if/assert_if_1/stdout.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/assert/assert_if/assert_if_2/main.k b/test/grammar/assert/assert_if/assert_if_2/main.k new file mode 100644 index 000000000..f80453656 --- /dev/null +++ b/test/grammar/assert/assert_if/assert_if_2/main.k @@ -0,0 +1,7 @@ +schema Data: + assert True if False + assert 1 == 1 if False + _x = "good case" + assert _x == "good case" if not _x, "_x need to be 'good case'" + +data = Data {} diff --git a/test/grammar/assert/assert_if/assert_if_2/stdout.golden b/test/grammar/assert/assert_if/assert_if_2/stdout.golden new file mode 100644 index 000000000..603819cac --- /dev/null +++ b/test/grammar/assert/assert_if/assert_if_2/stdout.golden @@ -0,0 +1 @@ +data: {} diff --git a/test/grammar/assert/invalid/fail_0/main.k b/test/grammar/assert/invalid/fail_0/main.k new file mode 100644 index 000000000..2cfffa40d --- /dev/null +++ b/test/grammar/assert/invalid/fail_0/main.k @@ -0,0 +1 @@ +assert False diff --git a/test/grammar/assert/invalid/fail_0/stderr.golden.py b/test/grammar/assert/invalid/fail_0/stderr.golden.py new file mode 100644 index 000000000..e559a04cc --- /dev/null +++ b/test/grammar/assert/invalid/fail_0/stderr.golden.py @@ -0,0 +1,15 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.AssertionError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1 + )] + ), + file=sys.stdout) diff --git a/test/grammar/assert/invalid/fail_1/main.k b/test/grammar/assert/invalid/fail_1/main.k new file mode 100644 index 000000000..f5def530f --- /dev/null +++ b/test/grammar/assert/invalid/fail_1/main.k @@ -0,0 +1 @@ +assert 1 == 2, '1 is not equal to 2' diff --git a/test/grammar/assert/invalid/fail_1/stderr.golden.py b/test/grammar/assert/invalid/fail_1/stderr.golden.py new file mode 100644 index 000000000..1ae5f6c7b --- /dev/null +++ b/test/grammar/assert/invalid/fail_1/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.AssertionError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1 + ) + ], + arg_msg="1 is not equal to 2" + ) + , file=sys.stdout +) + diff --git a/test/grammar/assert/invalid/fail_2/main.k b/test/grammar/assert/invalid/fail_2/main.k new file mode 100644 index 000000000..47d4dc3be --- /dev/null +++ b/test/grammar/assert/invalid/fail_2/main.k @@ -0,0 +1,2 @@ +_x = "good case" +assert _x == "bad case", "x should be 'good case'" diff --git a/test/grammar/assert/invalid/fail_2/stderr.golden.py b/test/grammar/assert/invalid/fail_2/stderr.golden.py new file mode 100644 index 000000000..b09935282 --- /dev/null +++ b/test/grammar/assert/invalid/fail_2/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.AssertionError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2 + ) + ], + arg_msg="x should be 'good case'" + ) + , file=sys.stdout +) + diff --git a/test/grammar/assert/invalid/fail_3/main.k b/test/grammar/assert/invalid/fail_3/main.k new file mode 100644 index 000000000..36e99cf01 --- /dev/null +++ b/test/grammar/assert/invalid/fail_3/main.k @@ -0,0 +1,3 @@ +_x = "good case" +assert _x == "bad case" if not _x, "x should be 'good case'" +assert _x == "bad case" if _x, "x should be 'good case'" diff --git a/test/grammar/assert/invalid/fail_3/stderr.golden.py b/test/grammar/assert/invalid/fail_3/stderr.golden.py new file mode 100644 index 000000000..ced74dad3 --- /dev/null +++ b/test/grammar/assert/invalid/fail_3/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.AssertionError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3 + ) + ], + arg_msg="x should be 'good case'" + ) + , file=sys.stdout +) + diff --git a/test/grammar/assert/valid/valid_0/main.k b/test/grammar/assert/valid/valid_0/main.k new file mode 100644 index 000000000..6665b9613 --- /dev/null +++ b/test/grammar/assert/valid/valid_0/main.k @@ -0,0 +1,4 @@ +assert True +assert 1 == 1 +_x = "good case" +assert _x == "good case" diff --git a/test/grammar/assert/valid/valid_0/stdout.golden b/test/grammar/assert/valid/valid_0/stdout.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/assert/valid/valid_1/main.k b/test/grammar/assert/valid/valid_1/main.k new file mode 100644 index 000000000..19806ec4f --- /dev/null +++ b/test/grammar/assert/valid/valid_1/main.k @@ -0,0 +1,7 @@ +if True: + assert True, "Error messgae" + assert 1 == 1, "" + _x = "good case" + assert _x == "good case" +else: + assert False diff --git a/test/grammar/assert/valid/valid_1/stdout.golden b/test/grammar/assert/valid/valid_1/stdout.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/attr_operator/config_inside/insert/dict_0/main.k b/test/grammar/attr_operator/config_inside/insert/dict_0/main.k new file mode 100644 index 000000000..4e6e8ce97 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/insert/dict_0/main.k @@ -0,0 +1,12 @@ +config = { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: { + env += [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/insert/dict_0/stdout.golden b/test/grammar/attr_operator/config_inside/insert/dict_0/stdout.golden new file mode 100644 index 000000000..853930d54 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/insert/dict_0/stdout.golden @@ -0,0 +1,7 @@ +config: + main: + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/config_inside/insert/dict_1/main.k b/test/grammar/attr_operator/config_inside/insert/dict_1/main.k new file mode 100644 index 000000000..0947f0aef --- /dev/null +++ b/test/grammar/attr_operator/config_inside/insert/dict_1/main.k @@ -0,0 +1,15 @@ +config = { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: { + env += [ + {name: "ENV_2", value: "2"} + ] + env += [ + {name: "ENV_3", value: "3"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/insert/dict_1/stdout.golden b/test/grammar/attr_operator/config_inside/insert/dict_1/stdout.golden new file mode 100644 index 000000000..1874b50dd --- /dev/null +++ b/test/grammar/attr_operator/config_inside/insert/dict_1/stdout.golden @@ -0,0 +1,9 @@ +config: + main: + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' + - name: ENV_3 + value: '3' diff --git a/test/grammar/attr_operator/config_inside/insert/schema_0/main.k b/test/grammar/attr_operator/config_inside/insert/schema_0/main.k new file mode 100644 index 000000000..a5be9b755 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/insert/schema_0/main.k @@ -0,0 +1,24 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +_main = Main { + env: [ + {name: "ENV_1", value: "1"} + ] +} + +config = Config { + main: _main + main: Main { + env += [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/insert/schema_0/stdout.golden b/test/grammar/attr_operator/config_inside/insert/schema_0/stdout.golden new file mode 100644 index 000000000..853930d54 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/insert/schema_0/stdout.golden @@ -0,0 +1,7 @@ +config: + main: + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/config_inside/insert/schema_1/main.k b/test/grammar/attr_operator/config_inside/insert/schema_1/main.k new file mode 100644 index 000000000..57536e59a --- /dev/null +++ b/test/grammar/attr_operator/config_inside/insert/schema_1/main.k @@ -0,0 +1,25 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +config = Config { + main: Main { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: Main { + env += [ + {name: "ENV_2", value: "2"} + ] + env += [ + {name: "ENV_3", value: "3"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/insert/schema_1/stdout.golden b/test/grammar/attr_operator/config_inside/insert/schema_1/stdout.golden new file mode 100644 index 000000000..1874b50dd --- /dev/null +++ b/test/grammar/attr_operator/config_inside/insert/schema_1/stdout.golden @@ -0,0 +1,9 @@ +config: + main: + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' + - name: ENV_3 + value: '3' diff --git a/test/grammar/attr_operator/config_inside/insert/schema_2/main.k b/test/grammar/attr_operator/config_inside/insert/schema_2/main.k new file mode 100644 index 000000000..ea70268ba --- /dev/null +++ b/test/grammar/attr_operator/config_inside/insert/schema_2/main.k @@ -0,0 +1,28 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +config: Config { + main: Main { + env: [ + {name: "ENV_1", value: "1"} + ] + } +} + +config: Config { + main: Main { + env += [ + {name: "ENV_2", value: "2"} + ] + env += [ + {name: "ENV_3", value: "3"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/insert/schema_2/stdout.golden b/test/grammar/attr_operator/config_inside/insert/schema_2/stdout.golden new file mode 100644 index 000000000..1874b50dd --- /dev/null +++ b/test/grammar/attr_operator/config_inside/insert/schema_2/stdout.golden @@ -0,0 +1,9 @@ +config: + main: + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' + - name: ENV_3 + value: '3' diff --git a/test/grammar/attr_operator/config_inside/override/dict_0/main.k b/test/grammar/attr_operator/config_inside/override/dict_0/main.k new file mode 100644 index 000000000..08b2632b8 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/override/dict_0/main.k @@ -0,0 +1,12 @@ +config = { + main: { + env = [ + {name: "ENV_1", value: "1"} + ] + } + main: { + env = [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/override/dict_0/stdout.golden b/test/grammar/attr_operator/config_inside/override/dict_0/stdout.golden new file mode 100644 index 000000000..77b6b1b9d --- /dev/null +++ b/test/grammar/attr_operator/config_inside/override/dict_0/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/config_inside/override/dict_1/main.k b/test/grammar/attr_operator/config_inside/override/dict_1/main.k new file mode 100644 index 000000000..c18aeb9b0 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/override/dict_1/main.k @@ -0,0 +1,15 @@ +config = { + main: { + env = [ + {name: "ENV_1", value: "1"} + ] + } + main: { + env = [ + {name: "ENV_2", value: "2"} + ] + env = [ + {name: "ENV_3", value: "3"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/override/dict_1/stdout.golden b/test/grammar/attr_operator/config_inside/override/dict_1/stdout.golden new file mode 100644 index 000000000..37f7be1fb --- /dev/null +++ b/test/grammar/attr_operator/config_inside/override/dict_1/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_3 + value: '3' diff --git a/test/grammar/attr_operator/config_inside/override/schema_0/main.k b/test/grammar/attr_operator/config_inside/override/schema_0/main.k new file mode 100644 index 000000000..760e4c24a --- /dev/null +++ b/test/grammar/attr_operator/config_inside/override/schema_0/main.k @@ -0,0 +1,24 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +_main = Main { + env: [ + {name: "ENV_1", value: "1"} + ] +} + +config = Config { + main: _main + main: Main { + env = [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/override/schema_0/stdout.golden b/test/grammar/attr_operator/config_inside/override/schema_0/stdout.golden new file mode 100644 index 000000000..77b6b1b9d --- /dev/null +++ b/test/grammar/attr_operator/config_inside/override/schema_0/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/config_inside/override/schema_1/main.k b/test/grammar/attr_operator/config_inside/override/schema_1/main.k new file mode 100644 index 000000000..5f8a1d498 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/override/schema_1/main.k @@ -0,0 +1,25 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +config = Config { + main: Main { + env = [ + {name: "ENV_1", value: "1"} + ] + } + main: Main { + env = [ + {name: "ENV_2", value: "2"} + ] + env = [ + {name: "ENV_3", value: "3"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/override/schema_1/stdout.golden b/test/grammar/attr_operator/config_inside/override/schema_1/stdout.golden new file mode 100644 index 000000000..37f7be1fb --- /dev/null +++ b/test/grammar/attr_operator/config_inside/override/schema_1/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_3 + value: '3' diff --git a/test/grammar/attr_operator/config_inside/override/schema_2/main.k b/test/grammar/attr_operator/config_inside/override/schema_2/main.k new file mode 100644 index 000000000..a412df0f5 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/override/schema_2/main.k @@ -0,0 +1,28 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +config: Config { + main: Main { + env = [ + {name: "ENV_1", value: "1"} + ] + } +} + +config: Config { + main: Main { + env += [ + {name: "ENV_2", value: "2"} + ] + env = [ + {name: "ENV_3", value: "3"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/override/schema_2/stdout.golden b/test/grammar/attr_operator/config_inside/override/schema_2/stdout.golden new file mode 100644 index 000000000..37f7be1fb --- /dev/null +++ b/test/grammar/attr_operator/config_inside/override/schema_2/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_3 + value: '3' diff --git a/test/grammar/attr_operator/config_inside/unification/dict_0/main.k b/test/grammar/attr_operator/config_inside/unification/dict_0/main.k new file mode 100644 index 000000000..808197d1f --- /dev/null +++ b/test/grammar/attr_operator/config_inside/unification/dict_0/main.k @@ -0,0 +1,12 @@ +config = { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/unification/dict_0/stdout.golden b/test/grammar/attr_operator/config_inside/unification/dict_0/stdout.golden new file mode 100644 index 000000000..e128b2406 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/unification/dict_0/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_1 + value: '1' diff --git a/test/grammar/attr_operator/config_inside/unification/dict_1/main.k b/test/grammar/attr_operator/config_inside/unification/dict_1/main.k new file mode 100644 index 000000000..c9c2f8097 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/unification/dict_1/main.k @@ -0,0 +1,15 @@ +config = { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + env: [ + {name: "ENV_1", value: "1"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/unification/dict_1/stdout.golden b/test/grammar/attr_operator/config_inside/unification/dict_1/stdout.golden new file mode 100644 index 000000000..e128b2406 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/unification/dict_1/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_1 + value: '1' diff --git a/test/grammar/attr_operator/config_inside/unification/schema_0/main.k b/test/grammar/attr_operator/config_inside/unification/schema_0/main.k new file mode 100644 index 000000000..1c50f52d2 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/unification/schema_0/main.k @@ -0,0 +1,27 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +_main = Main { + env: [ + {name: "ENV_1", value: "1"} + ] +} + +config: Config { + main: _main +} + +config: Config { + main: Main { + env: [ + {name: "ENV_1", value: "1"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/unification/schema_0/stdout.golden b/test/grammar/attr_operator/config_inside/unification/schema_0/stdout.golden new file mode 100644 index 000000000..e128b2406 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/unification/schema_0/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_1 + value: '1' diff --git a/test/grammar/attr_operator/config_inside/unification/schema_1/main.k b/test/grammar/attr_operator/config_inside/unification/schema_1/main.k new file mode 100644 index 000000000..2d754b73b --- /dev/null +++ b/test/grammar/attr_operator/config_inside/unification/schema_1/main.k @@ -0,0 +1,25 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +config = Config { + main: Main { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: Main { + env: [ + {name: "ENV_1", value: "1"} + ] + env: [ + {name: "ENV_1", value: "1"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/unification/schema_1/stdout.golden b/test/grammar/attr_operator/config_inside/unification/schema_1/stdout.golden new file mode 100644 index 000000000..e128b2406 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/unification/schema_1/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_1 + value: '1' diff --git a/test/grammar/attr_operator/config_inside/unification/schema_2/main.k b/test/grammar/attr_operator/config_inside/unification/schema_2/main.k new file mode 100644 index 000000000..d98331cbe --- /dev/null +++ b/test/grammar/attr_operator/config_inside/unification/schema_2/main.k @@ -0,0 +1,33 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +config: Config { + main: Main { + env: [ + {name: "ENV_1", value: "1"} + ] + } +} + +config: Config { + main: Main { + env: [ + {name: "ENV_1", value: "1"} + ] + } +} + +config: Config { + main: Main { + env: [ + {name: "ENV_1", value: "1"} + ] + } +} diff --git a/test/grammar/attr_operator/config_inside/unification/schema_2/stdout.golden b/test/grammar/attr_operator/config_inside/unification/schema_2/stdout.golden new file mode 100644 index 000000000..e128b2406 --- /dev/null +++ b/test/grammar/attr_operator/config_inside/unification/schema_2/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_1 + value: '1' diff --git a/test/grammar/attr_operator/if_entry/insert/dict_0/main.k b/test/grammar/attr_operator/if_entry/insert/dict_0/main.k new file mode 100644 index 000000000..8311b8f2d --- /dev/null +++ b/test/grammar/attr_operator/if_entry/insert/dict_0/main.k @@ -0,0 +1,13 @@ +config = { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + if True: + main: { + env += [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/if_entry/insert/dict_0/stdout.golden b/test/grammar/attr_operator/if_entry/insert/dict_0/stdout.golden new file mode 100644 index 000000000..853930d54 --- /dev/null +++ b/test/grammar/attr_operator/if_entry/insert/dict_0/stdout.golden @@ -0,0 +1,7 @@ +config: + main: + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/if_entry/insert/dict_1/main.k b/test/grammar/attr_operator/if_entry/insert/dict_1/main.k new file mode 100644 index 000000000..70200be92 --- /dev/null +++ b/test/grammar/attr_operator/if_entry/insert/dict_1/main.k @@ -0,0 +1,13 @@ +config = { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: { + if True: + env += [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/if_entry/insert/dict_1/stdout.golden b/test/grammar/attr_operator/if_entry/insert/dict_1/stdout.golden new file mode 100644 index 000000000..853930d54 --- /dev/null +++ b/test/grammar/attr_operator/if_entry/insert/dict_1/stdout.golden @@ -0,0 +1,7 @@ +config: + main: + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/if_entry/insert/dict_2/main.k b/test/grammar/attr_operator/if_entry/insert/dict_2/main.k new file mode 100644 index 000000000..b329433e5 --- /dev/null +++ b/test/grammar/attr_operator/if_entry/insert/dict_2/main.k @@ -0,0 +1,19 @@ +schema Main: + env: [{str:str}] + +schema Config: + main: Main + +config = Config { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: { + if True: + env += [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/if_entry/insert/dict_2/stdout.golden b/test/grammar/attr_operator/if_entry/insert/dict_2/stdout.golden new file mode 100644 index 000000000..853930d54 --- /dev/null +++ b/test/grammar/attr_operator/if_entry/insert/dict_2/stdout.golden @@ -0,0 +1,7 @@ +config: + main: + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/if_entry/override/dict_0/main.k b/test/grammar/attr_operator/if_entry/override/dict_0/main.k new file mode 100644 index 000000000..8cd49479a --- /dev/null +++ b/test/grammar/attr_operator/if_entry/override/dict_0/main.k @@ -0,0 +1,13 @@ +config = { + main: { + env = [ + {name: "ENV_1", value: "1"} + ] + } + if True: + main: { + env = [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/if_entry/override/dict_0/stdout.golden b/test/grammar/attr_operator/if_entry/override/dict_0/stdout.golden new file mode 100644 index 000000000..77b6b1b9d --- /dev/null +++ b/test/grammar/attr_operator/if_entry/override/dict_0/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/if_entry/override/dict_1/main.k b/test/grammar/attr_operator/if_entry/override/dict_1/main.k new file mode 100644 index 000000000..f2cd09b23 --- /dev/null +++ b/test/grammar/attr_operator/if_entry/override/dict_1/main.k @@ -0,0 +1,19 @@ +config = { + main: { + env = [ + {name: "ENV_1", value: "1"} + ] + } + if False: + main: { + env = [ + {name: "ENV_2", value: "2"} + ] + } + else: + main: { + env = [ + {name: "ENV_3", value: "3"} + ] + } +} diff --git a/test/grammar/attr_operator/if_entry/override/dict_1/stdout.golden b/test/grammar/attr_operator/if_entry/override/dict_1/stdout.golden new file mode 100644 index 000000000..37f7be1fb --- /dev/null +++ b/test/grammar/attr_operator/if_entry/override/dict_1/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_3 + value: '3' diff --git a/test/grammar/attr_operator/if_entry/override/dict_2/main.k b/test/grammar/attr_operator/if_entry/override/dict_2/main.k new file mode 100644 index 000000000..c0bb36394 --- /dev/null +++ b/test/grammar/attr_operator/if_entry/override/dict_2/main.k @@ -0,0 +1,25 @@ +schema Main: + env: [{str:str}] + +schema Config: + main: Main + +config = Config { + main: { + env = [ + {name: "ENV_1", value: "1"} + ] + } + if False: + main: { + env = [ + {name: "ENV_2", value: "2"} + ] + } + else: + main: { + env = [ + {name: "ENV_3", value: "3"} + ] + } +} diff --git a/test/grammar/attr_operator/if_entry/override/dict_2/stdout.golden b/test/grammar/attr_operator/if_entry/override/dict_2/stdout.golden new file mode 100644 index 000000000..37f7be1fb --- /dev/null +++ b/test/grammar/attr_operator/if_entry/override/dict_2/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_3 + value: '3' diff --git a/test/grammar/attr_operator/if_entry/unification/dict_0/main.k b/test/grammar/attr_operator/if_entry/unification/dict_0/main.k new file mode 100644 index 000000000..ae077d2f5 --- /dev/null +++ b/test/grammar/attr_operator/if_entry/unification/dict_0/main.k @@ -0,0 +1,13 @@ +config = { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + if True: + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } +} diff --git a/test/grammar/attr_operator/if_entry/unification/dict_0/stdout.golden b/test/grammar/attr_operator/if_entry/unification/dict_0/stdout.golden new file mode 100644 index 000000000..e128b2406 --- /dev/null +++ b/test/grammar/attr_operator/if_entry/unification/dict_0/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_1 + value: '1' diff --git a/test/grammar/attr_operator/if_entry/unification/dict_1/main.k b/test/grammar/attr_operator/if_entry/unification/dict_1/main.k new file mode 100644 index 000000000..c9861953c --- /dev/null +++ b/test/grammar/attr_operator/if_entry/unification/dict_1/main.k @@ -0,0 +1,13 @@ +config = { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: { + if True: + env: [ + {name: "ENV_1", value: "1"} + ] + } +} diff --git a/test/grammar/attr_operator/if_entry/unification/dict_1/stdout.golden b/test/grammar/attr_operator/if_entry/unification/dict_1/stdout.golden new file mode 100644 index 000000000..e128b2406 --- /dev/null +++ b/test/grammar/attr_operator/if_entry/unification/dict_1/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_1 + value: '1' diff --git a/test/grammar/attr_operator/if_entry/unification/dict_2/main.k b/test/grammar/attr_operator/if_entry/unification/dict_2/main.k new file mode 100644 index 000000000..390fbe118 --- /dev/null +++ b/test/grammar/attr_operator/if_entry/unification/dict_2/main.k @@ -0,0 +1,19 @@ +schema Main: + env: [{str:str}] + +schema Config: + main: Main + +config = Config { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: { + if True: + env: [ + {name: "ENV_1", value: "1"} + ] + } +} diff --git a/test/grammar/attr_operator/if_entry/unification/dict_2/stdout.golden b/test/grammar/attr_operator/if_entry/unification/dict_2/stdout.golden new file mode 100644 index 000000000..e128b2406 --- /dev/null +++ b/test/grammar/attr_operator/if_entry/unification/dict_2/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_1 + value: '1' diff --git a/test/grammar/attr_operator/nest_var/insert/dict_0/main.k b/test/grammar/attr_operator/nest_var/insert/dict_0/main.k new file mode 100644 index 000000000..893dd2ca6 --- /dev/null +++ b/test/grammar/attr_operator/nest_var/insert/dict_0/main.k @@ -0,0 +1,5 @@ +config = { + spec: {data: [1]} + spec.data += [2] + spec.data += [3] +} diff --git a/test/grammar/attr_operator/nest_var/insert/dict_0/stdout.golden b/test/grammar/attr_operator/nest_var/insert/dict_0/stdout.golden new file mode 100644 index 000000000..be8349e4c --- /dev/null +++ b/test/grammar/attr_operator/nest_var/insert/dict_0/stdout.golden @@ -0,0 +1,6 @@ +config: + spec: + data: + - 1 + - 2 + - 3 diff --git a/test/grammar/attr_operator/nest_var/insert/dict_1/main.k b/test/grammar/attr_operator/nest_var/insert/dict_1/main.k new file mode 100644 index 000000000..38fc1b9c2 --- /dev/null +++ b/test/grammar/attr_operator/nest_var/insert/dict_1/main.k @@ -0,0 +1,5 @@ +config = { + spec.data = [1] + spec.data += [2] + spec: {data += [3]} +} diff --git a/test/grammar/attr_operator/nest_var/insert/dict_1/stdout.golden b/test/grammar/attr_operator/nest_var/insert/dict_1/stdout.golden new file mode 100644 index 000000000..be8349e4c --- /dev/null +++ b/test/grammar/attr_operator/nest_var/insert/dict_1/stdout.golden @@ -0,0 +1,6 @@ +config: + spec: + data: + - 1 + - 2 + - 3 diff --git a/test/grammar/attr_operator/nest_var/insert/dict_2/main.k b/test/grammar/attr_operator/nest_var/insert/dict_2/main.k new file mode 100644 index 000000000..b68e51638 --- /dev/null +++ b/test/grammar/attr_operator/nest_var/insert/dict_2/main.k @@ -0,0 +1,11 @@ +schema Config: + spec: {str:} = { + replicas: 1 + data: [0] + } + +data = Config { + spec.labels: {key: "value"} + spec: {replicas = 2} + spec.data += [1] +} diff --git a/test/grammar/attr_operator/nest_var/insert/dict_2/stdout.golden b/test/grammar/attr_operator/nest_var/insert/dict_2/stdout.golden new file mode 100644 index 000000000..2a4879a33 --- /dev/null +++ b/test/grammar/attr_operator/nest_var/insert/dict_2/stdout.golden @@ -0,0 +1,8 @@ +data: + spec: + replicas: 2 + data: + - 0 + - 1 + labels: + key: value diff --git a/test/grammar/attr_operator/nest_var/override/dict_0/main.k b/test/grammar/attr_operator/nest_var/override/dict_0/main.k new file mode 100644 index 000000000..aba8f68d2 --- /dev/null +++ b/test/grammar/attr_operator/nest_var/override/dict_0/main.k @@ -0,0 +1,6 @@ +labels = {key: "value"} +config = { + spec: {replicas: 1} + spec.labels: labels + spec.replicas = 2 +} diff --git a/test/grammar/attr_operator/nest_var/override/dict_0/stdout.golden b/test/grammar/attr_operator/nest_var/override/dict_0/stdout.golden new file mode 100644 index 000000000..b555b7b38 --- /dev/null +++ b/test/grammar/attr_operator/nest_var/override/dict_0/stdout.golden @@ -0,0 +1,7 @@ +labels: + key: value +config: + spec: + replicas: 2 + labels: + key: value diff --git a/test/grammar/attr_operator/nest_var/override/dict_1/main.k b/test/grammar/attr_operator/nest_var/override/dict_1/main.k new file mode 100644 index 000000000..e7429047c --- /dev/null +++ b/test/grammar/attr_operator/nest_var/override/dict_1/main.k @@ -0,0 +1,7 @@ +_labels = {key1: "value1"} +config = { + spec: {replicas: 1} + spec.labels: _labels + spec.labels: {key2: "value2"} + spec.replicas = 2 +} diff --git a/test/grammar/attr_operator/nest_var/override/dict_1/stdout.golden b/test/grammar/attr_operator/nest_var/override/dict_1/stdout.golden new file mode 100644 index 000000000..e60d481a8 --- /dev/null +++ b/test/grammar/attr_operator/nest_var/override/dict_1/stdout.golden @@ -0,0 +1,6 @@ +config: + spec: + replicas: 2 + labels: + key1: value1 + key2: value2 diff --git a/test/grammar/attr_operator/nest_var/override/dict_2/main.k b/test/grammar/attr_operator/nest_var/override/dict_2/main.k new file mode 100644 index 000000000..1e33f7fea --- /dev/null +++ b/test/grammar/attr_operator/nest_var/override/dict_2/main.k @@ -0,0 +1,9 @@ +schema Config: + spec: {str:} = { + replicas: 1 + } + +data = Config { + spec.labels: {key: "value"} + spec: {replicas = 2} +} diff --git a/test/grammar/attr_operator/nest_var/override/dict_2/stdout.golden b/test/grammar/attr_operator/nest_var/override/dict_2/stdout.golden new file mode 100644 index 000000000..fa9cc9dce --- /dev/null +++ b/test/grammar/attr_operator/nest_var/override/dict_2/stdout.golden @@ -0,0 +1,5 @@ +data: + spec: + replicas: 2 + labels: + key: value diff --git a/test/grammar/attr_operator/nest_var/unification/dict_0/main.k b/test/grammar/attr_operator/nest_var/unification/dict_0/main.k new file mode 100644 index 000000000..fb1a23514 --- /dev/null +++ b/test/grammar/attr_operator/nest_var/unification/dict_0/main.k @@ -0,0 +1,6 @@ +labels = {key: "value"} +config = { + spec: {replicas: 1} + spec.labels: labels + spec.replicas: 1 +} diff --git a/test/grammar/attr_operator/nest_var/unification/dict_0/stdout.golden b/test/grammar/attr_operator/nest_var/unification/dict_0/stdout.golden new file mode 100644 index 000000000..94869c8a5 --- /dev/null +++ b/test/grammar/attr_operator/nest_var/unification/dict_0/stdout.golden @@ -0,0 +1,7 @@ +labels: + key: value +config: + spec: + replicas: 1 + labels: + key: value diff --git a/test/grammar/attr_operator/nest_var/unification/dict_1/main.k b/test/grammar/attr_operator/nest_var/unification/dict_1/main.k new file mode 100644 index 000000000..d89605708 --- /dev/null +++ b/test/grammar/attr_operator/nest_var/unification/dict_1/main.k @@ -0,0 +1,7 @@ +_labels = {key1: "value1"} +config = { + spec: {replicas: 1} + spec.labels: _labels + spec.labels: {key2: "value2"} + spec.replicas: 1 +} diff --git a/test/grammar/attr_operator/nest_var/unification/dict_1/stdout.golden b/test/grammar/attr_operator/nest_var/unification/dict_1/stdout.golden new file mode 100644 index 000000000..347f6866e --- /dev/null +++ b/test/grammar/attr_operator/nest_var/unification/dict_1/stdout.golden @@ -0,0 +1,6 @@ +config: + spec: + replicas: 1 + labels: + key1: value1 + key2: value2 diff --git a/test/grammar/attr_operator/nest_var/unification/dict_2/main.k b/test/grammar/attr_operator/nest_var/unification/dict_2/main.k new file mode 100644 index 000000000..e1a2102f4 --- /dev/null +++ b/test/grammar/attr_operator/nest_var/unification/dict_2/main.k @@ -0,0 +1,11 @@ +schema Config: + spec: {str:} = { + replicas: 1 + env: [{"key1": "value1"}] + } + +data = Config { + spec.labels: {key: "value"} + spec: {replicas = 2} + spec.env: [{"key2": "value2"}] +} diff --git a/test/grammar/attr_operator/nest_var/unification/dict_2/stdout.golden b/test/grammar/attr_operator/nest_var/unification/dict_2/stdout.golden new file mode 100644 index 000000000..5fbfeccae --- /dev/null +++ b/test/grammar/attr_operator/nest_var/unification/dict_2/stdout.golden @@ -0,0 +1,8 @@ +data: + spec: + replicas: 2 + env: + - key1: value1 + key2: value2 + labels: + key: value diff --git a/test/grammar/attr_operator/schema_inside/insert/test_0/main.k b/test/grammar/attr_operator/schema_inside/insert/test_0/main.k new file mode 100644 index 000000000..98d70a9f1 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_0/main.k @@ -0,0 +1,12 @@ +schema Config: + name: str + args: [str] + +schema Data: + config: Config = Config { + name = "config" + args: ["kcl", "-d"] + args += ["-p"] + } + +data = Data {} diff --git a/test/grammar/attr_operator/schema_inside/insert/test_0/stdout.golden b/test/grammar/attr_operator/schema_inside/insert/test_0/stdout.golden new file mode 100644 index 000000000..8e2c8dd4b --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_0/stdout.golden @@ -0,0 +1,7 @@ +data: + config: + name: config + args: + - kcl + - -d + - -p diff --git a/test/grammar/attr_operator/schema_inside/insert/test_1/main.k b/test/grammar/attr_operator/schema_inside/insert/test_1/main.k new file mode 100644 index 000000000..8f0f679d1 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_1/main.k @@ -0,0 +1,14 @@ +schema Config: + name?: str + args?: [str] + +config: Config { + name = "config" + args: ["kcl", "-d"] +} + +config: Config { + name = "config_new" + args += ["-p"] + args += ["-o", "out.txt"] +} diff --git a/test/grammar/attr_operator/schema_inside/insert/test_1/stdout.golden b/test/grammar/attr_operator/schema_inside/insert/test_1/stdout.golden new file mode 100644 index 000000000..da391b615 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_1/stdout.golden @@ -0,0 +1,8 @@ +config: + name: config_new + args: + - kcl + - -d + - -p + - -o + - out.txt diff --git a/test/grammar/attr_operator/schema_inside/insert/test_2/main.k b/test/grammar/attr_operator/schema_inside/insert/test_2/main.k new file mode 100644 index 000000000..d9bd35fc3 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_2/main.k @@ -0,0 +1,28 @@ +schema Config: + main: Main + +schema Main: + name: str + env: [Env] + +schema Env: + name: str + value: str + +_main = Main { + name: "main" + env: [ + {name: "ENV_1", value: "1"} + ] +} + +config: Config { + main: _main +} + +config: Config { + main.name: "main" + main.env += [ + {name: "ENV_2", value: "2"} + ] +} diff --git a/test/grammar/attr_operator/schema_inside/insert/test_2/stdout.golden b/test/grammar/attr_operator/schema_inside/insert/test_2/stdout.golden new file mode 100644 index 000000000..ee1eda2a3 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_2/stdout.golden @@ -0,0 +1,8 @@ +config: + main: + name: main + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/schema_inside/insert/test_3/main.k b/test/grammar/attr_operator/schema_inside/insert/test_3/main.k new file mode 100644 index 000000000..f64597b6d --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_3/main.k @@ -0,0 +1,30 @@ +schema Config: + main: Main + +schema Main: + name: str + env: [Env] + +schema Env: + name: str + value: str + +_main = Main { + name: "main" + env: [ + {name: "ENV_1", value: "1"} + ] +} + +config: Config { + main: _main +} + +config: Config { + main.name: "main" + main: Main { + env += [ + Env {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/schema_inside/insert/test_3/stdout.golden b/test/grammar/attr_operator/schema_inside/insert/test_3/stdout.golden new file mode 100644 index 000000000..ee1eda2a3 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_3/stdout.golden @@ -0,0 +1,8 @@ +config: + main: + name: main + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/schema_inside/insert/test_4/main.k b/test/grammar/attr_operator/schema_inside/insert/test_4/main.k new file mode 100644 index 000000000..81352a95c --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_4/main.k @@ -0,0 +1,12 @@ +schema Config: + name: str + args: [str] + +schema Data: + config: Config { + name = "config" + args: ["kcl", "-d"] + args += ["-p"] + } + +data = Data {} diff --git a/test/grammar/attr_operator/schema_inside/insert/test_4/stdout.golden b/test/grammar/attr_operator/schema_inside/insert/test_4/stdout.golden new file mode 100644 index 000000000..8e2c8dd4b --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_4/stdout.golden @@ -0,0 +1,7 @@ +data: + config: + name: config + args: + - kcl + - -d + - -p diff --git a/test/grammar/attr_operator/schema_inside/insert/test_5/main.k b/test/grammar/attr_operator/schema_inside/insert/test_5/main.k new file mode 100644 index 000000000..89eb93cd6 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_5/main.k @@ -0,0 +1,14 @@ +schema Config: + name: str + args: [str] + +schema Data: + _config: Config { + name = "config" + args: ["kcl", "-d"] + args += ["-p"] + args += ["-o", "stdout"] + } + config: Config = _config + +data = Data {} diff --git a/test/grammar/attr_operator/schema_inside/insert/test_5/stdout.golden b/test/grammar/attr_operator/schema_inside/insert/test_5/stdout.golden new file mode 100644 index 000000000..dc979e951 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_5/stdout.golden @@ -0,0 +1,9 @@ +data: + config: + name: config + args: + - kcl + - -d + - -p + - -o + - stdout diff --git a/test/grammar/attr_operator/schema_inside/insert/test_6/main.k b/test/grammar/attr_operator/schema_inside/insert/test_6/main.k new file mode 100644 index 000000000..fe9d96c7e --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_6/main.k @@ -0,0 +1,13 @@ +schema Config: + name: str + args: [str] + +schema Data: + config: Config = { + name = "config" + args: ["kcl", "-d"] + args += ["-p"] + args += ["-o", "stdout"] + } + +data = Data {} diff --git a/test/grammar/attr_operator/schema_inside/insert/test_6/stdout.golden b/test/grammar/attr_operator/schema_inside/insert/test_6/stdout.golden new file mode 100644 index 000000000..dc979e951 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_6/stdout.golden @@ -0,0 +1,9 @@ +data: + config: + name: config + args: + - kcl + - -d + - -p + - -o + - stdout diff --git a/test/grammar/attr_operator/schema_inside/insert/test_7/main.k b/test/grammar/attr_operator/schema_inside/insert/test_7/main.k new file mode 100644 index 000000000..78cbea8b7 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_7/main.k @@ -0,0 +1,12 @@ +schema Config: + name: str + args: [str] = ["kcl", "-d"] + +schema Data: + config: Config = { + name = "config" + args += ["-p"] + args += ["-o", "stdout"] + } + +data = Data {} diff --git a/test/grammar/attr_operator/schema_inside/insert/test_7/stdout.golden b/test/grammar/attr_operator/schema_inside/insert/test_7/stdout.golden new file mode 100644 index 000000000..dc979e951 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_7/stdout.golden @@ -0,0 +1,9 @@ +data: + config: + name: config + args: + - kcl + - -d + - -p + - -o + - stdout diff --git a/test/grammar/attr_operator/schema_inside/insert/test_8/main.k b/test/grammar/attr_operator/schema_inside/insert/test_8/main.k new file mode 100644 index 000000000..565a2fc48 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_8/main.k @@ -0,0 +1,14 @@ +schema Config: + name: str + args: [str] + +schema Data: + _config: Config { + name = "config" + args: ["kcl", "-d"] + args += ["-p"] + args += ["-o", "stdout"] + } + config = _config + +data = Data {} diff --git a/test/grammar/attr_operator/schema_inside/insert/test_8/stdout.golden b/test/grammar/attr_operator/schema_inside/insert/test_8/stdout.golden new file mode 100644 index 000000000..dc979e951 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/insert/test_8/stdout.golden @@ -0,0 +1,9 @@ +data: + config: + name: config + args: + - kcl + - -d + - -p + - -o + - stdout diff --git a/test/grammar/attr_operator/schema_inside/override/test_0/main.k b/test/grammar/attr_operator/schema_inside/override/test_0/main.k new file mode 100644 index 000000000..401b4760f --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/override/test_0/main.k @@ -0,0 +1,12 @@ +schema Config: + name?: str + args?: [str] + +config: Config { + name = "config" + args: ["kcl", "-d"] +} + +config: Config { + args = ["kcl"] +} diff --git a/test/grammar/attr_operator/schema_inside/override/test_0/stdout.golden b/test/grammar/attr_operator/schema_inside/override/test_0/stdout.golden new file mode 100644 index 000000000..663bbbd7c --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/override/test_0/stdout.golden @@ -0,0 +1,4 @@ +config: + name: config + args: + - kcl diff --git a/test/grammar/attr_operator/schema_inside/override/test_1/main.k b/test/grammar/attr_operator/schema_inside/override/test_1/main.k new file mode 100644 index 000000000..c781bcbb3 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/override/test_1/main.k @@ -0,0 +1,13 @@ +schema Config: + name?: str + args?: [str] + +config: Config { + name = "config" + args: ["kcl", "-d"] +} + +config: Config { + name = "config_new" + args = ["kcl"] +} diff --git a/test/grammar/attr_operator/schema_inside/override/test_1/stdout.golden b/test/grammar/attr_operator/schema_inside/override/test_1/stdout.golden new file mode 100644 index 000000000..2c986b33d --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/override/test_1/stdout.golden @@ -0,0 +1,4 @@ +config: + name: config_new + args: + - kcl diff --git a/test/grammar/attr_operator/schema_inside/override/test_2/main.k b/test/grammar/attr_operator/schema_inside/override/test_2/main.k new file mode 100644 index 000000000..1c1ca35b5 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/override/test_2/main.k @@ -0,0 +1,13 @@ +schema Config: + name: str + args: [str] + +schema Data: + config: Config { + name = "config" + args: ["kcl", "-d"] + args = ["kclvm"] + name = "kclvm" + } + +data = Data {} diff --git a/test/grammar/attr_operator/schema_inside/override/test_2/stdout.golden b/test/grammar/attr_operator/schema_inside/override/test_2/stdout.golden new file mode 100644 index 000000000..3506f2d82 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/override/test_2/stdout.golden @@ -0,0 +1,5 @@ +data: + config: + name: kclvm + args: + - kclvm diff --git a/test/grammar/attr_operator/schema_inside/override/test_3/main.k b/test/grammar/attr_operator/schema_inside/override/test_3/main.k new file mode 100644 index 000000000..d988ec61c --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/override/test_3/main.k @@ -0,0 +1,14 @@ +schema Config: + name: str + args: [str] + +schema Data: + _config: Config { + name = "config" + args: ["kcl", "-d"] + args = ["kclvm"] + name = "kclvm" + } + config: Config = _config | {name = "override"} + +data = Data {} diff --git a/test/grammar/attr_operator/schema_inside/override/test_3/stdout.golden b/test/grammar/attr_operator/schema_inside/override/test_3/stdout.golden new file mode 100644 index 000000000..f93216f52 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/override/test_3/stdout.golden @@ -0,0 +1,5 @@ +data: + config: + name: override + args: + - kclvm diff --git a/test/grammar/attr_operator/schema_inside/unification/test_0/main.k b/test/grammar/attr_operator/schema_inside/unification/test_0/main.k new file mode 100644 index 000000000..b7951dc3c --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/unification/test_0/main.k @@ -0,0 +1,13 @@ +schema Config: + name?: str + args?: [str] + +_config: Config { + name = "config" +} + +_config: Config { + args: ["kcl"] +} + +config = _config diff --git a/test/grammar/attr_operator/schema_inside/unification/test_0/stdout.golden b/test/grammar/attr_operator/schema_inside/unification/test_0/stdout.golden new file mode 100644 index 000000000..663bbbd7c --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/unification/test_0/stdout.golden @@ -0,0 +1,4 @@ +config: + name: config + args: + - kcl diff --git a/test/grammar/attr_operator/schema_inside/unification/test_1/main.k b/test/grammar/attr_operator/schema_inside/unification/test_1/main.k new file mode 100644 index 000000000..cace95a74 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/unification/test_1/main.k @@ -0,0 +1,18 @@ +schema Config: + name?: str + args?: [str] + id?: int + +_config: Config { + name = "config" +} + +_config: Config { + args: ["kcl"] +} + +_config: Config { + id: 1 +} + +config = _config diff --git a/test/grammar/attr_operator/schema_inside/unification/test_1/stdout.golden b/test/grammar/attr_operator/schema_inside/unification/test_1/stdout.golden new file mode 100644 index 000000000..95adff7ca --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/unification/test_1/stdout.golden @@ -0,0 +1,5 @@ +config: + name: config + args: + - kcl + id: 1 diff --git a/test/grammar/attr_operator/schema_inside/unification/test_2/main.k b/test/grammar/attr_operator/schema_inside/unification/test_2/main.k new file mode 100644 index 000000000..c25d84095 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/unification/test_2/main.k @@ -0,0 +1,12 @@ +schema Config: + name: str + labels: {str:} + +schema Data: + config: Config { + name = "config" + labels.key1: "value1" + labels.key2: "value2" + } + +data = Data {} diff --git a/test/grammar/attr_operator/schema_inside/unification/test_2/stdout.golden b/test/grammar/attr_operator/schema_inside/unification/test_2/stdout.golden new file mode 100644 index 000000000..00112cdf1 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/unification/test_2/stdout.golden @@ -0,0 +1,6 @@ +data: + config: + name: config + labels: + key1: value1 + key2: value2 diff --git a/test/grammar/attr_operator/schema_inside/unification/test_3/main.k b/test/grammar/attr_operator/schema_inside/unification/test_3/main.k new file mode 100644 index 000000000..2e12f9c76 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/unification/test_3/main.k @@ -0,0 +1,13 @@ +schema Config: + name: str + labels: {str:} + +schema Data: + _config: Config { + name = "config" + labels.key1: "value1" + labels.key2: "value2" + } + config: Config = _config | {name = "override"} + +data = Data {} diff --git a/test/grammar/attr_operator/schema_inside/unification/test_3/stdout.golden b/test/grammar/attr_operator/schema_inside/unification/test_3/stdout.golden new file mode 100644 index 000000000..421fd18e0 --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/unification/test_3/stdout.golden @@ -0,0 +1,6 @@ +data: + config: + name: override + labels: + key1: value1 + key2: value2 diff --git a/test/grammar/attr_operator/schema_inside/unification/test_4/main.k b/test/grammar/attr_operator/schema_inside/unification/test_4/main.k new file mode 100644 index 000000000..c43b42c8a --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/unification/test_4/main.k @@ -0,0 +1,8 @@ +schema Config: + [str]: str + +_c = Config {} +_c |= { + key = "value" +} +c = _c diff --git a/test/grammar/attr_operator/schema_inside/unification/test_4/stdout.golden b/test/grammar/attr_operator/schema_inside/unification/test_4/stdout.golden new file mode 100644 index 000000000..f43aaddad --- /dev/null +++ b/test/grammar/attr_operator/schema_inside/unification/test_4/stdout.golden @@ -0,0 +1,2 @@ +c: + key: value diff --git a/test/grammar/attr_operator/single_config/insert/dict_0/main.k b/test/grammar/attr_operator/single_config/insert/dict_0/main.k new file mode 100644 index 000000000..4e6e8ce97 --- /dev/null +++ b/test/grammar/attr_operator/single_config/insert/dict_0/main.k @@ -0,0 +1,12 @@ +config = { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: { + env += [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/single_config/insert/dict_0/stdout.golden b/test/grammar/attr_operator/single_config/insert/dict_0/stdout.golden new file mode 100644 index 000000000..853930d54 --- /dev/null +++ b/test/grammar/attr_operator/single_config/insert/dict_0/stdout.golden @@ -0,0 +1,7 @@ +config: + main: + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/single_config/insert/dict_1/main.k b/test/grammar/attr_operator/single_config/insert/dict_1/main.k new file mode 100644 index 000000000..0947f0aef --- /dev/null +++ b/test/grammar/attr_operator/single_config/insert/dict_1/main.k @@ -0,0 +1,15 @@ +config = { + main: { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: { + env += [ + {name: "ENV_2", value: "2"} + ] + env += [ + {name: "ENV_3", value: "3"} + ] + } +} diff --git a/test/grammar/attr_operator/single_config/insert/dict_1/stdout.golden b/test/grammar/attr_operator/single_config/insert/dict_1/stdout.golden new file mode 100644 index 000000000..1874b50dd --- /dev/null +++ b/test/grammar/attr_operator/single_config/insert/dict_1/stdout.golden @@ -0,0 +1,9 @@ +config: + main: + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' + - name: ENV_3 + value: '3' diff --git a/test/grammar/attr_operator/single_config/insert/schema_0/main.k b/test/grammar/attr_operator/single_config/insert/schema_0/main.k new file mode 100644 index 000000000..a5be9b755 --- /dev/null +++ b/test/grammar/attr_operator/single_config/insert/schema_0/main.k @@ -0,0 +1,24 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +_main = Main { + env: [ + {name: "ENV_1", value: "1"} + ] +} + +config = Config { + main: _main + main: Main { + env += [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/single_config/insert/schema_0/stdout.golden b/test/grammar/attr_operator/single_config/insert/schema_0/stdout.golden new file mode 100644 index 000000000..853930d54 --- /dev/null +++ b/test/grammar/attr_operator/single_config/insert/schema_0/stdout.golden @@ -0,0 +1,7 @@ +config: + main: + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/single_config/insert/schema_1/main.k b/test/grammar/attr_operator/single_config/insert/schema_1/main.k new file mode 100644 index 000000000..57536e59a --- /dev/null +++ b/test/grammar/attr_operator/single_config/insert/schema_1/main.k @@ -0,0 +1,25 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +config = Config { + main: Main { + env: [ + {name: "ENV_1", value: "1"} + ] + } + main: Main { + env += [ + {name: "ENV_2", value: "2"} + ] + env += [ + {name: "ENV_3", value: "3"} + ] + } +} diff --git a/test/grammar/attr_operator/single_config/insert/schema_1/stdout.golden b/test/grammar/attr_operator/single_config/insert/schema_1/stdout.golden new file mode 100644 index 000000000..1874b50dd --- /dev/null +++ b/test/grammar/attr_operator/single_config/insert/schema_1/stdout.golden @@ -0,0 +1,9 @@ +config: + main: + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' + - name: ENV_3 + value: '3' diff --git a/test/grammar/attr_operator/single_config/override/dict_0/main.k b/test/grammar/attr_operator/single_config/override/dict_0/main.k new file mode 100644 index 000000000..08b2632b8 --- /dev/null +++ b/test/grammar/attr_operator/single_config/override/dict_0/main.k @@ -0,0 +1,12 @@ +config = { + main: { + env = [ + {name: "ENV_1", value: "1"} + ] + } + main: { + env = [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/single_config/override/dict_0/stdout.golden b/test/grammar/attr_operator/single_config/override/dict_0/stdout.golden new file mode 100644 index 000000000..77b6b1b9d --- /dev/null +++ b/test/grammar/attr_operator/single_config/override/dict_0/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/single_config/override/dict_1/main.k b/test/grammar/attr_operator/single_config/override/dict_1/main.k new file mode 100644 index 000000000..c18aeb9b0 --- /dev/null +++ b/test/grammar/attr_operator/single_config/override/dict_1/main.k @@ -0,0 +1,15 @@ +config = { + main: { + env = [ + {name: "ENV_1", value: "1"} + ] + } + main: { + env = [ + {name: "ENV_2", value: "2"} + ] + env = [ + {name: "ENV_3", value: "3"} + ] + } +} diff --git a/test/grammar/attr_operator/single_config/override/dict_1/stdout.golden b/test/grammar/attr_operator/single_config/override/dict_1/stdout.golden new file mode 100644 index 000000000..37f7be1fb --- /dev/null +++ b/test/grammar/attr_operator/single_config/override/dict_1/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_3 + value: '3' diff --git a/test/grammar/attr_operator/single_config/override/schema_0/main.k b/test/grammar/attr_operator/single_config/override/schema_0/main.k new file mode 100644 index 000000000..760e4c24a --- /dev/null +++ b/test/grammar/attr_operator/single_config/override/schema_0/main.k @@ -0,0 +1,24 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +_main = Main { + env: [ + {name: "ENV_1", value: "1"} + ] +} + +config = Config { + main: _main + main: Main { + env = [ + {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/single_config/override/schema_0/stdout.golden b/test/grammar/attr_operator/single_config/override/schema_0/stdout.golden new file mode 100644 index 000000000..77b6b1b9d --- /dev/null +++ b/test/grammar/attr_operator/single_config/override/schema_0/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/single_config/override/schema_1/main.k b/test/grammar/attr_operator/single_config/override/schema_1/main.k new file mode 100644 index 000000000..5f8a1d498 --- /dev/null +++ b/test/grammar/attr_operator/single_config/override/schema_1/main.k @@ -0,0 +1,25 @@ +schema Env: + name: str + value: str + +schema Main: + env: [Env] + +schema Config: + main: Main + +config = Config { + main: Main { + env = [ + {name: "ENV_1", value: "1"} + ] + } + main: Main { + env = [ + {name: "ENV_2", value: "2"} + ] + env = [ + {name: "ENV_3", value: "3"} + ] + } +} diff --git a/test/grammar/attr_operator/single_config/override/schema_1/stdout.golden b/test/grammar/attr_operator/single_config/override/schema_1/stdout.golden new file mode 100644 index 000000000..37f7be1fb --- /dev/null +++ b/test/grammar/attr_operator/single_config/override/schema_1/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - name: ENV_3 + value: '3' diff --git a/test/grammar/attr_operator/single_config/unification/dict_0/main.k b/test/grammar/attr_operator/single_config/unification/dict_0/main.k new file mode 100644 index 000000000..5b7a255a5 --- /dev/null +++ b/test/grammar/attr_operator/single_config/unification/dict_0/main.k @@ -0,0 +1,12 @@ +config = { + main: { + env: [ + {"ENV1": "1"} + ] + } + main: { + env: [ + {"ENV2": "2"} + ] + } +} diff --git a/test/grammar/attr_operator/single_config/unification/dict_0/stdout.golden b/test/grammar/attr_operator/single_config/unification/dict_0/stdout.golden new file mode 100644 index 000000000..d43c7d0d9 --- /dev/null +++ b/test/grammar/attr_operator/single_config/unification/dict_0/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - ENV1: '1' + ENV2: '2' diff --git a/test/grammar/attr_operator/single_config/unification/dict_1/main.k b/test/grammar/attr_operator/single_config/unification/dict_1/main.k new file mode 100644 index 000000000..0d646b1c1 --- /dev/null +++ b/test/grammar/attr_operator/single_config/unification/dict_1/main.k @@ -0,0 +1,16 @@ +config = { + main: { + env: [ + {"ENV1": "1"} + ] + } + main: { + env: [ + {"ENV2": "2"} + ] + env: [ + {"ENV3": "3"} + ] + } + main.env: [{"ENV4": "4"}] +} diff --git a/test/grammar/attr_operator/single_config/unification/dict_1/stdout.golden b/test/grammar/attr_operator/single_config/unification/dict_1/stdout.golden new file mode 100644 index 000000000..d2d333d4f --- /dev/null +++ b/test/grammar/attr_operator/single_config/unification/dict_1/stdout.golden @@ -0,0 +1,7 @@ +config: + main: + env: + - ENV1: '1' + ENV2: '2' + ENV3: '3' + ENV4: '4' diff --git a/test/grammar/attr_operator/single_config/unification/schema_0/main.k b/test/grammar/attr_operator/single_config/unification/schema_0/main.k new file mode 100644 index 000000000..e8f37dc14 --- /dev/null +++ b/test/grammar/attr_operator/single_config/unification/schema_0/main.k @@ -0,0 +1,15 @@ +schema Config: + main: {str:[{str:str}]} = {env: [{"ENV0": "0"}]} + +config = Config { + main: { + env: [ + {"ENV1": "1"} + ] + } + main: { + env: [ + {"ENV2": "2"} + ] + } +} diff --git a/test/grammar/attr_operator/single_config/unification/schema_0/stdout.golden b/test/grammar/attr_operator/single_config/unification/schema_0/stdout.golden new file mode 100644 index 000000000..2a6e84121 --- /dev/null +++ b/test/grammar/attr_operator/single_config/unification/schema_0/stdout.golden @@ -0,0 +1,6 @@ +config: + main: + env: + - ENV0: '0' + ENV1: '1' + ENV2: '2' diff --git a/test/grammar/attr_operator/single_config/unification/schema_1/main.k b/test/grammar/attr_operator/single_config/unification/schema_1/main.k new file mode 100644 index 000000000..63f98d6d3 --- /dev/null +++ b/test/grammar/attr_operator/single_config/unification/schema_1/main.k @@ -0,0 +1,21 @@ +schema Config: + main: {str:[{str:str}]} = {env: [{"ENV0": "0"}]} + +config = Config { + main: { + env: [ + {"ENV1": "1"} + ] + } + main: { + env: [ + {"ENV2": "2"} + ] + env: [ + {"ENV3": "3"} + ] + } + main.env: [ + {"ENV4": "4"} + ] +} diff --git a/test/grammar/attr_operator/single_config/unification/schema_1/stdout.golden b/test/grammar/attr_operator/single_config/unification/schema_1/stdout.golden new file mode 100644 index 000000000..023e5986c --- /dev/null +++ b/test/grammar/attr_operator/single_config/unification/schema_1/stdout.golden @@ -0,0 +1,8 @@ +config: + main: + env: + - ENV0: '0' + ENV1: '1' + ENV2: '2' + ENV3: '3' + ENV4: '4' diff --git a/test/grammar/attr_operator/top_level/insert/test_0/main.k b/test/grammar/attr_operator/top_level/insert/test_0/main.k new file mode 100644 index 000000000..15caa991f --- /dev/null +++ b/test/grammar/attr_operator/top_level/insert/test_0/main.k @@ -0,0 +1,12 @@ +schema Config: + name?: str + args?: [str] + +config: Config { + name = "config" + args: ["kcl", "-d"] +} + +config: Config { + args += ["-p"] +} diff --git a/test/grammar/attr_operator/top_level/insert/test_0/stdout.golden b/test/grammar/attr_operator/top_level/insert/test_0/stdout.golden new file mode 100644 index 000000000..d50bb96a3 --- /dev/null +++ b/test/grammar/attr_operator/top_level/insert/test_0/stdout.golden @@ -0,0 +1,6 @@ +config: + name: config + args: + - kcl + - -d + - -p diff --git a/test/grammar/attr_operator/top_level/insert/test_1/main.k b/test/grammar/attr_operator/top_level/insert/test_1/main.k new file mode 100644 index 000000000..8f0f679d1 --- /dev/null +++ b/test/grammar/attr_operator/top_level/insert/test_1/main.k @@ -0,0 +1,14 @@ +schema Config: + name?: str + args?: [str] + +config: Config { + name = "config" + args: ["kcl", "-d"] +} + +config: Config { + name = "config_new" + args += ["-p"] + args += ["-o", "out.txt"] +} diff --git a/test/grammar/attr_operator/top_level/insert/test_1/stdout.golden b/test/grammar/attr_operator/top_level/insert/test_1/stdout.golden new file mode 100644 index 000000000..da391b615 --- /dev/null +++ b/test/grammar/attr_operator/top_level/insert/test_1/stdout.golden @@ -0,0 +1,8 @@ +config: + name: config_new + args: + - kcl + - -d + - -p + - -o + - out.txt diff --git a/test/grammar/attr_operator/top_level/insert/test_2/main.k b/test/grammar/attr_operator/top_level/insert/test_2/main.k new file mode 100644 index 000000000..d9bd35fc3 --- /dev/null +++ b/test/grammar/attr_operator/top_level/insert/test_2/main.k @@ -0,0 +1,28 @@ +schema Config: + main: Main + +schema Main: + name: str + env: [Env] + +schema Env: + name: str + value: str + +_main = Main { + name: "main" + env: [ + {name: "ENV_1", value: "1"} + ] +} + +config: Config { + main: _main +} + +config: Config { + main.name: "main" + main.env += [ + {name: "ENV_2", value: "2"} + ] +} diff --git a/test/grammar/attr_operator/top_level/insert/test_2/stdout.golden b/test/grammar/attr_operator/top_level/insert/test_2/stdout.golden new file mode 100644 index 000000000..ee1eda2a3 --- /dev/null +++ b/test/grammar/attr_operator/top_level/insert/test_2/stdout.golden @@ -0,0 +1,8 @@ +config: + main: + name: main + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/top_level/insert/test_3/main.k b/test/grammar/attr_operator/top_level/insert/test_3/main.k new file mode 100644 index 000000000..f64597b6d --- /dev/null +++ b/test/grammar/attr_operator/top_level/insert/test_3/main.k @@ -0,0 +1,30 @@ +schema Config: + main: Main + +schema Main: + name: str + env: [Env] + +schema Env: + name: str + value: str + +_main = Main { + name: "main" + env: [ + {name: "ENV_1", value: "1"} + ] +} + +config: Config { + main: _main +} + +config: Config { + main.name: "main" + main: Main { + env += [ + Env {name: "ENV_2", value: "2"} + ] + } +} diff --git a/test/grammar/attr_operator/top_level/insert/test_3/stdout.golden b/test/grammar/attr_operator/top_level/insert/test_3/stdout.golden new file mode 100644 index 000000000..ee1eda2a3 --- /dev/null +++ b/test/grammar/attr_operator/top_level/insert/test_3/stdout.golden @@ -0,0 +1,8 @@ +config: + main: + name: main + env: + - name: ENV_1 + value: '1' + - name: ENV_2 + value: '2' diff --git a/test/grammar/attr_operator/top_level/override/test_0/main.k b/test/grammar/attr_operator/top_level/override/test_0/main.k new file mode 100644 index 000000000..401b4760f --- /dev/null +++ b/test/grammar/attr_operator/top_level/override/test_0/main.k @@ -0,0 +1,12 @@ +schema Config: + name?: str + args?: [str] + +config: Config { + name = "config" + args: ["kcl", "-d"] +} + +config: Config { + args = ["kcl"] +} diff --git a/test/grammar/attr_operator/top_level/override/test_0/stdout.golden b/test/grammar/attr_operator/top_level/override/test_0/stdout.golden new file mode 100644 index 000000000..663bbbd7c --- /dev/null +++ b/test/grammar/attr_operator/top_level/override/test_0/stdout.golden @@ -0,0 +1,4 @@ +config: + name: config + args: + - kcl diff --git a/test/grammar/attr_operator/top_level/override/test_1/main.k b/test/grammar/attr_operator/top_level/override/test_1/main.k new file mode 100644 index 000000000..c781bcbb3 --- /dev/null +++ b/test/grammar/attr_operator/top_level/override/test_1/main.k @@ -0,0 +1,13 @@ +schema Config: + name?: str + args?: [str] + +config: Config { + name = "config" + args: ["kcl", "-d"] +} + +config: Config { + name = "config_new" + args = ["kcl"] +} diff --git a/test/grammar/attr_operator/top_level/override/test_1/stdout.golden b/test/grammar/attr_operator/top_level/override/test_1/stdout.golden new file mode 100644 index 000000000..2c986b33d --- /dev/null +++ b/test/grammar/attr_operator/top_level/override/test_1/stdout.golden @@ -0,0 +1,4 @@ +config: + name: config_new + args: + - kcl diff --git a/test/grammar/attr_operator/top_level/unification/test_0/main.k b/test/grammar/attr_operator/top_level/unification/test_0/main.k new file mode 100644 index 000000000..b7951dc3c --- /dev/null +++ b/test/grammar/attr_operator/top_level/unification/test_0/main.k @@ -0,0 +1,13 @@ +schema Config: + name?: str + args?: [str] + +_config: Config { + name = "config" +} + +_config: Config { + args: ["kcl"] +} + +config = _config diff --git a/test/grammar/attr_operator/top_level/unification/test_0/stdout.golden b/test/grammar/attr_operator/top_level/unification/test_0/stdout.golden new file mode 100644 index 000000000..663bbbd7c --- /dev/null +++ b/test/grammar/attr_operator/top_level/unification/test_0/stdout.golden @@ -0,0 +1,4 @@ +config: + name: config + args: + - kcl diff --git a/test/grammar/attr_operator/top_level/unification/test_1/main.k b/test/grammar/attr_operator/top_level/unification/test_1/main.k new file mode 100644 index 000000000..cace95a74 --- /dev/null +++ b/test/grammar/attr_operator/top_level/unification/test_1/main.k @@ -0,0 +1,18 @@ +schema Config: + name?: str + args?: [str] + id?: int + +_config: Config { + name = "config" +} + +_config: Config { + args: ["kcl"] +} + +_config: Config { + id: 1 +} + +config = _config diff --git a/test/grammar/attr_operator/top_level/unification/test_1/stdout.golden b/test/grammar/attr_operator/top_level/unification/test_1/stdout.golden new file mode 100644 index 000000000..95adff7ca --- /dev/null +++ b/test/grammar/attr_operator/top_level/unification/test_1/stdout.golden @@ -0,0 +1,5 @@ +config: + name: config + args: + - kcl + id: 1 diff --git a/test/grammar/attr_operator/unpack/insert/dict_0/main.k b/test/grammar/attr_operator/unpack/insert/dict_0/main.k new file mode 100644 index 000000000..75eafa71e --- /dev/null +++ b/test/grammar/attr_operator/unpack/insert/dict_0/main.k @@ -0,0 +1,7 @@ +base = { + val += [2] +} +config = { + val = [1] + **base +} diff --git a/test/grammar/attr_operator/unpack/insert/dict_0/stdout.golden b/test/grammar/attr_operator/unpack/insert/dict_0/stdout.golden new file mode 100644 index 000000000..ef8691c13 --- /dev/null +++ b/test/grammar/attr_operator/unpack/insert/dict_0/stdout.golden @@ -0,0 +1,7 @@ +base: + val: + - 2 +config: + val: + - 1 + - 2 diff --git a/test/grammar/attr_operator/unpack/insert/dict_1/main.k b/test/grammar/attr_operator/unpack/insert/dict_1/main.k new file mode 100644 index 000000000..9e558b15a --- /dev/null +++ b/test/grammar/attr_operator/unpack/insert/dict_1/main.k @@ -0,0 +1,7 @@ +base = { + val = [2] +} +config = { + **base + val += [1] +} diff --git a/test/grammar/attr_operator/unpack/insert/dict_1/stdout.golden b/test/grammar/attr_operator/unpack/insert/dict_1/stdout.golden new file mode 100644 index 000000000..feba271f2 --- /dev/null +++ b/test/grammar/attr_operator/unpack/insert/dict_1/stdout.golden @@ -0,0 +1,7 @@ +base: + val: + - 2 +config: + val: + - 2 + - 1 diff --git a/test/grammar/attr_operator/unpack/insert/dict_2/main.k b/test/grammar/attr_operator/unpack/insert/dict_2/main.k new file mode 100644 index 000000000..da76b4f29 --- /dev/null +++ b/test/grammar/attr_operator/unpack/insert/dict_2/main.k @@ -0,0 +1,11 @@ +schema Config: + val: [int] = [0] + +base = { + val = [2] +} + +config = Config { + **base + val += [1] +} diff --git a/test/grammar/attr_operator/unpack/insert/dict_2/stdout.golden b/test/grammar/attr_operator/unpack/insert/dict_2/stdout.golden new file mode 100644 index 000000000..9751d8924 --- /dev/null +++ b/test/grammar/attr_operator/unpack/insert/dict_2/stdout.golden @@ -0,0 +1,8 @@ +base: + val: + - 2 +config: + val: + - 0 + - 2 + - 1 diff --git a/test/grammar/attr_operator/unpack/override/dict_0/main.k b/test/grammar/attr_operator/unpack/override/dict_0/main.k new file mode 100644 index 000000000..04c9d6228 --- /dev/null +++ b/test/grammar/attr_operator/unpack/override/dict_0/main.k @@ -0,0 +1,7 @@ +base = { + val = [2] +} +config = { + val = [1] + **base +} diff --git a/test/grammar/attr_operator/unpack/override/dict_0/stdout.golden b/test/grammar/attr_operator/unpack/override/dict_0/stdout.golden new file mode 100644 index 000000000..d8a81ea1d --- /dev/null +++ b/test/grammar/attr_operator/unpack/override/dict_0/stdout.golden @@ -0,0 +1,6 @@ +base: + val: + - 2 +config: + val: + - 2 diff --git a/test/grammar/attr_operator/unpack/override/dict_1/main.k b/test/grammar/attr_operator/unpack/override/dict_1/main.k new file mode 100644 index 000000000..2b7e0be86 --- /dev/null +++ b/test/grammar/attr_operator/unpack/override/dict_1/main.k @@ -0,0 +1,7 @@ +base = { + val = [2] +} +config = { + **base + val = [1] +} diff --git a/test/grammar/attr_operator/unpack/override/dict_1/stdout.golden b/test/grammar/attr_operator/unpack/override/dict_1/stdout.golden new file mode 100644 index 000000000..1a15b5ee5 --- /dev/null +++ b/test/grammar/attr_operator/unpack/override/dict_1/stdout.golden @@ -0,0 +1,6 @@ +base: + val: + - 2 +config: + val: + - 1 diff --git a/test/grammar/attr_operator/unpack/override/dict_2/main.k b/test/grammar/attr_operator/unpack/override/dict_2/main.k new file mode 100644 index 000000000..a32f6be3e --- /dev/null +++ b/test/grammar/attr_operator/unpack/override/dict_2/main.k @@ -0,0 +1,10 @@ +schema Config: + val: [int] = [0] + +base = { + val = [2] +} +config = { + **base + val = [1] +} diff --git a/test/grammar/attr_operator/unpack/override/dict_2/stdout.golden b/test/grammar/attr_operator/unpack/override/dict_2/stdout.golden new file mode 100644 index 000000000..1a15b5ee5 --- /dev/null +++ b/test/grammar/attr_operator/unpack/override/dict_2/stdout.golden @@ -0,0 +1,6 @@ +base: + val: + - 2 +config: + val: + - 1 diff --git a/test/grammar/attr_operator/unpack/unification/dict_0/main.k b/test/grammar/attr_operator/unpack/unification/dict_0/main.k new file mode 100644 index 000000000..dff780ddb --- /dev/null +++ b/test/grammar/attr_operator/unpack/unification/dict_0/main.k @@ -0,0 +1,7 @@ +base = { + val: [1] +} +config = { + val: [1] + **base +} diff --git a/test/grammar/attr_operator/unpack/unification/dict_0/stdout.golden b/test/grammar/attr_operator/unpack/unification/dict_0/stdout.golden new file mode 100644 index 000000000..0e5a2d272 --- /dev/null +++ b/test/grammar/attr_operator/unpack/unification/dict_0/stdout.golden @@ -0,0 +1,6 @@ +base: + val: + - 1 +config: + val: + - 1 diff --git a/test/grammar/attr_operator/unpack/unification/dict_1/main.k b/test/grammar/attr_operator/unpack/unification/dict_1/main.k new file mode 100644 index 000000000..55cae933c --- /dev/null +++ b/test/grammar/attr_operator/unpack/unification/dict_1/main.k @@ -0,0 +1,7 @@ +base = { + val: [0] +} +config = { + **base + val: [0] +} diff --git a/test/grammar/attr_operator/unpack/unification/dict_1/stdout.golden b/test/grammar/attr_operator/unpack/unification/dict_1/stdout.golden new file mode 100644 index 000000000..50051a28b --- /dev/null +++ b/test/grammar/attr_operator/unpack/unification/dict_1/stdout.golden @@ -0,0 +1,6 @@ +base: + val: + - 0 +config: + val: + - 0 diff --git a/test/grammar/attr_operator/unpack/unification/dict_2/main.k b/test/grammar/attr_operator/unpack/unification/dict_2/main.k new file mode 100644 index 000000000..7a5f41fcd --- /dev/null +++ b/test/grammar/attr_operator/unpack/unification/dict_2/main.k @@ -0,0 +1,10 @@ +schema Config: + val: [{str:}] + +base = { + val: [{key1: "value1"}] +} +config = { + **base + val: [{key2: "value2"}] +} diff --git a/test/grammar/attr_operator/unpack/unification/dict_2/stdout.golden b/test/grammar/attr_operator/unpack/unification/dict_2/stdout.golden new file mode 100644 index 000000000..91c86a577 --- /dev/null +++ b/test/grammar/attr_operator/unpack/unification/dict_2/stdout.golden @@ -0,0 +1,7 @@ +base: + val: + - key1: value1 +config: + val: + - key1: value1 + key2: value2 diff --git a/test/grammar/builtins/base64/decode/main.k b/test/grammar/builtins/base64/decode/main.k new file mode 100644 index 000000000..3581ba8a4 --- /dev/null +++ b/test/grammar/builtins/base64/decode/main.k @@ -0,0 +1,3 @@ +import base64 + +decode = base64.decode("MC4zLjA=") diff --git a/test/grammar/builtins/base64/decode/stdout.golden b/test/grammar/builtins/base64/decode/stdout.golden new file mode 100644 index 000000000..d4a53946d --- /dev/null +++ b/test/grammar/builtins/base64/decode/stdout.golden @@ -0,0 +1 @@ +decode: 0.3.0 \ No newline at end of file diff --git a/test/grammar/builtins/base64/encode/main.k b/test/grammar/builtins/base64/encode/main.k new file mode 100644 index 000000000..4f4efd19a --- /dev/null +++ b/test/grammar/builtins/base64/encode/main.k @@ -0,0 +1,3 @@ +import base64 + +encode = base64.encode("0.3.0") diff --git a/test/grammar/builtins/base64/encode/stdout.golden b/test/grammar/builtins/base64/encode/stdout.golden new file mode 100644 index 000000000..24584daa5 --- /dev/null +++ b/test/grammar/builtins/base64/encode/stdout.golden @@ -0,0 +1 @@ +encode: MC4zLjA= diff --git a/test/grammar/builtins/bool/main.k b/test/grammar/builtins/bool/main.k new file mode 100644 index 000000000..8df251eb8 --- /dev/null +++ b/test/grammar/builtins/bool/main.k @@ -0,0 +1,13 @@ +a = True +b = False +c = not True +d = not False +e = bool(True) +f = bool(False) +g = bool(1) +h = bool(0) +i = bool(0.0) +j = bool(None) +l = bool('') +m = bool([]) +n = bool({}) diff --git a/test/grammar/builtins/bool/stdout.golden b/test/grammar/builtins/bool/stdout.golden new file mode 100644 index 000000000..c4f7bf341 --- /dev/null +++ b/test/grammar/builtins/bool/stdout.golden @@ -0,0 +1,13 @@ +a: true +b: false +c: false +d: true +e: true +f: false +g: true +h: false +i: false +j: false +l: false +m: false +n: false \ No newline at end of file diff --git a/test/grammar/builtins/crypto/md5/main.k b/test/grammar/builtins/crypto/md5/main.k new file mode 100644 index 000000000..f9305c466 --- /dev/null +++ b/test/grammar/builtins/crypto/md5/main.k @@ -0,0 +1,3 @@ +import crypto + +md5 = crypto.md5("ABCDEF") diff --git a/test/grammar/builtins/crypto/md5/stdout.golden b/test/grammar/builtins/crypto/md5/stdout.golden new file mode 100644 index 000000000..b42d39ca4 --- /dev/null +++ b/test/grammar/builtins/crypto/md5/stdout.golden @@ -0,0 +1 @@ +md5: 8827a41122a5028b9808c7bf84b9fcf6 \ No newline at end of file diff --git a/test/grammar/builtins/crypto/sha/sha1/main.k b/test/grammar/builtins/crypto/sha/sha1/main.k new file mode 100644 index 000000000..235361f7f --- /dev/null +++ b/test/grammar/builtins/crypto/sha/sha1/main.k @@ -0,0 +1,3 @@ +import crypto + +sha = crypto.sha1("ABCDEF") diff --git a/test/grammar/builtins/crypto/sha/sha1/stdout.golden b/test/grammar/builtins/crypto/sha/sha1/stdout.golden new file mode 100644 index 000000000..31f07004b --- /dev/null +++ b/test/grammar/builtins/crypto/sha/sha1/stdout.golden @@ -0,0 +1 @@ +sha: 970093678b182127f60bb51b8af2c94d539eca3a \ No newline at end of file diff --git a/test/grammar/builtins/crypto/sha/sha224/main.k b/test/grammar/builtins/crypto/sha/sha224/main.k new file mode 100644 index 000000000..5ebf55ed2 --- /dev/null +++ b/test/grammar/builtins/crypto/sha/sha224/main.k @@ -0,0 +1,3 @@ +import crypto + +sha = crypto.sha224("ABCDEF") diff --git a/test/grammar/builtins/crypto/sha/sha224/stdout.golden b/test/grammar/builtins/crypto/sha/sha224/stdout.golden new file mode 100644 index 000000000..136443125 --- /dev/null +++ b/test/grammar/builtins/crypto/sha/sha224/stdout.golden @@ -0,0 +1 @@ +sha: fd6639af1cc457b72148d78e90df45df4d344ca3b66fa44598148ce4 \ No newline at end of file diff --git a/test/grammar/builtins/crypto/sha/sha256/main.k b/test/grammar/builtins/crypto/sha/sha256/main.k new file mode 100644 index 000000000..99dbda4c7 --- /dev/null +++ b/test/grammar/builtins/crypto/sha/sha256/main.k @@ -0,0 +1,3 @@ +import crypto + +sha = crypto.sha256("ABCDEF") diff --git a/test/grammar/builtins/crypto/sha/sha256/stdout.golden b/test/grammar/builtins/crypto/sha/sha256/stdout.golden new file mode 100644 index 000000000..98bd79450 --- /dev/null +++ b/test/grammar/builtins/crypto/sha/sha256/stdout.golden @@ -0,0 +1 @@ +sha: e9c0f8b575cbfcb42ab3b78ecc87efa3b011d9a5d10b09fa4e96f240bf6a82f5 \ No newline at end of file diff --git a/test/grammar/builtins/crypto/sha/sha384/main.k b/test/grammar/builtins/crypto/sha/sha384/main.k new file mode 100644 index 000000000..6fafbe81c --- /dev/null +++ b/test/grammar/builtins/crypto/sha/sha384/main.k @@ -0,0 +1,3 @@ +import crypto + +sha = crypto.sha384("ABCDEF") diff --git a/test/grammar/builtins/crypto/sha/sha384/stdout.golden b/test/grammar/builtins/crypto/sha/sha384/stdout.golden new file mode 100644 index 000000000..d3ed52e9d --- /dev/null +++ b/test/grammar/builtins/crypto/sha/sha384/stdout.golden @@ -0,0 +1 @@ +sha: 39e0cc1b02b8c082b64643cdcae0e6180e460e1f717824c78b17f946c9864c90c04c55067b231e69b0ad5bd19d12a065 \ No newline at end of file diff --git a/test/grammar/builtins/crypto/sha/sha512/main.k b/test/grammar/builtins/crypto/sha/sha512/main.k new file mode 100644 index 000000000..97c0633e5 --- /dev/null +++ b/test/grammar/builtins/crypto/sha/sha512/main.k @@ -0,0 +1,3 @@ +import crypto + +sha = crypto.sha512("ABCDEF") diff --git a/test/grammar/builtins/crypto/sha/sha512/stdout.golden b/test/grammar/builtins/crypto/sha/sha512/stdout.golden new file mode 100644 index 000000000..328a49aa9 --- /dev/null +++ b/test/grammar/builtins/crypto/sha/sha512/stdout.golden @@ -0,0 +1 @@ +sha: 569350085b223ba854dfc5d607643ceb85e4607e46e5a9ad3696f898e29d8a3fe22610956167cefb7e2ba769e740f94b31e4e3c52195ba65e64ba40d82343591 \ No newline at end of file diff --git a/test/grammar/builtins/default/abs/main.k b/test/grammar/builtins/default/abs/main.k new file mode 100644 index 000000000..db656ac85 --- /dev/null +++ b/test/grammar/builtins/default/abs/main.k @@ -0,0 +1,2 @@ +a = abs(-40) +b = abs(100.10) diff --git a/test/grammar/builtins/default/abs/stdout.golden b/test/grammar/builtins/default/abs/stdout.golden new file mode 100644 index 000000000..ea746bfa9 --- /dev/null +++ b/test/grammar/builtins/default/abs/stdout.golden @@ -0,0 +1,2 @@ +a: 40 +b: 100.1 \ No newline at end of file diff --git a/test/grammar/builtins/default/all/main.k b/test/grammar/builtins/default/all/main.k new file mode 100644 index 000000000..ad6b86d90 --- /dev/null +++ b/test/grammar/builtins/default/all/main.k @@ -0,0 +1,3 @@ +a = all_true(['a', 'b', 'c', 'd']) +b = all_true(['a', 'b', '', 'd']) +c = all_true([0, 1, 2, 3]) diff --git a/test/grammar/builtins/default/all/stdout.golden b/test/grammar/builtins/default/all/stdout.golden new file mode 100644 index 000000000..428686ae6 --- /dev/null +++ b/test/grammar/builtins/default/all/stdout.golden @@ -0,0 +1,3 @@ +a: true +b: false +c: false \ No newline at end of file diff --git a/test/grammar/builtins/default/any/main.k b/test/grammar/builtins/default/any/main.k new file mode 100644 index 000000000..8727b2d3b --- /dev/null +++ b/test/grammar/builtins/default/any/main.k @@ -0,0 +1,4 @@ +a = any_true(['a', 'b', 'c', 'd']) +b = any_true(['a', 'b', '', 'd']) +c = any_true([0, '', False]) +d = any_true([]) diff --git a/test/grammar/builtins/default/any/stdout.golden b/test/grammar/builtins/default/any/stdout.golden new file mode 100644 index 000000000..3fcd522ae --- /dev/null +++ b/test/grammar/builtins/default/any/stdout.golden @@ -0,0 +1,4 @@ +a: true +b: true +c: false +d: false \ No newline at end of file diff --git a/test/grammar/builtins/default/bin/main.k b/test/grammar/builtins/default/bin/main.k new file mode 100644 index 000000000..a1541b545 --- /dev/null +++ b/test/grammar/builtins/default/bin/main.k @@ -0,0 +1,2 @@ +a = bin(10) +b = bin(20) diff --git a/test/grammar/builtins/default/bin/stdout.golden b/test/grammar/builtins/default/bin/stdout.golden new file mode 100644 index 000000000..b76ff25c8 --- /dev/null +++ b/test/grammar/builtins/default/bin/stdout.golden @@ -0,0 +1,2 @@ +a: '0b1010' +b: '0b10100' \ No newline at end of file diff --git a/test/grammar/builtins/default/hex/main.k b/test/grammar/builtins/default/hex/main.k new file mode 100644 index 000000000..54d19d924 --- /dev/null +++ b/test/grammar/builtins/default/hex/main.k @@ -0,0 +1,3 @@ +a = hex(255) +b = hex(-42) +c = hex(12) diff --git a/test/grammar/builtins/default/hex/stdout.golden b/test/grammar/builtins/default/hex/stdout.golden new file mode 100644 index 000000000..ac5f18882 --- /dev/null +++ b/test/grammar/builtins/default/hex/stdout.golden @@ -0,0 +1,3 @@ +a: '0xff' +b: '-0x2a' +c: '0xc' \ No newline at end of file diff --git a/test/grammar/builtins/default/isunique/main.k b/test/grammar/builtins/default/isunique/main.k new file mode 100644 index 000000000..d285836da --- /dev/null +++ b/test/grammar/builtins/default/isunique/main.k @@ -0,0 +1,8 @@ +a = [100, 10, 100] +b = [100, 10] +c = ["Hello", "world", "world"] +d = ["Hello", "world"] +A = isunique(a) +B = isunique(b) +C = isunique(c) +D = isunique(d) diff --git a/test/grammar/builtins/default/isunique/stdout.golden b/test/grammar/builtins/default/isunique/stdout.golden new file mode 100644 index 000000000..e8ef629df --- /dev/null +++ b/test/grammar/builtins/default/isunique/stdout.golden @@ -0,0 +1,18 @@ +a: +- 100 +- 10 +- 100 +b: +- 100 +- 10 +c: +- Hello +- world +- world +d: +- Hello +- world +A: false +B: true +C: false +D: true diff --git a/test/grammar/builtins/default/len/main.k b/test/grammar/builtins/default/len/main.k new file mode 100644 index 000000000..001746f9c --- /dev/null +++ b/test/grammar/builtins/default/len/main.k @@ -0,0 +1,4 @@ +a = "hello" +b = [1, 2, 3] +c = {'a': 1, 'b': 2} +d = len(a) + len(b) + len(c) diff --git a/test/grammar/builtins/default/len/stdout.golden b/test/grammar/builtins/default/len/stdout.golden new file mode 100644 index 000000000..027e19b3c --- /dev/null +++ b/test/grammar/builtins/default/len/stdout.golden @@ -0,0 +1,9 @@ +a: hello +b: +- 1 +- 2 +- 3 +c: + a: 1 + b: 2 +d: 10 diff --git a/test/grammar/builtins/default/max/main.k b/test/grammar/builtins/default/max/main.k new file mode 100644 index 000000000..c3a5b8b70 --- /dev/null +++ b/test/grammar/builtins/default/max/main.k @@ -0,0 +1,2 @@ +a = max(80, 100, 1000) +b = max(-80, -20, -10) diff --git a/test/grammar/builtins/default/max/stdout.golden b/test/grammar/builtins/default/max/stdout.golden new file mode 100644 index 000000000..5809d92d9 --- /dev/null +++ b/test/grammar/builtins/default/max/stdout.golden @@ -0,0 +1,2 @@ +a: 1000 +b: -10 \ No newline at end of file diff --git a/test/grammar/builtins/default/min/main.k b/test/grammar/builtins/default/min/main.k new file mode 100644 index 000000000..97a483a27 --- /dev/null +++ b/test/grammar/builtins/default/min/main.k @@ -0,0 +1,2 @@ +a = min(80, 100, 1000) +b = min(-80, -20, -10) diff --git a/test/grammar/builtins/default/min/stdout.golden b/test/grammar/builtins/default/min/stdout.golden new file mode 100644 index 000000000..16967eaeb --- /dev/null +++ b/test/grammar/builtins/default/min/stdout.golden @@ -0,0 +1,2 @@ +a: 80 +b: -80 \ No newline at end of file diff --git a/test/grammar/builtins/default/multiplyof/main.k b/test/grammar/builtins/default/multiplyof/main.k new file mode 100644 index 000000000..14ca3f1dc --- /dev/null +++ b/test/grammar/builtins/default/multiplyof/main.k @@ -0,0 +1,3 @@ +a = 100 +b = 10 +c = multiplyof(a, b) diff --git a/test/grammar/builtins/default/multiplyof/stdout.golden b/test/grammar/builtins/default/multiplyof/stdout.golden new file mode 100644 index 000000000..d372422ed --- /dev/null +++ b/test/grammar/builtins/default/multiplyof/stdout.golden @@ -0,0 +1,3 @@ +a: 100 +b: 10 +c: true diff --git a/test/grammar/builtins/default/oct/main.k b/test/grammar/builtins/default/oct/main.k new file mode 100644 index 000000000..08e2fc5aa --- /dev/null +++ b/test/grammar/builtins/default/oct/main.k @@ -0,0 +1,3 @@ +a = oct(10) +b = oct(20) +c = oct(15) diff --git a/test/grammar/builtins/default/oct/stdout.golden b/test/grammar/builtins/default/oct/stdout.golden new file mode 100644 index 000000000..e15ca4f32 --- /dev/null +++ b/test/grammar/builtins/default/oct/stdout.golden @@ -0,0 +1,3 @@ +a: '0o12' +b: '0o24' +c: '0o17' diff --git a/test/grammar/builtins/default/ord/main.k b/test/grammar/builtins/default/ord/main.k new file mode 100644 index 000000000..066306689 --- /dev/null +++ b/test/grammar/builtins/default/ord/main.k @@ -0,0 +1,2 @@ +a = ord('a') +b = ord('€') diff --git a/test/grammar/builtins/default/ord/stdout.golden b/test/grammar/builtins/default/ord/stdout.golden new file mode 100644 index 000000000..6f0d2deec --- /dev/null +++ b/test/grammar/builtins/default/ord/stdout.golden @@ -0,0 +1,2 @@ +a: 97 +b: 8364 \ No newline at end of file diff --git a/test/grammar/builtins/default/pow/main.k b/test/grammar/builtins/default/pow/main.k new file mode 100644 index 000000000..10d684968 --- /dev/null +++ b/test/grammar/builtins/default/pow/main.k @@ -0,0 +1 @@ +a = pow(100, 2) diff --git a/test/grammar/builtins/default/pow/stdout.golden b/test/grammar/builtins/default/pow/stdout.golden new file mode 100644 index 000000000..c42fd0bd1 --- /dev/null +++ b/test/grammar/builtins/default/pow/stdout.golden @@ -0,0 +1 @@ +a: 10000 \ No newline at end of file diff --git a/test/grammar/builtins/default/print/dict/main.k b/test/grammar/builtins/default/print/dict/main.k new file mode 100644 index 000000000..dfccb3b59 --- /dev/null +++ b/test/grammar/builtins/default/print/dict/main.k @@ -0,0 +1,7 @@ +d = {"key": "val"} + +a = d["key"] +#print(d["key"]) + +b = d["key_not_found"] or None +#print(d["key_not_found"]) diff --git a/test/grammar/builtins/default/print/dict/stdout.golden b/test/grammar/builtins/default/print/dict/stdout.golden new file mode 100644 index 000000000..4d373a08c --- /dev/null +++ b/test/grammar/builtins/default/print/dict/stdout.golden @@ -0,0 +1,4 @@ +d: + key: val +a: val +b: null diff --git a/test/grammar/builtins/default/print/hello_world/main.k b/test/grammar/builtins/default/print/hello_world/main.k new file mode 100644 index 000000000..90116d0eb --- /dev/null +++ b/test/grammar/builtins/default/print/hello_world/main.k @@ -0,0 +1 @@ +# print("Hello, World!") diff --git a/test/grammar/builtins/default/print/hello_world/stdout.golden b/test/grammar/builtins/default/print/hello_world/stdout.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/builtins/default/print/multiple_arguments_0/main.k b/test/grammar/builtins/default/print/multiple_arguments_0/main.k new file mode 100644 index 000000000..2893a26a7 --- /dev/null +++ b/test/grammar/builtins/default/print/multiple_arguments_0/main.k @@ -0,0 +1,3 @@ +a = 100 +b = "Hello" +#print("Hello, World!", a, b) diff --git a/test/grammar/builtins/default/print/multiple_arguments_0/stdout.golden b/test/grammar/builtins/default/print/multiple_arguments_0/stdout.golden new file mode 100644 index 000000000..02d6a06b8 --- /dev/null +++ b/test/grammar/builtins/default/print/multiple_arguments_0/stdout.golden @@ -0,0 +1,2 @@ +a: 100 +b: Hello diff --git a/test/grammar/builtins/default/print/multiple_arguments_1/main.k b/test/grammar/builtins/default/print/multiple_arguments_1/main.k new file mode 100644 index 000000000..0bace31be --- /dev/null +++ b/test/grammar/builtins/default/print/multiple_arguments_1/main.k @@ -0,0 +1,3 @@ +a = 100 +b = "Hello" +#print("Hello, World!", a, b, a, b) diff --git a/test/grammar/builtins/default/print/multiple_arguments_1/stdout.golden b/test/grammar/builtins/default/print/multiple_arguments_1/stdout.golden new file mode 100644 index 000000000..02d6a06b8 --- /dev/null +++ b/test/grammar/builtins/default/print/multiple_arguments_1/stdout.golden @@ -0,0 +1,2 @@ +a: 100 +b: Hello diff --git a/test/grammar/builtins/default/range/main.k b/test/grammar/builtins/default/range/main.k new file mode 100644 index 000000000..7381feba8 --- /dev/null +++ b/test/grammar/builtins/default/range/main.k @@ -0,0 +1,2 @@ +a = [*range(5)] +b = [*range(0, 1, 2)] diff --git a/test/grammar/builtins/default/range/stdout.golden b/test/grammar/builtins/default/range/stdout.golden new file mode 100644 index 000000000..8dc3cc828 --- /dev/null +++ b/test/grammar/builtins/default/range/stdout.golden @@ -0,0 +1,8 @@ +a: +- 0 +- 1 +- 2 +- 3 +- 4 +b: +- 0 diff --git a/test/grammar/builtins/default/round/main.k b/test/grammar/builtins/default/round/main.k new file mode 100644 index 000000000..2736f068a --- /dev/null +++ b/test/grammar/builtins/default/round/main.k @@ -0,0 +1,3 @@ +a = round(70.23456) +b = round(80.264, 2) +c = round(-100.000056, 3) diff --git a/test/grammar/builtins/default/round/stdout.golden b/test/grammar/builtins/default/round/stdout.golden new file mode 100644 index 000000000..67960e5d5 --- /dev/null +++ b/test/grammar/builtins/default/round/stdout.golden @@ -0,0 +1,3 @@ +a: 70 +b: 80.26 +c: -100.0 \ No newline at end of file diff --git a/test/grammar/builtins/default/sorted/main.k b/test/grammar/builtins/default/sorted/main.k new file mode 100644 index 000000000..030c44ac6 --- /dev/null +++ b/test/grammar/builtins/default/sorted/main.k @@ -0,0 +1,2 @@ +a = sorted([5, 2, 3, 1, 4]) +b = sorted([5, 0, 6, 1, 2, 7, 3, 4], reverse=True) # fixme, see handle_tree_call_expr \ No newline at end of file diff --git a/test/grammar/builtins/default/sorted/stdout.golden b/test/grammar/builtins/default/sorted/stdout.golden new file mode 100644 index 000000000..240ba63f6 --- /dev/null +++ b/test/grammar/builtins/default/sorted/stdout.golden @@ -0,0 +1,15 @@ +a: +- 1 +- 2 +- 3 +- 4 +- 5 +b: +- 7 +- 6 +- 5 +- 4 +- 3 +- 2 +- 1 +- 0 diff --git a/test/grammar/builtins/default/sum/main.k b/test/grammar/builtins/default/sum/main.k new file mode 100644 index 000000000..115f9d374 --- /dev/null +++ b/test/grammar/builtins/default/sum/main.k @@ -0,0 +1,2 @@ +a = sum([0, 1, 2]) +b = sum([0, 1, 2, 3, 4], 2) diff --git a/test/grammar/builtins/default/sum/stdout.golden b/test/grammar/builtins/default/sum/stdout.golden new file mode 100644 index 000000000..edd958b2d --- /dev/null +++ b/test/grammar/builtins/default/sum/stdout.golden @@ -0,0 +1,2 @@ +a: 3 +b: 12 \ No newline at end of file diff --git a/test/grammar/builtins/default/zip/main.k b/test/grammar/builtins/default/zip/main.k new file mode 100644 index 000000000..9d7e9aaf0 --- /dev/null +++ b/test/grammar/builtins/default/zip/main.k @@ -0,0 +1,3 @@ +a = zip([0, 1, 2], [3, 4, 5]) +b = zip([0, 1], [3, 4, 5]) +c = zip([0, 1, 2], [3, 4, 5, 6]) diff --git a/test/grammar/builtins/default/zip/stdout.golden b/test/grammar/builtins/default/zip/stdout.golden new file mode 100644 index 000000000..7ca4e21af --- /dev/null +++ b/test/grammar/builtins/default/zip/stdout.golden @@ -0,0 +1,19 @@ +a: +- - 0 + - 3 +- - 1 + - 4 +- - 2 + - 5 +b: +- - 0 + - 3 +- - 1 + - 4 +c: +- - 0 + - 3 +- - 1 + - 4 +- - 2 + - 5 diff --git a/test/grammar/builtins/float/create/main.k b/test/grammar/builtins/float/create/main.k new file mode 100644 index 000000000..22cccc7c6 --- /dev/null +++ b/test/grammar/builtins/float/create/main.k @@ -0,0 +1,7 @@ +a = 1.10 +b = 1.0 +c = -35.59 +# d = 32.3+e18 # fixme +e = -90. +# f = 70.2-E12 # fixme +g = float("112") # float constructor \ No newline at end of file diff --git a/test/grammar/builtins/float/create/stdout.golden b/test/grammar/builtins/float/create/stdout.golden new file mode 100644 index 000000000..029c308de --- /dev/null +++ b/test/grammar/builtins/float/create/stdout.golden @@ -0,0 +1,5 @@ +a: 1.1 +b: 1.0 +c: -35.59 +e: -90.0 +g: 112.0 \ No newline at end of file diff --git a/test/grammar/builtins/int/binary_prefix/main.k b/test/grammar/builtins/int/binary_prefix/main.k new file mode 100644 index 000000000..4b25bf242 --- /dev/null +++ b/test/grammar/builtins/int/binary_prefix/main.k @@ -0,0 +1,2 @@ +memory0 = 1024Mi +memory1 = int("1024Mi") \ No newline at end of file diff --git a/test/grammar/builtins/int/binary_prefix/stdout.golden b/test/grammar/builtins/int/binary_prefix/stdout.golden new file mode 100644 index 000000000..b385c7e62 --- /dev/null +++ b/test/grammar/builtins/int/binary_prefix/stdout.golden @@ -0,0 +1,2 @@ +memory0: 1073741824 +memory1: 1073741824 \ No newline at end of file diff --git a/test/grammar/builtins/int/create/main.k b/test/grammar/builtins/int/create/main.k new file mode 100644 index 000000000..1fdd4c5bc --- /dev/null +++ b/test/grammar/builtins/int/create/main.k @@ -0,0 +1,6 @@ +a = 1 +b = -1 +# c = 0x10 # fixme: hexadecimal literal +d = 0o10 # octal literal +e = 0b10 # binary literal +f = int("10") # int constructor \ No newline at end of file diff --git a/test/grammar/builtins/int/create/stdout.golden b/test/grammar/builtins/int/create/stdout.golden new file mode 100644 index 000000000..9d35550a0 --- /dev/null +++ b/test/grammar/builtins/int/create/stdout.golden @@ -0,0 +1,5 @@ +a: 1 +b: -1 +d: 8 +e: 2 +f: 10 \ No newline at end of file diff --git a/test/grammar/builtins/json/decode/main.k b/test/grammar/builtins/json/decode/main.k new file mode 100644 index 000000000..98b87ef2f --- /dev/null +++ b/test/grammar/builtins/json/decode/main.k @@ -0,0 +1,11 @@ +import json + +jsonStrList = [ + '{"key": "value"}', + '[1, 2, 3]', + '1', + '1.1', + 'null', + 'true', +] +data = [json.decode(s) for s in jsonStrList] diff --git a/test/grammar/builtins/json/decode/stdout.golden b/test/grammar/builtins/json/decode/stdout.golden new file mode 100644 index 000000000..a58edaceb --- /dev/null +++ b/test/grammar/builtins/json/decode/stdout.golden @@ -0,0 +1,16 @@ +jsonStrList: +- '{"key": "value"}' +- '[1, 2, 3]' +- '1' +- '1.1' +- 'null' +- 'true' +data: +- key: value +- - 1 + - 2 + - 3 +- 1 +- 1.1 +- null +- true diff --git a/test/grammar/builtins/json/encode_0/main.k b/test/grammar/builtins/json/encode_0/main.k new file mode 100644 index 000000000..6b73e6fda --- /dev/null +++ b/test/grammar/builtins/json/encode_0/main.k @@ -0,0 +1,10 @@ +import json + +dataDict = {"key": "value"} +dataList = [1, 2, 3] +dataInt = 1 +dataFloat = 1.1 +dataNone = None +dataBool = True + +jsonStr = [json.encode(data) for data in [dataDict, dataList, dataInt, dataFloat, dataNone, dataBool]] diff --git a/test/grammar/builtins/json/encode_0/stdout.golden b/test/grammar/builtins/json/encode_0/stdout.golden new file mode 100644 index 000000000..2a53e949b --- /dev/null +++ b/test/grammar/builtins/json/encode_0/stdout.golden @@ -0,0 +1,17 @@ +dataDict: + key: value +dataList: +- 1 +- 2 +- 3 +dataInt: 1 +dataFloat: 1.1 +dataNone: null +dataBool: true +jsonStr: +- '{"key": "value"}' +- '[1, 2, 3]' +- '1' +- '1.1' +- 'null' +- 'true' diff --git a/test/grammar/builtins/json/encode_1/main.k b/test/grammar/builtins/json/encode_1/main.k new file mode 100644 index 000000000..e9fc7bb27 --- /dev/null +++ b/test/grammar/builtins/json/encode_1/main.k @@ -0,0 +1,8 @@ +import json + +data = { + "key1": "value1" + "key2": "value2" + "data": [1, 2, 3] +} +data_string = json.encode(data) diff --git a/test/grammar/builtins/json/encode_1/stdout.golden b/test/grammar/builtins/json/encode_1/stdout.golden new file mode 100644 index 000000000..2e5ecd768 --- /dev/null +++ b/test/grammar/builtins/json/encode_1/stdout.golden @@ -0,0 +1,8 @@ +data: + key1: value1 + key2: value2 + data: + - 1 + - 2 + - 3 +data_string: '{"key1": "value1", "key2": "value2", "data": [1, 2, 3]}' diff --git a/test/grammar/builtins/json/encode_2/main.k b/test/grammar/builtins/json/encode_2/main.k new file mode 100644 index 000000000..ca7d0163b --- /dev/null +++ b/test/grammar/builtins/json/encode_2/main.k @@ -0,0 +1 @@ +x = "同步" diff --git a/test/grammar/builtins/json/encode_2/stdout.golden b/test/grammar/builtins/json/encode_2/stdout.golden new file mode 100644 index 000000000..bb4e6eca7 --- /dev/null +++ b/test/grammar/builtins/json/encode_2/stdout.golden @@ -0,0 +1 @@ +x: 同步 \ No newline at end of file diff --git a/test/grammar/builtins/json/output_0/main.k b/test/grammar/builtins/json/output_0/main.k new file mode 100644 index 000000000..5be6161d0 --- /dev/null +++ b/test/grammar/builtins/json/output_0/main.k @@ -0,0 +1,22 @@ + +import json + +schema Person: + __settings__: {str:str} = {"output_type": "STANDALONE"} + name?: str + age?: int + school?: str + data?: [int] = [1, 2, None] + +_person = Person { + name: "Alice" + age: 18 +} +#print("[", end="") +#print(json.encode(_person, indent=4), end=",\n") +#print(json.encode(_person, indent=4, ignore_private=True), end=",\n") +#print(json.encode(_person, indent=4, ignore_none=True), end=",\n") +#print(json.encode(_person, indent=4, ignore_private=True, ignore_none=True), end="]\n") + +a1 = json.encode(_person, indent=4, ignore_private=True) +a2 = json.encode(_person, indent=4, ignore_private=True, ignore_none=True) diff --git a/test/grammar/builtins/json/output_0/stdout.golden b/test/grammar/builtins/json/output_0/stdout.golden new file mode 100644 index 000000000..b1475f45e --- /dev/null +++ b/test/grammar/builtins/json/output_0/stdout.golden @@ -0,0 +1,19 @@ +a1: |- + { + "name": "Alice", + "age": 18, + "data": [ + 1, + 2, + null + ] + } +a2: |- + { + "name": "Alice", + "age": 18, + "data": [ + 1, + 2 + ] + } diff --git a/test/grammar/builtins/json/output_1/main.k b/test/grammar/builtins/json/output_1/main.k new file mode 100644 index 000000000..6e22b6137 --- /dev/null +++ b/test/grammar/builtins/json/output_1/main.k @@ -0,0 +1,17 @@ + +import json + +schema Person: + __settings__: {str:str} = {"output_type": "STANDALONE"} + name?: str + age?: int + school?: str + data?: [int] = [1, 2, None] + +_person = Person { + name: "Alice" + age: 18 +} +_filename = "out.json" +print("JSON output is in {}".format(_filename)) +json.dump_to_file(_person, _filename, indent=4, ignore_private=True, ignore_none=True) diff --git a/test/grammar/builtins/json/output_1/out.json b/test/grammar/builtins/json/output_1/out.json new file mode 100644 index 000000000..ec54b71d4 --- /dev/null +++ b/test/grammar/builtins/json/output_1/out.json @@ -0,0 +1,8 @@ +{ + "name": "Alice", + "age": 18, + "data": [ + 1, + 2 + ] +} \ No newline at end of file diff --git a/test/grammar/builtins/json/output_1/stdout.golden b/test/grammar/builtins/json/output_1/stdout.golden new file mode 100644 index 000000000..27297403a --- /dev/null +++ b/test/grammar/builtins/json/output_1/stdout.golden @@ -0,0 +1 @@ +JSON output is in out.json diff --git a/test/grammar/builtins/json/output_2/main.k b/test/grammar/builtins/json/output_2/main.k new file mode 100644 index 000000000..68d66e5d4 --- /dev/null +++ b/test/grammar/builtins/json/output_2/main.k @@ -0,0 +1,19 @@ + +import json + +_person = { + "_key": "value" + "name": "Alice" + "age": 18 + "data": [1, 2, None] + "labels": { + "key1": "value1" + "_key2": "value2" + "key3": None + } +} +print("[", end="") +print(json.encode(_person, indent=4), end=",\n") +print(json.encode(_person, indent=4, ignore_private=True), end=",\n") +print(json.encode(_person, indent=4, ignore_none=True), end=",\n") +print(json.encode(_person, indent=4, ignore_private=True, ignore_none=True), end="]\n") diff --git a/test/grammar/builtins/json/output_2/stdout.golden b/test/grammar/builtins/json/output_2/stdout.golden new file mode 100644 index 000000000..31b4bfe1e --- /dev/null +++ b/test/grammar/builtins/json/output_2/stdout.golden @@ -0,0 +1,52 @@ +[{ + "_key": "value", + "name": "Alice", + "age": 18, + "data": [ + 1, + 2, + null + ], + "labels": { + "key1": "value1", + "_key2": "value2", + "key3": null + } +}, +{ + "name": "Alice", + "age": 18, + "data": [ + 1, + 2, + null + ], + "labels": { + "key1": "value1", + "key3": null + } +}, +{ + "_key": "value", + "name": "Alice", + "age": 18, + "data": [ + 1, + 2 + ], + "labels": { + "key1": "value1", + "_key2": "value2" + } +}, +{ + "name": "Alice", + "age": 18, + "data": [ + 1, + 2 + ], + "labels": { + "key1": "value1" + } +}] diff --git a/test/grammar/builtins/math/ceil/main.k b/test/grammar/builtins/math/ceil/main.k new file mode 100644 index 000000000..82a75db8d --- /dev/null +++ b/test/grammar/builtins/math/ceil/main.k @@ -0,0 +1,4 @@ +import math + +a = math.ceil(-45.17) +b = math.ceil(100.12) diff --git a/test/grammar/builtins/math/ceil/stdout.golden b/test/grammar/builtins/math/ceil/stdout.golden new file mode 100644 index 000000000..6939b5405 --- /dev/null +++ b/test/grammar/builtins/math/ceil/stdout.golden @@ -0,0 +1,2 @@ +a: -45 +b: 101 \ No newline at end of file diff --git a/test/grammar/builtins/math/exp/main.k b/test/grammar/builtins/math/exp/main.k new file mode 100644 index 000000000..a905ff681 --- /dev/null +++ b/test/grammar/builtins/math/exp/main.k @@ -0,0 +1,4 @@ +import math + +a = math.exp(2) +b = math.exp(-6.89) diff --git a/test/grammar/builtins/math/exp/stdout.golden b/test/grammar/builtins/math/exp/stdout.golden new file mode 100644 index 000000000..2dbaceeb3 --- /dev/null +++ b/test/grammar/builtins/math/exp/stdout.golden @@ -0,0 +1,2 @@ +a: 7.38905609893065 +b: 0.0010179138409954387 \ No newline at end of file diff --git a/test/grammar/builtins/math/expm1/main.k b/test/grammar/builtins/math/expm1/main.k new file mode 100644 index 000000000..4d56bf8fb --- /dev/null +++ b/test/grammar/builtins/math/expm1/main.k @@ -0,0 +1,4 @@ +import math + +a = math.expm1(32) +b = math.expm1(-10.89) diff --git a/test/grammar/builtins/math/expm1/stdout.golden b/test/grammar/builtins/math/expm1/stdout.golden new file mode 100644 index 000000000..819d6db65 --- /dev/null +++ b/test/grammar/builtins/math/expm1/stdout.golden @@ -0,0 +1,2 @@ +a: 78962960182679.69 +b: -0.9999813562576685 \ No newline at end of file diff --git a/test/grammar/builtins/math/factorial/main.k b/test/grammar/builtins/math/factorial/main.k new file mode 100644 index 000000000..2e4087b57 --- /dev/null +++ b/test/grammar/builtins/math/factorial/main.k @@ -0,0 +1,3 @@ +import math + +a = math.factorial(5) diff --git a/test/grammar/builtins/math/factorial/stdout.golden b/test/grammar/builtins/math/factorial/stdout.golden new file mode 100644 index 000000000..65fb6ed4f --- /dev/null +++ b/test/grammar/builtins/math/factorial/stdout.golden @@ -0,0 +1 @@ +a: 120 \ No newline at end of file diff --git a/test/grammar/builtins/math/floor/main.k b/test/grammar/builtins/math/floor/main.k new file mode 100644 index 000000000..a6a56a4d6 --- /dev/null +++ b/test/grammar/builtins/math/floor/main.k @@ -0,0 +1,4 @@ +import math + +a = math.floor(-45.17) +b = math.floor(100.12) diff --git a/test/grammar/builtins/math/floor/stdout.golden b/test/grammar/builtins/math/floor/stdout.golden new file mode 100644 index 000000000..f4e990f49 --- /dev/null +++ b/test/grammar/builtins/math/floor/stdout.golden @@ -0,0 +1,2 @@ +a: -46 +b: 100 \ No newline at end of file diff --git a/test/grammar/builtins/math/gcd/main.k b/test/grammar/builtins/math/gcd/main.k new file mode 100644 index 000000000..0c96a0799 --- /dev/null +++ b/test/grammar/builtins/math/gcd/main.k @@ -0,0 +1,3 @@ +import math + +a = math.gcd(60, 48) diff --git a/test/grammar/builtins/math/gcd/stdout.golden b/test/grammar/builtins/math/gcd/stdout.golden new file mode 100644 index 000000000..106e61b97 --- /dev/null +++ b/test/grammar/builtins/math/gcd/stdout.golden @@ -0,0 +1 @@ +a: 12 \ No newline at end of file diff --git a/test/grammar/builtins/math/isfinite/main.k b/test/grammar/builtins/math/isfinite/main.k new file mode 100644 index 000000000..af9b33391 --- /dev/null +++ b/test/grammar/builtins/math/isfinite/main.k @@ -0,0 +1,5 @@ +import math + +a = math.isfinite(1) +b = math.isfinite(0) +c = math.isfinite(float("nan")) diff --git a/test/grammar/builtins/math/isfinite/stdout.golden b/test/grammar/builtins/math/isfinite/stdout.golden new file mode 100644 index 000000000..5ae4b6dc6 --- /dev/null +++ b/test/grammar/builtins/math/isfinite/stdout.golden @@ -0,0 +1,3 @@ +a: true +b: true +c: false \ No newline at end of file diff --git a/test/grammar/builtins/math/isinf/main.k b/test/grammar/builtins/math/isinf/main.k new file mode 100644 index 000000000..177c1fcac --- /dev/null +++ b/test/grammar/builtins/math/isinf/main.k @@ -0,0 +1,5 @@ +import math + +a = math.isinf(1) +b = math.isinf(0) +c = math.isinf(float("nan")) diff --git a/test/grammar/builtins/math/isinf/stdout.golden b/test/grammar/builtins/math/isinf/stdout.golden new file mode 100644 index 000000000..07ff627e9 --- /dev/null +++ b/test/grammar/builtins/math/isinf/stdout.golden @@ -0,0 +1,3 @@ +a: false +b: false +c: false \ No newline at end of file diff --git a/test/grammar/builtins/math/isnan/main.k b/test/grammar/builtins/math/isnan/main.k new file mode 100644 index 000000000..f8d19b126 --- /dev/null +++ b/test/grammar/builtins/math/isnan/main.k @@ -0,0 +1,5 @@ +import math + +a = math.isnan(1) +b = math.isnan(0) +c = math.isnan(float("nan")) diff --git a/test/grammar/builtins/math/isnan/stdout.golden b/test/grammar/builtins/math/isnan/stdout.golden new file mode 100644 index 000000000..60a5ebdba --- /dev/null +++ b/test/grammar/builtins/math/isnan/stdout.golden @@ -0,0 +1,3 @@ +a: false +b: false +c: true \ No newline at end of file diff --git a/test/grammar/builtins/math/log/main.k b/test/grammar/builtins/math/log/main.k new file mode 100644 index 000000000..017166c4e --- /dev/null +++ b/test/grammar/builtins/math/log/main.k @@ -0,0 +1,5 @@ +import math + +a = math.log(2.7183) +b = math.log(2.0) +c = math.log(1.0) diff --git a/test/grammar/builtins/math/log/stdout.golden b/test/grammar/builtins/math/log/stdout.golden new file mode 100644 index 000000000..470234f10 --- /dev/null +++ b/test/grammar/builtins/math/log/stdout.golden @@ -0,0 +1,3 @@ +a: 1.0000066849139877 +b: 0.6931471805599453 +c: 0.0 \ No newline at end of file diff --git a/test/grammar/builtins/math/log10/main.k b/test/grammar/builtins/math/log10/main.k new file mode 100644 index 000000000..aaa1b9d0b --- /dev/null +++ b/test/grammar/builtins/math/log10/main.k @@ -0,0 +1,5 @@ +import math + +a = math.log10(2.7183) +b = math.log10(2) +c = math.log10(1) diff --git a/test/grammar/builtins/math/log10/stdout.golden b/test/grammar/builtins/math/log10/stdout.golden new file mode 100644 index 000000000..256931db0 --- /dev/null +++ b/test/grammar/builtins/math/log10/stdout.golden @@ -0,0 +1,3 @@ +a: 0.43429738512450866 +b: 0.3010299956639812 +c: 0.0 \ No newline at end of file diff --git a/test/grammar/builtins/math/log1p/main.k b/test/grammar/builtins/math/log1p/main.k new file mode 100644 index 000000000..6537ed7ee --- /dev/null +++ b/test/grammar/builtins/math/log1p/main.k @@ -0,0 +1,9 @@ +import math + +_a = math.log1p(2.7183) +_b = math.log1p(2) +_c = math.log1p(1) + +a = 1.3132665 < _a +b = 1.0986122 < _b +c = 0.6931471 < _c diff --git a/test/grammar/builtins/math/log1p/stdout.golden b/test/grammar/builtins/math/log1p/stdout.golden new file mode 100644 index 000000000..26ca2d501 --- /dev/null +++ b/test/grammar/builtins/math/log1p/stdout.golden @@ -0,0 +1,3 @@ +a: true +b: true +c: true \ No newline at end of file diff --git a/test/grammar/builtins/math/log2/main.k b/test/grammar/builtins/math/log2/main.k new file mode 100644 index 000000000..c7f4bfa66 --- /dev/null +++ b/test/grammar/builtins/math/log2/main.k @@ -0,0 +1,5 @@ +import math + +a = math.log2(2.7183) +b = math.log2(2) +c = math.log2(1) diff --git a/test/grammar/builtins/math/log2/stdout.golden b/test/grammar/builtins/math/log2/stdout.golden new file mode 100644 index 000000000..1660c0258 --- /dev/null +++ b/test/grammar/builtins/math/log2/stdout.golden @@ -0,0 +1,3 @@ +a: 1.4427046851812222 +b: 1.0 +c: 0.0 \ No newline at end of file diff --git a/test/grammar/builtins/math/modf/main.k b/test/grammar/builtins/math/modf/main.k new file mode 100644 index 000000000..a4a4cb6e1 --- /dev/null +++ b/test/grammar/builtins/math/modf/main.k @@ -0,0 +1,4 @@ +import math + +a = math.modf(100.12) +b = math.modf(100.72) diff --git a/test/grammar/builtins/math/modf/stdout.golden b/test/grammar/builtins/math/modf/stdout.golden new file mode 100644 index 000000000..86587c451 --- /dev/null +++ b/test/grammar/builtins/math/modf/stdout.golden @@ -0,0 +1,6 @@ +a: +- 0.12000000000000455 +- 100.0 +b: +- 0.7199999999999989 +- 100.0 \ No newline at end of file diff --git a/test/grammar/builtins/math/pow/main.k b/test/grammar/builtins/math/pow/main.k new file mode 100644 index 000000000..0726b7fdd --- /dev/null +++ b/test/grammar/builtins/math/pow/main.k @@ -0,0 +1,3 @@ +import math + +a = math.pow(9, 3) diff --git a/test/grammar/builtins/math/pow/stdout.golden b/test/grammar/builtins/math/pow/stdout.golden new file mode 100644 index 000000000..e9f3b07ae --- /dev/null +++ b/test/grammar/builtins/math/pow/stdout.golden @@ -0,0 +1 @@ +a: 729.0 \ No newline at end of file diff --git a/test/grammar/builtins/math/sqrt/main.k b/test/grammar/builtins/math/sqrt/main.k new file mode 100644 index 000000000..ed1d09b12 --- /dev/null +++ b/test/grammar/builtins/math/sqrt/main.k @@ -0,0 +1,3 @@ +import math + +a = math.sqrt(9) diff --git a/test/grammar/builtins/math/sqrt/stdout.golden b/test/grammar/builtins/math/sqrt/stdout.golden new file mode 100644 index 000000000..8bd80ba96 --- /dev/null +++ b/test/grammar/builtins/math/sqrt/stdout.golden @@ -0,0 +1 @@ +a: 3.0 \ No newline at end of file diff --git a/test/grammar/builtins/net/host_port/main.k b/test/grammar/builtins/net/host_port/main.k new file mode 100644 index 000000000..57e54607f --- /dev/null +++ b/test/grammar/builtins/net/host_port/main.k @@ -0,0 +1,4 @@ +import net + +host_and_port = net.split_host_port("B-K0NZJGH6-0048.local:80") +host_port = net.join_host_port("B-K0NZJGH6-0048.local", 80) diff --git a/test/grammar/builtins/net/host_port/stdout.golden b/test/grammar/builtins/net/host_port/stdout.golden new file mode 100644 index 000000000..55f227459 --- /dev/null +++ b/test/grammar/builtins/net/host_port/stdout.golden @@ -0,0 +1,4 @@ +host_and_port: +- B-K0NZJGH6-0048.local +- '80' +host_port: B-K0NZJGH6-0048.local:80 \ No newline at end of file diff --git a/test/grammar/builtins/net/is_ip_0/main.k b/test/grammar/builtins/net/is_ip_0/main.k new file mode 100644 index 000000000..9a82476fd --- /dev/null +++ b/test/grammar/builtins/net/is_ip_0/main.k @@ -0,0 +1,34 @@ +import math +import net + +schema IPCheck[ipValue]: + ip: str = ipValue + + """ + # FIXME: built-in func in schema check + check: + net.is_IP(ip) + """ + +ipcheck = IPCheck("192.168.0.1") {} + +isip00 = net.is_IP("192.168.0,1") +isip01 = net.is_IP("192.168.0.") +isip02 = net.is_IP("192.168.") +isip03 = net.is_IP("192.") +isip04 = net.is_IP("256.168.0,1") +isip05 = net.is_IP("255.255.0.-1") +isip06 = net.is_IP("192.168.") +isip07 = net.is_IP("192.0022.1.1") +isip08 = net.is_IP("492.10.123.12313") +isip09 = net.is_IP("0xFF.0xFF.0xFF.0xFF") + +isip10 = net.is_IP("255.255.0.1") +isip11 = net.is_IP("1.0.0.0") +isip12 = net.is_IP("192.190.0.1") +isip14 = net.is_IP("128.0.0.0") +isip15 = net.is_IP("172.16.0.0") +isip16 = net.is_IP("172.31.255.255") +isip17 = net.is_IP("169.254.0.1") +isip18 = net.is_IP("191.255.255.255") +isip19 = net.is_IP("223.255.255.0") diff --git a/test/grammar/builtins/net/is_ip_0/stdout.golden b/test/grammar/builtins/net/is_ip_0/stdout.golden new file mode 100644 index 000000000..a7268545c --- /dev/null +++ b/test/grammar/builtins/net/is_ip_0/stdout.golden @@ -0,0 +1,21 @@ +ipcheck: + ip: 192.168.0.1 +isip00: false +isip01: false +isip02: false +isip03: false +isip04: false +isip05: false +isip06: false +isip07: false +isip08: false +isip09: false +isip10: true +isip11: true +isip12: true +isip14: true +isip15: true +isip16: true +isip17: true +isip18: true +isip19: true \ No newline at end of file diff --git a/test/grammar/builtins/net/is_ip_1/main.k b/test/grammar/builtins/net/is_ip_1/main.k new file mode 100644 index 000000000..dd7806570 --- /dev/null +++ b/test/grammar/builtins/net/is_ip_1/main.k @@ -0,0 +1,4 @@ +import net + +isip0 = net.is_IP("2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b") +isip1 = net.is_IP("2001:0db8:3c4d:0015+0000:0000:1a2f:1a2b") diff --git a/test/grammar/builtins/net/is_ip_1/stdout.golden b/test/grammar/builtins/net/is_ip_1/stdout.golden new file mode 100644 index 000000000..23bbe9773 --- /dev/null +++ b/test/grammar/builtins/net/is_ip_1/stdout.golden @@ -0,0 +1,2 @@ +isip0: true +isip1: false \ No newline at end of file diff --git a/test/grammar/builtins/net/is_ip_2/main.k b/test/grammar/builtins/net/is_ip_2/main.k new file mode 100644 index 000000000..d215848d3 --- /dev/null +++ b/test/grammar/builtins/net/is_ip_2/main.k @@ -0,0 +1,8 @@ +import net + +isip0 = net.is_multicast_IP("239.255.255.255") +isip1 = net.is_loopback_IP("127.0.0.1") +isip2 = net.is_link_local_multicast_IP("224.0.0.0") +isip3 = net.is_link_local_unicast_IP("fe80::2012:1") +isip4 = net.is_global_unicast_IP("220.181.108.89") +isip5 = net.is_unspecified_IP("0.0.0.0") diff --git a/test/grammar/builtins/net/is_ip_2/stdout.golden b/test/grammar/builtins/net/is_ip_2/stdout.golden new file mode 100644 index 000000000..03265e7b7 --- /dev/null +++ b/test/grammar/builtins/net/is_ip_2/stdout.golden @@ -0,0 +1,6 @@ +isip0: true +isip1: true +isip2: false +isip3: true +isip4: true +isip5: true \ No newline at end of file diff --git a/test/grammar/builtins/operator/operator_fail_0/main.k b/test/grammar/builtins/operator/operator_fail_0/main.k new file mode 100644 index 000000000..c0ab8b669 --- /dev/null +++ b/test/grammar/builtins/operator/operator_fail_0/main.k @@ -0,0 +1,2 @@ +a = None +b = 1 + None diff --git a/test/grammar/builtins/operator/operator_fail_0/stderr.golden.py b/test/grammar/builtins/operator/operator_fail_0/stderr.golden.py new file mode 100644 index 000000000..3da23c34b --- /dev/null +++ b/test/grammar/builtins/operator/operator_fail_0/stderr.golden.py @@ -0,0 +1,21 @@ + +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2 + ) + ], + arg_msg="unsupported operand type(s) for +: 'int(1)' and 'NoneType'" + ), + file=sys.stdout +) diff --git a/test/grammar/builtins/operator/operator_fail_1/main.k b/test/grammar/builtins/operator/operator_fail_1/main.k new file mode 100644 index 000000000..f387eade6 --- /dev/null +++ b/test/grammar/builtins/operator/operator_fail_1/main.k @@ -0,0 +1,2 @@ +a = None +b = None + 1 diff --git a/test/grammar/builtins/operator/operator_fail_1/stderr.golden.py b/test/grammar/builtins/operator/operator_fail_1/stderr.golden.py new file mode 100644 index 000000000..21347a90e --- /dev/null +++ b/test/grammar/builtins/operator/operator_fail_1/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2 + ) + ], + arg_msg="unsupported operand type(s) for +: 'NoneType' and 'int(1)'" + ), + file=sys.stdout +) + diff --git a/test/grammar/builtins/operator/operator_fail_2/main.k b/test/grammar/builtins/operator/operator_fail_2/main.k new file mode 100644 index 000000000..32b4eb4a9 --- /dev/null +++ b/test/grammar/builtins/operator/operator_fail_2/main.k @@ -0,0 +1,2 @@ +a = [1, 2, {"1": "2"}] +b = 1 + a diff --git a/test/grammar/builtins/operator/operator_fail_2/stderr.golden.py b/test/grammar/builtins/operator/operator_fail_2/stderr.golden.py new file mode 100644 index 000000000..72e10e9f9 --- /dev/null +++ b/test/grammar/builtins/operator/operator_fail_2/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2 + ) + ], + arg_msg="unsupported operand type(s) for +: 'int(1)' and '[int|{str:str}]'" + ), + file=sys.stdout +) + diff --git a/test/grammar/builtins/regex/test_0/main.k b/test/grammar/builtins/regex/test_0/main.k new file mode 100644 index 000000000..748262d1d --- /dev/null +++ b/test/grammar/builtins/regex/test_0/main.k @@ -0,0 +1,10 @@ +import regex + +regex_source = "Apple,Google,Baidu,Xiaomi" +regex_split = regex.split(regex_source, ",") +regex_replace = regex.replace(regex_source, ",", "|") +regex_compile = regex.compile("$^") +regex_search = regex.search("aaaa", "a") +regex_find_all = regex.findall("aaaa", "a") +regex_result = regex.match("192.168.0.1", "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."+"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$") +regex_result_false = regex.match("192.168.0,1", "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."+"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$") diff --git a/test/grammar/builtins/regex/test_0/stdout.golden b/test/grammar/builtins/regex/test_0/stdout.golden new file mode 100644 index 000000000..8013ad517 --- /dev/null +++ b/test/grammar/builtins/regex/test_0/stdout.golden @@ -0,0 +1,16 @@ +regex_source: Apple,Google,Baidu,Xiaomi +regex_split: +- Apple +- Google +- Baidu +- Xiaomi +regex_replace: Apple|Google|Baidu|Xiaomi +regex_compile: true +regex_search: true +regex_find_all: +- a +- a +- a +- a +regex_result: true +regex_result_false: false diff --git a/test/grammar/builtins/regex/test_1/main.k b/test/grammar/builtins/regex/test_1/main.k new file mode 100644 index 000000000..a56f5abdd --- /dev/null +++ b/test/grammar/builtins/regex/test_1/main.k @@ -0,0 +1,12 @@ +import regex + +# True cases +x0 = regex.match("x", r"^[A-Za-z_][A-Za-z0-9_]*$") +x1 = regex.match("x1", r"^[A-Za-z_][A-Za-z0-9_]*$") +x2 = regex.match("_x2", r"^[A-Za-z_][A-Za-z0-9_]*$") +x3 = regex.match("__X3__", r"^[A-Za-z_][A-Za-z0-9_]*$") +x4 = regex.match("x_4", r"^[A-Za-z_][A-Za-z0-9_]*$") +# False cases +x5 = regex.match("$", r"^[A-Za-z_][A-Za-z0-9_]*$") +x6 = regex.match("6x", r"^[A-Za-z_][A-Za-z0-9_]*$") +x7 = regex.match("x-7", r"^[A-Za-z_][A-Za-z0-9_]*$") diff --git a/test/grammar/builtins/regex/test_1/stdout.golden b/test/grammar/builtins/regex/test_1/stdout.golden new file mode 100644 index 000000000..dcc1e3379 --- /dev/null +++ b/test/grammar/builtins/regex/test_1/stdout.golden @@ -0,0 +1,8 @@ +x0: true +x1: true +x2: true +x3: true +x4: true +x5: false +x6: false +x7: false diff --git a/test/grammar/builtins/regex/test_2/main.k b/test/grammar/builtins/regex/test_2/main.k new file mode 100644 index 000000000..d84c2956f --- /dev/null +++ b/test/grammar/builtins/regex/test_2/main.k @@ -0,0 +1,14 @@ +import regex + +_pattern = "^(?!-)[a-z0-9-]{1,63}(? 2 else i + 1 for i in data] # [2, 3, 3] diff --git a/test/grammar/comprehension/list/multi_vars_0/stdout.golden b/test/grammar/comprehension/list/multi_vars_0/stdout.golden new file mode 100644 index 000000000..b17b35142 --- /dev/null +++ b/test/grammar/comprehension/list/multi_vars_0/stdout.golden @@ -0,0 +1,18 @@ +data: +- 1 +- 2 +- 3 +dataLoop1: +- 0 +- 1 +- 2 +dataLoop2: +- 2 +- 4 +- 6 +dataLoop3: +- 2 +dataLoop4: +- 2 +- 3 +- 3 diff --git a/test/grammar/comprehension/list/multi_vars_1/main.k b/test/grammar/comprehension/list/multi_vars_1/main.k new file mode 100644 index 000000000..56b32263d --- /dev/null +++ b/test/grammar/comprehension/list/multi_vars_1/main.k @@ -0,0 +1,4 @@ +data = [1, 2, 3] +dataLoop1 = [i + v for i, v in data] # [1, 3, 5] +dataLoop2 = [v for i, v in data if v == 2] # [2] +dataLoop3 = [v if v > 2 else v + i for i, v in data] # [1, 3, 3] diff --git a/test/grammar/comprehension/list/multi_vars_1/stdout.golden b/test/grammar/comprehension/list/multi_vars_1/stdout.golden new file mode 100644 index 000000000..72ba3c840 --- /dev/null +++ b/test/grammar/comprehension/list/multi_vars_1/stdout.golden @@ -0,0 +1,14 @@ +data: +- 1 +- 2 +- 3 +dataLoop1: +- 1 +- 3 +- 5 +dataLoop2: +- 2 +dataLoop3: +- 1 +- 3 +- 3 diff --git a/test/grammar/comprehension/list/multi_vars_2/main.k b/test/grammar/comprehension/list/multi_vars_2/main.k new file mode 100644 index 000000000..d5cb3bbb0 --- /dev/null +++ b/test/grammar/comprehension/list/multi_vars_2/main.k @@ -0,0 +1,3 @@ +data = [1, 2, 3] +dataLoop1 = [i for i, _ in data] # [0, 1, 2] +dataLoop2 = [v for _, v in data if v == 2] # [2] diff --git a/test/grammar/comprehension/list/multi_vars_2/stdout.golden b/test/grammar/comprehension/list/multi_vars_2/stdout.golden new file mode 100644 index 000000000..8317107a2 --- /dev/null +++ b/test/grammar/comprehension/list/multi_vars_2/stdout.golden @@ -0,0 +1,10 @@ +data: +- 1 +- 2 +- 3 +dataLoop1: +- 0 +- 1 +- 2 +dataLoop2: +- 2 diff --git a/test/grammar/comprehension/list/nested/main.k b/test/grammar/comprehension/list/nested/main.k new file mode 100644 index 000000000..d49be0947 --- /dev/null +++ b/test/grammar/comprehension/list/nested/main.k @@ -0,0 +1,6 @@ +matrix = [ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11, 12] +] +tmp = [[_row[_i] for _row in matrix] for _i in range(4)] diff --git a/test/grammar/comprehension/list/nested/stdout.golden b/test/grammar/comprehension/list/nested/stdout.golden new file mode 100644 index 000000000..7003c1a1a --- /dev/null +++ b/test/grammar/comprehension/list/nested/stdout.golden @@ -0,0 +1,26 @@ +matrix: +- - 1 + - 2 + - 3 + - 4 +- - 5 + - 6 + - 7 + - 8 +- - 9 + - 10 + - 11 + - 12 +tmp: +- - 1 + - 5 + - 9 +- - 2 + - 6 + - 10 +- - 3 + - 7 + - 11 +- - 4 + - 8 + - 12 \ No newline at end of file diff --git a/test/grammar/comprehension/list/normal/main.k b/test/grammar/comprehension/list/normal/main.k new file mode 100644 index 000000000..d27a989a3 --- /dev/null +++ b/test/grammar/comprehension/list/normal/main.k @@ -0,0 +1,2 @@ +list1 = [3, 4, 5] +multiplied = [item * 3 for item in list1] diff --git a/test/grammar/comprehension/list/normal/stdout.golden b/test/grammar/comprehension/list/normal/stdout.golden new file mode 100644 index 000000000..8e7555b2b --- /dev/null +++ b/test/grammar/comprehension/list/normal/stdout.golden @@ -0,0 +1,8 @@ +list1: +- 3 +- 4 +- 5 +multiplied: +- 9 +- 12 +- 15 \ No newline at end of file diff --git a/test/grammar/comprehension/list/to_dict_0/main.k b/test/grammar/comprehension/list/to_dict_0/main.k new file mode 100644 index 000000000..e26566a93 --- /dev/null +++ b/test/grammar/comprehension/list/to_dict_0/main.k @@ -0,0 +1,11 @@ +input = { + "k0": "v0", + "k1": "v1" +} + +output = [{ + "metadata": { + "name": k + }, + "data": input[k] +} for k in input] diff --git a/test/grammar/comprehension/list/to_dict_0/stdout.golden b/test/grammar/comprehension/list/to_dict_0/stdout.golden new file mode 100644 index 000000000..3e75aceda --- /dev/null +++ b/test/grammar/comprehension/list/to_dict_0/stdout.golden @@ -0,0 +1,10 @@ +input: + k0: v0 + k1: v1 +output: +- metadata: + name: k0 + data: v0 +- metadata: + name: k1 + data: v1 diff --git a/test/grammar/comprehension/list/to_dict_1/main.k b/test/grammar/comprehension/list/to_dict_1/main.k new file mode 100644 index 000000000..4a4d32589 --- /dev/null +++ b/test/grammar/comprehension/list/to_dict_1/main.k @@ -0,0 +1,20 @@ +input = { + "k0": { + "value": "v0" + }, + "k1": { + "value": "v1" + } +} + +output = [{ + "metadata": { + "name": k + }, + "spec": { + "data": { + "type": "mock_resource", + "value": input[k]["value"] + } + } +} for k in input] diff --git a/test/grammar/comprehension/list/to_dict_1/stdout.golden b/test/grammar/comprehension/list/to_dict_1/stdout.golden new file mode 100644 index 000000000..a9af48ffb --- /dev/null +++ b/test/grammar/comprehension/list/to_dict_1/stdout.golden @@ -0,0 +1,18 @@ +input: + k0: + value: v0 + k1: + value: v1 +output: +- metadata: + name: k0 + spec: + data: + type: mock_resource + value: v0 +- metadata: + name: k1 + spec: + data: + type: mock_resource + value: v1 diff --git a/test/grammar/comprehension/list/type_convertion_0/main.k b/test/grammar/comprehension/list/type_convertion_0/main.k new file mode 100644 index 000000000..99724bd9a --- /dev/null +++ b/test/grammar/comprehension/list/type_convertion_0/main.k @@ -0,0 +1 @@ +a = [k / 2 for k in [1, 2, 3, 4]] diff --git a/test/grammar/comprehension/list/type_convertion_0/stdout.golden b/test/grammar/comprehension/list/type_convertion_0/stdout.golden new file mode 100644 index 000000000..b4d556a50 --- /dev/null +++ b/test/grammar/comprehension/list/type_convertion_0/stdout.golden @@ -0,0 +1,5 @@ +a: +- 0.5 +- 1.0 +- 1.5 +- 2.0 diff --git a/test/grammar/comprehension/list/type_convertion_1/main.k b/test/grammar/comprehension/list/type_convertion_1/main.k new file mode 100644 index 000000000..e910212e6 --- /dev/null +++ b/test/grammar/comprehension/list/type_convertion_1/main.k @@ -0,0 +1 @@ +a = [k / 2 for k in range(4)] diff --git a/test/grammar/comprehension/list/type_convertion_1/stdout.golden b/test/grammar/comprehension/list/type_convertion_1/stdout.golden new file mode 100644 index 000000000..d0547f47f --- /dev/null +++ b/test/grammar/comprehension/list/type_convertion_1/stdout.golden @@ -0,0 +1,5 @@ +a: +- 0.0 +- 0.5 +- 1.0 +- 1.5 diff --git a/test/grammar/comprehension/str/func_call/main.k b/test/grammar/comprehension/str/func_call/main.k new file mode 100644 index 000000000..bffba4c42 --- /dev/null +++ b/test/grammar/comprehension/str/func_call/main.k @@ -0,0 +1,4 @@ + +dataString = "abc" +dataList = [c for c in dataString.upper()] +dataMap = {str(i): c for i, c in dataString[0:2]} diff --git a/test/grammar/comprehension/str/func_call/stdout.golden b/test/grammar/comprehension/str/func_call/stdout.golden new file mode 100644 index 000000000..f60038362 --- /dev/null +++ b/test/grammar/comprehension/str/func_call/stdout.golden @@ -0,0 +1,8 @@ +dataString: abc +dataList: +- A +- B +- C +dataMap: + '0': a + '1': b diff --git a/test/grammar/comprehension/str/in_schema_expr/main.k b/test/grammar/comprehension/str/in_schema_expr/main.k new file mode 100644 index 000000000..7d7a11132 --- /dev/null +++ b/test/grammar/comprehension/str/in_schema_expr/main.k @@ -0,0 +1,8 @@ +name = "Alice" + +schema Data: + charsStr: [str] + +data = Data { + charsStr = [c * 2 for c in name] +} diff --git a/test/grammar/comprehension/str/in_schema_expr/stdout.golden b/test/grammar/comprehension/str/in_schema_expr/stdout.golden new file mode 100644 index 000000000..cffc9b164 --- /dev/null +++ b/test/grammar/comprehension/str/in_schema_expr/stdout.golden @@ -0,0 +1,8 @@ +name: Alice +data: + charsStr: + - AA + - ll + - ii + - cc + - ee diff --git a/test/grammar/comprehension/str/invalid_loop_var_fail_0/main.k b/test/grammar/comprehension/str/invalid_loop_var_fail_0/main.k new file mode 100644 index 000000000..a680a955c --- /dev/null +++ b/test/grammar/comprehension/str/invalid_loop_var_fail_0/main.k @@ -0,0 +1,2 @@ +dataString = "abc" +dataLoop = [i for i, j, k in dataString] # error diff --git a/test/grammar/comprehension/str/invalid_loop_var_fail_0/stderr.golden.py b/test/grammar/comprehension/str/invalid_loop_var_fail_0/stderr.golden.py new file mode 100644 index 000000000..7b68df657 --- /dev/null +++ b/test/grammar/comprehension/str/invalid_loop_var_fail_0/stderr.golden.py @@ -0,0 +1,23 @@ +import os +import sys + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=19, + end_col_no=26 + ) + ], + arg_msg="the number of loop variables is 3, which can only be 1 or 2" + ), + file=sys.stdout +) + diff --git a/test/grammar/comprehension/str/invalid_loop_var_fail_1/main.k b/test/grammar/comprehension/str/invalid_loop_var_fail_1/main.k new file mode 100644 index 000000000..75996d85d --- /dev/null +++ b/test/grammar/comprehension/str/invalid_loop_var_fail_1/main.k @@ -0,0 +1,2 @@ +data = [1, 2, 3] +dataLoop = [i for i.j.k in data] # error diff --git a/test/grammar/comprehension/str/invalid_loop_var_fail_1/stderr.golden.py b/test/grammar/comprehension/str/invalid_loop_var_fail_1/stderr.golden.py new file mode 100644 index 000000000..a28d6dd59 --- /dev/null +++ b/test/grammar/comprehension/str/invalid_loop_var_fail_1/stderr.golden.py @@ -0,0 +1,23 @@ +import os +import sys + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=19, + end_col_no=24 + ) + ], + arg_msg="loop variables can only be ordinary identifiers" + ), + file=sys.stdout +) + diff --git a/test/grammar/comprehension/str/invalid_loop_var_fail_2/main.k b/test/grammar/comprehension/str/invalid_loop_var_fail_2/main.k new file mode 100644 index 000000000..84f033761 --- /dev/null +++ b/test/grammar/comprehension/str/invalid_loop_var_fail_2/main.k @@ -0,0 +1,2 @@ +data = [1, 2, 3] +dataLoop = [i for i() in data] # error diff --git a/test/grammar/comprehension/str/invalid_loop_var_fail_2/stderr.golden.py b/test/grammar/comprehension/str/invalid_loop_var_fail_2/stderr.golden.py new file mode 100644 index 000000000..5beb56e27 --- /dev/null +++ b/test/grammar/comprehension/str/invalid_loop_var_fail_2/stderr.golden.py @@ -0,0 +1,22 @@ +import os +import sys + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=19, + end_col_no=22 + ) + ], + arg_msg="loop variables can only be ordinary identifiers" + ), + file=sys.stdout +) diff --git a/test/grammar/comprehension/str/nested/main.k b/test/grammar/comprehension/str/nested/main.k new file mode 100644 index 000000000..782ec63d6 --- /dev/null +++ b/test/grammar/comprehension/str/nested/main.k @@ -0,0 +1,6 @@ +dataString = "abc" +dataList = [ + "{} {}".format(c1, c2) \ + for c1 in dataString.lower() \ + for c2 in dataString.upper() +] diff --git a/test/grammar/comprehension/str/nested/stdout.golden b/test/grammar/comprehension/str/nested/stdout.golden new file mode 100644 index 000000000..f533bbf55 --- /dev/null +++ b/test/grammar/comprehension/str/nested/stdout.golden @@ -0,0 +1,11 @@ +dataString: abc +dataList: +- a A +- a B +- a C +- b A +- b B +- b C +- c A +- c B +- c C diff --git a/test/grammar/comprehension/str/normal/main.k b/test/grammar/comprehension/str/normal/main.k new file mode 100644 index 000000000..3ce4c0338 --- /dev/null +++ b/test/grammar/comprehension/str/normal/main.k @@ -0,0 +1,4 @@ + +dataString = "abc" +dataList = [c for c in dataString] # ['a', 'b', 'c'] +dataMap = {str(i): c for i, c in dataString} # {0: 'a', 1: 'b', 2: 'c'} diff --git a/test/grammar/comprehension/str/normal/stdout.golden b/test/grammar/comprehension/str/normal/stdout.golden new file mode 100644 index 000000000..f8428dd42 --- /dev/null +++ b/test/grammar/comprehension/str/normal/stdout.golden @@ -0,0 +1,9 @@ +dataString: abc +dataList: +- a +- b +- c +dataMap: + '0': a + '1': b + '2': c diff --git a/test/grammar/datatype/conversion/int2float_0/main.k b/test/grammar/datatype/conversion/int2float_0/main.k new file mode 100644 index 000000000..1a6908d78 --- /dev/null +++ b/test/grammar/datatype/conversion/int2float_0/main.k @@ -0,0 +1,15 @@ +i = 10 +f = 1.1 +s = i + f +t = f + i + +schema Simple: + f0: float + f1: float + f2: float + +myschema = Simple { + f0: f + f1: s + f2: t +} diff --git a/test/grammar/datatype/conversion/int2float_0/stdout.golden b/test/grammar/datatype/conversion/int2float_0/stdout.golden new file mode 100644 index 000000000..ac8a837cd --- /dev/null +++ b/test/grammar/datatype/conversion/int2float_0/stdout.golden @@ -0,0 +1,8 @@ +i: 10 +f: 1.1 +s: 11.1 +t: 11.1 +myschema: + f0: 1.1 + f1: 11.1 + f2: 11.1 diff --git a/test/grammar/datatype/conversion/int2float_1/main.k b/test/grammar/datatype/conversion/int2float_1/main.k new file mode 100644 index 000000000..21deec3b8 --- /dev/null +++ b/test/grammar/datatype/conversion/int2float_1/main.k @@ -0,0 +1,8 @@ +i = 10 +f = 1.1 + +schema Simple: + f0: float + f1: float + +myschema = Simple {"f0": f, "f1": float(i)} diff --git a/test/grammar/datatype/conversion/int2float_1/stdout.golden b/test/grammar/datatype/conversion/int2float_1/stdout.golden new file mode 100644 index 000000000..d87496857 --- /dev/null +++ b/test/grammar/datatype/conversion/int2float_1/stdout.golden @@ -0,0 +1,5 @@ +i: 10 +f: 1.1 +myschema: + f0: 1.1 + f1: 10.0 diff --git a/test/grammar/datatype/datetime/today/main.k b/test/grammar/datatype/datetime/today/main.k new file mode 100644 index 000000000..f094dd421 --- /dev/null +++ b/test/grammar/datatype/datetime/today/main.k @@ -0,0 +1,5 @@ +import datetime + +_t1 = datetime.today() +_t2 = str(_t1) +t = _t2[0:2] diff --git a/test/grammar/datatype/datetime/today/stdout.golden b/test/grammar/datatype/datetime/today/stdout.golden new file mode 100644 index 000000000..281bc795d --- /dev/null +++ b/test/grammar/datatype/datetime/today/stdout.golden @@ -0,0 +1 @@ +t: '20' diff --git a/test/grammar/datatype/dict/empty/main.k b/test/grammar/datatype/dict/empty/main.k new file mode 100644 index 000000000..27b821218 --- /dev/null +++ b/test/grammar/datatype/dict/empty/main.k @@ -0,0 +1,12 @@ +data0 = {} +data1 = { + +} +data2 = { + + + +} +data3 = { + # Empty Dict +} diff --git a/test/grammar/datatype/dict/empty/stdout.golden b/test/grammar/datatype/dict/empty/stdout.golden new file mode 100644 index 000000000..628a0bf5e --- /dev/null +++ b/test/grammar/datatype/dict/empty/stdout.golden @@ -0,0 +1,4 @@ +data0: {} +data1: {} +data2: {} +data3: {} diff --git a/test/grammar/datatype/dict/if_item_0/main.k b/test/grammar/datatype/dict/if_item_0/main.k new file mode 100644 index 000000000..ac9163c62 --- /dev/null +++ b/test/grammar/datatype/dict/if_item_0/main.k @@ -0,0 +1,10 @@ +data1 = { + if False: "key1": "value1" + "key2": "value2" + if True: "key3": "value3" +} +data2 = { + "key1": "value1" + if data1["key2"]: + "key2": data1["key2"] +} diff --git a/test/grammar/datatype/dict/if_item_0/stdout.golden b/test/grammar/datatype/dict/if_item_0/stdout.golden new file mode 100644 index 000000000..517233c5b --- /dev/null +++ b/test/grammar/datatype/dict/if_item_0/stdout.golden @@ -0,0 +1,6 @@ +data1: + key2: value2 + key3: value3 +data2: + key1: value1 + key2: value2 diff --git a/test/grammar/datatype/dict/if_item_1/main.k b/test/grammar/datatype/dict/if_item_1/main.k new file mode 100644 index 000000000..119afe9de --- /dev/null +++ b/test/grammar/datatype/dict/if_item_1/main.k @@ -0,0 +1,9 @@ +data1 = {"key1": "value1", "key2": "value2"} +data2 = { + **data1 + if data1["key1"]: "key3": data1["key1"] +} +data3 = { + **data2 + if data2["key3"] == "value3": "key4": "value4" +} diff --git a/test/grammar/datatype/dict/if_item_1/stdout.golden b/test/grammar/datatype/dict/if_item_1/stdout.golden new file mode 100644 index 000000000..f565a2f9e --- /dev/null +++ b/test/grammar/datatype/dict/if_item_1/stdout.golden @@ -0,0 +1,11 @@ +data1: + key1: value1 + key2: value2 +data2: + key1: value1 + key2: value2 + key3: value1 +data3: + key1: value1 + key2: value2 + key3: value1 diff --git a/test/grammar/datatype/dict/if_item_2/main.k b/test/grammar/datatype/dict/if_item_2/main.k new file mode 100644 index 000000000..0de4c562b --- /dev/null +++ b/test/grammar/datatype/dict/if_item_2/main.k @@ -0,0 +1,7 @@ +schema Data: + data: {str:} = { + if True: key1: "value1" + key2: "value2" + } + +data = Data {} diff --git a/test/grammar/datatype/dict/if_item_2/stdout.golden b/test/grammar/datatype/dict/if_item_2/stdout.golden new file mode 100644 index 000000000..1fc301b8d --- /dev/null +++ b/test/grammar/datatype/dict/if_item_2/stdout.golden @@ -0,0 +1,4 @@ +data: + data: + key1: value1 + key2: value2 diff --git a/test/grammar/datatype/dict/if_item_3/main.k b/test/grammar/datatype/dict/if_item_3/main.k new file mode 100644 index 000000000..69dbefc84 --- /dev/null +++ b/test/grammar/datatype/dict/if_item_3/main.k @@ -0,0 +1,8 @@ +schema Data: + shouldAddKey: bool = False + data: {str:} = { + if shouldAddKey: key1: "value1" + key2: "value2" + } + +data = Data {} diff --git a/test/grammar/datatype/dict/if_item_3/stdout.golden b/test/grammar/datatype/dict/if_item_3/stdout.golden new file mode 100644 index 000000000..aa1b6feec --- /dev/null +++ b/test/grammar/datatype/dict/if_item_3/stdout.golden @@ -0,0 +1,4 @@ +data: + shouldAddKey: false + data: + key2: value2 diff --git a/test/grammar/datatype/dict/if_item_4/main.k b/test/grammar/datatype/dict/if_item_4/main.k new file mode 100644 index 000000000..f65e7214f --- /dev/null +++ b/test/grammar/datatype/dict/if_item_4/main.k @@ -0,0 +1,40 @@ +data1 = { + if True: + key1: "value1" + elif True: + key2: "value2" + elif True: + key3: "value3" + else: + key4: "value4" +} +data2 = { + if False: + key1: "value1" + elif True: + key2: "value2" + elif True: + key3: "value3" + else: + key4: "value4" +} +data3 = { + if False: + key1: "value1" + elif False: + key2: "value2" + elif True: + key3: "value3" + else: + key4: "value4" +} +data4 = { + if False: + key1: "value1" + elif False: + key2: "value2" + elif False: + key3: "value3" + else: + key4: "value4" +} diff --git a/test/grammar/datatype/dict/if_item_4/stdout.golden b/test/grammar/datatype/dict/if_item_4/stdout.golden new file mode 100644 index 000000000..3131f60b6 --- /dev/null +++ b/test/grammar/datatype/dict/if_item_4/stdout.golden @@ -0,0 +1,8 @@ +data1: + key1: value1 +data2: + key2: value2 +data3: + key3: value3 +data4: + key4: value4 diff --git a/test/grammar/datatype/dict/if_item_5/main.k b/test/grammar/datatype/dict/if_item_5/main.k new file mode 100644 index 000000000..bb2c7abad --- /dev/null +++ b/test/grammar/datatype/dict/if_item_5/main.k @@ -0,0 +1,56 @@ +data1 = { + if True: + key1: "value1" + key11: 1, key12: True + elif True: + key2: "value2" + key21: 2, key22: True + elif True: + key3: "value3" + key31: 3, key32: True + else: + key4: "value4" + key41: 4, key42: True +} +data2 = { + if False: + key1: "value1" + key11: 1, key12: True + elif True: + key2: "value2" + key21: 2, key22: True + elif True: + key3: "value3" + key31: 3, key32: True + else: + key4: "value4" + key41: 4, key42: True +} +data3 = { + if False: + key1: "value1" + key11: 1, key12: True + elif False: + key2: "value2" + key21: 2, key22: True + elif True: + key3: "value3" + key31: 3, key32: True + else: + key4: "value4" + key41: 4, key42: True +} +data4 = { + if False: + key1: "value1" + key11: 1, key12: True + elif False: + key2: "value2" + key21: 2, key22: True + elif False: + key3: "value3" + key31: 3, key32: True + else: + key4: "value4" + key41: 4, key42: True +} diff --git a/test/grammar/datatype/dict/if_item_5/stdout.golden b/test/grammar/datatype/dict/if_item_5/stdout.golden new file mode 100644 index 000000000..485254343 --- /dev/null +++ b/test/grammar/datatype/dict/if_item_5/stdout.golden @@ -0,0 +1,16 @@ +data1: + key1: value1 + key11: 1 + key12: true +data2: + key2: value2 + key21: 2 + key22: true +data3: + key3: value3 + key31: 3 + key32: true +data4: + key4: value4 + key41: 4 + key42: true diff --git a/test/grammar/datatype/dict/if_item_6/main.k b/test/grammar/datatype/dict/if_item_6/main.k new file mode 100644 index 000000000..51f67e3f5 --- /dev/null +++ b/test/grammar/datatype/dict/if_item_6/main.k @@ -0,0 +1,16 @@ +a = 1 +data1 = { + key = "value1" + if a == 1: + key = "value2" + elif a == 2: + key = "value3" + else: + key = "value4" +} +data2 = { + key = "value1" + if a == 1: key ="value2" + elif a == 2: key = "value3" + else: key = "value4" +} diff --git a/test/grammar/datatype/dict/if_item_6/stdout.golden b/test/grammar/datatype/dict/if_item_6/stdout.golden new file mode 100644 index 000000000..fc94e8619 --- /dev/null +++ b/test/grammar/datatype/dict/if_item_6/stdout.golden @@ -0,0 +1,5 @@ +a: 1 +data1: + key: value2 +data2: + key: value2 diff --git a/test/grammar/datatype/dict/if_item_7/main.k b/test/grammar/datatype/dict/if_item_7/main.k new file mode 100644 index 000000000..0092931fc --- /dev/null +++ b/test/grammar/datatype/dict/if_item_7/main.k @@ -0,0 +1,16 @@ +a = 1 +data1 = { + **{key = "value1"} + if a == 1: + **{key = "value2"} + elif a == 2: + **{key = "value3"} + else: + **{key = "value4"} +} +data2 = { + **{key = "value1"} + if a == 1: **{key = "value2"} + elif a == 2: **{key = "value3"} + else: **{key = "value4"} +} diff --git a/test/grammar/datatype/dict/if_item_7/stdout.golden b/test/grammar/datatype/dict/if_item_7/stdout.golden new file mode 100644 index 000000000..fc94e8619 --- /dev/null +++ b/test/grammar/datatype/dict/if_item_7/stdout.golden @@ -0,0 +1,5 @@ +a: 1 +data1: + key: value2 +data2: + key: value2 diff --git a/test/grammar/datatype/dict/if_item_8/main.k b/test/grammar/datatype/dict/if_item_8/main.k new file mode 100644 index 000000000..8fa659981 --- /dev/null +++ b/test/grammar/datatype/dict/if_item_8/main.k @@ -0,0 +1,16 @@ +a = 1 +b = 1 +data1 = { + **{key = "value1"} + if a == 1: + if b == 1: + key = "value2" + elif a == 2: + **{key = "value3"} + else: + **{key = "value4"} +} +data2 = { + **{key = "value1"} + if a == 1: if b == 1: key = "value2" +} diff --git a/test/grammar/datatype/dict/if_item_8/stdout.golden b/test/grammar/datatype/dict/if_item_8/stdout.golden new file mode 100644 index 000000000..9c574f25d --- /dev/null +++ b/test/grammar/datatype/dict/if_item_8/stdout.golden @@ -0,0 +1,6 @@ +a: 1 +b: 1 +data1: + key: value2 +data2: + key: value2 diff --git a/test/grammar/datatype/dict/if_item_9/main.k b/test/grammar/datatype/dict/if_item_9/main.k new file mode 100644 index 000000000..f4511a2d2 --- /dev/null +++ b/test/grammar/datatype/dict/if_item_9/main.k @@ -0,0 +1,5 @@ +x = { + if True: + **{}, + else: **{} +} diff --git a/test/grammar/datatype/dict/if_item_9/stdout.golden b/test/grammar/datatype/dict/if_item_9/stdout.golden new file mode 100644 index 000000000..b50c21862 --- /dev/null +++ b/test/grammar/datatype/dict/if_item_9/stdout.golden @@ -0,0 +1 @@ +x: {} diff --git a/test/grammar/datatype/dict/indexing_0/main.k b/test/grammar/datatype/dict/indexing_0/main.k new file mode 100644 index 000000000..ccc47ff70 --- /dev/null +++ b/test/grammar/datatype/dict/indexing_0/main.k @@ -0,0 +1,2 @@ +_dict = {"key": "value"} +v = _dict["key"] diff --git a/test/grammar/datatype/dict/indexing_0/stdout.golden b/test/grammar/datatype/dict/indexing_0/stdout.golden new file mode 100644 index 000000000..aa3175c18 --- /dev/null +++ b/test/grammar/datatype/dict/indexing_0/stdout.golden @@ -0,0 +1 @@ +v: value diff --git a/test/grammar/datatype/dict/indexing_1/main.k b/test/grammar/datatype/dict/indexing_1/main.k new file mode 100644 index 000000000..a0f7d9810 --- /dev/null +++ b/test/grammar/datatype/dict/indexing_1/main.k @@ -0,0 +1,4 @@ +_dict = {"key": "value"} +v1 = _dict["keyNotExist"] or None +v2 = _dict[None] or None +v3 = _dict[Undefined] or None diff --git a/test/grammar/datatype/dict/indexing_1/stdout.golden b/test/grammar/datatype/dict/indexing_1/stdout.golden new file mode 100644 index 000000000..eed87ea20 --- /dev/null +++ b/test/grammar/datatype/dict/indexing_1/stdout.golden @@ -0,0 +1,3 @@ +v1: null +v2: null +v3: null diff --git a/test/grammar/datatype/dict/indexing_in_comprehension_0/main.k b/test/grammar/datatype/dict/indexing_in_comprehension_0/main.k new file mode 100644 index 000000000..d7fdb3a23 --- /dev/null +++ b/test/grammar/datatype/dict/indexing_in_comprehension_0/main.k @@ -0,0 +1,7 @@ +_family = [{ + "name": "John", + "relation": "father" +}] +afterFormat = [{ + person["relation"]: person["name"] +} for person in _family] diff --git a/test/grammar/datatype/dict/indexing_in_comprehension_0/stdout.golden b/test/grammar/datatype/dict/indexing_in_comprehension_0/stdout.golden new file mode 100644 index 000000000..1b4f8a904 --- /dev/null +++ b/test/grammar/datatype/dict/indexing_in_comprehension_0/stdout.golden @@ -0,0 +1,2 @@ +afterFormat: +- father: John \ No newline at end of file diff --git a/test/grammar/datatype/dict/indexing_in_comprehension_1/main.k b/test/grammar/datatype/dict/indexing_in_comprehension_1/main.k new file mode 100644 index 000000000..6b1deec27 --- /dev/null +++ b/test/grammar/datatype/dict/indexing_in_comprehension_1/main.k @@ -0,0 +1,25 @@ +schema Family: + father?: str = None + mother?: str = None + +schema Person: + family?: [Family] + +schema Front: + family?: [{str:}] + +schema Wrapper[data]: + person?: Person = { + "family": [{ + p["relation"]: p["name"] + } for p in data.family] + } + +f = Front { + family: [{ + "name": "John", + "relation": "father" + }] +} + +result = Wrapper(f) {} diff --git a/test/grammar/datatype/dict/indexing_in_comprehension_1/stdout.golden b/test/grammar/datatype/dict/indexing_in_comprehension_1/stdout.golden new file mode 100644 index 000000000..4edf7f91e --- /dev/null +++ b/test/grammar/datatype/dict/indexing_in_comprehension_1/stdout.golden @@ -0,0 +1,9 @@ +f: + family: + - name: John + relation: father +result: + person: + family: + - father: John + mother: null \ No newline at end of file diff --git a/test/grammar/datatype/dict/indexing_in_comprehension_2/main.k b/test/grammar/datatype/dict/indexing_in_comprehension_2/main.k new file mode 100644 index 000000000..848236ec3 --- /dev/null +++ b/test/grammar/datatype/dict/indexing_in_comprehension_2/main.k @@ -0,0 +1,20 @@ + +schema Label: + service: str + app: str + +schema Service: + services: [Label] + +services = [{ + "name": "Alice" +}] + +my_service = Service { + services: [ + Label { + "service": service["name"], + "app": "123" + } for service in services + ] +} diff --git a/test/grammar/datatype/dict/indexing_in_comprehension_2/stdout.golden b/test/grammar/datatype/dict/indexing_in_comprehension_2/stdout.golden new file mode 100644 index 000000000..2eba32c27 --- /dev/null +++ b/test/grammar/datatype/dict/indexing_in_comprehension_2/stdout.golden @@ -0,0 +1,6 @@ +services: +- name: Alice +my_service: + services: + - service: Alice + app: '123' \ No newline at end of file diff --git a/test/grammar/datatype/dict/indexing_in_comprehension_3/main.k b/test/grammar/datatype/dict/indexing_in_comprehension_3/main.k new file mode 100644 index 000000000..ecba909c8 --- /dev/null +++ b/test/grammar/datatype/dict/indexing_in_comprehension_3/main.k @@ -0,0 +1,23 @@ +schema Family: + father?: str = None + mother?: str = None + +schema Person: + family?: [Family] + +schema Front: + family?: [{str:}] + +schema Wrapper[data]: + person?: Person = { + family = [Family { + "${p.relation}" = p.name + } for p in data.family] + } + +result = Wrapper(Front { + family: [{ + name = "John", + relation = "father" + }] +}) {} diff --git a/test/grammar/datatype/dict/indexing_in_comprehension_3/stdout.golden b/test/grammar/datatype/dict/indexing_in_comprehension_3/stdout.golden new file mode 100644 index 000000000..4d182d856 --- /dev/null +++ b/test/grammar/datatype/dict/indexing_in_comprehension_3/stdout.golden @@ -0,0 +1,5 @@ +result: + person: + family: + - father: John + mother: null diff --git a/test/grammar/datatype/dict/indexing_in_comprehension_fail_0/main.k b/test/grammar/datatype/dict/indexing_in_comprehension_fail_0/main.k new file mode 100644 index 000000000..b9ae1c9da --- /dev/null +++ b/test/grammar/datatype/dict/indexing_in_comprehension_fail_0/main.k @@ -0,0 +1,20 @@ + +schema Label: + service: str + app: str + +schema Service: + services: [Label] + +services = [{ + "name": "Alice" +}] + +my_service = Service { + services: [ + Label { + service: service["name"], + "app": "123" + } for service in services + ] +} diff --git a/test/grammar/datatype/dict/indexing_in_comprehension_fail_0/stderr.golden.py b/test/grammar/datatype/dict/indexing_in_comprehension_fail_0/stderr.golden.py new file mode 100644 index 000000000..f3131eb01 --- /dev/null +++ b/test/grammar/datatype/dict/indexing_in_comprehension_fail_0/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IllegalAttributeError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=16, + col_no=13 + ) + ], + arg_msg="type '{str:str}'" + ) + , file=sys.stdout +) diff --git a/test/grammar/datatype/dict/insert_0/main.k b/test/grammar/datatype/dict/insert_0/main.k new file mode 100644 index 000000000..45c5df90d --- /dev/null +++ b/test/grammar/datatype/dict/insert_0/main.k @@ -0,0 +1,4 @@ +data = { + key: [0] + key += [1] +} diff --git a/test/grammar/datatype/dict/insert_0/stdout.golden b/test/grammar/datatype/dict/insert_0/stdout.golden new file mode 100644 index 000000000..6df4993a1 --- /dev/null +++ b/test/grammar/datatype/dict/insert_0/stdout.golden @@ -0,0 +1,4 @@ +data: + key: + - 0 + - 1 diff --git a/test/grammar/datatype/dict/insert_1/main.k b/test/grammar/datatype/dict/insert_1/main.k new file mode 100644 index 000000000..a0acbcd53 --- /dev/null +++ b/test/grammar/datatype/dict/insert_1/main.k @@ -0,0 +1,3 @@ +data = { + key += [1] +} diff --git a/test/grammar/datatype/dict/insert_1/stdout.golden b/test/grammar/datatype/dict/insert_1/stdout.golden new file mode 100644 index 000000000..5b6dc8cb3 --- /dev/null +++ b/test/grammar/datatype/dict/insert_1/stdout.golden @@ -0,0 +1,3 @@ +data: + key: + - 1 diff --git a/test/grammar/datatype/dict/insert_2/main.k b/test/grammar/datatype/dict/insert_2/main.k new file mode 100644 index 000000000..c7c341116 --- /dev/null +++ b/test/grammar/datatype/dict/insert_2/main.k @@ -0,0 +1,4 @@ +data = { + key: [0] + key[0] += [1] +} diff --git a/test/grammar/datatype/dict/insert_2/stdout.golden b/test/grammar/datatype/dict/insert_2/stdout.golden new file mode 100644 index 000000000..03df415ce --- /dev/null +++ b/test/grammar/datatype/dict/insert_2/stdout.golden @@ -0,0 +1,4 @@ +data: + key: + - 1 + - 0 diff --git a/test/grammar/datatype/dict/merge_0/main.k b/test/grammar/datatype/dict/merge_0/main.k new file mode 100644 index 000000000..538833877 --- /dev/null +++ b/test/grammar/datatype/dict/merge_0/main.k @@ -0,0 +1,3 @@ +a = {"a": 1} +b = {"b": 2} +c = {**a, **b} diff --git a/test/grammar/datatype/dict/merge_0/stdout.golden b/test/grammar/datatype/dict/merge_0/stdout.golden new file mode 100644 index 000000000..dd55149dd --- /dev/null +++ b/test/grammar/datatype/dict/merge_0/stdout.golden @@ -0,0 +1,7 @@ +a: + a: 1 +b: + b: 2 +c: + a: 1 + b: 2 diff --git a/test/grammar/datatype/dict/merge_1/main.k b/test/grammar/datatype/dict/merge_1/main.k new file mode 100644 index 000000000..2cf0d87af --- /dev/null +++ b/test/grammar/datatype/dict/merge_1/main.k @@ -0,0 +1,3 @@ +a = {"a": 1, "b": 3} +b = {"b" = 2} +c = {**a, **b} diff --git a/test/grammar/datatype/dict/merge_1/stdout.golden b/test/grammar/datatype/dict/merge_1/stdout.golden new file mode 100644 index 000000000..770aebb9b --- /dev/null +++ b/test/grammar/datatype/dict/merge_1/stdout.golden @@ -0,0 +1,8 @@ +a: + a: 1 + b: 3 +b: + b: 2 +c: + a: 1 + b: 2 diff --git a/test/grammar/datatype/dict/merge_2/main.k b/test/grammar/datatype/dict/merge_2/main.k new file mode 100644 index 000000000..9131d07ea --- /dev/null +++ b/test/grammar/datatype/dict/merge_2/main.k @@ -0,0 +1,3 @@ +a = {"a": "x", "b": {"c": "d", "e": "f"}} +b = {"b" = {"c": "g"}} +c = {**a, **b} diff --git a/test/grammar/datatype/dict/merge_2/stdout.golden b/test/grammar/datatype/dict/merge_2/stdout.golden new file mode 100644 index 000000000..3b72cf6f9 --- /dev/null +++ b/test/grammar/datatype/dict/merge_2/stdout.golden @@ -0,0 +1,12 @@ +a: + a: x + b: + c: d + e: f +b: + b: + c: g +c: + a: x + b: + c: g \ No newline at end of file diff --git a/test/grammar/datatype/dict/merge_3/main.k b/test/grammar/datatype/dict/merge_3/main.k new file mode 100644 index 000000000..f644baff7 --- /dev/null +++ b/test/grammar/datatype/dict/merge_3/main.k @@ -0,0 +1,20 @@ +schema Person: + mixin [SumMixin] + x: int + y: int + z: int + +schema SumMixin: + z: int = x + y + +person1 = Person { + "x" = 1 + "y" = 2 +} +data = { + "x": 2 + **person1 +} +person2 = Person { + **data +} diff --git a/test/grammar/datatype/dict/merge_3/stdout.golden b/test/grammar/datatype/dict/merge_3/stdout.golden new file mode 100644 index 000000000..90ed034f9 --- /dev/null +++ b/test/grammar/datatype/dict/merge_3/stdout.golden @@ -0,0 +1,12 @@ +person1: + x: 1 + y: 2 + z: 3 +data: + x: 1 + y: 2 + z: 3 +person2: + x: 1 + y: 2 + z: 3 diff --git a/test/grammar/datatype/dict/merge_None_0/main.k b/test/grammar/datatype/dict/merge_None_0/main.k new file mode 100644 index 000000000..64e421185 --- /dev/null +++ b/test/grammar/datatype/dict/merge_None_0/main.k @@ -0,0 +1,3 @@ +a = None +b = {"b": "c"} +c = {**a, **b} diff --git a/test/grammar/datatype/dict/merge_None_0/stdout.golden b/test/grammar/datatype/dict/merge_None_0/stdout.golden new file mode 100644 index 000000000..ae170a848 --- /dev/null +++ b/test/grammar/datatype/dict/merge_None_0/stdout.golden @@ -0,0 +1,5 @@ +a: null +b: + b: c +c: + b: c \ No newline at end of file diff --git a/test/grammar/datatype/dict/merge_None_1/main.k b/test/grammar/datatype/dict/merge_None_1/main.k new file mode 100644 index 000000000..ae19d6537 --- /dev/null +++ b/test/grammar/datatype/dict/merge_None_1/main.k @@ -0,0 +1,3 @@ +a = None +b = None +c = {**a, **b} diff --git a/test/grammar/datatype/dict/merge_None_1/stdout.golden b/test/grammar/datatype/dict/merge_None_1/stdout.golden new file mode 100644 index 000000000..97527c6de --- /dev/null +++ b/test/grammar/datatype/dict/merge_None_1/stdout.golden @@ -0,0 +1,3 @@ +a: null +b: null +c: {} \ No newline at end of file diff --git a/test/grammar/datatype/dict/merge_if_expr_0/main.k b/test/grammar/datatype/dict/merge_if_expr_0/main.k new file mode 100644 index 000000000..a0cbc9dbf --- /dev/null +++ b/test/grammar/datatype/dict/merge_if_expr_0/main.k @@ -0,0 +1,16 @@ +service = { + "name": "name", + "labels": { + "key": "value" + } +} + +result = { + "metadata": { + "name": service["name"], + "labels": { + **(dict(service["labels"]) if "labels" in service else {}), + **{"app": service["name"]} + } + } +} diff --git a/test/grammar/datatype/dict/merge_if_expr_0/stdout.golden b/test/grammar/datatype/dict/merge_if_expr_0/stdout.golden new file mode 100644 index 000000000..7dfaf3bf4 --- /dev/null +++ b/test/grammar/datatype/dict/merge_if_expr_0/stdout.golden @@ -0,0 +1,10 @@ +service: + name: name + labels: + key: value +result: + metadata: + name: name + labels: + key: value + app: name diff --git a/test/grammar/datatype/dict/merge_indent/main.k b/test/grammar/datatype/dict/merge_indent/main.k new file mode 100644 index 000000000..060248d18 --- /dev/null +++ b/test/grammar/datatype/dict/merge_indent/main.k @@ -0,0 +1,9 @@ +a = { + "key": "value" +} +m = { + **a, + **{ + "key" = "value2" + } +} diff --git a/test/grammar/datatype/dict/merge_indent/stdout.golden b/test/grammar/datatype/dict/merge_indent/stdout.golden new file mode 100644 index 000000000..e8fb81146 --- /dev/null +++ b/test/grammar/datatype/dict/merge_indent/stdout.golden @@ -0,0 +1,4 @@ +a: + key: value +m: + key: value2 diff --git a/test/grammar/datatype/dict/override_0/main.k b/test/grammar/datatype/dict/override_0/main.k new file mode 100644 index 000000000..30e741092 --- /dev/null +++ b/test/grammar/datatype/dict/override_0/main.k @@ -0,0 +1,2 @@ +myDict = {"key": "value"} +myDictNew = {**myDict, "key" = Undefined} diff --git a/test/grammar/datatype/dict/override_0/stdout.golden b/test/grammar/datatype/dict/override_0/stdout.golden new file mode 100644 index 000000000..2e3844bfe --- /dev/null +++ b/test/grammar/datatype/dict/override_0/stdout.golden @@ -0,0 +1,3 @@ +myDict: + key: value +myDictNew: {} diff --git a/test/grammar/datatype/dict/override_1/main.k b/test/grammar/datatype/dict/override_1/main.k new file mode 100644 index 000000000..51ea88b59 --- /dev/null +++ b/test/grammar/datatype/dict/override_1/main.k @@ -0,0 +1 @@ +myDict = {"key": "value", key = Undefined} diff --git a/test/grammar/datatype/dict/override_1/stdout.golden b/test/grammar/datatype/dict/override_1/stdout.golden new file mode 100644 index 000000000..fc95c2029 --- /dev/null +++ b/test/grammar/datatype/dict/override_1/stdout.golden @@ -0,0 +1 @@ +myDict: {} diff --git a/test/grammar/datatype/dict/select_0/main.k b/test/grammar/datatype/dict/select_0/main.k new file mode 100644 index 000000000..fecacec70 --- /dev/null +++ b/test/grammar/datatype/dict/select_0/main.k @@ -0,0 +1,3 @@ +myDict = {"key": "value"} +result1 = myDict.key +result2 = myDict["key"] diff --git a/test/grammar/datatype/dict/select_0/stdout.golden b/test/grammar/datatype/dict/select_0/stdout.golden new file mode 100644 index 000000000..e7d57ddbf --- /dev/null +++ b/test/grammar/datatype/dict/select_0/stdout.golden @@ -0,0 +1,4 @@ +myDict: + key: value +result1: value +result2: value diff --git a/test/grammar/datatype/dict/select_1/main.k b/test/grammar/datatype/dict/select_1/main.k new file mode 100644 index 000000000..698fbd2a3 --- /dev/null +++ b/test/grammar/datatype/dict/select_1/main.k @@ -0,0 +1,3 @@ +myDict = {"key": "value"} +result1 = myDict.err_key or None +result2 = myDict["err_key"] or None diff --git a/test/grammar/datatype/dict/select_1/stdout.golden b/test/grammar/datatype/dict/select_1/stdout.golden new file mode 100644 index 000000000..ddfaaa8a7 --- /dev/null +++ b/test/grammar/datatype/dict/select_1/stdout.golden @@ -0,0 +1,4 @@ +myDict: + key: value +result1: null +result2: null diff --git a/test/grammar/datatype/dict/string_call_in_comprehension/main.k b/test/grammar/datatype/dict/string_call_in_comprehension/main.k new file mode 100644 index 000000000..ad6f20b3a --- /dev/null +++ b/test/grammar/datatype/dict/string_call_in_comprehension/main.k @@ -0,0 +1,3 @@ +_keys = ["one", "two", "three"] + +d = {"index {}".format(key): "{} value".format(key) for key in _keys} diff --git a/test/grammar/datatype/dict/string_call_in_comprehension/stdout.golden b/test/grammar/datatype/dict/string_call_in_comprehension/stdout.golden new file mode 100644 index 000000000..3526fb8ae --- /dev/null +++ b/test/grammar/datatype/dict/string_call_in_comprehension/stdout.golden @@ -0,0 +1,4 @@ +d: + index one: one value + index two: two value + index three: three value \ No newline at end of file diff --git a/test/grammar/datatype/int/int_0/main.k b/test/grammar/datatype/int/int_0/main.k new file mode 100644 index 000000000..1fc466947 --- /dev/null +++ b/test/grammar/datatype/int/int_0/main.k @@ -0,0 +1,8 @@ +a = 1 +b = -1 +c = 0x10 # hexadecimal literal +d = 0o10 # octal literal +e = 010 # octal literal +f = 0b10 # binary literal +g = int("10") # int constructor +h = int("10", base=2) diff --git a/test/grammar/datatype/int/int_0/stdout.golden b/test/grammar/datatype/int/int_0/stdout.golden new file mode 100644 index 000000000..bf90c39c8 --- /dev/null +++ b/test/grammar/datatype/int/int_0/stdout.golden @@ -0,0 +1,8 @@ +a: 1 +b: -1 +c: 16 +d: 8 +e: 8 +f: 2 +g: 10 +h: 2 diff --git a/test/grammar/datatype/list/add_0/main.k b/test/grammar/datatype/list/add_0/main.k new file mode 100644 index 000000000..3f44edaf6 --- /dev/null +++ b/test/grammar/datatype/list/add_0/main.k @@ -0,0 +1 @@ +a = [1, 2] + [3, 4] diff --git a/test/grammar/datatype/list/add_0/stdout.golden b/test/grammar/datatype/list/add_0/stdout.golden new file mode 100644 index 000000000..9b35d91ad --- /dev/null +++ b/test/grammar/datatype/list/add_0/stdout.golden @@ -0,0 +1,5 @@ +a: +- 1 +- 2 +- 3 +- 4 diff --git a/test/grammar/datatype/list/add_1/main.k b/test/grammar/datatype/list/add_1/main.k new file mode 100644 index 000000000..f1c6d1ab1 --- /dev/null +++ b/test/grammar/datatype/list/add_1/main.k @@ -0,0 +1 @@ +a = [1, 2] + [3, 4] + [5, 6] diff --git a/test/grammar/datatype/list/add_1/stdout.golden b/test/grammar/datatype/list/add_1/stdout.golden new file mode 100644 index 000000000..6f6a5a5cb --- /dev/null +++ b/test/grammar/datatype/list/add_1/stdout.golden @@ -0,0 +1,7 @@ +a: +- 1 +- 2 +- 3 +- 4 +- 5 +- 6 diff --git a/test/grammar/datatype/list/add_2/main.k b/test/grammar/datatype/list/add_2/main.k new file mode 100644 index 000000000..4f3be3c1a --- /dev/null +++ b/test/grammar/datatype/list/add_2/main.k @@ -0,0 +1,5 @@ +_list1 = [1, 2, 3] +_list2 = [3, 4] + +result1 = [*[1, 2], *[4]] +result2 = [*_list1, *_list2] diff --git a/test/grammar/datatype/list/add_2/stdout.golden b/test/grammar/datatype/list/add_2/stdout.golden new file mode 100644 index 000000000..b8f24af42 --- /dev/null +++ b/test/grammar/datatype/list/add_2/stdout.golden @@ -0,0 +1,10 @@ +result1: +- 1 +- 2 +- 4 +result2: +- 1 +- 2 +- 3 +- 3 +- 4 diff --git a/test/grammar/datatype/list/add_None_0/main.k b/test/grammar/datatype/list/add_None_0/main.k new file mode 100644 index 000000000..617ea29cd --- /dev/null +++ b/test/grammar/datatype/list/add_None_0/main.k @@ -0,0 +1,4 @@ +_list1 = [1, 2, 3] +_list2 = None + +result2 = [*_list1, *_list2] diff --git a/test/grammar/datatype/list/add_None_0/stdout.golden b/test/grammar/datatype/list/add_None_0/stdout.golden new file mode 100644 index 000000000..ce3760932 --- /dev/null +++ b/test/grammar/datatype/list/add_None_0/stdout.golden @@ -0,0 +1,4 @@ +result2: +- 1 +- 2 +- 3 diff --git a/test/grammar/datatype/list/add_None_1/main.k b/test/grammar/datatype/list/add_None_1/main.k new file mode 100644 index 000000000..19ec99a0d --- /dev/null +++ b/test/grammar/datatype/list/add_None_1/main.k @@ -0,0 +1,4 @@ +_list1 = None +_list2 = None + +result = [0, *_list1, *_list2] diff --git a/test/grammar/datatype/list/add_None_1/stdout.golden b/test/grammar/datatype/list/add_None_1/stdout.golden new file mode 100644 index 000000000..aee44254f --- /dev/null +++ b/test/grammar/datatype/list/add_None_1/stdout.golden @@ -0,0 +1,2 @@ +result: +- 0 diff --git a/test/grammar/datatype/list/add_None_fail/main.k b/test/grammar/datatype/list/add_None_fail/main.k new file mode 100644 index 000000000..c0257cf53 --- /dev/null +++ b/test/grammar/datatype/list/add_None_fail/main.k @@ -0,0 +1,4 @@ +_list1 = [1, 2, 3] +_list2 = None + +result2 = _list1 + _list2 diff --git a/test/grammar/datatype/list/add_None_fail/stderr.golden.py b/test/grammar/datatype/list/add_None_fail/stderr.golden.py new file mode 100644 index 000000000..7a0bbd6e7 --- /dev/null +++ b/test/grammar/datatype/list/add_None_fail/stderr.golden.py @@ -0,0 +1,20 @@ +import os +import sys + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4 + ) + ], + arg_msg="can only concatenate list (not \"NoneType\") to list" + ) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/list/add_if_expr/main.k b/test/grammar/datatype/list/add_if_expr/main.k new file mode 100644 index 000000000..d85636a03 --- /dev/null +++ b/test/grammar/datatype/list/add_if_expr/main.k @@ -0,0 +1,2 @@ +_b = False +a = [1, 2] + ([3, 4] if _b else [5, 6]) diff --git a/test/grammar/datatype/list/add_if_expr/stdout.golden b/test/grammar/datatype/list/add_if_expr/stdout.golden new file mode 100644 index 000000000..756f8d802 --- /dev/null +++ b/test/grammar/datatype/list/add_if_expr/stdout.golden @@ -0,0 +1,5 @@ +a: +- 1 +- 2 +- 5 +- 6 diff --git a/test/grammar/datatype/list/if_item_0/main.k b/test/grammar/datatype/list/if_item_0/main.k new file mode 100644 index 000000000..9950d0dfd --- /dev/null +++ b/test/grammar/datatype/list/if_item_0/main.k @@ -0,0 +1,11 @@ +data1 = [1, if True: 2, 3] +data2 = [ + 1 + if False: 2 + 3 +] +data3 = [ + if data2[0] == 1: data2[0] + if data1[1] == 2: + data1[1] +] diff --git a/test/grammar/datatype/list/if_item_0/stdout.golden b/test/grammar/datatype/list/if_item_0/stdout.golden new file mode 100644 index 000000000..53224cd9c --- /dev/null +++ b/test/grammar/datatype/list/if_item_0/stdout.golden @@ -0,0 +1,10 @@ +data1: +- 1 +- 2 +- 3 +data2: +- 1 +- 3 +data3: +- 1 +- 2 diff --git a/test/grammar/datatype/list/if_item_1/main.k b/test/grammar/datatype/list/if_item_1/main.k new file mode 100644 index 000000000..b0327862b --- /dev/null +++ b/test/grammar/datatype/list/if_item_1/main.k @@ -0,0 +1,12 @@ +env1 = "envVar1" +env2 = "" +data = [ + if env1: { + "key": "ENV_1" + "value": env1 + } + { + "key": "ENV_2" + "value": env2 or "envVar2" + } +] diff --git a/test/grammar/datatype/list/if_item_1/stdout.golden b/test/grammar/datatype/list/if_item_1/stdout.golden new file mode 100644 index 000000000..ea5e2d448 --- /dev/null +++ b/test/grammar/datatype/list/if_item_1/stdout.golden @@ -0,0 +1,7 @@ +env1: envVar1 +env2: '' +data: +- key: ENV_1 + value: envVar1 +- key: ENV_2 + value: envVar2 diff --git a/test/grammar/datatype/list/if_item_2/main.k b/test/grammar/datatype/list/if_item_2/main.k new file mode 100644 index 000000000..620471b57 --- /dev/null +++ b/test/grammar/datatype/list/if_item_2/main.k @@ -0,0 +1,17 @@ +schema Template: + appName: str = "app" + idc: str = "" + result?: {str:} = { + "env": [ + if appName: { + "name": "APPNAME" + "value": appName + }, + if idc: { + "name": "IDCNAME" + "value": idc + }, + ] + } + +template = Template {} diff --git a/test/grammar/datatype/list/if_item_2/stdout.golden b/test/grammar/datatype/list/if_item_2/stdout.golden new file mode 100644 index 000000000..1a9f4db2d --- /dev/null +++ b/test/grammar/datatype/list/if_item_2/stdout.golden @@ -0,0 +1,7 @@ +template: + appName: app + idc: '' + result: + env: + - name: APPNAME + value: app diff --git a/test/grammar/datatype/list/if_item_3/main.k b/test/grammar/datatype/list/if_item_3/main.k new file mode 100644 index 000000000..5f78d6f79 --- /dev/null +++ b/test/grammar/datatype/list/if_item_3/main.k @@ -0,0 +1,40 @@ +data1 = [ + if True: + "value1" + elif True: + "value2" + elif True: + "value3" + else: + "value4" +] +data2 = [ + if False: + "value1" + elif True: + "value2" + elif True: + "value3" + else: + "value4" +] +data3 = [ + if False: + "value1" + elif False: + "value2" + elif True: + "value3" + else: + "value4" +] +data4 = [ + if False: + "value1" + elif False: + "value2" + elif False: + "value3" + else: + "value4" +] diff --git a/test/grammar/datatype/list/if_item_3/stdout.golden b/test/grammar/datatype/list/if_item_3/stdout.golden new file mode 100644 index 000000000..73d6a50ec --- /dev/null +++ b/test/grammar/datatype/list/if_item_3/stdout.golden @@ -0,0 +1,8 @@ +data1: +- value1 +data2: +- value2 +data3: +- value3 +data4: +- value4 diff --git a/test/grammar/datatype/list/if_item_4/main.k b/test/grammar/datatype/list/if_item_4/main.k new file mode 100644 index 000000000..a97020fcd --- /dev/null +++ b/test/grammar/datatype/list/if_item_4/main.k @@ -0,0 +1,56 @@ +data1 = [ + if True: + "value1" + 1 + elif True: + "value2" + 2 + elif True: + "value3" + 3 + else: + "value4" + 4 +] +data2 = [ + if False: + "value1" + 1 + elif True: + "value2" + 2 + elif True: + "value3" + 3 + else: + "value4" + 4 +] +data3 = [ + if False: + "value1" + 1 + elif False: + "value2" + 2 + elif True: + "value3" + 3 + else: + "value4" + 4 +] +data4 = [ + if False: + "value1" + 1 + elif False: + "value2" + 2 + elif False: + "value3" + 3 + else: + "value4" + 4 +] diff --git a/test/grammar/datatype/list/if_item_4/stdout.golden b/test/grammar/datatype/list/if_item_4/stdout.golden new file mode 100644 index 000000000..ac397aa19 --- /dev/null +++ b/test/grammar/datatype/list/if_item_4/stdout.golden @@ -0,0 +1,12 @@ +data1: +- value1 +- 1 +data2: +- value2 +- 2 +data3: +- value3 +- 3 +data4: +- value4 +- 4 diff --git a/test/grammar/datatype/list/if_item_5/main.k b/test/grammar/datatype/list/if_item_5/main.k new file mode 100644 index 000000000..d4d9e6b56 --- /dev/null +++ b/test/grammar/datatype/list/if_item_5/main.k @@ -0,0 +1,16 @@ +a = 1 # 1 +data1 = [ + 1 + if a == 1: + 2 + elif a == 2: + 3 + else: + 3 +] # [1, 2] +data2 = [ + 1 + if a == 1: 2 + elif a == 2: 3 + else: 3 +] # [1, 2] diff --git a/test/grammar/datatype/list/if_item_5/stdout.golden b/test/grammar/datatype/list/if_item_5/stdout.golden new file mode 100644 index 000000000..425fcfa2a --- /dev/null +++ b/test/grammar/datatype/list/if_item_5/stdout.golden @@ -0,0 +1,7 @@ +a: 1 +data1: +- 1 +- 2 +data2: +- 1 +- 2 diff --git a/test/grammar/datatype/list/if_item_6/main.k b/test/grammar/datatype/list/if_item_6/main.k new file mode 100644 index 000000000..30f38af9d --- /dev/null +++ b/test/grammar/datatype/list/if_item_6/main.k @@ -0,0 +1,16 @@ +a = 1 # 1 +data1 = [ + *[1] + if a == 1: + *[2] + elif a == 2: + *[3] + else: + *[3] +] # [1, 2] +data2 = [ + *[1] + if a == 1: *[2] + elif a == 2: *[3] + else: *[3] +] # [1, 2] diff --git a/test/grammar/datatype/list/if_item_6/stdout.golden b/test/grammar/datatype/list/if_item_6/stdout.golden new file mode 100644 index 000000000..425fcfa2a --- /dev/null +++ b/test/grammar/datatype/list/if_item_6/stdout.golden @@ -0,0 +1,7 @@ +a: 1 +data1: +- 1 +- 2 +data2: +- 1 +- 2 diff --git a/test/grammar/datatype/list/if_item_7/main.k b/test/grammar/datatype/list/if_item_7/main.k new file mode 100644 index 000000000..274c45473 --- /dev/null +++ b/test/grammar/datatype/list/if_item_7/main.k @@ -0,0 +1,19 @@ +a = 1 # 1 +b = 1 # 1 +data1 = [ + *[1] + if a == 1: + *[2, 3 ,4], + if b == 1: + b + else: + b + 1 + elif a == 2: + if b == 1: + b + else: + b +] # [1, 2, 3, 4, 1] +data2 = [ + if a == 1: if b == 1: a + b +] # [1, 2] diff --git a/test/grammar/datatype/list/if_item_7/stdout.golden b/test/grammar/datatype/list/if_item_7/stdout.golden new file mode 100644 index 000000000..97edb7a48 --- /dev/null +++ b/test/grammar/datatype/list/if_item_7/stdout.golden @@ -0,0 +1,10 @@ +a: 1 +b: 1 +data1: +- 1 +- 2 +- 3 +- 4 +- 1 +data2: +- 2 diff --git a/test/grammar/datatype/list/merge_dict/main.k b/test/grammar/datatype/list/merge_dict/main.k new file mode 100644 index 000000000..2a2d39cd3 --- /dev/null +++ b/test/grammar/datatype/list/merge_dict/main.k @@ -0,0 +1,2 @@ +data = {"key": "value"} +listData = [*data, 0] diff --git a/test/grammar/datatype/list/merge_dict/stdout.golden b/test/grammar/datatype/list/merge_dict/stdout.golden new file mode 100644 index 000000000..4580787ab --- /dev/null +++ b/test/grammar/datatype/list/merge_dict/stdout.golden @@ -0,0 +1,5 @@ +data: + key: value +listData: +- key +- 0 diff --git a/test/grammar/datatype/list/slice/main.k b/test/grammar/datatype/list/slice/main.k new file mode 100644 index 000000000..fd21fd15c --- /dev/null +++ b/test/grammar/datatype/list/slice/main.k @@ -0,0 +1,13 @@ +data = [1, 2, 3, 4] +data0 = data[1:] +data1 = data[:-1] +data2 = data[1:-1] +data3 = data[1::2] +data4 = data[4::-2] + +val = "banana" +val0 = val[1:] +val1 = val[:-1] +val2 = val[1:-1] +val3 = val[1::2] +val4 = val[4::-2] diff --git a/test/grammar/datatype/list/slice/stdout.golden b/test/grammar/datatype/list/slice/stdout.golden new file mode 100644 index 000000000..68ab6cba2 --- /dev/null +++ b/test/grammar/datatype/list/slice/stdout.golden @@ -0,0 +1,28 @@ +data: +- 1 +- 2 +- 3 +- 4 +data0: +- 2 +- 3 +- 4 +data1: +- 1 +- 2 +- 3 +data2: +- 2 +- 3 +data3: +- 2 +- 4 +data4: +- 4 +- 2 +val: banana +val0: anana +val1: banan +val2: anan +val3: aaa +val4: nnb diff --git a/test/grammar/datatype/range_check_float/normal/main.k b/test/grammar/datatype/range_check_float/normal/main.k new file mode 100644 index 000000000..a98fb962e --- /dev/null +++ b/test/grammar/datatype/range_check_float/normal/main.k @@ -0,0 +1,7 @@ +downlimit = 1.175494351e-10 +uplimit = 3.402823466e+10 + +epsilon = 2.220446049250313e-1 + +a = uplimit / (1 + epsilon) +b = downlimit * (1 + epsilon) diff --git a/test/grammar/datatype/range_check_float/normal/stdout.golden b/test/grammar/datatype/range_check_float/normal/stdout.golden new file mode 100644 index 000000000..b837978ec --- /dev/null +++ b/test/grammar/datatype/range_check_float/normal/stdout.golden @@ -0,0 +1,5 @@ +downlimit: 1.175494351e-10 +uplimit: 34028234660.0 +epsilon: 0.2220446049250313 +a: 27845329477.222748 +b: 1.436506529759401e-10 diff --git a/test/grammar/datatype/range_check_float/overflow/inf/main.k b/test/grammar/datatype/range_check_float/overflow/inf/main.k new file mode 100644 index 000000000..f31843c23 --- /dev/null +++ b/test/grammar/datatype/range_check_float/overflow/inf/main.k @@ -0,0 +1,7 @@ +downlimit = 1.175494351e-38 +uplimit = 3.402823466e+38 + +epsilon = 2.220446049250313e-16 + +a = float("inf") / (1 + epsilon) +b = downlimit * (1 + epsilon) diff --git a/test/grammar/datatype/range_check_float/overflow/inf/settings.yaml b/test/grammar/datatype/range_check_float/overflow/inf/settings.yaml new file mode 100644 index 000000000..542d01cf5 --- /dev/null +++ b/test/grammar/datatype/range_check_float/overflow/inf/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d diff --git a/test/grammar/datatype/range_check_float/overflow/inf/stderr.golden.py b/test/grammar/datatype/range_check_float/overflow/inf/stderr.golden.py new file mode 100644 index 000000000..1234c318c --- /dev/null +++ b/test/grammar/datatype/range_check_float/overflow/inf/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.FloatOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6 + ) + ], + arg_msg=kcl_error.FLOAT_OVER_FLOW_MSG.format("inf", 64) + ) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/range_check_float/overflow/number_0/main.k b/test/grammar/datatype/range_check_float/overflow/number_0/main.k new file mode 100644 index 000000000..3f118436f --- /dev/null +++ b/test/grammar/datatype/range_check_float/overflow/number_0/main.k @@ -0,0 +1,9 @@ +downlimit = 1.175494351e-38 +# uplimit = 3.402823466e+38 +uplimit = 3.4e+38 + +# epsilon = 2.220446049250313e-16 +epsilon = 2.22e-16 + +a = uplimit * (100 + epsilon) +b = downlimit * (100 + epsilon) diff --git a/test/grammar/datatype/range_check_float/overflow/number_0/settings.yaml b/test/grammar/datatype/range_check_float/overflow/number_0/settings.yaml new file mode 100644 index 000000000..5fae1f7de --- /dev/null +++ b/test/grammar/datatype/range_check_float/overflow/number_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -r -d diff --git a/test/grammar/datatype/range_check_float/overflow/number_0/stderr.golden.py b/test/grammar/datatype/range_check_float/overflow/number_0/stderr.golden.py new file mode 100644 index 000000000..ebb3e5553 --- /dev/null +++ b/test/grammar/datatype/range_check_float/overflow/number_0/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.FloatOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=8 + ) + ], + arg_msg=kcl_error.FLOAT_OVER_FLOW_MSG.format(3.4e+40, 32) + ) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/range_check_float/overflow/number_1/main.k b/test/grammar/datatype/range_check_float/overflow/number_1/main.k new file mode 100644 index 000000000..3f118436f --- /dev/null +++ b/test/grammar/datatype/range_check_float/overflow/number_1/main.k @@ -0,0 +1,9 @@ +downlimit = 1.175494351e-38 +# uplimit = 3.402823466e+38 +uplimit = 3.4e+38 + +# epsilon = 2.220446049250313e-16 +epsilon = 2.22e-16 + +a = uplimit * (100 + epsilon) +b = downlimit * (100 + epsilon) diff --git a/test/grammar/datatype/range_check_float/overflow/number_1/settings.yaml b/test/grammar/datatype/range_check_float/overflow/number_1/settings.yaml new file mode 100644 index 000000000..542d01cf5 --- /dev/null +++ b/test/grammar/datatype/range_check_float/overflow/number_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d diff --git a/test/grammar/datatype/range_check_float/overflow/number_1/stdout.golden b/test/grammar/datatype/range_check_float/overflow/number_1/stdout.golden new file mode 100644 index 000000000..03bf70e7b --- /dev/null +++ b/test/grammar/datatype/range_check_float/overflow/number_1/stdout.golden @@ -0,0 +1,5 @@ +downlimit: 1.175494351e-38 +uplimit: 3.4e+38 +epsilon: 2.22e-16 +a: 3.4e+40 +b: 1.175494351e-36 diff --git a/test/grammar/datatype/range_check_float/underflow/number_0/_main.k b/test/grammar/datatype/range_check_float/underflow/number_0/_main.k new file mode 100644 index 000000000..81c180d08 --- /dev/null +++ b/test/grammar/datatype/range_check_float/underflow/number_0/_main.k @@ -0,0 +1,7 @@ +downlimit = 1.175494351e-38 +uplimit = 3.402823466e+38 + +epsilon = 2.220446049250313e-16 + +a = uplimit / (1 + epsilon) +b = downlimit / (1 + epsilon) diff --git a/test/grammar/datatype/range_check_float/underflow/number_0/settings.yaml b/test/grammar/datatype/range_check_float/underflow/number_0/settings.yaml new file mode 100644 index 000000000..5fae1f7de --- /dev/null +++ b/test/grammar/datatype/range_check_float/underflow/number_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -r -d diff --git a/test/grammar/datatype/range_check_float/underflow/number_0/stderr.golden.py b/test/grammar/datatype/range_check_float/underflow/number_0/stderr.golden.py new file mode 100644 index 000000000..0b31d3b1d --- /dev/null +++ b/test/grammar/datatype/range_check_float/underflow/number_0/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_warning_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.FloatUnderflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7 + ) + ], + arg_msg=kcl_error.FLOAT_UNDER_FLOW_MSG.format(1.1754943509999997e-38, 32) + ) + , file=sys.stdout +) diff --git a/test/grammar/datatype/range_check_float/underflow/number_1/_main.k b/test/grammar/datatype/range_check_float/underflow/number_1/_main.k new file mode 100644 index 000000000..81c180d08 --- /dev/null +++ b/test/grammar/datatype/range_check_float/underflow/number_1/_main.k @@ -0,0 +1,7 @@ +downlimit = 1.175494351e-38 +uplimit = 3.402823466e+38 + +epsilon = 2.220446049250313e-16 + +a = uplimit / (1 + epsilon) +b = downlimit / (1 + epsilon) diff --git a/test/grammar/datatype/range_check_float/underflow/number_1/settings.yaml b/test/grammar/datatype/range_check_float/underflow/number_1/settings.yaml new file mode 100644 index 000000000..542d01cf5 --- /dev/null +++ b/test/grammar/datatype/range_check_float/underflow/number_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d diff --git a/test/grammar/datatype/range_check_float/underflow/number_1/stdout.golden b/test/grammar/datatype/range_check_float/underflow/number_1/stdout.golden new file mode 100644 index 000000000..a14329770 --- /dev/null +++ b/test/grammar/datatype/range_check_float/underflow/number_1/stdout.golden @@ -0,0 +1,5 @@ +downlimit: 1.175494351e-38 +uplimit: 3.402823466e+38 +epsilon: 2.220446049250313e-16 +a: 3.4028234659999994e+38 +b: 1.1754943509999997e-38 diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_0/main.k b/test/grammar/datatype/range_check_int/augment_assign_fail_0/main.k new file mode 100644 index 000000000..800a1fd1a --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_0/main.k @@ -0,0 +1,2 @@ +_a = 2147483647 +_a += 1 diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_0/settings.yaml b/test/grammar/datatype/range_check_int/augment_assign_fail_0/settings.yaml new file mode 100644 index 000000000..5fae1f7de --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -r -d diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_0/stderr.golden.py b/test/grammar/datatype/range_check_int/augment_assign_fail_0/stderr.golden.py new file mode 100644 index 000000000..225e2ee6b --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_0/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2 + ), + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(2147483648, 32)) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_1/main.k b/test/grammar/datatype/range_check_int/augment_assign_fail_1/main.k new file mode 100644 index 000000000..d06b001d3 --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_1/main.k @@ -0,0 +1,2 @@ +_a = 9223372036854775807 +_a += 1 diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_1/settings.yaml b/test/grammar/datatype/range_check_int/augment_assign_fail_1/settings.yaml new file mode 100644 index 000000000..542d01cf5 --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_1/stderr.golden.py b/test/grammar/datatype/range_check_int/augment_assign_fail_1/stderr.golden.py new file mode 100644 index 000000000..bafe89e16 --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_1/stderr.golden.py @@ -0,0 +1,21 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2 + ) + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(9223372036854775808, 64) + ) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_2/main.k b/test/grammar/datatype/range_check_int/augment_assign_fail_2/main.k new file mode 100644 index 000000000..d06b001d3 --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_2/main.k @@ -0,0 +1,2 @@ +_a = 9223372036854775807 +_a += 1 diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_2/settings.yaml b/test/grammar/datatype/range_check_int/augment_assign_fail_2/settings.yaml new file mode 100644 index 000000000..542d01cf5 --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_2/stderr.golden.py b/test/grammar/datatype/range_check_int/augment_assign_fail_2/stderr.golden.py new file mode 100644 index 000000000..bafe89e16 --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_2/stderr.golden.py @@ -0,0 +1,21 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2 + ) + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(9223372036854775808, 64) + ) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_3/main.k b/test/grammar/datatype/range_check_int/augment_assign_fail_3/main.k new file mode 100644 index 000000000..800a1fd1a --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_3/main.k @@ -0,0 +1,2 @@ +_a = 2147483647 +_a += 1 diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_3/settings.yaml b/test/grammar/datatype/range_check_int/augment_assign_fail_3/settings.yaml new file mode 100644 index 000000000..5fae1f7de --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_3/settings.yaml @@ -0,0 +1 @@ +kcl_options: -r -d diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_3/stderr.golden.py b/test/grammar/datatype/range_check_int/augment_assign_fail_3/stderr.golden.py new file mode 100644 index 000000000..15e8b3254 --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_3/stderr.golden.py @@ -0,0 +1,21 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2 + ) + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(2147483648, 32) + ) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_4/main.k b/test/grammar/datatype/range_check_int/augment_assign_fail_4/main.k new file mode 100644 index 000000000..999b70a0b --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_4/main.k @@ -0,0 +1,2 @@ +a = 2147483646 +a += 1 diff --git a/test/grammar/datatype/range_check_int/augment_assign_fail_4/stderr.golden.py b/test/grammar/datatype/range_check_int/augment_assign_fail_4/stderr.golden.py new file mode 100644 index 000000000..f291e2f54 --- /dev/null +++ b/test/grammar/datatype/range_check_int/augment_assign_fail_4/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=1 + ) + ], + ) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/range_check_int/dict/main.k b/test/grammar/datatype/range_check_int/dict/main.k new file mode 100644 index 000000000..7586a68cc --- /dev/null +++ b/test/grammar/datatype/range_check_int/dict/main.k @@ -0,0 +1,6 @@ + +a = 2147483646 +b = 1234 +c = 3 +myList = [1, 2, 3, 4] +myDict = {"${2147483647}": 1234, "(a, b, c)": 2, "${int(c)}": 3, "${9}": 132, "${int(a)}": 1234, "${1234}": 1234} diff --git a/test/grammar/datatype/range_check_int/dict/stdout.golden b/test/grammar/datatype/range_check_int/dict/stdout.golden new file mode 100644 index 000000000..feb673fb9 --- /dev/null +++ b/test/grammar/datatype/range_check_int/dict/stdout.golden @@ -0,0 +1,15 @@ +a: 2147483646 +b: 1234 +c: 3 +myList: +- 1 +- 2 +- 3 +- 4 +myDict: + '2147483647': 1234 + (a, b, c): 2 + '3': 3 + '9': 132 + '2147483646': 1234 + '1234': 1234 diff --git a/test/grammar/datatype/range_check_int/dict_fail_0/main.k b/test/grammar/datatype/range_check_int/dict_fail_0/main.k new file mode 100644 index 000000000..cd5bbd2a3 --- /dev/null +++ b/test/grammar/datatype/range_check_int/dict_fail_0/main.k @@ -0,0 +1 @@ +a = {"key": 2147483648} diff --git a/test/grammar/datatype/range_check_int/dict_fail_0/settings.yaml b/test/grammar/datatype/range_check_int/dict_fail_0/settings.yaml new file mode 100644 index 000000000..5fae1f7de --- /dev/null +++ b/test/grammar/datatype/range_check_int/dict_fail_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -r -d diff --git a/test/grammar/datatype/range_check_int/dict_fail_0/stderr.golden.py b/test/grammar/datatype/range_check_int/dict_fail_0/stderr.golden.py new file mode 100644 index 000000000..8643f7a62 --- /dev/null +++ b/test/grammar/datatype/range_check_int/dict_fail_0/stderr.golden.py @@ -0,0 +1,20 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1 + ) + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(2147483648, 32) + ) + , file=sys.stdout +) diff --git a/test/grammar/datatype/range_check_int/dict_fail_1/main.k b/test/grammar/datatype/range_check_int/dict_fail_1/main.k new file mode 100644 index 000000000..c216d620d --- /dev/null +++ b/test/grammar/datatype/range_check_int/dict_fail_1/main.k @@ -0,0 +1 @@ +a = {"key": 9223372036854775808} diff --git a/test/grammar/datatype/range_check_int/dict_fail_1/settings.yaml b/test/grammar/datatype/range_check_int/dict_fail_1/settings.yaml new file mode 100644 index 000000000..542d01cf5 --- /dev/null +++ b/test/grammar/datatype/range_check_int/dict_fail_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d diff --git a/test/grammar/datatype/range_check_int/dict_fail_1/stderr.golden.py b/test/grammar/datatype/range_check_int/dict_fail_1/stderr.golden.py new file mode 100644 index 000000000..877df4f8a --- /dev/null +++ b/test/grammar/datatype/range_check_int/dict_fail_1/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1 + ), + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(9223372036854775808, 64)) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/range_check_int/list/main.k b/test/grammar/datatype/range_check_int/list/main.k new file mode 100644 index 000000000..d166df8bb --- /dev/null +++ b/test/grammar/datatype/range_check_int/list/main.k @@ -0,0 +1,3 @@ + + +myList = [1, 2, 3, 2147483646] diff --git a/test/grammar/datatype/range_check_int/list/stdout.golden b/test/grammar/datatype/range_check_int/list/stdout.golden new file mode 100644 index 000000000..d82abd1fe --- /dev/null +++ b/test/grammar/datatype/range_check_int/list/stdout.golden @@ -0,0 +1,5 @@ +myList: +- 1 +- 2 +- 3 +- 2147483646 \ No newline at end of file diff --git a/test/grammar/datatype/range_check_int/list_fail_0/main.k b/test/grammar/datatype/range_check_int/list_fail_0/main.k new file mode 100644 index 000000000..d2f87881d --- /dev/null +++ b/test/grammar/datatype/range_check_int/list_fail_0/main.k @@ -0,0 +1 @@ +myList = [1, 2, 3, 2147483648] diff --git a/test/grammar/datatype/range_check_int/list_fail_0/settings.yaml b/test/grammar/datatype/range_check_int/list_fail_0/settings.yaml new file mode 100644 index 000000000..5fae1f7de --- /dev/null +++ b/test/grammar/datatype/range_check_int/list_fail_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -r -d diff --git a/test/grammar/datatype/range_check_int/list_fail_0/stderr.golden.py b/test/grammar/datatype/range_check_int/list_fail_0/stderr.golden.py new file mode 100644 index 000000000..602e555a9 --- /dev/null +++ b/test/grammar/datatype/range_check_int/list_fail_0/stderr.golden.py @@ -0,0 +1,18 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1 + ), + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(2147483648, 32)) + , file=sys.stdout +) diff --git a/test/grammar/datatype/range_check_int/list_fail_1/main.k b/test/grammar/datatype/range_check_int/list_fail_1/main.k new file mode 100644 index 000000000..116e4937c --- /dev/null +++ b/test/grammar/datatype/range_check_int/list_fail_1/main.k @@ -0,0 +1 @@ +myList = [1, 2, 3, 9223372036854775808] diff --git a/test/grammar/datatype/range_check_int/list_fail_1/settings.yaml b/test/grammar/datatype/range_check_int/list_fail_1/settings.yaml new file mode 100644 index 000000000..542d01cf5 --- /dev/null +++ b/test/grammar/datatype/range_check_int/list_fail_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d diff --git a/test/grammar/datatype/range_check_int/list_fail_1/stderr.golden.py b/test/grammar/datatype/range_check_int/list_fail_1/stderr.golden.py new file mode 100644 index 000000000..877df4f8a --- /dev/null +++ b/test/grammar/datatype/range_check_int/list_fail_1/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1 + ), + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(9223372036854775808, 64)) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/range_check_int/normal_assign/main.k b/test/grammar/datatype/range_check_int/normal_assign/main.k new file mode 100644 index 000000000..2389a2f11 --- /dev/null +++ b/test/grammar/datatype/range_check_int/normal_assign/main.k @@ -0,0 +1,3 @@ +a = 20 +b = 5.5 +c = True diff --git a/test/grammar/datatype/range_check_int/normal_assign/stdout.golden b/test/grammar/datatype/range_check_int/normal_assign/stdout.golden new file mode 100644 index 000000000..58484dcf5 --- /dev/null +++ b/test/grammar/datatype/range_check_int/normal_assign/stdout.golden @@ -0,0 +1,3 @@ +a: 20 +b: 5.5 +c: true \ No newline at end of file diff --git a/test/grammar/datatype/range_check_int/normal_assign_fail_0/main.k b/test/grammar/datatype/range_check_int/normal_assign_fail_0/main.k new file mode 100644 index 000000000..0dc548955 --- /dev/null +++ b/test/grammar/datatype/range_check_int/normal_assign_fail_0/main.k @@ -0,0 +1 @@ +a = 2147483648 diff --git a/test/grammar/datatype/range_check_int/normal_assign_fail_0/settings.yaml b/test/grammar/datatype/range_check_int/normal_assign_fail_0/settings.yaml new file mode 100644 index 000000000..5fae1f7de --- /dev/null +++ b/test/grammar/datatype/range_check_int/normal_assign_fail_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -r -d diff --git a/test/grammar/datatype/range_check_int/normal_assign_fail_0/stderr.golden.py b/test/grammar/datatype/range_check_int/normal_assign_fail_0/stderr.golden.py new file mode 100644 index 000000000..b52d322b3 --- /dev/null +++ b/test/grammar/datatype/range_check_int/normal_assign_fail_0/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1 + ), + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(2147483648, 32)) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/range_check_int/normal_assign_fail_1/main.k b/test/grammar/datatype/range_check_int/normal_assign_fail_1/main.k new file mode 100644 index 000000000..d12c93eb4 --- /dev/null +++ b/test/grammar/datatype/range_check_int/normal_assign_fail_1/main.k @@ -0,0 +1 @@ +a = 9223372036854775808 diff --git a/test/grammar/datatype/range_check_int/normal_assign_fail_1/settings.yaml b/test/grammar/datatype/range_check_int/normal_assign_fail_1/settings.yaml new file mode 100644 index 000000000..542d01cf5 --- /dev/null +++ b/test/grammar/datatype/range_check_int/normal_assign_fail_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d diff --git a/test/grammar/datatype/range_check_int/normal_assign_fail_1/stderr.golden.py b/test/grammar/datatype/range_check_int/normal_assign_fail_1/stderr.golden.py new file mode 100644 index 000000000..877df4f8a --- /dev/null +++ b/test/grammar/datatype/range_check_int/normal_assign_fail_1/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1 + ), + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(9223372036854775808, 64)) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/range_check_int/oneliner/main.k b/test/grammar/datatype/range_check_int/oneliner/main.k new file mode 100644 index 000000000..2adf5ea41 --- /dev/null +++ b/test/grammar/datatype/range_check_int/oneliner/main.k @@ -0,0 +1,3 @@ +a = 1 +b = -2147483647 +c = -0 diff --git a/test/grammar/datatype/range_check_int/oneliner/settings.yaml b/test/grammar/datatype/range_check_int/oneliner/settings.yaml new file mode 100644 index 000000000..542d01cf5 --- /dev/null +++ b/test/grammar/datatype/range_check_int/oneliner/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d diff --git a/test/grammar/datatype/range_check_int/oneliner/stdout.golden b/test/grammar/datatype/range_check_int/oneliner/stdout.golden new file mode 100644 index 000000000..13bcd05b8 --- /dev/null +++ b/test/grammar/datatype/range_check_int/oneliner/stdout.golden @@ -0,0 +1,3 @@ +a: 1 +b: -2147483647 +c: 0 diff --git a/test/grammar/datatype/range_check_int/oneliner_fail_0/main.k b/test/grammar/datatype/range_check_int/oneliner_fail_0/main.k new file mode 100644 index 000000000..ef1b82d52 --- /dev/null +++ b/test/grammar/datatype/range_check_int/oneliner_fail_0/main.k @@ -0,0 +1,2 @@ +a = 1 +c = +2147483648 diff --git a/test/grammar/datatype/range_check_int/oneliner_fail_0/settings.yaml b/test/grammar/datatype/range_check_int/oneliner_fail_0/settings.yaml new file mode 100644 index 000000000..d4fd18f4b --- /dev/null +++ b/test/grammar/datatype/range_check_int/oneliner_fail_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -r -d diff --git a/test/grammar/datatype/range_check_int/oneliner_fail_0/stderr.golden.py b/test/grammar/datatype/range_check_int/oneliner_fail_0/stderr.golden.py new file mode 100644 index 000000000..225e2ee6b --- /dev/null +++ b/test/grammar/datatype/range_check_int/oneliner_fail_0/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2 + ), + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(2147483648, 32)) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/range_check_int/oneliner_fail_1/main.k b/test/grammar/datatype/range_check_int/oneliner_fail_1/main.k new file mode 100644 index 000000000..5a13773fd --- /dev/null +++ b/test/grammar/datatype/range_check_int/oneliner_fail_1/main.k @@ -0,0 +1,2 @@ +a = 1 +c = +9223372036854775808 diff --git a/test/grammar/datatype/range_check_int/oneliner_fail_1/settings.yaml b/test/grammar/datatype/range_check_int/oneliner_fail_1/settings.yaml new file mode 100644 index 000000000..542d01cf5 --- /dev/null +++ b/test/grammar/datatype/range_check_int/oneliner_fail_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d diff --git a/test/grammar/datatype/range_check_int/oneliner_fail_1/stderr.golden.py b/test/grammar/datatype/range_check_int/oneliner_fail_1/stderr.golden.py new file mode 100644 index 000000000..f8cdeca0e --- /dev/null +++ b/test/grammar/datatype/range_check_int/oneliner_fail_1/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2 + ), + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(9223372036854775808, 64)) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/str/count_0/main.k b/test/grammar/datatype/str/count_0/main.k new file mode 100644 index 000000000..9bd6823f2 --- /dev/null +++ b/test/grammar/datatype/str/count_0/main.k @@ -0,0 +1,5 @@ +x0 = "aaaa".count("a") +x1 = "aaaa".count("a", 1) +x2 = "aaaa".count("a", 1, None) +x3 = "aaaa".count("a", None, None) +x4 = "aaaa".count("a", None, -1) diff --git a/test/grammar/datatype/str/count_0/stdout.golden b/test/grammar/datatype/str/count_0/stdout.golden new file mode 100644 index 000000000..2ec278d04 --- /dev/null +++ b/test/grammar/datatype/str/count_0/stdout.golden @@ -0,0 +1,5 @@ +x0: 4 +x1: 3 +x2: 3 +x3: 4 +x4: 3 diff --git a/test/grammar/datatype/str/index_0/main.k b/test/grammar/datatype/str/index_0/main.k new file mode 100644 index 000000000..38b213051 --- /dev/null +++ b/test/grammar/datatype/str/index_0/main.k @@ -0,0 +1,5 @@ +data = "Hello world" +c0 = "Hello world"[0] +c1 = "Hello world"[-1] +c2 = data[0] +c3 = data[-1] diff --git a/test/grammar/datatype/str/index_0/stdout.golden b/test/grammar/datatype/str/index_0/stdout.golden new file mode 100644 index 000000000..db08669a6 --- /dev/null +++ b/test/grammar/datatype/str/index_0/stdout.golden @@ -0,0 +1,5 @@ +data: Hello world +c0: H +c1: d +c2: H +c3: d diff --git a/test/grammar/datatype/str/slice_0/main.k b/test/grammar/datatype/str/slice_0/main.k new file mode 100644 index 000000000..db0cf03f0 --- /dev/null +++ b/test/grammar/datatype/str/slice_0/main.k @@ -0,0 +1,5 @@ +data = "Hello world" +c0 = "Hello world"[0:1] +c1 = "Hello world"[-1:] +c2 = data[0:1] +c3 = data[-1:] diff --git a/test/grammar/datatype/str/slice_0/stdout.golden b/test/grammar/datatype/str/slice_0/stdout.golden new file mode 100644 index 000000000..db08669a6 --- /dev/null +++ b/test/grammar/datatype/str/slice_0/stdout.golden @@ -0,0 +1,5 @@ +data: Hello world +c0: H +c1: d +c2: H +c3: d diff --git a/test/grammar/datatype/str_interpolation/complex_0/main.k b/test/grammar/datatype/str_interpolation/complex_0/main.k new file mode 100644 index 000000000..c917c0488 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/complex_0/main.k @@ -0,0 +1,9 @@ +schema Data: + a: int = 1 + b: float = 1.2 + c: bool = True + d: [int] = [1] + e: {str:str} = {"key": "value"} + data: str = "${a} ${b} ${c} ${d} ${e}" + +data = Data {} diff --git a/test/grammar/datatype/str_interpolation/complex_0/stdout.golden b/test/grammar/datatype/str_interpolation/complex_0/stdout.golden new file mode 100644 index 000000000..40c1f227a --- /dev/null +++ b/test/grammar/datatype/str_interpolation/complex_0/stdout.golden @@ -0,0 +1,9 @@ +data: + a: 1 + b: 1.2 + c: true + d: + - 1 + e: + key: value + data: "1 1.2 True [1] {'key': 'value'}" diff --git a/test/grammar/datatype/str_interpolation/complex_1/main.k b/test/grammar/datatype/str_interpolation/complex_1/main.k new file mode 100644 index 000000000..c591fc2ff --- /dev/null +++ b/test/grammar/datatype/str_interpolation/complex_1/main.k @@ -0,0 +1,7 @@ +CONSTANT = {"key1": "value1", "key2": "value2"} + +schema Data: + jsonStr: str = "${CONSTANT: #json}" + yamlStr: str = "${CONSTANT: #yaml}" + +data = Data {} diff --git a/test/grammar/datatype/str_interpolation/complex_1/stdout.golden b/test/grammar/datatype/str_interpolation/complex_1/stdout.golden new file mode 100644 index 000000000..30d5cd7b7 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/complex_1/stdout.golden @@ -0,0 +1,8 @@ +CONSTANT: + key1: value1 + key2: value2 +data: + jsonStr: '{"key1": "value1", "key2": "value2"}' + yamlStr: | + key1: value1 + key2: value2 diff --git a/test/grammar/datatype/str_interpolation/complex_2/kcl.mod b/test/grammar/datatype/str_interpolation/complex_2/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/datatype/str_interpolation/complex_2/main.k b/test/grammar/datatype/str_interpolation/complex_2/main.k new file mode 100644 index 000000000..b3a207039 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/complex_2/main.k @@ -0,0 +1,4 @@ +import pkg + +a = pkg.a +aStr = "${pkg.a}" diff --git a/test/grammar/datatype/str_interpolation/complex_2/pkg/pkg.k b/test/grammar/datatype/str_interpolation/complex_2/pkg/pkg.k new file mode 100644 index 000000000..1337a530c --- /dev/null +++ b/test/grammar/datatype/str_interpolation/complex_2/pkg/pkg.k @@ -0,0 +1 @@ +a = 1 diff --git a/test/grammar/datatype/str_interpolation/complex_2/stdout.golden b/test/grammar/datatype/str_interpolation/complex_2/stdout.golden new file mode 100644 index 000000000..d6d2ef24d --- /dev/null +++ b/test/grammar/datatype/str_interpolation/complex_2/stdout.golden @@ -0,0 +1,2 @@ +a: 1 +aStr: '1' diff --git a/test/grammar/datatype/str_interpolation/complex_3/kcl.mod b/test/grammar/datatype/str_interpolation/complex_3/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/datatype/str_interpolation/complex_3/main.k b/test/grammar/datatype/str_interpolation/complex_3/main.k new file mode 100644 index 000000000..4e88e08d9 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/complex_3/main.k @@ -0,0 +1,10 @@ +import pkg + +schema Data: + a = pkg.a + aStr = "${pkg.a}" + config = { + "${pkg.a}": "1" + } + +data = Data {} diff --git a/test/grammar/datatype/str_interpolation/complex_3/pkg/pkg.k b/test/grammar/datatype/str_interpolation/complex_3/pkg/pkg.k new file mode 100644 index 000000000..1337a530c --- /dev/null +++ b/test/grammar/datatype/str_interpolation/complex_3/pkg/pkg.k @@ -0,0 +1 @@ +a = 1 diff --git a/test/grammar/datatype/str_interpolation/complex_3/stdout.golden b/test/grammar/datatype/str_interpolation/complex_3/stdout.golden new file mode 100644 index 000000000..50fd03bf3 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/complex_3/stdout.golden @@ -0,0 +1,5 @@ +data: + a: 1 + aStr: '1' + config: + '1': '1' diff --git a/test/grammar/datatype/str_interpolation/index/main.k b/test/grammar/datatype/str_interpolation/index/main.k new file mode 100644 index 000000000..43864677f --- /dev/null +++ b/test/grammar/datatype/str_interpolation/index/main.k @@ -0,0 +1,5 @@ +CONSTANT = 100 + +data0 = "${CONSTANT}"[0] +data01 = "${CONSTANT}"[0:1] +data02 = "${CONSTANT}"[0:2] diff --git a/test/grammar/datatype/str_interpolation/index/stdout.golden b/test/grammar/datatype/str_interpolation/index/stdout.golden new file mode 100644 index 000000000..5f2f3e21c --- /dev/null +++ b/test/grammar/datatype/str_interpolation/index/stdout.golden @@ -0,0 +1,4 @@ +CONSTANT: 100 +data0: '1' +data01: '1' +data02: '10' diff --git a/test/grammar/datatype/str_interpolation/invalid_format_spec_fail_0/main.k b/test/grammar/datatype/str_interpolation/invalid_format_spec_fail_0/main.k new file mode 100644 index 000000000..f37ffe54d --- /dev/null +++ b/test/grammar/datatype/str_interpolation/invalid_format_spec_fail_0/main.k @@ -0,0 +1,3 @@ +a = 1 +b = 1 +data = "${a: #js}" + " $$ " diff --git a/test/grammar/datatype/str_interpolation/invalid_format_spec_fail_0/stderr.golden.py b/test/grammar/datatype/str_interpolation/invalid_format_spec_fail_0/stderr.golden.py new file mode 100644 index 000000000..389a3a524 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/invalid_format_spec_fail_0/stderr.golden.py @@ -0,0 +1,21 @@ +import os +import sys + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=11, + ) + ], + arg_msg="#js is a invalid format spec" + ) + , file=sys.stdout +) diff --git a/test/grammar/datatype/str_interpolation/invalid_format_spec_fail_1/main.k b/test/grammar/datatype/str_interpolation/invalid_format_spec_fail_1/main.k new file mode 100644 index 000000000..47adeee69 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/invalid_format_spec_fail_1/main.k @@ -0,0 +1,3 @@ +a = 1 +b = 1 +data = "${a: #yamll}" + " $$ " diff --git a/test/grammar/datatype/str_interpolation/invalid_format_spec_fail_1/stderr.golden.py b/test/grammar/datatype/str_interpolation/invalid_format_spec_fail_1/stderr.golden.py new file mode 100644 index 000000000..eced44cbe --- /dev/null +++ b/test/grammar/datatype/str_interpolation/invalid_format_spec_fail_1/stderr.golden.py @@ -0,0 +1,21 @@ +import os +import sys + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=11, + ) + ], + arg_msg="#yamll is a invalid format spec" + ) + , file=sys.stdout +) diff --git a/test/grammar/datatype/str_interpolation/invalid_format_value_fail_0/main.k b/test/grammar/datatype/str_interpolation/invalid_format_value_fail_0/main.k new file mode 100644 index 000000000..672fa2686 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/invalid_format_value_fail_0/main.k @@ -0,0 +1,2 @@ +a = 1 +b = "${b = a + 1}" diff --git a/test/grammar/datatype/str_interpolation/invalid_format_value_fail_0/stderr.golden.py b/test/grammar/datatype/str_interpolation/invalid_format_value_fail_0/stderr.golden.py new file mode 100644 index 000000000..475f5969e --- /dev/null +++ b/test/grammar/datatype/str_interpolation/invalid_format_value_fail_0/stderr.golden.py @@ -0,0 +1,23 @@ +import os +import sys + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.InvalidFormatSpec_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + end_col_no=19 + ) + ], + arg_msg="invalid string interpolation expression 'b = a + 1'" + ), + file=sys.stdout +) + diff --git a/test/grammar/datatype/str_interpolation/invalid_format_value_fail_1/main.k b/test/grammar/datatype/str_interpolation/invalid_format_value_fail_1/main.k new file mode 100644 index 000000000..7f61501a7 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/invalid_format_value_fail_1/main.k @@ -0,0 +1,3 @@ +a = 1 +b = 1 +data = "$$${a * 1}$$ ${b / 1} $$$$$" + " $$ " diff --git a/test/grammar/datatype/str_interpolation/invalid_format_value_fail_1/stderr.golden.py b/test/grammar/datatype/str_interpolation/invalid_format_value_fail_1/stderr.golden.py new file mode 100644 index 000000000..3b79bbd30 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/invalid_format_value_fail_1/stderr.golden.py @@ -0,0 +1,23 @@ +import os +import sys + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.InvalidFormatSpec_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=8, + end_col_no=37 + ) + ], + arg_msg="invalid single '$', expecting '$' or '{'" + ), + file=sys.stdout +) + diff --git a/test/grammar/datatype/str_interpolation/select/main.k b/test/grammar/datatype/str_interpolation/select/main.k new file mode 100644 index 000000000..bba9ee8a9 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/select/main.k @@ -0,0 +1,2 @@ +appName = "App" +appLower = "${appName}${abs(-1)}-suffix".lower() diff --git a/test/grammar/datatype/str_interpolation/select/stdout.golden b/test/grammar/datatype/str_interpolation/select/stdout.golden new file mode 100644 index 000000000..786337d65 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/select/stdout.golden @@ -0,0 +1,2 @@ +appName: App +appLower: app1-suffix diff --git a/test/grammar/datatype/str_interpolation/simple_0/main.k b/test/grammar/datatype/str_interpolation/simple_0/main.k new file mode 100644 index 000000000..bb008ce9e --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_0/main.k @@ -0,0 +1,6 @@ +a = 1 +b = 1.2 +c = True +d = [1] +e = {"key": "value"} +data = "${a} ${b} ${c} ${d} ${e}" diff --git a/test/grammar/datatype/str_interpolation/simple_0/stdout.golden b/test/grammar/datatype/str_interpolation/simple_0/stdout.golden new file mode 100644 index 000000000..67e417e17 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_0/stdout.golden @@ -0,0 +1,8 @@ +a: 1 +b: 1.2 +c: true +d: +- 1 +e: + key: value +data: "1 1.2 True [1] {'key': 'value'}" diff --git a/test/grammar/datatype/str_interpolation/simple_1/main.k b/test/grammar/datatype/str_interpolation/simple_1/main.k new file mode 100644 index 000000000..92cc224c4 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_1/main.k @@ -0,0 +1,6 @@ +a = 1 +b = 1.2 +c = True +d = [1] +e = {"key": "value"} +data = "${a + 1} ${b} ${int(c)} ${d * 2} ${e['key']}" diff --git a/test/grammar/datatype/str_interpolation/simple_1/stdout.golden b/test/grammar/datatype/str_interpolation/simple_1/stdout.golden new file mode 100644 index 000000000..af46a7202 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_1/stdout.golden @@ -0,0 +1,8 @@ +a: 1 +b: 1.2 +c: true +d: +- 1 +e: + key: value +data: 2 1.2 1 [1, 1] value diff --git a/test/grammar/datatype/str_interpolation/simple_2/main.k b/test/grammar/datatype/str_interpolation/simple_2/main.k new file mode 100644 index 000000000..8e0130c5c --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_2/main.k @@ -0,0 +1,3 @@ +a = 1 +b = 1 +data = "$$${a * 1}$$ ${b / 1} $$$$" + " $$ " diff --git a/test/grammar/datatype/str_interpolation/simple_2/stdout.golden b/test/grammar/datatype/str_interpolation/simple_2/stdout.golden new file mode 100644 index 000000000..7f87c2cca --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_2/stdout.golden @@ -0,0 +1,3 @@ +a: 1 +b: 1 +data: '$1$ 1.0 $$ $$ ' diff --git a/test/grammar/datatype/str_interpolation/simple_3/main.k b/test/grammar/datatype/str_interpolation/simple_3/main.k new file mode 100644 index 000000000..618024ad2 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_3/main.k @@ -0,0 +1,6 @@ +a = 1 +b = 1.2 +c = True +d = [1] +e = {"key": "value"} +data = "${a + 1 if a else b} ${b - 1 + int(c)} ${int(c) + 3} ${(d * 2)[0]} ${e['key']}" diff --git a/test/grammar/datatype/str_interpolation/simple_3/stdout.golden b/test/grammar/datatype/str_interpolation/simple_3/stdout.golden new file mode 100644 index 000000000..36d799995 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_3/stdout.golden @@ -0,0 +1,8 @@ +a: 1 +b: 1.2 +c: true +d: +- 1 +e: + key: value +data: 2 1.2 4 1 value diff --git a/test/grammar/datatype/str_interpolation/simple_4/main.k b/test/grammar/datatype/str_interpolation/simple_4/main.k new file mode 100644 index 000000000..4a8f1dc7d --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_4/main.k @@ -0,0 +1,8 @@ +a = 1 +b = 1.2 +c = True +d = [1] +e = {"key": "value"} +data = "${a + 1 if a else b} ${b - 1 + int(c)} ${int(c) + 3} ${(d * 2)[0]} ${e['key']}" +if a == 1: + raw = data + " ${a}" + str(a) diff --git a/test/grammar/datatype/str_interpolation/simple_4/stdout.golden b/test/grammar/datatype/str_interpolation/simple_4/stdout.golden new file mode 100644 index 000000000..642ba2c90 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_4/stdout.golden @@ -0,0 +1,9 @@ +a: 1 +b: 1.2 +c: true +d: +- 1 +e: + key: value +data: 2 1.2 4 1 value +raw: 2 1.2 4 1 value 11 diff --git a/test/grammar/datatype/str_interpolation/simple_json_spec_0/main.k b/test/grammar/datatype/str_interpolation/simple_json_spec_0/main.k new file mode 100644 index 000000000..46bd975f9 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_json_spec_0/main.k @@ -0,0 +1,6 @@ +a = 1 +b = 1.2 +c = True +d = [1] +e = {"key": "value"} +data = "${a: #json} ${b: #json} ${c: #json} ${d: #JSON} ${e : #jsON}" diff --git a/test/grammar/datatype/str_interpolation/simple_json_spec_0/stdout.golden b/test/grammar/datatype/str_interpolation/simple_json_spec_0/stdout.golden new file mode 100644 index 000000000..a37df1f81 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_json_spec_0/stdout.golden @@ -0,0 +1,8 @@ +a: 1 +b: 1.2 +c: true +d: +- 1 +e: + key: value +data: '1 1.2 true [1] {"key": "value"}' diff --git a/test/grammar/datatype/str_interpolation/simple_json_spec_1/main.k b/test/grammar/datatype/str_interpolation/simple_json_spec_1/main.k new file mode 100644 index 000000000..130ccf235 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_json_spec_1/main.k @@ -0,0 +1,6 @@ +a = 1 + 2 +b = 1.2 + 3.4 +c = None +d = [1, 2] +e = {"key1": "value1", "key2": "value2"} +data = "${a : #JSON} ${b:#JSON} ${c:#json} ${d:#json} ${e:#json}" diff --git a/test/grammar/datatype/str_interpolation/simple_json_spec_1/stdout.golden b/test/grammar/datatype/str_interpolation/simple_json_spec_1/stdout.golden new file mode 100644 index 000000000..a0d84515f --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_json_spec_1/stdout.golden @@ -0,0 +1,10 @@ +a: 3 +b: 4.6 +c: null +d: +- 1 +- 2 +e: + key1: value1 + key2: value2 +data: '3 4.6 null [1, 2] {"key1": "value1", "key2": "value2"}' diff --git a/test/grammar/datatype/str_interpolation/simple_yaml_spec_0/main.k b/test/grammar/datatype/str_interpolation/simple_yaml_spec_0/main.k new file mode 100644 index 000000000..35adfb2df --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_yaml_spec_0/main.k @@ -0,0 +1,6 @@ +a = 1 +b = 1.2 +c = True +d = [1] +e = {"key": "value"} +data = "${e: #yaML}" diff --git a/test/grammar/datatype/str_interpolation/simple_yaml_spec_0/stdout.golden b/test/grammar/datatype/str_interpolation/simple_yaml_spec_0/stdout.golden new file mode 100644 index 000000000..3b2b77d05 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_yaml_spec_0/stdout.golden @@ -0,0 +1,9 @@ +a: 1 +b: 1.2 +c: true +d: +- 1 +e: + key: value +data: | + key: value diff --git a/test/grammar/datatype/str_interpolation/simple_yaml_spec_1/_main.k b/test/grammar/datatype/str_interpolation/simple_yaml_spec_1/_main.k new file mode 100644 index 000000000..8b73efe29 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_yaml_spec_1/_main.k @@ -0,0 +1,6 @@ +data = { + "key1": "value1" + "key2": "value2" + "data": [1, 2, 3] +} +data_string = "${data: #yaml}" diff --git a/test/grammar/datatype/str_interpolation/simple_yaml_spec_1/stdout.golden b/test/grammar/datatype/str_interpolation/simple_yaml_spec_1/stdout.golden new file mode 100644 index 000000000..ecac01b28 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/simple_yaml_spec_1/stdout.golden @@ -0,0 +1,14 @@ +data: + key1: value1 + key2: value2 + data: + - 1 + - 2 + - 3 +data_string: | + key1: value1 + key2: value2 + data: + - 1 + - 2 + - 3 diff --git a/test/grammar/datatype/str_interpolation/string_with_raw_prefix_0/main.k b/test/grammar/datatype/str_interpolation/string_with_raw_prefix_0/main.k new file mode 100644 index 000000000..83e2ed6c6 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/string_with_raw_prefix_0/main.k @@ -0,0 +1,6 @@ +a = 1 +b = 3 +data1 = "${a + 1 if a else b}" +data2 = r"${a + 1 if a else b}" +data3 = '${a + 1 if a else b}' +data4 = r'${a + 1 if a else b}' diff --git a/test/grammar/datatype/str_interpolation/string_with_raw_prefix_0/stdout.golden b/test/grammar/datatype/str_interpolation/string_with_raw_prefix_0/stdout.golden new file mode 100644 index 000000000..a3067c21e --- /dev/null +++ b/test/grammar/datatype/str_interpolation/string_with_raw_prefix_0/stdout.golden @@ -0,0 +1,6 @@ +a: 1 +b: 3 +data1: '2' +data2: ${a + 1 if a else b} +data3: '2' +data4: ${a + 1 if a else b} diff --git a/test/grammar/datatype/str_interpolation/var_not_define_fail_0/main.k b/test/grammar/datatype/str_interpolation/var_not_define_fail_0/main.k new file mode 100644 index 000000000..6f03c260b --- /dev/null +++ b/test/grammar/datatype/str_interpolation/var_not_define_fail_0/main.k @@ -0,0 +1,2 @@ +a = 1 +b = "${c + 1}" diff --git a/test/grammar/datatype/str_interpolation/var_not_define_fail_0/stderr.golden.py b/test/grammar/datatype/str_interpolation/var_not_define_fail_0/stderr.golden.py new file mode 100644 index 000000000..b311d3ab7 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/var_not_define_fail_0/stderr.golden.py @@ -0,0 +1,22 @@ +import os +import sys + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=8 + ) + ], + arg_msg="name 'c' is not defined" + ), + file=sys.stdout +) + diff --git a/test/grammar/datatype/str_interpolation/var_not_define_fail_1/main.k b/test/grammar/datatype/str_interpolation/var_not_define_fail_1/main.k new file mode 100644 index 000000000..72a2a4efa --- /dev/null +++ b/test/grammar/datatype/str_interpolation/var_not_define_fail_1/main.k @@ -0,0 +1,3 @@ +a = 1 +b = "${c + 1}" +c = 2 diff --git a/test/grammar/datatype/str_interpolation/var_not_define_fail_1/stderr.golden.py b/test/grammar/datatype/str_interpolation/var_not_define_fail_1/stderr.golden.py new file mode 100644 index 000000000..b311d3ab7 --- /dev/null +++ b/test/grammar/datatype/str_interpolation/var_not_define_fail_1/stderr.golden.py @@ -0,0 +1,22 @@ +import os +import sys + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=8 + ) + ], + arg_msg="name 'c' is not defined" + ), + file=sys.stdout +) + diff --git a/test/grammar/datatype/subscript/subscript_0/main.k b/test/grammar/datatype/subscript/subscript_0/main.k new file mode 100644 index 000000000..cc8cb10eb --- /dev/null +++ b/test/grammar/datatype/subscript/subscript_0/main.k @@ -0,0 +1,4 @@ +main = { + "args": ["a", "b", "c", "d"] +} +args = main["args"][0:-2] diff --git a/test/grammar/datatype/subscript/subscript_0/stdout.golden b/test/grammar/datatype/subscript/subscript_0/stdout.golden new file mode 100644 index 000000000..c6e03f916 --- /dev/null +++ b/test/grammar/datatype/subscript/subscript_0/stdout.golden @@ -0,0 +1,9 @@ +main: + args: + - a + - b + - c + - d +args: +- a +- b diff --git a/test/grammar/datatype/subscript/subscript_1/main.k b/test/grammar/datatype/subscript/subscript_1/main.k new file mode 100644 index 000000000..e8884b215 --- /dev/null +++ b/test/grammar/datatype/subscript/subscript_1/main.k @@ -0,0 +1,4 @@ +main = { + "args": ["abc", "bcd", "cde", "def"] +} +args = main["args"][0:-2][0][0:2] diff --git a/test/grammar/datatype/subscript/subscript_1/stdout.golden b/test/grammar/datatype/subscript/subscript_1/stdout.golden new file mode 100644 index 000000000..2cb222373 --- /dev/null +++ b/test/grammar/datatype/subscript/subscript_1/stdout.golden @@ -0,0 +1,7 @@ +main: + args: + - abc + - bcd + - cde + - def +args: ab diff --git a/test/grammar/datatype/subscript/subscript_2/main.k b/test/grammar/datatype/subscript/subscript_2/main.k new file mode 100644 index 000000000..6a2c4326c --- /dev/null +++ b/test/grammar/datatype/subscript/subscript_2/main.k @@ -0,0 +1,5 @@ +main = { + "args": ["abc", "bcd", "cde", "def"] +} +args1 = main["args"][0:-2][0][0:2].upper()[0:1].lower() +args2 = "alice".upper()[::-1].lower().upper()[::-1] diff --git a/test/grammar/datatype/subscript/subscript_2/stdout.golden b/test/grammar/datatype/subscript/subscript_2/stdout.golden new file mode 100644 index 000000000..33dbf7477 --- /dev/null +++ b/test/grammar/datatype/subscript/subscript_2/stdout.golden @@ -0,0 +1,8 @@ +main: + args: + - abc + - bcd + - cde + - def +args1: a +args2: ALICE diff --git a/test/grammar/datatype/subscript/subscript_3/main.k b/test/grammar/datatype/subscript/subscript_3/main.k new file mode 100644 index 000000000..e9c25967d --- /dev/null +++ b/test/grammar/datatype/subscript/subscript_3/main.k @@ -0,0 +1,7 @@ +main = {"args": [1, 2, 3, 4, 5, 6]} +args = main["args"] +args_unpack = [ + *args[0:2], + "1", "1", + *args[2:len(args)] +] diff --git a/test/grammar/datatype/subscript/subscript_3/stdout.golden b/test/grammar/datatype/subscript/subscript_3/stdout.golden new file mode 100644 index 000000000..d704d9d47 --- /dev/null +++ b/test/grammar/datatype/subscript/subscript_3/stdout.golden @@ -0,0 +1,24 @@ +main: + args: + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 +args: +- 1 +- 2 +- 3 +- 4 +- 5 +- 6 +args_unpack: +- 1 +- 2 +- '1' +- '1' +- 3 +- 4 +- 5 +- 6 diff --git a/test/grammar/datatype/undefined/builtin/main.k b/test/grammar/datatype/undefined/builtin/main.k new file mode 100644 index 000000000..5b447b691 --- /dev/null +++ b/test/grammar/datatype/undefined/builtin/main.k @@ -0,0 +1,4 @@ +undefinedBool = bool(Undefined) +noneBool = bool(None) +undefinedStr = str(Undefined) +noneStr = str(None) diff --git a/test/grammar/datatype/undefined/builtin/stdout.golden b/test/grammar/datatype/undefined/builtin/stdout.golden new file mode 100644 index 000000000..a3d8d8e33 --- /dev/null +++ b/test/grammar/datatype/undefined/builtin/stdout.golden @@ -0,0 +1,4 @@ +undefinedBool: false +noneBool: false +undefinedStr: Undefined +noneStr: None diff --git a/test/grammar/datatype/undefined/condition_0/main.k b/test/grammar/datatype/undefined/condition_0/main.k new file mode 100644 index 000000000..9df28c9e5 --- /dev/null +++ b/test/grammar/datatype/undefined/condition_0/main.k @@ -0,0 +1,10 @@ +a = 1 +if not a: + b = 1 +if a is Undefined: + c = 1 +if a == Undefined: + d = 1 +e = "isNone" if a is None else "isUndefined" +f = bool(Undefined) +g = str(Undefined) diff --git a/test/grammar/datatype/undefined/condition_0/stdout.golden b/test/grammar/datatype/undefined/condition_0/stdout.golden new file mode 100644 index 000000000..b2d0d8a85 --- /dev/null +++ b/test/grammar/datatype/undefined/condition_0/stdout.golden @@ -0,0 +1,4 @@ +a: 1 +e: isUndefined +f: false +g: Undefined diff --git a/test/grammar/datatype/undefined/condition_1/main.k b/test/grammar/datatype/undefined/condition_1/main.k new file mode 100644 index 000000000..9b994ebab --- /dev/null +++ b/test/grammar/datatype/undefined/condition_1/main.k @@ -0,0 +1,10 @@ +value1 = Undefined == None +value2 = None == Undefined +value3 = Undefined != None +value4 = None != Undefined +value5 = not Undefined +value6 = not None +value7 = Undefined is None +value8 = None is Undefined +value9 = Undefined is not None +value10 = None is not Undefined diff --git a/test/grammar/datatype/undefined/condition_1/stdout.golden b/test/grammar/datatype/undefined/condition_1/stdout.golden new file mode 100644 index 000000000..b8c1b9a15 --- /dev/null +++ b/test/grammar/datatype/undefined/condition_1/stdout.golden @@ -0,0 +1,10 @@ +value1: false +value2: false +value3: true +value4: true +value5: true +value6: true +value7: false +value8: false +value9: true +value10: true diff --git a/test/grammar/datatype/undefined/dict/main.k b/test/grammar/datatype/undefined/dict/main.k new file mode 100644 index 000000000..b07f60269 --- /dev/null +++ b/test/grammar/datatype/undefined/dict/main.k @@ -0,0 +1,3 @@ +data = {"key": "value"} +errDataUndefined = data["err_key"] is Undefined +errDataNone = data["err_key"] is None diff --git a/test/grammar/datatype/undefined/dict/stdout.golden b/test/grammar/datatype/undefined/dict/stdout.golden new file mode 100644 index 000000000..15e05687d --- /dev/null +++ b/test/grammar/datatype/undefined/dict/stdout.golden @@ -0,0 +1,4 @@ +data: + key: value +errDataUndefined: true +errDataNone: false \ No newline at end of file diff --git a/test/grammar/datatype/undefined/fail_0/main.k b/test/grammar/datatype/undefined/fail_0/main.k new file mode 100644 index 000000000..3a2e977e3 --- /dev/null +++ b/test/grammar/datatype/undefined/fail_0/main.k @@ -0,0 +1,3 @@ +a = 1 +b = Undefined +data = a + b diff --git a/test/grammar/datatype/undefined/fail_0/stderr.golden.py b/test/grammar/datatype/undefined/fail_0/stderr.golden.py new file mode 100644 index 000000000..f522c2ad5 --- /dev/null +++ b/test/grammar/datatype/undefined/fail_0/stderr.golden.py @@ -0,0 +1,21 @@ +import os +import sys + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3 + ) + ], + arg_msg="unsupported operand type(s) for +: 'int' and 'UndefinedType'" + ) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/undefined/fail_1/main.k b/test/grammar/datatype/undefined/fail_1/main.k new file mode 100644 index 000000000..39fa4075e --- /dev/null +++ b/test/grammar/datatype/undefined/fail_1/main.k @@ -0,0 +1 @@ +result = int(Undefined) diff --git a/test/grammar/datatype/undefined/fail_1/stderr.golden.py b/test/grammar/datatype/undefined/fail_1/stderr.golden.py new file mode 100644 index 000000000..08affb444 --- /dev/null +++ b/test/grammar/datatype/undefined/fail_1/stderr.golden.py @@ -0,0 +1,21 @@ +import os +import sys + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1 + ) + ], + arg_msg="int() argument must be a string, a bytes-like object or a number, not 'UndefinedType'" + ) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/undefined/schema_0/main.k b/test/grammar/datatype/undefined/schema_0/main.k new file mode 100644 index 000000000..5f8a36b8e --- /dev/null +++ b/test/grammar/datatype/undefined/schema_0/main.k @@ -0,0 +1,6 @@ +schema Person: + name?: str = None + age?: int = Undefined + labels?: {str:str} + +person = Person {} \ No newline at end of file diff --git a/test/grammar/datatype/undefined/schema_0/stdout.golden b/test/grammar/datatype/undefined/schema_0/stdout.golden new file mode 100644 index 000000000..3763503d3 --- /dev/null +++ b/test/grammar/datatype/undefined/schema_0/stdout.golden @@ -0,0 +1,2 @@ +person: + name: null diff --git a/test/grammar/datatype/undefined/schema_1/main.k b/test/grammar/datatype/undefined/schema_1/main.k new file mode 100644 index 000000000..1bd48d299 --- /dev/null +++ b/test/grammar/datatype/undefined/schema_1/main.k @@ -0,0 +1,10 @@ +schema Base: + id?: int = 1 + name?: str = None + labels?: {str:} = {"key": "value"} + +schema Sub(Base): + id?: int = Undefined + +base = Base {} +sub = Sub {} diff --git a/test/grammar/datatype/undefined/schema_1/stdout.golden b/test/grammar/datatype/undefined/schema_1/stdout.golden new file mode 100644 index 000000000..2094c0155 --- /dev/null +++ b/test/grammar/datatype/undefined/schema_1/stdout.golden @@ -0,0 +1,9 @@ +base: + id: 1 + name: null + labels: + key: value +sub: + name: null + labels: + key: value diff --git a/test/grammar/datatype/undefined/simple_0/main.k b/test/grammar/datatype/undefined/simple_0/main.k new file mode 100644 index 000000000..ce2a40178 --- /dev/null +++ b/test/grammar/datatype/undefined/simple_0/main.k @@ -0,0 +1,4 @@ +a = None +b = Undefined +c = [1, None, Undefined, 2] +d = {"key1": 1, "key2": None, "key3": Undefined} diff --git a/test/grammar/datatype/undefined/simple_0/stdout.golden b/test/grammar/datatype/undefined/simple_0/stdout.golden new file mode 100644 index 000000000..e54caee0f --- /dev/null +++ b/test/grammar/datatype/undefined/simple_0/stdout.golden @@ -0,0 +1,8 @@ +a: null +c: +- 1 +- null +- 2 +d: + key1: 1 + key2: null diff --git a/test/grammar/datatype/undefined/simple_1/main.k b/test/grammar/datatype/undefined/simple_1/main.k new file mode 100644 index 000000000..6b01f2b1e --- /dev/null +++ b/test/grammar/datatype/undefined/simple_1/main.k @@ -0,0 +1,4 @@ +data1 = Undefined or [] or [1, 2, 3] +data2 = Undefined or {} or {"key": "value"} +data3 = None or [] or data1 +data4 = None or {} or data2 diff --git a/test/grammar/datatype/undefined/simple_1/stdout.golden b/test/grammar/datatype/undefined/simple_1/stdout.golden new file mode 100644 index 000000000..2bad85bf5 --- /dev/null +++ b/test/grammar/datatype/undefined/simple_1/stdout.golden @@ -0,0 +1,12 @@ +data1: +- 1 +- 2 +- 3 +data2: + key: value +data3: +- 1 +- 2 +- 3 +data4: + key: value diff --git a/test/grammar/datatype/undefined/simple_2/main.k b/test/grammar/datatype/undefined/simple_2/main.k new file mode 100644 index 000000000..b6db5796c --- /dev/null +++ b/test/grammar/datatype/undefined/simple_2/main.k @@ -0,0 +1,4 @@ +data1 = Undefined | Undefined +data2 = Undefined | None +data3 = None | None +data4 = None | Undefined diff --git a/test/grammar/datatype/undefined/simple_2/stdout.golden b/test/grammar/datatype/undefined/simple_2/stdout.golden new file mode 100644 index 000000000..7b0bed751 --- /dev/null +++ b/test/grammar/datatype/undefined/simple_2/stdout.golden @@ -0,0 +1,2 @@ +data2: null +data3: null diff --git a/test/grammar/datatype/undefined/unpack/main.k b/test/grammar/datatype/undefined/unpack/main.k new file mode 100644 index 000000000..42785fcc7 --- /dev/null +++ b/test/grammar/datatype/undefined/unpack/main.k @@ -0,0 +1,4 @@ +a = None +b = Undefined +c = [1, *None, *Undefined, 2] +d = {"key": "value", **Undefined} diff --git a/test/grammar/datatype/undefined/unpack/stdout.golden b/test/grammar/datatype/undefined/unpack/stdout.golden new file mode 100644 index 000000000..e3089f393 --- /dev/null +++ b/test/grammar/datatype/undefined/unpack/stdout.golden @@ -0,0 +1,6 @@ +a: null +c: +- 1 +- 2 +d: + key: value diff --git a/test/grammar/datatype/units/invalid_units_fail_0/main.k b/test/grammar/datatype/units/invalid_units_fail_0/main.k new file mode 100644 index 000000000..49e387ae6 --- /dev/null +++ b/test/grammar/datatype/units/invalid_units_fail_0/main.k @@ -0,0 +1 @@ +mi = 1mi diff --git a/test/grammar/datatype/units/invalid_units_fail_0/stderr.golden.py b/test/grammar/datatype/units/invalid_units_fail_0/stderr.golden.py new file mode 100644 index 000000000..dddc2c730 --- /dev/null +++ b/test/grammar/datatype/units/invalid_units_fail_0/stderr.golden.py @@ -0,0 +1,16 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.InvalidSyntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=7, + arg_msg="Expected one of ['newline']" + )], + ), + file=sys.stdout) diff --git a/test/grammar/datatype/units/invalid_units_fail_1/main.k b/test/grammar/datatype/units/invalid_units_fail_1/main.k new file mode 100644 index 000000000..1ff26d079 --- /dev/null +++ b/test/grammar/datatype/units/invalid_units_fail_1/main.k @@ -0,0 +1 @@ +ni = 1ni diff --git a/test/grammar/datatype/units/invalid_units_fail_1/stderr.golden.py b/test/grammar/datatype/units/invalid_units_fail_1/stderr.golden.py new file mode 100644 index 000000000..dddc2c730 --- /dev/null +++ b/test/grammar/datatype/units/invalid_units_fail_1/stderr.golden.py @@ -0,0 +1,16 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.InvalidSyntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=7, + arg_msg="Expected one of ['newline']" + )], + ), + file=sys.stdout) diff --git a/test/grammar/datatype/units/invalid_units_fail_2/main.k b/test/grammar/datatype/units/invalid_units_fail_2/main.k new file mode 100644 index 000000000..b27a06cfa --- /dev/null +++ b/test/grammar/datatype/units/invalid_units_fail_2/main.k @@ -0,0 +1 @@ +ui = 1ui diff --git a/test/grammar/datatype/units/invalid_units_fail_2/stderr.golden.py b/test/grammar/datatype/units/invalid_units_fail_2/stderr.golden.py new file mode 100644 index 000000000..dddc2c730 --- /dev/null +++ b/test/grammar/datatype/units/invalid_units_fail_2/stderr.golden.py @@ -0,0 +1,16 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.InvalidSyntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=7, + arg_msg="Expected one of ['newline']" + )], + ), + file=sys.stdout) diff --git a/test/grammar/datatype/units/range_check_fail_0/main.k b/test/grammar/datatype/units/range_check_fail_0/main.k new file mode 100644 index 000000000..f4e6dc5b1 --- /dev/null +++ b/test/grammar/datatype/units/range_check_fail_0/main.k @@ -0,0 +1 @@ +Pi = int(1024Pi) * int(1024Pi) diff --git a/test/grammar/datatype/units/range_check_fail_0/settings.yaml b/test/grammar/datatype/units/range_check_fail_0/settings.yaml new file mode 100644 index 000000000..542d01cf5 --- /dev/null +++ b/test/grammar/datatype/units/range_check_fail_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d diff --git a/test/grammar/datatype/units/range_check_fail_0/stderr.golden.py b/test/grammar/datatype/units/range_check_fail_0/stderr.golden.py new file mode 100644 index 000000000..8685f546b --- /dev/null +++ b/test/grammar/datatype/units/range_check_fail_0/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1 + ), + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(1329227995784915872903807060280344576, 64)) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/units/range_check_fail_1/main.k b/test/grammar/datatype/units/range_check_fail_1/main.k new file mode 100644 index 000000000..3c3965eba --- /dev/null +++ b/test/grammar/datatype/units/range_check_fail_1/main.k @@ -0,0 +1 @@ +Pi = int(1024102410241024Mi) diff --git a/test/grammar/datatype/units/range_check_fail_1/settings.yaml b/test/grammar/datatype/units/range_check_fail_1/settings.yaml new file mode 100644 index 000000000..542d01cf5 --- /dev/null +++ b/test/grammar/datatype/units/range_check_fail_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d diff --git a/test/grammar/datatype/units/range_check_fail_1/stderr.golden.py b/test/grammar/datatype/units/range_check_fail_1/stderr.golden.py new file mode 100644 index 000000000..5b3cac3db --- /dev/null +++ b/test/grammar/datatype/units/range_check_fail_1/stderr.golden.py @@ -0,0 +1,21 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IntOverflow_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1 + ) + ], + arg_msg=kcl_error.INT_OVER_FLOW_MSG.format(1073849208920891981824, 64) + ) + , file=sys.stdout +) + diff --git a/test/grammar/datatype/units/simple_0/main.k b/test/grammar/datatype/units/simple_0/main.k new file mode 100644 index 000000000..a2b4e024e --- /dev/null +++ b/test/grammar/datatype/units/simple_0/main.k @@ -0,0 +1,3 @@ +cpu = "1" +memory = 1024Mi +disk = "10Gi" diff --git a/test/grammar/datatype/units/simple_0/stdout.golden b/test/grammar/datatype/units/simple_0/stdout.golden new file mode 100644 index 000000000..ad174f8d6 --- /dev/null +++ b/test/grammar/datatype/units/simple_0/stdout.golden @@ -0,0 +1,3 @@ +cpu: '1' +memory: 1073741824 +disk: 10Gi diff --git a/test/grammar/datatype/units/simple_1/main.k b/test/grammar/datatype/units/simple_1/main.k new file mode 100644 index 000000000..b867025d1 --- /dev/null +++ b/test/grammar/datatype/units/simple_1/main.k @@ -0,0 +1,16 @@ +# SI +n = 1n +u = 1u +m = 1m +k = 1k +K = 1K +M = 1M +G = 1G +T = 1T +P = 1P +# IEC +Ki = 1Ki +Mi = 1Mi +Gi = 1Gi +Ti = 1Ti +Pi = 1Pi diff --git a/test/grammar/datatype/units/simple_1/stdout.golden b/test/grammar/datatype/units/simple_1/stdout.golden new file mode 100644 index 000000000..72db4978b --- /dev/null +++ b/test/grammar/datatype/units/simple_1/stdout.golden @@ -0,0 +1,14 @@ +n: 1e-09 +u: 1e-06 +m: 0.001 +k: 1000 +K: 1000 +M: 1000000 +G: 1000000000 +T: 1000000000000 +P: 1000000000000000 +Ki: 1024 +Mi: 1048576 +Gi: 1073741824 +Ti: 1099511627776 +Pi: 1125899906842624 diff --git a/test/grammar/datatype/units/simple_2/main.k b/test/grammar/datatype/units/simple_2/main.k new file mode 100644 index 000000000..13135d0a1 --- /dev/null +++ b/test/grammar/datatype/units/simple_2/main.k @@ -0,0 +1,17 @@ + +# SI +n = 1024n +u = 1024u +m = 1024m +k = 1024k +K = 1024K +M = 1024M +G = 1024G +T = 1024T +P = 1024P +# IEC +Ki = 1024Ki +Mi = 1024Mi +Gi = 1024Gi +Ti = 1024Ti +Pi = 1Pi diff --git a/test/grammar/datatype/units/simple_2/stdout.golden b/test/grammar/datatype/units/simple_2/stdout.golden new file mode 100644 index 000000000..343788099 --- /dev/null +++ b/test/grammar/datatype/units/simple_2/stdout.golden @@ -0,0 +1,14 @@ +n: 1.024e-06 +u: 0.001024 +m: 1.024 +k: 1024000 +K: 1024000 +M: 1024000000 +G: 1024000000000 +T: 1024000000000000 +P: 1024000000000000000 +Ki: 1048576 +Mi: 1073741824 +Gi: 1099511627776 +Ti: 1125899906842624 +Pi: 1125899906842624 diff --git a/test/grammar/datatype/units/simple_3/main.k b/test/grammar/datatype/units/simple_3/main.k new file mode 100644 index 000000000..7b2f87a6f --- /dev/null +++ b/test/grammar/datatype/units/simple_3/main.k @@ -0,0 +1,19 @@ + + +import math +# SI +n = float(1024n) +u = str(1024u) +m = int(1024m) +k = bool(1024k) +K = int(1024K) + 10 +M = int(1024M) - 10 +G = int(1024G) * 10 +T = int(1024T) +P = int(1024P) ** 1 +# IEC +Ki = int(1024Ki) // 10 +Mi = int(1024Mi) +Gi = int(1024Gi) +Ti = abs(int(1024Ti)) +Pi = math.floor(int(1Pi)) diff --git a/test/grammar/datatype/units/simple_3/stdout.golden b/test/grammar/datatype/units/simple_3/stdout.golden new file mode 100644 index 000000000..51b163742 --- /dev/null +++ b/test/grammar/datatype/units/simple_3/stdout.golden @@ -0,0 +1,14 @@ +n: 1.024e-06 +u: 1024u +m: 1 +k: true +K: 1024010 +M: 1023999990 +G: 10240000000000 +T: 1024000000000000 +P: 1024000000000000000 +Ki: 104857 +Mi: 1073741824 +Gi: 1099511627776 +Ti: 1125899906842624 +Pi: 1125899906842624 diff --git a/test/grammar/datatype/units/simple_4/main.k b/test/grammar/datatype/units/simple_4/main.k new file mode 100644 index 000000000..86261ff38 --- /dev/null +++ b/test/grammar/datatype/units/simple_4/main.k @@ -0,0 +1,21 @@ +import units + +_data = {k: 1Ki} + +x0: units.NumberMultiplier = 1M +x1: units.NumberMultiplier = x0 +x2: int = int(x0) +x3: float = float(x0) +x4 = x0 if x0 else 1 +x5 = x0 +x6 = [1Ki, 1M][0] +x7 = _data["k"] + +x0str = str(1M) +x1str = str(x0) +x2str = str(int(x0)) +x3str = str(float(x0)) +x4str = str(x0 if x0 else 1) +x5str = "{}".format(x0) +x6str = "${[1Ki, 1M][0]}" +x7str = "${_data: #json}" diff --git a/test/grammar/datatype/units/simple_4/stdout.golden b/test/grammar/datatype/units/simple_4/stdout.golden new file mode 100644 index 000000000..10b6fed8c --- /dev/null +++ b/test/grammar/datatype/units/simple_4/stdout.golden @@ -0,0 +1,16 @@ +x0: 1000000 +x1: 1000000 +x2: 1000000 +x3: 1000000.0 +x4: 1000000 +x5: 1000000 +x6: 1024 +x7: 1024 +x0str: 1M +x1str: 1M +x2str: '1000000' +x3str: '1000000.0' +x4str: 1M +x5str: 1M +x6str: 1Ki +x7str: '{"k": "1Ki"}' diff --git a/test/grammar/datatype/units/simple_5/main.k b/test/grammar/datatype/units/simple_5/main.k new file mode 100644 index 000000000..146c1da00 --- /dev/null +++ b/test/grammar/datatype/units/simple_5/main.k @@ -0,0 +1,3 @@ +import units + +cpu: int | units.NumberMultiplier = 1 diff --git a/test/grammar/datatype/units/simple_5/stdout.golden b/test/grammar/datatype/units/simple_5/stdout.golden new file mode 100644 index 000000000..f0cf2cfb1 --- /dev/null +++ b/test/grammar/datatype/units/simple_5/stdout.golden @@ -0,0 +1 @@ +cpu: 1 diff --git a/test/grammar/expr/braket_expr/main.k b/test/grammar/expr/braket_expr/main.k new file mode 100644 index 000000000..51f6a3ca3 --- /dev/null +++ b/test/grammar/expr/braket_expr/main.k @@ -0,0 +1,3 @@ +a = ((2 + 3) * (2 ** 3)) +b = (2 >> 2) + 3 * 4 + (2 - 1) +# FIXME because precedence of AST operators \ No newline at end of file diff --git a/test/grammar/expr/braket_expr/stdout.golden b/test/grammar/expr/braket_expr/stdout.golden new file mode 100644 index 000000000..8ac7e2bc5 --- /dev/null +++ b/test/grammar/expr/braket_expr/stdout.golden @@ -0,0 +1,2 @@ +a: 40 +b: 13 \ No newline at end of file diff --git a/test/grammar/expr/identifier_prefix/fail_0/main.k b/test/grammar/expr/identifier_prefix/fail_0/main.k new file mode 100644 index 000000000..93d1a1a28 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/fail_0/main.k @@ -0,0 +1,2 @@ +a = 1 +$a = 2 diff --git a/test/grammar/expr/identifier_prefix/fail_0/stderr.golden.py b/test/grammar/expr/identifier_prefix/fail_0/stderr.golden.py new file mode 100644 index 000000000..3f4ae726b --- /dev/null +++ b/test/grammar/expr/identifier_prefix/fail_0/stderr.golden.py @@ -0,0 +1,22 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=1, + end_col_no=3 + + ) + ], + ), + file=sys.stdout +) + diff --git a/test/grammar/expr/identifier_prefix/fail_1/main.k b/test/grammar/expr/identifier_prefix/fail_1/main.k new file mode 100644 index 000000000..54ffc21b3 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/fail_1/main.k @@ -0,0 +1,2 @@ +$a = 1 +a = 2 diff --git a/test/grammar/expr/identifier_prefix/fail_1/stderr.golden.py b/test/grammar/expr/identifier_prefix/fail_1/stderr.golden.py new file mode 100644 index 000000000..c8b15b5a3 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/fail_1/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=1 + ) + ], + ), + file=sys.stdout +) + diff --git a/test/grammar/expr/identifier_prefix/if_0/main.k b/test/grammar/expr/identifier_prefix/if_0/main.k new file mode 100644 index 000000000..ee9e983fe --- /dev/null +++ b/test/grammar/expr/identifier_prefix/if_0/main.k @@ -0,0 +1,15 @@ +$all = 1 +if $all == 1: + $any = 2 +$and = 3 +if $and == 4: + $map = 4 +else: + $filter = 5 +$schema = 6 +if $schema == 5: + $mixin = 6 +elif $schema == 6: + $check = 7 +else: + $relaxed = 8 diff --git a/test/grammar/expr/identifier_prefix/if_0/stdout.golden b/test/grammar/expr/identifier_prefix/if_0/stdout.golden new file mode 100644 index 000000000..f347bb777 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/if_0/stdout.golden @@ -0,0 +1,6 @@ +all: 1 +any: 2 +and: 3 +filter: 5 +schema: 6 +check: 7 diff --git a/test/grammar/expr/identifier_prefix/if_1/main.k b/test/grammar/expr/identifier_prefix/if_1/main.k new file mode 100644 index 000000000..d4bd9f0d6 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/if_1/main.k @@ -0,0 +1,18 @@ +schema $Data: + $all = 1 + if $all == 1: + $any = 2 + $and = 3 + if $and == 4: + $map = 4 + else: + $filter = 5 + $schema = 6 + if $schema == 5: + $mixin = 6 + elif $schema == 6: + $check = 7 + else: + $relaxed = 8 + +data = Data {} diff --git a/test/grammar/expr/identifier_prefix/if_1/stdout.golden b/test/grammar/expr/identifier_prefix/if_1/stdout.golden new file mode 100644 index 000000000..f9a22feb8 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/if_1/stdout.golden @@ -0,0 +1,7 @@ +data: + all: 1 + any: 2 + and: 3 + filter: 5 + schema: 6 + check: 7 diff --git a/test/grammar/expr/identifier_prefix/if_2/main.k b/test/grammar/expr/identifier_prefix/if_2/main.k new file mode 100644 index 000000000..023eac222 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/if_2/main.k @@ -0,0 +1,4 @@ +a = 1 +if $a: + $b = 2 +$c = 3 diff --git a/test/grammar/expr/identifier_prefix/if_2/stdout.golden b/test/grammar/expr/identifier_prefix/if_2/stdout.golden new file mode 100644 index 000000000..894c6553a --- /dev/null +++ b/test/grammar/expr/identifier_prefix/if_2/stdout.golden @@ -0,0 +1,3 @@ +a: 1 +b: 2 +c: 3 diff --git a/test/grammar/expr/identifier_prefix/test_0/main.k b/test/grammar/expr/identifier_prefix/test_0/main.k new file mode 100644 index 000000000..b0c92a2c0 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/test_0/main.k @@ -0,0 +1,27 @@ +$_var = "var0" +_var = "var1" +var = _var +$all = 1 +$any = 2 +$map = 3 +$filter = 4 +$schema = 5 +$mixin = 6 +$import = 7 +$pass = 8 +$in = 9 +$and = 10 +$or = 11 +$is = 12 +$not = 13 +$return = 14 +$check = 15 +$assert = 16 +$if = 17 +$else = 18 +$elif = 19 +$for = 20 +$final = 21 +$relaxed = 22 +$rule = 23 +$def = 24 diff --git a/test/grammar/expr/identifier_prefix/test_0/stdout.golden b/test/grammar/expr/identifier_prefix/test_0/stdout.golden new file mode 100644 index 000000000..4eba98493 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/test_0/stdout.golden @@ -0,0 +1,25 @@ +var: var1 +all: 1 +any: 2 +map: 3 +filter: 4 +schema: 5 +mixin: 6 +import: 7 +pass: 8 +in: 9 +and: 10 +or: 11 +is: 12 +not: 13 +return: 14 +check: 15 +assert: 16 +if: 17 +else: 18 +elif: 19 +for: 20 +final: 21 +relaxed: 22 +rule: 23 +def: 24 diff --git a/test/grammar/expr/identifier_prefix/test_1/main.k b/test/grammar/expr/identifier_prefix/test_1/main.k new file mode 100644 index 000000000..b446a0897 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/test_1/main.k @@ -0,0 +1,27 @@ +schema Data: + $all: int = 1 + $any: int = 2 + $map: int = 3 + $filter: int = 4 + $schema: int = 5 + $mixin: int = 6 + $import: int = 7 + $pass: int = 8 + $in: int = 9 + $and: int = 10 + $or: int = 11 + $is: int = 12 + $not: int = 13 + $return: int = 14 + $check: int = 15 + $assert: int = 16 + $if: int = 17 + $else: int = 18 + $elif: int = 19 + $for: int = 20 + $final: int = 21 + $relaxed: int = 22 + $rule: int = 23 + $def: int = 24 + +data = Data {} diff --git a/test/grammar/expr/identifier_prefix/test_1/stdout.golden b/test/grammar/expr/identifier_prefix/test_1/stdout.golden new file mode 100644 index 000000000..5b57a91b6 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/test_1/stdout.golden @@ -0,0 +1,25 @@ +data: + all: 1 + any: 2 + map: 3 + filter: 4 + schema: 5 + mixin: 6 + import: 7 + pass: 8 + in: 9 + and: 10 + or: 11 + is: 12 + not: 13 + return: 14 + check: 15 + assert: 16 + if: 17 + else: 18 + elif: 19 + for: 20 + final: 21 + relaxed: 22 + rule: 23 + def: 24 diff --git a/test/grammar/expr/identifier_prefix/test_2/main.k b/test/grammar/expr/identifier_prefix/test_2/main.k new file mode 100644 index 000000000..0ef6e5f76 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/test_2/main.k @@ -0,0 +1,52 @@ +schema Data: + $all: int + $any: int + $map: int + $filter: int + $schema: int + $mixin: int + $import: int + $pass: int + $in: int + $and: int + $or: int + $is: int + $not: int + $return: int + $check: int + $assert: int + $if: int + $else: int + $elif: int + $for: int + $final: int + $relaxed: int + $rule: int + $def: int + +data = Data { + $all: 1 + $any: 2 + $map: 3 + $filter: 4 + $schema: 5 + $mixin: 6 + $import: 7 + $pass: 8 + $in: 9 + $and: 10 + $or: 11 + $is: 12 + $not: 13 + $return: 14 + $check: 15 + $assert: 16 + $if: 17 + $else: 18 + $elif: 19 + $for: 20 + $final: 21 + $relaxed: 22 + $rule: 23 + $def: 24 +} diff --git a/test/grammar/expr/identifier_prefix/test_2/stdout.golden b/test/grammar/expr/identifier_prefix/test_2/stdout.golden new file mode 100644 index 000000000..5b57a91b6 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/test_2/stdout.golden @@ -0,0 +1,25 @@ +data: + all: 1 + any: 2 + map: 3 + filter: 4 + schema: 5 + mixin: 6 + import: 7 + pass: 8 + in: 9 + and: 10 + or: 11 + is: 12 + not: 13 + return: 14 + check: 15 + assert: 16 + if: 17 + else: 18 + elif: 19 + for: 20 + final: 21 + relaxed: 22 + rule: 23 + def: 24 diff --git a/test/grammar/expr/identifier_prefix/test_3/main.k b/test/grammar/expr/identifier_prefix/test_3/main.k new file mode 100644 index 000000000..9989b5898 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/test_3/main.k @@ -0,0 +1,3 @@ +$if = 1 +$else = "s" +a = 1 diff --git a/test/grammar/expr/identifier_prefix/test_3/stdout.golden b/test/grammar/expr/identifier_prefix/test_3/stdout.golden new file mode 100644 index 000000000..193c6c651 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/test_3/stdout.golden @@ -0,0 +1,3 @@ +if: 1 +else: s +a: 1 diff --git a/test/grammar/expr/identifier_prefix/test_4/main.k b/test/grammar/expr/identifier_prefix/test_4/main.k new file mode 100644 index 000000000..89571aa98 --- /dev/null +++ b/test/grammar/expr/identifier_prefix/test_4/main.k @@ -0,0 +1,7 @@ +$if = 1 +$else = "s" + +schema Data: + $filter: str = "filter" + +data = Data {} diff --git a/test/grammar/expr/identifier_prefix/test_4/stdout.golden b/test/grammar/expr/identifier_prefix/test_4/stdout.golden new file mode 100644 index 000000000..3acb6920f --- /dev/null +++ b/test/grammar/expr/identifier_prefix/test_4/stdout.golden @@ -0,0 +1,4 @@ +if: 1 +else: s +data: + filter: filter diff --git a/test/grammar/expr/logic_expr/test_0/main.k b/test/grammar/expr/logic_expr/test_0/main.k new file mode 100644 index 000000000..42d3bf2d8 --- /dev/null +++ b/test/grammar/expr/logic_expr/test_0/main.k @@ -0,0 +1,6 @@ +data = None +if data and "key" in data: + _temp = 1 +else: + _temp = 2 +temp = _temp diff --git a/test/grammar/expr/logic_expr/test_0/stdout.golden b/test/grammar/expr/logic_expr/test_0/stdout.golden new file mode 100644 index 000000000..65ac12546 --- /dev/null +++ b/test/grammar/expr/logic_expr/test_0/stdout.golden @@ -0,0 +1,2 @@ +data: null +temp: 2 diff --git a/test/grammar/expr/logic_expr/test_1/main.k b/test/grammar/expr/logic_expr/test_1/main.k new file mode 100644 index 000000000..291cd1398 --- /dev/null +++ b/test/grammar/expr/logic_expr/test_1/main.k @@ -0,0 +1,6 @@ +data = {"key": "value"} +if data and "key" in data: + _temp = data["key"] +else: + _temp = "other_value" +temp = _temp diff --git a/test/grammar/expr/logic_expr/test_1/stdout.golden b/test/grammar/expr/logic_expr/test_1/stdout.golden new file mode 100644 index 000000000..00a0bbebe --- /dev/null +++ b/test/grammar/expr/logic_expr/test_1/stdout.golden @@ -0,0 +1,3 @@ +data: + key: value +temp: value diff --git a/test/grammar/expr/logic_expr/test_2/main.k b/test/grammar/expr/logic_expr/test_2/main.k new file mode 100644 index 000000000..ba3333f69 --- /dev/null +++ b/test/grammar/expr/logic_expr/test_2/main.k @@ -0,0 +1,6 @@ +data = {} +if (data and "key" in data) or True: + _temp = str(data["key"]) +else: + _temp = "other_value" +temp = _temp diff --git a/test/grammar/expr/logic_expr/test_2/stdout.golden b/test/grammar/expr/logic_expr/test_2/stdout.golden new file mode 100644 index 000000000..a239c4a12 --- /dev/null +++ b/test/grammar/expr/logic_expr/test_2/stdout.golden @@ -0,0 +1,2 @@ +data: {} +temp: Undefined diff --git a/test/grammar/expr/paren_expr/test_0/main.k b/test/grammar/expr/paren_expr/test_0/main.k new file mode 100644 index 000000000..fc6e72ab2 --- /dev/null +++ b/test/grammar/expr/paren_expr/test_0/main.k @@ -0,0 +1,3 @@ +a = (0) +b = ((1)) +c = (((2))) diff --git a/test/grammar/expr/paren_expr/test_0/stdout.golden b/test/grammar/expr/paren_expr/test_0/stdout.golden new file mode 100644 index 000000000..1f807e17d --- /dev/null +++ b/test/grammar/expr/paren_expr/test_0/stdout.golden @@ -0,0 +1,3 @@ +a: 0 +b: 1 +c: 2 diff --git a/test/grammar/expr/paren_expr/test_1/main.k b/test/grammar/expr/paren_expr/test_1/main.k new file mode 100644 index 000000000..b9b07b612 --- /dev/null +++ b/test/grammar/expr/paren_expr/test_1/main.k @@ -0,0 +1,3 @@ +a = (0 + 1) +b = ((1 * 2)) +c = (((3 - 4))) diff --git a/test/grammar/expr/paren_expr/test_1/stdout.golden b/test/grammar/expr/paren_expr/test_1/stdout.golden new file mode 100644 index 000000000..0407ba1a4 --- /dev/null +++ b/test/grammar/expr/paren_expr/test_1/stdout.golden @@ -0,0 +1,3 @@ +a: 1 +b: 2 +c: -1 diff --git a/test/grammar/expr/paren_expr/test_2/main.k b/test/grammar/expr/paren_expr/test_2/main.k new file mode 100644 index 000000000..4dd33e303 --- /dev/null +++ b/test/grammar/expr/paren_expr/test_2/main.k @@ -0,0 +1,3 @@ +a = ({}).key1 or 1 +b = ({}.key2) or 2 +c = ({}.key3 or (3)) diff --git a/test/grammar/expr/paren_expr/test_2/stdout.golden b/test/grammar/expr/paren_expr/test_2/stdout.golden new file mode 100644 index 000000000..894c6553a --- /dev/null +++ b/test/grammar/expr/paren_expr/test_2/stdout.golden @@ -0,0 +1,3 @@ +a: 1 +b: 2 +c: 3 diff --git a/test/grammar/expr/paren_expr/test_3/main.k b/test/grammar/expr/paren_expr/test_3/main.k new file mode 100644 index 000000000..a514becab --- /dev/null +++ b/test/grammar/expr/paren_expr/test_3/main.k @@ -0,0 +1,3 @@ +a = ([])?[0] or 1 +b = ([]?[0]) or 2 +c = ([]?[0] or (3)) diff --git a/test/grammar/expr/paren_expr/test_3/stdout.golden b/test/grammar/expr/paren_expr/test_3/stdout.golden new file mode 100644 index 000000000..894c6553a --- /dev/null +++ b/test/grammar/expr/paren_expr/test_3/stdout.golden @@ -0,0 +1,3 @@ +a: 1 +b: 2 +c: 3 diff --git a/test/grammar/expr/select_expr/in_for_0/main.k b/test/grammar/expr/select_expr/in_for_0/main.k new file mode 100644 index 000000000..2605dbb55 --- /dev/null +++ b/test/grammar/expr/select_expr/in_for_0/main.k @@ -0,0 +1,7 @@ +c = [ + container | { + volumeMounts = [ + {} for mount in [{"container" = "c"}] if mount.container == container.name + ] + } for container in [{"name" = "main"}] +] diff --git a/test/grammar/expr/select_expr/in_for_0/stdout.golden b/test/grammar/expr/select_expr/in_for_0/stdout.golden new file mode 100644 index 000000000..a13ffd4bb --- /dev/null +++ b/test/grammar/expr/select_expr/in_for_0/stdout.golden @@ -0,0 +1,2 @@ +c: +- name: main diff --git a/test/grammar/expr/select_expr/in_for_1/main.k b/test/grammar/expr/select_expr/in_for_1/main.k new file mode 100644 index 000000000..fde942424 --- /dev/null +++ b/test/grammar/expr/select_expr/in_for_1/main.k @@ -0,0 +1,7 @@ +c = [ + container | { + volumeMounts = [ + {} for mount in [{"container" = {"name" = "main"}}] if mount.container.name == container.name + ] + } for container in [{"name" = "main"}] +] diff --git a/test/grammar/expr/select_expr/in_for_1/stdout.golden b/test/grammar/expr/select_expr/in_for_1/stdout.golden new file mode 100644 index 000000000..9fe1c683e --- /dev/null +++ b/test/grammar/expr/select_expr/in_for_1/stdout.golden @@ -0,0 +1,4 @@ +c: +- name: main + volumeMounts: + - {} diff --git a/test/grammar/expr/select_expr/optional/complex_0/main.k b/test/grammar/expr/select_expr/optional/complex_0/main.k new file mode 100644 index 000000000..caf482dbc --- /dev/null +++ b/test/grammar/expr/select_expr/optional/complex_0/main.k @@ -0,0 +1,10 @@ +schema Person: + name: str + +person = Person {name: "Alice"} +data = {"name": {"key": "value"}} +name1 = person?.name?.lower() # Alice +name2 = data["name"]["key"]?.upper()?.lower() +datalist = [[1], 2, 3] +item = list(datalist?[0])?[0] +name3 = data?["name"]?["err_key"]?.lower() or data?["name"]?["err_key"]?.upper() or "name3_value" diff --git a/test/grammar/expr/select_expr/optional/complex_0/stdout.golden b/test/grammar/expr/select_expr/optional/complex_0/stdout.golden new file mode 100644 index 000000000..ad6880e42 --- /dev/null +++ b/test/grammar/expr/select_expr/optional/complex_0/stdout.golden @@ -0,0 +1,13 @@ +person: + name: Alice +data: + name: + key: value +name1: alice +name2: value +datalist: +- - 1 +- 2 +- 3 +item: 1 +name3: name3_value diff --git a/test/grammar/expr/select_expr/optional/complex_1/main.k b/test/grammar/expr/select_expr/optional/complex_1/main.k new file mode 100644 index 000000000..550463135 --- /dev/null +++ b/test/grammar/expr/select_expr/optional/complex_1/main.k @@ -0,0 +1,12 @@ +schema Person: + name: str + env: [{str:}] = [{ + "key1": "value1" + }, { + "key2": "value2" + }] + data: {str:} = {"name": {"key": "value"}} + value: str = env?[0].key1 + env?[1].key2 + +person = Person {name: "Alice"} +name = person?.env?[0].key1 + person?.env?[1].key2 diff --git a/test/grammar/expr/select_expr/optional/complex_1/stdout.golden b/test/grammar/expr/select_expr/optional/complex_1/stdout.golden new file mode 100644 index 000000000..8ee78d366 --- /dev/null +++ b/test/grammar/expr/select_expr/optional/complex_1/stdout.golden @@ -0,0 +1,10 @@ +person: + name: Alice + env: + - key1: value1 + - key2: value2 + data: + name: + key: value + value: value1value2 +name: value1value2 diff --git a/test/grammar/expr/select_expr/optional/simple_0/main.k b/test/grammar/expr/select_expr/optional/simple_0/main.k new file mode 100644 index 000000000..9c7f41d1d --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_0/main.k @@ -0,0 +1,8 @@ +schema Person: + name: str + +person = Person {name: "Alice"} +data = {} +name1 = person?.name +name2 = data["key"]?.name +name3 = name1?.lower() or "" diff --git a/test/grammar/expr/select_expr/optional/simple_0/stdout.golden b/test/grammar/expr/select_expr/optional/simple_0/stdout.golden new file mode 100644 index 000000000..66fe7617e --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_0/stdout.golden @@ -0,0 +1,6 @@ +person: + name: Alice +data: {} +name1: Alice +name2: null +name3: alice diff --git a/test/grammar/expr/select_expr/optional/simple_1/main.k b/test/grammar/expr/select_expr/optional/simple_1/main.k new file mode 100644 index 000000000..36c0280b8 --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_1/main.k @@ -0,0 +1,7 @@ +schema Person: + name: str + +person = Person {name: "Alice"} +data = {} +name1 = person?.name +name2 = name1?.lower()?.upper()[:3] or "" diff --git a/test/grammar/expr/select_expr/optional/simple_1/stdout.golden b/test/grammar/expr/select_expr/optional/simple_1/stdout.golden new file mode 100644 index 000000000..bd133a88b --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_1/stdout.golden @@ -0,0 +1,5 @@ +person: + name: Alice +data: {} +name1: Alice +name2: ALI diff --git a/test/grammar/expr/select_expr/optional/simple_2/main.k b/test/grammar/expr/select_expr/optional/simple_2/main.k new file mode 100644 index 000000000..02d100244 --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_2/main.k @@ -0,0 +1,8 @@ +schema Person: + name: str + +person = Person {name: "Alice"} +data = {"name": {"key": "value"}} +name1 = person?.name?.lower() # alice +name2 = data["name"]?["key"]?.upper() +# name3 = data["name"]["err_key"]?.upper() diff --git a/test/grammar/expr/select_expr/optional/simple_2/stdout.golden b/test/grammar/expr/select_expr/optional/simple_2/stdout.golden new file mode 100644 index 000000000..9f7371f16 --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_2/stdout.golden @@ -0,0 +1,7 @@ +person: + name: Alice +data: + name: + key: value +name1: alice +name2: VALUE diff --git a/test/grammar/expr/select_expr/optional/simple_3/main.k b/test/grammar/expr/select_expr/optional/simple_3/main.k new file mode 100644 index 000000000..62a1169e5 --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_3/main.k @@ -0,0 +1,7 @@ +schema Person: + name: str + +person = Person {name: "Alice"} +data = {"name": {"key": "value"}} +name1 = person?.name?.lower() # alice +name2 = data["name"]["key"]?.upper()?.lower()[:3] diff --git a/test/grammar/expr/select_expr/optional/simple_3/stdout.golden b/test/grammar/expr/select_expr/optional/simple_3/stdout.golden new file mode 100644 index 000000000..999e7ba37 --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_3/stdout.golden @@ -0,0 +1,7 @@ +person: + name: Alice +data: + name: + key: value +name1: alice +name2: val diff --git a/test/grammar/expr/select_expr/optional/simple_4/main.k b/test/grammar/expr/select_expr/optional/simple_4/main.k new file mode 100644 index 000000000..3a2fc4dfd --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_4/main.k @@ -0,0 +1,8 @@ +schema Person: + name: str + +person = Person {name: "Alice"} +data = {"name": {"key": "value"}} +name1 = person?.name?.lower() # alice +name2 = data["name"]["key"]?.upper()?.lower() +name3 = data["name"]["err_key"]?.upper() or "name3_value" diff --git a/test/grammar/expr/select_expr/optional/simple_4/stdout.golden b/test/grammar/expr/select_expr/optional/simple_4/stdout.golden new file mode 100644 index 000000000..1d17a3d4d --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_4/stdout.golden @@ -0,0 +1,8 @@ +person: + name: Alice +data: + name: + key: value +name1: alice +name2: value +name3: name3_value diff --git a/test/grammar/expr/select_expr/optional/simple_5/main.k b/test/grammar/expr/select_expr/optional/simple_5/main.k new file mode 100644 index 000000000..3a2fc4dfd --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_5/main.k @@ -0,0 +1,8 @@ +schema Person: + name: str + +person = Person {name: "Alice"} +data = {"name": {"key": "value"}} +name1 = person?.name?.lower() # alice +name2 = data["name"]["key"]?.upper()?.lower() +name3 = data["name"]["err_key"]?.upper() or "name3_value" diff --git a/test/grammar/expr/select_expr/optional/simple_5/stdout.golden b/test/grammar/expr/select_expr/optional/simple_5/stdout.golden new file mode 100644 index 000000000..1d17a3d4d --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_5/stdout.golden @@ -0,0 +1,8 @@ +person: + name: Alice +data: + name: + key: value +name1: alice +name2: value +name3: name3_value diff --git a/test/grammar/expr/select_expr/optional/simple_6/main.k b/test/grammar/expr/select_expr/optional/simple_6/main.k new file mode 100644 index 000000000..daee0cbf4 --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_6/main.k @@ -0,0 +1,9 @@ +listData = None +listItem0 = listData?[0] +listItem1 = listData?[1] +dictData0 = {} +dictItem0 = dictData0?["key"] +dictItem1 = dictData0?["key1"] +dictData1 = {"key1": "value1", "key2": "value2"} +dictItem2 = dictData1?["key1"] +dictItem3 = dictData1?["key2"] diff --git a/test/grammar/expr/select_expr/optional/simple_6/stdout.golden b/test/grammar/expr/select_expr/optional/simple_6/stdout.golden new file mode 100644 index 000000000..febcfa03a --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_6/stdout.golden @@ -0,0 +1,11 @@ +listData: null +listItem0: null +listItem1: null +dictData0: {} +dictItem0: null +dictItem1: null +dictData1: + key1: value1 + key2: value2 +dictItem2: value1 +dictItem3: value2 diff --git a/test/grammar/expr/select_expr/optional/simple_7/main.k b/test/grammar/expr/select_expr/optional/simple_7/main.k new file mode 100644 index 000000000..05797be12 --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_7/main.k @@ -0,0 +1,13 @@ +data = { + x = { + y = { + z = { + k = 1 + } + } + } +} +k0 = data?.x?.y.z.k +k1 = data.x.y.z.k +k2 = data.x?.y.z.k +k3 = data.x.y.z.k diff --git a/test/grammar/expr/select_expr/optional/simple_7/stdout.golden b/test/grammar/expr/select_expr/optional/simple_7/stdout.golden new file mode 100644 index 000000000..88a48b02c --- /dev/null +++ b/test/grammar/expr/select_expr/optional/simple_7/stdout.golden @@ -0,0 +1,9 @@ +data: + x: + y: + z: + k: 1 +k0: 1 +k1: 1 +k2: 1 +k3: 1 diff --git a/test/grammar/expr/sub_expr/in_schema_0/main.k b/test/grammar/expr/sub_expr/in_schema_0/main.k new file mode 100644 index 000000000..2943e3287 --- /dev/null +++ b/test/grammar/expr/sub_expr/in_schema_0/main.k @@ -0,0 +1,5 @@ +schema Person: + name: str = "Alice" + nameLower: str = str(name).lower().upper() + +person = Person {} diff --git a/test/grammar/expr/sub_expr/in_schema_0/stdout.golden b/test/grammar/expr/sub_expr/in_schema_0/stdout.golden new file mode 100644 index 000000000..b9a79cd0d --- /dev/null +++ b/test/grammar/expr/sub_expr/in_schema_0/stdout.golden @@ -0,0 +1,3 @@ +person: + name: Alice + nameLower: ALICE diff --git a/test/grammar/expr/sub_expr/in_schema_1/main.k b/test/grammar/expr/sub_expr/in_schema_1/main.k new file mode 100644 index 000000000..9abdb157e --- /dev/null +++ b/test/grammar/expr/sub_expr/in_schema_1/main.k @@ -0,0 +1,6 @@ +lists = ["Alice", "Bob", "Green"] + +schema Person: + name: str = lists[0].upper()[1] + +person = Person {} diff --git a/test/grammar/expr/sub_expr/in_schema_1/stdout.golden b/test/grammar/expr/sub_expr/in_schema_1/stdout.golden new file mode 100644 index 000000000..0d20bda09 --- /dev/null +++ b/test/grammar/expr/sub_expr/in_schema_1/stdout.golden @@ -0,0 +1,6 @@ +lists: +- Alice +- Bob +- Green +person: + name: L diff --git a/test/grammar/expr/sub_expr/in_schema_2/main.k b/test/grammar/expr/sub_expr/in_schema_2/main.k new file mode 100644 index 000000000..42f656887 --- /dev/null +++ b/test/grammar/expr/sub_expr/in_schema_2/main.k @@ -0,0 +1,8 @@ +schema Info: + lists: [str] = ["Alice", "Bob", "Green"] + name: str = lists[0].upper()[0:3] + dicts: {str:} = {"key": "value + {}"} + val: str = dicts["key"].lower().format(str(lists[0].upper().lower()[0:3]).upper().count('A')) + val_temp: str = str(lists[1]).upper() + +info = Info {} diff --git a/test/grammar/expr/sub_expr/in_schema_2/stdout.golden b/test/grammar/expr/sub_expr/in_schema_2/stdout.golden new file mode 100644 index 000000000..4f75d2a13 --- /dev/null +++ b/test/grammar/expr/sub_expr/in_schema_2/stdout.golden @@ -0,0 +1,10 @@ +info: + lists: + - Alice + - Bob + - Green + name: ALI + dicts: + key: value + {} + val: value + 1 + val_temp: BOB diff --git a/test/grammar/expr/sub_expr/test_0/main.k b/test/grammar/expr/sub_expr/test_0/main.k new file mode 100644 index 000000000..c93aaeffc --- /dev/null +++ b/test/grammar/expr/sub_expr/test_0/main.k @@ -0,0 +1,2 @@ +lists = ["Alice", "Bob", "Green"] +name = lists[0].upper()[1] diff --git a/test/grammar/expr/sub_expr/test_0/stdout.golden b/test/grammar/expr/sub_expr/test_0/stdout.golden new file mode 100644 index 000000000..1834bf633 --- /dev/null +++ b/test/grammar/expr/sub_expr/test_0/stdout.golden @@ -0,0 +1,5 @@ +lists: +- Alice +- Bob +- Green +name: L diff --git a/test/grammar/expr/sub_expr/test_1/main.k b/test/grammar/expr/sub_expr/test_1/main.k new file mode 100644 index 000000000..78caf9ba9 --- /dev/null +++ b/test/grammar/expr/sub_expr/test_1/main.k @@ -0,0 +1,5 @@ +lists = ["Alice", "Bob", "Green"] +name = lists[0].upper()[0:3] +dicts = {"key": "value + {}"} +val = dicts["key"].lower().format(str(lists[0].upper().lower()[0:3]).upper().count('A')) +val_temp = str(lists[1]).upper() diff --git a/test/grammar/expr/sub_expr/test_1/stdout.golden b/test/grammar/expr/sub_expr/test_1/stdout.golden new file mode 100644 index 000000000..cb4a4d7fd --- /dev/null +++ b/test/grammar/expr/sub_expr/test_1/stdout.golden @@ -0,0 +1,9 @@ +lists: +- Alice +- Bob +- Green +name: ALI +dicts: + key: value + {} +val: value + 1 +val_temp: BOB diff --git a/test/grammar/if/if_expr/test_0/main.k b/test/grammar/if/if_expr/test_0/main.k new file mode 100644 index 000000000..9c66f0038 --- /dev/null +++ b/test/grammar/if/if_expr/test_0/main.k @@ -0,0 +1,4 @@ + +a = 10 +_condition = 1 if a == 10 else 2 +condition = _condition diff --git a/test/grammar/if/if_expr/test_0/stdout.golden b/test/grammar/if/if_expr/test_0/stdout.golden new file mode 100644 index 000000000..30b6ed105 --- /dev/null +++ b/test/grammar/if/if_expr/test_0/stdout.golden @@ -0,0 +1,2 @@ +a: 10 +condition: 1 diff --git a/test/grammar/if/if_expr/test_1/main.k b/test/grammar/if/if_expr/test_1/main.k new file mode 100644 index 000000000..7bdfa67e3 --- /dev/null +++ b/test/grammar/if/if_expr/test_1/main.k @@ -0,0 +1,4 @@ + +a = 10 +_condition = 1 if a == 11 else (2 if a == 10 else 3) +condition = _condition diff --git a/test/grammar/if/if_expr/test_1/stdout.golden b/test/grammar/if/if_expr/test_1/stdout.golden new file mode 100644 index 000000000..af07e6178 --- /dev/null +++ b/test/grammar/if/if_expr/test_1/stdout.golden @@ -0,0 +1,2 @@ +a: 10 +condition: 2 diff --git a/test/grammar/if/if_expr/test_2/main.k b/test/grammar/if/if_expr/test_2/main.k new file mode 100644 index 000000000..f91680bdc --- /dev/null +++ b/test/grammar/if/if_expr/test_2/main.k @@ -0,0 +1,5 @@ + +a = 10 +b = 12 +_condition = 1 if a == 11 or b == 13 else (2 if a == 10 and b == 12 else 0) +condition = _condition diff --git a/test/grammar/if/if_expr/test_2/stdout.golden b/test/grammar/if/if_expr/test_2/stdout.golden new file mode 100644 index 000000000..1c3466810 --- /dev/null +++ b/test/grammar/if/if_expr/test_2/stdout.golden @@ -0,0 +1,3 @@ +a: 10 +b: 12 +condition: 2 diff --git a/test/grammar/if/if_stmt/test_0/main.k b/test/grammar/if/if_stmt/test_0/main.k new file mode 100644 index 000000000..312488f57 --- /dev/null +++ b/test/grammar/if/if_stmt/test_0/main.k @@ -0,0 +1,8 @@ + +a = 10 +_condition = 0 +if a == 10: + _condition = 1 +else: + _condition = 2 +condition = _condition diff --git a/test/grammar/if/if_stmt/test_0/stdout.golden b/test/grammar/if/if_stmt/test_0/stdout.golden new file mode 100644 index 000000000..30b6ed105 --- /dev/null +++ b/test/grammar/if/if_stmt/test_0/stdout.golden @@ -0,0 +1,2 @@ +a: 10 +condition: 1 diff --git a/test/grammar/if/if_stmt/test_1/main.k b/test/grammar/if/if_stmt/test_1/main.k new file mode 100644 index 000000000..adaceef32 --- /dev/null +++ b/test/grammar/if/if_stmt/test_1/main.k @@ -0,0 +1,10 @@ + +a = 10 +_condition = 0 +if a == 11: + _condition = 1 +elif a == 10: + _condition = 2 +else: + _condition = 3 +condition = _condition diff --git a/test/grammar/if/if_stmt/test_1/stdout.golden b/test/grammar/if/if_stmt/test_1/stdout.golden new file mode 100644 index 000000000..af07e6178 --- /dev/null +++ b/test/grammar/if/if_stmt/test_1/stdout.golden @@ -0,0 +1,2 @@ +a: 10 +condition: 2 diff --git a/test/grammar/if/if_stmt/test_2/main.k b/test/grammar/if/if_stmt/test_2/main.k new file mode 100644 index 000000000..ccf4f858e --- /dev/null +++ b/test/grammar/if/if_stmt/test_2/main.k @@ -0,0 +1,9 @@ + +a = 10 +b = 12 +_condition = 0 +if a == 11 or b == 13: + _condition = 1 +elif a == 10 and b == 12: + _condition = 2 +condition = _condition diff --git a/test/grammar/if/if_stmt/test_2/stdout.golden b/test/grammar/if/if_stmt/test_2/stdout.golden new file mode 100644 index 000000000..1c3466810 --- /dev/null +++ b/test/grammar/if/if_stmt/test_2/stdout.golden @@ -0,0 +1,3 @@ +a: 10 +b: 12 +condition: 2 diff --git a/test/grammar/if/if_stmt/test_3/main.k b/test/grammar/if/if_stmt/test_3/main.k new file mode 100644 index 000000000..64e892fc3 --- /dev/null +++ b/test/grammar/if/if_stmt/test_3/main.k @@ -0,0 +1,7 @@ + +a = 10 +b = 12 +_condition = 0 +if a == 11 or b == 13: _condition = 1 +elif a == 10 and b == 12: _condition = 2 +condition = _condition diff --git a/test/grammar/if/if_stmt/test_3/stdout.golden b/test/grammar/if/if_stmt/test_3/stdout.golden new file mode 100644 index 000000000..1c3466810 --- /dev/null +++ b/test/grammar/if/if_stmt/test_3/stdout.golden @@ -0,0 +1,3 @@ +a: 10 +b: 12 +condition: 2 diff --git a/test/grammar/import/builtin/main.k b/test/grammar/import/builtin/main.k new file mode 100644 index 000000000..adf613519 --- /dev/null +++ b/test/grammar/import/builtin/main.k @@ -0,0 +1,3 @@ +import math + +two = math.log10(100) diff --git a/test/grammar/import/builtin/stdout.golden b/test/grammar/import/builtin/stdout.golden new file mode 100644 index 000000000..a7d2cbd37 --- /dev/null +++ b/test/grammar/import/builtin/stdout.golden @@ -0,0 +1 @@ +two: 2.0 diff --git a/test/grammar/import/builtin_import_as/main.k b/test/grammar/import/builtin_import_as/main.k new file mode 100644 index 000000000..d246cf750 --- /dev/null +++ b/test/grammar/import/builtin_import_as/main.k @@ -0,0 +1,3 @@ +import math as mt + +two = mt.log10(100) diff --git a/test/grammar/import/builtin_import_as/stdout.golden b/test/grammar/import/builtin_import_as/stdout.golden new file mode 100644 index 000000000..a7d2cbd37 --- /dev/null +++ b/test/grammar/import/builtin_import_as/stdout.golden @@ -0,0 +1 @@ +two: 2.0 diff --git a/test/grammar/import/empty_file_import/kcl.mod b/test/grammar/import/empty_file_import/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/empty_file_import/main.k b/test/grammar/import/empty_file_import/main.k new file mode 100644 index 000000000..9f30b939f --- /dev/null +++ b/test/grammar/import/empty_file_import/main.k @@ -0,0 +1,3 @@ +import pkg + +a = 1 diff --git a/test/grammar/import/empty_file_import/pkg/empty.k b/test/grammar/import/empty_file_import/pkg/empty.k new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/empty_file_import/stdout.golden b/test/grammar/import/empty_file_import/stdout.golden new file mode 100644 index 000000000..a016ac454 --- /dev/null +++ b/test/grammar/import/empty_file_import/stdout.golden @@ -0,0 +1 @@ +a: 1 \ No newline at end of file diff --git a/test/grammar/import/empty_import_fail/kcl.mod b/test/grammar/import/empty_import_fail/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/empty_import_fail/main.k b/test/grammar/import/empty_import_fail/main.k new file mode 100644 index 000000000..e26158c59 --- /dev/null +++ b/test/grammar/import/empty_import_fail/main.k @@ -0,0 +1,6 @@ +import pkg_empty + +a = 1 +b = 1.2 +c = [1.3] +d = {"1.4": 1.5} diff --git a/test/grammar/import/empty_import_fail/pkg_empty/1.txt b/test/grammar/import/empty_import_fail/pkg_empty/1.txt new file mode 100644 index 000000000..fd7271a52 --- /dev/null +++ b/test/grammar/import/empty_import_fail/pkg_empty/1.txt @@ -0,0 +1 @@ +kcl \ No newline at end of file diff --git a/test/grammar/import/empty_import_fail/stderr.golden.py b/test/grammar/import/empty_import_fail/stderr.golden.py new file mode 100644 index 000000000..4bfd2c927 --- /dev/null +++ b/test/grammar/import/empty_import_fail/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) +modulename = 'pkg_empty' + +location = os.path.join(cwd, modulename) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CannotFindModule_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=1, + end_col_no=17 + ) + ], + arg_msg=kcl_error.CANNOT_FIND_MODULE_MSG.format(modulename, '{}'.format(location)) + ), + file=sys.stdout +) diff --git a/test/grammar/import/import_abs_fail_0/app-main/main.k b/test/grammar/import/import_abs_fail_0/app-main/main.k new file mode 100644 index 000000000..b06a4ce0f --- /dev/null +++ b/test/grammar/import/import_abs_fail_0/app-main/main.k @@ -0,0 +1,3 @@ +import .some0.pkg1 as some00 # some0 not found in app-main package + +Name1 = some00.Name # some0.pkg1.name diff --git a/test/grammar/import/import_abs_fail_0/app-main/some1/pkg1/pkg1.k b/test/grammar/import/import_abs_fail_0/app-main/some1/pkg1/pkg1.k new file mode 100644 index 000000000..3fcc664c9 --- /dev/null +++ b/test/grammar/import/import_abs_fail_0/app-main/some1/pkg1/pkg1.k @@ -0,0 +1 @@ +Name = '.some1.pkg1.name' diff --git a/test/grammar/import/import_abs_fail_0/app-main/stderr.golden.py b/test/grammar/import/import_abs_fail_0/app-main/stderr.golden.py new file mode 100644 index 000000000..0c3f64f19 --- /dev/null +++ b/test/grammar/import/import_abs_fail_0/app-main/stderr.golden.py @@ -0,0 +1,25 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) +modulename = './some0/pkg1' + +packagename = os.path.abspath(os.path.join(cwd, modulename)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CannotFindModule_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=1, + end_col_no=29 + ) + ], + arg_msg=kcl_error.CANNOT_FIND_MODULE_MSG.format(".some0.pkg1", packagename) + ), + file=sys.stdout +) + diff --git a/test/grammar/import/import_abs_fail_0/kcl.mod b/test/grammar/import/import_abs_fail_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_abs_fail_0/some0/pkg1/pkg1.k b/test/grammar/import/import_abs_fail_0/some0/pkg1/pkg1.k new file mode 100644 index 000000000..6ddf6bada --- /dev/null +++ b/test/grammar/import/import_abs_fail_0/some0/pkg1/pkg1.k @@ -0,0 +1 @@ +Name = 'some0.pkg1.name' diff --git a/test/grammar/import/import_abs_fail_0/some1/pkg1/pkg1.k b/test/grammar/import/import_abs_fail_0/some1/pkg1/pkg1.k new file mode 100644 index 000000000..6e08e1de8 --- /dev/null +++ b/test/grammar/import/import_abs_fail_0/some1/pkg1/pkg1.k @@ -0,0 +1 @@ +Name = 'some1.pkg1.name' diff --git a/test/grammar/import/import_abs_fail_1/app-main/main.k b/test/grammar/import/import_abs_fail_1/app-main/main.k new file mode 100644 index 000000000..a9bac6f14 --- /dev/null +++ b/test/grammar/import/import_abs_fail_1/app-main/main.k @@ -0,0 +1,3 @@ +import ...some0.pkg1 as some00 # some0 not found in import package + +Name1 = some00.Name # some0.pkg1.name diff --git a/test/grammar/import/import_abs_fail_1/app-main/some1/pkg1/pkg1.k b/test/grammar/import/import_abs_fail_1/app-main/some1/pkg1/pkg1.k new file mode 100644 index 000000000..3fcc664c9 --- /dev/null +++ b/test/grammar/import/import_abs_fail_1/app-main/some1/pkg1/pkg1.k @@ -0,0 +1 @@ +Name = '.some1.pkg1.name' diff --git a/test/grammar/import/import_abs_fail_1/app-main/stderr.golden.py b/test/grammar/import/import_abs_fail_1/app-main/stderr.golden.py new file mode 100644 index 000000000..53cc7ac1a --- /dev/null +++ b/test/grammar/import/import_abs_fail_1/app-main/stderr.golden.py @@ -0,0 +1,25 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) +modulename = './../' + +packagename = os.path.abspath(os.path.join(cwd, modulename)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CannotFindModule_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=1, + end_col_no=31 + ) + ], + arg_msg=kcl_error.CANNOT_FIND_MODULE_MSG.format("...some0.pkg1", packagename) + ), + file=sys.stdout +) + diff --git a/test/grammar/import/import_abs_fail_1/kcl.mod b/test/grammar/import/import_abs_fail_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_abs_fail_1/some0/pkg1/pkg1.k b/test/grammar/import/import_abs_fail_1/some0/pkg1/pkg1.k new file mode 100644 index 000000000..6ddf6bada --- /dev/null +++ b/test/grammar/import/import_abs_fail_1/some0/pkg1/pkg1.k @@ -0,0 +1 @@ +Name = 'some0.pkg1.name' diff --git a/test/grammar/import/import_abs_fail_1/some1/pkg1/pkg1.k b/test/grammar/import/import_abs_fail_1/some1/pkg1/pkg1.k new file mode 100644 index 000000000..6e08e1de8 --- /dev/null +++ b/test/grammar/import/import_abs_fail_1/some1/pkg1/pkg1.k @@ -0,0 +1 @@ +Name = 'some1.pkg1.name' diff --git a/test/grammar/import/import_abs_path/app-main/main.k b/test/grammar/import/import_abs_path/app-main/main.k new file mode 100644 index 000000000..912eb0857 --- /dev/null +++ b/test/grammar/import/import_abs_path/app-main/main.k @@ -0,0 +1,7 @@ +import some0.pkg1 as some00 +import some1.pkg1 as some10 +import ..some1.pkg1 as some11 + +Name1 = some00.Name # some0.pkg1.name +Name2 = some10.Name # some1.pkg1.name +Name3 = some11.Name # some1.pkg1.name diff --git a/test/grammar/import/import_abs_path/app-main/some1/pkg1/pkg1.k b/test/grammar/import/import_abs_path/app-main/some1/pkg1/pkg1.k new file mode 100644 index 000000000..3fcc664c9 --- /dev/null +++ b/test/grammar/import/import_abs_path/app-main/some1/pkg1/pkg1.k @@ -0,0 +1 @@ +Name = '.some1.pkg1.name' diff --git a/test/grammar/import/import_abs_path/app-main/stdout.golden b/test/grammar/import/import_abs_path/app-main/stdout.golden new file mode 100644 index 000000000..8177d41a8 --- /dev/null +++ b/test/grammar/import/import_abs_path/app-main/stdout.golden @@ -0,0 +1,3 @@ +Name1: some0.pkg1.name +Name2: some1.pkg1.name +Name3: some1.pkg1.name \ No newline at end of file diff --git a/test/grammar/import/import_abs_path/kcl.mod b/test/grammar/import/import_abs_path/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_abs_path/some0/pkg1/pkg1.k b/test/grammar/import/import_abs_path/some0/pkg1/pkg1.k new file mode 100644 index 000000000..6ddf6bada --- /dev/null +++ b/test/grammar/import/import_abs_path/some0/pkg1/pkg1.k @@ -0,0 +1 @@ +Name = 'some0.pkg1.name' diff --git a/test/grammar/import/import_abs_path/some1/pkg1/pkg1.k b/test/grammar/import/import_abs_path/some1/pkg1/pkg1.k new file mode 100644 index 000000000..6e08e1de8 --- /dev/null +++ b/test/grammar/import/import_abs_path/some1/pkg1/pkg1.k @@ -0,0 +1 @@ +Name = 'some1.pkg1.name' diff --git a/test/grammar/import/import_as_diff_alias/kcl.mod b/test/grammar/import/import_as_diff_alias/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_as_diff_alias/main.k b/test/grammar/import/import_as_diff_alias/main.k new file mode 100644 index 000000000..b00fa08a3 --- /dev/null +++ b/test/grammar/import/import_as_diff_alias/main.k @@ -0,0 +1,24 @@ +import name_pkg.v1 as name +import person_pkg.v1 as person + +schema FullName(name.Name): + fullName: str = "{} {}".format(firstName, lastName) + + +johnName = name.Name { + "firstName": "John", + "lastName": "Snow" +} + +johnPerson = person.Person { + "name": { + "firstName": "John", + "lastName": "Snow" + }, + "age": 10 +} + +johnFullName = FullName { + "firstName": "John", + "lastName": "Snow" +} diff --git a/test/grammar/import/import_as_diff_alias/name_pkg/v1/name.k b/test/grammar/import/import_as_diff_alias/name_pkg/v1/name.k new file mode 100644 index 000000000..47a076863 --- /dev/null +++ b/test/grammar/import/import_as_diff_alias/name_pkg/v1/name.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str diff --git a/test/grammar/import/import_as_diff_alias/person_pkg/v1/person.k b/test/grammar/import/import_as_diff_alias/person_pkg/v1/person.k new file mode 100644 index 000000000..3106e8a0c --- /dev/null +++ b/test/grammar/import/import_as_diff_alias/person_pkg/v1/person.k @@ -0,0 +1,5 @@ +import ...name_pkg.v1 + +schema Person: + name: v1.Name + age: int diff --git a/test/grammar/import/import_as_diff_alias/stdout.golden b/test/grammar/import/import_as_diff_alias/stdout.golden new file mode 100644 index 000000000..e75db0c59 --- /dev/null +++ b/test/grammar/import/import_as_diff_alias/stdout.golden @@ -0,0 +1,12 @@ +johnName: + firstName: John + lastName: Snow +johnPerson: + name: + firstName: John + lastName: Snow + age: 10 +johnFullName: + firstName: John + lastName: Snow + fullName: John Snow \ No newline at end of file diff --git a/test/grammar/import/import_complex/kcl.mod b/test/grammar/import/import_complex/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_complex/main.k b/test/grammar/import/import_complex/main.k new file mode 100644 index 000000000..288701ba1 --- /dev/null +++ b/test/grammar/import/import_complex/main.k @@ -0,0 +1 @@ +import pkg diff --git a/test/grammar/import/import_complex/pkg/a.k b/test/grammar/import/import_complex/pkg/a.k new file mode 100644 index 000000000..c778e3d55 --- /dev/null +++ b/test/grammar/import/import_complex/pkg/a.k @@ -0,0 +1 @@ +import ..pkg2 diff --git a/test/grammar/import/import_complex/pkg/c.k b/test/grammar/import/import_complex/pkg/c.k new file mode 100644 index 000000000..c778e3d55 --- /dev/null +++ b/test/grammar/import/import_complex/pkg/c.k @@ -0,0 +1 @@ +import ..pkg2 diff --git a/test/grammar/import/import_complex/pkg/d.k b/test/grammar/import/import_complex/pkg/d.k new file mode 100644 index 000000000..c778e3d55 --- /dev/null +++ b/test/grammar/import/import_complex/pkg/d.k @@ -0,0 +1 @@ +import ..pkg2 diff --git a/test/grammar/import/import_complex/pkg2/b.k b/test/grammar/import/import_complex/pkg2/b.k new file mode 100644 index 000000000..e66affe56 --- /dev/null +++ b/test/grammar/import/import_complex/pkg2/b.k @@ -0,0 +1,2 @@ +schema B: + b: str diff --git a/test/grammar/import/import_complex/stdout.golden b/test/grammar/import/import_complex/stdout.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_file/kcl.mod b/test/grammar/import/import_file/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_file/main.k b/test/grammar/import/import_file/main.k new file mode 100644 index 000000000..96245c71e --- /dev/null +++ b/test/grammar/import/import_file/main.k @@ -0,0 +1,5 @@ +import module + +result0 = module.data.num + +result1 = 100 diff --git a/test/grammar/import/import_file/module.k b/test/grammar/import/import_file/module.k new file mode 100644 index 000000000..db9135a5f --- /dev/null +++ b/test/grammar/import/import_file/module.k @@ -0,0 +1,6 @@ +schema Data: + num: int = 1 + +data = Data { + "num" = 100 +} diff --git a/test/grammar/import/import_file/stdout.golden b/test/grammar/import/import_file/stdout.golden new file mode 100644 index 000000000..404fe214e --- /dev/null +++ b/test/grammar/import/import_file/stdout.golden @@ -0,0 +1,2 @@ +result0: 100 +result1: 100 \ No newline at end of file diff --git a/test/grammar/import/import_main_file_fail_0/kcl.mod b/test/grammar/import/import_main_file_fail_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_main_file_fail_0/main.k b/test/grammar/import/import_main_file_fail_0/main.k new file mode 100644 index 000000000..64b8ac4d3 --- /dev/null +++ b/test/grammar/import/import_main_file_fail_0/main.k @@ -0,0 +1,3 @@ +import module + +print('main') diff --git a/test/grammar/import/import_main_file_fail_0/module.k b/test/grammar/import/import_main_file_fail_0/module.k new file mode 100644 index 000000000..9279d5a91 --- /dev/null +++ b/test/grammar/import/import_main_file_fail_0/module.k @@ -0,0 +1,4 @@ + +import main + +print('module') diff --git a/test/grammar/import/import_main_file_fail_0/stderr.golden.py b/test/grammar/import/import_main_file_fail_0/stderr.golden.py new file mode 100644 index 000000000..2de4f133d --- /dev/null +++ b/test/grammar/import/import_main_file_fail_0/stderr.golden.py @@ -0,0 +1,23 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +main_file = os.path.join(cwd, 'main.k') +module_file = os.path.join(cwd, 'module.k') + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=module_file, + line_no=2, + col_no=1, + ), + ], + arg_msg=f"Cannot import {main_file} in the main package") + , file=sys.stdout +) + diff --git a/test/grammar/import/import_main_file_fail_1/main.k b/test/grammar/import/import_main_file_fail_1/main.k new file mode 100644 index 000000000..1a17916d3 --- /dev/null +++ b/test/grammar/import/import_main_file_fail_1/main.k @@ -0,0 +1,11 @@ +import main + + + + + + + + + +print('main') diff --git a/test/grammar/import/import_main_file_fail_1/stderr.golden.py b/test/grammar/import/import_main_file_fail_1/stderr.golden.py new file mode 100644 index 000000000..6304f8236 --- /dev/null +++ b/test/grammar/import/import_main_file_fail_1/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +file = os.path.join(cwd, 'main.k') + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=file, + line_no=1, + col_no=1, + ), + ], + arg_msg=f"Cannot import {file} in the main package") + , file=sys.stdout +) + diff --git a/test/grammar/import/import_package/kcl.mod b/test/grammar/import/import_package/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_package/main.k b/test/grammar/import/import_package/main.k new file mode 100644 index 000000000..a6a55d26e --- /dev/null +++ b/test/grammar/import/import_package/main.k @@ -0,0 +1,3 @@ +import mymodule + +result = mymodule.data0.num diff --git a/test/grammar/import/import_package/mymodule/a.k b/test/grammar/import/import_package/mymodule/a.k new file mode 100644 index 000000000..5832188b9 --- /dev/null +++ b/test/grammar/import/import_package/mymodule/a.k @@ -0,0 +1,6 @@ +schema data: + num: int = 1 + +data0 = data { + "num" = 100 +} diff --git a/test/grammar/import/import_package/mymodule/b.k b/test/grammar/import/import_package/mymodule/b.k new file mode 100644 index 000000000..2bdb75321 --- /dev/null +++ b/test/grammar/import/import_package/mymodule/b.k @@ -0,0 +1,3 @@ +data1 = data { + "num" = 23 +} diff --git a/test/grammar/import/import_package/stdout.golden b/test/grammar/import/import_package/stdout.golden new file mode 100644 index 000000000..7fae1987d --- /dev/null +++ b/test/grammar/import/import_package/stdout.golden @@ -0,0 +1 @@ +result: 100 diff --git a/test/grammar/import/import_package_as/kcl.mod b/test/grammar/import/import_package_as/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_package_as/main.k b/test/grammar/import/import_package_as/main.k new file mode 100644 index 000000000..a9af244e8 --- /dev/null +++ b/test/grammar/import/import_package_as/main.k @@ -0,0 +1,3 @@ +import mymodule as mm + +result = mm.data0.num diff --git a/test/grammar/import/import_package_as/mymodule/a.k b/test/grammar/import/import_package_as/mymodule/a.k new file mode 100644 index 000000000..1f57b2604 --- /dev/null +++ b/test/grammar/import/import_package_as/mymodule/a.k @@ -0,0 +1,6 @@ +schema Data: + num : int = 1 + +data0 = Data { + "num" = 100 +} diff --git a/test/grammar/import/import_package_as/mymodule/b.k b/test/grammar/import/import_package_as/mymodule/b.k new file mode 100644 index 000000000..d168400ad --- /dev/null +++ b/test/grammar/import/import_package_as/mymodule/b.k @@ -0,0 +1,5 @@ + + +data1 = Data { + "num" = 10 +} diff --git a/test/grammar/import/import_package_as/stdout.golden b/test/grammar/import/import_package_as/stdout.golden new file mode 100644 index 000000000..7fae1987d --- /dev/null +++ b/test/grammar/import/import_package_as/stdout.golden @@ -0,0 +1 @@ +result: 100 diff --git a/test/grammar/import/import_package_module_0/kcl.mod b/test/grammar/import/import_package_module_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_package_module_0/main.k b/test/grammar/import/import_package_module_0/main.k new file mode 100644 index 000000000..c2613fe45 --- /dev/null +++ b/test/grammar/import/import_package_module_0/main.k @@ -0,0 +1,3 @@ +import pkg_b + +a = pkg_b.a diff --git a/test/grammar/import/import_package_module_0/pkg_b/a.k b/test/grammar/import/import_package_module_0/pkg_b/a.k new file mode 100644 index 000000000..12e38bc76 --- /dev/null +++ b/test/grammar/import/import_package_module_0/pkg_b/a.k @@ -0,0 +1,5 @@ +import .pkg_c.a as pkgca + +a = pkgca.A { + field_A: "123" +} diff --git a/test/grammar/import/import_package_module_0/pkg_b/pkg_c/a.k b/test/grammar/import/import_package_module_0/pkg_b/pkg_c/a.k new file mode 100644 index 000000000..3585ae61f --- /dev/null +++ b/test/grammar/import/import_package_module_0/pkg_b/pkg_c/a.k @@ -0,0 +1,4 @@ +import .b + +schema A(b.B): + field_A?: str = None diff --git a/test/grammar/import/import_package_module_0/pkg_b/pkg_c/b.k b/test/grammar/import/import_package_module_0/pkg_b/pkg_c/b.k new file mode 100644 index 000000000..52ee280df --- /dev/null +++ b/test/grammar/import/import_package_module_0/pkg_b/pkg_c/b.k @@ -0,0 +1,3 @@ + +schema B: + field_B?: str = None diff --git a/test/grammar/import/import_package_module_0/stdout.golden b/test/grammar/import/import_package_module_0/stdout.golden new file mode 100644 index 000000000..0bb995d44 --- /dev/null +++ b/test/grammar/import/import_package_module_0/stdout.golden @@ -0,0 +1,3 @@ +a: + field_B: null + field_A: '123' \ No newline at end of file diff --git a/test/grammar/import/import_package_module_1/kcl.mod b/test/grammar/import/import_package_module_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_package_module_1/main.k b/test/grammar/import/import_package_module_1/main.k new file mode 100644 index 000000000..23236a114 --- /dev/null +++ b/test/grammar/import/import_package_module_1/main.k @@ -0,0 +1,3 @@ +import pkg + +var = pkg.VarA diff --git a/test/grammar/import/import_package_module_1/pkg/moduleA.k b/test/grammar/import/import_package_module_1/pkg/moduleA.k new file mode 100644 index 000000000..dcc53d344 --- /dev/null +++ b/test/grammar/import/import_package_module_1/pkg/moduleA.k @@ -0,0 +1,5 @@ +import .pkg2.moduleA + +VarA = moduleA.SchemaA { + field_A = "override" +} diff --git a/test/grammar/import/import_package_module_1/pkg/pkg2/moduleA.k b/test/grammar/import/import_package_module_1/pkg/pkg2/moduleA.k new file mode 100644 index 000000000..0cb9a38ed --- /dev/null +++ b/test/grammar/import/import_package_module_1/pkg/pkg2/moduleA.k @@ -0,0 +1,5 @@ +import .moduleB + +schema SchemaA(moduleB.SchemaB): + field_A: str = "A" + field_A_int: int = 1 diff --git a/test/grammar/import/import_package_module_1/pkg/pkg2/moduleB.k b/test/grammar/import/import_package_module_1/pkg/pkg2/moduleB.k new file mode 100644 index 000000000..93fcf6c8b --- /dev/null +++ b/test/grammar/import/import_package_module_1/pkg/pkg2/moduleB.k @@ -0,0 +1,3 @@ +schema SchemaB: + field_B: str = "B" + field_B_int: int = 2 diff --git a/test/grammar/import/import_package_module_1/stdout.golden b/test/grammar/import/import_package_module_1/stdout.golden new file mode 100644 index 000000000..1a75053f1 --- /dev/null +++ b/test/grammar/import/import_package_module_1/stdout.golden @@ -0,0 +1,5 @@ +var: + field_B: B + field_B_int: 2 + field_A: override + field_A_int: 1 \ No newline at end of file diff --git a/test/grammar/import/import_regular_module/kcl.mod b/test/grammar/import/import_regular_module/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_regular_module/main.k b/test/grammar/import/import_regular_module/main.k new file mode 100644 index 000000000..a12cf1ce8 --- /dev/null +++ b/test/grammar/import/import_regular_module/main.k @@ -0,0 +1,3 @@ +import mymodule + +result = mymodule.data.num diff --git a/test/grammar/import/import_regular_module/mymodule.k b/test/grammar/import/import_regular_module/mymodule.k new file mode 100644 index 000000000..db9135a5f --- /dev/null +++ b/test/grammar/import/import_regular_module/mymodule.k @@ -0,0 +1,6 @@ +schema Data: + num: int = 1 + +data = Data { + "num" = 100 +} diff --git a/test/grammar/import/import_regular_module/stdout.golden b/test/grammar/import/import_regular_module/stdout.golden new file mode 100644 index 000000000..7fae1987d --- /dev/null +++ b/test/grammar/import/import_regular_module/stdout.golden @@ -0,0 +1 @@ +result: 100 diff --git a/test/grammar/import/import_regular_module_as/kcl.mod b/test/grammar/import/import_regular_module_as/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_regular_module_as/main.k b/test/grammar/import/import_regular_module_as/main.k new file mode 100644 index 000000000..c6ff5e75a --- /dev/null +++ b/test/grammar/import/import_regular_module_as/main.k @@ -0,0 +1,3 @@ +import mymodule as mm + +result = mm.data.num diff --git a/test/grammar/import/import_regular_module_as/mymodule.k b/test/grammar/import/import_regular_module_as/mymodule.k new file mode 100644 index 000000000..db9135a5f --- /dev/null +++ b/test/grammar/import/import_regular_module_as/mymodule.k @@ -0,0 +1,6 @@ +schema Data: + num: int = 1 + +data = Data { + "num" = 100 +} diff --git a/test/grammar/import/import_regular_module_as/stdout.golden b/test/grammar/import/import_regular_module_as/stdout.golden new file mode 100644 index 000000000..7fae1987d --- /dev/null +++ b/test/grammar/import/import_regular_module_as/stdout.golden @@ -0,0 +1 @@ +result: 100 diff --git a/test/grammar/import/import_same_as_name_0/kcl.mod b/test/grammar/import/import_same_as_name_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_same_as_name_0/main.k b/test/grammar/import/import_same_as_name_0/main.k new file mode 100644 index 000000000..287462c4a --- /dev/null +++ b/test/grammar/import/import_same_as_name_0/main.k @@ -0,0 +1,3 @@ +import pkg + +deploy = pkg.CafeDeploy {} diff --git a/test/grammar/import/import_same_as_name_0/pkg/core/v1/deploy.k b/test/grammar/import/import_same_as_name_0/pkg/core/v1/deploy.k new file mode 100644 index 000000000..eff88c5da --- /dev/null +++ b/test/grammar/import/import_same_as_name_0/pkg/core/v1/deploy.k @@ -0,0 +1,3 @@ +import pkg.mixin.v1 +schema Deploy: + name: str \ No newline at end of file diff --git a/test/grammar/import/import_same_as_name_0/pkg/data.k b/test/grammar/import/import_same_as_name_0/pkg/data.k new file mode 100644 index 000000000..3a1e7ae23 --- /dev/null +++ b/test/grammar/import/import_same_as_name_0/pkg/data.k @@ -0,0 +1,4 @@ +import pkg.mixin.v1 + +schema Deploy: + mixin [v1.DeployMixin] diff --git a/test/grammar/import/import_same_as_name_0/pkg/mixin/v1/mixin.k b/test/grammar/import/import_same_as_name_0/pkg/mixin/v1/mixin.k new file mode 100644 index 000000000..1d101fd7f --- /dev/null +++ b/test/grammar/import/import_same_as_name_0/pkg/mixin/v1/mixin.k @@ -0,0 +1,2 @@ +schema DeployMixin: + name: str diff --git a/test/grammar/import/import_same_as_name_0/pkg/temp.k b/test/grammar/import/import_same_as_name_0/pkg/temp.k new file mode 100644 index 000000000..f334ab821 --- /dev/null +++ b/test/grammar/import/import_same_as_name_0/pkg/temp.k @@ -0,0 +1,6 @@ +import pkg.core.v1 as v1 + +schema CafeDeploy: + data: v1.Deploy = v1.Deploy { + name: "deploy" + } diff --git a/test/grammar/import/import_same_as_name_0/stdout.golden b/test/grammar/import/import_same_as_name_0/stdout.golden new file mode 100644 index 000000000..f2c5b0c17 --- /dev/null +++ b/test/grammar/import/import_same_as_name_0/stdout.golden @@ -0,0 +1,3 @@ +deploy: + data: + name: deploy diff --git a/test/grammar/import/import_submodule/app-main/main.k b/test/grammar/import/import_submodule/app-main/main.k new file mode 100644 index 000000000..4d9b4580f --- /dev/null +++ b/test/grammar/import/import_submodule/app-main/main.k @@ -0,0 +1,3 @@ +import mydir.mydir2.mymodule + +result = mymodule.data.num diff --git a/test/grammar/import/import_submodule/app-main/stdout.golden b/test/grammar/import/import_submodule/app-main/stdout.golden new file mode 100644 index 000000000..7fae1987d --- /dev/null +++ b/test/grammar/import/import_submodule/app-main/stdout.golden @@ -0,0 +1 @@ +result: 100 diff --git a/test/grammar/import/import_submodule/kcl.mod b/test/grammar/import/import_submodule/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_submodule/mydir/mydir2/mymodule.k b/test/grammar/import/import_submodule/mydir/mydir2/mymodule.k new file mode 100644 index 000000000..db9135a5f --- /dev/null +++ b/test/grammar/import/import_submodule/mydir/mydir2/mymodule.k @@ -0,0 +1,6 @@ +schema Data: + num: int = 1 + +data = Data { + "num" = 100 +} diff --git a/test/grammar/import/import_submodule_as/app-main/main.k b/test/grammar/import/import_submodule_as/app-main/main.k new file mode 100644 index 000000000..57d742585 --- /dev/null +++ b/test/grammar/import/import_submodule_as/app-main/main.k @@ -0,0 +1,3 @@ +import mydir.mydir2.mymodule as mm + +result = mm.data.num diff --git a/test/grammar/import/import_submodule_as/app-main/stdout.golden b/test/grammar/import/import_submodule_as/app-main/stdout.golden new file mode 100644 index 000000000..7fae1987d --- /dev/null +++ b/test/grammar/import/import_submodule_as/app-main/stdout.golden @@ -0,0 +1 @@ +result: 100 diff --git a/test/grammar/import/import_submodule_as/kcl.mod b/test/grammar/import/import_submodule_as/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_submodule_as/mydir/mydir2/mymodule.k b/test/grammar/import/import_submodule_as/mydir/mydir2/mymodule.k new file mode 100644 index 000000000..db9135a5f --- /dev/null +++ b/test/grammar/import/import_submodule_as/mydir/mydir2/mymodule.k @@ -0,0 +1,6 @@ +schema Data: + num: int = 1 + +data = Data { + "num" = 100 +} diff --git a/test/grammar/import/import_syntax_error_0/app-main/main.k b/test/grammar/import/import_syntax_error_0/app-main/main.k new file mode 100644 index 000000000..7647694ab --- /dev/null +++ b/test/grammar/import/import_syntax_error_0/app-main/main.k @@ -0,0 +1,3 @@ +import .some0..pkg1 as some00 # some0 not found in app-main package + +Name1 = some00.Name # some0.pkg1.name diff --git a/test/grammar/import/import_syntax_error_0/app-main/some1/pkg1/pkg1.k b/test/grammar/import/import_syntax_error_0/app-main/some1/pkg1/pkg1.k new file mode 100644 index 000000000..3fcc664c9 --- /dev/null +++ b/test/grammar/import/import_syntax_error_0/app-main/some1/pkg1/pkg1.k @@ -0,0 +1 @@ +Name = '.some1.pkg1.name' diff --git a/test/grammar/import/import_syntax_error_0/app-main/stderr.golden.py b/test/grammar/import/import_syntax_error_0/app-main/stderr.golden.py new file mode 100644 index 000000000..9e3e9c4bd --- /dev/null +++ b/test/grammar/import/import_syntax_error_0/app-main/stderr.golden.py @@ -0,0 +1,16 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.InvalidSyntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=15, + arg_msg="Expected one of ['name']" + )], + ), + file=sys.stdout) diff --git a/test/grammar/import/import_syntax_error_0/kcl.mod b/test/grammar/import/import_syntax_error_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_syntax_error_0/some0/pkg1/pkg1.k b/test/grammar/import/import_syntax_error_0/some0/pkg1/pkg1.k new file mode 100644 index 000000000..6ddf6bada --- /dev/null +++ b/test/grammar/import/import_syntax_error_0/some0/pkg1/pkg1.k @@ -0,0 +1 @@ +Name = 'some0.pkg1.name' diff --git a/test/grammar/import/import_syntax_error_0/some1/pkg1/pkg1.k b/test/grammar/import/import_syntax_error_0/some1/pkg1/pkg1.k new file mode 100644 index 000000000..6e08e1de8 --- /dev/null +++ b/test/grammar/import/import_syntax_error_0/some1/pkg1/pkg1.k @@ -0,0 +1 @@ +Name = 'some1.pkg1.name' diff --git a/test/grammar/import/module/no_module_attr_fail_0/kcl.mod b/test/grammar/import/module/no_module_attr_fail_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/module/no_module_attr_fail_0/main.k b/test/grammar/import/module/no_module_attr_fail_0/main.k new file mode 100644 index 000000000..69374c7e4 --- /dev/null +++ b/test/grammar/import/module/no_module_attr_fail_0/main.k @@ -0,0 +1,3 @@ +import pkg as p + +a = p.D + 1 diff --git a/test/grammar/import/module/no_module_attr_fail_0/pkg/a.k b/test/grammar/import/module/no_module_attr_fail_0/pkg/a.k new file mode 100644 index 000000000..5981be98e --- /dev/null +++ b/test/grammar/import/module/no_module_attr_fail_0/pkg/a.k @@ -0,0 +1,3 @@ + +schema A: + field_A: str diff --git a/test/grammar/import/module/no_module_attr_fail_0/stderr.golden.py b/test/grammar/import/module/no_module_attr_fail_0/stderr.golden.py new file mode 100644 index 000000000..a002a3a27 --- /dev/null +++ b/test/grammar/import/module/no_module_attr_fail_0/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.AttributeError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5 + ) + ], + arg_msg="module 'pkg' has no attribute 'D'" + ), + file=sys.stdout +) + diff --git a/test/grammar/import/module/no_module_attr_fail_1/kcl.mod b/test/grammar/import/module/no_module_attr_fail_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/module/no_module_attr_fail_1/main.k b/test/grammar/import/module/no_module_attr_fail_1/main.k new file mode 100644 index 000000000..2ba0ffa2b --- /dev/null +++ b/test/grammar/import/module/no_module_attr_fail_1/main.k @@ -0,0 +1,8 @@ +import pkg as p + +schema A(p.D): + attr: {str:} + +a = A { + attr: {} +} diff --git a/test/grammar/import/module/no_module_attr_fail_1/pkg/a.k b/test/grammar/import/module/no_module_attr_fail_1/pkg/a.k new file mode 100644 index 000000000..5981be98e --- /dev/null +++ b/test/grammar/import/module/no_module_attr_fail_1/pkg/a.k @@ -0,0 +1,3 @@ + +schema A: + field_A: str diff --git a/test/grammar/import/module/no_module_attr_fail_1/stderr.golden.py b/test/grammar/import/module/no_module_attr_fail_1/stderr.golden.py new file mode 100644 index 000000000..bb7333979 --- /dev/null +++ b/test/grammar/import/module/no_module_attr_fail_1/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.AttributeError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=10 + ) + ], + arg_msg="module 'pkg' has no attribute 'D'" + ), + file=sys.stdout +) + diff --git a/test/grammar/import/module/no_module_attr_fail_2/main.k b/test/grammar/import/module/no_module_attr_fail_2/main.k new file mode 100644 index 000000000..2e715b120 --- /dev/null +++ b/test/grammar/import/module/no_module_attr_fail_2/main.k @@ -0,0 +1,3 @@ +import math + +a = math.err_func(1) diff --git a/test/grammar/import/module/no_module_attr_fail_2/stderr.golden.py b/test/grammar/import/module/no_module_attr_fail_2/stderr.golden.py new file mode 100644 index 000000000..3cb9d21df --- /dev/null +++ b/test/grammar/import/module/no_module_attr_fail_2/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.AttributeError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5 + ) + ], + arg_msg="module 'math' has no attribute 'err_func'" + ), + file=sys.stdout +) + diff --git a/test/grammar/import/module/no_module_attr_fail_3/main.k b/test/grammar/import/module/no_module_attr_fail_3/main.k new file mode 100644 index 000000000..3437eaefd --- /dev/null +++ b/test/grammar/import/module/no_module_attr_fail_3/main.k @@ -0,0 +1,3 @@ +import math as mymath + +a = mymath.err_func(1) diff --git a/test/grammar/import/module/no_module_attr_fail_3/stderr.golden.py b/test/grammar/import/module/no_module_attr_fail_3/stderr.golden.py new file mode 100644 index 000000000..3cb9d21df --- /dev/null +++ b/test/grammar/import/module/no_module_attr_fail_3/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.AttributeError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5 + ) + ], + arg_msg="module 'math' has no attribute 'err_func'" + ), + file=sys.stdout +) + diff --git a/test/grammar/import/no_kcl_mod_file/main.k b/test/grammar/import/no_kcl_mod_file/main.k new file mode 100644 index 000000000..cd9eda3dd --- /dev/null +++ b/test/grammar/import/no_kcl_mod_file/main.k @@ -0,0 +1,7 @@ +import pkg1 +import pkg2 + +# no kcl.mod, use dirname(main.k) as root + +Path1 = pkg1.Path +Path2 = pkg2.Path diff --git a/test/grammar/import/no_kcl_mod_file/pkg1/pkg.k b/test/grammar/import/no_kcl_mod_file/pkg1/pkg.k new file mode 100644 index 000000000..b6ec63378 --- /dev/null +++ b/test/grammar/import/no_kcl_mod_file/pkg1/pkg.k @@ -0,0 +1 @@ +Path = "pkg1" \ No newline at end of file diff --git a/test/grammar/import/no_kcl_mod_file/pkg2.k b/test/grammar/import/no_kcl_mod_file/pkg2.k new file mode 100644 index 000000000..d296fe1ab --- /dev/null +++ b/test/grammar/import/no_kcl_mod_file/pkg2.k @@ -0,0 +1 @@ +Path = "pkg2" \ No newline at end of file diff --git a/test/grammar/import/no_kcl_mod_file/stdout.golden b/test/grammar/import/no_kcl_mod_file/stdout.golden new file mode 100644 index 000000000..87b7c93f3 --- /dev/null +++ b/test/grammar/import/no_kcl_mod_file/stdout.golden @@ -0,0 +1,2 @@ +Path1: pkg1 +Path2: pkg2 diff --git a/test/grammar/import/pkg_inplace_modify_fail_0/kcl.mod b/test/grammar/import/pkg_inplace_modify_fail_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/pkg_inplace_modify_fail_0/main.k b/test/grammar/import/pkg_inplace_modify_fail_0/main.k new file mode 100644 index 000000000..4fb05f3c3 --- /dev/null +++ b/test/grammar/import/pkg_inplace_modify_fail_0/main.k @@ -0,0 +1,3 @@ +import pkg + +pkg.a = 1 diff --git a/test/grammar/import/pkg_inplace_modify_fail_0/pkg/pkg.k b/test/grammar/import/pkg_inplace_modify_fail_0/pkg/pkg.k new file mode 100644 index 000000000..1337a530c --- /dev/null +++ b/test/grammar/import/pkg_inplace_modify_fail_0/pkg/pkg.k @@ -0,0 +1 @@ +a = 1 diff --git a/test/grammar/import/pkg_inplace_modify_fail_0/stderr.golden.py b/test/grammar/import/pkg_inplace_modify_fail_0/stderr.golden.py new file mode 100644 index 000000000..26a35d289 --- /dev/null +++ b/test/grammar/import/pkg_inplace_modify_fail_0/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=1, + ), + ], + arg_msg="Immutable variable is modified during compiling") + , file=sys.stdout +) + diff --git a/test/grammar/import/pkg_inplace_modify_fail_1/main.k b/test/grammar/import/pkg_inplace_modify_fail_1/main.k new file mode 100644 index 000000000..7f3b1b8f0 --- /dev/null +++ b/test/grammar/import/pkg_inplace_modify_fail_1/main.k @@ -0,0 +1,3 @@ +import pkg + +pkg.a |= 2 diff --git a/test/grammar/import/pkg_inplace_modify_fail_1/pkg/pkg.k b/test/grammar/import/pkg_inplace_modify_fail_1/pkg/pkg.k new file mode 100644 index 000000000..1337a530c --- /dev/null +++ b/test/grammar/import/pkg_inplace_modify_fail_1/pkg/pkg.k @@ -0,0 +1 @@ +a = 1 diff --git a/test/grammar/import/pkg_inplace_modify_fail_1/stderr.golden.py b/test/grammar/import/pkg_inplace_modify_fail_1/stderr.golden.py new file mode 100644 index 000000000..88ee9a09b --- /dev/null +++ b/test/grammar/import/pkg_inplace_modify_fail_1/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=1, + ) + ], + arg_msg="only schema and dict object can be updated attribute" + ), + file=sys.stdout +) + diff --git a/test/grammar/import/pkg_inplace_modify_fail_2/main.k b/test/grammar/import/pkg_inplace_modify_fail_2/main.k new file mode 100644 index 000000000..e2b4f6b04 --- /dev/null +++ b/test/grammar/import/pkg_inplace_modify_fail_2/main.k @@ -0,0 +1,6 @@ +import pkg + +schema Data: + pkg.a = 2 + +data = Data {} diff --git a/test/grammar/import/pkg_inplace_modify_fail_2/pkg/pkg.k b/test/grammar/import/pkg_inplace_modify_fail_2/pkg/pkg.k new file mode 100644 index 000000000..1337a530c --- /dev/null +++ b/test/grammar/import/pkg_inplace_modify_fail_2/pkg/pkg.k @@ -0,0 +1 @@ +a = 1 diff --git a/test/grammar/import/pkg_inplace_modify_fail_2/stderr.golden.py b/test/grammar/import/pkg_inplace_modify_fail_2/stderr.golden.py new file mode 100644 index 000000000..e430951f1 --- /dev/null +++ b/test/grammar/import/pkg_inplace_modify_fail_2/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=5, + ), + ], + arg_msg="only schema and dict object can be updated attribute") + , file=sys.stdout +) + diff --git a/test/grammar/import/relative_import/main.k b/test/grammar/import/relative_import/main.k new file mode 100644 index 000000000..346d8034b --- /dev/null +++ b/test/grammar/import/relative_import/main.k @@ -0,0 +1,4 @@ +import .mydir.mymodule +import .mydir.mydir2.mymodule2 + +result = mymodule2.data1.num \ No newline at end of file diff --git a/test/grammar/import/relative_import/mydir/mydir2/mymodule2.k b/test/grammar/import/relative_import/mydir/mydir2/mymodule2.k new file mode 100644 index 000000000..efa3e35aa --- /dev/null +++ b/test/grammar/import/relative_import/mydir/mydir2/mymodule2.k @@ -0,0 +1,6 @@ +schema data: + num : int = 1 + +data1 = data { + "num" = 10 +} diff --git a/test/grammar/import/relative_import/mydir/mymodule.k b/test/grammar/import/relative_import/mydir/mymodule.k new file mode 100644 index 000000000..72367c5c3 --- /dev/null +++ b/test/grammar/import/relative_import/mydir/mymodule.k @@ -0,0 +1,6 @@ +schema data: + num : int = 1 + +data0 = data { + "num" = 100 +} diff --git a/test/grammar/import/relative_import/stdout.golden b/test/grammar/import/relative_import/stdout.golden new file mode 100644 index 000000000..0e0834d8b --- /dev/null +++ b/test/grammar/import/relative_import/stdout.golden @@ -0,0 +1 @@ +result: 10 diff --git a/test/grammar/import/relative_import_as/main.k b/test/grammar/import/relative_import_as/main.k new file mode 100644 index 000000000..5ba0e0e4f --- /dev/null +++ b/test/grammar/import/relative_import_as/main.k @@ -0,0 +1,4 @@ +import .mydir.mymodule as m1 +import .mydir.mydir2.mymodule2 as m2 + +result = m2.data1.num diff --git a/test/grammar/import/relative_import_as/mydir/mydir2/mymodule2.k b/test/grammar/import/relative_import_as/mydir/mydir2/mymodule2.k new file mode 100644 index 000000000..7fc22a7b6 --- /dev/null +++ b/test/grammar/import/relative_import_as/mydir/mydir2/mymodule2.k @@ -0,0 +1,6 @@ +schema data: + num: int = 1 + +data1 = data { + "num" = 10 +} diff --git a/test/grammar/import/relative_import_as/mydir/mymodule.k b/test/grammar/import/relative_import_as/mydir/mymodule.k new file mode 100644 index 000000000..5832188b9 --- /dev/null +++ b/test/grammar/import/relative_import_as/mydir/mymodule.k @@ -0,0 +1,6 @@ +schema data: + num: int = 1 + +data0 = data { + "num" = 100 +} diff --git a/test/grammar/import/relative_import_as/stdout.golden b/test/grammar/import/relative_import_as/stdout.golden new file mode 100644 index 000000000..0e0834d8b --- /dev/null +++ b/test/grammar/import/relative_import_as/stdout.golden @@ -0,0 +1 @@ +result: 10 diff --git a/test/grammar/lambda/in_for_0/main.k b/test/grammar/lambda/in_for_0/main.k new file mode 100644 index 000000000..7b734fde8 --- /dev/null +++ b/test/grammar/lambda/in_for_0/main.k @@ -0,0 +1,9 @@ +f = lambda x, y { + x * y +} +x0 = [ + (lambda x, y { + x + y + })(x, y) for x in [1, 2, 3] for y in [1, 2, 3] +] +x1 = [f(x, y) for x in [1, 2, 3] for y in [1, 2, 3]] diff --git a/test/grammar/lambda/in_for_0/stdout.golden b/test/grammar/lambda/in_for_0/stdout.golden new file mode 100644 index 000000000..fe09dbb02 --- /dev/null +++ b/test/grammar/lambda/in_for_0/stdout.golden @@ -0,0 +1,20 @@ +x0: +- 2 +- 3 +- 4 +- 3 +- 4 +- 5 +- 4 +- 5 +- 6 +x1: +- 1 +- 2 +- 3 +- 2 +- 4 +- 6 +- 3 +- 6 +- 9 diff --git a/test/grammar/lambda/in_for_1/main.k b/test/grammar/lambda/in_for_1/main.k new file mode 100644 index 000000000..21e15f0ac --- /dev/null +++ b/test/grammar/lambda/in_for_1/main.k @@ -0,0 +1,15 @@ +f = lambda x, y, z { + x * y + z +} +schema Data: + val: int = 0 + x0 = [ + (lambda x, y, z { + x + y + })(x, y, val) for x in [1, 2] for y in [1, 2] + ] + x1 = [f(x, y, val) for x in [1, 2] for y in [1, 2]] + +data = Data { + val = 1 +} diff --git a/test/grammar/lambda/in_for_1/stdout.golden b/test/grammar/lambda/in_for_1/stdout.golden new file mode 100644 index 000000000..a647b35d4 --- /dev/null +++ b/test/grammar/lambda/in_for_1/stdout.golden @@ -0,0 +1,12 @@ +data: + val: 1 + x0: + - 2 + - 3 + - 3 + - 4 + x1: + - 2 + - 3 + - 3 + - 5 diff --git a/test/grammar/lambda/in_for_2/main.k b/test/grammar/lambda/in_for_2/main.k new file mode 100644 index 000000000..fec4fde6e --- /dev/null +++ b/test/grammar/lambda/in_for_2/main.k @@ -0,0 +1,12 @@ +data0 = (lambda x, y { + z = x * 2 + z + y +})(1, 1) + +schema Data: + data = [(lambda x, y { + _z = 2 * x + _z = _z + y + })(x, y) for x in [1, 2] for y in [1, 2]] + +data = Data() diff --git a/test/grammar/lambda/in_for_2/stdout.golden b/test/grammar/lambda/in_for_2/stdout.golden new file mode 100644 index 000000000..144005631 --- /dev/null +++ b/test/grammar/lambda/in_for_2/stdout.golden @@ -0,0 +1,7 @@ +data0: 3 +data: + data: + - 3 + - 4 + - 5 + - 6 diff --git a/test/grammar/lambda/in_pkg_0/kcl.mod b/test/grammar/lambda/in_pkg_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/lambda/in_pkg_0/main.k b/test/grammar/lambda/in_pkg_0/main.k new file mode 100644 index 000000000..1e1157f63 --- /dev/null +++ b/test/grammar/lambda/in_pkg_0/main.k @@ -0,0 +1,7 @@ +import pkg + +func = pkg.func +a = pkg.a +b = pkg.b +c = func(1, 1) +d = func("1", "1") diff --git a/test/grammar/lambda/in_pkg_0/pkg/pkg.k b/test/grammar/lambda/in_pkg_0/pkg/pkg.k new file mode 100644 index 000000000..b484b6026 --- /dev/null +++ b/test/grammar/lambda/in_pkg_0/pkg/pkg.k @@ -0,0 +1,5 @@ +func = lambda x: int | str, y: int | str { + int(x) + int(y) +} +a = func(1, 1) +b = func("1", "1") diff --git a/test/grammar/lambda/in_pkg_0/stdout.golden b/test/grammar/lambda/in_pkg_0/stdout.golden new file mode 100644 index 000000000..b9996f995 --- /dev/null +++ b/test/grammar/lambda/in_pkg_0/stdout.golden @@ -0,0 +1,4 @@ +a: 2 +b: 2 +c: 2 +d: 2 diff --git a/test/grammar/lambda/in_pkg_1/kcl.mod b/test/grammar/lambda/in_pkg_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/lambda/in_pkg_1/main.k b/test/grammar/lambda/in_pkg_1/main.k new file mode 100644 index 000000000..1e1157f63 --- /dev/null +++ b/test/grammar/lambda/in_pkg_1/main.k @@ -0,0 +1,7 @@ +import pkg + +func = pkg.func +a = pkg.a +b = pkg.b +c = func(1, 1) +d = func("1", "1") diff --git a/test/grammar/lambda/in_pkg_1/pkg/pkg.k b/test/grammar/lambda/in_pkg_1/pkg/pkg.k new file mode 100644 index 000000000..97cbfc84a --- /dev/null +++ b/test/grammar/lambda/in_pkg_1/pkg/pkg.k @@ -0,0 +1,8 @@ +global = 1 +func = lambda x: int | str, y: int | str { + (lambda x, y { + int(x) + int(y) + global + })(x, y) +} +a = func(1, 1) +b = func("1", "1") diff --git a/test/grammar/lambda/in_pkg_1/stdout.golden b/test/grammar/lambda/in_pkg_1/stdout.golden new file mode 100644 index 000000000..b899d7959 --- /dev/null +++ b/test/grammar/lambda/in_pkg_1/stdout.golden @@ -0,0 +1,4 @@ +a: 3 +b: 3 +c: 3 +d: 3 diff --git a/test/grammar/lambda/in_schema_0/main.k b/test/grammar/lambda/in_schema_0/main.k new file mode 100644 index 000000000..1d66606e3 --- /dev/null +++ b/test/grammar/lambda/in_schema_0/main.k @@ -0,0 +1,12 @@ +schema Data: + var: int = 1 + _func = lambda x: int | str, y: int | str { + (lambda x, y { + int(x) + int(y) + var + })(x, y) + } + + a = _func(1, 1) + b = _func("123", "456") + +data = Data() diff --git a/test/grammar/lambda/in_schema_0/stdout.golden b/test/grammar/lambda/in_schema_0/stdout.golden new file mode 100644 index 000000000..32df9eb86 --- /dev/null +++ b/test/grammar/lambda/in_schema_0/stdout.golden @@ -0,0 +1,4 @@ +data: + var: 1 + a: 3 + b: 580 diff --git a/test/grammar/lambda/in_schema_1/main.k b/test/grammar/lambda/in_schema_1/main.k new file mode 100644 index 000000000..e5d0061b9 --- /dev/null +++ b/test/grammar/lambda/in_schema_1/main.k @@ -0,0 +1,14 @@ +global: int = 10 + +schema Data: + var: int = 1 + _func = lambda x: int | str, y: int | str { + (lambda x, y { + int(x) + int(y) + var + global + })(x, y) + } + + a = _func(1, 1) + b = _func("123", "456") + +data = Data() diff --git a/test/grammar/lambda/in_schema_1/stdout.golden b/test/grammar/lambda/in_schema_1/stdout.golden new file mode 100644 index 000000000..50ae2ab28 --- /dev/null +++ b/test/grammar/lambda/in_schema_1/stdout.golden @@ -0,0 +1,5 @@ +global: 10 +data: + var: 1 + a: 13 + b: 590 diff --git a/test/grammar/lambda/in_schema_2/main.k b/test/grammar/lambda/in_schema_2/main.k new file mode 100644 index 000000000..ee38647d9 --- /dev/null +++ b/test/grammar/lambda/in_schema_2/main.k @@ -0,0 +1,15 @@ +global: int = 10 + +schema Data: + var: int = 1 + _func = lambda x: int | str, y: int | str { + (lambda x, y { + _val = int(x) + int(y) + _val = _val + var + global + })(x, y) + } + + a = _func(1, 1) + b = _func("123", "456") + +data = Data() diff --git a/test/grammar/lambda/in_schema_2/stdout.golden b/test/grammar/lambda/in_schema_2/stdout.golden new file mode 100644 index 000000000..50ae2ab28 --- /dev/null +++ b/test/grammar/lambda/in_schema_2/stdout.golden @@ -0,0 +1,5 @@ +global: 10 +data: + var: 1 + a: 13 + b: 590 diff --git a/test/grammar/lambda/in_schema_3/main.k b/test/grammar/lambda/in_schema_3/main.k new file mode 100644 index 000000000..6b174acc0 --- /dev/null +++ b/test/grammar/lambda/in_schema_3/main.k @@ -0,0 +1,11 @@ +schema Person: + name: str = "kcl" + age: int = 1 + ageStr: str = (lambda { + str(age) + })() + +x0 = Person {} +x1 = Person { + age = 101 +} diff --git a/test/grammar/lambda/in_schema_3/stdout.golden b/test/grammar/lambda/in_schema_3/stdout.golden new file mode 100644 index 000000000..1f1a2600f --- /dev/null +++ b/test/grammar/lambda/in_schema_3/stdout.golden @@ -0,0 +1,8 @@ +x0: + name: kcl + age: 1 + ageStr: '1' +x1: + name: kcl + age: 101 + ageStr: '101' diff --git a/test/grammar/lambda/in_schema_4/main.k b/test/grammar/lambda/in_schema_4/main.k new file mode 100644 index 000000000..9ff86601d --- /dev/null +++ b/test/grammar/lambda/in_schema_4/main.k @@ -0,0 +1,12 @@ +schema Person: + name: str = "kcl" + age: int = 1 + ageStr: str = (lambda { + prefix = "age: " + prefix + str(age) + })() + +x0 = Person {} +x1 = Person { + age = 101 +} diff --git a/test/grammar/lambda/in_schema_4/stdout.golden b/test/grammar/lambda/in_schema_4/stdout.golden new file mode 100644 index 000000000..71079dab6 --- /dev/null +++ b/test/grammar/lambda/in_schema_4/stdout.golden @@ -0,0 +1,8 @@ +x0: + name: kcl + age: 1 + ageStr: 'age: 1' +x1: + name: kcl + age: 101 + ageStr: 'age: 101' diff --git a/test/grammar/lambda/top_level_0/main.k b/test/grammar/lambda/top_level_0/main.k new file mode 100644 index 000000000..d689df401 --- /dev/null +++ b/test/grammar/lambda/top_level_0/main.k @@ -0,0 +1,5 @@ +func = lambda x: int | str, y: int | str { + int(x) + int(y) +} +a = func(1, 1) +b = func("123", "456") diff --git a/test/grammar/lambda/top_level_0/stdout.golden b/test/grammar/lambda/top_level_0/stdout.golden new file mode 100644 index 000000000..73f656a99 --- /dev/null +++ b/test/grammar/lambda/top_level_0/stdout.golden @@ -0,0 +1,2 @@ +a: 2 +b: 579 diff --git a/test/grammar/lambda/top_level_1/main.k b/test/grammar/lambda/top_level_1/main.k new file mode 100644 index 000000000..517227d8f --- /dev/null +++ b/test/grammar/lambda/top_level_1/main.k @@ -0,0 +1,7 @@ +func = lambda x: int | str, y: int | str { + (lambda x, y { + int(x) + int(y) + })(x, y) +} +a = func(1, 1) +b = func("123", "456") diff --git a/test/grammar/lambda/top_level_1/stdout.golden b/test/grammar/lambda/top_level_1/stdout.golden new file mode 100644 index 000000000..73f656a99 --- /dev/null +++ b/test/grammar/lambda/top_level_1/stdout.golden @@ -0,0 +1,2 @@ +a: 2 +b: 579 diff --git a/test/grammar/lambda/top_level_2/main.k b/test/grammar/lambda/top_level_2/main.k new file mode 100644 index 000000000..547ee740d --- /dev/null +++ b/test/grammar/lambda/top_level_2/main.k @@ -0,0 +1,8 @@ +func = lambda x: int | str, y: int | str { + (lambda x, y { + int(x) + int(y) + })(x, y) +} +afunc = func +a = afunc(1, 1) +b = afunc("123", "456") diff --git a/test/grammar/lambda/top_level_2/stdout.golden b/test/grammar/lambda/top_level_2/stdout.golden new file mode 100644 index 000000000..73f656a99 --- /dev/null +++ b/test/grammar/lambda/top_level_2/stdout.golden @@ -0,0 +1,2 @@ +a: 2 +b: 579 diff --git a/test/grammar/lambda/top_level_3/main.k b/test/grammar/lambda/top_level_3/main.k new file mode 100644 index 000000000..709e35099 --- /dev/null +++ b/test/grammar/lambda/top_level_3/main.k @@ -0,0 +1,14 @@ +x = (lambda { + _e = 1 + if True: + _e = 2 + { + e: _e + } +})() +_e = 1 +if True: + _e = 2 +y = { + e: _e +} diff --git a/test/grammar/lambda/top_level_3/stdout.golden b/test/grammar/lambda/top_level_3/stdout.golden new file mode 100644 index 000000000..2919ac9da --- /dev/null +++ b/test/grammar/lambda/top_level_3/stdout.golden @@ -0,0 +1,4 @@ +x: + e: 2 +y: + e: 2 diff --git a/test/grammar/lambda/top_level_4/main.k b/test/grammar/lambda/top_level_4/main.k new file mode 100644 index 000000000..b157bc388 --- /dev/null +++ b/test/grammar/lambda/top_level_4/main.k @@ -0,0 +1,9 @@ +f = lambda x: {str:} -> {str:} { + 1 + _x = 1 + { + x = _x + } +} + +x = f({data = [1]}) diff --git a/test/grammar/lambda/top_level_4/stdout.golden b/test/grammar/lambda/top_level_4/stdout.golden new file mode 100644 index 000000000..6f947823f --- /dev/null +++ b/test/grammar/lambda/top_level_4/stdout.golden @@ -0,0 +1,2 @@ +x: + x: 1 diff --git a/test/grammar/lambda/top_level_5/main.k b/test/grammar/lambda/top_level_5/main.k new file mode 100644 index 000000000..dcb77ad96 --- /dev/null +++ b/test/grammar/lambda/top_level_5/main.k @@ -0,0 +1,4 @@ +f = lambda -> int { + 1 +} +x = f() diff --git a/test/grammar/lambda/top_level_5/stdout.golden b/test/grammar/lambda/top_level_5/stdout.golden new file mode 100644 index 000000000..d508cf756 --- /dev/null +++ b/test/grammar/lambda/top_level_5/stdout.golden @@ -0,0 +1 @@ +x: 1 diff --git a/test/grammar/lambda/top_level_6/main.k b/test/grammar/lambda/top_level_6/main.k new file mode 100644 index 000000000..6c644b6cf --- /dev/null +++ b/test/grammar/lambda/top_level_6/main.k @@ -0,0 +1,13 @@ +x = (lambda { + if True: + _x = False + else: + _x = True + _x +})() +y = (lambda { + _x = True + if True: + _x = False + _x +})() diff --git a/test/grammar/lambda/top_level_6/stdout.golden b/test/grammar/lambda/top_level_6/stdout.golden new file mode 100644 index 000000000..e48ef5528 --- /dev/null +++ b/test/grammar/lambda/top_level_6/stdout.golden @@ -0,0 +1,2 @@ +x: false +y: false diff --git a/test/grammar/misc/disable_none/main.k b/test/grammar/misc/disable_none/main.k new file mode 100644 index 000000000..a800cdbda --- /dev/null +++ b/test/grammar/misc/disable_none/main.k @@ -0,0 +1,2 @@ +a = None +b = "Hello world!" diff --git a/test/grammar/misc/disable_none/settings.yaml b/test/grammar/misc/disable_none/settings.yaml new file mode 100644 index 000000000..6dcdd25b7 --- /dev/null +++ b/test/grammar/misc/disable_none/settings.yaml @@ -0,0 +1 @@ +kcl_options: -n diff --git a/test/grammar/misc/disable_none/stdout.golden b/test/grammar/misc/disable_none/stdout.golden new file mode 100644 index 000000000..434a4da7f --- /dev/null +++ b/test/grammar/misc/disable_none/stdout.golden @@ -0,0 +1 @@ +b: Hello world! diff --git a/test/grammar/misc/dump_order/main.k b/test/grammar/misc/dump_order/main.k new file mode 100644 index 000000000..80a28cdb8 --- /dev/null +++ b/test/grammar/misc/dump_order/main.k @@ -0,0 +1,9 @@ +c = 1 + +b = [1, 2, 3] + +a = { + "C": "c", + "B": "b", + "A": "a" +} diff --git a/test/grammar/misc/dump_order/stdout.golden b/test/grammar/misc/dump_order/stdout.golden new file mode 100644 index 000000000..51ff5fb33 --- /dev/null +++ b/test/grammar/misc/dump_order/stdout.golden @@ -0,0 +1,9 @@ +c: 1 +b: +- 1 +- 2 +- 3 +a: + C: c + B: b + A: a diff --git a/test/grammar/misc/emit_empty/empty_dict_0/main.k b/test/grammar/misc/emit_empty/empty_dict_0/main.k new file mode 100644 index 000000000..1e7b120bf --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_0/main.k @@ -0,0 +1,12 @@ +schema Name: + first?: str + last?: str + +schema Person: + name?: Name + age?: int + +alice = Person { + "name": {}, + "age": 10 +} diff --git a/test/grammar/misc/emit_empty/empty_dict_0/settings.yaml b/test/grammar/misc/emit_empty/empty_dict_0/settings.yaml new file mode 100644 index 000000000..6dcdd25b7 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -n diff --git a/test/grammar/misc/emit_empty/empty_dict_0/stdout.golden b/test/grammar/misc/emit_empty/empty_dict_0/stdout.golden new file mode 100644 index 000000000..5b506ee41 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_0/stdout.golden @@ -0,0 +1,3 @@ +alice: + name: {} + age: 10 \ No newline at end of file diff --git a/test/grammar/misc/emit_empty/empty_dict_1/main.k b/test/grammar/misc/emit_empty/empty_dict_1/main.k new file mode 100644 index 000000000..ddcfcb449 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_1/main.k @@ -0,0 +1,12 @@ +schema Name: + first?: str = None + last?: str = None + +schema Person: + name?: Name + age?: int + +alice = Person { + "name": {}, + "age": 10 +} diff --git a/test/grammar/misc/emit_empty/empty_dict_1/stdout.golden b/test/grammar/misc/emit_empty/empty_dict_1/stdout.golden new file mode 100644 index 000000000..ac15932b6 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_1/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + first: null + last: null + age: 10 \ No newline at end of file diff --git a/test/grammar/misc/emit_empty/empty_dict_2/main.k b/test/grammar/misc/emit_empty/empty_dict_2/main.k new file mode 100644 index 000000000..9bae603b8 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_2/main.k @@ -0,0 +1,62 @@ +schema ObjectMeta: + name?: str + +schema EmptyDirVolumeSource: + medium?: str + sizeLimit?: str + +schema Volume: + name?: str + emptyDir?: EmptyDirVolumeSource + +schema PodSpec: + volumes?: [Volume] + activeDeadlineSeconds?: int + +schema PodTemplateSpec: + metadata?: ObjectMeta + spec?: PodSpec + +schema Deployment: + apiVersion?: str = "apps/v1" + kind?: str = "Deployment" + spec?: DeploymentSpec + metadata?: ObjectMeta + +schema DeploymentSpec: + template?: PodTemplateSpec + replicas?: int + +schema Server: + name?: str + workloadType?: str + replicas?: int = 1 + pod?: {str:} + deploymentModel?: Deployment = _deploymentModel + + _metadata = { + "name": name + } + _podTemplateSpec = { + "metadata": { + "name": name + }, + "spec": pod + } + if workloadType == "Deployment": + _deploymentModel = Deployment { + "metadata": _metadata, + "spec": { + "replicas": replicas, + "template": _podTemplateSpec + } + } + +server = Server { + "name": "productPage", + "workloadType": "Deployment", + "pod": { + "volumes": [{"name": "tmp", "emptyDir": {}}], + "activeDeadlineSeconds": 600 + } +} diff --git a/test/grammar/misc/emit_empty/empty_dict_2/settings.yaml b/test/grammar/misc/emit_empty/empty_dict_2/settings.yaml new file mode 100644 index 000000000..6dcdd25b7 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: -n diff --git a/test/grammar/misc/emit_empty/empty_dict_2/stdout.golden b/test/grammar/misc/emit_empty/empty_dict_2/stdout.golden new file mode 100644 index 000000000..3a819fab2 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_2/stdout.golden @@ -0,0 +1,24 @@ +server: + name: productPage + workloadType: Deployment + replicas: 1 + pod: + volumes: + - name: tmp + emptyDir: {} + activeDeadlineSeconds: 600 + deploymentModel: + apiVersion: apps/v1 + kind: Deployment + spec: + template: + metadata: + name: productPage + spec: + volumes: + - name: tmp + emptyDir: {} + activeDeadlineSeconds: 600 + replicas: 1 + metadata: + name: productPage diff --git a/test/grammar/misc/emit_empty/empty_dict_3/apicore/empty_dir_volume_source.k b/test/grammar/misc/emit_empty/empty_dict_3/apicore/empty_dir_volume_source.k new file mode 100644 index 000000000..7766263ab --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_3/apicore/empty_dir_volume_source.k @@ -0,0 +1,3 @@ +schema EmptyDirVolumeSource: + medium?: str + sizeLimit?: str diff --git a/test/grammar/misc/emit_empty/empty_dict_3/apicore/pod_spec.k b/test/grammar/misc/emit_empty/empty_dict_3/apicore/pod_spec.k new file mode 100644 index 000000000..0b752139c --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_3/apicore/pod_spec.k @@ -0,0 +1,3 @@ +schema PodSpec: + volumes?: [Volume] + activeDeadlineSeconds?: int diff --git a/test/grammar/misc/emit_empty/empty_dict_3/apicore/pod_template_spec.k b/test/grammar/misc/emit_empty/empty_dict_3/apicore/pod_template_spec.k new file mode 100644 index 000000000..f9bfadcf5 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_3/apicore/pod_template_spec.k @@ -0,0 +1,5 @@ +import ..apimachinery + +schema PodTemplateSpec: + metadata?: apimachinery.ObjectMeta + spec?: PodSpec diff --git a/test/grammar/misc/emit_empty/empty_dict_3/apicore/volume.k b/test/grammar/misc/emit_empty/empty_dict_3/apicore/volume.k new file mode 100644 index 000000000..068e6bb3c --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_3/apicore/volume.k @@ -0,0 +1,3 @@ +schema Volume: + name?: str + emptyDir?: EmptyDirVolumeSource diff --git a/test/grammar/misc/emit_empty/empty_dict_3/apimachinery/object_meta.k b/test/grammar/misc/emit_empty/empty_dict_3/apimachinery/object_meta.k new file mode 100644 index 000000000..266251d67 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_3/apimachinery/object_meta.k @@ -0,0 +1,2 @@ +schema ObjectMeta: + name?: str diff --git a/test/grammar/misc/emit_empty/empty_dict_3/apps/deployment.k b/test/grammar/misc/emit_empty/empty_dict_3/apps/deployment.k new file mode 100644 index 000000000..0351a10c8 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_3/apps/deployment.k @@ -0,0 +1,7 @@ +import ..apimachinery + +schema Deployment: + apiVersion?: str = "apps/v1" + kind?: str = "Deployment" + spec?: DeploymentSpec + metadata?: apimachinery.ObjectMeta diff --git a/test/grammar/misc/emit_empty/empty_dict_3/apps/deployment_spec.k b/test/grammar/misc/emit_empty/empty_dict_3/apps/deployment_spec.k new file mode 100644 index 000000000..e5a472b24 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_3/apps/deployment_spec.k @@ -0,0 +1,5 @@ +import ..apicore + +schema DeploymentSpec: + template?: apicore.PodTemplateSpec + replicas?: int diff --git a/test/grammar/misc/emit_empty/empty_dict_3/kcl.mod b/test/grammar/misc/emit_empty/empty_dict_3/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/misc/emit_empty/empty_dict_3/main.k b/test/grammar/misc/emit_empty/empty_dict_3/main.k new file mode 100644 index 000000000..c07402104 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_3/main.k @@ -0,0 +1,10 @@ +import .models + +server = models.Server { + "name": "productPage", + "workloadType": "Deployment", + "pod": { + "volumes": [{"name": "tmp", "emptyDir": {}}], + "activeDeadlineSeconds": 600 + } +} diff --git a/test/grammar/misc/emit_empty/empty_dict_3/models/server.k b/test/grammar/misc/emit_empty/empty_dict_3/models/server.k new file mode 100644 index 000000000..1fc8a9127 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_3/models/server.k @@ -0,0 +1,26 @@ +import ..apps + +schema Server: + name?: str + workloadType?: str + replicas?: int = 1 + pod?: {str:} + deploymentModel?: apps.Deployment = _deploymentModel + + _metadata = { + "name": name + } + _podTemplateSpec = { + "metadata": { + "name": name + }, + "spec": pod + } + if workloadType == "Deployment": + _deploymentModel = apps.Deployment { + "metadata": _metadata, + "spec": { + "replicas": replicas, + "template": _podTemplateSpec + } + } diff --git a/test/grammar/misc/emit_empty/empty_dict_3/settings.yaml b/test/grammar/misc/emit_empty/empty_dict_3/settings.yaml new file mode 100644 index 000000000..6dcdd25b7 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_3/settings.yaml @@ -0,0 +1 @@ +kcl_options: -n diff --git a/test/grammar/misc/emit_empty/empty_dict_3/stdout.golden b/test/grammar/misc/emit_empty/empty_dict_3/stdout.golden new file mode 100644 index 000000000..3a819fab2 --- /dev/null +++ b/test/grammar/misc/emit_empty/empty_dict_3/stdout.golden @@ -0,0 +1,24 @@ +server: + name: productPage + workloadType: Deployment + replicas: 1 + pod: + volumes: + - name: tmp + emptyDir: {} + activeDeadlineSeconds: 600 + deploymentModel: + apiVersion: apps/v1 + kind: Deployment + spec: + template: + metadata: + name: productPage + spec: + volumes: + - name: tmp + emptyDir: {} + activeDeadlineSeconds: 600 + replicas: 1 + metadata: + name: productPage diff --git a/test/grammar/misc/empty_file/main.k b/test/grammar/misc/empty_file/main.k new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/misc/empty_file/stdout.golden b/test/grammar/misc/empty_file/stdout.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/misc/no_line_terminator/main.k b/test/grammar/misc/no_line_terminator/main.k new file mode 100644 index 000000000..1337a530c --- /dev/null +++ b/test/grammar/misc/no_line_terminator/main.k @@ -0,0 +1 @@ +a = 1 diff --git a/test/grammar/misc/no_line_terminator/stdout.golden b/test/grammar/misc/no_line_terminator/stdout.golden new file mode 100644 index 000000000..a8926a52d --- /dev/null +++ b/test/grammar/misc/no_line_terminator/stdout.golden @@ -0,0 +1 @@ +a: 1 diff --git a/test/grammar/misc/only_line_continuation/test_0/main.k b/test/grammar/misc/only_line_continuation/test_0/main.k new file mode 100644 index 000000000..57ddad2ae --- /dev/null +++ b/test/grammar/misc/only_line_continuation/test_0/main.k @@ -0,0 +1 @@ +\ diff --git a/test/grammar/misc/only_line_continuation/test_0/stdout.golden b/test/grammar/misc/only_line_continuation/test_0/stdout.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/misc/only_line_continuation/test_1/main.k b/test/grammar/misc/only_line_continuation/test_1/main.k new file mode 100644 index 000000000..b7d5379f9 --- /dev/null +++ b/test/grammar/misc/only_line_continuation/test_1/main.k @@ -0,0 +1 @@ +\ \ No newline at end of file diff --git a/test/grammar/misc/only_line_continuation/test_1/stdout.golden b/test/grammar/misc/only_line_continuation/test_1/stdout.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/misc/only_line_continuation/test_2/main.k b/test/grammar/misc/only_line_continuation/test_2/main.k new file mode 100644 index 000000000..7f8a04ba1 --- /dev/null +++ b/test/grammar/misc/only_line_continuation/test_2/main.k @@ -0,0 +1,2 @@ +\ +\ \ No newline at end of file diff --git a/test/grammar/misc/only_line_continuation/test_2/stdout.golden b/test/grammar/misc/only_line_continuation/test_2/stdout.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/misc/profile/main.k b/test/grammar/misc/profile/main.k new file mode 100644 index 000000000..80a28cdb8 --- /dev/null +++ b/test/grammar/misc/profile/main.k @@ -0,0 +1,9 @@ +c = 1 + +b = [1, 2, 3] + +a = { + "C": "c", + "B": "b", + "A": "a" +} diff --git a/test/grammar/misc/profile/settings.yaml b/test/grammar/misc/profile/settings.yaml new file mode 100644 index 000000000..b017869d8 --- /dev/null +++ b/test/grammar/misc/profile/settings.yaml @@ -0,0 +1 @@ +kcl_options: -p diff --git a/test/grammar/multi_file_compilation/complex/complex_0/main.k b/test/grammar/multi_file_compilation/complex/complex_0/main.k new file mode 100644 index 000000000..b4c3153b0 --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_0/main.k @@ -0,0 +1,3 @@ +schema Person: + name: str + age: int diff --git a/test/grammar/multi_file_compilation/complex/complex_0/pkg.k b/test/grammar/multi_file_compilation/complex/complex_0/pkg.k new file mode 100644 index 000000000..912ec6637 --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_0/pkg.k @@ -0,0 +1,4 @@ +person = Person { + name: "Alice" + age: 18 +} diff --git a/test/grammar/multi_file_compilation/complex/complex_0/settings.yaml b/test/grammar/multi_file_compilation/complex/complex_0/settings.yaml new file mode 100644 index 000000000..cf0ee535a --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: pkg.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/complex/complex_0/stdout.golden b/test/grammar/multi_file_compilation/complex/complex_0/stdout.golden new file mode 100644 index 000000000..1a9d164c4 --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_0/stdout.golden @@ -0,0 +1,3 @@ +person: + name: Alice + age: 18 diff --git a/test/grammar/multi_file_compilation/complex/complex_1/kcl.mod b/test/grammar/multi_file_compilation/complex/complex_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/multi_file_compilation/complex/complex_1/main.k b/test/grammar/multi_file_compilation/complex/complex_1/main.k new file mode 100644 index 000000000..a9aeef048 --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_1/main.k @@ -0,0 +1,3 @@ +a = 1 +b = 2 +c = 3 diff --git a/test/grammar/multi_file_compilation/complex/complex_1/pkg/pkg.k b/test/grammar/multi_file_compilation/complex/complex_1/pkg/pkg.k new file mode 100644 index 000000000..bc029980a --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_1/pkg/pkg.k @@ -0,0 +1,3 @@ +pkga = a +pkgb = b +pkgc = c diff --git a/test/grammar/multi_file_compilation/complex/complex_1/settings.yaml b/test/grammar/multi_file_compilation/complex/complex_1/settings.yaml new file mode 100644 index 000000000..996d016b5 --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: ./pkg/pkg.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/complex/complex_1/stdout.golden b/test/grammar/multi_file_compilation/complex/complex_1/stdout.golden new file mode 100644 index 000000000..e41abc5a1 --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_1/stdout.golden @@ -0,0 +1,6 @@ +a: 1 +b: 2 +c: 3 +pkga: 1 +pkgb: 2 +pkgc: 3 diff --git a/test/grammar/multi_file_compilation/complex/complex_2/kcl.mod b/test/grammar/multi_file_compilation/complex/complex_2/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/multi_file_compilation/complex/complex_2/main.k b/test/grammar/multi_file_compilation/complex/complex_2/main.k new file mode 100644 index 000000000..9cd99bbfe --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_2/main.k @@ -0,0 +1,8 @@ +schema Person: + name: str + age: int + +person = Person { + name: "Alice" + age: 18 +} diff --git a/test/grammar/multi_file_compilation/complex/complex_2/pkg.k b/test/grammar/multi_file_compilation/complex/complex_2/pkg.k new file mode 100644 index 000000000..bc26235e0 --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_2/pkg.k @@ -0,0 +1,4 @@ +schema PersonInfo[personVal]: + info: str = personVal.name + " " + str(personVal.age) + +info = PersonInfo(person) {} diff --git a/test/grammar/multi_file_compilation/complex/complex_2/settings.yaml b/test/grammar/multi_file_compilation/complex/complex_2/settings.yaml new file mode 100644 index 000000000..cf0ee535a --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: pkg.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/complex/complex_2/stdout.golden b/test/grammar/multi_file_compilation/complex/complex_2/stdout.golden new file mode 100644 index 000000000..ee874e116 --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_2/stdout.golden @@ -0,0 +1,5 @@ +person: + name: Alice + age: 18 +info: + info: Alice 18 diff --git a/test/grammar/multi_file_compilation/complex/complex_3/kcl.mod b/test/grammar/multi_file_compilation/complex/complex_3/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/multi_file_compilation/complex/complex_3/main.k b/test/grammar/multi_file_compilation/complex/complex_3/main.k new file mode 100644 index 000000000..40f3b77e9 --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_3/main.k @@ -0,0 +1,3 @@ +import pkg + +meta = pkg.pkg_meta diff --git a/test/grammar/multi_file_compilation/complex/complex_3/pkg/pkg.k b/test/grammar/multi_file_compilation/complex/complex_3/pkg/pkg.k new file mode 100644 index 000000000..981f9a8d4 --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_3/pkg/pkg.k @@ -0,0 +1 @@ +pkg_meta = 1 diff --git a/test/grammar/multi_file_compilation/complex/complex_3/settings.yaml b/test/grammar/multi_file_compilation/complex/complex_3/settings.yaml new file mode 100644 index 000000000..996d016b5 --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_3/settings.yaml @@ -0,0 +1 @@ +kcl_options: ./pkg/pkg.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/complex/complex_3/stdout.golden b/test/grammar/multi_file_compilation/complex/complex_3/stdout.golden new file mode 100644 index 000000000..a2440cb2b --- /dev/null +++ b/test/grammar/multi_file_compilation/complex/complex_3/stdout.golden @@ -0,0 +1,2 @@ +meta: 1 +pkg_meta: 1 diff --git a/test/grammar/multi_file_compilation/instances/instances_0/main.k b/test/grammar/multi_file_compilation/instances/instances_0/main.k new file mode 100644 index 000000000..2e8c4ec3b --- /dev/null +++ b/test/grammar/multi_file_compilation/instances/instances_0/main.k @@ -0,0 +1,9 @@ +schema Base: + name: str + age: int + +schema Person(Base): + name: str = "Alice" + age: int = 18 + +person = Person {} diff --git a/test/grammar/multi_file_compilation/instances/instances_0/pkg.k b/test/grammar/multi_file_compilation/instances/instances_0/pkg.k new file mode 100644 index 000000000..3ef76edcb --- /dev/null +++ b/test/grammar/multi_file_compilation/instances/instances_0/pkg.k @@ -0,0 +1 @@ +personInst = Person.instances()[0] diff --git a/test/grammar/multi_file_compilation/instances/instances_0/settings.yaml b/test/grammar/multi_file_compilation/instances/instances_0/settings.yaml new file mode 100644 index 000000000..cf0ee535a --- /dev/null +++ b/test/grammar/multi_file_compilation/instances/instances_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: pkg.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/instances/instances_0/stdout.golden b/test/grammar/multi_file_compilation/instances/instances_0/stdout.golden new file mode 100644 index 000000000..df06f630d --- /dev/null +++ b/test/grammar/multi_file_compilation/instances/instances_0/stdout.golden @@ -0,0 +1,6 @@ +person: + name: Alice + age: 18 +personInst: + name: Alice + age: 18 diff --git a/test/grammar/multi_file_compilation/instances/instances_1/main.k b/test/grammar/multi_file_compilation/instances/instances_1/main.k new file mode 100644 index 000000000..604c35291 --- /dev/null +++ b/test/grammar/multi_file_compilation/instances/instances_1/main.k @@ -0,0 +1,11 @@ +schema Person: + name: str + age: int + +_persons = [Person { + name: "Alice" + age: 18 +}, Person { + name: "Bob" + age: 10 +}] diff --git a/test/grammar/multi_file_compilation/instances/instances_1/pkg.k b/test/grammar/multi_file_compilation/instances/instances_1/pkg.k new file mode 100644 index 000000000..e439c50b4 --- /dev/null +++ b/test/grammar/multi_file_compilation/instances/instances_1/pkg.k @@ -0,0 +1,2 @@ + +instances = {person.name: person.age for person in Person.instances()} diff --git a/test/grammar/multi_file_compilation/instances/instances_1/settings.yaml b/test/grammar/multi_file_compilation/instances/instances_1/settings.yaml new file mode 100644 index 000000000..cf0ee535a --- /dev/null +++ b/test/grammar/multi_file_compilation/instances/instances_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: pkg.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/instances/instances_1/stdout.golden b/test/grammar/multi_file_compilation/instances/instances_1/stdout.golden new file mode 100644 index 000000000..ab41869e6 --- /dev/null +++ b/test/grammar/multi_file_compilation/instances/instances_1/stdout.golden @@ -0,0 +1,3 @@ +instances: + Alice: 18 + Bob: 10 diff --git a/test/grammar/multi_file_compilation/invalid/invalid_0/main.k b/test/grammar/multi_file_compilation/invalid/invalid_0/main.k new file mode 100644 index 000000000..a42ecf124 --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_0/main.k @@ -0,0 +1 @@ +list_data = [1, 2, 3] diff --git a/test/grammar/multi_file_compilation/invalid/invalid_0/pkg.k b/test/grammar/multi_file_compilation/invalid/invalid_0/pkg.k new file mode 100644 index 000000000..e9a4ac92b --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_0/pkg.k @@ -0,0 +1 @@ +list_data = 1 diff --git a/test/grammar/multi_file_compilation/invalid/invalid_0/settings.yaml b/test/grammar/multi_file_compilation/invalid/invalid_0/settings.yaml new file mode 100644 index 000000000..cf0ee535a --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: pkg.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/invalid/invalid_0/stderr.golden.py b/test/grammar/multi_file_compilation/invalid/invalid_0/stderr.golden.py new file mode 100644 index 000000000..412c13ee0 --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_0/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/pkg.k", + line_no=1, + col_no=1 + ) + ], + ), + file=sys.stdout +) + diff --git a/test/grammar/multi_file_compilation/invalid/invalid_1/main.k b/test/grammar/multi_file_compilation/invalid/invalid_1/main.k new file mode 100644 index 000000000..b4c3153b0 --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_1/main.k @@ -0,0 +1,3 @@ +schema Person: + name: str + age: int diff --git a/test/grammar/multi_file_compilation/invalid/invalid_1/pkg.k b/test/grammar/multi_file_compilation/invalid/invalid_1/pkg.k new file mode 100644 index 000000000..6f97284bb --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_1/pkg.k @@ -0,0 +1,4 @@ +person = Person { + name: "Alice" + age: "18" +} diff --git a/test/grammar/multi_file_compilation/invalid/invalid_1/settings.yaml b/test/grammar/multi_file_compilation/invalid/invalid_1/settings.yaml new file mode 100644 index 000000000..cf0ee535a --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: pkg.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/invalid/invalid_1/stderr.golden.py b/test/grammar/multi_file_compilation/invalid/invalid_1/stderr.golden.py new file mode 100644 index 000000000..f88f1fe78 --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_1/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect int", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/pkg.k", + line_no=3, + col_no=5, + arg_msg="got str(18)" + ) + ], + arg_msg="expect int, got str(18)" + ), + file=sys.stdout +) + diff --git a/test/grammar/multi_file_compilation/invalid/invalid_2/main.k b/test/grammar/multi_file_compilation/invalid/invalid_2/main.k new file mode 100644 index 000000000..c6894f552 --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_2/main.k @@ -0,0 +1 @@ +_a = 1 diff --git a/test/grammar/multi_file_compilation/invalid/invalid_2/pkg1.k b/test/grammar/multi_file_compilation/invalid/invalid_2/pkg1.k new file mode 100644 index 000000000..31929ebb0 --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_2/pkg1.k @@ -0,0 +1 @@ +a = _a diff --git a/test/grammar/multi_file_compilation/invalid/invalid_2/pkg2.k b/test/grammar/multi_file_compilation/invalid/invalid_2/pkg2.k new file mode 100644 index 000000000..367805cb8 --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_2/pkg2.k @@ -0,0 +1 @@ +a = 3 diff --git a/test/grammar/multi_file_compilation/invalid/invalid_2/settings.yaml b/test/grammar/multi_file_compilation/invalid/invalid_2/settings.yaml new file mode 100644 index 000000000..25e6c5f7c --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: pkg1.k pkg2.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/invalid/invalid_2/stderr.golden.py b/test/grammar/multi_file_compilation/invalid/invalid_2/stderr.golden.py new file mode 100644 index 000000000..afe307785 --- /dev/null +++ b/test/grammar/multi_file_compilation/invalid/invalid_2/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/pkg2.k", + line_no=1, + col_no=1 + ) + ], + ), + file=sys.stdout +) + diff --git a/test/grammar/multi_file_compilation/simple/simple_0/main.k b/test/grammar/multi_file_compilation/simple/simple_0/main.k new file mode 100644 index 000000000..a9aeef048 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_0/main.k @@ -0,0 +1,3 @@ +a = 1 +b = 2 +c = 3 diff --git a/test/grammar/multi_file_compilation/simple/simple_0/pkg.k b/test/grammar/multi_file_compilation/simple/simple_0/pkg.k new file mode 100644 index 000000000..98687190c --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_0/pkg.k @@ -0,0 +1,3 @@ +pkga = 1 +pkgb = 2 +pkgc = 3 diff --git a/test/grammar/multi_file_compilation/simple/simple_0/settings.yaml b/test/grammar/multi_file_compilation/simple/simple_0/settings.yaml new file mode 100644 index 000000000..cf0ee535a --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: pkg.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/simple/simple_0/stdout.golden b/test/grammar/multi_file_compilation/simple/simple_0/stdout.golden new file mode 100644 index 000000000..e41abc5a1 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_0/stdout.golden @@ -0,0 +1,6 @@ +a: 1 +b: 2 +c: 3 +pkga: 1 +pkgb: 2 +pkgc: 3 diff --git a/test/grammar/multi_file_compilation/simple/simple_1/main.k b/test/grammar/multi_file_compilation/simple/simple_1/main.k new file mode 100644 index 000000000..a9aeef048 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_1/main.k @@ -0,0 +1,3 @@ +a = 1 +b = 2 +c = 3 diff --git a/test/grammar/multi_file_compilation/simple/simple_1/pkg.k b/test/grammar/multi_file_compilation/simple/simple_1/pkg.k new file mode 100644 index 000000000..bc029980a --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_1/pkg.k @@ -0,0 +1,3 @@ +pkga = a +pkgb = b +pkgc = c diff --git a/test/grammar/multi_file_compilation/simple/simple_1/settings.yaml b/test/grammar/multi_file_compilation/simple/simple_1/settings.yaml new file mode 100644 index 000000000..cf0ee535a --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: pkg.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/simple/simple_1/stdout.golden b/test/grammar/multi_file_compilation/simple/simple_1/stdout.golden new file mode 100644 index 000000000..e41abc5a1 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_1/stdout.golden @@ -0,0 +1,6 @@ +a: 1 +b: 2 +c: 3 +pkga: 1 +pkgb: 2 +pkgc: 3 diff --git a/test/grammar/multi_file_compilation/simple/simple_2/main.k b/test/grammar/multi_file_compilation/simple/simple_2/main.k new file mode 100644 index 000000000..892c983e2 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_2/main.k @@ -0,0 +1,3 @@ +var = 1 +_list_data = [0] +_dict_data = {"key1": "value1"} diff --git a/test/grammar/multi_file_compilation/simple/simple_2/pkg.k b/test/grammar/multi_file_compilation/simple/simple_2/pkg.k new file mode 100644 index 000000000..1efb4fa00 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_2/pkg.k @@ -0,0 +1,4 @@ +_list_data |= [1] +_dict_data |= {"key2": "value2"} +list_data = _list_data +dict_data = _dict_data diff --git a/test/grammar/multi_file_compilation/simple/simple_2/settings.yaml b/test/grammar/multi_file_compilation/simple/simple_2/settings.yaml new file mode 100644 index 000000000..cf0ee535a --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: pkg.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/simple/simple_2/stdout.golden b/test/grammar/multi_file_compilation/simple/simple_2/stdout.golden new file mode 100644 index 000000000..1b09e3109 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_2/stdout.golden @@ -0,0 +1,6 @@ +var: 1 +list_data: +- 1 +dict_data: + key1: value1 + key2: value2 diff --git a/test/grammar/multi_file_compilation/simple/simple_3/main.k b/test/grammar/multi_file_compilation/simple/simple_3/main.k new file mode 100644 index 000000000..a9aeef048 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_3/main.k @@ -0,0 +1,3 @@ +a = 1 +b = 2 +c = 3 diff --git a/test/grammar/multi_file_compilation/simple/simple_3/pkg1.k b/test/grammar/multi_file_compilation/simple/simple_3/pkg1.k new file mode 100644 index 000000000..7da8e6d6e --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_3/pkg1.k @@ -0,0 +1,3 @@ +pkg1a = 1 +pkg1b = 2 +pkg1c = 3 diff --git a/test/grammar/multi_file_compilation/simple/simple_3/pkg2.k b/test/grammar/multi_file_compilation/simple/simple_3/pkg2.k new file mode 100644 index 000000000..e0ebc2c04 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_3/pkg2.k @@ -0,0 +1,3 @@ +pkg2a = 1 +pkg2b = 2 +pkg2c = 3 diff --git a/test/grammar/multi_file_compilation/simple/simple_3/settings.yaml b/test/grammar/multi_file_compilation/simple/simple_3/settings.yaml new file mode 100644 index 000000000..25e6c5f7c --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_3/settings.yaml @@ -0,0 +1 @@ +kcl_options: pkg1.k pkg2.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/simple/simple_3/stdout.golden b/test/grammar/multi_file_compilation/simple/simple_3/stdout.golden new file mode 100644 index 000000000..6cc32c328 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_3/stdout.golden @@ -0,0 +1,9 @@ +a: 1 +b: 2 +c: 3 +pkg1a: 1 +pkg1b: 2 +pkg1c: 3 +pkg2a: 1 +pkg2b: 2 +pkg2c: 3 diff --git a/test/grammar/multi_file_compilation/simple/simple_4/kcl.mod b/test/grammar/multi_file_compilation/simple/simple_4/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/multi_file_compilation/simple/simple_4/main.k b/test/grammar/multi_file_compilation/simple/simple_4/main.k new file mode 100644 index 000000000..d6c10fcfb --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_4/main.k @@ -0,0 +1,3 @@ +import pkg + +a = pkg.a diff --git a/test/grammar/multi_file_compilation/simple/simple_4/pkg/pkg.k b/test/grammar/multi_file_compilation/simple/simple_4/pkg/pkg.k new file mode 100644 index 000000000..223ca50d9 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_4/pkg/pkg.k @@ -0,0 +1,2 @@ +a = 1 +b = 2 diff --git a/test/grammar/multi_file_compilation/simple/simple_4/pkg1.k b/test/grammar/multi_file_compilation/simple/simple_4/pkg1.k new file mode 100644 index 000000000..b3b8d48e4 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_4/pkg1.k @@ -0,0 +1 @@ +b = pkg.b diff --git a/test/grammar/multi_file_compilation/simple/simple_4/settings.yaml b/test/grammar/multi_file_compilation/simple/simple_4/settings.yaml new file mode 100644 index 000000000..f2df415e9 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_4/settings.yaml @@ -0,0 +1 @@ +kcl_options: pkg1.k \ No newline at end of file diff --git a/test/grammar/multi_file_compilation/simple/simple_4/stdout.golden b/test/grammar/multi_file_compilation/simple/simple_4/stdout.golden new file mode 100644 index 000000000..083c5aec5 --- /dev/null +++ b/test/grammar/multi_file_compilation/simple/simple_4/stdout.golden @@ -0,0 +1,2 @@ +a: 1 +b: 2 diff --git a/test/grammar/nest_var/nest_var_0/main.k b/test/grammar/nest_var/nest_var_0/main.k new file mode 100644 index 000000000..92015802d --- /dev/null +++ b/test/grammar/nest_var/nest_var_0/main.k @@ -0,0 +1,14 @@ +schema Son: + metadata: {str:} | str + name: str + +_keyVals = { + "key1": "value" + "key2": 2 +} + +son = Son { + metadata.labels.key1: 123, + metadata.labels.key2: _keyVals, + name: "name" +} diff --git a/test/grammar/nest_var/nest_var_0/stdout.golden b/test/grammar/nest_var/nest_var_0/stdout.golden new file mode 100644 index 000000000..3ea7e3cdc --- /dev/null +++ b/test/grammar/nest_var/nest_var_0/stdout.golden @@ -0,0 +1,8 @@ +son: + metadata: + labels: + key1: 123 + key2: + key1: value + key2: 2 + name: name diff --git a/test/grammar/nest_var/nest_var_1/main.k b/test/grammar/nest_var/nest_var_1/main.k new file mode 100644 index 000000000..9d589140f --- /dev/null +++ b/test/grammar/nest_var/nest_var_1/main.k @@ -0,0 +1,15 @@ +schema KeyVal: + labels?: {str:} + +schema Son: + metadata?: {str:} | str + name?: str = None + keyVals?: KeyVal + +son = Son { + metadata.labels.key: 123, + keyVals: { + labels.key: 456 + } + keyVals.labels.key2: 789 +} diff --git a/test/grammar/nest_var/nest_var_1/stdout.golden b/test/grammar/nest_var/nest_var_1/stdout.golden new file mode 100644 index 000000000..bf5bf87f9 --- /dev/null +++ b/test/grammar/nest_var/nest_var_1/stdout.golden @@ -0,0 +1,9 @@ +son: + metadata: + labels: + key: 123 + name: null + keyVals: + labels: + key: 456 + key2: 789 diff --git a/test/grammar/nest_var/nest_var_10/main.k b/test/grammar/nest_var/nest_var_10/main.k new file mode 100644 index 000000000..54f24a435 --- /dev/null +++ b/test/grammar/nest_var/nest_var_10/main.k @@ -0,0 +1,8 @@ +alice = { + name: "Alice" + age: 18 +} +bob = { + name: "Alice" + age: 18 +} diff --git a/test/grammar/nest_var/nest_var_10/stdout.golden b/test/grammar/nest_var/nest_var_10/stdout.golden new file mode 100644 index 000000000..32915ffaf --- /dev/null +++ b/test/grammar/nest_var/nest_var_10/stdout.golden @@ -0,0 +1,6 @@ +alice: + name: Alice + age: 18 +bob: + name: Alice + age: 18 diff --git a/test/grammar/nest_var/nest_var_11/main.k b/test/grammar/nest_var/nest_var_11/main.k new file mode 100644 index 000000000..967681a14 --- /dev/null +++ b/test/grammar/nest_var/nest_var_11/main.k @@ -0,0 +1,7 @@ +data = { + metadata.labels.key: 123, + keyVals: { + labels.key: 456 + } + keyVals.labels.key2: 789 +} diff --git a/test/grammar/nest_var/nest_var_11/stdout.golden b/test/grammar/nest_var/nest_var_11/stdout.golden new file mode 100644 index 000000000..4ce8cb10a --- /dev/null +++ b/test/grammar/nest_var/nest_var_11/stdout.golden @@ -0,0 +1,8 @@ +data: + metadata: + labels: + key: 123 + keyVals: + labels: + key: 456 + key2: 789 diff --git a/test/grammar/nest_var/nest_var_12/main.k b/test/grammar/nest_var/nest_var_12/main.k new file mode 100644 index 000000000..71e53bc7f --- /dev/null +++ b/test/grammar/nest_var/nest_var_12/main.k @@ -0,0 +1,6 @@ +base = {"key": [1, 2, 3]} + +data = { + **base + key = [4, 5, 6] +} diff --git a/test/grammar/nest_var/nest_var_12/stdout.golden b/test/grammar/nest_var/nest_var_12/stdout.golden new file mode 100644 index 000000000..e1a62be4a --- /dev/null +++ b/test/grammar/nest_var/nest_var_12/stdout.golden @@ -0,0 +1,10 @@ +base: + key: + - 1 + - 2 + - 3 +data: + key: + - 4 + - 5 + - 6 diff --git a/test/grammar/nest_var/nest_var_13/main.k b/test/grammar/nest_var/nest_var_13/main.k new file mode 100644 index 000000000..58cfe82d0 --- /dev/null +++ b/test/grammar/nest_var/nest_var_13/main.k @@ -0,0 +1,7 @@ +base = {data.key: [1, 2, 3]} + +data = { + **base + if True: + data.key = [4, 5, 6] +} diff --git a/test/grammar/nest_var/nest_var_13/stdout.golden b/test/grammar/nest_var/nest_var_13/stdout.golden new file mode 100644 index 000000000..33101dd01 --- /dev/null +++ b/test/grammar/nest_var/nest_var_13/stdout.golden @@ -0,0 +1,12 @@ +base: + data: + key: + - 1 + - 2 + - 3 +data: + data: + key: + - 4 + - 5 + - 6 diff --git a/test/grammar/nest_var/nest_var_2/main.k b/test/grammar/nest_var/nest_var_2/main.k new file mode 100644 index 000000000..c917d36a3 --- /dev/null +++ b/test/grammar/nest_var/nest_var_2/main.k @@ -0,0 +1,12 @@ +schema Person: + version: str + +schema Son(Person): + metadata: {str:} | str + name: str + +son = Son { + version: "v1" + name: "Alice" + metadata.labels.key: 123 +} diff --git a/test/grammar/nest_var/nest_var_2/stdout.golden b/test/grammar/nest_var/nest_var_2/stdout.golden new file mode 100644 index 000000000..e20cdc746 --- /dev/null +++ b/test/grammar/nest_var/nest_var_2/stdout.golden @@ -0,0 +1,6 @@ +son: + version: v1 + metadata: + labels: + key: 123 + name: Alice diff --git a/test/grammar/nest_var/nest_var_3/main.k b/test/grammar/nest_var/nest_var_3/main.k new file mode 100644 index 000000000..c2e0a0917 --- /dev/null +++ b/test/grammar/nest_var/nest_var_3/main.k @@ -0,0 +1,23 @@ +schema Son: + metadata?: {str:} + name?: str + key?: str + +son1 = Son { + metadata.labels.key: 123, name: "Bob" + key: "123" +} + +son2 = Son {metadata.labels.key: 456, name: "Alice", "key": "123"} + +son3 = Son { + metadata: {} + name: "Green" + key: "key" +} + +son4 = Son { + metadata.key: "val" + name: None + key: None +} diff --git a/test/grammar/nest_var/nest_var_3/stdout.golden b/test/grammar/nest_var/nest_var_3/stdout.golden new file mode 100644 index 000000000..3afad377d --- /dev/null +++ b/test/grammar/nest_var/nest_var_3/stdout.golden @@ -0,0 +1,21 @@ +son1: + metadata: + labels: + key: 123 + name: Bob + key: '123' +son2: + metadata: + labels: + key: 456 + name: Alice + key: '123' +son3: + metadata: {} + name: Green + key: key +son4: + metadata: + key: val + name: null + key: null diff --git a/test/grammar/nest_var/nest_var_4/main.k b/test/grammar/nest_var/nest_var_4/main.k new file mode 100644 index 000000000..a424118a8 --- /dev/null +++ b/test/grammar/nest_var/nest_var_4/main.k @@ -0,0 +1,11 @@ +var = {"key": "val"} +var1 = { + "key1": "val1" + "key2": "val2" +} + +schema Person[varArg]: + labels: {str:} = var + annotations: {str:} = varArg + +person = Person(var1) {} diff --git a/test/grammar/nest_var/nest_var_4/stdout.golden b/test/grammar/nest_var/nest_var_4/stdout.golden new file mode 100644 index 000000000..c4f0ab6db --- /dev/null +++ b/test/grammar/nest_var/nest_var_4/stdout.golden @@ -0,0 +1,11 @@ +var: + key: val +var1: + key1: val1 + key2: val2 +person: + labels: + key: val + annotations: + key1: val1 + key2: val2 diff --git a/test/grammar/nest_var/nest_var_5/main.k b/test/grammar/nest_var/nest_var_5/main.k new file mode 100644 index 000000000..aac954413 --- /dev/null +++ b/test/grammar/nest_var/nest_var_5/main.k @@ -0,0 +1,12 @@ +schema Metadata: + name: str + labels: {str:} + +schema Person: + metadata: Metadata + +person = Person { + metadata.name: "person" + metadata.labels.key1: "value1" + metadata.labels.key2: "value2" +} diff --git a/test/grammar/nest_var/nest_var_5/stdout.golden b/test/grammar/nest_var/nest_var_5/stdout.golden new file mode 100644 index 000000000..d57c15960 --- /dev/null +++ b/test/grammar/nest_var/nest_var_5/stdout.golden @@ -0,0 +1,6 @@ +person: + metadata: + name: person + labels: + key1: value1 + key2: value2 diff --git a/test/grammar/nest_var/nest_var_6/main.k b/test/grammar/nest_var/nest_var_6/main.k new file mode 100644 index 000000000..fbd2e04e3 --- /dev/null +++ b/test/grammar/nest_var/nest_var_6/main.k @@ -0,0 +1,15 @@ +schema Metadata: + name: str + labels: {str:} + +schema Person: + metadata: Metadata = Metadata { + name: "default" + labels.default: 0 + } + +person = Person { + metadata.name = "person" + metadata.labels.key1: "value1" + metadata.labels.key2: "value2" +} diff --git a/test/grammar/nest_var/nest_var_6/stdout.golden b/test/grammar/nest_var/nest_var_6/stdout.golden new file mode 100644 index 000000000..8fb7ca613 --- /dev/null +++ b/test/grammar/nest_var/nest_var_6/stdout.golden @@ -0,0 +1,7 @@ +person: + metadata: + name: person + labels: + default: 0 + key1: value1 + key2: value2 diff --git a/test/grammar/nest_var/nest_var_7/main.k b/test/grammar/nest_var/nest_var_7/main.k new file mode 100644 index 000000000..8f65030d9 --- /dev/null +++ b/test/grammar/nest_var/nest_var_7/main.k @@ -0,0 +1,9 @@ +schema Person: + key: [int] + +base = {"key": [1, 2, 3]} + +person = Person { + **base + key = [4, 5, 6] +} diff --git a/test/grammar/nest_var/nest_var_7/stdout.golden b/test/grammar/nest_var/nest_var_7/stdout.golden new file mode 100644 index 000000000..afc28bcea --- /dev/null +++ b/test/grammar/nest_var/nest_var_7/stdout.golden @@ -0,0 +1,10 @@ +base: + key: + - 1 + - 2 + - 3 +person: + key: + - 4 + - 5 + - 6 diff --git a/test/grammar/nest_var/nest_var_8/main.k b/test/grammar/nest_var/nest_var_8/main.k new file mode 100644 index 000000000..7341fcbda --- /dev/null +++ b/test/grammar/nest_var/nest_var_8/main.k @@ -0,0 +1,9 @@ +schema Person: + labels: {str:} + +base = {"labels": {"key": "value"}} + +person = Person { + **base + labels.key = "override" +} diff --git a/test/grammar/nest_var/nest_var_8/stdout.golden b/test/grammar/nest_var/nest_var_8/stdout.golden new file mode 100644 index 000000000..7c40729d9 --- /dev/null +++ b/test/grammar/nest_var/nest_var_8/stdout.golden @@ -0,0 +1,6 @@ +base: + labels: + key: value +person: + labels: + key: override diff --git a/test/grammar/nest_var/nest_var_9/main.k b/test/grammar/nest_var/nest_var_9/main.k new file mode 100644 index 000000000..611b628df --- /dev/null +++ b/test/grammar/nest_var/nest_var_9/main.k @@ -0,0 +1,11 @@ +schema Person: + [str]: str + name: str = "Alice" + +person = { + "name": "Alice" +} + +data = Person { + person["name"]: person["name"].upper() +} diff --git a/test/grammar/nest_var/nest_var_9/stdout.golden b/test/grammar/nest_var/nest_var_9/stdout.golden new file mode 100644 index 000000000..694d8dc38 --- /dev/null +++ b/test/grammar/nest_var/nest_var_9/stdout.golden @@ -0,0 +1,5 @@ +person: + name: Alice +data: + name: Alice + Alice: ALICE diff --git a/test/grammar/nest_var/nest_var_fail_0/main.k b/test/grammar/nest_var/nest_var_fail_0/main.k new file mode 100644 index 000000000..831cff497 --- /dev/null +++ b/test/grammar/nest_var/nest_var_fail_0/main.k @@ -0,0 +1,6 @@ +schema Person: + name: str + +person = Person { + name.name: "Alice" +} diff --git a/test/grammar/nest_var/nest_var_fail_0/stderr.golden.py b/test/grammar/nest_var/nest_var_fail_0/stderr.golden.py new file mode 100644 index 000000000..375e24f9b --- /dev/null +++ b/test/grammar/nest_var/nest_var_fail_0/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect str", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + arg_msg="got {str(name):str(Alice)}" + ) + ], + arg_msg="expect str, got {str(name):str(Alice)}" + ), + file=sys.stdout +) + diff --git a/test/grammar/nest_var/nest_var_fail_1/main.k b/test/grammar/nest_var/nest_var_fail_1/main.k new file mode 100644 index 000000000..0a0ec4018 --- /dev/null +++ b/test/grammar/nest_var/nest_var_fail_1/main.k @@ -0,0 +1,9 @@ +schema Name: + name: str + +schema Person: + name: Name + +person = Person { + name.err_name: "Alice" +} diff --git a/test/grammar/nest_var/nest_var_fail_1/stderr.golden.py b/test/grammar/nest_var/nest_var_fail_1/stderr.golden.py new file mode 100644 index 000000000..c541677e3 --- /dev/null +++ b/test/grammar/nest_var/nest_var_fail_1/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CannotAddMembers_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=8, + col_no=5, + arg_msg="'err_name' is not defined in schema 'Name'" + ), + ], + arg_msg="Cannot add member 'err_name' to schema 'Name'") + , file=sys.stdout +) + diff --git a/test/grammar/option/complex_type_option/main.k b/test/grammar/option/complex_type_option/main.k new file mode 100644 index 000000000..58254b7d0 --- /dev/null +++ b/test/grammar/option/complex_type_option/main.k @@ -0,0 +1,5 @@ +a = option("key1") +b = option("key2") +c = option("key3") +d = option("key4") +e = option("key5") diff --git a/test/grammar/option/complex_type_option/settings.yaml b/test/grammar/option/complex_type_option/settings.yaml new file mode 100644 index 000000000..87b5c02f2 --- /dev/null +++ b/test/grammar/option/complex_type_option/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D key1=val -D key2=2 -D key3=4.4 -D key4=[1,2,3] -D key5={'key':'value'} -S :app.value diff --git a/test/grammar/option/complex_type_option/stdout.golden b/test/grammar/option/complex_type_option/stdout.golden new file mode 100644 index 000000000..484cb3e62 --- /dev/null +++ b/test/grammar/option/complex_type_option/stdout.golden @@ -0,0 +1,9 @@ +a: val +b: 2 +c: 4.4 +d: +- 1 +- 2 +- 3 +e: + key: value \ No newline at end of file diff --git a/test/grammar/option/complex_type_option_1/main.k b/test/grammar/option/complex_type_option_1/main.k new file mode 100644 index 000000000..9f7393cb5 --- /dev/null +++ b/test/grammar/option/complex_type_option_1/main.k @@ -0,0 +1,6 @@ +schema Server: + dnsConfig: {str:} + +serverInstance = Server { + dnsConfig: option("dns") +} diff --git a/test/grammar/option/complex_type_option_1/settings.yaml b/test/grammar/option/complex_type_option_1/settings.yaml new file mode 100644 index 000000000..e64a3666a --- /dev/null +++ b/test/grammar/option/complex_type_option_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D dns={"nameservers":["172.17.255.252","172.17.255.253"],"searches":["test1.com","test2.com"]} diff --git a/test/grammar/option/complex_type_option_1/stdout.golden b/test/grammar/option/complex_type_option_1/stdout.golden new file mode 100644 index 000000000..560afee6e --- /dev/null +++ b/test/grammar/option/complex_type_option_1/stdout.golden @@ -0,0 +1,8 @@ +serverInstance: + dnsConfig: + nameservers: + - 172.17.255.252 + - 172.17.255.253 + searches: + - test1.com + - test2.com diff --git a/test/grammar/option/file_empty_options/main.k b/test/grammar/option/file_empty_options/main.k new file mode 100644 index 000000000..d90d8332d --- /dev/null +++ b/test/grammar/option/file_empty_options/main.k @@ -0,0 +1 @@ +a = option("key") or 1 diff --git a/test/grammar/option/file_empty_options/settings.yaml b/test/grammar/option/file_empty_options/settings.yaml new file mode 100644 index 000000000..fdea6ff69 --- /dev/null +++ b/test/grammar/option/file_empty_options/settings.yaml @@ -0,0 +1 @@ +kcl_options: -Y temp.yaml diff --git a/test/grammar/option/file_empty_options/stdout.golden b/test/grammar/option/file_empty_options/stdout.golden new file mode 100644 index 000000000..a8926a52d --- /dev/null +++ b/test/grammar/option/file_empty_options/stdout.golden @@ -0,0 +1 @@ +a: 1 diff --git a/test/grammar/option/file_empty_options/temp.yaml b/test/grammar/option/file_empty_options/temp.yaml new file mode 100644 index 000000000..2beb8a49e --- /dev/null +++ b/test/grammar/option/file_empty_options/temp.yaml @@ -0,0 +1 @@ +kcl_options: \ No newline at end of file diff --git a/test/grammar/option/file_options/main.k b/test/grammar/option/file_options/main.k new file mode 100644 index 000000000..8c5f7eac8 --- /dev/null +++ b/test/grammar/option/file_options/main.k @@ -0,0 +1,5 @@ +a = option("key0") +b = option("key1") +c = option("key_number") +d = option("key_dict") +e = option("key_list") diff --git a/test/grammar/option/file_options/settings.yaml b/test/grammar/option/file_options/settings.yaml new file mode 100644 index 000000000..bae5397e5 --- /dev/null +++ b/test/grammar/option/file_options/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D key0=value0 -D key1=value1 -Y temp.yaml diff --git a/test/grammar/option/file_options/stdout.golden b/test/grammar/option/file_options/stdout.golden new file mode 100644 index 000000000..faf17f7fe --- /dev/null +++ b/test/grammar/option/file_options/stdout.golden @@ -0,0 +1,9 @@ +a: value0 +b: value1 +c: 1 +d: + innerDictKey: innerDictValue +e: +- 1 +- 2 +- 3 \ No newline at end of file diff --git a/test/grammar/option/file_options/temp.yaml b/test/grammar/option/file_options/temp.yaml new file mode 100644 index 000000000..8cb5d40d5 --- /dev/null +++ b/test/grammar/option/file_options/temp.yaml @@ -0,0 +1,11 @@ +kcl_options: + - key: key_number + value: 1 + - key: key_dict + value: + innerDictKey: innerDictValue + - key: key_list + value: + - 1 + - 2 + - 3 diff --git a/test/grammar/option/file_options_fail_0/main.k b/test/grammar/option/file_options_fail_0/main.k new file mode 100644 index 000000000..d15b81b62 --- /dev/null +++ b/test/grammar/option/file_options_fail_0/main.k @@ -0,0 +1 @@ +a = option("key") diff --git a/test/grammar/option/file_options_fail_0/settings.yaml b/test/grammar/option/file_options_fail_0/settings.yaml new file mode 100644 index 000000000..fdea6ff69 --- /dev/null +++ b/test/grammar/option/file_options_fail_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -Y temp.yaml diff --git a/test/grammar/option/file_options_fail_0/stderr.golden.py b/test/grammar/option/file_options_fail_0/stderr.golden.py new file mode 100644 index 000000000..5dac81fe3 --- /dev/null +++ b/test/grammar/option/file_options_fail_0/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error + +file = 'temp.yaml' + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IllegalArgumentError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=file, + ) + ], + arg_msg="Invalid configuration in setting file:\nsetting file content should be a mapping, got: 1" + ), + file=sys.stdout +) + + diff --git a/test/grammar/option/file_options_fail_0/temp.yaml b/test/grammar/option/file_options_fail_0/temp.yaml new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/grammar/option/file_options_fail_0/temp.yaml @@ -0,0 +1 @@ +1 diff --git a/test/grammar/option/file_options_fail_1/main.k b/test/grammar/option/file_options_fail_1/main.k new file mode 100644 index 000000000..d15b81b62 --- /dev/null +++ b/test/grammar/option/file_options_fail_1/main.k @@ -0,0 +1 @@ +a = option("key") diff --git a/test/grammar/option/file_options_fail_1/settings.yaml b/test/grammar/option/file_options_fail_1/settings.yaml new file mode 100644 index 000000000..fdea6ff69 --- /dev/null +++ b/test/grammar/option/file_options_fail_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -Y temp.yaml diff --git a/test/grammar/option/file_options_fail_1/stderr.golden.py b/test/grammar/option/file_options_fail_1/stderr.golden.py new file mode 100644 index 000000000..e77a4cf22 --- /dev/null +++ b/test/grammar/option/file_options_fail_1/stderr.golden.py @@ -0,0 +1,27 @@ +import sys +import kclvm.kcl.error as kcl_error + +file = 'temp.yaml' + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IllegalArgumentError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=file, + ) + ], + arg_msg="""Invalid configuration in setting file: +invalid kcl_options value, should be list of key/value mapping. +=== A good example will be:=== +kcl_options: + - key: myArg # the option key must be a string value + value: myArgValue +=== got: === +kcl_options: + key: key + value: value +""" + ), + file=sys.stdout +) diff --git a/test/grammar/option/file_options_fail_1/temp.yaml b/test/grammar/option/file_options_fail_1/temp.yaml new file mode 100644 index 000000000..05097c7ef --- /dev/null +++ b/test/grammar/option/file_options_fail_1/temp.yaml @@ -0,0 +1,3 @@ +kcl_options: + key: key + value: value \ No newline at end of file diff --git a/test/grammar/option/file_options_fail_2/main.k b/test/grammar/option/file_options_fail_2/main.k new file mode 100644 index 000000000..d15b81b62 --- /dev/null +++ b/test/grammar/option/file_options_fail_2/main.k @@ -0,0 +1 @@ +a = option("key") diff --git a/test/grammar/option/file_options_fail_2/settings.yaml b/test/grammar/option/file_options_fail_2/settings.yaml new file mode 100644 index 000000000..fdea6ff69 --- /dev/null +++ b/test/grammar/option/file_options_fail_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: -Y temp.yaml diff --git a/test/grammar/option/file_options_fail_2/stderr.golden.py b/test/grammar/option/file_options_fail_2/stderr.golden.py new file mode 100644 index 000000000..bd2c44876 --- /dev/null +++ b/test/grammar/option/file_options_fail_2/stderr.golden.py @@ -0,0 +1,26 @@ +import sys +import kclvm.kcl.error as kcl_error + +file = 'temp.yaml' + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IllegalArgumentError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=file, + ) + ], + arg_msg="""Invalid configuration in setting file: +invalid kcl_options value, should be list of key/value mapping. +=== A good example will be:=== +kcl_options: + - key: myArg # the option key must be a string value + value: myArgValue +=== got: === +kcl_options: +- key +""" + ), + file=sys.stdout +) diff --git a/test/grammar/option/file_options_fail_2/temp.yaml b/test/grammar/option/file_options_fail_2/temp.yaml new file mode 100644 index 000000000..6648dcabb --- /dev/null +++ b/test/grammar/option/file_options_fail_2/temp.yaml @@ -0,0 +1,2 @@ +kcl_options: + - key \ No newline at end of file diff --git a/test/grammar/option/file_options_fail_3/main.k b/test/grammar/option/file_options_fail_3/main.k new file mode 100644 index 000000000..d15b81b62 --- /dev/null +++ b/test/grammar/option/file_options_fail_3/main.k @@ -0,0 +1 @@ +a = option("key") diff --git a/test/grammar/option/file_options_fail_3/settings.yaml b/test/grammar/option/file_options_fail_3/settings.yaml new file mode 100644 index 000000000..fdea6ff69 --- /dev/null +++ b/test/grammar/option/file_options_fail_3/settings.yaml @@ -0,0 +1 @@ +kcl_options: -Y temp.yaml diff --git a/test/grammar/option/file_options_fail_3/stderr.golden.py b/test/grammar/option/file_options_fail_3/stderr.golden.py new file mode 100644 index 000000000..30e113c52 --- /dev/null +++ b/test/grammar/option/file_options_fail_3/stderr.golden.py @@ -0,0 +1,25 @@ +import sys +import kclvm.kcl.error as kcl_error + +file = 'temp.yaml' + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IllegalArgumentError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=file, + ) + ], + arg_msg="""Invalid yaml content of setting file: +while scanning a quoted scalar + in "", line 1, column 1: + " + ^ (line: 1) +found unexpected end of stream + in "", line 1, column 2: + " + ^ (line: 1)""" + ), + file=sys.stdout +) diff --git a/test/grammar/option/file_options_fail_3/temp.yaml b/test/grammar/option/file_options_fail_3/temp.yaml new file mode 100644 index 000000000..9d68933c4 --- /dev/null +++ b/test/grammar/option/file_options_fail_3/temp.yaml @@ -0,0 +1 @@ +" \ No newline at end of file diff --git a/test/grammar/option/file_options_fail_4/main.k b/test/grammar/option/file_options_fail_4/main.k new file mode 100644 index 000000000..d15b81b62 --- /dev/null +++ b/test/grammar/option/file_options_fail_4/main.k @@ -0,0 +1 @@ +a = option("key") diff --git a/test/grammar/option/file_options_fail_4/settings.yaml b/test/grammar/option/file_options_fail_4/settings.yaml new file mode 100644 index 000000000..fdea6ff69 --- /dev/null +++ b/test/grammar/option/file_options_fail_4/settings.yaml @@ -0,0 +1 @@ +kcl_options: -Y temp.yaml diff --git a/test/grammar/option/file_options_fail_4/stderr.golden.py b/test/grammar/option/file_options_fail_4/stderr.golden.py new file mode 100644 index 000000000..21055f494 --- /dev/null +++ b/test/grammar/option/file_options_fail_4/stderr.golden.py @@ -0,0 +1,18 @@ +import sys +import kclvm.kcl.error as kcl_error + +file = 'temp.yaml' + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IllegalArgumentError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=file, + ) + ], + arg_msg="""Invalid configuration in setting file: +setting file content should be a mapping, got: :2""" + ), + file=sys.stdout +) diff --git a/test/grammar/option/file_options_fail_4/temp.yaml b/test/grammar/option/file_options_fail_4/temp.yaml new file mode 100644 index 000000000..ae4d31dce --- /dev/null +++ b/test/grammar/option/file_options_fail_4/temp.yaml @@ -0,0 +1 @@ +:2 \ No newline at end of file diff --git a/test/grammar/option/invalid_option_fail_0/main.k b/test/grammar/option/invalid_option_fail_0/main.k new file mode 100644 index 000000000..d15b81b62 --- /dev/null +++ b/test/grammar/option/invalid_option_fail_0/main.k @@ -0,0 +1 @@ +a = option("key") diff --git a/test/grammar/option/invalid_option_fail_0/settings.yaml b/test/grammar/option/invalid_option_fail_0/settings.yaml new file mode 100644 index 000000000..195e8253b --- /dev/null +++ b/test/grammar/option/invalid_option_fail_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D key=value= diff --git a/test/grammar/option/invalid_option_fail_0/stderr.golden.py b/test/grammar/option/invalid_option_fail_0/stderr.golden.py new file mode 100644 index 000000000..3ee0b6cf3 --- /dev/null +++ b/test/grammar/option/invalid_option_fail_0/stderr.golden.py @@ -0,0 +1,15 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IllegalArgumentError_TYPE, + arg_msg="Invalid value for option \"--argument(-D)\": should be in = pattern, got: key=value=" + ), + file=sys.stdout +) + diff --git a/test/grammar/option/invalid_option_fail_1/main.k b/test/grammar/option/invalid_option_fail_1/main.k new file mode 100644 index 000000000..d15b81b62 --- /dev/null +++ b/test/grammar/option/invalid_option_fail_1/main.k @@ -0,0 +1 @@ +a = option("key") diff --git a/test/grammar/option/invalid_option_fail_1/settings.yaml b/test/grammar/option/invalid_option_fail_1/settings.yaml new file mode 100644 index 000000000..568875d00 --- /dev/null +++ b/test/grammar/option/invalid_option_fail_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D key= diff --git a/test/grammar/option/invalid_option_fail_1/stderr.golden.py b/test/grammar/option/invalid_option_fail_1/stderr.golden.py new file mode 100644 index 000000000..a77745145 --- /dev/null +++ b/test/grammar/option/invalid_option_fail_1/stderr.golden.py @@ -0,0 +1,15 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IllegalArgumentError_TYPE, + arg_msg="Invalid value for option \"--argument(-D)\": should be in = pattern, got: key=" + ), + file=sys.stdout +) + diff --git a/test/grammar/option/invalid_option_fail_2/main.k b/test/grammar/option/invalid_option_fail_2/main.k new file mode 100644 index 000000000..d15b81b62 --- /dev/null +++ b/test/grammar/option/invalid_option_fail_2/main.k @@ -0,0 +1 @@ +a = option("key") diff --git a/test/grammar/option/invalid_option_fail_2/settings.yaml b/test/grammar/option/invalid_option_fail_2/settings.yaml new file mode 100644 index 000000000..a3333f3fa --- /dev/null +++ b/test/grammar/option/invalid_option_fail_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D =123 diff --git a/test/grammar/option/invalid_option_fail_2/stderr.golden.py b/test/grammar/option/invalid_option_fail_2/stderr.golden.py new file mode 100644 index 000000000..2b9f41c64 --- /dev/null +++ b/test/grammar/option/invalid_option_fail_2/stderr.golden.py @@ -0,0 +1,11 @@ +import sys +import kclvm.kcl.error as kcl_error + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IllegalArgumentError_TYPE, + arg_msg="Invalid value for option \"--argument(-D)\": Invalid option name: ''. should be a non-empty string" + ), + file=sys.stdout +) + diff --git a/test/grammar/option/multiple_options/main.k b/test/grammar/option/multiple_options/main.k new file mode 100644 index 000000000..70e389628 --- /dev/null +++ b/test/grammar/option/multiple_options/main.k @@ -0,0 +1,2 @@ +a = option("key0") +b = option("key1") diff --git a/test/grammar/option/multiple_options/settings.yaml b/test/grammar/option/multiple_options/settings.yaml new file mode 100644 index 000000000..2b4f424d8 --- /dev/null +++ b/test/grammar/option/multiple_options/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D key0=value0 -D key1=value1 diff --git a/test/grammar/option/multiple_options/stdout.golden b/test/grammar/option/multiple_options/stdout.golden new file mode 100644 index 000000000..4cfb00ad4 --- /dev/null +++ b/test/grammar/option/multiple_options/stdout.golden @@ -0,0 +1,2 @@ +a: value0 +b: value1 \ No newline at end of file diff --git a/test/grammar/option/no_option/main.k b/test/grammar/option/no_option/main.k new file mode 100644 index 000000000..d15b81b62 --- /dev/null +++ b/test/grammar/option/no_option/main.k @@ -0,0 +1 @@ +a = option("key") diff --git a/test/grammar/option/no_option/settings.yaml b/test/grammar/option/no_option/settings.yaml new file mode 100644 index 000000000..6dcdd25b7 --- /dev/null +++ b/test/grammar/option/no_option/settings.yaml @@ -0,0 +1 @@ +kcl_options: -n diff --git a/test/grammar/option/no_option/stdout.golden b/test/grammar/option/no_option/stdout.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/option/option_help_0/main.k b/test/grammar/option/option_help_0/main.k new file mode 100644 index 000000000..bb8d12465 --- /dev/null +++ b/test/grammar/option/option_help_0/main.k @@ -0,0 +1,9 @@ +name = option("name", required=True, help="set name value") +a = option("a", default=42, help="set a value") +b = option("b", help="set b value") + +# -D obj="{'a':1,'b':1}" +obj = option("obj") + +# -D obj="[1,2,3]" +obj2 = option("obj2") diff --git a/test/grammar/option/option_help_0/settings.yaml b/test/grammar/option/option_help_0/settings.yaml new file mode 100644 index 000000000..403895928 --- /dev/null +++ b/test/grammar/option/option_help_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -l diff --git a/test/grammar/option/option_help_0/stdout.golden b/test/grammar/option/option_help_0/stdout.golden new file mode 100644 index 000000000..0da235e23 --- /dev/null +++ b/test/grammar/option/option_help_0/stdout.golden @@ -0,0 +1,6 @@ +option list: + -D name=? (required) set name value + -D a=42 set a value + -D b=? set b value + -D obj=? + -D obj2=? diff --git a/test/grammar/option/option_help_fail_0/main.k b/test/grammar/option/option_help_fail_0/main.k new file mode 100644 index 000000000..bb8d12465 --- /dev/null +++ b/test/grammar/option/option_help_fail_0/main.k @@ -0,0 +1,9 @@ +name = option("name", required=True, help="set name value") +a = option("a", default=42, help="set a value") +b = option("b", help="set b value") + +# -D obj="{'a':1,'b':1}" +obj = option("obj") + +# -D obj="[1,2,3]" +obj2 = option("obj2") diff --git a/test/grammar/option/option_help_fail_0/settings.yaml b/test/grammar/option/option_help_fail_0/settings.yaml new file mode 100644 index 000000000..4e71a2930 --- /dev/null +++ b/test/grammar/option/option_help_fail_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: diff --git a/test/grammar/option/option_help_fail_0/stderr.golden.py b/test/grammar/option/option_help_fail_0/stderr.golden.py new file mode 100644 index 000000000..d70de9542 --- /dev/null +++ b/test/grammar/option/option_help_fail_0/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) +file = os.path.join(cwd, 'main.k') + +name = 'name' + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=file, + line_no=1, + ), + ], + arg_msg=f"option('{name}') must be initialized, try '-D {name}=?' argument") + , file=sys.stdout +) + diff --git a/test/grammar/option/option_help_in_schema/_main.k b/test/grammar/option/option_help_in_schema/_main.k new file mode 100644 index 000000000..662c01427 --- /dev/null +++ b/test/grammar/option/option_help_in_schema/_main.k @@ -0,0 +1,5 @@ + +schema A: + name?: str = option('name', type='str') + +a = A {} diff --git a/test/grammar/option/option_help_in_schema/settings.yaml b/test/grammar/option/option_help_in_schema/settings.yaml new file mode 100644 index 000000000..403895928 --- /dev/null +++ b/test/grammar/option/option_help_in_schema/settings.yaml @@ -0,0 +1 @@ +kcl_options: -l diff --git a/test/grammar/option/option_help_in_schema/stdout.golden b/test/grammar/option/option_help_in_schema/stdout.golden new file mode 100644 index 000000000..9f1054e9f --- /dev/null +++ b/test/grammar/option/option_help_in_schema/stdout.golden @@ -0,0 +1,2 @@ +option list: + -D name=? (str) diff --git a/test/grammar/option/option_help_type_0/_main.k b/test/grammar/option/option_help_type_0/_main.k new file mode 100644 index 000000000..d361a09d9 --- /dev/null +++ b/test/grammar/option/option_help_type_0/_main.k @@ -0,0 +1,12 @@ +name = option("name", type='str', required=True, help="set name value") +a = option("a", default=42, help="set a value") +b = option("b", help="set b value") + +# -D c="{'a':2,'b':3}" +c = option("c", default='{"a":1}', type='dict', help="set b value") + +# -D obj="{'a':1,'b':1}" +obj = option("obj") + +# -D obj="[1,2,3]" +obj2 = option("obj2") diff --git a/test/grammar/option/option_help_type_0/settings.yaml b/test/grammar/option/option_help_type_0/settings.yaml new file mode 100644 index 000000000..403895928 --- /dev/null +++ b/test/grammar/option/option_help_type_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -l diff --git a/test/grammar/option/option_help_type_0/stdout.golden b/test/grammar/option/option_help_type_0/stdout.golden new file mode 100644 index 000000000..2c47cf89b --- /dev/null +++ b/test/grammar/option/option_help_type_0/stdout.golden @@ -0,0 +1,7 @@ +option list: + -D name=? (str,required) set name value + -D a=42 set a value + -D b=? set b value + -D c={"a":1} (dict) set b value + -D obj=? + -D obj2=? diff --git a/test/grammar/option/option_help_type_fail_0/main.k b/test/grammar/option/option_help_type_fail_0/main.k new file mode 100644 index 000000000..6561c1191 --- /dev/null +++ b/test/grammar/option/option_help_type_fail_0/main.k @@ -0,0 +1,12 @@ +name = option("name", type='str', required=True, help="set name value") +a = option("a", default=42, help="set a value") +b = option("b", help="set b value") + +# -D c="{'a':2,'b':3}" +c = option("c", default='abc', type='dict', help="set b value") + +# -D obj="{'a':1,'b':1}" +obj = option("obj") + +# -D obj="[1,2,3]" +obj2 = option("obj2") diff --git a/test/grammar/option/option_help_type_fail_0/settings.yaml b/test/grammar/option/option_help_type_fail_0/settings.yaml new file mode 100644 index 000000000..4e71a2930 --- /dev/null +++ b/test/grammar/option/option_help_type_fail_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: diff --git a/test/grammar/option/option_help_type_fail_0/stderr.golden.py b/test/grammar/option/option_help_type_fail_0/stderr.golden.py new file mode 100644 index 000000000..f54aea77c --- /dev/null +++ b/test/grammar/option/option_help_type_fail_0/stderr.golden.py @@ -0,0 +1,20 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) +file = os.path.join(cwd, 'main.k') + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=file, + line_no=1, + ), + ], + arg_msg=f"option('name') must be initialized, try '-D name=?' argument") + , file=sys.stdout +) + diff --git a/test/grammar/option/option_same_key/main.k b/test/grammar/option/option_same_key/main.k new file mode 100644 index 000000000..d15b81b62 --- /dev/null +++ b/test/grammar/option/option_same_key/main.k @@ -0,0 +1 @@ +a = option("key") diff --git a/test/grammar/option/option_same_key/settings.yaml b/test/grammar/option/option_same_key/settings.yaml new file mode 100644 index 000000000..5c23a629f --- /dev/null +++ b/test/grammar/option/option_same_key/settings.yaml @@ -0,0 +1 @@ +kcl_options: -n -D key=val -D key=1 -D key=2 diff --git a/test/grammar/option/option_same_key/stdout.golden b/test/grammar/option/option_same_key/stdout.golden new file mode 100644 index 000000000..9dfc208df --- /dev/null +++ b/test/grammar/option/option_same_key/stdout.golden @@ -0,0 +1 @@ +a: 2 diff --git a/test/grammar/option/simple_0/main.k b/test/grammar/option/simple_0/main.k new file mode 100644 index 000000000..c4450ee25 --- /dev/null +++ b/test/grammar/option/simple_0/main.k @@ -0,0 +1,2 @@ +a = option("key1") +b = option("key2") diff --git a/test/grammar/option/simple_0/settings.yaml b/test/grammar/option/simple_0/settings.yaml new file mode 100644 index 000000000..61b492a51 --- /dev/null +++ b/test/grammar/option/simple_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D key1=0 -D key2=False diff --git a/test/grammar/option/simple_0/stdout.golden b/test/grammar/option/simple_0/stdout.golden new file mode 100644 index 000000000..be522024f --- /dev/null +++ b/test/grammar/option/simple_0/stdout.golden @@ -0,0 +1,2 @@ +a: 0 +b: false diff --git a/test/grammar/option/simple_1/main.k b/test/grammar/option/simple_1/main.k new file mode 100644 index 000000000..4dfc8efa8 --- /dev/null +++ b/test/grammar/option/simple_1/main.k @@ -0,0 +1,4 @@ +a = option("key1") +b = option("key2") +c = option("key3") +d = option("key4") diff --git a/test/grammar/option/simple_1/settings.yaml b/test/grammar/option/simple_1/settings.yaml new file mode 100644 index 000000000..4542f110c --- /dev/null +++ b/test/grammar/option/simple_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D key1=0 -D key2=False -D key3=0.0 -D key4=.0 diff --git a/test/grammar/option/simple_1/stdout.golden b/test/grammar/option/simple_1/stdout.golden new file mode 100644 index 000000000..0cfe38fa6 --- /dev/null +++ b/test/grammar/option/simple_1/stdout.golden @@ -0,0 +1,4 @@ +a: 0 +b: false +c: 0.0 +d: 0.0 diff --git a/test/grammar/option/simple_2/main.k b/test/grammar/option/simple_2/main.k new file mode 100644 index 000000000..f6e747d96 --- /dev/null +++ b/test/grammar/option/simple_2/main.k @@ -0,0 +1 @@ +id = option("id") diff --git a/test/grammar/option/simple_2/settings.yaml b/test/grammar/option/simple_2/settings.yaml new file mode 100644 index 000000000..0e8dd7f2c --- /dev/null +++ b/test/grammar/option/simple_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D id='000000123456' diff --git a/test/grammar/option/simple_2/stdout.golden b/test/grammar/option/simple_2/stdout.golden new file mode 100644 index 000000000..20fd4f1ae --- /dev/null +++ b/test/grammar/option/simple_2/stdout.golden @@ -0,0 +1 @@ +id: '000000123456' diff --git a/test/grammar/option/single_option/main.k b/test/grammar/option/single_option/main.k new file mode 100644 index 000000000..d15b81b62 --- /dev/null +++ b/test/grammar/option/single_option/main.k @@ -0,0 +1 @@ +a = option("key") diff --git a/test/grammar/option/single_option/settings.yaml b/test/grammar/option/single_option/settings.yaml new file mode 100644 index 000000000..c09318a7d --- /dev/null +++ b/test/grammar/option/single_option/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D key=value diff --git a/test/grammar/option/single_option/stdout.golden b/test/grammar/option/single_option/stdout.golden new file mode 100644 index 000000000..7852727f1 --- /dev/null +++ b/test/grammar/option/single_option/stdout.golden @@ -0,0 +1 @@ +a: value \ No newline at end of file diff --git a/test/grammar/option/type_convert_0/main.k b/test/grammar/option/type_convert_0/main.k new file mode 100644 index 000000000..af1a22ebb --- /dev/null +++ b/test/grammar/option/type_convert_0/main.k @@ -0,0 +1,4 @@ +a = option("key1", type="bool") +b = option("key2", type="bool") +c = option("key3", type="int") +d = option("key4", type="int") diff --git a/test/grammar/option/type_convert_0/settings.yaml b/test/grammar/option/type_convert_0/settings.yaml new file mode 100644 index 000000000..4542f110c --- /dev/null +++ b/test/grammar/option/type_convert_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D key1=0 -D key2=False -D key3=0.0 -D key4=.0 diff --git a/test/grammar/option/type_convert_0/stdout.golden b/test/grammar/option/type_convert_0/stdout.golden new file mode 100644 index 000000000..ff13dd9f8 --- /dev/null +++ b/test/grammar/option/type_convert_0/stdout.golden @@ -0,0 +1,4 @@ +a: false +b: false +c: 0 +d: 0 diff --git a/test/grammar/option/type_convert_1/main.k b/test/grammar/option/type_convert_1/main.k new file mode 100644 index 000000000..02d485185 --- /dev/null +++ b/test/grammar/option/type_convert_1/main.k @@ -0,0 +1,4 @@ +a = option("key1", type="bool", default=0.0) +b = option("key2", type="bool", default="") +c = option("key3", type="float", default="0.123") +d = option("key4", type="float", default="0") diff --git a/test/grammar/option/type_convert_1/stdout.golden b/test/grammar/option/type_convert_1/stdout.golden new file mode 100644 index 000000000..7448b79fe --- /dev/null +++ b/test/grammar/option/type_convert_1/stdout.golden @@ -0,0 +1,4 @@ +a: false +b: false +c: 0.123 +d: 0.0 diff --git a/test/grammar/option/type_convert_2/main.k b/test/grammar/option/type_convert_2/main.k new file mode 100644 index 000000000..ff55654c2 --- /dev/null +++ b/test/grammar/option/type_convert_2/main.k @@ -0,0 +1,4 @@ +a = option("key1", type="bool", default=0.0) +b = option("key2", type="bool", default="") +c = option("key3", type="float", default="1.5") +d = option("key4", type="float", default="0") diff --git a/test/grammar/option/type_convert_2/settings.yaml b/test/grammar/option/type_convert_2/settings.yaml new file mode 100644 index 000000000..9d6dfb16d --- /dev/null +++ b/test/grammar/option/type_convert_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D key1=0 -D key2=False -D key3=0.0 -D key4=.0 \ No newline at end of file diff --git a/test/grammar/option/type_convert_2/stdout.golden b/test/grammar/option/type_convert_2/stdout.golden new file mode 100644 index 000000000..d5b92ccf6 --- /dev/null +++ b/test/grammar/option/type_convert_2/stdout.golden @@ -0,0 +1,4 @@ +a: false +b: false +c: 0.0 +d: 0.0 diff --git a/test/grammar/option/type_convert_fail_0/main.k b/test/grammar/option/type_convert_fail_0/main.k new file mode 100644 index 000000000..51c7eded6 --- /dev/null +++ b/test/grammar/option/type_convert_fail_0/main.k @@ -0,0 +1,4 @@ +a = option("key1", type="float") +b = option("key2", type="bool") +c = option("key3", type="dict") +d = option("key4", type="list") diff --git a/test/grammar/option/type_convert_fail_0/settings.yaml b/test/grammar/option/type_convert_fail_0/settings.yaml new file mode 100644 index 000000000..4542f110c --- /dev/null +++ b/test/grammar/option/type_convert_fail_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D key1=0 -D key2=False -D key3=0.0 -D key4=.0 diff --git a/test/grammar/option/type_convert_fail_0/stderr.golden.py b/test/grammar/option/type_convert_fail_0/stderr.golden.py new file mode 100644 index 000000000..b57d90909 --- /dev/null +++ b/test/grammar/option/type_convert_fail_0/stderr.golden.py @@ -0,0 +1,20 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) +file = os.path.join(cwd, 'main.k') + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=file, + line_no=3, + ), + ], + arg_msg=f"cannot use '0.0' as type 'dict'") + , file=sys.stdout +) + diff --git a/test/grammar/option/type_convert_fail_1/main.k b/test/grammar/option/type_convert_fail_1/main.k new file mode 100644 index 000000000..7db647dab --- /dev/null +++ b/test/grammar/option/type_convert_fail_1/main.k @@ -0,0 +1,4 @@ +a = option("key1", type="list", default={"key": "value"}) +b = option("key2", type="bool", default=None) +c = option("key3", type="int", default=0.0) +d = option("key4", type="str", default=[1,2,3]) diff --git a/test/grammar/option/type_convert_fail_1/stderr.golden.py b/test/grammar/option/type_convert_fail_1/stderr.golden.py new file mode 100644 index 000000000..04e105ffd --- /dev/null +++ b/test/grammar/option/type_convert_fail_1/stderr.golden.py @@ -0,0 +1,20 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) +file = os.path.join(cwd, 'main.k') + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=file, + line_no=1, + ), + ], + arg_msg=f"cannot use '{{'key': 'value'}}' as type 'list'") + , file=sys.stdout +) + diff --git a/test/grammar/option/type_convert_fail_2/main.k b/test/grammar/option/type_convert_fail_2/main.k new file mode 100644 index 000000000..ab8b1737d --- /dev/null +++ b/test/grammar/option/type_convert_fail_2/main.k @@ -0,0 +1 @@ +a = option(type="list", default={"key": "value"}, "key1") \ No newline at end of file diff --git a/test/grammar/option/type_convert_fail_2/stderr.golden.py b/test/grammar/option/type_convert_fail_2/stderr.golden.py new file mode 100644 index 000000000..7b2d64745 --- /dev/null +++ b/test/grammar/option/type_convert_fail_2/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) +file = os.path.join(cwd, 'main.k') + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=51, + end_col_no=57 + ) + ], + arg_msg="positional argument follows keyword argument"), + file=sys.stdout +) diff --git a/test/grammar/override/combination/_main.k b/test/grammar/override/combination/_main.k new file mode 100644 index 000000000..6080cccd7 --- /dev/null +++ b/test/grammar/override/combination/_main.k @@ -0,0 +1,15 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +alice = Person { + name: { + firstName: "Alice", + lastName: "Terry" + }, + age: 12 +} diff --git a/test/grammar/override/combination/settings.yaml b/test/grammar/override/combination/settings.yaml new file mode 100644 index 000000000..9645d2e78 --- /dev/null +++ b/test/grammar/override/combination/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O :alice.name.firstName=A -O :alice.name.lastName=B \ No newline at end of file diff --git a/test/grammar/override/combination/stdout.golden b/test/grammar/override/combination/stdout.golden new file mode 100644 index 000000000..5ec9a89c5 --- /dev/null +++ b/test/grammar/override/combination/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + firstName: A + lastName: B + age: 12 \ No newline at end of file diff --git a/test/grammar/override/delete_0/_main.k b/test/grammar/override/delete_0/_main.k new file mode 100644 index 000000000..c2244231c --- /dev/null +++ b/test/grammar/override/delete_0/_main.k @@ -0,0 +1,9 @@ +schema Person: + firstName?: str + lastName?: str + fullName: str = (firstName or "") + "_" + (lastName or "") + +JohnDoe = Person { + firstName: "John" + lastName: "Doe" +} diff --git a/test/grammar/override/delete_0/settings.yaml b/test/grammar/override/delete_0/settings.yaml new file mode 100644 index 000000000..0cfacfd0f --- /dev/null +++ b/test/grammar/override/delete_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O :JohnDoe.firstName- -O :JohnDoe.lastName- diff --git a/test/grammar/override/delete_0/stdout.golden b/test/grammar/override/delete_0/stdout.golden new file mode 100644 index 000000000..f88bbe4cd --- /dev/null +++ b/test/grammar/override/delete_0/stdout.golden @@ -0,0 +1,2 @@ +JohnDoe: + fullName: _ diff --git a/test/grammar/override/delete_1/_main.k b/test/grammar/override/delete_1/_main.k new file mode 100644 index 000000000..c2244231c --- /dev/null +++ b/test/grammar/override/delete_1/_main.k @@ -0,0 +1,9 @@ +schema Person: + firstName?: str + lastName?: str + fullName: str = (firstName or "") + "_" + (lastName or "") + +JohnDoe = Person { + firstName: "John" + lastName: "Doe" +} diff --git a/test/grammar/override/delete_1/settings.yaml b/test/grammar/override/delete_1/settings.yaml new file mode 100644 index 000000000..1df8b66a1 --- /dev/null +++ b/test/grammar/override/delete_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O :JohnDoe.firstName- -O :JohnDoe.lastName='last' diff --git a/test/grammar/override/delete_1/stdout.golden b/test/grammar/override/delete_1/stdout.golden new file mode 100644 index 000000000..671ec9382 --- /dev/null +++ b/test/grammar/override/delete_1/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + lastName: last + fullName: _last diff --git a/test/grammar/override/dict/_main.k b/test/grammar/override/dict/_main.k new file mode 100644 index 000000000..9c9b355ed --- /dev/null +++ b/test/grammar/override/dict/_main.k @@ -0,0 +1,6 @@ +schema Person: + labels: {str:str} + +alice = Person { + "labels": {"skin": "yellow"} +} diff --git a/test/grammar/override/dict/settings.yaml b/test/grammar/override/dict/settings.yaml new file mode 100644 index 000000000..a42c3f53f --- /dev/null +++ b/test/grammar/override/dict/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O :alice.labels.skin=white \ No newline at end of file diff --git a/test/grammar/override/dict/stdout.golden b/test/grammar/override/dict/stdout.golden new file mode 100644 index 000000000..28e19be3b --- /dev/null +++ b/test/grammar/override/dict/stdout.golden @@ -0,0 +1,3 @@ +alice: + labels: + skin: white \ No newline at end of file diff --git a/test/grammar/override/fail/type_fail/_main.k b/test/grammar/override/fail/type_fail/_main.k new file mode 100644 index 000000000..15558e2bf --- /dev/null +++ b/test/grammar/override/fail/type_fail/_main.k @@ -0,0 +1,18 @@ +schema Person: + firstName: str + lastName: str + age: int + +schema Scholar(Person): + subject: str + +JonSnow = Person { + "firstName": "Jon", + "lastName": "Snow" +} + +JohnDoe = Scholar { + firstName: "John" + lastName: "Doe" + subject: "CS" +} diff --git a/test/grammar/override/fail/type_fail/settings.yaml b/test/grammar/override/fail/type_fail/settings.yaml new file mode 100644 index 000000000..53ead3cb6 --- /dev/null +++ b/test/grammar/override/fail/type_fail/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O :JonSnow.age=A \ No newline at end of file diff --git a/test/grammar/override/fail/type_fail/stderr.golden.py b/test/grammar/override/fail/type_fail/stderr.golden.py new file mode 100644 index 000000000..5b4c31676 --- /dev/null +++ b/test/grammar/override/fail/type_fail/stderr.golden.py @@ -0,0 +1,23 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=5, + arg_msg="expect int", + err_level=kcl_error.ErrLevel.ORDINARY + ), + ], + arg_msg="expect int, got str(A)" + ), + file=sys.stdout +) + diff --git a/test/grammar/override/import_package/_main.k b/test/grammar/override/import_package/_main.k new file mode 100644 index 000000000..c97ea636a --- /dev/null +++ b/test/grammar/override/import_package/_main.k @@ -0,0 +1,3 @@ +import mymodule + +result = mymodule.data0.num + mymodule.data1.num diff --git a/test/grammar/override/import_package/kcl.mod b/test/grammar/override/import_package/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/override/import_package/mymodule/a.k b/test/grammar/override/import_package/mymodule/a.k new file mode 100644 index 000000000..5832188b9 --- /dev/null +++ b/test/grammar/override/import_package/mymodule/a.k @@ -0,0 +1,6 @@ +schema data: + num: int = 1 + +data0 = data { + "num" = 100 +} diff --git a/test/grammar/override/import_package/mymodule/b.k b/test/grammar/override/import_package/mymodule/b.k new file mode 100644 index 000000000..2bdb75321 --- /dev/null +++ b/test/grammar/override/import_package/mymodule/b.k @@ -0,0 +1,3 @@ +data1 = data { + "num" = 23 +} diff --git a/test/grammar/override/import_package/settings.yaml b/test/grammar/override/import_package/settings.yaml new file mode 100644 index 000000000..ec9eff8a8 --- /dev/null +++ b/test/grammar/override/import_package/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O mymodule:data0.num=1 \ No newline at end of file diff --git a/test/grammar/override/import_package/stdout.golden b/test/grammar/override/import_package/stdout.golden new file mode 100644 index 000000000..90d5da7ef --- /dev/null +++ b/test/grammar/override/import_package/stdout.golden @@ -0,0 +1 @@ +result: 24 \ No newline at end of file diff --git a/test/grammar/override/inherit/_main.k b/test/grammar/override/inherit/_main.k new file mode 100644 index 000000000..1cff7bd7e --- /dev/null +++ b/test/grammar/override/inherit/_main.k @@ -0,0 +1,17 @@ +schema Person: + firstName: str + lastName: str + +schema Scholar(Person): + subject: str + +JonSnow = Person { + "firstName": "Jon", + "lastName": "Snow" +} + +JohnDoe = Scholar { + firstName: "John" + lastName: "Doe" + subject: "CS" +} diff --git a/test/grammar/override/inherit/settings.yaml b/test/grammar/override/inherit/settings.yaml new file mode 100644 index 000000000..068a4f2d6 --- /dev/null +++ b/test/grammar/override/inherit/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O :JohnDoe.firstName=A -O :JohnDoe.lastName=B \ No newline at end of file diff --git a/test/grammar/override/inherit/stdout.golden b/test/grammar/override/inherit/stdout.golden new file mode 100644 index 000000000..afa1245d8 --- /dev/null +++ b/test/grammar/override/inherit/stdout.golden @@ -0,0 +1,7 @@ +JonSnow: + firstName: Jon + lastName: Snow +JohnDoe: + firstName: A + lastName: B + subject: CS \ No newline at end of file diff --git a/test/grammar/override/kcl.mod b/test/grammar/override/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/override/mod_root/_main.k b/test/grammar/override/mod_root/_main.k new file mode 100644 index 000000000..686d4023c --- /dev/null +++ b/test/grammar/override/mod_root/_main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + lastName: str + +JohnDoe = Person { + firstName: "John" + lastName: "Doe" +} diff --git a/test/grammar/override/mod_root/kcl.mod b/test/grammar/override/mod_root/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/override/mod_root/settings.yaml b/test/grammar/override/mod_root/settings.yaml new file mode 100644 index 000000000..068a4f2d6 --- /dev/null +++ b/test/grammar/override/mod_root/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O :JohnDoe.firstName=A -O :JohnDoe.lastName=B \ No newline at end of file diff --git a/test/grammar/override/mod_root/stdout.golden b/test/grammar/override/mod_root/stdout.golden new file mode 100644 index 000000000..f55752654 --- /dev/null +++ b/test/grammar/override/mod_root/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: A + lastName: B \ No newline at end of file diff --git a/test/grammar/override/more_assign/more_assign_0/_main.k b/test/grammar/override/more_assign/more_assign_0/_main.k new file mode 100644 index 000000000..5be0a0b08 --- /dev/null +++ b/test/grammar/override/more_assign/more_assign_0/_main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + lastName: str + +JohnDoeA = JohnDoeB = Person { + firstName: "John" + lastName: "Doe" +} diff --git a/test/grammar/override/more_assign/more_assign_0/kcl.mod b/test/grammar/override/more_assign/more_assign_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/override/more_assign/more_assign_0/settings.yaml b/test/grammar/override/more_assign/more_assign_0/settings.yaml new file mode 100644 index 000000000..ad936b8de --- /dev/null +++ b/test/grammar/override/more_assign/more_assign_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O :JohnDoeA.firstName=A -O :JohnDoeB.lastName=B \ No newline at end of file diff --git a/test/grammar/override/more_assign/more_assign_0/stdout.golden b/test/grammar/override/more_assign/more_assign_0/stdout.golden new file mode 100644 index 000000000..c1723e2ba --- /dev/null +++ b/test/grammar/override/more_assign/more_assign_0/stdout.golden @@ -0,0 +1,6 @@ +JohnDoeA: + firstName: A + lastName: Doe +JohnDoeB: + firstName: John + lastName: B diff --git a/test/grammar/override/more_assign/more_assign_1/_main.k b/test/grammar/override/more_assign/more_assign_1/_main.k new file mode 100644 index 000000000..e1c680a63 --- /dev/null +++ b/test/grammar/override/more_assign/more_assign_1/_main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + lastName: str + +JohnDoeA = JohnDoeB = JohnDoeC = Person { + firstName: "John" + lastName: "Doe" +} diff --git a/test/grammar/override/more_assign/more_assign_1/kcl.mod b/test/grammar/override/more_assign/more_assign_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/override/more_assign/more_assign_1/settings.yaml b/test/grammar/override/more_assign/more_assign_1/settings.yaml new file mode 100644 index 000000000..b58cd1267 --- /dev/null +++ b/test/grammar/override/more_assign/more_assign_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O :JohnDoeA.firstName=A \ No newline at end of file diff --git a/test/grammar/override/more_assign/more_assign_1/stdout.golden b/test/grammar/override/more_assign/more_assign_1/stdout.golden new file mode 100644 index 000000000..befe7487d --- /dev/null +++ b/test/grammar/override/more_assign/more_assign_1/stdout.golden @@ -0,0 +1,9 @@ +JohnDoeA: + firstName: A + lastName: Doe +JohnDoeB: + firstName: John + lastName: Doe +JohnDoeC: + firstName: John + lastName: Doe diff --git a/test/grammar/override/nested0/kcl.mod b/test/grammar/override/nested0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/override/nested0/nested1/simple/_main.k b/test/grammar/override/nested0/nested1/simple/_main.k new file mode 100644 index 000000000..686d4023c --- /dev/null +++ b/test/grammar/override/nested0/nested1/simple/_main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + lastName: str + +JohnDoe = Person { + firstName: "John" + lastName: "Doe" +} diff --git a/test/grammar/override/nested0/nested1/simple/settings.yaml b/test/grammar/override/nested0/nested1/simple/settings.yaml new file mode 100644 index 000000000..068a4f2d6 --- /dev/null +++ b/test/grammar/override/nested0/nested1/simple/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O :JohnDoe.firstName=A -O :JohnDoe.lastName=B \ No newline at end of file diff --git a/test/grammar/override/nested0/nested1/simple/stdout.golden b/test/grammar/override/nested0/nested1/simple/stdout.golden new file mode 100644 index 000000000..f55752654 --- /dev/null +++ b/test/grammar/override/nested0/nested1/simple/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: A + lastName: B \ No newline at end of file diff --git a/test/grammar/override/pkg/_main.k b/test/grammar/override/pkg/_main.k new file mode 100644 index 000000000..6be921995 --- /dev/null +++ b/test/grammar/override/pkg/_main.k @@ -0,0 +1,3 @@ +import internal_pkg as pkg + +person = pkg.alice diff --git a/test/grammar/override/pkg/internal_pkg/main.k b/test/grammar/override/pkg/internal_pkg/main.k new file mode 100644 index 000000000..1f09a9724 --- /dev/null +++ b/test/grammar/override/pkg/internal_pkg/main.k @@ -0,0 +1,8 @@ +schema Person: + name: str + age: int + +alice = Person { + name: "Alice" + age: 12 +} diff --git a/test/grammar/override/pkg/kcl.mod b/test/grammar/override/pkg/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/override/pkg/settings.yaml b/test/grammar/override/pkg/settings.yaml new file mode 100644 index 000000000..e78a3dd5b --- /dev/null +++ b/test/grammar/override/pkg/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O internal_pkg:alice.name=o_alice -O :person.name=o_person \ No newline at end of file diff --git a/test/grammar/override/pkg/stdout.golden b/test/grammar/override/pkg/stdout.golden new file mode 100644 index 000000000..55cc43fc2 --- /dev/null +++ b/test/grammar/override/pkg/stdout.golden @@ -0,0 +1,3 @@ +person: + name: o_alice + age: 12 \ No newline at end of file diff --git a/test/grammar/override/simple_0/_main.k b/test/grammar/override/simple_0/_main.k new file mode 100644 index 000000000..d8c8f738b --- /dev/null +++ b/test/grammar/override/simple_0/_main.k @@ -0,0 +1,9 @@ +schema Person: + firstName: str + lastName: str + fullName: str = firstName + "_" + lastName + +JohnDoe = Person { + firstName: "John" + lastName: "Doe" +} diff --git a/test/grammar/override/simple_0/settings.yaml b/test/grammar/override/simple_0/settings.yaml new file mode 100644 index 000000000..4ee88f35c --- /dev/null +++ b/test/grammar/override/simple_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O :JohnDoe.firstName=A -O :JohnDoe.lastName=B diff --git a/test/grammar/override/simple_0/stdout.golden b/test/grammar/override/simple_0/stdout.golden new file mode 100644 index 000000000..25c46e2f7 --- /dev/null +++ b/test/grammar/override/simple_0/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: A + lastName: B + fullName: A_B \ No newline at end of file diff --git a/test/grammar/override/simple_1/_main.k b/test/grammar/override/simple_1/_main.k new file mode 100644 index 000000000..d8c8f738b --- /dev/null +++ b/test/grammar/override/simple_1/_main.k @@ -0,0 +1,9 @@ +schema Person: + firstName: str + lastName: str + fullName: str = firstName + "_" + lastName + +JohnDoe = Person { + firstName: "John" + lastName: "Doe" +} diff --git a/test/grammar/override/simple_1/settings.yaml b/test/grammar/override/simple_1/settings.yaml new file mode 100644 index 000000000..7dcf0188f --- /dev/null +++ b/test/grammar/override/simple_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O JohnDoe.firstName=A -O JohnDoe.lastName=B diff --git a/test/grammar/override/simple_1/stdout.golden b/test/grammar/override/simple_1/stdout.golden new file mode 100644 index 000000000..25c46e2f7 --- /dev/null +++ b/test/grammar/override/simple_1/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: A + lastName: B + fullName: A_B \ No newline at end of file diff --git a/test/grammar/override/simple_2/_main.k b/test/grammar/override/simple_2/_main.k new file mode 100644 index 000000000..8f89d3895 --- /dev/null +++ b/test/grammar/override/simple_2/_main.k @@ -0,0 +1,7 @@ +schema Data: + labels?: {str:} + +data = Data { + labels.key1 = "value1" + labels: {} +} diff --git a/test/grammar/override/simple_2/settings.yaml b/test/grammar/override/simple_2/settings.yaml new file mode 100644 index 000000000..e772b587b --- /dev/null +++ b/test/grammar/override/simple_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: -O data.labels.key2=value2 diff --git a/test/grammar/override/simple_2/stdout.golden b/test/grammar/override/simple_2/stdout.golden new file mode 100644 index 000000000..3e20a1f7e --- /dev/null +++ b/test/grammar/override/simple_2/stdout.golden @@ -0,0 +1,4 @@ +data: + labels: + key1: value1 + key2: value2 diff --git a/test/grammar/override/type_dict/_main.k b/test/grammar/override/type_dict/_main.k new file mode 100644 index 000000000..1d1a70719 --- /dev/null +++ b/test/grammar/override/type_dict/_main.k @@ -0,0 +1,22 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema Group: + persons: {str:Person} + +group = Group { + "persons": { + "me": { + "name": { + "firstName": "Alice", + "lastName": "Terry" + }, + "age": 12 + } + } +} diff --git a/test/grammar/override/type_dict/settings.yaml b/test/grammar/override/type_dict/settings.yaml new file mode 100644 index 000000000..70aa862ef --- /dev/null +++ b/test/grammar/override/type_dict/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S type_dict:group.persons.me.age \ No newline at end of file diff --git a/test/grammar/override/type_dict/stdout.golden b/test/grammar/override/type_dict/stdout.golden new file mode 100644 index 000000000..a9896146f --- /dev/null +++ b/test/grammar/override/type_dict/stdout.golden @@ -0,0 +1,4 @@ +group: + persons: + me: + age: 12 \ No newline at end of file diff --git a/test/grammar/path_selector/all_elements/kcl.mod b/test/grammar/path_selector/all_elements/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/path_selector/all_elements/main.k b/test/grammar/path_selector/all_elements/main.k new file mode 100644 index 000000000..61bcb63ed --- /dev/null +++ b/test/grammar/path_selector/all_elements/main.k @@ -0,0 +1,10 @@ +schema Person: + firstName: str + lastName: str + +JohnDoe = Person { + firstName: "John" + lastName: "Doe" +} +list_data = [1, 2, 3] +dict_data = {"key1": "value1", "key2": "value2"} diff --git a/test/grammar/path_selector/all_elements/settings.yaml b/test/grammar/path_selector/all_elements/settings.yaml new file mode 100644 index 000000000..e09717388 --- /dev/null +++ b/test/grammar/path_selector/all_elements/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S :JohnDoe.* -S :list_data.* -S :dict_data.* diff --git a/test/grammar/path_selector/all_elements/stdout.golden b/test/grammar/path_selector/all_elements/stdout.golden new file mode 100644 index 000000000..956be1106 --- /dev/null +++ b/test/grammar/path_selector/all_elements/stdout.golden @@ -0,0 +1,10 @@ +JohnDoe: + firstName: John + lastName: Doe +list_data: +- 1 +- 2 +- 3 +dict_data: + key1: value1 + key2: value2 diff --git a/test/grammar/path_selector/combination/main.k b/test/grammar/path_selector/combination/main.k new file mode 100644 index 000000000..6080cccd7 --- /dev/null +++ b/test/grammar/path_selector/combination/main.k @@ -0,0 +1,15 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +alice = Person { + name: { + firstName: "Alice", + lastName: "Terry" + }, + age: 12 +} diff --git a/test/grammar/path_selector/combination/settings.yaml b/test/grammar/path_selector/combination/settings.yaml new file mode 100644 index 000000000..7f415f82a --- /dev/null +++ b/test/grammar/path_selector/combination/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S combination:alice.name.firstName -S combination:alice.name.lastName \ No newline at end of file diff --git a/test/grammar/path_selector/combination/stdout.golden b/test/grammar/path_selector/combination/stdout.golden new file mode 100644 index 000000000..88ef30116 --- /dev/null +++ b/test/grammar/path_selector/combination/stdout.golden @@ -0,0 +1,4 @@ +alice: + name: + firstName: Alice + lastName: Terry \ No newline at end of file diff --git a/test/grammar/path_selector/dict/main.k b/test/grammar/path_selector/dict/main.k new file mode 100644 index 000000000..9c9b355ed --- /dev/null +++ b/test/grammar/path_selector/dict/main.k @@ -0,0 +1,6 @@ +schema Person: + labels: {str:str} + +alice = Person { + "labels": {"skin": "yellow"} +} diff --git a/test/grammar/path_selector/dict/settings.yaml b/test/grammar/path_selector/dict/settings.yaml new file mode 100644 index 000000000..3cf5354b1 --- /dev/null +++ b/test/grammar/path_selector/dict/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S dict:alice.labels.skin \ No newline at end of file diff --git a/test/grammar/path_selector/dict/stdout.golden b/test/grammar/path_selector/dict/stdout.golden new file mode 100644 index 000000000..37aaa884e --- /dev/null +++ b/test/grammar/path_selector/dict/stdout.golden @@ -0,0 +1,3 @@ +alice: + labels: + skin: yellow \ No newline at end of file diff --git a/test/grammar/path_selector/import_package/kcl.mod b/test/grammar/path_selector/import_package/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/path_selector/import_package/main.k b/test/grammar/path_selector/import_package/main.k new file mode 100644 index 000000000..de8e19bf3 --- /dev/null +++ b/test/grammar/path_selector/import_package/main.k @@ -0,0 +1,4 @@ +import mymodule + +result1 = mymodule.data0.num + mymodule.data1.num +result2 = mymodule.data0.num - mymodule.data1.num diff --git a/test/grammar/path_selector/import_package/mymodule/a.k b/test/grammar/path_selector/import_package/mymodule/a.k new file mode 100644 index 000000000..b53040070 --- /dev/null +++ b/test/grammar/path_selector/import_package/mymodule/a.k @@ -0,0 +1,6 @@ +schema data: + num: int = 1 + +data0 = data { + "num": 100 +} diff --git a/test/grammar/path_selector/import_package/mymodule/b.k b/test/grammar/path_selector/import_package/mymodule/b.k new file mode 100644 index 000000000..f3e49d838 --- /dev/null +++ b/test/grammar/path_selector/import_package/mymodule/b.k @@ -0,0 +1,3 @@ +data1 = data { + "num": 23 +} diff --git a/test/grammar/path_selector/import_package/settings.yaml b/test/grammar/path_selector/import_package/settings.yaml new file mode 100644 index 000000000..cfa71fd91 --- /dev/null +++ b/test/grammar/path_selector/import_package/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S result1 \ No newline at end of file diff --git a/test/grammar/path_selector/import_package/stdout.golden b/test/grammar/path_selector/import_package/stdout.golden new file mode 100644 index 000000000..8163ffb9b --- /dev/null +++ b/test/grammar/path_selector/import_package/stdout.golden @@ -0,0 +1 @@ +result1: 123 diff --git a/test/grammar/path_selector/index/kcl.mod b/test/grammar/path_selector/index/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/path_selector/index/main.k b/test/grammar/path_selector/index/main.k new file mode 100644 index 000000000..61bcb63ed --- /dev/null +++ b/test/grammar/path_selector/index/main.k @@ -0,0 +1,10 @@ +schema Person: + firstName: str + lastName: str + +JohnDoe = Person { + firstName: "John" + lastName: "Doe" +} +list_data = [1, 2, 3] +dict_data = {"key1": "value1", "key2": "value2"} diff --git a/test/grammar/path_selector/index/settings.yaml b/test/grammar/path_selector/index/settings.yaml new file mode 100644 index 000000000..9db2d5881 --- /dev/null +++ b/test/grammar/path_selector/index/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S :list_data.[0] diff --git a/test/grammar/path_selector/index/stdout.golden b/test/grammar/path_selector/index/stdout.golden new file mode 100644 index 000000000..e13b1baa0 --- /dev/null +++ b/test/grammar/path_selector/index/stdout.golden @@ -0,0 +1 @@ +list_data: 1 diff --git a/test/grammar/path_selector/inherit/main.k b/test/grammar/path_selector/inherit/main.k new file mode 100644 index 000000000..1cff7bd7e --- /dev/null +++ b/test/grammar/path_selector/inherit/main.k @@ -0,0 +1,17 @@ +schema Person: + firstName: str + lastName: str + +schema Scholar(Person): + subject: str + +JonSnow = Person { + "firstName": "Jon", + "lastName": "Snow" +} + +JohnDoe = Scholar { + firstName: "John" + lastName: "Doe" + subject: "CS" +} diff --git a/test/grammar/path_selector/inherit/settings.yaml b/test/grammar/path_selector/inherit/settings.yaml new file mode 100644 index 000000000..ea74437c2 --- /dev/null +++ b/test/grammar/path_selector/inherit/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S inherit:JohnDoe.firstName -S inherit:JohnDoe.lastName \ No newline at end of file diff --git a/test/grammar/path_selector/inherit/stdout.golden b/test/grammar/path_selector/inherit/stdout.golden new file mode 100644 index 000000000..6ee1692a6 --- /dev/null +++ b/test/grammar/path_selector/inherit/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: John + lastName: Doe \ No newline at end of file diff --git a/test/grammar/path_selector/invalid/invalid_0/kcl.mod b/test/grammar/path_selector/invalid/invalid_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/path_selector/invalid/invalid_0/main.k b/test/grammar/path_selector/invalid/invalid_0/main.k new file mode 100644 index 000000000..7da8de02a --- /dev/null +++ b/test/grammar/path_selector/invalid/invalid_0/main.k @@ -0,0 +1,2 @@ +list_data = [1, 2, 3] +dict_data = {"key1": "value1", "key2": "value2"} diff --git a/test/grammar/path_selector/invalid/invalid_0/settings.yaml b/test/grammar/path_selector/invalid/invalid_0/settings.yaml new file mode 100644 index 000000000..534403f36 --- /dev/null +++ b/test/grammar/path_selector/invalid/invalid_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S :list_data.[0,1] \ No newline at end of file diff --git a/test/grammar/path_selector/invalid/invalid_0/stderr.golden.py b/test/grammar/path_selector/invalid/invalid_0/stderr.golden.py new file mode 100644 index 000000000..8cdd91d5e --- /dev/null +++ b/test/grammar/path_selector/invalid/invalid_0/stderr.golden.py @@ -0,0 +1,14 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + arg_msg="invalid path selector value [0,1]" + ), + file=sys.stdout +) + diff --git a/test/grammar/path_selector/invalid/invalid_1/_main.k b/test/grammar/path_selector/invalid/invalid_1/_main.k new file mode 100644 index 000000000..7da8de02a --- /dev/null +++ b/test/grammar/path_selector/invalid/invalid_1/_main.k @@ -0,0 +1,2 @@ +list_data = [1, 2, 3] +dict_data = {"key1": "value1", "key2": "value2"} diff --git a/test/grammar/path_selector/invalid/invalid_1/kcl.mod b/test/grammar/path_selector/invalid/invalid_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/path_selector/invalid/invalid_1/settings.yaml b/test/grammar/path_selector/invalid/invalid_1/settings.yaml new file mode 100644 index 000000000..7485bdfcf --- /dev/null +++ b/test/grammar/path_selector/invalid/invalid_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S :list_data.{err_key} \ No newline at end of file diff --git a/test/grammar/path_selector/invalid/invalid_1/stderr.golden.py b/test/grammar/path_selector/invalid/invalid_1/stderr.golden.py new file mode 100644 index 000000000..311862206 --- /dev/null +++ b/test/grammar/path_selector/invalid/invalid_1/stderr.golden.py @@ -0,0 +1,13 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.SelectorError_TYPE, + arg_msg="list variable can't be used with dict selector expression" + ), + file=sys.stdout +) diff --git a/test/grammar/path_selector/invalid/invalid_2/_main.k b/test/grammar/path_selector/invalid/invalid_2/_main.k new file mode 100644 index 000000000..7da8de02a --- /dev/null +++ b/test/grammar/path_selector/invalid/invalid_2/_main.k @@ -0,0 +1,2 @@ +list_data = [1, 2, 3] +dict_data = {"key1": "value1", "key2": "value2"} diff --git a/test/grammar/path_selector/invalid/invalid_2/kcl.mod b/test/grammar/path_selector/invalid/invalid_2/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/path_selector/invalid/invalid_2/settings.yaml b/test/grammar/path_selector/invalid/invalid_2/settings.yaml new file mode 100644 index 000000000..ac8154c6e --- /dev/null +++ b/test/grammar/path_selector/invalid/invalid_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S :dict_data.[0] \ No newline at end of file diff --git a/test/grammar/path_selector/invalid/invalid_2/stderr.golden.py b/test/grammar/path_selector/invalid/invalid_2/stderr.golden.py new file mode 100644 index 000000000..ed87bbf16 --- /dev/null +++ b/test/grammar/path_selector/invalid/invalid_2/stderr.golden.py @@ -0,0 +1,14 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.SelectorError_TYPE, + arg_msg="dict variable can't be used with list selector expression" + ), + file=sys.stdout +) + diff --git a/test/grammar/path_selector/kcl.mod b/test/grammar/path_selector/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/path_selector/list_content/kcl.mod b/test/grammar/path_selector/list_content/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/path_selector/list_content/main.k b/test/grammar/path_selector/list_content/main.k new file mode 100644 index 000000000..462c9f24e --- /dev/null +++ b/test/grammar/path_selector/list_content/main.k @@ -0,0 +1,12 @@ +schema Person: + name: str + age: int + +persons = [Person { + name: "Alice" + age: 18 +}, Person{ + name: "Bob" + age: 10 +}] +persons2 = persons diff --git a/test/grammar/path_selector/list_content/settings.yaml b/test/grammar/path_selector/list_content/settings.yaml new file mode 100644 index 000000000..5de8059c3 --- /dev/null +++ b/test/grammar/path_selector/list_content/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S :persons.* -S :persons2 \ No newline at end of file diff --git a/test/grammar/path_selector/list_content/stdout.golden b/test/grammar/path_selector/list_content/stdout.golden new file mode 100644 index 000000000..10afb74d5 --- /dev/null +++ b/test/grammar/path_selector/list_content/stdout.golden @@ -0,0 +1,10 @@ +persons: +- name: Alice + age: 18 +- name: Bob + age: 10 +persons2: +- name: Alice + age: 18 +- name: Bob + age: 10 diff --git a/test/grammar/path_selector/mod_root/kcl.mod b/test/grammar/path_selector/mod_root/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/path_selector/mod_root/main.k b/test/grammar/path_selector/mod_root/main.k new file mode 100644 index 000000000..686d4023c --- /dev/null +++ b/test/grammar/path_selector/mod_root/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + lastName: str + +JohnDoe = Person { + firstName: "John" + lastName: "Doe" +} diff --git a/test/grammar/path_selector/mod_root/settings.yaml b/test/grammar/path_selector/mod_root/settings.yaml new file mode 100644 index 000000000..0764771ad --- /dev/null +++ b/test/grammar/path_selector/mod_root/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S :JohnDoe.firstName -S :JohnDoe.lastName \ No newline at end of file diff --git a/test/grammar/path_selector/mod_root/stdout.golden b/test/grammar/path_selector/mod_root/stdout.golden new file mode 100644 index 000000000..6ee1692a6 --- /dev/null +++ b/test/grammar/path_selector/mod_root/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: John + lastName: Doe \ No newline at end of file diff --git a/test/grammar/path_selector/mutiple_keys/kcl.mod b/test/grammar/path_selector/mutiple_keys/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/path_selector/mutiple_keys/main.k b/test/grammar/path_selector/mutiple_keys/main.k new file mode 100644 index 000000000..686d4023c --- /dev/null +++ b/test/grammar/path_selector/mutiple_keys/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + lastName: str + +JohnDoe = Person { + firstName: "John" + lastName: "Doe" +} diff --git a/test/grammar/path_selector/mutiple_keys/settings.yaml b/test/grammar/path_selector/mutiple_keys/settings.yaml new file mode 100644 index 000000000..f0ba3c85a --- /dev/null +++ b/test/grammar/path_selector/mutiple_keys/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S :JohnDoe.{firstName,lastName} diff --git a/test/grammar/path_selector/mutiple_keys/stdout.golden b/test/grammar/path_selector/mutiple_keys/stdout.golden new file mode 100644 index 000000000..19b06330f --- /dev/null +++ b/test/grammar/path_selector/mutiple_keys/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: John + lastName: Doe diff --git a/test/grammar/path_selector/nested0/nested1/simple/main.k b/test/grammar/path_selector/nested0/nested1/simple/main.k new file mode 100644 index 000000000..686d4023c --- /dev/null +++ b/test/grammar/path_selector/nested0/nested1/simple/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + lastName: str + +JohnDoe = Person { + firstName: "John" + lastName: "Doe" +} diff --git a/test/grammar/path_selector/nested0/nested1/simple/settings.yaml b/test/grammar/path_selector/nested0/nested1/simple/settings.yaml new file mode 100644 index 000000000..19e08148d --- /dev/null +++ b/test/grammar/path_selector/nested0/nested1/simple/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S nested0.nested1.simple:JohnDoe.firstName -S nested0.nested1.simple:JohnDoe.lastName \ No newline at end of file diff --git a/test/grammar/path_selector/nested0/nested1/simple/stdout.golden b/test/grammar/path_selector/nested0/nested1/simple/stdout.golden new file mode 100644 index 000000000..6ee1692a6 --- /dev/null +++ b/test/grammar/path_selector/nested0/nested1/simple/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: John + lastName: Doe \ No newline at end of file diff --git a/test/grammar/path_selector/simple/main.k b/test/grammar/path_selector/simple/main.k new file mode 100644 index 000000000..686d4023c --- /dev/null +++ b/test/grammar/path_selector/simple/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + lastName: str + +JohnDoe = Person { + firstName: "John" + lastName: "Doe" +} diff --git a/test/grammar/path_selector/simple/settings.yaml b/test/grammar/path_selector/simple/settings.yaml new file mode 100644 index 000000000..b45b9fc60 --- /dev/null +++ b/test/grammar/path_selector/simple/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S simple:JohnDoe.firstName -S simple:JohnDoe.lastName \ No newline at end of file diff --git a/test/grammar/path_selector/simple/stdout.golden b/test/grammar/path_selector/simple/stdout.golden new file mode 100644 index 000000000..6ee1692a6 --- /dev/null +++ b/test/grammar/path_selector/simple/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: John + lastName: Doe \ No newline at end of file diff --git a/test/grammar/path_selector/type_dict/main.k b/test/grammar/path_selector/type_dict/main.k new file mode 100644 index 000000000..1d1a70719 --- /dev/null +++ b/test/grammar/path_selector/type_dict/main.k @@ -0,0 +1,22 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema Group: + persons: {str:Person} + +group = Group { + "persons": { + "me": { + "name": { + "firstName": "Alice", + "lastName": "Terry" + }, + "age": 12 + } + } +} diff --git a/test/grammar/path_selector/type_dict/settings.yaml b/test/grammar/path_selector/type_dict/settings.yaml new file mode 100644 index 000000000..70aa862ef --- /dev/null +++ b/test/grammar/path_selector/type_dict/settings.yaml @@ -0,0 +1 @@ +kcl_options: -S type_dict:group.persons.me.age \ No newline at end of file diff --git a/test/grammar/path_selector/type_dict/stdout.golden b/test/grammar/path_selector/type_dict/stdout.golden new file mode 100644 index 000000000..a9896146f --- /dev/null +++ b/test/grammar/path_selector/type_dict/stdout.golden @@ -0,0 +1,4 @@ +group: + persons: + me: + age: 12 \ No newline at end of file diff --git a/test/grammar/plugin/fail_0/main.k b/test/grammar/plugin/fail_0/main.k new file mode 100644 index 000000000..6873e7f95 --- /dev/null +++ b/test/grammar/plugin/fail_0/main.k @@ -0,0 +1,3 @@ +import kcl_plugin.hello as hello + +err_result = hello.add(None, None) diff --git a/test/grammar/plugin/fail_0/stderr.golden.py b/test/grammar/plugin/fail_0/stderr.golden.py new file mode 100644 index 000000000..f7f3d55c1 --- /dev/null +++ b/test/grammar/plugin/fail_0/stderr.golden.py @@ -0,0 +1,17 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + ) + ], + arg_msg="unsupported operand type(s) for +: 'NoneType' and 'NoneType'", + ) +) diff --git a/test/grammar/plugin/fail_1/main.k b/test/grammar/plugin/fail_1/main.k new file mode 100644 index 000000000..66f5af14c --- /dev/null +++ b/test/grammar/plugin/fail_1/main.k @@ -0,0 +1,4 @@ +import kcl_plugin.hello as hello + + +err_result = hello.add(1, "2") diff --git a/test/grammar/plugin/fail_1/stderr.golden.py b/test/grammar/plugin/fail_1/stderr.golden.py new file mode 100644 index 000000000..b554fcbca --- /dev/null +++ b/test/grammar/plugin/fail_1/stderr.golden.py @@ -0,0 +1,17 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + ) + ], + arg_msg="unsupported operand type(s) for +: 'int' and 'str'", + ) +) diff --git a/test/grammar/plugin/hello/_main.k b/test/grammar/plugin/hello/_main.k new file mode 100644 index 000000000..1dd9f55ca --- /dev/null +++ b/test/grammar/plugin/hello/_main.k @@ -0,0 +1,9 @@ +import kcl_plugin.hello as hello + +name = hello.tolower('KCL') + +three = hello.add(1, 2) + +a = hello.get_global_int() +hello.set_global_int(1) +b = hello.get_global_int() diff --git a/test/grammar/plugin/hello/stdout.golden b/test/grammar/plugin/hello/stdout.golden new file mode 100644 index 000000000..c1f51a805 --- /dev/null +++ b/test/grammar/plugin/hello/stdout.golden @@ -0,0 +1,4 @@ +name: kcl +three: 3 +a: 0 +b: 1 diff --git a/test/grammar/quant/all/multi_cons_invalid_0/main.k b/test/grammar/quant/all/multi_cons_invalid_0/main.k new file mode 100644 index 000000000..089b7778b --- /dev/null +++ b/test/grammar/quant/all/multi_cons_invalid_0/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + all port in ports { + int(port) >= 1024 and int(port) <= 65535 + }, "invalid port" + + +main = app_conf { + ports: [-1] +} \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_invalid_0/stderr.golden.py b/test/grammar/quant/all/multi_cons_invalid_0/stderr.golden.py new file mode 100644 index 000000000..e5943ab87 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_invalid_0/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="invalid port") + , file=sys.stdout +) diff --git a/test/grammar/quant/all/multi_cons_invalid_1/main.k b/test/grammar/quant/all/multi_cons_invalid_1/main.k new file mode 100644 index 000000000..4b53c0b23 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_invalid_1/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + all port in ports { + int(port) == 80 or int(port) == 8080 + }, "invalid port" + + +main = app_conf { + ports: [80, 8080, 9090] +} \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_invalid_1/stderr.golden.py b/test/grammar/quant/all/multi_cons_invalid_1/stderr.golden.py new file mode 100644 index 000000000..5c35815bf --- /dev/null +++ b/test/grammar/quant/all/multi_cons_invalid_1/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="invalid port") + , file=sys.stdout +) \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_invalid_2/main.k b/test/grammar/quant/all/multi_cons_invalid_2/main.k new file mode 100644 index 000000000..4a8b3a7b3 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_invalid_2/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + all port in ports { + (int(port) == 80 or int(port) == 8080) or (6000 <= int(port) <= 7000) + }, "invalid port" + + +main = app_conf { + ports: [80, 8080, 6666, 9090] +} \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_invalid_2/stderr.golden.py b/test/grammar/quant/all/multi_cons_invalid_2/stderr.golden.py new file mode 100644 index 000000000..e5943ab87 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_invalid_2/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="invalid port") + , file=sys.stdout +) diff --git a/test/grammar/quant/all/multi_cons_invalid_3/main.k b/test/grammar/quant/all/multi_cons_invalid_3/main.k new file mode 100644 index 000000000..52f2d0e77 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_invalid_3/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + all port in ports { + (int(port) >= 3000 and int(port) <= 4000) or (int(port) >= 6000 and int(port) <= 7000) + }, "invalid port" + + +main = app_conf { + ports: [3333, 6666, 2333] +} \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_invalid_3/stderr.golden.py b/test/grammar/quant/all/multi_cons_invalid_3/stderr.golden.py new file mode 100644 index 000000000..e5943ab87 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_invalid_3/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="invalid port") + , file=sys.stdout +) diff --git a/test/grammar/quant/all/multi_cons_invalid_4/main.k b/test/grammar/quant/all/multi_cons_invalid_4/main.k new file mode 100644 index 000000000..e5468dfb7 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_invalid_4/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + all port in ports { + (int(port) >= 3000 and int(port) <= 4000) and (int(port) >= 6000 and int(port) <= 7000) + }, "invalid port" + + +main = app_conf { + ports: [3333, 6666, 2333] +} \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_invalid_4/stderr.golden.py b/test/grammar/quant/all/multi_cons_invalid_4/stderr.golden.py new file mode 100644 index 000000000..e5943ab87 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_invalid_4/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="invalid port") + , file=sys.stdout +) diff --git a/test/grammar/quant/all/multi_cons_valid_0/main.k b/test/grammar/quant/all/multi_cons_valid_0/main.k new file mode 100644 index 000000000..801687bd7 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_valid_0/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + all port in ports { + int(port) >= 1024 and int(port) <= 65535 + }, "invalid port" + + +main = app_conf { + ports: [8080, 8081] +} \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_valid_0/stdout.golden b/test/grammar/quant/all/multi_cons_valid_0/stdout.golden new file mode 100644 index 000000000..dda95c744 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_valid_0/stdout.golden @@ -0,0 +1,4 @@ +main: + ports: + - 8080 + - 8081 \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_valid_1/main.k b/test/grammar/quant/all/multi_cons_valid_1/main.k new file mode 100644 index 000000000..8848544e7 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_valid_1/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + all port in ports { + int(port) == 80 or int(port) == 8080 + }, "invalid port" + + +main = app_conf { + ports: [80, 8080] +} \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_valid_1/stdout.golden b/test/grammar/quant/all/multi_cons_valid_1/stdout.golden new file mode 100644 index 000000000..5ab5e0fe1 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_valid_1/stdout.golden @@ -0,0 +1,4 @@ +main: + ports: + - 80 + - 8080 \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_valid_2/main.k b/test/grammar/quant/all/multi_cons_valid_2/main.k new file mode 100644 index 000000000..7d977537d --- /dev/null +++ b/test/grammar/quant/all/multi_cons_valid_2/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + all port in ports { + (int(port) == 80 or int(port) == 8080) or (6000 <= int(port) <= 7000) + }, "invalid port" + + +main = app_conf { + ports: [80, 8080, 6666] +} \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_valid_2/stdout.golden b/test/grammar/quant/all/multi_cons_valid_2/stdout.golden new file mode 100644 index 000000000..69723d58e --- /dev/null +++ b/test/grammar/quant/all/multi_cons_valid_2/stdout.golden @@ -0,0 +1,5 @@ +main: + ports: + - 80 + - 8080 + - 6666 \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_valid_3/main.k b/test/grammar/quant/all/multi_cons_valid_3/main.k new file mode 100644 index 000000000..dfd010b53 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_valid_3/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + all port in ports { + (int(port) >= 3000 and int(port) <= 4000) or (int(port) >= 6000 and int(port) <= 7000) + }, "invalid port" + + +main = app_conf { + ports: [3333, 6666] +} \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_valid_3/stdout.golden b/test/grammar/quant/all/multi_cons_valid_3/stdout.golden new file mode 100644 index 000000000..d16c41499 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_valid_3/stdout.golden @@ -0,0 +1,4 @@ +main: + ports: + - 3333 + - 6666 \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_valid_4/main.k b/test/grammar/quant/all/multi_cons_valid_4/main.k new file mode 100644 index 000000000..e4fc74abb --- /dev/null +++ b/test/grammar/quant/all/multi_cons_valid_4/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + all port in ports { + (int(port) >= 3000 or int(port) <= 4000) and (int(port) >= 6000 or int(port) <= 7000) + }, "invalid port" + + +main = app_conf { + ports: [3333, 6666, 2333] +} \ No newline at end of file diff --git a/test/grammar/quant/all/multi_cons_valid_4/stdout.golden b/test/grammar/quant/all/multi_cons_valid_4/stdout.golden new file mode 100644 index 000000000..268a4ab77 --- /dev/null +++ b/test/grammar/quant/all/multi_cons_valid_4/stdout.golden @@ -0,0 +1,5 @@ +main: + ports: + - 3333 + - 6666 + - 2333 \ No newline at end of file diff --git a/test/grammar/quant/all/simple_invalid_0/main.k b/test/grammar/quant/all/simple_invalid_0/main.k new file mode 100644 index 000000000..204dff6ae --- /dev/null +++ b/test/grammar/quant/all/simple_invalid_0/main.k @@ -0,0 +1,15 @@ +schema app_conf: + services: [{str:str}] + + check: + all service in services { + service.clusterIP == "NONE" if service.type == "ClusterIP" + }, "invalid cluster ip" + + +main = app_conf { + services: [{ + type: "ClusterIP" + clusterIP: "10.0.0.1" + }] +} diff --git a/test/grammar/quant/all/simple_invalid_0/stderr.golden.py b/test/grammar/quant/all/simple_invalid_0/stderr.golden.py new file mode 100644 index 000000000..fa6173c1e --- /dev/null +++ b/test/grammar/quant/all/simple_invalid_0/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="invalid cluster ip") + , file=sys.stdout +) diff --git a/test/grammar/quant/all/simple_valid_0/main.k b/test/grammar/quant/all/simple_valid_0/main.k new file mode 100644 index 000000000..97e449505 --- /dev/null +++ b/test/grammar/quant/all/simple_valid_0/main.k @@ -0,0 +1,15 @@ +schema app_conf: + services: [{str:str}] + + check: + all service in services { + service.clusterIP == "NONE" if service.type == "ClusterIP" + }, "invalid cluster ip" + + +main = app_conf { + services: [{ + type: "ClusterIP" + clusterIP: "NONE" + }] +} \ No newline at end of file diff --git a/test/grammar/quant/all/simple_valid_0/stdout.golden b/test/grammar/quant/all/simple_valid_0/stdout.golden new file mode 100644 index 000000000..c04b738f0 --- /dev/null +++ b/test/grammar/quant/all/simple_valid_0/stdout.golden @@ -0,0 +1,4 @@ +main: + services: + - type: ClusterIP + clusterIP: NONE \ No newline at end of file diff --git a/test/grammar/quant/all/simple_valid_1/main.k b/test/grammar/quant/all/simple_valid_1/main.k new file mode 100644 index 000000000..dfc3c40ea --- /dev/null +++ b/test/grammar/quant/all/simple_valid_1/main.k @@ -0,0 +1,7 @@ +schema Config: + data: str = "aaa" + + check: + all c in data {c == 'a'} + +config = Config {} diff --git a/test/grammar/quant/all/simple_valid_1/stdout.golden b/test/grammar/quant/all/simple_valid_1/stdout.golden new file mode 100644 index 000000000..df296ed82 --- /dev/null +++ b/test/grammar/quant/all/simple_valid_1/stdout.golden @@ -0,0 +1,2 @@ +config: + data: aaa diff --git a/test/grammar/quant/all/simple_valid_2/main.k b/test/grammar/quant/all/simple_valid_2/main.k new file mode 100644 index 000000000..c39399efc --- /dev/null +++ b/test/grammar/quant/all/simple_valid_2/main.k @@ -0,0 +1,8 @@ +data = { + k1: "123" + k2: "456" +} +x0 = [1 if all c in v {c == "1"} else 0 for k, v in data] +y0 = [1 if any c in v {c == "1"} else 0 for k, v in data] +x1 = [1 for k, v in data if all c in v {c == "1"}] or [0] +y1 = [1 for k, v in data if any c in v {c == "1"}] diff --git a/test/grammar/quant/all/simple_valid_2/stdout.golden b/test/grammar/quant/all/simple_valid_2/stdout.golden new file mode 100644 index 000000000..535d3dcfc --- /dev/null +++ b/test/grammar/quant/all/simple_valid_2/stdout.golden @@ -0,0 +1,13 @@ +data: + k1: '123' + k2: '456' +x0: +- 0 +- 0 +y0: +- 1 +- 0 +x1: +- 0 +y1: +- 1 diff --git a/test/grammar/quant/all/simple_valid_3/main.k b/test/grammar/quant/all/simple_valid_3/main.k new file mode 100644 index 000000000..bb83787db --- /dev/null +++ b/test/grammar/quant/all/simple_valid_3/main.k @@ -0,0 +1,8 @@ +data = { + k1: ["1", "2", "3"] + k2: ["4", "5", "6"] +} +x0 = [1 if filter c in v {c == "1"} else 0 for k, v in data] +y0 = [1 if map c in v {c == "1"} else 0 for k, v in data] +x1 = [1 for k, v in data if filter c in v {c == "1"}] +y1 = [1 for k, v in data if map c in v {c == "1"}] diff --git a/test/grammar/quant/all/simple_valid_3/stdout.golden b/test/grammar/quant/all/simple_valid_3/stdout.golden new file mode 100644 index 000000000..7da970a1c --- /dev/null +++ b/test/grammar/quant/all/simple_valid_3/stdout.golden @@ -0,0 +1,20 @@ +data: + k1: + - '1' + - '2' + - '3' + k2: + - '4' + - '5' + - '6' +x0: +- 1 +- 0 +y0: +- 1 +- 1 +x1: +- 1 +y1: +- 1 +- 1 diff --git a/test/grammar/quant/any/multi_cons_invalid_0/main.k b/test/grammar/quant/any/multi_cons_invalid_0/main.k new file mode 100644 index 000000000..c57af7009 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_invalid_0/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + any port in ports { + int(port) >= 1024 and int(port) <= 65535 + }, "invalid port" + + +main = app_conf { + ports: [-1] +} \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_invalid_0/stderr.golden.py b/test/grammar/quant/any/multi_cons_invalid_0/stderr.golden.py new file mode 100644 index 000000000..e5943ab87 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_invalid_0/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="invalid port") + , file=sys.stdout +) diff --git a/test/grammar/quant/any/multi_cons_invalid_1/main.k b/test/grammar/quant/any/multi_cons_invalid_1/main.k new file mode 100644 index 000000000..d4ecbab07 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_invalid_1/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + any port in ports { + int(port) == 80 or int(port) == 8080 + }, "invalid port" + + +main = app_conf { + ports: [9090] +} \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_invalid_1/stderr.golden.py b/test/grammar/quant/any/multi_cons_invalid_1/stderr.golden.py new file mode 100644 index 000000000..e5943ab87 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_invalid_1/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="invalid port") + , file=sys.stdout +) diff --git a/test/grammar/quant/any/multi_cons_invalid_2/main.k b/test/grammar/quant/any/multi_cons_invalid_2/main.k new file mode 100644 index 000000000..f331aa131 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_invalid_2/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + any port in ports { + (int(port) == 80 or int(port) == 8080) or (6000 <= int(port) <= 7000) + }, "invalid port" + + +main = app_conf { + ports: [9090] +} \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_invalid_2/stderr.golden.py b/test/grammar/quant/any/multi_cons_invalid_2/stderr.golden.py new file mode 100644 index 000000000..7322e4809 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_invalid_2/stderr.golden.py @@ -0,0 +1,26 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="invalid port") + , file=sys.stdout +) + diff --git a/test/grammar/quant/any/multi_cons_invalid_3/main.k b/test/grammar/quant/any/multi_cons_invalid_3/main.k new file mode 100644 index 000000000..023d105b0 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_invalid_3/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + any port in ports { + (int(port) >= 3000 and int(port) <= 4000) or (int(port) >= 6000 and int(port) <= 7000) + }, "invalid port" + + +main = app_conf { + ports: [2333] +} \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_invalid_3/stderr.golden.py b/test/grammar/quant/any/multi_cons_invalid_3/stderr.golden.py new file mode 100644 index 000000000..903348ae9 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_invalid_3/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="invalid port") + , file=sys.stdout +) + diff --git a/test/grammar/quant/any/multi_cons_invalid_4/main.k b/test/grammar/quant/any/multi_cons_invalid_4/main.k new file mode 100644 index 000000000..b358ee4aa --- /dev/null +++ b/test/grammar/quant/any/multi_cons_invalid_4/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + any port in ports { + (int(port) >= 3000 and int(port) <= 4000) and (int(port) >= 6000 and int(port) <= 7000) + }, "invalid port" + + +main = app_conf { + ports: [2333] +} \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_invalid_4/stderr.golden.py b/test/grammar/quant/any/multi_cons_invalid_4/stderr.golden.py new file mode 100644 index 000000000..7322e4809 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_invalid_4/stderr.golden.py @@ -0,0 +1,26 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="invalid port") + , file=sys.stdout +) + diff --git a/test/grammar/quant/any/multi_cons_valid_0/main.k b/test/grammar/quant/any/multi_cons_valid_0/main.k new file mode 100644 index 000000000..a3d83ca9d --- /dev/null +++ b/test/grammar/quant/any/multi_cons_valid_0/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + any port in ports { + int(port) >= 1024 and int(port) <= 65535 + }, "invalid port" + + +main = app_conf { + ports: [1024, 65536] +} \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_valid_0/stdout.golden b/test/grammar/quant/any/multi_cons_valid_0/stdout.golden new file mode 100644 index 000000000..eab05c9fc --- /dev/null +++ b/test/grammar/quant/any/multi_cons_valid_0/stdout.golden @@ -0,0 +1,4 @@ +main: + ports: + - 1024 + - 65536 \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_valid_1/main.k b/test/grammar/quant/any/multi_cons_valid_1/main.k new file mode 100644 index 000000000..e1a6dd6dc --- /dev/null +++ b/test/grammar/quant/any/multi_cons_valid_1/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + any port in ports { + int(port) == 80 or int(port) == 8080 + }, "invalid port" + + +main = app_conf { + ports: [80, 8081] +} \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_valid_1/stdout.golden b/test/grammar/quant/any/multi_cons_valid_1/stdout.golden new file mode 100644 index 000000000..ba8f3c44a --- /dev/null +++ b/test/grammar/quant/any/multi_cons_valid_1/stdout.golden @@ -0,0 +1,4 @@ +main: + ports: + - 80 + - 8081 \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_valid_2/main.k b/test/grammar/quant/any/multi_cons_valid_2/main.k new file mode 100644 index 000000000..867c393d7 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_valid_2/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + any port in ports { + (int(port) == 80 or int(port) == 8080) or (6000 <= int(port) <= 7000) + }, "invalid port" + + +main = app_conf { + ports: [6666, 9999] +} \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_valid_2/stdout.golden b/test/grammar/quant/any/multi_cons_valid_2/stdout.golden new file mode 100644 index 000000000..4a08ee952 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_valid_2/stdout.golden @@ -0,0 +1,4 @@ +main: + ports: + - 6666 + - 9999 \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_valid_3/main.k b/test/grammar/quant/any/multi_cons_valid_3/main.k new file mode 100644 index 000000000..a737ce1d4 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_valid_3/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + any port in ports { + (int(port) >= 3000 and int(port) <= 4000) or (int(port) >= 6000 and int(port) <= 7000) + }, "invalid port" + + +main = app_conf { + ports: [3333, 6666, 9999] +} \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_valid_3/stdout.golden b/test/grammar/quant/any/multi_cons_valid_3/stdout.golden new file mode 100644 index 000000000..ded78e21a --- /dev/null +++ b/test/grammar/quant/any/multi_cons_valid_3/stdout.golden @@ -0,0 +1,5 @@ +main: + ports: + - 3333 + - 6666 + - 9999 \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_valid_4/main.k b/test/grammar/quant/any/multi_cons_valid_4/main.k new file mode 100644 index 000000000..746b7fc3b --- /dev/null +++ b/test/grammar/quant/any/multi_cons_valid_4/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + any port in ports { + (int(port) >= 3000 or int(port) <= 4000) and (int(port) >= 6000 or int(port) <= 7000) + }, "invalid port" + + +main = app_conf { + ports: [3333, 6666, 2333] +} \ No newline at end of file diff --git a/test/grammar/quant/any/multi_cons_valid_4/stdout.golden b/test/grammar/quant/any/multi_cons_valid_4/stdout.golden new file mode 100644 index 000000000..268a4ab77 --- /dev/null +++ b/test/grammar/quant/any/multi_cons_valid_4/stdout.golden @@ -0,0 +1,5 @@ +main: + ports: + - 3333 + - 6666 + - 2333 \ No newline at end of file diff --git a/test/grammar/quant/any/simple_invalid_0/main.k b/test/grammar/quant/any/simple_invalid_0/main.k new file mode 100644 index 000000000..ede81fc9c --- /dev/null +++ b/test/grammar/quant/any/simple_invalid_0/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + any port in ports { + int(port) == 8080 + }, "no server defined" + + +main = app_conf { + ports: [80] +} \ No newline at end of file diff --git a/test/grammar/quant/any/simple_invalid_0/stderr.golden.py b/test/grammar/quant/any/simple_invalid_0/stderr.golden.py new file mode 100644 index 000000000..45187ecc5 --- /dev/null +++ b/test/grammar/quant/any/simple_invalid_0/stderr.golden.py @@ -0,0 +1,26 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="no server defined") + , file=sys.stdout +) + diff --git a/test/grammar/quant/any/simple_valid_0/main.k b/test/grammar/quant/any/simple_valid_0/main.k new file mode 100644 index 000000000..95b2f9f78 --- /dev/null +++ b/test/grammar/quant/any/simple_valid_0/main.k @@ -0,0 +1,12 @@ +schema app_conf: + ports: [int | str] + + check: + any port in ports { + int(port) == 8080 + }, "invalid port" + + +main = app_conf { + ports: [80, 8080] +} \ No newline at end of file diff --git a/test/grammar/quant/any/simple_valid_0/stdout.golden b/test/grammar/quant/any/simple_valid_0/stdout.golden new file mode 100644 index 000000000..5ab5e0fe1 --- /dev/null +++ b/test/grammar/quant/any/simple_valid_0/stdout.golden @@ -0,0 +1,4 @@ +main: + ports: + - 80 + - 8080 \ No newline at end of file diff --git a/test/grammar/quant/any/simple_valid_1/main.k b/test/grammar/quant/any/simple_valid_1/main.k new file mode 100644 index 000000000..e4ec9ce57 --- /dev/null +++ b/test/grammar/quant/any/simple_valid_1/main.k @@ -0,0 +1,7 @@ +schema Config: + data: str = "aaa" + + check: + any c in data {c == 'a'} + +config = Config {} diff --git a/test/grammar/quant/any/simple_valid_1/stdout.golden b/test/grammar/quant/any/simple_valid_1/stdout.golden new file mode 100644 index 000000000..df296ed82 --- /dev/null +++ b/test/grammar/quant/any/simple_valid_1/stdout.golden @@ -0,0 +1,2 @@ +config: + data: aaa diff --git a/test/grammar/quant/filter/simple_dict_0/main.k b/test/grammar/quant/filter/simple_dict_0/main.k new file mode 100644 index 000000000..882ae6372 --- /dev/null +++ b/test/grammar/quant/filter/simple_dict_0/main.k @@ -0,0 +1,9 @@ +data0 = filter _, v in { "a": "foo", "b": "bar" } { v == "foo" } +data1 = filter _, v in { "a": "foo", "b": "bar" } { + v == "foo" +} +_a = { "a": "foo", "b": "bar" } +data2 = filter _, v in _a { v == "foo" } +data3 = filter _, v in _a { + v == "foo" +} \ No newline at end of file diff --git a/test/grammar/quant/filter/simple_dict_0/stdout.golden b/test/grammar/quant/filter/simple_dict_0/stdout.golden new file mode 100644 index 000000000..58c3722b5 --- /dev/null +++ b/test/grammar/quant/filter/simple_dict_0/stdout.golden @@ -0,0 +1,8 @@ +data0: + a: foo +data1: + a: foo +data2: + a: foo +data3: + a: foo \ No newline at end of file diff --git a/test/grammar/quant/filter/simple_dict_1/main.k b/test/grammar/quant/filter/simple_dict_1/main.k new file mode 100644 index 000000000..0a26846a4 --- /dev/null +++ b/test/grammar/quant/filter/simple_dict_1/main.k @@ -0,0 +1,4 @@ +data0 = filter k, v in { "a": "foo", "b": "bar" } { k == "a" } +data1 = filter k, v in { "a": "foo", "b": "bar" } { + k == "a" and v == "foo" +} diff --git a/test/grammar/quant/filter/simple_dict_1/stdout.golden b/test/grammar/quant/filter/simple_dict_1/stdout.golden new file mode 100644 index 000000000..2206be7f8 --- /dev/null +++ b/test/grammar/quant/filter/simple_dict_1/stdout.golden @@ -0,0 +1,4 @@ +data0: + a: foo +data1: + a: foo diff --git a/test/grammar/quant/filter/simple_list_0/main.k b/test/grammar/quant/filter/simple_list_0/main.k new file mode 100644 index 000000000..05e99b5c9 --- /dev/null +++ b/test/grammar/quant/filter/simple_list_0/main.k @@ -0,0 +1,9 @@ +data0 = filter e in [{"name": "1", "value": 1}, {"name":"2", "value": 2}] { int(e.value) > 1 } +data1 = filter e in [{"name": "1", "value": 1}, {"name":"2", "value": 2}] { + int(e.value) > 1 +} +_var = [{"name": "1", "value": 1}, {"name":"2", "value": 2}] +data2 = filter e in _var { int(e.value) > 1 } +data3 = filter e in _var { + int(e.value) > 1 +} \ No newline at end of file diff --git a/test/grammar/quant/filter/simple_list_0/stdout.golden b/test/grammar/quant/filter/simple_list_0/stdout.golden new file mode 100644 index 000000000..21e86a2f3 --- /dev/null +++ b/test/grammar/quant/filter/simple_list_0/stdout.golden @@ -0,0 +1,12 @@ +data0: +- name: '2' + value: 2 +data1: +- name: '2' + value: 2 +data2: +- name: '2' + value: 2 +data3: +- name: '2' + value: 2 \ No newline at end of file diff --git a/test/grammar/quant/filter/simple_list_1/main.k b/test/grammar/quant/filter/simple_list_1/main.k new file mode 100644 index 000000000..8d80f7afa --- /dev/null +++ b/test/grammar/quant/filter/simple_list_1/main.k @@ -0,0 +1,4 @@ +data0 = filter i, e in [{"name": "1", "value": 1}, {"name":"2", "value": 2}] { int(e.value) > 1 and i > 0 } +data1 = filter i, e in [{"name": "1", "value": 1}, {"name":"2", "value": 2}] { + int(e.value) >= 1 and i == 0 +} diff --git a/test/grammar/quant/filter/simple_list_1/stdout.golden b/test/grammar/quant/filter/simple_list_1/stdout.golden new file mode 100644 index 000000000..7c857ad16 --- /dev/null +++ b/test/grammar/quant/filter/simple_list_1/stdout.golden @@ -0,0 +1,6 @@ +data0: +- name: '2' + value: 2 +data1: +- name: '1' + value: 1 diff --git a/test/grammar/quant/map/simple_dict_0/main.k b/test/grammar/quant/map/simple_dict_0/main.k new file mode 100644 index 000000000..71df4cba9 --- /dev/null +++ b/test/grammar/quant/map/simple_dict_0/main.k @@ -0,0 +1,9 @@ +data0 = map _, v in { "a": "foo", "b": "bar" } { v } +data1 = map _, v in { "a": "foo", "b": "bar" } { + v +} +_a = { "a": "foo", "b": "bar" } +data2 = map _, v in _a { v } +data3 = map _, v in _a { + v +} diff --git a/test/grammar/quant/map/simple_dict_0/stdout.golden b/test/grammar/quant/map/simple_dict_0/stdout.golden new file mode 100644 index 000000000..d2c972f24 --- /dev/null +++ b/test/grammar/quant/map/simple_dict_0/stdout.golden @@ -0,0 +1,12 @@ +data0: +- foo +- bar +data1: +- foo +- bar +data2: +- foo +- bar +data3: +- foo +- bar \ No newline at end of file diff --git a/test/grammar/quant/map/simple_list_0/main.k b/test/grammar/quant/map/simple_list_0/main.k new file mode 100644 index 000000000..f2b66f902 --- /dev/null +++ b/test/grammar/quant/map/simple_list_0/main.k @@ -0,0 +1,9 @@ +data0 = map e in [{"name": "1", "value": 1}, {"name":"2", "value": 2}] {{ "name": e.name, "value": int(e.value) ** 2 }} +data1 = map e in [{"name": "1", "value": 1}, {"name":"2", "value": 2}] { + {"name": e.name, "value": int(e.value) ** 2} +} +_var = [{"name": "1", "value": 1}, {"name":"2", "value": 2}] +data2 = map e in _var {{ "name": e.name, "value": int(e.value) ** 2 }} +data3 = map e in _var { + {"name": e.name, "value": int(e.value) ** 2} +} \ No newline at end of file diff --git a/test/grammar/quant/map/simple_list_0/stdout.golden b/test/grammar/quant/map/simple_list_0/stdout.golden new file mode 100644 index 000000000..0ff36c7d4 --- /dev/null +++ b/test/grammar/quant/map/simple_list_0/stdout.golden @@ -0,0 +1,20 @@ +data0: +- name: '1' + value: 1 +- name: '2' + value: 4 +data1: +- name: '1' + value: 1 +- name: '2' + value: 4 +data2: +- name: '1' + value: 1 +- name: '2' + value: 4 +data3: +- name: '1' + value: 1 +- name: '2' + value: 4 \ No newline at end of file diff --git a/test/grammar/quant/map/simple_list_1/main.k b/test/grammar/quant/map/simple_list_1/main.k new file mode 100644 index 000000000..72fd0dd7c --- /dev/null +++ b/test/grammar/quant/map/simple_list_1/main.k @@ -0,0 +1,4 @@ +data0 = map i, e in [{"name": "1", "value": 1}, {"name":"2", "value": 2}] { int(e.value) > 1 and i > 0 } +data1 = map i, e in [{"name": "1", "value": 1}, {"name":"2", "value": 2}] { + int(e.value) >= 1 and i == 0 +} diff --git a/test/grammar/quant/map/simple_list_1/stdout.golden b/test/grammar/quant/map/simple_list_1/stdout.golden new file mode 100644 index 000000000..d3e792c93 --- /dev/null +++ b/test/grammar/quant/map/simple_list_1/stdout.golden @@ -0,0 +1,6 @@ +data0: +- false +- true +data1: +- true +- false diff --git a/test/grammar/quant/map/simple_str_0/main.k b/test/grammar/quant/map/simple_str_0/main.k new file mode 100644 index 000000000..fd769a321 --- /dev/null +++ b/test/grammar/quant/map/simple_str_0/main.k @@ -0,0 +1,9 @@ +data0 = map e in "abC" { e.islower() } +data1 = map e in "abC" { + e.isupper() +} +_var = "abC" +data2 = map e in _var { e.islower() } +data3 = map e in _var { + e.isupper() +} diff --git a/test/grammar/quant/map/simple_str_0/stdout.golden b/test/grammar/quant/map/simple_str_0/stdout.golden new file mode 100644 index 000000000..947058a9f --- /dev/null +++ b/test/grammar/quant/map/simple_str_0/stdout.golden @@ -0,0 +1,16 @@ +data0: +- true +- true +- false +data1: +- false +- false +- true +data2: +- true +- true +- false +data3: +- false +- false +- true diff --git a/test/grammar/schema/check_block/check_block_0/main.k b/test/grammar/schema/check_block/check_block_0/main.k new file mode 100644 index 000000000..6e637039c --- /dev/null +++ b/test/grammar/schema/check_block/check_block_0/main.k @@ -0,0 +1,10 @@ + +schema Person: + firstName: str = "John" + lastName: str + check: + len(lastName) > 0 + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/check_block/check_block_0/stdout.golden b/test/grammar/schema/check_block/check_block_0/stdout.golden new file mode 100644 index 000000000..19b06330f --- /dev/null +++ b/test/grammar/schema/check_block/check_block_0/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: John + lastName: Doe diff --git a/test/grammar/schema/check_block/check_block_1/main.k b/test/grammar/schema/check_block/check_block_1/main.k new file mode 100644 index 000000000..795370aec --- /dev/null +++ b/test/grammar/schema/check_block/check_block_1/main.k @@ -0,0 +1,11 @@ + +schema Person: + firstName: str = "John" + lastName: str + check: + len(lastName) > 0 + len(firstName) > 0 + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/check_block/check_block_1/stdout.golden b/test/grammar/schema/check_block/check_block_1/stdout.golden new file mode 100644 index 000000000..19b06330f --- /dev/null +++ b/test/grammar/schema/check_block/check_block_1/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: John + lastName: Doe diff --git a/test/grammar/schema/check_block/check_block_10/main.k b/test/grammar/schema/check_block/check_block_10/main.k new file mode 100644 index 000000000..9780b4656 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_10/main.k @@ -0,0 +1,27 @@ +schema TitleMixin: + if gender == "female": + title = "Ms.{}".format(name) + else: + title = "Mr.{}".format(name) + + check: + gender.endswith("male"), "gender must be in *male format" + +schema Person: + name: str + title: str + gender: str + + check: + name.istitle(), "name of person must be titled" + +schema Girl(Person): + mixin [TitleMixin] + gender: str = "female" + + check: + gender.endswith("male"), "gender must be in *male format" + +alice = Girl { + "name": "Alice" +} diff --git a/test/grammar/schema/check_block/check_block_10/stdout.golden b/test/grammar/schema/check_block/check_block_10/stdout.golden new file mode 100644 index 000000000..fdca5a21f --- /dev/null +++ b/test/grammar/schema/check_block/check_block_10/stdout.golden @@ -0,0 +1,4 @@ +alice: + name: Alice + title: Ms.Alice + gender: female \ No newline at end of file diff --git a/test/grammar/schema/check_block/check_block_11/main.k b/test/grammar/schema/check_block/check_block_11/main.k new file mode 100644 index 000000000..9c435a217 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_11/main.k @@ -0,0 +1,28 @@ + +schema Person: + name: str + title: str + gender: str + + check: + name.istitle(), "name of person must be titled" + +schema Girl(Person): + mixin [TitleMixin] + gender: str = "female" + + check: + gender.endswith("male"), "gender must be in *male format" + +schema TitleMixin: + if gender == "female": + title = "Ms.{}".format(name) + else: + title = "Mr.{}".format(name) + + check: + gender.endswith("male"), "gender must be in *male format" + +alice = Girl { + "name": "Alice" +} diff --git a/test/grammar/schema/check_block/check_block_11/stdout.golden b/test/grammar/schema/check_block/check_block_11/stdout.golden new file mode 100644 index 000000000..871d1b48a --- /dev/null +++ b/test/grammar/schema/check_block/check_block_11/stdout.golden @@ -0,0 +1,4 @@ +alice: + name: Alice + title: Ms.Alice + gender: female diff --git a/test/grammar/schema/check_block/check_block_12/main.k b/test/grammar/schema/check_block/check_block_12/main.k new file mode 100644 index 000000000..74902bbde --- /dev/null +++ b/test/grammar/schema/check_block/check_block_12/main.k @@ -0,0 +1,11 @@ +schema Info: + msg: str + +schema Person: + info: Info = { + "msg": "message" + } + check: + info.msg, "{}".format(info.msg) + +person = Person {} diff --git a/test/grammar/schema/check_block/check_block_12/stdout.golden b/test/grammar/schema/check_block/check_block_12/stdout.golden new file mode 100644 index 000000000..0ee121a8e --- /dev/null +++ b/test/grammar/schema/check_block/check_block_12/stdout.golden @@ -0,0 +1,3 @@ +person: + info: + msg: message diff --git a/test/grammar/schema/check_block/check_block_13/main.k b/test/grammar/schema/check_block/check_block_13/main.k new file mode 100644 index 000000000..3ec3264a0 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_13/main.k @@ -0,0 +1,9 @@ +schema Person: + name: str + + check: + name, "{}".format(name) + +person = Person { + name: "Alice" +} diff --git a/test/grammar/schema/check_block/check_block_13/stdout.golden b/test/grammar/schema/check_block/check_block_13/stdout.golden new file mode 100644 index 000000000..bcbe4170c --- /dev/null +++ b/test/grammar/schema/check_block/check_block_13/stdout.golden @@ -0,0 +1,2 @@ +person: + name: Alice diff --git a/test/grammar/schema/check_block/check_block_14/main.k b/test/grammar/schema/check_block/check_block_14/main.k new file mode 100644 index 000000000..088455fd8 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_14/main.k @@ -0,0 +1,7 @@ +schema Config[data: {str:str}]: + result: {str:str} = {name = "value"} if data.name == "value" else Undefined + + check: + result, "Invalid data name value {}".format(data.name) + +config = Config({name = "value"}) diff --git a/test/grammar/schema/check_block/check_block_14/stdout.golden b/test/grammar/schema/check_block/check_block_14/stdout.golden new file mode 100644 index 000000000..9eb5f2af0 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_14/stdout.golden @@ -0,0 +1,3 @@ +config: + result: + name: value diff --git a/test/grammar/schema/check_block/check_block_2/main.k b/test/grammar/schema/check_block/check_block_2/main.k new file mode 100644 index 000000000..2e293735f --- /dev/null +++ b/test/grammar/schema/check_block/check_block_2/main.k @@ -0,0 +1,23 @@ +schema Person: + firstName: str = "John" + midName: str = "Jon" + lastName: str + image: str + image_pull_policy: str + check: + image is not None and image_pull_policy is not None, "oh no, {}".format("we fail here") + image is not None and image_pull_policy is not None + image_pull_policy in ["Always", "Never", "IfNotPresent"] + len(lastName) + (lastName is not None) + lastName is not None + (image_pull_policy is not None) if image + image_pull_policy if image + len(lastName) > 0 + len(lastName), "oh no, {}".format("we fail here") + +JohnDoe = Person { + "lastName": "Doe", + "image": "docker.image", + "image_pull_policy": "Never" +} diff --git a/test/grammar/schema/check_block/check_block_2/stdout.golden b/test/grammar/schema/check_block/check_block_2/stdout.golden new file mode 100644 index 000000000..ad2417798 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_2/stdout.golden @@ -0,0 +1,6 @@ +JohnDoe: + firstName: John + midName: Jon + lastName: Doe + image: docker.image + image_pull_policy: Never diff --git a/test/grammar/schema/check_block/check_block_3/main.k b/test/grammar/schema/check_block/check_block_3/main.k new file mode 100644 index 000000000..77fa501e4 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_3/main.k @@ -0,0 +1,16 @@ + +schema Person: + firstName: str = "John" + lastName: str + check: + len(lastName) > 0 + +schema Student(Person): + age: int + check: + 12 <= age <= 18 + +JohnDoe = Student { + "lastName": "Doe", + "age": 15 +} diff --git a/test/grammar/schema/check_block/check_block_3/stdout.golden b/test/grammar/schema/check_block/check_block_3/stdout.golden new file mode 100644 index 000000000..b359bc8b7 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_3/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + age: 15 diff --git a/test/grammar/schema/check_block/check_block_4/main.k b/test/grammar/schema/check_block/check_block_4/main.k new file mode 100644 index 000000000..00361888d --- /dev/null +++ b/test/grammar/schema/check_block/check_block_4/main.k @@ -0,0 +1,11 @@ +schema Person: + name: str + family: {str:str} + + check: + ("father" in family) if family + +SameenShaw = Person { + "name": "Shaw", + "family": {"father": "Finch"} +} diff --git a/test/grammar/schema/check_block/check_block_4/stdout.golden b/test/grammar/schema/check_block/check_block_4/stdout.golden new file mode 100644 index 000000000..885c89a6f --- /dev/null +++ b/test/grammar/schema/check_block/check_block_4/stdout.golden @@ -0,0 +1,4 @@ +SameenShaw: + name: Shaw + family: + father: Finch \ No newline at end of file diff --git a/test/grammar/schema/check_block/check_block_5/main.k b/test/grammar/schema/check_block/check_block_5/main.k new file mode 100644 index 000000000..41badadd4 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_5/main.k @@ -0,0 +1,10 @@ +schema Person: + name?: str = None + family?: {str:str} = None + + check: + ("father" in family) if family + +SameenShaw = Person { + "name": "Shaw" +} diff --git a/test/grammar/schema/check_block/check_block_5/stdout.golden b/test/grammar/schema/check_block/check_block_5/stdout.golden new file mode 100644 index 000000000..c75d40301 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_5/stdout.golden @@ -0,0 +1,3 @@ +SameenShaw: + name: Shaw + family: null \ No newline at end of file diff --git a/test/grammar/schema/check_block/check_block_6/main.k b/test/grammar/schema/check_block/check_block_6/main.k new file mode 100644 index 000000000..b0428fa47 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_6/main.k @@ -0,0 +1,10 @@ +schema Person: + name?: str = None + family?: {str:str} = None + + check: + ("father" not in family) if family + +SameenShaw = Person { + "name": "Shaw" +} diff --git a/test/grammar/schema/check_block/check_block_6/stdout.golden b/test/grammar/schema/check_block/check_block_6/stdout.golden new file mode 100644 index 000000000..c75d40301 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_6/stdout.golden @@ -0,0 +1,3 @@ +SameenShaw: + name: Shaw + family: null \ No newline at end of file diff --git a/test/grammar/schema/check_block/check_block_7/main.k b/test/grammar/schema/check_block/check_block_7/main.k new file mode 100644 index 000000000..aee95ae25 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_7/main.k @@ -0,0 +1,14 @@ +schema Person: + labels: {str:str} + name: str + + check: + name, "name should be defined and not empty" + labels, "labels should be defined and not empty" + +JohnDoe = Person { + "name": "John", + "labels": { + "school": "PKU" + } +} diff --git a/test/grammar/schema/check_block/check_block_7/stdout.golden b/test/grammar/schema/check_block/check_block_7/stdout.golden new file mode 100644 index 000000000..1ca4ccc4c --- /dev/null +++ b/test/grammar/schema/check_block/check_block_7/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + labels: + school: PKU + name: John diff --git a/test/grammar/schema/check_block/check_block_8/main.k b/test/grammar/schema/check_block/check_block_8/main.k new file mode 100644 index 000000000..d34ff2f6f --- /dev/null +++ b/test/grammar/schema/check_block/check_block_8/main.k @@ -0,0 +1,14 @@ +schema Person: + name: str + gender: str + + check: + name, "name of person should be defined and not empty" + gender, "gender of person should be defined and not empty" + +schema Girl(Person): + gender: str = "female" + +alice = Girl { + "name": "alice" +} diff --git a/test/grammar/schema/check_block/check_block_8/stdout.golden b/test/grammar/schema/check_block/check_block_8/stdout.golden new file mode 100644 index 000000000..7eefa0114 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_8/stdout.golden @@ -0,0 +1,3 @@ +alice: + name: alice + gender: female diff --git a/test/grammar/schema/check_block/check_block_9/main.k b/test/grammar/schema/check_block/check_block_9/main.k new file mode 100644 index 000000000..8fbfb551b --- /dev/null +++ b/test/grammar/schema/check_block/check_block_9/main.k @@ -0,0 +1,9 @@ +schema Person: + name: str + + check: + name.istitle(), "name of person must be titled" + +alice = Person { + "name": "Alice" +} diff --git a/test/grammar/schema/check_block/check_block_9/stdout.golden b/test/grammar/schema/check_block/check_block_9/stdout.golden new file mode 100644 index 000000000..ed18f916f --- /dev/null +++ b/test/grammar/schema/check_block/check_block_9/stdout.golden @@ -0,0 +1,2 @@ +alice: + name: Alice diff --git a/test/grammar/schema/check_block/check_block_fail_0/main.k b/test/grammar/schema/check_block/check_block_fail_0/main.k new file mode 100644 index 000000000..71bb02a44 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_0/main.k @@ -0,0 +1,23 @@ +schema Person: + firstName: str = "John" + midName: str = "Jon" + lastName: str + image: str + image_pull_policy: str + check: + image not None and image_pull_policy not None, "oh no, {}".format("we fail here") + image not None and image_pull_policy not None + image_pull_policy in ["Always", "Never", "IfNotPresent"] + len(lastName) + (lastName not None) + lastName not None + image_pull_policy not None if image + image_pull_policy if image + len(lastName) > 0 + len(lastName), "oh no, {}".format("we fail here") + +JohnDoe = Person { + "lastName": "Doe", + "image": "docker.image", + "image_pull_policy": "" +} diff --git a/test/grammar/schema/check_block/check_block_fail_0/stderr.golden.py b/test/grammar/schema/check_block/check_block_fail_0/stderr.golden.py new file mode 100644 index 000000000..c58bf295a --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_0/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=19, + col_no=11, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ]) + , file=sys.stdout +) + diff --git a/test/grammar/schema/check_block/check_block_fail_1/main.k b/test/grammar/schema/check_block/check_block_fail_1/main.k new file mode 100644 index 000000000..1db385450 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_1/main.k @@ -0,0 +1,12 @@ +schema Person: + firstName: str = "John" + midName: str = "Spam" + lastName: str + age: int + check: + age < 140, "age is too large" + +JohnDoe = Person { + "lastName": "Doe", + "age": 1000 +} diff --git a/test/grammar/schema/check_block/check_block_fail_1/stderr.golden.py b/test/grammar/schema/check_block/check_block_fail_1/stderr.golden.py new file mode 100644 index 000000000..478860683 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_1/stderr.golden.py @@ -0,0 +1,26 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND, + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=9, + col_no=11, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR, + ), + ], + arg_msg="age is too large") + , file=sys.stdout +) + diff --git a/test/grammar/schema/check_block/check_block_fail_10/main.k b/test/grammar/schema/check_block/check_block_fail_10/main.k new file mode 100644 index 000000000..8d322bb21 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_10/main.k @@ -0,0 +1,11 @@ +schema Count: + firstCount: int + secondCount: int + check: + firstCount > 100 + secondCount < 100 + +count = Count { + firstCount: 200, + secondCount: 200 +} diff --git a/test/grammar/schema/check_block/check_block_fail_10/stderr.golden.py b/test/grammar/schema/check_block/check_block_fail_10/stderr.golden.py new file mode 100644 index 000000000..55d498f65 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_10/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=8, + col_no=9, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ]) + , file=sys.stdout +) + diff --git a/test/grammar/schema/check_block/check_block_fail_11/kcl.mod b/test/grammar/schema/check_block/check_block_fail_11/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/check_block/check_block_fail_11/main.k b/test/grammar/schema/check_block/check_block_fail_11/main.k new file mode 100644 index 000000000..97462bd9f --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_11/main.k @@ -0,0 +1,3 @@ +import pkg + +alice = pkg.Person {} diff --git a/test/grammar/schema/check_block/check_block_fail_11/pkg/person.k b/test/grammar/schema/check_block/check_block_fail_11/pkg/person.k new file mode 100644 index 000000000..63a55680a --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_11/pkg/person.k @@ -0,0 +1,5 @@ +schema Person: + name?: str + + check: + name, "name should be defined" diff --git a/test/grammar/schema/check_block/check_block_fail_11/stderr.golden.py b/test/grammar/schema/check_block/check_block_fail_11/stderr.golden.py new file mode 100644 index 000000000..80933a12c --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_11/stderr.golden.py @@ -0,0 +1,26 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/pkg/person.k", + line_no=5, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=9, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="name should be defined") + , file=sys.stdout +) + diff --git a/test/grammar/schema/check_block/check_block_fail_2/main.k b/test/grammar/schema/check_block/check_block_fail_2/main.k new file mode 100644 index 000000000..1516b438b --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_2/main.k @@ -0,0 +1,16 @@ + +schema Person: + firstName: str = "John" + lastName: str + check: + len(lastName) > 0 + +schema Student(Person): + age: int + check: + 12 <= age <= 18 + +JohnDoe = Student { + "lastName": "Doe", + "age": 19 +} diff --git a/test/grammar/schema/check_block/check_block_fail_2/stderr.golden.py b/test/grammar/schema/check_block/check_block_fail_2/stderr.golden.py new file mode 100644 index 000000000..899f3ee56 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_2/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=11, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=13, + col_no=11, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ]) + , file=sys.stdout +) + diff --git a/test/grammar/schema/check_block/check_block_fail_3/main.k b/test/grammar/schema/check_block/check_block_fail_3/main.k new file mode 100644 index 000000000..ac9a75abc --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_3/main.k @@ -0,0 +1,16 @@ + +schema Person: + firstName: str = "John" + lastName: str + check: + len(lastName) < 10 + +schema Student(Person): + age: int + check: + 12 <= age <= 18 + +JohnDoe = Student { + "lastName": "Doeblablablabla", + "age": 17 +} diff --git a/test/grammar/schema/check_block/check_block_fail_3/stderr.golden.py b/test/grammar/schema/check_block/check_block_fail_3/stderr.golden.py new file mode 100644 index 000000000..51ccccd68 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_3/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=13, + col_no=11, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ]) + , file=sys.stdout +) + diff --git a/test/grammar/schema/check_block/check_block_fail_4/main.k b/test/grammar/schema/check_block/check_block_fail_4/main.k new file mode 100644 index 000000000..d3b00cdcd --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_4/main.k @@ -0,0 +1,11 @@ +schema Person: + name: str + family: {str:str} + + check: + "father" not in family if family + +SameenShaw = Person { + "name": "Shaw", + "family": {"father": "Finch"} +} diff --git a/test/grammar/schema/check_block/check_block_fail_4/stderr.golden.py b/test/grammar/schema/check_block/check_block_fail_4/stderr.golden.py new file mode 100644 index 000000000..cebae8ba3 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_4/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=8, + col_no=14, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ]) + , file=sys.stdout +) + diff --git a/test/grammar/schema/check_block/check_block_fail_5/main.k b/test/grammar/schema/check_block/check_block_fail_5/main.k new file mode 100644 index 000000000..68d8c9c2d --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_5/main.k @@ -0,0 +1,14 @@ +schema Person: + labels: {str:str} + name: str + + check: + name, "name should be defined and not empty" + labels, "labels should be defined and not empty" + +JohnDoe = Person { + "name": "", + "labels": { + "school": "PKU" + } +} diff --git a/test/grammar/schema/check_block/check_block_fail_5/stderr.golden.py b/test/grammar/schema/check_block/check_block_fail_5/stderr.golden.py new file mode 100644 index 000000000..f93873685 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_5/stderr.golden.py @@ -0,0 +1,24 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=9, + col_no=11, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="name should be defined and not empty") + , file=sys.stdout +) diff --git a/test/grammar/schema/check_block/check_block_fail_6/main.k b/test/grammar/schema/check_block/check_block_fail_6/main.k new file mode 100644 index 000000000..ab30cf589 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_6/main.k @@ -0,0 +1,11 @@ +schema Person: + labels?: {str:str} + name?: str + + check: + name, "name should be defined and not empty" + labels, "labels should be defined and not empty" + +JohnDoe = Person { + "name": "John" +} diff --git a/test/grammar/schema/check_block/check_block_fail_6/stderr.golden.py b/test/grammar/schema/check_block/check_block_fail_6/stderr.golden.py new file mode 100644 index 000000000..bd9a20b70 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_6/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=9, + col_no=11, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="labels should be defined and not empty") + , file=sys.stdout +) diff --git a/test/grammar/schema/check_block/check_block_fail_7/main.k b/test/grammar/schema/check_block/check_block_fail_7/main.k new file mode 100644 index 000000000..2736826c4 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_7/main.k @@ -0,0 +1,12 @@ +schema Person: + labels: {str:str} + name: str + + check: + name, "name should be defined and not empty" + labels, "labels should be defined and not empty" + +JohnDoe = Person { + "name": "John", + "labels": {} +} diff --git a/test/grammar/schema/check_block/check_block_fail_7/stderr.golden.py b/test/grammar/schema/check_block/check_block_fail_7/stderr.golden.py new file mode 100644 index 000000000..bd9a20b70 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_7/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=9, + col_no=11, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="labels should be defined and not empty") + , file=sys.stdout +) diff --git a/test/grammar/schema/check_block/check_block_fail_8/main.k b/test/grammar/schema/check_block/check_block_fail_8/main.k new file mode 100644 index 000000000..bf49eb2c0 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_8/main.k @@ -0,0 +1,12 @@ +schema Person: + labels: [str] + name: str + + check: + name, "name should be defined and not empty" + labels, "labels should be defined and not empty" + +JohnDoe = Person { + "name": "John", + "labels": [] +} diff --git a/test/grammar/schema/check_block/check_block_fail_8/stderr.golden.py b/test/grammar/schema/check_block/check_block_fail_8/stderr.golden.py new file mode 100644 index 000000000..bd9a20b70 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_8/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=9, + col_no=11, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="labels should be defined and not empty") + , file=sys.stdout +) diff --git a/test/grammar/schema/check_block/check_block_fail_9/main.k b/test/grammar/schema/check_block/check_block_fail_9/main.k new file mode 100644 index 000000000..f483345e4 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_9/main.k @@ -0,0 +1,11 @@ +schema Name: + firstName?: str + check: + firstName, "firstName should be defined and not empty" + +schema Person: + name: Name + +JohnDoe = Person { + "name": Name {} +} diff --git a/test/grammar/schema/check_block/check_block_fail_9/stderr.golden.py b/test/grammar/schema/check_block/check_block_fail_9/stderr.golden.py new file mode 100644 index 000000000..64402d7a5 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_fail_9/stderr.golden.py @@ -0,0 +1,26 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=13, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ], + arg_msg="firstName should be defined and not empty") + , file=sys.stdout +) + diff --git a/test/grammar/schema/check_block/check_block_with_init/main.k b/test/grammar/schema/check_block/check_block_with_init/main.k new file mode 100644 index 000000000..1fca49d74 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_with_init/main.k @@ -0,0 +1,12 @@ + +schema Person: + name: str + + # print("init block do nothing") + + check: + name not None, "we fail here" + +JohnDoe = Person { + "name": "John" +} diff --git a/test/grammar/schema/check_block/check_block_with_init/stdout.golden b/test/grammar/schema/check_block/check_block_with_init/stdout.golden new file mode 100644 index 000000000..0947ff9a6 --- /dev/null +++ b/test/grammar/schema/check_block/check_block_with_init/stdout.golden @@ -0,0 +1,2 @@ +JohnDoe: + name: John diff --git a/test/grammar/schema/check_block/check_iteratively_list_0/main.k b/test/grammar/schema/check_block/check_iteratively_list_0/main.k new file mode 100644 index 000000000..440e3a8c8 --- /dev/null +++ b/test/grammar/schema/check_block/check_iteratively_list_0/main.k @@ -0,0 +1,19 @@ +schema Person: + family: [{str:str}] + name: str + names: [str] = [people["name"] for people in family] + + check: + len([True for people in family if "name" in people]) == len(family), "all family should have name defined." + +alice = Person { + name: "alice", + family: [ + { + name: "bob" + }, + { + name: "john" + } + ] +} diff --git a/test/grammar/schema/check_block/check_iteratively_list_0/stdout.golden b/test/grammar/schema/check_block/check_iteratively_list_0/stdout.golden new file mode 100644 index 000000000..9e6508f7e --- /dev/null +++ b/test/grammar/schema/check_block/check_iteratively_list_0/stdout.golden @@ -0,0 +1,8 @@ +alice: + family: + - name: bob + - name: john + name: alice + names: + - bob + - john \ No newline at end of file diff --git a/test/grammar/schema/complex/init_check_order_0/main.k b/test/grammar/schema/complex/init_check_order_0/main.k new file mode 100644 index 000000000..a76f90c8b --- /dev/null +++ b/test/grammar/schema/complex/init_check_order_0/main.k @@ -0,0 +1,11 @@ +schema Person: + name: str + age: int + gender: str + info: str = "{}, {}, {} years old".format(name, gender, age) + +alice = Person { + "name": "alice", + "age": 10, + "gender": "female" +} diff --git a/test/grammar/schema/complex/init_check_order_0/stdout.golden b/test/grammar/schema/complex/init_check_order_0/stdout.golden new file mode 100644 index 000000000..7662011b6 --- /dev/null +++ b/test/grammar/schema/complex/init_check_order_0/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: alice + age: 10 + gender: female + info: alice, female, 10 years old \ No newline at end of file diff --git a/test/grammar/schema/complex/init_check_order_1/main.k b/test/grammar/schema/complex/init_check_order_1/main.k new file mode 100644 index 000000000..e728b4a93 --- /dev/null +++ b/test/grammar/schema/complex/init_check_order_1/main.k @@ -0,0 +1,62 @@ + +schema Name: + mixin [UpperMixin] + firstName: str + lastName: str + upper: str + + # print("init name") + +schema Person(Name): + gender: str + title: str + info: str + + # print("init person") + +schema Girl(Person): + mixin [TitleMixin, InfoMixin] + gender: str = "female" + added: str = "some girl attr" + + # print("init girl") + + check: + gender == "female", "gender should be female in Girl" + +schema Boy(Person): + mixin [TitleMixin, InfoMixin] + gender: str = "male" + added: str = "some boy attr" + + # print("init boy") + + check: + gender == "male", "gender should be male in Boy" + +schema UpperMixin: + # print("init upperMixin") + upper: str = lastName.upper() + +schema TitleMixin: + # print("init title mixin") + if gender == "female": + title = "Ms.{}".format(lastName) + else: + title = "Mr.{}".format(lastName) + +schema InfoMixin: + # print("init info mixin") + info = "{}, {}".format(title, gender) + +alice = Girl { + "firstName": "Alice", + "lastName": "Smith" +} + +# print(" ===") + +bob = Boy { + "firstName": "Bob", + "lastName": "Green" +} diff --git a/test/grammar/schema/complex/init_check_order_1/stdout.golden b/test/grammar/schema/complex/init_check_order_1/stdout.golden new file mode 100644 index 000000000..66df8e134 --- /dev/null +++ b/test/grammar/schema/complex/init_check_order_1/stdout.golden @@ -0,0 +1,16 @@ +alice: + firstName: Alice + lastName: Smith + upper: SMITH + gender: female + title: Ms.Smith + info: Ms.Smith, female + added: some girl attr +bob: + firstName: Bob + lastName: Green + upper: GREEN + gender: male + title: Mr.Green + info: Mr.Green, male + added: some boy attr diff --git a/test/grammar/schema/config_op/delete/delete_0/main.k b/test/grammar/schema/config_op/delete/delete_0/main.k new file mode 100644 index 000000000..77b9de075 --- /dev/null +++ b/test/grammar/schema/config_op/delete/delete_0/main.k @@ -0,0 +1,8 @@ +schema Person: + info: {str:} = {"key1": "value1"} + hc: [int] = [1, 2, 3] + +person = Person { + info = {"key2": "value2"} + hc[0] = None +} diff --git a/test/grammar/schema/config_op/delete/delete_0/stdout.golden b/test/grammar/schema/config_op/delete/delete_0/stdout.golden new file mode 100644 index 000000000..5d7086c6e --- /dev/null +++ b/test/grammar/schema/config_op/delete/delete_0/stdout.golden @@ -0,0 +1,6 @@ +person: + info: + key2: value2 + hc: + - 2 + - 3 diff --git a/test/grammar/schema/config_op/insert/insert_0/main.k b/test/grammar/schema/config_op/insert/insert_0/main.k new file mode 100644 index 000000000..1f451a418 --- /dev/null +++ b/test/grammar/schema/config_op/insert/insert_0/main.k @@ -0,0 +1,8 @@ +schema Person: + info: {str:} = {"key1": "value1"} + hc: [int] = [1, 2, 3] + +person = Person { + info = {"key2": "value2"} + hc += [4] +} diff --git a/test/grammar/schema/config_op/insert/insert_0/stdout.golden b/test/grammar/schema/config_op/insert/insert_0/stdout.golden new file mode 100644 index 000000000..2743354e0 --- /dev/null +++ b/test/grammar/schema/config_op/insert/insert_0/stdout.golden @@ -0,0 +1,8 @@ +person: + info: + key2: value2 + hc: + - 1 + - 2 + - 3 + - 4 diff --git a/test/grammar/schema/config_op/insert/insert_1/main.k b/test/grammar/schema/config_op/insert/insert_1/main.k new file mode 100644 index 000000000..1f451a418 --- /dev/null +++ b/test/grammar/schema/config_op/insert/insert_1/main.k @@ -0,0 +1,8 @@ +schema Person: + info: {str:} = {"key1": "value1"} + hc: [int] = [1, 2, 3] + +person = Person { + info = {"key2": "value2"} + hc += [4] +} diff --git a/test/grammar/schema/config_op/insert/insert_1/stdout.golden b/test/grammar/schema/config_op/insert/insert_1/stdout.golden new file mode 100644 index 000000000..2743354e0 --- /dev/null +++ b/test/grammar/schema/config_op/insert/insert_1/stdout.golden @@ -0,0 +1,8 @@ +person: + info: + key2: value2 + hc: + - 1 + - 2 + - 3 + - 4 diff --git a/test/grammar/schema/config_op/insert/insert_2/main.k b/test/grammar/schema/config_op/insert/insert_2/main.k new file mode 100644 index 000000000..9b53b975c --- /dev/null +++ b/test/grammar/schema/config_op/insert/insert_2/main.k @@ -0,0 +1,8 @@ +schema Person: + info: {str:} = {"key1": "value1"} + hc: [int] = [1, 2, 3] + +person = Person { + info = {"key2": "value2"} + hc += [4, 5] +} diff --git a/test/grammar/schema/config_op/insert/insert_2/stdout.golden b/test/grammar/schema/config_op/insert/insert_2/stdout.golden new file mode 100644 index 000000000..f80f3d199 --- /dev/null +++ b/test/grammar/schema/config_op/insert/insert_2/stdout.golden @@ -0,0 +1,9 @@ +person: + info: + key2: value2 + hc: + - 1 + - 2 + - 3 + - 4 + - 5 diff --git a/test/grammar/schema/config_op/insert/insert_3/main.k b/test/grammar/schema/config_op/insert/insert_3/main.k new file mode 100644 index 000000000..4eb0ad342 --- /dev/null +++ b/test/grammar/schema/config_op/insert/insert_3/main.k @@ -0,0 +1,18 @@ +schema Person: + info: {str:str} = { + "name": "default name" + } + hc: [int] = [1, 2, 3] + hc1: [int] = [1, 2, 3] + hc2: [int] = [1, 2, 3] + hc3: [int] = [1, 2, 3] + hc4: [int] = [1, 2, 3] + hc5: [int] = [1, 2, 3] + +person = Person { + hc += [4] + hc += [5] + hc1 += [4] + hc2 += [4] + hc3 = [4, 5, 6] +} diff --git a/test/grammar/schema/config_op/insert/insert_3/stdout.golden b/test/grammar/schema/config_op/insert/insert_3/stdout.golden new file mode 100644 index 000000000..398a75016 --- /dev/null +++ b/test/grammar/schema/config_op/insert/insert_3/stdout.golden @@ -0,0 +1,31 @@ +person: + info: + name: default name + hc: + - 1 + - 2 + - 3 + - 4 + - 5 + hc1: + - 1 + - 2 + - 3 + - 4 + hc2: + - 1 + - 2 + - 3 + - 4 + hc3: + - 4 + - 5 + - 6 + hc4: + - 1 + - 2 + - 3 + hc5: + - 1 + - 2 + - 3 diff --git a/test/grammar/schema/config_op/insert/insert_4/main.k b/test/grammar/schema/config_op/insert/insert_4/main.k new file mode 100644 index 000000000..4001d3b55 --- /dev/null +++ b/test/grammar/schema/config_op/insert/insert_4/main.k @@ -0,0 +1,12 @@ +schema Spec: + counts: [int] + +schema Data: + spec?: Spec + +d1 = Data { + spec.counts += [0] +} +d2 = d1 { + spec.counts += [1] +} diff --git a/test/grammar/schema/config_op/insert/insert_4/stdout.golden b/test/grammar/schema/config_op/insert/insert_4/stdout.golden new file mode 100644 index 000000000..bb0c83cc8 --- /dev/null +++ b/test/grammar/schema/config_op/insert/insert_4/stdout.golden @@ -0,0 +1,9 @@ +d1: + spec: + counts: + - 0 +d2: + spec: + counts: + - 0 + - 1 diff --git a/test/grammar/schema/config_op/override/override_0/main.k b/test/grammar/schema/config_op/override/override_0/main.k new file mode 100644 index 000000000..e2a8aaf11 --- /dev/null +++ b/test/grammar/schema/config_op/override/override_0/main.k @@ -0,0 +1,7 @@ +schema Person: + firstName: str = "John" + lastName: str + +JohnDoe = Person { + "lastName" = "Doe" +} diff --git a/test/grammar/schema/config_op/override/override_0/stdout.golden b/test/grammar/schema/config_op/override/override_0/stdout.golden new file mode 100644 index 000000000..19b06330f --- /dev/null +++ b/test/grammar/schema/config_op/override/override_0/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: John + lastName: Doe diff --git a/test/grammar/schema/config_op/override/override_1/main.k b/test/grammar/schema/config_op/override/override_1/main.k new file mode 100644 index 000000000..5d42a5d4e --- /dev/null +++ b/test/grammar/schema/config_op/override/override_1/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName: str = "John" + lastName: str + age: int = 10 + +JohnDoe = Person { + lastName = "Doe" + age = 10 +} diff --git a/test/grammar/schema/config_op/override/override_1/stdout.golden b/test/grammar/schema/config_op/override/override_1/stdout.golden new file mode 100644 index 000000000..276ea43a5 --- /dev/null +++ b/test/grammar/schema/config_op/override/override_1/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + age: 10 diff --git a/test/grammar/schema/config_op/override/override_2/main.k b/test/grammar/schema/config_op/override/override_2/main.k new file mode 100644 index 000000000..a66825e4e --- /dev/null +++ b/test/grammar/schema/config_op/override/override_2/main.k @@ -0,0 +1,8 @@ +schema Person: + info: {str:} = {"key1": "value1"} + hc: [int] = [1, 2, 3] + +person = Person { + info = {"key2": "value2"} + hc = [4, 5] +} diff --git a/test/grammar/schema/config_op/override/override_2/stdout.golden b/test/grammar/schema/config_op/override/override_2/stdout.golden new file mode 100644 index 000000000..de8524409 --- /dev/null +++ b/test/grammar/schema/config_op/override/override_2/stdout.golden @@ -0,0 +1,6 @@ +person: + info: + key2: value2 + hc: + - 4 + - 5 diff --git a/test/grammar/schema/config_op/union/union_0/main.k b/test/grammar/schema/config_op/union/union_0/main.k new file mode 100644 index 000000000..4f727fd16 --- /dev/null +++ b/test/grammar/schema/config_op/union/union_0/main.k @@ -0,0 +1,7 @@ +schema Person: + firstName: str = "John" + lastName: str + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/config_op/union/union_0/stdout.golden b/test/grammar/schema/config_op/union/union_0/stdout.golden new file mode 100644 index 000000000..19b06330f --- /dev/null +++ b/test/grammar/schema/config_op/union/union_0/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: John + lastName: Doe diff --git a/test/grammar/schema/config_op/union/union_1/main.k b/test/grammar/schema/config_op/union/union_1/main.k new file mode 100644 index 000000000..98bc268a4 --- /dev/null +++ b/test/grammar/schema/config_op/union/union_1/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName: str = "John" + lastName: str + age: int = 10 + +JohnDoe = Person { + lastName: "Doe" + age: 10 +} diff --git a/test/grammar/schema/config_op/union/union_1/stdout.golden b/test/grammar/schema/config_op/union/union_1/stdout.golden new file mode 100644 index 000000000..276ea43a5 --- /dev/null +++ b/test/grammar/schema/config_op/union/union_1/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + age: 10 diff --git a/test/grammar/schema/config_op/union/union_2/main.k b/test/grammar/schema/config_op/union/union_2/main.k new file mode 100644 index 000000000..9cf8111ce --- /dev/null +++ b/test/grammar/schema/config_op/union/union_2/main.k @@ -0,0 +1,8 @@ +schema Person: + info: {str:} = {"key1": "value1"} + hc: [int] = [1, 2, 3] + +person = Person { + info: {"key2": "value2"} + hc = [4, 5] +} diff --git a/test/grammar/schema/config_op/union/union_2/stdout.golden b/test/grammar/schema/config_op/union/union_2/stdout.golden new file mode 100644 index 000000000..c7163e414 --- /dev/null +++ b/test/grammar/schema/config_op/union/union_2/stdout.golden @@ -0,0 +1,7 @@ +person: + info: + key1: value1 + key2: value2 + hc: + - 4 + - 5 diff --git a/test/grammar/schema/default_value/default_value_0/main.k b/test/grammar/schema/default_value/default_value_0/main.k new file mode 100644 index 000000000..4a288a049 --- /dev/null +++ b/test/grammar/schema/default_value/default_value_0/main.k @@ -0,0 +1,8 @@ + +schema Person: + firstName: str = "John" + lastName: str + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/default_value/default_value_0/stdout.golden b/test/grammar/schema/default_value/default_value_0/stdout.golden new file mode 100644 index 000000000..19b06330f --- /dev/null +++ b/test/grammar/schema/default_value/default_value_0/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: John + lastName: Doe diff --git a/test/grammar/schema/default_value/default_value_1/main.k b/test/grammar/schema/default_value/default_value_1/main.k new file mode 100644 index 000000000..23700ea10 --- /dev/null +++ b/test/grammar/schema/default_value/default_value_1/main.k @@ -0,0 +1,10 @@ +schema Person: + firstName: str = "John" + lastName: str + list: [str] = [] + +JoeDoe = Person { + "firstName" = "Joe", + "lastName" = "Doe", + "list" = ["value"] +} diff --git a/test/grammar/schema/default_value/default_value_1/stdout.golden b/test/grammar/schema/default_value/default_value_1/stdout.golden new file mode 100644 index 000000000..69ecbce54 --- /dev/null +++ b/test/grammar/schema/default_value/default_value_1/stdout.golden @@ -0,0 +1,5 @@ +JoeDoe: + firstName: Joe + lastName: Doe + list: + - value diff --git a/test/grammar/schema/default_value/default_value_2/main.k b/test/grammar/schema/default_value/default_value_2/main.k new file mode 100644 index 000000000..e972f050b --- /dev/null +++ b/test/grammar/schema/default_value/default_value_2/main.k @@ -0,0 +1,10 @@ +schema Person: + firstName : str = "John" + lastName : str + dict: {str:} = {} + +JoeDoe = Person { + "firstName" = "Joe", + "lastName" = "Doe", + "dict" = {"key": "value", "key2": 1} +} diff --git a/test/grammar/schema/default_value/default_value_2/stdout.golden b/test/grammar/schema/default_value/default_value_2/stdout.golden new file mode 100644 index 000000000..e24bc329f --- /dev/null +++ b/test/grammar/schema/default_value/default_value_2/stdout.golden @@ -0,0 +1,6 @@ +JoeDoe: + firstName: Joe + lastName: Doe + dict: + key: value + key2: 1 diff --git a/test/grammar/schema/deprecated/illegal_arg_fail_0/main.k b/test/grammar/schema/deprecated/illegal_arg_fail_0/main.k new file mode 100644 index 000000000..4f8b1b4f3 --- /dev/null +++ b/test/grammar/schema/deprecated/illegal_arg_fail_0/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName: str = "John" + @deprecated(strict=False, "1.16") + lastName: str + name: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/illegal_arg_fail_0/stderr.golden.py b/test/grammar/schema/deprecated/illegal_arg_fail_0/stderr.golden.py new file mode 100644 index 000000000..701db9bc1 --- /dev/null +++ b/test/grammar/schema/deprecated/illegal_arg_fail_0/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=31, + end_col_no=37 + ) + ], + arg_msg="positional argument follows keyword argument"), + file=sys.stdout +) diff --git a/test/grammar/schema/deprecated/member_simple_0/main.k b/test/grammar/schema/deprecated/member_simple_0/main.k new file mode 100644 index 000000000..889b5c4b3 --- /dev/null +++ b/test/grammar/schema/deprecated/member_simple_0/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName: str = "John" + lastName: str + @deprecated + name: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/member_simple_0/stderr.golden.py b/test/grammar/schema/deprecated/member_simple_0/stderr.golden.py new file mode 100644 index 000000000..b42f84d01 --- /dev/null +++ b/test/grammar/schema/deprecated/member_simple_0/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + ), + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("name","")) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/member_simple_1/main.k b/test/grammar/schema/deprecated/member_simple_1/main.k new file mode 100644 index 000000000..fa2c8504f --- /dev/null +++ b/test/grammar/schema/deprecated/member_simple_1/main.k @@ -0,0 +1,10 @@ +schema Person: + firstName: str = "John" + @deprecated + lastName: str + @deprecated + name: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/member_simple_1/stderr.golden.py b/test/grammar/schema/deprecated/member_simple_1/stderr.golden.py new file mode 100644 index 000000000..670370979 --- /dev/null +++ b/test/grammar/schema/deprecated/member_simple_1/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=8, + ), + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("name", "")) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/member_simple_2/main.k b/test/grammar/schema/deprecated/member_simple_2/main.k new file mode 100644 index 000000000..59d792f05 --- /dev/null +++ b/test/grammar/schema/deprecated/member_simple_2/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName?: str = "John" + @deprecated + lastName?: str = None + name?: str = None + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/member_simple_2/stdout.golden b/test/grammar/schema/deprecated/member_simple_2/stdout.golden new file mode 100644 index 000000000..62779fdac --- /dev/null +++ b/test/grammar/schema/deprecated/member_simple_2/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: null + name: deprecated diff --git a/test/grammar/schema/deprecated/member_simple_3/main.k b/test/grammar/schema/deprecated/member_simple_3/main.k new file mode 100644 index 000000000..5794f4f4b --- /dev/null +++ b/test/grammar/schema/deprecated/member_simple_3/main.k @@ -0,0 +1,9 @@ +@deprecated +schema ObsoleteSchema: + attr?: str + +schema Person: + name: str = "John" + attrs: ObsoleteSchema = ObsoleteSchema {} + +person = Person {} diff --git a/test/grammar/schema/deprecated/member_simple_3/stderr.golden.py b/test/grammar/schema/deprecated/member_simple_3/stderr.golden.py new file mode 100644 index 000000000..0e44f2624 --- /dev/null +++ b/test/grammar/schema/deprecated/member_simple_3/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + ), + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("ObsoleteSchema", "")) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/member_standard_0/main.k b/test/grammar/schema/deprecated/member_standard_0/main.k new file mode 100644 index 000000000..686cf62b4 --- /dev/null +++ b/test/grammar/schema/deprecated/member_standard_0/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName: str = "John" + lastName: str + @deprecated(version="1.16", reason="use firstName and lastName instead", strict=True) + name: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/member_standard_0/stderr.golden.py b/test/grammar/schema/deprecated/member_standard_0/stderr.golden.py new file mode 100644 index 000000000..a0e057c99 --- /dev/null +++ b/test/grammar/schema/deprecated/member_standard_0/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + ), + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("name", "since version 1.16, use firstName and lastName instead")) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/member_standard_1/main.k b/test/grammar/schema/deprecated/member_standard_1/main.k new file mode 100644 index 000000000..686cf62b4 --- /dev/null +++ b/test/grammar/schema/deprecated/member_standard_1/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName: str = "John" + lastName: str + @deprecated(version="1.16", reason="use firstName and lastName instead", strict=True) + name: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/member_standard_1/stderr.golden.py b/test/grammar/schema/deprecated/member_standard_1/stderr.golden.py new file mode 100644 index 000000000..a0e057c99 --- /dev/null +++ b/test/grammar/schema/deprecated/member_standard_1/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + ), + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("name", "since version 1.16, use firstName and lastName instead")) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/member_standard_2/main.k b/test/grammar/schema/deprecated/member_standard_2/main.k new file mode 100644 index 000000000..7b33ae70a --- /dev/null +++ b/test/grammar/schema/deprecated/member_standard_2/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName: str = "John" + lastName: str + @deprecated(version="1.16", reason="use firstName and lastName instead", strict=True) + name: str = "default_deprecated" + +JohnDoe = Person { + name = "override_deprecated" +} diff --git a/test/grammar/schema/deprecated/member_standard_2/stderr.golden.py b/test/grammar/schema/deprecated/member_standard_2/stderr.golden.py new file mode 100644 index 000000000..a0e057c99 --- /dev/null +++ b/test/grammar/schema/deprecated/member_standard_2/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + ), + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("name", "since version 1.16, use firstName and lastName instead")) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/member_standard_3/main.k b/test/grammar/schema/deprecated/member_standard_3/main.k new file mode 100644 index 000000000..f953645c4 --- /dev/null +++ b/test/grammar/schema/deprecated/member_standard_3/main.k @@ -0,0 +1,11 @@ +schema Person: + firstName: str = "John" + lastName: str = "Doe" + @deprecated(version="1.16", reason="use firstName and lastName instead", strict=True) + name: str + + name = firstName + lastName + +JohnDoe = Person { + name = "JohnDoe" +} diff --git a/test/grammar/schema/deprecated/member_standard_3/stderr.golden.py b/test/grammar/schema/deprecated/member_standard_3/stderr.golden.py new file mode 100644 index 000000000..c5f919d4a --- /dev/null +++ b/test/grammar/schema/deprecated/member_standard_3/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=9, + ), + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("name", "since version 1.16, use firstName and lastName instead")) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/member_standard_4/main.k b/test/grammar/schema/deprecated/member_standard_4/main.k new file mode 100644 index 000000000..87fcc4376 --- /dev/null +++ b/test/grammar/schema/deprecated/member_standard_4/main.k @@ -0,0 +1,12 @@ +schema Person: + firstName: str = "John" + lastName: str + @deprecated(version="1.16", reason="use firstName and lastName instead", strict=True) + name: str + +schema Son(Person): + name = "template value" + +JohnDoe = Son { + name = "override value" +} diff --git a/test/grammar/schema/deprecated/member_standard_4/stderr.golden.py b/test/grammar/schema/deprecated/member_standard_4/stderr.golden.py new file mode 100644 index 000000000..d1112365b --- /dev/null +++ b/test/grammar/schema/deprecated/member_standard_4/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + ), + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("name", "since version 1.16, use firstName and lastName instead")) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/member_warning_0/main.k b/test/grammar/schema/deprecated/member_warning_0/main.k new file mode 100644 index 000000000..914fc1e9d --- /dev/null +++ b/test/grammar/schema/deprecated/member_warning_0/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName?: str = "John" + lastName?: str + @deprecated(version="1.16", reason="use firstName and lastName instead", strict=False) + name?: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/member_warning_0/stderr.golden.py b/test/grammar/schema/deprecated/member_warning_0/stderr.golden.py new file mode 100644 index 000000000..4aee93cfa --- /dev/null +++ b/test/grammar/schema/deprecated/member_warning_0/stderr.golden.py @@ -0,0 +1,14 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_warning_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.Deprecated_Warning_TYPE, + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("name", "since version 1.16, use firstName and lastName instead") + ) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/member_warning_1/main.k b/test/grammar/schema/deprecated/member_warning_1/main.k new file mode 100644 index 000000000..bcd3d64b6 --- /dev/null +++ b/test/grammar/schema/deprecated/member_warning_1/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName?: str = "John" + lastName?: str + @deprecated(strict=False) + name?: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/member_warning_1/stderr.golden.py b/test/grammar/schema/deprecated/member_warning_1/stderr.golden.py new file mode 100644 index 000000000..6ca2b9b2a --- /dev/null +++ b/test/grammar/schema/deprecated/member_warning_1/stderr.golden.py @@ -0,0 +1,14 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_warning_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.Deprecated_Warning_TYPE, + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("name", "") + ) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/schema_simple_0/main.k b/test/grammar/schema/deprecated/schema_simple_0/main.k new file mode 100644 index 000000000..9325c3ea4 --- /dev/null +++ b/test/grammar/schema/deprecated/schema_simple_0/main.k @@ -0,0 +1,9 @@ +@deprecated +schema Person: + firstName: str = "John" + lastName: str + name: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/schema_simple_0/stderr.golden.py b/test/grammar/schema/deprecated/schema_simple_0/stderr.golden.py new file mode 100644 index 000000000..ac5835190 --- /dev/null +++ b/test/grammar/schema/deprecated/schema_simple_0/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + ), + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("Person", "")) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/schema_simple_1/main.k b/test/grammar/schema/deprecated/schema_simple_1/main.k new file mode 100644 index 000000000..1dc5f42af --- /dev/null +++ b/test/grammar/schema/deprecated/schema_simple_1/main.k @@ -0,0 +1,10 @@ +@deprecated +schema Person: + firstName: str = "John" + lastName: str + name: str + +schema Son(Person): + name: str = "Son" + +JohnDoe = Son {} diff --git a/test/grammar/schema/deprecated/schema_simple_1/stderr.golden.py b/test/grammar/schema/deprecated/schema_simple_1/stderr.golden.py new file mode 100644 index 000000000..8d8688d3d --- /dev/null +++ b/test/grammar/schema/deprecated/schema_simple_1/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + ), + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("Person", "")) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/schema_simple_2/main.k b/test/grammar/schema/deprecated/schema_simple_2/main.k new file mode 100644 index 000000000..4fcf1820c --- /dev/null +++ b/test/grammar/schema/deprecated/schema_simple_2/main.k @@ -0,0 +1,10 @@ +schema Person: + firstName?: str = "John" + lastName?: str = None + name?: str = None + +@deprecated +schema Son(Person): + name: str = "Son" + +JohnDoe = Person {} diff --git a/test/grammar/schema/deprecated/schema_simple_2/stdout.golden b/test/grammar/schema/deprecated/schema_simple_2/stdout.golden new file mode 100644 index 000000000..a1c64e841 --- /dev/null +++ b/test/grammar/schema/deprecated/schema_simple_2/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: null + name: null diff --git a/test/grammar/schema/deprecated/schema_standard_0/main.k b/test/grammar/schema/deprecated/schema_standard_0/main.k new file mode 100644 index 000000000..cba86d53f --- /dev/null +++ b/test/grammar/schema/deprecated/schema_standard_0/main.k @@ -0,0 +1,9 @@ +@deprecated(version="1.16", reason="use SuperPerson instead", strict=True) +schema Person: + firstName: str = "John" + lastName: str + name: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/schema_standard_0/stderr.golden.py b/test/grammar/schema/deprecated/schema_standard_0/stderr.golden.py new file mode 100644 index 000000000..39bcfffb9 --- /dev/null +++ b/test/grammar/schema/deprecated/schema_standard_0/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + ), + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("Person", "since version 1.16, use SuperPerson instead")) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/schema_standard_1/main.k b/test/grammar/schema/deprecated/schema_standard_1/main.k new file mode 100644 index 000000000..afdd67fd1 --- /dev/null +++ b/test/grammar/schema/deprecated/schema_standard_1/main.k @@ -0,0 +1,10 @@ +@deprecated(version="1.16", reason="use SuperPerson instead", strict=True) +schema Person: + firstName: str = "John" + lastName: str + name: str + +schema Son(Person): + name: str = "Son" + +JohnDoe = Son {} diff --git a/test/grammar/schema/deprecated/schema_standard_1/stderr.golden.py b/test/grammar/schema/deprecated/schema_standard_1/stderr.golden.py new file mode 100644 index 000000000..70b808315 --- /dev/null +++ b/test/grammar/schema/deprecated/schema_standard_1/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.Deprecated_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10 + ), + ], + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("Person", "since version 1.16, use SuperPerson instead")) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/schema_warning_0/main.k b/test/grammar/schema/deprecated/schema_warning_0/main.k new file mode 100644 index 000000000..15f204313 --- /dev/null +++ b/test/grammar/schema/deprecated/schema_warning_0/main.k @@ -0,0 +1,9 @@ +@deprecated(strict=False) +schema Person: + firstName?: str = "John" + lastName?: str + name?: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/schema_warning_0/stderr.golden.py b/test/grammar/schema/deprecated/schema_warning_0/stderr.golden.py new file mode 100644 index 000000000..a66cea3b4 --- /dev/null +++ b/test/grammar/schema/deprecated/schema_warning_0/stderr.golden.py @@ -0,0 +1,14 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_warning_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.Deprecated_Warning_TYPE, + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("Person", "") + ) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/schema_warning_1/main.k b/test/grammar/schema/deprecated/schema_warning_1/main.k new file mode 100644 index 000000000..b592c29ed --- /dev/null +++ b/test/grammar/schema/deprecated/schema_warning_1/main.k @@ -0,0 +1,9 @@ +@deprecated(strict=False, version="1.16", reason="use SuperPerson instead") +schema Person: + firstName?: str = "John" + lastName?: str + name?: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/schema_warning_1/stderr.golden.py b/test/grammar/schema/deprecated/schema_warning_1/stderr.golden.py new file mode 100644 index 000000000..c9fc44588 --- /dev/null +++ b/test/grammar/schema/deprecated/schema_warning_1/stderr.golden.py @@ -0,0 +1,14 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_warning_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.Deprecated_Warning_TYPE, + arg_msg=kcl_error.DEPRECATED_WARNING_MSG.format("Person", "since version 1.16, use SuperPerson instead") + ) + , file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/unknown_fail_0/main.k b/test/grammar/schema/deprecated/unknown_fail_0/main.k new file mode 100644 index 000000000..ec0fac259 --- /dev/null +++ b/test/grammar/schema/deprecated/unknown_fail_0/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName: str = "John" + lastName: str + @err_deprecated(version="1.16", reason="use firstName and lastName instead", strict=False) + name: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/unknown_fail_0/stderr.golden.py b/test/grammar/schema/deprecated/unknown_fail_0/stderr.golden.py new file mode 100644 index 000000000..ffe47b71b --- /dev/null +++ b/test/grammar/schema/deprecated/unknown_fail_0/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.UnKnownDecorator_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=6 + ) + ], + arg_msg=kcl_error.UNKNOWN_DECORATOR_MSG.format("err_deprecated") + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/deprecated/unknown_fail_1/main.k b/test/grammar/schema/deprecated/unknown_fail_1/main.k new file mode 100644 index 000000000..589de29b2 --- /dev/null +++ b/test/grammar/schema/deprecated/unknown_fail_1/main.k @@ -0,0 +1,9 @@ +@err_deprecated +schema Person: + firstName: str = "John" + lastName: str + name: str + +JohnDoe = Person { + name: "deprecated" +} diff --git a/test/grammar/schema/deprecated/unknown_fail_1/stderr.golden.py b/test/grammar/schema/deprecated/unknown_fail_1/stderr.golden.py new file mode 100644 index 000000000..f6477ad73 --- /dev/null +++ b/test/grammar/schema/deprecated/unknown_fail_1/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.UnKnownDecorator_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=2 + ) + ], + arg_msg=kcl_error.UNKNOWN_DECORATOR_MSG.format("err_deprecated") + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/empty/empty_0/main.k b/test/grammar/schema/empty/empty_0/main.k new file mode 100644 index 000000000..9eba3309c --- /dev/null +++ b/test/grammar/schema/empty/empty_0/main.k @@ -0,0 +1,3 @@ +schema Data: + +data = Data {} diff --git a/test/grammar/schema/empty/empty_0/stdout.golden b/test/grammar/schema/empty/empty_0/stdout.golden new file mode 100644 index 000000000..603819cac --- /dev/null +++ b/test/grammar/schema/empty/empty_0/stdout.golden @@ -0,0 +1 @@ +data: {} diff --git a/test/grammar/schema/if_item/if_item_0/main.k b/test/grammar/schema/if_item/if_item_0/main.k new file mode 100644 index 000000000..9061f609d --- /dev/null +++ b/test/grammar/schema/if_item/if_item_0/main.k @@ -0,0 +1,15 @@ +schema Data: + key1?: str + key2?: str + key3?: str + +data1 = Data { + if False: key1: "value1" + key2: "value2" + if True: key3: "value3" +} +data2 = Data { + key1: "value1" + if data1["key2"]: + key2: data1["key2"] +} diff --git a/test/grammar/schema/if_item/if_item_0/stdout.golden b/test/grammar/schema/if_item/if_item_0/stdout.golden new file mode 100644 index 000000000..517233c5b --- /dev/null +++ b/test/grammar/schema/if_item/if_item_0/stdout.golden @@ -0,0 +1,6 @@ +data1: + key2: value2 + key3: value3 +data2: + key1: value1 + key2: value2 diff --git a/test/grammar/schema/if_item/if_item_2/main.k b/test/grammar/schema/if_item/if_item_2/main.k new file mode 100644 index 000000000..4a53c69fc --- /dev/null +++ b/test/grammar/schema/if_item/if_item_2/main.k @@ -0,0 +1,11 @@ +schema Data: + key1?: str + key2?: str + +schema NamedData: + data: Data = Data { + if True: key1: "value1" + key2: "value2" + } + +data = NamedData {} diff --git a/test/grammar/schema/if_item/if_item_2/stdout.golden b/test/grammar/schema/if_item/if_item_2/stdout.golden new file mode 100644 index 000000000..1fc301b8d --- /dev/null +++ b/test/grammar/schema/if_item/if_item_2/stdout.golden @@ -0,0 +1,4 @@ +data: + data: + key1: value1 + key2: value2 diff --git a/test/grammar/schema/if_item/if_item_3/main.k b/test/grammar/schema/if_item/if_item_3/main.k new file mode 100644 index 000000000..895674ab6 --- /dev/null +++ b/test/grammar/schema/if_item/if_item_3/main.k @@ -0,0 +1,12 @@ +schema Data: + key1?: str + key2?: str + +schema NamedData: + shouldAddKey: bool = False + data: Data = { + if shouldAddKey: key1: "value1" + key2: "value2" + } + +data = NamedData {} diff --git a/test/grammar/schema/if_item/if_item_3/stdout.golden b/test/grammar/schema/if_item/if_item_3/stdout.golden new file mode 100644 index 000000000..aa1b6feec --- /dev/null +++ b/test/grammar/schema/if_item/if_item_3/stdout.golden @@ -0,0 +1,4 @@ +data: + shouldAddKey: false + data: + key2: value2 diff --git a/test/grammar/schema/if_item/if_item_4/main.k b/test/grammar/schema/if_item/if_item_4/main.k new file mode 100644 index 000000000..65adff871 --- /dev/null +++ b/test/grammar/schema/if_item/if_item_4/main.k @@ -0,0 +1,19 @@ +schema Data: + key1?: str + key2?: str + +schema NamedData: + shouldAddKey: bool = False + data: Data = { + if shouldAddKey: + key1: "value1" + else: + key1: "1" + key2: "2" + key2: "value2" + } + +data = NamedData { + if True: + shouldAddKey = True +} diff --git a/test/grammar/schema/if_item/if_item_4/stdout.golden b/test/grammar/schema/if_item/if_item_4/stdout.golden new file mode 100644 index 000000000..44ea88195 --- /dev/null +++ b/test/grammar/schema/if_item/if_item_4/stdout.golden @@ -0,0 +1,5 @@ +data: + shouldAddKey: true + data: + key1: value1 + key2: value2 diff --git a/test/grammar/schema/if_item/if_item_5/main.k b/test/grammar/schema/if_item/if_item_5/main.k new file mode 100644 index 000000000..906053521 --- /dev/null +++ b/test/grammar/schema/if_item/if_item_5/main.k @@ -0,0 +1,22 @@ +schema Config: + name: str + env: str + +env = "env" + +data1 = Config { + if env == "env": + name: env + env: env + else: + name: "name" + env: "name" +} +data2 = Config { + if env != "env": + name: env + env: env + else: + name: "name" + env: "name" +} diff --git a/test/grammar/schema/if_item/if_item_5/stdout.golden b/test/grammar/schema/if_item/if_item_5/stdout.golden new file mode 100644 index 000000000..1aed9c29a --- /dev/null +++ b/test/grammar/schema/if_item/if_item_5/stdout.golden @@ -0,0 +1,7 @@ +env: env +data1: + name: env + env: env +data2: + name: name + env: name diff --git a/test/grammar/schema/if_item/if_item_6/main.k b/test/grammar/schema/if_item/if_item_6/main.k new file mode 100644 index 000000000..4e3e45969 --- /dev/null +++ b/test/grammar/schema/if_item/if_item_6/main.k @@ -0,0 +1,14 @@ +schema Config: + name: str + env?: str + +env = "env" + +data1 = Config { + if env == "env": name: env + else: name: "name" +} +data2 = Config { + if env != "env": name: env + else: name: "name" +} diff --git a/test/grammar/schema/if_item/if_item_6/stdout.golden b/test/grammar/schema/if_item/if_item_6/stdout.golden new file mode 100644 index 000000000..c69ea2fbc --- /dev/null +++ b/test/grammar/schema/if_item/if_item_6/stdout.golden @@ -0,0 +1,5 @@ +env: env +data1: + name: env +data2: + name: name diff --git a/test/grammar/schema/if_item/if_item_7/main.k b/test/grammar/schema/if_item/if_item_7/main.k new file mode 100644 index 000000000..279428890 --- /dev/null +++ b/test/grammar/schema/if_item/if_item_7/main.k @@ -0,0 +1,30 @@ +schema Data: + labels?: {str:str} + +data0 = Data { + labels.key = "value" +} +data1 = Data { + if True: + labels.key = "value" +} +data2 = Data { + if False: + labels.key = "value1" + else: + labels.key = "value2" +} +data3 = Data { + if True: + if True: + labels.key = "value1" +} +data4 = Data { + if False: + if False: + labels.key = "value1" + else: + labels.key = "value2" + else: + labels.key = "value3" +} diff --git a/test/grammar/schema/if_item/if_item_7/stdout.golden b/test/grammar/schema/if_item/if_item_7/stdout.golden new file mode 100644 index 000000000..388704d4d --- /dev/null +++ b/test/grammar/schema/if_item/if_item_7/stdout.golden @@ -0,0 +1,15 @@ +data0: + labels: + key: value +data1: + labels: + key: value +data2: + labels: + key: value2 +data3: + labels: + key: value1 +data4: + labels: + key: value3 diff --git a/test/grammar/schema/if_item/if_item_fail_0/main.k b/test/grammar/schema/if_item/if_item_fail_0/main.k new file mode 100644 index 000000000..36781e3e6 --- /dev/null +++ b/test/grammar/schema/if_item/if_item_fail_0/main.k @@ -0,0 +1,14 @@ +schema Data: + key1?: str + key2?: str + key3?: str + +data1 = Data {"key1": "value1", "key2": "value2"} +data2 = Data { + **data1 + if data1["key1"]: "key3": data1["key1"] +} +data3 = Data { + **data2 + if data2["key3"] == "value3": "key4": "value4" +} diff --git a/test/grammar/schema/if_item/if_item_fail_0/stderr.golden.py b/test/grammar/schema/if_item/if_item_fail_0/stderr.golden.py new file mode 100644 index 000000000..38ab3417d --- /dev/null +++ b/test/grammar/schema/if_item/if_item_fail_0/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CannotAddMembers_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=13, + col_no=35, + arg_msg="'key4' is not defined in schema 'Data'" + ), + ], + arg_msg="Cannot add member 'key4' to schema 'Data'") + , file=sys.stdout +) + diff --git a/test/grammar/schema/import/import_as/kcl.mod b/test/grammar/schema/import/import_as/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_as/main.k b/test/grammar/schema/import/import_as/main.k new file mode 100644 index 000000000..874389098 --- /dev/null +++ b/test/grammar/schema/import/import_as/main.k @@ -0,0 +1,13 @@ +import pkg1 as pkg + +schema Another: + my: pkg.MySchema + +schema Son(pkg.MySchema): + tt: str + +a = Another{"my": {"s1": "asdf"}} + +b = Son{"s1": "123x", "tt": "432e"} + +c = pkg.MySchema{"s1": "asdf"} \ No newline at end of file diff --git a/test/grammar/schema/import/import_as/pkg1/module1.k b/test/grammar/schema/import/import_as/pkg1/module1.k new file mode 100644 index 000000000..be4a4c856 --- /dev/null +++ b/test/grammar/schema/import/import_as/pkg1/module1.k @@ -0,0 +1,2 @@ +schema MySchema: + s1: str diff --git a/test/grammar/schema/import/import_as/stdout.golden b/test/grammar/schema/import/import_as/stdout.golden new file mode 100644 index 000000000..6cc5c5622 --- /dev/null +++ b/test/grammar/schema/import/import_as/stdout.golden @@ -0,0 +1,8 @@ +a: + my: + s1: asdf +b: + s1: 123x + tt: 432e +c: + s1: asdf \ No newline at end of file diff --git a/test/grammar/schema/import/import_combination_0/kcl.mod b/test/grammar/schema/import/import_combination_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_combination_0/main.k b/test/grammar/schema/import/import_combination_0/main.k new file mode 100644 index 000000000..7f8f9b074 --- /dev/null +++ b/test/grammar/schema/import/import_combination_0/main.k @@ -0,0 +1,13 @@ +import pkg + +schema Person: + name: pkg.Name + age: int + +alice = Person { + "name": pkg.Name { + "firstName": "alice", + "lastName": "smith" + }, + "age": 10 +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_combination_0/pkg/name_module.k b/test/grammar/schema/import/import_combination_0/pkg/name_module.k new file mode 100644 index 000000000..c24cd619d --- /dev/null +++ b/test/grammar/schema/import/import_combination_0/pkg/name_module.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str \ No newline at end of file diff --git a/test/grammar/schema/import/import_combination_0/stdout.golden b/test/grammar/schema/import/import_combination_0/stdout.golden new file mode 100644 index 000000000..42ac3150f --- /dev/null +++ b/test/grammar/schema/import/import_combination_0/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + firstName: alice + lastName: smith + age: 10 \ No newline at end of file diff --git a/test/grammar/schema/import/import_combination_1/kcl.mod b/test/grammar/schema/import/import_combination_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_combination_1/main.k b/test/grammar/schema/import/import_combination_1/main.k new file mode 100644 index 000000000..19418f013 --- /dev/null +++ b/test/grammar/schema/import/import_combination_1/main.k @@ -0,0 +1,9 @@ +import pkg + +alice = pkg.Person { + "name": pkg.Name { + "firstName": "alice", + "lastName": "smith" + }, + "age": 10 +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_combination_1/pkg/name.k b/test/grammar/schema/import/import_combination_1/pkg/name.k new file mode 100644 index 000000000..c24cd619d --- /dev/null +++ b/test/grammar/schema/import/import_combination_1/pkg/name.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str \ No newline at end of file diff --git a/test/grammar/schema/import/import_combination_1/pkg/person.k b/test/grammar/schema/import/import_combination_1/pkg/person.k new file mode 100644 index 000000000..955af82f6 --- /dev/null +++ b/test/grammar/schema/import/import_combination_1/pkg/person.k @@ -0,0 +1,3 @@ +schema Person: + name: Name + age: int \ No newline at end of file diff --git a/test/grammar/schema/import/import_combination_1/stdout.golden b/test/grammar/schema/import/import_combination_1/stdout.golden new file mode 100644 index 000000000..42ac3150f --- /dev/null +++ b/test/grammar/schema/import/import_combination_1/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + firstName: alice + lastName: smith + age: 10 \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_0/kcl.mod b/test/grammar/schema/import/import_dict_2_schema_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_dict_2_schema_0/main.k b/test/grammar/schema/import/import_dict_2_schema_0/main.k new file mode 100644 index 000000000..02cb9b065 --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_0/main.k @@ -0,0 +1,13 @@ +import pkg + +schema Person: + name: pkg.Name + age: int + +alice = Person { + "name": { + "firstName": "alice", + "lastName": "smith" + }, + "age": 10 +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_0/pkg/name_module.k b/test/grammar/schema/import/import_dict_2_schema_0/pkg/name_module.k new file mode 100644 index 000000000..c24cd619d --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_0/pkg/name_module.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_0/stdout.golden b/test/grammar/schema/import/import_dict_2_schema_0/stdout.golden new file mode 100644 index 000000000..42ac3150f --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_0/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + firstName: alice + lastName: smith + age: 10 \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_1/kcl.mod b/test/grammar/schema/import/import_dict_2_schema_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_dict_2_schema_1/main.k b/test/grammar/schema/import/import_dict_2_schema_1/main.k new file mode 100644 index 000000000..7e13d0565 --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_1/main.k @@ -0,0 +1,9 @@ +import person + +alice = person.Person { + "name": { + "firstName": "alice", + "lastName": "smith" + }, + "age": 10 +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_1/name/name.k b/test/grammar/schema/import/import_dict_2_schema_1/name/name.k new file mode 100644 index 000000000..c24cd619d --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_1/name/name.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_1/person/person.k b/test/grammar/schema/import/import_dict_2_schema_1/person/person.k new file mode 100644 index 000000000..25a9f829e --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_1/person/person.k @@ -0,0 +1,5 @@ +import ..name + +schema Person: + name: name.Name + age: int \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_1/stdout.golden b/test/grammar/schema/import/import_dict_2_schema_1/stdout.golden new file mode 100644 index 000000000..42ac3150f --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_1/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + firstName: alice + lastName: smith + age: 10 \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_2/kcl.mod b/test/grammar/schema/import/import_dict_2_schema_2/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_dict_2_schema_2/main.k b/test/grammar/schema/import/import_dict_2_schema_2/main.k new file mode 100644 index 000000000..b249e3d30 --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_2/main.k @@ -0,0 +1,9 @@ +import scholar + +alice = scholar.Scholar { + "name": { + "firstName": "alice", + "lastName": "smith" + }, + "age": 10 +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_2/name/name.k b/test/grammar/schema/import/import_dict_2_schema_2/name/name.k new file mode 100644 index 000000000..c24cd619d --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_2/name/name.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_2/person/person.k b/test/grammar/schema/import/import_dict_2_schema_2/person/person.k new file mode 100644 index 000000000..25a9f829e --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_2/person/person.k @@ -0,0 +1,5 @@ +import ..name + +schema Person: + name: name.Name + age: int \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_2/scholar/scholar.k b/test/grammar/schema/import/import_dict_2_schema_2/scholar/scholar.k new file mode 100644 index 000000000..2f87cc795 --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_2/scholar/scholar.k @@ -0,0 +1,3 @@ +import ..person + +schema Scholar(person.Person): \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_2/stdout.golden b/test/grammar/schema/import/import_dict_2_schema_2/stdout.golden new file mode 100644 index 000000000..42ac3150f --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_2/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + firstName: alice + lastName: smith + age: 10 \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_3/kcl.mod b/test/grammar/schema/import/import_dict_2_schema_3/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_dict_2_schema_3/main.k b/test/grammar/schema/import/import_dict_2_schema_3/main.k new file mode 100644 index 000000000..bce28d5e8 --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_3/main.k @@ -0,0 +1,9 @@ +import scholar + +alice = scholar.Scholar { + "name": { + "firstName": "alice", + "lastName": "smith" + }, + "age" = 10 +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_3/name/name.k b/test/grammar/schema/import/import_dict_2_schema_3/name/name.k new file mode 100644 index 000000000..c24cd619d --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_3/name/name.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_3/person/person.k b/test/grammar/schema/import/import_dict_2_schema_3/person/person.k new file mode 100644 index 000000000..18b00d92e --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_3/person/person.k @@ -0,0 +1,5 @@ +import ..name + +schema Person: + name: name.Name + age: int = 9 \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_3/scholar/scholar.k b/test/grammar/schema/import/import_dict_2_schema_3/scholar/scholar.k new file mode 100644 index 000000000..4e79c1a17 --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_3/scholar/scholar.k @@ -0,0 +1,4 @@ +import ..person + +schema Scholar(person.Person): + age: int = 18 \ No newline at end of file diff --git a/test/grammar/schema/import/import_dict_2_schema_3/stdout.golden b/test/grammar/schema/import/import_dict_2_schema_3/stdout.golden new file mode 100644 index 000000000..42ac3150f --- /dev/null +++ b/test/grammar/schema/import/import_dict_2_schema_3/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + firstName: alice + lastName: smith + age: 10 \ No newline at end of file diff --git a/test/grammar/schema/import/import_inherit/kcl.mod b/test/grammar/schema/import/import_inherit/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_inherit/main.k b/test/grammar/schema/import/import_inherit/main.k new file mode 100644 index 000000000..b17230294 --- /dev/null +++ b/test/grammar/schema/import/import_inherit/main.k @@ -0,0 +1,10 @@ +import pkg + +schema Nameable(pkg.Name): + id: int + +myPlant = Nameable { + "firstName": "seed", + "lastName": "green", + "id": 1 +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_inherit/pkg/name_module.k b/test/grammar/schema/import/import_inherit/pkg/name_module.k new file mode 100644 index 000000000..c24cd619d --- /dev/null +++ b/test/grammar/schema/import/import_inherit/pkg/name_module.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str \ No newline at end of file diff --git a/test/grammar/schema/import/import_inherit/stdout.golden b/test/grammar/schema/import/import_inherit/stdout.golden new file mode 100644 index 000000000..16f142ec4 --- /dev/null +++ b/test/grammar/schema/import/import_inherit/stdout.golden @@ -0,0 +1,4 @@ +myPlant: + firstName: seed + lastName: green + id: 1 \ No newline at end of file diff --git a/test/grammar/schema/import/import_init/kcl.mod b/test/grammar/schema/import/import_init/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_init/main.k b/test/grammar/schema/import/import_init/main.k new file mode 100644 index 000000000..1f872232b --- /dev/null +++ b/test/grammar/schema/import/import_init/main.k @@ -0,0 +1,6 @@ +import pkg + +aliceName = pkg.Name { + "firstName": "alice", + "lastName": "smith" +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_init/pkg/name_module.k b/test/grammar/schema/import/import_init/pkg/name_module.k new file mode 100644 index 000000000..c24cd619d --- /dev/null +++ b/test/grammar/schema/import/import_init/pkg/name_module.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str \ No newline at end of file diff --git a/test/grammar/schema/import/import_init/stdout.golden b/test/grammar/schema/import/import_init/stdout.golden new file mode 100644 index 000000000..82aec976b --- /dev/null +++ b/test/grammar/schema/import/import_init/stdout.golden @@ -0,0 +1,3 @@ +aliceName: + firstName: alice + lastName: smith diff --git a/test/grammar/schema/import/import_multi_pkgs_0/kcl.mod b/test/grammar/schema/import/import_multi_pkgs_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_multi_pkgs_0/main.k b/test/grammar/schema/import/import_multi_pkgs_0/main.k new file mode 100644 index 000000000..8e6648bcb --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_0/main.k @@ -0,0 +1,13 @@ +import person + +alice = person.Person { + "labels": {"diligent":"true"}, + "mathematics": { + "versions": ["1","2","3"] + }, + "art": { + "labels": {"talent":"required"}, + "major": False, + "credit" = 4 + } +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_0/mixins/subject_mixin.k b/test/grammar/schema/import/import_multi_pkgs_0/mixins/subject_mixin.k new file mode 100644 index 000000000..1c22c8554 --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_0/mixins/subject_mixin.k @@ -0,0 +1,19 @@ +import ..org.v1 as org_v1 + +schema SubjectMixin: + subjects = [ + org_v1.Subject { + "name": "mathematics", + "credit" = 5, + "major": True, + "labels": labels, + "versions": mathematics["versions"] + }, + org_v1.Subject { + "name": "art", + "credit" = art["credit"], + "major": art["major"], + "labels": art["labels"], + "versions": ["1","2"] + } + ] \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_0/org/v1/subject.k b/test/grammar/schema/import/import_multi_pkgs_0/org/v1/subject.k new file mode 100644 index 000000000..7e95285f1 --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_0/org/v1/subject.k @@ -0,0 +1,6 @@ +schema Subject: + name: str + credit: int = 3 + major: bool + labels: {str:str} + versions: [str] \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_0/person/person.k b/test/grammar/schema/import/import_multi_pkgs_0/person/person.k new file mode 100644 index 000000000..146d36cdf --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_0/person/person.k @@ -0,0 +1,11 @@ +import ..org.v1 as org +import ..mixins + +schema Person: + mixin [mixins.SubjectMixin] + + labels: {str:str} + mathematics: {str:} + art: {str:} + + subjects: [org.Subject] \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_0/stdout.golden b/test/grammar/schema/import/import_multi_pkgs_0/stdout.golden new file mode 100644 index 000000000..f12444bb3 --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_0/stdout.golden @@ -0,0 +1,31 @@ +alice: + labels: + diligent: 'true' + mathematics: + versions: + - '1' + - '2' + - '3' + art: + labels: + talent: required + major: false + credit: 4 + subjects: + - name: mathematics + credit: 5 + major: true + labels: + diligent: 'true' + versions: + - '1' + - '2' + - '3' + - name: art + credit: 4 + major: false + labels: + talent: required + versions: + - '1' + - '2' \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_1/kcl.mod b/test/grammar/schema/import/import_multi_pkgs_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_multi_pkgs_1/main.k b/test/grammar/schema/import/import_multi_pkgs_1/main.k new file mode 100644 index 000000000..8e6648bcb --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_1/main.k @@ -0,0 +1,13 @@ +import person + +alice = person.Person { + "labels": {"diligent":"true"}, + "mathematics": { + "versions": ["1","2","3"] + }, + "art": { + "labels": {"talent":"required"}, + "major": False, + "credit" = 4 + } +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_1/mixins/subject_mixin.k b/test/grammar/schema/import/import_multi_pkgs_1/mixins/subject_mixin.k new file mode 100644 index 000000000..3ac3bb1b6 --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_1/mixins/subject_mixin.k @@ -0,0 +1,17 @@ +schema SubjectMixin: + subjects = [ + { + "name": "mathematics", + "credit" = 5, + "major": True, + "labels": labels, + "versions": mathematics["versions"] + }, + { + "name": "art", + "credit" = art["credit"], + "major": art["major"], + "labels": art["labels"], + "versions": ["1","2"] + } + ] \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_1/org/v1/subject.k b/test/grammar/schema/import/import_multi_pkgs_1/org/v1/subject.k new file mode 100644 index 000000000..7e95285f1 --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_1/org/v1/subject.k @@ -0,0 +1,6 @@ +schema Subject: + name: str + credit: int = 3 + major: bool + labels: {str:str} + versions: [str] \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_1/person/person.k b/test/grammar/schema/import/import_multi_pkgs_1/person/person.k new file mode 100644 index 000000000..146d36cdf --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_1/person/person.k @@ -0,0 +1,11 @@ +import ..org.v1 as org +import ..mixins + +schema Person: + mixin [mixins.SubjectMixin] + + labels: {str:str} + mathematics: {str:} + art: {str:} + + subjects: [org.Subject] \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_1/stdout.golden b/test/grammar/schema/import/import_multi_pkgs_1/stdout.golden new file mode 100644 index 000000000..f12444bb3 --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_1/stdout.golden @@ -0,0 +1,31 @@ +alice: + labels: + diligent: 'true' + mathematics: + versions: + - '1' + - '2' + - '3' + art: + labels: + talent: required + major: false + credit: 4 + subjects: + - name: mathematics + credit: 5 + major: true + labels: + diligent: 'true' + versions: + - '1' + - '2' + - '3' + - name: art + credit: 4 + major: false + labels: + talent: required + versions: + - '1' + - '2' \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_2/kcl.mod b/test/grammar/schema/import/import_multi_pkgs_2/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_multi_pkgs_2/main.k b/test/grammar/schema/import/import_multi_pkgs_2/main.k new file mode 100644 index 000000000..8e6648bcb --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_2/main.k @@ -0,0 +1,13 @@ +import person + +alice = person.Person { + "labels": {"diligent":"true"}, + "mathematics": { + "versions": ["1","2","3"] + }, + "art": { + "labels": {"talent":"required"}, + "major": False, + "credit" = 4 + } +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_2/mixins/subject_mixin.k b/test/grammar/schema/import/import_multi_pkgs_2/mixins/subject_mixin.k new file mode 100644 index 000000000..109897759 --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_2/mixins/subject_mixin.k @@ -0,0 +1,19 @@ +import ..org.v1 as org + +schema SubjectMixin: + subjects = [ + org.Subject { + "name": "mathematics", + "credit" = 5, + "major": True, + "labels": labels, + "versions": mathematics["versions"] + }, + org.Subject { + "name": "art", + "credit" = art["credit"], + "major": art["major"], + "labels": art["labels"], + "versions": ["1","2"] + } + ] \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_2/org/v1/subject.k b/test/grammar/schema/import/import_multi_pkgs_2/org/v1/subject.k new file mode 100644 index 000000000..7e95285f1 --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_2/org/v1/subject.k @@ -0,0 +1,6 @@ +schema Subject: + name: str + credit: int = 3 + major: bool + labels: {str:str} + versions: [str] \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_2/person/person.k b/test/grammar/schema/import/import_multi_pkgs_2/person/person.k new file mode 100644 index 000000000..21b6b0fbe --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_2/person/person.k @@ -0,0 +1,11 @@ +import ..org.v1 as org_v1 +import ..mixins + +schema Person: + mixin [mixins.SubjectMixin] + + labels: {str:str} + mathematics: {str:} + art: {str:} + + subjects: [org_v1.Subject] \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_2/stdout.golden b/test/grammar/schema/import/import_multi_pkgs_2/stdout.golden new file mode 100644 index 000000000..f12444bb3 --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_2/stdout.golden @@ -0,0 +1,31 @@ +alice: + labels: + diligent: 'true' + mathematics: + versions: + - '1' + - '2' + - '3' + art: + labels: + talent: required + major: false + credit: 4 + subjects: + - name: mathematics + credit: 5 + major: true + labels: + diligent: 'true' + versions: + - '1' + - '2' + - '3' + - name: art + credit: 4 + major: false + labels: + talent: required + versions: + - '1' + - '2' \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_3/kcl.mod b/test/grammar/schema/import/import_multi_pkgs_3/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_multi_pkgs_3/main.k b/test/grammar/schema/import/import_multi_pkgs_3/main.k new file mode 100644 index 000000000..8e6648bcb --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_3/main.k @@ -0,0 +1,13 @@ +import person + +alice = person.Person { + "labels": {"diligent":"true"}, + "mathematics": { + "versions": ["1","2","3"] + }, + "art": { + "labels": {"talent":"required"}, + "major": False, + "credit" = 4 + } +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_3/mixins/v1/subject_mixin.k b/test/grammar/schema/import/import_multi_pkgs_3/mixins/v1/subject_mixin.k new file mode 100644 index 000000000..34fc2a632 --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_3/mixins/v1/subject_mixin.k @@ -0,0 +1,19 @@ +import ...org.v1 + +schema SubjectMixin: + subjects = [ + v1.Subject { + "name": "mathematics", + "credit" = 5, + "major": True, + "labels": labels, + "versions": mathematics["versions"] + }, + v1.Subject { + "name": "art", + "credit" = art["credit"], + "major": art["major"], + "labels": art["labels"], + "versions": ["1","2"] + } + ] \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_3/org/v1/subject.k b/test/grammar/schema/import/import_multi_pkgs_3/org/v1/subject.k new file mode 100644 index 000000000..7e95285f1 --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_3/org/v1/subject.k @@ -0,0 +1,6 @@ +schema Subject: + name: str + credit: int = 3 + major: bool + labels: {str:str} + versions: [str] \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_3/person/person.k b/test/grammar/schema/import/import_multi_pkgs_3/person/person.k new file mode 100644 index 000000000..0dc57ab6f --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_3/person/person.k @@ -0,0 +1,11 @@ +import ..org.v1 as org_v1 +import ..mixins.v1 + +schema Person: + mixin [v1.SubjectMixin] + + labels: {str:str} + mathematics: {str:} + art: {str:} + + subjects: [org_v1.Subject] \ No newline at end of file diff --git a/test/grammar/schema/import/import_multi_pkgs_3/stdout.golden b/test/grammar/schema/import/import_multi_pkgs_3/stdout.golden new file mode 100644 index 000000000..f12444bb3 --- /dev/null +++ b/test/grammar/schema/import/import_multi_pkgs_3/stdout.golden @@ -0,0 +1,31 @@ +alice: + labels: + diligent: 'true' + mathematics: + versions: + - '1' + - '2' + - '3' + art: + labels: + talent: required + major: false + credit: 4 + subjects: + - name: mathematics + credit: 5 + major: true + labels: + diligent: 'true' + versions: + - '1' + - '2' + - '3' + - name: art + credit: 4 + major: false + labels: + talent: required + versions: + - '1' + - '2' \ No newline at end of file diff --git a/test/grammar/schema/import/import_same_module_inherit/kcl.mod b/test/grammar/schema/import/import_same_module_inherit/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_same_module_inherit/main.k b/test/grammar/schema/import/import_same_module_inherit/main.k new file mode 100644 index 000000000..cec80c245 --- /dev/null +++ b/test/grammar/schema/import/import_same_module_inherit/main.k @@ -0,0 +1,7 @@ +import pkg + +myPet = pkg.Nameable { + "firstName": "little", + "lastName": "cat", + "id": 2 +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_same_module_inherit/pkg/name.k b/test/grammar/schema/import/import_same_module_inherit/pkg/name.k new file mode 100644 index 000000000..bb2b428c4 --- /dev/null +++ b/test/grammar/schema/import/import_same_module_inherit/pkg/name.k @@ -0,0 +1,6 @@ +schema Name: + firstName: str + lastName: str + +schema Nameable(Name): + id: int \ No newline at end of file diff --git a/test/grammar/schema/import/import_same_module_inherit/stdout.golden b/test/grammar/schema/import/import_same_module_inherit/stdout.golden new file mode 100644 index 000000000..789fda15e --- /dev/null +++ b/test/grammar/schema/import/import_same_module_inherit/stdout.golden @@ -0,0 +1,4 @@ +myPet: + firstName: little + lastName: cat + id: 2 \ No newline at end of file diff --git a/test/grammar/schema/import/import_same_pkg/kcl.mod b/test/grammar/schema/import/import_same_pkg/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_same_pkg/main.k b/test/grammar/schema/import/import_same_pkg/main.k new file mode 100644 index 000000000..954240f66 --- /dev/null +++ b/test/grammar/schema/import/import_same_pkg/main.k @@ -0,0 +1,3 @@ +import pkg + +alice = pkg.aliceName diff --git a/test/grammar/schema/import/import_same_pkg/pkg/alice.k b/test/grammar/schema/import/import_same_pkg/pkg/alice.k new file mode 100644 index 000000000..209f1b224 --- /dev/null +++ b/test/grammar/schema/import/import_same_pkg/pkg/alice.k @@ -0,0 +1,5 @@ + +aliceName = Name { + "firstName": "alice", + "lastName": "smith" +} diff --git a/test/grammar/schema/import/import_same_pkg/pkg/name.k b/test/grammar/schema/import/import_same_pkg/pkg/name.k new file mode 100644 index 000000000..c24cd619d --- /dev/null +++ b/test/grammar/schema/import/import_same_pkg/pkg/name.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str \ No newline at end of file diff --git a/test/grammar/schema/import/import_same_pkg/stdout.golden b/test/grammar/schema/import/import_same_pkg/stdout.golden new file mode 100644 index 000000000..3b491c3f0 --- /dev/null +++ b/test/grammar/schema/import/import_same_pkg/stdout.golden @@ -0,0 +1,3 @@ +alice: + firstName: alice + lastName: smith diff --git a/test/grammar/schema/import/import_same_pkg_field/kcl.mod b/test/grammar/schema/import/import_same_pkg_field/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_same_pkg_field/main.k b/test/grammar/schema/import/import_same_pkg_field/main.k new file mode 100644 index 000000000..1aa3cbe8b --- /dev/null +++ b/test/grammar/schema/import/import_same_pkg_field/main.k @@ -0,0 +1,9 @@ +import pkg + +myPet = pkg.Nameable { + "id": 2, + "name": { + "firstName": "little", + "lastName": "cat" + } +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_same_pkg_field/pkg/name.k b/test/grammar/schema/import/import_same_pkg_field/pkg/name.k new file mode 100644 index 000000000..c24cd619d --- /dev/null +++ b/test/grammar/schema/import/import_same_pkg_field/pkg/name.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str \ No newline at end of file diff --git a/test/grammar/schema/import/import_same_pkg_field/pkg/nameable.k b/test/grammar/schema/import/import_same_pkg_field/pkg/nameable.k new file mode 100644 index 000000000..1ecaf49f4 --- /dev/null +++ b/test/grammar/schema/import/import_same_pkg_field/pkg/nameable.k @@ -0,0 +1,3 @@ +schema Nameable: + id: int + name: Name diff --git a/test/grammar/schema/import/import_same_pkg_field/stdout.golden b/test/grammar/schema/import/import_same_pkg_field/stdout.golden new file mode 100644 index 000000000..75c453eb4 --- /dev/null +++ b/test/grammar/schema/import/import_same_pkg_field/stdout.golden @@ -0,0 +1,5 @@ +myPet: + id: 2 + name: + firstName: little + lastName: cat \ No newline at end of file diff --git a/test/grammar/schema/import/import_same_pkg_inherit/kcl.mod b/test/grammar/schema/import/import_same_pkg_inherit/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/import/import_same_pkg_inherit/main.k b/test/grammar/schema/import/import_same_pkg_inherit/main.k new file mode 100644 index 000000000..cec80c245 --- /dev/null +++ b/test/grammar/schema/import/import_same_pkg_inherit/main.k @@ -0,0 +1,7 @@ +import pkg + +myPet = pkg.Nameable { + "firstName": "little", + "lastName": "cat", + "id": 2 +} \ No newline at end of file diff --git a/test/grammar/schema/import/import_same_pkg_inherit/pkg/name.k b/test/grammar/schema/import/import_same_pkg_inherit/pkg/name.k new file mode 100644 index 000000000..c24cd619d --- /dev/null +++ b/test/grammar/schema/import/import_same_pkg_inherit/pkg/name.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str \ No newline at end of file diff --git a/test/grammar/schema/import/import_same_pkg_inherit/pkg/nameable.k b/test/grammar/schema/import/import_same_pkg_inherit/pkg/nameable.k new file mode 100644 index 000000000..40148e137 --- /dev/null +++ b/test/grammar/schema/import/import_same_pkg_inherit/pkg/nameable.k @@ -0,0 +1,2 @@ +schema Nameable(Name): + id: int diff --git a/test/grammar/schema/import/import_same_pkg_inherit/stdout.golden b/test/grammar/schema/import/import_same_pkg_inherit/stdout.golden new file mode 100644 index 000000000..789fda15e --- /dev/null +++ b/test/grammar/schema/import/import_same_pkg_inherit/stdout.golden @@ -0,0 +1,4 @@ +myPet: + firstName: little + lastName: cat + id: 2 \ No newline at end of file diff --git a/test/grammar/schema/index_signature/any_other_0/main.k b/test/grammar/schema/index_signature/any_other_0/main.k new file mode 100644 index 000000000..ef804a1b9 --- /dev/null +++ b/test/grammar/schema/index_signature/any_other_0/main.k @@ -0,0 +1,9 @@ +schema Data: + name: str + label: str + [str]: str + +data = Data { + name: "test" + label: "test" +} diff --git a/test/grammar/schema/index_signature/any_other_0/stdout.golden b/test/grammar/schema/index_signature/any_other_0/stdout.golden new file mode 100644 index 000000000..2d32b0aee --- /dev/null +++ b/test/grammar/schema/index_signature/any_other_0/stdout.golden @@ -0,0 +1,3 @@ +data: + name: test + label: test diff --git a/test/grammar/schema/index_signature/any_other_1/main.k b/test/grammar/schema/index_signature/any_other_1/main.k new file mode 100644 index 000000000..c57639506 --- /dev/null +++ b/test/grammar/schema/index_signature/any_other_1/main.k @@ -0,0 +1,10 @@ +schema Data: + name: str + count: int + [...str]: str + +data = Data { + name: "test" + count: 1 + label: "test" +} diff --git a/test/grammar/schema/index_signature/any_other_1/stdout.golden b/test/grammar/schema/index_signature/any_other_1/stdout.golden new file mode 100644 index 000000000..cb85b4961 --- /dev/null +++ b/test/grammar/schema/index_signature/any_other_1/stdout.golden @@ -0,0 +1,4 @@ +data: + name: test + count: 1 + label: test diff --git a/test/grammar/schema/index_signature/check_0/main.k b/test/grammar/schema/index_signature/check_0/main.k new file mode 100644 index 000000000..80225a4cf --- /dev/null +++ b/test/grammar/schema/index_signature/check_0/main.k @@ -0,0 +1,10 @@ +schema Data: + [name: str]: str + check: + name in ["Alice", "Bob", "John"] + +lowerMap = Data { + Alice: "alice" + Bob: "bob" + John: "john" +} diff --git a/test/grammar/schema/index_signature/check_0/stdout.golden b/test/grammar/schema/index_signature/check_0/stdout.golden new file mode 100644 index 000000000..bc161815a --- /dev/null +++ b/test/grammar/schema/index_signature/check_0/stdout.golden @@ -0,0 +1,4 @@ +lowerMap: + Alice: alice + Bob: bob + John: john diff --git a/test/grammar/schema/index_signature/check_1/main.k b/test/grammar/schema/index_signature/check_1/main.k new file mode 100644 index 000000000..f9cd215bf --- /dev/null +++ b/test/grammar/schema/index_signature/check_1/main.k @@ -0,0 +1,11 @@ +import regex + +schema Data: + [attr: str]: str + check: + regex.match(attr, r'^[-._a-zA-Z0-9]+$') + +data = Data { + "key1": "value1" + "key2": "value2" +} diff --git a/test/grammar/schema/index_signature/check_1/stdout.golden b/test/grammar/schema/index_signature/check_1/stdout.golden new file mode 100644 index 000000000..c81952463 --- /dev/null +++ b/test/grammar/schema/index_signature/check_1/stdout.golden @@ -0,0 +1,3 @@ +data: + key1: value1 + key2: value2 diff --git a/test/grammar/schema/index_signature/fail_0/main.k b/test/grammar/schema/index_signature/fail_0/main.k new file mode 100644 index 000000000..def228f9c --- /dev/null +++ b/test/grammar/schema/index_signature/fail_0/main.k @@ -0,0 +1,9 @@ +schema Data: + name: str + label: int + [str]: str + +data = Data { + name: "test" + label: 2 +} diff --git a/test/grammar/schema/index_signature/fail_0/stderr.golden.py b/test/grammar/schema/index_signature/fail_0/stderr.golden.py new file mode 100644 index 000000000..cb9d9e2bb --- /dev/null +++ b/test/grammar/schema/index_signature/fail_0/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IndexSignatureError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5 + ) + ], + arg_msg="the type 'int' of schema attribute 'label' does not meet the index signature definition [str]: str" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/index_signature/fail_1/main.k b/test/grammar/schema/index_signature/fail_1/main.k new file mode 100644 index 000000000..e0d568069 --- /dev/null +++ b/test/grammar/schema/index_signature/fail_1/main.k @@ -0,0 +1,7 @@ +schema Data: + [str]: str + [str]: int + +data = Data { + name: "test" +} diff --git a/test/grammar/schema/index_signature/fail_1/stderr.golden.py b/test/grammar/schema/index_signature/fail_1/stderr.golden.py new file mode 100644 index 000000000..30a2a11f9 --- /dev/null +++ b/test/grammar/schema/index_signature/fail_1/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IndexSignatureError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + end_col_no=15 + ) + ], + arg_msg="only one index signature is allowed in the schema" + ), + file=sys.stdout +) diff --git a/test/grammar/schema/index_signature/fail_2/main.k b/test/grammar/schema/index_signature/fail_2/main.k new file mode 100644 index 000000000..8653e1310 --- /dev/null +++ b/test/grammar/schema/index_signature/fail_2/main.k @@ -0,0 +1,7 @@ +schema Data: + name: str + [name: str]: str + +data = Data { + name: "test" +} diff --git a/test/grammar/schema/index_signature/fail_2/stderr.golden.py b/test/grammar/schema/index_signature/fail_2/stderr.golden.py new file mode 100644 index 000000000..f7ae1afd3 --- /dev/null +++ b/test/grammar/schema/index_signature/fail_2/stderr.golden.py @@ -0,0 +1,21 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IndexSignatureError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5 + ) + ], + arg_msg="index signature attribute name 'name' cannot have the same name as schema attributes" + ), + file=sys.stdout +) diff --git a/test/grammar/schema/index_signature/fail_3/main.k b/test/grammar/schema/index_signature/fail_3/main.k new file mode 100644 index 000000000..c5af164e7 --- /dev/null +++ b/test/grammar/schema/index_signature/fail_3/main.k @@ -0,0 +1,6 @@ +schema Data: + [str]: int + +data = Data { + name: "test" +} diff --git a/test/grammar/schema/index_signature/fail_3/stderr.golden.py b/test/grammar/schema/index_signature/fail_3/stderr.golden.py new file mode 100644 index 000000000..1bcb1104f --- /dev/null +++ b/test/grammar/schema/index_signature/fail_3/stderr.golden.py @@ -0,0 +1,30 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + arg_msg="expect int", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + arg_msg="got str(test)" + ) + ], + arg_msg="expect int, got str(test)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/index_signature/fail_4/main.k b/test/grammar/schema/index_signature/fail_4/main.k new file mode 100644 index 000000000..c26f22405 --- /dev/null +++ b/test/grammar/schema/index_signature/fail_4/main.k @@ -0,0 +1,7 @@ +schema Data: + count: int + [str]: str + +data = Data { + count: 1 +} diff --git a/test/grammar/schema/index_signature/fail_4/stderr.golden.py b/test/grammar/schema/index_signature/fail_4/stderr.golden.py new file mode 100644 index 000000000..62be229e4 --- /dev/null +++ b/test/grammar/schema/index_signature/fail_4/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IndexSignatureError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5 + ) + ], + arg_msg="the type 'int' of schema attribute 'count' does not meet the index signature definition [str]: str" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/index_signature/fail_5/main.k b/test/grammar/schema/index_signature/fail_5/main.k new file mode 100644 index 000000000..d3dfbf4e2 --- /dev/null +++ b/test/grammar/schema/index_signature/fail_5/main.k @@ -0,0 +1,8 @@ +schema Data: + [name: str]: str + check: + name in ["Alice", "Bob", "John"] + +data = Data { + key1: "value1" +} diff --git a/test/grammar/schema/index_signature/fail_5/stderr.golden.py b/test/grammar/schema/index_signature/fail_5/stderr.golden.py new file mode 100644 index 000000000..2785c515d --- /dev/null +++ b/test/grammar/schema/index_signature/fail_5/stderr.golden.py @@ -0,0 +1,25 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ]) + , file=sys.stdout +) + diff --git a/test/grammar/schema/index_signature/fail_6/main.k b/test/grammar/schema/index_signature/fail_6/main.k new file mode 100644 index 000000000..ebca7c722 --- /dev/null +++ b/test/grammar/schema/index_signature/fail_6/main.k @@ -0,0 +1,12 @@ +import regex + +schema Data: + [attr: str]: str + check: + regex.match(attr, r'^[-._a-zA-Z0-9]+$') + +data = Data { + "key1": "value1" + "key2": "value2" + "invalid/label": "value3" +} diff --git a/test/grammar/schema/index_signature/fail_6/stderr.golden.py b/test/grammar/schema/index_signature/fail_6/stderr.golden.py new file mode 100644 index 000000000..fa5180808 --- /dev/null +++ b/test/grammar/schema/index_signature/fail_6/stderr.golden.py @@ -0,0 +1,24 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=8, + col_no=8, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ]) + , file=sys.stdout +) diff --git a/test/grammar/schema/index_signature/normal_0/main.k b/test/grammar/schema/index_signature/normal_0/main.k new file mode 100644 index 000000000..3ee65c65f --- /dev/null +++ b/test/grammar/schema/index_signature/normal_0/main.k @@ -0,0 +1,18 @@ +schema DataMap1: + [name: str]: int = 1 + +schema DataMap2: + [str]: str + +schema DataMap3: + [str]: str + +map1 = { + "key1": "value1" +} +map2 = { + "key2": "value2" +} +map3 = { + "key3": "value3" +} diff --git a/test/grammar/schema/index_signature/normal_0/stdout.golden b/test/grammar/schema/index_signature/normal_0/stdout.golden new file mode 100644 index 000000000..aa657844b --- /dev/null +++ b/test/grammar/schema/index_signature/normal_0/stdout.golden @@ -0,0 +1,6 @@ +map1: + key1: value1 +map2: + key2: value2 +map3: + key3: value3 diff --git a/test/grammar/schema/index_signature/normal_1/main.k b/test/grammar/schema/index_signature/normal_1/main.k new file mode 100644 index 000000000..63583e6d3 --- /dev/null +++ b/test/grammar/schema/index_signature/normal_1/main.k @@ -0,0 +1,15 @@ +schema LabelMap: + [str]: str | int + +schema NamedMap: + [str]: {str:str} = {"default_key": "default_value"} + +labelMap = LabelMap { + "key1": "value1" + "key2": 2 +} + +namedMap = NamedMap { + "key1": {"key1": "value1"} + "key2": {"key2": "value2"} +} diff --git a/test/grammar/schema/index_signature/normal_1/stdout.golden b/test/grammar/schema/index_signature/normal_1/stdout.golden new file mode 100644 index 000000000..39b1fd967 --- /dev/null +++ b/test/grammar/schema/index_signature/normal_1/stdout.golden @@ -0,0 +1,10 @@ +labelMap: + key1: value1 + key2: 2 +namedMap: + key1: + default_key: default_value + key1: value1 + key2: + default_key: default_value + key2: value2 diff --git a/test/grammar/schema/index_signature/normal_2/main.k b/test/grammar/schema/index_signature/normal_2/main.k new file mode 100644 index 000000000..2a7b43467 --- /dev/null +++ b/test/grammar/schema/index_signature/normal_2/main.k @@ -0,0 +1,11 @@ + +schema NumberMap: + [num: str]: int + + check: + int(num) % 2 == 0 + +numMap = NumberMap { + str(0): 0 + str(2): 2 +} diff --git a/test/grammar/schema/index_signature/normal_2/stdout.golden b/test/grammar/schema/index_signature/normal_2/stdout.golden new file mode 100644 index 000000000..c6f615719 --- /dev/null +++ b/test/grammar/schema/index_signature/normal_2/stdout.golden @@ -0,0 +1,3 @@ +numMap: + '0': 0 + '2': 2 diff --git a/test/grammar/schema/index_signature/normal_3/main.k b/test/grammar/schema/index_signature/normal_3/main.k new file mode 100644 index 000000000..8cfdd26b7 --- /dev/null +++ b/test/grammar/schema/index_signature/normal_3/main.k @@ -0,0 +1,7 @@ + +schema NumberMap: + [num: str]: int | str + +numMap = NumberMap { + **{str(i): str(i * 2) if i < 2 else i * 2 for i in range(0, 5)} +} diff --git a/test/grammar/schema/index_signature/normal_3/stdout.golden b/test/grammar/schema/index_signature/normal_3/stdout.golden new file mode 100644 index 000000000..14b5cabac --- /dev/null +++ b/test/grammar/schema/index_signature/normal_3/stdout.golden @@ -0,0 +1,6 @@ +numMap: + '0': '0' + '1': '2' + '2': 4 + '3': 6 + '4': 8 diff --git a/test/grammar/schema/index_signature/normal_4/main.k b/test/grammar/schema/index_signature/normal_4/main.k new file mode 100644 index 000000000..7c08180a1 --- /dev/null +++ b/test/grammar/schema/index_signature/normal_4/main.k @@ -0,0 +1,10 @@ +schema S: + [str]: [] + +_s: S { + a += [1] +} +_s |= { + b += [1] +} +s = _s diff --git a/test/grammar/schema/index_signature/normal_4/stdout.golden b/test/grammar/schema/index_signature/normal_4/stdout.golden new file mode 100644 index 000000000..27aeea427 --- /dev/null +++ b/test/grammar/schema/index_signature/normal_4/stdout.golden @@ -0,0 +1,5 @@ +s: + a: + - 1 + b: + - 1 diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_0/main.k b/test/grammar/schema/inherit/cycle_inherit_fail_0/main.k new file mode 100644 index 000000000..90b4342e5 --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_0/main.k @@ -0,0 +1,9 @@ +schema Parent(Son): + parent_field: str + +schema Son(Parent): + son_field: str + +parent = Parent { + parent_field: "" +} diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_0/stderr.golden.py b/test/grammar/schema/inherit/cycle_inherit_fail_0/stderr.golden.py new file mode 100644 index 000000000..a8fce6ca4 --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_0/stderr.golden.py @@ -0,0 +1,20 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CycleInheritError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=1 + ), + ], + arg_msg="Son and Parent") + , file=sys.stdout +) + diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_1/main.k b/test/grammar/schema/inherit/cycle_inherit_fail_1/main.k new file mode 100644 index 000000000..3d68fae77 --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_1/main.k @@ -0,0 +1,12 @@ +schema Parent(Son): + parent_field: str + +schema Son(GrandSon): + son_field: str + +schema GrandSon(Parent): + grandson_field: str + +parent = Parent { + parent_field: "" +} diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_1/stderr.golden.py b/test/grammar/schema/inherit/cycle_inherit_fail_1/stderr.golden.py new file mode 100644 index 000000000..7987bfaf7 --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_1/stderr.golden.py @@ -0,0 +1,20 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CycleInheritError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=1 + ), + ], + arg_msg="GrandSon and Parent") + , file=sys.stdout +) + diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_2/kcl.mod b/test/grammar/schema/inherit/cycle_inherit_fail_2/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_2/main.k b/test/grammar/schema/inherit/cycle_inherit_fail_2/main.k new file mode 100644 index 000000000..959405078 --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_2/main.k @@ -0,0 +1,11 @@ +import pkg + +schema Son(pkg.B): + field: str + check: + field == "123" + +son = Son { + fields: "asa", + field: '123' +} diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_2/pkg/b.k b/test/grammar/schema/inherit/cycle_inherit_fail_2/pkg/b.k new file mode 100644 index 000000000..b6d4b0245 --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_2/pkg/b.k @@ -0,0 +1,3 @@ + +schema B(C): + field_B: str diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_2/pkg/c.k b/test/grammar/schema/inherit/cycle_inherit_fail_2/pkg/c.k new file mode 100644 index 000000000..8e6c95df8 --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_2/pkg/c.k @@ -0,0 +1,3 @@ + +schema C(B): + field_C: str diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_2/stderr.golden.py b/test/grammar/schema/inherit/cycle_inherit_fail_2/stderr.golden.py new file mode 100644 index 000000000..eab09126b --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_2/stderr.golden.py @@ -0,0 +1,20 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CycleInheritError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/pkg/c.k", + line_no=2, + col_no=1 + ), + ], + arg_msg="C and B") + , file=sys.stdout +) + diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_3/kcl.mod b/test/grammar/schema/inherit/cycle_inherit_fail_3/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_3/main.k b/test/grammar/schema/inherit/cycle_inherit_fail_3/main.k new file mode 100644 index 000000000..959405078 --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_3/main.k @@ -0,0 +1,11 @@ +import pkg + +schema Son(pkg.B): + field: str + check: + field == "123" + +son = Son { + fields: "asa", + field: '123' +} diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_3/pkg/b.k b/test/grammar/schema/inherit/cycle_inherit_fail_3/pkg/b.k new file mode 100644 index 000000000..4ec86f66c --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_3/pkg/b.k @@ -0,0 +1,2 @@ +schema B(C): + field_B: str diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_3/pkg/c.k b/test/grammar/schema/inherit/cycle_inherit_fail_3/pkg/c.k new file mode 100644 index 000000000..959361941 --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_3/pkg/c.k @@ -0,0 +1,2 @@ +schema C(B): + field_C: str diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_3/stderr.golden.py b/test/grammar/schema/inherit/cycle_inherit_fail_3/stderr.golden.py new file mode 100644 index 000000000..adffa131f --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_3/stderr.golden.py @@ -0,0 +1,20 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CycleInheritError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/pkg/c.k", + line_no=1, + col_no=1 + ), + ], + arg_msg="C and B") + , file=sys.stdout +) + diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_4/kcl.mod b/test/grammar/schema/inherit/cycle_inherit_fail_4/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_4/main.k b/test/grammar/schema/inherit/cycle_inherit_fail_4/main.k new file mode 100644 index 000000000..959405078 --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_4/main.k @@ -0,0 +1,11 @@ +import pkg + +schema Son(pkg.B): + field: str + check: + field == "123" + +son = Son { + fields: "asa", + field: '123' +} diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_4/pkg/b.k b/test/grammar/schema/inherit/cycle_inherit_fail_4/pkg/b.k new file mode 100644 index 000000000..4ec86f66c --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_4/pkg/b.k @@ -0,0 +1,2 @@ +schema B(C): + field_B: str diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_4/pkg/c.k b/test/grammar/schema/inherit/cycle_inherit_fail_4/pkg/c.k new file mode 100644 index 000000000..959361941 --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_4/pkg/c.k @@ -0,0 +1,2 @@ +schema C(B): + field_C: str diff --git a/test/grammar/schema/inherit/cycle_inherit_fail_4/stderr.golden.py b/test/grammar/schema/inherit/cycle_inherit_fail_4/stderr.golden.py new file mode 100644 index 000000000..adffa131f --- /dev/null +++ b/test/grammar/schema/inherit/cycle_inherit_fail_4/stderr.golden.py @@ -0,0 +1,20 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CycleInheritError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/pkg/c.k", + line_no=1, + col_no=1 + ), + ], + arg_msg="C and B") + , file=sys.stdout +) + diff --git a/test/grammar/schema/inherit/defaulting_0/main.k b/test/grammar/schema/inherit/defaulting_0/main.k new file mode 100644 index 000000000..786e9521b --- /dev/null +++ b/test/grammar/schema/inherit/defaulting_0/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName?: str = "John" + lastName?: str = None + +schema Student(Person): + firstName?: str + +student = Student {} diff --git a/test/grammar/schema/inherit/defaulting_0/stdout.golden b/test/grammar/schema/inherit/defaulting_0/stdout.golden new file mode 100644 index 000000000..f21542489 --- /dev/null +++ b/test/grammar/schema/inherit/defaulting_0/stdout.golden @@ -0,0 +1,3 @@ +student: + firstName: John + lastName: null \ No newline at end of file diff --git a/test/grammar/schema/inherit/defaulting_1/main.k b/test/grammar/schema/inherit/defaulting_1/main.k new file mode 100644 index 000000000..66ee94f71 --- /dev/null +++ b/test/grammar/schema/inherit/defaulting_1/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName?: str = "firstName" + lastName?: str = None + +schema Student(Person): + firstName?: str = None + +student = Student {} diff --git a/test/grammar/schema/inherit/defaulting_1/stdout.golden b/test/grammar/schema/inherit/defaulting_1/stdout.golden new file mode 100644 index 000000000..f88b31355 --- /dev/null +++ b/test/grammar/schema/inherit/defaulting_1/stdout.golden @@ -0,0 +1,3 @@ +student: + firstName: null + lastName: null \ No newline at end of file diff --git a/test/grammar/schema/inherit/defaulting_2/main.k b/test/grammar/schema/inherit/defaulting_2/main.k new file mode 100644 index 000000000..6b2cd779c --- /dev/null +++ b/test/grammar/schema/inherit/defaulting_2/main.k @@ -0,0 +1,10 @@ +schema Person: + firstName?: str = "firstName" + lastName?: str = None + +schema Student(Person): + firstName?: str + +student = Student { + "firstName" = "John" +} diff --git a/test/grammar/schema/inherit/defaulting_2/stdout.golden b/test/grammar/schema/inherit/defaulting_2/stdout.golden new file mode 100644 index 000000000..f21542489 --- /dev/null +++ b/test/grammar/schema/inherit/defaulting_2/stdout.golden @@ -0,0 +1,3 @@ +student: + firstName: John + lastName: null \ No newline at end of file diff --git a/test/grammar/schema/inherit/defaulting_3/main.k b/test/grammar/schema/inherit/defaulting_3/main.k new file mode 100644 index 000000000..6896e339c --- /dev/null +++ b/test/grammar/schema/inherit/defaulting_3/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName?: str = None + lastName?: str = None + +schema Student(Person): + firstName?: str = "John" + +student = Student {} diff --git a/test/grammar/schema/inherit/defaulting_3/stdout.golden b/test/grammar/schema/inherit/defaulting_3/stdout.golden new file mode 100644 index 000000000..f21542489 --- /dev/null +++ b/test/grammar/schema/inherit/defaulting_3/stdout.golden @@ -0,0 +1,3 @@ +student: + firstName: John + lastName: null \ No newline at end of file diff --git a/test/grammar/schema/inherit/defaulting_4/main.k b/test/grammar/schema/inherit/defaulting_4/main.k new file mode 100644 index 000000000..39b5946c4 --- /dev/null +++ b/test/grammar/schema/inherit/defaulting_4/main.k @@ -0,0 +1,10 @@ +schema Person: + firstName?: str = "firstName_person" + lastName?: str = None + +schema Student(Person): + firstName?: str = "firstName_student" + +student = Student { + "firstName" = "John" +} diff --git a/test/grammar/schema/inherit/defaulting_4/stdout.golden b/test/grammar/schema/inherit/defaulting_4/stdout.golden new file mode 100644 index 000000000..f21542489 --- /dev/null +++ b/test/grammar/schema/inherit/defaulting_4/stdout.golden @@ -0,0 +1,3 @@ +student: + firstName: John + lastName: null \ No newline at end of file diff --git a/test/grammar/schema/inherit/illegal_inheritance_fail_0/main.k b/test/grammar/schema/inherit/illegal_inheritance_fail_0/main.k new file mode 100644 index 000000000..c9486e6d1 --- /dev/null +++ b/test/grammar/schema/inherit/illegal_inheritance_fail_0/main.k @@ -0,0 +1,8 @@ +a: int = 2 + +schema Son(a): + field: str + +p = Son { + fields: "asa" +} diff --git a/test/grammar/schema/inherit/illegal_inheritance_fail_0/stderr.golden.py b/test/grammar/schema/inherit/illegal_inheritance_fail_0/stderr.golden.py new file mode 100644 index 000000000..bbac2d6a5 --- /dev/null +++ b/test/grammar/schema/inherit/illegal_inheritance_fail_0/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IllegalInheritError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=1 + ) + ], + arg_msg="illegal schema inherit object type 'int'" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/inherit/import_0/kcl.mod b/test/grammar/schema/inherit/import_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/inherit/import_0/main.k b/test/grammar/schema/inherit/import_0/main.k new file mode 100644 index 000000000..ebfbb300a --- /dev/null +++ b/test/grammar/schema/inherit/import_0/main.k @@ -0,0 +1,15 @@ +import pkg + +schema Scholar(pkg.Person): + subject: str + +JonSnow = pkg.Person { + "firstName": "Jon", + "lastName": "Snow" +} + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "subject": "CS" +} diff --git a/test/grammar/schema/inherit/import_0/pkg/module.k b/test/grammar/schema/inherit/import_0/pkg/module.k new file mode 100644 index 000000000..20e9f1f5d --- /dev/null +++ b/test/grammar/schema/inherit/import_0/pkg/module.k @@ -0,0 +1,3 @@ +schema Person: + firstName: str + lastName: str diff --git a/test/grammar/schema/inherit/import_0/stdout.golden b/test/grammar/schema/inherit/import_0/stdout.golden new file mode 100644 index 000000000..be4120b9a --- /dev/null +++ b/test/grammar/schema/inherit/import_0/stdout.golden @@ -0,0 +1,7 @@ +JonSnow: + firstName: Jon + lastName: Snow +JohnDoe: + firstName: John + lastName: Doe + subject: CS diff --git a/test/grammar/schema/inherit/inherit_0/kcl.mod b/test/grammar/schema/inherit/inherit_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/inherit/inherit_0/main.k b/test/grammar/schema/inherit/inherit_0/main.k new file mode 100644 index 000000000..d8d2609cb --- /dev/null +++ b/test/grammar/schema/inherit/inherit_0/main.k @@ -0,0 +1,17 @@ +schema Person: + firstName: str + lastName: str + +schema Scholar(Person): + subject: str + +JonSnow = Person { + "firstName": "Jon", + "lastName": "Snow" +} + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "subject": "CS" +} diff --git a/test/grammar/schema/inherit/inherit_0/stdout.golden b/test/grammar/schema/inherit/inherit_0/stdout.golden new file mode 100644 index 000000000..be4120b9a --- /dev/null +++ b/test/grammar/schema/inherit/inherit_0/stdout.golden @@ -0,0 +1,7 @@ +JonSnow: + firstName: Jon + lastName: Snow +JohnDoe: + firstName: John + lastName: Doe + subject: CS diff --git a/test/grammar/schema/inherit/inherit_1/kcl.mod b/test/grammar/schema/inherit/inherit_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/inherit/inherit_1/main.k b/test/grammar/schema/inherit/inherit_1/main.k new file mode 100644 index 000000000..e45bd93e3 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_1/main.k @@ -0,0 +1,19 @@ +schema Person: + firstName: str + lastName: str + +schema Scholar(Person): + subject: str + school: str + +JonSnow = Person { + "firstName": "Jon", + "lastName": "Snow" +} + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "subject": "CS", + "school": "PKU" +} diff --git a/test/grammar/schema/inherit/inherit_1/stdout.golden b/test/grammar/schema/inherit/inherit_1/stdout.golden new file mode 100644 index 000000000..8d161db36 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_1/stdout.golden @@ -0,0 +1,8 @@ +JonSnow: + firstName: Jon + lastName: Snow +JohnDoe: + firstName: John + lastName: Doe + subject: CS + school: PKU diff --git a/test/grammar/schema/inherit/inherit_2/kcl.mod b/test/grammar/schema/inherit/inherit_2/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/inherit/inherit_2/main.k b/test/grammar/schema/inherit/inherit_2/main.k new file mode 100644 index 000000000..45c834e87 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_2/main.k @@ -0,0 +1,29 @@ +schema Name: + firstName: str + lastName: str + +schema Person(Name): + age: int + +schema Scholar(Person): + subject: str + school: str + +JohnReese = Name { + "firstName": "John", + "lastName": "Reese" +} + +JonSnow = Person { + "firstName": "Jon", + "lastName": "Snow", + "age": 10 +} + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "age": 10, + "subject": "CS", + "school": "PKU" +} diff --git a/test/grammar/schema/inherit/inherit_2/stdout.golden b/test/grammar/schema/inherit/inherit_2/stdout.golden new file mode 100644 index 000000000..da08a09db --- /dev/null +++ b/test/grammar/schema/inherit/inherit_2/stdout.golden @@ -0,0 +1,13 @@ +JohnReese: + firstName: John + lastName: Reese +JonSnow: + firstName: Jon + lastName: Snow + age: 10 +JohnDoe: + firstName: John + lastName: Doe + age: 10 + subject: CS + school: PKU diff --git a/test/grammar/schema/inherit/inherit_change_field_type_0/main.k b/test/grammar/schema/inherit/inherit_change_field_type_0/main.k new file mode 100644 index 000000000..ecbf92bf4 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_0/main.k @@ -0,0 +1,20 @@ +schema Person: + firstName: str + lastName: str + +schema Scholar(Person): + firstName: int + subject: str + school: str + +JonSnow = Person { + "firstName": "Jon", + "lastName": "Snow" +} + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "subject": "CS", + "school": "PKU" +} diff --git a/test/grammar/schema/inherit/inherit_change_field_type_0/stderr.golden.py b/test/grammar/schema/inherit/inherit_change_field_type_0/stderr.golden.py new file mode 100644 index 000000000..7e3673ca7 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_0/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + col_no=5 + ) + ], + arg_msg="can't change schema field type of 'firstName'" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/inherit/inherit_change_field_type_1/kcl.mod b/test/grammar/schema/inherit/inherit_change_field_type_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/inherit/inherit_change_field_type_1/main.k b/test/grammar/schema/inherit/inherit_change_field_type_1/main.k new file mode 100644 index 000000000..5d8f0154a --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_1/main.k @@ -0,0 +1,23 @@ +import pkg + +schema Person: + name: pkg.Name + +schema Scholar(Person): + name: pkg.Name + school: str + +JonSnow = Person { + "name": { + "firstName": "Jon", + "lastName": "Snow" + } +} + +JohnDoe = Scholar { + "name": { + "firstName": "John", + "lastName": "Doe" + }, + "school": "PKU" +} diff --git a/test/grammar/schema/inherit/inherit_change_field_type_1/pkg/name.k b/test/grammar/schema/inherit/inherit_change_field_type_1/pkg/name.k new file mode 100644 index 000000000..c6b7c2bee --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_1/pkg/name.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str diff --git a/test/grammar/schema/inherit/inherit_change_field_type_1/stdout.golden b/test/grammar/schema/inherit/inherit_change_field_type_1/stdout.golden new file mode 100644 index 000000000..3a777e55b --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_1/stdout.golden @@ -0,0 +1,9 @@ +JonSnow: + name: + firstName: Jon + lastName: Snow +JohnDoe: + name: + firstName: John + lastName: Doe + school: PKU \ No newline at end of file diff --git a/test/grammar/schema/inherit/inherit_change_field_type_2/kcl.mod b/test/grammar/schema/inherit/inherit_change_field_type_2/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/inherit/inherit_change_field_type_2/main.k b/test/grammar/schema/inherit/inherit_change_field_type_2/main.k new file mode 100644 index 000000000..b66eb8a58 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_2/main.k @@ -0,0 +1,23 @@ +import pkg + +schema Person: + name: pkg.Name + +schema Scholar(Person): + name: pkg.Name0 + school: str + +JonSnow = Person { + "name": { + "firstName": "Jon", + "lastName": "Snow" + } +} + +JohnDoe = Scholar { + "name": { + "firstName": "John", + "lastName": "Doe" + }, + "school": "PKU" +} diff --git a/test/grammar/schema/inherit/inherit_change_field_type_2/pkg/name.k b/test/grammar/schema/inherit/inherit_change_field_type_2/pkg/name.k new file mode 100644 index 000000000..f76b37516 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_2/pkg/name.k @@ -0,0 +1,7 @@ +schema Name: + firstName: str + lastName: str + +schema Name0: + firstName: str + lastName: str diff --git a/test/grammar/schema/inherit/inherit_change_field_type_2/stderr.golden.py b/test/grammar/schema/inherit/inherit_change_field_type_2/stderr.golden.py new file mode 100644 index 000000000..6db11703e --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_2/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5 + ) + ], + arg_msg="can't change schema field type of 'name'" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/inherit/inherit_change_field_type_3/kcl.mod b/test/grammar/schema/inherit/inherit_change_field_type_3/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/inherit/inherit_change_field_type_3/main.k b/test/grammar/schema/inherit/inherit_change_field_type_3/main.k new file mode 100644 index 000000000..57980f1ae --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_3/main.k @@ -0,0 +1,13 @@ +import pkg + +schema Scholar(pkg.Person): + name: pkg.Name0 + school: str + +JohnDoe = Scholar { + "name": { + "firstName": "John", + "lastName": "Doe" + }, + "school": "PKU" +} diff --git a/test/grammar/schema/inherit/inherit_change_field_type_3/pkg/name.k b/test/grammar/schema/inherit/inherit_change_field_type_3/pkg/name.k new file mode 100644 index 000000000..bf533f3a2 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_3/pkg/name.k @@ -0,0 +1,10 @@ +schema Name: + firstName: str + lastName: str + +schema Name0: + firstName: str + lastName: str + +schema Person: + name: Name diff --git a/test/grammar/schema/inherit/inherit_change_field_type_3/stderr.golden.py b/test/grammar/schema/inherit/inherit_change_field_type_3/stderr.golden.py new file mode 100644 index 000000000..5e50f2e73 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_3/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=5 + ) + ], + arg_msg="can't change schema field type of 'name'" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/inherit/inherit_change_field_type_4/kcl.mod b/test/grammar/schema/inherit/inherit_change_field_type_4/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/inherit/inherit_change_field_type_4/main.k b/test/grammar/schema/inherit/inherit_change_field_type_4/main.k new file mode 100644 index 000000000..8401455bc --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_4/main.k @@ -0,0 +1,13 @@ +import pkg + +schema Scholar(pkg.Person): + name: pkg.Name + school: str + +JohnDoe = Scholar { + "name": { + "firstName": "John", + "lastName": "Doe" + }, + "school": "PKU" +} diff --git a/test/grammar/schema/inherit/inherit_change_field_type_4/pkg/name.k b/test/grammar/schema/inherit/inherit_change_field_type_4/pkg/name.k new file mode 100644 index 000000000..bf533f3a2 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_4/pkg/name.k @@ -0,0 +1,10 @@ +schema Name: + firstName: str + lastName: str + +schema Name0: + firstName: str + lastName: str + +schema Person: + name: Name diff --git a/test/grammar/schema/inherit/inherit_change_field_type_4/stdout.golden b/test/grammar/schema/inherit/inherit_change_field_type_4/stdout.golden new file mode 100644 index 000000000..0a36ee687 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_change_field_type_4/stdout.golden @@ -0,0 +1,5 @@ +JohnDoe: + name: + firstName: John + lastName: Doe + school: PKU diff --git a/test/grammar/schema/inherit/inherit_mixin_fail/main.k b/test/grammar/schema/inherit/inherit_mixin_fail/main.k new file mode 100644 index 000000000..4d5325d73 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_mixin_fail/main.k @@ -0,0 +1,14 @@ +schema Person: + firstName: str + lastName: str + +schema FullnameMixin: + fullName = "{} {}".format(firstName, lastName) + +schema Scholar(FullnameMixin): + school: str + +JohnDoe = Scholar { + "firstName": "Jon", + "lastName": "Doe" +} diff --git a/test/grammar/schema/inherit/inherit_mixin_fail/stderr.golden.py b/test/grammar/schema/inherit/inherit_mixin_fail/stderr.golden.py new file mode 100644 index 000000000..165aec4a6 --- /dev/null +++ b/test/grammar/schema/inherit/inherit_mixin_fail/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IllegalInheritError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=8, + col_no=1 + ) + ], + arg_msg="mixin inheritance FullnameMixin is prohibited" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/inherit/multi_inherit_fail_0/main.k b/test/grammar/schema/inherit/multi_inherit_fail_0/main.k new file mode 100644 index 000000000..b15e5590e --- /dev/null +++ b/test/grammar/schema/inherit/multi_inherit_fail_0/main.k @@ -0,0 +1,22 @@ +schema Person: + firstName: str + lastName: str + +schema Knowledge: + firstName: int + subject: str + +schema Scholar(Person, Knowledge): + school: str + +JonSnow = Person { + "firstName": "Jon", + "lastName": "Snow" +} + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "subject": "CS", + "school": "PKU" +} diff --git a/test/grammar/schema/inherit/multi_inherit_fail_0/stderr.golden.py b/test/grammar/schema/inherit/multi_inherit_fail_0/stderr.golden.py new file mode 100644 index 000000000..5c8f2cc43 --- /dev/null +++ b/test/grammar/schema/inherit/multi_inherit_fail_0/stderr.golden.py @@ -0,0 +1,23 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.MultiInheritError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=9, + col_no=16, + end_col_no=33, + ) + ], + arg_msg=kcl_error.MULTI_INHERIT_MSG.format("Scholar") + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/inherit/multi_inherit_fail_1/main.k b/test/grammar/schema/inherit/multi_inherit_fail_1/main.k new file mode 100644 index 000000000..8453e735c --- /dev/null +++ b/test/grammar/schema/inherit/multi_inherit_fail_1/main.k @@ -0,0 +1,22 @@ +schema Person: + firstName: str + lastName: str + +schema KnowledgeMixin: + firstName: int + subject: str + +schema Scholar(KnowledgeMixin, Person): + school: str + +JonSnow = Person { + "firstName": "Jon", + "lastName": "Snow" +} + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "subject": "CS", + "school": "PKU" +} diff --git a/test/grammar/schema/inherit/multi_inherit_fail_1/stderr.golden.py b/test/grammar/schema/inherit/multi_inherit_fail_1/stderr.golden.py new file mode 100644 index 000000000..b00dd4090 --- /dev/null +++ b/test/grammar/schema/inherit/multi_inherit_fail_1/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.MultiInheritError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=9, + col_no=16, + end_col_no=38 + ) + ], + arg_msg=kcl_error.MULTI_INHERIT_MSG.format("Scholar") + ), + file=sys.stdout +) diff --git a/test/grammar/schema/init/init_0/main.k b/test/grammar/schema/init/init_0/main.k new file mode 100644 index 000000000..ba4a1c63a --- /dev/null +++ b/test/grammar/schema/init/init_0/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str = "John" + lastName: str + fullName: str = firstName + ' ' + lastName + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/init/init_0/stdout.golden b/test/grammar/schema/init/init_0/stdout.golden new file mode 100644 index 000000000..7caad7724 --- /dev/null +++ b/test/grammar/schema/init/init_0/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: John Doe diff --git a/test/grammar/schema/init/init_1/main.k b/test/grammar/schema/init/init_1/main.k new file mode 100644 index 000000000..5638d6edb --- /dev/null +++ b/test/grammar/schema/init/init_1/main.k @@ -0,0 +1,8 @@ +schema Person: + name: str = "default name" + gender: str = "default gender" + +alice = Person { + "name" = "alice", + "gender" = "female" +} diff --git a/test/grammar/schema/init/init_1/stdout.golden b/test/grammar/schema/init/init_1/stdout.golden new file mode 100644 index 000000000..313b3ceb1 --- /dev/null +++ b/test/grammar/schema/init/init_1/stdout.golden @@ -0,0 +1,3 @@ +alice: + name: alice + gender: female \ No newline at end of file diff --git a/test/grammar/schema/init/init_add_member_fail_0/main.k b/test/grammar/schema/init/init_add_member_fail_0/main.k new file mode 100644 index 000000000..3aa68ac17 --- /dev/null +++ b/test/grammar/schema/init/init_add_member_fail_0/main.k @@ -0,0 +1,9 @@ +schema Person: + name: str = "rename" + gender: str = "overwrite" + +alice = Person { + "name" = "alice", + "gender" = "female" + fullName = "full name" +} diff --git a/test/grammar/schema/init/init_add_member_fail_0/stderr.golden.py b/test/grammar/schema/init/init_add_member_fail_0/stderr.golden.py new file mode 100644 index 000000000..a4c8d2879 --- /dev/null +++ b/test/grammar/schema/init/init_add_member_fail_0/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CannotAddMembers_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=8, + col_no=5, + arg_msg="'fullName' is not defined in schema 'Person'" + ), + ], + arg_msg="Cannot add member 'fullName' to schema 'Person'") + , file=sys.stdout +) + diff --git a/test/grammar/schema/init/init_add_member_fail_1/main.k b/test/grammar/schema/init/init_add_member_fail_1/main.k new file mode 100644 index 000000000..bd4136301 --- /dev/null +++ b/test/grammar/schema/init/init_add_member_fail_1/main.k @@ -0,0 +1,16 @@ +schema Name: + name: str + +schema Person: + gender: str = "overwrite" + name: Name = { + "name": "rename", + "fullName": "full name" + } + +alice = Person { + "name" = { + "name": "alice" + }, + "gender" = "female" +} diff --git a/test/grammar/schema/init/init_add_member_fail_1/stderr.golden.py b/test/grammar/schema/init/init_add_member_fail_1/stderr.golden.py new file mode 100644 index 000000000..eaf744eb6 --- /dev/null +++ b/test/grammar/schema/init/init_add_member_fail_1/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CannotAddMembers_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=8, + col_no=9, + arg_msg="'fullName' is not defined in schema 'Name'" + ), + ], + arg_msg="Cannot add member 'fullName' to schema 'Name'") + , file=sys.stdout +) diff --git a/test/grammar/schema/init/init_add_member_fail_2/main.k b/test/grammar/schema/init/init_add_member_fail_2/main.k new file mode 100644 index 000000000..bcc196d74 --- /dev/null +++ b/test/grammar/schema/init/init_add_member_fail_2/main.k @@ -0,0 +1,16 @@ +schema Name: + name: str + +schema Person: + gender: str = "overwrite" + name: Name = { + "name": "rename", + } + +alice = Person { + "name" = { + "name": "alice" + }, + "gender" = "female" + error = "CannotAddMembers" +} diff --git a/test/grammar/schema/init/init_add_member_fail_2/stderr.golden.py b/test/grammar/schema/init/init_add_member_fail_2/stderr.golden.py new file mode 100644 index 000000000..689aab545 --- /dev/null +++ b/test/grammar/schema/init/init_add_member_fail_2/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CannotAddMembers_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=15, + col_no=5, + arg_msg="'error' is not defined in schema 'Person'" + ), + ], + arg_msg="Cannot add member 'error' to schema 'Person'") + , file=sys.stdout +) + diff --git a/test/grammar/schema/init/init_args/main.k b/test/grammar/schema/init/init_args/main.k new file mode 100644 index 000000000..12d55307b --- /dev/null +++ b/test/grammar/schema/init/init_args/main.k @@ -0,0 +1,36 @@ +schema Backend: + metadata?: {str:} + spec?: {str:} + key?: str = None + +schema Frontend: + name?: str = None + image?: str = None + +schema Wrapper[data]: + ok?: str = "123" + dataOn?: Frontend = Frontend { + name: "str" + } + kubeResource?: Backend = Backend { + metadata: { + "name": data.name, + spec: dataOn.name, + key: data.name + }, + spec: { + podSpec: { + template: { + containers: [{ + image: ok + }] + } + } + } + } + +_front = Frontend { + name: "front", + image: "docker:xx" +} +back = Wrapper(_front) {} diff --git a/test/grammar/schema/init/init_args/stdout.golden b/test/grammar/schema/init/init_args/stdout.golden new file mode 100644 index 000000000..53e24ae15 --- /dev/null +++ b/test/grammar/schema/init/init_args/stdout.golden @@ -0,0 +1,16 @@ +back: + ok: '123' + dataOn: + name: str + image: null + kubeResource: + metadata: + name: front + spec: str + key: front + spec: + podSpec: + template: + containers: + - image: '123' + key: null \ No newline at end of file diff --git a/test/grammar/schema/init/init_args_0/main.k b/test/grammar/schema/init/init_args_0/main.k new file mode 100644 index 000000000..072a6bd30 --- /dev/null +++ b/test/grammar/schema/init/init_args_0/main.k @@ -0,0 +1,8 @@ +schema Person[separator]: + firstName: str = "John" + lastName: str + fullName: str = firstName + separator + lastName + +JohnDoe = Person('_') { + "lastName": "Doe" +} diff --git a/test/grammar/schema/init/init_args_0/stdout.golden b/test/grammar/schema/init/init_args_0/stdout.golden new file mode 100644 index 000000000..389dfa7cf --- /dev/null +++ b/test/grammar/schema/init/init_args_0/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: John_Doe diff --git a/test/grammar/schema/init/init_args_1/main.k b/test/grammar/schema/init/init_args_1/main.k new file mode 100644 index 000000000..2b3a56e9f --- /dev/null +++ b/test/grammar/schema/init/init_args_1/main.k @@ -0,0 +1,30 @@ +schema Backend: + metadata: {str:} + spec: {str:} + +schema Wrapper[data]: + kubeResource: Backend = Backend { + metadata: { + "name": data.name + }, + spec: { + podSpec: { + template: { + containers: [{ + image: data.image + }] + } + } + } + } + +schema Frontend: + name: str + image: str + +_front = Frontend { + name: "front", + image: "docker:xx" +} + +back = Wrapper(_front) {} diff --git a/test/grammar/schema/init/init_args_1/stdout.golden b/test/grammar/schema/init/init_args_1/stdout.golden new file mode 100644 index 000000000..4425b423d --- /dev/null +++ b/test/grammar/schema/init/init_args_1/stdout.golden @@ -0,0 +1,9 @@ +back: + kubeResource: + metadata: + name: front + spec: + podSpec: + template: + containers: + - image: docker:xx \ No newline at end of file diff --git a/test/grammar/schema/init/init_args_2/main.k b/test/grammar/schema/init/init_args_2/main.k new file mode 100644 index 000000000..5237efe5a --- /dev/null +++ b/test/grammar/schema/init/init_args_2/main.k @@ -0,0 +1,26 @@ +schema Backend: + metadata: {str:} + spec: {str:} + +schema Wrapper[data]: + kubeResource: Backend = Backend { + metadata: { + "name": data["name"] + }, + spec: { + podSpec: { + template: { + containers: [{ + image: data["image"] + }] + } + } + } + } + +_front = { + "name": "front", + "image": "docker:xx" +} + +back = Wrapper(_front) {} diff --git a/test/grammar/schema/init/init_args_2/stdout.golden b/test/grammar/schema/init/init_args_2/stdout.golden new file mode 100644 index 000000000..4425b423d --- /dev/null +++ b/test/grammar/schema/init/init_args_2/stdout.golden @@ -0,0 +1,9 @@ +back: + kubeResource: + metadata: + name: front + spec: + podSpec: + template: + containers: + - image: docker:xx \ No newline at end of file diff --git a/test/grammar/schema/init/init_args_3/main.k b/test/grammar/schema/init/init_args_3/main.k new file mode 100644 index 000000000..88a07be49 --- /dev/null +++ b/test/grammar/schema/init/init_args_3/main.k @@ -0,0 +1,8 @@ + + +schema Person[name_, age_, count_]: + name: str = name_ + age: int = age_ + count: int = count_ + +person = Person("Alice", 18, 12) {} diff --git a/test/grammar/schema/init/init_args_3/stdout.golden b/test/grammar/schema/init/init_args_3/stdout.golden new file mode 100644 index 000000000..2344956f6 --- /dev/null +++ b/test/grammar/schema/init/init_args_3/stdout.golden @@ -0,0 +1,4 @@ +person: + name: Alice + age: 18 + count: 12 diff --git a/test/grammar/schema/init/init_assign/main.k b/test/grammar/schema/init/init_assign/main.k new file mode 100644 index 000000000..7520af0d3 --- /dev/null +++ b/test/grammar/schema/init/init_assign/main.k @@ -0,0 +1,8 @@ +schema Person: + age: int = 10 + name: str = "John" + info: {str:} = {"address": "abc", "old": False} + family: [str] = ["mom", "dad"] + adult: bool = False + +John = Person {} diff --git a/test/grammar/schema/init/init_assign/stdout.golden b/test/grammar/schema/init/init_assign/stdout.golden new file mode 100644 index 000000000..058f42608 --- /dev/null +++ b/test/grammar/schema/init/init_assign/stdout.golden @@ -0,0 +1,10 @@ +John: + age: 10 + name: John + info: + address: abc + old: false + family: + - mom + - dad + adult: false diff --git a/test/grammar/schema/init/init_assign_1/main.k b/test/grammar/schema/init/init_assign_1/main.k new file mode 100644 index 000000000..a521307f5 --- /dev/null +++ b/test/grammar/schema/init/init_assign_1/main.k @@ -0,0 +1,27 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + age: int + name: Name + role: str + info: {str:} + family: [str] + adult: bool + +schema Community: + members: [Person] + +John = Community { + "members": [ + { + "age": 15, + "role": "monitor", + "name": {"firstName": "John", "lastName": "Reese"}, + "info": {"address": "Beijing"}, + "family": ["mom", "dad"], + "adult": False + } + ] +} diff --git a/test/grammar/schema/init/init_assign_1/stdout.golden b/test/grammar/schema/init/init_assign_1/stdout.golden new file mode 100644 index 000000000..f2694433f --- /dev/null +++ b/test/grammar/schema/init/init_assign_1/stdout.golden @@ -0,0 +1,13 @@ +John: + members: + - age: 15 + name: + firstName: John + lastName: Reese + role: monitor + info: + address: Beijing + family: + - mom + - dad + adult: false diff --git a/test/grammar/schema/init/init_assign_2/kcl.mod b/test/grammar/schema/init/init_assign_2/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/init/init_assign_2/main.k b/test/grammar/schema/init/init_assign_2/main.k new file mode 100644 index 000000000..3eb6fe818 --- /dev/null +++ b/test/grammar/schema/init/init_assign_2/main.k @@ -0,0 +1,25 @@ +import pkg + +schema Person: + age: int + name: pkg.Name + role: str + info: {str:} + family: [str] + adult: bool + +schema Community: + members: [Person] + +John = Community { + "members": [ + { + "age": 15, + "role": "monitor", + "name": {"firstName": "John", "lastName": "Reese"}, + "info": {"address": "Beijing"}, + "family": ["mom", "dad"], + "adult": False + } + ] +} diff --git a/test/grammar/schema/init/init_assign_2/pkg/name.k b/test/grammar/schema/init/init_assign_2/pkg/name.k new file mode 100644 index 000000000..c6b7c2bee --- /dev/null +++ b/test/grammar/schema/init/init_assign_2/pkg/name.k @@ -0,0 +1,3 @@ +schema Name: + firstName: str + lastName: str diff --git a/test/grammar/schema/init/init_assign_2/stdout.golden b/test/grammar/schema/init/init_assign_2/stdout.golden new file mode 100644 index 000000000..f2694433f --- /dev/null +++ b/test/grammar/schema/init/init_assign_2/stdout.golden @@ -0,0 +1,13 @@ +John: + members: + - age: 15 + name: + firstName: John + lastName: Reese + role: monitor + info: + address: Beijing + family: + - mom + - dad + adult: false diff --git a/test/grammar/schema/init/init_assign_3/kcl.mod b/test/grammar/schema/init/init_assign_3/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/init/init_assign_3/main.k b/test/grammar/schema/init/init_assign_3/main.k new file mode 100644 index 000000000..d3abcb176 --- /dev/null +++ b/test/grammar/schema/init/init_assign_3/main.k @@ -0,0 +1,18 @@ +import person + +alice = person.Person { + "name": "alice", + "tagged" = True, + "labels": {"diligent": "true"}, + "address": "St.01", + "partitions": ["p1", "p2"], + "principals": {"latest": "Smith"}, + "mathematics": { + "versions": ["1", "2", "3"] + }, + "art": { + "labels": {"talent": "required"}, + "major": False, + "credit" = 4 + } +} diff --git a/test/grammar/schema/init/init_assign_3/metadata/v1/object_meta.k b/test/grammar/schema/init/init_assign_3/metadata/v1/object_meta.k new file mode 100644 index 000000000..59a899f63 --- /dev/null +++ b/test/grammar/schema/init/init_assign_3/metadata/v1/object_meta.k @@ -0,0 +1,6 @@ +schema ObjectMeta: + name?: str = None + tagged?: bool = None + version?: int = None + historyVersions?: [str] = None + labels?: {str:str} = None diff --git a/test/grammar/schema/init/init_assign_3/mixins/subject_mixin.k b/test/grammar/schema/init/init_assign_3/mixins/subject_mixin.k new file mode 100644 index 000000000..ffe8044b9 --- /dev/null +++ b/test/grammar/schema/init/init_assign_3/mixins/subject_mixin.k @@ -0,0 +1,19 @@ +import ..org.v1 as org + +schema SubjectMixin: + subjects = [ + org.Subject { + "name": "mathematics", + "credit" = 5, + "major": True, + "labels": labels, + "versions": mathematics["versions"] + }, + org.Subject { + "name": "art", + "credit" = art["credit"], + "major": art["major"], + "labels": art["labels"], + "versions": ["1", "2"] + } + ] diff --git a/test/grammar/schema/init/init_assign_3/org/v1/scholar.k b/test/grammar/schema/init/init_assign_3/org/v1/scholar.k new file mode 100644 index 000000000..d3b2d6e81 --- /dev/null +++ b/test/grammar/schema/init/init_assign_3/org/v1/scholar.k @@ -0,0 +1,5 @@ +import ...metadata.v1 + +schema Scholar: + school?: School = None + metadata?: v1.ObjectMeta = None diff --git a/test/grammar/schema/init/init_assign_3/org/v1/school.k b/test/grammar/schema/init/init_assign_3/org/v1/school.k new file mode 100644 index 000000000..970223824 --- /dev/null +++ b/test/grammar/schema/init/init_assign_3/org/v1/school.k @@ -0,0 +1,10 @@ +import ...metadata.v1 + +schema School: + kind?: str = "School" + metadata?: v1.ObjectMeta + address?: str = None + famous?: bool = None + subjects?: [Subject] = None + partitions?: [str] = None + principals?: {str:str} = None diff --git a/test/grammar/schema/init/init_assign_3/org/v1/subject.k b/test/grammar/schema/init/init_assign_3/org/v1/subject.k new file mode 100644 index 000000000..56918d294 --- /dev/null +++ b/test/grammar/schema/init/init_assign_3/org/v1/subject.k @@ -0,0 +1,6 @@ +schema Subject: + name?: str = None + credit?: int = 3 + major?: bool = None + labels?: {str:str} = None + versions?: [str] = None diff --git a/test/grammar/schema/init/init_assign_3/person/person.k b/test/grammar/schema/init/init_assign_3/person/person.k new file mode 100644 index 000000000..827fb0cce --- /dev/null +++ b/test/grammar/schema/init/init_assign_3/person/person.k @@ -0,0 +1,36 @@ +import ..org.v1 as org +import ..metadata.v1 as meta +import ..mixins + +schema Person: + mixin [mixins.SubjectMixin] + name?: str = None + tagged?: bool = False + labels?: {str:str} = None + famous?: bool = False + address?: str = None + partitions?: [str] = None + principals?: {str:str} = None + + mathematics?: {str:} = None + art?: {str:} = None + subjects?: [org.Subject] = None + + metadata?: meta.ObjectMeta = { + "name": name, + "tagged": tagged, + "labels": labels + } + scholarModel?: org.Scholar = { + "school": { + "metadata": { + "labels": labels + }, + "address": address, + "famous": famous, + "subjects": subjects, + "partitions": partitions, + "principals": principals + }, + "metadata": metadata + } diff --git a/test/grammar/schema/init/init_assign_3/stdout.golden b/test/grammar/schema/init/init_assign_3/stdout.golden new file mode 100644 index 000000000..3855fa187 --- /dev/null +++ b/test/grammar/schema/init/init_assign_3/stdout.golden @@ -0,0 +1,89 @@ +alice: + name: alice + tagged: true + labels: + diligent: 'true' + famous: false + address: St.01 + partitions: + - p1 + - p2 + principals: + latest: Smith + mathematics: + versions: + - '1' + - '2' + - '3' + art: + labels: + talent: required + major: false + credit: 4 + subjects: + - name: mathematics + credit: 5 + major: true + labels: + diligent: 'true' + versions: + - '1' + - '2' + - '3' + - name: art + credit: 4 + major: false + labels: + talent: required + versions: + - '1' + - '2' + metadata: + name: alice + tagged: true + version: null + historyVersions: null + labels: + diligent: 'true' + scholarModel: + school: + kind: School + metadata: + name: null + tagged: null + version: null + historyVersions: null + labels: + diligent: 'true' + address: St.01 + famous: false + subjects: + - name: mathematics + credit: 5 + major: true + labels: + diligent: 'true' + versions: + - '1' + - '2' + - '3' + - name: art + credit: 4 + major: false + labels: + talent: required + versions: + - '1' + - '2' + partitions: + - p1 + - p2 + principals: + latest: Smith + metadata: + name: alice + tagged: true + version: null + historyVersions: null + labels: + diligent: 'true' diff --git a/test/grammar/schema/init/init_assign_4/main.k b/test/grammar/schema/init/init_assign_4/main.k new file mode 100644 index 000000000..0321ee4c6 --- /dev/null +++ b/test/grammar/schema/init/init_assign_4/main.k @@ -0,0 +1,9 @@ +_dict = {"k": "v", "key": "value"} +_labels = {k: _dict[k] for k in _dict if k in ["key"]} + +schema Person: + labels: {str:str} + +alice = Person { + "labels": _labels +} diff --git a/test/grammar/schema/init/init_assign_4/stdout.golden b/test/grammar/schema/init/init_assign_4/stdout.golden new file mode 100644 index 000000000..a24f564d7 --- /dev/null +++ b/test/grammar/schema/init/init_assign_4/stdout.golden @@ -0,0 +1,3 @@ +alice: + labels: + key: value diff --git a/test/grammar/schema/init/init_assign_5/main.k b/test/grammar/schema/init/init_assign_5/main.k new file mode 100644 index 000000000..40211cfd5 --- /dev/null +++ b/test/grammar/schema/init/init_assign_5/main.k @@ -0,0 +1,8 @@ +_dict = {"k": "v", "key": "value"} + +schema Person: + labels: {str:str} + +alice = Person { + "labels": {k: _dict[k] for k in _dict if k in ["key"]} +} diff --git a/test/grammar/schema/init/init_assign_5/stdout.golden b/test/grammar/schema/init/init_assign_5/stdout.golden new file mode 100644 index 000000000..a24f564d7 --- /dev/null +++ b/test/grammar/schema/init/init_assign_5/stdout.golden @@ -0,0 +1,3 @@ +alice: + labels: + key: value diff --git a/test/grammar/schema/init/init_cycle_fail_0/_main.k b/test/grammar/schema/init/init_cycle_fail_0/_main.k new file mode 100644 index 000000000..1d9b2628c --- /dev/null +++ b/test/grammar/schema/init/init_cycle_fail_0/_main.k @@ -0,0 +1,19 @@ +schema Parent(Son): + parent_field: str + son: Son = Son { + parent: Parent { + parent_field: "123" + } + } + +schema Son: + son_field: str + parent: Parent = Parent { + son: Son { + son_field: "123" + } + } + +parent = Parent { + parent_field: "", +} diff --git a/test/grammar/schema/init/init_cycle_fail_0/stderr.golden.py b/test/grammar/schema/init/init_cycle_fail_0/stderr.golden.py new file mode 100644 index 000000000..5e3d4ee7e --- /dev/null +++ b/test/grammar/schema/init/init_cycle_fail_0/stderr.golden.py @@ -0,0 +1,18 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.RecursionError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + ), + ], + arg_msg="maximum recursion depth exceeded in __instancecheck__") + , file=sys.stdout +) + diff --git a/test/grammar/schema/init/init_cycle_fail_1/_main.k b/test/grammar/schema/init/init_cycle_fail_1/_main.k new file mode 100644 index 000000000..1d9b2628c --- /dev/null +++ b/test/grammar/schema/init/init_cycle_fail_1/_main.k @@ -0,0 +1,19 @@ +schema Parent(Son): + parent_field: str + son: Son = Son { + parent: Parent { + parent_field: "123" + } + } + +schema Son: + son_field: str + parent: Parent = Parent { + son: Son { + son_field: "123" + } + } + +parent = Parent { + parent_field: "", +} diff --git a/test/grammar/schema/init/init_cycle_fail_1/stderr.golden.py b/test/grammar/schema/init/init_cycle_fail_1/stderr.golden.py new file mode 100644 index 000000000..5e3d4ee7e --- /dev/null +++ b/test/grammar/schema/init/init_cycle_fail_1/stderr.golden.py @@ -0,0 +1,18 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.RecursionError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + ), + ], + arg_msg="maximum recursion depth exceeded in __instancecheck__") + , file=sys.stdout +) + diff --git a/test/grammar/schema/init/init_cycle_fail_2/_main.k b/test/grammar/schema/init/init_cycle_fail_2/_main.k new file mode 100644 index 000000000..64b1c99d5 --- /dev/null +++ b/test/grammar/schema/init/init_cycle_fail_2/_main.k @@ -0,0 +1,15 @@ +schema Parent(Son): + parent_field: str + son: Son = Son { + parent: Parent { + parent_field: "123" + } + } + +schema Son: + son_field: str + parent: Parent = None + +parent = Parent { + parent_field: "", +} diff --git a/test/grammar/schema/init/init_cycle_fail_2/stderr.golden.py b/test/grammar/schema/init/init_cycle_fail_2/stderr.golden.py new file mode 100644 index 000000000..38dbb6236 --- /dev/null +++ b/test/grammar/schema/init/init_cycle_fail_2/stderr.golden.py @@ -0,0 +1,18 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.RecursionError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + ), + ], + arg_msg="maximum recursion depth exceeded in comparison") + , file=sys.stdout +) + diff --git a/test/grammar/schema/init/init_dict_0/main.k b/test/grammar/schema/init/init_dict_0/main.k new file mode 100644 index 000000000..96b603994 --- /dev/null +++ b/test/grammar/schema/init/init_dict_0/main.k @@ -0,0 +1,15 @@ +schema Name: + name: str + +schema Person: + name: Name = { + "name": "default name" + } + gender: str = "default gender" + +alice = Person { + "name" = { + "name": "alice" + }, + "gender" = "female" +} diff --git a/test/grammar/schema/init/init_dict_0/stdout.golden b/test/grammar/schema/init/init_dict_0/stdout.golden new file mode 100644 index 000000000..91562aa33 --- /dev/null +++ b/test/grammar/schema/init/init_dict_0/stdout.golden @@ -0,0 +1,4 @@ +alice: + name: + name: alice + gender: female \ No newline at end of file diff --git a/test/grammar/schema/init/init_dict_1/main.k b/test/grammar/schema/init/init_dict_1/main.k new file mode 100644 index 000000000..a138ec557 --- /dev/null +++ b/test/grammar/schema/init/init_dict_1/main.k @@ -0,0 +1,5 @@ +schema Person: + name?: str = None + info?: {str:str} = {"name": name} if name else None + +JohnDoe = Person {} diff --git a/test/grammar/schema/init/init_dict_1/stdout.golden b/test/grammar/schema/init/init_dict_1/stdout.golden new file mode 100644 index 000000000..0e3c10de2 --- /dev/null +++ b/test/grammar/schema/init/init_dict_1/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + name: null + info: null \ No newline at end of file diff --git a/test/grammar/schema/init/init_dict_fail_0/main.k b/test/grammar/schema/init/init_dict_fail_0/main.k new file mode 100644 index 000000000..37e387bd1 --- /dev/null +++ b/test/grammar/schema/init/init_dict_fail_0/main.k @@ -0,0 +1,16 @@ +schema Name: + name: str + +schema Person: + name: Name = { + "name": "rename", + "gender": "female" + } + gender: str = "overwrite" + +alice = Person { + "name": { + "name": "alice" + }, + "gender": "female" +} diff --git a/test/grammar/schema/init/init_dict_fail_0/stderr.golden.py b/test/grammar/schema/init/init_dict_fail_0/stderr.golden.py new file mode 100644 index 000000000..1fd1d45ff --- /dev/null +++ b/test/grammar/schema/init/init_dict_fail_0/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CannotAddMembers_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=9, + arg_msg="'gender' is not defined in schema 'Name'" + ), + ], + arg_msg="Cannot add member 'gender' to schema 'Name'") + , file=sys.stdout +) diff --git a/test/grammar/schema/init/init_err_key_fail_0/main.k b/test/grammar/schema/init/init_err_key_fail_0/main.k new file mode 100644 index 000000000..c369a6beb --- /dev/null +++ b/test/grammar/schema/init/init_err_key_fail_0/main.k @@ -0,0 +1,36 @@ +schema Backend: + metadata?: {str:} + spec?: {str:} + key?: str + +schema Wrapper[data: Frontend]: + ok?: str = "123" + dataOn?: Frontend = Frontend { + name: "str" + } + kubeResource?: Backend = Backend { + metadata: { + "name": data.name_err_key, + "spec": dataOn.name, + key: data.name + }, + spec: { + podSpec: { + template: { + containers: [{ + image: ok + }] + } + } + } + } + +schema Frontend: + name?: str + image?: str + +_front = Frontend { + name: "front", + image: "docker:xx" +} +back = Wrapper(_front) {} diff --git a/test/grammar/schema/init/init_err_key_fail_0/stderr.golden.py b/test/grammar/schema/init/init_err_key_fail_0/stderr.golden.py new file mode 100644 index 000000000..47089180b --- /dev/null +++ b/test/grammar/schema/init/init_err_key_fail_0/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.AttributeError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=13, + col_no=21 + ) + ], + arg_msg="schema 'Frontend' attribute 'name_err_key' not found" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/init/init_if_0/main.k b/test/grammar/schema/init/init_if_0/main.k new file mode 100644 index 000000000..01a109b75 --- /dev/null +++ b/test/grammar/schema/init/init_if_0/main.k @@ -0,0 +1,12 @@ +schema Person: + config: {str:str} + name: str + + if config and config["name"]: + name = config["name"] + +JohnDoe = Person { + "config": { + "name": "John Doe" + } +} diff --git a/test/grammar/schema/init/init_if_0/stdout.golden b/test/grammar/schema/init/init_if_0/stdout.golden new file mode 100644 index 000000000..4c037003b --- /dev/null +++ b/test/grammar/schema/init/init_if_0/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + config: + name: John Doe + name: John Doe \ No newline at end of file diff --git a/test/grammar/schema/init/init_if_1/main.k b/test/grammar/schema/init/init_if_1/main.k new file mode 100644 index 000000000..640c750c9 --- /dev/null +++ b/test/grammar/schema/init/init_if_1/main.k @@ -0,0 +1,14 @@ +schema Person: + name: str + age: int + status: str = "adult" + + if age < 18: + status = "minor" + elif age > 70: + status = "aged" + +JohnDoe = Person { + "name": "John", + "age": 10 +} diff --git a/test/grammar/schema/init/init_if_1/stdout.golden b/test/grammar/schema/init/init_if_1/stdout.golden new file mode 100644 index 000000000..9f25b0b72 --- /dev/null +++ b/test/grammar/schema/init/init_if_1/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + name: John + age: 10 + status: minor \ No newline at end of file diff --git a/test/grammar/schema/init/init_if_2/main.k b/test/grammar/schema/init/init_if_2/main.k new file mode 100644 index 000000000..3fae069e8 --- /dev/null +++ b/test/grammar/schema/init/init_if_2/main.k @@ -0,0 +1,14 @@ +schema Person: + name: str + age: int + status: str + + if age < 18: + status = "minor" + else: + status = "adult" + +JohnDoe = Person { + "name": "John", + "age": 10 +} diff --git a/test/grammar/schema/init/init_if_2/stdout.golden b/test/grammar/schema/init/init_if_2/stdout.golden new file mode 100644 index 000000000..488614059 --- /dev/null +++ b/test/grammar/schema/init/init_if_2/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + name: John + age: 10 + status: minor diff --git a/test/grammar/schema/init/init_if_3/main.k b/test/grammar/schema/init/init_if_3/main.k new file mode 100644 index 000000000..3acb861bb --- /dev/null +++ b/test/grammar/schema/init/init_if_3/main.k @@ -0,0 +1,16 @@ +schema Person: + name: str + age: int + status: str + + if age < 18: + status = "minor" + elif age > 70: + status = "aged" + else: + status = "adult" + +JohnDoe = Person { + "name": "John", + "age": 20 +} diff --git a/test/grammar/schema/init/init_if_3/stdout.golden b/test/grammar/schema/init/init_if_3/stdout.golden new file mode 100644 index 000000000..bab4c4bd5 --- /dev/null +++ b/test/grammar/schema/init/init_if_3/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + name: John + age: 20 + status: adult diff --git a/test/grammar/schema/init/init_if_4/main.k b/test/grammar/schema/init/init_if_4/main.k new file mode 100644 index 000000000..cf955887b --- /dev/null +++ b/test/grammar/schema/init/init_if_4/main.k @@ -0,0 +1,8 @@ +schema Person: + name?: str = None + dependOnName?: str = "default" + + if name: + dependOnName = "value" + +JohnDoe = Person {} diff --git a/test/grammar/schema/init/init_if_4/stdout.golden b/test/grammar/schema/init/init_if_4/stdout.golden new file mode 100644 index 000000000..8c25ed270 --- /dev/null +++ b/test/grammar/schema/init/init_if_4/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + name: null + dependOnName: default \ No newline at end of file diff --git a/test/grammar/schema/init/init_if_5/main.k b/test/grammar/schema/init/init_if_5/main.k new file mode 100644 index 000000000..6e68b6f21 --- /dev/null +++ b/test/grammar/schema/init/init_if_5/main.k @@ -0,0 +1,8 @@ +schema Person: + name?: str = None + dependOnName?: str = None + + if name: + dependOnName = "value" + +JohnDoe = Person {} diff --git a/test/grammar/schema/init/init_if_5/stdout.golden b/test/grammar/schema/init/init_if_5/stdout.golden new file mode 100644 index 000000000..d88668f68 --- /dev/null +++ b/test/grammar/schema/init/init_if_5/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + name: null + dependOnName: null diff --git a/test/grammar/schema/init/init_if_expr_0/main.k b/test/grammar/schema/init/init_if_expr_0/main.k new file mode 100644 index 000000000..b49e2f1ed --- /dev/null +++ b/test/grammar/schema/init/init_if_expr_0/main.k @@ -0,0 +1,14 @@ +schema Person: + firstName?: str = None + lastName?: str = "Doe" + prefix?: str = None + lastName = "{}.{}".format(prefix, lastName) if prefix else lastName + +alice = Person { + "firstName" = "Alice", + "prefix" = "Mrs" +} + +John = Person { + "lastName" = "Green" +} diff --git a/test/grammar/schema/init/init_if_expr_0/stdout.golden b/test/grammar/schema/init/init_if_expr_0/stdout.golden new file mode 100644 index 000000000..c7a2bdb61 --- /dev/null +++ b/test/grammar/schema/init/init_if_expr_0/stdout.golden @@ -0,0 +1,8 @@ +alice: + firstName: Alice + lastName: Mrs.Doe + prefix: Mrs +John: + firstName: null + lastName: Green + prefix: null diff --git a/test/grammar/schema/init/init_if_expr_1/main.k b/test/grammar/schema/init/init_if_expr_1/main.k new file mode 100644 index 000000000..3be793fbd --- /dev/null +++ b/test/grammar/schema/init/init_if_expr_1/main.k @@ -0,0 +1,19 @@ +schema Person: + mixin [InfoMixin] + name?: str + age?: int = None + info?: {str: int|str} + +schema InfoMixin: + info |= {"age": age} if age else {} + +alice = Person { + "name": "alice", + "age": 10, + "info": {"gender": "girl"} +} + +John = Person { + "name": "john", + "info": {"gender": "boy"} +} diff --git a/test/grammar/schema/init/init_if_expr_1/stdout.golden b/test/grammar/schema/init/init_if_expr_1/stdout.golden new file mode 100644 index 000000000..a851f27fd --- /dev/null +++ b/test/grammar/schema/init/init_if_expr_1/stdout.golden @@ -0,0 +1,11 @@ +alice: + name: alice + age: 10 + info: + gender: girl + age: 10 +John: + name: john + age: null + info: + gender: boy diff --git a/test/grammar/schema/init/init_if_expr_2/main.k b/test/grammar/schema/init/init_if_expr_2/main.k new file mode 100644 index 000000000..c3f3ac908 --- /dev/null +++ b/test/grammar/schema/init/init_if_expr_2/main.k @@ -0,0 +1,33 @@ +schema Person: + mixin [InfoMixin] + name?: str + age?: int + info: Info + + __settings__: {str:str} = { + "output_type": "IGNORE" + } + +schema Info: + gender?: str + meta?: {str:} + __settings__: {str:str} = { + "output_type": "STANDALONE" + } + +schema InfoMixin: + info |= { + "meta": { + "name": name if name else "feak", + "age": age if age else -1 + } + } + +persons = [Person { + "name": "alice", + "age": 10, + "info": {"gender": "girl"} +}, Person { + "name": "john", + "info": {"gender": "boy"} +}] diff --git a/test/grammar/schema/init/init_if_expr_2/stdout.golden b/test/grammar/schema/init/init_if_expr_2/stdout.golden new file mode 100644 index 000000000..274bf55c3 --- /dev/null +++ b/test/grammar/schema/init/init_if_expr_2/stdout.golden @@ -0,0 +1,9 @@ +gender: girl +meta: + name: alice + age: 10 +--- +gender: boy +meta: + name: john + age: -1 diff --git a/test/grammar/schema/init/init_if_expr_3/main.k b/test/grammar/schema/init/init_if_expr_3/main.k new file mode 100644 index 000000000..902a5501e --- /dev/null +++ b/test/grammar/schema/init/init_if_expr_3/main.k @@ -0,0 +1,36 @@ +schema Person: + mixin [InfoMixin] + name?: str + age?: int + info?: Info + + __settings__: {str:str} = { + "output_type": "IGNORE" + } + +schema Info: + gender?: str + meta?: {str:} + + __settings__: {str:str} = { + "output_type": "STANDALONE" + } + +schema InfoMixin: + info |= { + "meta": { + "name": name if name else "feak", + "age": age if age else -1 + } + } + +person = "john" + +alice = Person { + "name": "alice", + "age": 10, + "info": {"gender": "girl"} +} if person == "alice" else Person { + "name": "john", + "info": {"gender": "boy"} +} diff --git a/test/grammar/schema/init/init_if_expr_3/stdout.golden b/test/grammar/schema/init/init_if_expr_3/stdout.golden new file mode 100644 index 000000000..b9795af58 --- /dev/null +++ b/test/grammar/schema/init/init_if_expr_3/stdout.golden @@ -0,0 +1,6 @@ +person: john +--- +gender: boy +meta: + name: john + age: -1 diff --git a/test/grammar/schema/init/init_if_expr_4/main.k b/test/grammar/schema/init/init_if_expr_4/main.k new file mode 100644 index 000000000..fb459367e --- /dev/null +++ b/test/grammar/schema/init/init_if_expr_4/main.k @@ -0,0 +1,36 @@ +schema Person: + mixin [InfoMixin] + name?: str + age?: int + info?: Info + + __settings__: {str:str} = { + output_type = "IGNORE" + } + +schema Info: + gender?: str + meta?: {str:} + + __settings__: {str:str} = { + output_type = "STANDALONE" + } + +schema InfoMixin: + info |= Info { + meta = { + name = name if name else "feak", + age = age if age else -1 + } + } + +person = "john" + +alice = Person { + name = "alice", + age = 10, + info: Info {gender = "girl"} +} if person == "alice" else Person { + name = "john", + info: Info {gender = "boy"} +} diff --git a/test/grammar/schema/init/init_if_expr_4/stdout.golden b/test/grammar/schema/init/init_if_expr_4/stdout.golden new file mode 100644 index 000000000..b9795af58 --- /dev/null +++ b/test/grammar/schema/init/init_if_expr_4/stdout.golden @@ -0,0 +1,6 @@ +person: john +--- +gender: boy +meta: + name: john + age: -1 diff --git a/test/grammar/schema/init/init_if_nested/main.k b/test/grammar/schema/init/init_if_nested/main.k new file mode 100644 index 000000000..56fa36d47 --- /dev/null +++ b/test/grammar/schema/init/init_if_nested/main.k @@ -0,0 +1,17 @@ +schema Person: + name: str + age: int + status: str + + if age > 18: + if age < 70: + status = "adult" + else: + status = "aged" + else: + status = "minor" + +JohnDoe = Person { + "name": "John", + "age": 80 +} diff --git a/test/grammar/schema/init/init_if_nested/stdout.golden b/test/grammar/schema/init/init_if_nested/stdout.golden new file mode 100644 index 000000000..08d23754e --- /dev/null +++ b/test/grammar/schema/init/init_if_nested/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + name: John + age: 80 + status: aged diff --git a/test/grammar/schema/init/init_in_sub_pkg_0/kcl.mod b/test/grammar/schema/init/init_in_sub_pkg_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/init/init_in_sub_pkg_0/main.k b/test/grammar/schema/init/init_in_sub_pkg_0/main.k new file mode 100644 index 000000000..4d4348d7a --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_0/main.k @@ -0,0 +1,6 @@ +import pkg + +myPet = pkg.Person { + "firstName": "little", + "lastName": "cat" +} diff --git a/test/grammar/schema/init/init_in_sub_pkg_0/pkg/person.k b/test/grammar/schema/init/init_in_sub_pkg_0/pkg/person.k new file mode 100644 index 000000000..ac8c20bda --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_0/pkg/person.k @@ -0,0 +1,9 @@ +schema Name: + name: str + +schema Person: + firstName: str + lastName: str + name: Name = Name { + "name": "name" + } diff --git a/test/grammar/schema/init/init_in_sub_pkg_0/stdout.golden b/test/grammar/schema/init/init_in_sub_pkg_0/stdout.golden new file mode 100644 index 000000000..942bf1a35 --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_0/stdout.golden @@ -0,0 +1,5 @@ +myPet: + firstName: little + lastName: cat + name: + name: name \ No newline at end of file diff --git a/test/grammar/schema/init/init_in_sub_pkg_1/kcl.mod b/test/grammar/schema/init/init_in_sub_pkg_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/init/init_in_sub_pkg_1/main.k b/test/grammar/schema/init/init_in_sub_pkg_1/main.k new file mode 100644 index 000000000..4d4348d7a --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_1/main.k @@ -0,0 +1,6 @@ +import pkg + +myPet = pkg.Person { + "firstName": "little", + "lastName": "cat" +} diff --git a/test/grammar/schema/init/init_in_sub_pkg_1/pkg/person.k b/test/grammar/schema/init/init_in_sub_pkg_1/pkg/person.k new file mode 100644 index 000000000..0b976f772 --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_1/pkg/person.k @@ -0,0 +1,9 @@ +schema Name: + fullName: str + +schema Person: + firstName: str + lastName: str + name: Name = Name { + "fullName": "{} {}".format(firstName, lastName) + } diff --git a/test/grammar/schema/init/init_in_sub_pkg_1/stdout.golden b/test/grammar/schema/init/init_in_sub_pkg_1/stdout.golden new file mode 100644 index 000000000..7c6d08531 --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_1/stdout.golden @@ -0,0 +1,5 @@ +myPet: + firstName: little + lastName: cat + name: + fullName: little cat \ No newline at end of file diff --git a/test/grammar/schema/init/init_in_sub_pkg_2/kcl.mod b/test/grammar/schema/init/init_in_sub_pkg_2/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/init/init_in_sub_pkg_2/main.k b/test/grammar/schema/init/init_in_sub_pkg_2/main.k new file mode 100644 index 000000000..4d4348d7a --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_2/main.k @@ -0,0 +1,6 @@ +import pkg + +myPet = pkg.Person { + "firstName": "little", + "lastName": "cat" +} diff --git a/test/grammar/schema/init/init_in_sub_pkg_2/pkg/name.k b/test/grammar/schema/init/init_in_sub_pkg_2/pkg/name.k new file mode 100644 index 000000000..4abb84846 --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_2/pkg/name.k @@ -0,0 +1,2 @@ +schema Name: + fullName: str diff --git a/test/grammar/schema/init/init_in_sub_pkg_2/pkg/person.k b/test/grammar/schema/init/init_in_sub_pkg_2/pkg/person.k new file mode 100644 index 000000000..cb203e859 --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_2/pkg/person.k @@ -0,0 +1,6 @@ +schema Person: + firstName: str + lastName: str + name: Name = Name { + "fullName": "{} {}".format(firstName, lastName) + } diff --git a/test/grammar/schema/init/init_in_sub_pkg_2/stdout.golden b/test/grammar/schema/init/init_in_sub_pkg_2/stdout.golden new file mode 100644 index 000000000..7c6d08531 --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_2/stdout.golden @@ -0,0 +1,5 @@ +myPet: + firstName: little + lastName: cat + name: + fullName: little cat \ No newline at end of file diff --git a/test/grammar/schema/init/init_in_sub_pkg_3/kcl.mod b/test/grammar/schema/init/init_in_sub_pkg_3/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/init/init_in_sub_pkg_3/main.k b/test/grammar/schema/init/init_in_sub_pkg_3/main.k new file mode 100644 index 000000000..df2c4743b --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_3/main.k @@ -0,0 +1,6 @@ +import pkg1 + +myPet = pkg1.Person { + "firstName": "little", + "lastName": "cat" +} diff --git a/test/grammar/schema/init/init_in_sub_pkg_3/pkg1/person.k b/test/grammar/schema/init/init_in_sub_pkg_3/pkg1/person.k new file mode 100644 index 000000000..a16415bd8 --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_3/pkg1/person.k @@ -0,0 +1,8 @@ +import ..pkg2 + +schema Person: + firstName: str + lastName: str + name: pkg2.Name = pkg2.Name { + "fullName": "{} {}".format(firstName, lastName) + } diff --git a/test/grammar/schema/init/init_in_sub_pkg_3/pkg2/name.k b/test/grammar/schema/init/init_in_sub_pkg_3/pkg2/name.k new file mode 100644 index 000000000..4abb84846 --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_3/pkg2/name.k @@ -0,0 +1,2 @@ +schema Name: + fullName: str diff --git a/test/grammar/schema/init/init_in_sub_pkg_3/stdout.golden b/test/grammar/schema/init/init_in_sub_pkg_3/stdout.golden new file mode 100644 index 000000000..7c6d08531 --- /dev/null +++ b/test/grammar/schema/init/init_in_sub_pkg_3/stdout.golden @@ -0,0 +1,5 @@ +myPet: + firstName: little + lastName: cat + name: + fullName: little cat \ No newline at end of file diff --git a/test/grammar/schema/init/init_inherit_check/main.k b/test/grammar/schema/init/init_inherit_check/main.k new file mode 100644 index 000000000..4a040f71a --- /dev/null +++ b/test/grammar/schema/init/init_inherit_check/main.k @@ -0,0 +1,16 @@ +schema Name: + firstName: str + lastName: str + fullName: str = "{} {}".format(firstName, lastName) + +schema Person(Name): + gender: str + + check: + fullName not None, "totalName should not be None" + +alice = Person { + "firstName": "Alice", + "lastName": "Smith", + "gender": "female" +} diff --git a/test/grammar/schema/init/init_inherit_check/stdout.golden b/test/grammar/schema/init/init_inherit_check/stdout.golden new file mode 100644 index 000000000..7a516f585 --- /dev/null +++ b/test/grammar/schema/init/init_inherit_check/stdout.golden @@ -0,0 +1,5 @@ +alice: + firstName: Alice + lastName: Smith + fullName: Alice Smith + gender: female \ No newline at end of file diff --git a/test/grammar/schema/init/init_inherit_order_0/main.k b/test/grammar/schema/init/init_inherit_order_0/main.k new file mode 100644 index 000000000..f2191ddb8 --- /dev/null +++ b/test/grammar/schema/init/init_inherit_order_0/main.k @@ -0,0 +1,17 @@ + +schema Name: + firstName: str + lastName: str + + # print("--initiating Name-- name:{} {}".format(firstName, lastName)) + +schema Person(Name): + gender: str + + # print("--initiating Person-- name:{} {}, gender:{}".format(firstName, lastName, gender)) + +alice = Person { + "firstName": "alice", + "lastName": "Smith", + "gender": "female" +} diff --git a/test/grammar/schema/init/init_inherit_order_0/stdout.golden b/test/grammar/schema/init/init_inherit_order_0/stdout.golden new file mode 100644 index 000000000..6975f6a73 --- /dev/null +++ b/test/grammar/schema/init/init_inherit_order_0/stdout.golden @@ -0,0 +1,4 @@ +alice: + firstName: alice + lastName: Smith + gender: female diff --git a/test/grammar/schema/init/init_inherit_order_1/main.k b/test/grammar/schema/init/init_inherit_order_1/main.k new file mode 100644 index 000000000..f2191ddb8 --- /dev/null +++ b/test/grammar/schema/init/init_inherit_order_1/main.k @@ -0,0 +1,17 @@ + +schema Name: + firstName: str + lastName: str + + # print("--initiating Name-- name:{} {}".format(firstName, lastName)) + +schema Person(Name): + gender: str + + # print("--initiating Person-- name:{} {}, gender:{}".format(firstName, lastName, gender)) + +alice = Person { + "firstName": "alice", + "lastName": "Smith", + "gender": "female" +} diff --git a/test/grammar/schema/init/init_inherit_order_1/stdout.golden b/test/grammar/schema/init/init_inherit_order_1/stdout.golden new file mode 100644 index 000000000..6975f6a73 --- /dev/null +++ b/test/grammar/schema/init/init_inherit_order_1/stdout.golden @@ -0,0 +1,4 @@ +alice: + firstName: alice + lastName: Smith + gender: female diff --git a/test/grammar/schema/init/init_kwargs_0/main.k b/test/grammar/schema/init/init_kwargs_0/main.k new file mode 100644 index 000000000..54bde6f80 --- /dev/null +++ b/test/grammar/schema/init/init_kwargs_0/main.k @@ -0,0 +1,4 @@ +schema Person[_name]: + name: str = _name + +person = Person(_name="Alice") {} diff --git a/test/grammar/schema/init/init_kwargs_0/stdout.golden b/test/grammar/schema/init/init_kwargs_0/stdout.golden new file mode 100644 index 000000000..f75a0b5f5 --- /dev/null +++ b/test/grammar/schema/init/init_kwargs_0/stdout.golden @@ -0,0 +1,2 @@ +person: + name: Alice \ No newline at end of file diff --git a/test/grammar/schema/init/init_kwargs_1/main.k b/test/grammar/schema/init/init_kwargs_1/main.k new file mode 100644 index 000000000..c5681a745 --- /dev/null +++ b/test/grammar/schema/init/init_kwargs_1/main.k @@ -0,0 +1,5 @@ +schema Person[_age, _name]: + name: str = _name + age: int = _age + +person = Person(12, _name="Alice") {} diff --git a/test/grammar/schema/init/init_kwargs_1/stdout.golden b/test/grammar/schema/init/init_kwargs_1/stdout.golden new file mode 100644 index 000000000..c581c53b6 --- /dev/null +++ b/test/grammar/schema/init/init_kwargs_1/stdout.golden @@ -0,0 +1,3 @@ +person: + name: Alice + age: 12 \ No newline at end of file diff --git a/test/grammar/schema/init/init_kwargs_2/main.k b/test/grammar/schema/init/init_kwargs_2/main.k new file mode 100644 index 000000000..da3e86a1d --- /dev/null +++ b/test/grammar/schema/init/init_kwargs_2/main.k @@ -0,0 +1,6 @@ +schema Person[_gender, _age, _name]: + name: str = _name + age: int = _age + gender: str = _gender + +person = Person(_gender="male", _name="Bob", _age=12) {} diff --git a/test/grammar/schema/init/init_kwargs_2/stdout.golden b/test/grammar/schema/init/init_kwargs_2/stdout.golden new file mode 100644 index 000000000..cba5f3aff --- /dev/null +++ b/test/grammar/schema/init/init_kwargs_2/stdout.golden @@ -0,0 +1,4 @@ +person: + name: Bob + age: 12 + gender: male \ No newline at end of file diff --git a/test/grammar/schema/init/init_kwargs_fail_0/main.k b/test/grammar/schema/init/init_kwargs_fail_0/main.k new file mode 100644 index 000000000..d856c1e81 --- /dev/null +++ b/test/grammar/schema/init/init_kwargs_fail_0/main.k @@ -0,0 +1,4 @@ +schema Person[_name]: + name: str = _name + +person = Person(_naem="Alice") {} diff --git a/test/grammar/schema/init/init_kwargs_fail_0/stderr.golden.py b/test/grammar/schema/init/init_kwargs_fail_0/stderr.golden.py new file mode 100644 index 000000000..58755b842 --- /dev/null +++ b/test/grammar/schema/init/init_kwargs_fail_0/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=17 + ) + ], + arg_msg="arguments got an unexpected keyword argument '_naem'" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/init/init_kwargs_fail_1/main.k b/test/grammar/schema/init/init_kwargs_fail_1/main.k new file mode 100644 index 000000000..864f2917d --- /dev/null +++ b/test/grammar/schema/init/init_kwargs_fail_1/main.k @@ -0,0 +1,5 @@ +schema Person[_name, _age]: + name: str = _name + age: int = _age + +person = Person(_name="Alice", 12) {} diff --git a/test/grammar/schema/init/init_kwargs_fail_1/stderr.golden.py b/test/grammar/schema/init/init_kwargs_fail_1/stderr.golden.py new file mode 100644 index 000000000..8dc531b3a --- /dev/null +++ b/test/grammar/schema/init/init_kwargs_fail_1/stderr.golden.py @@ -0,0 +1,20 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=32, + end_col_no=34 + ) + ], + arg_msg="positional argument follows keyword argument"), + file=sys.stdout +) diff --git a/test/grammar/schema/init/init_local_variable_0/main.k b/test/grammar/schema/init/init_local_variable_0/main.k new file mode 100644 index 000000000..8836be5d6 --- /dev/null +++ b/test/grammar/schema/init/init_local_variable_0/main.k @@ -0,0 +1,6 @@ +schema Person: + firstName: str = "John" + lastName: str = "Reese" + _tmp = "tmp variable" + +john = Person {} diff --git a/test/grammar/schema/init/init_local_variable_0/stdout.golden b/test/grammar/schema/init/init_local_variable_0/stdout.golden new file mode 100644 index 000000000..ce65cf4fa --- /dev/null +++ b/test/grammar/schema/init/init_local_variable_0/stdout.golden @@ -0,0 +1,3 @@ +john: + firstName: John + lastName: Reese \ No newline at end of file diff --git a/test/grammar/schema/init/init_local_variable_1/main.k b/test/grammar/schema/init/init_local_variable_1/main.k new file mode 100644 index 000000000..44ebc8b7a --- /dev/null +++ b/test/grammar/schema/init/init_local_variable_1/main.k @@ -0,0 +1,7 @@ +schema Person: + firstName: str = "John" + lastName: str + _tmp = "tmp variable" + lastName = _tmp + +john = Person {} diff --git a/test/grammar/schema/init/init_local_variable_1/stdout.golden b/test/grammar/schema/init/init_local_variable_1/stdout.golden new file mode 100644 index 000000000..4ab792967 --- /dev/null +++ b/test/grammar/schema/init/init_local_variable_1/stdout.golden @@ -0,0 +1,3 @@ +john: + firstName: John + lastName: tmp variable \ No newline at end of file diff --git a/test/grammar/schema/init/init_nested_schema_0/main.k b/test/grammar/schema/init/init_nested_schema_0/main.k new file mode 100644 index 000000000..15552991c --- /dev/null +++ b/test/grammar/schema/init/init_nested_schema_0/main.k @@ -0,0 +1,22 @@ +schema Name0: + name: Name1 + +schema Name1: + name: str + +schema Person: + name: Name0 = Name0 { + "name": Name1 { + "name": "default name" + } + } + gender: str = "default gender" + +alice = Person { + "name" = { + "name": { + "name": "alice" + } + }, + "gender" = "female" +} diff --git a/test/grammar/schema/init/init_nested_schema_0/stdout.golden b/test/grammar/schema/init/init_nested_schema_0/stdout.golden new file mode 100644 index 000000000..4bc6ac395 --- /dev/null +++ b/test/grammar/schema/init/init_nested_schema_0/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + name: + name: alice + gender: female \ No newline at end of file diff --git a/test/grammar/schema/init/init_nested_schema_1/main.k b/test/grammar/schema/init/init_nested_schema_1/main.k new file mode 100644 index 000000000..32c1b8680 --- /dev/null +++ b/test/grammar/schema/init/init_nested_schema_1/main.k @@ -0,0 +1,19 @@ +schema Name0: + name: Name1 + +schema Name1: + name: str + +schema Person: + name: Name0 + gender: str = "default gender" + name.name.name = "rename" + +alice = Person { + "name" = { + "name": { + "name": "alice" + } + }, + "gender" = "female" +} diff --git a/test/grammar/schema/init/init_nested_schema_1/stdout.golden b/test/grammar/schema/init/init_nested_schema_1/stdout.golden new file mode 100644 index 000000000..788dbbed5 --- /dev/null +++ b/test/grammar/schema/init/init_nested_schema_1/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + name: + name: rename + gender: female \ No newline at end of file diff --git a/test/grammar/schema/init/init_nested_schema_2/main.k b/test/grammar/schema/init/init_nested_schema_2/main.k new file mode 100644 index 000000000..71f7663d5 --- /dev/null +++ b/test/grammar/schema/init/init_nested_schema_2/main.k @@ -0,0 +1,17 @@ +schema Config: + replicas: int = 3 + +schema Data: + config: Config = Config { + replicas = 1 + } + config.replicas = 4 + replicas: int = config.replicas + +data = Data {} +_config = Config { + replicas = 1 +} +_config.replicas = 4 +config = _config +replicas = _config.replicas diff --git a/test/grammar/schema/init/init_nested_schema_2/stdout.golden b/test/grammar/schema/init/init_nested_schema_2/stdout.golden new file mode 100644 index 000000000..0b11a7caf --- /dev/null +++ b/test/grammar/schema/init/init_nested_schema_2/stdout.golden @@ -0,0 +1,7 @@ +data: + config: + replicas: 4 + replicas: 4 +config: + replicas: 4 +replicas: 4 diff --git a/test/grammar/schema/init/init_option_0/main.k b/test/grammar/schema/init/init_option_0/main.k new file mode 100644 index 000000000..aefe894ee --- /dev/null +++ b/test/grammar/schema/init/init_option_0/main.k @@ -0,0 +1,10 @@ +schema Person: + name: str + age: int + tagged: bool + +alice = Person { + "name": option("name"), + "age": int(option("age")), + "tagged": bool(option("tagged")) +} diff --git a/test/grammar/schema/init/init_option_0/settings.yaml b/test/grammar/schema/init/init_option_0/settings.yaml new file mode 100644 index 000000000..779148ec7 --- /dev/null +++ b/test/grammar/schema/init/init_option_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D name=alice -D age=10 -D tagged=True diff --git a/test/grammar/schema/init/init_option_0/stdout.golden b/test/grammar/schema/init/init_option_0/stdout.golden new file mode 100644 index 000000000..8ee75670f --- /dev/null +++ b/test/grammar/schema/init/init_option_0/stdout.golden @@ -0,0 +1,4 @@ +alice: + name: alice + age: 10 + tagged: true \ No newline at end of file diff --git a/test/grammar/schema/init/init_option_1/main.k b/test/grammar/schema/init/init_option_1/main.k new file mode 100644 index 000000000..64903d100 --- /dev/null +++ b/test/grammar/schema/init/init_option_1/main.k @@ -0,0 +1,14 @@ +schema Person: + name: str + age: int + tagged: bool + +_name = option("name") +_age = int(option("age")) +_tagged = bool(option("tagged")) + +alice = Person { + "name": _name, + "age": _age, + "tagged": _tagged +} diff --git a/test/grammar/schema/init/init_option_1/settings.yaml b/test/grammar/schema/init/init_option_1/settings.yaml new file mode 100644 index 000000000..779148ec7 --- /dev/null +++ b/test/grammar/schema/init/init_option_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: -D name=alice -D age=10 -D tagged=True diff --git a/test/grammar/schema/init/init_option_1/stdout.golden b/test/grammar/schema/init/init_option_1/stdout.golden new file mode 100644 index 000000000..8ee75670f --- /dev/null +++ b/test/grammar/schema/init/init_option_1/stdout.golden @@ -0,0 +1,4 @@ +alice: + name: alice + age: 10 + tagged: true \ No newline at end of file diff --git a/test/grammar/schema/init/init_outside_pkg_var_0/kcl.mod b/test/grammar/schema/init/init_outside_pkg_var_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/init/init_outside_pkg_var_0/main.k b/test/grammar/schema/init/init_outside_pkg_var_0/main.k new file mode 100644 index 000000000..df9ebddcd --- /dev/null +++ b/test/grammar/schema/init/init_outside_pkg_var_0/main.k @@ -0,0 +1,6 @@ +import pkg + +schema A: + data: {str:} = pkg.ConstMap + +a = A {} diff --git a/test/grammar/schema/init/init_outside_pkg_var_0/pkg/map.k b/test/grammar/schema/init/init_outside_pkg_var_0/pkg/map.k new file mode 100644 index 000000000..c6b3238ae --- /dev/null +++ b/test/grammar/schema/init/init_outside_pkg_var_0/pkg/map.k @@ -0,0 +1,4 @@ +ConstMap = { + "key1": "value1", + "key2": "value2", +} diff --git a/test/grammar/schema/init/init_outside_pkg_var_0/stdout.golden b/test/grammar/schema/init/init_outside_pkg_var_0/stdout.golden new file mode 100644 index 000000000..c723f1421 --- /dev/null +++ b/test/grammar/schema/init/init_outside_pkg_var_0/stdout.golden @@ -0,0 +1,4 @@ +a: + data: + key1: value1 + key2: value2 \ No newline at end of file diff --git a/test/grammar/schema/init/init_outside_pkg_var_1/kcl.mod b/test/grammar/schema/init/init_outside_pkg_var_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/init/init_outside_pkg_var_1/main.k b/test/grammar/schema/init/init_outside_pkg_var_1/main.k new file mode 100644 index 000000000..6f89dcca3 --- /dev/null +++ b/test/grammar/schema/init/init_outside_pkg_var_1/main.k @@ -0,0 +1,8 @@ +import pkg + +_my_map = pkg.ConstMap + +schema A: + data: {str:} = _my_map + +a = A {} diff --git a/test/grammar/schema/init/init_outside_pkg_var_1/pkg/map.k b/test/grammar/schema/init/init_outside_pkg_var_1/pkg/map.k new file mode 100644 index 000000000..c6b3238ae --- /dev/null +++ b/test/grammar/schema/init/init_outside_pkg_var_1/pkg/map.k @@ -0,0 +1,4 @@ +ConstMap = { + "key1": "value1", + "key2": "value2", +} diff --git a/test/grammar/schema/init/init_outside_pkg_var_1/stdout.golden b/test/grammar/schema/init/init_outside_pkg_var_1/stdout.golden new file mode 100644 index 000000000..c723f1421 --- /dev/null +++ b/test/grammar/schema/init/init_outside_pkg_var_1/stdout.golden @@ -0,0 +1,4 @@ +a: + data: + key1: value1 + key2: value2 \ No newline at end of file diff --git a/test/grammar/schema/init/init_outside_var_0/main.k b/test/grammar/schema/init/init_outside_var_0/main.k new file mode 100644 index 000000000..edc141c31 --- /dev/null +++ b/test/grammar/schema/init/init_outside_var_0/main.k @@ -0,0 +1,8 @@ +_name_private = "Alice" +name_public = "Alice" + +schema Person: + name: str = name_public + name = _name_private.lower() + +alice = Person {} diff --git a/test/grammar/schema/init/init_outside_var_0/stdout.golden b/test/grammar/schema/init/init_outside_var_0/stdout.golden new file mode 100644 index 000000000..13c4bf373 --- /dev/null +++ b/test/grammar/schema/init/init_outside_var_0/stdout.golden @@ -0,0 +1,3 @@ +name_public: Alice +alice: + name: alice \ No newline at end of file diff --git a/test/grammar/schema/init/init_outside_var_1/main.k b/test/grammar/schema/init/init_outside_var_1/main.k new file mode 100644 index 000000000..187fc305a --- /dev/null +++ b/test/grammar/schema/init/init_outside_var_1/main.k @@ -0,0 +1,12 @@ +patchlist = [1, 2] +data = 1 + +schema Person: + name: str + list: [int] = [4, 5, 6] + val: int = 2 + val = data + list |= patchlist + name = "Alice" + +person_alice = Person {} diff --git a/test/grammar/schema/init/init_outside_var_1/stdout.golden b/test/grammar/schema/init/init_outside_var_1/stdout.golden new file mode 100644 index 000000000..7b7b0ae70 --- /dev/null +++ b/test/grammar/schema/init/init_outside_var_1/stdout.golden @@ -0,0 +1,11 @@ +patchlist: +- 1 +- 2 +data: 1 +person_alice: + name: Alice + list: + - 1 + - 2 + - 6 + val: 1 diff --git a/test/grammar/schema/init/init_patch_0/main.k b/test/grammar/schema/init/init_patch_0/main.k new file mode 100644 index 000000000..5922f9a81 --- /dev/null +++ b/test/grammar/schema/init/init_patch_0/main.k @@ -0,0 +1,17 @@ +schema Name: + firstName?: str + lastName?: str + +schema Person: + name: Name + name: Name |= { + "lastName": "Dow" + } + gender: str = "default gender" + +alice = Person { + "name": { + "firstName": "John" + }, + "gender" = "female" +} diff --git a/test/grammar/schema/init/init_patch_0/stdout.golden b/test/grammar/schema/init/init_patch_0/stdout.golden new file mode 100644 index 000000000..9b57c1073 --- /dev/null +++ b/test/grammar/schema/init/init_patch_0/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + firstName: John + lastName: Dow + gender: female \ No newline at end of file diff --git a/test/grammar/schema/init/init_schema_0/main.k b/test/grammar/schema/init/init_schema_0/main.k new file mode 100644 index 000000000..496c10acb --- /dev/null +++ b/test/grammar/schema/init/init_schema_0/main.k @@ -0,0 +1,15 @@ +schema Name: + name: str + +schema Person: + name: Name = Name { + "name": "default name" + } + gender: str = "default gender" + +alice = Person { + "name" = { + "name": "alice" + }, + "gender" = "female" +} diff --git a/test/grammar/schema/init/init_schema_0/stdout.golden b/test/grammar/schema/init/init_schema_0/stdout.golden new file mode 100644 index 000000000..91562aa33 --- /dev/null +++ b/test/grammar/schema/init/init_schema_0/stdout.golden @@ -0,0 +1,4 @@ +alice: + name: + name: alice + gender: female \ No newline at end of file diff --git a/test/grammar/schema/init/init_schema_1/main.k b/test/grammar/schema/init/init_schema_1/main.k new file mode 100644 index 000000000..ce6d0345e --- /dev/null +++ b/test/grammar/schema/init/init_schema_1/main.k @@ -0,0 +1,15 @@ +schema Inner: + data?: {str:str} + +schema Person: + inner?: {str:str} = None + innerModel?: Inner = Inner { + "data": { + "key": "value" + } + } + name?: str + +JohnDoe = Person { + "name": "Joe" +} diff --git a/test/grammar/schema/init/init_schema_1/stdout.golden b/test/grammar/schema/init/init_schema_1/stdout.golden new file mode 100644 index 000000000..0369b72ae --- /dev/null +++ b/test/grammar/schema/init/init_schema_1/stdout.golden @@ -0,0 +1,6 @@ +JohnDoe: + inner: null + innerModel: + data: + key: value + name: Joe \ No newline at end of file diff --git a/test/grammar/schema/init/init_schema_2/main.k b/test/grammar/schema/init/init_schema_2/main.k new file mode 100644 index 000000000..97c559262 --- /dev/null +++ b/test/grammar/schema/init/init_schema_2/main.k @@ -0,0 +1,19 @@ +schema Inner: + data: {str:str} + +schema Person: + inner: {str:str} + innerModel: Inner + name: str + + if inner: + innerModel = Inner { + "data": inner + } + +JohnDoe = Person { + "name": "Joe", + "inner": { + "key": "value0" + } +} diff --git a/test/grammar/schema/init/init_schema_2/stdout.golden b/test/grammar/schema/init/init_schema_2/stdout.golden new file mode 100644 index 000000000..e3c575f70 --- /dev/null +++ b/test/grammar/schema/init/init_schema_2/stdout.golden @@ -0,0 +1,7 @@ +JohnDoe: + inner: + key: value0 + innerModel: + data: + key: value0 + name: Joe \ No newline at end of file diff --git a/test/grammar/schema/init/init_schema_3/main.k b/test/grammar/schema/init/init_schema_3/main.k new file mode 100644 index 000000000..4cd8d513e --- /dev/null +++ b/test/grammar/schema/init/init_schema_3/main.k @@ -0,0 +1,10 @@ +schema Person: + name: Name = {"first": "alice"} + age: int + +schema Name: + first: str + +alice = Person { + "age": 10 +} diff --git a/test/grammar/schema/init/init_schema_3/stdout.golden b/test/grammar/schema/init/init_schema_3/stdout.golden new file mode 100644 index 000000000..05b0e7d17 --- /dev/null +++ b/test/grammar/schema/init/init_schema_3/stdout.golden @@ -0,0 +1,4 @@ +alice: + name: + first: alice + age: 10 diff --git a/test/grammar/schema/init/init_schema_4/kcl.mod b/test/grammar/schema/init/init_schema_4/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/init/init_schema_4/main.k b/test/grammar/schema/init/init_schema_4/main.k new file mode 100644 index 000000000..ea10828e2 --- /dev/null +++ b/test/grammar/schema/init/init_schema_4/main.k @@ -0,0 +1,18 @@ +import .pkg.inner as pkg_inner + +schema Person: + inner: {str:str} + innerModel: pkg_inner.Inner + name: str + + if inner: + innerModel = pkg_inner.Inner { + "data": inner + } + +JohnDoe = Person { + "name": "Joe", + "inner": { + "key": "value0" + } +} diff --git a/test/grammar/schema/init/init_schema_4/pkg/inner.k b/test/grammar/schema/init/init_schema_4/pkg/inner.k new file mode 100644 index 000000000..c7ddb66e3 --- /dev/null +++ b/test/grammar/schema/init/init_schema_4/pkg/inner.k @@ -0,0 +1,2 @@ +schema Inner: + data: {str:str} diff --git a/test/grammar/schema/init/init_schema_4/stdout.golden b/test/grammar/schema/init/init_schema_4/stdout.golden new file mode 100644 index 000000000..11d3a4223 --- /dev/null +++ b/test/grammar/schema/init/init_schema_4/stdout.golden @@ -0,0 +1,7 @@ +JohnDoe: + inner: + key: value0 + innerModel: + data: + key: value0 + name: Joe diff --git a/test/grammar/schema/init/init_schema_5/kcl.mod b/test/grammar/schema/init/init_schema_5/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/init/init_schema_5/main.k b/test/grammar/schema/init/init_schema_5/main.k new file mode 100644 index 000000000..96d4b4697 --- /dev/null +++ b/test/grammar/schema/init/init_schema_5/main.k @@ -0,0 +1,18 @@ +import .pkg.v1 + +schema Person: + inner: {str:str} + innerModel: v1.Inner + name: str + + if inner: + innerModel = v1.Inner { + "data": inner + } + +JohnDoe = Person { + "name": "Joe", + "inner": { + "key": "value0" + } +} diff --git a/test/grammar/schema/init/init_schema_5/pkg/v1/inner.k b/test/grammar/schema/init/init_schema_5/pkg/v1/inner.k new file mode 100644 index 000000000..c7ddb66e3 --- /dev/null +++ b/test/grammar/schema/init/init_schema_5/pkg/v1/inner.k @@ -0,0 +1,2 @@ +schema Inner: + data: {str:str} diff --git a/test/grammar/schema/init/init_schema_5/stdout.golden b/test/grammar/schema/init/init_schema_5/stdout.golden new file mode 100644 index 000000000..e3c575f70 --- /dev/null +++ b/test/grammar/schema/init/init_schema_5/stdout.golden @@ -0,0 +1,7 @@ +JohnDoe: + inner: + key: value0 + innerModel: + data: + key: value0 + name: Joe \ No newline at end of file diff --git a/test/grammar/schema/init/init_schema_fail_0/main.k b/test/grammar/schema/init/init_schema_fail_0/main.k new file mode 100644 index 000000000..6b568946d --- /dev/null +++ b/test/grammar/schema/init/init_schema_fail_0/main.k @@ -0,0 +1,18 @@ +schema Name: + name: str + +schema Name0: + name: str + +schema Person: + name: Name = Name0 { + "name": "default" + } + gender: str = "default" + +alice = Person { + "name": { + "name": "alice" + }, + "gender": "female" +} diff --git a/test/grammar/schema/init/init_schema_fail_0/stderr.golden.py b/test/grammar/schema/init/init_schema_fail_0/stderr.golden.py new file mode 100644 index 000000000..2e6182da6 --- /dev/null +++ b/test/grammar/schema/init/init_schema_fail_0/stderr.golden.py @@ -0,0 +1,22 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=8, + col_no=5, + arg_msg="got Name0" + ) + ], + arg_msg="expect Name, got Name0" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/instances/complex/complex_0/backend.k b/test/grammar/schema/instances/complex/complex_0/backend.k new file mode 100644 index 000000000..4e1c078f3 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_0/backend.k @@ -0,0 +1,9 @@ +schema Backend: + name?: str + metadata?: {str:} + +backends = [Backend { + name: model.name + metadata.labels: model.labels + metadata.annotations: model.annotations +} for model in Model.instances()] diff --git a/test/grammar/schema/instances/complex/complex_0/main.k b/test/grammar/schema/instances/complex/complex_0/main.k new file mode 100644 index 000000000..f2c88af1c --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_0/main.k @@ -0,0 +1,16 @@ +schema Model: + name?: str + labels?: {str:} = None + annotations?: {str:} = None + +model1 = Model { + name: "model1" + labels.key1: "value1" + labels.key2: "value2" + annotations.key: "value" +} + +model2 = Model { + name: "model2" + labels.key: "value" +} diff --git a/test/grammar/schema/instances/complex/complex_0/settings.yaml b/test/grammar/schema/instances/complex/complex_0/settings.yaml new file mode 100644 index 000000000..9f5f2abb9 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: backend.k \ No newline at end of file diff --git a/test/grammar/schema/instances/complex/complex_0/stdout.golden b/test/grammar/schema/instances/complex/complex_0/stdout.golden new file mode 100644 index 000000000..93885805b --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_0/stdout.golden @@ -0,0 +1,25 @@ +model1: + name: model1 + labels: + key1: value1 + key2: value2 + annotations: + key: value +model2: + name: model2 + labels: + key: value + annotations: null +backends: +- name: model1 + metadata: + labels: + key1: value1 + key2: value2 + annotations: + key: value +- name: model2 + metadata: + labels: + key: value + annotations: null diff --git a/test/grammar/schema/instances/complex/complex_1/backend.k b/test/grammar/schema/instances/complex/complex_1/backend.k new file mode 100644 index 000000000..7f07ff4b4 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_1/backend.k @@ -0,0 +1,6 @@ +schema Backend: + name?: str + metadata?: {str:} + +backendsFirst = Model.instances()[0] +backendsSecond = Model.instances()[1] diff --git a/test/grammar/schema/instances/complex/complex_1/main.k b/test/grammar/schema/instances/complex/complex_1/main.k new file mode 100644 index 000000000..408c02604 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_1/main.k @@ -0,0 +1,15 @@ +schema Model: + name?: str + labels?: {str:} = None + annotations?: {str:} = None + +model1 = Model { + name: "model1" + labels.key1: "value1" + labels.key2: "value2" + annotations.key: "value" +} + +model2 = Model { + name: "model2" +} diff --git a/test/grammar/schema/instances/complex/complex_1/settings.yaml b/test/grammar/schema/instances/complex/complex_1/settings.yaml new file mode 100644 index 000000000..9f5f2abb9 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: backend.k \ No newline at end of file diff --git a/test/grammar/schema/instances/complex/complex_1/stdout.golden b/test/grammar/schema/instances/complex/complex_1/stdout.golden new file mode 100644 index 000000000..909cb6b7c --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_1/stdout.golden @@ -0,0 +1,22 @@ +model1: + name: model1 + labels: + key1: value1 + key2: value2 + annotations: + key: value +model2: + name: model2 + labels: null + annotations: null +backendsFirst: + name: model1 + labels: + key1: value1 + key2: value2 + annotations: + key: value +backendsSecond: + name: model2 + labels: null + annotations: null diff --git a/test/grammar/schema/instances/complex/complex_2/backend.k b/test/grammar/schema/instances/complex/complex_2/backend.k new file mode 100644 index 000000000..24e902f04 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_2/backend.k @@ -0,0 +1,24 @@ +schema Backend: + __settings__: {str:str} = {"output_type": "STANDALONE"} + """ existence of this attribute indicates that the model will be treated standalone by KCLVM. + """ + + apiVersion?: str = "v1" + kind?: str = "Deployment" + metadata?: {str:} + spec?: {str:} = { + "minReadySeconds" = 0 + "paused" = False + "progressDeadlineSeconds" = 600 + "replicas" = 1 + "revisionHistoryLimit" = 10 + "selector": {} + } + +backends = [Backend { + metadata.name: model.name + metadata.labels: model.labels + metadata.annotations: model.annotations + spec.selector.matchLabels: model.labels + spec.replicas = model.replicas +} for model in Model.instances()] diff --git a/test/grammar/schema/instances/complex/complex_2/main.k b/test/grammar/schema/instances/complex/complex_2/main.k new file mode 100644 index 000000000..1e4774b1a --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_2/main.k @@ -0,0 +1,18 @@ +schema Model: + name?: str + labels?: {str:} = None + annotations?: {str:} = None + replicas?: int + +_model1 = Model { + name: "model1" + labels.key1: "value1" + labels.key2: "value2" + annotations.key: "value" + replicas = 2 +} + +_model2 = Model { + name: "model2" + replicas = 3 +} diff --git a/test/grammar/schema/instances/complex/complex_2/settings.yaml b/test/grammar/schema/instances/complex/complex_2/settings.yaml new file mode 100644 index 000000000..9f5f2abb9 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: backend.k \ No newline at end of file diff --git a/test/grammar/schema/instances/complex/complex_2/stdout.golden b/test/grammar/schema/instances/complex/complex_2/stdout.golden new file mode 100644 index 000000000..e8f43377d --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_2/stdout.golden @@ -0,0 +1,34 @@ +apiVersion: v1 +kind: Deployment +metadata: + name: model1 + labels: + key1: value1 + key2: value2 + annotations: + key: value +spec: + minReadySeconds: 0 + paused: false + progressDeadlineSeconds: 600 + replicas: 2 + revisionHistoryLimit: 10 + selector: + matchLabels: + key1: value1 + key2: value2 +--- +apiVersion: v1 +kind: Deployment +metadata: + name: model2 + labels: null + annotations: null +spec: + minReadySeconds: 0 + paused: false + progressDeadlineSeconds: 600 + replicas: 3 + revisionHistoryLimit: 10 + selector: + matchLabels: null diff --git a/test/grammar/schema/instances/complex/complex_3/main.k b/test/grammar/schema/instances/complex/complex_3/main.k new file mode 100644 index 000000000..3ba286dc3 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_3/main.k @@ -0,0 +1,14 @@ +schema Data: + id: int = 1 + +schema Person: + name: str = "kcl" + age: int = 1 + data: Data = Data {} + +x0 = Person {} +x1 = Person { + age = 101 +} +x2 = Person.instances() +x3 = Data.instances() diff --git a/test/grammar/schema/instances/complex/complex_3/stdout.golden b/test/grammar/schema/instances/complex/complex_3/stdout.golden new file mode 100644 index 000000000..eef443624 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_3/stdout.golden @@ -0,0 +1,22 @@ +x0: + name: kcl + age: 1 + data: + id: 1 +x1: + name: kcl + age: 101 + data: + id: 1 +x2: +- name: kcl + age: 1 + data: + id: 1 +- name: kcl + age: 101 + data: + id: 1 +x3: +- id: 1 +- id: 1 diff --git a/test/grammar/schema/instances/complex/complex_4/main.k b/test/grammar/schema/instances/complex/complex_4/main.k new file mode 100644 index 000000000..df5a44dd6 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_4/main.k @@ -0,0 +1,7 @@ +import pkg + +x0 = pkg.Person {} +x1 = pkg.Person { + age = 101 +} +x2 = pkg.Person.instances() diff --git a/test/grammar/schema/instances/complex/complex_4/pkg/pkg.k b/test/grammar/schema/instances/complex/complex_4/pkg/pkg.k new file mode 100644 index 000000000..19cb2bef2 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_4/pkg/pkg.k @@ -0,0 +1,7 @@ +schema Data: + id: int = 1 + +schema Person: + name: str = "kcl" + age: int = 1 + data: Data = Data {} diff --git a/test/grammar/schema/instances/complex/complex_4/stdout.golden b/test/grammar/schema/instances/complex/complex_4/stdout.golden new file mode 100644 index 000000000..90006bcb9 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_4/stdout.golden @@ -0,0 +1,19 @@ +x0: + name: kcl + age: 1 + data: + id: 1 +x1: + name: kcl + age: 101 + data: + id: 1 +x2: +- name: kcl + age: 1 + data: + id: 1 +- name: kcl + age: 101 + data: + id: 1 diff --git a/test/grammar/schema/instances/invalid/invalid_0/main.k b/test/grammar/schema/instances/invalid/invalid_0/main.k new file mode 100644 index 000000000..da355b2d9 --- /dev/null +++ b/test/grammar/schema/instances/invalid/invalid_0/main.k @@ -0,0 +1,6 @@ +schema Person: + name: str = "Alice" + age: int = 18 + +person = Person {} +count = PersonErr.instances() diff --git a/test/grammar/schema/instances/invalid/invalid_0/stderr.golden.py b/test/grammar/schema/instances/invalid/invalid_0/stderr.golden.py new file mode 100644 index 000000000..35766480c --- /dev/null +++ b/test/grammar/schema/instances/invalid/invalid_0/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + col_no=9 + ) + ], + arg_msg="name 'PersonErr' is not defined" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/instances/invalid/invalid_1/main.k b/test/grammar/schema/instances/invalid/invalid_1/main.k new file mode 100644 index 000000000..e75ed355c --- /dev/null +++ b/test/grammar/schema/instances/invalid/invalid_1/main.k @@ -0,0 +1,6 @@ +schema Person: + name: str = "Alice" + age: int = 18 + +person = Person {} +count = Person.err_instances() diff --git a/test/grammar/schema/instances/invalid/invalid_1/stderr.golden.py b/test/grammar/schema/instances/invalid/invalid_1/stderr.golden.py new file mode 100644 index 000000000..a4d4d336b --- /dev/null +++ b/test/grammar/schema/instances/invalid/invalid_1/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.AttributeError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + col_no=9 + ) + ], + arg_msg="schema 'Person' attribute 'err_instances' not found" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/instances/simple/simple_0/main.k b/test/grammar/schema/instances/simple/simple_0/main.k new file mode 100644 index 000000000..c631fa0c8 --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_0/main.k @@ -0,0 +1,15 @@ +schema Person: + name?: str + age?: int + +person1 = Person { + name: "Alice" + age: 18 +} + +person2 = Person { + name: "Bob" + age: 16 +} + +instances = Person.instances() diff --git a/test/grammar/schema/instances/simple/simple_0/stdout.golden b/test/grammar/schema/instances/simple/simple_0/stdout.golden new file mode 100644 index 000000000..b641466f7 --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_0/stdout.golden @@ -0,0 +1,11 @@ +person1: + name: Alice + age: 18 +person2: + name: Bob + age: 16 +instances: +- name: Alice + age: 18 +- name: Bob + age: 16 diff --git a/test/grammar/schema/instances/simple/simple_1/main.k b/test/grammar/schema/instances/simple/simple_1/main.k new file mode 100644 index 000000000..2220defe1 --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_1/main.k @@ -0,0 +1,13 @@ +schema Person: + name?: str + age?: int + +_persons = [Person { + name: "Alice" + age: 18 +}, Person { + name: "Bob" + age: 16 +}] + +instances = Person.instances() diff --git a/test/grammar/schema/instances/simple/simple_1/stdout.golden b/test/grammar/schema/instances/simple/simple_1/stdout.golden new file mode 100644 index 000000000..003790ccb --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_1/stdout.golden @@ -0,0 +1,5 @@ +instances: +- name: Alice + age: 18 +- name: Bob + age: 16 diff --git a/test/grammar/schema/instances/simple/simple_2/main.k b/test/grammar/schema/instances/simple/simple_2/main.k new file mode 100644 index 000000000..008b57213 --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_2/main.k @@ -0,0 +1,17 @@ +schema Person: + name?: str = None + age?: int = None + +_persons = [Person { + name: "Alice" + age: 18 +}, Person { + name: "Bob" + age: 16 +}] + +instancesLen1 = len(Person.instances()) + +person = Person {} + +instancesLen2 = len(Person.instances()) diff --git a/test/grammar/schema/instances/simple/simple_2/stdout.golden b/test/grammar/schema/instances/simple/simple_2/stdout.golden new file mode 100644 index 000000000..77276a96c --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_2/stdout.golden @@ -0,0 +1,5 @@ +instancesLen1: 2 +person: + name: null + age: null +instancesLen2: 3 diff --git a/test/grammar/schema/instances/simple/simple_3/main.k b/test/grammar/schema/instances/simple/simple_3/main.k new file mode 100644 index 000000000..28c51d57c --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_3/main.k @@ -0,0 +1,21 @@ +schema Base: + hc?: int = None + +schema Person(Base): + name?: str = None + age?: int = None + +_base = Base { + hc: 3 +} + +_persons = [Person { + name: "Alice" + age: 18 +}, Person { + name: "Bob" + age: 16 +}] + +baseInstance = Base.instances() +personInstanceLen = len(Person.instances()) diff --git a/test/grammar/schema/instances/simple/simple_3/stdout.golden b/test/grammar/schema/instances/simple/simple_3/stdout.golden new file mode 100644 index 000000000..3a0a3ef67 --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_3/stdout.golden @@ -0,0 +1,9 @@ +baseInstance: +- hc: 3 +- hc: null + name: Alice + age: 18 +- hc: null + name: Bob + age: 16 +personInstanceLen: 2 diff --git a/test/grammar/schema/instances/simple/simple_4/kcl.mod b/test/grammar/schema/instances/simple/simple_4/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/instances/simple/simple_4/main.k b/test/grammar/schema/instances/simple/simple_4/main.k new file mode 100644 index 000000000..87f01b9ab --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_4/main.k @@ -0,0 +1,4 @@ +import pkg + +person = pkg.Person {} +personInstance = pkg.Person.instances()[0] diff --git a/test/grammar/schema/instances/simple/simple_4/pkg/person.k b/test/grammar/schema/instances/simple/simple_4/pkg/person.k new file mode 100644 index 000000000..d3ee02503 --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_4/pkg/person.k @@ -0,0 +1,3 @@ +schema Person: + name?: str = "Alice" + age?: int = 18 diff --git a/test/grammar/schema/instances/simple/simple_4/stdout.golden b/test/grammar/schema/instances/simple/simple_4/stdout.golden new file mode 100644 index 000000000..66b6aa747 --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_4/stdout.golden @@ -0,0 +1,6 @@ +person: + name: Alice + age: 18 +personInstance: + name: Alice + age: 18 diff --git a/test/grammar/schema/instances/simple/simple_5/kcl.mod b/test/grammar/schema/instances/simple/simple_5/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/instances/simple/simple_5/main.k b/test/grammar/schema/instances/simple/simple_5/main.k new file mode 100644 index 000000000..411175061 --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_5/main.k @@ -0,0 +1,10 @@ +import pkg + +schema Person(pkg.Person): + hc?: int = None + +personPkg = pkg.Person {} +person = Person {} + +personPkgInstanceCount = len(pkg.Person.instances()) +personInstanceCount = len(Person.instances()) diff --git a/test/grammar/schema/instances/simple/simple_5/pkg/person.k b/test/grammar/schema/instances/simple/simple_5/pkg/person.k new file mode 100644 index 000000000..d3ee02503 --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_5/pkg/person.k @@ -0,0 +1,3 @@ +schema Person: + name?: str = "Alice" + age?: int = 18 diff --git a/test/grammar/schema/instances/simple/simple_5/stdout.golden b/test/grammar/schema/instances/simple/simple_5/stdout.golden new file mode 100644 index 000000000..75244f69d --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_5/stdout.golden @@ -0,0 +1,9 @@ +personPkg: + name: Alice + age: 18 +person: + name: Alice + age: 18 + hc: null +personPkgInstanceCount: 2 +personInstanceCount: 1 diff --git a/test/grammar/schema/instances/simple/simple_6/kcl.mod b/test/grammar/schema/instances/simple/simple_6/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/instances/simple/simple_6/main.k b/test/grammar/schema/instances/simple/simple_6/main.k new file mode 100644 index 000000000..fc9a2941a --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_6/main.k @@ -0,0 +1,10 @@ +import pkg + +schema Person(pkg.Person): + hc?: int = None + +personPkg = pkg.Person {} +person = Person {} + +instancesTypeofStr = [typeof(inst) for inst in pkg.Person.instances()] +instancesFullTypeofStr = [typeof(inst, full_name=True) for inst in pkg.Person.instances()] diff --git a/test/grammar/schema/instances/simple/simple_6/pkg/person.k b/test/grammar/schema/instances/simple/simple_6/pkg/person.k new file mode 100644 index 000000000..d3ee02503 --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_6/pkg/person.k @@ -0,0 +1,3 @@ +schema Person: + name?: str = "Alice" + age?: int = 18 diff --git a/test/grammar/schema/instances/simple/simple_6/stdout.golden b/test/grammar/schema/instances/simple/simple_6/stdout.golden new file mode 100644 index 000000000..f36422c36 --- /dev/null +++ b/test/grammar/schema/instances/simple/simple_6/stdout.golden @@ -0,0 +1,13 @@ +personPkg: + name: Alice + age: 18 +person: + name: Alice + age: 18 + hc: null +instancesTypeofStr: +- Person +- Person +instancesFullTypeofStr: +- pkg.Person +- Person diff --git a/test/grammar/schema/invalid/add_attribute/main.k b/test/grammar/schema/invalid/add_attribute/main.k new file mode 100644 index 000000000..d1fc16f16 --- /dev/null +++ b/test/grammar/schema/invalid/add_attribute/main.k @@ -0,0 +1,13 @@ +schema Person: + first: str + last: str + age: int + +schema Girl: + gender: str = "female" + +alice = Girl { + "first": "alice", + "last": " Green", + "age": 10 +} diff --git a/test/grammar/schema/invalid/add_attribute/stderr.golden.py b/test/grammar/schema/invalid/add_attribute/stderr.golden.py new file mode 100644 index 000000000..5dcf7fe3d --- /dev/null +++ b/test/grammar/schema/invalid/add_attribute/stderr.golden.py @@ -0,0 +1,22 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CannotAddMembers_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=5, + arg_msg="'first' is not defined in schema 'Girl'" + ) + ], + arg_msg="Cannot add member 'first' to schema 'Girl'" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/invalid/change_field/main.k b/test/grammar/schema/invalid/change_field/main.k new file mode 100644 index 000000000..763c687e7 --- /dev/null +++ b/test/grammar/schema/invalid/change_field/main.k @@ -0,0 +1,10 @@ +schema Person: + firstName: str = "John" + lastName: str + +JohnDoe = Person { + "lastName": "Doe" +} + +JohnDoe.lastName = "John0" + diff --git a/test/grammar/schema/invalid/change_field/stderr.golden.py b/test/grammar/schema/invalid/change_field/stderr.golden.py new file mode 100644 index 000000000..59e12ea97 --- /dev/null +++ b/test/grammar/schema/invalid/change_field/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=9, + col_no=1, + end_col_no=17 + ) + ], + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/invalid/no_schema/main.k b/test/grammar/schema/invalid/no_schema/main.k new file mode 100644 index 000000000..c29a52ef4 --- /dev/null +++ b/test/grammar/schema/invalid/no_schema/main.k @@ -0,0 +1,3 @@ +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/invalid/no_schema/stderr.golden.py b/test/grammar/schema/invalid/no_schema/stderr.golden.py new file mode 100644 index 000000000..5b2fddce2 --- /dev/null +++ b/test/grammar/schema/invalid/no_schema/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=11 + ) + ], + arg_msg="name 'Person' is not defined" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/irrelevant_order/complex_0/main.k b/test/grammar/schema/irrelevant_order/complex_0/main.k new file mode 100644 index 000000000..782c0f554 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/complex_0/main.k @@ -0,0 +1,28 @@ +import math + +schema Base: + mixin [BaseMixin] + base_templating: str = "base" + +schema Sub[arg](Base): + mixin [SubMixin] + a: int = b + c + e + b: int = 2 + c: int = 3 + e: int + f: int + c = 2 + c = 4 + d = 4 + if d: + e = 2 + f = 3 + sub_templating: str = "sub" + +schema BaseMixin: + base_mixin: str = "base" + +schema SubMixin: + sub_mixin: str = "sub" + +data = Sub(1) {} diff --git a/test/grammar/schema/irrelevant_order/complex_0/stdout.golden b/test/grammar/schema/irrelevant_order/complex_0/stdout.golden new file mode 100644 index 000000000..963bf7595 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/complex_0/stdout.golden @@ -0,0 +1,11 @@ +data: + base_templating: base + base_mixin: base + a: 8 + b: 2 + c: 4 + e: 2 + f: 3 + d: 4 + sub_templating: sub + sub_mixin: sub diff --git a/test/grammar/schema/irrelevant_order/complex_1/main.k b/test/grammar/schema/irrelevant_order/complex_1/main.k new file mode 100644 index 000000000..65444ef81 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/complex_1/main.k @@ -0,0 +1,32 @@ + +import math + +schema Base: + mixin [BaseMixin] + base_templating: str = "base" + +schema Sub[arg](Base): + mixin [SubMixin] + # print("the value of a is", a) + a: int = b + c + e + b: int = 2 + _c = 3 + _c = 2 + _c = 4 + c: int = _c + e: int = _e + f: int = _f + d = 4 + if d: + _e = 2 + _f = 3 + # print("the value of d is", d) + sub_templating: str = "sub" + +schema BaseMixin: + base_mixin: str = "base" + +schema SubMixin: + sub_mixin: str = "sub" + +data = Sub(1) {} diff --git a/test/grammar/schema/irrelevant_order/complex_1/stdout.golden b/test/grammar/schema/irrelevant_order/complex_1/stdout.golden new file mode 100644 index 000000000..963bf7595 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/complex_1/stdout.golden @@ -0,0 +1,11 @@ +data: + base_templating: base + base_mixin: base + a: 8 + b: 2 + c: 4 + e: 2 + f: 3 + d: 4 + sub_templating: sub + sub_mixin: sub diff --git a/test/grammar/schema/irrelevant_order/complex_2/main.k b/test/grammar/schema/irrelevant_order/complex_2/main.k new file mode 100644 index 000000000..e317c61ff --- /dev/null +++ b/test/grammar/schema/irrelevant_order/complex_2/main.k @@ -0,0 +1,43 @@ +schema KubeNamespace: + __settings__: {str:str} = {"output_type": "STANDALONE"} + apiVersion: str = "v1" + kind: str = "Namespace" + metadata: {str:} + spec: {str:} + +schema MetadataMixin: + metadata: {str:} = { + "name": name, + "annotations": annotations, + "namespace": namespace, + "labels": labels + } + +schema NamespaceMixin: + _spec: {str:} = {"finalizers": finalizers} if finalizers else None + kubeNamespace: KubeNamespace = KubeNamespace { + metadata: metadata, + spec: _spec + } + +schema Metadata: + mixin [MetadataMixin] + + name: str + labels?: {str:str} + annotations?: {str:str} + namespace?: str + +schema Namespace(Metadata): + mixin [NamespaceMixin] + + name: str = "prod-frontend" + finalizers: [str] + + __settings__: {str:} = { + "output_type": "IGNORE" + } + +node = Namespace { + finalizers: ["test"] +} diff --git a/test/grammar/schema/irrelevant_order/complex_2/settings.yaml b/test/grammar/schema/irrelevant_order/complex_2/settings.yaml new file mode 100644 index 000000000..6dcdd25b7 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/complex_2/settings.yaml @@ -0,0 +1 @@ +kcl_options: -n diff --git a/test/grammar/schema/irrelevant_order/complex_2/stdout.golden b/test/grammar/schema/irrelevant_order/complex_2/stdout.golden new file mode 100644 index 000000000..588b0e586 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/complex_2/stdout.golden @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: prod-frontend +spec: + finalizers: + - test diff --git a/test/grammar/schema/irrelevant_order/complex_3/main.k b/test/grammar/schema/irrelevant_order/complex_3/main.k new file mode 100644 index 000000000..c4d5f3a37 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/complex_3/main.k @@ -0,0 +1,14 @@ +schema BaseServer: + name: str = option("name") + container: str + +schema ClassicServer(BaseServer): + container: str = name + +schema AppServer(ClassicServer): + globalApp: str = "app" + +schema UserServer(AppServer): + name = "user" + +app = UserServer {} diff --git a/test/grammar/schema/irrelevant_order/complex_3/stdout.golden b/test/grammar/schema/irrelevant_order/complex_3/stdout.golden new file mode 100644 index 000000000..79190146c --- /dev/null +++ b/test/grammar/schema/irrelevant_order/complex_3/stdout.golden @@ -0,0 +1,4 @@ +app: + name: user + container: user + globalApp: app diff --git a/test/grammar/schema/irrelevant_order/complex_4/main.k b/test/grammar/schema/irrelevant_order/complex_4/main.k new file mode 100644 index 000000000..51cfb601c --- /dev/null +++ b/test/grammar/schema/irrelevant_order/complex_4/main.k @@ -0,0 +1,44 @@ +schema KubeAction: + __settings__: {str:str} = {"output_type": "STANDALONE"} + apiVersion: str = "kusion/v1" + kind: str = "Action" + metadata: {str:} + spec: {str:} + +schema Action: + __settings__: {str:str} = {"output_type": "IGNORE"} + name: str + pod: str + namespace: str + cluster: str + type: str + + kubeAction: KubeAction = KubeAction { + metadata.name: name + spec: { + pod: pod, + namespace: namespace, + cluster: cluster, + type: type + } + } + + +schema KeyValueUpdateAction(Action): + keyValues: {str:} + query: {str:} + # kubeAction: KubeAction + pod = "podname" + namespace = "namespace" + cluster: str = "cluster" + type: str = "pods" + name: str = "pod_key_value_update" + + kubeAction.spec |= { + "keyValues": keyValues + } + +act = KeyValueUpdateAction() { + query.hostnames: ["foo.bar"] + keyValues.key: "val" +} diff --git a/test/grammar/schema/irrelevant_order/complex_4/stdout.golden b/test/grammar/schema/irrelevant_order/complex_4/stdout.golden new file mode 100644 index 000000000..d10ccc39a --- /dev/null +++ b/test/grammar/schema/irrelevant_order/complex_4/stdout.golden @@ -0,0 +1,11 @@ +apiVersion: kusion/v1 +kind: Action +metadata: + name: pod_key_value_update +spec: + pod: podname + namespace: namespace + cluster: cluster + type: pods + keyValues: + key: val diff --git a/test/grammar/schema/irrelevant_order/for_0/main.k b/test/grammar/schema/irrelevant_order/for_0/main.k new file mode 100644 index 000000000..74083b106 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/for_0/main.k @@ -0,0 +1,6 @@ +schema Data: + dataTrue: [int] = [k * e for k in data] if e else Undefined + data = [1, 2, 3] + e = 2 + +data = Data {} diff --git a/test/grammar/schema/irrelevant_order/for_0/stdout.golden b/test/grammar/schema/irrelevant_order/for_0/stdout.golden new file mode 100644 index 000000000..3b246fd90 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/for_0/stdout.golden @@ -0,0 +1,10 @@ +data: + dataTrue: + - 2 + - 4 + - 6 + data: + - 1 + - 2 + - 3 + e: 2 diff --git a/test/grammar/schema/irrelevant_order/for_1/main.k b/test/grammar/schema/irrelevant_order/for_1/main.k new file mode 100644 index 000000000..eec163058 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/for_1/main.k @@ -0,0 +1,14 @@ +schema Data: + a = 1 + e = 2 + if a == 1: + b = c + d = c + 1 + e + keys = [k for k in data] + data = { + "key1": "value1" + "key2": c + } + c = a + 1 * 2 + +data = Data {} diff --git a/test/grammar/schema/irrelevant_order/for_1/stdout.golden b/test/grammar/schema/irrelevant_order/for_1/stdout.golden new file mode 100644 index 000000000..2d258bc3a --- /dev/null +++ b/test/grammar/schema/irrelevant_order/for_1/stdout.golden @@ -0,0 +1,12 @@ +data: + a: 1 + e: 2 + b: 3 + d: 6 + keys: + - key1 + - key2 + data: + key1: value1 + key2: 3 + c: 3 diff --git a/test/grammar/schema/irrelevant_order/for_2/main.k b/test/grammar/schema/irrelevant_order/for_2/main.k new file mode 100644 index 000000000..563148c5b --- /dev/null +++ b/test/grammar/schema/irrelevant_order/for_2/main.k @@ -0,0 +1,11 @@ +schema Data: + keys = [k for k in data] + data: {str:str} = { + "key1": "value1" + } + data |= { + "key2": "value2" + } + dataOther = {**data} + +data = Data {} diff --git a/test/grammar/schema/irrelevant_order/for_2/stdout.golden b/test/grammar/schema/irrelevant_order/for_2/stdout.golden new file mode 100644 index 000000000..33363f055 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/for_2/stdout.golden @@ -0,0 +1,10 @@ +data: + keys: + - key1 + - key2 + data: + key1: value1 + key2: value2 + dataOther: + key1: value1 + key2: value2 diff --git a/test/grammar/schema/irrelevant_order/if_expr_0/main.k b/test/grammar/schema/irrelevant_order/if_expr_0/main.k new file mode 100644 index 000000000..9d9e1bcfb --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_expr_0/main.k @@ -0,0 +1,9 @@ +schema Person: + name: str = _name + age: int = _age or 18 + _name = "Alice" if True else Undefined + _age = 10 if False else Undefined + +person = Person { + name = "Overwrite" +} diff --git a/test/grammar/schema/irrelevant_order/if_expr_0/stdout.golden b/test/grammar/schema/irrelevant_order/if_expr_0/stdout.golden new file mode 100644 index 000000000..7e7205956 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_expr_0/stdout.golden @@ -0,0 +1,3 @@ +person: + name: Overwrite + age: 18 diff --git a/test/grammar/schema/irrelevant_order/if_expr_1/main.k b/test/grammar/schema/irrelevant_order/if_expr_1/main.k new file mode 100644 index 000000000..7417d4a7a --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_expr_1/main.k @@ -0,0 +1,20 @@ +schema FullNameMixin: + fullName: str = (firstName + ' ' + lastName) if _withFull else None + +schema Person[withFull]: + mixin [FullNameMixin] + + _withFull: bool = withFull + firstName: str = "John" + lastName: str + fullName: str + + mapping: {str:str} = { + "firstName": firstName + "lastName": lastName + "fullName": fullName + } + +JohnDoe = Person(True) { + lastName: "Doe" +} diff --git a/test/grammar/schema/irrelevant_order/if_expr_1/stdout.golden b/test/grammar/schema/irrelevant_order/if_expr_1/stdout.golden new file mode 100644 index 000000000..5cffb9ec6 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_expr_1/stdout.golden @@ -0,0 +1,8 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: John Doe + mapping: + firstName: John + lastName: Doe + fullName: John Doe diff --git a/test/grammar/schema/irrelevant_order/if_stmt_0/main.k b/test/grammar/schema/irrelevant_order/if_stmt_0/main.k new file mode 100644 index 000000000..47577f15a --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_0/main.k @@ -0,0 +1,13 @@ +schema Person: + name: str = _name + age: int = _age + if True: + _name = "Alice" + if False: + _age = 10 + else: + _age = 18 + +person = Person { + name = "Overwrite" +} diff --git a/test/grammar/schema/irrelevant_order/if_stmt_0/stdout.golden b/test/grammar/schema/irrelevant_order/if_stmt_0/stdout.golden new file mode 100644 index 000000000..7e7205956 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_0/stdout.golden @@ -0,0 +1,3 @@ +person: + name: Overwrite + age: 18 diff --git a/test/grammar/schema/irrelevant_order/if_stmt_1/main.k b/test/grammar/schema/irrelevant_order/if_stmt_1/main.k new file mode 100644 index 000000000..b7123d5da --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_1/main.k @@ -0,0 +1,21 @@ +schema FullNameMixin: + fullName: str + if _withFull: + fullName = firstName + ' ' + lastName + +schema Person[withFull]: + mixin [FullNameMixin] + + _withFull: bool = withFull + firstName: str = "John" + lastName: str + + mapping: {str:str} = { + "firstName": firstName + "lastName": lastName + "fullName": fullName + } + +JohnDoe = Person(True) { + lastName: "Doe" +} diff --git a/test/grammar/schema/irrelevant_order/if_stmt_1/stdout.golden b/test/grammar/schema/irrelevant_order/if_stmt_1/stdout.golden new file mode 100644 index 000000000..dfdadca9b --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_1/stdout.golden @@ -0,0 +1,8 @@ +JohnDoe: + firstName: John + lastName: Doe + mapping: + firstName: John + lastName: Doe + fullName: John Doe + fullName: John Doe diff --git a/test/grammar/schema/irrelevant_order/if_stmt_2/main.k b/test/grammar/schema/irrelevant_order/if_stmt_2/main.k new file mode 100644 index 000000000..f3daa98cc --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_2/main.k @@ -0,0 +1,99 @@ +schema Deployment: + [str]: any + +schema EnvMandatorySchedulingMixin: + """EnvMandatorySchedulingMixin""" + if data.envMandatory: + _envMandatorySchedulingTolerations = [ + { + "key": "app-env", + "operator" : "Equal", + "value" : "$env_type" + "effect" : "NoSchedule" + } + ] + _envMandatorySchedulingMatchExpressions = [ + { + "key": "app-env", + "operator": "In", + "values": "$env_type" + } + ] + +schema OverQuotaMixin: + """OverQuotaMixin""" + if data.overQuota: + _overQuotaTolerations = [ + { + "key": "cluster.k8s/is-over-quota", + "operator": "Equal", + "value": "true", + "effect": "NoSchedule" + } + ] + _overQuotaMatchExpressions = [ + { + "key": "cluster.k8s/is-over-quota", + "operator": "In", + "values": ["true"] + } + ] + +schema AppLogicPoolMixin: + """AppLogicPoolMixin""" + if data.appLogicPool: + _appLogicPoolTolerations = [ + { + "key": "mandatory.k8s/app-logic-pool", + "operator": "Equal", + "value": data.appLogicPool, + "effect": "NoSchedule" + } + ] + _appLogicPoolMatchExpressions = [ + { + "key": "mandatory.k8s/app-logic-pool", + "operator": "In", + "values": [data.appLogicPool] + } + ] + +schema AppConfigurationRender[inputData]: + mixin [ + EnvMandatorySchedulingMixin, + OverQuotaMixin, + AppLogicPoolMixin + ] + + data: {str:} = inputData + + _envMandatorySchedulingTolerations?: [] = None + _overQuotaTolerations?: [] = None + _appLogicPoolTolerations?: [] = None + + _envMandatorySchedulingMatchExpressions?: [] = None + _overQuotaMatchExpressions?: [] = None + _appLogicPoolMatchExpressions?: [] = None + + deployment = Deployment { + affinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: [ + { + matchExpressions: [ + *_envMandatorySchedulingMatchExpressions, + *_overQuotaMatchExpressions, + *_appLogicPoolMatchExpressions, + ] + } + ] + tolerations: [ + *_envMandatorySchedulingTolerations, + *_overQuotaTolerations, + *_appLogicPoolTolerations, + ] + } + +result = AppConfigurationRender(inputData={ + envMandatory = True + overQuota = True + appLogicPool = True +}) {} diff --git a/test/grammar/schema/irrelevant_order/if_stmt_2/stdout.golden b/test/grammar/schema/irrelevant_order/if_stmt_2/stdout.golden new file mode 100644 index 000000000..8511d0f8d --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_2/stdout.golden @@ -0,0 +1,34 @@ +result: + data: + envMandatory: true + overQuota: true + appLogicPool: true + deployment: + affinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: app-env + operator: In + values: $env_type + - key: cluster.k8s/is-over-quota + operator: In + values: + - 'true' + - key: mandatory.k8s/app-logic-pool + operator: In + values: + - true + tolerations: + - key: app-env + operator: Equal + value: $env_type + effect: NoSchedule + - key: cluster.k8s/is-over-quota + operator: Equal + value: 'true' + effect: NoSchedule + - key: mandatory.k8s/app-logic-pool + operator: Equal + value: true + effect: NoSchedule diff --git a/test/grammar/schema/irrelevant_order/if_stmt_3/main.k b/test/grammar/schema/irrelevant_order/if_stmt_3/main.k new file mode 100644 index 000000000..8d8c6a53a --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_3/main.k @@ -0,0 +1,9 @@ +schema Config: + getGroup: bool = True + group?: str + if getGroup: + _app = "app" + _tenant = "tenant" + group = _app + " " + _tenant + +config = Config {} diff --git a/test/grammar/schema/irrelevant_order/if_stmt_3/stdout.golden b/test/grammar/schema/irrelevant_order/if_stmt_3/stdout.golden new file mode 100644 index 000000000..35560726c --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_3/stdout.golden @@ -0,0 +1,3 @@ +config: + getGroup: true + group: app tenant diff --git a/test/grammar/schema/irrelevant_order/if_stmt_4/main.k b/test/grammar/schema/irrelevant_order/if_stmt_4/main.k new file mode 100644 index 000000000..88c8e6bdf --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_4/main.k @@ -0,0 +1,12 @@ +schema Config: + getGroup: bool = False + group?: str + if getGroup: + _app = "app1" + _tenant = "tenant1" + else: + _app = "app2" + _tenant = "tenant2" + group = _app + " " + _tenant + +config = Config {} diff --git a/test/grammar/schema/irrelevant_order/if_stmt_4/stdout.golden b/test/grammar/schema/irrelevant_order/if_stmt_4/stdout.golden new file mode 100644 index 000000000..58bd1620e --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_4/stdout.golden @@ -0,0 +1,3 @@ +config: + getGroup: false + group: app2 tenant2 diff --git a/test/grammar/schema/irrelevant_order/if_stmt_5/main.k b/test/grammar/schema/irrelevant_order/if_stmt_5/main.k new file mode 100644 index 000000000..92d6e45ee --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_5/main.k @@ -0,0 +1,9 @@ +schema Config: + getGroup: bool = True + group?: str + if getGroup: + _app = "app" + _tenant = _app.upper() if _app else "tenant" + group = _app + " " + _tenant + +config = Config {} diff --git a/test/grammar/schema/irrelevant_order/if_stmt_5/stdout.golden b/test/grammar/schema/irrelevant_order/if_stmt_5/stdout.golden new file mode 100644 index 000000000..7b9cf8fa7 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_5/stdout.golden @@ -0,0 +1,3 @@ +config: + getGroup: true + group: app APP diff --git a/test/grammar/schema/irrelevant_order/if_stmt_6/main.k b/test/grammar/schema/irrelevant_order/if_stmt_6/main.k new file mode 100644 index 000000000..d83ceba5c --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_6/main.k @@ -0,0 +1,19 @@ +schema Config: + label: str = "A" + name: str + if label == "A": + name = "{}-{}".format(label, "A") + elif label == "B": + name = "{}-{}".format(label, "B") + else: + name = "{}".format(label) + anotherName: str = name + +configA = Config {} +configB = Config { + label = "B" +} +configC = Config { + label = "C" +} + diff --git a/test/grammar/schema/irrelevant_order/if_stmt_6/stdout.golden b/test/grammar/schema/irrelevant_order/if_stmt_6/stdout.golden new file mode 100644 index 000000000..6cc30776f --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_6/stdout.golden @@ -0,0 +1,12 @@ +configA: + label: A + name: A-A + anotherName: A-A +configB: + label: B + name: B-B + anotherName: B-B +configC: + label: C + name: C + anotherName: C diff --git a/test/grammar/schema/irrelevant_order/inherit_0/main.k b/test/grammar/schema/irrelevant_order/inherit_0/main.k new file mode 100644 index 000000000..9d3c09449 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_0/main.k @@ -0,0 +1,10 @@ +schema BasePerson: + firstName: str = "Alice" + lastName: str = None + fullName: str = firstName + ' ' + lastName + +schema Person(BasePerson): + firstName: str = "John" + lastName: str = "Doe" + +JohnDoe = Person {} diff --git a/test/grammar/schema/irrelevant_order/inherit_0/stdout.golden b/test/grammar/schema/irrelevant_order/inherit_0/stdout.golden new file mode 100644 index 000000000..7caad7724 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_0/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: John Doe diff --git a/test/grammar/schema/irrelevant_order/inherit_1/main.k b/test/grammar/schema/irrelevant_order/inherit_1/main.k new file mode 100644 index 000000000..766908ca8 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_1/main.k @@ -0,0 +1,10 @@ +schema BasePerson: + fullName = firstName + ' ' + lastName + +schema Person(BasePerson): + firstName = "John" + lastName: str + +JohnDoe = Person { + lastName: "Doe" +} diff --git a/test/grammar/schema/irrelevant_order/inherit_1/stdout.golden b/test/grammar/schema/irrelevant_order/inherit_1/stdout.golden new file mode 100644 index 000000000..c7bb08aef --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_1/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + fullName: John Doe + firstName: John + lastName: Doe diff --git a/test/grammar/schema/irrelevant_order/inherit_2/main.k b/test/grammar/schema/irrelevant_order/inherit_2/main.k new file mode 100644 index 000000000..2a940e855 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_2/main.k @@ -0,0 +1,13 @@ +schema BasePerson: + firstName: str = "Alice" + lastName: str = None + fullName: str = firstName + ' ' + lastName + +schema Person(BasePerson): + firstName: str = "John" + lastName: str = "Doe" + +schema SubPerson(Person): + firstName = "Bob" + +person = SubPerson {} diff --git a/test/grammar/schema/irrelevant_order/inherit_2/stdout.golden b/test/grammar/schema/irrelevant_order/inherit_2/stdout.golden new file mode 100644 index 000000000..6a9cd1a79 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_2/stdout.golden @@ -0,0 +1,4 @@ +person: + firstName: Bob + lastName: Doe + fullName: Bob Doe diff --git a/test/grammar/schema/irrelevant_order/inherit_3/main.k b/test/grammar/schema/irrelevant_order/inherit_3/main.k new file mode 100644 index 000000000..0c520861e --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_3/main.k @@ -0,0 +1,16 @@ +schema BasePerson: + firstName: str = "Alice" + lastName: str = None + fullName: str = firstName + ' ' + lastName + +schema Person(BasePerson): + firstName: str = "John" + lastName: str = "Doe" + +schema SubPerson(Person): + firstName = "Bob" + +person = SubPerson { + firstName = "firstName_override" + lastName = "lastName_override" +} diff --git a/test/grammar/schema/irrelevant_order/inherit_3/stdout.golden b/test/grammar/schema/irrelevant_order/inherit_3/stdout.golden new file mode 100644 index 000000000..7936a01fe --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_3/stdout.golden @@ -0,0 +1,4 @@ +person: + firstName: firstName_override + lastName: lastName_override + fullName: firstName_override lastName_override diff --git a/test/grammar/schema/irrelevant_order/inherit_4/main.k b/test/grammar/schema/irrelevant_order/inherit_4/main.k new file mode 100644 index 000000000..b0964f987 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_4/main.k @@ -0,0 +1,25 @@ +schema Config: + name: str = "defualt" + help: str = name + labels?: {str:} = None + metaLabels?: {str:} = labels + overQuota: bool = False + +schema AppConfig(Config): + appName: str = name + name: str = "app" + + if overQuota: + labels |= { + "key1": "value1" + "key2": "value2" + } + +config = Config { + name = "override", + overQuota = True +} + +appConfig = AppConfig { + overQuota = True +} diff --git a/test/grammar/schema/irrelevant_order/inherit_4/stdout.golden b/test/grammar/schema/irrelevant_order/inherit_4/stdout.golden new file mode 100644 index 000000000..dd64628f3 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_4/stdout.golden @@ -0,0 +1,17 @@ +config: + name: override + help: override + labels: null + metaLabels: null + overQuota: true +appConfig: + name: app + help: app + labels: + key1: value1 + key2: value2 + metaLabels: + key1: value1 + key2: value2 + overQuota: true + appName: app diff --git a/test/grammar/schema/irrelevant_order/inherit_5/main.k b/test/grammar/schema/irrelevant_order/inherit_5/main.k new file mode 100644 index 000000000..81c95f006 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_5/main.k @@ -0,0 +1,28 @@ +schema Config: + name: str = "defualt" + help: str = name + labels?: {str:} = None + metaLabels?: {str:} = labels + overQuota: bool = False + +schema AppConfig(Config): + appName: str = name + name: str = "app" + + if overQuota: + labels |= { + "key1": "value1" + } + if overQuota: + labels |= { + "key2": "value2" + } + +config = Config { + overQuota = False +} + +appConfig = AppConfig { + appName = "myApp" + overQuota = True +} diff --git a/test/grammar/schema/irrelevant_order/inherit_5/stdout.golden b/test/grammar/schema/irrelevant_order/inherit_5/stdout.golden new file mode 100644 index 000000000..0ef8fe5ac --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_5/stdout.golden @@ -0,0 +1,17 @@ +config: + name: defualt + help: defualt + labels: null + metaLabels: null + overQuota: false +appConfig: + name: app + help: app + labels: + key1: value1 + key2: value2 + metaLabels: + key1: value1 + key2: value2 + overQuota: true + appName: myApp diff --git a/test/grammar/schema/irrelevant_order/inherit_6/main.k b/test/grammar/schema/irrelevant_order/inherit_6/main.k new file mode 100644 index 000000000..b48c7089d --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_6/main.k @@ -0,0 +1,27 @@ +schema Parent: + name: str = "Alice" + key: str = "key" + " " + name + name2: str = "Alice" + key2: str = "key2" + " " + name2 + _tempA = "tempA" + +schema Son1(Parent): + name: str = "Son1" + _tempB = "tempB" + +schema Son2(Son1): + name: str = "Son2" + _tempA + name2: str = "Son2" + +schema Son3(Son2): + name: str = "Son3" + _tempB + +schema Son4(Son3): + name: str = "Son4" + _tempA + _tempB + name2: str = "Son4" + +parent = Parent {} +son1 = Son1 {} +son2 = Son2 {} +son3 = Son3 {} +son4 = Son4 {} diff --git a/test/grammar/schema/irrelevant_order/inherit_6/stdout.golden b/test/grammar/schema/irrelevant_order/inherit_6/stdout.golden new file mode 100644 index 000000000..a7a1363b7 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/inherit_6/stdout.golden @@ -0,0 +1,25 @@ +parent: + name: Alice + key: key Alice + name2: Alice + key2: key2 Alice +son1: + name: Son1 + key: key Son1 + name2: Alice + key2: key2 Alice +son2: + name: Son2tempA + key: key Son2tempA + name2: Son2 + key2: key2 Son2 +son3: + name: Son3tempB + key: key Son3tempB + name2: Son2 + key2: key2 Son2 +son4: + name: Son4tempAtempB + key: key Son4tempAtempB + name2: Son4 + key2: key2 Son4 diff --git a/test/grammar/schema/irrelevant_order/mixin_0/main.k b/test/grammar/schema/irrelevant_order/mixin_0/main.k new file mode 100644 index 000000000..ac5930f74 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/mixin_0/main.k @@ -0,0 +1,13 @@ +schema FullNameMixin: + fullName: str = firstName + ' ' + lastName + +schema Person: + mixin [FullNameMixin] + + firstName: str = "John" + lastName: str + + +JohnDoe = Person { + lastName: "Doe" +} diff --git a/test/grammar/schema/irrelevant_order/mixin_0/stdout.golden b/test/grammar/schema/irrelevant_order/mixin_0/stdout.golden new file mode 100644 index 000000000..7caad7724 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/mixin_0/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: John Doe diff --git a/test/grammar/schema/irrelevant_order/mixin_1/main.k b/test/grammar/schema/irrelevant_order/mixin_1/main.k new file mode 100644 index 000000000..eaefe3727 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/mixin_1/main.k @@ -0,0 +1,15 @@ +schema FullNameMixin: + fullName: str = firstName + ' ' + lastName + +schema Person: + mixin [FullNameMixin] + + firstName: str = "John" + lastName: str + + firstName = firstName.upper() + + +JohnDoe = Person { + lastName: "Doe" +} diff --git a/test/grammar/schema/irrelevant_order/mixin_1/stdout.golden b/test/grammar/schema/irrelevant_order/mixin_1/stdout.golden new file mode 100644 index 000000000..4081dafbb --- /dev/null +++ b/test/grammar/schema/irrelevant_order/mixin_1/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: JOHN + lastName: Doe + fullName: JOHN Doe diff --git a/test/grammar/schema/irrelevant_order/mixin_2/main.k b/test/grammar/schema/irrelevant_order/mixin_2/main.k new file mode 100644 index 000000000..cba2b333c --- /dev/null +++ b/test/grammar/schema/irrelevant_order/mixin_2/main.k @@ -0,0 +1,11 @@ +schema Data: + mixin [DataMixin] + labels: {str:} = {} + metaLabels: {str:} = labels + +schema DataMixin: + labels |= { + "key": "value" + } + +data = Data {} diff --git a/test/grammar/schema/irrelevant_order/mixin_2/stdout.golden b/test/grammar/schema/irrelevant_order/mixin_2/stdout.golden new file mode 100644 index 000000000..7c4dccf69 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/mixin_2/stdout.golden @@ -0,0 +1,5 @@ +data: + labels: + key: value + metaLabels: + key: value diff --git a/test/grammar/schema/irrelevant_order/mixin_3/main.k b/test/grammar/schema/irrelevant_order/mixin_3/main.k new file mode 100644 index 000000000..1528261b7 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/mixin_3/main.k @@ -0,0 +1,10 @@ +schema ConfigMixin: + output: str = option("output") or "default_output" + data |= {"output": output} + +schema Config: + mixin [ConfigMixin] + name: str = "config" + data: {str:} = {} + +config = Config {} diff --git a/test/grammar/schema/irrelevant_order/mixin_3/stdout.golden b/test/grammar/schema/irrelevant_order/mixin_3/stdout.golden new file mode 100644 index 000000000..574886d46 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/mixin_3/stdout.golden @@ -0,0 +1,5 @@ +config: + name: config + data: + output: default_output + output: default_output diff --git a/test/grammar/schema/irrelevant_order/relaxed_0/main.k b/test/grammar/schema/irrelevant_order/relaxed_0/main.k new file mode 100644 index 000000000..797c9f587 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/relaxed_0/main.k @@ -0,0 +1,8 @@ +schema Person: + fullName = firstName + ' ' + lastName + firstName: str = "John" + lastName?: str = None + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/irrelevant_order/relaxed_0/stdout.golden b/test/grammar/schema/irrelevant_order/relaxed_0/stdout.golden new file mode 100644 index 000000000..c7bb08aef --- /dev/null +++ b/test/grammar/schema/irrelevant_order/relaxed_0/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + fullName: John Doe + firstName: John + lastName: Doe diff --git a/test/grammar/schema/irrelevant_order/relaxed_1/main.k b/test/grammar/schema/irrelevant_order/relaxed_1/main.k new file mode 100644 index 000000000..4c3245369 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/relaxed_1/main.k @@ -0,0 +1,14 @@ +schema Fib: + n?: int = None + n1 = n - 1 + n2 = n1 - 1 + value?: int = None + + if n <= 1: + value = 1 + elif n == 2: + value = 1 + else: + value = (Fib {n: n1}).value + (Fib {n: n2}).value + +fib8 = (Fib {n: 8}).value diff --git a/test/grammar/schema/irrelevant_order/relaxed_1/stdout.golden b/test/grammar/schema/irrelevant_order/relaxed_1/stdout.golden new file mode 100644 index 000000000..fa541ca8d --- /dev/null +++ b/test/grammar/schema/irrelevant_order/relaxed_1/stdout.golden @@ -0,0 +1 @@ +fib8: 21 diff --git a/test/grammar/schema/irrelevant_order/relaxed_2/main.k b/test/grammar/schema/irrelevant_order/relaxed_2/main.k new file mode 100644 index 000000000..b586fce7b --- /dev/null +++ b/test/grammar/schema/irrelevant_order/relaxed_2/main.k @@ -0,0 +1,6 @@ +schema Data: + a = b + b = 2 + c = b + +data = Data {} diff --git a/test/grammar/schema/irrelevant_order/relaxed_2/stdout.golden b/test/grammar/schema/irrelevant_order/relaxed_2/stdout.golden new file mode 100644 index 000000000..ff4b438f5 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/relaxed_2/stdout.golden @@ -0,0 +1,4 @@ +data: + a: 2 + b: 2 + c: 2 diff --git a/test/grammar/schema/irrelevant_order/simple_0/main.k b/test/grammar/schema/irrelevant_order/simple_0/main.k new file mode 100644 index 000000000..fc349bb94 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/simple_0/main.k @@ -0,0 +1,8 @@ +schema Person: + fullName: str = firstName + ' ' + lastName + firstName: str = "John" + lastName: str + +JohnDoe = Person { + lastName: "Doe" +} diff --git a/test/grammar/schema/irrelevant_order/simple_0/stdout.golden b/test/grammar/schema/irrelevant_order/simple_0/stdout.golden new file mode 100644 index 000000000..c7bb08aef --- /dev/null +++ b/test/grammar/schema/irrelevant_order/simple_0/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + fullName: John Doe + firstName: John + lastName: Doe diff --git a/test/grammar/schema/irrelevant_order/simple_1/main.k b/test/grammar/schema/irrelevant_order/simple_1/main.k new file mode 100644 index 000000000..ae31c0440 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/simple_1/main.k @@ -0,0 +1,14 @@ +schema Fib: + n1: int = n - 1 + n2: int = n1 - 1 + n: int + value: int + + if n <= 1: + value = 1 + elif n == 2: + value = 1 + else: + value = (Fib {n: n1}).value + (Fib {n: n2}).value + +fib8 = (Fib {n: 8}).value diff --git a/test/grammar/schema/irrelevant_order/simple_1/stdout.golden b/test/grammar/schema/irrelevant_order/simple_1/stdout.golden new file mode 100644 index 000000000..fa541ca8d --- /dev/null +++ b/test/grammar/schema/irrelevant_order/simple_1/stdout.golden @@ -0,0 +1 @@ +fib8: 21 diff --git a/test/grammar/schema/irrelevant_order/simple_2/main.k b/test/grammar/schema/irrelevant_order/simple_2/main.k new file mode 100644 index 000000000..37ee77e4f --- /dev/null +++ b/test/grammar/schema/irrelevant_order/simple_2/main.k @@ -0,0 +1,7 @@ +schema Data: + a = b + c = 1 + if c: + b = 2 + +data = Data {} diff --git a/test/grammar/schema/irrelevant_order/simple_2/stdout.golden b/test/grammar/schema/irrelevant_order/simple_2/stdout.golden new file mode 100644 index 000000000..5748fba86 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/simple_2/stdout.golden @@ -0,0 +1,4 @@ +data: + a: 2 + c: 1 + b: 2 diff --git a/test/grammar/schema/irrelevant_order/simple_3/main.k b/test/grammar/schema/irrelevant_order/simple_3/main.k new file mode 100644 index 000000000..1f6a3fe7d --- /dev/null +++ b/test/grammar/schema/irrelevant_order/simple_3/main.k @@ -0,0 +1,16 @@ +schema Data0: + value: int + if n == 0: + value = 0 + n1: int = n + 1 + n: int = 0 + +schema Data1: + value: int + if n == 0: + value = 0 + n: int = 0 + n1: int = n + 1 + +data0 = Data0 {} +data1 = Data1 {} diff --git a/test/grammar/schema/irrelevant_order/simple_3/stdout.golden b/test/grammar/schema/irrelevant_order/simple_3/stdout.golden new file mode 100644 index 000000000..8a0e51141 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/simple_3/stdout.golden @@ -0,0 +1,8 @@ +data0: + value: 0 + n1: 1 + n: 0 +data1: + value: 0 + n: 0 + n1: 1 diff --git a/test/grammar/schema/irrelevant_order/simple_4/main.k b/test/grammar/schema/irrelevant_order/simple_4/main.k new file mode 100644 index 000000000..c3f76c4b2 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/simple_4/main.k @@ -0,0 +1,11 @@ +schema Config: + a: int = b + b: int + if True: + b = 1 + b += 1 + +x0 = Config {} +x1 = Config { + a = 10 +} diff --git a/test/grammar/schema/irrelevant_order/simple_4/stdout.golden b/test/grammar/schema/irrelevant_order/simple_4/stdout.golden new file mode 100644 index 000000000..f8641cee6 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/simple_4/stdout.golden @@ -0,0 +1,6 @@ +x0: + a: 2 + b: 2 +x1: + a: 10 + b: 2 diff --git a/test/grammar/schema/irrelevant_order/simple_5/main.k b/test/grammar/schema/irrelevant_order/simple_5/main.k new file mode 100644 index 000000000..f77dff09c --- /dev/null +++ b/test/grammar/schema/irrelevant_order/simple_5/main.k @@ -0,0 +1,10 @@ +schema Config: + a: int = b + b: int = 1 + if True: + b += 2 + +x0 = Config {} +x1 = Config { + a = 10 +} diff --git a/test/grammar/schema/irrelevant_order/simple_5/stdout.golden b/test/grammar/schema/irrelevant_order/simple_5/stdout.golden new file mode 100644 index 000000000..1a580e0e8 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/simple_5/stdout.golden @@ -0,0 +1,6 @@ +x0: + a: 3 + b: 3 +x1: + a: 10 + b: 3 diff --git a/test/grammar/schema/mixin/add_member_fail/main.k b/test/grammar/schema/mixin/add_member_fail/main.k new file mode 100644 index 000000000..038e909f5 --- /dev/null +++ b/test/grammar/schema/mixin/add_member_fail/main.k @@ -0,0 +1,22 @@ +schema Person: + first: str + last: str + age: int + +schema Chinese: + mixin [ChineseMixin] + first: str + last: str + age: int + +schema ChineseMixin: + chinesePerson = Person { + "last": last, + "age": age, + "frist": first + } + +alice = Chinese { + "first": "alice", + "age": 10 +} diff --git a/test/grammar/schema/mixin/add_member_fail/stderr.golden.py b/test/grammar/schema/mixin/add_member_fail/stderr.golden.py new file mode 100644 index 000000000..b7a19ac26 --- /dev/null +++ b/test/grammar/schema/mixin/add_member_fail/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CannotAddMembers_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=16, + col_no=9, + arg_msg="'frist' is not defined in schema 'Person'" + ) + ], + arg_msg="Cannot add member 'frist' to schema 'Person'" + ), + file=sys.stdout +) diff --git a/test/grammar/schema/mixin/dict_2_schema_0/main.k b/test/grammar/schema/mixin/dict_2_schema_0/main.k new file mode 100644 index 000000000..3ea5962e2 --- /dev/null +++ b/test/grammar/schema/mixin/dict_2_schema_0/main.k @@ -0,0 +1,43 @@ +schema Person: + name: str + relatives: [Family] + +schema Family: + relation: str + name: str + +schema Rule: + kind: str = "Rule" + access: [str] + entity: Person + __settings__: {str:} = { + "output_type": "STANDALONE" + } + +schema RuleMixin: + if rule: + ruleModel = { + "entity": { + "name": rule["name"], + "relatives": rule["relatives"] + }, + "access": rule["access"] + } + +schema Model: + mixin [RuleMixin] + rule: {str:} + ruleModel: Rule + +alice = Model { + "rule": { + "name": "alice", + "relatives": [ + { + "relation": "father", + "name": "Reese" + } + ], + "access": ["read", "write"] + } +} diff --git a/test/grammar/schema/mixin/dict_2_schema_0/stdout.golden b/test/grammar/schema/mixin/dict_2_schema_0/stdout.golden new file mode 100644 index 000000000..8a6f91af1 --- /dev/null +++ b/test/grammar/schema/mixin/dict_2_schema_0/stdout.golden @@ -0,0 +1,19 @@ +alice: + rule: + name: alice + relatives: + - relation: father + name: Reese + access: + - read + - write +--- +kind: Rule +access: +- read +- write +entity: + name: alice + relatives: + - relation: father + name: Reese \ No newline at end of file diff --git a/test/grammar/schema/mixin/dict_2_schema_1/main.k b/test/grammar/schema/mixin/dict_2_schema_1/main.k new file mode 100644 index 000000000..8321e6cf4 --- /dev/null +++ b/test/grammar/schema/mixin/dict_2_schema_1/main.k @@ -0,0 +1,43 @@ +schema Person: + name: str + relatives: [Family] + +schema Family: + relation: str + name: str + +schema Rule: + kind: str = "Rule" + access: [str] + entity: Person + __settings__: {str:} = { + "output_type": "STANDALONE" + } + +schema RuleMixin: + if rule: + ruleModels = [{ + "entity": { + "name": rule["name"], + "relatives": rule["relatives"] + }, + "access": rule["access"] + }] + +schema Model: + mixin [RuleMixin] + rule: {str:} + ruleModels: [Rule] + +alice = Model { + "rule": { + "name": "alice", + "relatives": [ + { + "relation": "father", + "name": "Reese" + } + ], + "access": ["read", "write"] + } +} diff --git a/test/grammar/schema/mixin/dict_2_schema_1/stdout.golden b/test/grammar/schema/mixin/dict_2_schema_1/stdout.golden new file mode 100644 index 000000000..e92a589ac --- /dev/null +++ b/test/grammar/schema/mixin/dict_2_schema_1/stdout.golden @@ -0,0 +1,19 @@ +alice: + rule: + name: alice + relatives: + - relation: father + name: Reese + access: + - read + - write +--- +kind: Rule +access: +- read +- write +entity: + name: alice + relatives: + - relation: father + name: Reese diff --git a/test/grammar/schema/mixin/dict_2_schema_2/main.k b/test/grammar/schema/mixin/dict_2_schema_2/main.k new file mode 100644 index 000000000..2029b2adb --- /dev/null +++ b/test/grammar/schema/mixin/dict_2_schema_2/main.k @@ -0,0 +1,43 @@ +schema Person: + name: str + relatives: [Family] + +schema Family: + relation: str + name: str + +schema Rule: + kind: str = "Rule" + access: [str] + entity: Person + __settings__: {str:} = { + output_type = "STANDALONE" + } + +schema RuleMixin: + if rule: + ruleModels = [{ + entity: Person { + "name": rule["name"], + "relatives": rule["relatives"] + }, + access: rule["access"] + }] + +schema Model: + mixin [RuleMixin] + rule: {str:} + ruleModels: [Rule] + +alice = Model { + rule: { + name = "alice", + relatives = [ + { + relation = "father", + name = "Reese" + } + ], + access = ["read", "write"] + } +} diff --git a/test/grammar/schema/mixin/dict_2_schema_2/stdout.golden b/test/grammar/schema/mixin/dict_2_schema_2/stdout.golden new file mode 100644 index 000000000..e92a589ac --- /dev/null +++ b/test/grammar/schema/mixin/dict_2_schema_2/stdout.golden @@ -0,0 +1,19 @@ +alice: + rule: + name: alice + relatives: + - relation: father + name: Reese + access: + - read + - write +--- +kind: Rule +access: +- read +- write +entity: + name: alice + relatives: + - relation: father + name: Reese diff --git a/test/grammar/schema/mixin/dict_2_schema_3/kcl.mod b/test/grammar/schema/mixin/dict_2_schema_3/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/mixin/dict_2_schema_3/main.k b/test/grammar/schema/mixin/dict_2_schema_3/main.k new file mode 100644 index 000000000..86701c396 --- /dev/null +++ b/test/grammar/schema/mixin/dict_2_schema_3/main.k @@ -0,0 +1,17 @@ +import pkg + +config = pkg.Config { + metadata = { + name = "config" + } + spec = { + apps = [ + { + spec = { + appName = "app" + appType = "type" + } + } + ] + } +} diff --git a/test/grammar/schema/mixin/dict_2_schema_3/pkg/pkg.k b/test/grammar/schema/mixin/dict_2_schema_3/pkg/pkg.k new file mode 100644 index 000000000..b93ecc12e --- /dev/null +++ b/test/grammar/schema/mixin/dict_2_schema_3/pkg/pkg.k @@ -0,0 +1,16 @@ +schema Config: + metadata: MetaData + spec: Spec + +schema MetaData: + name: str + +schema Spec: + apps: [App] + +schema App: + spec: AppSpec + +schema AppSpec: + appName: str + appType: str diff --git a/test/grammar/schema/mixin/dict_2_schema_3/stdout.golden b/test/grammar/schema/mixin/dict_2_schema_3/stdout.golden new file mode 100644 index 000000000..ebbed1d7c --- /dev/null +++ b/test/grammar/schema/mixin/dict_2_schema_3/stdout.golden @@ -0,0 +1,8 @@ +config: + metadata: + name: config + spec: + apps: + - spec: + appName: app + appType: type diff --git a/test/grammar/schema/mixin/host-type/main.k b/test/grammar/schema/mixin/host-type/main.k new file mode 100644 index 000000000..202a4fcd8 --- /dev/null +++ b/test/grammar/schema/mixin/host-type/main.k @@ -0,0 +1,13 @@ +schema PersonProtocol: + name: str + +mixin PersonMixin for PersonProtocol: + name = 'PersonMixin-name' + +schema Person: + mixin [PersonMixin] + name: str = "kcl" + age: int = 1 + +x0 = Person{} +x1 = Person{age = 101} diff --git a/test/grammar/schema/mixin/host-type/stdout.golden b/test/grammar/schema/mixin/host-type/stdout.golden new file mode 100644 index 000000000..a3b7e1db6 --- /dev/null +++ b/test/grammar/schema/mixin/host-type/stdout.golden @@ -0,0 +1,6 @@ +x0: + name: PersonMixin-name + age: 1 +x1: + name: PersonMixin-name + age: 101 diff --git a/test/grammar/schema/mixin/inherit/main.k b/test/grammar/schema/mixin/inherit/main.k new file mode 100644 index 000000000..fa57fc2dc --- /dev/null +++ b/test/grammar/schema/mixin/inherit/main.k @@ -0,0 +1,17 @@ +schema Person: + firstName: str + lastName: str + fullName: str + +schema FullnameMixin: + middleName: str = "middleName" + fullName = "{} {}".format(firstName, lastName) + +schema Scholar(Person): + mixin [FullnameMixin] + school?: str = None + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe" +} diff --git a/test/grammar/schema/mixin/inherit/stdout.golden b/test/grammar/schema/mixin/inherit/stdout.golden new file mode 100644 index 000000000..6a04be462 --- /dev/null +++ b/test/grammar/schema/mixin/inherit/stdout.golden @@ -0,0 +1,6 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: John Doe + school: null + middleName: middleName \ No newline at end of file diff --git a/test/grammar/schema/mixin/inherit_0/main.k b/test/grammar/schema/mixin/inherit_0/main.k new file mode 100644 index 000000000..35ef6e779 --- /dev/null +++ b/test/grammar/schema/mixin/inherit_0/main.k @@ -0,0 +1,19 @@ +schema Person: + firstName: str + lastName: str + nameUpper: str + +schema UpperMixin: + nameUpper = firstName.upper() + schoolUpper = school.upper() + +schema Scholar(Person): + mixin [UpperMixin] + school: str + schoolUpper: str + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "school": "cs" +} diff --git a/test/grammar/schema/mixin/inherit_0/stdout.golden b/test/grammar/schema/mixin/inherit_0/stdout.golden new file mode 100644 index 000000000..148f10a1a --- /dev/null +++ b/test/grammar/schema/mixin/inherit_0/stdout.golden @@ -0,0 +1,6 @@ +JohnDoe: + firstName: John + lastName: Doe + nameUpper: JOHN + school: cs + schoolUpper: CS \ No newline at end of file diff --git a/test/grammar/schema/mixin/inherit_1/main.k b/test/grammar/schema/mixin/inherit_1/main.k new file mode 100644 index 000000000..ff67fb9e7 --- /dev/null +++ b/test/grammar/schema/mixin/inherit_1/main.k @@ -0,0 +1,44 @@ +schema FullNameMixin: + fullName = "{} {}".format(firstName, lastName) + +schema AcademyFullNameMixin: + academyFullName = "{} {}".format(school, subject) + +schema PersonInfoMixin: + info = "{}, {} years old".format(fullName, age) + +schema Name: + mixin [FullNameMixin] + firstName: str + lastName: str + fullName: str + +schema Person(Name): + mixin [PersonInfoMixin] + age: int + info: str + +schema Scholar(Person): + mixin [AcademyFullNameMixin] + subject: str + school: str + academyFullName: str + +JohnReese = Name { + "firstName": "John", + "lastName": "Reese" +} + +JonSnow = Person { + "firstName": "Jon", + "lastName": "Snow", + "age": 10 +} + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "age": 10, + "subject": "CS", + "school": "PKU" +} diff --git a/test/grammar/schema/mixin/inherit_1/stdout.golden b/test/grammar/schema/mixin/inherit_1/stdout.golden new file mode 100644 index 000000000..f9ea6f193 --- /dev/null +++ b/test/grammar/schema/mixin/inherit_1/stdout.golden @@ -0,0 +1,19 @@ +JohnReese: + firstName: John + lastName: Reese + fullName: John Reese +JonSnow: + firstName: Jon + lastName: Snow + fullName: Jon Snow + age: 10 + info: Jon Snow, 10 years old +JohnDoe: + firstName: John + lastName: Doe + fullName: John Doe + age: 10 + info: John Doe, 10 years old + subject: CS + school: PKU + academyFullName: PKU CS \ No newline at end of file diff --git a/test/grammar/schema/mixin/init_dict/main.k b/test/grammar/schema/mixin/init_dict/main.k new file mode 100644 index 000000000..a309dbf33 --- /dev/null +++ b/test/grammar/schema/mixin/init_dict/main.k @@ -0,0 +1,14 @@ +schema card: + num: int + +schema Person: + mixin [cardMixin] + card_ids: [{str:int}] + cards: [card] + +schema cardMixin: + cards = card_ids + +person = Person { + "card_ids": [{"num": 123}] +} diff --git a/test/grammar/schema/mixin/init_dict/stdout.golden b/test/grammar/schema/mixin/init_dict/stdout.golden new file mode 100644 index 000000000..d84648367 --- /dev/null +++ b/test/grammar/schema/mixin/init_dict/stdout.golden @@ -0,0 +1,5 @@ +person: + card_ids: + - num: 123 + cards: + - num: 123 \ No newline at end of file diff --git a/test/grammar/schema/mixin/invalid_name_failure/main.k b/test/grammar/schema/mixin/invalid_name_failure/main.k new file mode 100644 index 000000000..9c9f882e0 --- /dev/null +++ b/test/grammar/schema/mixin/invalid_name_failure/main.k @@ -0,0 +1,17 @@ +schema Person: + firstName: str + lastName: str + fullName: str + +schema Fullname: + fullName = "{} {}".format(firstName, lastName) + +schema Scholar(Person): + mixin [Fullname] + school: str + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "fullName": "Doe Jon" +} diff --git a/test/grammar/schema/mixin/invalid_name_failure/stderr.golden.py b/test/grammar/schema/mixin/invalid_name_failure/stderr.golden.py new file mode 100644 index 000000000..7a718bc8f --- /dev/null +++ b/test/grammar/schema/mixin/invalid_name_failure/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.MixinNamingError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=12 + ) + ], + arg_msg="a valid mixin name should end with 'Mixin', got 'Fullname'" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/mixin/multi_mixins_0/main.k b/test/grammar/schema/mixin/multi_mixins_0/main.k new file mode 100644 index 000000000..8f5d34774 --- /dev/null +++ b/test/grammar/schema/mixin/multi_mixins_0/main.k @@ -0,0 +1,20 @@ +schema Person: + firstName: str + lastName: str + fullName: str + upper: str + +schema FullnameMixin: + fullName = "{} {}".format(firstName, lastName) + +schema UpperMixin: + upper = fullName.upper() + +schema Scholar(Person): + mixin [FullnameMixin, UpperMixin] + school?: str = None + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe" +} diff --git a/test/grammar/schema/mixin/multi_mixins_0/stdout.golden b/test/grammar/schema/mixin/multi_mixins_0/stdout.golden new file mode 100644 index 000000000..03b29b282 --- /dev/null +++ b/test/grammar/schema/mixin/multi_mixins_0/stdout.golden @@ -0,0 +1,6 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: John Doe + upper: JOHN DOE + school: null \ No newline at end of file diff --git a/test/grammar/schema/mixin/multi_mixins_1/main.k b/test/grammar/schema/mixin/multi_mixins_1/main.k new file mode 100644 index 000000000..f7bcc764d --- /dev/null +++ b/test/grammar/schema/mixin/multi_mixins_1/main.k @@ -0,0 +1,23 @@ +schema Person: + firstName: str + lastName: str + fullName: str + upper: str + +schema FullnameMixin: + fullName = "{} {}".format(firstName, lastName) + +schema UpperMixin: + upper = fullName.upper() + +schema Scholar(Person): + mixin [ + FullnameMixin, + UpperMixin + ] + school?: str = None + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe" +} diff --git a/test/grammar/schema/mixin/multi_mixins_1/stdout.golden b/test/grammar/schema/mixin/multi_mixins_1/stdout.golden new file mode 100644 index 000000000..03b29b282 --- /dev/null +++ b/test/grammar/schema/mixin/multi_mixins_1/stdout.golden @@ -0,0 +1,6 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: John Doe + upper: JOHN DOE + school: null \ No newline at end of file diff --git a/test/grammar/schema/mixin/package_mixin/kcl.mod b/test/grammar/schema/mixin/package_mixin/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/mixin/package_mixin/main.k b/test/grammar/schema/mixin/package_mixin/main.k new file mode 100644 index 000000000..26643ec47 --- /dev/null +++ b/test/grammar/schema/mixin/package_mixin/main.k @@ -0,0 +1,5 @@ +import pkg + +container = pkg.Container { + name = "container" +} diff --git a/test/grammar/schema/mixin/package_mixin/pkg/container.k b/test/grammar/schema/mixin/package_mixin/pkg/container.k new file mode 100644 index 000000000..18484cc01 --- /dev/null +++ b/test/grammar/schema/mixin/package_mixin/pkg/container.k @@ -0,0 +1,5 @@ +import .container_mixin as c_mixin + +schema Container(c_mixin.BaseContainer): + mixin [c_mixin.ContainerMixin] + key?: str = None diff --git a/test/grammar/schema/mixin/package_mixin/pkg/container_mixin.k b/test/grammar/schema/mixin/package_mixin/pkg/container_mixin.k new file mode 100644 index 000000000..9b4132a76 --- /dev/null +++ b/test/grammar/schema/mixin/package_mixin/pkg/container_mixin.k @@ -0,0 +1,5 @@ +schema ContainerMixin: + name?: str = "MixinName" + +schema BaseContainer: + name?: str diff --git a/test/grammar/schema/mixin/package_mixin/stdout.golden b/test/grammar/schema/mixin/package_mixin/stdout.golden new file mode 100644 index 000000000..c4e4da1d4 --- /dev/null +++ b/test/grammar/schema/mixin/package_mixin/stdout.golden @@ -0,0 +1,3 @@ +container: + name: container + key: null diff --git a/test/grammar/schema/mixin/relaxed_schema/main.k b/test/grammar/schema/mixin/relaxed_schema/main.k new file mode 100644 index 000000000..2e5a4c421 --- /dev/null +++ b/test/grammar/schema/mixin/relaxed_schema/main.k @@ -0,0 +1,15 @@ +schema Person: + firstName?: str + lastName?: str + +schema FullnameMixin: + fullName: str = "{} {}".format(firstName, lastName) + +schema Scholar(Person): + mixin [FullnameMixin] + school?: str = None + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe" +} diff --git a/test/grammar/schema/mixin/relaxed_schema/stdout.golden b/test/grammar/schema/mixin/relaxed_schema/stdout.golden new file mode 100644 index 000000000..6ddab469c --- /dev/null +++ b/test/grammar/schema/mixin/relaxed_schema/stdout.golden @@ -0,0 +1,5 @@ +JohnDoe: + firstName: John + lastName: Doe + school: null + fullName: John Doe \ No newline at end of file diff --git a/test/grammar/schema/mixin/schema_field_append_list/main.k b/test/grammar/schema/mixin/schema_field_append_list/main.k new file mode 100644 index 000000000..35374de7d --- /dev/null +++ b/test/grammar/schema/mixin/schema_field_append_list/main.k @@ -0,0 +1,19 @@ +schema Person: + firstName: str + lastName: str + fullName: str + tags: [str] + +schema InfoMixin: + tags += ["234"] + +schema Scholar(Person): + mixin [InfoMixin] + school?: str = None + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "fullName": "Doe Jon", + "tags" = ["123"] +} diff --git a/test/grammar/schema/mixin/schema_field_append_list/stdout.golden b/test/grammar/schema/mixin/schema_field_append_list/stdout.golden new file mode 100644 index 000000000..094d9ba4a --- /dev/null +++ b/test/grammar/schema/mixin/schema_field_append_list/stdout.golden @@ -0,0 +1,7 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: Doe Jon + tags: + - '123' + school: null diff --git a/test/grammar/schema/mixin/schema_field_change_dict/main.k b/test/grammar/schema/mixin/schema_field_change_dict/main.k new file mode 100644 index 000000000..b2da3f64a --- /dev/null +++ b/test/grammar/schema/mixin/schema_field_change_dict/main.k @@ -0,0 +1,21 @@ +schema Person: + firstName: str + lastName: str + fullName: str + info: {str:str} + +schema InfoMixin: + info |= {"phone": "321"} + +schema Scholar(Person): + mixin [InfoMixin] + school?: str = None + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "fullName": "Doe Jon", + "info": { + "id": "123" + } +} diff --git a/test/grammar/schema/mixin/schema_field_change_dict/stdout.golden b/test/grammar/schema/mixin/schema_field_change_dict/stdout.golden new file mode 100644 index 000000000..064bd2f75 --- /dev/null +++ b/test/grammar/schema/mixin/schema_field_change_dict/stdout.golden @@ -0,0 +1,8 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: Doe Jon + info: + id: '123' + phone: '321' + school: null diff --git a/test/grammar/schema/mixin/schema_field_change_mixin/main.k b/test/grammar/schema/mixin/schema_field_change_mixin/main.k new file mode 100644 index 000000000..05dbec68e --- /dev/null +++ b/test/grammar/schema/mixin/schema_field_change_mixin/main.k @@ -0,0 +1,17 @@ +schema Person: + firstName: str + lastName: str + fullName: str + +schema FullnameMixin: + fullName = "{} {}".format(firstName, lastName) + +schema Scholar(Person): + mixin [FullnameMixin] + school?: str = None + +JohnDoe = Scholar { + "firstName" = "John", + "lastName" = "Doe", + "fullName" = "Doe Jon" +} diff --git a/test/grammar/schema/mixin/schema_field_change_mixin/stdout.golden b/test/grammar/schema/mixin/schema_field_change_mixin/stdout.golden new file mode 100644 index 000000000..cfa25f354 --- /dev/null +++ b/test/grammar/schema/mixin/schema_field_change_mixin/stdout.golden @@ -0,0 +1,5 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: Doe Jon + school: null diff --git a/test/grammar/schema/mixin/schema_field_change_private/main.k b/test/grammar/schema/mixin/schema_field_change_private/main.k new file mode 100644 index 000000000..1a763a0a4 --- /dev/null +++ b/test/grammar/schema/mixin/schema_field_change_private/main.k @@ -0,0 +1,18 @@ +schema Person: + firstName: str + lastName: str + fullName: str + _tmp: str = "default" + +schema FullnameMixin: + fullName = "{} {}".format(firstName, lastName) + _tmp = "fullname_changed" + +schema Scholar(Person): + mixin [FullnameMixin] + school?: str = None + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe" +} diff --git a/test/grammar/schema/mixin/schema_field_change_private/stdout.golden b/test/grammar/schema/mixin/schema_field_change_private/stdout.golden new file mode 100644 index 000000000..598e69b43 --- /dev/null +++ b/test/grammar/schema/mixin/schema_field_change_private/stdout.golden @@ -0,0 +1,5 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: John Doe + school: null \ No newline at end of file diff --git a/test/grammar/schema/mixin/schema_field_union_list/main.k b/test/grammar/schema/mixin/schema_field_union_list/main.k new file mode 100644 index 000000000..8c49257d2 --- /dev/null +++ b/test/grammar/schema/mixin/schema_field_union_list/main.k @@ -0,0 +1,18 @@ +schema Person: + firstName: str + lastName: str + fullName: str + tags: [str] = ["123"] + +schema InfoMixin: + tags |= ["234"] + +schema Scholar(Person): + mixin [InfoMixin] + school?: str = None + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe", + "fullName": "Doe Jon", +} diff --git a/test/grammar/schema/mixin/schema_field_union_list/stdout.golden b/test/grammar/schema/mixin/schema_field_union_list/stdout.golden new file mode 100644 index 000000000..638f5f70c --- /dev/null +++ b/test/grammar/schema/mixin/schema_field_union_list/stdout.golden @@ -0,0 +1,7 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: Doe Jon + tags: + - '234' + school: null diff --git a/test/grammar/schema/mixin/simple/main.k b/test/grammar/schema/mixin/simple/main.k new file mode 100644 index 000000000..f5a4cfc5d --- /dev/null +++ b/test/grammar/schema/mixin/simple/main.k @@ -0,0 +1,16 @@ +schema Person: + firstName: str + lastName: str + fullName: str + +schema FullnameMixin: + fullName = "{} {}".format(firstName, lastName) + +schema Scholar(Person): + mixin [FullnameMixin] + school?: str = None + +JohnDoe = Scholar { + "firstName": "John", + "lastName": "Doe" +} diff --git a/test/grammar/schema/mixin/simple/stdout.golden b/test/grammar/schema/mixin/simple/stdout.golden new file mode 100644 index 000000000..598e69b43 --- /dev/null +++ b/test/grammar/schema/mixin/simple/stdout.golden @@ -0,0 +1,5 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: John Doe + school: null \ No newline at end of file diff --git a/test/grammar/schema/modification/modification_0/main.k b/test/grammar/schema/modification/modification_0/main.k new file mode 100644 index 000000000..e61892791 --- /dev/null +++ b/test/grammar/schema/modification/modification_0/main.k @@ -0,0 +1,11 @@ +schema Config: + id: int + name?: str + +configOrigin = Config { + id = 1 + name = "origin config name" +} +configNew = configOrigin { + name = "override config name" +} diff --git a/test/grammar/schema/modification/modification_0/stdout.golden b/test/grammar/schema/modification/modification_0/stdout.golden new file mode 100644 index 000000000..136e26f73 --- /dev/null +++ b/test/grammar/schema/modification/modification_0/stdout.golden @@ -0,0 +1,6 @@ +configOrigin: + id: 1 + name: origin config name +configNew: + id: 1 + name: override config name diff --git a/test/grammar/schema/modification/modification_1/main.k b/test/grammar/schema/modification/modification_1/main.k new file mode 100644 index 000000000..68114da28 --- /dev/null +++ b/test/grammar/schema/modification/modification_1/main.k @@ -0,0 +1,12 @@ +schema Config: + id: int + values: [int] + +configOrigin = Config { + id = 1 + values = [0, 1] +} +configNew = configOrigin { + id = 2 + values += [2, 3] +} diff --git a/test/grammar/schema/modification/modification_1/stdout.golden b/test/grammar/schema/modification/modification_1/stdout.golden new file mode 100644 index 000000000..9c59c1ea4 --- /dev/null +++ b/test/grammar/schema/modification/modification_1/stdout.golden @@ -0,0 +1,12 @@ +configOrigin: + id: 1 + values: + - 0 + - 1 +configNew: + id: 2 + values: + - 0 + - 1 + - 2 + - 3 diff --git a/test/grammar/schema/optional_attr/fail_0/main.k b/test/grammar/schema/optional_attr/fail_0/main.k new file mode 100644 index 000000000..4d2b893fc --- /dev/null +++ b/test/grammar/schema/optional_attr/fail_0/main.k @@ -0,0 +1,5 @@ +schema Person: + name: str + age?: int + +person = Person {} diff --git a/test/grammar/schema/optional_attr/fail_0/stderr.golden.py b/test/grammar/schema/optional_attr/fail_0/stderr.golden.py new file mode 100644 index 000000000..25adb3b6c --- /dev/null +++ b/test/grammar/schema/optional_attr/fail_0/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + ), + ], + arg_msg="attribute 'name' of Person is required and can't be None or Undefined") + , file=sys.stdout +) + diff --git a/test/grammar/schema/optional_attr/fail_1/main.k b/test/grammar/schema/optional_attr/fail_1/main.k new file mode 100644 index 000000000..ba4fe89fa --- /dev/null +++ b/test/grammar/schema/optional_attr/fail_1/main.k @@ -0,0 +1,11 @@ +schema Base: + info: str + +schema Person(Base): + name?: str + age?: int + +person = Person { + name: "Alice" + age: 18 +} diff --git a/test/grammar/schema/optional_attr/fail_1/stderr.golden.py b/test/grammar/schema/optional_attr/fail_1/stderr.golden.py new file mode 100644 index 000000000..d8d89cc67 --- /dev/null +++ b/test/grammar/schema/optional_attr/fail_1/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=8, + ), + ], + arg_msg="attribute 'info' of Person is required and can't be None or Undefined") + , file=sys.stdout +) + diff --git a/test/grammar/schema/optional_attr/fail_2/main.k b/test/grammar/schema/optional_attr/fail_2/main.k new file mode 100644 index 000000000..62abcac8c --- /dev/null +++ b/test/grammar/schema/optional_attr/fail_2/main.k @@ -0,0 +1,11 @@ +schema Base: + name: str + +schema Person(Base): + name?: str + age?: int + +person = Person { + name: "Alice" + age: 18 +} diff --git a/test/grammar/schema/optional_attr/fail_2/stderr.golden.py b/test/grammar/schema/optional_attr/fail_2/stderr.golden.py new file mode 100644 index 000000000..e7a974a57 --- /dev/null +++ b/test/grammar/schema/optional_attr/fail_2/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + ), + ], + arg_msg="can't change the required schema attribute of 'name' to optional") + , file=sys.stdout +) + diff --git a/test/grammar/schema/optional_attr/inherit_0/main.k b/test/grammar/schema/optional_attr/inherit_0/main.k new file mode 100644 index 000000000..03e55a547 --- /dev/null +++ b/test/grammar/schema/optional_attr/inherit_0/main.k @@ -0,0 +1,12 @@ +schema Base: + info: str + +schema Person(Base): + name?: str + age?: int + +person = Person { + info: "PersonInfo" + name: "Alice" + age: 18 +} diff --git a/test/grammar/schema/optional_attr/inherit_0/stdout.golden b/test/grammar/schema/optional_attr/inherit_0/stdout.golden new file mode 100644 index 000000000..c1cd03ca4 --- /dev/null +++ b/test/grammar/schema/optional_attr/inherit_0/stdout.golden @@ -0,0 +1,4 @@ +person: + info: PersonInfo + name: Alice + age: 18 diff --git a/test/grammar/schema/optional_attr/inherit_1/main.k b/test/grammar/schema/optional_attr/inherit_1/main.k new file mode 100644 index 000000000..0bd0f16a0 --- /dev/null +++ b/test/grammar/schema/optional_attr/inherit_1/main.k @@ -0,0 +1,10 @@ +schema Base: + info: str = "BaseInfo" + +schema Person(Base): + name: str = "Alice" + age: int + +person = Person { + age: 18 +} diff --git a/test/grammar/schema/optional_attr/inherit_1/stdout.golden b/test/grammar/schema/optional_attr/inherit_1/stdout.golden new file mode 100644 index 000000000..2e1934ec7 --- /dev/null +++ b/test/grammar/schema/optional_attr/inherit_1/stdout.golden @@ -0,0 +1,4 @@ +person: + info: BaseInfo + name: Alice + age: 18 diff --git a/test/grammar/schema/optional_attr/simple_0/main.k b/test/grammar/schema/optional_attr/simple_0/main.k new file mode 100644 index 000000000..67ce2a079 --- /dev/null +++ b/test/grammar/schema/optional_attr/simple_0/main.k @@ -0,0 +1,11 @@ +schema Person: + name: str + age?: int = None + +person1 = Person { + name: "Alice" +} +person2 = Person { + name: "Bob" + age: 10 +} diff --git a/test/grammar/schema/optional_attr/simple_0/stdout.golden b/test/grammar/schema/optional_attr/simple_0/stdout.golden new file mode 100644 index 000000000..9aabe0f14 --- /dev/null +++ b/test/grammar/schema/optional_attr/simple_0/stdout.golden @@ -0,0 +1,6 @@ +person1: + name: Alice + age: null +person2: + name: Bob + age: 10 diff --git a/test/grammar/schema/optional_attr/simple_1/main.k b/test/grammar/schema/optional_attr/simple_1/main.k new file mode 100644 index 000000000..3ca50b40e --- /dev/null +++ b/test/grammar/schema/optional_attr/simple_1/main.k @@ -0,0 +1,9 @@ +schema Person: + name?: str = "Alice" + age?: int = None + +person1 = Person {} +person2 = Person { + name = "Bob" + age = 10 +} diff --git a/test/grammar/schema/optional_attr/simple_1/stdout.golden b/test/grammar/schema/optional_attr/simple_1/stdout.golden new file mode 100644 index 000000000..9aabe0f14 --- /dev/null +++ b/test/grammar/schema/optional_attr/simple_1/stdout.golden @@ -0,0 +1,6 @@ +person1: + name: Alice + age: null +person2: + name: Bob + age: 10 diff --git a/test/grammar/schema/relaxed/complex/main.k b/test/grammar/schema/relaxed/complex/main.k new file mode 100644 index 000000000..41d1a8c77 --- /dev/null +++ b/test/grammar/schema/relaxed/complex/main.k @@ -0,0 +1,23 @@ +schema Data: + spec: Spec + +schema Spec: + config: Config + +schema Config: + template: Template + +schema Template: + [...str]: int + name: str + +data = Data { + spec: { + config: { + template: { + name: "template" + id: 1 + } + } + } +} diff --git a/test/grammar/schema/relaxed/complex/stdout.golden b/test/grammar/schema/relaxed/complex/stdout.golden new file mode 100644 index 000000000..373d3582f --- /dev/null +++ b/test/grammar/schema/relaxed/complex/stdout.golden @@ -0,0 +1,6 @@ +data: + spec: + config: + template: + name: template + id: 1 diff --git a/test/grammar/schema/relaxed/simple/main.k b/test/grammar/schema/relaxed/simple/main.k new file mode 100644 index 000000000..1b17ac128 --- /dev/null +++ b/test/grammar/schema/relaxed/simple/main.k @@ -0,0 +1,9 @@ +schema Person: + [str]: str + firstName : str = "John" + lastName : str + +JohnDoe = Person { + "lastName": "Doe", + "fullName": "Tom Cat" +} diff --git a/test/grammar/schema/relaxed/simple/stdout.golden b/test/grammar/schema/relaxed/simple/stdout.golden new file mode 100644 index 000000000..8ea82eb9e --- /dev/null +++ b/test/grammar/schema/relaxed/simple/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + fullName: Tom Cat diff --git a/test/grammar/schema/same_name_fail/main.k b/test/grammar/schema/same_name_fail/main.k new file mode 100644 index 000000000..91bcd9288 --- /dev/null +++ b/test/grammar/schema/same_name_fail/main.k @@ -0,0 +1,9 @@ +schema Person: + name: str = "kcl" + age: int = 1 + +schema Person: + aa: int + +x0 = Person{} +x1 = Person{age:101} \ No newline at end of file diff --git a/test/grammar/schema/same_name_fail/stderr.golden.py b/test/grammar/schema/same_name_fail/stderr.golden.py new file mode 100644 index 000000000..ece44c526 --- /dev/null +++ b/test/grammar/schema/same_name_fail/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.UniqueKeyError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=1 + ) + ], + arg_msg=kcl_error.UNIQUE_KEY_MSG.format("Person") + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/simple/main.k b/test/grammar/schema/simple/main.k new file mode 100644 index 000000000..9e84a04a3 --- /dev/null +++ b/test/grammar/schema/simple/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + lastName: str + +JohnDoe = Person { + firstName: "John", + lastName: "Doe" +} diff --git a/test/grammar/schema/simple/stdout.golden b/test/grammar/schema/simple/stdout.golden new file mode 100644 index 000000000..19b06330f --- /dev/null +++ b/test/grammar/schema/simple/stdout.golden @@ -0,0 +1,3 @@ +JohnDoe: + firstName: John + lastName: Doe diff --git a/test/grammar/schema/stmt_block/stmt_block_0/main.k b/test/grammar/schema/stmt_block/stmt_block_0/main.k new file mode 100644 index 000000000..5ebd90ef7 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_0/main.k @@ -0,0 +1,13 @@ +schema Person: + firstName: str = "John" + lastName: str + name: str = _name + + if len(lastName) > 0: + _name = firstName + " " + lastName + else: + _name = firstName + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_0/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_0/stdout.golden new file mode 100644 index 000000000..ad05e9b01 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_0/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + name: John Doe \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_1/main.k b/test/grammar/schema/stmt_block/stmt_block_1/main.k new file mode 100644 index 000000000..97eb08843 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_1/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str = "John" + lastName: str + name: str = (firstName + " " + lastName) if len(lastName) > 0 else firstName + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_1/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_1/stdout.golden new file mode 100644 index 000000000..ad05e9b01 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_1/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + name: John Doe \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_10/main.k b/test/grammar/schema/stmt_block/stmt_block_10/main.k new file mode 100644 index 000000000..0402a418b --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_10/main.k @@ -0,0 +1,19 @@ +schema PersonMixin: + fullName = "{} {}".format(firstName, lastName) + +schema SonMixin: + sonName = "{} {}".format(firstName, lastName) + +schema Person: + mixin [PersonMixin] + + firstName: str = "John" + lastName: str = "Doe" + +schema Son(Person): + mixin [SonMixin] + + sonName: str + firstName: str + +AceDoe = Son {} diff --git a/test/grammar/schema/stmt_block/stmt_block_10/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_10/stdout.golden new file mode 100644 index 000000000..105d885c4 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_10/stdout.golden @@ -0,0 +1,5 @@ +AceDoe: + firstName: John + lastName: Doe + fullName: John Doe + sonName: John Doe diff --git a/test/grammar/schema/stmt_block/stmt_block_11/main.k b/test/grammar/schema/stmt_block/stmt_block_11/main.k new file mode 100644 index 000000000..3aec5dcd9 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_11/main.k @@ -0,0 +1,23 @@ + +schema PersonMixin: + fullName: str = "{} {}".format(firstName, lastName) + # print("PersonMixin") + +schema SonMixin: + sonName: str = "{} {}".format(firstName, lastName) + # print("SonMixin") + +schema Person: + mixin [PersonMixin] + # print("PersonStmt") + firstName: str = "John" + lastName: str = "Doe" + +schema Son(Person): + mixin [SonMixin] + # print("SonStmt") + sonName: str + firstName: str + +AceDoe = Son {} +# print("===") diff --git a/test/grammar/schema/stmt_block/stmt_block_11/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_11/stdout.golden new file mode 100644 index 000000000..105d885c4 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_11/stdout.golden @@ -0,0 +1,5 @@ +AceDoe: + firstName: John + lastName: Doe + fullName: John Doe + sonName: John Doe diff --git a/test/grammar/schema/stmt_block/stmt_block_12/main.k b/test/grammar/schema/stmt_block/stmt_block_12/main.k new file mode 100644 index 000000000..423c0e74c --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_12/main.k @@ -0,0 +1,14 @@ +schema Person: + name: str + age: int + gender: str + info_with_type: str = "{}, {}, {} years old".format(name, gender, age) + info_without_type: str = "{}, {}, {} years old".format(name, gender, age) + +schema Girl(Person): + gender: str = "female" + +alice = Girl { + "name": "alice", + "age": 10 +} diff --git a/test/grammar/schema/stmt_block/stmt_block_12/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_12/stdout.golden new file mode 100644 index 000000000..b9de8819e --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_12/stdout.golden @@ -0,0 +1,6 @@ +alice: + name: alice + age: 10 + gender: female + info_with_type: alice, female, 10 years old + info_without_type: alice, female, 10 years old \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_13/main.k b/test/grammar/schema/stmt_block/stmt_block_13/main.k new file mode 100644 index 000000000..e1fdbf41e --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_13/main.k @@ -0,0 +1,21 @@ +schema Person: + name: str + age: int + status: str + + _status_aged = "aged" + _status_adult = "adult" + _status_minor = "minor" + + if age > 18: + if age < 70: + status = _status_adult + else: + status = _status_aged + else: + status = _status_minor + +JohnDoe = Person { + "name": "John", + "age": 80 +} diff --git a/test/grammar/schema/stmt_block/stmt_block_13/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_13/stdout.golden new file mode 100644 index 000000000..537cfb847 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_13/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + name: John + age: 80 + status: aged \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_14/main.k b/test/grammar/schema/stmt_block/stmt_block_14/main.k new file mode 100644 index 000000000..3499ebf1d --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_14/main.k @@ -0,0 +1,9 @@ +schema Test: + a: str + b: str = "a={}".format(a) + c: str + +t = Test { + a: "v1", + c: "v2" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_14/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_14/stdout.golden new file mode 100644 index 000000000..4e6ec8dc7 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_14/stdout.golden @@ -0,0 +1,4 @@ +t: + a: v1 + b: a=v1 + c: v2 \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_15/main.k b/test/grammar/schema/stmt_block/stmt_block_15/main.k new file mode 100644 index 000000000..9d222239b --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_15/main.k @@ -0,0 +1,10 @@ +schema Test: + a: str + b: str = "a={}".format(a) + c: str + +t = Test { + a = "v1", + b = "v2", + c = "v3" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_15/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_15/stdout.golden new file mode 100644 index 000000000..8faeb99eb --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_15/stdout.golden @@ -0,0 +1,4 @@ +t: + a: v1 + b: v2 + c: v3 \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_16/main.k b/test/grammar/schema/stmt_block/stmt_block_16/main.k new file mode 100644 index 000000000..ed341b94d --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_16/main.k @@ -0,0 +1,7 @@ +_dictlist = [{"key1": "value1", "key2": "value2"}, {"key1": "value3", "key2": "value4"}] +_keys = ["key1", "key2"] + +schema Person: + names: {str:} = {dl[k]: k + ":" + dl[k] for dl in _dictlist for k in _keys} + +alice = Person {} diff --git a/test/grammar/schema/stmt_block/stmt_block_16/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_16/stdout.golden new file mode 100644 index 000000000..42481a2c3 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_16/stdout.golden @@ -0,0 +1,6 @@ +alice: + names: + value1: key1:value1 + value2: key2:value2 + value3: key1:value3 + value4: key2:value4 diff --git a/test/grammar/schema/stmt_block/stmt_block_17/main.k b/test/grammar/schema/stmt_block/stmt_block_17/main.k new file mode 100644 index 000000000..8e937b9c6 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_17/main.k @@ -0,0 +1,14 @@ +schema Test: + app: str + meta: {str:str} = { + "name": "value", + "test": app + } + labels: {str:str} = { + "otherLabels": "value", + **meta + } + +instance = Test { + app: "override_app" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_17/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_17/stdout.golden new file mode 100644 index 000000000..a78a62ff7 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_17/stdout.golden @@ -0,0 +1,9 @@ +instance: + app: override_app + meta: + name: value + test: override_app + labels: + otherLabels: value + name: value + test: override_app diff --git a/test/grammar/schema/stmt_block/stmt_block_18/main.k b/test/grammar/schema/stmt_block/stmt_block_18/main.k new file mode 100644 index 000000000..1285825a9 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_18/main.k @@ -0,0 +1,16 @@ +schema Test: + app: str = "" + name: str + meta: {str:} = { + "name": "value", + "test": app + } + labels: {str:} = { + "otherLabels": name, + **meta + } + +instance = Test { + app = "override_app" + name = "value" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_18/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_18/stdout.golden new file mode 100644 index 000000000..a871f771e --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_18/stdout.golden @@ -0,0 +1,10 @@ +instance: + app: override_app + name: value + meta: + name: value + test: override_app + labels: + otherLabels: value + name: value + test: override_app diff --git a/test/grammar/schema/stmt_block/stmt_block_19/main.k b/test/grammar/schema/stmt_block/stmt_block_19/main.k new file mode 100644 index 000000000..9bf44326f --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_19/main.k @@ -0,0 +1,5 @@ +schema Person: + labels: {str: str} = {"key": "value"} + data: [str] = [name for name in labels if labels[name] == "value"] + +person = Person {} diff --git a/test/grammar/schema/stmt_block/stmt_block_19/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_19/stdout.golden new file mode 100644 index 000000000..b58ee57dd --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_19/stdout.golden @@ -0,0 +1,5 @@ +person: + labels: + key: value + data: + - key diff --git a/test/grammar/schema/stmt_block/stmt_block_2/main.k b/test/grammar/schema/stmt_block/stmt_block_2/main.k new file mode 100644 index 000000000..5b8a9c63f --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_2/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName: str = "John" + lastName: str + names: [str] = [firstName, lastName] + namePair: {str:} = {"${firstName}": lastName} + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_2/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_2/stdout.golden new file mode 100644 index 000000000..a1e1b60dd --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_2/stdout.golden @@ -0,0 +1,8 @@ +JohnDoe: + firstName: John + lastName: Doe + names: + - John + - Doe + namePair: + John: Doe \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_20/main.k b/test/grammar/schema/stmt_block/stmt_block_20/main.k new file mode 100644 index 000000000..efdbf216d --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_20/main.k @@ -0,0 +1,5 @@ +schema Person: + labels: {str:str} = {"key": "value"} + data: {str:str} = {name: labels[name] for name in labels if labels[name] == "value" and name} + +person = Person {} diff --git a/test/grammar/schema/stmt_block/stmt_block_20/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_20/stdout.golden new file mode 100644 index 000000000..b770804b7 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_20/stdout.golden @@ -0,0 +1,5 @@ +person: + labels: + key: value + data: + key: value diff --git a/test/grammar/schema/stmt_block/stmt_block_21/main.k b/test/grammar/schema/stmt_block/stmt_block_21/main.k new file mode 100644 index 000000000..a255fe8ed --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_21/main.k @@ -0,0 +1,28 @@ +schema Config[data]: + name: str = "appName" + globalAppName: str = name + affinity?: {str:} = { + "podAntiAffinity": { + "preferredDuringSchedulingIgnoredDuringExecution": [ + { + "weight": 100, + "podAffinityTerm": { + "labelSelector": { + "matchExpressions": [ + { + "key": "cluster.k8s/app-name", + "operator": "In", + "values": [ + globalAppName + ] + } + ] + }, + "topologyKey": "kubernetes.io/hostname" + } + } + ] + } + } if data and data.antiSelf else None + +config = [Config({"antiSelf": True}) {}, Config(None) {}, Config({"antiSelf": False}) {}] diff --git a/test/grammar/schema/stmt_block/stmt_block_21/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_21/stdout.golden new file mode 100644 index 000000000..3a21fd765 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_21/stdout.golden @@ -0,0 +1,21 @@ +config: +- name: appName + globalAppName: appName + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: cluster.k8s/app-name + operator: In + values: + - appName + topologyKey: kubernetes.io/hostname +- name: appName + globalAppName: appName + affinity: null +- name: appName + globalAppName: appName + affinity: null diff --git a/test/grammar/schema/stmt_block/stmt_block_22/main.k b/test/grammar/schema/stmt_block/stmt_block_22/main.k new file mode 100644 index 000000000..6ae4eaa3e --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_22/main.k @@ -0,0 +1,13 @@ +schema Data: + name: str = "dataName" + _data: [int] = [] + _hc: [int] = [] + if _hc and len(_hc) > 0: + if len(_hc) > 3: + temp1 = _hc + elif len(_hc) > 1: + temp2 = _hc + else: + temp3 = _hc + +data = Data {} diff --git a/test/grammar/schema/stmt_block/stmt_block_22/settings.yaml b/test/grammar/schema/stmt_block/stmt_block_22/settings.yaml new file mode 100644 index 000000000..6dcdd25b7 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_22/settings.yaml @@ -0,0 +1 @@ +kcl_options: -n diff --git a/test/grammar/schema/stmt_block/stmt_block_22/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_22/stdout.golden new file mode 100644 index 000000000..dcf05d498 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_22/stdout.golden @@ -0,0 +1,2 @@ +data: + name: dataName diff --git a/test/grammar/schema/stmt_block/stmt_block_23/main.k b/test/grammar/schema/stmt_block/stmt_block_23/main.k new file mode 100644 index 000000000..d78ea6903 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_23/main.k @@ -0,0 +1,21 @@ +schema PersonMixin: + headcount: [int] = [i for i in [1, 2, 3]] + _dict: {str:} = {"key1": "value1", "key2": "value2"} + keyValue: {str:} = {key: _dict[key] for key in _dict} + fullName: str = "{} {}".format(firstName, lastName) + +schema SonMixin: + sonName = "{} {}".format(firstName, lastName) + +schema Person: + mixin [PersonMixin] + + firstName: str = "John" + lastName: str = "Doe" + +schema Son(Person): + mixin [SonMixin] + + sonName: str + +AceDoe = Son {} diff --git a/test/grammar/schema/stmt_block/stmt_block_23/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_23/stdout.golden new file mode 100644 index 000000000..3711e41cd --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_23/stdout.golden @@ -0,0 +1,12 @@ +AceDoe: + firstName: John + lastName: Doe + headcount: + - 1 + - 2 + - 3 + keyValue: + key1: value1 + key2: value2 + fullName: John Doe + sonName: John Doe diff --git a/test/grammar/schema/stmt_block/stmt_block_24/main.k b/test/grammar/schema/stmt_block/stmt_block_24/main.k new file mode 100644 index 000000000..2c9251a38 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_24/main.k @@ -0,0 +1,6 @@ +schema Data: + i: int = 0 + data: [int] = [1, 2, 3] + dataNew: [int] = [i * 2 for i in data] + +data = Data {} diff --git a/test/grammar/schema/stmt_block/stmt_block_24/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_24/stdout.golden new file mode 100644 index 000000000..c518b9601 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_24/stdout.golden @@ -0,0 +1,10 @@ +data: + i: 0 + data: + - 1 + - 2 + - 3 + dataNew: + - 2 + - 4 + - 6 diff --git a/test/grammar/schema/stmt_block/stmt_block_25/main.k b/test/grammar/schema/stmt_block/stmt_block_25/main.k new file mode 100644 index 000000000..2b058c1d2 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_25/main.k @@ -0,0 +1,17 @@ +schema Name: + name: str + +schema Person: + names?: [Name] + +_names = [Name { + name: "Alice" +}] + +_names += [Name { + name: "Bob" +}] + +person = Person { + names: _names +} diff --git a/test/grammar/schema/stmt_block/stmt_block_25/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_25/stdout.golden new file mode 100644 index 000000000..21eaf14b2 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_25/stdout.golden @@ -0,0 +1,4 @@ +person: + names: + - name: Alice + - name: Bob diff --git a/test/grammar/schema/stmt_block/stmt_block_26/main.k b/test/grammar/schema/stmt_block/stmt_block_26/main.k new file mode 100644 index 000000000..03dabb36e --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_26/main.k @@ -0,0 +1,5 @@ +schema Person: + name?: str = _name + _name = None + +person = Person {} diff --git a/test/grammar/schema/stmt_block/stmt_block_26/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_26/stdout.golden new file mode 100644 index 000000000..3763503d3 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_26/stdout.golden @@ -0,0 +1,2 @@ +person: + name: null diff --git a/test/grammar/schema/stmt_block/stmt_block_27/main.k b/test/grammar/schema/stmt_block/stmt_block_27/main.k new file mode 100644 index 000000000..54a80f268 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_27/main.k @@ -0,0 +1,10 @@ +schema Name: + name: str + +schema Person: + names: [str] = [name for name in ["Alice", "Bob", "John"]] + name: Name = Name { + name: names[0] + } + +person = Person {} diff --git a/test/grammar/schema/stmt_block/stmt_block_27/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_27/stdout.golden new file mode 100644 index 000000000..131987879 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_27/stdout.golden @@ -0,0 +1,7 @@ +person: + names: + - Alice + - Bob + - John + name: + name: Alice diff --git a/test/grammar/schema/stmt_block/stmt_block_28/main.k b/test/grammar/schema/stmt_block/stmt_block_28/main.k new file mode 100644 index 000000000..8bc205b6f --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_28/main.k @@ -0,0 +1,10 @@ +person = Person { + name: "name" + base: "base" +} + +schema Person(Parent): + name: str + +schema Parent: + base: str diff --git a/test/grammar/schema/stmt_block/stmt_block_28/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_28/stdout.golden new file mode 100644 index 000000000..b15030eaf --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_28/stdout.golden @@ -0,0 +1,3 @@ +person: + base: base + name: name diff --git a/test/grammar/schema/stmt_block/stmt_block_29/main.k b/test/grammar/schema/stmt_block/stmt_block_29/main.k new file mode 100644 index 000000000..19a02e4a9 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_29/main.k @@ -0,0 +1,10 @@ +schema Person(Parent): + name: str + +schema Parent: + base: str + +person = Person { + name: "name" + base: "base" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_29/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_29/stdout.golden new file mode 100644 index 000000000..b15030eaf --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_29/stdout.golden @@ -0,0 +1,3 @@ +person: + base: base + name: name diff --git a/test/grammar/schema/stmt_block/stmt_block_3/main.k b/test/grammar/schema/stmt_block/stmt_block_3/main.k new file mode 100644 index 000000000..0d300076a --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_3/main.k @@ -0,0 +1,11 @@ +_separator_out_side = '-' + +schema Person: + firstName: str = "John" + lastName: str + _separator: str = '+' + name: str = firstName + _separator + _separator_out_side + lastName + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_3/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_3/stdout.golden new file mode 100644 index 000000000..6f69310fc --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_3/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + name: John+-Doe \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_30/main.k b/test/grammar/schema/stmt_block/stmt_block_30/main.k new file mode 100644 index 000000000..a0d786a3b --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_30/main.k @@ -0,0 +1,10 @@ +schema Parent: + base: str + +person = Person { + name: "name" + base: "base" +} + +schema Person(Parent): + name: str diff --git a/test/grammar/schema/stmt_block/stmt_block_30/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_30/stdout.golden new file mode 100644 index 000000000..b15030eaf --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_30/stdout.golden @@ -0,0 +1,3 @@ +person: + base: base + name: name diff --git a/test/grammar/schema/stmt_block/stmt_block_31/main.k b/test/grammar/schema/stmt_block/stmt_block_31/main.k new file mode 100644 index 000000000..8c45a5122 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_31/main.k @@ -0,0 +1,14 @@ +_alice = A { + _name: { + first: "aa" + } +} + +_alice._name |= {last: "value"} + +schema Name: + first: str + last?: str + +schema A: + _name: Name diff --git a/test/grammar/schema/stmt_block/stmt_block_31/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_31/stdout.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/stmt_block/stmt_block_32/main.k b/test/grammar/schema/stmt_block/stmt_block_32/main.k new file mode 100644 index 000000000..ebf2ea3c5 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_32/main.k @@ -0,0 +1,19 @@ +_alice = A { + name: { + first: "aa" + age: 1 + } +} + +_alice.name |= {last: "value"} +_alice.name.age = _alice.name.age + 1 +_alice.name.age += 1 +alice = _alice + +schema Name: + first: str + last?: str + age?: int + +schema A: + name: Name diff --git a/test/grammar/schema/stmt_block/stmt_block_32/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_32/stdout.golden new file mode 100644 index 000000000..f7684a0e7 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_32/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + first: aa + last: value + age: 3 diff --git a/test/grammar/schema/stmt_block/stmt_block_33/main.k b/test/grammar/schema/stmt_block/stmt_block_33/main.k new file mode 100644 index 000000000..f753a9d88 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_33/main.k @@ -0,0 +1,21 @@ +schema Name: + first: str + last?: str + age?: int + +schema A: + name: Name + +schema Data: + _alice = A { + name: { + first: "aa" + age: 1 + } + } + + _alice.name |= {last: "value"} + _alice.name.age += 1 + alice = _alice + +data = Data {} diff --git a/test/grammar/schema/stmt_block/stmt_block_33/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_33/stdout.golden new file mode 100644 index 000000000..ebf4c39f8 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_33/stdout.golden @@ -0,0 +1,6 @@ +data: + alice: + name: + first: aa + last: value + age: 2 diff --git a/test/grammar/schema/stmt_block/stmt_block_34/main.k b/test/grammar/schema/stmt_block/stmt_block_34/main.k new file mode 100644 index 000000000..5443990fa --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_34/main.k @@ -0,0 +1,10 @@ +schema Person: + name: str = "kcl" + age: int = 1 + +person = Person { + **{k: v for k, v in { + name: None + age: Undefined + }} +} diff --git a/test/grammar/schema/stmt_block/stmt_block_34/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_34/stdout.golden new file mode 100644 index 000000000..4ada95546 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_34/stdout.golden @@ -0,0 +1,3 @@ +person: + name: kcl + age: 1 diff --git a/test/grammar/schema/stmt_block/stmt_block_4/main.k b/test/grammar/schema/stmt_block/stmt_block_4/main.k new file mode 100644 index 000000000..a1fb1527c --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_4/main.k @@ -0,0 +1,11 @@ +schema Person: + firstName: str = "John" + lastName: str + name: str = "Doe" + + _name = "123" + name += _name + +JohnDoe = Person { + "lastName": "Doe", +} diff --git a/test/grammar/schema/stmt_block/stmt_block_4/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_4/stdout.golden new file mode 100644 index 000000000..a97f10325 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_4/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + name: Doe123 \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_5/main.k b/test/grammar/schema/stmt_block/stmt_block_5/main.k new file mode 100644 index 000000000..2752e148c --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_5/main.k @@ -0,0 +1,11 @@ +schema Person: + firstName: str = "John" + lastName: str + age: int = 12 + + if age >= 18: + lastName = "Doe" + +JohnDoe = Person { + "age" = 18 +} diff --git a/test/grammar/schema/stmt_block/stmt_block_5/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_5/stdout.golden new file mode 100644 index 000000000..bedf50f4f --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_5/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + age: 18 \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_6/main.k b/test/grammar/schema/stmt_block/stmt_block_6/main.k new file mode 100644 index 000000000..ea1a3c081 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_6/main.k @@ -0,0 +1,11 @@ +schema Person: + firstName: str = "John" + lastName: str + name: str = (firstName + " " + lastName) if len(lastName) > 0 else firstName + +schema Son(Person): + name = "Son" + +son = Son { + "lastName": "Doe" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_6/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_6/stdout.golden new file mode 100644 index 000000000..27b2ea86d --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_6/stdout.golden @@ -0,0 +1,4 @@ +son: + firstName: John + lastName: Doe + name: Son \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_7/main.k b/test/grammar/schema/stmt_block/stmt_block_7/main.k new file mode 100644 index 000000000..17865e476 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_7/main.k @@ -0,0 +1,17 @@ +schema Person: + firstName: str = "John" + lastName: str = "Doe" + headcount: [int] + keyValue: {str:} + + headcount |= [1, 2, 3] + headcount += [4, 5, 6] + + keyValue |= {"key1": "value1"} + _keyValue = {"key2": "value2"} + keyValue = { + **keyValue, + **_keyValue + } + +JohnDoe = Person {} diff --git a/test/grammar/schema/stmt_block/stmt_block_7/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_7/stdout.golden new file mode 100644 index 000000000..88b27a675 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_7/stdout.golden @@ -0,0 +1,13 @@ +JohnDoe: + firstName: John + lastName: Doe + headcount: + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + keyValue: + key1: value1 + key2: value2 \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_8/main.k b/test/grammar/schema/stmt_block/stmt_block_8/main.k new file mode 100644 index 000000000..d4da1f0df --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_8/main.k @@ -0,0 +1,10 @@ +schema Person: + firstName: str = "John" + lastName: str = "Doe" + + headcount: [int] = [i for i in [1, 2, 3]] + + _dict: {str:} = {"key1": "value1", "key2": "value2"} + keyValue: {str:} = {key: _dict[key] for key in _dict} + +JohnDoe = Person {} diff --git a/test/grammar/schema/stmt_block/stmt_block_8/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_8/stdout.golden new file mode 100644 index 000000000..02b00677f --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_8/stdout.golden @@ -0,0 +1,10 @@ +JohnDoe: + firstName: John + lastName: Doe + headcount: + - 1 + - 2 + - 3 + keyValue: + key1: value1 + key2: value2 \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_9/main.k b/test/grammar/schema/stmt_block/stmt_block_9/main.k new file mode 100644 index 000000000..c8fc5bbf8 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_9/main.k @@ -0,0 +1,13 @@ +schema PersonMixin: + headcount: [int] = [i for i in [1, 2, 3]] + _dict: {str:} = {"key1": "value1", "key2": "value2"} + keyValue: {str:} = {key: _dict[key] for key in _dict} + fullName: str = "{} {}".format(firstName, lastName) + +schema Person: + mixin [PersonMixin] + + firstName: str = "John" + lastName: str = "Doe" + +JohnDoe = Person {} diff --git a/test/grammar/schema/stmt_block/stmt_block_9/stdout.golden b/test/grammar/schema/stmt_block/stmt_block_9/stdout.golden new file mode 100644 index 000000000..3fc8ffa57 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_9/stdout.golden @@ -0,0 +1,11 @@ +JohnDoe: + firstName: John + lastName: Doe + headcount: + - 1 + - 2 + - 3 + keyValue: + key1: value1 + key2: value2 + fullName: John Doe \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_cycle_fail_0/_main.k b/test/grammar/schema/stmt_block/stmt_block_cycle_fail_0/_main.k new file mode 100644 index 000000000..778942b3d --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_cycle_fail_0/_main.k @@ -0,0 +1,13 @@ +schema Son: + son_field: str + parent: Parent = None + +schema Parent[name](Son): + field: str = name + son: Son = Son { + parent: Parent(name) { + "field": "123" + } + } + +parent = Parent(name = "123") {} diff --git a/test/grammar/schema/stmt_block/stmt_block_cycle_fail_0/stderr.golden.py b/test/grammar/schema/stmt_block/stmt_block_cycle_fail_0/stderr.golden.py new file mode 100644 index 000000000..5ee369e26 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_cycle_fail_0/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.RecursionError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + ), + ], + arg_msg="maximum recursion depth exceeded in comparison") + , file=sys.stdout +) + diff --git a/test/grammar/schema/stmt_block/stmt_block_cycle_fail_1/_main.k b/test/grammar/schema/stmt_block/stmt_block_cycle_fail_1/_main.k new file mode 100644 index 000000000..806334d27 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_cycle_fail_1/_main.k @@ -0,0 +1,15 @@ +schema Parent: + field: str = "123" + son: Son = Son { + parent: Parent { + "field": "123" + } + } + +schema Son(Parent): + son_field: str + parent: Parent = Parent {} + +parent = Parent { + "field" = "name" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_cycle_fail_1/stderr.golden.py b/test/grammar/schema/stmt_block/stmt_block_cycle_fail_1/stderr.golden.py new file mode 100644 index 000000000..a28a83371 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_cycle_fail_1/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.RecursionError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + ), + ], + arg_msg="maximum recursion depth exceeded in comparison") + , file=sys.stdout +) + diff --git a/test/grammar/schema/stmt_block/stmt_block_fail_0/main.k b/test/grammar/schema/stmt_block/stmt_block_fail_0/main.k new file mode 100644 index 000000000..cd5bb5e7e --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_fail_0/main.k @@ -0,0 +1,11 @@ +schema Person: + firstName: str = "John" + lastName: str + name: str + + _name: int = 123 + name = _name + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_fail_0/stderr.golden.py b/test/grammar/schema/stmt_block/stmt_block_fail_0/stderr.golden.py new file mode 100644 index 000000000..92cc27027 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_fail_0/stderr.golden.py @@ -0,0 +1,21 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got int" + ), + ], + arg_msg='expect str, got int') + , file=sys.stdout +) + diff --git a/test/grammar/schema/stmt_block/stmt_block_fail_1/main.k b/test/grammar/schema/stmt_block/stmt_block_fail_1/main.k new file mode 100644 index 000000000..3c8c82264 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_fail_1/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str = "John" + lastName: str + name: str + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/stmt_block/stmt_block_fail_1/stderr.golden.py b/test/grammar/schema/stmt_block/stmt_block_fail_1/stderr.golden.py new file mode 100644 index 000000000..347394ed5 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_fail_1/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + ), + ], + arg_msg="attribute 'name' of Person is required and can't be None or Undefined") + , file=sys.stdout +) + diff --git a/test/grammar/schema/stmt_block/stmt_block_fail_2/main.k b/test/grammar/schema/stmt_block/stmt_block_fail_2/main.k new file mode 100644 index 000000000..02d8d515a --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_fail_2/main.k @@ -0,0 +1,15 @@ +schema Person: + firstName: str = "John" + lastName: str + name: str + sonName: str = None + + if sonName + "123" == "123": + name = sonName + +schema Son(Person): + sonName: str = None + +JohnDoe = Son { + "lastName": "Doe", +} diff --git a/test/grammar/schema/stmt_block/stmt_block_fail_2/stderr.golden.py b/test/grammar/schema/stmt_block/stmt_block_fail_2/stderr.golden.py new file mode 100644 index 000000000..68f167c86 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_fail_2/stderr.golden.py @@ -0,0 +1,18 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + ), + ], + arg_msg='unsupported operand type(s) for +: \'NoneType\' and \'str\'') + , file=sys.stdout +) diff --git a/test/grammar/schema/stmt_block/stmt_block_fail_3/main.k b/test/grammar/schema/stmt_block/stmt_block_fail_3/main.k new file mode 100644 index 000000000..af3d1507d --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_fail_3/main.k @@ -0,0 +1,10 @@ +schema Parent: + a?: str + b?: str + c?: str + +schema Son: + assert False, "assert in schema" + +parent = Parent {} +son = Son {} diff --git a/test/grammar/schema/stmt_block/stmt_block_fail_3/stderr.golden.py b/test/grammar/schema/stmt_block/stmt_block_fail_3/stderr.golden.py new file mode 100644 index 000000000..c9202e8f0 --- /dev/null +++ b/test/grammar/schema/stmt_block/stmt_block_fail_3/stderr.golden.py @@ -0,0 +1,19 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.AssertionError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + ), + ], + arg_msg='assert in schema') + , file=sys.stdout +) + diff --git a/test/grammar/schema/type/combination/main.k b/test/grammar/schema/type/combination/main.k new file mode 100644 index 000000000..652d12a73 --- /dev/null +++ b/test/grammar/schema/type/combination/main.k @@ -0,0 +1,16 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +alice = Person { + name: Name { + firstName: "Alice", + lastName: "Terry" + }, + age: 12 +} + \ No newline at end of file diff --git a/test/grammar/schema/type/combination/stdout.golden b/test/grammar/schema/type/combination/stdout.golden new file mode 100644 index 000000000..d920c84af --- /dev/null +++ b/test/grammar/schema/type/combination/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + firstName: Alice + lastName: Terry + age: 12 diff --git a/test/grammar/schema/type/combination_1/main.k b/test/grammar/schema/type/combination_1/main.k new file mode 100644 index 000000000..45e2da1ec --- /dev/null +++ b/test/grammar/schema/type/combination_1/main.k @@ -0,0 +1,32 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema Group: + name: str + persons: [Person] + +schema Model: + name: str + persons: [{str:}] + groupModel: Group = { + "name": name, + "persons": persons + } + +alice = Model { + "name": "myModel", + "persons": [ + { + "name": { + "firstName": "Alice", + "lastName": "Green" + }, + "age": 10 + } + ] +} diff --git a/test/grammar/schema/type/combination_1/stdout.golden b/test/grammar/schema/type/combination_1/stdout.golden new file mode 100644 index 000000000..bef5af36a --- /dev/null +++ b/test/grammar/schema/type/combination_1/stdout.golden @@ -0,0 +1,14 @@ +alice: + name: myModel + persons: + - name: + firstName: Alice + lastName: Green + age: 10 + groupModel: + name: myModel + persons: + - name: + firstName: Alice + lastName: Green + age: 10 diff --git a/test/grammar/schema/type/combination_2/main.k b/test/grammar/schema/type/combination_2/main.k new file mode 100644 index 000000000..48d83ca48 --- /dev/null +++ b/test/grammar/schema/type/combination_2/main.k @@ -0,0 +1,23 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema Model: + name: {str:str} + age: int + personModel: Person = { + "name": name, + "age": age + } + +alice = Model { + "name": { + "firstName": "alice", + "lastName": "Green" + }, + "age": 10 +} diff --git a/test/grammar/schema/type/combination_2/stdout.golden b/test/grammar/schema/type/combination_2/stdout.golden new file mode 100644 index 000000000..0c9b8462e --- /dev/null +++ b/test/grammar/schema/type/combination_2/stdout.golden @@ -0,0 +1,10 @@ +alice: + name: + firstName: alice + lastName: Green + age: 10 + personModel: + name: + firstName: alice + lastName: Green + age: 10 \ No newline at end of file diff --git a/test/grammar/schema/type/combination_3/main.k b/test/grammar/schema/type/combination_3/main.k new file mode 100644 index 000000000..68859d7a2 --- /dev/null +++ b/test/grammar/schema/type/combination_3/main.k @@ -0,0 +1,30 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema Rule: + entity: Person + access: [str] + +schema Model: + access: [str] + user: {str:} + rule: Rule = { + "access": access, + "entity": user + } + +alice = Model { + "user": { + "name": { + "firstName": "alice", + "lastName": "Green" + }, + "age": 10 + }, + "access": ["read", "write"] +} diff --git a/test/grammar/schema/type/combination_3/stdout.golden b/test/grammar/schema/type/combination_3/stdout.golden new file mode 100644 index 000000000..bcdbdef0f --- /dev/null +++ b/test/grammar/schema/type/combination_3/stdout.golden @@ -0,0 +1,18 @@ +alice: + access: + - read + - write + user: + name: + firstName: alice + lastName: Green + age: 10 + rule: + entity: + name: + firstName: alice + lastName: Green + age: 10 + access: + - read + - write \ No newline at end of file diff --git a/test/grammar/schema/type/combination_4/main.k b/test/grammar/schema/type/combination_4/main.k new file mode 100644 index 000000000..a0ac2de69 --- /dev/null +++ b/test/grammar/schema/type/combination_4/main.k @@ -0,0 +1,43 @@ +schema RuleMixin: + if rule: + ruleModel = Rule { + "entity": { + "name": rule["name"], + "relatives": rule["relatives"] + }, + "access": rule["access"] + } + +schema Family: + relation: str + name: str + +schema Person: + name: str + relatives: [Family] + +schema Rule: + kind: str = "Rule" + access: [str] + entity: Person + __settings__: {str:} = { + "output_type": "STANDALONE" + } + +schema Model: + mixin [RuleMixin] + rule: {str:} + ruleModel: Rule + +alice = Model { + "rule": { + "name": "alice", + "relatives": [ + { + "relation": "father", + "name": "Reese" + } + ], + "access": ["read", "write"] + } +} diff --git a/test/grammar/schema/type/combination_4/stdout.golden b/test/grammar/schema/type/combination_4/stdout.golden new file mode 100644 index 000000000..8a6f91af1 --- /dev/null +++ b/test/grammar/schema/type/combination_4/stdout.golden @@ -0,0 +1,19 @@ +alice: + rule: + name: alice + relatives: + - relation: father + name: Reese + access: + - read + - write +--- +kind: Rule +access: +- read +- write +entity: + name: alice + relatives: + - relation: father + name: Reese \ No newline at end of file diff --git a/test/grammar/schema/type/combination_5_type_fail/main.k b/test/grammar/schema/type/combination_5_type_fail/main.k new file mode 100644 index 000000000..ae9508635 --- /dev/null +++ b/test/grammar/schema/type/combination_5_type_fail/main.k @@ -0,0 +1,32 @@ +schema Name: + firstName: int + lastName: str + +schema Person: + name: Name + age: int = 20 + +schema Group: + name: str + persons: [Person] + +schema Model: + name: str + persons: [{str:}] + groupModel: Group = { + "name": name, + "persons": persons + } + +alice = Model { + "name": "myModel", + "persons": [ + { + "name": Name { + "firstName": "Alice", + "lastName": "Green" + }, + "age": 10 + } + ] +} diff --git a/test/grammar/schema/type/combination_5_type_fail/stderr.golden.py b/test/grammar/schema/type/combination_5_type_fail/stderr.golden.py new file mode 100644 index 000000000..83eb88b56 --- /dev/null +++ b/test/grammar/schema/type/combination_5_type_fail/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect int", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=26, + col_no=17, + arg_msg="got str(Alice)" + ) + ], + arg_msg="expect int, got str(Alice)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/config_expr_index_signature_fail/main.k b/test/grammar/schema/type/config_expr_index_signature_fail/main.k new file mode 100644 index 000000000..e568a1831 --- /dev/null +++ b/test/grammar/schema/type/config_expr_index_signature_fail/main.k @@ -0,0 +1,12 @@ +schema Name: + age: int = 10 + [...str]: int + +schema Person: + name: Name + +John = Person { + name: { + "classID" = "aa" + } +} diff --git a/test/grammar/schema/type/config_expr_index_signature_fail/stderr.golden.py b/test/grammar/schema/type/config_expr_index_signature_fail/stderr.golden.py new file mode 100644 index 000000000..e27b6925a --- /dev/null +++ b/test/grammar/schema/type/config_expr_index_signature_fail/stderr.golden.py @@ -0,0 +1,29 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + arg_msg="expect int", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=9, + arg_msg="got str(aa)" + ) + ], + arg_msg="expect int, got str(aa)" + ), + file=sys.stdout +) diff --git a/test/grammar/schema/type/config_expr_type_fail_0/main.k b/test/grammar/schema/type/config_expr_type_fail_0/main.k new file mode 100644 index 000000000..278837640 --- /dev/null +++ b/test/grammar/schema/type/config_expr_type_fail_0/main.k @@ -0,0 +1,51 @@ +schema Name: + name: str = "Kiki" + age: int = 10 + ID: int = 1 + +schema Name0: + name0: Name + +schema Name1: + name1: Name0 + +schema Name2: + name2: Name1 + +schema Name3: + name3: Name2 + +schema Name4: + name4: Name3 + +schema Name5: + name5: Name4 + +schema Name6: + name6: Name5 + +schema Name7: + name7: Name6 + +schema Person: + name: Name7 + +John = Person{ + name.name7.name6:{ + "name5": Name4{ + "name4":{ + "name3": Name2{ + "name2": { + "name1": { + "name0":{ + if True: age = 200 + elif False: ID = "123" + else: "name" : "Tom" + } + } + } + } + } + } + } +} diff --git a/test/grammar/schema/type/config_expr_type_fail_0/stderr.golden.py b/test/grammar/schema/type/config_expr_type_fail_0/stderr.golden.py new file mode 100644 index 000000000..29d577e3b --- /dev/null +++ b/test/grammar/schema/type/config_expr_type_fail_0/stderr.golden.py @@ -0,0 +1,28 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=5, + arg_msg="expect int", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=42, + col_no=44, + arg_msg="got str(123)" + ), + ], + arg_msg="expect int, got str(123)") + , file=sys.stdout +) + diff --git a/test/grammar/schema/type/config_expr_type_fail_1/main.k b/test/grammar/schema/type/config_expr_type_fail_1/main.k new file mode 100644 index 000000000..736a318c7 --- /dev/null +++ b/test/grammar/schema/type/config_expr_type_fail_1/main.k @@ -0,0 +1,13 @@ +schema Name: + name: str = "Kiki" + age: int = 10 + +schema Name0: + name0: Name + +schema Person: + name: Name0 + +John = Person{ + name.name0.name = 123 +} diff --git a/test/grammar/schema/type/config_expr_type_fail_1/stderr.golden.py b/test/grammar/schema/type/config_expr_type_fail_1/stderr.golden.py new file mode 100644 index 000000000..a43657872 --- /dev/null +++ b/test/grammar/schema/type/config_expr_type_fail_1/stderr.golden.py @@ -0,0 +1,31 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect str", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=12, + col_no=5, + arg_msg="got int(123)" + ), + ], + arg_msg="expect str, got int(123)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/config_expr_type_fail_2/main.k b/test/grammar/schema/type/config_expr_type_fail_2/main.k new file mode 100644 index 000000000..63b5e69b6 --- /dev/null +++ b/test/grammar/schema/type/config_expr_type_fail_2/main.k @@ -0,0 +1,43 @@ +schema Name: + name: str = "Kiki" + age: int = 10 + ID: int = 1 + +schema Name0: + name0: Name + +schema Name1: + name1: Name0 + +schema Name2: + name2: Name1 + +schema Name3: + name3: Name2 + +schema Name4: + name4: Name3 + +schema Name5: + name5: Name4 + +schema Name6: + name6: Name5 + +schema Name7: + name7: Name6 + +schema Person: + name: Name7 + +John = Person{ + name.name7.name6:{ + "name5": Name4{ + "name4":{ + "name3": Name2{ + "name2": 1 + } + } + } + } +} diff --git a/test/grammar/schema/type/config_expr_type_fail_2/stderr.golden.py b/test/grammar/schema/type/config_expr_type_fail_2/stderr.golden.py new file mode 100644 index 000000000..4910e263a --- /dev/null +++ b/test/grammar/schema/type/config_expr_type_fail_2/stderr.golden.py @@ -0,0 +1,28 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=13, + col_no=5, + arg_msg="expect Name1", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=38, + col_no=21, + arg_msg="got int(1)" + ), + ], + arg_msg="expect Name1, got int(1)") + , file=sys.stdout +) + diff --git a/test/grammar/schema/type/config_expr_type_fail_3/main.k b/test/grammar/schema/type/config_expr_type_fail_3/main.k new file mode 100644 index 000000000..c07181a06 --- /dev/null +++ b/test/grammar/schema/type/config_expr_type_fail_3/main.k @@ -0,0 +1,44 @@ +schema Name: + name: str = "Kiki" + age: int = 10 + ID: int = 1 + +schema Name0: + name0: Name + +schema Name1: + name1: Name0 + +schema Name2: + name2: Name1 + +schema Name3: + name3: Name2 + +schema Name4: + name4: Name3 + +schema Name5: + name5: Name4 + +schema Name6: + name6: Name5 + +schema Name7: + name7: Name6 + +schema Person: + name: Name7 + +John = Person{ + name.name7.name6 = { + name5.name4: { + name3.name2 = { + name1.name0 :{ + name = "test_name" + age = "123" + } + } + } + } +} diff --git a/test/grammar/schema/type/config_expr_type_fail_3/stderr.golden.py b/test/grammar/schema/type/config_expr_type_fail_3/stderr.golden.py new file mode 100644 index 000000000..dd5bf12bb --- /dev/null +++ b/test/grammar/schema/type/config_expr_type_fail_3/stderr.golden.py @@ -0,0 +1,28 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + arg_msg="expect int", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=39, + col_no=25, + arg_msg="got str(123)" + ), + ], + arg_msg="expect int, got str(123)") + , file=sys.stdout +) + diff --git a/test/grammar/schema/type/dict_0/main.k b/test/grammar/schema/type/dict_0/main.k new file mode 100644 index 000000000..9c9b355ed --- /dev/null +++ b/test/grammar/schema/type/dict_0/main.k @@ -0,0 +1,6 @@ +schema Person: + labels: {str:str} + +alice = Person { + "labels": {"skin": "yellow"} +} diff --git a/test/grammar/schema/type/dict_0/stdout.golden b/test/grammar/schema/type/dict_0/stdout.golden new file mode 100644 index 000000000..37aaa884e --- /dev/null +++ b/test/grammar/schema/type/dict_0/stdout.golden @@ -0,0 +1,3 @@ +alice: + labels: + skin: yellow \ No newline at end of file diff --git a/test/grammar/schema/type/dict_1/main.k b/test/grammar/schema/type/dict_1/main.k new file mode 100644 index 000000000..936ac1f7e --- /dev/null +++ b/test/grammar/schema/type/dict_1/main.k @@ -0,0 +1,10 @@ +schema Person: + labels: {str:} + +alice = Person { + "labels": { + "skin": "yellow", + "height": 100, + "female": True + } +} \ No newline at end of file diff --git a/test/grammar/schema/type/dict_1/stdout.golden b/test/grammar/schema/type/dict_1/stdout.golden new file mode 100644 index 000000000..de5464820 --- /dev/null +++ b/test/grammar/schema/type/dict_1/stdout.golden @@ -0,0 +1,5 @@ +alice: + labels: + skin: yellow + height: 100 + female: true \ No newline at end of file diff --git a/test/grammar/schema/type/dict_2/main.k b/test/grammar/schema/type/dict_2/main.k new file mode 100644 index 000000000..9109247d1 --- /dev/null +++ b/test/grammar/schema/type/dict_2/main.k @@ -0,0 +1,6 @@ +schema Person: + labels: {str:str} + +alice = Person { + "labels": {} +} diff --git a/test/grammar/schema/type/dict_2/stdout.golden b/test/grammar/schema/type/dict_2/stdout.golden new file mode 100644 index 000000000..f38bb1983 --- /dev/null +++ b/test/grammar/schema/type/dict_2/stdout.golden @@ -0,0 +1,2 @@ +alice: + labels: {} \ No newline at end of file diff --git a/test/grammar/schema/type/dict_4/main.k b/test/grammar/schema/type/dict_4/main.k new file mode 100644 index 000000000..b78b051ec --- /dev/null +++ b/test/grammar/schema/type/dict_4/main.k @@ -0,0 +1,6 @@ +schema Person: + labels: {str:str} + +alice = Person { + "labels": {"key": None} +} diff --git a/test/grammar/schema/type/dict_4/stdout.golden b/test/grammar/schema/type/dict_4/stdout.golden new file mode 100644 index 000000000..f53c02b4e --- /dev/null +++ b/test/grammar/schema/type/dict_4/stdout.golden @@ -0,0 +1,3 @@ +alice: + labels: + key: null \ No newline at end of file diff --git a/test/grammar/schema/type/dict_5/main.k b/test/grammar/schema/type/dict_5/main.k new file mode 100644 index 000000000..37e668df8 --- /dev/null +++ b/test/grammar/schema/type/dict_5/main.k @@ -0,0 +1,28 @@ +schema Config[data]: + name: str = "appName" + globalAppName: str = name + affinity: {str:} = { + "podAntiAffinity": { + "preferredDuringSchedulingIgnoredDuringExecution": [ + { + "weight": 100, + "podAffinityTerm": { + "labelSelector": { + "matchExpressions": [ + { + "key": "cluster.k8s/app-name", + "operator": "In", + "values": [ + globalAppName + ] + } + ] + }, + "topologyKey": "kubernetes.io/hostname" + } + } + ] + } + } if data and data.antiSelf else None + +config = Config({"antiSelf": True}) {} diff --git a/test/grammar/schema/type/dict_5/stdout.golden b/test/grammar/schema/type/dict_5/stdout.golden new file mode 100644 index 000000000..8859a257c --- /dev/null +++ b/test/grammar/schema/type/dict_5/stdout.golden @@ -0,0 +1,15 @@ +config: + name: appName + globalAppName: appName + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: cluster.k8s/app-name + operator: In + values: + - appName + topologyKey: kubernetes.io/hostname diff --git a/test/grammar/schema/type/dict_6/main.k b/test/grammar/schema/type/dict_6/main.k new file mode 100644 index 000000000..be2ee05f7 --- /dev/null +++ b/test/grammar/schema/type/dict_6/main.k @@ -0,0 +1,15 @@ +schema Data: + labels: {str:} + +schema Config: + data: Data + +data = Data { + labels.key: "value" +} + +config = Config { + data: data if data else { + labels.key: "value" + } +} diff --git a/test/grammar/schema/type/dict_6/stdout.golden b/test/grammar/schema/type/dict_6/stdout.golden new file mode 100644 index 000000000..2b98926aa --- /dev/null +++ b/test/grammar/schema/type/dict_6/stdout.golden @@ -0,0 +1,7 @@ +data: + labels: + key: value +config: + data: + labels: + key: value diff --git a/test/grammar/schema/type/dict_7/main.k b/test/grammar/schema/type/dict_7/main.k new file mode 100644 index 000000000..8132f0702 --- /dev/null +++ b/test/grammar/schema/type/dict_7/main.k @@ -0,0 +1,15 @@ +schema Data: + labels: {str:} + +schema Config: + data: Data + +data = Config { + data.labels.key: "value" +} + +config = Config { + **(data if data else { + data.labels.key: "value" + }) +} diff --git a/test/grammar/schema/type/dict_7/stdout.golden b/test/grammar/schema/type/dict_7/stdout.golden new file mode 100644 index 000000000..f55740d23 --- /dev/null +++ b/test/grammar/schema/type/dict_7/stdout.golden @@ -0,0 +1,8 @@ +data: + data: + labels: + key: value +config: + data: + labels: + key: value diff --git a/test/grammar/schema/type/dict_8/main.k b/test/grammar/schema/type/dict_8/main.k new file mode 100644 index 000000000..a4950500f --- /dev/null +++ b/test/grammar/schema/type/dict_8/main.k @@ -0,0 +1,14 @@ +schema Role1: + name: str + +schema Role2: + name: str + +schema Config: + data: Role1 | Role2 + +data = Config { + data: Role1 { + name: "name" + } +} diff --git a/test/grammar/schema/type/dict_8/stdout.golden b/test/grammar/schema/type/dict_8/stdout.golden new file mode 100644 index 000000000..5319c6156 --- /dev/null +++ b/test/grammar/schema/type/dict_8/stdout.golden @@ -0,0 +1,3 @@ +data: + data: + name: name diff --git a/test/grammar/schema/type/dict_fail_0/main.k b/test/grammar/schema/type/dict_fail_0/main.k new file mode 100644 index 000000000..6471e220c --- /dev/null +++ b/test/grammar/schema/type/dict_fail_0/main.k @@ -0,0 +1,6 @@ +schema Person: + labels: {str:str} + +alice = Person { + "labels": {None: None} +} diff --git a/test/grammar/schema/type/dict_fail_0/stderr.golden.py b/test/grammar/schema/type/dict_fail_0/stderr.golden.py new file mode 100644 index 000000000..19959e21b --- /dev/null +++ b/test/grammar/schema/type/dict_fail_0/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.IllegalAttributeError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=16 + ) + ], + arg_msg="type 'NoneType'" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/dict_nested_0/main.k b/test/grammar/schema/type/dict_nested_0/main.k new file mode 100644 index 000000000..9863f0bb2 --- /dev/null +++ b/test/grammar/schema/type/dict_nested_0/main.k @@ -0,0 +1,11 @@ +schema Person: + labels: {str:} + +alice = Person { + "labels": { + "skin": "yellow", + "height": 100, + "female": True, + "family": {"father":"John"} + } +} diff --git a/test/grammar/schema/type/dict_nested_0/stdout.golden b/test/grammar/schema/type/dict_nested_0/stdout.golden new file mode 100644 index 000000000..70a132b39 --- /dev/null +++ b/test/grammar/schema/type/dict_nested_0/stdout.golden @@ -0,0 +1,7 @@ +alice: + labels: + skin: yellow + height: 100 + female: true + family: + father: John \ No newline at end of file diff --git a/test/grammar/schema/type/dict_nested_1/main.k b/test/grammar/schema/type/dict_nested_1/main.k new file mode 100644 index 000000000..3e0e254e0 --- /dev/null +++ b/test/grammar/schema/type/dict_nested_1/main.k @@ -0,0 +1,11 @@ +schema Person: + labels: {str:} + +alice = Person { + "labels": { + "skin": "yellow", + "height": 100, + "female": True, + "teacher": ["Finch", "Root", "Shaw"] + } +} diff --git a/test/grammar/schema/type/dict_nested_1/stdout.golden b/test/grammar/schema/type/dict_nested_1/stdout.golden new file mode 100644 index 000000000..9ca3ac1fe --- /dev/null +++ b/test/grammar/schema/type/dict_nested_1/stdout.golden @@ -0,0 +1,9 @@ +alice: + labels: + skin: yellow + height: 100 + female: true + teacher: + - Finch + - Root + - Shaw \ No newline at end of file diff --git a/test/grammar/schema/type/dict_nested_2/kcl.mod b/test/grammar/schema/type/dict_nested_2/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/type/dict_nested_2/main.k b/test/grammar/schema/type/dict_nested_2/main.k new file mode 100644 index 000000000..94f8acd10 --- /dev/null +++ b/test/grammar/schema/type/dict_nested_2/main.k @@ -0,0 +1,10 @@ +import pkg + +person = pkg.Person { + name: { + spec: [{ + "image": "image", + "name": "name" + }] + } +} diff --git a/test/grammar/schema/type/dict_nested_2/pkg/container.k b/test/grammar/schema/type/dict_nested_2/pkg/container.k new file mode 100644 index 000000000..ece41cfaa --- /dev/null +++ b/test/grammar/schema/type/dict_nested_2/pkg/container.k @@ -0,0 +1,3 @@ +schema Container: + image?: str + name?: str diff --git a/test/grammar/schema/type/dict_nested_2/pkg/person.k b/test/grammar/schema/type/dict_nested_2/pkg/person.k new file mode 100644 index 000000000..8ca75b291 --- /dev/null +++ b/test/grammar/schema/type/dict_nested_2/pkg/person.k @@ -0,0 +1,6 @@ +schema Name: + image?: str = None + spec?: [Container] + +schema Person: + name?: Name diff --git a/test/grammar/schema/type/dict_nested_2/stdout.golden b/test/grammar/schema/type/dict_nested_2/stdout.golden new file mode 100644 index 000000000..9d3a96486 --- /dev/null +++ b/test/grammar/schema/type/dict_nested_2/stdout.golden @@ -0,0 +1,6 @@ +person: + name: + image: null + spec: + - image: image + name: name diff --git a/test/grammar/schema/type/dict_nested_fail_0/main.k b/test/grammar/schema/type/dict_nested_fail_0/main.k new file mode 100644 index 000000000..4281f5440 --- /dev/null +++ b/test/grammar/schema/type/dict_nested_fail_0/main.k @@ -0,0 +1,19 @@ +schema Person: + labels: {str:} + ids: {str:{str:{str:str}}} + +alice = Person { + "labels": { + "skin": "yellow", + "height": 100, + "female": True, + "family": {"father":"John"} + } + ids: { + "name1":{ + "name2":{ + "name3":123 + } + } + } +} diff --git a/test/grammar/schema/type/dict_nested_fail_0/stderr.golden.py b/test/grammar/schema/type/dict_nested_fail_0/stderr.golden.py new file mode 100644 index 000000000..626b1d7ff --- /dev/null +++ b/test/grammar/schema/type/dict_nested_fail_0/stderr.golden.py @@ -0,0 +1,27 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + arg_msg="expect str", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=15, + col_no=17, + arg_msg="got int(123)" + ), + ], + arg_msg='expect str, got int(123)') + , file=sys.stdout +) + diff --git a/test/grammar/schema/type/list/main.k b/test/grammar/schema/type/list/main.k new file mode 100644 index 000000000..f9f342ba6 --- /dev/null +++ b/test/grammar/schema/type/list/main.k @@ -0,0 +1,6 @@ +schema Person: + teacher: [str] + +alice = Person { + "teacher": ["Finch", "Root", "Shaw"] +} diff --git a/test/grammar/schema/type/list/stdout.golden b/test/grammar/schema/type/list/stdout.golden new file mode 100644 index 000000000..1fbb2053e --- /dev/null +++ b/test/grammar/schema/type/list/stdout.golden @@ -0,0 +1,5 @@ +alice: + teacher: + - Finch + - Root + - Shaw \ No newline at end of file diff --git a/test/grammar/schema/type/list_nested_0/main.k b/test/grammar/schema/type/list_nested_0/main.k new file mode 100644 index 000000000..17d4e3701 --- /dev/null +++ b/test/grammar/schema/type/list_nested_0/main.k @@ -0,0 +1,13 @@ +schema Person: + labels: [{str:str}] + +alice = Person { + "labels": [ + { + "skin": "yellow" + }, + { + "father": "John" + } + ] +} \ No newline at end of file diff --git a/test/grammar/schema/type/list_nested_0/stdout.golden b/test/grammar/schema/type/list_nested_0/stdout.golden new file mode 100644 index 000000000..7c5148f5e --- /dev/null +++ b/test/grammar/schema/type/list_nested_0/stdout.golden @@ -0,0 +1,4 @@ +alice: + labels: + - skin: yellow + - father: John \ No newline at end of file diff --git a/test/grammar/schema/type/list_nested_1/main.k b/test/grammar/schema/type/list_nested_1/main.k new file mode 100644 index 000000000..ff1650a20 --- /dev/null +++ b/test/grammar/schema/type/list_nested_1/main.k @@ -0,0 +1,12 @@ +schema Person: + labels: [{str:}] + +alice = Person { + "labels": [ + { + "skin": "yellow", + "height": 100, + "female": True + } + ] +} \ No newline at end of file diff --git a/test/grammar/schema/type/list_nested_1/stdout.golden b/test/grammar/schema/type/list_nested_1/stdout.golden new file mode 100644 index 000000000..2e203aacc --- /dev/null +++ b/test/grammar/schema/type/list_nested_1/stdout.golden @@ -0,0 +1,5 @@ +alice: + labels: + - skin: yellow + height: 100 + female: true \ No newline at end of file diff --git a/test/grammar/schema/type/list_nested_2/main.k b/test/grammar/schema/type/list_nested_2/main.k new file mode 100644 index 000000000..6701b0ba6 --- /dev/null +++ b/test/grammar/schema/type/list_nested_2/main.k @@ -0,0 +1,14 @@ +schema Person: + labels: [{str:}] + +alice = Person { + "labels": [ + { + "skin": "yellow", + "height": 100, + "female": True, + "family": {"father": "John"}, + "teacher": ["Finch", "Root", "Shaw"] + } + ] +} diff --git a/test/grammar/schema/type/list_nested_2/stdout.golden b/test/grammar/schema/type/list_nested_2/stdout.golden new file mode 100644 index 000000000..177a9d0a6 --- /dev/null +++ b/test/grammar/schema/type/list_nested_2/stdout.golden @@ -0,0 +1,11 @@ +alice: + labels: + - skin: yellow + height: 100 + female: true + family: + father: John + teacher: + - Finch + - Root + - Shaw \ No newline at end of file diff --git a/test/grammar/schema/type/multi_types_0/main.k b/test/grammar/schema/type/multi_types_0/main.k new file mode 100644 index 000000000..6f9fd08ee --- /dev/null +++ b/test/grammar/schema/type/multi_types_0/main.k @@ -0,0 +1,10 @@ +schema Service: + port: int | str + +srv_0 = Service { + "port": 80 +} + +srv_1 = Service { + "port": "http" +} diff --git a/test/grammar/schema/type/multi_types_0/stdout.golden b/test/grammar/schema/type/multi_types_0/stdout.golden new file mode 100644 index 000000000..b9744fe91 --- /dev/null +++ b/test/grammar/schema/type/multi_types_0/stdout.golden @@ -0,0 +1,4 @@ +srv_0: + port: 80 +srv_1: + port: http \ No newline at end of file diff --git a/test/grammar/schema/type/multi_types_1/main.k b/test/grammar/schema/type/multi_types_1/main.k new file mode 100644 index 000000000..0a12dd6fd --- /dev/null +++ b/test/grammar/schema/type/multi_types_1/main.k @@ -0,0 +1,6 @@ +schema Service: + port: int | str + +srv_0 = Service { + "port": float(80) +} diff --git a/test/grammar/schema/type/multi_types_1/stderr.golden.py b/test/grammar/schema/type/multi_types_1/stderr.golden.py new file mode 100644 index 000000000..47a35ac0d --- /dev/null +++ b/test/grammar/schema/type/multi_types_1/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect int|str", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + arg_msg="got float" + ) + ], + arg_msg="expect int|str, got float" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/multi_types_2/main.k b/test/grammar/schema/type/multi_types_2/main.k new file mode 100644 index 000000000..fde4ae930 --- /dev/null +++ b/test/grammar/schema/type/multi_types_2/main.k @@ -0,0 +1,10 @@ +schema Service: + port: bool | str + +srv_0 = Service { + port: True +} + +srv_1 = Service { + port: "8080" +} diff --git a/test/grammar/schema/type/multi_types_2/stdout.golden b/test/grammar/schema/type/multi_types_2/stdout.golden new file mode 100644 index 000000000..0dd32b282 --- /dev/null +++ b/test/grammar/schema/type/multi_types_2/stdout.golden @@ -0,0 +1,4 @@ +srv_0: + port: true +srv_1: + port: '8080' \ No newline at end of file diff --git a/test/grammar/schema/type/multi_types_3/main.k b/test/grammar/schema/type/multi_types_3/main.k new file mode 100644 index 000000000..43e48428c --- /dev/null +++ b/test/grammar/schema/type/multi_types_3/main.k @@ -0,0 +1,18 @@ +schema Name: + first: str + second: str | bool + +schema Person(Name): + age: int + +aliceGreen = Person { + first: "alice", + second: "Green", + age: 10 +} + +alice = Person { + first: "alice", + second: False, + age: 10 +} diff --git a/test/grammar/schema/type/multi_types_3/stdout.golden b/test/grammar/schema/type/multi_types_3/stdout.golden new file mode 100644 index 000000000..8ef52607c --- /dev/null +++ b/test/grammar/schema/type/multi_types_3/stdout.golden @@ -0,0 +1,8 @@ +aliceGreen: + first: alice + second: Green + age: 10 +alice: + first: alice + second: false + age: 10 \ No newline at end of file diff --git a/test/grammar/schema/type/multi_types_4/main.k b/test/grammar/schema/type/multi_types_4/main.k new file mode 100644 index 000000000..1b7a6aa61 --- /dev/null +++ b/test/grammar/schema/type/multi_types_4/main.k @@ -0,0 +1,13 @@ +schema Person: + name: {str:str} | str + +alice = Person { + name: "alice" +} + +aliceGreen = Person { + name: { + first: "alice", + second: "Green" + } +} diff --git a/test/grammar/schema/type/multi_types_4/stdout.golden b/test/grammar/schema/type/multi_types_4/stdout.golden new file mode 100644 index 000000000..6ef582395 --- /dev/null +++ b/test/grammar/schema/type/multi_types_4/stdout.golden @@ -0,0 +1,6 @@ +alice: + name: alice +aliceGreen: + name: + first: alice + second: Green \ No newline at end of file diff --git a/test/grammar/schema/type/multi_types_5/main.k b/test/grammar/schema/type/multi_types_5/main.k new file mode 100644 index 000000000..097b7ec2a --- /dev/null +++ b/test/grammar/schema/type/multi_types_5/main.k @@ -0,0 +1,10 @@ +schema Service: + port: [str] | str + +srv_0 = Service { + port: ["first", "second"] +} + +srv_1 = Service { + port: "portName" +} diff --git a/test/grammar/schema/type/multi_types_5/stdout.golden b/test/grammar/schema/type/multi_types_5/stdout.golden new file mode 100644 index 000000000..e9b58e083 --- /dev/null +++ b/test/grammar/schema/type/multi_types_5/stdout.golden @@ -0,0 +1,6 @@ +srv_0: + port: + - first + - second +srv_1: + port: portName \ No newline at end of file diff --git a/test/grammar/schema/type/multi_types_6/main.k b/test/grammar/schema/type/multi_types_6/main.k new file mode 100644 index 000000000..86bd5c2aa --- /dev/null +++ b/test/grammar/schema/type/multi_types_6/main.k @@ -0,0 +1,10 @@ +schema Service: + port: [str | int] + +srv_0 = Service { + port: ["port1", "port2"] +} + +srv_1 = Service { + port: [9080, 8080] +} diff --git a/test/grammar/schema/type/multi_types_6/stdout.golden b/test/grammar/schema/type/multi_types_6/stdout.golden new file mode 100644 index 000000000..ad2b2db9c --- /dev/null +++ b/test/grammar/schema/type/multi_types_6/stdout.golden @@ -0,0 +1,8 @@ +srv_0: + port: + - port1 + - port2 +srv_1: + port: + - 9080 + - 8080 \ No newline at end of file diff --git a/test/grammar/schema/type/multi_types_7/main.k b/test/grammar/schema/type/multi_types_7/main.k new file mode 100644 index 000000000..ec69d7abb --- /dev/null +++ b/test/grammar/schema/type/multi_types_7/main.k @@ -0,0 +1,11 @@ +schema Service: + port: {str: int|str} + +srv_0 = Service { + port: { + name: "metrics", + protocol: "TCP", + target: 8080, + containerPort: 8080 + } +} diff --git a/test/grammar/schema/type/multi_types_7/stdout.golden b/test/grammar/schema/type/multi_types_7/stdout.golden new file mode 100644 index 000000000..dc2fc962e --- /dev/null +++ b/test/grammar/schema/type/multi_types_7/stdout.golden @@ -0,0 +1,6 @@ +srv_0: + port: + name: metrics + protocol: TCP + target: 8080 + containerPort: 8080 \ No newline at end of file diff --git a/test/grammar/schema/type/type_dict_0/main.k b/test/grammar/schema/type/type_dict_0/main.k new file mode 100644 index 000000000..1a2668112 --- /dev/null +++ b/test/grammar/schema/type/type_dict_0/main.k @@ -0,0 +1,15 @@ + +schema Person: + firstName : str + cards : {str:} + cards2 : {:int} + cards3 : {str:int} + cards4 : {:} + +JohnDoe = Person { + "firstName": "John", + "cards": {"1":1, "2":2, "3":3}, + "cards2": {"1":1, "2":2, "3":3}, + "cards3": {"1":1, "2":2, "3":3}, + "cards4": {"1":1, "2":2, "3":3} +} diff --git a/test/grammar/schema/type/type_dict_0/stdout.golden b/test/grammar/schema/type/type_dict_0/stdout.golden new file mode 100644 index 000000000..90437c10a --- /dev/null +++ b/test/grammar/schema/type/type_dict_0/stdout.golden @@ -0,0 +1,18 @@ +JohnDoe: + firstName: John + cards: + '1': 1 + '2': 2 + '3': 3 + cards2: + '1': 1 + '2': 2 + '3': 3 + cards3: + '1': 1 + '2': 2 + '3': 3 + cards4: + '1': 1 + '2': 2 + '3': 3 diff --git a/test/grammar/schema/type/type_dict_1/main.k b/test/grammar/schema/type/type_dict_1/main.k new file mode 100644 index 000000000..7e29b6812 --- /dev/null +++ b/test/grammar/schema/type/type_dict_1/main.k @@ -0,0 +1,15 @@ +schema Name: + firstName : str + lastName:str + +schema Person: + name: Name + age: int + +alice = Person{ + "name" : { + "firstName": "Alice", + "lastName": "Terry" + }, + "age" : 12 +} diff --git a/test/grammar/schema/type/type_dict_1/stdout.golden b/test/grammar/schema/type/type_dict_1/stdout.golden new file mode 100644 index 000000000..d920c84af --- /dev/null +++ b/test/grammar/schema/type/type_dict_1/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: + firstName: Alice + lastName: Terry + age: 12 diff --git a/test/grammar/schema/type/type_dict_2/main.k b/test/grammar/schema/type/type_dict_2/main.k new file mode 100644 index 000000000..b84c5bc10 --- /dev/null +++ b/test/grammar/schema/type/type_dict_2/main.k @@ -0,0 +1,22 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema User: + person: Person + phone: int + +alice = User { + "person": { + "name": { + "firstName": "Alice", + "lastName": "Terry" + }, + "age": 12 + }, + "phone": 123 +} diff --git a/test/grammar/schema/type/type_dict_2/stdout.golden b/test/grammar/schema/type/type_dict_2/stdout.golden new file mode 100644 index 000000000..b17d6c04e --- /dev/null +++ b/test/grammar/schema/type/type_dict_2/stdout.golden @@ -0,0 +1,7 @@ +alice: + person: + name: + firstName: Alice + lastName: Terry + age: 12 + phone: 123 \ No newline at end of file diff --git a/test/grammar/schema/type/type_dict_3/main.k b/test/grammar/schema/type/type_dict_3/main.k new file mode 100644 index 000000000..7c653e016 --- /dev/null +++ b/test/grammar/schema/type/type_dict_3/main.k @@ -0,0 +1,20 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema Group: + persons: [Person] + +group = Group { + "persons": [{ + "name": { + "firstName": "Alice", + "lastName": "Terry" + }, + "age": 12 + }] +} diff --git a/test/grammar/schema/type/type_dict_3/stdout.golden b/test/grammar/schema/type/type_dict_3/stdout.golden new file mode 100644 index 000000000..bbe06e819 --- /dev/null +++ b/test/grammar/schema/type/type_dict_3/stdout.golden @@ -0,0 +1,6 @@ +group: + persons: + - name: + firstName: Alice + lastName: Terry + age: 12 \ No newline at end of file diff --git a/test/grammar/schema/type/type_dict_4/main.k b/test/grammar/schema/type/type_dict_4/main.k new file mode 100644 index 000000000..586ca6e42 --- /dev/null +++ b/test/grammar/schema/type/type_dict_4/main.k @@ -0,0 +1,24 @@ + + +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema Group: + persons: {str:Person} + +group = Group { + "persons": { + "1": { + "name": { + "firstName": "Alice", + "lastName": "Terry" + }, + "age": 12 + } + } +} diff --git a/test/grammar/schema/type/type_dict_4/stdout.golden b/test/grammar/schema/type/type_dict_4/stdout.golden new file mode 100644 index 000000000..67efd1087 --- /dev/null +++ b/test/grammar/schema/type/type_dict_4/stdout.golden @@ -0,0 +1,7 @@ +group: + persons: + '1': + name: + firstName: Alice + lastName: Terry + age: 12 diff --git a/test/grammar/schema/type/type_dict_5/main.k b/test/grammar/schema/type/type_dict_5/main.k new file mode 100644 index 000000000..516080db5 --- /dev/null +++ b/test/grammar/schema/type/type_dict_5/main.k @@ -0,0 +1,6 @@ +schema Node: + labels: {str:str} + +node = Node { + "labels": {"kubernetes.io/os": "linux"}, +} diff --git a/test/grammar/schema/type/type_dict_5/stdout.golden b/test/grammar/schema/type/type_dict_5/stdout.golden new file mode 100644 index 000000000..576e9f1f1 --- /dev/null +++ b/test/grammar/schema/type/type_dict_5/stdout.golden @@ -0,0 +1,3 @@ +node: + labels: + kubernetes.io/os: linux diff --git a/test/grammar/schema/type/type_dict_6/main.k b/test/grammar/schema/type/type_dict_6/main.k new file mode 100644 index 000000000..ba8a2c0c1 --- /dev/null +++ b/test/grammar/schema/type/type_dict_6/main.k @@ -0,0 +1,18 @@ +schema Name0: + name: str + +schema Name: + name: Name0 | str + +schema Person: + name: Name + age: int + +person = Person { + name: Name { + name: Name0 { + name: "Alice" + } + } + age: 18 +} diff --git a/test/grammar/schema/type/type_dict_6/stdout.golden b/test/grammar/schema/type/type_dict_6/stdout.golden new file mode 100644 index 000000000..cb9f4c38c --- /dev/null +++ b/test/grammar/schema/type/type_dict_6/stdout.golden @@ -0,0 +1,5 @@ +person: + name: + name: + name: Alice + age: 18 diff --git a/test/grammar/schema/type/type_dict_fail_0/main.k b/test/grammar/schema/type/type_dict_fail_0/main.k new file mode 100644 index 000000000..60650bdc6 --- /dev/null +++ b/test/grammar/schema/type/type_dict_fail_0/main.k @@ -0,0 +1,22 @@ +schema Name: + firstName : str + lastName: int + +schema Person: + name: Name + age: int + +schema User: + person: Person + phone: int + +alice = User { + "person": { + "name" : { + "firstName": "Alice", + "lastName": "Terry" + }, + "age" : 12 + }, + "phone": 123 +} diff --git a/test/grammar/schema/type/type_dict_fail_0/stderr.golden.py b/test/grammar/schema/type/type_dict_fail_0/stderr.golden.py new file mode 100644 index 000000000..33e73efff --- /dev/null +++ b/test/grammar/schema/type/type_dict_fail_0/stderr.golden.py @@ -0,0 +1,27 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=3, + arg_msg="expect int", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=17, + col_no=11, + arg_msg="got str(Terry)" + ), + ], + arg_msg='expect int, got str(Terry)') + , file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_empty_any_0/main.k b/test/grammar/schema/type/type_empty_any_0/main.k new file mode 100644 index 000000000..34ceb1543 --- /dev/null +++ b/test/grammar/schema/type/type_empty_any_0/main.k @@ -0,0 +1,12 @@ +schema Base: + name: str + +schema Person: + info: {:} = {"key": []} + info: {:} = {"key": {}} + info: {:} = {"key": [1, 2, 3]} + info: {:} = {"key": [[]]} + info: {:} = {"key": [[], {}]} + info: {:} = {"key": "value"} + +person = Person {} diff --git a/test/grammar/schema/type/type_empty_any_0/stdout.golden b/test/grammar/schema/type/type_empty_any_0/stdout.golden new file mode 100644 index 000000000..dd9734ac1 --- /dev/null +++ b/test/grammar/schema/type/type_empty_any_0/stdout.golden @@ -0,0 +1,3 @@ +person: + info: + key: value diff --git a/test/grammar/schema/type/type_empty_any_1/main.k b/test/grammar/schema/type/type_empty_any_1/main.k new file mode 100644 index 000000000..4dc4d70f8 --- /dev/null +++ b/test/grammar/schema/type/type_empty_any_1/main.k @@ -0,0 +1,13 @@ +schema Base: + name?: str + +schema Person: + info: [] = [{}] + info: [] = [Base {}, Base {}] + info: [] = [1, 2, 3] + info: [] = [] + info: [] = [[]] + info: [] = [[], {}] + info: [] = [{"key": "value"}] + +person = Person {} diff --git a/test/grammar/schema/type/type_empty_any_1/stdout.golden b/test/grammar/schema/type/type_empty_any_1/stdout.golden new file mode 100644 index 000000000..c1d0e0277 --- /dev/null +++ b/test/grammar/schema/type/type_empty_any_1/stdout.golden @@ -0,0 +1,3 @@ +person: + info: + - key: value diff --git a/test/grammar/schema/type/type_fail_0/main.k b/test/grammar/schema/type/type_fail_0/main.k new file mode 100644 index 000000000..15b1d6f43 --- /dev/null +++ b/test/grammar/schema/type/type_fail_0/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + lastName: int + +JohnDoe = Person { + "firstName": "John", + "lastName": "Doe" +} diff --git a/test/grammar/schema/type/type_fail_0/stderr.golden.py b/test/grammar/schema/type/type_fail_0/stderr.golden.py new file mode 100644 index 000000000..829c37abe --- /dev/null +++ b/test/grammar/schema/type/type_fail_0/stderr.golden.py @@ -0,0 +1,31 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect int", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got str(Doe)" + ), + ], + arg_msg="expect int, got str(Doe)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_1/main.k b/test/grammar/schema/type/type_fail_1/main.k new file mode 100644 index 000000000..6f083881f --- /dev/null +++ b/test/grammar/schema/type/type_fail_1/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + lastName: str + +JohnDoe = Person { + firstName: "John", + lastName: 12 +} diff --git a/test/grammar/schema/type/type_fail_1/stderr.golden.py b/test/grammar/schema/type/type_fail_1/stderr.golden.py new file mode 100644 index 000000000..e6130025b --- /dev/null +++ b/test/grammar/schema/type/type_fail_1/stderr.golden.py @@ -0,0 +1,31 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect str", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got int(12)" + ) + ], + arg_msg="expect str, got int(12)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_10/main.k b/test/grammar/schema/type/type_fail_10/main.k new file mode 100644 index 000000000..0f4ad08a6 --- /dev/null +++ b/test/grammar/schema/type/type_fail_10/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + cards: [int] + +JohnDoe = Person { + "firstName": "John", + "cards": [1, "2"] +} diff --git a/test/grammar/schema/type/type_fail_10/stderr.golden.py b/test/grammar/schema/type/type_fail_10/stderr.golden.py new file mode 100644 index 000000000..f139cfbb4 --- /dev/null +++ b/test/grammar/schema/type/type_fail_10/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect [int]", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got [int(1)|str(2)]" + ) + ], + arg_msg="expect [int], got [int(1)|str(2)]" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_11/main.k b/test/grammar/schema/type/type_fail_11/main.k new file mode 100644 index 000000000..31bdb253b --- /dev/null +++ b/test/grammar/schema/type/type_fail_11/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + cards: [{int:}] + +JohnDoe = Person { + "firstName": "John", + "cards": [1] +} diff --git a/test/grammar/schema/type/type_fail_11/stderr.golden.py b/test/grammar/schema/type/type_fail_11/stderr.golden.py new file mode 100644 index 000000000..f3114005a --- /dev/null +++ b/test/grammar/schema/type/type_fail_11/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect [{int:any}]", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got [int(1)]" + ) + ], + arg_msg="expect [{int:any}], got [int(1)]" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_12/main.k b/test/grammar/schema/type/type_fail_12/main.k new file mode 100644 index 000000000..ff60317a4 --- /dev/null +++ b/test/grammar/schema/type/type_fail_12/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + cards: [{int:}] + +JohnDoe = Person { + "firstName": "John", + "cards": [{"error": "error"}] +} diff --git a/test/grammar/schema/type/type_fail_12/stderr.golden.py b/test/grammar/schema/type/type_fail_12/stderr.golden.py new file mode 100644 index 000000000..22a7de3e9 --- /dev/null +++ b/test/grammar/schema/type/type_fail_12/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect [{int:any}]", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got [{str(error):str(error)}]" + ) + ], + arg_msg="expect [{int:any}], got [{str(error):str(error)}]" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_13/main.k b/test/grammar/schema/type/type_fail_13/main.k new file mode 100644 index 000000000..0fa898137 --- /dev/null +++ b/test/grammar/schema/type/type_fail_13/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + cards: [{str:}] + +JohnDoe = Person { + "firstName": "John", + "cards": ["error"] +} diff --git a/test/grammar/schema/type/type_fail_13/stderr.golden.py b/test/grammar/schema/type/type_fail_13/stderr.golden.py new file mode 100644 index 000000000..fd471eb18 --- /dev/null +++ b/test/grammar/schema/type/type_fail_13/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect [{str:any}]", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got [str(error)]" + ) + ], + arg_msg="expect [{str:any}], got [str(error)]" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_14/main.k b/test/grammar/schema/type/type_fail_14/main.k new file mode 100644 index 000000000..110f4f640 --- /dev/null +++ b/test/grammar/schema/type/type_fail_14/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + cards: [{str:str}] + +JohnDoe = Person { + "firstName": "John", + "cards": ["error"] +} diff --git a/test/grammar/schema/type/type_fail_14/stderr.golden.py b/test/grammar/schema/type/type_fail_14/stderr.golden.py new file mode 100644 index 000000000..1c418fa4e --- /dev/null +++ b/test/grammar/schema/type/type_fail_14/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect [{str:str}]", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got [str(error)]" + ) + ], + arg_msg="expect [{str:str}], got [str(error)]" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_15/main.k b/test/grammar/schema/type/type_fail_15/main.k new file mode 100644 index 000000000..64a8f53b9 --- /dev/null +++ b/test/grammar/schema/type/type_fail_15/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + cards: [str] + +JohnDoe = Person { + "firstName": "John", + "cards": [{"error": "error"}] +} diff --git a/test/grammar/schema/type/type_fail_15/stderr.golden.py b/test/grammar/schema/type/type_fail_15/stderr.golden.py new file mode 100644 index 000000000..8bbfb554b --- /dev/null +++ b/test/grammar/schema/type/type_fail_15/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect [str]", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got [{str(error):str(error)}]" + ) + ], + arg_msg="expect [str], got [{str(error):str(error)}]" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_16/main.k b/test/grammar/schema/type/type_fail_16/main.k new file mode 100644 index 000000000..a5571c502 --- /dev/null +++ b/test/grammar/schema/type/type_fail_16/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName : str + cards : [str] + +JohnDoe = Person { + "firstName": "John", + "cards": {"error":"error"} +} diff --git a/test/grammar/schema/type/type_fail_16/stderr.golden.py b/test/grammar/schema/type/type_fail_16/stderr.golden.py new file mode 100644 index 000000000..5a23dc2d4 --- /dev/null +++ b/test/grammar/schema/type/type_fail_16/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect [str]", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got {str(error):str(error)}" + ) + ], + arg_msg="expect [str], got {str(error):str(error)}" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_17/main.k b/test/grammar/schema/type/type_fail_17/main.k new file mode 100644 index 000000000..4d34f0e45 --- /dev/null +++ b/test/grammar/schema/type/type_fail_17/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + cards: str + +JohnDoe = Person { + "firstName": "John", + "cards": {"error": "error"} +} diff --git a/test/grammar/schema/type/type_fail_17/stderr.golden.py b/test/grammar/schema/type/type_fail_17/stderr.golden.py new file mode 100644 index 000000000..89f6b69c9 --- /dev/null +++ b/test/grammar/schema/type/type_fail_17/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect str", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got {str(error):str(error)}" + ) + ], + arg_msg="expect str, got {str(error):str(error)}" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_18/main.k b/test/grammar/schema/type/type_fail_18/main.k new file mode 100644 index 000000000..c55f7cc09 --- /dev/null +++ b/test/grammar/schema/type/type_fail_18/main.k @@ -0,0 +1,34 @@ +schema card: + num: str + + check: + num not None + +schema info: + spec: info_spec + +schema info_spec: + info_0: info_0 + +schema info_0: + cards: [card] + +schema Person: + firstName : str + cards: [{str:}] + info0: info + + _spec: {str:} = { + "info_0": { + "cards": cards + } + } + + info0 = info { + "spec": _spec + } + +JohnDoe = Person { + "firstName": "John", + "cards": [card {"num":123}] +} diff --git a/test/grammar/schema/type/type_fail_18/stderr.golden.py b/test/grammar/schema/type/type_fail_18/stderr.golden.py new file mode 100644 index 000000000..90c064c0c --- /dev/null +++ b/test/grammar/schema/type/type_fail_18/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect str", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=33, + col_no=21, + arg_msg="got int(123)" + ) + ], + arg_msg="expect str, got int(123)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_19/kcl.mod b/test/grammar/schema/type/type_fail_19/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/type/type_fail_19/main.k b/test/grammar/schema/type/type_fail_19/main.k new file mode 100644 index 000000000..80d28959d --- /dev/null +++ b/test/grammar/schema/type/type_fail_19/main.k @@ -0,0 +1,20 @@ +import pkg.info + +schema Person: + firstName: str + cards: [{str:}] + info0: info.info + + _spec = { + "info_0": { + "cards": cards + } + } + info0 = info.info { + "spec": _spec + } + +JohnDoe = Person { + "firstName": "John", + "cards": [info.card{"num": 123}] +} diff --git a/test/grammar/schema/type/type_fail_19/pkg/info.k b/test/grammar/schema/type/type_fail_19/pkg/info.k new file mode 100644 index 000000000..f43223f8a --- /dev/null +++ b/test/grammar/schema/type/type_fail_19/pkg/info.k @@ -0,0 +1,14 @@ +schema card: + num: str + + check: + num not None + +schema info: + spec: info_spec + +schema info_spec: + info_0: info_0 + +schema info_0: + cards: [card] diff --git a/test/grammar/schema/type/type_fail_19/stderr.golden.py b/test/grammar/schema/type/type_fail_19/stderr.golden.py new file mode 100644 index 000000000..41a00aafc --- /dev/null +++ b/test/grammar/schema/type/type_fail_19/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/pkg/info.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect str", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=19, + col_no=25, + arg_msg="got int(123)" + ) + ], + arg_msg="expect str, got int(123)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_2/main.k b/test/grammar/schema/type/type_fail_2/main.k new file mode 100644 index 000000000..311c6a021 --- /dev/null +++ b/test/grammar/schema/type/type_fail_2/main.k @@ -0,0 +1,19 @@ +schema Name0: + firstName: str + lastName: str + +schema Name1: + firstName: str + lastName: str + +schema Person: + name: Name0 + age: int + +alice = Person { + "name": Name1 { + "firstName": "Alice", + "lastName": "Terry" + }, + "age": 12 +} diff --git a/test/grammar/schema/type/type_fail_2/stderr.golden.py b/test/grammar/schema/type/type_fail_2/stderr.golden.py new file mode 100644 index 000000000..09dd10936 --- /dev/null +++ b/test/grammar/schema/type/type_fail_2/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=5, + indent_count=1, + arg_msg="expect Name0", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=14, + col_no=5, + arg_msg="got Name1" + ) + ], + arg_msg="expect Name0, got Name1" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_20/kcl.mod b/test/grammar/schema/type/type_fail_20/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/type/type_fail_20/main.k b/test/grammar/schema/type/type_fail_20/main.k new file mode 100644 index 000000000..30b6c6b9c --- /dev/null +++ b/test/grammar/schema/type/type_fail_20/main.k @@ -0,0 +1,20 @@ +import pkg.info + +schema Person: + firstName : str + cards: [{str:}] + info0: info.inf + + _spec = { + "info_0": { + "cards": cards + } + } + info0 = info.info { + "spec": _spec + } + +JohnDoe = Person { + "firstName": "John", + "cards": [{"num":"123"}] +} diff --git a/test/grammar/schema/type/type_fail_20/pkg/info.k b/test/grammar/schema/type/type_fail_20/pkg/info.k new file mode 100644 index 000000000..f43223f8a --- /dev/null +++ b/test/grammar/schema/type/type_fail_20/pkg/info.k @@ -0,0 +1,14 @@ +schema card: + num: str + + check: + num not None + +schema info: + spec: info_spec + +schema info_spec: + info_0: info_0 + +schema info_0: + cards: [card] diff --git a/test/grammar/schema/type/type_fail_20/stderr.golden.py b/test/grammar/schema/type/type_fail_20/stderr.golden.py new file mode 100644 index 000000000..a7776263b --- /dev/null +++ b/test/grammar/schema/type/type_fail_20/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.AttributeError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + ) + ], + arg_msg="module 'pkg.info' has no attribute 'inf'" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_21/kcl.mod b/test/grammar/schema/type/type_fail_21/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/type/type_fail_21/main.k b/test/grammar/schema/type/type_fail_21/main.k new file mode 100644 index 000000000..8251eb47f --- /dev/null +++ b/test/grammar/schema/type/type_fail_21/main.k @@ -0,0 +1,7 @@ +schema Person: + firstName: str + lastName: int + +JohnDoe = Person { + "firstName": "张张张", "lastName": "Doe" +} diff --git a/test/grammar/schema/type/type_fail_21/pkg/info.k b/test/grammar/schema/type/type_fail_21/pkg/info.k new file mode 100644 index 000000000..f43223f8a --- /dev/null +++ b/test/grammar/schema/type/type_fail_21/pkg/info.k @@ -0,0 +1,14 @@ +schema card: + num: str + + check: + num not None + +schema info: + spec: info_spec + +schema info_spec: + info_0: info_0 + +schema info_0: + cards: [card] diff --git a/test/grammar/schema/type/type_fail_21/stderr.golden.py b/test/grammar/schema/type/type_fail_21/stderr.golden.py new file mode 100644 index 000000000..c39365003 --- /dev/null +++ b/test/grammar/schema/type/type_fail_21/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect int", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + col_no=25, + arg_msg="got str(Doe)" + ) + ], + arg_msg="expect int, got str(Doe)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_22/main.k b/test/grammar/schema/type/type_fail_22/main.k new file mode 100644 index 000000000..4ecd23468 --- /dev/null +++ b/test/grammar/schema/type/type_fail_22/main.k @@ -0,0 +1,11 @@ +schema Name: + name: str + +schema Person: + firstName: str | Name + lastName: str + +JohnDoe = Person { + "firstName": 2, + "lastName": "Doe" +} diff --git a/test/grammar/schema/type/type_fail_22/stderr.golden.py b/test/grammar/schema/type/type_fail_22/stderr.golden.py new file mode 100644 index 000000000..9ee902514 --- /dev/null +++ b/test/grammar/schema/type/type_fail_22/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + indent_count=1, + arg_msg="expect str|Name", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=9, + col_no=5, + arg_msg="got int(2)" + ) + ], + arg_msg="expect str|Name, got int(2)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_24/kcl.mod b/test/grammar/schema/type/type_fail_24/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/schema/type/type_fail_24/main.k b/test/grammar/schema/type/type_fail_24/main.k new file mode 100644 index 000000000..1e354454d --- /dev/null +++ b/test/grammar/schema/type/type_fail_24/main.k @@ -0,0 +1,10 @@ +import pkg + +person = pkg.Person { + name: { + spec: { + "image": "image", + "name": "name" + } + } +} diff --git a/test/grammar/schema/type/type_fail_24/pkg/container.k b/test/grammar/schema/type/type_fail_24/pkg/container.k new file mode 100644 index 000000000..ad8b51d7c --- /dev/null +++ b/test/grammar/schema/type/type_fail_24/pkg/container.k @@ -0,0 +1,3 @@ +schema Container: + image: str + name: str diff --git a/test/grammar/schema/type/type_fail_24/pkg/person.k b/test/grammar/schema/type/type_fail_24/pkg/person.k new file mode 100644 index 000000000..974fc7d2d --- /dev/null +++ b/test/grammar/schema/type/type_fail_24/pkg/person.k @@ -0,0 +1,6 @@ +schema Name: + image: str + spec: [Container] + +schema Person: + name: Name diff --git a/test/grammar/schema/type/type_fail_24/stderr.golden.py b/test/grammar/schema/type/type_fail_24/stderr.golden.py new file mode 100644 index 000000000..734a41fa4 --- /dev/null +++ b/test/grammar/schema/type/type_fail_24/stderr.golden.py @@ -0,0 +1,27 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/pkg/person.k", + line_no=3, + col_no=5, + arg_msg="expect [pkg.Container]", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=9, + arg_msg="got {str(image)|str(name):str(image)|str(name)}" + ), + ], + arg_msg="expect [pkg.Container], got {str(image)|str(name):str(image)|str(name)}") + , file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_25/main.k b/test/grammar/schema/type/type_fail_25/main.k new file mode 100644 index 000000000..e94e0f3e5 --- /dev/null +++ b/test/grammar/schema/type/type_fail_25/main.k @@ -0,0 +1,11 @@ +schema Other: + name: str + +schema Base: + name: ErrOther + +base = Base { + name: { + "name": "value" + } +} diff --git a/test/grammar/schema/type/type_fail_25/stderr.golden.py b/test/grammar/schema/type/type_fail_25/stderr.golden.py new file mode 100644 index 000000000..591bedfb0 --- /dev/null +++ b/test/grammar/schema/type/type_fail_25/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + ) + ], + arg_msg="name 'ErrOther' is not defined" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_26/main.k b/test/grammar/schema/type/type_fail_26/main.k new file mode 100644 index 000000000..c7c05c877 --- /dev/null +++ b/test/grammar/schema/type/type_fail_26/main.k @@ -0,0 +1,28 @@ +schema Config[data]: + name: str = "appName" + globalAppName: str = name + affinity: {str:str} = { + "podAntiAffinity": { + "preferredDuringSchedulingIgnoredDuringExecution": [ + { + "weight": 100, + "podAffinityTerm": { + "labelSelector": { + "matchExpressions": [ + { + "key": "cluster.k8s/app-name", + "operator": "In", + "values": [ + globalAppName + ] + } + ] + }, + "topologyKey": "kubernetes.io/hostname" + } + } + ] + } + } if data and data.antiSelf else None + +config = Config({"antiSelf": True}) {} diff --git a/test/grammar/schema/type/type_fail_26/stderr.golden.py b/test/grammar/schema/type/type_fail_26/stderr.golden.py new file mode 100644 index 000000000..8ca6006b8 --- /dev/null +++ b/test/grammar/schema/type/type_fail_26/stderr.golden.py @@ -0,0 +1,22 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=5, + arg_msg="got {str(podAntiAffinity):{str(preferredDuringSchedulingIgnoredDuringExecution):[{str(weight)|str(podAffinityTerm):int(100)|{str(labelSelector)|str(topologyKey):str(kubernetes.io/hostname)|{str(matchExpressions):[{str(key)|str(operator)|str(values):str(cluster.k8s/app-name)|str(In)|[str]}]}}}]}}" + ) + ], + arg_msg="expect {str:str}, got {str(podAntiAffinity):{str(preferredDuringSchedulingIgnoredDuringExecution):[{str(weight)|str(podAffinityTerm):int(100)|{str(labelSelector)|str(topologyKey):str(kubernetes.io/hostname)|{str(matchExpressions):[{str(key)|str(operator)|str(values):str(cluster.k8s/app-name)|str(In)|[str]}]}}}]}}" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_27/_main.k b/test/grammar/schema/type/type_fail_27/_main.k new file mode 100644 index 000000000..cdbbd6611 --- /dev/null +++ b/test/grammar/schema/type/type_fail_27/_main.k @@ -0,0 +1,10 @@ +schema Person: + name: str = "kcl" + age: int = 1 + config?: [str] + +x0 = Person {} +x1: any = x0 +x2 = Person { + config = x1 +} diff --git a/test/grammar/schema/type/type_fail_27/stderr.golden.py b/test/grammar/schema/type/type_fail_27/stderr.golden.py new file mode 100644 index 000000000..503a986d5 --- /dev/null +++ b/test/grammar/schema/type/type_fail_27/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=5, + ) + ], + arg_msg="expect [str], got Person" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_3/main.k b/test/grammar/schema/type/type_fail_3/main.k new file mode 100644 index 000000000..2324adf27 --- /dev/null +++ b/test/grammar/schema/type/type_fail_3/main.k @@ -0,0 +1,23 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema User: + person: Person + phone: int + +alice = User { + "person": { + "name": { + "firstName": "Alice", + "lastName": "Terry", + "fullName": "Alice Terry" + }, + "age": 12 + }, + "phone": 123 +} diff --git a/test/grammar/schema/type/type_fail_3/stderr.golden.py b/test/grammar/schema/type/type_fail_3/stderr.golden.py new file mode 100644 index 000000000..1571ede66 --- /dev/null +++ b/test/grammar/schema/type/type_fail_3/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CannotAddMembers_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=18, + col_no=13, + arg_msg="'fullName' is not defined in schema 'Name'" + ), + ], + arg_msg="Cannot add member 'fullName' to schema 'Name'") + , file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_4/main.k b/test/grammar/schema/type/type_fail_4/main.k new file mode 100644 index 000000000..da98b3bf4 --- /dev/null +++ b/test/grammar/schema/type/type_fail_4/main.k @@ -0,0 +1,9 @@ +schema Person: + firstName: str + lastName: str + +JohnDoe = Person { + "firstName": "John", + "lastName": "Doe", + "fullName": "John Doe" # undefined field +} diff --git a/test/grammar/schema/type/type_fail_4/stderr.golden.py b/test/grammar/schema/type/type_fail_4/stderr.golden.py new file mode 100644 index 000000000..f0b203020 --- /dev/null +++ b/test/grammar/schema/type/type_fail_4/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CannotAddMembers_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=8, + col_no=5, + arg_msg="'fullName' is not defined in schema 'Person'" + ) + ], + arg_msg="Cannot add member 'fullName' to schema 'Person'" + ), + file=sys.stdout +) diff --git a/test/grammar/schema/type/type_fail_5/main.k b/test/grammar/schema/type/type_fail_5/main.k new file mode 100644 index 000000000..361c88eae --- /dev/null +++ b/test/grammar/schema/type/type_fail_5/main.k @@ -0,0 +1,15 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +alice = Person { + "name": Name { + "firstName": {"key": "Alice"}, + "lastName": "Terry" + }, + "age": 12 +} diff --git a/test/grammar/schema/type/type_fail_5/stderr.golden.py b/test/grammar/schema/type/type_fail_5/stderr.golden.py new file mode 100644 index 000000000..461d3aee1 --- /dev/null +++ b/test/grammar/schema/type/type_fail_5/stderr.golden.py @@ -0,0 +1,29 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect str", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=11, + col_no=9, + arg_msg="got {str(key):str(Alice)}" + ) + ], + arg_msg="expect str, got {str(key):str(Alice)}" + ), + file=sys.stdout +) diff --git a/test/grammar/schema/type/type_fail_6/main.k b/test/grammar/schema/type/type_fail_6/main.k new file mode 100644 index 000000000..7e3255269 --- /dev/null +++ b/test/grammar/schema/type/type_fail_6/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + cards: {str:int} + +JohnDoe = Person { + "firstName": "John", + "cards": {"error": "error"} +} diff --git a/test/grammar/schema/type/type_fail_6/stderr.golden.py b/test/grammar/schema/type/type_fail_6/stderr.golden.py new file mode 100644 index 000000000..7e8c12318 --- /dev/null +++ b/test/grammar/schema/type/type_fail_6/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect int", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=15, + arg_msg="got str(error)" + ) + ], + arg_msg="expect int, got str(error)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_7/main.k b/test/grammar/schema/type/type_fail_7/main.k new file mode 100644 index 000000000..aae92b860 --- /dev/null +++ b/test/grammar/schema/type/type_fail_7/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + cards: {int:{int:}} + +JohnDoe = Person { + "firstName": "John", + "cards": {1: 1} +} diff --git a/test/grammar/schema/type/type_fail_7/stderr.golden.py b/test/grammar/schema/type/type_fail_7/stderr.golden.py new file mode 100644 index 000000000..a03ed6d1f --- /dev/null +++ b/test/grammar/schema/type/type_fail_7/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect {int:any}", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=15, + arg_msg="got int(1)" + ) + ], + arg_msg="expect {int:any}, got int(1)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_fail_8/main.k b/test/grammar/schema/type/type_fail_8/main.k new file mode 100644 index 000000000..31e5ac8d2 --- /dev/null +++ b/test/grammar/schema/type/type_fail_8/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName : str + cards : {int:{int:}} + +JohnDoe = Person { + "firstName": "John", + "cards": {1:{"error":"error"}} +} diff --git a/test/grammar/schema/type/type_fail_8/stderr.golden.py b/test/grammar/schema/type/type_fail_8/stderr.golden.py new file mode 100644 index 000000000..2a9e33d00 --- /dev/null +++ b/test/grammar/schema/type/type_fail_8/stderr.golden.py @@ -0,0 +1,29 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect {int:any}", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=15, + arg_msg="got {str(error):str(error)}" + ) + ], + arg_msg="expect {int:any}, got {str(error):str(error)}" + ), + file=sys.stdout +) diff --git a/test/grammar/schema/type/type_fail_9/main.k b/test/grammar/schema/type/type_fail_9/main.k new file mode 100644 index 000000000..15d388dcc --- /dev/null +++ b/test/grammar/schema/type/type_fail_9/main.k @@ -0,0 +1,8 @@ +schema Person: + firstName: str + cards: [int] + +JohnDoe = Person { + "firstName": "John", + "cards": 1 +} diff --git a/test/grammar/schema/type/type_fail_9/stderr.golden.py b/test/grammar/schema/type/type_fail_9/stderr.golden.py new file mode 100644 index 000000000..936db7c2b --- /dev/null +++ b/test/grammar/schema/type/type_fail_9/stderr.golden.py @@ -0,0 +1,29 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect [int]", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got int(1)" + ) + ], + arg_msg="expect [int], got int(1)" + ), + file=sys.stdout +) diff --git a/test/grammar/schema/type/type_fail_default_value_0/main.k b/test/grammar/schema/type/type_fail_default_value_0/main.k new file mode 100644 index 000000000..7e406f6af --- /dev/null +++ b/test/grammar/schema/type/type_fail_default_value_0/main.k @@ -0,0 +1,8 @@ + +schema Person: + firstName: int = "John" + lastName: str + +JohnDoe = Person { + "lastName": "Doe" +} diff --git a/test/grammar/schema/type/type_fail_default_value_0/stderr.golden.py b/test/grammar/schema/type/type_fail_default_value_0/stderr.golden.py new file mode 100644 index 000000000..19121f6fc --- /dev/null +++ b/test/grammar/schema/type/type_fail_default_value_0/stderr.golden.py @@ -0,0 +1,22 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + arg_msg="got str(John)" + ) + ], + arg_msg="expect int, got str(John)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/type/type_list/main.k b/test/grammar/schema/type/type_list/main.k new file mode 100644 index 000000000..4ec76ae97 --- /dev/null +++ b/test/grammar/schema/type/type_list/main.k @@ -0,0 +1,11 @@ + +schema Person: + firstName: str + cards: [int] + cards2: [] + +JohnDoe = Person { + "firstName": "John", + "cards": [1, 2, 3], + "cards2": [1, 2, 3] +} diff --git a/test/grammar/schema/type/type_list/stdout.golden b/test/grammar/schema/type/type_list/stdout.golden new file mode 100644 index 000000000..08f06eb1c --- /dev/null +++ b/test/grammar/schema/type/type_list/stdout.golden @@ -0,0 +1,10 @@ +JohnDoe: + firstName: John + cards: + - 1 + - 2 + - 3 + cards2: + - 1 + - 2 + - 3 diff --git a/test/grammar/schema/type/type_schema_list_0/main.k b/test/grammar/schema/type/type_schema_list_0/main.k new file mode 100644 index 000000000..62cb7f27e --- /dev/null +++ b/test/grammar/schema/type/type_schema_list_0/main.k @@ -0,0 +1,7 @@ +schema Person: + name: str = "Alice" + +schema Table: + persons: [Person] = [Person {}, Person {}] + +table = Table {} diff --git a/test/grammar/schema/type/type_schema_list_0/stdout.golden b/test/grammar/schema/type/type_schema_list_0/stdout.golden new file mode 100644 index 000000000..3bb5dcb6b --- /dev/null +++ b/test/grammar/schema/type/type_schema_list_0/stdout.golden @@ -0,0 +1,4 @@ +table: + persons: + - name: Alice + - name: Alice diff --git a/test/grammar/schema/type/type_schema_list_1/main.k b/test/grammar/schema/type/type_schema_list_1/main.k new file mode 100644 index 000000000..62cb7f27e --- /dev/null +++ b/test/grammar/schema/type/type_schema_list_1/main.k @@ -0,0 +1,7 @@ +schema Person: + name: str = "Alice" + +schema Table: + persons: [Person] = [Person {}, Person {}] + +table = Table {} diff --git a/test/grammar/schema/type/type_schema_list_1/stdout.golden b/test/grammar/schema/type/type_schema_list_1/stdout.golden new file mode 100644 index 000000000..3bb5dcb6b --- /dev/null +++ b/test/grammar/schema/type/type_schema_list_1/stdout.golden @@ -0,0 +1,4 @@ +table: + persons: + - name: Alice + - name: Alice diff --git a/test/grammar/schema/type/type_value_0/main.k b/test/grammar/schema/type/type_value_0/main.k new file mode 100644 index 000000000..ecd948785 --- /dev/null +++ b/test/grammar/schema/type/type_value_0/main.k @@ -0,0 +1,9 @@ +schema Person: + name: str + age: int + +NamedPerson = Person +alice = NamedPerson { + name = "Alice" + age = 18 +} diff --git a/test/grammar/schema/type/type_value_0/stdout.golden b/test/grammar/schema/type/type_value_0/stdout.golden new file mode 100644 index 000000000..3aa06b9d7 --- /dev/null +++ b/test/grammar/schema/type/type_value_0/stdout.golden @@ -0,0 +1,3 @@ +alice: + name: Alice + age: 18 diff --git a/test/grammar/schema/type/type_value_1/main.k b/test/grammar/schema/type/type_value_1/main.k new file mode 100644 index 000000000..8f97a78e8 --- /dev/null +++ b/test/grammar/schema/type/type_value_1/main.k @@ -0,0 +1,14 @@ +schema ConfigA: + id: int = 1 + idStr: str = "id: {}".format(id) + +schema ConfigB: + id: int = 2 + idStr: str = "idStr: {}".format(id) + +configs: {str:} = { + A = ConfigA + B = ConfigB +} +configA = configs.A() +configB = configs.B() diff --git a/test/grammar/schema/type/type_value_1/stdout.golden b/test/grammar/schema/type/type_value_1/stdout.golden new file mode 100644 index 000000000..de5a2c920 --- /dev/null +++ b/test/grammar/schema/type/type_value_1/stdout.golden @@ -0,0 +1,7 @@ +configs: {} +configA: + id: 1 + idStr: 'id: 1' +configB: + id: 2 + idStr: 'idStr: 2' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full/main.k b/test/grammar/schema/type_annotation/defaults/default_values_full/main.k new file mode 100644 index 000000000..cc97c26c4 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full/main.k @@ -0,0 +1,7 @@ +schema A[a1=100, a2=200, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A() \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_full/stdout.golden new file mode 100644 index 000000000..833c75eae --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 100 + b: 200 + c: '300' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_0/main.k b/test/grammar/schema/type_annotation/defaults/default_values_full_0/main.k new file mode 100644 index 000000000..c37761556 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_0/main.k @@ -0,0 +1,7 @@ +schema A[a1=100, a2=200, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_0/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_full_0/stdout.golden new file mode 100644 index 000000000..af874c5aa --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_0/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 200 + c: '300' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_1/main.k b/test/grammar/schema/type_annotation/defaults/default_values_full_1/main.k new file mode 100644 index 000000000..ced296323 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_1/main.k @@ -0,0 +1,7 @@ +schema A[a1=100, a2=200, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a2=2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_1/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_full_1/stdout.golden new file mode 100644 index 000000000..1387b9ad9 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_1/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 100 + b: 2 + c: '300' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_2/main.k b/test/grammar/schema/type_annotation/defaults/default_values_full_2/main.k new file mode 100644 index 000000000..a69126e08 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_2/main.k @@ -0,0 +1,7 @@ +schema A[a1=100, a2=200, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_2/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_full_2/stdout.golden new file mode 100644 index 000000000..f75989c27 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_2/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 100 + b: 200 + c: '3' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_3/main.k b/test/grammar/schema/type_annotation/defaults/default_values_full_3/main.k new file mode 100644 index 000000000..f7d4f0643 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_3/main.k @@ -0,0 +1,7 @@ +schema A[a1=100, a2=200, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_3/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_full_3/stdout.golden new file mode 100644 index 000000000..eacce90ab --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_3/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 100 + b: 2 + c: '3' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_4/main.k b/test/grammar/schema/type_annotation/defaults/default_values_full_4/main.k new file mode 100644 index 000000000..a57c78164 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_4/main.k @@ -0,0 +1,7 @@ +schema A[a1=100, a2=200, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_4/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_full_4/stdout.golden new file mode 100644 index 000000000..e1c352095 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_4/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 200 + c: '3' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_5/main.k b/test/grammar/schema/type_annotation/defaults/default_values_full_5/main.k new file mode 100644 index 000000000..5d43f4676 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_5/main.k @@ -0,0 +1,7 @@ +schema A[a1=100, a2=200, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_5/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_full_5/stdout.golden new file mode 100644 index 000000000..2ef839b6a --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_5/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 2 + c: '300' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_6/main.k b/test/grammar/schema/type_annotation/defaults/default_values_full_6/main.k new file mode 100644 index 000000000..1c396356c --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_6/main.k @@ -0,0 +1,7 @@ +schema A[a1=100, a2=200, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_full_6/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_full_6/stdout.golden new file mode 100644 index 000000000..19a1fa9ca --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_full_6/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 2 + c: '3' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full/main.k new file mode 100644 index 000000000..d36bf9647 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1 = 1,a2 = 2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_not_full/stdout.golden new file mode 100644 index 000000000..19a1fa9ca --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 2 + c: '3' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_0/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_0/main.k new file mode 100644 index 000000000..6940fae51 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_0/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2=200, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1 = 1,a2 = 2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_0/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_not_full_0/stdout.golden new file mode 100644 index 000000000..19a1fa9ca --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_0/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 2 + c: '3' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_1/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_1/main.k new file mode 100644 index 000000000..90e0b1962 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_1/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1 = 1,a2 = 2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_1/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_not_full_1/stdout.golden new file mode 100644 index 000000000..2ef839b6a --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_1/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 2 + c: '300' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_2/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_2/main.k new file mode 100644 index 000000000..0a9224970 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_2/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2=200, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1 = 1) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_2/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_not_full_2/stdout.golden new file mode 100644 index 000000000..af874c5aa --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_2/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 200 + c: '300' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_3/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_3/main.k new file mode 100644 index 000000000..c8c6b979f --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_3/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2=200, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1 = 1,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_3/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_not_full_3/stdout.golden new file mode 100644 index 000000000..e1c352095 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_3/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 200 + c: '3' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_4/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_4/main.k new file mode 100644 index 000000000..33e305195 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_4/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2=200, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1 = 1,a2 = 2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_4/stdout.golden b/test/grammar/schema/type_annotation/defaults/default_values_not_full_4/stdout.golden new file mode 100644 index 000000000..2ef839b6a --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_4/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 2 + c: '300' diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid/main.k new file mode 100644 index 000000000..68a845b63 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_0/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_0/main.k new file mode 100644 index 000000000..b959c7dd2 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_0/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_0/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_0/stderr.golden.py new file mode 100644 index 000000000..3685cf7c4 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_0/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=20, + end_col_no=28, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_1/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_1/main.k new file mode 100644 index 000000000..36d64606c --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_1/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3 = "300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_1/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_1/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_1/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_10/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_10/main.k new file mode 100644 index 000000000..b03921aec --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_10/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_10/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_10/stderr.golden.py new file mode 100644 index 000000000..e846328c0 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_10/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=14, + end_col_no=22, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_11/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_11/main.k new file mode 100644 index 000000000..398101cd7 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_11/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_11/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_11/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_11/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_12/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_12/main.k new file mode 100644 index 000000000..92741d3ef --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_12/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_12/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_12/stderr.golden.py new file mode 100644 index 000000000..3685cf7c4 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_12/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=20, + end_col_no=28, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_13/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_13/main.k new file mode 100644 index 000000000..71ce5c62e --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_13/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3 = "300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_13/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_13/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_13/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_14/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_14/main.k new file mode 100644 index 000000000..7b9d7a56f --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_14/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_14/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_14/stderr.golden.py new file mode 100644 index 000000000..e846328c0 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_14/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=14, + end_col_no=22, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_15/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_15/main.k new file mode 100644 index 000000000..039fc87b1 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_15/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_15/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_15/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_15/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_16/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_16/main.k new file mode 100644 index 000000000..50b8bd976 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_16/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_16/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_16/stderr.golden.py new file mode 100644 index 000000000..3685cf7c4 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_16/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=20, + end_col_no=28, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_17/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_17/main.k new file mode 100644 index 000000000..9d2061372 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_17/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3 = "300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_17/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_17/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_17/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_18/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_18/main.k new file mode 100644 index 000000000..89152a096 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_18/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_18/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_18/stderr.golden.py new file mode 100644 index 000000000..e846328c0 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_18/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=14, + end_col_no=22, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_19/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_19/main.k new file mode 100644 index 000000000..f0e15764d --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_19/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a2=2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_19/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_19/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_19/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_2/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_2/main.k new file mode 100644 index 000000000..2df6f0460 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_2/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_2/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_2/stderr.golden.py new file mode 100644 index 000000000..e846328c0 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_2/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=14, + end_col_no=22, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_20/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_20/main.k new file mode 100644 index 000000000..5063c2cbc --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_20/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a2=2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_20/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_20/stderr.golden.py new file mode 100644 index 000000000..3685cf7c4 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_20/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=20, + end_col_no=28, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_21/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_21/main.k new file mode 100644 index 000000000..70b9e225c --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_21/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3 = "300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a2=2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_21/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_21/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_21/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_22/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_22/main.k new file mode 100644 index 000000000..510a2d492 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_22/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a2=2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_22/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_22/stderr.golden.py new file mode 100644 index 000000000..e846328c0 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_22/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=14, + end_col_no=22, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_23/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_23/main.k new file mode 100644 index 000000000..ca4201316 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_23/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_23/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_23/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_23/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_24/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_24/main.k new file mode 100644 index 000000000..3cadbe619 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_24/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_24/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_24/stderr.golden.py new file mode 100644 index 000000000..3685cf7c4 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_24/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=20, + end_col_no=28, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_25/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_25/main.k new file mode 100644 index 000000000..bdc97350f --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_25/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3 = "300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_25/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_25/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_25/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_26/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_26/main.k new file mode 100644 index 000000000..e491c1f17 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_26/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_26/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_26/stderr.golden.py new file mode 100644 index 000000000..e846328c0 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_26/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=14, + end_col_no=22, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_27/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_27/main.k new file mode 100644 index 000000000..6b9a87ffe --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_27/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A() \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_27/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_27/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_27/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_28/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_28/main.k new file mode 100644 index 000000000..057d00dc0 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_28/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A() \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_28/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_28/stderr.golden.py new file mode 100644 index 000000000..3685cf7c4 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_28/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=20, + end_col_no=28, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_29/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_29/main.k new file mode 100644 index 000000000..4484bd127 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_29/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3 = "300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A() \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_29/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_29/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_29/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_3/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_3/main.k new file mode 100644 index 000000000..e5766bdeb --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_3/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_3/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_3/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_3/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_30/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_30/main.k new file mode 100644 index 000000000..198ba12ae --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_30/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A() \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_30/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_30/stderr.golden.py new file mode 100644 index 000000000..e846328c0 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_30/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=14, + end_col_no=22, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_4/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_4/main.k new file mode 100644 index 000000000..106f86fb2 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_4/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_4/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_4/stderr.golden.py new file mode 100644 index 000000000..3685cf7c4 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_4/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=20, + end_col_no=28, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_5/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_5/main.k new file mode 100644 index 000000000..cdfd44681 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_5/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3 = "300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_5/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_5/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_5/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_6/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_6/main.k new file mode 100644 index 000000000..636d54435 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_6/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_6/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_6/stderr.golden.py new file mode 100644 index 000000000..e846328c0 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_6/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=14, + end_col_no=22, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_7/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_7/main.k new file mode 100644 index 000000000..f7b78cc7e --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_7/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_7/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_7/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_7/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_8/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_8/main.k new file mode 100644 index 000000000..eec0328de --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_8/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2 = 200, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_8/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_8/stderr.golden.py new file mode 100644 index 000000000..3685cf7c4 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_8/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=20, + end_col_no=28, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_9/main.k b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_9/main.k new file mode 100644 index 000000000..e5b7a9794 --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_9/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3 = "300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_9/stderr.golden.py b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_9/stderr.golden.py new file mode 100644 index 000000000..2716275fd --- /dev/null +++ b/test/grammar/schema/type_annotation/defaults/default_values_not_full_invalid_9/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.IllegalArgumentError_Syntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=10, + end_col_no=18, + arg_msg="A default argument" + )], + arg_msg="non-default argument follows default argument" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/type_annotation_inconsistent/main.k b/test/grammar/schema/type_annotation/type_annotation_inconsistent/main.k new file mode 100644 index 000000000..d0977e883 --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_inconsistent/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2: int, a3: int]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/type_annotation_inconsistent/stderr.golden.py b/test/grammar/schema/type_annotation/type_annotation_inconsistent/stderr.golden.py new file mode 100644 index 000000000..c1f9947a9 --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_inconsistent/stderr.golden.py @@ -0,0 +1,18 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + arg_msg="got int", + filename=cwd + "/main.k", + line_no=4, + col_no=5, + )], + arg_msg="expect str, got int" + ), + file=sys.stdout) diff --git a/test/grammar/schema/type_annotation/type_annotation_not_full/main.k b/test/grammar/schema/type_annotation/type_annotation_not_full/main.k new file mode 100644 index 000000000..828940ec4 --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_not_full/main.k @@ -0,0 +1,7 @@ +schema A[a1, a2: int, a3: str]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/type_annotation_not_full/stdout.golden b/test/grammar/schema/type_annotation/type_annotation_not_full/stdout.golden new file mode 100644 index 000000000..19a1fa9ca --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_not_full/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 2 + c: '3' diff --git a/test/grammar/schema/type_annotation/type_annotation_not_full_0/main.k b/test/grammar/schema/type_annotation/type_annotation_not_full_0/main.k new file mode 100644 index 000000000..e61a89419 --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_not_full_0/main.k @@ -0,0 +1,7 @@ +schema A[a1: int, a2, a3="300"]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2) \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/type_annotation_not_full_0/stdout.golden b/test/grammar/schema/type_annotation/type_annotation_not_full_0/stdout.golden new file mode 100644 index 000000000..2ef839b6a --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_not_full_0/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 2 + c: '300' diff --git a/test/grammar/schema/type_annotation/type_annotation_not_full_1/main.k b/test/grammar/schema/type_annotation/type_annotation_not_full_1/main.k new file mode 100644 index 000000000..7d9d98ca3 --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_not_full_1/main.k @@ -0,0 +1,7 @@ +schema A[a1: int, a2: int, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a2=2,a3="3") \ No newline at end of file diff --git a/test/grammar/schema/type_annotation/type_annotation_not_full_1/stdout.golden b/test/grammar/schema/type_annotation/type_annotation_not_full_1/stdout.golden new file mode 100644 index 000000000..19a1fa9ca --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_not_full_1/stdout.golden @@ -0,0 +1,4 @@ +a: + a: 1 + b: 2 + c: '3' diff --git a/test/grammar/schema/type_annotation/type_annotation_not_full_2/main.k b/test/grammar/schema/type_annotation/type_annotation_not_full_2/main.k new file mode 100644 index 000000000..5d277e7f0 --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_not_full_2/main.k @@ -0,0 +1,6 @@ +schema A[arg=1]: + a: int = arg + +a1 = A() +a2 = A(2) +a3 = A(arg=3) diff --git a/test/grammar/schema/type_annotation/type_annotation_not_full_2/stdout.golden b/test/grammar/schema/type_annotation/type_annotation_not_full_2/stdout.golden new file mode 100644 index 000000000..147e0524c --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_not_full_2/stdout.golden @@ -0,0 +1,6 @@ +a1: + a: 1 +a2: + a: 2 +a3: + a: 3 diff --git a/test/grammar/schema/type_annotation/type_annotation_schema_0/main.k b/test/grammar/schema/type_annotation/type_annotation_schema_0/main.k new file mode 100644 index 000000000..1fb8efbe3 --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_schema_0/main.k @@ -0,0 +1,8 @@ +schema Person: + name: str = "kcl" + age: int = 1 + +x0: Person = Person {} +x1: Person = { + age = 101 +} diff --git a/test/grammar/schema/type_annotation/type_annotation_schema_0/stdout.golden b/test/grammar/schema/type_annotation/type_annotation_schema_0/stdout.golden new file mode 100644 index 000000000..6274b105f --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_schema_0/stdout.golden @@ -0,0 +1,6 @@ +x0: + name: kcl + age: 1 +x1: + name: kcl + age: 101 diff --git a/test/grammar/schema/type_annotation/type_annotation_schema_1/main.k b/test/grammar/schema/type_annotation/type_annotation_schema_1/main.k new file mode 100644 index 000000000..93414c6b0 --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_schema_1/main.k @@ -0,0 +1,6 @@ +schema Person: + name: str = "kcl" + age: int = 1 + +x0 = {} +x1: Person = {} diff --git a/test/grammar/schema/type_annotation/type_annotation_schema_1/stdout.golden b/test/grammar/schema/type_annotation/type_annotation_schema_1/stdout.golden new file mode 100644 index 000000000..fe008a532 --- /dev/null +++ b/test/grammar/schema/type_annotation/type_annotation_schema_1/stdout.golden @@ -0,0 +1,4 @@ +x0: {} +x1: + name: kcl + age: 1 diff --git a/test/grammar/schema/union/binary_union/bin_union_fail_0/main.k b/test/grammar/schema/union/binary_union/bin_union_fail_0/main.k new file mode 100644 index 000000000..da21422ce --- /dev/null +++ b/test/grammar/schema/union/binary_union/bin_union_fail_0/main.k @@ -0,0 +1,3 @@ +temp1 = [1, 2, 3] +temp2 = {"key": "value"} +temp3 = temp1 | temp2 diff --git a/test/grammar/schema/union/binary_union/bin_union_fail_0/stderr.golden.py b/test/grammar/schema/union/binary_union/bin_union_fail_0/stderr.golden.py new file mode 100644 index 000000000..41071e1b2 --- /dev/null +++ b/test/grammar/schema/union/binary_union/bin_union_fail_0/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + ) + ], + arg_msg="unsupported operand type(s) for |: '[int]' and '{str:str}'" + ), + file=sys.stdout +) \ No newline at end of file diff --git a/test/grammar/schema/union/binary_union/bin_union_fail_1/main.k b/test/grammar/schema/union/binary_union/bin_union_fail_1/main.k new file mode 100644 index 000000000..5aec884f0 --- /dev/null +++ b/test/grammar/schema/union/binary_union/bin_union_fail_1/main.k @@ -0,0 +1,3 @@ +temp1 = {"key": "value"} +temp2 = [1, 2, 3] +temp3 = temp1 | temp2 diff --git a/test/grammar/schema/union/binary_union/bin_union_fail_1/stderr.golden.py b/test/grammar/schema/union/binary_union/bin_union_fail_1/stderr.golden.py new file mode 100644 index 000000000..d9a8e1386 --- /dev/null +++ b/test/grammar/schema/union/binary_union/bin_union_fail_1/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + ) + ], + arg_msg="unsupported operand type(s) for |: '{str:str}' and '[int]'" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/union/binary_union/bin_union_fail_2/main.k b/test/grammar/schema/union/binary_union/bin_union_fail_2/main.k new file mode 100644 index 000000000..e41983158 --- /dev/null +++ b/test/grammar/schema/union/binary_union/bin_union_fail_2/main.k @@ -0,0 +1,10 @@ +schema Person: + name: str + age: int + +person = Person { + name: "Alice" + age: 18 +} + +val = {"key": "value"} | person diff --git a/test/grammar/schema/union/binary_union/bin_union_fail_2/stderr.golden.py b/test/grammar/schema/union/binary_union/bin_union_fail_2/stderr.golden.py new file mode 100644 index 000000000..f8ea7d50c --- /dev/null +++ b/test/grammar/schema/union/binary_union/bin_union_fail_2/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + ) + ], + arg_msg="unsupported operand type(s) for |: '{str(key):str(value)}' and 'Person'" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/union/binary_union/bin_union_fail_3/main.k b/test/grammar/schema/union/binary_union/bin_union_fail_3/main.k new file mode 100644 index 000000000..bed50e974 --- /dev/null +++ b/test/grammar/schema/union/binary_union/bin_union_fail_3/main.k @@ -0,0 +1,8 @@ +schema Person: + name: str + age: int + +val = [1, 2, 3] | Person { + name: "Alice" + age: 18 +} diff --git a/test/grammar/schema/union/binary_union/bin_union_fail_3/stderr.golden.py b/test/grammar/schema/union/binary_union/bin_union_fail_3/stderr.golden.py new file mode 100644 index 000000000..5ce4c6a12 --- /dev/null +++ b/test/grammar/schema/union/binary_union/bin_union_fail_3/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + ) + ], + arg_msg="unsupported operand type(s) for |: '[int(1)|int(2)|int(3)]' and 'Person'" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/union/binary_union/bin_union_fail_4/main.k b/test/grammar/schema/union/binary_union/bin_union_fail_4/main.k new file mode 100644 index 000000000..44d9a69de --- /dev/null +++ b/test/grammar/schema/union/binary_union/bin_union_fail_4/main.k @@ -0,0 +1,12 @@ +schema Person: + name: str + age: int + +_person = Person { + name: "Alice" + age: 18 +} +_person = _person | { + err: "123" + age: 10 +} diff --git a/test/grammar/schema/union/binary_union/bin_union_fail_4/stderr.golden.py b/test/grammar/schema/union/binary_union/bin_union_fail_4/stderr.golden.py new file mode 100644 index 000000000..2f09924ce --- /dev/null +++ b/test/grammar/schema/union/binary_union/bin_union_fail_4/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CannotAddMembers_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10, + col_no=5, + arg_msg="'err' is not defined in schema 'Person'" + ), + ], + arg_msg="Cannot add member 'err' to schema 'Person'") + , file=sys.stdout +) diff --git a/test/grammar/schema/union/binary_union/binary_union_0/main.k b/test/grammar/schema/union/binary_union/binary_union_0/main.k new file mode 100644 index 000000000..f537fa071 --- /dev/null +++ b/test/grammar/schema/union/binary_union/binary_union_0/main.k @@ -0,0 +1,6 @@ +_temp1 = [1, 2, 3] +_temp2 = [4, 5] +_temp3 = [6] +a = [1, 2, 3] | [5] +b = _temp1 | _temp2 +c = _temp1 | _temp2 | _temp3 diff --git a/test/grammar/schema/union/binary_union/binary_union_0/stdout.golden b/test/grammar/schema/union/binary_union/binary_union_0/stdout.golden new file mode 100644 index 000000000..510fbb0b3 --- /dev/null +++ b/test/grammar/schema/union/binary_union/binary_union_0/stdout.golden @@ -0,0 +1,12 @@ +a: +- 5 +- 2 +- 3 +b: +- 4 +- 5 +- 3 +c: +- 6 +- 5 +- 3 diff --git a/test/grammar/schema/union/binary_union/binary_union_1/main.k b/test/grammar/schema/union/binary_union/binary_union_1/main.k new file mode 100644 index 000000000..8be07f2f5 --- /dev/null +++ b/test/grammar/schema/union/binary_union/binary_union_1/main.k @@ -0,0 +1,6 @@ +_temp1 = {"key1": "value1"} +_temp2 = {"key2": "value2"} +_temp3 = {"key3": "value3"} +a = {"key1": "value1"} | {"key2": "value2"} +b = _temp1 | _temp2 +c = _temp1 | _temp2 | _temp3 diff --git a/test/grammar/schema/union/binary_union/binary_union_1/stdout.golden b/test/grammar/schema/union/binary_union/binary_union_1/stdout.golden new file mode 100644 index 000000000..81fe8fb7c --- /dev/null +++ b/test/grammar/schema/union/binary_union/binary_union_1/stdout.golden @@ -0,0 +1,10 @@ +a: + key1: value1 + key2: value2 +b: + key1: value1 + key2: value2 +c: + key1: value1 + key2: value2 + key3: value3 diff --git a/test/grammar/schema/union/binary_union/binary_union_2/main.k b/test/grammar/schema/union/binary_union/binary_union_2/main.k new file mode 100644 index 000000000..3dd09e085 --- /dev/null +++ b/test/grammar/schema/union/binary_union/binary_union_2/main.k @@ -0,0 +1,9 @@ +schema Name: + key1?: str = None + key2?: str = None + +schema Person: + name1?: Name = {"key1": "value1"} + name2?: Name = name1 | {"key2": "value2"} + +person = Person {} diff --git a/test/grammar/schema/union/binary_union/binary_union_2/stdout.golden b/test/grammar/schema/union/binary_union/binary_union_2/stdout.golden new file mode 100644 index 000000000..2d43757cd --- /dev/null +++ b/test/grammar/schema/union/binary_union/binary_union_2/stdout.golden @@ -0,0 +1,7 @@ +person: + name1: + key1: value1 + key2: null + name2: + key1: value1 + key2: value2 diff --git a/test/grammar/schema/union/binary_union/binary_union_3/main.k b/test/grammar/schema/union/binary_union/binary_union_3/main.k new file mode 100644 index 000000000..d1338a467 --- /dev/null +++ b/test/grammar/schema/union/binary_union/binary_union_3/main.k @@ -0,0 +1,17 @@ +schema Person: + name: str + labels: {str:str} + +person1 = Person { + name = "Alice" + labels: { + "key1": "value1" + } +} +person2 = Person { + name = "Bob" + labels: { + "key2": "value2" + } +} +person3 = person1 | person2 diff --git a/test/grammar/schema/union/binary_union/binary_union_3/stdout.golden b/test/grammar/schema/union/binary_union/binary_union_3/stdout.golden new file mode 100644 index 000000000..a4a993b87 --- /dev/null +++ b/test/grammar/schema/union/binary_union/binary_union_3/stdout.golden @@ -0,0 +1,13 @@ +person1: + name: Alice + labels: + key1: value1 +person2: + name: Bob + labels: + key2: value2 +person3: + name: Bob + labels: + key1: value1 + key2: value2 diff --git a/test/grammar/schema/union/binary_union/binary_union_4/main.k b/test/grammar/schema/union/binary_union/binary_union_4/main.k new file mode 100644 index 000000000..2748f02ed --- /dev/null +++ b/test/grammar/schema/union/binary_union/binary_union_4/main.k @@ -0,0 +1,8 @@ +schema Person: + data: int = 1 + +person = Person { + data = 0 +} +data1 = 2 | 0 +data2 = 2 | 1 diff --git a/test/grammar/schema/union/binary_union/binary_union_4/stdout.golden b/test/grammar/schema/union/binary_union/binary_union_4/stdout.golden new file mode 100644 index 000000000..d8737d475 --- /dev/null +++ b/test/grammar/schema/union/binary_union/binary_union_4/stdout.golden @@ -0,0 +1,4 @@ +person: + data: 0 +data1: 2 +data2: 3 diff --git a/test/grammar/schema/union/binary_union/binary_union_5/main.k b/test/grammar/schema/union/binary_union/binary_union_5/main.k new file mode 100644 index 000000000..72b22b6a9 --- /dev/null +++ b/test/grammar/schema/union/binary_union/binary_union_5/main.k @@ -0,0 +1,2 @@ +data1 = bin(0b00001111 | 0b10100101) +data2 = bin(0xFF | 0xAB) diff --git a/test/grammar/schema/union/binary_union/binary_union_5/stdout.golden b/test/grammar/schema/union/binary_union/binary_union_5/stdout.golden new file mode 100644 index 000000000..6ac69ff7b --- /dev/null +++ b/test/grammar/schema/union/binary_union/binary_union_5/stdout.golden @@ -0,0 +1,2 @@ +data1: '0b10101111' +data2: '0b11111111' diff --git a/test/grammar/schema/union/datatype/valid/main.k b/test/grammar/schema/union/datatype/valid/main.k new file mode 100644 index 000000000..67f404cf6 --- /dev/null +++ b/test/grammar/schema/union/datatype/valid/main.k @@ -0,0 +1,11 @@ +schema Person: + firstName: str = "firstName" + lastName: str = "lastName" + age: int = 0 + +schema Boy(Person): + firstName: str = "John" + lastName: str = "Doe" + age: int |= 18 + +JohnDoe = Boy {} diff --git a/test/grammar/schema/union/datatype/valid/stdout.golden b/test/grammar/schema/union/datatype/valid/stdout.golden new file mode 100644 index 000000000..bedf50f4f --- /dev/null +++ b/test/grammar/schema/union/datatype/valid/stdout.golden @@ -0,0 +1,4 @@ +JohnDoe: + firstName: John + lastName: Doe + age: 18 \ No newline at end of file diff --git a/test/grammar/schema/union/dict/instance_0/main.k b/test/grammar/schema/union/dict/instance_0/main.k new file mode 100644 index 000000000..0c663dee5 --- /dev/null +++ b/test/grammar/schema/union/dict/instance_0/main.k @@ -0,0 +1,24 @@ +schema Person: + name: {str:str} = { + "firstName": "firstName", + "lastName": "lastName" + } + age: int = 0 + info: {str:str} = { + "card": "123" + } + +schema Boy(Person): + name: {str:str} |= { + "firstName" = "John" + } + age: int = 18 + info: {str:str} |= { + "phoneNum": "123" + } + +JohnDoe = Boy { + "name": { + "lastName" = "Doe" + } +} diff --git a/test/grammar/schema/union/dict/instance_0/stdout.golden b/test/grammar/schema/union/dict/instance_0/stdout.golden new file mode 100644 index 000000000..52d7395e3 --- /dev/null +++ b/test/grammar/schema/union/dict/instance_0/stdout.golden @@ -0,0 +1,8 @@ +JohnDoe: + name: + firstName: John + lastName: Doe + age: 18 + info: + card: '123' + phoneNum: '123' \ No newline at end of file diff --git a/test/grammar/schema/union/dict/schema_0/main.k b/test/grammar/schema/union/dict/schema_0/main.k new file mode 100644 index 000000000..a284cffc7 --- /dev/null +++ b/test/grammar/schema/union/dict/schema_0/main.k @@ -0,0 +1,20 @@ +schema Person: + name: {str:str} = { + "firstName": "firstName", + "lastName": "lastName" + } + age: int = 0 + info: {str:str} = { + "card": "123" + } + +schema Boy(Person): + name: {str:str} |= { + "firstName" = "John" + } + age: int = 18 + info: {str:str} |= { + "phoneNum": "123" + } + +JohnDoe = Boy {} diff --git a/test/grammar/schema/union/dict/schema_0/stdout.golden b/test/grammar/schema/union/dict/schema_0/stdout.golden new file mode 100644 index 000000000..fc9605f91 --- /dev/null +++ b/test/grammar/schema/union/dict/schema_0/stdout.golden @@ -0,0 +1,8 @@ +JohnDoe: + name: + firstName: John + lastName: lastName + age: 18 + info: + card: '123' + phoneNum: '123' \ No newline at end of file diff --git a/test/grammar/schema/union/fail/fail_0/main.k b/test/grammar/schema/union/fail/fail_0/main.k new file mode 100644 index 000000000..ff941222e --- /dev/null +++ b/test/grammar/schema/union/fail/fail_0/main.k @@ -0,0 +1,2 @@ +_data = [1, 2, 3] +_data |= {"key": "value"} diff --git a/test/grammar/schema/union/fail/fail_0/stderr.golden.py b/test/grammar/schema/union/fail/fail_0/stderr.golden.py new file mode 100644 index 000000000..916ba80a6 --- /dev/null +++ b/test/grammar/schema/union/fail/fail_0/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2 + ) + ], + arg_msg="unsupported operand type(s) for |=: '[int]' and '{str(key):str(value)}'" + ), + file=sys.stdout +) diff --git a/test/grammar/schema/union/fail/fail_1/main.k b/test/grammar/schema/union/fail/fail_1/main.k new file mode 100644 index 000000000..91a9e1d33 --- /dev/null +++ b/test/grammar/schema/union/fail/fail_1/main.k @@ -0,0 +1,2 @@ +_data = [1, 2, 3] +_data |= "value" diff --git a/test/grammar/schema/union/fail/fail_1/stderr.golden.py b/test/grammar/schema/union/fail/fail_1/stderr.golden.py new file mode 100644 index 000000000..cec5cc65d --- /dev/null +++ b/test/grammar/schema/union/fail/fail_1/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + ) + ], + arg_msg="unsupported operand type(s) for |=: '[int]' and 'str(value)'" + ), + file=sys.stdout +) diff --git a/test/grammar/schema/union/fail/fail_2/main.k b/test/grammar/schema/union/fail/fail_2/main.k new file mode 100644 index 000000000..fbf6ac5d4 --- /dev/null +++ b/test/grammar/schema/union/fail/fail_2/main.k @@ -0,0 +1,6 @@ +schema Person: + name: str = "Alice" + +_person = Person {} +_person = _person | {"key": "value"} +person = _person diff --git a/test/grammar/schema/union/fail/fail_2/stderr.golden.py b/test/grammar/schema/union/fail/fail_2/stderr.golden.py new file mode 100644 index 000000000..bea26c76d --- /dev/null +++ b/test/grammar/schema/union/fail/fail_2/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.CannotAddMembers_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=22, + arg_msg="'key' is not defined in schema 'Person'", + ), + ], + arg_msg="Cannot add member 'key' to schema 'Person'" + ) +) diff --git a/test/grammar/schema/union/fail/fail_3/main.k b/test/grammar/schema/union/fail/fail_3/main.k new file mode 100644 index 000000000..88f602201 --- /dev/null +++ b/test/grammar/schema/union/fail/fail_3/main.k @@ -0,0 +1,5 @@ +schema Person: + name: str = "Alice" + +personA = Person {} +personB: Person = personA | {"name" = 123} diff --git a/test/grammar/schema/union/fail/fail_3/stderr.golden.py b/test/grammar/schema/union/fail/fail_3/stderr.golden.py new file mode 100644 index 000000000..be169371d --- /dev/null +++ b/test/grammar/schema/union/fail/fail_3/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect str", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=30, + arg_msg="got int(123)" + ) + ], + arg_msg="expect str, got int(123)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/union/fail/fail_4/main.k b/test/grammar/schema/union/fail/fail_4/main.k new file mode 100644 index 000000000..a22c9a70a --- /dev/null +++ b/test/grammar/schema/union/fail/fail_4/main.k @@ -0,0 +1,6 @@ +schema Person: + name: str = "Alice" + +_personA = Person {} +_personA = _personA | {"name" = 123.0} +personA = _personA diff --git a/test/grammar/schema/union/fail/fail_4/stderr.golden.py b/test/grammar/schema/union/fail/fail_4/stderr.golden.py new file mode 100644 index 000000000..dd7a55d69 --- /dev/null +++ b/test/grammar/schema/union/fail/fail_4/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect str", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=24, + arg_msg="got float(123.0)" + ) + ], + arg_msg="expect str, got float(123.0)" + ), + file=sys.stdout +) + diff --git a/test/grammar/schema/union/list/init_0/main.k b/test/grammar/schema/union/list/init_0/main.k new file mode 100644 index 000000000..5df236323 --- /dev/null +++ b/test/grammar/schema/union/list/init_0/main.k @@ -0,0 +1,31 @@ +schema Group: + mixin [AGroupMixin] + nums: [{str:}] = [ + { + "id": "000", + "num": 0 + }, + { + "id": "001", + "num": 1 + } + ] + +schema AGroupMixin: + nums |= [ + { + "title": "A" + }, + { + "title": "B" + } + ] + +group = Group { + "nums": [ + {}, + { + "description": "A_000" + } + ] +} diff --git a/test/grammar/schema/union/list/init_0/stdout.golden b/test/grammar/schema/union/list/init_0/stdout.golden new file mode 100644 index 000000000..16d108930 --- /dev/null +++ b/test/grammar/schema/union/list/init_0/stdout.golden @@ -0,0 +1,9 @@ +group: + nums: + - id: '000' + num: 0 + title: A + - id: '001' + num: 1 + description: A_000 + title: B diff --git a/test/grammar/schema/union/list/instance_0/main.k b/test/grammar/schema/union/list/instance_0/main.k new file mode 100644 index 000000000..eea582e2d --- /dev/null +++ b/test/grammar/schema/union/list/instance_0/main.k @@ -0,0 +1,32 @@ +schema Group: + nums: [{str:}] = [ + { + "id": "000", + "num": 0 + }, + { + "id": "001", + "num": 1 + } + ] + +schema AGroup(Group): + nums: [{str:}] |= [ + { + "title": "A" + }, + { + "title": "B" + } + ] + +group = AGroup { + "nums": [ + { + "description": "A_000" + }, + { + "description": "B_000" + }, + ] +} diff --git a/test/grammar/schema/union/list/instance_0/stdout.golden b/test/grammar/schema/union/list/instance_0/stdout.golden new file mode 100644 index 000000000..e974fc94d --- /dev/null +++ b/test/grammar/schema/union/list/instance_0/stdout.golden @@ -0,0 +1,10 @@ +group: + nums: + - id: '000' + num: 0 + description: A_000 + title: A + - id: '001' + num: 1 + description: B_000 + title: B diff --git a/test/grammar/schema/union/list/schema_0/main.k b/test/grammar/schema/union/list/schema_0/main.k new file mode 100644 index 000000000..dfd18765a --- /dev/null +++ b/test/grammar/schema/union/list/schema_0/main.k @@ -0,0 +1,23 @@ +schema Group: + nums: [{str:}] = [ + { + "id": "000", + "num": 0 + }, + { + "id": "001", + "num": 1 + } + ] + +schema AGroup(Group): + nums: [{str:}] |= [ + { + "title": "A" + }, + { + "title": "B" + } + ] + +group = AGroup {} diff --git a/test/grammar/schema/union/list/schema_0/stdout.golden b/test/grammar/schema/union/list/schema_0/stdout.golden new file mode 100644 index 000000000..1833c8ca5 --- /dev/null +++ b/test/grammar/schema/union/list/schema_0/stdout.golden @@ -0,0 +1,8 @@ +group: + nums: + - id: '000' + num: 0 + title: A + - id: '001' + num: 1 + title: B \ No newline at end of file diff --git a/test/grammar/schema/union/list/schema_1/main.k b/test/grammar/schema/union/list/schema_1/main.k new file mode 100644 index 000000000..790121aba --- /dev/null +++ b/test/grammar/schema/union/list/schema_1/main.k @@ -0,0 +1,7 @@ +schema Group: + nums: [str] = ["value1"] + +schema AGroup(Group): + nums: [str] |= ["value1", "value2"] + +group = AGroup {} diff --git a/test/grammar/schema/union/list/schema_1/stdout.golden b/test/grammar/schema/union/list/schema_1/stdout.golden new file mode 100644 index 000000000..281a086fd --- /dev/null +++ b/test/grammar/schema/union/list/schema_1/stdout.golden @@ -0,0 +1,4 @@ +group: + nums: + - value1 + - value2 diff --git a/test/grammar/schema/union/list/variable_fail_0/main.k b/test/grammar/schema/union/list/variable_fail_0/main.k new file mode 100644 index 000000000..cf87f7737 --- /dev/null +++ b/test/grammar/schema/union/list/variable_fail_0/main.k @@ -0,0 +1,4 @@ +lists = ["va", "d"] + +lists |= ["val", "value2"] + diff --git a/test/grammar/schema/union/list/variable_fail_0/stderr.golden.py b/test/grammar/schema/union/list/variable_fail_0/stderr.golden.py new file mode 100644 index 000000000..674b2e2aa --- /dev/null +++ b/test/grammar/schema/union/list/variable_fail_0/stderr.golden.py @@ -0,0 +1,17 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=1, + ), + ],) + , file=sys.stdout +) diff --git a/test/grammar/schema/union/schema/instance_0/main.k b/test/grammar/schema/union/schema/instance_0/main.k new file mode 100644 index 000000000..3f9344ece --- /dev/null +++ b/test/grammar/schema/union/schema/instance_0/main.k @@ -0,0 +1,28 @@ +schema Name: + firstName?: str + lastName?: str + +schema Person: + name: Name = { + "firstName": "firstName", + "lastName": "lastName" + } + age: int = 0 + info: {str:str} = { + "card": "123" + } + +schema Boy(Person): + name: Name |= { + "firstName" = "John" + } + age: int = 18 + info: {str:str} |= { + "phoneNum": "123" + } + +JohnDoe = Boy { + "name": { + "lastName" = "Dow" + } +} diff --git a/test/grammar/schema/union/schema/instance_0/stdout.golden b/test/grammar/schema/union/schema/instance_0/stdout.golden new file mode 100644 index 000000000..8b47ea464 --- /dev/null +++ b/test/grammar/schema/union/schema/instance_0/stdout.golden @@ -0,0 +1,8 @@ +JohnDoe: + name: + firstName: John + lastName: Dow + age: 18 + info: + card: '123' + phoneNum: '123' \ No newline at end of file diff --git a/test/grammar/schema/union/schema/schema_0/main.k b/test/grammar/schema/union/schema/schema_0/main.k new file mode 100644 index 000000000..15b29cf0b --- /dev/null +++ b/test/grammar/schema/union/schema/schema_0/main.k @@ -0,0 +1,24 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name = { + "firstName": "firstName", + "lastName": "lastName" + } + age: int = 0 + info: {str:str} = { + "card": "123" + } + +schema Boy(Person): + name: Name |= { + "firstName" = "John" + } + age: int = 18 + info: {str:str} |= { + "phoneNum": "123" + } + +JohnDoe = Boy {} diff --git a/test/grammar/schema/union/schema/schema_0/stdout.golden b/test/grammar/schema/union/schema/schema_0/stdout.golden new file mode 100644 index 000000000..fc9605f91 --- /dev/null +++ b/test/grammar/schema/union/schema/schema_0/stdout.golden @@ -0,0 +1,8 @@ +JohnDoe: + name: + firstName: John + lastName: lastName + age: 18 + info: + card: '123' + phoneNum: '123' \ No newline at end of file diff --git a/test/grammar/schema/union/variable_fail/int/main.k b/test/grammar/schema/union/variable_fail/int/main.k new file mode 100644 index 000000000..188ddedbc --- /dev/null +++ b/test/grammar/schema/union/variable_fail/int/main.k @@ -0,0 +1,5 @@ +# Note that |= between int behaves just as Python. +# | is bitwise-OR operation and: ```var |= value``` is short for ```var = var | value``` +a = 5 +a |= 20 +b = 5 | 20 diff --git a/test/grammar/schema/union/variable_fail/int/stderr.golden.py b/test/grammar/schema/union/variable_fail/int/stderr.golden.py new file mode 100644 index 000000000..3eda7e065 --- /dev/null +++ b/test/grammar/schema/union/variable_fail/int/stderr.golden.py @@ -0,0 +1,17 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=1 + ), + ]) + , file=sys.stdout +) diff --git a/test/grammar/schema/union/variable_fail/list/main.k b/test/grammar/schema/union/variable_fail/list/main.k new file mode 100644 index 000000000..7e72adcd6 --- /dev/null +++ b/test/grammar/schema/union/variable_fail/list/main.k @@ -0,0 +1,3 @@ +lists1 = [1,2,3] +lists1 |= [5] +lists2 = [1,2,3] | [5] diff --git a/test/grammar/schema/union/variable_fail/list/stderr.golden.py b/test/grammar/schema/union/variable_fail/list/stderr.golden.py new file mode 100644 index 000000000..f861b1242 --- /dev/null +++ b/test/grammar/schema/union/variable_fail/list/stderr.golden.py @@ -0,0 +1,17 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=1, + ), + ],) + , file=sys.stdout +) diff --git a/test/grammar/schema/without_brakets/test_0/main.k b/test/grammar/schema/without_brakets/test_0/main.k new file mode 100644 index 000000000..d88b130e7 --- /dev/null +++ b/test/grammar/schema/without_brakets/test_0/main.k @@ -0,0 +1,6 @@ +schema Person: + name: str = "Alice" + age: int = 18 + +person = Person() +name = Person().name diff --git a/test/grammar/schema/without_brakets/test_0/stdout.golden b/test/grammar/schema/without_brakets/test_0/stdout.golden new file mode 100644 index 000000000..317d1bb3b --- /dev/null +++ b/test/grammar/schema/without_brakets/test_0/stdout.golden @@ -0,0 +1,4 @@ +person: + name: Alice + age: 18 +name: Alice diff --git a/test/grammar/schema/without_brakets/test_1/main.k b/test/grammar/schema/without_brakets/test_1/main.k new file mode 100644 index 000000000..8ffbb7c6c --- /dev/null +++ b/test/grammar/schema/without_brakets/test_1/main.k @@ -0,0 +1,5 @@ +schema Person[data, n]: + name: str = data + count: int = n + +person = Person("Alice", 1) diff --git a/test/grammar/schema/without_brakets/test_1/stdout.golden b/test/grammar/schema/without_brakets/test_1/stdout.golden new file mode 100644 index 000000000..058e2ac88 --- /dev/null +++ b/test/grammar/schema/without_brakets/test_1/stdout.golden @@ -0,0 +1,3 @@ +person: + name: Alice + count: 1 diff --git a/test/grammar/schema/without_brakets/test_2/main.k b/test/grammar/schema/without_brakets/test_2/main.k new file mode 100644 index 000000000..313d1ee06 --- /dev/null +++ b/test/grammar/schema/without_brakets/test_2/main.k @@ -0,0 +1,5 @@ +schema Person[data, n]: + name: str = data + count: int = n + +count = Person("Alice", 10).count diff --git a/test/grammar/schema/without_brakets/test_2/stdout.golden b/test/grammar/schema/without_brakets/test_2/stdout.golden new file mode 100644 index 000000000..040fbd7b0 --- /dev/null +++ b/test/grammar/schema/without_brakets/test_2/stdout.golden @@ -0,0 +1 @@ +count: 10 diff --git a/test/grammar/sort_keys/hello/main.k b/test/grammar/sort_keys/hello/main.k new file mode 100644 index 000000000..4eda95246 --- /dev/null +++ b/test/grammar/sort_keys/hello/main.k @@ -0,0 +1,2 @@ +b = 1 +a = 2 diff --git a/test/grammar/sort_keys/hello/settings.yaml b/test/grammar/sort_keys/hello/settings.yaml new file mode 100644 index 000000000..a27cb7724 --- /dev/null +++ b/test/grammar/sort_keys/hello/settings.yaml @@ -0,0 +1 @@ +kcl_options: -d --sort diff --git a/test/grammar/sort_keys/hello/stdout.golden b/test/grammar/sort_keys/hello/stdout.golden new file mode 100644 index 000000000..137853197 --- /dev/null +++ b/test/grammar/sort_keys/hello/stdout.golden @@ -0,0 +1,2 @@ +a: 2 +b: 1 diff --git a/test/grammar/syntax/general/multiple_assign/case0/main.k b/test/grammar/syntax/general/multiple_assign/case0/main.k new file mode 100644 index 000000000..ab2e7ea59 --- /dev/null +++ b/test/grammar/syntax/general/multiple_assign/case0/main.k @@ -0,0 +1 @@ +a = 1, 2 diff --git a/test/grammar/syntax/general/multiple_assign/case0/stderr.golden.py b/test/grammar/syntax/general/multiple_assign/case0/stderr.golden.py new file mode 100644 index 000000000..2a94b0170 --- /dev/null +++ b/test/grammar/syntax/general/multiple_assign/case0/stderr.golden.py @@ -0,0 +1,16 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.InvalidSyntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=6, + arg_msg="Expected one of ['newline']" + )], + ), + file=sys.stdout) diff --git a/test/grammar/syntax/general/multiple_assign/case1/main.k b/test/grammar/syntax/general/multiple_assign/case1/main.k new file mode 100644 index 000000000..267526547 --- /dev/null +++ b/test/grammar/syntax/general/multiple_assign/case1/main.k @@ -0,0 +1 @@ +a, b = 1, 2 diff --git a/test/grammar/syntax/general/multiple_assign/case1/stderr.golden.py b/test/grammar/syntax/general/multiple_assign/case1/stderr.golden.py new file mode 100644 index 000000000..c6623a871 --- /dev/null +++ b/test/grammar/syntax/general/multiple_assign/case1/stderr.golden.py @@ -0,0 +1,16 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.InvalidSyntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=6, + arg_msg="Expected one of [',', 'newline']" + )], + ), + file=sys.stdout) diff --git a/test/grammar/syntax/general/unnamed/case0/main.k b/test/grammar/syntax/general/unnamed/case0/main.k new file mode 100644 index 000000000..10f9ce328 --- /dev/null +++ b/test/grammar/syntax/general/unnamed/case0/main.k @@ -0,0 +1 @@ +a== diff --git a/test/grammar/syntax/general/unnamed/case0/stderr.golden.py b/test/grammar/syntax/general/unnamed/case0/stderr.golden.py new file mode 100644 index 000000000..1e99db1a1 --- /dev/null +++ b/test/grammar/syntax/general/unnamed/case0/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.InvalidSyntax_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=4, + arg_msg="Expected one of ['all', 'any', " + "'bin_number', 'dec_number', 'False', " + "'filter', 'float_number', 'hex_number', 'lambda', " + "'{', '[', '(', 'long_string', 'not', 'map', " + "'-', 'name', 'None', '~', 'oct_number', '+" + "', 'string', 'True', 'Undefined']", + )], + ), + file=sys.stdout) diff --git a/test/grammar/syntax/indent/indent_error_0/main.k b/test/grammar/syntax/indent/indent_error_0/main.k new file mode 100644 index 000000000..ff6c318ae --- /dev/null +++ b/test/grammar/syntax/indent/indent_error_0/main.k @@ -0,0 +1,4 @@ +schema Person: + name: str + age: int + info: str diff --git a/test/grammar/syntax/indent/indent_error_0/stderr.golden.py b/test/grammar/syntax/indent/indent_error_0/stderr.golden.py new file mode 100644 index 000000000..5a9a8bf1f --- /dev/null +++ b/test/grammar/syntax/indent/indent_error_0/stderr.golden.py @@ -0,0 +1,18 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IndentationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=4, + ) + ], + arg_msg=kcl_error.INDENTATION_ERROR_MSG.format("3")), + file=sys.stdout +) diff --git a/test/grammar/syntax/indent/indent_error_1/main.k b/test/grammar/syntax/indent/indent_error_1/main.k new file mode 100644 index 000000000..9060540fd --- /dev/null +++ b/test/grammar/syntax/indent/indent_error_1/main.k @@ -0,0 +1,4 @@ +a = 1 +if a == 1: + b = 2 + c = 3 diff --git a/test/grammar/syntax/indent/indent_error_1/stderr.golden.py b/test/grammar/syntax/indent/indent_error_1/stderr.golden.py new file mode 100644 index 000000000..d59809ba1 --- /dev/null +++ b/test/grammar/syntax/indent/indent_error_1/stderr.golden.py @@ -0,0 +1,18 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.IndentationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=3, + ) + ], + arg_msg=kcl_error.INDENTATION_ERROR_MSG.format("2")), + file=sys.stdout +) \ No newline at end of file diff --git a/test/grammar/syntax/tab/tab_error_0/main.k b/test/grammar/syntax/tab/tab_error_0/main.k new file mode 100644 index 000000000..1c55edaa6 --- /dev/null +++ b/test/grammar/syntax/tab/tab_error_0/main.k @@ -0,0 +1,3 @@ +schema Person: + name: str + age: int diff --git a/test/grammar/syntax/tab/tab_error_0/stderr.golden.py b/test/grammar/syntax/tab/tab_error_0/stderr.golden.py new file mode 100644 index 000000000..69ef274f5 --- /dev/null +++ b/test/grammar/syntax/tab/tab_error_0/stderr.golden.py @@ -0,0 +1,17 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.TabError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=2, + ) + ]), + file=sys.stdout +) diff --git a/test/grammar/syntax/tab/tab_error_1/main.k b/test/grammar/syntax/tab/tab_error_1/main.k new file mode 100644 index 000000000..d295d879b --- /dev/null +++ b/test/grammar/syntax/tab/tab_error_1/main.k @@ -0,0 +1,4 @@ +a = 1 +if a == 1: + b = 2 + c = 3 diff --git a/test/grammar/syntax/tab/tab_error_1/stderr.golden.py b/test/grammar/syntax/tab/tab_error_1/stderr.golden.py new file mode 100644 index 000000000..1c17a5c34 --- /dev/null +++ b/test/grammar/syntax/tab/tab_error_1/stderr.golden.py @@ -0,0 +1,15 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message(kcl_error.get_exception(err_type=kcl_error.ErrType.TabError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=2 + )], + ) + , file=sys.stdout) diff --git a/test/grammar/syntax/tab/tab_normal_0/main.k b/test/grammar/syntax/tab/tab_normal_0/main.k new file mode 100644 index 000000000..f1d396fa1 --- /dev/null +++ b/test/grammar/syntax/tab/tab_normal_0/main.k @@ -0,0 +1,9 @@ +a = 1 +if a == 1: + _b = 2 + _c = 3 +elif a == 2: + _b = 2 + _c = 3 +b = _b +c = _c diff --git a/test/grammar/syntax/tab/tab_normal_0/stdout.golden b/test/grammar/syntax/tab/tab_normal_0/stdout.golden new file mode 100644 index 000000000..894c6553a --- /dev/null +++ b/test/grammar/syntax/tab/tab_normal_0/stdout.golden @@ -0,0 +1,3 @@ +a: 1 +b: 2 +c: 3 diff --git a/test/grammar/test_grammar.py b/test/grammar/test_grammar.py new file mode 100644 index 000000000..a6fbec652 --- /dev/null +++ b/test/grammar/test_grammar.py @@ -0,0 +1,138 @@ +"""This is a scripts to run KCL grammar test cases""" +import pytest +import os +import subprocess +import re +import yaml +import pathlib + +TEST_FILE = "main.k" +STDOUT_GOLDEN = "stdout.golden" +STDERR_GOLDEN = "stderr.golden" +STDOUT_GOLDEN_PY = "stdout.golden.py" +STDERR_GOLDEN_PY = "stderr.golden.py" +SETTINGS_FILE = "settings.yaml" + + +def find_test_dirs(path, category): + result = [] + for root, dirs, files in os.walk(path + category): + for name in files: + if name == "main.k": + result.append(root) + return result + + +def compare_strings(result_strings, golden_strings): + assert result_strings == golden_strings + + +def compare_results(result, golden_result): + """Convert bytestring (result) and list of strings (golden_lines) both to + list of strings with line ending stripped, then compare. + """ + + result_strings = result.decode().split("\n") + golden_strings = golden_result.decode().split("\n") + compare_strings(result_strings, golden_strings) + + +def compare_results_with_lines(result, golden_lines): + """Convert bytestring (result) and list of strings (golden_lines) both to + list of strings with line ending stripped, then compare. + """ + + result_strings = result.decode().split("\n") + golden_strings = [] + for line in golden_lines: + clean_line = re.sub("\n$", "", line) + golden_strings.append(clean_line) + # List generated by split() has an ending empty string, when the '\n' is + # the last character + assert result_strings[-1] == "", "The result string does not end with a NEWLINE" + golden_strings.append("") + compare_strings(result_strings, golden_strings) + + +def generate_golden_file(py_file_name): + if os.path.isfile(py_file_name): + try: + process = subprocess.Popen( + ["kclvm", py_file_name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=dict(os.environ), + ) + stdout, stderr = process.communicate() + assert ( + process.returncode == 0 + ), "Error executing file {}, exit code = {}".format( + py_file_name, process.returncode + ) + except Exception: + raise + return stdout + return None + + +def read_settings_file(settings_file_name): + if os.path.isfile(settings_file_name): + try: + with open(settings_file_name, "r") as stream: + settings = yaml.safe_load(stream) + except Exception: + raise + return settings + return None + + +print("##### K Language Grammar Test Suite #####") +test_dirs = find_test_dirs(str(pathlib.Path(__file__).parent), "") + + +@pytest.mark.parametrize("test_dir", test_dirs) +def test_grammar(test_dir): + print("Testing {}".format(test_dir)) + test_settings = read_settings_file(os.path.join(test_dir, SETTINGS_FILE)) + kcl_command = ["kcl", TEST_FILE] + if test_settings and test_settings["kcl_options"]: + kcl_command.extend(test_settings["kcl_options"].split()) + process = subprocess.Popen( + kcl_command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=os.path.abspath(test_dir), + env=dict(os.environ), + ) + stdout, stderr = process.communicate() + print("STDOUT:\n{}".format(stdout.decode())) + print("STDERR:\n{}".format(stderr.decode())) + RETURN_CODE = 0 + KCLVM_OUTPUT = 1 + GOLDEN_FILE = 2 + GOLDEN_FILE_SCRIPT = 3 + settings = { + "stdout": (None, stdout, STDOUT_GOLDEN, STDOUT_GOLDEN_PY), + "stderr": (1, stderr, STDERR_GOLDEN, STDERR_GOLDEN_PY), + } + for _, setting in settings.items(): + # Attempt to generate a golden stdout. + golden_file_result = generate_golden_file( + os.path.join(test_dir, setting[GOLDEN_FILE_SCRIPT]) + ) + if golden_file_result: + compare_results(setting[KCLVM_OUTPUT], golden_file_result) + else: + # Attempt to use existing golden stdout. + try: + with open( + os.path.join(test_dir, setting[GOLDEN_FILE]), "r" + ) as golden_file: + compare_results_with_lines(setting[KCLVM_OUTPUT], golden_file) + if setting[RETURN_CODE] is not None: + assert process.returncode == setting[RETURN_CODE] + except OSError: + # Ignore when a golden file does not exist. + pass + except Exception: + raise diff --git a/test/grammar/types/any/any_01/main.k b/test/grammar/types/any/any_01/main.k new file mode 100644 index 000000000..53f2e7d4f --- /dev/null +++ b/test/grammar/types/any/any_01/main.k @@ -0,0 +1,23 @@ + +schema CheckAnyType: + x_1?: any + x_2?: any + + x_list_0?: any = None + x_list_1?: [any] + x_list_2?: [any] + + x_dict_v_1?: {str:any} + x_dict_v_2?: {str:any} + +x = CheckAnyType { + x_1: 1 + x_2: 'abc' + + x_list_0: ['a'] + x_list_1: [1, 2, 3] + x_list_2: ['a', 1, True] + + x_dict_v_1: {'a': 1} + x_dict_v_2: {'a': 1, 'b':True} +} diff --git a/test/grammar/types/any/any_01/stdout.golden b/test/grammar/types/any/any_01/stdout.golden new file mode 100644 index 000000000..7be6a518d --- /dev/null +++ b/test/grammar/types/any/any_01/stdout.golden @@ -0,0 +1,18 @@ +x: + x_1: 1 + x_2: abc + x_list_0: + - a + x_list_1: + - 1 + - 2 + - 3 + x_list_2: + - a + - 1 + - true + x_dict_v_1: + a: 1 + x_dict_v_2: + a: 1 + b: true \ No newline at end of file diff --git a/test/grammar/types/args/lambda_types_01/main.k b/test/grammar/types/args/lambda_types_01/main.k new file mode 100644 index 000000000..4d1f8712b --- /dev/null +++ b/test/grammar/types/args/lambda_types_01/main.k @@ -0,0 +1,6 @@ +typeFunc = lambda age_: int, name_: "KCL"|"CUE" { + age = age_ + name = name_ +} + +x1 = typeFunc(1, "KCL") diff --git a/test/grammar/types/args/lambda_types_01/stdout.golden b/test/grammar/types/args/lambda_types_01/stdout.golden new file mode 100644 index 000000000..a83d51459 --- /dev/null +++ b/test/grammar/types/args/lambda_types_01/stdout.golden @@ -0,0 +1 @@ +x1: KCL diff --git a/test/grammar/types/args/lambda_types_02/main.k b/test/grammar/types/args/lambda_types_02/main.k new file mode 100644 index 000000000..aa4ef4ae9 --- /dev/null +++ b/test/grammar/types/args/lambda_types_02/main.k @@ -0,0 +1,6 @@ +typeFunc = lambda age_: int, name_ -> str { + age = age_ + name = name_ +} + +x1 = typeFunc(1, "KCL") diff --git a/test/grammar/types/args/lambda_types_02/stdout.golden b/test/grammar/types/args/lambda_types_02/stdout.golden new file mode 100644 index 000000000..a83d51459 --- /dev/null +++ b/test/grammar/types/args/lambda_types_02/stdout.golden @@ -0,0 +1 @@ +x1: KCL diff --git a/test/grammar/types/args/lambda_types_err_01/main.k b/test/grammar/types/args/lambda_types_err_01/main.k new file mode 100644 index 000000000..8980d00bf --- /dev/null +++ b/test/grammar/types/args/lambda_types_err_01/main.k @@ -0,0 +1,6 @@ +typeFunc = lambda age_: int, name_: "KCL"|"CUE" { + age = age_ + name = name_ +} + +x1 = typeFunc(1, "Golang") diff --git a/test/grammar/types/args/lambda_types_err_01/stderr.golden.py b/test/grammar/types/args/lambda_types_err_01/stderr.golden.py new file mode 100644 index 000000000..113d483f2 --- /dev/null +++ b/test/grammar/types/args/lambda_types_err_01/stderr.golden.py @@ -0,0 +1,22 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + col_no=18, + arg_msg="got str(Golang)" + ) + ], + arg_msg="expect str(KCL)|str(CUE), got str(Golang)" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/args/lambda_types_err_02/main.k b/test/grammar/types/args/lambda_types_err_02/main.k new file mode 100644 index 000000000..0fe4101e9 --- /dev/null +++ b/test/grammar/types/args/lambda_types_err_02/main.k @@ -0,0 +1,6 @@ +typeFunc = lambda age_: int, name_: "KCL"|"CUE" { + age = age_ + name = name_ +} + +x1 = typeFunc("1", "Golang") diff --git a/test/grammar/types/args/lambda_types_err_02/stderr.golden.py b/test/grammar/types/args/lambda_types_err_02/stderr.golden.py new file mode 100644 index 000000000..d90e9898f --- /dev/null +++ b/test/grammar/types/args/lambda_types_err_02/stderr.golden.py @@ -0,0 +1,22 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + col_no=15, + arg_msg="got str(1)" + ) + ], + arg_msg="expect int, got str(1)" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/args/schema_types_01/main.k b/test/grammar/types/args/schema_types_01/main.k new file mode 100644 index 000000000..938485c5e --- /dev/null +++ b/test/grammar/types/args/schema_types_01/main.k @@ -0,0 +1,6 @@ + +schema CheckArgType[age_:int, name_:"KCL"|"CUE"]: + age: any = age_ + name: any = name_ + +x1 = CheckArgType(1, "KCL") {} diff --git a/test/grammar/types/args/schema_types_01/stdout.golden b/test/grammar/types/args/schema_types_01/stdout.golden new file mode 100644 index 000000000..790ec511b --- /dev/null +++ b/test/grammar/types/args/schema_types_01/stdout.golden @@ -0,0 +1,3 @@ +x1: + age: 1 + name: KCL \ No newline at end of file diff --git a/test/grammar/types/args/schema_types_02_schema/kcl.mod b/test/grammar/types/args/schema_types_02_schema/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/types/args/schema_types_02_schema/main.k b/test/grammar/types/args/schema_types_02_schema/main.k new file mode 100644 index 000000000..b921f828f --- /dev/null +++ b/test/grammar/types/args/schema_types_02_schema/main.k @@ -0,0 +1,13 @@ +import sub as pkg + +schema CheckArgType[x: pkg.Person]: + user: any = x + +schema Person: + name: str = "KCL" + +_x = pkg.Person{ + name = "person" +} + +x1 = CheckArgType(_x) {} diff --git a/test/grammar/types/args/schema_types_02_schema/stdout.golden b/test/grammar/types/args/schema_types_02_schema/stdout.golden new file mode 100644 index 000000000..c653109f0 --- /dev/null +++ b/test/grammar/types/args/schema_types_02_schema/stdout.golden @@ -0,0 +1,3 @@ +x1: + user: + name: person \ No newline at end of file diff --git a/test/grammar/types/args/schema_types_02_schema/sub/sub.k b/test/grammar/types/args/schema_types_02_schema/sub/sub.k new file mode 100644 index 000000000..f0cfcae9d --- /dev/null +++ b/test/grammar/types/args/schema_types_02_schema/sub/sub.k @@ -0,0 +1,2 @@ +schema Person: + name: str = "KCL" diff --git a/test/grammar/types/args/schema_types_03_list/main.k b/test/grammar/types/args/schema_types_03_list/main.k new file mode 100644 index 000000000..0a4d7680d --- /dev/null +++ b/test/grammar/types/args/schema_types_03_list/main.k @@ -0,0 +1,7 @@ + +schema CheckArgType[a0: [], a1: [any], a2: [str|bool]]: + attr0: any = a0 + attr1: any = a1 + attr2: any = a2 + +x1 = CheckArgType([123, True], ["abc", 456], ["aa", "bb", True]) {} diff --git a/test/grammar/types/args/schema_types_03_list/stdout.golden b/test/grammar/types/args/schema_types_03_list/stdout.golden new file mode 100644 index 000000000..dc3e5e8a1 --- /dev/null +++ b/test/grammar/types/args/schema_types_03_list/stdout.golden @@ -0,0 +1,11 @@ +x1: + attr0: + - 123 + - true + attr1: + - abc + - 456 + attr2: + - aa + - bb + - true \ No newline at end of file diff --git a/test/grammar/types/args/schema_types_04_partial/main.k b/test/grammar/types/args/schema_types_04_partial/main.k new file mode 100644 index 000000000..e6a32e80e --- /dev/null +++ b/test/grammar/types/args/schema_types_04_partial/main.k @@ -0,0 +1,5 @@ +schema Person[data: str, n]: + name: str = data + count: int = n + +person = Person("Alice", 1) {} diff --git a/test/grammar/types/args/schema_types_04_partial/stdout.golden b/test/grammar/types/args/schema_types_04_partial/stdout.golden new file mode 100644 index 000000000..058e2ac88 --- /dev/null +++ b/test/grammar/types/args/schema_types_04_partial/stdout.golden @@ -0,0 +1,3 @@ +person: + name: Alice + count: 1 diff --git a/test/grammar/types/args/schema_types_05_without_config/main.k b/test/grammar/types/args/schema_types_05_without_config/main.k new file mode 100644 index 000000000..37dbc5ee9 --- /dev/null +++ b/test/grammar/types/args/schema_types_05_without_config/main.k @@ -0,0 +1,5 @@ +schema Person[data: str, n]: + name: str = data + count: int = n + +person = Person("Alice", 1) diff --git a/test/grammar/types/args/schema_types_05_without_config/stdout.golden b/test/grammar/types/args/schema_types_05_without_config/stdout.golden new file mode 100644 index 000000000..058e2ac88 --- /dev/null +++ b/test/grammar/types/args/schema_types_05_without_config/stdout.golden @@ -0,0 +1,3 @@ +person: + name: Alice + count: 1 diff --git a/test/grammar/types/args/schema_types_06_kwargs/main.k b/test/grammar/types/args/schema_types_06_kwargs/main.k new file mode 100644 index 000000000..f2d610b02 --- /dev/null +++ b/test/grammar/types/args/schema_types_06_kwargs/main.k @@ -0,0 +1,5 @@ +schema Person[data: str, n]: + name: str = data + count: int = n + +person = Person(data="Alice", n=1) diff --git a/test/grammar/types/args/schema_types_06_kwargs/stdout.golden b/test/grammar/types/args/schema_types_06_kwargs/stdout.golden new file mode 100644 index 000000000..058e2ac88 --- /dev/null +++ b/test/grammar/types/args/schema_types_06_kwargs/stdout.golden @@ -0,0 +1,3 @@ +person: + name: Alice + count: 1 diff --git a/test/grammar/types/args/schema_types_07_union_types/main.k b/test/grammar/types/args/schema_types_07_union_types/main.k new file mode 100644 index 000000000..fbd5768dd --- /dev/null +++ b/test/grammar/types/args/schema_types_07_union_types/main.k @@ -0,0 +1,5 @@ +schema Person[data: str, n]: + name: str = data + count: int | float | str = n + +person = Person(data="Alice", n=1) diff --git a/test/grammar/types/args/schema_types_07_union_types/stdout.golden b/test/grammar/types/args/schema_types_07_union_types/stdout.golden new file mode 100644 index 000000000..058e2ac88 --- /dev/null +++ b/test/grammar/types/args/schema_types_07_union_types/stdout.golden @@ -0,0 +1,3 @@ +person: + name: Alice + count: 1 diff --git a/test/grammar/types/args/schema_types_err_01/main.k b/test/grammar/types/args/schema_types_err_01/main.k new file mode 100644 index 000000000..3c9b0e2d5 --- /dev/null +++ b/test/grammar/types/args/schema_types_err_01/main.k @@ -0,0 +1,5 @@ +schema CheckArgType[age_:int, name_:"KCL"|"CUE"]: + age: any = age_ + name: any = name_ + +x1 = CheckArgType(1, "Golang") {} diff --git a/test/grammar/types/args/schema_types_err_01/stderr.golden.py b/test/grammar/types/args/schema_types_err_01/stderr.golden.py new file mode 100644 index 000000000..9442f883c --- /dev/null +++ b/test/grammar/types/args/schema_types_err_01/stderr.golden.py @@ -0,0 +1,22 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=22, + arg_msg="got str(Golang)" + ) + ], + arg_msg="expect str(KCL)|str(CUE), got str(Golang)" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/args/schema_types_err_02_schema/kcl.mod b/test/grammar/types/args/schema_types_err_02_schema/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/types/args/schema_types_err_02_schema/main.k b/test/grammar/types/args/schema_types_err_02_schema/main.k new file mode 100644 index 000000000..c16b08779 --- /dev/null +++ b/test/grammar/types/args/schema_types_err_02_schema/main.k @@ -0,0 +1,13 @@ +import sub as pkg + +schema CheckArgType[x: pkg.Person]: + user: any = x + +schema Person: + name: str = "KCL" + +_x = Person{ + name: "person" +} + +x1 = CheckArgType(_x) {} diff --git a/test/grammar/types/args/schema_types_err_02_schema/stderr.golden.py b/test/grammar/types/args/schema_types_err_02_schema/stderr.golden.py new file mode 100644 index 000000000..52849e382 --- /dev/null +++ b/test/grammar/types/args/schema_types_err_02_schema/stderr.golden.py @@ -0,0 +1,22 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=13, + col_no=19, + arg_msg="got Person" + ) + ], + arg_msg='expect sub.Person, got Person' + ), + file=sys.stdout +) + diff --git a/test/grammar/types/args/schema_types_err_02_schema/sub/sub.k b/test/grammar/types/args/schema_types_err_02_schema/sub/sub.k new file mode 100644 index 000000000..f0cfcae9d --- /dev/null +++ b/test/grammar/types/args/schema_types_err_02_schema/sub/sub.k @@ -0,0 +1,2 @@ +schema Person: + name: str = "KCL" diff --git a/test/grammar/types/args/schema_types_err_03_list/main.k b/test/grammar/types/args/schema_types_err_03_list/main.k new file mode 100644 index 000000000..3bf131f52 --- /dev/null +++ b/test/grammar/types/args/schema_types_err_03_list/main.k @@ -0,0 +1,6 @@ +schema CheckArgType[a0: [], a1: [any], a2: [str]]: + attr0: any = a0 + attr1: any = a1 + attr2: any = a2 + +x1 = CheckArgType([123, True], ["abc", 456], ["aa", True]) {} diff --git a/test/grammar/types/args/schema_types_err_03_list/stderr.golden.py b/test/grammar/types/args/schema_types_err_03_list/stderr.golden.py new file mode 100644 index 000000000..a361f5f3a --- /dev/null +++ b/test/grammar/types/args/schema_types_err_03_list/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + col_no=46, + arg_msg="got [bool(True)|str(aa)]" + ) + ], + arg_msg='expect [str], got [bool(True)|str(aa)]' + ), + file=sys.stdout +) + diff --git a/test/grammar/types/args/schema_types_err_04_without_config/main.k b/test/grammar/types/args/schema_types_err_04_without_config/main.k new file mode 100644 index 000000000..54b0d8dad --- /dev/null +++ b/test/grammar/types/args/schema_types_err_04_without_config/main.k @@ -0,0 +1,6 @@ +schema CheckArgType[a0: [], a1: [any], a2: [str]]: + attr0: any = a0 + attr1: any = a1 + attr2: any = a2 + +x1 = CheckArgType([123, True], ["abc", 456], ["aa", True]) diff --git a/test/grammar/types/args/schema_types_err_04_without_config/stderr.golden.py b/test/grammar/types/args/schema_types_err_04_without_config/stderr.golden.py new file mode 100644 index 000000000..42785b85c --- /dev/null +++ b/test/grammar/types/args/schema_types_err_04_without_config/stderr.golden.py @@ -0,0 +1,21 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + col_no=46, + arg_msg='got [bool(True)|str(aa)]' + ) + ], + arg_msg='expect [str], got [bool(True)|str(aa)]' + ), + file=sys.stdout +) diff --git a/test/grammar/types/args/schema_types_err_05_kwargs/main.k b/test/grammar/types/args/schema_types_err_05_kwargs/main.k new file mode 100644 index 000000000..4a3dc41c8 --- /dev/null +++ b/test/grammar/types/args/schema_types_err_05_kwargs/main.k @@ -0,0 +1,5 @@ +schema Person[data: str, n: int]: + name: str = data + count: int = n + +person = Person(data="Alice", n="1") diff --git a/test/grammar/types/args/schema_types_err_05_kwargs/stderr.golden.py b/test/grammar/types/args/schema_types_err_05_kwargs/stderr.golden.py new file mode 100644 index 000000000..338fc8cb6 --- /dev/null +++ b/test/grammar/types/args/schema_types_err_05_kwargs/stderr.golden.py @@ -0,0 +1,22 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=31, + arg_msg="got str(1)" + ) + ], + arg_msg='expect int, got str(1)' + ), + file=sys.stdout +) + diff --git a/test/grammar/types/literal/lit_01/main.k b/test/grammar/types/literal/lit_01/main.k new file mode 100644 index 000000000..d9ef6cec4 --- /dev/null +++ b/test/grammar/types/literal/lit_01/main.k @@ -0,0 +1,55 @@ + + +schema CheckLiteralType: + + bool_0: True + bool_1: False + bool_2_1: True|False + bool_2_2: True|False + bool_3_1?: True|False + bool_3_2?: True|False + bool_3_3?: True|False + + int_0: 0 + int_1: 123 + int_2_1: 80|8080 + int_2_2: 80|8080 + + float_p: 3.14 + float_e: 2.71 + float_e_p_1?: 3.14 | 2.71 + float_e_p_2?: 3.14 | 2.71 + float_e_p_3?: 3.14 | 2.71 + + str_TCP: "TCP" + str_UDP: "UDP" + str_TCP_UCP_1?: "TCP" | "UDP" + str_TCP_UCP_2?: "TCP" | "UDP" + str_TCP_UCP_3?: "TCP" | "UDP" + +all_lit_types = CheckLiteralType { + bool_0: True + bool_1: False + bool_2_1: True + bool_2_2: False + bool_3_1: True + bool_3_2: False + bool_3_3: None + + int_0: 0 + int_1: 123 + int_2_1: 80 + int_2_2: 8080 + + float_p: 3.14 + float_e: 2.71 + float_e_p_1: 3.14 + float_e_p_2: 2.71 + float_e_p_3: None + + str_TCP: "TCP" + str_UDP: "UDP" + str_TCP_UCP_1: "TCP" + str_TCP_UCP_2: "UDP" + str_TCP_UCP_3: None +} diff --git a/test/grammar/types/literal/lit_01/stdout.golden b/test/grammar/types/literal/lit_01/stdout.golden new file mode 100644 index 000000000..b4c4a8f44 --- /dev/null +++ b/test/grammar/types/literal/lit_01/stdout.golden @@ -0,0 +1,22 @@ +all_lit_types: + bool_0: true + bool_1: false + bool_2_1: true + bool_2_2: false + bool_3_1: true + bool_3_2: false + bool_3_3: null + int_0: 0 + int_1: 123 + int_2_1: 80 + int_2_2: 8080 + float_p: 3.14 + float_e: 2.71 + float_e_p_1: 3.14 + float_e_p_2: 2.71 + float_e_p_3: null + str_TCP: TCP + str_UDP: UDP + str_TCP_UCP_1: TCP + str_TCP_UCP_2: UDP + str_TCP_UCP_3: null diff --git a/test/grammar/types/literal/lit_02_union/main.k b/test/grammar/types/literal/lit_02_union/main.k new file mode 100644 index 000000000..ba310f646 --- /dev/null +++ b/test/grammar/types/literal/lit_02_union/main.k @@ -0,0 +1,22 @@ + + +schema CheckLiteralType: + x_1?: True|123|3.14|"abc"|[]|{str:} + x_2?: True|123|3.14|"abc"|[]|{str:} + x_3?: True|123|3.14|"abc"|[]|{str:} + x_4?: True|123|3.14|"abc"|[]|{str:} + x_5?: True|123|3.14|"abc"|[]|{str:} + x_6?: True|123|3.14|"abc"|[]|{str:} + + s_00: "" + +x = CheckLiteralType { + x_1: True + x_2: 123 + x_3: 3.14 + x_4: "abc" + x_5: [1, 2, "111"] + x_6: {"a":1, "b":2} + + s_00: '' +} diff --git a/test/grammar/types/literal/lit_02_union/stdout.golden b/test/grammar/types/literal/lit_02_union/stdout.golden new file mode 100644 index 000000000..44d7c0278 --- /dev/null +++ b/test/grammar/types/literal/lit_02_union/stdout.golden @@ -0,0 +1,13 @@ +x: + x_1: true + x_2: 123 + x_3: 3.14 + x_4: abc + x_5: + - 1 + - 2 + - '111' + x_6: + a: 1 + b: 2 + s_00: '' diff --git a/test/grammar/types/literal/lit_03_int_none/main.k b/test/grammar/types/literal/lit_03_int_none/main.k new file mode 100644 index 000000000..de51f47ef --- /dev/null +++ b/test/grammar/types/literal/lit_03_int_none/main.k @@ -0,0 +1,6 @@ +schema CheckLiteralType: + none_0?: 1 + +failed = CheckLiteralType { + none_0 = None +} diff --git a/test/grammar/types/literal/lit_03_int_none/stdout.golden b/test/grammar/types/literal/lit_03_int_none/stdout.golden new file mode 100644 index 000000000..562f4a66c --- /dev/null +++ b/test/grammar/types/literal/lit_03_int_none/stdout.golden @@ -0,0 +1,2 @@ +failed: + none_0: null diff --git a/test/grammar/types/literal/lit_04_unit/main.k b/test/grammar/types/literal/lit_04_unit/main.k new file mode 100644 index 000000000..b83b7ebec --- /dev/null +++ b/test/grammar/types/literal/lit_04_unit/main.k @@ -0,0 +1,8 @@ +schema CheckLiteralType: + int_01: 123K | 456 + int_02: 123 | 456K + +x = CheckLiteralType { + int_01 = 456 + int_02 = 123 +} diff --git a/test/grammar/types/literal/lit_04_unit/stdout.golden b/test/grammar/types/literal/lit_04_unit/stdout.golden new file mode 100644 index 000000000..91dff4413 --- /dev/null +++ b/test/grammar/types/literal/lit_04_unit/stdout.golden @@ -0,0 +1,3 @@ +x: + int_01: 456 + int_02: 123 diff --git a/test/grammar/types/literal/lit_05_dict_key_lit_union/main.k b/test/grammar/types/literal/lit_05_dict_key_lit_union/main.k new file mode 100644 index 000000000..405ffc2d3 --- /dev/null +++ b/test/grammar/types/literal/lit_05_dict_key_lit_union/main.k @@ -0,0 +1,4 @@ +x: {"A"|"B": int} = { + "A": 1 + "B": 2 +} \ No newline at end of file diff --git a/test/grammar/types/literal/lit_05_dict_key_lit_union/stdout.golden b/test/grammar/types/literal/lit_05_dict_key_lit_union/stdout.golden new file mode 100644 index 000000000..b9cdb72ae --- /dev/null +++ b/test/grammar/types/literal/lit_05_dict_key_lit_union/stdout.golden @@ -0,0 +1,3 @@ +x: + A: 1 + B: 2 diff --git a/test/grammar/types/literal/lit_err_01_bool_01/main.k b/test/grammar/types/literal/lit_err_01_bool_01/main.k new file mode 100644 index 000000000..82276812b --- /dev/null +++ b/test/grammar/types/literal/lit_err_01_bool_01/main.k @@ -0,0 +1,6 @@ +schema CheckLiteralType: + bool_01: True + +failed = CheckLiteralType { + bool_01 = False +} diff --git a/test/grammar/types/literal/lit_err_01_bool_01/stderr.golden.py b/test/grammar/types/literal/lit_err_01_bool_01/stderr.golden.py new file mode 100644 index 000000000..ec79eee44 --- /dev/null +++ b/test/grammar/types/literal/lit_err_01_bool_01/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect bool(True)", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + arg_msg="got bool(False)" + ) + ], + arg_msg="expect bool(True), got bool(False)" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/literal/lit_err_01_bool_02/main.k b/test/grammar/types/literal/lit_err_01_bool_02/main.k new file mode 100644 index 000000000..372c2214d --- /dev/null +++ b/test/grammar/types/literal/lit_err_01_bool_02/main.k @@ -0,0 +1,6 @@ +schema CheckLiteralType: + bool_01: True + +failed = CheckLiteralType { + bool_01 = 123 +} diff --git a/test/grammar/types/literal/lit_err_01_bool_02/stderr.golden.py b/test/grammar/types/literal/lit_err_01_bool_02/stderr.golden.py new file mode 100644 index 000000000..a557519e8 --- /dev/null +++ b/test/grammar/types/literal/lit_err_01_bool_02/stderr.golden.py @@ -0,0 +1,29 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect bool(True)", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + arg_msg="got int(123)" + ) + ], + arg_msg="expect bool(True), got int(123)" + ), + file=sys.stdout +) diff --git a/test/grammar/types/literal/lit_err_02_int_01/main.k b/test/grammar/types/literal/lit_err_02_int_01/main.k new file mode 100644 index 000000000..7c29adb98 --- /dev/null +++ b/test/grammar/types/literal/lit_err_02_int_01/main.k @@ -0,0 +1,8 @@ +schema CheckLiteralType: + int_01: 123 | 456 + int_02: 123 | 456 + +x = CheckLiteralType { + int_01: 123 + int_02: 789 +} diff --git a/test/grammar/types/literal/lit_err_02_int_01/stderr.golden.py b/test/grammar/types/literal/lit_err_02_int_01/stderr.golden.py new file mode 100644 index 000000000..cd5eab6c2 --- /dev/null +++ b/test/grammar/types/literal/lit_err_02_int_01/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=5, + indent_count=1, + arg_msg="expect int(123)|int(456)", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=5, + arg_msg="got int(789)" + ) + ], + arg_msg="expect int(123)|int(456), got int(789)" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/literal/lit_err_02_int_02/main.k b/test/grammar/types/literal/lit_err_02_int_02/main.k new file mode 100644 index 000000000..f2043a750 --- /dev/null +++ b/test/grammar/types/literal/lit_err_02_int_02/main.k @@ -0,0 +1,6 @@ +schema CheckLiteralType: + int_0: 0 + +x = CheckLiteralType { + int_0: 0.0 +} diff --git a/test/grammar/types/literal/lit_err_02_int_02/stderr.golden.py b/test/grammar/types/literal/lit_err_02_int_02/stderr.golden.py new file mode 100644 index 000000000..75906d989 --- /dev/null +++ b/test/grammar/types/literal/lit_err_02_int_02/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect int(0)", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + arg_msg="got float(0.0)" + ) + ], + arg_msg="expect int(0), got float(0.0)" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/literal/lit_err_03_float_01/main.k b/test/grammar/types/literal/lit_err_03_float_01/main.k new file mode 100644 index 000000000..c5651ca73 --- /dev/null +++ b/test/grammar/types/literal/lit_err_03_float_01/main.k @@ -0,0 +1,6 @@ +schema CheckLiteralType: + float_01: 0.0 + +x = CheckLiteralType { + float_01: 0 +} diff --git a/test/grammar/types/literal/lit_err_03_float_01/stderr.golden.py b/test/grammar/types/literal/lit_err_03_float_01/stderr.golden.py new file mode 100644 index 000000000..ca5d449bd --- /dev/null +++ b/test/grammar/types/literal/lit_err_03_float_01/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect float(0.0)", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + arg_msg="got int(0)" + ) + ], + arg_msg="expect float(0.0), got int(0)" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/literal/lit_err_03_float_02/main.k b/test/grammar/types/literal/lit_err_03_float_02/main.k new file mode 100644 index 000000000..19d9be9c6 --- /dev/null +++ b/test/grammar/types/literal/lit_err_03_float_02/main.k @@ -0,0 +1,6 @@ +schema CheckLiteralType: + x_01: 0.0 | 3.14 | 9527 + +x = CheckLiteralType { + x_01: 0 +} diff --git a/test/grammar/types/literal/lit_err_03_float_02/stderr.golden.py b/test/grammar/types/literal/lit_err_03_float_02/stderr.golden.py new file mode 100644 index 000000000..f54ca85cc --- /dev/null +++ b/test/grammar/types/literal/lit_err_03_float_02/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect int(9527)|float(0.0)|float(3.14)", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + arg_msg="got int(0)" + ) + ], + arg_msg="expect int(9527)|float(0.0)|float(3.14), got int(0)" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/literal/lit_err_04_str_01/main.k b/test/grammar/types/literal/lit_err_04_str_01/main.k new file mode 100644 index 000000000..3bc12f64b --- /dev/null +++ b/test/grammar/types/literal/lit_err_04_str_01/main.k @@ -0,0 +1,6 @@ +schema CheckLiteralType: + x_01: "TCP" | 'UDP' + +x = CheckLiteralType { + x_01: "HTTP" +} diff --git a/test/grammar/types/literal/lit_err_04_str_01/stderr.golden.py b/test/grammar/types/literal/lit_err_04_str_01/stderr.golden.py new file mode 100644 index 000000000..3f899a78a --- /dev/null +++ b/test/grammar/types/literal/lit_err_04_str_01/stderr.golden.py @@ -0,0 +1,30 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect str(TCP)|str(UDP)", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + arg_msg="got str(HTTP)" + ) + ], + arg_msg='expect str(TCP)|str(UDP), got str(HTTP)' + ), + file=sys.stdout +) + diff --git a/test/grammar/types/literal/lit_err_04_str_02/main.k b/test/grammar/types/literal/lit_err_04_str_02/main.k new file mode 100644 index 000000000..2fabb94eb --- /dev/null +++ b/test/grammar/types/literal/lit_err_04_str_02/main.k @@ -0,0 +1,6 @@ +schema CheckLiteralType: + x_01: "TCP" | 'UDP' + +x = CheckLiteralType { + x_01: 443 +} diff --git a/test/grammar/types/literal/lit_err_04_str_02/stderr.golden.py b/test/grammar/types/literal/lit_err_04_str_02/stderr.golden.py new file mode 100644 index 000000000..284fee05e --- /dev/null +++ b/test/grammar/types/literal/lit_err_04_str_02/stderr.golden.py @@ -0,0 +1,29 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect str(TCP)|str(UDP)", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + arg_msg="got int(443)" + ) + ], + arg_msg='expect str(TCP)|str(UDP), got int(443)' + ), + file=sys.stdout +) diff --git a/test/grammar/types/literal/lit_err_05_union_01/main.k b/test/grammar/types/literal/lit_err_05_union_01/main.k new file mode 100644 index 000000000..beaf72d80 --- /dev/null +++ b/test/grammar/types/literal/lit_err_05_union_01/main.k @@ -0,0 +1,6 @@ +schema CheckLiteralType: + x_1?: True|123|3.14|"abc"|[]|{str:} + +x = CheckLiteralType { + x_1: 443 +} diff --git a/test/grammar/types/literal/lit_err_05_union_01/stderr.golden.py b/test/grammar/types/literal/lit_err_05_union_01/stderr.golden.py new file mode 100644 index 000000000..ba16a2272 --- /dev/null +++ b/test/grammar/types/literal/lit_err_05_union_01/stderr.golden.py @@ -0,0 +1,29 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect bool(True)|int(123)|float(3.14)|str(abc)|[any]|{str:any}", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + col_no=5, + arg_msg="got int(443)" + ) + ], + arg_msg='expect bool(True)|int(123)|float(3.14)|str(abc)|[any]|{str:any}, got int(443)' + ), + file=sys.stdout +) diff --git a/test/grammar/types/literal/lit_err_06_unit/main.k b/test/grammar/types/literal/lit_err_06_unit/main.k new file mode 100644 index 000000000..38affe53f --- /dev/null +++ b/test/grammar/types/literal/lit_err_06_unit/main.k @@ -0,0 +1,8 @@ +schema CheckLiteralType: + int_01: 123K | 456 + int_02: 123 | 456K + +x = CheckLiteralType { + int_01 = 123 + int_02 = 123 +} diff --git a/test/grammar/types/literal/lit_err_06_unit/stderr.golden.py b/test/grammar/types/literal/lit_err_06_unit/stderr.golden.py new file mode 100644 index 000000000..5ca3f54e8 --- /dev/null +++ b/test/grammar/types/literal/lit_err_06_unit/stderr.golden.py @@ -0,0 +1,29 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5, + indent_count=1, + arg_msg="expect int(456)|number_multiplier(123K)", + err_level=kcl_error.ErrLevel.ORDINARY + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=6, + col_no=5, + arg_msg="got int(123)" + ) + ], + arg_msg='expect int(456)|number_multiplier(123K), got int(123)' + ), + file=sys.stdout +) diff --git a/test/grammar/types/type_alias/type_alias_0/main.k b/test/grammar/types/type_alias/type_alias_0/main.k new file mode 100644 index 000000000..1b0cd68af --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_0/main.k @@ -0,0 +1,10 @@ +type Int = int +type Float = float +type Bool = bool +type String = str +type Any = any +a: Int = 1 +b: Float = 2.0 +c: Bool = True +d: String = "xx" +e: Any = None diff --git a/test/grammar/types/type_alias/type_alias_0/stdout.golden b/test/grammar/types/type_alias/type_alias_0/stdout.golden new file mode 100644 index 000000000..07ec3cb6b --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_0/stdout.golden @@ -0,0 +1,5 @@ +a: 1 +b: 2.0 +c: true +d: xx +e: null diff --git a/test/grammar/types/type_alias/type_alias_1/main.k b/test/grammar/types/type_alias/type_alias_1/main.k new file mode 100644 index 000000000..3d515571a --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_1/main.k @@ -0,0 +1,14 @@ +type Int = int +type Float = float +type Bool = bool +type String = str +type Any = any + +schema Data: + a?: Int = 1 + b?: Float = 2.0 + c?: Bool = True + d?: String = "xx" + e?: Any = None + +data = Data {} diff --git a/test/grammar/types/type_alias/type_alias_1/stdout.golden b/test/grammar/types/type_alias/type_alias_1/stdout.golden new file mode 100644 index 000000000..e67602f33 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_1/stdout.golden @@ -0,0 +1,6 @@ +data: + a: 1 + b: 2.0 + c: true + d: xx + e: null diff --git a/test/grammar/types/type_alias/type_alias_2/main.k b/test/grammar/types/type_alias/type_alias_2/main.k new file mode 100644 index 000000000..858393d87 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_2/main.k @@ -0,0 +1,8 @@ +type Int = 1 +type Float = 2.0 +type Bool = True +type String = "xx" +a: Int = 1 +b: Float = 2.0 +c: Bool = True +d: String = "xx" diff --git a/test/grammar/types/type_alias/type_alias_2/stdout.golden b/test/grammar/types/type_alias/type_alias_2/stdout.golden new file mode 100644 index 000000000..3a41c6234 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_2/stdout.golden @@ -0,0 +1,4 @@ +a: 1 +b: 2.0 +c: true +d: xx diff --git a/test/grammar/types/type_alias/type_alias_3/main.k b/test/grammar/types/type_alias/type_alias_3/main.k new file mode 100644 index 000000000..4be26de46 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_3/main.k @@ -0,0 +1,8 @@ +type Int = 1 | 2 | 3 +type Float = 1.0 | 2.0 | 3.0 +type Bool = True | False +type String = "xx" | "yy" | "zz" +a: Int = 1 +b: Float = 2.0 +c: Bool = True +d: String = "xx" diff --git a/test/grammar/types/type_alias/type_alias_3/stdout.golden b/test/grammar/types/type_alias/type_alias_3/stdout.golden new file mode 100644 index 000000000..3a41c6234 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_3/stdout.golden @@ -0,0 +1,4 @@ +a: 1 +b: 2.0 +c: true +d: xx diff --git a/test/grammar/types/type_alias/type_alias_4/main.k b/test/grammar/types/type_alias/type_alias_4/main.k new file mode 100644 index 000000000..7acd181c2 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_4/main.k @@ -0,0 +1,9 @@ +type Int = int +type FloatUnionInt = float | Int +type BoolUnionFloatUnionInt = bool | FloatUnionInt +type StringUnionBoolUnionFloatUnionInt = str | BoolUnionFloatUnionInt + +a: Int = 1 +b: FloatUnionInt = 2.0 +c: BoolUnionFloatUnionInt = True +d: StringUnionBoolUnionFloatUnionInt = "xx" diff --git a/test/grammar/types/type_alias/type_alias_4/stdout.golden b/test/grammar/types/type_alias/type_alias_4/stdout.golden new file mode 100644 index 000000000..3a41c6234 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_4/stdout.golden @@ -0,0 +1,4 @@ +a: 1 +b: 2.0 +c: true +d: xx diff --git a/test/grammar/types/type_alias/type_alias_5/main.k b/test/grammar/types/type_alias/type_alias_5/main.k new file mode 100644 index 000000000..9924443d4 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_5/main.k @@ -0,0 +1,18 @@ +type Color = "Red" | "Yellow" | "Blue" + +colorRed: Color = "Red" +colorYellow: Color = "Yellow" +colorBlue: Color = "Blue" + +schema Data: + color: Color + +dataColorRed = Data { + color = "Red" +} +dataColorYellow = Data { + color = "Yellow" +} +dataColorBlue = Data { + color = "Blue" +} diff --git a/test/grammar/types/type_alias/type_alias_5/stdout.golden b/test/grammar/types/type_alias/type_alias_5/stdout.golden new file mode 100644 index 000000000..360f83507 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_5/stdout.golden @@ -0,0 +1,9 @@ +colorRed: Red +colorYellow: Yellow +colorBlue: Blue +dataColorRed: + color: Red +dataColorYellow: + color: Yellow +dataColorBlue: + color: Blue diff --git a/test/grammar/types/type_alias/type_alias_6/main.k b/test/grammar/types/type_alias/type_alias_6/main.k new file mode 100644 index 000000000..ec3fcb35d --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_6/main.k @@ -0,0 +1,10 @@ +type Data1 = "1" | "2" | "3" +type Data2 = "4" | "5" | "6" +type Data = Data1 | Data2 + +a: Data = "1" +b: Data = "2" +c: Data = "3" +d: Data = "4" +e: Data = "5" +f: Data = "6" diff --git a/test/grammar/types/type_alias/type_alias_6/stdout.golden b/test/grammar/types/type_alias/type_alias_6/stdout.golden new file mode 100644 index 000000000..eaf6f4aae --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_6/stdout.golden @@ -0,0 +1,6 @@ +a: '1' +b: '2' +c: '3' +d: '4' +e: '5' +f: '6' diff --git a/test/grammar/types/type_alias/type_alias_7/kcl.mod b/test/grammar/types/type_alias/type_alias_7/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/types/type_alias/type_alias_7/main.k b/test/grammar/types/type_alias/type_alias_7/main.k new file mode 100644 index 000000000..78b639b1f --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_7/main.k @@ -0,0 +1,4 @@ +import pkg + +alice = pkg.Person { name = "Alice" } +bob = pkg.PersonOther { name = "Bob" } diff --git a/test/grammar/types/type_alias/type_alias_7/pkg/pkg.k b/test/grammar/types/type_alias/type_alias_7/pkg/pkg.k new file mode 100644 index 000000000..8dba5fb37 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_7/pkg/pkg.k @@ -0,0 +1,6 @@ +type String = str + +schema Person: + name: String + +type PersonOther = Person diff --git a/test/grammar/types/type_alias/type_alias_7/stdout.golden b/test/grammar/types/type_alias/type_alias_7/stdout.golden new file mode 100644 index 000000000..4721514a4 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_7/stdout.golden @@ -0,0 +1,4 @@ +alice: + name: Alice +bob: + name: Bob diff --git a/test/grammar/types/type_alias/type_alias_8/kcl.mod b/test/grammar/types/type_alias/type_alias_8/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/types/type_alias/type_alias_8/main.k b/test/grammar/types/type_alias/type_alias_8/main.k new file mode 100644 index 000000000..0e6d524bb --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_8/main.k @@ -0,0 +1,7 @@ +import pkg + +type Person = pkg.Person +type PersonOther = pkg.PersonOther + +alice = Person { name = "Alice" } +bob = PersonOther { name = "Bob" } diff --git a/test/grammar/types/type_alias/type_alias_8/pkg/pkg.k b/test/grammar/types/type_alias/type_alias_8/pkg/pkg.k new file mode 100644 index 000000000..8dba5fb37 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_8/pkg/pkg.k @@ -0,0 +1,6 @@ +type String = str + +schema Person: + name: String + +type PersonOther = Person diff --git a/test/grammar/types/type_alias/type_alias_8/stdout.golden b/test/grammar/types/type_alias/type_alias_8/stdout.golden new file mode 100644 index 000000000..4721514a4 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_8/stdout.golden @@ -0,0 +1,4 @@ +alice: + name: Alice +bob: + name: Bob diff --git a/test/grammar/types/type_alias/type_alias_err_0/main.k b/test/grammar/types/type_alias/type_alias_err_0/main.k new file mode 100644 index 000000000..ed995cbe8 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_err_0/main.k @@ -0,0 +1,7 @@ +type Lang = "KCL"|"CUE" + +schema CheckArgType[age_:int, name_:Lang]: + age: any = age_ + name: any = name_ + +x1 = CheckArgType(1, "Golang") {} diff --git a/test/grammar/types/type_alias/type_alias_err_0/stderr.golden.py b/test/grammar/types/type_alias/type_alias_err_0/stderr.golden.py new file mode 100644 index 000000000..417f01bd8 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_err_0/stderr.golden.py @@ -0,0 +1,22 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=7, + col_no=22, + arg_msg="got str(Golang)" + ) + ], + arg_msg="expect str(KCL)|str(CUE), got str(Golang)" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/type_alias/type_alias_err_1/main.k b/test/grammar/types/type_alias/type_alias_err_1/main.k new file mode 100644 index 000000000..234591d5f --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_err_1/main.k @@ -0,0 +1,3 @@ +type Int = int + +_name: Int = "Green" diff --git a/test/grammar/types/type_alias/type_alias_err_1/stderr.golden.py b/test/grammar/types/type_alias/type_alias_err_1/stderr.golden.py new file mode 100644 index 000000000..2808775f5 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_err_1/stderr.golden.py @@ -0,0 +1,23 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=3, + col_no=1, + arg_msg="got str(Green)" + ) + ], + arg_msg="expect int, got str(Green)" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/type_alias/type_alias_err_2/main.k b/test/grammar/types/type_alias/type_alias_err_2/main.k new file mode 100644 index 000000000..4c9d931d4 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_err_2/main.k @@ -0,0 +1,4 @@ +type String = str + +_name: String = "s" +_name: String = 1 diff --git a/test/grammar/types/type_alias/type_alias_err_2/stderr.golden.py b/test/grammar/types/type_alias/type_alias_err_2/stderr.golden.py new file mode 100644 index 000000000..13bdcd2d1 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_err_2/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=1, + arg_msg="got int(1)" + ) + ], + arg_msg="expect str, got int(1)" + ), + file=sys.stdout +) diff --git a/test/grammar/types/type_alias/type_alias_err_3/main.k b/test/grammar/types/type_alias/type_alias_err_3/main.k new file mode 100644 index 000000000..a2ab874df --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_err_3/main.k @@ -0,0 +1,4 @@ +type List = [str|int] + +_name: List = ["s", 1] +_name: List = [1.1] diff --git a/test/grammar/types/type_alias/type_alias_err_3/stderr.golden.py b/test/grammar/types/type_alias/type_alias_err_3/stderr.golden.py new file mode 100644 index 000000000..caefb2b20 --- /dev/null +++ b/test/grammar/types/type_alias/type_alias_err_3/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=4, + col_no=1, + arg_msg="got [float(1.1)]" + ) + ], + arg_msg="expect [int|str], got [float(1.1)]" + ), + file=sys.stdout +) diff --git a/test/grammar/types/type_as/type_as_0/main.k b/test/grammar/types/type_as/type_as_0/main.k new file mode 100644 index 000000000..0e43576b6 --- /dev/null +++ b/test/grammar/types/type_as/type_as_0/main.k @@ -0,0 +1,13 @@ +schema Data[idVar: int]: + id?: int = idVar + +type IntList = [int] +type StrMap = {str:str} + +a = 1 as int +b = 2.0 as float +c = True as bool +d = "s" as str +e = [1] as IntList +f = {key: "value"} as StrMap +g = Data(1) as Data diff --git a/test/grammar/types/type_as/type_as_0/stdout.golden b/test/grammar/types/type_as/type_as_0/stdout.golden new file mode 100644 index 000000000..d4ce11259 --- /dev/null +++ b/test/grammar/types/type_as/type_as_0/stdout.golden @@ -0,0 +1,10 @@ +a: 1 +b: 2.0 +c: true +d: s +e: +- 1 +f: + key: value +g: + id: 1 diff --git a/test/grammar/types/type_as/type_as_1/main.k b/test/grammar/types/type_as/type_as_1/main.k new file mode 100644 index 000000000..ea375db55 --- /dev/null +++ b/test/grammar/types/type_as/type_as_1/main.k @@ -0,0 +1,15 @@ +type UnionType = int | str | bool + +schema Data[a: UnionType]: + if typeof(a) == "int": + b = (a as int) + 1 + elif typeof(a) == "str": + c = (a as str) + "1" + elif typeof(a) == "bool": + d = (a as bool) + else: + e = a + +x0 = Data(0) +x1 = Data("s") +x2 = Data(True) diff --git a/test/grammar/types/type_as/type_as_1/stdout.golden b/test/grammar/types/type_as/type_as_1/stdout.golden new file mode 100644 index 000000000..48d0dee30 --- /dev/null +++ b/test/grammar/types/type_as/type_as_1/stdout.golden @@ -0,0 +1,6 @@ +x0: + b: 1 +x1: + c: s1 +x2: + d: true diff --git a/test/grammar/types/type_as/type_as_2/main.k b/test/grammar/types/type_as/type_as_2/main.k new file mode 100644 index 000000000..31028418c --- /dev/null +++ b/test/grammar/types/type_as/type_as_2/main.k @@ -0,0 +1,14 @@ +schema Data[a: any]: + if typeof(a) == "int": + b = (a as int) + 1 + elif typeof(a) == "str": + c = (a as str) + "1" + elif typeof(a) == "bool": + d = (a as bool) + else: + e = a + +x0 = Data(0) +x1 = Data("s") +x2 = Data(True) +x3 = Data([0]) diff --git a/test/grammar/types/type_as/type_as_2/stdout.golden b/test/grammar/types/type_as/type_as_2/stdout.golden new file mode 100644 index 000000000..9b8eb223f --- /dev/null +++ b/test/grammar/types/type_as/type_as_2/stdout.golden @@ -0,0 +1,9 @@ +x0: + b: 1 +x1: + c: s1 +x2: + d: true +x3: + e: + - 0 diff --git a/test/grammar/types/type_as/type_as_3/main.k b/test/grammar/types/type_as/type_as_3/main.k new file mode 100644 index 000000000..5733a77d6 --- /dev/null +++ b/test/grammar/types/type_as/type_as_3/main.k @@ -0,0 +1,8 @@ +schema Base: + id?: int = 1 + +schema Sub(Base): + data?: int = 2 + +sub: Base = Sub {} +id = (sub as Sub).id diff --git a/test/grammar/types/type_as/type_as_3/stdout.golden b/test/grammar/types/type_as/type_as_3/stdout.golden new file mode 100644 index 000000000..0223be805 --- /dev/null +++ b/test/grammar/types/type_as/type_as_3/stdout.golden @@ -0,0 +1,4 @@ +sub: + id: 1 + data: 2 +id: 1 diff --git a/test/grammar/types/type_as/type_as_4/kcl.mod b/test/grammar/types/type_as/type_as_4/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/types/type_as/type_as_4/main.k b/test/grammar/types/type_as/type_as_4/main.k new file mode 100644 index 000000000..f381b4608 --- /dev/null +++ b/test/grammar/types/type_as/type_as_4/main.k @@ -0,0 +1,7 @@ +import pkg + +schema Sub(pkg.Base): + data?: int = 2 + +base: pkg.Base = pkg.Base {} as pkg.Base +sub: Sub = Sub {} as Sub diff --git a/test/grammar/types/type_as/type_as_4/pkg/pkg.k b/test/grammar/types/type_as/type_as_4/pkg/pkg.k new file mode 100644 index 000000000..cb44e19e9 --- /dev/null +++ b/test/grammar/types/type_as/type_as_4/pkg/pkg.k @@ -0,0 +1,2 @@ +schema Base: + id?: int = 1 diff --git a/test/grammar/types/type_as/type_as_4/stdout.golden b/test/grammar/types/type_as/type_as_4/stdout.golden new file mode 100644 index 000000000..ff2709378 --- /dev/null +++ b/test/grammar/types/type_as/type_as_4/stdout.golden @@ -0,0 +1,5 @@ +base: + id: 1 +sub: + id: 1 + data: 2 diff --git a/test/grammar/types/type_as/type_as_err_0/main.k b/test/grammar/types/type_as/type_as_err_0/main.k new file mode 100644 index 000000000..4846b5c67 --- /dev/null +++ b/test/grammar/types/type_as/type_as_err_0/main.k @@ -0,0 +1 @@ +a = 1 as str diff --git a/test/grammar/types/type_as/type_as_err_0/stderr.golden.py b/test/grammar/types/type_as/type_as_err_0/stderr.golden.py new file mode 100644 index 000000000..795286494 --- /dev/null +++ b/test/grammar/types/type_as/type_as_err_0/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + ) + ], + arg_msg="Conversion of type 'int(1)' to type 'str' may be a mistake because neither type sufficiently overlaps with the other" + ), + file=sys.stdout +) diff --git a/test/grammar/types/type_as/type_as_err_1/main.k b/test/grammar/types/type_as/type_as_err_1/main.k new file mode 100644 index 000000000..a92b44fae --- /dev/null +++ b/test/grammar/types/type_as/type_as_err_1/main.k @@ -0,0 +1,2 @@ +a: int | str = "ss" +b: float = a as float diff --git a/test/grammar/types/type_as/type_as_err_1/stderr.golden.py b/test/grammar/types/type_as/type_as_err_1/stderr.golden.py new file mode 100644 index 000000000..2e3ee4c80 --- /dev/null +++ b/test/grammar/types/type_as/type_as_err_1/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.CompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + ) + ], + arg_msg="Conversion of type 'int|str' to type 'float' may be a mistake because neither type sufficiently overlaps with the other" + ), + file=sys.stdout +) diff --git a/test/grammar/types/var_type_annotation/type_fail_0/main.k b/test/grammar/types/var_type_annotation/type_fail_0/main.k new file mode 100644 index 000000000..0fc19e28e --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_0/main.k @@ -0,0 +1 @@ +_name: int = "Green" diff --git a/test/grammar/types/var_type_annotation/type_fail_0/stderr.golden.py b/test/grammar/types/var_type_annotation/type_fail_0/stderr.golden.py new file mode 100644 index 000000000..668619141 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_0/stderr.golden.py @@ -0,0 +1,23 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=1, + arg_msg="got str(Green)" + ) + ], + arg_msg="expect int, got str(Green)" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/var_type_annotation/type_fail_1/main.k b/test/grammar/types/var_type_annotation/type_fail_1/main.k new file mode 100644 index 000000000..78ed30294 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_1/main.k @@ -0,0 +1 @@ +_name: str = 1 diff --git a/test/grammar/types/var_type_annotation/type_fail_1/stderr.golden.py b/test/grammar/types/var_type_annotation/type_fail_1/stderr.golden.py new file mode 100644 index 000000000..6442aca70 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_1/stderr.golden.py @@ -0,0 +1,23 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=1, + arg_msg="got int(1)" + ) + ], + arg_msg="expect str, got int(1)" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/var_type_annotation/type_fail_2/main.k b/test/grammar/types/var_type_annotation/type_fail_2/main.k new file mode 100644 index 000000000..29f26eb70 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_2/main.k @@ -0,0 +1,2 @@ +_name = 1 +_name = "s" diff --git a/test/grammar/types/var_type_annotation/type_fail_2/stderr.golden.py b/test/grammar/types/var_type_annotation/type_fail_2/stderr.golden.py new file mode 100644 index 000000000..e2dfd60c7 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_2/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=1, + arg_msg="got str(s)" + ) + ], + arg_msg="expect int, got str(s)" + ), + file=sys.stdout +) diff --git a/test/grammar/types/var_type_annotation/type_fail_3/main.k b/test/grammar/types/var_type_annotation/type_fail_3/main.k new file mode 100644 index 000000000..adc8810c3 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_3/main.k @@ -0,0 +1,2 @@ +_name = "s" +_name = 1 diff --git a/test/grammar/types/var_type_annotation/type_fail_3/stderr.golden.py b/test/grammar/types/var_type_annotation/type_fail_3/stderr.golden.py new file mode 100644 index 000000000..7d9b6c6b1 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_3/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=1, + arg_msg="got int(1)" + ) + ], + arg_msg="expect str, got int(1)" + ), + file=sys.stdout +) diff --git a/test/grammar/types/var_type_annotation/type_fail_4/main.k b/test/grammar/types/var_type_annotation/type_fail_4/main.k new file mode 100644 index 000000000..cbbbd2660 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_4/main.k @@ -0,0 +1,2 @@ +_name = ["s"] +_name = [1] diff --git a/test/grammar/types/var_type_annotation/type_fail_4/stderr.golden.py b/test/grammar/types/var_type_annotation/type_fail_4/stderr.golden.py new file mode 100644 index 000000000..489f83b52 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_4/stderr.golden.py @@ -0,0 +1,23 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=1, + arg_msg="got [int(1)]" + ) + ], + arg_msg="expect [str], got [int(1)]" + ), + file=sys.stdout +) + diff --git a/test/grammar/types/var_type_annotation/type_fail_5/main.k b/test/grammar/types/var_type_annotation/type_fail_5/main.k new file mode 100644 index 000000000..598157fd3 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_5/main.k @@ -0,0 +1,2 @@ +_name = ["s", 1] +_name = [1.1] diff --git a/test/grammar/types/var_type_annotation/type_fail_5/stderr.golden.py b/test/grammar/types/var_type_annotation/type_fail_5/stderr.golden.py new file mode 100644 index 000000000..f0387221d --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_5/stderr.golden.py @@ -0,0 +1,22 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=1, + arg_msg="got [float(1.1)]" + ) + ], + arg_msg="expect [int|str], got [float(1.1)]" + ), + file=sys.stdout +) diff --git a/test/grammar/types/var_type_annotation/type_fail_6/main.k b/test/grammar/types/var_type_annotation/type_fail_6/main.k new file mode 100644 index 000000000..7d5defe95 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_6/main.k @@ -0,0 +1 @@ +name: "aa\"ab|" = "aa\"ab" diff --git a/test/grammar/types/var_type_annotation/type_fail_6/stderr.golden.py b/test/grammar/types/var_type_annotation/type_fail_6/stderr.golden.py new file mode 100644 index 000000000..86b1bd64c --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_6/stderr.golden.py @@ -0,0 +1,23 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=1, + arg_msg='got str(aa"ab)' + ) + ], + arg_msg='expect str(aa"ab|), got str(aa"ab)' + ), + file=sys.stdout +) + diff --git a/test/grammar/types/var_type_annotation/type_fail_7/main.k b/test/grammar/types/var_type_annotation/type_fail_7/main.k new file mode 100644 index 000000000..a2acdab54 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_7/main.k @@ -0,0 +1 @@ +name: "aa\"ab|" | "abc" = "aa\"ab" diff --git a/test/grammar/types/var_type_annotation/type_fail_7/stderr.golden.py b/test/grammar/types/var_type_annotation/type_fail_7/stderr.golden.py new file mode 100644 index 000000000..30d594587 --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_7/stderr.golden.py @@ -0,0 +1,23 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=1, + arg_msg='got str(aa"ab)' + ) + ], + arg_msg='expect str(aa"ab|)|str(abc), got str(aa"ab)' + ), + file=sys.stdout +) + diff --git a/test/grammar/types/var_type_annotation/type_fail_8/main.k b/test/grammar/types/var_type_annotation/type_fail_8/main.k new file mode 100644 index 000000000..3866ff2fa --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_8/main.k @@ -0,0 +1 @@ +name: ["aa\"ab|"] = ["aa\"ab"] diff --git a/test/grammar/types/var_type_annotation/type_fail_8/stderr.golden.py b/test/grammar/types/var_type_annotation/type_fail_8/stderr.golden.py new file mode 100644 index 000000000..9cb15209b --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_8/stderr.golden.py @@ -0,0 +1,23 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=1, + arg_msg='got [str(aa"ab)]' + ) + ], + arg_msg='expect [str(aa"ab|)], got [str(aa"ab)]' + ), + file=sys.stdout +) + diff --git a/test/grammar/types/var_type_annotation/type_fail_9/main.k b/test/grammar/types/var_type_annotation/type_fail_9/main.k new file mode 100644 index 000000000..18f05450a --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_9/main.k @@ -0,0 +1,2 @@ +_a: {str:str} = {"key": "value"} +_a: {str:int} = {"key2": 1} \ No newline at end of file diff --git a/test/grammar/types/var_type_annotation/type_fail_9/stderr.golden.py b/test/grammar/types/var_type_annotation/type_fail_9/stderr.golden.py new file mode 100644 index 000000000..e749a64ef --- /dev/null +++ b/test/grammar/types/var_type_annotation/type_fail_9/stderr.golden.py @@ -0,0 +1,28 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.TypeError_Compile_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=1, + col_no=1, + arg_msg='expect {str:str}', + err_level=kcl_error.ErrLevel.ORDINARY, + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=1, + arg_msg='got {str:int}', + ), + ], + arg_msg='can not change type of _a', + ), + file=sys.stdout, +) diff --git a/test/grammar/unification/append_0/main.k b/test/grammar/unification/append_0/main.k new file mode 100644 index 000000000..cd6ee4e96 --- /dev/null +++ b/test/grammar/unification/append_0/main.k @@ -0,0 +1,17 @@ +schema Main: + env: [str] + +schema Config: + main: Main + +config = Config { + main: Main { + env: ["s1"] + } +} + +config = Config { + main: Main { + env += ["s2"] + } +} diff --git a/test/grammar/unification/append_0/stdout.golden b/test/grammar/unification/append_0/stdout.golden new file mode 100644 index 000000000..6d9cb6165 --- /dev/null +++ b/test/grammar/unification/append_0/stdout.golden @@ -0,0 +1,5 @@ +config: + main: + env: + - s1 + - s2 diff --git a/test/grammar/unification/append_1/main.k b/test/grammar/unification/append_1/main.k new file mode 100644 index 000000000..4be4b226d --- /dev/null +++ b/test/grammar/unification/append_1/main.k @@ -0,0 +1,10 @@ +schema Main: + env: [str] + +main = Main { + env: ["s1"] +} + +main = Main { + env += ["s2"] +} diff --git a/test/grammar/unification/append_1/stdout.golden b/test/grammar/unification/append_1/stdout.golden new file mode 100644 index 000000000..449aeeae3 --- /dev/null +++ b/test/grammar/unification/append_1/stdout.golden @@ -0,0 +1,4 @@ +main: + env: + - s1 + - s2 diff --git a/test/grammar/unification/append_2/main.k b/test/grammar/unification/append_2/main.k new file mode 100644 index 000000000..26c71180a --- /dev/null +++ b/test/grammar/unification/append_2/main.k @@ -0,0 +1,11 @@ +schema Data: + [str]: [] + +if True: + _x: Data { + v += [{name: "1"}] + } + _x: Data { + v += [{name: "2"}] + } +x = _x diff --git a/test/grammar/unification/append_2/stdout.golden b/test/grammar/unification/append_2/stdout.golden new file mode 100644 index 000000000..c46fef0a9 --- /dev/null +++ b/test/grammar/unification/append_2/stdout.golden @@ -0,0 +1,4 @@ +x: + v: + - name: '1' + - name: '2' diff --git a/test/grammar/unification/collection_if_0/main.k b/test/grammar/unification/collection_if_0/main.k new file mode 100644 index 000000000..a95e599c0 --- /dev/null +++ b/test/grammar/unification/collection_if_0/main.k @@ -0,0 +1,16 @@ +schema Person: + name: str + age: int + +alice = Person { + if True: + name: "Alice" +} + +alice = Person { + if True: + age = 18 +} + +name = alice.name +age = alice.age diff --git a/test/grammar/unification/collection_if_0/stdout.golden b/test/grammar/unification/collection_if_0/stdout.golden new file mode 100644 index 000000000..0024c8b8b --- /dev/null +++ b/test/grammar/unification/collection_if_0/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: Alice + age: 18 +name: Alice +age: 18 diff --git a/test/grammar/unification/collection_if_1/main.k b/test/grammar/unification/collection_if_1/main.k new file mode 100644 index 000000000..c0f6980da --- /dev/null +++ b/test/grammar/unification/collection_if_1/main.k @@ -0,0 +1,20 @@ +schema Person: + name: str + age: int + +alice = Person { + if True: + name: "Alice" +} + +alice = Person { + if True: + age = 18 +} + +alice = Person { + age = 10 +} + +name = alice.name +age = alice.age diff --git a/test/grammar/unification/collection_if_1/stdout.golden b/test/grammar/unification/collection_if_1/stdout.golden new file mode 100644 index 000000000..373803b75 --- /dev/null +++ b/test/grammar/unification/collection_if_1/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: Alice + age: 10 +name: Alice +age: 10 diff --git a/test/grammar/unification/empty_0/main.k b/test/grammar/unification/empty_0/main.k new file mode 100644 index 000000000..b09bc9bf6 --- /dev/null +++ b/test/grammar/unification/empty_0/main.k @@ -0,0 +1,9 @@ +schema Person: + name?: str = None + age?: int = None + +alice = Person {} +alice = Person {} + +name = alice.name or "Alice" +age = alice.age or 18 diff --git a/test/grammar/unification/empty_0/stdout.golden b/test/grammar/unification/empty_0/stdout.golden new file mode 100644 index 000000000..803caf813 --- /dev/null +++ b/test/grammar/unification/empty_0/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: null + age: null +name: Alice +age: 18 diff --git a/test/grammar/unification/empty_1/main.k b/test/grammar/unification/empty_1/main.k new file mode 100644 index 000000000..e9c28d6df --- /dev/null +++ b/test/grammar/unification/empty_1/main.k @@ -0,0 +1,10 @@ +schema Person: + name?: str = None + age?: int = None + +alice = Person {} + +alice = Person {} + +name = alice.name or "Alice" +age = alice.age or 18 diff --git a/test/grammar/unification/empty_1/stdout.golden b/test/grammar/unification/empty_1/stdout.golden new file mode 100644 index 000000000..803caf813 --- /dev/null +++ b/test/grammar/unification/empty_1/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: null + age: null +name: Alice +age: 18 diff --git a/test/grammar/unification/fail_0/main.k b/test/grammar/unification/fail_0/main.k new file mode 100644 index 000000000..0d16e0d20 --- /dev/null +++ b/test/grammar/unification/fail_0/main.k @@ -0,0 +1,9 @@ +schema Person: + name: str + age: int + +alice = Person { + name: "Alice" +} + +alice.age = 18 diff --git a/test/grammar/unification/fail_0/stderr.golden.py b/test/grammar/unification/fail_0/stderr.golden.py new file mode 100644 index 000000000..3d8d30018 --- /dev/null +++ b/test/grammar/unification/fail_0/stderr.golden.py @@ -0,0 +1,22 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=9, + col_no=1, + end_col_no=10 + ) + ], + ), + file=sys.stdout +) + diff --git a/test/grammar/unification/fail_1/main.k b/test/grammar/unification/fail_1/main.k new file mode 100644 index 000000000..f2fb6e8bc --- /dev/null +++ b/test/grammar/unification/fail_1/main.k @@ -0,0 +1,12 @@ +schema Config: + name?: str + age?: int + hc: [int] + +config = Config { + name: "Alice" +} + +config = Config { + age: 18 +} diff --git a/test/grammar/unification/fail_1/stderr.golden.py b/test/grammar/unification/fail_1/stderr.golden.py new file mode 100644 index 000000000..8c6abaff1 --- /dev/null +++ b/test/grammar/unification/fail_1/stderr.golden.py @@ -0,0 +1,18 @@ +import sys +import os + +import kclvm.kcl.error as kcl_error + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.EvaluationError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=10 + ), + ], + arg_msg="attribute 'hc' of Config is required and can't be None or Undefined") + , file=sys.stdout +) diff --git a/test/grammar/unification/fail_2/main.k b/test/grammar/unification/fail_2/main.k new file mode 100644 index 000000000..ba5d4d062 --- /dev/null +++ b/test/grammar/unification/fail_2/main.k @@ -0,0 +1,11 @@ +schema Config: + name?: str + age?: int + check: + age > 0 if age + +_config = Config { + name: "Alice" + age: 18 +} +_config.age = -1 diff --git a/test/grammar/unification/fail_2/stderr.golden.py b/test/grammar/unification/fail_2/stderr.golden.py new file mode 100644 index 000000000..7d1d3e7d4 --- /dev/null +++ b/test/grammar/unification/fail_2/stderr.golden.py @@ -0,0 +1,23 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=11, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ]) + , file=sys.stdout +) diff --git a/test/grammar/unification/fail_3/main.k b/test/grammar/unification/fail_3/main.k new file mode 100644 index 000000000..58581a4d8 --- /dev/null +++ b/test/grammar/unification/fail_3/main.k @@ -0,0 +1,11 @@ +schema Config: + name?: str + age?: int + check: + age > 0 if age + +_config = Config { + name: "Alice" + age: 18 +} +_config |= {age = -1} diff --git a/test/grammar/unification/fail_3/stderr.golden.py b/test/grammar/unification/fail_3/stderr.golden.py new file mode 100644 index 000000000..7d1d3e7d4 --- /dev/null +++ b/test/grammar/unification/fail_3/stderr.golden.py @@ -0,0 +1,23 @@ + +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception(err_type=kcl_error.ErrType.SchemaCheckFailure_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=5, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_COND + ), + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=11, + arg_msg=kcl_error.SCHEMA_CHECK_FILE_MSG_ERR + ), + ]) + , file=sys.stdout +) diff --git a/test/grammar/unification/instances_0/main.k b/test/grammar/unification/instances_0/main.k new file mode 100644 index 000000000..47f5e038e --- /dev/null +++ b/test/grammar/unification/instances_0/main.k @@ -0,0 +1,7 @@ +schema Person: + name: str + age: int + +_person = Person {name: "Alice", age: 18} +_persons = Person.instances() +persons = _persons diff --git a/test/grammar/unification/instances_0/stdout.golden b/test/grammar/unification/instances_0/stdout.golden new file mode 100644 index 000000000..c36cc3654 --- /dev/null +++ b/test/grammar/unification/instances_0/stdout.golden @@ -0,0 +1,3 @@ +persons: +- name: Alice + age: 18 diff --git a/test/grammar/unification/instances_1/main.k b/test/grammar/unification/instances_1/main.k new file mode 100644 index 000000000..4bf9f4a37 --- /dev/null +++ b/test/grammar/unification/instances_1/main.k @@ -0,0 +1,10 @@ +schema Person: + name: str + age: int + +_persons = [ + Person {name: "Alice", age: 18} + Person {name: "Bob", age: 10} +] +_persons = Person.instances() +persons = _persons diff --git a/test/grammar/unification/instances_1/stdout.golden b/test/grammar/unification/instances_1/stdout.golden new file mode 100644 index 000000000..4e0c6827d --- /dev/null +++ b/test/grammar/unification/instances_1/stdout.golden @@ -0,0 +1,5 @@ +persons: +- name: Alice + age: 18 +- name: Bob + age: 10 diff --git a/test/grammar/unification/multi_file_compile_0/main.k b/test/grammar/unification/multi_file_compile_0/main.k new file mode 100644 index 000000000..2974347a6 --- /dev/null +++ b/test/grammar/unification/multi_file_compile_0/main.k @@ -0,0 +1,10 @@ +schema Config: + name?: str + args: [str] + labels: {str:} + +config = Config { + name: "config1" + args: ["kcl", "main.k"] + labels.key1: "value1" +} diff --git a/test/grammar/unification/multi_file_compile_0/settings.yaml b/test/grammar/unification/multi_file_compile_0/settings.yaml new file mode 100644 index 000000000..4d4d2d013 --- /dev/null +++ b/test/grammar/unification/multi_file_compile_0/settings.yaml @@ -0,0 +1 @@ +kcl_options: stack.k \ No newline at end of file diff --git a/test/grammar/unification/multi_file_compile_0/stack.k b/test/grammar/unification/multi_file_compile_0/stack.k new file mode 100644 index 000000000..c5bc5dd1d --- /dev/null +++ b/test/grammar/unification/multi_file_compile_0/stack.k @@ -0,0 +1,3 @@ +config = Config { + name = "config2" +} diff --git a/test/grammar/unification/multi_file_compile_0/stdout.golden b/test/grammar/unification/multi_file_compile_0/stdout.golden new file mode 100644 index 000000000..1017cd683 --- /dev/null +++ b/test/grammar/unification/multi_file_compile_0/stdout.golden @@ -0,0 +1,7 @@ +config: + name: config2 + args: + - kcl + - main.k + labels: + key1: value1 diff --git a/test/grammar/unification/multi_file_compile_1/main.k b/test/grammar/unification/multi_file_compile_1/main.k new file mode 100644 index 000000000..2974347a6 --- /dev/null +++ b/test/grammar/unification/multi_file_compile_1/main.k @@ -0,0 +1,10 @@ +schema Config: + name?: str + args: [str] + labels: {str:} + +config = Config { + name: "config1" + args: ["kcl", "main.k"] + labels.key1: "value1" +} diff --git a/test/grammar/unification/multi_file_compile_1/settings.yaml b/test/grammar/unification/multi_file_compile_1/settings.yaml new file mode 100644 index 000000000..4d4d2d013 --- /dev/null +++ b/test/grammar/unification/multi_file_compile_1/settings.yaml @@ -0,0 +1 @@ +kcl_options: stack.k \ No newline at end of file diff --git a/test/grammar/unification/multi_file_compile_1/stack.k b/test/grammar/unification/multi_file_compile_1/stack.k new file mode 100644 index 000000000..d59c1c7c5 --- /dev/null +++ b/test/grammar/unification/multi_file_compile_1/stack.k @@ -0,0 +1,4 @@ +_NAME = "config2" +config = Config { + name = _NAME +} diff --git a/test/grammar/unification/multi_file_compile_1/stdout.golden b/test/grammar/unification/multi_file_compile_1/stdout.golden new file mode 100644 index 000000000..1017cd683 --- /dev/null +++ b/test/grammar/unification/multi_file_compile_1/stdout.golden @@ -0,0 +1,7 @@ +config: + name: config2 + args: + - kcl + - main.k + labels: + key1: value1 diff --git a/test/grammar/unification/nest_var_0/main.k b/test/grammar/unification/nest_var_0/main.k new file mode 100644 index 000000000..7b945cc6e --- /dev/null +++ b/test/grammar/unification/nest_var_0/main.k @@ -0,0 +1,20 @@ +schema Config: + args: [str] + labels: {str:} + +config = Config { + args: ["kcl", "main.k"] + labels.key1: "value1" +} + +config = Config { + labels: { + key2: "value2" + } +} + +config = Config { + "labels": { + "key3": "value3" + } +} diff --git a/test/grammar/unification/nest_var_0/stdout.golden b/test/grammar/unification/nest_var_0/stdout.golden new file mode 100644 index 000000000..11835ce8a --- /dev/null +++ b/test/grammar/unification/nest_var_0/stdout.golden @@ -0,0 +1,8 @@ +config: + args: + - kcl + - main.k + labels: + key1: value1 + key2: value2 + key3: value3 diff --git a/test/grammar/unification/nest_var_1/main.k b/test/grammar/unification/nest_var_1/main.k new file mode 100644 index 000000000..acbcbbec5 --- /dev/null +++ b/test/grammar/unification/nest_var_1/main.k @@ -0,0 +1,24 @@ +schema Name0: + name?: str + data?: int + +schema Name: + name?: Name0 + +schema Config: + args?: [str] + labels: {str:} + name: Name + +config = Config { + args: ["kcl", "main.k"] + labels.key1: "value1" + name.name.name: "name" +} + +config = Config { + labels: { + key2: "value2" + } + name.name: Name0 {data: 1} +} diff --git a/test/grammar/unification/nest_var_1/stdout.golden b/test/grammar/unification/nest_var_1/stdout.golden new file mode 100644 index 000000000..6ca62f265 --- /dev/null +++ b/test/grammar/unification/nest_var_1/stdout.golden @@ -0,0 +1,11 @@ +config: + args: + - kcl + - main.k + labels: + key1: value1 + key2: value2 + name: + name: + name: name + data: 1 diff --git a/test/grammar/unification/override_0/main.k b/test/grammar/unification/override_0/main.k new file mode 100644 index 000000000..4a6c96944 --- /dev/null +++ b/test/grammar/unification/override_0/main.k @@ -0,0 +1,18 @@ +schema Person: + name: str + age: int + hc: [int] + +alice = Person { + name: "Alice" + hc: [1, 2] +} + +alice = Person { + age: 18 + hc = [2] +} + +name = alice.name +age = alice.age +hc = alice.hc diff --git a/test/grammar/unification/override_0/stdout.golden b/test/grammar/unification/override_0/stdout.golden new file mode 100644 index 000000000..dc5f71b89 --- /dev/null +++ b/test/grammar/unification/override_0/stdout.golden @@ -0,0 +1,9 @@ +alice: + name: Alice + age: 18 + hc: + - 2 +name: Alice +age: 18 +hc: +- 2 diff --git a/test/grammar/unification/override_1/main.k b/test/grammar/unification/override_1/main.k new file mode 100644 index 000000000..c530ef80b --- /dev/null +++ b/test/grammar/unification/override_1/main.k @@ -0,0 +1,23 @@ +schema Person: + name: str + age: int + env: [{str:}] + +alice = Person { + name: "Alice" + env: [{ + name: "key1" + value: "value1" + }, { + name: "key2" + value: "value2" + }] +} + +alice = Person { + age: 18 + env = [{ + name: "key2" + value: "value2" + }] +} diff --git a/test/grammar/unification/override_1/stdout.golden b/test/grammar/unification/override_1/stdout.golden new file mode 100644 index 000000000..eeb3e577e --- /dev/null +++ b/test/grammar/unification/override_1/stdout.golden @@ -0,0 +1,6 @@ +alice: + name: Alice + age: 18 + env: + - name: key2 + value: value2 diff --git a/test/grammar/unification/schema_simple_0/main.k b/test/grammar/unification/schema_simple_0/main.k new file mode 100644 index 000000000..a2024fd99 --- /dev/null +++ b/test/grammar/unification/schema_simple_0/main.k @@ -0,0 +1,11 @@ +schema Person: + name: str + age: int + +alice = Person { + name: "Alice" +} + +alice = Person { + age: 18 +} diff --git a/test/grammar/unification/schema_simple_0/stdout.golden b/test/grammar/unification/schema_simple_0/stdout.golden new file mode 100644 index 000000000..3aa06b9d7 --- /dev/null +++ b/test/grammar/unification/schema_simple_0/stdout.golden @@ -0,0 +1,3 @@ +alice: + name: Alice + age: 18 diff --git a/test/grammar/unification/schema_simple_1/main.k b/test/grammar/unification/schema_simple_1/main.k new file mode 100644 index 000000000..d0e0b97d6 --- /dev/null +++ b/test/grammar/unification/schema_simple_1/main.k @@ -0,0 +1,18 @@ +schema Config: + name?: str + age?: int + hc: [int] + +config = Config { + name: "Alice" +} + +config = Config { + age: 18 +} + +config = Config { + hc: [1] +} + +data = [1, 2, 3] diff --git a/test/grammar/unification/schema_simple_1/stdout.golden b/test/grammar/unification/schema_simple_1/stdout.golden new file mode 100644 index 000000000..86cddc06e --- /dev/null +++ b/test/grammar/unification/schema_simple_1/stdout.golden @@ -0,0 +1,9 @@ +config: + name: Alice + age: 18 + hc: + - 1 +data: +- 1 +- 2 +- 3 diff --git a/test/grammar/unification/schema_simple_2/main.k b/test/grammar/unification/schema_simple_2/main.k new file mode 100644 index 000000000..bfed69eb2 --- /dev/null +++ b/test/grammar/unification/schema_simple_2/main.k @@ -0,0 +1,16 @@ +schema Config: + name?: str + age?: int + hc: [int] + +config = Config { + name: "Alice" +} +age = 18 +config = Config { + age: age +} + +config = Config { + hc: [1] +} diff --git a/test/grammar/unification/schema_simple_2/stdout.golden b/test/grammar/unification/schema_simple_2/stdout.golden new file mode 100644 index 000000000..424cdb455 --- /dev/null +++ b/test/grammar/unification/schema_simple_2/stdout.golden @@ -0,0 +1,6 @@ +config: + name: Alice + age: 18 + hc: + - 1 +age: 18 diff --git a/test/grammar/unification/schema_simple_3/main.k b/test/grammar/unification/schema_simple_3/main.k new file mode 100644 index 000000000..249adf7b6 --- /dev/null +++ b/test/grammar/unification/schema_simple_3/main.k @@ -0,0 +1,27 @@ +schema Main: + env: [str] + args: [str] + +schema Config: + name?: str + age?: int + hc?: [int] + main: Main + +_main = Main { + env: ["123", "456"] + args: ["1"] +} + +config = Config { + name: "Alice" + main: _main +} + +config = Config { + age: 18 + main: Main { + env = ["789", "456"] + args = ["1"] + } +} diff --git a/test/grammar/unification/schema_simple_3/stdout.golden b/test/grammar/unification/schema_simple_3/stdout.golden new file mode 100644 index 000000000..fa89d10b1 --- /dev/null +++ b/test/grammar/unification/schema_simple_3/stdout.golden @@ -0,0 +1,9 @@ +config: + name: Alice + age: 18 + main: + env: + - '789' + - '456' + args: + - '1' diff --git a/test/grammar/unification/schema_simple_4/main.k b/test/grammar/unification/schema_simple_4/main.k new file mode 100644 index 000000000..20a76b5c9 --- /dev/null +++ b/test/grammar/unification/schema_simple_4/main.k @@ -0,0 +1,20 @@ +schema PersonMixin: + info: str = "name: ${name}, age: ${age}" + +schema Person: + mixin [PersonMixin] + + name?: str + age?: int + check: + age > 0 if age + +schema Data: + person: Person { + age = 1 + } + person: Person { + name = "Alice" + } + +data = Data() diff --git a/test/grammar/unification/schema_simple_4/stdout.golden b/test/grammar/unification/schema_simple_4/stdout.golden new file mode 100644 index 000000000..66ddef126 --- /dev/null +++ b/test/grammar/unification/schema_simple_4/stdout.golden @@ -0,0 +1,5 @@ +data: + person: + name: Alice + age: 1 + info: 'name: Alice, age: 1' diff --git a/test/grammar/unification/schema_simple_5/main.k b/test/grammar/unification/schema_simple_5/main.k new file mode 100644 index 000000000..663465f08 --- /dev/null +++ b/test/grammar/unification/schema_simple_5/main.k @@ -0,0 +1,19 @@ +schema PersonMixin: + person: Person { + name = "Alice" + } + +schema Person: + name?: str + age?: int + check: + age > 0 if age + +schema Data: + mixin [PersonMixin] + + person: Person { + age = 1 + } + +data = Data() diff --git a/test/grammar/unification/schema_simple_5/stdout.golden b/test/grammar/unification/schema_simple_5/stdout.golden new file mode 100644 index 000000000..9b0d4a6c8 --- /dev/null +++ b/test/grammar/unification/schema_simple_5/stdout.golden @@ -0,0 +1,4 @@ +data: + person: + name: Alice + age: 1 diff --git a/test/grammar/unification/schema_simple_6/main.k b/test/grammar/unification/schema_simple_6/main.k new file mode 100644 index 000000000..4c64a9bf1 --- /dev/null +++ b/test/grammar/unification/schema_simple_6/main.k @@ -0,0 +1,30 @@ +schema PersonFooMixin: + person: Person { + name = "Alice" + info.key2: "value2" + } + +schema PersonBarMixin: + person: Person { + info.key3: "value3" + } + +schema Person: + name?: str + age?: int + info?: {str:} + check: + age > 0 if age + +schema Data: + mixin [ + PersonFooMixin, + PersonBarMixin + ] + + person: Person { + info.key1: "value1" + age = 1 + } + +data = Data() diff --git a/test/grammar/unification/schema_simple_6/stdout.golden b/test/grammar/unification/schema_simple_6/stdout.golden new file mode 100644 index 000000000..af11fa75a --- /dev/null +++ b/test/grammar/unification/schema_simple_6/stdout.golden @@ -0,0 +1,8 @@ +data: + person: + name: Alice + age: 1 + info: + key1: value1 + key2: value2 + key3: value3 diff --git a/test/grammar/unification/schema_simple_7/main.k b/test/grammar/unification/schema_simple_7/main.k new file mode 100644 index 000000000..7c6d3026f --- /dev/null +++ b/test/grammar/unification/schema_simple_7/main.k @@ -0,0 +1,35 @@ +schema PersonFooMixin: + person: Person { + name = "Alice" + if appendInfo: + info.key2: "value2" + } + +schema PersonBarMixin: + person: Person { + if appendInfo: + info.key3: "value3" + } + +schema Person: + name?: str + age?: int + info?: {str:} + check: + age > 0 if age + +schema Data[appendInfoVar: bool]: + mixin [ + PersonFooMixin, + PersonBarMixin + ] + + appendInfo: bool = appendInfoVar + person: Person { + if appendInfo: + info.key1: "value1" + age = 1 + } + +dataWithInfo = Data(True) +dataWithoutInfo = Data(False) diff --git a/test/grammar/unification/schema_simple_7/stdout.golden b/test/grammar/unification/schema_simple_7/stdout.golden new file mode 100644 index 000000000..b6160fd7c --- /dev/null +++ b/test/grammar/unification/schema_simple_7/stdout.golden @@ -0,0 +1,14 @@ +dataWithInfo: + appendInfo: true + person: + name: Alice + age: 1 + info: + key1: value1 + key2: value2 + key3: value3 +dataWithoutInfo: + appendInfo: false + person: + name: Alice + age: 1 diff --git a/test/grammar/unification/schema_simple_8/main.k b/test/grammar/unification/schema_simple_8/main.k new file mode 100644 index 000000000..bd8dc7dd8 --- /dev/null +++ b/test/grammar/unification/schema_simple_8/main.k @@ -0,0 +1,35 @@ +schema PersonFooMixin: + person: Person { + if appendInfo: + info.key2: "value2" + name = "Alice" + } + +schema PersonBarMixin: + person: Person { + if appendInfo: + info.key3: "value3" + } + +schema Person: + name?: str + age?: int + info?: {str:} + check: + age > 0 if age + +schema Data[appendInfoVar: bool]: + mixin [ + PersonFooMixin, + PersonBarMixin + ] + + appendInfo: bool = appendInfoVar + person: Person { + if appendInfo: + info.key1: "value1" + age = 1 + } + +dataWithInfo = Data(True) +dataWithoutInfo = Data(False) diff --git a/test/grammar/unification/schema_simple_8/stdout.golden b/test/grammar/unification/schema_simple_8/stdout.golden new file mode 100644 index 000000000..1756542a5 --- /dev/null +++ b/test/grammar/unification/schema_simple_8/stdout.golden @@ -0,0 +1,12 @@ +dataWithInfo: + appendInfo: true + person: + name: Alice + age: 1 + info: + key1: value1 + key2: value2 + key3: value3 +dataWithoutInfo: + appendInfo: false + person: {} diff --git a/test/grammar/unification/str_interpolation/main.k b/test/grammar/unification/str_interpolation/main.k new file mode 100644 index 000000000..a1ce3777a --- /dev/null +++ b/test/grammar/unification/str_interpolation/main.k @@ -0,0 +1,10 @@ +schema Person: + name: str + age: int + +key = "age" +_alice = Person { + name: "Alice" +} +_alice = Person {"${key}": 18} +alice = _alice diff --git a/test/grammar/unification/str_interpolation/stdout.golden b/test/grammar/unification/str_interpolation/stdout.golden new file mode 100644 index 000000000..50a38309c --- /dev/null +++ b/test/grammar/unification/str_interpolation/stdout.golden @@ -0,0 +1,4 @@ +key: age +alice: + name: Alice + age: 18 diff --git a/test/grammar/unification/subscript_0/main.k b/test/grammar/unification/subscript_0/main.k new file mode 100644 index 000000000..f0758f7e4 --- /dev/null +++ b/test/grammar/unification/subscript_0/main.k @@ -0,0 +1,14 @@ +schema Person: + name: str + age: int + +alice = Person { + name: "Alice" +} + +alice = Person { + age: 18 +} + +name = alice.name +age = alice.age diff --git a/test/grammar/unification/subscript_0/stdout.golden b/test/grammar/unification/subscript_0/stdout.golden new file mode 100644 index 000000000..0024c8b8b --- /dev/null +++ b/test/grammar/unification/subscript_0/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: Alice + age: 18 +name: Alice +age: 18 diff --git a/test/grammar/unification/subscript_1/main.k b/test/grammar/unification/subscript_1/main.k new file mode 100644 index 000000000..f16cab36a --- /dev/null +++ b/test/grammar/unification/subscript_1/main.k @@ -0,0 +1,13 @@ +schema Person: + name: str + age: int + +alice = Person { + name: "Alice" + age: 18 +} + +alice = Person {} + +name = alice.name +age = alice.age diff --git a/test/grammar/unification/subscript_1/stdout.golden b/test/grammar/unification/subscript_1/stdout.golden new file mode 100644 index 000000000..0024c8b8b --- /dev/null +++ b/test/grammar/unification/subscript_1/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: Alice + age: 18 +name: Alice +age: 18 diff --git a/test/grammar/unification/unpack_0/main.k b/test/grammar/unification/unpack_0/main.k new file mode 100644 index 000000000..8a6b73661 --- /dev/null +++ b/test/grammar/unification/unpack_0/main.k @@ -0,0 +1,20 @@ +schema Person: + name: str + age: int + +_base = { + name = "Bob" + age = 18 +} + +alice = Person { + **_base +} + +alice = Person { + name = "Alice" + age = 18 +} + +name = alice.name +age = alice.age diff --git a/test/grammar/unification/unpack_0/stdout.golden b/test/grammar/unification/unpack_0/stdout.golden new file mode 100644 index 000000000..0024c8b8b --- /dev/null +++ b/test/grammar/unification/unpack_0/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: Alice + age: 18 +name: Alice +age: 18 diff --git a/test/grammar/unification/unpack_1/main.k b/test/grammar/unification/unpack_1/main.k new file mode 100644 index 000000000..672db3a1d --- /dev/null +++ b/test/grammar/unification/unpack_1/main.k @@ -0,0 +1,19 @@ +schema Person: + name: str + age: int + +_base = { + name = "Bob" + age = 10 +} + +alice = Person { + age: 18 +} + +alice = Person { + **_base +} + +name = alice.name +age = alice.age diff --git a/test/grammar/unification/unpack_1/stdout.golden b/test/grammar/unification/unpack_1/stdout.golden new file mode 100644 index 000000000..8d62b7456 --- /dev/null +++ b/test/grammar/unification/unpack_1/stdout.golden @@ -0,0 +1,5 @@ +alice: + name: Bob + age: 10 +name: Bob +age: 10 diff --git a/test/grammar/variable/export/default/main.k b/test/grammar/variable/export/default/main.k new file mode 100644 index 000000000..ecb418c3c --- /dev/null +++ b/test/grammar/variable/export/default/main.k @@ -0,0 +1,3 @@ +_a = 1 +_a << 1 +b = 2 \ No newline at end of file diff --git a/test/grammar/variable/export/default/stdout.golden b/test/grammar/variable/export/default/stdout.golden new file mode 100644 index 000000000..cf0609d29 --- /dev/null +++ b/test/grammar/variable/export/default/stdout.golden @@ -0,0 +1 @@ +b: 2 \ No newline at end of file diff --git a/test/grammar/variable/export/if_expr_0/main.k b/test/grammar/variable/export/if_expr_0/main.k new file mode 100644 index 000000000..e460dd1a5 --- /dev/null +++ b/test/grammar/variable/export/if_expr_0/main.k @@ -0,0 +1,3 @@ +_b = True + +a = [1, 2, 3] if not _b else {"first": 1, "second": 2} diff --git a/test/grammar/variable/export/if_expr_0/stdout.golden b/test/grammar/variable/export/if_expr_0/stdout.golden new file mode 100644 index 000000000..49ea86dc7 --- /dev/null +++ b/test/grammar/variable/export/if_expr_0/stdout.golden @@ -0,0 +1,3 @@ +a: + first: 1 + second: 2 \ No newline at end of file diff --git a/test/grammar/variable/export/if_expr_1/main.k b/test/grammar/variable/export/if_expr_1/main.k new file mode 100644 index 000000000..30ac84153 --- /dev/null +++ b/test/grammar/variable/export/if_expr_1/main.k @@ -0,0 +1,2 @@ +_b = True +a = 1 + (2 if not _b else 3) diff --git a/test/grammar/variable/export/if_expr_1/stdout.golden b/test/grammar/variable/export/if_expr_1/stdout.golden new file mode 100644 index 000000000..6fe1e8a01 --- /dev/null +++ b/test/grammar/variable/export/if_expr_1/stdout.golden @@ -0,0 +1 @@ +a: 4 diff --git a/test/grammar/variable/export/if_stmt_0/main.k b/test/grammar/variable/export/if_stmt_0/main.k new file mode 100644 index 000000000..5e7488d82 --- /dev/null +++ b/test/grammar/variable/export/if_stmt_0/main.k @@ -0,0 +1,3 @@ +if True: + a = 1 +b = 2 diff --git a/test/grammar/variable/export/if_stmt_0/stdout.golden b/test/grammar/variable/export/if_stmt_0/stdout.golden new file mode 100644 index 000000000..083c5aec5 --- /dev/null +++ b/test/grammar/variable/export/if_stmt_0/stdout.golden @@ -0,0 +1,2 @@ +a: 1 +b: 2 diff --git a/test/grammar/variable/export/if_stmt_1/main.k b/test/grammar/variable/export/if_stmt_1/main.k new file mode 100644 index 000000000..1cd0a0629 --- /dev/null +++ b/test/grammar/variable/export/if_stmt_1/main.k @@ -0,0 +1,5 @@ +if False: + a = 1 +else: + b = 2 +c = 3 diff --git a/test/grammar/variable/export/if_stmt_1/stdout.golden b/test/grammar/variable/export/if_stmt_1/stdout.golden new file mode 100644 index 000000000..14330e4a8 --- /dev/null +++ b/test/grammar/variable/export/if_stmt_1/stdout.golden @@ -0,0 +1,2 @@ +b: 2 +c: 3 diff --git a/test/grammar/variable/export/immutable_0/main.k b/test/grammar/variable/export/immutable_0/main.k new file mode 100644 index 000000000..e83d70d94 --- /dev/null +++ b/test/grammar/variable/export/immutable_0/main.k @@ -0,0 +1,2 @@ +a = 1 +a = 2 diff --git a/test/grammar/variable/export/immutable_0/stderr.golden.py b/test/grammar/variable/export/immutable_0/stderr.golden.py new file mode 100644 index 000000000..7b85a3b1d --- /dev/null +++ b/test/grammar/variable/export/immutable_0/stderr.golden.py @@ -0,0 +1,19 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=1 + ) + ], + ), + file=sys.stdout +) diff --git a/test/grammar/variable/export/immutable_1/main.k b/test/grammar/variable/export/immutable_1/main.k new file mode 100644 index 000000000..be907b243 --- /dev/null +++ b/test/grammar/variable/export/immutable_1/main.k @@ -0,0 +1,2 @@ +a = 1 +a = b = 2 \ No newline at end of file diff --git a/test/grammar/variable/export/immutable_1/stderr.golden.py b/test/grammar/variable/export/immutable_1/stderr.golden.py new file mode 100644 index 000000000..c8b15b5a3 --- /dev/null +++ b/test/grammar/variable/export/immutable_1/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=1 + ) + ], + ), + file=sys.stdout +) + diff --git a/test/grammar/variable/export/immutable_2/main.k b/test/grammar/variable/export/immutable_2/main.k new file mode 100644 index 000000000..b2c713f99 --- /dev/null +++ b/test/grammar/variable/export/immutable_2/main.k @@ -0,0 +1,2 @@ +a = 1 +b = a = 2 diff --git a/test/grammar/variable/export/immutable_2/stderr.golden.py b/test/grammar/variable/export/immutable_2/stderr.golden.py new file mode 100644 index 000000000..665417538 --- /dev/null +++ b/test/grammar/variable/export/immutable_2/stderr.golden.py @@ -0,0 +1,20 @@ +import sys +import kclvm.kcl.error as kcl_error +import os + +cwd = os.path.dirname(os.path.realpath(__file__)) + +kcl_error.print_kcl_error_message( + kcl_error.get_exception( + err_type=kcl_error.ErrType.ImmutableCompileError_TYPE, + file_msgs=[ + kcl_error.ErrFileMsg( + filename=cwd + "/main.k", + line_no=2, + col_no=5 + ) + ], + ), + file=sys.stdout +) + diff --git a/test/grammar/variable/unexport/default/main.k b/test/grammar/variable/unexport/default/main.k new file mode 100644 index 000000000..99badb7f8 --- /dev/null +++ b/test/grammar/variable/unexport/default/main.k @@ -0,0 +1,3 @@ +_a = 1 +_a = 2 +b = 2 diff --git a/test/grammar/variable/unexport/default/stdout.golden b/test/grammar/variable/unexport/default/stdout.golden new file mode 100644 index 000000000..f6f4a0223 --- /dev/null +++ b/test/grammar/variable/unexport/default/stdout.golden @@ -0,0 +1 @@ +b: 2 diff --git a/test/grammar/variable/unexport/for_0/main.k b/test/grammar/variable/unexport/for_0/main.k new file mode 100644 index 000000000..ec9ca6c80 --- /dev/null +++ b/test/grammar/variable/unexport/for_0/main.k @@ -0,0 +1 @@ +a = [i for i in [0, 1, 2]] diff --git a/test/grammar/variable/unexport/for_0/stdout.golden b/test/grammar/variable/unexport/for_0/stdout.golden new file mode 100644 index 000000000..3c17b4feb --- /dev/null +++ b/test/grammar/variable/unexport/for_0/stdout.golden @@ -0,0 +1,4 @@ +a: +- 0 +- 1 +- 2 diff --git a/test/grammar/variable/unexport/for_1/main.k b/test/grammar/variable/unexport/for_1/main.k new file mode 100644 index 000000000..3b260bf51 --- /dev/null +++ b/test/grammar/variable/unexport/for_1/main.k @@ -0,0 +1 @@ +a = [v for i, v in [0, 1, 2]] diff --git a/test/grammar/variable/unexport/for_1/stdout.golden b/test/grammar/variable/unexport/for_1/stdout.golden new file mode 100644 index 000000000..3c17b4feb --- /dev/null +++ b/test/grammar/variable/unexport/for_1/stdout.golden @@ -0,0 +1,4 @@ +a: +- 0 +- 1 +- 2 diff --git a/test/grammar/variable/unexport/if_stmt_0/main.k b/test/grammar/variable/unexport/if_stmt_0/main.k new file mode 100644 index 000000000..80a95ddf6 --- /dev/null +++ b/test/grammar/variable/unexport/if_stmt_0/main.k @@ -0,0 +1,4 @@ +if True: + _a = 1 + _a = 2 +b = 2 diff --git a/test/grammar/variable/unexport/if_stmt_0/stdout.golden b/test/grammar/variable/unexport/if_stmt_0/stdout.golden new file mode 100644 index 000000000..f6f4a0223 --- /dev/null +++ b/test/grammar/variable/unexport/if_stmt_0/stdout.golden @@ -0,0 +1 @@ +b: 2 diff --git a/test/grammar/variable/unexport/if_stmt_1/main.k b/test/grammar/variable/unexport/if_stmt_1/main.k new file mode 100644 index 000000000..7e2a8385c --- /dev/null +++ b/test/grammar/variable/unexport/if_stmt_1/main.k @@ -0,0 +1,7 @@ +if False: + _a = 1 + _a = 2 +else: + _a = 3 + _a = 4 +b = 2 diff --git a/test/grammar/variable/unexport/if_stmt_1/stdout.golden b/test/grammar/variable/unexport/if_stmt_1/stdout.golden new file mode 100644 index 000000000..f6f4a0223 --- /dev/null +++ b/test/grammar/variable/unexport/if_stmt_1/stdout.golden @@ -0,0 +1 @@ +b: 2 diff --git a/test/grammar/variable/unexport/unique_key_normal_0/main.k b/test/grammar/variable/unexport/unique_key_normal_0/main.k new file mode 100644 index 000000000..a10586880 --- /dev/null +++ b/test/grammar/variable/unexport/unique_key_normal_0/main.k @@ -0,0 +1,5 @@ +_a = 1 +_a = 2 +_a += 2 +_a -= 1 +a = _a diff --git a/test/grammar/variable/unexport/unique_key_normal_0/stdout.golden b/test/grammar/variable/unexport/unique_key_normal_0/stdout.golden new file mode 100644 index 000000000..c648d9591 --- /dev/null +++ b/test/grammar/variable/unexport/unique_key_normal_0/stdout.golden @@ -0,0 +1 @@ +a: 3 diff --git a/test/grammar/variable/unexport/unique_key_normal_1/main.k b/test/grammar/variable/unexport/unique_key_normal_1/main.k new file mode 100644 index 000000000..788f6c64f --- /dev/null +++ b/test/grammar/variable/unexport/unique_key_normal_1/main.k @@ -0,0 +1,6 @@ +_a = _b = 1 +_b += 2 +_a -= 1 +_a = _a + _b + 1 +a = _a +b = _b diff --git a/test/grammar/variable/unexport/unique_key_normal_1/stdout.golden b/test/grammar/variable/unexport/unique_key_normal_1/stdout.golden new file mode 100644 index 000000000..29824cffc --- /dev/null +++ b/test/grammar/variable/unexport/unique_key_normal_1/stdout.golden @@ -0,0 +1,2 @@ +a: 4 +b: 3 diff --git a/test/test_units/test_kclvm/__init__.py b/test/test_units/test_kclvm/__init__.py new file mode 100644 index 000000000..378661dd8 --- /dev/null +++ b/test/test_units/test_kclvm/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- + diff --git a/test/test_units/test_kclvm/_test_plugin/test_data/VERSION b/test/test_units/test_kclvm/_test_plugin/test_data/VERSION new file mode 100644 index 000000000..da9921cdc --- /dev/null +++ b/test/test_units/test_kclvm/_test_plugin/test_data/VERSION @@ -0,0 +1 @@ +test_plugin_version \ No newline at end of file diff --git a/test/test_units/test_kclvm/_test_plugin/test_plugin.py b/test/test_units/test_kclvm/_test_plugin/test_plugin.py new file mode 100644 index 000000000..13bb5cdf7 --- /dev/null +++ b/test/test_units/test_kclvm/_test_plugin/test_plugin.py @@ -0,0 +1,54 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import kclvm.compiler.extension.plugin as plugin + +hello_plugin = plugin.get_plugin("kcl_plugin.hello") + + +def test_reset_plugin(): + plugin.reset_plugin(plugin.get_plugin_root()) + + +def test_plugin(): + assert "hello" in plugin.get_plugin_names() + + +def test_plugin_info(): + info = plugin.get_info("hello") + assert info["name"] == "hello" + + +def test_plugin_hello_add(): + assert hello_plugin.add(1, 2) == 3 + + +def test_plugin_hello_tolower(): + assert hello_plugin.tolower("KCL") == "kcl" + + +def test_plugin_hello_update_dict(): + assert hello_plugin.update_dict({"name": 123}, "name", "kcl")["name"] == "kcl" + + +def test_plugin_hello_list_append(): + data = hello_plugin.list_append(["abc"], "name", 123) + assert len(data) == 3 + assert data[0] == "abc" + assert data[1] == "name" + assert data[2] == 123 + + +def test_plugin_hello_foo(): + v = hello_plugin.foo("aaa", "bbb", x=123, y=234, abcd=1234) + assert len(v) == 5 + assert v["a"] == "aaa" + assert v["b"] == "bbb" + assert v["x"] == 123 + assert v["y"] == 234 + assert v["abcd"] == 1234 + + v = hello_plugin.foo("aaa", "bbb", x=123) + assert len(v) == 3 + assert v["a"] == "aaa" + assert v["b"] == "bbb" + assert v["x"] == 123 diff --git a/test/test_units/test_kclvm/_test_plugin/test_plugin_root.py b/test/test_units/test_kclvm/_test_plugin/test_plugin_root.py new file mode 100644 index 000000000..7a9d099bf --- /dev/null +++ b/test/test_units/test_kclvm/_test_plugin/test_plugin_root.py @@ -0,0 +1,55 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import os +import pathlib +import sys + +import kclvm.compiler.extension.plugin.plugin as plugin + + +def test_find_plugin_root(): + os.environ["KCL_PLUGINS_ROOT"] = "" + + root1 = plugin.find_plugin_root() + sys.stdout.write(f"root1:{root1}\n") + if root1 is None: + return + + + root1_hello_plugin_py = "" + if os.path.exists(f"{root1}/hello/plugin.py"): + root1_hello_plugin_py = f"{root1}/hello/plugin.py" + os.rename(root1_hello_plugin_py, root1_hello_plugin_py+"_test_tmp") + + root2 = plugin.find_plugin_root() + sys.stdout.write(f"root2:{root2}\n") + if root2 is None: + if root1_hello_plugin_py: + os.rename(root1_hello_plugin_py+"_test_tmp", root1_hello_plugin_py) + return + + root2_hello_plugin_py = "" + if os.path.exists(f"{root2}/hello/plugin.py"): + root2_hello_plugin_py = f"{root2}/hello/plugin.py" + os.rename(root2_hello_plugin_py, root2_hello_plugin_py+"_test_tmp") + + plugin.find_plugin_root() # skip $HOME/.kusion/kclvm/plugins + root3 = plugin.find_plugin_root() + assert root3 is None + + if root1_hello_plugin_py: + os.rename(root1_hello_plugin_py+"_test_tmp", root1_hello_plugin_py) + if root2_hello_plugin_py: + os.rename(root2_hello_plugin_py+"_test_tmp", root2_hello_plugin_py) + + root4 = plugin.find_plugin_root() + assert root4 == root1 + + +def test_plugin_version(): + plugin.init_.plugins_root = None + assert plugin.get_plugin_version() == plugin.UNKNOWN_VERSION + plugin.init_.plugins_root = str(pathlib.Path(__file__).parent) + assert plugin.get_plugin_version() == plugin.UNKNOWN_VERSION + plugin.init_.plugins_root = str(pathlib.Path(__file__).parent.joinpath("test_data")) + assert plugin.get_plugin_version() == "test_plugin_version" diff --git a/test/test_units/test_kclvm/_test_plugin/test_template.py b/test/test_units/test_kclvm/_test_plugin/test_template.py new file mode 100644 index 000000000..733f8afcc --- /dev/null +++ b/test/test_units/test_kclvm/_test_plugin/test_template.py @@ -0,0 +1,11 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import kclvm.compiler.extension.plugin.template as template + + +hello_plugin_name = "kcl_plugin.hello" + + +def test_reset_plugin(): + template.get_plugin_template_code(hello_plugin_name) + template.get_plugin_test_template_code(hello_plugin_name) diff --git a/test/test_units/test_kclvm/test_api/test_internal/test_common_functions.py b/test/test_units/test_kclvm/test_api/test_internal/test_common_functions.py new file mode 100644 index 000000000..d3dda347b --- /dev/null +++ b/test/test_units/test_kclvm/test_api/test_internal/test_common_functions.py @@ -0,0 +1,232 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest + +import kclvm.api.object.internal.common as common + + +class TestCommonInternal(unittest.TestCase): + def test_is_builtin_type(self): + cases = [ + # True cases + {"type": "int", "expected": True}, + {"type": "float", "expected": True}, + {"type": "str", "expected": True}, + {"type": "bool", "expected": True}, + # False cases + {"type": "", "expected": False}, + {"type": "int8", "expected": False}, + {"type": "string", "expected": False}, + {"type": "{:}", "expected": False}, + {"type": "[]", "expected": False}, + {"type": "pkg.Schema", "expected": False}, + ] + for case in cases: + tpe_str, expected = case["type"], case["expected"] + value = common.is_builtin_type(tpe_str) + self.assertEqual(value, expected) + + def test_is_dict_type(self): + cases = [ + # True cases + {"type": "{}", "expected": True}, + {"type": "{:}", "expected": True}, + {"type": "{str:}", "expected": True}, + {"type": "{str:str}", "expected": True}, + {"type": "{str: str}", "expected": True}, + {"type": "{str:int}", "expected": True}, + {"type": "{str: int}", "expected": True}, + {"type": "{str:{str:}}", "expected": True}, + {"type": "{str:{str:str}}", "expected": True}, + {"type": "{str:str|int}", "expected": True}, + {"type": "{str:pkg.Schema}", "expected": True}, + # False cases + {"type": "int", "expected": False}, + {"type": "float", "expected": False}, + {"type": "str", "expected": False}, + {"type": "bool", "expected": False}, + {"type": "", "expected": False}, + {"type": "int8", "expected": False}, + {"type": "string", "expected": False}, + {"type": "{", "expected": False}, + {"type": "}", "expected": False}, + ] + for case in cases: + tpe_str, expected = case["type"], case["expected"] + value = common.isdicttype(tpe_str) + self.assertEqual(value, expected) + + def test_is_list_type(self): + cases = [ + # True cases + {"type": "[]", "expected": True}, + {"type": "[int]", "expected": True}, + {"type": "[str]", "expected": True}, + {"type": "[[str]]", "expected": True}, + {"type": "[str|int]", "expected": True}, + {"type": "[pkg.Schema]", "expected": True}, + # False cases + {"type": "int", "expected": False}, + {"type": "float", "expected": False}, + {"type": "str", "expected": False}, + {"type": "bool", "expected": False}, + {"type": "", "expected": False}, + {"type": "int8", "expected": False}, + {"type": "string", "expected": False}, + {"type": "[", "expected": False}, + {"type": "]", "expected": False}, + ] + for case in cases: + tpe_str, expected = case["type"], case["expected"] + value = common.islisttype(tpe_str) + self.assertEqual(value, expected) + + def test_separate_kv(self): + cases = [ + {"type": "{}", "expected": ("", "")}, + {"type": "{:}", "expected": ("", "")}, + {"type": "{str:}", "expected": ("str", "")}, + {"type": "{str:str}", "expected": ("str", "str")}, + {"type": "{str:int}", "expected": ("str", "int")}, + {"type": "{str:{str:}}", "expected": ("str", "{str:}")}, + {"type": "{str:{str:str}}", "expected": ("str", "{str:str}")}, + {"type": "{str:str|int}", "expected": ("str", "str|int")}, + {"type": "{str:pkg.Schema}", "expected": ("str", "pkg.Schema")}, + ] + for case in cases: + tpe_str, expected = case["type"], case["expected"] + value = common.separate_kv(common.dereferencetype(tpe_str)) + self.assertEqual(value, expected, msg=case["type"]) + + def test_union_native(self): + cases = [ + # Left None + {"value1": None, "value2": 1, "expected": 1}, + {"value1": None, "value2": 1.1, "expected": 1.1}, + {"value1": None, "value2": [], "expected": []}, + {"value1": None, "value2": {}, "expected": {}}, + {"value1": None, "value2": "s", "expected": "s"}, + {"value1": None, "value2": True, "expected": True}, + {"value1": None, "value2": False, "expected": False}, + {"value1": None, "value2": None, "expected": None}, + # Right None + {"value1": 1, "value2": None, "expected": 1}, + {"value1": 1.1, "value2": None, "expected": 1.1}, + {"value1": [], "value2": None, "expected": []}, + {"value1": {}, "value2": None, "expected": {}}, + {"value1": "s", "value2": None, "expected": "s"}, + {"value1": True, "value2": None, "expected": True}, + {"value1": False, "value2": None, "expected": False}, + {"value1": None, "value2": None, "expected": None}, + # Int + {"value1": 1, "value2": 1, "expected": 1}, + {"value1": 1, "value2": 2, "expected": 2}, + {"value1": 1, "value2": 3, "expected": 3}, + # Float + {"value1": 1.0, "value2": 1.0, "expected": 1.0}, + {"value1": 1.0, "value2": 1.5, "expected": 1.5}, + # String + {"value1": "s", "value2": "", "expected": ""}, + {"value1": "s", "value2": "s", "expected": "s"}, + {"value1": "s", "value2": "ss", "expected": "ss"}, + # Boolean True + {"value1": True, "value2": True, "expected": True}, + {"value1": True, "value2": False, "expected": False}, + # Boolean False + {"value1": False, "value2": False, "expected": False}, + {"value1": False, "value2": True, "expected": True}, + # List + {"value1": [], "value2": [], "expected": []}, + {"value1": [], "value2": [1], "expected": [1]}, + {"value1": [], "value2": [1, 2], "expected": [1, 2]}, + {"value1": [1], "value2": [1], "expected": [1]}, + {"value1": [1], "value2": [2], "expected": [2]}, + {"value1": [1], "value2": [2, 2], "expected": [2, 2]}, + {"value1": [1, 2], "value2": [3, 4], "expected": [3, 4]}, + {"value1": [1, 2, 3], "value2": [3, 4], "expected": [3, 4, 3]}, + { + "value1": [{"key1": "value1"}], + "value2": [{"key1": "value1"}], + "expected": [{"key1": "value1"}], + }, + { + "value1": [{"key1": "value1"}], + "value2": [{"key1": "value2"}], + "expected": [{"key1": "value2"}], + }, + { + "value1": [{"key1": "value1"}], + "value2": [{"key2": "value2"}], + "expected": [{"key1": "value1", "key2": "value2"}], + }, + { + "value1": [{"key1": "value1"}], + "value2": [{"key1": "value1", "key2": "value2"}], + "expected": [{"key1": "value1", "key2": "value2"}], + }, + { + "value1": [{"key1": "value1"}], + "value2": [{"key2": "value2", "key1": "value1"}], + "expected": [{"key1": "value1", "key2": "value2"}], + }, + { + "value1": [{"key1": "value1", "key2": "value2"}], + "value2": [{"key1": "value1"}], + "expected": [{"key1": "value1", "key2": "value2"}], + }, + # Dict + {"value1": {}, "value2": {}, "expected": {}}, + { + "value1": {}, + "value2": {"key1": "value1"}, + "expected": {"key1": "value1"}, + }, + { + "value1": {"key1": "value1"}, + "value2": {}, + "expected": {"key1": "value1"}, + }, + { + "value1": {"key1": "value1"}, + "value2": {"key1": "value1"}, + "expected": {"key1": "value1"}, + }, + { + "value1": {"key1": "value1"}, + "value2": {"key1": "value1", "key2": "value2"}, + "expected": {"key1": "value1", "key2": "value2"}, + }, + { + "value1": {"key1": "value1", "key2": "value2"}, + "value2": {"key1": "value1"}, + "expected": {"key1": "value1", "key2": "value2"}, + }, + { + "value1": {"s": {"key1": "value1"}}, + "value2": {"s": {"key1": "value1", "key2": "value2"}}, + "expected": {"s": {"key1": "value1", "key2": "value2"}}, + }, + { + "value1": {"s": {"key1": "value1", "key2": "value2"}}, + "value2": {"s": {"key1": "value1"}}, + "expected": {"s": {"key1": "value1", "key2": "value2"}}, + }, + { + "value1": {"key2": "value2", "key1": "value1"}, + "value2": {"key1": "value1", "key2": "value2"}, + "expected": {"key1": "value1", "key2": "value2"}, + }, + { + "value1": {"key2": "value2", "key1": "value1"}, + "value2": {"key1": "value1", "key2": "value2"}, + "expected": {"key1": "value1", "key2": "value2"}, + }, + ] + for case in cases: + value1, value2, expected = case["value1"], case["value2"], case["expected"] + union_value = common.union(value1, value2) + self.assertEqual(union_value, expected) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_api/test_internal/test_decorator.py b/test/test_units/test_kclvm/test_api/test_internal/test_decorator.py new file mode 100644 index 000000000..165001426 --- /dev/null +++ b/test/test_units/test_kclvm/test_api/test_internal/test_decorator.py @@ -0,0 +1,109 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import sys +import typing +import unittest + +import kclvm.kcl.error as kcl_error +import kclvm.api.object.internal.decorators as decorators + + +targets = [ + decorators.DecoratorTargetType.SCHEMA_TYPE, + decorators.DecoratorTargetType.ATTRIBUTE, +] + + +class TestDecoratorFactory(unittest.TestCase): + def test_decorator_factory_normal(self): + for target in targets: + decorators.decorator_factory.get(decorators.Deprecated.NAME, target) + + def test_decorator_factory_invalid(self): + for target in targets: + with self.assertRaises(kcl_error.UnKnownDecoratorError) as err: + decorators.decorator_factory.get(None, target) + self.assertEqual(err.exception.ewcode, kcl_error.ErrEwcode.UnKnownDecorator_Ew) + self.assertEqual(err.exception.arg_msg, "UnKnown decorator ") + + +class TestDecoratorDeprecated(unittest.TestCase): + def test_deprecated_schema(self): + decorator = decorators.Deprecated( + decorators.Deprecated.NAME, decorators.DecoratorTargetType.SCHEMA_TYPE + ) + with self.assertRaises(kcl_error.DeprecatedError) as err: + decorator.run(key="key", value="value") + self.assertEqual(err.exception.ewcode, kcl_error.ErrEwcode.Deprecated_Ew) + self.assertEqual(str(err.exception.arg_msg), "key was deprecated ") + + def test_deprecated_attr(self): + decorator = decorators.Deprecated( + decorators.Deprecated.NAME, decorators.DecoratorTargetType.ATTRIBUTE + ) + with self.assertRaises(kcl_error.DeprecatedError) as err: + decorator.run(key="key", value="value") + self.assertEqual(err.exception.ewcode, kcl_error.ErrEwcode.Deprecated_Ew) + self.assertEqual(str(err.exception.arg_msg), "key was deprecated ") + + def test_deprecated_attr_without_value(self): + decorator = decorators.Deprecated( + decorators.Deprecated.NAME, decorators.DecoratorTargetType.ATTRIBUTE + ) + decorator.run(key="key", value=None) + + def test_deprecated_invalid_target(self): + with self.assertRaises(kcl_error.InvalidDecoratorTargetError) as err: + decorators.Deprecated(decorators.Deprecated.NAME, None) + self.assertEqual(err.exception.ewcode, kcl_error.ErrEwcode.InvalidDecoratorTarget_Ew) + self.assertEqual(str(err.exception.arg_msg), "Invalid decorator target ") + + def test_deprecated_invalid_key(self): + for target in targets: + with self.assertRaises(kcl_error.KCLNameError) as err: + decorators.Deprecated(decorators.Deprecated.NAME, target).run("", "") + self.assertEqual(err.exception.ewcode, kcl_error.ErrEwcode.KCLNameError_Ew) + self.assertEqual(err.exception.arg_msg, "Name error : Decorator target name cannot be empty") + + def test_deprecated_version_parameter(self): + for target in targets: + decorator = decorators.Deprecated( + decorators.Deprecated.NAME, + target, + version="v1.16", + ) + with self.assertRaises(kcl_error.DeprecatedError) as err: + decorator.run(key="key", value="value") + self.assertEqual(err.exception.ewcode, kcl_error.ErrEwcode.Deprecated_Ew) + self.assertEqual( + str(err.exception.arg_msg), "key was deprecated since version v1.16" + ) + + def test_deprecated_version_reason_parameter(self): + for target in targets: + decorator = decorators.Deprecated( + decorators.Deprecated.NAME, + target, + reason="key is not supported", + version="v1.16", + ) + with self.assertRaises(kcl_error.DeprecatedError) as err: + decorator.run(key="key", value="value") + self.assertEqual(err.exception.ewcode, kcl_error.ErrEwcode.Deprecated_Ew) + self.assertEqual( + str(err.exception.arg_msg), + "key was deprecated since version v1.16, key is not supported", + ) + + def test_deprecated_version_strict_parameter(self): + for target in targets: + decorator = decorators.Deprecated( + decorators.Deprecated.NAME, + target, + strict=False, + ) + decorator.run(key="key", value="value") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_api/test_internal/test_option.py b/test/test_units/test_kclvm/test_api/test_internal/test_option.py new file mode 100644 index 000000000..c1993892e --- /dev/null +++ b/test/test_units/test_kclvm/test_api/test_internal/test_option.py @@ -0,0 +1,77 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest + +import kclvm.config +import kclvm.api.object.internal.option as option + + +class TestOption(unittest.TestCase): + def setUp(self): + kclvm.config.arguments = [ + ("key1", "value1"), + ("key1", "value2"), + ("key2", "s"), + ("key3", 1), + ("key4", 1.0), + ("key5", True), + ("key6", False), + ("key7", "1"), + ("key8", "1.0"), + ] + option.kcl_option_init_all() + return super().setUp() + + def tearDown(self): + kclvm.config.arguments = [] + option.kcl_option_reset() + return super().tearDown() + + def test_kcl_option_elem(self): + elem = option.KclOptionElem("key") + self.assertEqual(str(elem), "key=?") + elem.default = "value" + self.assertEqual(str(elem), "key=value") + elem.value_type = "str" + self.assertEqual(str(elem), "key=value (str)") + elem.required = True + self.assertEqual(str(elem), "key=value (str,required)") + elem.value_type = "" + self.assertEqual(str(elem), "key=value (required)") + elem.file = "main.k" + elem.line = 1 + self.assertEqual(elem.get_help(verbose_mode=2), "key=value (required) (main.k:1)") + + def test_kcl_option_dict(self): + elem_key1 = option.KclOptionElem("key1") + elem_key2 = option.KclOptionElem("key2") + option_dict = option._KclOptionDict() + self.assertEqual(option_dict.help(), "") + option_dict.m["key1"] = elem_key1 + option_dict.m["key2"] = elem_key2 + self.assertEqual(option_dict.len(), 2) + self.assertEqual(option_dict.get_dict(), option_dict.m) + self.assertEqual(option_dict.keys(), ["key1", "key2"]) + self.assertEqual(option_dict.has_key("key1"), True) + self.assertEqual(option_dict.has_key("key_err"), False) + self.assertEqual(option_dict.help(), "option list:\nkey1=?\nkey2=?") + + def test_option_not_exist(self): + self.assertEqual(option.kcl_option("not_exist_key"), None) + self.assertEqual(option.kcl_option("not_exist_key", default=1), 1) + self.assertEqual(option.kcl_option("not_exist_key", default=1.0), 1.0) + self.assertEqual(option.kcl_option("not_exist_key", default=True), True) + + def test_option(self): + self.assertEqual(option.kcl_option("key1"), "value2") + self.assertEqual(option.kcl_option("key2"), "s") + self.assertEqual(option.kcl_option("key3"), 1) + self.assertEqual(option.kcl_option("key4"), 1.0) + self.assertEqual(option.kcl_option("key5"), True) + self.assertEqual(option.kcl_option("key6"), False) + self.assertEqual(option.kcl_option("key7", type="int"), 1) + self.assertEqual(option.kcl_option("key8", type="float"), 1.0) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_api/test_internal/test_path_selector.py b/test/test_units/test_kclvm/test_api/test_internal/test_path_selector.py new file mode 100644 index 000000000..fb8b6e315 --- /dev/null +++ b/test/test_units/test_kclvm/test_api/test_internal/test_path_selector.py @@ -0,0 +1,46 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest + +import kclvm.api.object.internal.path_selector as ps + + +class TestPathSelector(unittest.TestCase): + def test_select_instance_attributes(self): + cases = [ + {"inst": {"key1": "value1"}, "attrs": None, "expected": {"key1": "value1"}}, + {"inst": {"key1": "value1"}, "attrs": {}, "expected": {"key1": "value1"}}, + { + "inst": {"key1": "value1"}, + "attrs": {"key1": {}}, + "expected": {"key1": "value1"}, + }, + { + "inst": {"key1": "value1"}, + "attrs": {"err_key": {}}, + "expected": None, + }, + { + "inst": {"key1": {"internal_key": "value1"}}, + "attrs": {"key1": {}}, + "expected": {"key1": {"internal_key": "value1"}}, + }, + { + "inst": {"key1": {"internal_key": "value1"}}, + "attrs": {"key1": {"internal_key": {}}}, + "expected": {"key1": {"internal_key": "value1"}}, + }, + { + "inst": {"key1": "value1", "key2": "value2"}, + "attrs": {"key1": {}}, + "expected": {"key1": "value1"}, + }, + ] + for case in cases: + inst, attrs, expected = case["inst"], case["attrs"], case["expected"] + value = ps.select_instance_attributes(inst, attrs) + self.assertEqual(value, expected) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_api/test_object/path_selector_test_data/kcl.mod b/test/test_units/test_kclvm/test_api/test_object/path_selector_test_data/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_api/test_object/path_selector_test_data/main.k b/test/test_units/test_kclvm/test_api/test_object/path_selector_test_data/main.k new file mode 100644 index 000000000..e122d4c05 --- /dev/null +++ b/test/test_units/test_kclvm/test_api/test_object/path_selector_test_data/main.k @@ -0,0 +1,6 @@ +schema Data: + id?: int + +data0 = Data { id = 0 } +data1 = Data { id = 1 } +data2 = Data { id = 2 } diff --git a/test/test_units/test_kclvm/test_api/test_object/test_bytecode.py b/test/test_units/test_kclvm/test_api/test_object/test_bytecode.py new file mode 100644 index 000000000..85dd97fa4 --- /dev/null +++ b/test/test_units/test_kclvm/test_api/test_object/test_bytecode.py @@ -0,0 +1,38 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import pathlib +import unittest + +import kclvm.kcl.error as kcl_error +import kclvm.config as kcfg +import kclvm.api.object as objpkg +import kclvm.api.object.internal.path_selector as path_selector + + +class TestBytecodeObject(unittest.TestCase): + def setUp(self): + kcfg.path_selector = [["", "data0"]] + return super().setUp() + + def tearDown(self): + kcfg.path_selector = [] + return super().tearDown() + + def test_kcl_result(self): + filename = str( + pathlib.Path(__file__) + .parent.joinpath("path_selector_test_data") + .joinpath("main.k") + ) + data = {"data0": {"id": 0}, "data1": {"id": 1}, "data2": {"id": 2}} + dict_obj = objpkg.to_kcl_obj(data).value + result = objpkg.KCLResult(dict_obj, filename) + self.assertEqual(str(result), str(dict_obj)) + self.assertEqual( + result.filter_by_path_selector(), + objpkg.to_kcl_obj({"data0": {"id": 0}}).value, + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_api/test_object/test_decorator.py b/test/test_units/test_kclvm/test_api/test_object/test_decorator.py new file mode 100644 index 000000000..b0a1552da --- /dev/null +++ b/test/test_units/test_kclvm/test_api/test_object/test_decorator.py @@ -0,0 +1,31 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest + +import kclvm.kcl.error as kcl_error +import kclvm.api.object as objpkg + +from kclvm.api.object.internal import Decorator + + +class TestDecoratorObject(unittest.TestCase): + def test_decorator_deprecated(self): + schema_name = "Person" + deprecated_decorator_obj = objpkg.KCLDecoratorObject( + target=objpkg.DecoratorTargetType.SCHEMA_TYPE, + name="deprecated", + key=schema_name, + ) + deprecated_decorator_obj.resolve([], []) + self.assertEqual( + deprecated_decorator_obj.type(), objpkg.KCLObjectType.DECORATOR + ) + self.assertEqual(deprecated_decorator_obj.type_str(), "decorator") + self.assertIsInstance(deprecated_decorator_obj.decorator, Decorator) + with self.assertRaises(kcl_error.DeprecatedError) as err: + deprecated_decorator_obj.call([], [], key=schema_name) + self.assertIn(f"{schema_name} was deprecated", str(err.exception)) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_api/test_object/test_function.py b/test/test_units/test_kclvm/test_api/test_object/test_function.py new file mode 100644 index 000000000..5b8d2cab3 --- /dev/null +++ b/test/test_units/test_kclvm/test_api/test_object/test_function.py @@ -0,0 +1,45 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import pathlib +import unittest + +import kclvm.kcl.error as kcl_error +import kclvm.api.object as objpkg + + +class TestFunctionObject(unittest.TestCase): + def test_function_object(self): + empty_func_obj = objpkg.KCLFunctionObject(name="fake") + self.assertEqual(empty_func_obj.type(), objpkg.KCLObjectType.FUNCTION) + self.assertEqual(empty_func_obj.type_str(), "function") + self.assertEqual(empty_func_obj.call([], []), None) + + def test_closure_object(self): + empty_func_obj = objpkg.KCLClosureObject(name="fake") + self.assertEqual(empty_func_obj.type(), objpkg.KCLObjectType.CLOSURE) + self.assertEqual(empty_func_obj.type_str(), "closure") + + def test_builtin_function_object(self): + print_builtin_func_obj = objpkg.KCLBuiltinFunctionObject( + name="print", + function=print, + ) + self.assertEqual(print_builtin_func_obj.call([], []), objpkg.NONE_INSTANCE) + sum_builtin_func_obj = objpkg.KCLBuiltinFunctionObject( + name="sum", + function=sum, + ) + self.assertEqual( + sum_builtin_func_obj.call([objpkg.to_kcl_obj([1, 2, 3])], []), + objpkg.to_kcl_obj(6), + ) + invalid_builtin_func_obj = objpkg.KCLBuiltinFunctionObject( + name="sum", + function=None, + ) + with self.assertRaises(Exception): + invalid_builtin_func_obj.call([], []) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_api/test_object/test_object.py b/test/test_units/test_kclvm/test_api/test_object/test_object.py new file mode 100644 index 000000000..14304199e --- /dev/null +++ b/test/test_units/test_kclvm/test_api/test_object/test_object.py @@ -0,0 +1,360 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest +from typing import cast + +import kclvm.kcl.ast as ast +import kclvm.kcl.error as kcl_error +from kclvm.api.object import ( + KCLIntObject, + KCLNumberMultiplierObject, + KCLDictObject, + KCLSchemaConfigObject, + KCLSchemaObject, + Undefined, + to_kcl_obj, + to_python_obj, +) + + +def to_kcl_dict_obj(data: dict) -> KCLDictObject: + return cast(KCLDictObject, to_kcl_obj(data)) + + +class TestNumberMultiplierObject(unittest.TestCase): + def test_number_multiplier_member_function(self): + obj = KCLNumberMultiplierObject( + value=1024, + raw_value=1, + binary_suffix="Mi", + ) + self.assertEqual(obj.type_str(), "number_multiplier(1Mi)") + self.assertEqual(str(obj), "1Mi") + self.assertEqual(repr(obj), "1Mi") + self.assertEqual(int(obj), 1024) + self.assertEqual(float(obj), 1024.0) + self.assertEqual(bool(obj), True) + + +class TestDictObject(unittest.TestCase): + def test_dict_object_append_unpack(self): + cases = [ + { + "data": {"key1": 1}, + "item": {"key2": 2}, + "expected": {"key1": 1, "key2": 2}, + }, + {"data": {"key1": 1}, "item": None, "expected": {"key1": 1}}, + {"data": {"key1": 1}, "item": Undefined, "expected": {"key1": 1}}, + { + "data": {"key1": 1}, + "item": KCLSchemaConfigObject( + value={"key1": KCLIntObject(2)}, + operation_map={"key1": ast.ConfigEntryOperation.OVERRIDE}, + ), + "expected": {"key1": 2}, + }, + { + "data": {"key1": 1}, + "item": KCLSchemaObject( + attrs={"key1": KCLIntObject(2)}, + operation_map={"key1": ast.ConfigEntryOperation.OVERRIDE}, + ), + "expected": {"key1": 2}, + }, + ] + for case in cases: + data, item, expected = ( + to_kcl_obj(case["data"]), + to_kcl_obj(case["item"]), + to_kcl_obj(case["expected"]), + ) + data.append_unpack(item) + self.assertEqual(to_python_obj(data), to_python_obj(expected)) + + def test_dict_object_insert_with_key(self): + cases = [ + { + "data": {"key": []}, + "key": "key", + "value": [1], + "index": None, + "expected": {"key": [1]}, + }, + { + "data": {"key": []}, + "key": "key", + "value": [1], + "index": -1, + "expected": {"key": [1]}, + }, + { + "data": {"key": [0]}, + "key": "key", + "value": [1], + "index": -1, + "expected": {"key": [0, 1]}, + }, + { + "data": {"key": [0]}, + "key": "key", + "value": [1], + "index": 0, + "expected": {"key": [1, 0]}, + }, + { + "data": {"key": None}, + "key": "key", + "value": [1], + "index": -1, + "expected": {"key": [1]}, + }, + ] + invalid_cases = [ + { + "data": {"key": 1}, + "key": "key", + "value": [1], + "index": -1, + "expected": {"key": [1]}, + }, + ] + for case in cases: + data, key, value, index, expected = ( + to_kcl_dict_obj(case["data"]), + case["key"], + to_kcl_obj(case["value"]), + case["index"], + case["expected"], + ) + data.insert_with_key(key, value, index) + self.assertEqual(to_python_obj(data), expected) + for case in invalid_cases: + data, key, value, index, expected = ( + to_kcl_dict_obj(case["data"]), + case["key"], + to_kcl_obj(case["value"]), + case["index"], + case["expected"], + ) + with self.assertRaises(kcl_error.KCLException): + data.insert_with_key(key, value, index) + + def test_dict_object_list_key_override(self): + cases = [ + { + "data": {"key": [0]}, + "attr": "key", + "value": 1, + "index": 0, + "expected": {"key": [1]}, + }, + { + "data": {"key": [0, 0]}, + "attr": "key", + "value": 1, + "index": 1, + "expected": {"key": [0, 1]}, + }, + { + "data": {"key": [0]}, + "attr": "key", + "value": Undefined, + "index": 0, + "expected": {"key": []}, + }, + ] + invalid_cases = [ + { + "data": {"key": 1}, + "attr": "key", + "value": [1], + "index": None, + "expected": {}, + }, + ] + for case in cases: + data, attr, value, index, expected = ( + to_kcl_dict_obj(case["data"]), + case["attr"], + to_kcl_obj(case["value"]), + case["index"], + case["expected"], + ) + data.list_key_override(attr, value, index) + self.assertEqual(to_python_obj(data), expected) + for case in invalid_cases: + data, attr, value, index, expected = ( + to_kcl_dict_obj(case["data"]), + case["attr"], + to_kcl_obj(case["value"]), + case["index"], + case["expected"], + ) + with self.assertRaises(kcl_error.KCLException): + data.list_key_override(attr, value, index) + + def test_dict_object_insert_with(self): + cases = [ + {"data": {}, "insert_data": {}, "index": None, "expected": {}}, + {"data": {}, "insert_data": None, "index": None, "expected": {}}, + { + "data": {"key": []}, + "insert_data": {"key": [0]}, + "index": None, + "expected": {"key": [0]}, + }, + { + "data": {"key": [0]}, + "insert_data": {"key": [1]}, + "index": None, + "expected": {"key": [0, 1]}, + }, + { + "data": {"key": [0], "key_val": "val"}, + "insert_data": {"key": [1]}, + "index": None, + "expected": {"key": [0, 1], "key_val": "val"}, + }, + { + "data": {"key": [0]}, + "insert_data": {"key": [1]}, + "index": -1, + "expected": {"key": [0, 1]}, + }, + { + "data": {"key": [0]}, + "insert_data": {"key": [1]}, + "index": 1, + "expected": {"key": [0, 1]}, + }, + { + "data": {"key": [0]}, + "insert_data": {"key": [1]}, + "index": 0, + "expected": {"key": [1, 0]}, + }, + ] + for case in cases: + data, insert_data, index, expected = ( + to_kcl_dict_obj(case["data"]), + to_kcl_dict_obj(case["insert_data"]), + case["index"], + case["expected"], + ) + data.insert_with(insert_data, index) + self.assertEqual(to_python_obj(data), expected) + + def test_dict_object_has_key(self): + cases = [ + {"data": {}, "key": None, "expected": False}, + {"data": {}, "key": "1", "expected": False}, + {"data": {}, "key": 1, "expected": False}, + {"data": {"key": "value"}, "key": "key_err", "expected": False}, + {"data": {"key": "value"}, "key": "key", "expected": True}, + {"data": {"key": {"key": "value"}}, "key": "key", "expected": True}, + ] + for case in cases: + data, key, expected = ( + to_kcl_dict_obj(case["data"]), + case["key"], + case["expected"], + ) + self.assertEqual(data.has_key(key), expected) + self.assertEqual(key in data, expected) + + def test_dict_object_get_key(self): + cases = [ + {"data": {}, "key": None, "expected": Undefined}, + {"data": {}, "key": "1", "expected": Undefined}, + {"data": {}, "key": 1, "expected": Undefined}, + {"data": {"key": "value"}, "key": "key_err", "expected": Undefined}, + {"data": {"key": None}, "key": "key", "expected": None}, + {"data": {"key": "value"}, "key": "key", "expected": "value"}, + { + "data": {"key": {"key": "value"}}, + "key": "key", + "expected": {"key": "value"}, + }, + ] + for case in cases: + data, key, expected = ( + to_kcl_dict_obj(case["data"]), + case["key"], + case["expected"], + ) + self.assertEqual(to_python_obj(data.get(key)), expected) + + def test_dict_object_update(self): + cases = [ + {"data": {}, "update": {"key": "value"}, "expected": {"key": "value"}}, + {"data": {}, "update": {"key": 1}, "expected": {"key": 1}}, + { + "data": {"key": "value"}, + "update": {"key": "override"}, + "expected": {"key": "override"}, + }, + { + "data": {"key1": "value1"}, + "update": {"key2": "value2"}, + "expected": {"key1": "value1", "key2": "value2"}, + }, + ] + for case in cases: + data, update, expected = ( + to_kcl_dict_obj(case["data"]), + case["update"], + case["expected"], + ) + data.update(update) + self.assertEqual(to_python_obj(data), expected) + + for case in cases: + data, update, expected = ( + to_kcl_dict_obj(case["data"]), + to_kcl_obj(case["update"]), + case["expected"], + ) + data.update(update) + self.assertEqual(to_python_obj(data), expected) + + def test_dict_unique_merge(self): + cases = [ + {"data": {}, "update": {"key": "value"}, "expected": {"key": "value"}}, + {"data": {}, "update": {"key": 1}, "expected": {"key": 1}}, + { + "data": {"key1": "value1"}, + "update": {"key2": "value2"}, + "expected": {"key1": "value1", "key2": "value2"}, + }, + ] + for case in cases: + data, update, expected = ( + to_kcl_dict_obj(case["data"]), + to_kcl_obj(case["update"]), + case["expected"], + ) + data.unique_merge_with(update) + self.assertEqual(to_python_obj(data), expected) + + def test_dict_delete(self): + cases = [ + {"data": {"key": "value"}, "key": "key", "expected": {}}, + { + "data": {"key1": "value1", "key2": "value2"}, + "key": "key1", + "expected": {"key2": "value2"}, + }, + ] + for case in cases: + data, key, expected = ( + to_kcl_dict_obj(case["data"]), + case["key"], + case["expected"], + ) + data.delete(key) + self.assertEqual(to_python_obj(data), expected) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_api/test_object/test_range_check.py b/test/test_units/test_kclvm/test_api/test_object/test_range_check.py new file mode 100644 index 000000000..0984788ac --- /dev/null +++ b/test/test_units/test_kclvm/test_api/test_object/test_range_check.py @@ -0,0 +1,49 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest + +import kclvm.config +import kclvm.kcl.info as kcl_info +import kclvm.kcl.error as kcl_error +import kclvm.api.object as objpkg + +from kclvm.compiler.check.check_type import check + + +class TestRangeCheck(unittest.TestCase): + def test_object_range_check_normal(self): + kclvm.config.debug = True + cases = [ + 1, + 2.0, + kcl_info.INT32_MAX + 1, + kcl_info.INT64_MAX, + kcl_info.FLOAT32_MAX, + ] + for case in cases: + check(objpkg.to_kcl_obj(case)) + kclvm.config.debug = False + + def test_object_range_check_invalid(self): + kclvm.config.debug = True + kclvm.config.strict_range_check = True + cases = [ + kcl_info.INT32_MAX + 1, + kcl_info.FLOAT32_MAX * 2, + ] + for case in cases: + with self.assertRaises(kcl_error.KCLException): + check(objpkg.to_kcl_obj(case)) + kclvm.config.strict_range_check = False + cases = [ + kcl_info.INT64_MAX + 1, + kcl_info.FLOAT64_MAX * 2, + ] + for case in cases: + with self.assertRaises(kcl_error.KCLException): + check(objpkg.to_kcl_obj(case)) + kclvm.config.debug = False + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_api/test_object/test_schema.py b/test/test_units/test_kclvm/test_api/test_object/test_schema.py new file mode 100644 index 000000000..d44c2b0fc --- /dev/null +++ b/test/test_units/test_kclvm/test_api/test_object/test_schema.py @@ -0,0 +1,246 @@ +# Copyright 2020 The KCL Authors. All rights reserved. +import unittest + +import kclvm.kcl.error as kcl_error +import kclvm.api.object.internal.decorators as decorators +from kclvm.api.object.object import ( + to_kcl_obj, + to_python_obj, + Undefined, + KCLIntObject, + KCLSchemaObject, + KCLSchemaConfigObject, + KCLStringLitTypeObject, +) +from kclvm.api.object.function import KCLCompiledFunctionObject, Parameter, KWArg +from kclvm.api.object.schema import KCLSchemaTypeObject +from kclvm.api.object.decorator import KCLDecoratorObject +from kclvm.kcl import ast + + +def build_test_schema_type_obj() -> KCLSchemaTypeObject: + schema_type_obj = KCLSchemaTypeObject.new( + "Person", None, "test.k", pkgpath="__main__", attr_list=["name", "age"] + ) + schema_type_obj.set_func( + KCLCompiledFunctionObject( + name="Person", + params=[ + Parameter( + name="name", value=to_kcl_obj("Alice"), type_annotation="str" + ), + Parameter(name="age", value=to_kcl_obj(18), type_annotation="int"), + Parameter( + name="sex", + value=to_kcl_obj("Male"), + type_annotation='"Male"|"Female"', + ), + ], + ) + ) + return schema_type_obj + + +def to_kcl_schema_obj(data: dict) -> KCLSchemaObject: + return KCLSchemaObject(name="Person", attrs=to_kcl_obj(data).value) + + +class TestSchemaObject(unittest.TestCase): + def test_dict_object_append_unpack(self): + cases = [ + { + "data": KCLSchemaObject(attrs={"key1": KCLIntObject(1)}), + "item": {"key2": 2}, + "expected": {"key1": 1, "key2": 2}, + }, + {"data": KCLSchemaObject(attrs={"key1": KCLIntObject(1)}), "item": None, "expected": {"key1": 1}}, + {"data": KCLSchemaObject(attrs={"key1": KCLIntObject(1)}), "item": Undefined, "expected": {"key1": 1}}, + { + "data": KCLSchemaObject(attrs={"key1": KCLIntObject(1)}), + "item": KCLSchemaConfigObject( + value={"key1": KCLIntObject(2)}, + operation_map={"key1": ast.ConfigEntryOperation.OVERRIDE}, + ), + "expected": {"key1": 2}, + }, + { + "data": KCLSchemaObject(attrs={"key1": KCLIntObject(1)}), + "item": KCLSchemaObject( + attrs={"key1": KCLIntObject(2)}, + operation_map={"key1": ast.ConfigEntryOperation.OVERRIDE}, + ), + "expected": {"key1": 2}, + }, + ] + for case in cases: + data, item, expected = ( + to_kcl_obj(case["data"]), + to_kcl_obj(case["item"]), + to_kcl_obj(case["expected"]), + ) + data.append_unpack(item) + self.assertEqual(to_python_obj(data), to_python_obj(expected)) + + def test_schema_object_update(self): + cases = [ + {"data": {}, "update": {"key": "value"}, "expected": {"key": "value"}}, + {"data": {}, "update": {"key": 1}, "expected": {"key": 1}}, + { + "data": {"key": "value"}, + "update": {"key": "override"}, + "expected": {"key": "override"}, + }, + { + "data": {"key1": "value1"}, + "update": {"key2": "value2"}, + "expected": {"key1": "value1", "key2": "value2"}, + }, + ] + for case in cases: + data, update, expected = ( + to_kcl_schema_obj(case["data"]), + case["update"], + case["expected"], + ) + data.update(update) + self.assertEqual(to_python_obj(data), expected) + + for case in cases: + data, update, expected = ( + to_kcl_schema_obj(case["data"]), + case["update"], + case["expected"], + ) + for k, v in update.items(): + data.update_key_value(k, v) + self.assertEqual(to_python_obj(data), expected) + + def test_schema_delete(self): + cases = [ + {"data": {"key": "value"}, "key": "key", "expected": {}}, + { + "data": {"key1": "value1", "key2": "value2"}, + "key": "key1", + "expected": {"key2": "value2"}, + }, + ] + for case in cases: + data, key, expected = ( + to_kcl_schema_obj(case["data"]), + case["key"], + case["expected"], + ) + data.delete(key) + self.assertEqual(to_python_obj(data), expected) + + def test_schema_set_node_of_attr(self): + schema_type_obj = build_test_schema_type_obj() + node = ast.AST() + schema_type_obj.set_node_of_attr("test_name", node) + self.assertEqual(schema_type_obj.attr_obj_map["test_name"].attr_node, node) + + node_1 = ast.AST() + schema_type_obj.set_node_of_attr("test_name", node_1) + self.assertNotEqual(schema_type_obj.attr_obj_map["test_name"].attr_node, node) + self.assertEqual(schema_type_obj.attr_obj_map["test_name"].attr_node, node_1) + + def test_schema_set_type_of_attr(self): + schema_type_obj = build_test_schema_type_obj() + tpe = KCLStringLitTypeObject() + schema_type_obj.set_type_of_attr("test_name", tpe) + self.assertEqual(schema_type_obj.attr_obj_map["test_name"].attr_type, tpe) + + tpe_1 = KCLStringLitTypeObject(value="test_tpe") + schema_type_obj.set_type_of_attr("test_name", tpe_1) + self.assertNotEqual(schema_type_obj.attr_obj_map["test_name"].attr_type, tpe) + self.assertEqual(schema_type_obj.attr_obj_map["test_name"].attr_type, tpe_1) + + def test_schema_decorator(self): + schema_obj = to_kcl_schema_obj({"key": "value"}) + schema_obj.add_decorator("key", KCLDecoratorObject( + name="Deprecated", + target=decorators.DecoratorTargetType.ATTRIBUTE, + key="key", + value="value", + decorator=decorators.Deprecated, + )) + # Deprecated decorator will raise an error + with self.assertRaises(Exception): + schema_obj.run_all_decorators() + + +class TestSchemaArgsTypeCheck(unittest.TestCase): + def test_schema_object_do_args_type_check_normal_only_args(self): + schema_type_obj = build_test_schema_type_obj() + cases = [ + [to_kcl_obj("Alice"), to_kcl_obj(18)], + [to_kcl_obj("Bob"), to_kcl_obj(10)], + [to_kcl_obj("John"), to_kcl_obj(10), to_kcl_obj("Female")], + ] + for case in cases: + schema_type_obj.do_args_type_check(case, None, {}) + + def test_schema_object_do_args_type_check_normal_only_kwargs(self): + schema_type_obj = build_test_schema_type_obj() + cases = [ + [ + KWArg(name=to_kcl_obj("name"), value=to_kcl_obj("Alice")), + KWArg(name=to_kcl_obj("age"), value=to_kcl_obj(18)), + ], + [ + KWArg(name=to_kcl_obj("name"), value=to_kcl_obj("Bob")), + KWArg(name=to_kcl_obj("age"), value=to_kcl_obj(10)), + ], + [ + KWArg(name=to_kcl_obj("sex"), value=to_kcl_obj("Male")), + KWArg(name=to_kcl_obj("name"), value=to_kcl_obj("Bob")), + KWArg(name=to_kcl_obj("age"), value=to_kcl_obj(10)), + ], + ] + for case in cases: + schema_type_obj.do_args_type_check(None, case, {}) + + def test_schema_object_unexpected_keyword_argument(self): + schema_type_obj = build_test_schema_type_obj() + cases = [ + [ + KWArg(name=to_kcl_obj("err_name"), value=to_kcl_obj("Alice")), + KWArg(name=to_kcl_obj("age"), value=to_kcl_obj(18)), + ], + [ + KWArg(name=to_kcl_obj("name"), value=to_kcl_obj("Bob")), + KWArg(name=to_kcl_obj("err_age"), value=to_kcl_obj(10)), + ], + [ + KWArg(name=to_kcl_obj("err_sex"), value=to_kcl_obj("Male")), + KWArg(name=to_kcl_obj("name"), value=to_kcl_obj("Bob")), + KWArg(name=to_kcl_obj("age"), value=to_kcl_obj(10)), + ], + ] + for case in cases: + with self.assertRaises(kcl_error.EvaluationError) as err: + schema_type_obj.do_args_type_check(None, case, {}) + self.assertEqual( + err.exception.ewcode, kcl_error.ErrEwcode.EvaluationError_Ew + ) + self.assertIn( + "schema arguments got an unexpected keyword argument", + str(err.exception), + ) + + +class TestSchemaTypeInstancesFunction(unittest.TestCase): + def test_schema_type_instances(self): + schema_type_obj = build_test_schema_type_obj() + schema_type_obj.__refs__.append( + KCLSchemaObject(name="Person", instance_pkgpath="__main__") + ) + schema_type_obj.__refs__.append( + KCLSchemaObject(name="Person", instance_pkgpath="pkg.to.path") + ) + self.assertEqual(len(schema_type_obj.instances()), 1) + self.assertEqual(len(schema_type_obj.instances(main_pkg=False)), 2) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_api/test_object/test_undefined.py b/test/test_units/test_kclvm/test_api/test_object/test_undefined.py new file mode 100644 index 000000000..cd181eb72 --- /dev/null +++ b/test/test_units/test_kclvm/test_api/test_object/test_undefined.py @@ -0,0 +1,19 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest + +from kclvm.api.object.internal import Undefined + + +class TestUndefinedObject(unittest.TestCase): + def test_undefined_object(self): + self.assertEqual(str(Undefined), "Undefined") + self.assertEqual(repr(Undefined), "Undefined") + self.assertEqual(Undefined.type_str(), "UndefinedType") + self.assertEqual(bool(Undefined), False) + self.assertEqual(not Undefined, True) + self.assertEqual(Undefined.value, None) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_ast/test_ast/test_ast.py b/test/test_units/test_kclvm/test_ast/test_ast/test_ast.py new file mode 100644 index 000000000..cd5866d0f --- /dev/null +++ b/test/test_units/test_kclvm/test_ast/test_ast/test_ast.py @@ -0,0 +1,227 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import typing +import pathlib +import hashlib +import unittest + +import kclvm.kcl.ast as ast +import kclvm.compiler.parser as parser + + +class TestAst(unittest.TestCase): + INIT_LINE = 0 + INIT_COLUMN = 0 + INIT_END_LINE = 0 + INIT_END_COLUMN = 0 + ast_node = ast.AST(INIT_LINE, INIT_COLUMN, INIT_END_LINE, INIT_END_COLUMN) + + set_methods = [ + "set_line", + "set_column", + "set_end_line", + "set_end_column", + ] + + get_methods = [ + "get_line", + "get_column", + "get_end_line", + "get_end_column", + ] + + offset_method = [ + "offset_line", + "offset_column", + "offset_end_line", + "offset_end_column", + ] + + # line, column, end_line, end_column + test_params = [0, 10, 22, 23] + offset_parmas = [-1, 10, 0] + + def test_ast_offset(self): + for i, method in enumerate(self.offset_method): + test_method = getattr(self.ast_node, method) + for offset_value in self.offset_parmas: + get_method = getattr(self.ast_node, self.get_methods[i]) + before = get_method() + test_method(offset_value) + assert get_method() == before + offset_value + + def test_ast_set_get_line_column(self): + for i, method in enumerate(self.set_methods): + test_method = getattr(self.ast_node, method) + test_method(self.test_params[i]) + + for i, method in enumerate(self.get_methods): + test_method = getattr(self.ast_node, method) + assert test_method() == self.test_params[i] + + def test_set_invalid(self): + for method in self.set_methods: + test_method = getattr(self.ast_node, method) + with self.assertRaises(AssertionError): + test_method("-1") + with self.assertRaises(AssertionError): + self.ast_node.set_line(-1) + + def test_offset_line_invalid(self): + for method in self.offset_method: + test_method = getattr(self.ast_node, method) + with self.assertRaises(AssertionError): + test_method("-1") + + def test_position_less_than(self): + _ONE = "one" + _OTHER = "other" + _RESULT = "result" + test_cases = [ + { + # position invalid + _ONE: ast.Position(filename="one.k", line=0, column=1), + _OTHER: ast.Position(filename="one.k", line=0, column=1), + _RESULT: False, + }, + { + # different filename + _ONE: ast.Position(filename="one.k", line=1, column=1), + _OTHER: ast.Position(filename="other.k", line=1, column=1), + _RESULT: False, + }, + { + # line number less than + _ONE: ast.Position(filename="one.k", line=1, column=1), + _OTHER: ast.Position(filename="one.k", line=2, column=1), + _RESULT: True, + }, + { + # line number larger than + _ONE: ast.Position(filename="one.k", line=2, column=1), + _OTHER: ast.Position(filename="one.k", line=1, column=1), + _RESULT: False, + }, + { + # line number equal, column number less than + _ONE: ast.Position(filename="one.k", line=1, column=0), + _OTHER: ast.Position(filename="one.k", line=1, column=1), + _RESULT: True, + }, + ] + for t in test_cases: + expect = t[_RESULT] + got = t[_ONE].less(t[_OTHER]) + assert ( + expect == got + ), f"position less than check between {t[_ONE]} and {t[_OTHER]}, expect: {expect}, got: {got}" + + def test_position_valid(self): + _POS = "pos" + _RESULT = "result" + test_cases = [ + { + # empty filename + _POS: ast.Position(line=1, column=0), + _RESULT: False, + }, + { + # line number < 1 + _POS: ast.Position(filename="pos.k", line=0, column=0), + _RESULT: False, + }, + ] + for t in test_cases: + expect = t[_RESULT] + got = t[_POS].is_valid() + assert ( + expect == got + ), f"position valid on {t[_POS]}, expect: {expect}, got: {got}" + + def test_position_less_equal(self): + _ONE = "one" + _OTHER = "other" + _RESULT = "result" + test_cases = [ + { + # position invalid + _ONE: ast.Position(filename="one.k", line=0, column=1), + _OTHER: ast.Position(filename="one.k", line=0, column=1), + _RESULT: False, + }, + { + # different filename + _ONE: ast.Position(filename="one.k", line=1, column=1), + _OTHER: ast.Position(filename="other.k", line=1, column=1), + _RESULT: False, + }, + { + # position less than + _ONE: ast.Position(filename="one.k", line=1, column=1), + _OTHER: ast.Position(filename="one.k", line=2, column=1), + _RESULT: True, + }, + { + # position equal + _ONE: ast.Position(filename="one.k", line=1, column=1), + _OTHER: ast.Position(filename="one.k", line=1, column=1), + _RESULT: True, + }, + ] + + for t in test_cases: + expect = t[_RESULT] + got = t[_ONE].less_equal(t[_OTHER]) + assert ( + expect == got + ), f"position less equal check between {t[_ONE]} and {t[_OTHER]}, expect: {expect}, got: {got}" + + def test_position_equal(self): + _ONE = "one" + _OTHER = "other" + _RESULT = "result" + test_cases = [ + { + # position equal + _ONE: ast.Position(filename="one.k", line=0, column=1), + _OTHER: ast.Position(filename="one.k", line=0, column=1), + _RESULT: True, + }, + { + # position not equal + _ONE: ast.Position(filename="one.k", line=0, column=1), + _OTHER: ast.Position(filename="one.k", line=0, column=2), + _RESULT: False, + }, + ] + for t in test_cases: + expect = t[_RESULT] + got = t[_ONE] == (t[_OTHER]) + assert ( + expect == got + ), f"position equal check between {t[_ONE]} and {t[_OTHER]}, expect: {expect}, got: {got}" + + def test_get_check_sum(self): + filename = str(pathlib.Path(__file__).parent.joinpath("test_data/check_sum.k")) + prog = parser.LoadProgram(filename) + with open(filename, "rb") as f: + check_sum_expected = hashlib.md5() + check_sum_expected.update(filename.encode("utf-8")) + check_sum_expected.update(f.read()) + self.assertEqual(prog.get_check_sum(), check_sum_expected.hexdigest()) + + def test_GetArgDefault_invalid(self): + arg = ast.Arguments() + self.assertEqual(arg.GetArgDefault(10), None) + + def test_find_nearest_parent_by_type(self): + prog = parser.LoadProgram("mock.k", k_code_list=["a=1"], set_ast_parent=True) + target_identifier = prog.pkgs[prog.MAIN_PKGPATH][0].body[0].targets[0] + self.assertIsNotNone(target_identifier) + self.assertIsNotNone(target_identifier.parent) + nearest_schema_expr = target_identifier.find_nearest_parent_by_type(tpe=ast.SchemaExpr) + self.assertIsNone(nearest_schema_expr) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_ast/test_ast/test_data/check_sum.k b/test/test_units/test_kclvm/test_ast/test_ast/test_data/check_sum.k new file mode 100644 index 000000000..1337a530c --- /dev/null +++ b/test/test_units/test_kclvm/test_ast/test_ast/test_data/check_sum.k @@ -0,0 +1 @@ +a = 1 diff --git a/test/test_units/test_kclvm/test_ast/test_filter/test_filter.py b/test/test_units/test_kclvm/test_ast/test_filter/test_filter.py new file mode 100644 index 000000000..db4720a3c --- /dev/null +++ b/test/test_units/test_kclvm/test_ast/test_filter/test_filter.py @@ -0,0 +1,51 @@ +#! /usr/bin/env python3 + +import unittest + +from kclvm.compiler.astutil import filter_declarations +from kclvm.compiler.parser import ParseFile + +import kclvm.kcl.ast as ast +import kclvm.kcl.error as kcl_error + +code = """ +schema Person: + name: str + age: int + +schema Config: + data?: [int] + +a = 1 +b = 2 +person = Person { + name: "Alice" + age: 18 +} +config = Config {} +""" + + +class KCLASTFilterTest(unittest.TestCase): + """ + KCL AST filter test + """ + + def test_filter(self): + module = ParseFile("test.k", code) + global_declarations = filter_declarations(module) + schema_declarations = filter_declarations(module, ast.SchemaExpr) + binary_declarations = filter_declarations(module, ast.BinaryExpr) + self.assertEqual(len(global_declarations), 4) + self.assertEqual(len(schema_declarations), 2) + self.assertEqual(len(binary_declarations), 0) + self.assertEqual(global_declarations[0].name, "a") + self.assertEqual(global_declarations[1].name, "b") + self.assertEqual(global_declarations[2].name, "person") + self.assertEqual(global_declarations[3].name, "config") + self.assertEqual(schema_declarations[0].name, "person") + self.assertEqual(schema_declarations[1].name, "config") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_ast/test_precedence/test_precedence.py b/test/test_units/test_kclvm/test_ast/test_precedence/test_precedence.py new file mode 100644 index 000000000..b7a319155 --- /dev/null +++ b/test/test_units/test_kclvm/test_ast/test_precedence/test_precedence.py @@ -0,0 +1,60 @@ +#! /usr/bin/env python3 + +import unittest + +import kclvm.kcl.ast as ast + +import kclvm.kcl.ast.lark_token as lark_token + + +class KCLPrecedenceTest(unittest.TestCase): + """KCL AST operator precedence test""" + + def test_precedence(self): + self.assertEqual(ast.precedence(None), 0) + self.assertEqual(ast.precedence(ast.lark_token.LarkToken.L_simple_expr), 0) + self.assertEqual(ast.precedence(ast.AugOp.Add), 0) + self.assertEqual(ast.precedence(ast.AugOp.Sub), 0) + self.assertEqual(ast.precedence(ast.AugOp.Mul), 0) + self.assertEqual(ast.precedence(ast.AugOp.Div), 0) + self.assertEqual(ast.precedence(ast.AugOp.Mod), 0) + self.assertEqual(ast.precedence(ast.AugOp.Pow), 0) + self.assertEqual(ast.precedence(ast.AugOp.LShift), 0) + self.assertEqual(ast.precedence(ast.AugOp.RShift), 0) + self.assertEqual(ast.precedence(ast.AugOp.BitOr), 0) + self.assertEqual(ast.precedence(ast.AugOp.BitXor), 0) + self.assertEqual(ast.precedence(ast.AugOp.BitAnd), 0) + self.assertEqual(ast.precedence(ast.AugOp.FloorDiv), 0) + self.assertEqual(ast.precedence(ast.BinOp.Add), 9) + self.assertEqual(ast.precedence(ast.BinOp.Sub), 9) + self.assertEqual(ast.precedence(ast.BinOp.Mul), 10) + self.assertEqual(ast.precedence(ast.BinOp.Div), 10) + self.assertEqual(ast.precedence(ast.BinOp.Mod), 10) + self.assertEqual(ast.precedence(ast.BinOp.Pow), 12) + self.assertEqual(ast.precedence(ast.BinOp.LShift), 8) + self.assertEqual(ast.precedence(ast.BinOp.RShift), 8) + self.assertEqual(ast.precedence(ast.BinOp.BitOr), 6) + self.assertEqual(ast.precedence(ast.BinOp.BitXor), 5) + self.assertEqual(ast.precedence(ast.BinOp.BitAnd), 7) + self.assertEqual(ast.precedence(ast.BinOp.FloorDiv), 10) + self.assertEqual(ast.precedence(ast.BinOp.And), 2) + self.assertEqual(ast.precedence(ast.BinOp.Or), 1) + self.assertEqual(ast.precedence(ast.CmpOp.Eq), 4) + self.assertEqual(ast.precedence(ast.CmpOp.NotEq), 4) + self.assertEqual(ast.precedence(ast.CmpOp.Lt), 4) + self.assertEqual(ast.precedence(ast.CmpOp.LtE), 4) + self.assertEqual(ast.precedence(ast.CmpOp.Gt), 4) + self.assertEqual(ast.precedence(ast.CmpOp.GtE), 4) + self.assertEqual(ast.precedence(ast.CmpOp.Is), 4) + self.assertEqual(ast.precedence(ast.CmpOp.In), 4) + self.assertEqual(ast.precedence(ast.CmpOp.Not), 4) + self.assertEqual(ast.precedence(ast.CmpOp.IsNot), 4) + self.assertEqual(ast.precedence(ast.CmpOp.NotIn), 4) + self.assertEqual(ast.precedence(ast.UnaryOp.UAdd), 11) + self.assertEqual(ast.precedence(ast.UnaryOp.USub), 11) + self.assertEqual(ast.precedence(ast.UnaryOp.Invert), 11) + self.assertEqual(ast.precedence(ast.UnaryOp.Not), 3) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_ast/test_transfomer/test_data/assign.input b/test/test_units/test_kclvm/test_ast/test_transfomer/test_data/assign.input new file mode 100644 index 000000000..a5ebf7ab2 --- /dev/null +++ b/test/test_units/test_kclvm/test_ast/test_transfomer/test_data/assign.input @@ -0,0 +1,4 @@ +a = 1 +b = 2 +if True: + c = 3 diff --git a/test/test_units/test_kclvm/test_ast/test_transfomer/test_data/assign.output b/test/test_units/test_kclvm/test_ast/test_transfomer/test_data/assign.output new file mode 100644 index 000000000..d63f7aa3f --- /dev/null +++ b/test/test_units/test_kclvm/test_ast/test_transfomer/test_data/assign.output @@ -0,0 +1,5 @@ +a = aa = 1 +b = bb = 2 +if True: + c = cc = 3 + diff --git a/test/test_units/test_kclvm/test_ast/test_transfomer/test_data/assign_split.input b/test/test_units/test_kclvm/test_ast/test_transfomer/test_data/assign_split.input new file mode 100644 index 000000000..a4f1d33e2 --- /dev/null +++ b/test/test_units/test_kclvm/test_ast/test_transfomer/test_data/assign_split.input @@ -0,0 +1,6 @@ +schema Config: + data?: int + +config1 = config2 = Config { + data: 1 +} diff --git a/test/test_units/test_kclvm/test_ast/test_transfomer/test_data/assign_split.output b/test/test_units/test_kclvm/test_ast/test_transfomer/test_data/assign_split.output new file mode 100644 index 000000000..ec6c12e13 --- /dev/null +++ b/test/test_units/test_kclvm/test_ast/test_transfomer/test_data/assign_split.output @@ -0,0 +1,11 @@ +schema Config: + data?: int + +config1 = Config { + data: 1 +} + +config2 = Config { + data: 1 +} + diff --git a/test/test_units/test_kclvm/test_ast/test_transfomer/test_transformer.py b/test/test_units/test_kclvm/test_ast/test_transfomer/test_transformer.py new file mode 100644 index 000000000..ff5f3c7e7 --- /dev/null +++ b/test/test_units/test_kclvm/test_ast/test_transfomer/test_transformer.py @@ -0,0 +1,81 @@ +#! /usr/bin/env python3 + +import io +import unittest +import pathlib +from copy import deepcopy +from typing import List + +from kclvm.compiler.parser import ParseFile +from kclvm.tools.printer import PrintAST +import kclvm.kcl.ast as ast + + +_FILE_INPUT_SUFFIX = ".input" +_FILE_OUTPUT_SUFFIX = ".output" +_PATH_NAME = "test_data" +_DIR_PATH = pathlib.Path(__file__).parent.joinpath(_PATH_NAME) + + +class TestTransformer(ast.TreeTransformer): + def walk_AssignStmt(self, t: ast.AssignStmt): + name = t.targets[0].get_name() + t.targets.append( + ast.Identifier( + line=t.line, + column=t.column, + names=[name * 2], + ctx=ast.ExprContext.STORE, + ) + ) + return t + + +class AssignSplitTransformer(ast.TreeTransformer): + def walk_AssignStmt(self, t: ast.AssignStmt): + if len(t.targets) > 1: + assign_stmt_list: List[ast.AssertStmt] = [] + for target in t.targets: + t_copy = deepcopy(t) + t_copy.targets = [target] + assign_stmt_list.append(t_copy) + return assign_stmt_list + return t + + +class KCLBaseTreeTransformerTest(unittest.TestCase): + """KCL AST transfomer test""" + + def setUp(self): + inputs = list(sorted(pathlib.Path(_DIR_PATH).glob("*" + _FILE_INPUT_SUFFIX))) + case_inputs = [input.read_text() for input in inputs] + outputs = list(sorted(pathlib.Path(_DIR_PATH).glob("*" + _FILE_OUTPUT_SUFFIX))) + case_outputs = [output.read_text() for output in outputs] + names = [str(output.with_suffix("").name) for output in outputs] + self.TEST_CASES = zip(names, case_inputs, case_outputs) + return super().setUp() + + def transform_code(self, code: str, transformer: ast.TreeTransformer) -> str: + module = ParseFile("", code) + transformer().walk(module) + with io.StringIO() as buf: + PrintAST(module, buf) + return buf.getvalue() + + +class KCLTreeTransformerTest(KCLBaseTreeTransformerTest): + """KCL AST transfomer test""" + + transformer_mapping = { + "assign": TestTransformer, + "assign_split": AssignSplitTransformer, + } + + def test_transform(self): + for name, case_input, case_output in self.TEST_CASES: + transformer = self.transformer_mapping.get(name, TestTransformer) + self.assertEqual(self.transform_code(case_input, transformer), case_output) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_astutil/test_builder.py b/test/test_units/test_kclvm/test_compiler/test_astutil/test_builder.py new file mode 100644 index 000000000..f94abd2c8 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_astutil/test_builder.py @@ -0,0 +1,65 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import os +import pathlib +import unittest + +import kclvm.api.object.internal as internal +import kclvm.kcl.ast as ast +import kclvm.compiler.astutil.builder as ast_builder + + +class TestASTUtil(unittest.TestCase): + def get_fake_schema_expr(self, name: str) -> ast.SchemaExpr: + schema_expr = ast.SchemaExpr(line=1, column=1) + schema_name = ast.Identifier(line=1, column=1) + schema_name.names = [name] + schema_expr.name = schema_name + schema_expr.config = ast.ConfigExpr(line=1, column=1 + 1 +len(name)) + return schema_expr + + def test_build_lit_node_from_string(self): + cases = [ + {"value": "a", "expected": ast.StringLit(value="a", line=1, column=1)}, + {"value": "'a'", "expected": ast.StringLit(value="a", line=1, column=1)}, + {"value": '"a"', "expected": ast.StringLit(value="a", line=1, column=1)}, + {"value": "kclvm:v1", "expected": ast.StringLit(value="kclvm:v1", line=1, column=1)}, + {"value": "1", "expected": ast.NumberLit(value=1, line=1, column=1)}, + {"value": "1.1", "expected": ast.NumberLit(value=1.1, line=1, column=1)}, + {"value": "1.0e1", "expected": ast.NumberLit(value=10.0, line=1, column=1)}, + {"value": "True", "expected": ast.NameConstantLit(value=True, line=1, column=1)}, + {"value": "False", "expected": ast.NameConstantLit(value=False, line=1, column=1)}, + {"value": "None", "expected": ast.NameConstantLit(value=None, line=1, column=1)}, + {"value": "Undefined", "expected": ast.NameConstantLit(value=internal.Undefined, line=1, column=1)}, + {"value": "[]", "expected": ast.ListExpr(line=1, column=1)}, + {"value": "{}", "expected": ast.ConfigExpr(line=1, column=1)}, + {"value": "Data {}", "expected": self.get_fake_schema_expr("Data")}, + {"value": "pkg.Data {}", "expected": self.get_fake_schema_expr("pkg.Data")}, + ] + for case in cases: + value, expected = case["value"], case["expected"] + ast_node = ast_builder.BuildNodeFromString(value, 1, 1) + self.assertEqual(str(ast_node), str(expected)) + + def test_build_lit_node_from_value(self): + cases = [ + {"value": "a", "expected": ast.StringLit(value="a", line=1, column=1)}, + {"value": "'a'", "expected": ast.StringLit(value="'a'", line=1, column=1)}, + {"value": '"a"', "expected": ast.StringLit(value="\"a\"", line=1, column=1)}, + {"value": "kclvm:v1", "expected": ast.StringLit(value="kclvm:v1", line=1, column=1)}, + {"value": 1, "expected": ast.NumberLit(value=1, line=1, column=1)}, + {"value": 1.1, "expected": ast.NumberLit(value=1.1, line=1, column=1)}, + {"value": 1.0e1, "expected": ast.NumberLit(value=10.0, line=1, column=1)}, + {"value": True, "expected": ast.NameConstantLit(value=True, line=1, column=1)}, + {"value": False, "expected": ast.NameConstantLit(value=False, line=1, column=1)}, + {"value": None, "expected": ast.NameConstantLit(value=None, line=1, column=1)}, + {"value": internal.Undefined, "expected": ast.NameConstantLit(value=internal.Undefined, line=1, column=1)}, + ] + for case in cases: + value, expected = case["value"], case["expected"] + ast_node = ast_builder.BuildLitNodeFromValue(value, 1, 1) + self.assertEqual(str(ast_node), str(expected)) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_astutil/test_filter.py b/test/test_units/test_kclvm/test_compiler/test_astutil/test_filter.py new file mode 100644 index 000000000..c18f8b1bc --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_astutil/test_filter.py @@ -0,0 +1,37 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import os +import pathlib +import unittest + +import kclvm.kcl.ast as ast +import kclvm.compiler.parser as parser +import kclvm.compiler.astutil.filter as filter + +filter_simple_case = """ +schema Config: + id: int + +config1 = Config {id = 1} +config2: Config {id = 2} +config3 = {id = 3} +""" + + +class TestASTFilter(unittest.TestCase): + def test_filter_declarations(self): + module = parser.ParseFile( + "__main__.k", code=filter_simple_case + ) + declarations = filter.filter_declarations(module) + self.assertEqual(len(declarations), 3) + declarations = filter.filter_declarations(module, ast_type=ast.SchemaExpr) + self.assertEqual(len(declarations), 2) + declarations = filter.filter_declarations(module, ast_type="SchemaExpr") + self.assertEqual(len(declarations), 2) + declarations = filter.filter_declarations(module, ast_type=(ast.SchemaExpr, ast.ConfigExpr)) + self.assertEqual(len(declarations), 3) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_astutil/test_fix.py b/test/test_units/test_kclvm/test_compiler/test_astutil/test_fix.py new file mode 100644 index 000000000..23d5f02d6 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_astutil/test_fix.py @@ -0,0 +1,118 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import os +import pathlib +import unittest +import typing + +import kclvm.kcl.ast as ast +import kclvm.compiler.parser as parser +import kclvm.compiler.astutil.fix as fix + +MAIN_FILE = "main.k" +MAIN_PKG = "__main__" +ROOT_PATH = str(pathlib.Path(__file__).parent) + +fix_test_schema_relaxed_case = """ +import path.to.pkg as pkgname + +x = pkgname.Name + +schema Person: + name: str + age: int + +schema TestPerson: + name = "Alice" + age = 18 + person: Person = Person { + name: name + age: age + } + assert person.name == name + assert person.age == age +""" +fix_module_import_list_case = """ +import path.to.pkg as pkgname +import another.path.to.pkg as another_pkgname + +x = pkgname.Name + +schema Person[dataVar: pkgname.Data | another_pkgname.Version]: + name: str + age: int + data?: pkgname.Data | another_pkgname.Version = dataVar + +rule PersonCheck[data: pkgname.Data]: + data.id > 0 + +func = lambda x: pkgname.Data, y -> pkgname.Data { + x + y +} + +person = Person(pkgname.Data {id = 1}) { + name = "Alice" + age = 18 +} +var: pkgname.Data = pkgname.Data {} +type Data = pkgname.Data +""" + + +class TestFixQualifiedIdentifier(unittest.TestCase): + def test_fix_qualified_identifier(self): + module = parser.ParseFile( + MAIN_FILE, code=fix_test_schema_relaxed_case, pkg=MAIN_PKG + ) + fix.fix_qualified_identifier(module) + + +class TestFixAndGetModuleImportList(unittest.TestCase): + def test_fix_and_get_module_import_list(self): + module = parser.ParseFile( + MAIN_FILE, code=fix_module_import_list_case, pkg=MAIN_PKG + ) + self.assertIsInstance(module.body[0], ast.ImportStmt) + self.assertIsInstance(module.body[1], ast.ImportStmt) + self.assertIsInstance(module.body[2], ast.AssignStmt) + self.assertIsInstance(module.body[3], ast.SchemaStmt) + self.assertIsInstance(module.body[4], ast.RuleStmt) + self.assertIsInstance(module.body[5].value, ast.LambdaExpr) + self.assertIsInstance(module.body[6], ast.AssignStmt) + self.assertIsInstance(module.body[7], ast.AssignStmt) + self.assertIsInstance(module.body[8], ast.TypeAliasStmt) + schema_args = typing.cast(ast.SchemaStmt, module.body[3]).args + self.assertEqual(schema_args.GetArgType(0), "pkgname.Data|another_pkgname.Version") + import_list = fix.fix_and_get_module_import_list(ROOT_PATH, module) + self.assertEqual(len(import_list), 2) + self.assertEqual(import_list[0].path, "path.to.pkg") + self.assertEqual(import_list[0].asname, "pkgname") + self.assertEqual(schema_args.GetArgType(0), "@path.to.pkg.Data|@another.path.to.pkg.Version") + fix.fix_and_get_module_import_list(ROOT_PATH, module, reversed=True) + self.assertEqual(schema_args.GetArgType(0), "pkgname.Data|another_pkgname.Version") + + +class TestFixSchemaAutoRelaxed(unittest.TestCase): + def test_fix_test_schema_auto_relaxed_invalid(self): + module = parser.ParseFile("invalid_name.k", code=fix_test_schema_relaxed_case) + # Before fix + self.assertEqual(len(module.body), 4) + self.assertIsInstance(module.body[3], ast.SchemaStmt) + # After fix + fix.fix_test_schema_auto_relaxed(module) + self.assertEqual(len(module.body), 4) + self.assertIsInstance(module.body[3], ast.SchemaStmt) + + def test_fix_test_schema_auto_relaxed_normal(self): + module = parser.ParseFile("person_test.k", code=fix_test_schema_relaxed_case) + # Before fix + self.assertEqual(len(module.body), 4) + self.assertIsInstance(module.body[3], ast.SchemaStmt) + # After fix + fix.fix_test_schema_auto_relaxed(module) + self.assertEqual(len(module.body), 4) + self.assertIsInstance(module.body[3], ast.SchemaStmt) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/kcl.mod b/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/kcl.mod new file mode 100644 index 000000000..f4e7091a3 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/kcl.mod @@ -0,0 +1,3 @@ +[build] +enable_pkg_cache=true +cached_pkg_prefix="pkg." diff --git a/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/main.k b/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/main.k new file mode 100644 index 000000000..d6c10fcfb --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/main.k @@ -0,0 +1,3 @@ +import pkg + +a = pkg.a diff --git a/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/pkg/pkg.k b/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/pkg/pkg.k new file mode 100644 index 000000000..7650d717f --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/pkg/pkg.k @@ -0,0 +1,3 @@ +import pkg.pkg1 + +a = pkg1.a diff --git a/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/pkg/pkg1/pkg.k b/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/pkg/pkg1/pkg.k new file mode 100644 index 000000000..1f2c735b1 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/pkg/pkg1/pkg.k @@ -0,0 +1,3 @@ +import pkg.pkg1.pkg2 + +a = pkg2.a diff --git a/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/pkg/pkg1/pkg2/pkg.k b/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/pkg/pkg1/pkg2/pkg.k new file mode 100644 index 000000000..d25d49e0f --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/cache_expired_testdata/pkg/pkg1/pkg2/pkg.k @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/kcl.mod b/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/kcl.mod new file mode 100644 index 000000000..f4e7091a3 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/kcl.mod @@ -0,0 +1,3 @@ +[build] +enable_pkg_cache=true +cached_pkg_prefix="pkg." diff --git a/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/main.k b/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/main.k new file mode 100644 index 000000000..d6c10fcfb --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/main.k @@ -0,0 +1,3 @@ +import pkg + +a = pkg.a diff --git a/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/pkg/pkg.k b/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/pkg/pkg.k new file mode 100644 index 000000000..7650d717f --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/pkg/pkg.k @@ -0,0 +1,3 @@ +import pkg.pkg1 + +a = pkg1.a diff --git a/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/pkg/pkg1/pkg.k b/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/pkg/pkg1/pkg.k new file mode 100644 index 000000000..1f2c735b1 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/pkg/pkg1/pkg.k @@ -0,0 +1,3 @@ +import pkg.pkg1.pkg2 + +a = pkg2.a diff --git a/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/pkg/pkg1/pkg2/pkg.k b/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/pkg/pkg1/pkg2/pkg.k new file mode 100644 index 000000000..d25d49e0f --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/cache_testdata/pkg/pkg1/pkg2/pkg.k @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_compiler/test_build/hello.k b/test/test_units/test_kclvm/test_compiler/test_build/hello.k new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/defaults_not_full_invalid/main.k b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/defaults_not_full_invalid/main.k new file mode 100644 index 000000000..f7b78cc7e --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/defaults_not_full_invalid/main.k @@ -0,0 +1,7 @@ +schema A[a1 = 100, a2, a3]: + a: int = a1 + b: int = a2 + c: str = a3 + + +a = A(a1=1,a3="3") \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/kcl.mod b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/name_not_defined/main.k b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/name_not_defined/main.k new file mode 100644 index 000000000..453a53ca1 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/name_not_defined/main.k @@ -0,0 +1,2 @@ +a = b +b = 1 diff --git a/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/kcl.mod b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/main.k b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/main.k new file mode 100644 index 000000000..fd2b83aef --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/main.k @@ -0,0 +1,5 @@ +import module1 + +result0 = module1.module.data.num + +result1 = 100 diff --git a/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/module.k b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/module.k new file mode 100644 index 000000000..db9135a5f --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/module.k @@ -0,0 +1,6 @@ +schema Data: + num: int = 1 + +data = Data { + "num" = 100 +} diff --git a/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/module1.k b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/module1.k new file mode 100644 index 000000000..92211e140 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/module1.k @@ -0,0 +1 @@ +import module diff --git a/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/module2.k b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/module2.k new file mode 100644 index 000000000..92211e140 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/nest_import/module2.k @@ -0,0 +1 @@ +import module diff --git a/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/package_1.k b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/package_1.k new file mode 100644 index 000000000..3b2f8e656 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/package_1.k @@ -0,0 +1,3 @@ +import pkg + +pkg.a |= 1 diff --git a/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/package_2.k b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/package_2.k new file mode 100644 index 000000000..4fb05f3c3 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/package_2.k @@ -0,0 +1,3 @@ +import pkg + +pkg.a = 1 diff --git a/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/pkg/pkg.k b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/pkg/pkg.k new file mode 100644 index 000000000..1337a530c --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/invalid_testdata/pkg/pkg.k @@ -0,0 +1 @@ +a = 1 diff --git a/test/test_units/test_kclvm/test_compiler/test_build/scope_testdata/kcl.mod b/test/test_units/test_kclvm/test_compiler/test_build/scope_testdata/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_compiler/test_build/scope_testdata/main.k b/test/test_units/test_kclvm/test_compiler/test_build/scope_testdata/main.k new file mode 100644 index 000000000..f381b4608 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/scope_testdata/main.k @@ -0,0 +1,7 @@ +import pkg + +schema Sub(pkg.Base): + data?: int = 2 + +base: pkg.Base = pkg.Base {} as pkg.Base +sub: Sub = Sub {} as Sub diff --git a/test/test_units/test_kclvm/test_compiler/test_build/scope_testdata/pkg/pkg.k b/test/test_units/test_kclvm/test_compiler/test_build/scope_testdata/pkg/pkg.k new file mode 100644 index 000000000..cb44e19e9 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/scope_testdata/pkg/pkg.k @@ -0,0 +1,2 @@ +schema Base: + id?: int = 1 diff --git a/test/test_units/test_kclvm/test_compiler/test_build/scope_testdata/stdout.golden b/test/test_units/test_kclvm/test_compiler/test_build/scope_testdata/stdout.golden new file mode 100644 index 000000000..ff2709378 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/scope_testdata/stdout.golden @@ -0,0 +1,5 @@ +base: + id: 1 +sub: + id: 1 + data: 2 diff --git a/test/test_units/test_kclvm/test_compiler/test_build/test_build.py b/test/test_units/test_kclvm/test_compiler/test_build/test_build.py new file mode 100644 index 000000000..056b204dc --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/test_build.py @@ -0,0 +1,349 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import os +import shutil +import unittest +import pathlib + +import kclvm.api.object as objpkg +import kclvm.api.object.internal as obj_internal +import kclvm.kcl.ast as ast +import kclvm.kcl.types as types +import kclvm.compiler.vfs as vfs +from kclvm.kcl.error import KCLCompileException +from kclvm.kcl.error.kcl_error import IllegalArgumentSyntaxError +from kclvm.kcl.types import ProgramScope, Scope +from kclvm.compiler.parser import LoadProgram +from kclvm.compiler.build.compiler import RuntimeCode, CompileProgram, Compiler, SymbolScope +from kclvm.compiler.extension.builtin import get_builtin_func_objects +from kclvm.vm.code import Opcode, Label + +code_empty_list = ["""""", """"""] +code_simple_list = [ + """ +schema Person: + name: str + +a = 1 +""", + """ +b = a +person = Person { + name: "Alice" +} +""", +] +path = str(pathlib.Path(__file__).parent) + + +class KCLCompilerBuildTest(unittest.TestCase): + def test_runtime_code(self): + runtime_code = RuntimeCode( + names=[], + constants=[], + codes=[], + ) + self.assertEqual(runtime_code.type(), objpkg.KCLObjectType.RUNTIME_CODE) + self.assertEqual(runtime_code.type_str(), "runtime_code") + + def test_compile_program_empty(self): + compiled_program = CompileProgram(LoadProgram(path, k_code_list=code_empty_list)) + self.assertEqual(compiled_program.main, "__main__") + self.assertEqual(len(compiled_program.pkgs[compiled_program.main].names), 0) + self.assertEqual( + len(compiled_program.pkgs[compiled_program.main].constants), + len(get_builtin_func_objects()), + ) + + def test_compile_program_simple(self): + compiled_program = CompileProgram( + LoadProgram(path, k_code_list=code_simple_list) + ) + self.assertEqual(compiled_program.main, "__main__") + self.assertEqual(len(compiled_program.pkgs[compiled_program.main].names), 9) + + def test_compile_program_invalid(self): + testdata_path = pathlib.Path(__file__).parent.joinpath("invalid_testdata") + cases = testdata_path.glob("*.k") + for case in cases: + with self.assertRaises(KCLCompileException): + CompileProgram(LoadProgram(case, work_dir=str(testdata_path))) + + + def test_compile_program_invalid_nest_import(self): + testdata_path = pathlib.Path(__file__).parent.joinpath("invalid_testdata").joinpath("nest_import") + cases = testdata_path.glob("main.k") + for case in cases: + with self.assertRaises(KCLCompileException): + CompileProgram(LoadProgram(case, work_dir=str(testdata_path))) + + +class KCLCompilerWalkerFunctionTest(unittest.TestCase): + def setUp(self): + scope = ProgramScope(scope_map={"__main__": Scope()}) + self.fake_compiler = Compiler(scope) + self.fake_compiler.pkgpath = "__main__" + self.fake_compiler.pkg_scope = program_scope = types.ResolveProgram( + LoadProgram(path, k_code_list=code_simple_list) + ).main_scope + self.err_compiler = Compiler(scope) + self.err_compiler.scopes = None + super().setUp() + + def test_generic_compiler_functions(self): + # Expr + numberlit = ast.NumberLit(value=1) + # Stmt + exprstmt = ast.ExprStmt() + exprstmt.exprs = [numberlit] + # Module + module = ast.Module() + module.body = [exprstmt] + self.fake_compiler.generic_walk(module) + self.fake_compiler.generic_walk(exprstmt) + self.fake_compiler.generic_walk(numberlit) + self.fake_compiler.expr(None) + self.fake_compiler.stmt(None) + + def test_generic_compiler_functions_invalid(self): + with self.assertRaises(KCLCompileException): + self.fake_compiler.generic_walk(None) + with self.assertRaises(KCLCompileException): + self.fake_compiler.raise_err("Raise an error") + with self.assertRaises(KCLCompileException): + self.fake_compiler.change_operand(0, 100, 0) + with self.assertRaises(KCLCompileException): + self.err_compiler.leave_scope() + with self.assertRaises(KCLCompileException): + self.err_compiler.add_instruction([0]) + with self.assertRaises(Exception): + self.fake_compiler.make_func_with_content(None, None) + with self.assertRaises(AttributeError): + self.err_compiler.enter_scope() + + def test_emit_call_invalid(self): + keyword = ast.Keyword() + keyword.arg = ast.Identifier(names=["x"]) + keyword.value = ast.NumberLit(value=1) + keywords = [keyword, keyword] + with self.assertRaises(KCLCompileException): + self.fake_compiler.emit_call([], keywords) + + def test_set_jmp_invalid(self): + op, label = Opcode.NOP, Label() + with self.assertRaises(KCLCompileException): + self.fake_compiler.set_jmp(op, label) + + def test_op_decorator(self): + keyword = ast.Keyword() + keyword.arg = ast.Identifier(names=["x"]) + keyword.value = ast.NumberLit(value=1) + args = ast.CallExpr() + args.args = [] + args.keywords = [keyword] + cases = [ + { + "name": "Deprecated", + "key": "Person", + "args": None, + "target": obj_internal.DecoratorTargetType.SCHEMA_TYPE, + }, + { + "name": "Deprecated", + "key": "Person", + "args": args, + "target": obj_internal.DecoratorTargetType.SCHEMA_TYPE, + }, + ] + for case in cases: + name, key, args, target = case["name"], case["key"], case["args"], case["target"] + self.fake_compiler.op_decorator(name, key, args, target) + + def test_op_decorator_invalid(self): + keyword = ast.Keyword() + keyword.arg = ast.Identifier(names=["x"]) + keyword.value = ast.NumberLit(value=1) + args = ast.CallExpr() + args.args = [] + args.keywords = [keyword, keyword] + cases = [ + { + "name": "Deprecated", + "key": "Person", + "args": args, + "target": obj_internal.DecoratorTargetType.SCHEMA_TYPE, + }, + { + "name": None, + "key": "Person", + "args": args, + "target": obj_internal.DecoratorTargetType.SCHEMA_TYPE, + }, + ] + for case in cases: + name, key, args, target = case["name"], case["key"], case["args"], case["target"] + with self.assertRaises(KCLCompileException): + self.fake_compiler.op_decorator(name, key, args, target) + + def test_store_symbol(self): + self.fake_compiler.store_symbol("key") + with self.assertRaises(KCLCompileException): + self.fake_compiler.store_symbol("key") + self.fake_compiler.store_symbol("_key", scope=SymbolScope.INTERNAL) + self.fake_compiler.store_symbol("_key", scope=SymbolScope.INTERNAL) + + def test_load_symbol(self): + with self.assertRaises(KCLCompileException): + self.fake_compiler.load_symbol(None) + with self.assertRaises(KCLCompileException): + self.fake_compiler.load_symbol("_err_test_key") + + def test_walk_module(self): + module = ast.Module() + self.fake_compiler.walk_Module(module) + + def test_walk_expr_stmt(self): + expr_stmt = ast.ExprStmt() + expr_stmt.exprs = [ast.NumberLit(value=1)] + self.fake_compiler.walk_ExprStmt(expr_stmt) + self.fake_compiler._is_in_schema_stmt.append(True) + self.fake_compiler.walk_ExprStmt(expr_stmt) + self.fake_compiler._is_in_schema_stmt.pop() + + def test_walk_schema_stmt_invalid(self): + schema_stmt = ast.SchemaStmt() + schema_stmt.pkgpath = "__main__" + schema_stmt.name = "Person" + schema_stmt.decorators = [ast.Decorator()] + with self.assertRaises(AttributeError): + self.fake_compiler.walk_SchemaStmt(schema_stmt) + # Invalid schema mixin name + name = ast.Identifier(names=["pkg", "MixinError"]) + name.pkgpath = "@pkg" + schema_stmt.mixins = [name] + with self.assertRaises(KCLCompileException): + self.fake_compiler.walk_SchemaStmt(schema_stmt) + # Invalid schema parent name + schema_stmt.parent_name = ast.Identifier(names=["PersonMixin"]) + with self.assertRaises(KCLCompileException): + self.fake_compiler.walk_SchemaStmt(schema_stmt) + + def test_walk_schema_attr_invalid(self): + schema_attr = ast.SchemaAttr() + schema_attr.decorators = [ast.Decorator()] + with self.assertRaises(AttributeError): + self.fake_compiler.walk_SchemaAttr(schema_attr) + + def test_make_func_with_content_invalid_by_defaults(self): + file_path = str(pathlib.Path(__file__).parent.joinpath("invalid_testdata/defaults_not_full_invalid/main.k")) + try: + CompileProgram(LoadProgram(file_path)) + except IllegalArgumentSyntaxError as err: + self.assertEqual(err.arg_msg, "non-default argument follows default argument") + + def test_walk_index_signature_invalid(self): + with self.assertRaises(KCLCompileException): + t = ast.SchemaIndexSignature() + t.key_type = "err_str" + self.fake_compiler.walk_SchemaIndexSignature(t) + + def test_walk_unary_expr_invalid(self): + unary_expr = ast.UnaryExpr() + with self.assertRaises(KCLCompileException): + self.fake_compiler.walk_UnaryExpr(unary_expr) + + def test_walk_binary_expr(self): + bin_expr = ast.BinaryExpr() + bin_expr.left = ast.NumberLit(value=1) + bin_expr.right = ast.NumberLit(value=1) + bin_expr.op = ast.BinOp.Add + self.fake_compiler.walk_BinaryExpr(bin_expr) + bin_expr.op = ast.CmpOp.Eq + self.fake_compiler.walk_BinaryExpr(bin_expr) + bin_expr.op = ast.UnaryOp.Invert + with self.assertRaises(KCLCompileException): + self.fake_compiler.walk_BinaryExpr(bin_expr) + + def test_walk_selector_expr(self): + selector_expr = ast.SelectorExpr() + key = ast.Identifier(names=["selector_key"]) + key.ctx = ast.ExprContext.STORE + self.fake_compiler.walk_Identifier(key) + key.ctx = ast.ExprContext.LOAD + selector_expr.value = key + selector_expr.attr = ast.Identifier(names=["key"]) + selector_expr.ctx = ast.ExprContext.LOAD + self.fake_compiler.walk_SelectorExpr(selector_expr) + selector_expr.has_question = True + self.fake_compiler.walk_SelectorExpr(selector_expr) + selector_expr.ctx = ast.ExprContext.AUGLOAD + self.fake_compiler.walk_SelectorExpr(selector_expr) + selector_expr.ctx = ast.ExprContext.AUGSTORE + self.fake_compiler.walk_SelectorExpr(selector_expr) + + def test_walk_subscript(self): + subscript = ast.Subscript() + subscript.value = ast.StringLit(value="123") + subscript.index = ast.NumberLit(value=0) + subscript.has_question = True + self.fake_compiler.walk_Subscript(subscript) + + +class KCLCompilerProgramScopeTest(unittest.TestCase): + def test_get_type_from_identifier(self): + file_path = str(pathlib.Path(__file__).parent.joinpath("scope_testdata/main.k")) + scope = types.ResolveProgram( + LoadProgram(file_path) + ) + compiler = Compiler(scope) + self.assertIsInstance(compiler.get_type_from_identifier(None), objpkg.KCLAnyTypeObject) + identifier = ast.Identifier(names=["Sub"]) + self.assertIsInstance(compiler.get_type_from_identifier(identifier), objpkg.KCLSchemaDefTypeObject) + del compiler.pkg_scope.elems["Sub"] + self.assertIsInstance(compiler.get_type_from_identifier(identifier), objpkg.KCLAnyTypeObject) + identifier = ast.Identifier(names=["pkg", "Base"]) + self.assertIsInstance(compiler.get_type_from_identifier(identifier), objpkg.KCLAnyTypeObject) + identifier.pkgpath = "@pkg" + self.assertIsInstance(compiler.get_type_from_identifier(identifier), objpkg.KCLSchemaDefTypeObject) + compiler.pkg_scope.elems["@pkg"].type = None + self.assertIsInstance(compiler.get_type_from_identifier(identifier), objpkg.KCLAnyTypeObject) + with self.assertRaises(KCLCompileException): + compiler.get_type_from_identifier(ast.Identifier(names=["pkg", "to", "type"])) + + +class KCLCompilerBuildCacheTest(unittest.TestCase): + def test_compile_cache(self): + cache_path = str(pathlib.Path(__file__).parent.joinpath("cache_testdata/.kclvm/")) + file_path = str(pathlib.Path(__file__).parent.joinpath("cache_testdata/main.k")) + compiled_program = CompileProgram(LoadProgram(file_path)) + compiled_program = CompileProgram(LoadProgram(file_path)) + if os.path.exists(cache_path): + shutil.rmtree(cache_path) + + def test_compile_expired_cache(self): + test_data_path_name = "cache_expired_testdata" + rename_test_data_path = "rename_cache_expired_testdata" + root = str(pathlib.Path(__file__).parent.joinpath(test_data_path_name)) + cache_path = str(pathlib.Path(__file__).parent.joinpath(f"{test_data_path_name}/.kclvm/")) + file_path = str(pathlib.Path(__file__).parent.joinpath(f"{test_data_path_name}/main.k")) + rename_test_data_path = str(pathlib.Path(__file__).parent.joinpath(rename_test_data_path)) + ast_program = LoadProgram(file_path) + compiled_program = CompileProgram(ast_program) + cached_ast = vfs.LoadPkgCache(root, "pkg.pkg1") + cached_bytecode = vfs.LoadBytecodeCache(root, ast_program) + self.assertIsNotNone(cached_ast) + self.assertIsNotNone(cached_bytecode) + # Rename root to test cache expired + os.rename(root, rename_test_data_path) + cached_ast = vfs.LoadPkgCache(rename_test_data_path, "pkg.pkg1") + cached_bytecode = vfs.LoadBytecodeCache(rename_test_data_path, ast_program) + self.assertIsNotNone(cached_ast) + self.assertIsNotNone(cached_bytecode) + # Clear temp file + os.rename(rename_test_data_path, root) + if os.path.exists(cache_path): + shutil.rmtree(cache_path) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_build/test_utils/test_units.py b/test/test_units/test_kclvm/test_compiler/test_build/test_utils/test_units.py new file mode 100644 index 000000000..7eb1497ae --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_build/test_utils/test_units.py @@ -0,0 +1,141 @@ +import unittest +from kclvm.compiler.build.utils import units + + +class UnitsTest(unittest.TestCase): + + def test_cal_num_si(self) -> None: + """ + Test cal_num function with SI suffix + """ + cases = [ + {"value": 1, "suffix": "n", "expected": 1e-09}, + {"value": 1, "suffix": "u", "expected": 1e-06}, + {"value": 1, "suffix": "m", "expected": 0.001}, + {"value": 1, "suffix": "", "expected": 1}, + {"value": 1, "suffix": "k", "expected": 1_000}, + {"value": 1, "suffix": "K", "expected": 1_000}, + {"value": 1, "suffix": "M", "expected": 1_000_000}, + {"value": 1, "suffix": "G", "expected": 1_000_000_000}, + {"value": 1, "suffix": "T", "expected": 1_000_000_000_000}, + {"value": 1, "suffix": "P", "expected": 1_000_000_000_000_000}, + ] + + for case in cases: + real = units.cal_num(case["value"], case["suffix"]) + self.assertEqual(real, case["expected"]) + + def test_cal_num_iec(self) -> None: + """ + Test cal_num function with IEC suffix + """ + cases = [ + {"value": 1, "suffix": "Ki", "expected": 1024}, + {"value": 1, "suffix": "Mi", "expected": 1024 ** 2}, + {"value": 1, "suffix": "Gi", "expected": 1024 ** 3}, + {"value": 1, "suffix": "Ti", "expected": 1024 ** 4}, + {"value": 1, "suffix": "Pi", "expected": 1024 ** 5}, + ] + + for case in cases: + real = units.cal_num(case["value"], case["suffix"]) + self.assertEqual(real, case["expected"]) + + def test_cal_num_invalid_suffix(self) -> None: + """ + Test cal_num function with invalid suffix + """ + cases = [ + {"value": 1, "suffix": "x"}, + {"value": 1, "suffix": "ki"}, + {"value": 1, "suffix": "mi"}, + {"value": 1, "suffix": "ui"}, + {"value": 1, "suffix": "ni"}, + ] + + for case in cases: + exception = None + + try: + units.cal_num(case["value"], case["suffix"]) + except ValueError as e: + exception = e + self.assertEqual(str(e), f"Invalid suffix { case['suffix'] }") + + if not exception: + self.assertFalse(True, f"ValueError should be thrown, case: {case['suffix']}") + + def test_to_quantity_si(self) -> None: + """ + Test to_quantity function with SI suffix + """ + cases = [ + {"quantity": "1n", "expected": 1e-09}, + {"quantity": "1u", "expected": 1e-06}, + {"quantity": "1m", "expected": 0.001}, + {"quantity": "1", "expected": 1}, + {"quantity": "1k", "expected": 1_000}, + {"quantity": "1K", "expected": 1_000}, + {"quantity": "1M", "expected": 1_000_000}, + {"quantity": "1G", "expected": 1_000_000_000}, + {"quantity": "1T", "expected": 1_000_000_000_000}, + {"quantity": "1P", "expected": 1_000_000_000_000_000}, + ] + + for case in cases: + real = units.to_quantity(case["quantity"]) + self.assertEqual(real, case["expected"]) + + def test_to_quantity_iec(self) -> None: + """ + Test to_quantity function with SI suffix + """ + cases = [ + {"quantity": "1Ki", "expected": 1024}, + {"quantity": "1Mi", "expected": 1024 ** 2}, + {"quantity": "1Gi", "expected": 1024 ** 3}, + {"quantity": "1Ti", "expected": 1024 ** 4}, + {"quantity": "1Pi", "expected": 1024 ** 5}, + ] + + for case in cases: + real = units.to_quantity(case["quantity"]) + self.assertEqual(real, case["expected"]) + + def test_to_quantity_invalid_suffix(self) -> None: + """ + Test to_quantity function with invalid quantity + """ + cases = [ + {"quantity": "1x"}, + {"quantity": "1ki"}, + {"quantity": "1mi"}, + {"quantity": "1ui"}, + {"quantity": "1ni"}, + {"quantity": "1Kii"}, + {"quantity": "x"}, + ] + + for case in cases: + try: + units.to_quantity(case["quantity"]) + except ValueError as e: + self.assertEqual(str(e), "invalid literal for int() with base 10: '{}'".format(case["quantity"])) + + cases = [ + {"quantity": ""}, + {"quantity": "ki"}, + {"quantity": "mi"}, + {"quantity": "ui"}, + {"quantity": "ni"}, + ] + + for case in cases: + try: + units.to_quantity(case["quantity"]) + except ValueError as e: + self.assertEqual(str(e), "Number can't be empty") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/assert.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/assert.k new file mode 100644 index 000000000..8c4a2fab9 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/assert.k @@ -0,0 +1,5 @@ +assert True +assert 1 == 1 if True +_x = "good case" +assert _x == "good case" if _x, "_x need to be 'good case'" +x = _x diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/assert.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/assert.yaml new file mode 100644 index 000000000..20ca90b92 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/assert.yaml @@ -0,0 +1 @@ +x: good case diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/aug_assign.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/aug_assign.k new file mode 100644 index 000000000..4120f37a1 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/aug_assign.k @@ -0,0 +1,31 @@ +schema Name: + first: str + last?: str + age?: int + +schema A: + name: Name + +schema Data: + _alice = A { + name: { + first: "aa" + age: 1 + } + } + + _alice.name |= {last: "value"} + _alice.name.age += 1 + alice = _alice + +data = Data {} +_alice = A { + name: { + first: "aa" + age: 1 + } +} + +_alice.name |= {last: "value"} +_alice.name.age += 1 +alice = _alice diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/aug_assign.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/aug_assign.yaml new file mode 100644 index 000000000..1c52b662c --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/aug_assign.yaml @@ -0,0 +1,11 @@ +data: + alice: + name: + first: aa + last: value + age: 2 +alice: + name: + first: aa + last: value + age: 2 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/calculation.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/calculation.k new file mode 100644 index 000000000..838ddf065 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/calculation.k @@ -0,0 +1,40 @@ +a = 1 + 1 +b = 1 - 1 +c = 1 * 1 +d = 1 / 1 +e = 1 // 1 +f = 1 % 1 +g = 1 >> 1 +h = 1 << 1 +i = 1 ** 1 +j = 1 and 1 +k = 1 or 1 +l = 1 & 1 +m = 1 | 1 +n = 1 ^ 1 + +_o = 1 +_o += 1 +_o -= 1 +_o /= 1 +_o //= 1 +_o %= 1 +_o = int(_o) +_o >>= 1 +_o <<= 1 +_o **= 1 +_o &= 1 +_o |= 1 +_o ^= 1 +o = _o + +p = 1 == 1 +q = 1 != 1 +r = 1 < 1 +s = 1 <= 1 +t = 1 > 1 +u = 1 >= 1 +v = 1 is 1 +w = 1 is not 1 +x = 1 in [1] +y = 1 not in [1] diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/calculation.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/calculation.yaml new file mode 100644 index 000000000..c1b56e73b --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/calculation.yaml @@ -0,0 +1,25 @@ +a: 2 +b: 0 +c: 1 +d: 1.0 +e: 1 +f: 0 +g: 0 +h: 2 +i: 1 +j: 1 +k: 1 +l: 1 +m: 1 +n: 0 +o: 0 +p: true +q: false +r: false +s: true +t: false +u: true +v: true +w: false +x: true +y: false diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/collection_if.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/collection_if.k new file mode 100644 index 000000000..3449107fd --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/collection_if.k @@ -0,0 +1,61 @@ +schema Config: + name: str + env: str + +env = "env" + +data1 = Config { + if env == "env": + name: env + env: env + else: + name: "name" + env: "name" +} + +data2 = Config { + if env != "env": + name: env + env: env + else: + name: "name" + env: "name" +} + +data3 = { + if True: + key1: "value1" + elif True: + key2: "value2" + elif True: + key3: "value3" + else: + key4: "value4" +} + +data4 = [ + if True: + "value1" + elif True: + "value2" + elif True: + "value3" + else: + "value4" +] +data5 = [ + if True: "value1" +] +data6 = { + if True: key: "value1" +} +data7 = [ + if False: *[0] + elif True: *[1] + else: *[2] +] +data8 = { + if False: **{k1: "v1"} + elif False: **{k2: "v2"} + else: **{k3: "v3"} +} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/collection_if.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/collection_if.yaml new file mode 100644 index 000000000..3780384df --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/collection_if.yaml @@ -0,0 +1,19 @@ +env: env +data1: + name: env + env: env +data2: + name: name + env: name +data3: + key1: value1 +data4: +- value1 +data5: +- value1 +data6: + key: value1 +data7: +- 1 +data8: + k3: v3 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/compare.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/compare.k new file mode 100644 index 000000000..45270da0b --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/compare.k @@ -0,0 +1,2 @@ +x0 = 1 < 2 < 3 +x1 = 1 > 2 > 3 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/compare.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/compare.yaml new file mode 100644 index 000000000..d43c311a2 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/compare.yaml @@ -0,0 +1,2 @@ +x0: true +x1: false diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/complex.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/complex.k new file mode 100644 index 000000000..7e2e32b06 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/complex.k @@ -0,0 +1,42 @@ +import math + +name = "Alice" +schema Base: + hc: int = 3 + key: str = name + +schema Person(Base): + name: str = "Alice" + age: int = 18 + labels: {str:str} = {"key": "value", "ageLabel": "ageVal " + str(age)} + info: [int|str] = [name, age] + +person1 = Person { + "name" = "Bob" + "age" = 16 +} +ceil_val = math.ceil(1.1) +a = 1.1 + 1.1 + 6.6 + 3.2 + abs(-1.2) +b = {"key1": "value1", "key2": "value2"} +attr = b.key1 + person1.name +c = 3 - 2 * 3 / 4 +d = a + 2 +e = a + 2 +f = "ABC" + "234{}" +p = "ABC"[::-1] +ff = f.format("123") +fff = ff.lower() +q = "1" * 12 +g = True +l = [1, 2, 3, attr + "value1"] +ll = [*l, 1] +_e = 1 + 1 +aug1 = aug2 = 4 + 3 +data = [1, 2, 3, 4] +lcomp = [_d * 2 for _d in data for _d in data] +dcomp = {dd: dd * 2 for dd in data} +data0 = data[0] +data12 = data[::-1] +pk = "ABC"[::-1] +qk = [1, 2, 3][::-1] +dict_data = {**person1, **{"key" = "value"}, "key2": "value2"} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/complex.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/complex.yaml new file mode 100644 index 000000000..d4f74014a --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/complex.yaml @@ -0,0 +1,90 @@ +name: Alice +person1: + hc: 3 + key: Bob + name: Bob + age: 16 + labels: + key: value + ageLabel: ageVal 16 + info: + - Bob + - 16 +ceil_val: 2 +a: 13.2 +b: + key1: value1 + key2: value2 +attr: value1Bob +c: 1.5 +d: 15.2 +e: 15.2 +f: ABC234{} +p: CBA +ff: ABC234123 +fff: abc234123 +q: '111111111111' +g: true +l: +- 1 +- 2 +- 3 +- value1Bobvalue1 +ll: +- 1 +- 2 +- 3 +- value1Bobvalue1 +- 1 +aug1: 7 +aug2: 7 +data: +- 1 +- 2 +- 3 +- 4 +lcomp: +- 2 +- 4 +- 6 +- 8 +- 2 +- 4 +- 6 +- 8 +- 2 +- 4 +- 6 +- 8 +- 2 +- 4 +- 6 +- 8 +dcomp: + 1: 2 + 2: 4 + 3: 6 + 4: 8 +data0: 1 +data12: +- 4 +- 3 +- 2 +- 1 +pk: CBA +qk: +- 3 +- 2 +- 1 +dict_data: + hc: 3 + key: value + name: Bob + age: 16 + labels: + key: value + ageLabel: ageVal 16 + info: + - Bob + - 16 + key2: value2 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/config.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/config.k new file mode 100644 index 000000000..4cc6d7dc8 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/config.k @@ -0,0 +1,18 @@ +data = { + key1: [0] + key1 += [1] + key2: [0] + key2 = [1] + key3 = [0] + key3 = [1] +} + +schema Config: + data: [int] + env: [{str:}] = [{key1: 1}, {key2: 2}] + +config = Config { + data = [1] + data += [2] + env[0]: {key2: 2} +} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/config.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/config.yaml new file mode 100644 index 000000000..d3f9a6470 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/config.yaml @@ -0,0 +1,16 @@ +data: + key1: + - 0 + - 1 + key2: + - 1 + key3: + - 1 +config: + data: + - 1 + - 2 + env: + - key1: 1 + key2: 2 + - key2: 2 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/convert_collection_value.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/convert_collection_value.k new file mode 100644 index 000000000..b3965435f --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/convert_collection_value.k @@ -0,0 +1,19 @@ +schema Node: + name: str + +schema AdvancedConfig: + id?: int + +schema Config: + nodes: [Node] + advancedConfig: AdvancedConfig + +config = Config { + nodes: [ + {name: "node1"} + {name: "node2"} + ] + advancedConfig: { + id: 1 + } +} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/convert_collection_value.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/convert_collection_value.yaml new file mode 100644 index 000000000..a94581b2f --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/convert_collection_value.yaml @@ -0,0 +1,6 @@ +config: + nodes: + - name: node1 + - name: node2 + advancedConfig: + id: 1 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/emit_expr.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/emit_expr.k new file mode 100644 index 000000000..f1ad59941 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/emit_expr.k @@ -0,0 +1,15 @@ +schema Config: + __settings__: {str:str} = { + "output_type": "STANDALONE" + } + id?: int + value?: str + +Config { + id: 1 + value: "value1" +} +Config { + id: 2 + value: "value2" +} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/emit_expr.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/emit_expr.yaml new file mode 100644 index 000000000..762bd5387 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/emit_expr.yaml @@ -0,0 +1,5 @@ +id: 1 +value: value1 +--- +id: 2 +value: value2 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/empty.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/empty.k new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/empty.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/empty.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/expr.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/expr.k new file mode 100644 index 000000000..c91f75ccb --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/expr.k @@ -0,0 +1,14 @@ +a = 1 +b = 2.2 +c = True +d = "ABC" +e = 'cba' +f = [1, 2, 3] +1 +2.2e3 +False +None +Undefined +"""123""" +myDict = {"key": "value"} +myDictNew = {**myDict, "key" = Undefined} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/expr.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/expr.yaml new file mode 100644 index 000000000..d2f330827 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/expr.yaml @@ -0,0 +1,12 @@ +a: 1 +b: 2.2 +c: true +d: ABC +e: cba +f: +- 1 +- 2 +- 3 +myDict: + key: value +myDictNew: {} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/for.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/for.k new file mode 100644 index 000000000..16ec9db50 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/for.k @@ -0,0 +1,7 @@ +x0 = [i for i in [1, 2, 3] if i > 1] +x1 = [i if i > 1 else i + 1 for i in [1, 2, 3]] +x2 = [i + j for i, j in [1, 2, 3]] + +x3 = {k: k for k in {key1: "value1", key2: "value2"}} +x4 = {k: k for k in {key1: "value1", key2: "value2"} if k == "key1"} +x5 = {k: v for k, v in {key1: "value1", key2: "value2"}} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/for.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/for.yaml new file mode 100644 index 000000000..a733b7306 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/for.yaml @@ -0,0 +1,19 @@ +x0: +- 2 +- 3 +x1: +- 2 +- 2 +- 3 +x2: +- 1 +- 3 +- 5 +x3: + key1: key1 + key2: key2 +x4: + key1: key1 +x5: + key1: value1 + key2: value2 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/if.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/if.k new file mode 100644 index 000000000..4d1bcc7a6 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/if.k @@ -0,0 +1,27 @@ +a = 1 if True else 2 +b = 2 if False else 3 +c = 4 if a else b +ok = 3 if False else (2 if True else 4) + +_a = 1 +if True: + if True: + _a = 2 + elif True: + _a = 3 + else: + _a = 4 +else: + _a = 5 + +schema Data: + id?: int = 1 + if True: + if True: + id = 2 + elif True: + id = 3 + else: + id = 4 + else: + id = 5 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/if.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/if.yaml new file mode 100644 index 000000000..e750014d1 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/if.yaml @@ -0,0 +1,4 @@ +a: 1 +b: 3 +c: 4 +ok: 2 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/index_signature.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/index_signature.k new file mode 100644 index 000000000..57cc0ae1b --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/index_signature.k @@ -0,0 +1,10 @@ +schema DataMap: + [name: str]: str + + check: + name.islower() + +data = DataMap { + "key1": "1" + "key2": "2" +} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/index_signature.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/index_signature.yaml new file mode 100644 index 000000000..72dc0ed60 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/index_signature.yaml @@ -0,0 +1,3 @@ +data: + key1: '1' + key2: '2' diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/kcl.mod b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/lambda.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/lambda.k new file mode 100644 index 000000000..640b945c2 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/lambda.k @@ -0,0 +1,36 @@ +sumFunc1 = lambda x, y { + x + y +} +sumFunc2 = lambda x, y = 1 { + x + y +} +sumFunc3 = lambda x = 1, y: int = 1 { + x + y +} +sumFunc4 = lambda x: int = 1, y: int = 1 -> int { + x + y +} +x0 = sumFunc1(1, 2) +x1 = sumFunc1(2, 3) +x2 = sumFunc1(3, 4) +x3 = sumFunc1(4, 5) + +schema Data: + var: int = 1 + _func = lambda x: int | str, y: int | str { + (lambda x, y { + int(x) + int(y) + var + })(x, y) + } + + a = _func(1, 1) + b = _func("123", "456") + +data = Data() + +result = (lambda x: int, y: int -> int { + a = 1 + (lambda { + x + y + a + 1 + })() +})(1, 1) diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/lambda.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/lambda.yaml new file mode 100644 index 000000000..036eadb05 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/lambda.yaml @@ -0,0 +1,9 @@ +x0: 3 +x1: 5 +x2: 7 +x3: 9 +data: + var: 1 + a: 3 + b: 580 +result: 4 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/line_continue.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/line_continue.k new file mode 100644 index 000000000..b7d5379f9 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/line_continue.k @@ -0,0 +1 @@ +\ \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/line_continue.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/line_continue.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/list.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/list.k new file mode 100644 index 000000000..07b0eeb1a --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/list.k @@ -0,0 +1,12 @@ +schema Data: + __settings__: {str:} = { + "output_type": "STANDALONE" + } + id?: int + name: str + +x0 = [[{a: 1, b: Undefined}]] +x1 = [[[{a: 1, b: Undefined}]]] +x2 = [[{a: 1, b: None}]] +x3 = [[[{a: 1, b: None}]]] +x4 = [[[1, Data { name = "data" }]]] diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/list.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/list.yaml new file mode 100644 index 000000000..067a879c4 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/list.yaml @@ -0,0 +1,14 @@ +x0: +- - a: 1 +x1: +- - - a: 1 +x2: +- - a: 1 + b: null +x3: +- - - a: 1 + b: null +x4: +- - - 1 +--- +name: data diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/member_ship.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/member_ship.k new file mode 100644 index 000000000..e02800697 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/member_ship.k @@ -0,0 +1,7 @@ +schema Data: + key: str = "value" + +result1 = "key" in {key: "value"} +result2 = "key" not in Data {} +result3 = "key" is None +result4 = "key" is not Undefined diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/member_ship.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/member_ship.yaml new file mode 100644 index 000000000..b2407e839 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/member_ship.yaml @@ -0,0 +1,4 @@ +result1: true +result2: false +result3: false +result4: true diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/nest_var.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/nest_var.k new file mode 100644 index 000000000..c83c0c39a --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/nest_var.k @@ -0,0 +1,15 @@ +a = { + k1.k2.k3: 1 +} +b = { + k1.k2: { + k3.k4: 2 + } +} +c = { + k1: { + k2.k3: { + k4: 3 + } + } +} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/nest_var.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/nest_var.yaml new file mode 100644 index 000000000..d6b3d6e45 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/nest_var.yaml @@ -0,0 +1,14 @@ +a: + k1: + k2: + k3: 1 +b: + k1: + k2: + k3: + k4: 2 +c: + k1: + k2: + k3: + k4: 3 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/plugin.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/plugin.k new file mode 100644 index 000000000..3257efaaa --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/plugin.k @@ -0,0 +1,3 @@ +import kcl_plugin.hello + +x = hello.add(1, 1) diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/plugin.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/plugin.yaml new file mode 100644 index 000000000..ad932ccf5 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/plugin.yaml @@ -0,0 +1 @@ +x: 2 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/plus.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/plus.k new file mode 100644 index 000000000..fe8d341bb --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/plus.k @@ -0,0 +1,10 @@ +schema Person: + name: str = "Alice" + count: int = 18 + +_persons = [Person {}] + [Person {}] +_persons += [Person {}] +_data = "123" + "456" +_data += "789" +persons = _persons +data = _data diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/plus.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/plus.yaml new file mode 100644 index 000000000..51667d2c2 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/plus.yaml @@ -0,0 +1,8 @@ +persons: +- name: Alice + count: 18 +- name: Alice + count: 18 +- name: Alice + count: 18 +data: '123456789' diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/quant_expr.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/quant_expr.k new file mode 100644 index 000000000..d3446e200 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/quant_expr.k @@ -0,0 +1,24 @@ +x0 = map i in [1, 2, 3] { + i + 1 if i > 1 +} +x1 = filter i in [1, 2, 3] { + i == 1 if i +} +x2 = all i in [1, 2, 3] { + i > 1 +} +x3 = any i in [1, 2, 3] { + i > 1 +} +x4 = filter i, e in [{"name": "1", "value": 1}, {"name":"2", "value": 2}] { + int(e.value) > 1 and i > 0 +} +x5 = filter i, e in [{"name": "1", "value": 1}, {"name":"2", "value": 2}] { + int(e.value) >= 1 and i == 0 +} +x6 = filter k, v in { "a": "foo", "b": "bar" } { + k == "a" +} +x7 = filter k, v in { "a": "foo", "b": "bar" } { + k == "a" and v == "foo" +} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/quant_expr.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/quant_expr.yaml new file mode 100644 index 000000000..ed01ebb33 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/quant_expr.yaml @@ -0,0 +1,17 @@ +x0: +- 3 +- 4 +x1: +- 1 +x2: false +x3: true +x4: +- name: '2' + value: 2 +x5: +- name: '1' + value: 1 +x6: + a: foo +x7: + a: foo diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/regex.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/regex.k new file mode 100644 index 000000000..77bcb4537 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/regex.k @@ -0,0 +1,9 @@ +import regex + +schema Data: + name: str = "name" + + check: + regex.match(name, "name") + +data = Data {} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/regex.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/regex.yaml new file mode 100644 index 000000000..854c9927a --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/regex.yaml @@ -0,0 +1,2 @@ +data: + name: name diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/rule.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/rule.k new file mode 100644 index 000000000..2822d2e80 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/rule.k @@ -0,0 +1,15 @@ +age = 1 + +@deprecated +rule DeprecatedRule: + age == 0 + +schema RuleProtocol: + age: int + +rule Base: + age > 0 + +rule Main(Base) for RuleProtocol: + +Main {} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/rule.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/rule.yaml new file mode 100644 index 000000000..059b61de3 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/rule.yaml @@ -0,0 +1 @@ +age: 1 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/schema.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/schema.k new file mode 100644 index 000000000..86878edaf --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/schema.k @@ -0,0 +1,40 @@ +name = "Alice" +schema Base: + hc: int = 3 + key: str = name + + check: + hc > 0 + +schema Person(Base): + name: str = "Alice" + age: int = 18 + labels: {str:str} = {"key": "value", "ageLabel": "ageVal " + str(age)} + info: [int|str] = [name, age] + +person1 = Person { + "name" = "Bob" + "age" = 16 +} + +person2 = Person() + +person3 = person1 { + name = "Alice" + age = 18 +} + +schema InfoMixin: + info |= {"age": age} if age else {} + +schema Info: + mixin [InfoMixin] + name?: str + age?: int = None + info?: {str: int|str} + +info = Info { + name: "alice" + age: 10 + info.gender: "girl" +} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/schema.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/schema.yaml new file mode 100644 index 000000000..c7fd865cd --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/schema.yaml @@ -0,0 +1,40 @@ +name: Alice +person1: + hc: 3 + key: Bob + name: Bob + age: 16 + labels: + key: value + ageLabel: ageVal 16 + info: + - Bob + - 16 +person2: + hc: 3 + key: Alice + name: Alice + age: 18 + labels: + key: value + ageLabel: ageVal 18 + info: + - Alice + - 18 +person3: + hc: 3 + key: Alice + name: Alice + age: 18 + labels: + key: value + ageLabel: ageVal 18 + info: + - Alice + - 18 +info: + name: alice + age: 10 + info: + gender: girl + age: 10 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/schema_args.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/schema_args.k new file mode 100644 index 000000000..201d9c0eb --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/schema_args.k @@ -0,0 +1,9 @@ +schema Data[arg1: int, arg2: int = 2]: + id1: int = arg1 + id2: int = arg2 + +data0 = Data(1, 2) +data1 = Data(arg1=1, arg2=2) +data2 = Data(1, arg2 = 2) +data3 = Data(arg2=2, arg1=1) +data4 = Data(1) diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/schema_args.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/schema_args.yaml new file mode 100644 index 000000000..da5ff0d10 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/schema_args.yaml @@ -0,0 +1,15 @@ +data0: + id1: 1 + id2: 2 +data1: + id1: 1 + id2: 2 +data2: + id1: 1 + id2: 2 +data3: + id1: 1 + id2: 2 +data4: + id1: 1 + id2: 2 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/str.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/str.k new file mode 100644 index 000000000..624cf6d1f --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/str.k @@ -0,0 +1,4 @@ +hello = "Hello" +world = "World" +hello_world = "Hello ${world}" +hello_world_2 = "Hello ${world} ${hello_world}" diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/str.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/str.yaml new file mode 100644 index 000000000..880991697 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/str.yaml @@ -0,0 +1,4 @@ +hello: Hello +world: World +hello_world: Hello World +hello_world_2: Hello World Hello World diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/type_alias.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/type_alias.k new file mode 100644 index 000000000..9924443d4 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/type_alias.k @@ -0,0 +1,18 @@ +type Color = "Red" | "Yellow" | "Blue" + +colorRed: Color = "Red" +colorYellow: Color = "Yellow" +colorBlue: Color = "Blue" + +schema Data: + color: Color + +dataColorRed = Data { + color = "Red" +} +dataColorYellow = Data { + color = "Yellow" +} +dataColorBlue = Data { + color = "Blue" +} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/type_alias.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/type_alias.yaml new file mode 100644 index 000000000..360f83507 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/type_alias.yaml @@ -0,0 +1,9 @@ +colorRed: Red +colorYellow: Yellow +colorBlue: Blue +dataColorRed: + color: Red +dataColorYellow: + color: Yellow +dataColorBlue: + color: Blue diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/type_as.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/type_as.k new file mode 100644 index 000000000..0e43576b6 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/type_as.k @@ -0,0 +1,13 @@ +schema Data[idVar: int]: + id?: int = idVar + +type IntList = [int] +type StrMap = {str:str} + +a = 1 as int +b = 2.0 as float +c = True as bool +d = "s" as str +e = [1] as IntList +f = {key: "value"} as StrMap +g = Data(1) as Data diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/type_as.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/type_as.yaml new file mode 100644 index 000000000..d4ce11259 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/type_as.yaml @@ -0,0 +1,10 @@ +a: 1 +b: 2.0 +c: true +d: s +e: +- 1 +f: + key: value +g: + id: 1 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/types.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/types.k new file mode 100644 index 000000000..73d6d4991 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/types.k @@ -0,0 +1,6 @@ +schema Person[data: str, n]: + name: str = data + count: int = n + +person = Person("Alice", 1) {} +personOther = person as Person diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/types.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/types.yaml new file mode 100644 index 000000000..4658fc41d --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/types.yaml @@ -0,0 +1,6 @@ +person: + name: Alice + count: 1 +personOther: + name: Alice + count: 1 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unary.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unary.k new file mode 100644 index 000000000..03a939210 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unary.k @@ -0,0 +1,4 @@ +a = -1 +b = +1 +c = not None +d = ~1 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unary.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unary.yaml new file mode 100644 index 000000000..6bd4f4ab0 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unary.yaml @@ -0,0 +1,4 @@ +a: -1 +b: 1 +c: true +d: -2 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unification.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unification.k new file mode 100644 index 000000000..b57d51f4e --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unification.k @@ -0,0 +1,19 @@ +schema Config: + data: int + +_config: Config { + data = 1 +} +if True: + _config: Config { + data = 2 + } + +config = _config + +schema Map: + config: Config { + data = 1 + } + +data = Map {} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unification.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unification.yaml new file mode 100644 index 000000000..4518f66ce --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unification.yaml @@ -0,0 +1,5 @@ +config: + data: 2 +data: + config: + data: 1 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unification_with_mixin.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unification_with_mixin.k new file mode 100644 index 000000000..4c64a9bf1 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unification_with_mixin.k @@ -0,0 +1,30 @@ +schema PersonFooMixin: + person: Person { + name = "Alice" + info.key2: "value2" + } + +schema PersonBarMixin: + person: Person { + info.key3: "value3" + } + +schema Person: + name?: str + age?: int + info?: {str:} + check: + age > 0 if age + +schema Data: + mixin [ + PersonFooMixin, + PersonBarMixin + ] + + person: Person { + info.key1: "value1" + age = 1 + } + +data = Data() diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unification_with_mixin.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unification_with_mixin.yaml new file mode 100644 index 000000000..af11fa75a --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/unification_with_mixin.yaml @@ -0,0 +1,8 @@ +data: + person: + name: Alice + age: 1 + info: + key1: value1 + key2: value2 + key3: value3 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/units.k b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/units.k new file mode 100644 index 000000000..0ad60f3ea --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/units.k @@ -0,0 +1,32 @@ +import units + +_data = {k: 1Ki} + +type Unit = units.NumberMultiplier + +x0: Unit = 1M +x1 = x0 +x2 = int(x0) +x3 = float(x0) +x4 = x0 if x0 else 1 +x5 = x0 +x6 = [1Ki, 1M][0] +x7 = _data["k"] +x8: 1M = 1M + +x0str = str(1M) +x1str = str(x0) +x2str = str(int(x0)) +x3str = str(float(x0)) +x4str = str(x0 if x0 else 1) +x5str = "{}".format(x0) +x6str = "${[1Ki, 1M][0]}" +x7str = "${_data: #json}" + +schema Data: + x0: Unit = 1Mi + x1: any = 1Ki + x2: 1M | 1Ki = x1 + x3: Unit = 1Ki + +data = Data() diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/units.yaml b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/units.yaml new file mode 100644 index 000000000..0bacc4908 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/eval_data/units.yaml @@ -0,0 +1,22 @@ +x0: 1000000 +x1: 1000000 +x2: 1000000 +x3: 1000000.0 +x4: 1000000 +x5: 1000000 +x6: 1024 +x7: 1024 +x8: 1000000 +x0str: 1M +x1str: 1M +x2str: '1000000' +x3str: '1000000.0' +x4str: 1M +x5str: 1M +x6str: 1Ki +x7str: '{"k": "1Ki"}' +data: + x0: 1048576 + x1: 1024 + x2: 1024 + x3: 1024 diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/invalid_eval_data/list_schema_match.k b/test/test_units/test_kclvm/test_compiler/test_eval/invalid_eval_data/list_schema_match.k new file mode 100644 index 000000000..87554aa93 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/invalid_eval_data/list_schema_match.k @@ -0,0 +1,17 @@ +schema Data: + field: Field + +schema Field: + id?: int + +schema Config: + data0: Data = { + field: { + id: 1 + } + } + data1: Data = { + field: [] + } + +x0 = Config {} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/invalid_eval_data/mixin_not_defined.k b/test/test_units/test_kclvm/test_compiler/test_eval/invalid_eval_data/mixin_not_defined.k new file mode 100644 index 000000000..8ce933d74 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/invalid_eval_data/mixin_not_defined.k @@ -0,0 +1,4 @@ +schema Person: + mixin [PersonMixin] + +person = Person {} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/invalid_eval_data/name_not_defined.k b/test/test_units/test_kclvm/test_compiler/test_eval/invalid_eval_data/name_not_defined.k new file mode 100644 index 000000000..86e9f1b69 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/invalid_eval_data/name_not_defined.k @@ -0,0 +1,8 @@ +schema GetData: + result: str = name + +schema Person: + name: str = "kcl" + result: GetData = GetData {} + +x0 = Person {} diff --git a/test/test_units/test_kclvm/test_compiler/test_eval/test_eval.py b/test/test_units/test_kclvm/test_compiler/test_eval/test_eval.py new file mode 100644 index 000000000..d21eff3d8 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_eval/test_eval.py @@ -0,0 +1,98 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import unittest +import pathlib +from typing import Tuple + +import kclvm.api.object as kcl_object +import kclvm.kcl.error as kcl_error +from kclvm.program.eval import EvalCode +from kclvm.vm.code import Opcode +from kclvm.vm.runtime.evaluator.eval import Evaluator + +_FILE_INPUT_SUFFIX = ".k" +_FILE_OUTPUT_SUFFIX = ".yaml" +_PATH_NAME = "eval_data" +_DIR_PATH = pathlib.Path(__file__).parent.joinpath(_PATH_NAME) +_TEST_CASES = [ + "assert", + "aug_assign", + "calculation", + "collection_if", + "compare", + "convert_collection_value", + "config", + "emit_expr", + "empty", + "expr", + "quant_expr", + "regex", + "rule", + "schema_args", + "schema", + "type_alias", + "type_as", + "str", + "types", + "complex", + "for", + "if", + "index_signature", + "lambda", + "member_ship", + "nest_var", + "plugin", + "plus", + "line_continue", + "list", + "unary", + "unification_with_mixin", + "unification", + "units", +] + + +class KCLBaseEvaluatorTest(unittest.TestCase): + """ + KCL base Evaluator test + """ + + def read_data(self, data_name: str) -> Tuple[str, str]: + """ + Read format data according to data name + """ + input_filename = data_name + _FILE_INPUT_SUFFIX + output_filename = data_name + _FILE_OUTPUT_SUFFIX + data_input = (_DIR_PATH / input_filename).read_text() + data_output = (_DIR_PATH / output_filename).read_text() + return data_input, data_output + + +class KCLEvaluatorTest(KCLBaseEvaluatorTest): + def test_Evaluator_eval_binary_op(self): + kcl_eval = Evaluator() + self.assertEqual(kcl_object.KCLIntObject(value=11), + kcl_eval.eval_binary_op(left=kcl_object.KCLIntObject(value=10), + right=kcl_object.KCLIntObject(value=11), + code=Opcode.BINARY_OR)) + + def test_eval_data(self) -> None: + """ + Test format data for the comparison of input and golden files + """ + self.maxDiff = None + for case in _TEST_CASES: + try: + input_code, output = self.read_data(case) + self.assertEqual(EvalCode(filename=case+".k", code=input_code), output, msg=f"the case is {case}") + except Exception as err: + print(f"case {case} raise an error: {err}") + + def test_invalid_eval_data(self): + for p in pathlib.Path(__file__).parent.joinpath("invalid_eval_data").glob("*.k"): + with self.assertRaises(kcl_error.KCLException): + EvalCode(filename=str(p)) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_extension/test_builtin/test_builtin.py b/test/test_units/test_kclvm/test_compiler/test_extension/test_builtin/test_builtin.py new file mode 100644 index 000000000..e742bd9d6 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_extension/test_builtin/test_builtin.py @@ -0,0 +1,16 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest + +import kclvm.compiler.extension.builtin.builtin as builtin + + +class TestBuiltinFcuntion(unittest.TestCase): + def test_list(self): + self.assertEqual(builtin.KMANGLED_list(), []) + self.assertEqual(builtin.KMANGLED_list([0]), [0]) + self.assertEqual(builtin.KMANGLED_list({"k": "v"}), ["k"]) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_extension/test_builtin/test_net.py b/test/test_units/test_kclvm/test_compiler/test_extension/test_builtin/test_net.py new file mode 100644 index 000000000..629368331 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_extension/test_builtin/test_net.py @@ -0,0 +1,100 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest + +import kclvm.compiler.extension.builtin.system_module.net as net + + +class TestNetSystemModule(unittest.TestCase): + def test_host_port(self): + + self.assertEqual( + net.KMANGLED_split_host_port("B-K0NZJGH6-0048.local:80"), + ["B-K0NZJGH6-0048.local", "80"], + ) + self.assertEqual( + net.KMANGLED_join_host_port("B-K0NZJGH6-0048.local", 80), + "B-K0NZJGH6-0048.local:80", + ) + + def test_is_ip(self): + cases = [ + # False cases + {"value": "192.168.0,1", "expected": False}, + {"value": "192.168.0.", "expected": False}, + {"value": "192.168.", "expected": False}, + {"value": "192.", "expected": False}, + {"value": "", "expected": False}, + {"value": "256.168.0,1", "expected": False}, + {"value": "255.255.0.-1", "expected": False}, + {"value": "192.0022.1.1", "expected": False}, + {"value": "492.10.123.12313", "expected": False}, + {"value": "0xFF.0xFF.0xFF.0xFF", "expected": False}, + {"value": "2001:0db8:3c4d:0015+0000:0000:1a2f:1a2b", "expected": False}, + # True cases + {"value": "255.255.0.1", "expected": True}, + {"value": "1.0.0.0", "expected": True}, + {"value": "192.190.0.1", "expected": True}, + {"value": "128.0.0.0", "expected": True}, + {"value": "172.16.0.0", "expected": True}, + {"value": "172.31.255.255", "expected": True}, + {"value": "169.254.0.1", "expected": True}, + {"value": "191.255.255.255", "expected": True}, + {"value": "223.255.255.0", "expected": True}, + {"value": "2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b", "expected": True}, + ] + for case in cases: + value, expected = case["value"], case["expected"] + self.assertEqual(net.KMANGLED_is_IP(value), expected) + + def test_is_multicast_IP(self): + cases = [ + {"value": "239.255.255.255", "expected": True}, + ] + for case in cases: + value, expected = case["value"], case["expected"] + self.assertEqual(net.KMANGLED_is_multicast_IP(value), expected) + + def test_is_loopback_IP(self): + cases = [ + {"value": "127.0.0.1", "expected": True}, + ] + for case in cases: + value, expected = case["value"], case["expected"] + self.assertEqual(net.KMANGLED_is_loopback_IP(value), expected) + + def test_is_link_local_multicast_IP(self): + cases = [ + {"value": "224.0.0.0", "expected": False}, + ] + for case in cases: + value, expected = case["value"], case["expected"] + self.assertEqual(net.KMANGLED_is_link_local_multicast_IP(value), expected) + + def test_is_link_local_unicast_IP(self): + cases = [ + {"value": "fe80::2012:1", "expected": True}, + ] + for case in cases: + value, expected = case["value"], case["expected"] + self.assertEqual(net.KMANGLED_is_link_local_unicast_IP(value), expected) + + def test_is_global_unicast_IP(self): + cases = [ + {"value": "220.181.108.89", "expected": True}, + ] + for case in cases: + value, expected = case["value"], case["expected"] + self.assertEqual(net.KMANGLED_is_global_unicast_IP(value), expected) + + def test_is_unspecified_IP(self): + cases = [ + {"value": "0.0.0.0", "expected": True}, + ] + for case in cases: + value, expected = case["value"], case["expected"] + self.assertEqual(net.KMANGLED_is_unspecified_IP(value), expected) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_extension/test_builtin/test_units.py b/test/test_units/test_kclvm/test_compiler/test_extension/test_builtin/test_units.py new file mode 100644 index 000000000..acbfc4ab3 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_extension/test_builtin/test_units.py @@ -0,0 +1,21 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest + +import kclvm.compiler.extension.builtin.system_module.units as units + + +class TestUnitsSystemModule(unittest.TestCase): + def test_to_unit(self): + cases = [ + {"num": 1e9, "suffix": "G", "expected": "1G"}, + {"num": 1e10, "suffix": "G", "expected": "10G"}, + {"num": 1e9, "suffix": "M", "expected": "1000M"}, + ] + for case in cases: + num, suffix, expected = case["num"], case["suffix"], case["expected"] + self.assertEqual(units.to_unit(num, suffix), expected) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_extension/test_builtin/test_yaml.py b/test/test_units/test_kclvm/test_compiler/test_extension/test_builtin/test_yaml.py new file mode 100644 index 000000000..59c31e539 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_extension/test_builtin/test_yaml.py @@ -0,0 +1,242 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest + +import kclvm.compiler.extension.builtin.system_module.yaml as yaml + + +class TestYAMLSystemModule(unittest.TestCase): + def test_decode(self): + yamlStrList = [ + "key: value", + "- 1\n- 2\n- 3", + "1", + "1.1", + "null", + "true", + ] + expected = [ + {"key": "value"}, + [1, 2, 3], + 1, + 1.1, + None, + True, + ] + data = [yaml.KMANGLED_decode(s) for s in yamlStrList] + self.assertListEqual(data, expected) + + def test_encode_literal(self): + dataDict = {"key": "value"} + dataList = [1, 2, 3] + dataInt = 1 + dataFloat = 1.1 + dataNone = None + dataBool = True + expected = [ + "key: value\n", + "- 1\n- 2\n- 3\n", + "1\n...\n", + "1.1\n...\n", + "null\n...\n", + "true\n...\n", + ] + yamlStr = [ + yaml.KMANGLED_encode(data) + for data in [dataDict, dataList, dataInt, dataFloat, dataNone, dataBool] + ] + self.assertEqual(yamlStr, expected) + + def test_encode_dict(self): + cases = [ + { + "value": { + "key1": "value1", + "key2": "value2", + "data": [1, 2, 3], + }, + "expected": """\ +key1: value1 +key2: value2 +data: +- 1 +- 2 +- 3 +""", + }, + { + "value": { + "key1": {"key1": "value1", "key2": "value2"}, + "key2": "value2", + "data": [1, 2, 3], + }, + "expected": """\ +key1: + key1: value1 + key2: value2 +key2: value2 +data: +- 1 +- 2 +- 3 +""", + }, + ] + for case in cases: + value, expected = case["value"], case["expected"] + self.assertEqual(yaml.KMANGLED_encode(value), expected) + + def test_encode_dict(self): + cases = [ + { + "value": { + "key1": "value1", + "key2": "value2", + "data": [1, 2, 3], + }, + "expected": """\ +key1: value1 +key2: value2 +data: +- 1 +- 2 +- 3 +""", + }, + { + "value": { + "key1": {"key1": "value1", "key2": "value2"}, + "key2": "value2", + "data": [1, 2, 3], + }, + "expected": """\ +key1: + key1: value1 + key2: value2 +key2: value2 +data: +- 1 +- 2 +- 3 +""", + }, + ] + for case in cases: + value, expected = case["value"], case["expected"] + self.assertEqual(yaml.KMANGLED_encode(value), expected) + + def test_encode_with_ignore_none(self): + cases = [ + { + "value": { + "key1": None, + "key2": "value2", + "data": [1, 2, 3], + }, + "expected": """\ +key2: value2 +data: +- 1 +- 2 +- 3 +""", + }, + { + "value": { + "key1": {"key1": "value1", "key2": "value2"}, + "key2": None, + "data": [1, 2, 3], + }, + "expected": """\ +key1: + key1: value1 + key2: value2 +data: +- 1 +- 2 +- 3 +""", + }, + ] + for case in cases: + value, expected = case["value"], case["expected"] + self.assertEqual(yaml.KMANGLED_encode(value, ignore_none=True), expected) + + def test_encode_with_ignore_private(self): + cases = [ + { + "value": { + "_key1": "value1", + "key2": "value2", + "data": [1, 2, 3], + }, + "expected": """\ +key2: value2 +data: +- 1 +- 2 +- 3 +""", + }, + { + "value": { + "key1": {"key1": "value1", "key2": "value2"}, + "_key2": "value2", + "data": [1, 2, 3], + }, + "expected": """\ +key1: + key1: value1 + key2: value2 +data: +- 1 +- 2 +- 3 +""", + }, + ] + for case in cases: + value, expected = case["value"], case["expected"] + self.assertEqual(yaml.KMANGLED_encode(value, ignore_private=True), expected) + + def test_encode_with_sort_keys(self): + cases = [ + { + "value": { + "key1": "value1", + "key2": "value2", + "data": [1, 2, 3], + }, + "expected": """\ +data: +- 1 +- 2 +- 3 +key1: value1 +key2: value2 +""", + }, + { + "value": { + "key1": {"key1": "value1", "key2": "value2"}, + "key2": "value2", + "data": [1, 2, 3], + }, + "expected": """\ +data: +- 1 +- 2 +- 3 +key1: + key1: value1 + key2: value2 +key2: value2 +""", + }, + ] + for case in cases: + value, expected = case["value"], case["expected"] + self.assertEqual(yaml.KMANGLED_encode(value, sort_keys=True), expected) + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_preprocess/test_preprocess.py b/test/test_units/test_kclvm/test_compiler/test_preprocess/test_preprocess.py new file mode 100644 index 000000000..9b181c788 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_preprocess/test_preprocess.py @@ -0,0 +1,85 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import unittest +from typing import cast + +import kclvm.kcl.ast as ast +from kclvm.compiler.parser import ParseFile +from kclvm.compiler.build.preprocess import fix_identifier_prefix + + +simple_case = """ +schema $all: + data?: int + +schema $filter($all): + $name: str + +$map = $filter { + name: "Data" +} +""" + +if_case = """ +schema Data: + if False: + $name = "1" + elif True: + $name = "2" + else: + $name = "3" +""" + +import_case = """ +import $import.$all as $all +""" + + +class CompilerPreprocessTest(unittest.TestCase): + def test_fix_identifier_prefix_invalid_case(self): + self.assertEqual(fix_identifier_prefix(None), None) + + def test_fix_identifier_prefix_simple_case(self): + module = ParseFile("", code=simple_case) + module = cast(ast.Module, fix_identifier_prefix(module)) + self.assertEqual(len(module.body), 3) + schema_stmt_all: ast.SchemaStmt = cast(ast.SchemaStmt, module.body[0]) + schema_stmt_filter: ast.SchemaStmt = cast(ast.SchemaStmt, module.body[1]) + assign_stmt: ast.AssignStmt = cast(ast.AssignStmt, module.body[2]) + self.assertEqual(schema_stmt_all.name, "all") + self.assertEqual(schema_stmt_filter.name, "filter") + self.assertEqual(schema_stmt_filter.parent_name.get_name(), "all") + self.assertEqual(schema_stmt_filter.body[0].name, "name") + self.assertEqual(assign_stmt.value.name.get_name(), "filter") + + def test_fix_identifier_prefix_if_case(self): + module = ParseFile("", code=if_case) + module = cast(ast.Module, fix_identifier_prefix(module)) + self.assertEqual(len(module.body), 1) + schema_stmt: ast.SchemaStmt = cast(ast.SchemaStmt, module.body[0]) + self.assertEqual(len(schema_stmt.body), 1) + if_stmt: ast.IfStmt = cast(ast.IfStmt, schema_stmt.body[0]) + self.assertEqual(if_stmt.body[0].targets[0].get_name(), "name") + self.assertEqual(if_stmt.body[0].value.value, "1") + self.assertEqual(if_stmt.elif_body[0][0].targets[0].get_name(), "name") + self.assertEqual(if_stmt.elif_body[0][0].value.value, "2") + self.assertEqual(if_stmt.else_body[0].targets[0].get_name(), "name") + self.assertEqual(if_stmt.else_body[0].value.value, "3") + + def test_fix_identifier_prefix_import_case(self): + module = ParseFile("", code=import_case) + module = cast(ast.Module, fix_identifier_prefix(module)) + self.assertEqual(len(module.body), 1) + import_stmt: ast.ImportStmt = cast(ast.ImportStmt, module.body[0]) + self.assertEqual(import_stmt.asname, "all") + self.assertEqual(import_stmt.name, "all") + self.assertEqual(import_stmt.pkg_name, "all") + self.assertEqual(import_stmt.path, "import.all") + self.assertEqual(len(import_stmt.path_nodes), 2) + self.assertEqual(import_stmt.path_nodes[0].value, "import") + self.assertEqual(import_stmt.path_nodes[1].value, "all") + + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_vfs/kcl.mod b/test/test_units/test_kclvm/test_compiler/test_vfs/kcl.mod new file mode 100644 index 000000000..b8356d10c --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_vfs/kcl.mod @@ -0,0 +1,2 @@ +[build] +enable_pkg_cache=true diff --git a/test/test_units/test_kclvm/test_compiler/test_vfs/main.k b/test/test_units/test_kclvm/test_compiler/test_vfs/main.k new file mode 100644 index 000000000..d25d49e0f --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_vfs/main.k @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_compiler/test_vfs/pkg/pkg.k b/test/test_units/test_kclvm/test_compiler/test_vfs/pkg/pkg.k new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_compiler/test_vfs/test_data_bytecode_cache/kcl.mod b/test/test_units/test_kclvm/test_compiler/test_vfs/test_data_bytecode_cache/kcl.mod new file mode 100644 index 000000000..f4e7091a3 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_vfs/test_data_bytecode_cache/kcl.mod @@ -0,0 +1,3 @@ +[build] +enable_pkg_cache=true +cached_pkg_prefix="pkg." diff --git a/test/test_units/test_kclvm/test_compiler/test_vfs/test_data_bytecode_cache/main.k b/test/test_units/test_kclvm/test_compiler/test_vfs/test_data_bytecode_cache/main.k new file mode 100644 index 000000000..1337a530c --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_vfs/test_data_bytecode_cache/main.k @@ -0,0 +1 @@ +a = 1 diff --git a/test/test_units/test_kclvm/test_compiler/test_vfs/test_data_bytecode_cache/pkg/pkg.k b/test/test_units/test_kclvm/test_compiler/test_vfs/test_data_bytecode_cache/pkg/pkg.k new file mode 100644 index 000000000..1337a530c --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_vfs/test_data_bytecode_cache/pkg/pkg.k @@ -0,0 +1 @@ +a = 1 diff --git a/test/test_units/test_kclvm/test_compiler/test_vfs/test_get_pkg_root/kcl.mod b/test/test_units/test_kclvm/test_compiler/test_vfs/test_get_pkg_root/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_compiler/test_vfs/test_vfs.py b/test/test_units/test_kclvm/test_compiler/test_vfs/test_vfs.py new file mode 100644 index 000000000..bec902f16 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_vfs/test_vfs.py @@ -0,0 +1,303 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import os +import pathlib +import unittest +import shutil + +import kclvm.api.object as obj +import kclvm.kcl.ast as ast +import kclvm.compiler.vfs.vfs as vfs +import kclvm.compiler.parser as parser +import kclvm.compiler.build.compiler as compiler + + +class TestVfsLoadPkgCache(unittest.TestCase): + def test_invalid_parameters(self): + self.assertEqual(vfs.LoadPkgCache(None, None), None) + self.assertEqual(vfs.LoadPkgCache(None, ""), None) + self.assertEqual(vfs.LoadPkgCache("", None), None) + self.assertEqual(vfs.LoadPkgCache("", ""), None) + + def test_missing_cache(self): + self.assertEqual( + vfs.LoadPkgCache(os.path.dirname(__file__), "pkgpath.missing"), None + ) + + +class TestVfsSavePkgCache(unittest.TestCase): + def test_invalid_parameters(self): + self.assertEqual(vfs.SavePkgCache(None, None, None), None) + self.assertEqual(vfs.SavePkgCache(None, "", ""), None) + self.assertEqual(vfs.SavePkgCache("", None, ""), None) + self.assertEqual(vfs.SavePkgCache("", "", None), None) + self.assertEqual(vfs.SavePkgCache(None, None, ""), None) + self.assertEqual(vfs.SavePkgCache("", None, None), None) + self.assertEqual(vfs.SavePkgCache(None, "", None), None) + + def test_save(self): + vfs.SavePkgCache( + os.path.dirname(__file__), "pkgpath.test_save", {"key": "value"} + ) + + def tearDown(self): + cache_root = f"{os.path.dirname(__file__)}/.kclvm" + if os.path.exists(cache_root): + shutil.rmtree(cache_root) + + +class TestVfsIsAbsPkgPath(unittest.TestCase): + def test_is_abs_pkg_path_with_true_case(self): + tests = ["a", "a.b.c"] + for i in range(len(tests)): + self.assertTrue(vfs.IsAbsPkgPath(tests[i]), msg=f"{tests[i]}") + + def test_is_abs_pkg_path_with_false_case(self): + tests = ["", ".", ".a", "a..b", "a b", None, 1] + for i in range(len(tests)): + self.assertFalse(vfs.IsAbsPkgPath(tests[i]), msg=f"{tests[i]}") + + +class TestVfsIsRelPkgPath(unittest.TestCase): + def test_is_abs_pkg_path_with_true_case(self): + tests = [".", ".a", ".a.b.c", "..a.b", " .a.b.c"] + for i in range(len(tests)): + self.assertTrue(vfs.IsRelPkgPath(tests[i]), msg=f"{tests[i]}") + + def test_is_abs_pkg_path_with_false_case(self): + tests = ["", "a", "a.b.c", "a b", None, 1] + for i in range(len(tests)): + self.assertFalse(vfs.IsRelPkgPath(tests[i]), msg=f"{tests[i]}") + + +class TestFixImportPath(unittest.TestCase): + def test_fix_import_path_invalid_case(self): + cases = [ + {"root": None, "filepath": None, "import_path": None}, + {"root": "", "filepath": "", "import_path": ""}, + ] + for case in cases: + root, filepath, import_path = ( + case["root"], + case["filepath"], + case["import_path"], + ) + with self.assertRaises(AssertionError): + vfs.FixImportPath(root, filepath, import_path) + + def test_fix_import_path_normal_case(self): + cases = [ + { + "root": ".", + "filepath": "path/to/app/file.k", + "import_path": ".sub", + "expected": "path.to.app.sub", + }, + { + "root": ".", + "filepath": "path/to/app/file.k", + "import_path": "..sub", + "expected": "path.to.sub", + }, + { + "root": ".", + "filepath": "path/to/app/file.k", + "import_path": "...sub", + "expected": "path.sub", + }, + { + "root": ".", + "filepath": "path/to/app/file.k", + "import_path": "....sub", + "expected": "sub", + }, + { + "root": ".", + "filepath": "path/to/app/file.k", + "import_path": ".....sub", + "expected": "", + }, + { + "root": ".", + "filepath": "path/to/app/file.k", + "import_path": "path.to.sub", + "expected": "path.to.sub", + }, + { + "root": "path/to/app/", + "filepath": "path/to/app/file.k", + "import_path": ".sub", + "expected": "sub", + }, + { + "root": "path/to/app/", + "filepath": "path/to/app/file.k", + "import_path": "..sub", + "expected": "", + }, + { + "root": "path/to/", + "filepath": "path/to/app/file.k", + "import_path": ".sub", + "expected": "app.sub", + }, + { + "root": "path/to/", + "filepath": "path/to/app/file.k", + "import_path": "..sub", + "expected": "sub", + }, + { + "root": "path/", + "filepath": "path/to/app/file.k", + "import_path": "..sub", + "expected": "to.sub", + }, + { + "root": "path/", + "filepath": "path/to/app/file.k", + "import_path": ".sub", + "expected": "to.app.sub", + }, + ] + for case in cases: + root, filepath, import_path, expected = ( + case["root"], + case["filepath"], + case["import_path"], + case["expected"], + ) + self.assertEqual( + vfs.FixImportPath(root, filepath, import_path), + expected, + f"Case test failed: root: {root}, filepath: {filepath}, import_path: {import_path}", + ) + + +class TestVfsSaveAndLoadASTPkgCache(unittest.TestCase): + def test_ast_save_and_load(self): + codes = ["a = 1", "b = 1"] + pkgs = ["pkg", "pkg.pkg"] + for code, pkg in zip(codes, pkgs): + module = parser.ParseFile("test.k", code) + module_load = vfs.LoadPkgCache(os.path.dirname(__file__), pkg) + self.assertEqual(module_load, None) + vfs.SavePkgCache(os.path.dirname(__file__), pkg, module) + module_load = vfs.LoadPkgCache(os.path.dirname(__file__), pkg) + self.assertIsInstance(module_load, ast.Module) + + def test_cache_loaded_expired(self): + code = "a = 1" + pkg = "pkg" + module = parser.ParseFile("test.k", code) + vfs.SavePkgCache(os.path.dirname(__file__), pkg, module) + module_load = vfs.LoadPkgCache(os.path.dirname(__file__), pkg) + self.assertIsInstance(module_load, ast.Module) + pathlib.Path(__file__).parent.joinpath("pkg/pkg.k").write_text(code) + module_load = vfs.LoadPkgCache(os.path.dirname(__file__), pkg) + self.assertIsNone(module_load) + pathlib.Path(__file__).parent.joinpath("pkg/pkg.k").write_text("") + + +class TestVfsSaveAndLoadASTMainPkgCache(unittest.TestCase): + def test_ast_main_save_and_load(self): + codes = ["a = 1", "b = 1"] + filenames = ["main_test1.k", "main_test2.k"] + for code, filename in zip(codes, filenames): + module = parser.ParseFile(filename, code) + module_load = vfs.LoadMainPkgCache(os.path.dirname(__file__), filename) + self.assertEqual(module_load, None) + vfs.SaveMainPkgCache(os.path.dirname(__file__), filename, module) + module_load = vfs.LoadMainPkgCache(os.path.dirname(__file__), filename) + self.assertIsInstance(module_load, ast.Module) + + def test_main_cache_loaded_expired(self): + code = "a = 1" + pkg = "pkg" + module = parser.ParseFile("test.k", code) + k_filepath = pathlib.Path(__file__).parent.joinpath("pkg/pkg.k") + vfs.SaveMainPkgCache(os.path.dirname(__file__), str(k_filepath), module) + module_load = vfs.LoadMainPkgCache(os.path.dirname(__file__), str(k_filepath)) + self.assertIsInstance(module_load, ast.Module) + k_filepath.write_text(code) + module_load = vfs.LoadMainPkgCache(os.path.dirname(__file__), str(k_filepath)) + self.assertIsNone(module_load) + k_filepath.write_text("") + + def test_kcl_mod_with_main_pkg_save(self): + work_dir = pathlib.Path(__file__).parent + main_file = str(work_dir / "main.k") + # Save main package cache + parser.LoadProgram(main_file, work_dir=str(work_dir)) + # Load main package cache + parser.LoadProgram(main_file, work_dir=str(work_dir)) + + +class TestCacheInfo(unittest.TestCase): + def test_cache_info(self): + root = str(pathlib.Path(__file__).parent) + filepath = str(pathlib.Path(__file__).parent / "pkg") + vfs.write_info_cache({}, root, filepath) + # Read info cache + vfs.read_info_cache(root) + + def test_cache_info_invalid_root(self): + root = str(pathlib.Path(__file__).parent / "err_pkg") + self.assertEqual(vfs.read_info_cache(root), {}) + + def test_cache_info_file(self): + cases = [ + {"filepath": str(pathlib.Path(__file__).parent / "pkg"), "expected": "d41d8cd98f00b204e9800998ecf8427e"}, + {"filepath": str(pathlib.Path(__file__).parent / "pkg/pkg.k"), "expected": "d41d8cd98f00b204e9800998ecf8427e"}, + {"filepath": str(pathlib.Path(__file__).parent / "main.k"), "expected": "fd352b68bf83391284e044021cab0339"}, + ] + for case in cases: + filepath, expected = case["filepath"], case["expected"] + self.assertEqual(vfs.get_cache_info(filepath), expected) + + +class TestVfsSaveAndLoadBytecodeCache(unittest.TestCase): + def setUp(self): + self.test_data_path_name = "test_data_bytecode_cache" + self.root = str(pathlib.Path(__file__).parent.joinpath(self.test_data_path_name)) + self.main_file = str(pathlib.Path(__file__).parent.joinpath(f"{self.test_data_path_name}/main.k")) + self.cache_path = str(pathlib.Path(__file__).parent.joinpath(f"{self.test_data_path_name}/.kclvm")) + return super().setUp() + + def test_save_bytecode_cache(self): + ast_program = parser.LoadProgram(self.main_file) + kcl_program = compiler.CompileProgram(ast_program) + self.assertEqual(kcl_program.root, self.root) + vfs.SaveBytecodeCache(ast_program.root, ast_program, kcl_program) + program_loaded = vfs.LoadBytecodeCache(self.root, ast_program) + self.assertIsNotNone(program_loaded) + if os.path.exists(self.cache_path): + shutil.rmtree(self.cache_path) + + def test_load_bytecode_cache(self): + pass + + def test_save_bytecode_cache_invalid_parameters(self): + cases = [ + {"root": "", "ast_program": None, "program": None}, + {"root": self.root, "ast_program": None, "program": None}, + {"root": self.root, "ast_program": ast.Program(), "program": None}, + {"root": self.root, "ast_program": ast.Program(), "program": obj.KCLProgram()}, + ] + for case in cases: + root, ast_program, program = case["root"], case["ast_program"], case["program"] + vfs.SaveBytecodeCache(root, ast_program, program) + + def test_load_bytecode_cache_invalid_parameters(self): + cases = [ + {"root": "", "ast_program": None}, + {"root": self.root, "ast_program": None}, + {"root": self.root, "ast_program": ast.Program()}, + ] + for case in cases: + root, ast_program = case["root"], case["ast_program"] + vfs.LoadBytecodeCache(root, ast_program) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_compiler/test_vfs/test_vfs_get_root.py b/test/test_units/test_kclvm/test_compiler/test_vfs/test_vfs_get_root.py new file mode 100644 index 000000000..89741acf2 --- /dev/null +++ b/test/test_units/test_kclvm/test_compiler/test_vfs/test_vfs_get_root.py @@ -0,0 +1,45 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest +import pathlib + +import kclvm.kcl.error as kcl_error +import kclvm.compiler.vfs as vfs + +CURRENT_PATH = str(pathlib.Path(__file__).parent) + + +class TestVfsGetRoot(unittest.TestCase): + def test_get_pkg_root(self): + cases = [ + {"path": "../..", "expected": None}, + {"path": f"{CURRENT_PATH}/pkg", "expected": str(CURRENT_PATH)}, + {"path": f"{CURRENT_PATH}/test_get_pkg_root", "expected": f"{CURRENT_PATH}/test_get_pkg_root"}, + ] + for case in cases: + path, expected = case["path"], case["expected"] + result = vfs.GetPkgRoot(path) + self.assertEqual(result, expected) + + def test_must_get_pkg_root(self): + cases = [ + {"paths": ["../..", "."], "expected": None}, + {"paths": [f"{CURRENT_PATH}/pkg", "."], "expected": str(CURRENT_PATH)}, + ] + for case in cases: + paths, expected = case["paths"], case["expected"] + result = vfs.MustGetPkgRoot(paths) + self.assertEqual(result, expected) + + def test_must_get_pkg_root_invalid(self): + cases = [ + {"paths": [f"{CURRENT_PATH}/test_get_pkg_root", str(CURRENT_PATH)]}, + ] + for case in cases: + paths = case["paths"] + with self.assertRaises(kcl_error.CompileError): + result = vfs.MustGetPkgRoot(paths) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_config/test_config.py b/test/test_units/test_kclvm/test_config/test_config.py new file mode 100644 index 000000000..643b9aabf --- /dev/null +++ b/test/test_units/test_kclvm/test_config/test_config.py @@ -0,0 +1,19 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest +import pathlib + +import kclvm.config as config + + +class TestConfigSettingFile(unittest.TestCase): + def test_cli_setting_action(self): + settings_file = str(pathlib.Path(__file__).parent.joinpath("test_data/settings.yaml")) + action = config.KCLCLISettingAction() + keys, values = [], [] + action.deal_setting_file(settings_file, keys, values) + self.assertListEqual(keys, ["app", "env-type"]) + self.assertListEqual(values, ["kclvm", "dev"]) + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_config/test_data/base.k b/test/test_units/test_kclvm/test_config/test_data/base.k new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_config/test_data/kcl.mod b/test/test_units/test_kclvm/test_config/test_data/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_config/test_data/main.k b/test/test_units/test_kclvm/test_config/test_data/main.k new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_config/test_data/settings.yaml b/test/test_units/test_kclvm/test_config/test_data/settings.yaml new file mode 100644 index 000000000..328ccb6b2 --- /dev/null +++ b/test/test_units/test_kclvm/test_config/test_data/settings.yaml @@ -0,0 +1,10 @@ +# This is a comment +kcl_options: + - key: app + value: kclvm + - key: env-type + value: dev +kcl_cli_configs: + files: + - ./base.k + - ${KCL_MOD}/main.k diff --git a/test/test_units/test_kclvm/test_config/test_settings.py b/test/test_units/test_kclvm/test_config/test_settings.py new file mode 100644 index 000000000..4a168a3eb --- /dev/null +++ b/test/test_units/test_kclvm/test_config/test_settings.py @@ -0,0 +1,28 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest +import pathlib + +import kclvm.config as config + + +class TestSettingsFile(unittest.TestCase): + def test_load_settings_files(self): + files = [ + str(pathlib.Path(__file__).parent.joinpath("test_data/settings.yaml")), + ] + work_dir = str(pathlib.Path(__file__).parent.joinpath("test_data")) + cli_config = config.load_settings_files(work_dir, files) + expected_files = [ + str(pathlib.Path(__file__).parent.joinpath("test_data/base.k")), + str(pathlib.Path(__file__).parent.joinpath("test_data/main.k")), + ] + self.assertEqual(cli_config.kcl_cli_configs.files, expected_files) + keys = [opt.key for opt in cli_config.kcl_options] + values = [opt.value for opt in cli_config.kcl_options] + self.assertListEqual(keys, ["app", "env-type"]) + self.assertListEqual(values, ["kclvm", "dev"]) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_encoding/test_protobuf/test_parser.py b/test/test_units/test_kclvm/test_encoding/test_protobuf/test_parser.py new file mode 100644 index 000000000..0e6b0e439 --- /dev/null +++ b/test/test_units/test_kclvm/test_encoding/test_protobuf/test_parser.py @@ -0,0 +1,48 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest +import pathlib + +import kclvm.encoding.protobuf as protobuf + + +simple_test_case = """\ +syntax = "proto3"; +import public "other.proto"; +option java_package = "com.example.foo"; + +enum EnumAllowingAlias { + option allow_alias = true; + UNKNOWN = 0; + STARTED = 1; + RUNNING = 2 [(custom_option) = "hello world"]; +} + +message outer { + option (my_option).a = true; + option (my_option).b = -1; + option (my_option).c = "\\xAA"; + + message inner { + int64 ival = 1; + } + repeated inner inner_message = 2; + EnumAllowingAlias enum_field = 3; + map my_map = 4; +} +""" + + +class TestProtobufParseCode(unittest.TestCase): + def test_simple_case(self): + proto = protobuf.parse_code(simple_test_case) + self.assertEqual(len(proto.statements), 4) + self.assertEqual(proto.syntax, "proto3") + self.assertIsInstance(proto.statements[0], protobuf.Import) + self.assertIsInstance(proto.statements[1], protobuf.Option) + self.assertIsInstance(proto.statements[2], protobuf.Enum) + self.assertIsInstance(proto.statements[3], protobuf.Message) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_encoding/test_protobuf/test_printer.py b/test/test_units/test_kclvm/test_encoding/test_protobuf/test_printer.py new file mode 100644 index 000000000..33fc89896 --- /dev/null +++ b/test/test_units/test_kclvm/test_encoding/test_protobuf/test_printer.py @@ -0,0 +1,119 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import io +import unittest +import pathlib + +import kclvm.encoding.protobuf.parser as parser +import kclvm.encoding.protobuf.printer as printer + + +simple_test_case = """\ +syntax = "proto3"; +package kclvm.runtime.api; +import public "other.proto"; +option java_package = "com.example.foo"; + +enum EnumAllowingAlias { + option allow_alias = true; + UNKNOWN = 0; + STARTED = 1; + RUNNING = 2 [(custom_option) = "hello world"]; +} + +message outer { + option my_option.a = true; + option my_option.b = false; + + message inner { + int64 ival = 1; + } + repeated inner inner_message = 2; + EnumAllowingAlias enum_field = 3; + map my_map = 4; + oneof x { + int32 id = 1; + string name = 2; + } +} + +message Foo { + reserved 2, 15, 9 to 11; + reserved "foo", "bar"; +} + +message Args { + bool result = 1; +} + +service Service { + rpc Method(Args) returns (Args); +} +""" +simple_test_case_expected = """\ +syntax = "proto3"; +package kclvm.runtime.api; +import public "other.proto"; +option java_package = "com.example.foo"; + +enum EnumAllowingAlias { + option allow_alias = true; + UNKNOWN = 0; + STARTED = 1; + RUNNING = 2 [(custom_option) = "hello world"]; +} + +message outer { + option my_option.a = true; + option my_option.b = false; + + message inner { + int64 ival = 1; + } + repeated inner inner_message = 2; + EnumAllowingAlias enum_field = 3; + map my_map = 4; + oneof x { + int32 id = 1; + string name = 2; + } +} + +message Foo { + reserved 2, 15, 9 to 11; + reserved "foo", "bar"; +} + +message Args { + bool result = 1; +} + +service Service { + rpc Method(Args) returns (Args); +} +""" + + +class TestProtobufPrinter(unittest.TestCase): + def setUp(self): + super().setUp() + self.maxDiff = None + + def test_protobuf_printer(self): + proto = parser.parse_code(simple_test_case) + proto_text = printer.print_node_to_string(proto) + self.assertEqual(proto_text, simple_test_case_expected) + + def test_interleave(self): + out = io.StringIO() + printer.BasePrinter.interleave( + lambda: out.write(", "), lambda n: out.write(str(n)), [] + ) + printer.BasePrinter.interleave( + lambda: out.write(", "), lambda n: out.write(str(n)), [1, 2, 3] + ) + self.assertEqual(out.getvalue(), "1, 2, 3") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_encoding/test_protobuf/test_protobuf.py b/test/test_units/test_kclvm/test_encoding/test_protobuf/test_protobuf.py new file mode 100644 index 000000000..5fe085170 --- /dev/null +++ b/test/test_units/test_kclvm/test_encoding/test_protobuf/test_protobuf.py @@ -0,0 +1,101 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest +import pathlib + +import kclvm.encoding.protobuf.protobuf as protobuf + + +simple_to_kcl_test_case = """\ +syntax = "proto3"; +import public "other.proto"; +option java_package = "com.example.foo"; + +enum EnumAllowingAlias { + option allow_alias = true; + UNKNOWN = 0; + STARTED = 1; + RUNNING = 2 [(custom_option) = "hello world"]; +} + +message outer { + option (my_option).a = true; + message inner { + int64 ival = 1; + } + repeated inner inner_message = 2; + EnumAllowingAlias enum_field = 3; + map my_map = 4; + oneof x { + int32 id = 1; + string name = 2; + } +} +""" +simple_to_kcl_test_case_expected = """\ +type EnumAllowingAlias = 0 | 1 | 2 +schema outer: + inner_message: [inner] + enum_field: EnumAllowingAlias + my_map: {int:str} + x: int | str + +schema inner: + ival: int + +""" +simple_to_pb_test_case = """\ +schema Inner: + ival: int + +schema Outer: + inner: Inner + inner_message: [Inner] + my_map: {str:str} + x: int | str +""" +simple_to_pb_test_case_expected = """\ +syntax = "proto3"; + +message Inner { + int64 ival = 1; +} + +message Outer { + Inner inner = 1; + repeated Inner inner_message = 2; + map my_map = 3; + oneof x { + int x1 = 1; + str x2 = 2; + } +} +""" + + +class TestProtobufToKCLCode(unittest.TestCase): + def test_to_kcl_simple_case(self): + kcl_code = protobuf.protobuf_to_kcl(simple_to_kcl_test_case) + self.assertEqual(kcl_code, simple_to_kcl_test_case_expected) + + def test_invalid_function_arguments(self): + with self.assertRaises(ValueError): + protobuf.convert_enum_to_type_alias(None) + with self.assertRaises(ValueError): + protobuf.convert_message_to_schema_list(None) + with self.assertRaises(ValueError): + protobuf.convert_schema_to_message(None) + with self.assertRaises(ValueError): + protobuf.convert_schema_attr_to_message_body(None) + with self.assertRaises(ValueError): + protobuf.convert_kcl_type_to_proto_type("") + + +class TestKCLToProtobufCode(unittest.TestCase): + def test_to_pb_simple_case(self): + kcl_code = protobuf.kcl_to_protobuf(simple_to_pb_test_case) + self.assertEqual(kcl_code, simple_to_pb_test_case_expected) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_info/test_naming.py b/test/test_units/test_kclvm/test_info/test_naming.py new file mode 100644 index 000000000..bfca31544 --- /dev/null +++ b/test/test_units/test_kclvm/test_info/test_naming.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- + +import os +import unittest + +import kclvm.kcl.info as kcl_info + + +class TestKCLInfoNaming(unittest.TestCase): + def test_mangle(self): + cases = [ + {"name": "", "expected": "KMANGLED_"}, + {"name": "a", "expected": "KMANGLED_a"}, + {"name": "a.b", "expected": "KMANGLED_a.KMANGLED_b"}, + ] + for case in cases: + name, expected = case["name"], case["expected"] + self.assertEqual(kcl_info.mangle(name), expected) + + def test_demangle(self): + cases = [ + {"name": "", "expected": ""}, + {"name": "KMANGLED_a", "expected": "a"}, + {"name": "KMANGLED_a.KMANGLED_b", "expected": "a.b"}, + ] + for case in cases: + name, expected = case["name"], case["expected"] + self.assertEqual(kcl_info.demangle(name), expected) + + + def test_tagging(self): + cases = [ + {"name": "", "tag": "", "expected": "KTAG__"}, + {"name": "", "tag": "attr", "expected": "KTAG_attr_"}, + {"name": "a", "tag": "attr", "expected": "KTAG_attr_a"}, + {"name": "a.b", "tag": "attr", "expected": "KTAG_attr_a.b"}, + ] + for case in cases: + name, tag, expected = case["name"], case["tag"], case["expected"] + self.assertEqual(kcl_info.tagging(tag, name), expected) + + def test_detagging(self): + cases = [ + {"name": "", "tag": "", "expected": ""}, + {"name": "KTAG_attr_", "tag": "attr", "expected": ""}, + {"name": "KTAG_attr_a", "tag": "attr", "expected": "a"}, + {"name": "KTAG_attr_a.b", "tag": "attr", "expected": "a.b"}, + ] + for case in cases: + name, tag, expected = case["name"], case["tag"], case["expected"] + self.assertEqual(kcl_info.detagging(tag, name), expected) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_program/test_exec/test_runner.py b/test/test_units/test_kclvm/test_program/test_exec/test_runner.py new file mode 100644 index 000000000..5112d7646 --- /dev/null +++ b/test/test_units/test_kclvm/test_program/test_exec/test_runner.py @@ -0,0 +1,24 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import unittest +import pathlib + +import kclvm.program.exec.runner as runner + + +class KCLCompilerBuildCacheTest(unittest.TestCase): + def test_schema_infer(self): + test_path = pathlib.Path(__file__).parent.joinpath("testdata/schema_infer") + test_main = test_path.joinpath("main.k") + result = runner.Run([str(test_main)], work_dir=str(test_path)) + self.assertEqual(list(result.filter_by_path_selector().keys()), ['Person', 'x0', 'x1', 'x2', '@pkg']) + + def _test_schema_infer_native(self): + test_path = pathlib.Path(__file__).parent.joinpath("testdata/schema_infer") + test_main = test_path.joinpath("main.k") + result = runner.Run([str(test_main)], work_dir=str(test_path), target="native") + self.assertEqual(list(result.filter_by_path_selector().keys()), ['Person', 'x0', 'x1', 'x2', '@pkg']) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_program/test_exec/testdata/schema_infer/kcl.mod b/test/test_units/test_kclvm/test_program/test_exec/testdata/schema_infer/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_program/test_exec/testdata/schema_infer/main.k b/test/test_units/test_kclvm/test_program/test_exec/testdata/schema_infer/main.k new file mode 100644 index 000000000..309c70eae --- /dev/null +++ b/test/test_units/test_kclvm/test_program/test_exec/testdata/schema_infer/main.k @@ -0,0 +1,14 @@ +import pkg + +schema Person: + name: str = "kcl" + age: int = 1 + +x0: pkg.Person = { + age = 101 +} + +x1: Person = { + age = 101 +} +x2: Person = Person() diff --git a/test/test_units/test_kclvm/test_program/test_exec/testdata/schema_infer/pkg/pkg.k b/test/test_units/test_kclvm/test_program/test_exec/testdata/schema_infer/pkg/pkg.k new file mode 100644 index 000000000..a49235a51 --- /dev/null +++ b/test/test_units/test_kclvm/test_program/test_exec/testdata/schema_infer/pkg/pkg.k @@ -0,0 +1,3 @@ +schema Person: + name: str = "kcl" + age: int = 1 diff --git a/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/eval-code.json b/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/eval-code.json new file mode 100644 index 000000000..b1609343c --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/eval-code.json @@ -0,0 +1,3 @@ +{ + "code": "schema Data:\n foo: int = 1\n bar: str = 'ss'\n\ndata = ata {}" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/exec-program.json b/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/exec-program.json new file mode 100644 index 000000000..699059a78 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/exec-program.json @@ -0,0 +1,9 @@ + +{ + "k_filename_list":[ + "hello.k" + ], + "k_code_list":[ + "a=b" + ] +} diff --git a/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/get-schema.json b/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/get-schema.json new file mode 100644 index 000000000..df3c78203 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/get-schema.json @@ -0,0 +1,3 @@ +{ + "code": "\nschema Person:\n key: str\n val:Str\n check:\n \"value\" in key # 'key' is required and 'key' must contain \"value\"\n" +} diff --git a/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/resolve-code.json b/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/resolve-code.json new file mode 100644 index 000000000..bd996995d --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/resolve-code.json @@ -0,0 +1,3 @@ +{ + "code": "schema Data:\n foo: int = 1\n bar: str = 1\n\ndata = Data {}" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/splice-code.json b/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/splice-code.json new file mode 100644 index 000000000..3df2d69be --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/splice-code.json @@ -0,0 +1,8 @@ +{ + "codeSnippets": [ + { + "schema": "schema Person:1\n age: int\n", + "rule": "age > 0" + } + ] +} diff --git a/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/vet-simple.json b/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/vet-simple.json new file mode 100644 index 000000000..cfa09e5cf --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/invalid_testdata/vet-simple.json @@ -0,0 +1,4 @@ +{ + "code": "import regex\n\nschema Sample:\n foo: str # Required, 不能为None, 且类型必须为str\n bar: int # Required, 不能为None, 且类型必须为int\n fooList: [str] # Required, 不能为None, 且类型必须为str列表\n color: \"Red\" | \"Yellow\" | \"Blue\" # Required, 字面值联合类型,且必须为\"Red\", \"Yellow\", \"Blue\"中的一个,枚举作用\n id?: int # Optional,可以留空,类型必须为int\n \n check:\n bar >= 0 # bar必须大于等于0\n bar < 100 # bar必须小于100\n len(fooList) > 0 # fooList不能为None,并且长度必须大于0\n len(fooList) < 100 # fooList不能为None,并且长度必须小于100\n regex.match(foo, \"^The.*Foo$\") # regex 正则表达式匹配\n bar in range(100) # range, bar范围只能为1到99\n bar in [2, 4, 6, 8] # enum, bar只能取2, 4, 6, 8\n bar % 2 == 0 # bar必须为2的倍数\n abs(id) > 10 if id is not None # check if 表达式,当 id 不为空时,id的绝对值必须大于10\n", + "data": "{\"attr_name\":{\n \"foo\": \"ThehFoo\",\n \"bar\": 3,\n \"fooList\": [\n \"a\",\n \"b\"\n ],\n \"color\": \"Red\",\n \"id\": 100\n}}" +} diff --git a/test/test_units/test_kclvm/test_rpc_server/test_rpc_server.py b/test/test_units/test_kclvm/test_rpc_server/test_rpc_server.py new file mode 100644 index 000000000..43c5be39b --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/test_rpc_server.py @@ -0,0 +1,140 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import time +import json +import pathlib +import unittest +import subprocess +import requests +import os + +import kclvm.internal.util as util + + +SERVER_CMD = "kclvm -m kclvm.program.rpc-server -http=127.0.0.1:2021" +HEADERS = { + "content-type": "accept/json", +} + + +class TestRpcServer(unittest.TestCase): + def setUp(self): + self.p = subprocess.Popen(SERVER_CMD.split()) + # Sleep enough time to establish connection + time.sleep(10) + super().setUp() + + def tearDown(self): + self.p.kill() + super().tearDown() + + def test_rpc_server_normal(self): + apis = [ + "EvalCode", + "ExecProgram", + "GetSchemaType", + "ResolveCode", + "SpliceCode", + "ValidateCode", + "ValidateCode", + "ValidateCode", + ] + data_files = [ + "eval-code", + "exec-program", + "get-schema", + "resolve-code", + "splice-code", + "vet-hello", + "vet-simple", + "vet-single", + ] + test_path = "testdata" + for api, data_file in zip(apis, data_files): + try: + json_file = pathlib.Path(__file__).parent.joinpath( + f"{test_path}/{data_file}.json" + ) + json_data = json_file.read_text(encoding="utf-8") + res = requests.post( + f"http://127.0.0.1:2021/api:protorpc/KclvmService.{api}", + data=json_data.encode("utf-8"), + headers=HEADERS, + ) + res_data = res.json() + self.assertEqual( + res_data["error"], "", msg=f"api: {api}, data_file: {data_file}" + ) + except Exception as err: + self.assertTrue(False, msg=f"api: {api}, data_file: {data_file}, reason: {err}") + continue + + def test_rpc_server_invalid(self): + apis = [ + "EvalCode", + "ExecProgram", + "GetSchemaType", + "ResolveCode", + "SpliceCode", + "ValidateCode", + ] + data_files = [ + "eval-code", + "exec-program", + "get-schema", + "resolve-code", + "splice-code", + "vet-simple", + ] + test_path = "invalid_testdata" + for api, data_file in zip(apis, data_files): + try: + json_file = pathlib.Path(__file__).parent.joinpath( + f"{test_path}/{data_file}.json" + ) + json_data = json_file.read_text(encoding="utf-8") + res = requests.post( + f"http://127.0.0.1:2021/api:protorpc/KclvmService.{api}", + data=json_data.encode("utf-8"), + headers=HEADERS, + ) + res_data = res.json() + self.assertTrue( + bool(res_data["error"]), msg=f"api: {api}, data_file: {data_file}" + ) + except: + self.assertTrue(False, msg=f"api: {api}, data_file: {data_file}") + continue + + # TODO: enable this test after the kcl-go upgraded + def _test_rpc_server_ListDepFiles(self): + appWorkDir = pathlib.Path(__file__).parent.joinpath(f"testdata/kcl-module/app0") + + api = "ListDepFiles" + json_data = f"{{\"work_dir\": \"{appWorkDir}\"}}" + res = requests.post( + f"http://127.0.0.1:2021/api:protorpc/KclvmService.{api}", + data=json_data.encode("utf-8"), + headers=HEADERS, + ) + + res_data = res.json() + self.assertEqual(res_data["error"], "", msg=f"api: {api}, res_data: {res_data}, appWorkDir: {appWorkDir}") + self.assertTrue(res_data["result"], msg=f"api: {api}, res_data: {res_data}, appWorkDir: {appWorkDir}") + + expect = [ + "main.k", + "app0/before/base.k", + "app0/main.k", + "app0/sub/sub.k", + ] + + self.assertEqual( + sorted(res_data["result"]["files"]), + sorted(expect), + msg=f"api: {api}, res_data: {res_data}, expect: {expect}, appWorkDir: {appWorkDir}" + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/eval-code.json b/test/test_units/test_kclvm/test_rpc_server/testdata/eval-code.json new file mode 100644 index 000000000..ac6306d99 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/eval-code.json @@ -0,0 +1,3 @@ +{ + "code": "schema Data:\n foo: int = 1\n bar: str = 'ss'\n\ndata = Data {}" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/eval-code.response.json b/test/test_units/test_kclvm/test_rpc_server/testdata/eval-code.response.json new file mode 100644 index 000000000..f0f22018f --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/eval-code.response.json @@ -0,0 +1,6 @@ +{ + "result": { + "json_result": "{\n \"data\": {\n \"foo\": 1,\n \"bar\": \"ss\"\n }\n}" + }, + "error": "" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/exec-program.json b/test/test_units/test_kclvm/test_rpc_server/testdata/exec-program.json new file mode 100644 index 000000000..864eab235 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/exec-program.json @@ -0,0 +1,9 @@ + +{ + "k_filename_list":[ + "hello.k" + ], + "k_code_list":[ + "a=1" + ] +} diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/exec-program.response.json b/test/test_units/test_kclvm/test_rpc_server/testdata/exec-program.response.json new file mode 100644 index 000000000..39e04cab6 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/exec-program.response.json @@ -0,0 +1,8 @@ +{ + "result": { + "json_result": "[\n {\n \"a\": 1\n }\n]", + "yaml_result": "a: 1\n", + "escaped_time": "0.002061128616333008" + }, + "error": "" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/get-schema.json b/test/test_units/test_kclvm/test_rpc_server/testdata/get-schema.json new file mode 100644 index 000000000..5f374d679 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/get-schema.json @@ -0,0 +1,3 @@ +{ + "code": "\nschema Person:\n key: str\n\n check:\n \"value\" in key # 'key' is required and 'key' must contain \"value\"\n" +} diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/get-schema.response.json b/test/test_units/test_kclvm/test_rpc_server/testdata/get-schema.response.json new file mode 100644 index 000000000..639ed96ac --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/get-schema.response.json @@ -0,0 +1,28 @@ +{ + "result": { + "schema_type_list": [ + { + "type": "schema", + "schema_name": "Person", + "properties": { + "key": { + "type": "str", + "line": 1, + "union_types": [], + "default": "", + "schema_name": "", + "schema_doc": "", + "properties": {}, + "required": [] + } + }, + "union_types": [], + "default": "", + "schema_doc": "", + "required": [], + "line": 0 + } + ] + }, + "error": "" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/app0/before/base.k b/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/app0/before/base.k new file mode 100644 index 000000000..62b769890 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/app0/before/base.k @@ -0,0 +1 @@ +base1 = 1 diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/app0/kcl.yaml b/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/app0/kcl.yaml new file mode 100644 index 000000000..7bb840a6e --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/app0/kcl.yaml @@ -0,0 +1,10 @@ +kcl_cli_configs: + file: + - ../main.k + - ./before/base.k + - main.k + - ${KCL_MOD}/app0/sub/sub.k + + disable_none: false + strict_range_check: false + debug: false diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/app0/main.k b/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/app0/main.k new file mode 100644 index 000000000..d8b72a0eb --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/app0/main.k @@ -0,0 +1,9 @@ +import .sub as sub + +main1=3 + +app_name = option("app-name") +image = option("image") +deploy_topology = option("deploy-topology") + +x = deploy_topology[1] diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/app0/sub/sub.k b/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/app0/sub/sub.k new file mode 100644 index 000000000..64d5c3823 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/app0/sub/sub.k @@ -0,0 +1 @@ +sub1 = 2 diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/kcl.mod b/test/test_units/test_kclvm/test_rpc_server/testdata/kcl-module/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/nginx.conf b/test/test_units/test_kclvm/test_rpc_server/testdata/nginx.conf new file mode 100644 index 000000000..349a973a2 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/nginx.conf @@ -0,0 +1,20 @@ +# nginx -c ./nginx.conf + +events {} + +http { + upstream kclvm_service { + server 127.0.0.1:3021 weight=1; + server 127.0.0.1:3022 weight=1; + server 127.0.0.1:3023 weight=1; + server 127.0.0.1:3024 weight=1; + } + + server { + listen 2021; + server_name localhost; + location / { + proxy_pass http://kclvm_service; + } + } +} diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/resolve-code.json b/test/test_units/test_kclvm/test_rpc_server/testdata/resolve-code.json new file mode 100644 index 000000000..ac6306d99 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/resolve-code.json @@ -0,0 +1,3 @@ +{ + "code": "schema Data:\n foo: int = 1\n bar: str = 'ss'\n\ndata = Data {}" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/resolve-code.response.json b/test/test_units/test_kclvm/test_rpc_server/testdata/resolve-code.response.json new file mode 100644 index 000000000..877e5b33c --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/resolve-code.response.json @@ -0,0 +1,6 @@ +{ + "result": { + "success": true + }, + "error": "" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/splice-code.json b/test/test_units/test_kclvm/test_rpc_server/testdata/splice-code.json new file mode 100644 index 000000000..2b796b552 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/splice-code.json @@ -0,0 +1,8 @@ +{ + "codeSnippets": [ + { + "schema": "schema Person:\n age: int\n", + "rule": "age > 0" + } + ] +} diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/splice-code.response.json b/test/test_units/test_kclvm/test_rpc_server/testdata/splice-code.response.json new file mode 100644 index 000000000..efd1f6633 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/splice-code.response.json @@ -0,0 +1,6 @@ +{ + "result": { + "spliceCode": "schema Person:\n age: int\n\n check:\n age > 0\n" + }, + "error": "" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/vet-hello.json b/test/test_units/test_kclvm/test_rpc_server/testdata/vet-hello.json new file mode 100644 index 000000000..4997d0458 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/vet-hello.json @@ -0,0 +1,4 @@ +{ + "code": "\nschema Person:\n key: str\n\n check:\n \"value\" in key # 'key' is required and 'key' must contain \"value\"\n", + "data": "{\"attr_name\": {\"key\": \"value\"}}" +} diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/vet-hello.response.json b/test/test_units/test_kclvm/test_rpc_server/testdata/vet-hello.response.json new file mode 100644 index 000000000..e32421862 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/vet-hello.response.json @@ -0,0 +1,7 @@ +{ + "result": { + "success": true, + "err_message": "" + }, + "error": "" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/vet-simple.json b/test/test_units/test_kclvm/test_rpc_server/testdata/vet-simple.json new file mode 100644 index 000000000..13a004f73 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/vet-simple.json @@ -0,0 +1,4 @@ +{ + "code": "import regex\n\nschema Sample:\n foo: str # Required, 不能为None, 且类型必须为str\n bar: int # Required, 不能为None, 且类型必须为int\n fooList: [str] # Required, 不能为None, 且类型必须为str列表\n color: \"Red\" | \"Yellow\" | \"Blue\" # Required, 字面值联合类型,且必须为\"Red\", \"Yellow\", \"Blue\"中的一个,枚举作用\n id?: int # Optional,可以留空,类型必须为int\n \n check:\n bar >= 0 # bar必须大于等于0\n bar < 100 # bar必须小于100\n len(fooList) > 0 # fooList不能为None,并且长度必须大于0\n len(fooList) < 100 # fooList不能为None,并且长度必须小于100\n regex.match(foo, \"^The.*Foo$\") # regex 正则表达式匹配\n bar in range(100) # range, bar范围只能为1到99\n bar in [2, 4, 6, 8] # enum, bar只能取2, 4, 6, 8\n bar % 2 == 0 # bar必须为2的倍数\n abs(id) > 10 if id is not None # check if 表达式,当 id 不为空时,id的绝对值必须大于10\n", + "data": "{\"attr_name\":{\n \"foo\": \"ThehFoo\",\n \"bar\": 2,\n \"fooList\": [\n \"a\",\n \"b\"\n ],\n \"color\": \"Red\",\n \"id\": 100\n}}" +} diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/vet-simple.response.json b/test/test_units/test_kclvm/test_rpc_server/testdata/vet-simple.response.json new file mode 100644 index 000000000..e32421862 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/vet-simple.response.json @@ -0,0 +1,7 @@ +{ + "result": { + "success": true, + "err_message": "" + }, + "error": "" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/vet-single.json b/test/test_units/test_kclvm/test_rpc_server/testdata/vet-single.json new file mode 100644 index 000000000..2fc93142c --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/vet-single.json @@ -0,0 +1,4 @@ +{ + "code": "assert mname==\"100\"", + "data": "{\"mname\": \"100\"}" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/vet-single.response.json b/test/test_units/test_kclvm/test_rpc_server/testdata/vet-single.response.json new file mode 100644 index 000000000..e32421862 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/vet-single.response.json @@ -0,0 +1,7 @@ +{ + "result": { + "success": true, + "err_message": "" + }, + "error": "" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/vet/hello.k b/test/test_units/test_kclvm/test_rpc_server/testdata/vet/hello.k new file mode 100644 index 000000000..ecf5fd5b3 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/vet/hello.k @@ -0,0 +1,6 @@ + +schema Person: + key: str + + check: + "value" in key # 'key' is required and 'key' must contain "value" diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/vet/hello.k.json b/test/test_units/test_kclvm/test_rpc_server/testdata/vet/hello.k.json new file mode 100644 index 000000000..9082d25ef --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/vet/hello.k.json @@ -0,0 +1 @@ +{"key": "value"} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/vet/simple.k b/test/test_units/test_kclvm/test_rpc_server/testdata/vet/simple.k new file mode 100644 index 000000000..1ed734eea --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/vet/simple.k @@ -0,0 +1,23 @@ +import regex +schema Sample: + foo: str # Required, 不能为None, 且类型必须为str + bar: int # Required, 不能为None, 且类型必须为int + fooList: [int] # Required, 不能为None, 且类型必须为str列表 + color: "Red" | "Yellow" | "Blue" # Required, 字面值联合类型,且必须为"Red", "Yellow", "Blue"中的一个,枚举作用 + id?: int # Optional,可以留空,类型必须为int + check: + bar >= 0 # bar必须大于等于0 + bar < 100 # bar必须小于100 + len(fooList) > 0 # fooList不能为None,并且长度必须大于0 + len(fooList) < 100 # fooList不能为None,并且长度必须小于100 + regex.match(foo, "^The.*Foo$") # regex 正则表达式匹配 + bar in range(100) # range, bar范围只能为1到99 + bar in [2, 4, 6, 8] # enum, bar只能取2, 4, 6, 8 + bar % 2 == 0 # bar必须为2的倍数 + all foo in fooList { + foo > 1 + } # fooList中的所有元素必须大于1 + any foo in fooList { + foo > 10 + } # fooList中至少有一个元素必须大于10 + abs(id) > 10 if id is not None # check if 表达式,当 id 不为空时,id的绝对值必须大于10 diff --git a/test/test_units/test_kclvm/test_rpc_server/testdata/vet/simple.k.json b/test/test_units/test_kclvm/test_rpc_server/testdata/vet/simple.k.json new file mode 100644 index 000000000..d6d403b90 --- /dev/null +++ b/test/test_units/test_kclvm/test_rpc_server/testdata/vet/simple.k.json @@ -0,0 +1,10 @@ +{ + "foo": "good boy", + "bar": 100, + "fooList": [ + "a", + "b" + ], + "color": "Red", + "id": 100 +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_runtime/test_lazy_eval.py b/test/test_units/test_kclvm/test_runtime/test_lazy_eval.py new file mode 100644 index 000000000..27ebb8a57 --- /dev/null +++ b/test/test_units/test_kclvm/test_runtime/test_lazy_eval.py @@ -0,0 +1,68 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import unittest + +import kclvm.api.object as obj +import kclvm.vm as vm + +from kclvm.vm.runtime.evaluator import ValueCache, Backtracking + +KEY_FIELD = "key" +VALUE_FIELD = "value" + + +class KCLSchemaLazyEvalTest(unittest.TestCase): + def test_cache(self): + """Runtime cache test""" + values = [ + 1, + 1.1, + "s", + True, + False, + None, + [], + {}, + [1, 2, 3], + {"key": "value"}, + ] + cases = [ + {KEY_FIELD: KEY_FIELD + str(i), VALUE_FIELD: obj.to_kcl_obj(v)} + for i, v in enumerate(values) + ] + cache = ValueCache() + for case in cases: + cache.set(case[KEY_FIELD], case[VALUE_FIELD]) + self.assertEqual(cache.get(case[KEY_FIELD]), case[VALUE_FIELD]) + + def test_back_track(self): + name = "key" + err_name = "err" + backtracking = Backtracking() + self.assertEqual(backtracking.is_backtracking(name), False) + with backtracking.catch(name): + level = backtracking.tracking_level(name) + self.assertEqual(level, 1) + self.assertEqual(backtracking.is_backtracking(name), True) + self.assertEqual(backtracking.is_backtracking(err_name), False) + with backtracking.catch(name): + level = backtracking.tracking_level(name) + self.assertEqual(level, 2) + self.assertEqual(backtracking.is_backtracking(name), True) + with backtracking.catch(name): + level = backtracking.tracking_level(name) + self.assertEqual(level, 3) + self.assertEqual(backtracking.is_backtracking(name), True) + with backtracking.catch(name): + level = backtracking.tracking_level(name) + self.assertEqual(level, 2) + self.assertEqual(backtracking.is_backtracking(name), True) + level = backtracking.tracking_level(name) + self.assertEqual(level, 1) + self.assertEqual(backtracking.is_backtracking(name), True) + self.assertEqual(backtracking.is_backtracking(err_name), False) + self.assertEqual(backtracking.is_backtracking(name), False) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_runtime/test_union.py b/test/test_units/test_kclvm/test_runtime/test_union.py new file mode 100644 index 000000000..205f239db --- /dev/null +++ b/test/test_units/test_kclvm/test_runtime/test_union.py @@ -0,0 +1,294 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import unittest + +import kclvm.kcl.ast as ast +import kclvm.kcl.error as kcl_error +import kclvm.api.object as obj +import kclvm.vm as vm +from kclvm.vm.runtime.evaluator.union import union, resolve_schema_obj + +VALUE1_KEY = "value1" +VALUE2_KEY = "value2" +EXPECTED_KEY = "expected" + + +class TestUnion(unittest.TestCase): + def get_vm(self): + app = obj.KCLProgram() + app.pkgs = {"testpkg": obj.KCLBytecode()} + app.main = "testpkg" + test_vm = vm.VirtualMachine(app=app) + test_f = vm.Frame() + test_f.locals = {} + test_f.globals = {} + test_vm.ctx = test_f + return test_vm + + def test_union(self): + cases = [ + # Left None + {VALUE1_KEY: None, VALUE2_KEY: 1, EXPECTED_KEY: 1}, + {VALUE1_KEY: None, VALUE2_KEY: 1.1, EXPECTED_KEY: 1.1}, + {VALUE1_KEY: None, VALUE2_KEY: [], EXPECTED_KEY: []}, + {VALUE1_KEY: None, VALUE2_KEY: {}, EXPECTED_KEY: {}}, + {VALUE1_KEY: None, VALUE2_KEY: "s", EXPECTED_KEY: "s"}, + {VALUE1_KEY: None, VALUE2_KEY: True, EXPECTED_KEY: True}, + {VALUE1_KEY: None, VALUE2_KEY: False, EXPECTED_KEY: False}, + {VALUE1_KEY: None, VALUE2_KEY: None, EXPECTED_KEY: None}, + # Right None + {VALUE1_KEY: 1, VALUE2_KEY: None, EXPECTED_KEY: 1}, + {VALUE1_KEY: 1.1, VALUE2_KEY: None, EXPECTED_KEY: 1.1}, + {VALUE1_KEY: [], VALUE2_KEY: None, EXPECTED_KEY: []}, + {VALUE1_KEY: {}, VALUE2_KEY: None, EXPECTED_KEY: {}}, + {VALUE1_KEY: "s", VALUE2_KEY: None, EXPECTED_KEY: "s"}, + {VALUE1_KEY: True, VALUE2_KEY: None, EXPECTED_KEY: True}, + {VALUE1_KEY: False, VALUE2_KEY: None, EXPECTED_KEY: False}, + {VALUE1_KEY: None, VALUE2_KEY: None, EXPECTED_KEY: None}, + # Int + {VALUE1_KEY: 1, VALUE2_KEY: 1, EXPECTED_KEY: 1}, + {VALUE1_KEY: 1, VALUE2_KEY: 2, EXPECTED_KEY: 2}, + {VALUE1_KEY: 1, VALUE2_KEY: 3, EXPECTED_KEY: 3}, + # Float + {VALUE1_KEY: 1.0, VALUE2_KEY: 1.0, EXPECTED_KEY: 1.0}, + {VALUE1_KEY: 1.0, VALUE2_KEY: 1.5, EXPECTED_KEY: 1.5}, + # String + {VALUE1_KEY: "s", VALUE2_KEY: "", EXPECTED_KEY: ""}, + {VALUE1_KEY: "s", VALUE2_KEY: "s", EXPECTED_KEY: "s"}, + {VALUE1_KEY: "s", VALUE2_KEY: "ss", EXPECTED_KEY: "ss"}, + # Boolean True + {VALUE1_KEY: True, VALUE2_KEY: True, EXPECTED_KEY: True}, + {VALUE1_KEY: True, VALUE2_KEY: False, EXPECTED_KEY: False}, + # Boolean False + {VALUE1_KEY: False, VALUE2_KEY: False, EXPECTED_KEY: False}, + {VALUE1_KEY: False, VALUE2_KEY: True, EXPECTED_KEY: True}, + # List + {VALUE1_KEY: [], VALUE2_KEY: [], EXPECTED_KEY: []}, + {VALUE1_KEY: [], VALUE2_KEY: [1], EXPECTED_KEY: [1]}, + {VALUE1_KEY: [], VALUE2_KEY: [1, 2], EXPECTED_KEY: [1, 2]}, + {VALUE1_KEY: [1], VALUE2_KEY: [1], EXPECTED_KEY: [1]}, + {VALUE1_KEY: [1], VALUE2_KEY: [2], EXPECTED_KEY: [2]}, + {VALUE1_KEY: [1], VALUE2_KEY: [2, 2], EXPECTED_KEY: [2, 2]}, + {VALUE1_KEY: [1, 2], VALUE2_KEY: [3, 4], EXPECTED_KEY: [3, 4]}, + {VALUE1_KEY: [1, 2, 3], VALUE2_KEY: [3, 4], EXPECTED_KEY: [3, 4, 3]}, + { + VALUE1_KEY: [{"key1": "value1"}], + VALUE2_KEY: [{"key1": "value1"}], + EXPECTED_KEY: [{"key1": "value1"}], + }, + { + VALUE1_KEY: [{"key1": "value1"}], + VALUE2_KEY: [{"key1": "value2"}], + EXPECTED_KEY: [{"key1": "value2"}], + }, + { + VALUE1_KEY: [{"key1": "value1"}], + VALUE2_KEY: [{"key2": "value2"}], + EXPECTED_KEY: [{"key1": "value1", "key2": "value2"}], + }, + { + VALUE1_KEY: [{"key1": "value1"}], + VALUE2_KEY: [{"key1": "value1", "key2": "value2"}], + EXPECTED_KEY: [{"key1": "value1", "key2": "value2"}], + }, + { + VALUE1_KEY: [{"key1": "value1"}], + VALUE2_KEY: [{"key2": "value2", "key1": "value1"}], + EXPECTED_KEY: [{"key1": "value1", "key2": "value2"}], + }, + { + VALUE1_KEY: [{"key1": "value1", "key2": "value2"}], + VALUE2_KEY: [{"key1": "value1"}], + EXPECTED_KEY: [{"key1": "value1", "key2": "value2"}], + }, + # Dict + {VALUE1_KEY: {}, VALUE2_KEY: {}, EXPECTED_KEY: {}}, + { + VALUE1_KEY: {}, + VALUE2_KEY: {"key1": "value1"}, + EXPECTED_KEY: {"key1": "value1"}, + }, + { + VALUE1_KEY: {"key1": "value1"}, + VALUE2_KEY: {}, + EXPECTED_KEY: {"key1": "value1"}, + }, + { + VALUE1_KEY: {"key1": "value1"}, + VALUE2_KEY: {"key1": "value1"}, + EXPECTED_KEY: {"key1": "value1"}, + }, + { + VALUE1_KEY: {"key1": "value1"}, + VALUE2_KEY: {"key1": "value1", "key2": "value2"}, + EXPECTED_KEY: {"key1": "value1", "key2": "value2"}, + }, + { + VALUE1_KEY: {"key1": "value1", "key2": "value2"}, + VALUE2_KEY: {"key1": "value1"}, + EXPECTED_KEY: {"key1": "value1", "key2": "value2"}, + }, + { + VALUE1_KEY: {"s": {"key1": "value1"}}, + VALUE2_KEY: {"s": {"key1": "value1", "key2": "value2"}}, + EXPECTED_KEY: {"s": {"key1": "value1", "key2": "value2"}}, + }, + { + VALUE1_KEY: {"s": {"key1": "value1", "key2": "value2"}}, + VALUE2_KEY: {"s": {"key1": "value1"}}, + EXPECTED_KEY: {"s": {"key1": "value1", "key2": "value2"}}, + }, + { + VALUE1_KEY: {"key2": "value2", "key1": "value1"}, + VALUE2_KEY: {"key1": "value1", "key2": "value2"}, + EXPECTED_KEY: {"key1": "value1", "key2": "value2"}, + }, + { + VALUE1_KEY: {"key2": "value2", "key1": "value1"}, + VALUE2_KEY: {"key1": "value1", "key2": "value2"}, + EXPECTED_KEY: {"key1": "value1", "key2": "value2"}, + }, + ] + for case in cases: + try: + value1 = obj.to_kcl_obj(case[VALUE1_KEY]) + value2 = obj.to_kcl_obj(case[VALUE2_KEY]) + union_value = obj.to_python_obj(union(value1, value2)) + expected = obj.to_python_obj(case[EXPECTED_KEY]) + self.assertEqual(union_value, expected) + except AssertionError as err: + print( + f"Assert fail between the value1 {value1} and the value2 {value2}" + ) + raise err + + def test_union_with_idempotent_check(self): + cases = [ + { + VALUE1_KEY: obj.KCLSchemaConfigObject( + value=obj.to_kcl_obj({"key": [0]}).value, + ), + VALUE2_KEY: obj.KCLSchemaConfigObject( + value=obj.to_kcl_obj({"key": [1]}).value, + operation_map={"key": ast.ConfigEntryOperation.INSERT}, + insert_index_map={"key": -1}, + ), + EXPECTED_KEY: {"key": [0, 1]}, + }, + { + VALUE1_KEY: obj.KCLSchemaConfigObject( + value=obj.to_kcl_obj({"key": [0]}).value, + ), + VALUE2_KEY: obj.KCLSchemaConfigObject( + value=obj.to_kcl_obj({"key": 1}).value, + operation_map={"key": ast.ConfigEntryOperation.INSERT}, + insert_index_map={"key": 0}, + ), + EXPECTED_KEY: {"key": [1, 0]}, + }, + { + VALUE1_KEY: obj.KCLSchemaObject( + attrs=obj.to_kcl_obj({"key": [0]}).value, + ), + VALUE2_KEY: obj.KCLSchemaObject( + attrs=obj.to_kcl_obj({"key": 1}).value, + operation_map={"key": ast.ConfigEntryOperation.INSERT}, + insert_index_map={"key": 0}, + ), + EXPECTED_KEY: {"key": [1, 0]}, + }, + { + VALUE1_KEY: obj.KCLSchemaObject( + attrs=obj.to_kcl_obj({"key": [0]}).value, + ), + VALUE2_KEY: obj.KCLSchemaObject( + attrs=obj.to_kcl_obj({"key": [1]}).value, + operation_map={"key": ast.ConfigEntryOperation.OVERRIDE}, + ), + EXPECTED_KEY: {"key": [1]}, + }, + { + VALUE1_KEY: obj.KCLSchemaObject( + attrs=obj.to_kcl_obj({"key": [0, 1]}).value, + operation_map={"key": ast.ConfigEntryOperation.OVERRIDE}, + ), + VALUE2_KEY: obj.KCLSchemaObject( + attrs=obj.to_kcl_obj({"key": obj.Undefined}).value, + operation_map={"key": ast.ConfigEntryOperation.OVERRIDE}, + insert_index_map={"key": 0}, + ), + EXPECTED_KEY: {"key": [1]}, + }, + ] + invalid_cases = [ + { + VALUE1_KEY: {"key": 1}, + VALUE2_KEY: {"key": 2}, + }, + { + VALUE1_KEY: {"key": 1.0}, + VALUE2_KEY: {"key": 2.0}, + }, + { + VALUE1_KEY: {"key": True}, + VALUE2_KEY: {"key": False}, + }, + { + VALUE1_KEY: obj.KCLSchemaConfigObject( + value=obj.to_kcl_obj({"key": [0]}).value, + ), + VALUE2_KEY: obj.KCLSchemaConfigObject( + value=obj.to_kcl_obj({"key": [1]}).value, + ), + }, + { + VALUE1_KEY: obj.KCLSchemaObject( + attrs=obj.to_kcl_obj({"key": [0]}).value, + ), + VALUE2_KEY: obj.KCLSchemaObject( + attrs=obj.to_kcl_obj({"key": [1]}).value, + ), + }, + ] + for case in cases: + try: + value1 = obj.to_kcl_obj(case[VALUE1_KEY]) + value2 = obj.to_kcl_obj(case[VALUE2_KEY]) + union_value = obj.to_python_obj( + union(value1, value2, should_idempotent_check=True) + ) + expected = obj.to_python_obj(case[EXPECTED_KEY]) + self.assertEqual(union_value, expected) + except AssertionError as err: + print( + f"Assert fail between the value1 {value1} and the value2 {value2}" + ) + raise err + for case in invalid_cases: + value1 = obj.to_kcl_obj(case[VALUE1_KEY]) + value2 = obj.to_kcl_obj(case[VALUE2_KEY]) + with self.assertRaises(kcl_error.KCLException): + union(value1, value2, should_idempotent_check=True) + + def test_resolve_schema_obj(self): + cases = [ + {"schema_obj": None, "keys": None, "vm": None, "expected": None}, + { + "schema_obj": obj.KCLSchemaObject(name="Person"), + "keys": set(), + "vm": self.get_vm(), + "expected": obj.KCLSchemaObject(name="Person"), + }, + ] + for case in cases: + schema_obj, keys, vm, expected = ( + case["schema_obj"], + case["keys"], + case["vm"], + case["expected"], + ) + self.assertEqual(resolve_schema_obj(schema_obj, keys, vm), expected) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_base_schema_pkg_en/base_schema_pkg/base/doc_person_en.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_base_schema_pkg_en/base_schema_pkg/base/doc_person_en.md new file mode 100644 index 000000000..4bf12d7db --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_base_schema_pkg_en/base_schema_pkg/base/doc_person_en.md @@ -0,0 +1,13 @@ +# person + +Source: [base_schema_pkg/base/person.k](https://url/to/source_code/base_schema_pkg/base/person.k) + +## Schema Person + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**name**|str|Undefined|**required**| +|**age**|int|Undefined|**required**| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_base_schema_pkg_en/base_schema_pkg/sub/doc_student_en.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_base_schema_pkg_en/base_schema_pkg/sub/doc_student_en.md new file mode 100644 index 000000000..eb757e9b8 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_base_schema_pkg_en/base_schema_pkg/sub/doc_student_en.md @@ -0,0 +1,17 @@ +# student + +Source: [base_schema_pkg/sub/student.k](https://url/to/source_code/base_schema_pkg/sub/student.k) + +## Schema Student + +Student is the person with a grade + +### Base Schema +[@base_schema_pkg.base.Person](../base/doc_person_en#schema-person) + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**grade**
    the current grade that the student is in.|int|Undefined|**required**| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_config_map_en/config_map/core/doc_metadata_en.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_config_map_en/config_map/core/doc_metadata_en.md new file mode 100644 index 000000000..126f9794e --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_config_map_en/config_map/core/doc_metadata_en.md @@ -0,0 +1,17 @@ +# metadata + +Source: [config_map/core/metadata.k](https://url/to/source_code/config_map/core/metadata.k) + +## Schema Metadata + +Metadata is the base schema of all models, which contains data
    that helps uniquely identify the object. + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**name**
    The name of the resource.
    Name must be unique within a namespace. It's required when creating
    resources, although some resources may allow a client to request the
    generation of an appropriate name automatically.
    Name is primarily intended for creation idempotence and configuration
    definition. Cannot be updated. More info:
    http://kubernetes.io/docs/user-guide/identifiers\#names|str|Undefined|**required**| +|**labels**
    Labels is a map of string keys and values that can be used to
    organize and categorize (scope and select) objects.
    May match selectors of replication controllers and services.
    More info: http://kubernetes.io/docs/user-guide/labels|{str: str}|Undefined|optional| +|**annotations**
    Annotations is an unstructured key value map stored with a
    resource that may be set by external tools to store and retrieve
    arbitrary metadata. They are not queryable and should be preserved
    when modifying objects.
    More info: http://kubernetes.io/docs/user-guide/annotations|{str: str}|Undefined|optional| +|**namespace**
    Namespaces are intended for use in environments with many users spread
    across multiple teams, or projects.
    For clusters with a few to tens of users, you should not need to create
    or think about namespaces at all. Start using namespaces when you need the features they provide.
    More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/|str|Undefined|optional| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_config_map_en/config_map/core/v1/doc_config_map_en.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_config_map_en/config_map/core/v1/doc_config_map_en.md new file mode 100644 index 000000000..b0d46f892 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_config_map_en/config_map/core/v1/doc_config_map_en.md @@ -0,0 +1,16 @@ +# config_map + +Source: [config_map/core/v1/config_map.k](https://url/to/source_code/config_map/core/v1/config_map.k) + +## Schema ConfigMap + +### Base Schema +[@config_map.core.metadata.Metadata](../doc_metadata_en#schema-metadata) + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**data**|{str: str}|Undefined|optional| +|**binaryData**|{str: str}|Undefined|optional| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_frontend_zh_cn/frontend/doc_container_zh_cn.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_frontend_zh_cn/frontend/doc_container_zh_cn.md new file mode 100644 index 000000000..907995b18 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_frontend_zh_cn/frontend/doc_container_zh_cn.md @@ -0,0 +1,23 @@ +# container + +## Schema Container + +在 pod 中运行的单应用容器 + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**image**
    Docker 镜像名称|str|Undefined|**required**| +|**ports**
    容器对外暴露的端口列表|[frontend.ContainerPort]|Undefined|optional| +## Schema ContainerPort + +ContainerPort 表示单个容器中的一个网络端口 + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**port**
    在 pod IP 地址上暴露的端口号。必须是合法的端口号:介于 0 和 65536 之间(不包含)|int|Undefined|**required**| +|**protocol**
    端口协议,合法取值有:"UDP","TCP","SCTP"|"TCP" | "UDP" | "SCTP"|"TCP"|**required**| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_frontend_zh_cn/frontend/doc_server_zh_cn.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_frontend_zh_cn/frontend/doc_server_zh_cn.md new file mode 100644 index 000000000..e2f572e6b --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_frontend_zh_cn/frontend/doc_server_zh_cn.md @@ -0,0 +1,15 @@ +# server + +## Schema Server + +Server 定义了经典的长连接服务 + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**name**
    server 的名称|str|Undefined|**required**| +|**workloadType**
    工作负载的类型|"Deployment" | "StatefulSet" | "DaemonSet"|"Deployment"|**required**| +|**replica**
    目标副本数|int|1|**required**| +|**mainContainer**
    主容器|Container|Undefined|**required**| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apis/doc_label_selector_en.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apis/doc_label_selector_en.md new file mode 100644 index 000000000..3fec3ab69 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apis/doc_label_selector_en.md @@ -0,0 +1,16 @@ +# label_selector + +Source: [import_pkg/apis/label_selector.k](https://url/to/source_code/import_pkg/apis/label_selector.k) + +This is the label\_selector module in kusion\_kubernetes.apimachinery.apis package.
    This file was generated by the KCL auto-gen tool. DO NOT EDIT.
    Editing this file might prove futile when you re-run the KCL auto-gen generate command. + +## Schema LabelSelector + +A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**matchLabels**
    matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.|{str: str}|Undefined|optional| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apis/doc_object_meta_en.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apis/doc_object_meta_en.md new file mode 100644 index 000000000..0ba6ae800 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apis/doc_object_meta_en.md @@ -0,0 +1,29 @@ +# object_meta + +Source: [import_pkg/apis/object_meta.k](https://url/to/source_code/import_pkg/apis/object_meta.k) + +This is the object\_meta module in kusion\_kubernetes.apimachinery.apis package.
    This file was generated by the KCL auto-gen tool. DO NOT EDIT.
    Editing this file might prove futile when you re-run the KCL auto-gen generate command. + +## Schema ObjectMeta + +ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create. + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**annotations**
    Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations|{str: str}|Undefined|optional| +|**clusterName**
    The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.|str|Undefined|optional| +|**creationTimestamp**
    CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md\#metadata|str|Undefined|optional| +|**deletionGracePeriodSeconds**
    Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.|int|Undefined|optional| +|**deletionTimestamp**
    DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested. Populated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md\#metadata|str|Undefined|optional| +|**finalizers**
    Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.|[str]|Undefined|optional| +|**generateName**
    GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server. If this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header). Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md\#idempotency|str|Undefined|optional| +|**generation**
    A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.|int|Undefined|optional| +|**labels**
    Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels|{str: str}|Undefined|optional| +|**name**
    Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers\#names|str|Undefined|optional| +|**namespace**
    Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty. Must be a DNS\_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces|str|Undefined|optional| +|**resourceVersion**
    An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources. Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md\#concurrency-control-and-consistency|str|Undefined|optional| +|**selfLink**
    SelfLink is a URL representing this object. Populated by the system. Read-only. DEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release.|str|Undefined|optional| +|**uid**
    UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations. Populated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers\#uids|str|Undefined|optional| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apps/doc_deployment_en.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apps/doc_deployment_en.md new file mode 100644 index 000000000..cc6779352 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apps/doc_deployment_en.md @@ -0,0 +1,19 @@ +# deployment + +Source: [import_pkg/apps/deployment.k](https://url/to/source_code/import_pkg/apps/deployment.k) + +This is the deployment module in kusion\_kubernetes.api.apps.v1 package.
    This file was generated by the KCL auto-gen tool. DO NOT EDIT.
    Editing this file might prove futile when you re-run the KCL auto-gen generate command. + +## Schema Deployment + +Deployment enables declarative updates for Pods and ReplicaSets. + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**apiVersion**
    APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md\#resources|"apps/v1"|"apps/v1"|**required**| +|**kind**
    Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md\#types-kinds|"Deployment"|"Deployment"|**required**| +|**metadata**
    Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md\#metadata|[apis.ObjectMeta](../apis/doc_object_meta_en#schema-objectmeta)|Undefined|optional| +|**spec**
    Specification of the desired behavior of the Deployment.|[DeploymentSpec](doc_deployment_spec_en#schema-deploymentspec)|Undefined|optional| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apps/doc_deployment_spec_en.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apps/doc_deployment_spec_en.md new file mode 100644 index 000000000..af0fd770b --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apps/doc_deployment_spec_en.md @@ -0,0 +1,23 @@ +# deployment_spec + +Source: [import_pkg/apps/deployment_spec.k](https://url/to/source_code/import_pkg/apps/deployment_spec.k) + +This is the deployment\_spec module in kusion\_kubernetes.api.apps.v1 package.
    This file was generated by the KCL auto-gen tool. DO NOT EDIT.
    Editing this file might prove futile when you re-run the KCL auto-gen generate command. + +## Schema DeploymentSpec + +DeploymentSpec is the specification of the desired behavior of the Deployment. + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**minReadySeconds**
    Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)|int|Undefined|optional| +|**paused**
    Indicates that the deployment is paused.|bool|Undefined|optional| +|**progressDeadlineSeconds**
    The maximum time in seconds for a deployment to make progress before it is considered to be failed. The deployment controller will continue to process failed deployments and a condition with a ProgressDeadlineExceeded reason will be surfaced in the deployment status. Note that progress will not be estimated during the time a deployment is paused. Defaults to 600s.|int|Undefined|optional| +|**replicas**
    Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1.|int|Undefined|optional| +|**revisionHistoryLimit**
    The number of old ReplicaSets to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.|int|Undefined|optional| +|**selector**
    Label selector for pods. Existing ReplicaSets whose pods are selected by this will be the ones affected by this deployment. It must match the pod template's labels.|[apis.LabelSelector](../apis/doc_label_selector_en#schema-labelselector)|Undefined|**required**| +|**strategy**
    The deployment strategy to use to replace existing pods with new ones.|[DeploymentStrategy](doc_deployment_strategy_en#schema-deploymentstrategy)|Undefined|optional| +|**template**
    Template describes the pods that will be created.|[core.PodTemplateSpec](../core/doc_pod_template_spec_en#schema-podtemplatespec)|Undefined|**required**| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apps/doc_deployment_strategy_en.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apps/doc_deployment_strategy_en.md new file mode 100644 index 000000000..74d7ede7a --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apps/doc_deployment_strategy_en.md @@ -0,0 +1,17 @@ +# deployment_strategy + +Source: [import_pkg/apps/deployment_strategy.k](https://url/to/source_code/import_pkg/apps/deployment_strategy.k) + +This is the deployment\_strategy module in kusion\_kubernetes.api.apps.v1 package.
    This file was generated by the KCL auto-gen tool. DO NOT EDIT.
    Editing this file might prove futile when you re-run the KCL auto-gen generate command. + +## Schema DeploymentStrategy + +DeploymentStrategy describes how to replace existing pods with new ones. + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**type**
    Type of deployment. Can be "Recreate" or "RollingUpdate". Default is RollingUpdate.|str|Undefined|optional| +|**rollingUpdate**
    Rolling update config params. Present only if DeploymentStrategyType = RollingUpdate.|[RollingUpdateDeployment](doc_rolling_update_deployment_en#schema-rollingupdatedeployment)|Undefined|optional| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apps/doc_rolling_update_deployment_en.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apps/doc_rolling_update_deployment_en.md new file mode 100644 index 000000000..0c88f53bd --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/apps/doc_rolling_update_deployment_en.md @@ -0,0 +1,17 @@ +# rolling_update_deployment + +Source: [import_pkg/apps/rolling_update_deployment.k](https://url/to/source_code/import_pkg/apps/rolling_update_deployment.k) + +This is the rolling\_update\_deployment module in kusion\_kubernetes.api.apps.v1 package.
    This file was generated by the KCL auto-gen tool. DO NOT EDIT.
    Editing this file might prove futile when you re-run the KCL auto-gen generate command. + +## Schema RollingUpdateDeployment + +Spec to control the desired behavior of rolling update. + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**maxSurge**
    The maximum number of pods that can be scheduled above the desired number of pods. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxUnavailable is 0. Absolute number is calculated from percentage by rounding up. Defaults to 25%. Example: when this is set to 30%, the new ReplicaSet can be scaled up immediately when the rolling update starts, such that the total number of old and new pods do not exceed 130% of desired pods. Once old pods have been killed, new ReplicaSet can be scaled up further, ensuring that total number of pods running at any time during the update is at most 130% of desired pods.|int \| str|Undefined|optional| +|**maxUnavailable**
    The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding down. This can not be 0 if MaxSurge is 0. Defaults to 25%. Example: when this is set to 30%, the old ReplicaSet can be scaled down to 70% of desired pods immediately when the rolling update starts. Once new pods are ready, old ReplicaSet can be scaled down further, followed by scaling up the new ReplicaSet, ensuring that the total number of pods available at all times during the update is at least 70% of desired pods.|int \| str|Undefined|optional| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/core/doc_pod_spec_en.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/core/doc_pod_spec_en.md new file mode 100644 index 000000000..12f43bb4c --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/core/doc_pod_spec_en.md @@ -0,0 +1,38 @@ +# pod_spec + +Source: [import_pkg/core/pod_spec.k](https://url/to/source_code/import_pkg/core/pod_spec.k) + +This is the pod\_spec module in kusion\_kubernetes.api.core.v1 package.
    This file was generated by the KCL auto-gen tool. DO NOT EDIT.
    Editing this file might prove futile when you re-run the KCL auto-gen generate command. + +## Schema PodSpec + +PodSpec is a description of a pod. + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**activeDeadlineSeconds**
    Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer.|int|Undefined|optional| +|**automountServiceAccountToken**
    AutomountServiceAccountToken indicates whether a service account token should be automatically mounted.|bool|Undefined|optional| +|**dnsPolicy**
    Set DNS policy for the pod. Defaults to "ClusterFirst". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'.|str|Undefined|optional| +|**enableServiceLinks**
    EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.|bool|Undefined|optional| +|**hostIPC**
    Use the host's ipc namespace. Optional: Default to false.|bool|Undefined|optional| +|**hostNetwork**
    Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.|bool|Undefined|optional| +|**hostPID**
    Use the host's pid namespace. Optional: Default to false.|bool|Undefined|optional| +|**hostname**
    Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value.|str|Undefined|optional| +|**nodeName**
    NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.|str|Undefined|optional| +|**nodeSelector**
    NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/|{str: str}|Undefined|optional| +|**overhead**
    Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.|{str: str}|Undefined|optional| +|**preemptionPolicy**
    PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate.|str|Undefined|optional| +|**priority**
    The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority.|int|Undefined|optional| +|**priorityClassName**
    If specified, indicates the pod's priority. "system-node-critical" and "system-cluster-critical" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default.|str|Undefined|optional| +|**restartPolicy**
    Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/\#restart-policy|str|Undefined|optional| +|**runtimeClassName**
    RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the "legacy" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14.|str|Undefined|optional| +|**schedulerName**
    If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler.|str|Undefined|optional| +|**serviceAccount**
    DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.|str|Undefined|optional| +|**serviceAccountName**
    ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/|str|Undefined|optional| +|**setHostnameAsFQDN**
    If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY\_LOCAL\_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false.|bool|Undefined|optional| +|**shareProcessNamespace**
    Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false.|bool|Undefined|optional| +|**subdomain**
    If specified, the fully qualified Pod hostname will be "\.\.\.svc.\". If not specified, the pod will not have a domainname at all.|str|Undefined|optional| +|**terminationGracePeriodSeconds**
    Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds.|int|Undefined|optional| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/core/doc_pod_template_spec_en.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/core/doc_pod_template_spec_en.md new file mode 100644 index 000000000..06a577abd --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_import_pkg_en/import_pkg/core/doc_pod_template_spec_en.md @@ -0,0 +1,17 @@ +# pod_template_spec + +Source: [import_pkg/core/pod_template_spec.k](https://url/to/source_code/import_pkg/core/pod_template_spec.k) + +This is the pod\_template\_spec module in kusion\_kubernetes.api.core.v1 package.
    This file was generated by the KCL auto-gen tool. DO NOT EDIT.
    Editing this file might prove futile when you re-run the KCL auto-gen generate command. + +## Schema PodTemplateSpec + +PodTemplateSpec describes the data a pod should have when created from a template + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**metadata**
    Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md\#metadata|[apis.ObjectMeta](../apis/doc_object_meta_en#schema-objectmeta)|Undefined|optional| +|**spec**
    Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md\#spec-and-status|[PodSpec](doc_pod_spec_en#schema-podspec)|Undefined|optional| + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_simple_zh_cn/doc_simple_zh_cn.md b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_simple_zh_cn/doc_simple_zh_cn.md new file mode 100644 index 000000000..b245eb546 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/docs_markdown_simple_zh_cn/doc_simple_zh_cn.md @@ -0,0 +1,25 @@ +# simple + +Source: [simple.k](https://url/to/source_code/simple.k) + +模块文档,当前模块中定义了一个名为 "Person" 的模型 + +## Schema Person + +Person 是一个简单的模型示例 + +### Attributes + +|Name and Description|Type|Default Value|Required| +|--------------------|----|-------------|--------| +|**name**
    普通属性,名为 "name",表示人的名称|str|"Default"|**required**| +|**age**
    普通属性,名为 "age",表示人的年龄|int|18|optional| +### Examples +```python +person = Person { + name: "Alice" + age: 18 +} +``` + + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/i18n_docs_YAML_compact_type_zh_cn/i18n_compact_type_zh_cn.yaml b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/i18n_docs_YAML_compact_type_zh_cn/i18n_compact_type_zh_cn.yaml new file mode 100644 index 000000000..6f55165c2 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/i18n_docs_YAML_compact_type_zh_cn/i18n_compact_type_zh_cn.yaml @@ -0,0 +1,138 @@ +name: compact_type +relative_path: ./compact_type.k +schemas: +- name: Metadata + doc: | + Metadata is the base schema of all models, which contains data + that helps uniquely identify the object. + attributes: + - name: name + doc: | + The name of the resource. + Name must be unique within a namespace. It's required when creating + resources, although some resources may allow a client to request the + generation of an appropriate name automatically. + Name is primarily intended for creation idempotence and configuration + definition. Cannot be updated. More info: + http://kubernetes.io/docs/user-guide/identifiers#names + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + is_optional: false + default_value: '' + - name: labels + doc: | + Labels is a map of string keys and values that can be used to + organize and categorize (scope and select) objects. + May match selectors of replication controllers and services. + More info: http://kubernetes.io/docs/user-guide/labels + type: + type_str: '{str: str}' + type_category: DICT + dict_type: + key_type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + value_type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + is_optional: true + default_value: '' + - name: annotations + doc: | + Annotations is an unstructured key value map stored with a + resource that may be set by external tools to store and retrieve + arbitrary metadata. They are not queryable and should be preserved + when modifying objects. + More info: http://kubernetes.io/docs/user-guide/annotations + type: + type_str: '{str: str}' + type_category: DICT + dict_type: + key_type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + value_type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + is_optional: true + default_value: '' + - name: namespace + doc: | + Namespaces are intended for use in environments with many users spread + across multiple teams, or projects. + For clusters with a few to tens of users, you should not need to create + or think about namespaces at all. Start using namespaces when you need the features they provide. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + is_optional: true + default_value: default + examples: '' +- name: AppConfiguration + doc: | + AppConfiguration is the common user interface for long-running + services adopting the best practice of Kubernetes. + attributes: + - name: workloadType + doc: | + Use this attribute to specify which kind of long-running service you want. + Valid values: Deployment, CafeDeployment. + Default to Deployment. + See also: kusion_models/core/v1/workload_metadata.k + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + default_value: Deployment + is_optional: false + - name: name + doc: | + Required. + A Server-level attribute. + The name of the long-running service. + See also: kusion_models/core/v1/metadata.k + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + is_optional: false + default_value: '' + - name: namespace + doc: | + Required. + A Server-level attribute. + The namespace of the long-running service. + See also: kusion_models/core/v1/metadata.k + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + default_value: default + is_optional: false + - name: app + doc: | + A Server-level attribute. + The name of the application. + If specified, it will be used as the value of the default label "app". + If not specified, the value of the attribute name will be used. + See also: kusion_models/core/v1/workload_metadata.k + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + is_optional: false + default_value: '' + examples: | + myCustomApp = AppConfiguration { + name: "componentName" + } +doc: '' +source_code_url: '' diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/i18n_docs_YAML_frontend_zh_cn/i18n_container_zh_cn.yaml b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/i18n_docs_YAML_frontend_zh_cn/i18n_container_zh_cn.yaml new file mode 100644 index 000000000..44e03447d --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/i18n_docs_YAML_frontend_zh_cn/i18n_container_zh_cn.yaml @@ -0,0 +1,70 @@ +name: container +relative_path: ./frontend/container.k +schemas: +- name: Container + doc: | + A single application container that you want to run within a pod + attributes: + - name: image + doc: | + Docker image name. + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + default_value: Undefined + is_optional: false + - name: ports + doc: | + List of ports to expose from the container. + type: + type_str: '[frontend.ContainerPort]' + type_category: LIST + list_type: + item_type: + type_str: frontend.ContainerPort + type_category: SCHEMA + schema_type: + name: ContainerPort + relative_path: ./frontend/container.k + is_optional: true + default_value: Undefined + examples: '' +- name: ContainerPort + doc: | + ContainerPort represents a network port in a single container. + attributes: + - name: port + doc: | + Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536. + type: + type_str: int + type_category: BUILTIN + builtin_type: INT + default_value: Undefined + is_optional: false + - name: protocol + doc: | + Protocol for port. Must be UDP, TCP, or SCTP. + type: + type_str: '"TCP" | "UDP" | "SCTP"' + union_type: + types: + - type_str: '"TCP"' + type_category: LIT + lit_type: + string_lit: TCP + - type_str: '"UDP"' + type_category: LIT + lit_type: + string_lit: UDP + - type_str: '"SCTP"' + type_category: LIT + lit_type: + string_lit: SCTP + type_category: UNION + default_value: '"TCP"' + is_optional: false + examples: '' +doc: '' +source_code_url: '' diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/i18n_docs_YAML_frontend_zh_cn/i18n_server_zh_cn.yaml b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/i18n_docs_YAML_frontend_zh_cn/i18n_server_zh_cn.yaml new file mode 100644 index 000000000..a0e684900 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/i18n_docs_YAML_frontend_zh_cn/i18n_server_zh_cn.yaml @@ -0,0 +1,61 @@ +name: server +relative_path: ./frontend/server.k +schemas: +- name: Server + doc: | + Server defined the classical Long-running Service. + attributes: + - name: name + doc: | + The name of the server. + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + default_value: Undefined + is_optional: false + - name: workloadType + doc: | + The type of the workload. + type: + type_str: '"Deployment" | "StatefulSet" | "DaemonSet"' + union_type: + types: + - type_str: '"Deployment"' + type_category: LIT + lit_type: + string_lit: Deployment + - type_str: '"StatefulSet"' + type_category: LIT + lit_type: + string_lit: StatefulSet + - type_str: '"DaemonSet"' + type_category: LIT + lit_type: + string_lit: DaemonSet + type_category: UNION + default_value: '"Deployment"' + is_optional: false + - name: replica + doc: | + The desired replica count + type: + type_str: int + type_category: BUILTIN + builtin_type: INT + default_value: '1' + is_optional: false + - name: mainContainer + doc: | + The main container. + type: + type_str: Container + type_category: SCHEMA + schema_type: + name: Container + relative_path: ./frontend/container.k + default_value: Undefined + is_optional: false + examples: '' +doc: '' +source_code_url: '' diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/i18n_docs_YAML_simple_zh_cn/i18n_simple_zh_cn.yaml b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/i18n_docs_YAML_simple_zh_cn/i18n_simple_zh_cn.yaml new file mode 100644 index 000000000..096bff10b --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/docs/i18n_docs_YAML_simple_zh_cn/i18n_simple_zh_cn.yaml @@ -0,0 +1,32 @@ +name: simple +relative_path: ./simple.k +schemas: +- name: Person + doc: | + Person is a simple schema + attributes: + - name: name + doc: | + A Normal attribute named 'name' + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + default_value: '"Default"' + is_optional: false + - name: age + doc: | + A Normal attribute named 'age' + type: + type_str: int + type_category: BUILTIN + builtin_type: INT + is_optional: true + default_value: '18' + examples: | + person = Person { + name: "Alice" + age: 18 + } +doc: '' +source_code_url: '' diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/i18n_inputs/frontend/i18n_container_zh_cn.yaml b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/i18n_inputs/frontend/i18n_container_zh_cn.yaml new file mode 100644 index 000000000..0ae2aeeb8 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/i18n_inputs/frontend/i18n_container_zh_cn.yaml @@ -0,0 +1,70 @@ +name: container +relative_path: ./frontend/container.k +schemas: +- name: Container + doc: | + 在 pod 中运行的单应用容器 + attributes: + - name: image + doc: | + Docker 镜像名称 + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + default_value: Undefined + is_optional: false + - name: ports + doc: | + 容器对外暴露的端口列表 + type: + type_str: '[frontend.ContainerPort]' + type_category: LIST + list_type: + item_type: + type_str: frontend.ContainerPort + type_category: SCHEMA + schema_type: + name: ContainerPort + relative_path: ./frontend/container.k + is_optional: true + default_value: Undefined + examples: '' +- name: ContainerPort + doc: | + ContainerPort 表示单个容器中的一个网络端口 + attributes: + - name: port + doc: | + 在 pod IP 地址上暴露的端口号。必须是合法的端口号:介于 0 和 65536 之间(不包含) + type: + type_str: int + type_category: BUILTIN + builtin_type: INT + default_value: Undefined + is_optional: false + - name: protocol + doc: | + 端口协议,合法取值有:"UDP","TCP","SCTP" + type: + type_str: '"TCP" | "UDP" | "SCTP"' + union_type: + types: + - type_str: '"TCP"' + type_category: LIT + lit_type: + string_lit: TCP + - type_str: '"UDP"' + type_category: LIT + lit_type: + string_lit: UDP + - type_str: '"SCTP"' + type_category: LIT + lit_type: + string_lit: SCTP + type_category: UNION + default_value: '"TCP"' + is_optional: false + examples: '' +doc: '' +source_code_url: '' diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/i18n_inputs/frontend/i18n_server_zh_cn.yaml b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/i18n_inputs/frontend/i18n_server_zh_cn.yaml new file mode 100644 index 000000000..c620ff808 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/i18n_inputs/frontend/i18n_server_zh_cn.yaml @@ -0,0 +1,61 @@ +name: server +relative_path: ./frontend/server.k +schemas: +- name: Server + doc: | + Server 定义了经典的长连接服务 + attributes: + - name: name + doc: | + server 的名称 + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + default_value: Undefined + is_optional: false + - name: workloadType + doc: | + 工作负载的类型 + type: + type_str: '"Deployment" | "StatefulSet" | "DaemonSet"' + union_type: + types: + - type_str: '"Deployment"' + type_category: LIT + lit_type: + string_lit: Deployment + - type_str: '"StatefulSet"' + type_category: LIT + lit_type: + string_lit: StatefulSet + - type_str: '"DaemonSet"' + type_category: LIT + lit_type: + string_lit: DaemonSet + type_category: UNION + default_value: '"Deployment"' + is_optional: false + - name: replica + doc: | + 目标副本数 + type: + type_str: int + type_category: BUILTIN + builtin_type: INT + default_value: '1' + is_optional: false + - name: mainContainer + doc: | + 主容器 + type: + type_str: Container + type_category: SCHEMA + schema_type: + name: Container + relative_path: ./frontend/container.k + default_value: Undefined + is_optional: false + examples: '' +doc: '' +source_code_url: '' diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/i18n_inputs/i18n_simple_zh_cn.yaml b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/i18n_inputs/i18n_simple_zh_cn.yaml new file mode 100644 index 000000000..74c251ac1 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/i18n_inputs/i18n_simple_zh_cn.yaml @@ -0,0 +1,32 @@ +name: simple +relative_path: ./simple.k +schemas: +- name: Person + doc: | + Person 是一个简单的模型示例 + attributes: + - name: name + doc: | + 普通属性,名为 "name",表示人的名称 + type: + type_str: str + type_category: BUILTIN + builtin_type: STRING + default_value: '"Default"' + is_optional: false + - name: age + doc: | + 普通属性,名为 "age",表示人的年龄 + type: + type_str: int + type_category: BUILTIN + builtin_type: INT + is_optional: true + default_value: '18' + examples: | + person = Person { + name: "Alice" + age: 18 + } +doc: '模块文档,当前模块中定义了一个名为 "Person" 的模型' +source_code_url: 'https://url/to/source_code' diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/base_schema.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/base_schema.k new file mode 100644 index 000000000..175b4d741 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/base_schema.k @@ -0,0 +1,14 @@ +schema Person: + name: str + age: int + +schema Student(Person): + """Student is the person with a grade + + Attributes + ---------- + grade : int, default is Undefined, required + the current grade that the student is in. + + """ + grade: int diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/base_schema_pkg/base/person.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/base_schema_pkg/base/person.k new file mode 100644 index 000000000..b4c3153b0 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/base_schema_pkg/base/person.k @@ -0,0 +1,3 @@ +schema Person: + name: str + age: int diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/base_schema_pkg/sub/student.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/base_schema_pkg/sub/student.k new file mode 100644 index 000000000..9b49e8373 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/base_schema_pkg/sub/student.k @@ -0,0 +1,12 @@ +import base_schema_pkg.base + +schema Student(base.Person): + """Student is the person with a grade + + Attributes + ---------- + grade : int, default is Undefined, required + the current grade that the student is in. + + """ + grade: int diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/compact_type.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/compact_type.k new file mode 100644 index 000000000..842b747d3 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/compact_type.k @@ -0,0 +1,75 @@ +schema Metadata: + '''Metadata is the base schema of all models, which contains data + that helps uniquely identify the object. + + Attributes + ---------- + name: str + The name of the resource. + Name must be unique within a namespace. It's required when creating + resources, although some resources may allow a client to request the + generation of an appropriate name automatically. + Name is primarily intended for creation idempotence and configuration + definition. Cannot be updated. More info: + http://kubernetes.io/docs/user-guide/identifiers#names + labels: {str:str}, optional + Labels is a map of string keys and values that can be used to + organize and categorize (scope and select) objects. + May match selectors of replication controllers and services. + More info: http://kubernetes.io/docs/user-guide/labels + annotations: {str:str}, optional + Annotations is an unstructured key value map stored with a + resource that may be set by external tools to store and retrieve + arbitrary metadata. They are not queryable and should be preserved + when modifying objects. + More info: http://kubernetes.io/docs/user-guide/annotations + namespace: str, default is default, optional + Namespaces are intended for use in environments with many users spread + across multiple teams, or projects. + For clusters with a few to tens of users, you should not need to create + or think about namespaces at all. Start using namespaces when you need the features they provide. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + ''' + name: str + labels?: {str: str} + annotations?: {str: str} + namespace?: str + +schema AppConfiguration: + ''' AppConfiguration is the common user interface for long-running + services adopting the best practice of Kubernetes. + + Attributes + ---------- + workloadType: str, default is Deployment + Use this attribute to specify which kind of long-running service you want. + Valid values: Deployment, CafeDeployment. + Default to Deployment. + See also: kusion_models/core/v1/workload_metadata.k + name: str + Required. + A Server-level attribute. + The name of the long-running service. + See also: kusion_models/core/v1/metadata.k + namespace: str, default = default + Required. + A Server-level attribute. + The namespace of the long-running service. + See also: kusion_models/core/v1/metadata.k + app: str + A Server-level attribute. + The name of the application. + If specified, it will be used as the value of the default label "app". + If not specified, the value of the attribute name will be used. + See also: kusion_models/core/v1/workload_metadata.k + + Examples + -------- + myCustomApp = AppConfiguration { + name: "componentName" + } + ''' + workloadType: str = "Deployment" + name: str + namespace: str = "default" + app: str \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/config_map/core/metadata.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/config_map/core/metadata.k new file mode 100644 index 000000000..1f7d6fd2c --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/config_map/core/metadata.k @@ -0,0 +1,37 @@ +schema Metadata: + """Metadata is the base schema of all models, which contains data + that helps uniquely identify the object. + + Attributes + ---------- + name: str, default is Undefined, required. + The name of the resource. + Name must be unique within a namespace. It's required when creating + resources, although some resources may allow a client to request the + generation of an appropriate name automatically. + Name is primarily intended for creation idempotence and configuration + definition. Cannot be updated. More info: + http://kubernetes.io/docs/user-guide/identifiers#names + labels: {str:str}, default is Undefined, optional. + Labels is a map of string keys and values that can be used to + organize and categorize (scope and select) objects. + May match selectors of replication controllers and services. + More info: http://kubernetes.io/docs/user-guide/labels + annotations: {str:str}, default is Undefined, optional. + Annotations is an unstructured key value map stored with a + resource that may be set by external tools to store and retrieve + arbitrary metadata. They are not queryable and should be preserved + when modifying objects. + More info: http://kubernetes.io/docs/user-guide/annotations + namespace: str, default is Undefined, optional. + Namespaces are intended for use in environments with many users spread + across multiple teams, or projects. + For clusters with a few to tens of users, you should not need to create + or think about namespaces at all. Start using namespaces when you need the features they provide. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + """ + + name: str + labels?: {str:str} + annotations?: {str:str} + namespace?: str diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/config_map/core/v1/config_map.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/config_map/core/v1/config_map.k new file mode 100644 index 000000000..5c961c61a --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/config_map/core/v1/config_map.k @@ -0,0 +1,7 @@ +import config_map.core.metadata + +schema ConfigMap(metadata.Metadata): + # 目的是根据入参生成 Kubernetes 资源 + # 入参列表 + data?: {str:str} + binaryData?: {str:str} diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/frontend/container.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/frontend/container.k new file mode 100644 index 000000000..4bda9fae2 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/frontend/container.k @@ -0,0 +1,31 @@ +schema Container: + """A single application container that you want to run within a pod + + Attributes + ---------- + image : str, Default is Undefined, required + Docker image name. + ports: [ContainerPort], Default is Undefined, optional + List of ports to expose from the container. + """ + image: str + ports?: [ContainerPort] + + +schema ContainerPort: + """ContainerPort represents a network port in a single container. + + Attributes + ---------- + port : int, Default is Undefined, required + Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536. + protocol : "TCP" | "UDP" | "SCTP", Default is "TCP", required + Protocol for port. Must be UDP, TCP, or SCTP. + """ + port: int + protocol: "TCP" | "UDP" | "SCTP" = "TCP" + + check: + 0 < port < 65536 + + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/frontend/server.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/frontend/server.k new file mode 100644 index 000000000..6a421b234 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/frontend/server.k @@ -0,0 +1,18 @@ +schema Server: + """Server defined the classical Long-running Service. + + Attributes + ---------- + name : str, Default is Undefined, required + The name of the server. + workloadType : "Deployment" | "StatefulSet" | "DaemonSet", Default is "Deployment", required + The type of the workload. + replica : int, Default is 1, required + The desired replica count + mainContainer : Container, Default is Undefined, required + The main container. + """ + name: str + workloadType: "Deployment" | "StatefulSet" | "DaemonSet" = "Deployment" + replica: int = 1 + mainContainer: Container diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/good.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/good.k new file mode 100644 index 000000000..551d8ef53 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/good.k @@ -0,0 +1,29 @@ +schema Server: + """Server is the common user interface for long-running + services adopting the best practice of Kubernetes. + + Attributes + ---------- + workloadType : str, default is Deployment + Use this attribute to specify which kind of long-running service you want. + Valid values: Deployment, CafeDeployment. + See also: kusion_models/core/v1/workload_metadata.k. + name : str + A Server-level attribute. + The name of the long-running service. + See also: kusion_models/core/v1/metadata.k. + labels : {str:str}, optional + A Server-level attribute. + The labels of the long-running service. + See also: kusion_models/core/v1/metadata.k. + + Examples + ---------------------- + myCustomApp = AppConfiguration { + name: "componentName" + } + """ + workloadType: str = "Deployment" + name: str + labels?: {str:str} + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apis/label_selector.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apis/label_selector.k new file mode 100644 index 000000000..e4a76ef9d --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apis/label_selector.k @@ -0,0 +1,20 @@ +""" +This is the label_selector module in kusion_kubernetes.apimachinery.apis package. +This file was generated by the KCL auto-gen tool. DO NOT EDIT. +Editing this file might prove futile when you re-run the KCL auto-gen generate command. +""" + + +schema LabelSelector: + """ A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. + + Attributes + ---------- + matchLabels : {str:str}, default is Undefined, optional + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. + """ + + + matchLabels?: {str:str} + + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apis/object_meta.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apis/object_meta.k new file mode 100644 index 000000000..73b3690ed --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apis/object_meta.k @@ -0,0 +1,72 @@ +""" +This is the object_meta module in kusion_kubernetes.apimachinery.apis package. +This file was generated by the KCL auto-gen tool. DO NOT EDIT. +Editing this file might prove futile when you re-run the KCL auto-gen generate command. +""" + + +schema ObjectMeta: + """ ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create. + + Attributes + ---------- + annotations : {str:str}, default is Undefined, optional + Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations + clusterName : str, default is Undefined, optional + The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request. + creationTimestamp : str, default is Undefined, optional + CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + deletionGracePeriodSeconds : int, default is Undefined, optional + Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only. + deletionTimestamp : str, default is Undefined, optional + DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested. Populated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + finalizers : [str], default is Undefined, optional + Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list. + generateName : str, default is Undefined, optional + GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server. If this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header). Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency + generation : int, default is Undefined, optional + A sequence number representing a specific generation of the desired state. Populated by the system. Read-only. + labels : {str:str}, default is Undefined, optional + Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels + name : str, default is Undefined, optional + Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names + namespace : str, default is Undefined, optional + Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty. Must be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces + resourceVersion : str, default is Undefined, optional + An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources. Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + selfLink : str, default is Undefined, optional + SelfLink is a URL representing this object. Populated by the system. Read-only. DEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release. + uid : str, default is Undefined, optional + UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations. Populated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids + """ + + + annotations?: {str:str} + + clusterName?: str + + creationTimestamp?: str + + deletionGracePeriodSeconds?: int + + deletionTimestamp?: str + + finalizers?: [str] + + generateName?: str + + generation?: int + + labels?: {str:str} + + name?: str + + namespace?: str + + resourceVersion?: str + + selfLink?: str + + uid?: str + + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apps/deployment.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apps/deployment.k new file mode 100644 index 000000000..308874421 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apps/deployment.k @@ -0,0 +1,33 @@ +""" +This is the deployment module in kusion_kubernetes.api.apps.v1 package. +This file was generated by the KCL auto-gen tool. DO NOT EDIT. +Editing this file might prove futile when you re-run the KCL auto-gen generate command. +""" +import import_pkg.apis + + +schema Deployment: + """ Deployment enables declarative updates for Pods and ReplicaSets. + + Attributes + ---------- + apiVersion : "apps/v1", default is "apps/v1", required + APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + kind : "Deployment", default is "Deployment", required + Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + metadata : apis.ObjectMeta, default is Undefined, optional + Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + spec : DeploymentSpec, default is Undefined, optional + Specification of the desired behavior of the Deployment. + """ + + + apiVersion: "apps/v1" = "apps/v1" + + kind: "Deployment" = "Deployment" + + metadata?: apis.ObjectMeta + + spec?: DeploymentSpec + + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apps/deployment_spec.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apps/deployment_spec.k new file mode 100644 index 000000000..082a965cf --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apps/deployment_spec.k @@ -0,0 +1,50 @@ +""" +This is the deployment_spec module in kusion_kubernetes.api.apps.v1 package. +This file was generated by the KCL auto-gen tool. DO NOT EDIT. +Editing this file might prove futile when you re-run the KCL auto-gen generate command. +""" +import import_pkg.apis +import import_pkg.core + + +schema DeploymentSpec: + """ DeploymentSpec is the specification of the desired behavior of the Deployment. + + Attributes + ---------- + minReadySeconds : int, default is Undefined, optional + Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready) + paused : bool, default is Undefined, optional + Indicates that the deployment is paused. + progressDeadlineSeconds : int, default is Undefined, optional + The maximum time in seconds for a deployment to make progress before it is considered to be failed. The deployment controller will continue to process failed deployments and a condition with a ProgressDeadlineExceeded reason will be surfaced in the deployment status. Note that progress will not be estimated during the time a deployment is paused. Defaults to 600s. + replicas : int, default is Undefined, optional + Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1. + revisionHistoryLimit : int, default is Undefined, optional + The number of old ReplicaSets to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10. + selector : apis.LabelSelector, default is Undefined, required + Label selector for pods. Existing ReplicaSets whose pods are selected by this will be the ones affected by this deployment. It must match the pod template's labels. + strategy : DeploymentStrategy, default is Undefined, optional + The deployment strategy to use to replace existing pods with new ones. + template : core.PodTemplateSpec, default is Undefined, required + Template describes the pods that will be created. + """ + + + minReadySeconds?: int + + paused?: bool + + progressDeadlineSeconds?: int + + replicas?: int + + revisionHistoryLimit?: int + + selector: apis.LabelSelector + + strategy?: DeploymentStrategy + + template: core.PodTemplateSpec + + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apps/deployment_strategy.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apps/deployment_strategy.k new file mode 100644 index 000000000..b5b655160 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apps/deployment_strategy.k @@ -0,0 +1,24 @@ +""" +This is the deployment_strategy module in kusion_kubernetes.api.apps.v1 package. +This file was generated by the KCL auto-gen tool. DO NOT EDIT. +Editing this file might prove futile when you re-run the KCL auto-gen generate command. +""" + + +schema DeploymentStrategy: + """ DeploymentStrategy describes how to replace existing pods with new ones. + + Attributes + ---------- + $type : str, default is Undefined, optional + Type of deployment. Can be "Recreate" or "RollingUpdate". Default is RollingUpdate. + rollingUpdate : RollingUpdateDeployment, default is Undefined, optional + Rolling update config params. Present only if DeploymentStrategyType = RollingUpdate. + """ + + + $type?: str + + rollingUpdate?: RollingUpdateDeployment + + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apps/rolling_update_deployment.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apps/rolling_update_deployment.k new file mode 100644 index 000000000..f63d3e178 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/apps/rolling_update_deployment.k @@ -0,0 +1,24 @@ +""" +This is the rolling_update_deployment module in kusion_kubernetes.api.apps.v1 package. +This file was generated by the KCL auto-gen tool. DO NOT EDIT. +Editing this file might prove futile when you re-run the KCL auto-gen generate command. +""" + + +schema RollingUpdateDeployment: + """ Spec to control the desired behavior of rolling update. + + Attributes + ---------- + maxSurge : int | str, default is Undefined, optional + The maximum number of pods that can be scheduled above the desired number of pods. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxUnavailable is 0. Absolute number is calculated from percentage by rounding up. Defaults to 25%. Example: when this is set to 30%, the new ReplicaSet can be scaled up immediately when the rolling update starts, such that the total number of old and new pods do not exceed 130% of desired pods. Once old pods have been killed, new ReplicaSet can be scaled up further, ensuring that total number of pods running at any time during the update is at most 130% of desired pods. + maxUnavailable : int | str, default is Undefined, optional + The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding down. This can not be 0 if MaxSurge is 0. Defaults to 25%. Example: when this is set to 30%, the old ReplicaSet can be scaled down to 70% of desired pods immediately when the rolling update starts. Once new pods are ready, old ReplicaSet can be scaled down further, followed by scaling up the new ReplicaSet, ensuring that the total number of pods available at all times during the update is at least 70% of desired pods. + """ + + + maxSurge?: int | str + + maxUnavailable?: int | str + + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/core/pod_spec.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/core/pod_spec.k new file mode 100644 index 000000000..b8014641e --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/core/pod_spec.k @@ -0,0 +1,108 @@ +""" +This is the pod_spec module in kusion_kubernetes.api.core.v1 package. +This file was generated by the KCL auto-gen tool. DO NOT EDIT. +Editing this file might prove futile when you re-run the KCL auto-gen generate command. +""" + + +schema PodSpec: + """ PodSpec is a description of a pod. + + Attributes + ---------- + activeDeadlineSeconds : int, default is Undefined, optional + Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer. + automountServiceAccountToken : bool, default is Undefined, optional + AutomountServiceAccountToken indicates whether a service account token should be automatically mounted. + dnsPolicy : str, default is Undefined, optional + Set DNS policy for the pod. Defaults to "ClusterFirst". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'. + enableServiceLinks : bool, default is Undefined, optional + EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true. + hostIPC : bool, default is Undefined, optional + Use the host's ipc namespace. Optional: Default to false. + hostNetwork : bool, default is Undefined, optional + Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false. + hostPID : bool, default is Undefined, optional + Use the host's pid namespace. Optional: Default to false. + hostname : str, default is Undefined, optional + Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value. + nodeName : str, default is Undefined, optional + NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements. + nodeSelector : {str:str}, default is Undefined, optional + NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + overhead : {str:str}, default is Undefined, optional + Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. + preemptionPolicy : str, default is Undefined, optional + PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate. + priority : int, default is Undefined, optional + The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. + priorityClassName : str, default is Undefined, optional + If specified, indicates the pod's priority. "system-node-critical" and "system-cluster-critical" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default. + restartPolicy : str, default is Undefined, optional + Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy + runtimeClassName : str, default is Undefined, optional + RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the "legacy" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14. + schedulerName : str, default is Undefined, optional + If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler. + serviceAccount : str, default is Undefined, optional + DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead. + serviceAccountName : str, default is Undefined, optional + ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + setHostnameAsFQDN : bool, default is Undefined, optional + If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false. + shareProcessNamespace : bool, default is Undefined, optional + Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false. + subdomain : str, default is Undefined, optional + If specified, the fully qualified Pod hostname will be "...svc.". If not specified, the pod will not have a domainname at all. + terminationGracePeriodSeconds : int, default is Undefined, optional + Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds. + """ + + + activeDeadlineSeconds?: int + + automountServiceAccountToken?: bool + + dnsPolicy?: str + + enableServiceLinks?: bool + + hostIPC?: bool + + hostNetwork?: bool + + hostPID?: bool + + hostname?: str + + nodeName?: str + + nodeSelector?: {str:str} + + overhead?: {str:str} + + preemptionPolicy?: str + + priority?: int + + priorityClassName?: str + + restartPolicy?: str + + runtimeClassName?: str + + schedulerName?: str + + serviceAccount?: str + + serviceAccountName?: str + + setHostnameAsFQDN?: bool + + shareProcessNamespace?: bool + + subdomain?: str + + terminationGracePeriodSeconds?: int + + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/core/pod_template_spec.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/core/pod_template_spec.k new file mode 100644 index 000000000..9a068268b --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/import_pkg/core/pod_template_spec.k @@ -0,0 +1,25 @@ +""" +This is the pod_template_spec module in kusion_kubernetes.api.core.v1 package. +This file was generated by the KCL auto-gen tool. DO NOT EDIT. +Editing this file might prove futile when you re-run the KCL auto-gen generate command. +""" +import import_pkg.apis + + +schema PodTemplateSpec: + """ PodTemplateSpec describes the data a pod should have when created from a template + + Attributes + ---------- + metadata : apis.ObjectMeta, default is Undefined, optional + Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + spec : PodSpec, default is Undefined, optional + Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + """ + + + metadata?: apis.ObjectMeta + + spec?: PodSpec + + diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/kcl.mod b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/no_type.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/no_type.k new file mode 100644 index 000000000..2f0e36f21 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/no_type.k @@ -0,0 +1,31 @@ +schema Server: + """Server is the common user interface for long-running + services adopting the best practice of Kubernetes. + + Attributes + ---------- + workloadType : default is Deployment + Use this attribute to specify which kind of long-running service you want. + Valid values: Deployment, CafeDeployment. + See also: kusion_models/core/v1/workload_metadata.k. + name + A Server-level attribute. + The name of the long-running service. + See also: kusion_models/core/v1/metadata.k. + labels : optional + A Server-level attribute. + The labels of the long-running service. + See also: kusion_models/core/v1/metadata.k. + replica : default is 1, optional + Replica of the server. + + Examples + ---------------------- + myCustomApp = AppConfiguration { + name: "componentName" + } + """ + workloadType: str = "Deployment" + name: str + labels?: {str: str} + replica?: int = 1 diff --git a/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/simple.k b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/simple.k new file mode 100644 index 000000000..5feb31e32 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/doc_data/source_files/simple.k @@ -0,0 +1,21 @@ +schema Person: + '''Person is a simple schema + + Attributes + ---------- + name: str, default is "Default" + A Normal attribute named 'name' + age: int, default = 18, optional + A Normal attribute named 'age' + + Examples + -------- + person = Person { + name: "Alice" + age: 18 + } + ''' + name: str + age?: int + +person = Person {} diff --git a/test/test_units/test_kclvm/test_tools/test_doc/test_checker.py b/test/test_units/test_kclvm/test_tools/test_doc/test_checker.py new file mode 100644 index 000000000..7602f6c2c --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/test_checker.py @@ -0,0 +1,57 @@ +import unittest +import pathlib +import typing + +import kclvm.compiler.parser.parser as parser +import kclvm.tools.docs.doc_parser as doc_parser +import kclvm.kcl.types.checker as type_checker +import kclvm.api.object as obj_pkg +import kclvm.tools.docs.model_pb2 as model + + +_DIR_PATH = pathlib.Path(__file__).parent.joinpath("doc_data") / "source_files" + + +def resolve(kcl_file: str) -> typing.List[model.SchemaDoc]: + prog = parser.LoadProgram(kcl_file) + type_checker.ResolveProgramImport(prog) + checker = type_checker.TypeChecker(prog, type_checker.CheckConfig()) + checker.check_import(prog.MAIN_PKGPATH) + checker.init_global_types() + schemas = prog.pkgs[prog.MAIN_PKGPATH][0].GetSchemaList() + + schema_docs: typing.List[model.SchemaDoc] = [] + for schema in schemas: + schema_obj_type = checker.scope_map[prog.MAIN_PKGPATH].elems[schema.name].type + assert isinstance(schema_obj_type, obj_pkg.KCLSchemaDefTypeObject) + schema_docs.append( + doc_parser.SchemaDocParser( + schema=schema, + schema_type=schema_obj_type.schema_type, + root=prog.root, + ).doc + ) + return schema_docs + + +class KCLDocCheckerTest(unittest.TestCase): + def test_simple_case(self) -> None: + docs = resolve(_DIR_PATH / "simple.k") + assert len(docs) == 1 + doc = docs[0] + assert doc.doc.startswith("Person is a simple schema") + assert doc.attributes[0].name == "name" + assert doc.attributes[0].type.type_str == "str" + assert doc.attributes[0].is_optional is False + assert doc.attributes[0].default_value == '"Default"' + assert doc.attributes[0].doc.startswith("A Normal attribute named 'name'") + assert doc.attributes[1].name == "age" + assert doc.attributes[1].type.type_str == "int" + assert doc.attributes[1].is_optional is True + assert doc.attributes[1].default_value == "18" + assert doc.attributes[1].doc.startswith("A Normal attribute named 'age'") + assert doc.examples.startswith("person = Person {") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_doc/test_doc_gen.py b/test/test_units/test_kclvm/test_tools/test_doc/test_doc_gen.py new file mode 100644 index 000000000..faa500315 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/test_doc_gen.py @@ -0,0 +1,160 @@ +import unittest +import pathlib +import shutil +import filecmp + +from kclvm.tools.docs.doc import kcl_doc_generate +import kclvm.tools.docs.formats as doc_formats + +_DIR_PATH = pathlib.Path(__file__).parent.joinpath("doc_data") +_SOURCE_PATH = _DIR_PATH / "source_files" +_DOCS_PATH = _DIR_PATH / "docs" +_I18N_PATH = _DIR_PATH / "i18n_inputs" + + +class KCLDocGenTestData: + def __init__( + self, + filename: str, + recursive: bool, + format: str, + locale: str = "en", + i18n_path: str = None, + ): + self.filename: str = filename + self.recursive: bool = recursive + self.format: str = format + self.locale: str = locale + self.i18n_path: str = i18n_path + + +def read_file_content(path) -> str: + with open(path, "r") as f: + return f.read() + + +class KCLDocGenerateTest(unittest.TestCase): + test_cases = [ + KCLDocGenTestData( + filename="import_pkg", + recursive=True, + format="markdown", + ), + KCLDocGenTestData( + filename="base_schema_pkg", + recursive=True, + format="markdown", + ), + KCLDocGenTestData( + filename="config_map", + recursive=True, + format="markdown", + ), + ] + + def test_doc_gen(self) -> None: + # make tmp output dir + tmp_output = _DIR_PATH / "tmp" + for t_case in self.test_cases: + tmp_output_current = ( + tmp_output + / f"docs_{t_case.format}_{t_case.filename.rsplit('.k', 1)[0]}_{t_case.locale}" + ) + expect_output_current = ( + _DOCS_PATH + / f"docs_{t_case.format}_{t_case.filename.rsplit('.k', 1)[0]}_{t_case.locale}" + ) + # generate docs to tmp output dir + kcl_doc_generate( + kcl_files=[str(_SOURCE_PATH / t_case.filename)], + recursively=t_case.recursive, + output=str(tmp_output_current), + # output=str(expect_output_current), # for expect docs generate + format=t_case.format, + repo_url="https://url/to/source_code", + with_locale_suffix=True, + ) + # compare docs between expect and got + match, mismatch, errors = filecmp.cmpfiles( + expect_output_current, + tmp_output_current, + common=[ + str(f.relative_to(tmp_output_current)) + for f in list( + tmp_output_current.rglob( + f"*{doc_formats.KCLDocSuffix.TO_SUFFIX[t_case.format.upper()]}" + ) + ) + ], + ) + assert len(mismatch) == 0, f"mismatch exists: {mismatch}. {t_case.filename}" + assert len(errors) == 0, f"errors exists: {errors}. {t_case.filename}" + + # clear tmp files + shutil.rmtree(tmp_output) + + +class KCLDocI18nGenTest(unittest.TestCase): + test_cases = [ + KCLDocGenTestData( + filename="simple.k", + format="markdown", + recursive=True, + locale="zh_cn", + i18n_path="i18n_simple_zh_cn.yaml", + ), + KCLDocGenTestData( + filename="frontend", + format="markdown", + recursive=True, + locale="zh_cn", + i18n_path="frontend", + ), + ] + + def test_doc_gen_by_i18n(self) -> None: + # make tmp output dir + tmp_output = _DIR_PATH / "tmp" + for t_case in self.test_cases: + tmp_output_current = ( + tmp_output + / f"docs_{t_case.format}_{t_case.filename.rsplit('.k', 1)[0]}_{t_case.locale}" + ) + expect_output_current = ( + _DOCS_PATH + / f"docs_{t_case.format}_{t_case.filename.rsplit('.k', 1)[0]}_{t_case.locale}" + ) + # generate docs to tmp output dir + kcl_doc_generate( + kcl_files=[str(_SOURCE_PATH / t_case.filename)], + recursively=t_case.recursive, + output=str(tmp_output_current), + # output=str(expect_output_current), # for expect docs generate + format=t_case.format, + repo_url="https://url/to/source_code", + locale=t_case.locale, + i18n_path=str(_I18N_PATH / t_case.i18n_path), + with_locale_suffix=True, + ) + # compare docs between expect and got + match, mismatch, errors = filecmp.cmpfiles( + expect_output_current, + tmp_output_current, + common=[ + str(f.relative_to(tmp_output_current)) + for f in list( + tmp_output_current.rglob( + f"*{doc_formats.KCLDocSuffix.TO_SUFFIX[t_case.format.upper()]}" + ) + ) + ], + ) + assert len(mismatch) == 0, f"mismatch exists: {mismatch}. {t_case.filename}" + assert len(errors) == 0, f"errors exists: {errors}. {t_case.filename}" + + # clear tmp files + shutil.rmtree(tmp_output) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_doc/test_doc_parser.py b/test/test_units/test_kclvm/test_tools/test_doc/test_doc_parser.py new file mode 100644 index 000000000..2bf62dba2 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/test_doc_parser.py @@ -0,0 +1,194 @@ +import unittest +import pathlib +import typing + +import kclvm.compiler.parser.parser as parser +import kclvm.tools.docs.doc_parser as doc_parser +import kclvm.kcl.types.checker as type_checker +import kclvm.api.object as obj_pkg +import kclvm.tools.docs.model_pb2 as model + + +_DIR_PATH = pathlib.Path(__file__).parent.joinpath("doc_data") / "source_files" + + +def resolve(kcl_file: str) -> typing.List[model.SchemaDoc]: + prog = parser.LoadProgram(kcl_file) + type_checker.ResolveProgramImport(prog) + checker = type_checker.TypeChecker(prog, type_checker.CheckConfig()) + checker.check_import(prog.MAIN_PKGPATH) + checker.init_global_types() + schemas = prog.pkgs[prog.MAIN_PKGPATH][0].GetSchemaList() + + schema_docs: typing.List[model.SchemaDoc] = [] + for schema in schemas: + schema_obj_type = checker.scope_map[prog.MAIN_PKGPATH].elems[schema.name].type + assert isinstance(schema_obj_type, obj_pkg.KCLSchemaDefTypeObject) + schema_docs.append( + doc_parser.SchemaDocParser( + schema=schema, + schema_type=schema_obj_type.schema_type, + root=prog.root, + ).doc + ) + return schema_docs + + +class KCLDocGenTest(unittest.TestCase): + def test_simple_case(self) -> None: + docs = resolve(_DIR_PATH / "simple.k") + assert len(docs) == 1 + doc = docs[0] + assert doc.doc.startswith("Person is a simple schema") + assert doc.attributes[0].name == "name" + assert doc.attributes[0].type.type_str == "str" + assert doc.attributes[0].is_optional is False + assert doc.attributes[0].default_value == '"Default"' + assert doc.attributes[0].doc.startswith("A Normal attribute named 'name'") + assert doc.attributes[1].name == "age" + assert doc.attributes[1].type.type_str == "int" + assert doc.attributes[1].is_optional is True + assert doc.attributes[1].default_value == "18" + assert doc.attributes[1].doc.startswith("A Normal attribute named 'age'") + assert doc.examples.startswith("person = Person {") + + def test_good_case(self) -> None: + docs = resolve(_DIR_PATH / "good.k") + assert len(docs) == 1 + doc = docs[0] + assert doc.doc.startswith( + "Server is the common user interface for long-running" + ) + assert doc.attributes[0].name == "workloadType" + assert doc.attributes[0].type.type_str == "str" + assert doc.attributes[0].is_optional is False + assert doc.attributes[0].default_value == "Deployment" + assert doc.attributes[0].doc.startswith( + "Use this attribute to specify which kind of long-running service you want" + ) + assert doc.attributes[1].name == "name" + assert doc.attributes[1].type.type_str == "str" + assert doc.attributes[1].is_optional is False + assert doc.attributes[1].default_value == "" + assert doc.attributes[1].doc.startswith("A Server-level attribute") + assert doc.attributes[2].name == "labels" + assert doc.attributes[2].type.type_str == "{str: str}" + assert doc.attributes[2].is_optional is True + assert doc.attributes[2].default_value == "" + assert doc.attributes[2].doc.startswith("A Server-level attribute") + assert doc.examples.startswith("myCustomApp = AppConfiguration") + + def test_no_type_case(self) -> None: + docs = resolve(_DIR_PATH / "no_type.k") + assert len(docs) == 1 + doc = docs[0] + assert doc.doc.startswith( + "Server is the common user interface for long-running" + ) + assert doc.attributes[0].name == "workloadType" + assert doc.attributes[0].type.type_str == "str" + assert doc.attributes[0].is_optional is False + assert doc.attributes[0].default_value == "Deployment" + assert doc.attributes[0].doc.startswith( + "Use this attribute to specify which kind of long-running service you want" + ) + assert doc.attributes[1].name == "name" + assert doc.attributes[1].type.type_str == "str" + assert doc.attributes[1].is_optional is False + assert doc.attributes[1].default_value == "" + assert doc.attributes[1].doc.startswith("A Server-level attribute") + assert doc.attributes[2].name == "labels" + assert doc.attributes[2].type.type_str == "{str: str}" + assert doc.attributes[2].is_optional is True + assert doc.attributes[2].default_value == "" + assert doc.attributes[2].doc.startswith("A Server-level attribute") + assert doc.attributes[3].name == "replica" + assert doc.attributes[3].type.type_str == "int" + assert doc.attributes[3].is_optional is True + assert doc.attributes[3].default_value == "1" + assert doc.attributes[3].doc.startswith("Replica of the server") + assert doc.examples.startswith("myCustomApp = AppConfiguration") + + def test_compact_type_case(self) -> None: + docs = resolve(_DIR_PATH / "compact_type.k") + assert len(docs) == 2 + doc = docs[0] + assert doc.doc.startswith("Metadata is the base schema of all models") + assert doc.attributes[0].name == "name" + assert doc.attributes[0].type.type_str == "str" + assert doc.attributes[0].is_optional is False + assert doc.attributes[0].default_value == "" + assert doc.attributes[0].doc.startswith("The name of the resource.") + assert doc.attributes[1].name == "labels" + assert doc.attributes[1].type.type_str == "{str: str}" + assert doc.attributes[1].is_optional is True + assert doc.attributes[1].default_value == "" + assert doc.attributes[1].doc.startswith( + "Labels is a map of string keys and values" + ) + assert doc.attributes[2].name == "annotations" + assert doc.attributes[2].type.type_str == "{str: str}" + assert doc.attributes[2].is_optional is True + assert doc.attributes[2].default_value == "" + assert doc.attributes[2].doc.startswith( + "Annotations is an unstructured key value map" + ) + assert doc.attributes[3].name == "namespace" + assert doc.attributes[3].type.type_str == "str" + assert doc.attributes[3].is_optional is True + assert doc.attributes[3].default_value == "default" + assert doc.attributes[3].doc.startswith( + "Namespaces are intended for use in environments" + ) + assert doc.examples == "" + + doc = docs[1] + assert doc.doc.startswith("AppConfiguration is the common user interface") + assert doc.attributes[0].name == "workloadType" + assert doc.attributes[0].type.type_str == "str" + assert doc.attributes[0].is_optional is False + assert doc.attributes[0].default_value == "Deployment" + assert doc.attributes[0].doc.startswith("Use this attribute to specify") + assert doc.attributes[1].name == "name" + assert doc.attributes[1].type.type_str == "str" + assert doc.attributes[1].is_optional is False + assert doc.attributes[1].default_value == "" + assert doc.attributes[1].doc.startswith("Required.\nA Server-level attribute.") + assert doc.attributes[2].name == "namespace" + assert doc.attributes[2].type.type_str == "str" + assert doc.attributes[2].is_optional is False + assert doc.attributes[2].default_value == "default" + assert doc.attributes[2].doc.startswith("Required.\nA Server-level attribute.") + assert doc.attributes[3].name == "app" + assert doc.attributes[3].type.type_str == "str" + assert doc.attributes[3].is_optional is False + assert doc.attributes[3].default_value == "" + assert doc.attributes[3].doc.startswith("A Server-level attribute.") + assert doc.examples.startswith("myCustomApp = AppConfiguration {") + + def test_base_schema_case(self) -> None: + docs = resolve(_DIR_PATH / "base_schema.k") + assert len(docs) == 2 + doc = docs[0] + assert doc.attributes[0].name == "name" + assert doc.attributes[0].type.type_str == "str" + assert doc.attributes[0].is_optional is False + assert doc.attributes[0].default_value == "" + assert doc.attributes[1].name == "age" + assert doc.attributes[1].type.type_str == "int" + assert doc.attributes[1].is_optional is False + assert doc.attributes[1].default_value == "" + + doc = docs[1] + assert doc.doc.startswith("Student is the person with a grade") + assert doc.attributes[0].name == "grade" + assert doc.attributes[0].type.type_str == "int" + assert doc.attributes[0].is_optional is False + assert doc.attributes[0].default_value == "Undefined" + assert doc.attributes[0].doc.startswith( + "the current grade that the student is in." + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_doc/test_i18n.py b/test/test_units/test_kclvm/test_tools/test_doc/test_i18n.py new file mode 100644 index 000000000..1783416b0 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_doc/test_i18n.py @@ -0,0 +1,83 @@ +import filecmp +import pathlib +import shutil +import unittest + +import kclvm.tools.docs.formats as doc_formats +from kclvm.tools.docs.doc import kcl_i18n_init + +_DIR_PATH = pathlib.Path(__file__).parent.joinpath("doc_data") +_SOURCE_PATH = _DIR_PATH / "source_files" +_DOCS_PATH = _DIR_PATH / "docs" + + +class KCLDocI18nTestData: + def __init__(self, filename: str, format: str, recursive: bool, locale: str): + self.filename: str = filename + self.format: str = format + self.recursive: bool = recursive + self.locale: str = locale + + +class KCLDocI18nInitTest(unittest.TestCase): + test_cases = [ + KCLDocI18nTestData( + filename="frontend", + format=doc_formats.KCLI18NFormat.YAML, + recursive=True, + locale="zh_cn", + ), + KCLDocI18nTestData( + filename="simple.k", + format=doc_formats.KCLI18NFormat.YAML, + recursive=True, + locale="zh_cn", + ), + KCLDocI18nTestData( + filename="compact_type.k", + format=doc_formats.KCLI18NFormat.YAML, + recursive=True, + locale="zh_cn", + ), + ] + + def test_doc_i18n_init(self) -> None: + # make tmp output dir + tmp_output = _DIR_PATH / "tmp" + for t_case in self.test_cases: + tmp_output_current = ( + tmp_output + / f"i18n_docs_{t_case.format}_{t_case.filename.rsplit('.k', 1)[0]}_{t_case.locale}" + ) + expect_output_current = ( + _DOCS_PATH + / f"i18n_docs_{t_case.format}_{t_case.filename.rsplit('.k', 1)[0]}_{t_case.locale}" + ) + # generate docs to tmp output dir + kcl_i18n_init( + kcl_files=[str(_SOURCE_PATH / t_case.filename)], + recursively=t_case.recursive, + output=str(tmp_output_current), + # output=str(expect_output_current), # for expect docs generate + format=t_case.format, + locale=t_case.locale, + with_locale_suffix=True, + ) + + match, mismatch, errors = filecmp.cmpfiles( + expect_output_current, + tmp_output_current, + common=[ + str(f.relative_to(tmp_output_current)) + for f in list( + tmp_output_current.rglob( + f"*{doc_formats.KCLDocSuffix.TO_SUFFIX[t_case.format.upper()]}" + ) + ) + ], + ) + assert len(mismatch) == 0, f"mismatch exists: {mismatch}. {t_case.filename}" + assert len(errors) == 0, f"errors exists: {errors}. {t_case.filename}" + + # clear tmp files + shutil.rmtree(tmp_output) diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/assert.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/assert.golden new file mode 100644 index 000000000..6bf676029 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/assert.golden @@ -0,0 +1,3 @@ +assert True if True, "message" +assert False if data, "message" # Comment +assert 1 diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/assert.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/assert.input new file mode 100644 index 000000000..f0a13a045 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/assert.input @@ -0,0 +1,3 @@ +assert True if True, "message" +assert False if data , "message" # Comment +assert 1 diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/blankline.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/blankline.golden new file mode 100644 index 000000000..b92ce49ef --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/blankline.golden @@ -0,0 +1,7 @@ +a = 1 + +b = 2 + +c = 3 + +d = 4 diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/blankline.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/blankline.input new file mode 100644 index 000000000..b80849aac --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/blankline.input @@ -0,0 +1,12 @@ + +a=1 + + +b= 2 + + +c =3 + + + +d = 4 \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/breakline.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/breakline.golden new file mode 100644 index 000000000..25c0861d1 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/breakline.golden @@ -0,0 +1,9 @@ +import math + +schema Base: + name: str + +schema Person(Base): + age: int + +person = Person {} diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/breakline.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/breakline.input new file mode 100644 index 000000000..a8b397101 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/breakline.input @@ -0,0 +1,6 @@ +import math +schema Base: + name: str +schema Person(Base): + age: int +person = Person{} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/check.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/check.golden new file mode 100644 index 000000000..84cef43b7 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/check.golden @@ -0,0 +1,12 @@ + +schema Person: + firstName: str = "John" + lastName: str + times: int + check: + len(lastName) > 0 if times > 5 + +JohnDoe = Person { + "lastName": "Doe" + "times": 10 +} diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/check.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/check.input new file mode 100644 index 000000000..861b042ac --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/check.input @@ -0,0 +1,12 @@ + +schema Person: + firstName: str = "John" + lastName: str + times: int + check: + len(lastName) > 0 if times > 5 + +JohnDoe = Person { + "lastName": "Doe" + "times":10 +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/codelayout.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/codelayout.golden new file mode 100644 index 000000000..6d7e766da --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/codelayout.golden @@ -0,0 +1,73 @@ +import math as alias_math + +schema Person: + name: str # inline comment + age: int + +person = Person { + name: "Alice" + age: 18 +} +if True: + a = 1 +elif True: + b = 2 +else: + c = 3 +d = 1 + 2 +e = (1 + 2) +f = [1, 2, 3] +g = {"key": "value"} +# block comment +print(1) +dct = {"key": "value"} +lst = [1, 2, 3] +h = dct['key'] +i = lst[1] +x = 1 +y = 2 +long_variable = 3 +i = i + 1 +submitted += 1 +x = x * 2 - 1 +hypot2 = x * x + y * y +_c = (a + b) * (a - b) +_b = 2 +_c = 3 +_d = 4 + +_value = (1 + 2 * 3) +_value = (1 + 2 * 3) +_value = 1 + -2 * ~3 +_list = [1, 2, 3] +_list = [*_list, [4, 5, 6]] +_list = [*_list, [4, 5, 6]] + +_dict = {**{"k": "v"}, **{"k": "v"}} +a = [1, 2, 3] +b = [ + 1, 2, 3, + 4, 5, 6, +] +_dict = { + "k1": "v1" + "k2": "v2" + "k3": "v3" + "k4": "v4" + "k5": "v5" +} +foo = 1 +if foo is not None: + _a = 1 + _dict |= {} + hello = "world{}".format(1)[2:4].lower() + range_int = [i for i in range(10) if i > 1] +op = 1 + 2 - -3 + (3 - 1) // 3 +op += 1 +op -= 12 + 23 +print(" ", end='') +log = math.log(12) +aa = 1 +assert aa == 1, "message" +aaaa = (1 + 2 / 2) if _a == 2 + +134.3 else ("a" * 3) +bbbb = "{}".format(a) diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/codelayout.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/codelayout.input new file mode 100644 index 000000000..a64e4feed --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/codelayout.input @@ -0,0 +1,74 @@ + + + +import math as alias_math +schema Person: + name:str# inline comment + age:int +person = Person{ + name:"Alice" + age:18 +} +if True: + a = 1 +elif True: + b = 2 +else: + c = 3 +d = 1 + 2 +e = ( 1 + 2 ) +f=[ 1, 2, 3 ] +g = { "key" : "value" } +# block comment +print (1) +dct={"key": "value"} +lst=[1,2,3] +h = dct [ 'key' ] +i = lst [ 1 ] +x = 1 +y = 2 +long_variable = 3 +i = i+1 +submitted+=1 +x = x*2 - 1 +hypot2 = x*x + y*y +_c = (a+b) * (a-b) +_b=2 +_c= 3 +_d =4 + +_value = (1 + 2 * 3) +_value = (1+2*3) +_value =1+ - 2 * ~ 3 +_list = [1, 2, 3] +_list = [*_list, [4, 5 ,6]] +_list = [* _list, [4, 5 ,6]] + +_dict = {** {"k": "v"}, ** {"k": "v"}} +a = [1,2,3] +b = [ + 1,2,3, + 4,5,6, +] +_dict={ + "k1":"v1" + "k2" :"v2" + "k3": "v3" + "k4" : "v4" + "k5" : "v5" +} +foo=1 +if foo is not None: + _a = 1 + _dict|={} + hello = "world{}" . format( 1 )[2 : 4] . lower( ) + range_int = [ i for i in range( 10 ) if i > 1 ] +op = 1+2 - - 3 + (3 - 1) // 3 +op += 1 +op -= 12 + 23 +print( " " , end= '') +log = math. log(12) +aa = 1 +assert aa == 1,"message" +aaaa = (1 + 2 / 2) if _a == 2 + + 134.3 else ("a"*3) +bbbb = "{}". format(a) \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/collection_if.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/collection_if.golden new file mode 100644 index 000000000..25fb0aa88 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/collection_if.golden @@ -0,0 +1,32 @@ +dataDict0 = { + if True: + age = 101 + elif True: + age = 123 + else: + age = 111 +} +dataDict1 = { + if True: + age: 101 + elif True: + age: 123 + else: + age: 111 +} +dataList0 = [ + if True: + 1 + elif False: + 2 + else: + 3 +] +dataList1 = [ + if True: + *[1] + elif False: + 2 + else: + 3 +] diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/collection_if.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/collection_if.input new file mode 100644 index 000000000..a1e25c13f --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/collection_if.input @@ -0,0 +1,32 @@ +dataDict0 = { + if True: + age = 101 + elif True: + age = 123 + else: + age = 111 +} +dataDict1 = { + if True: + age : 101 + elif True: + age : 123 + else: + age : 111 +} +dataList0 = [ + if True: + 1 + elif False: + 2 + else : + 3 +] +dataList1 = [ + if True: + * [1] + elif False: + 2 + else : + 3 +] diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/comment.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/comment.golden new file mode 100644 index 000000000..a82bf0a81 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/comment.golden @@ -0,0 +1,10 @@ +# Block comment +a = 1 # Inline comment + +schema Person: + """ + Schema doc string + """ + name: str = "Alice" # Inline comment in schema + # Block comment in schema + age: int = 18 diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/comment.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/comment.input new file mode 100644 index 000000000..2134faeb9 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/comment.input @@ -0,0 +1,9 @@ +# Block comment +a = 1# Inline comment +schema Person: + """ + Schema doc string + """ + name:str="Alice"# Inline comment in schema + # Block comment in schema + age:int=18 \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/comp_for.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/comp_for.golden new file mode 100644 index 000000000..e20b45832 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/comp_for.golden @@ -0,0 +1,7 @@ +data0 = [ + i + 1 for i in range(10) if i > 1 +] +data1 = [ + i + 1 for i in range(10) + if i > 1 +] diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/comp_for.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/comp_for.input new file mode 100644 index 000000000..a4bb9ab42 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/comp_for.input @@ -0,0 +1,7 @@ +data0 = [ + i + 1 for i in range(10) if i > 1 +] +data1 = [ + i + 1 for i in range(10) + if i > 1 +] diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/empty.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/empty.golden new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/empty.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/empty.input new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/import.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/import.golden new file mode 100644 index 000000000..977a13711 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/import.golden @@ -0,0 +1,10 @@ +import a + +import b + +import c +import d + +import e as e + +a = 1 diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/import.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/import.input new file mode 100644 index 000000000..977a13711 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/import.input @@ -0,0 +1,10 @@ +import a + +import b + +import c +import d + +import e as e + +a = 1 diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/indent.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/indent.golden new file mode 100644 index 000000000..874e8f629 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/indent.golden @@ -0,0 +1,14 @@ +if True: + a = 1 +elif True: + b = 2 + if True: + c = 3 + else: + d = 4 +else: + e = 8 + +schema Person: + name: str + age: int diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/indent.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/indent.input new file mode 100644 index 000000000..40d99bce0 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/indent.input @@ -0,0 +1,14 @@ +if True: + a = 1 +elif True: + b = 2 + if True: + c = 3 + else: + d = 4 +else: + e = 8 + +schema Person: + name: str + age: int \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/inline_comment.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/inline_comment.golden new file mode 100644 index 000000000..f9c221663 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/inline_comment.golden @@ -0,0 +1,17 @@ +# Inline comment 1 +# Inline comment 2 +# Inline comment 3 +a = 1 +# Inline comment 4 +# Inline comment 5 +# +# +# Inline comment 6 +# Inline comment 7 +# +# Inline comment 8 +b = 2 +# Same inline comment +# Same inline comment +# Same inline comment +c = b + 1 diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/inline_comment.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/inline_comment.input new file mode 100644 index 000000000..04aa1815b --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/inline_comment.input @@ -0,0 +1,17 @@ +# Inline comment 1 +# Inline comment 2 +# Inline comment 3 +a = 1 +# Inline comment 4 +# Inline comment 5 +# +# +# Inline comment 6 +# Inline comment 7 +# +# Inline comment 8 +b=2 +# Same inline comment +# Same inline comment +# Same inline comment +c=b+1 diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/lambda.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/lambda.golden new file mode 100644 index 000000000..08d1b9b63 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/lambda.golden @@ -0,0 +1,6 @@ +f0 = lambda { + 1 + 1 +} +f1 = lambda x: int, y: int -> int { + x + y +} diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/lambda.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/lambda.input new file mode 100644 index 000000000..55039b367 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/lambda.input @@ -0,0 +1,6 @@ +f0 = lambda { + 1 + 1 +} +f1 = lambda x : int , y : int ->int{ + x + y +} diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/quant.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/quant.golden new file mode 100644 index 000000000..a6a65a3c2 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/quant.golden @@ -0,0 +1,8 @@ +a = all x in [-1, 0, 1, 2, 3] { + x >= 1 if x > 0 +} +b = any x in {k1 = "v1", k2 = "v2"} {x in ["k1", "v2"]} +c = map x in {k1 = "v1", k2 = "v2"} {x} +d = filter x in [1, 2, 3] { + x > 1 +} diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/quant.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/quant.input new file mode 100644 index 000000000..a6a65a3c2 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/quant.input @@ -0,0 +1,8 @@ +a = all x in [-1, 0, 1, 2, 3] { + x >= 1 if x > 0 +} +b = any x in {k1 = "v1", k2 = "v2"} {x in ["k1", "v2"]} +c = map x in {k1 = "v1", k2 = "v2"} {x} +d = filter x in [1, 2, 3] { + x > 1 +} diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/schema.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/schema.golden new file mode 100644 index 000000000..04f126520 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/schema.golden @@ -0,0 +1,21 @@ +import math + +schema XXMixin: + nameVar: str + +schema Base: + """ + Base schema doc string + """ + mixin [XXMixin, XXMixin] + name: str = "Alice" + labels: {str:str} = None + +schema Person[para1:str = "value", para2="value"](Base): + age: int = 18 + name = para + check: + True + bool(math.log(10)) + +person = Person(para1="12") {} diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/schema.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/schema.input new file mode 100644 index 000000000..2c877a1a1 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/schema.input @@ -0,0 +1,17 @@ +import math +schema XXMixin: + nameVar: str +schema Base: + """ + Base schema doc string + """ + mixin[XXMixin,XXMixin] + name:str="Alice" + labels: {str : str}=None +schema Person[para1:str="value",para2="value"] ( Base ) : + age:int=18 + name=para + check : + True + bool (math. log(10)) +person = Person(para1 = "12"){} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/string.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/string.golden new file mode 100644 index 000000000..2c66a7158 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/string.golden @@ -0,0 +1,4 @@ +strA = '123' +strB = ''' +long string +''' diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/string.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/string.input new file mode 100644 index 000000000..91b9f9486 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/string.input @@ -0,0 +1,4 @@ +strA='123' +strB= ''' +long string +''' \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/type_alias.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/type_alias.golden new file mode 100644 index 000000000..87616fb37 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/type_alias.golden @@ -0,0 +1,7 @@ +schema Person: + name: str = "kcl" + age: int = 1 + +type PersonOther = Person +type Int = int +type UnionType = int | float | str diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/type_alias.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/type_alias.input new file mode 100644 index 000000000..237ab7f85 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/type_alias.input @@ -0,0 +1,7 @@ +schema Person: + name: str = "kcl" + age: int = 1 + +type PersonOther = Person +type Int = int +type UnionType = int | float | str diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/unary.golden b/test/test_units/test_kclvm/test_tools/test_format/format_data/unary.golden new file mode 100644 index 000000000..146472978 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/unary.golden @@ -0,0 +1,3 @@ +x = not True or not False +y = +1 + -1 +z = ~0x11 diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_data/unary.input b/test/test_units/test_kclvm/test_tools/test_format/format_data/unary.input new file mode 100644 index 000000000..15b91de48 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_data/unary.input @@ -0,0 +1,3 @@ +x = not True or not False +y = +1 + -1 +z = ~ 0x11 diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_path_data/internal_pkg/test.k b/test/test_units/test_kclvm/test_tools/test_format/format_path_data/internal_pkg/test.k new file mode 100644 index 000000000..457e49fa0 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_path_data/internal_pkg/test.k @@ -0,0 +1,14 @@ +if True: + a = 1 +elif True: + b = 2 + if True: + c = 3 + else: + d = 4 +else: + e = 8 + +schema Person: + name: str + age: int diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_path_data/output.golden b/test/test_units/test_kclvm/test_tools/test_format/format_path_data/output.golden new file mode 100644 index 000000000..874e8f629 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_path_data/output.golden @@ -0,0 +1,14 @@ +if True: + a = 1 +elif True: + b = 2 + if True: + c = 3 + else: + d = 4 +else: + e = 8 + +schema Person: + name: str + age: int diff --git a/test/test_units/test_kclvm/test_tools/test_format/format_path_data/test.k b/test/test_units/test_kclvm/test_tools/test_format/format_path_data/test.k new file mode 100644 index 000000000..457e49fa0 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/format_path_data/test.k @@ -0,0 +1,14 @@ +if True: + a = 1 +elif True: + b = 2 + if True: + c = 3 + else: + d = 4 +else: + e = 8 + +schema Person: + name: str + age: int diff --git a/test/test_units/test_kclvm/test_tools/test_format/test_format.py b/test/test_units/test_kclvm/test_tools/test_format/test_format.py new file mode 100644 index 000000000..eff9ff89f --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_format/test_format.py @@ -0,0 +1,232 @@ +#! /usr/bin/env python3 + +import io +import sys +import unittest +import pathlib +from typing import Tuple + +from kclvm.tools.format import kcl_fmt_source, kcl_fmt, TextAdapterWalker, Formatter + + +_FILE_INPUT_SUFFIX = ".input" +_FILE_OUTPUT_SUFFIX = ".golden" +_PATH_NAME = "format_data" +_DIR_PATH = pathlib.Path(__file__).parent.joinpath(_PATH_NAME) +_TEST_CASES = [ + "assert", + "check", + "blankline", + "breakline", + "codelayout", + "collection_if", + "comment", + "comp_for", + "empty", + "import", + "indent", + "inline_comment", + "lambda", + "quant", + "schema", + "string", + "type_alias", + "unary", +] +TEST_FMT_PATH = pathlib.Path(__file__).parent.joinpath("format_path_data") +FMT_PATH_EXPECTED_OUTPUT = pathlib.Path(__file__).parent.joinpath("format_path_data/output.golden").read_text() + + +class KCLBaseFormatterTest(unittest.TestCase): + """ + KCL base formatter test + """ + + def read_data(self, data_name: str) -> Tuple[str, str]: + """ + Read format data according to data name + """ + input_filename = data_name + _FILE_INPUT_SUFFIX + output_filename = data_name + _FILE_OUTPUT_SUFFIX + data_input = (_DIR_PATH / input_filename).read_text() + data_output = (_DIR_PATH / output_filename).read_text() + return data_input, data_output + + def assert_format_equal(self, data_name: str) -> None: + """ + Read format test data according to data name and assert equal. + """ + data_input, data_output = self.read_data(data_name) + formatted, _ = kcl_fmt_source(data_input) + self.assertMultiLineEqual(formatted, data_output) + + def assert_printer_equal(self, string: str, walker: TextAdapterWalker) -> bool: + """ + Read walker printer buffer string and assert it is equal to 'string' + """ + walker.printer.seek(0) + self.assertEqual(string, walker.printer.read()) + + +class KCLFormatterTest(KCLBaseFormatterTest): + def test_format_data(self) -> None: + """ + Test format data for the comparison of input and golden files + """ + self.maxDiff = None + for case in _TEST_CASES: + print(case+"-begin") + self.assert_format_equal(case) + print(case+"-end") + + def test_text_adapter_walker_write(self) -> None: + """ + Test TextAdapterWalker class write function in kclvm format.py + """ + inputs = ["test", "123", "456", "\n"] + outputs = ["test", "test123", "test123456", "test123456\n"] + walker = TextAdapterWalker() + for input, output in zip(inputs, outputs): + walker.write(input) + self.assert_printer_equal(output, walker) + + def test_text_adapter_walker_blankline(self) -> None: + """ + Test TextAdapterWalker class blankline function in kclvm format.py + """ + inputs = [ + "\n", + "\n\n", + "\n\n\n", + "\n \n \n", + "\n # comment \n \n", + "\n # comment \n # comment \n", + ] + outputs = [0, 1, 2, 2, 1, 0] + walker = TextAdapterWalker() + for input, output in zip(inputs, outputs): + self.assertEqual(walker.count_blank_line(input), output) + + def test_text_adapter_walker_indent(self) -> None: + """ + Test TextAdapterWalker class indent function in kclvm format.py + """ + inputs = ["", " ", "\n ", " \n ", "\n ", "\n\n ", "\n ", "\n"] + outputs = [0, 0, 2, 2, 4, 4, 2, 0] + levels = [0, 0, 1, 1, 2, 2, 1, 0] + walker = TextAdapterWalker() + for input, output, level in zip(inputs, outputs, levels): + self.assertEqual(walker.count_indent(input), output) + self.assertEqual(walker.indent_level, level) + + def test_formatter_split_newline(self) -> None: + """ + Test Formatter class split_newline function in kclvm format.py + """ + inputs = [ + "\n# comment\n", + "\n # comment \n", + "\n # comment \n ", + "\n # comment # \n", + "\n\n # comment \\n # \n", + "\n\n # comment \\n # \n#comment\n", + "\n \n # comment \n # comment \n\n # comment \n", + "\n\n # comment \\n # # \n # \n # \n \n# comment\n\n# comment \n", + ] + outputs = [ + ["\n", "# comment", "\n"], + ["\n ", "# comment ", "\n"], + ["\n ", "# comment ", "\n "], + ["\n ", "# comment # ", "\n"], + ["\n\n ", "# comment \\n # ", "\n"], + ["\n\n ", "# comment \\n # ", "\n", "#comment", "\n"], + ["\n \n ", "# comment ", "\n ", "# comment ", "\n\n ", "# comment ", "\n"], + [ + "\n\n ", + "# comment \\n # # ", + "\n ", + "# ", + "\n ", + "# ", + "\n \n", + "# comment", + "\n\n", + "# comment ", + "\n", + ], + ] + formatter = Formatter() + for input, output in zip(inputs, outputs): + self.assertListEqual(formatter.split_newline_value(input), output) + + def test_formatter_write_newline(self) -> None: + """ + Test Formatter class write newline function in kclvm format.py + """ + inputs = ["\n", "\n\n", "\n\n\n", "\n\n\n\n"] + outputs = ["\n", "\n\n", "\n\n", "\n\n"] + for input, output in zip(inputs, outputs): + formatter = Formatter() + formatter.write_newline(input) + self.assert_printer_equal(output, formatter) + + def test_formatter_write_string(self) -> None: + """ + Test Formatter class write newline function in kclvm format.py + """ + inputs = ["'test'", '"test"', "R'test'", 'B"test"'] + outputs = ["'test'", '"test"', "r'test'", 'b"test"'] + for input, output in zip(inputs, outputs): + formatter = Formatter() + formatter.write_string(input) + self.assert_printer_equal(output, formatter) + + def test_kcl_fmt_recursively(self): + with io.StringIO() as buf: + origin_io = sys.stdout + sys.stdout = buf + kcl_fmt(TEST_FMT_PATH, is_stdout=True, recursively=True) + # Format two files recursively + self.assertEqual(buf.getvalue().count(FMT_PATH_EXPECTED_OUTPUT), 2) + sys.stdout = origin_io + + def test_kcl_fmt_non_recursively(self): + # Format one file non-recursively + with io.StringIO() as buf: + origin_io = sys.stdout + sys.stdout = buf + kcl_fmt(TEST_FMT_PATH, is_stdout=True, recursively=False) + # only one file formatted + self.assertEqual(buf.getvalue().count(FMT_PATH_EXPECTED_OUTPUT), 1) + sys.stdout = origin_io + + def test_kcl_fmt_single_file(self): + # Format single file + for case in _TEST_CASES: + with io.StringIO() as buf: + origin_io = sys.stdout + sys.stdout = buf + input_filename = str(_DIR_PATH / (case + _FILE_INPUT_SUFFIX)) + output_filename = case + _FILE_OUTPUT_SUFFIX + expect_output = (_DIR_PATH / output_filename).read_text() + + kcl_fmt(input_filename, is_stdout=True) + self.assertEqual(expect_output, buf.getvalue()) + sys.stdout = origin_io + + + def test_kcl_fmt_not_stdout(self): + for case in _TEST_CASES: + input_filepath = _DIR_PATH / (case + _FILE_INPUT_SUFFIX) + output_filepath = _DIR_PATH / (case + _FILE_OUTPUT_SUFFIX) + ori_content_backup = input_filepath.read_text() + expect_output = output_filepath.read_text() + + kcl_fmt(input_filepath, is_stdout=False) + formatted_content = input_filepath.read_text() + self.assertEqual(expect_output, formatted_content) + input_filepath.write_text(ori_content_backup) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_checker.py b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_checker.py new file mode 100644 index 000000000..41ba1a904 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_checker.py @@ -0,0 +1,177 @@ +import unittest +import pathlib +import kclvm.tools.lint.lint.KCLLint as KCLLint +from kclvm.tools.lint.message.message import Message +import kclvm.compiler.parser.parser as parser + +_FILE_INPUT_SUFFIX = ".k" +_FILE_OUTPUT_SUFFIX = ".golden" +_PATH_NAME = "test_data" +_DIR_PATH = pathlib.Path(__file__).parent.joinpath(_PATH_NAME) +_TEST_CASE_NAMES = [ + "import", + "misc", + "basic", +] +_TEST_CASE_OUTPUT = { + "import": [ + Message("E0401", + str(_DIR_PATH.joinpath("import.k")), + "Unable to import abc.", + "import abc # unable to import", + (1, 1), + ["abc"]), + Message("W0404", + str(_DIR_PATH.joinpath("import.k")), + "a is reimported multiple times.", + "import a # reimport", + (3, 1), + ["a"]), + Message("E0413", + str(_DIR_PATH.joinpath("import.k")), + "Import b should be placed at the top of the module.", + "import b # import position", + (18, 1), + ["b"]), + Message("W0411", + str(_DIR_PATH.joinpath("import.k")), + "math imported but unused.", + "import math", + (5, 1), + ["math"]), + Message("W0411", + str(_DIR_PATH.joinpath("import.k")), + "b imported but unused.", + "import b # import position", + (18, 1), + ["b"]), + + ], + "misc": [ + Message("E0501", + str(_DIR_PATH.joinpath("misc.k")), + "line too long (121 > 120 characters).", + "# line too long, line too long, line too long, line too long, line too long, line too long, line too long, line too long,", + (1, 1), + [121, 120]) + ], + "basic": [ + Message("C0103", + str(_DIR_PATH.joinpath("basic.k")), + "Variable name \"I\" doesn't conform to ^[^\W\dA-Z][^\W_]+$.", + "I = 1", + (2, 1), + ['Variable', 'I', '^[^\\W\\dA-Z][^\\W_]+$']), + + Message("C0104", + str(_DIR_PATH.joinpath("basic.k")), + "Disallowed name \"I\".", + "I = 1", + (2, 1), + ['I']), + Message("C0103", + str(_DIR_PATH.joinpath("basic.k")), + "Schema name \"person\" doesn't conform to PascalCase naming style.", + "schema person:", + (5, 8), + ['Schema', 'person', 'PascalCase naming style']), + Message("C0103", + str(_DIR_PATH.joinpath("basic.k")), + "Mixin name \"personMixin\" doesn't conform to PascalCase naming style.", + "mixin personMixin:", + (9, 7), + ['Mixin', 'personMixin', 'PascalCase naming style']), + Message("C0103", + str(_DIR_PATH.joinpath("basic.k")), + "Argument name \"PaP\" doesn't conform to camelCase naming style.", + "schema Person[PaP]:", + (13, 15), + ['Argument', 'PaP', 'camelCase naming style']), + Message("C0103", + str(_DIR_PATH.joinpath("basic.k")), + "Schema attribute name \"Age\" doesn't conform to camelCase naming style.", + " Age : int = 1", + (15, 5), + ['Schema attribute', 'Age', 'camelCase naming style']), + Message("C0103", + str(_DIR_PATH.joinpath("basic.k")), + "Variable name \"pers_on\" doesn't conform to ^[^\W\dA-Z][^\W_]+$.", + "pers_on = Person {", + (19, 1), + ['Variable', 'pers_on', '^[^\\W\\dA-Z][^\\W_]+$']), + Message("C0103", + str(_DIR_PATH.joinpath("basic.k")), + "Protocol name \"personProtocol\" doesn't conform to PascalCase naming style.", + "protocol personProtocol:", + (24, 10), + ['Protocol', 'personProtocol', 'PascalCase naming style']), + Message("C0103", + str(_DIR_PATH.joinpath("basic.k")), + "Schema name \"someRule\" doesn't conform to PascalCase naming style.", + "rule someRule:", + (39, 6), + ['Schema', 'someRule', 'PascalCase naming style']), + ], +} +CHECKER_CONFIG = { + "check_list": ["import", "misc", "basic"], + "ignore": [], + "max_line_length": 120, + "output": ["stdout"], + "output_path": None, + "module_naming_style": "ANY", + "package_naming_style": "ANY", + "schema_naming_style": "PascalCase", + "mixin_naming_style": "PascalCase", + "argument_naming_style": "camelCase", + "schema_attribute_naming_style": "camelCase", + "variable_rgx": r"^[^\W\dA-Z][^\W_]+$", + "bad_names": ["foo", "bar", "baz", "toto", "tata", "tutu", "I", "l", "O"], +} + + +class KCLCheckerBaseTest(unittest.TestCase): + def setUp(self): + self.maxDiff = None + + def read_data(self, data_name: str): + """Read test data""" + input_filename = data_name + _FILE_INPUT_SUFFIX + input_file = _DIR_PATH / input_filename + with open(input_file) as f: + code = f.read() + + return input_file, code + + def assert_checker_msgs_equal( + self, case: str, input_file: str, code: str + ): + checker = KCLLint.CheckerFactory.get_checker(case) + checker.options = KCLLint.LinterConfig() + checker.options.update(CHECKER_CONFIG) + prog = parser.LoadProgram(input_file) + checker.check(prog, code) + for i, m in enumerate(checker.msgs): + expect = _TEST_CASE_OUTPUT[case][i] + self.assertEqual(expect, m, f"Expected:\n{expect}\narguments:{expect.arguments}\nActual:\n{m}\narguments:{m.arguments}") + + +class KCLCheckerTest(KCLCheckerBaseTest): + def test_checker_msgs(self) -> None: + for case in _TEST_CASE_NAMES: + input_file, code = self.read_data(case) + self.assert_checker_msgs_equal(case, input_file, code) + + def test_dot_path(self) -> None: + # When the path is `.`, the filename in ast is "a/./b". This may affect import path check, e.g: a/./b == a/b + input_file = _DIR_PATH / ("./import" + _FILE_INPUT_SUFFIX) + with open(input_file) as f: + code = f.read() + checker = KCLLint.CheckerFactory.get_checker("import") + checker.options = CHECKER_CONFIG + prog = parser.LoadProgram(input_file) + checker.check(prog, code) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/a.k b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/a.k new file mode 100644 index 000000000..12a18c3c3 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/a.k @@ -0,0 +1,4 @@ +_a = 1 +schema Person: + name: str + age: int \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/b.k b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/b.k new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/basic.k b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/basic.k new file mode 100644 index 000000000..618d8e703 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/basic.k @@ -0,0 +1,40 @@ +# disallowed name +I = 1 + +#schema name +schema person: + name : str = "kcl" + +# mixin +mixin personMixin: + name : str = "kcl" + +# schema argument, camelCase +schema Person[PaP]: + # schema attr, camelCase + Age : int = 1 + name : str = "1" + +# variable, rgx: ^[^\W\dA-Z][^\W_]+$ +pers_on = Person { + age = 1 +} + +# protocol +protocol personProtocol: + name : str = "kcl" + +# TODO: just for test coverage, remove when fix generic_walk for IfStmt.elif_body: List[List[Stmt]] +if Ture: + _c = 1 +elif True: + _c = 2 +else: + _c = 3 + +# rule name +rule SomeRule: + i > 1 + +rule someRule: + i > 1 diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/c.k b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/c.k new file mode 100644 index 000000000..f4832d10f --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/c.k @@ -0,0 +1,2 @@ +schema TestOfMixin: + age?: int diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/d.k b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/d.k new file mode 100644 index 000000000..5f833b16d --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/d.k @@ -0,0 +1,2 @@ +schema Parent: + age1?: int \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/e.k b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/e.k new file mode 100644 index 000000000..30670d303 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/e.k @@ -0,0 +1,2 @@ +schema UnionType: + a?: int \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/f.k b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/f.k new file mode 100644 index 000000000..ad344532c --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/f.k @@ -0,0 +1,2 @@ +schema UnionType: + b?: int \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/import.k b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/import.k new file mode 100644 index 000000000..5a0cdf332 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/import.k @@ -0,0 +1,32 @@ +import abc # unable to import +import a +import a # reimport + +import math +import c # test unused import for mixin name +import d # test unused import for parent name +import e # test unused import for union type +import f # test unused import for union type + +schema Main(d.Parent): + mixin [c.TestOfMixin] + age?: int = 18 + person?: a.Person + list_union_type: [e.UnionType|int] + dict_union_type: {f.UnionType|int:float} + +import b # import position + +if a._a > 1: + _c = 1 +elif a._a == 1: + _c = 2 +else: + _c = 3 + +p = Main{ + age = a._a +} + + + diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/kcl.mod b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/misc.k b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/misc.k new file mode 100644 index 000000000..da988199a --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_checker/test_data/misc.k @@ -0,0 +1 @@ +# line too long, line too long, line too long, line too long, line too long, line too long, line too long, line too long, \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_exception/test_data/main.k b/test/test_units/test_kclvm/test_tools/test_lint/test_exception/test_data/main.k new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_exception/test_exceptions.py b/test/test_units/test_kclvm/test_tools/test_lint/test_exception/test_exceptions.py new file mode 100644 index 000000000..49a5fc262 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_exception/test_exceptions.py @@ -0,0 +1,109 @@ +import pathlib +import unittest + +import kclvm.tools.lint.lint.KCLLint as KCLLint +import kclvm.tools.lint.lint.exceptions as exceptions + + +LINT_CONFIG_SUFFIX = ".kcllint" +_PATH_NAME = "test_data" +_FILE_NAME = "main.k" +_DIR_PATH = pathlib.Path(__file__).parent.joinpath(_PATH_NAME) +_TEST_FILE = _DIR_PATH.joinpath(_FILE_NAME) +_WRONG_FILE_NAME = "mian.k" +_WRONG_TEST_FILE = _DIR_PATH.joinpath(_WRONG_FILE_NAME) + + +TEST_CASE = { + "invalid_checker": { + "code": "", + "path": _TEST_FILE, + "config": { + "check_list": ["invalid_checker"], + "ignore": [], + "max_line_length": 200, + "output": ["stdout"], + } + }, + "invalid_reporter": { + "code": "", + "path": _TEST_FILE, + "config": { + "check_list": ["import"], + "ignore": [], + "max_line_length": 200, + "output": ["invalid_reporter"], + } + }, + "empty_reporter": { + "code": "", + "path": _TEST_FILE, + "config": { + "output": [], + } + }, + "without_output_path": { + "code": "", + "path": _TEST_FILE, + "config": { + "output": ["file"], + } + }, + "wrong_path": { + "path": _WRONG_TEST_FILE, + } +} + + +class KCLLintExceptionTest(unittest.TestCase): + def test_invalid_checker(self): + code = TEST_CASE["invalid_checker"]["code"] + config = TEST_CASE["invalid_checker"]["config"] + path = TEST_CASE["invalid_checker"]["path"] + try: + KCLLint.kcl_lint_code(path, k_code_list=[code], config=config) + assert False + except Exception as err: + assert isinstance(err, exceptions.InvalidCheckerError) + + def test_invalid_reporter(self): + code = TEST_CASE["invalid_reporter"]["code"] + config = TEST_CASE["invalid_reporter"]["config"] + path = TEST_CASE["invalid_reporter"]["path"] + try: + KCLLint.kcl_lint_code(path, k_code_list=[code], config=config) + assert False + except Exception as err: + assert isinstance(err, exceptions.InvalidReporterError) + + def test_empty_reporter(self): + code = TEST_CASE["empty_reporter"]["code"] + config = TEST_CASE["empty_reporter"]["config"] + path = TEST_CASE["empty_reporter"]["path"] + try: + KCLLint.kcl_lint_code(path, k_code_list=[code], config=config) + assert False + except Exception as err: + assert isinstance(err, exceptions.EmptyReporterError) + + def test_without_output_path(self): + code = TEST_CASE["without_output_path"]["code"] + config = TEST_CASE["without_output_path"]["config"] + path = TEST_CASE["without_output_path"]["path"] + try: + KCLLint.kcl_lint_code(path, k_code_list=[code], config=config) + assert False + except Exception as err: + assert isinstance(err, AssertionError) + self.assertEqual(err.args, ("Without ouput file path",)) + + def test_wrong_path(self): + path = TEST_CASE["wrong_path"]["path"] + try: + KCLLint.kcl_lint(path) + assert False + except Exception as err: + assert isinstance(err, FileNotFoundError) + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/.kcllint b/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/.kcllint new file mode 100644 index 000000000..003edc9a8 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/.kcllint @@ -0,0 +1 @@ +max_line_length: 120 diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/empty_file.k b/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/empty_file.k new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/failed.k b/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/failed.k new file mode 100644 index 000000000..a62a04da4 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/failed.k @@ -0,0 +1 @@ +a ==== 1 # Parse failed \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/kcl.mod b/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/main.k b/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/main.k new file mode 100644 index 000000000..6df733dce --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_data/main.k @@ -0,0 +1,2 @@ +import empty_file +$for = 1 \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_lint_integration.py b/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_lint_integration.py new file mode 100644 index 000000000..4d403f50a --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_lint_integration/test_lint_integration.py @@ -0,0 +1,126 @@ +import pathlib +import unittest + +import kclvm.tools.lint.lint.KCLLint as KCLLint +from kclvm.tools.lint.checkers.imports import ImportsChecker +from kclvm.tools.lint.checkers.misc import MiscChecker +from kclvm.tools.lint.checkers.basic import BasicChecker +from kclvm.tools.lint.reporters.stdout_reporter import STDOUTReporter +from kclvm.tools.lint.message.message import Message + +LINT_CONFIG_SUFFIX = ".kcllint" +_FILE_INPUT_SUFFIX = ".k" +_PATH_NAME = "test_data" +_DIR_PATH = pathlib.Path(__file__).parent.joinpath(_PATH_NAME) + + +class KCLLintIntegrationTest(unittest.TestCase): + msgs = [ + Message( + "E0999", + str(_DIR_PATH.joinpath("failed.k")), + "Parse failed:InvalidSyntax.", + "a ==== 1 # Parse failed", + (1, 5), + ["InvalidSyntax"] + ), + Message( + "W0411", + str(_DIR_PATH.joinpath("main.k")), + "empty_file imported but unused.", + "import empty_file", + (1, 1), + ["empty_file"] + ), + ] + lint_code_test_case = [ + { + "code": """a ==== 1 # Parse failed +""", + "file": str(_DIR_PATH.joinpath("failed.k")), + "msgs": [ + Message( + "E0999", + str(_DIR_PATH.joinpath("failed.k")), + "Parse failed:InvalidSyntax.", + "a ==== 1 # Parse failed", + (1, 5), + ["InvalidSyntax"] + ) + ], + }, + { + "code": """import empty_file +""", + "file": str(_DIR_PATH.joinpath("main.k")), + "msgs": [ + Message( + "W0411", + str(_DIR_PATH.joinpath("main.k")), + "empty_file imported but unused.", + "import empty_file", + (1, 1), + ["empty_file"] + ) + ], + }, + ] + + def setUp(self) -> None: + self.linter = KCLLint.KCLLinter(_DIR_PATH) + self.linter.run() + + def test_load_config(self): + config = { + "check_list": ["import", "misc", "basic"], + "ignore": [], + "max_line_length": 120, # default 200, overwrite in .kcllint + "output": ["stdout"], + "output_path":None, + "module_naming_style": "ANY", + "package_naming_style": "ANY", + "schema_naming_style": "PascalCase", + "mixin_naming_style": "PascalCase", + "argument_naming_style": "camelCase", + "variable_naming_style": "ANY", + "schema_attribute_naming_style": "ANY", + "module_rgx": None, + "package_rgx": None, + "schema_rgx": None, + "mixin_rgx": None, + "argument_rgx": None, + "variable_rgx": None, + "schema_attribute_rgx": None, + "bad_names": ["foo", "bar", "baz", "toto", "tata", "tutu", "I", "l", "O"], + } + for k, v in config.items(): + self.assertEqual(getattr(self.linter.config, k), v) + + def test_register_checkers(self): + checker_list = [ImportsChecker(self.linter), MiscChecker(self.linter), BasicChecker(self.linter)] + self.assertListEqual(self.linter.checkers, checker_list) + + def test_register_reporter(self): + reporter_list = [STDOUTReporter(self.linter)] + self.assertListEqual(self.linter.reporters, reporter_list) + + def test_ignore_msg(self): + self.linter.config.ignore = ["E0999", "W0411"] + self.linter.run() + self.assertEqual(len(self.linter.msgs), 0) + + def test_kcl_lint_fun(self): + msgs = KCLLint.kcl_lint(_DIR_PATH) + self.assertListEqual(msgs, self.msgs) + + def test_kcl_lint_code_fun(self): + for case in self.lint_code_test_case: + code = case["code"] + msgs = KCLLint.kcl_lint_code(case["file"], k_code_list=[code]) + for i, m in enumerate(msgs): + expect = case["msgs"][i] + self.assertEqual(expect, m, f"Expected:\n{expect}\narguments:{expect.arguments}\nActual:\n{m}\narguments:{m.arguments}") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_data/file_reporter.golden b/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_data/file_reporter.golden new file mode 100644 index 000000000..eb52f3124 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_data/file_reporter.golden @@ -0,0 +1,7 @@ +:1:1: E0401 Unable to import abc. +import abc as abc +^ + +Check total 1 files: +1 E0401: Unable to import +KCL Lint: 1 problems diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_data/kcl.mod b/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_data/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_data/reporter.k b/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_data/reporter.k new file mode 100644 index 000000000..479f37a3a --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_data/reporter.k @@ -0,0 +1,3 @@ +import abc as abc # This file is only used to create linter + + diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_data/sarif_reporter.golden b/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_data/sarif_reporter.golden new file mode 100644 index 000000000..c51270eb7 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_data/sarif_reporter.golden @@ -0,0 +1,52 @@ +{ + "runs": [ + { + "results": [ + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "kclvm/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_data/reporter.k" + }, + "region": { + "startColumn": 1, + "startLine": 1 + } + } + } + ], + "message": { + "arguments": [ + "abc" + ], + "id": "default" + }, + "ruleId": "E0401" + } + ], + "tool": { + "driver": { + "informationUri": "https://kusion-docs.com/docs/reference/cli/kcl/lint", + "name": "kcl-lint", + "rules": [ + { + "id": "E0401", + "messageStrings": { + "default": { + "text": "Unable to import '{0}'." + }, + "shortStrings": { + "text": "Unable to import" + } + } + } + ], + "version": "0.0.1" + } + } + } + ], + "version": "2.1.0" +} diff --git a/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_reporter.py b/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_reporter.py new file mode 100644 index 000000000..c409e59b3 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_lint/test_reporter/test_reporter.py @@ -0,0 +1,179 @@ +import unittest +import pathlib +import sys +import os + +import kclvm.tools.lint.lint.KCLLint as KCLLint +from kclvm.tools.lint.reporters.stdout_reporter import color +from kclvm.tools.lint.message.message import Message + +_FILE_INPUT_SUFFIX = ".k" +_FILE_OUTPUT_SUFFIX = ".golden" +_FILE_TEST_OUTPUT_SUFFIX = ".test" +_PATH_NAME = "test_data" +_DIR_PATH = pathlib.Path(__file__).parent.joinpath(_PATH_NAME) + +_TEST_CASE_NAMES = [ + "stdout", + "file" +] + +_TEST_CASE = { + "stdout": { + "msg": [ + Message("E0401", + str(_DIR_PATH.joinpath("reporter.k")), + "Unable to import abc.", + "import abc as abc", + (1, 1), + ["abc"]), + ], + "msgs_map": {"E0401": 1}, + "expected": { + "msg_with_color": color(str(_DIR_PATH.joinpath("reporter.k")), "FILE_NAME") + + f''':{color("1", "LINE_COLUMN")}:{color("1", "LINE_COLUMN")}: {color("E0401", "ID")} Unable to import abc. +import abc as abc +{color("^", "MARK")} + +Check total {color("1", "NUMBER")} files: +{color("1", "NUMBER")} {color("E0401", "ID")}: Unable to import +KCL Lint: {color("1", "NUMBER")} problems +''', + "msg_without_color": str(_DIR_PATH.joinpath("reporter.k")) + + f''':1:1: E0401 Unable to import abc. +import abc as abc +^ + +Check total 1 files: +1 E0401: Unable to import +KCL Lint: 1 problems +'''}}, + "file": + {"msg": [ + Message("E0401", + str(_DIR_PATH.joinpath("reporter.k")), + "Unable to import abc.", + "import abc as abc", + (1, 1), + ["abc"]), + ], + "msgs_map": {"E0401": 1}, + "expected": "" + }, + "sarif": + {"msg": [ + Message("E0401", + str(_DIR_PATH.joinpath("reporter.k")), + "Unable to import abc.", + "import abc as abc", + (1, 1), + ["abc"]), + ], + "msgs_map": {"E0401": 1}, + "expected": "" + }, +} +DEFAULT_CONFIG = { + "check_list": ["import", "misc"], + "ignore": [], + "max_line_length": 200, + "output": ["stdout"], + "output_path": None +} +MSGS = { + "E0401": ( + "Unable to import %s.", + "Unable to import", + "Unable to import '{0}'." + ) +} + + +class KCLReporterBaseTest(unittest.TestCase): + def setUp(self): + self.maxDiff = None + + def read_data(self, data_name: str): + """Read test data""" + input_filename = data_name + _FILE_INPUT_SUFFIX + input_file = _DIR_PATH / input_filename + return input_file + + def build_reporter(self, case): + input_file = self.read_data("reporter") + linter = KCLLint.KCLLinter(input_file, config=DEFAULT_CONFIG) + linter.msgs = _TEST_CASE[case]["msg"] + linter.msgs_map = _TEST_CASE[case]["msgs_map"] + if case == "file" or case == "sarif": + linter.config.output_path = str(_DIR_PATH.joinpath(case + "_reporter" + _FILE_TEST_OUTPUT_SUFFIX)) + reporter = KCLLint.ReporterFactory.get_reporter(case, linter) + linter.MSGS = MSGS + return reporter + + +class KCLReporterTest(KCLReporterBaseTest): + def test_stdout_reporter(self) -> None: + class _redirect: + content = "" + is_atty = False + + def write(self, in_str): + self.content += in_str + + def flush(self): + self.content = "" + + def isatty(self): + return self.is_atty + + def getvalue(self): + return self.content + + r = _redirect() + sys.stdout = r + reporter = self.build_reporter("stdout") + reporter.print_msg(reporter.linter.msgs, r) + self.assertEqual(r.content, _TEST_CASE["stdout"]["expected"]["msg_without_color"]) + + """ + Print msg with color, remove it temporarily. + r.is_atty = True + r.content = "" + reporter.print_msg(reporter.linter.msgs, r) + self.assertEqual(r.content, _TEST_CASE["stdout"]["expected"]["msg_with_color"]) + """ + + def test_file_reporter(self) -> None: + reporter = self.build_reporter("file") + reporter.display() + test_output = str(_DIR_PATH.joinpath("file_reporter" + _FILE_TEST_OUTPUT_SUFFIX)) + golden_output = str(_DIR_PATH.joinpath("file_reporter" + _FILE_OUTPUT_SUFFIX)) + with open(golden_output) as f: + golden_output_list = f.readlines() + golden_output_list[0] = str(_DIR_PATH.joinpath("reporter" + _FILE_INPUT_SUFFIX)) + golden_output_list[0] + with open(test_output) as f: + test_output_list = f.readlines() + test_output_list[0] = str( + _DIR_PATH.joinpath("reporter" + _FILE_INPUT_SUFFIX)) + ":1:1: E0401 Unable to import abc.\n" + self.assertListEqual(test_output_list, golden_output_list) + os.remove(test_output) + + def test_sarif_reporter(self) -> None: + reporter = self.build_reporter("sarif") + reporter.display() + test_output = str(_DIR_PATH.joinpath("sarif_reporter" + _FILE_TEST_OUTPUT_SUFFIX)) + golden_output = str(_DIR_PATH.joinpath("sarif_reporter" + _FILE_OUTPUT_SUFFIX)) + with open(golden_output) as f: + golden_output_list = f.readlines() + golden_output_list[10] = " \"uri\": \"" + str( + _DIR_PATH.joinpath("reporter" + _FILE_INPUT_SUFFIX)) + "\"" + with open(test_output) as f: + test_output_list = f.readlines() + test_output_list[10] = " \"uri\": \"" + str( + _DIR_PATH.joinpath("reporter" + _FILE_INPUT_SUFFIX)) + "\"" + self.assertListEqual(test_output_list, golden_output_list) + os.remove(test_output) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/complex.k b/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/complex.k new file mode 100644 index 000000000..c720d1c8b --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/complex.k @@ -0,0 +1,19 @@ +@info(version="v1") +schema User: + @info(message="name") + name: str = "default_name" + age: int = 1 + homeTown?: HomeTown + +schema HomeTown: + province: str + city: Custom + +schema Custom: + left: int + right: int + +schema Color: + color: "Yellow" | "Blue" | "Green" + listAttr: [int] + dictAttr: {str:} diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/complex.k.json b/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/complex.k.json new file mode 100644 index 000000000..5463416fa --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/complex.k.json @@ -0,0 +1,166 @@ +[ + { + "type": "schema", + "schemaName": "User", + "properties": { + "name": { + "type": "str", + "default": "default_name", + "line": 1, + "decorators": [ + { + "name": "info", + "keywords": { + "message": "name" + } + } + ] + }, + "homeTown": { + "type": "schema", + "schemaName": "HomeTown", + "properties": { + "province": { + "type": "str", + "line": 1 + }, + "city": { + "type": "schema", + "schemaName": "Custom", + "properties": { + "left": { + "type": "int", + "line": 1 + }, + "right": { + "type": "int", + "line": 2 + } + }, + "required": [ + "left", + "right" + ], + "line": 2 + } + }, + "required": [ + "city", + "province" + ], + "line": 3 + }, + "age": { + "type": "int", + "default": "1", + "line": 2 + } + }, + "required": [ + "age", + "name" + ], + "decorators": [ + { + "name": "info", + "keywords": { + "version": "v1" + } + } + ] + }, + { + "type": "schema", + "schemaName": "HomeTown", + "properties": { + "province": { + "type": "str", + "line": 1 + }, + "city": { + "type": "schema", + "schemaName": "Custom", + "properties": { + "right": { + "type": "int", + "line": 2 + }, + "left": { + "type": "int", + "line": 1 + } + }, + "required": [ + "left", + "right" + ], + "line": 2 + } + }, + "required": [ + "city", + "province" + ] + }, + { + "type": "schema", + "schemaName": "Custom", + "properties": { + "left": { + "type": "int", + "line": 1 + }, + "right": { + "type": "int", + "line": 2 + } + }, + "required": [ + "left", + "right" + ] + }, + { + "type": "schema", + "schemaName": "Color", + "properties": { + "dictAttr": { + "type": "dict", + "key": { + "type": "str" + }, + "item": { + "type": "any" + }, + "line": 3 + }, + "listAttr": { + "type": "list", + "item": { + "type": "int" + }, + "line": 2 + }, + "color": { + "type": "union", + "unionTypes": [ + { + "type": "str(Yellow)" + }, + { + "type": "str(Blue)" + }, + { + "type": "str(Green)" + } + ], + "line": 1 + } + }, + "required": [ + "color", + "dictAttr", + "listAttr" + ] + } +] diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/empty.k b/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/empty.k new file mode 100644 index 000000000..1337a530c --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/empty.k @@ -0,0 +1 @@ +a = 1 diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/empty.k.json b/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/empty.k.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/empty.k.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/simple.k b/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/simple.k new file mode 100644 index 000000000..e90c53e93 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/simple.k @@ -0,0 +1,6 @@ +import units + +schema Data: + id?: int + name: str + cpu: units.NumberMultiplier diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/simple.k.json b/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/simple.k.json new file mode 100644 index 000000000..50fb43ddb --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/schema_testdata/simple.k.json @@ -0,0 +1,24 @@ +[ + { + "type": "schema", + "schemaName": "Data", + "properties": { + "name": { + "type": "str", + "line": 2 + }, + "id": { + "type": "int", + "line": 1 + }, + "cpu": { + "line": 3, + "type": "number_multiplier" + } + }, + "required": [ + "cpu", + "name" + ] + } +] \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/import_file.k b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/import_file.k new file mode 100644 index 000000000..6a5ad3a1f --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/import_file.k @@ -0,0 +1,4 @@ +import importfile +schema Person(Persona): + name: str + age: int diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/importfile/_import_file.k b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/importfile/_import_file.k new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/importfile/nested_import_file.k b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/importfile/nested_import_file.k new file mode 100644 index 000000000..f887109a4 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/importfile/nested_import_file.k @@ -0,0 +1,3 @@ +schema Persona: + name: str + age: int \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/importfile/testfile_test.k b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/importfile/testfile_test.k new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/kcl.mod b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/list_attr.full_schema b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/list_attr.full_schema new file mode 100644 index 000000000..569fe8c34 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/list_attr.full_schema @@ -0,0 +1,3 @@ +FullName, attr:[firstName, lastName], +Person1, attr:[name, card, _mybkLabels, commands], parent:Person, attr:[name, age], parent:Persona, attr:[name, age], +Person2, attr:[card], parent:Person1, attr:[name, card, _mybkLabels, commands], parent:Person, attr:[name, age], parent:Persona, attr:[name, age], \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/list_attr.golden b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/list_attr.golden new file mode 100644 index 000000000..d0dda0509 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/list_attr.golden @@ -0,0 +1,44 @@ +------------ schema list ------------ +Here are schemas defined in list_attr.k: +- FullName +- Person1 +- Person2 +Here are schemas imported to list_attr.k: +imported from import_file.k +- Person +imported from nested_import_file.k +- Persona +------------ schema structures ------------ +schema FullName: +name type default is_final is_optional +firstName str Required +lastName str Required + +schema Person1: +name type default is_final is_optional +name FullName FullName{...} Required +card str "abc" Required +_mybkLabels {str:str} ... +commands [str] ... Required +attrs inherited from Person +name str Required +age int Required +attrs inherited from Persona +name str Required +age int Required + +schema Person2: +name type default is_final is_optional +card str Required +attrs inherited from Person1 +name FullName FullName{...} Required +card str "abc" Required +_mybkLabels {str:str} ... +commands [str] ... Required +attrs inherited from Person +name str Required +age int Required +attrs inherited from Persona +name str Required +age int Required + diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/list_attr.k b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/list_attr.k new file mode 100644 index 000000000..d93954389 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/test_data/list_attr.k @@ -0,0 +1,25 @@ +import import_file +# test for builtin +import math +# test for plugin +import kcl_plugin.app_context as ctx + +schema FullName: + firstName: str + lastName: str + +schema Person1(Person): + name: FullName = FullName{ + firstName = "hello" + lastName = "world" + } + card: str = "abc" + _mybkLabels?: {str:str} = { + if _workspace: "app.stack.io/workspace": _workspace.lower() + "app.kubernetes.io/name": _appName + "app.kubernetes.io/instance": _appName + } if _globalTenantName == "CLOUDCORE" else Undefined + commands: [str] = ["a", if True : "b"] + +schema Person2(Person1): + card: str diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/test_listattr.py b/test/test_units/test_kclvm/test_tools/test_list_attr/test_listattr.py new file mode 100644 index 000000000..8b6785c24 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/test_listattr.py @@ -0,0 +1,85 @@ +import os +import sys +import unittest +import pathlib +import subprocess +from typing import Tuple +import kclvm.kcl.ast as ast +from kclvm.compiler.parser import ParseFile +from kclvm.tools.list_attribute.utils import ListAttributePrinter + +_FILE_INPUT_SUFFIX = ".k" +_FILE_OUTPUT_SUFFIX = ".golden" +_FILE_FULLSCHEMA_SUFFIX = ".full_schema" +_PATH_NAME = "test_data" +_DIR_PATH = pathlib.Path(__file__).parent.joinpath(_PATH_NAME) +_TEST_CASE_NAMES = [ + "list_attr" +] + + +class KCLListAttrBaseTest(unittest.TestCase): + def setUp(self): + self.maxDiff = None + + def read_data(self, data_name: str): + """Read test data""" + input_filename = data_name + _FILE_INPUT_SUFFIX + output_filename = data_name + _FILE_OUTPUT_SUFFIX + full_schema_filename = data_name + _FILE_FULLSCHEMA_SUFFIX + input_file = _DIR_PATH / input_filename + data_input = (_DIR_PATH / input_filename).read_text() + data_output = (_DIR_PATH / output_filename).read_text() + data_full_schema = (_DIR_PATH / full_schema_filename).read_text() + return input_file, data_input, data_output, data_full_schema + + def assert_full_schema_equal( + self, + data_name: str + ): + input_file, input_str, output_str, full_schema_str = self.read_data(data_name) + printer = ListAttributePrinter(str(input_file)) + printer.build_full_schema_list() + if not printer.full_schema_list: + return + full_schema_list = printer.full_schema_list + full_schema = "" + for s in full_schema_list: + full_schema += str(s) + "\n" + full_schema = full_schema[:-1] + self.assertEqual(full_schema, full_schema_str) + + def assert_list_attr_equal( + self, + data_name: str + ): + input_file, input_str, output_str, full_schema_str = self.read_data(data_name) + buffer = Redirect() + current = sys.stdout + sys.stdout = buffer + ListAttributePrinter(str(input_file)).print() + sys.stdout = current + self.assertEqual(buffer.content, output_str) + + +# rewrite sys.stdout.write() to get content of print() +class Redirect: + def __init__(self): + self.content = "" + + def write(self, str): + self.content += str + + +class KCLListAttrTest(KCLListAttrBaseTest): + def test_full_schema(self) -> None: + for case in _TEST_CASE_NAMES: + self.assert_full_schema_equal(case) + + def test_list_attr_schema(self) -> None: + for case in _TEST_CASE_NAMES: + self.assert_list_attr_equal(case) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_list_attr/test_schema.py b/test/test_units/test_kclvm/test_tools/test_list_attr/test_schema.py new file mode 100644 index 000000000..a94889c1a --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_list_attr/test_schema.py @@ -0,0 +1,48 @@ +import json +import unittest +import pathlib + +import kclvm.kcl.ast as ast +import kclvm.api.object as objpkg +import kclvm.tools.list_attribute.schema as schema +import kclvm.internal.gpyrpc.gpyrpc_pb2 as pb2 + + +import google.protobuf.json_format as json_format + + +class KCLListAttrSchemaTest(unittest.TestCase): + def test_get_schema_type_from_code(self): + self.maxDiff = None + testdata_path = pathlib.Path(__file__).parent.joinpath("schema_testdata") + k_files = list(sorted(testdata_path.glob("*.k"))) + json_files = list(sorted(testdata_path.glob("*.k.json"))) + for k_file, json_file in zip(k_files, json_files): + k_code = k_file.read_text() + type_list = schema.get_schema_type_from_code("", code=k_code) + type_list = [json_format.MessageToDict(t) for t in type_list] + json_str = json_file.read_text() + expected_data = json.loads(json_str) + self.assertEqual(type_list, expected_data) + + def test_get_schema_type_from_code_with_schema_name_para(self): + testdata_path = pathlib.Path(__file__).parent.joinpath("schema_testdata").joinpath("complex.k") + k_code = testdata_path.read_text() + schema_names = ["User", "HomeTown", "Custom", "Color"] + for name in schema_names: + type_list = schema.get_schema_type_from_code("", code=k_code, schema_name=name) + self.assertEqual(len(type_list), 1) + err_name = "ErrSchema" + type_list = schema.get_schema_type_from_code("", code=k_code, schema_name=err_name) + self.assertEqual(len(type_list), 0) + + def test_get_schema_type_from_code_invalid(self): + with self.assertRaises(Exception): + schema.get_schema_type_from_code(None, None) + + def test_kcl_type_obj_to_pb_kcl_type(self): + self.assertEqual(schema.kcl_type_obj_to_pb_kcl_type(None), None) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/kcl.mod b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/pkg/internal_pkg/data.k b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/pkg/internal_pkg/data.k new file mode 100644 index 000000000..7a530baa9 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/pkg/internal_pkg/data.k @@ -0,0 +1,2 @@ +schema Data: + id?: int diff --git a/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/pkg/person.k b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/pkg/person.k new file mode 100644 index 000000000..8e2889433 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/pkg/person.k @@ -0,0 +1,6 @@ +import pkg.internal_pkg + +schema Person: + name: str = "kcl" + age: int = 1 + data?: internal_pkg.Data diff --git a/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/test.k b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/test.k new file mode 100644 index 000000000..57613032a --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/test.k @@ -0,0 +1,20 @@ +schema Data: + id?: int = 0 + value?: str = "value" + +schema Config: + image: str + data?: Data + +if True: + configOther = Config { + image = "image/other:v1" + } + +config = Config { + image = "image/image:v1" + data = { + id = 1 + value = "override_value" + } +} diff --git a/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/test_auto_fix.k b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/test_auto_fix.k new file mode 100644 index 000000000..9459f9ac8 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/test_auto_fix.k @@ -0,0 +1,20 @@ +schema Data: + id?: int = 0 + value?: str = "value" + +schema Config: + image: str + data?: Data + +if True: + configOther = Config { + image = "image/other:v1" + } + +config = Config { + image = "image/image:v1" + data = { + id = 1 + value = str(1) + } +} diff --git a/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/test_auto_import_schema.k b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/test_auto_import_schema.k new file mode 100644 index 000000000..4da128f62 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/test_auto_import_schema.k @@ -0,0 +1,6 @@ +import pkg.internal_pkg +import pkg + +x0 = pkg.Person { + data = internal_pkg.Data {id = 1} +} diff --git a/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/test_import_paths.k b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/test_import_paths.k new file mode 100644 index 000000000..f2eb06591 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_overrides/file_test_data/test_import_paths.k @@ -0,0 +1,10 @@ +import pkg.pkg +import pkg + +schema Data: + id?: int = 0 + value?: str = "value" + +data = Data { + value = "override_value" +} diff --git a/test/test_units/test_kclvm/test_tools/test_overrides/test_data/config.input b/test/test_units/test_kclvm/test_tools/test_overrides/test_data/config.input new file mode 100644 index 000000000..fcaa8ba77 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_overrides/test_data/config.input @@ -0,0 +1,59 @@ +schema Main: + name?: str + env?: [{str:}] + +schema Probe: + initialDelaySeconds?: int + timeoutSeconds?: int + periodSeconds?: int = 10 + successThreshold?: int + failureThreshold?: int + +schema AppConfiguration: + appName: str + image: str + overQuota: bool = False + resource: {str:} + mainContainer?: Main + labels: {str:} + probe: Probe + +appConfiguration = AppConfiguration { + appName: "kusion" + image: "kusion/kusion:v0.3.0" + resource: { + cpu: "4" + disk: "50Gi" + memory: "12Gi" + } + labels: { + key: { + key: "value" + } + } + mainContainer: Main { + name: "kusion" + } + overQuota = True + overQuota = True + probe: Probe {} +} + +appConfigurationUnification: AppConfiguration { + appName: "kusion" + image: "kusion/kusion:v0.3.0" + resource: { + cpu: "4" + disk: "50Gi" + memory: "12Gi" + } + labels: { + key: { + key: "value" + } + } + mainContainer: Main { + name: "kusion" + } + overQuota: True +} diff --git a/test/test_units/test_kclvm/test_tools/test_overrides/test_data/config.output b/test/test_units/test_kclvm/test_tools/test_overrides/test_data/config.output new file mode 100644 index 000000000..562aca887 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_overrides/test_data/config.output @@ -0,0 +1,57 @@ +schema Main: + name?: str + env?: [{str:}] + +schema Probe: + initialDelaySeconds?: int + timeoutSeconds?: int + periodSeconds?: int = 10 + successThreshold?: int + failureThreshold?: int + +schema AppConfiguration: + appName: str + image: str + overQuota: bool = False + resource: {str:} + mainContainer?: Main + labels: {str:} + probe: Probe + +appConfiguration = AppConfiguration { + appName: "kusion" + image: "kusion/kusion:v0.3.1" + resource: { + cpu: "4" + disk: "50Gi" + memory: "12Gi" + } + labels: { + key: { + key: "override_value" + } + } + mainContainer: Main { + name: "kusion_override" + } + overQuota = False + overQuota = False + probe: { + periodSeconds = 20 + } +} + +appConfigurationUnification: AppConfiguration { + appName: "kusion" + image: "kusion/kusion:v0.3.1" + labels: { + key: { + key: "override_value" + } + } + mainContainer: Main { + name: "kusion_override" + } + overQuota: False +} + diff --git a/test/test_units/test_kclvm/test_tools/test_overrides/test_data/empty.input b/test/test_units/test_kclvm/test_tools/test_overrides/test_data/empty.input new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_overrides/test_data/empty.output b/test/test_units/test_kclvm/test_tools/test_overrides/test_data/empty.output new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_overrides/test_overrides.py b/test/test_units/test_kclvm/test_tools/test_overrides/test_overrides.py new file mode 100644 index 000000000..f599a3ad1 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_overrides/test_overrides.py @@ -0,0 +1,161 @@ +#! /usr/bin/env python3 + +import io +import os +import unittest +import pathlib + +from typing import Tuple, List, Union + +import kclvm.kcl.ast as ast +import kclvm.kcl.error as kcl_error +from kclvm.compiler.parser import ParseFile, LoadProgram + + +FILE_INPUT_SUFFIX = ".input" +FILE_OUTPUT_SUFFIX = ".output" +PATH_NAME = "test_data" +TEST_DIR_PATH = pathlib.Path(__file__).parent.joinpath(PATH_NAME) +CMD_OVERRIDES = [ + ast.CmdOverrideSpec( + field_path="appConfiguration.image", + field_value="kusion/kusion:v0.3.1", + ), + ast.CmdOverrideSpec( + field_path="appConfiguration.mainContainer.name", + field_value="kusion_override", + ), + ast.CmdOverrideSpec( + field_path="appConfiguration.labels.key.key", + field_value="override_value", + ), + ast.CmdOverrideSpec( + field_path="appConfiguration.overQuota", + field_value="False", + ), + ast.CmdOverrideSpec( + field_path="appConfiguration.probe", + field_value="{periodSeconds=20}", + ), + ast.CmdOverrideSpec( + field_path="appConfigurationUnification.image", + field_value="kusion/kusion:v0.3.1", + ), + ast.CmdOverrideSpec( + field_path="appConfigurationUnification.mainContainer.name", + field_value="kusion_override", + ), + ast.CmdOverrideSpec( + field_path="appConfigurationUnification.labels.key.key", + field_value="override_value", + ), + ast.CmdOverrideSpec( + field_path="appConfigurationUnification.overQuota", + field_value="False", + ), + ast.CmdOverrideSpec( + field_path="appConfigurationUnification.resource", + action=ast.OverrideAction.DELETE, + ), +] + + +def _read_override_test_cases_from_dir( + dir: Union[pathlib.Path, str] +) -> List[Tuple[str, str, str]]: + if not dir: + return [] + inputs = list(sorted(pathlib.Path(dir).glob("*" + FILE_INPUT_SUFFIX))) + case_inputs = [input.read_text() for input in inputs] + outputs = list(sorted(pathlib.Path(dir).glob("*" + FILE_OUTPUT_SUFFIX))) + case_outputs = [output.read_text() for output in outputs] + filenames = [str(input) for input in inputs] + return zip(filenames, case_inputs, case_outputs) + + +class KCLBaseOverrideTest(unittest.TestCase): + """KCL Override test""" + + def setUp(self): + self.test_cases = _read_override_test_cases_from_dir(dir=TEST_DIR_PATH) + self.maxDiff = None + return super().setUp() + + def assertOverrideEqual(self, filename: str, case_input: str, case_output: str): + from kclvm.tools.query import ApplyOverrides + from kclvm.tools.printer import PrintAST + + if not filename or not case_input or not case_output: + return + prog = LoadProgram(filename) + ApplyOverrides(prog, CMD_OVERRIDES) + with io.StringIO() as out: + PrintAST(prog.pkgs[prog.main][0], out) + out.write("\n") + self.assertEqual(out.getvalue(), case_output) + + def print_overrides_ast(self): + from kclvm.tools.query import PrintOverridesAST, OverrideInfo + + for i in range(len(OverrideInfo.MODIFIED)): + OverrideInfo.MODIFIED[i].filename = OverrideInfo.MODIFIED[i].filename.replace( + FILE_INPUT_SUFFIX, + FILE_OUTPUT_SUFFIX, + ) + PrintOverridesAST() + OverrideInfo.MODIFIED = [] + + +class KCLOverridesTest(KCLBaseOverrideTest): + """KCL Override test""" + + def test_overrides(self): + for filename, case_input, case_output in self.test_cases: + self.assertOverrideEqual(filename, case_input, case_output) + + def test_override_file(self): + from kclvm.tools.query.override import override_file + + file = str(pathlib.Path(__file__).parent.joinpath("file_test_data").joinpath("test.k")) + specs = ["config.image=\"image/image\"", ":config.image=\"image/image:v1\"", ":config.data={id=1,value=\"override_value\"}"] + self.assertEqual(override_file(file, specs), True) + + def test_override_file_auto_fix(self): + from kclvm.tools.query.override import override_file + from kclvm.tools.query.override import KCL_FEATURE_GATE_OVERRIDE_AUTO_FIX_ENV + + os.environ[KCL_FEATURE_GATE_OVERRIDE_AUTO_FIX_ENV] = "1" + file = str(pathlib.Path(__file__).parent.joinpath("file_test_data").joinpath("test_auto_fix.k")) + specs = ["config.image=\"image/image\"", ":config.image=\"image/image:v1\"", ":config.data={id=1,value=1}"] + self.assertEqual(override_file(file, specs), True) + del os.environ[KCL_FEATURE_GATE_OVERRIDE_AUTO_FIX_ENV] + + def test_override_file_auto_schema_import(self): + from kclvm.tools.query.override import override_file + from kclvm.tools.query.override import KCL_FEATURE_GATE_OVERRIDE_AUTO_FIX_ENV + + os.environ[KCL_FEATURE_GATE_OVERRIDE_AUTO_FIX_ENV] = "1" + file = str(pathlib.Path(__file__).parent.joinpath("file_test_data").joinpath("test_auto_import_schema.k")) + specs = ["x0.data=Data{id=1}"] + self.assertEqual(override_file(file, specs), True) + del os.environ[KCL_FEATURE_GATE_OVERRIDE_AUTO_FIX_ENV] + + def test_override_file_with_import_paths(self): + from kclvm.tools.query.override import override_file + + file = str(pathlib.Path(__file__).parent.joinpath("file_test_data").joinpath("test_import_paths.k")) + specs = ["data.value=\"override_value\""] + import_paths = ["pkg", "pkg.pkg"] + self.assertEqual(override_file(file, specs, import_paths), True) + + def test_override_file_invalid(self): + from kclvm.tools.query.override import override_file + + specs = [":a:", "a=1", ":a", "a-1"] + for spec in specs: + with self.assertRaises(kcl_error.KCLException): + override_file("main.k", [spec]) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/arguments.input b/test/test_units/test_kclvm/test_tools/test_printer/test_data/arguments.input new file mode 100644 index 000000000..d356f83fb --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/arguments.input @@ -0,0 +1,6 @@ +schema Config[nameVar, textVar: str, idVar: int = 1]: + name: str = nameVar + text: str = textVar + id?: int = idVar + +config = Config("test", "text") diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/arguments.output b/test/test_units/test_kclvm/test_tools/test_printer/test_data/arguments.output new file mode 100644 index 000000000..d356f83fb --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/arguments.output @@ -0,0 +1,6 @@ +schema Config[nameVar, textVar: str, idVar: int = 1]: + name: str = nameVar + text: str = textVar + id?: int = idVar + +config = Config("test", "text") diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/codelayout.input b/test/test_units/test_kclvm/test_tools/test_printer/test_data/codelayout.input new file mode 100644 index 000000000..5f44a591a --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/codelayout.input @@ -0,0 +1,77 @@ + + + +import math as alias_math +schema Person ( Base): + name:str# inline comment + age:int + check : + age>0 if age , "age must > 0" +person = Person{ + name:"Alice" + age:18 +} +if True: + a = 1 +elif True: + b = 2 +else: + c = 3 +d = 1 + 2 +e = ( 1 + 2 ) +f=[ 1, 2, 3 ] +g = { "key" : "value" } +# block comment +print (1) +dct={"key": "value"} +lst=[1,2,3] +h = dct [ 'key' ] +i = lst [ 1 ] +x = 1 +y = 2 +long_variable = 3 +i = i+1 +submitted+=1 +x = x*2 - 1 +hypot2 = x*x + y*y +_c = (a+b) * (a-b) +_b=2 +_c= 3 +_d =4 + +_value = (1 + 2 * 3) +_value = (1+2*3) +_value =1+ - 2 * ~ 3 +_list = [1, 2, 3] +_list = [*_list, [4, 5 ,6]] +_list = [* _list, [4, 5 ,6]] + +_dict = {** {"k": "v"}, ** {"k": "v"}} +a = [1,2,3] +b = [ + 1,2,3, + 4,5,6, +] +_dict={ + "k1":"v1" + "k2" :"v2" + "k3": "v3" + "k4" : "v4" + "k5" : "v5" +} +foo=1 +if foo is not None: + _a = 1 + _dict|={} + hello = "world{}" . format( 1 )[2 : 4] . lower( ) + range_int = [ i for i in range( 10 ) ] +op = 1+2 - - 3 + (3 - 1) // 3 +op += 1 +op -= 12 + 23 +print( " " , end= '') +log = math. log(12) +aa = 1 +assert aa == 1,"message" +assert aa == 1 if aa,"message" +aaaa = (1 + 2 / 2) if _a == 2 + + 134.3 else ("a"*3) +bbbb = "{}". format(a) \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/codelayout.output b/test/test_units/test_kclvm/test_tools/test_printer/test_data/codelayout.output new file mode 100644 index 000000000..568ecf7a5 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/codelayout.output @@ -0,0 +1,82 @@ +import math as alias_math +schema Person(Base): + # inline comment + name: str + age: int + + check: + age > 0 if age, "age must > 0" + +person = Person { + name: "Alice" + age: 18 +} + +if True: + a = 1 +elif True: + b = 2 +else: + c = 3 + +d = 1 + 2 +e = (1 + 2) +f = [1, 2, 3] +g = {"key": "value"} +# block comment +print(1) +dct = {"key": "value"} +lst = [1, 2, 3] +h = dct['key'] +i = lst[1] +x = 1 +y = 2 +long_variable = 3 +i = i + 1 +submitted += 1 +x = x * 2 - 1 +hypot2 = x * x + y * y +_c = (a + b) * (a - b) +_b = 2 +_c = 3 +_d = 4 +_value = (1 + 2 * 3) +_value = (1 + 2 * 3) +_value = 1 + -2 * ~3 +_list = [1, 2, 3] +_list = [*_list, [4, 5, 6]] +_list = [*_list, [4, 5, 6]] +_dict = {**{"k": "v"}, **{"k": "v"}} +a = [1, 2, 3] +b = [ + 1 + 2 + 3 + 4 + 5 + 6 +] +_dict = { + "k1": "v1" + "k2": "v2" + "k3": "v3" + "k4": "v4" + "k5": "v5" +} +foo = 1 +if foo is not None: + _a = 1 + _dict |= {} + hello = "world{}".format(1)[2:4:].lower() + range_int = [i for i in range(10)] + +op = 1 + 2 - -3 + (3 - 1) // 3 +op += 1 +op -= 12 + 23 +print(" ", end='') +log = math.log(12) +aa = 1 +assert aa == 1, "message" +assert aa == 1 if aa, "message" +aaaa = (1 + 2 / 2) if _a == 2 + +134.3 else ("a" * 3) +bbbb = "{}".format(a) diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/collection_if.input b/test/test_units/test_kclvm/test_tools/test_printer/test_data/collection_if.input new file mode 100644 index 000000000..080538c1c --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/collection_if.input @@ -0,0 +1,51 @@ +schema Config: + name: str + env: str + data: [int] +env = "env" +data1 = Config { + if env == "env": + name: env + env: env + data += [0] + else: + name = "name" + env = "name" + data += [1] +} +data1 = Config { + if env == "env": + name: env + env: env + else: + name: "name" + env: "name" +} +data2 = Config { + if env != "env": + name: env + env: env + else: + name: "name" + env: "name" +} +data3 = { + if True: + key1: "value1" + elif True: + key2: "value2" + elif True: + key3: "value3" + else: + key4: "value4" +} +data4 = [ + if True: + "value1" + elif True: + "value2" + elif True: + "value3" + else: + "value4" +] diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/collection_if.output b/test/test_units/test_kclvm/test_tools/test_printer/test_data/collection_if.output new file mode 100644 index 000000000..009f8947a --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/collection_if.output @@ -0,0 +1,55 @@ +schema Config: + name: str + env: str + data: [int] + +env = "env" +data1 = Config { + if env == "env": + name: env + env: env + data += [0] + else: + name = "name" + env = "name" + data += [1] +} + +data1 = Config { + if env == "env": + name: env + env: env + else: + name: "name" + env: "name" +} + +data2 = Config { + if env != "env": + name: env + env: env + else: + name: "name" + env: "name" +} + +data3 = { + if True: + key1: "value1" + elif True: + key2: "value2" + elif True: + key3: "value3" + else: + key4: "value4" +} +data4 = [ + if True: + "value1" + elif True: + "value2" + elif True: + "value3" + else: + "value4" +] diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/comment.input b/test/test_units/test_kclvm/test_tools/test_printer/test_data/comment.input new file mode 100644 index 000000000..681a69c31 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/comment.input @@ -0,0 +1,38 @@ +# Comment One +schema Main: + name?: str + env?: [{str:}] + +# Comment Two +schema AppConfiguration: + appName: str + image: str + overQuota: bool = False + resource: {str:} + mainContainer?: Main + labels: {str:} + +# Comment Three +appConfiguration = AppConfiguration { + # Comment Four + appName: "kusion" + image: "test-image:v1" # Comment Five + resource: { + cpu: "4" + disk: "50Gi" + memory: "12Gi" + } + labels: { + key: { + key: 12 + } + } + # Comment Six + mainContainer: Main { + name: "kusion_override" + }# Comment Seven + + # Comment Eight + overQuota: True +} +# Comment Nine diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/comment.output b/test/test_units/test_kclvm/test_tools/test_printer/test_data/comment.output new file mode 100644 index 000000000..4689cf1af --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/comment.output @@ -0,0 +1,40 @@ +# Comment One +schema Main: + name?: str + env?: [{str:}] + +# Comment Two +schema AppConfiguration: + appName: str + image: str + overQuota: bool = False + resource: {str:} + mainContainer?: Main + labels: {str:} + +# Comment Three +appConfiguration = AppConfiguration { + # Comment Four + appName: "kusion" + # Comment Five + image: "test-image:v1" + resource: { + cpu: "4" + disk: "50Gi" + memory: "12Gi" + } + labels: { + key: { + key: 12 + } + } + # Comment Six + mainContainer: Main { + name: "kusion_override" + } + # Comment Seven + # Comment Eight + overQuota: True +} + +# Comment Nine diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/empty.input b/test/test_units/test_kclvm/test_tools/test_printer/test_data/empty.input new file mode 100644 index 000000000..fd40910d9 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/empty.input @@ -0,0 +1,4 @@ + + + + diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/empty.output b/test/test_units/test_kclvm/test_tools/test_printer/test_data/empty.output new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/index_sign.input b/test/test_units/test_kclvm/test_tools/test_printer/test_data/index_sign.input new file mode 100644 index 000000000..54d611873 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/index_sign.input @@ -0,0 +1,12 @@ +schema Data: + [str]: any + +schema DataOther: + [...str]: str + id?: int + +schema DataMap: + [name: str]: str + check: + name in ["A", "B", "C"] + \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/index_sign.output b/test/test_units/test_kclvm/test_tools/test_printer/test_data/index_sign.output new file mode 100644 index 000000000..7da6687ac --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/index_sign.output @@ -0,0 +1,13 @@ +schema Data: + [str]: any + +schema DataOther: + [...str]: str + id?: int + +schema DataMap: + [name: str]: str + + check: + name in ["A", "B", "C"] + diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/joined_str.input b/test/test_units/test_kclvm/test_tools/test_printer/test_data/joined_str.input new file mode 100644 index 000000000..8af336d4a --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/joined_str.input @@ -0,0 +1,3 @@ +a = 1 +b = "${a}" +c = "a.${1}" diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/joined_str.output b/test/test_units/test_kclvm/test_tools/test_printer/test_data/joined_str.output new file mode 100644 index 000000000..8af336d4a --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/joined_str.output @@ -0,0 +1,3 @@ +a = 1 +b = "${a}" +c = "a.${1}" diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/lambda.input b/test/test_units/test_kclvm/test_tools/test_printer/test_data/lambda.input new file mode 100644 index 000000000..32142fc2e --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/lambda.input @@ -0,0 +1,21 @@ +sumFunc1 = lambda x, y { + z = x + y + z + x + +} +sumFunc2 = lambda x, y = 1 { + x + y + +} +sumFunc3 = lambda x = 1, y = 1 { + x + y + +} +sumFunc4 = lambda x: int = 1, y: int = 1 -> int { + x + y + +} +x0 = sumFunc1(1, 2) +x1 = sumFunc1(2, 3) +x2 = sumFunc1(3, 4) +x3 = sumFunc1(4, 5) diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/lambda.output b/test/test_units/test_kclvm/test_tools/test_printer/test_data/lambda.output new file mode 100644 index 000000000..169ae8a1c --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/lambda.output @@ -0,0 +1,21 @@ +sumFunc1 = lambda x, y { + z = x + y + z + x + +} +sumFunc2 = lambda x, y = 1 { + x + y + +} +sumFunc3 = lambda x = 1, y = 1 { + x + y + +} +sumFunc4 = lambda x: int = 1, y: int = 1 -> int { + x + y + +} +x0 = sumFunc1(1, 2) +x1 = sumFunc1(2, 3) +x2 = sumFunc1(3, 4) +x3 = sumFunc1(4, 5) diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/quant.input b/test/test_units/test_kclvm/test_tools/test_printer/test_data/quant.input new file mode 100644 index 000000000..23022c694 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/quant.input @@ -0,0 +1,8 @@ +a = all x in [-1, 0, 1, 2, 3] { + x >= 1 if x > 0 +} +b = any x in {k1 = "v1", k2 = "v2"} {x in ["k1", "v2"]} +c = map x in {k1 = "v1", k2 = "v2"} {x} +d = filter x in [1, 2, 3] { + x > 1 +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/quant.output b/test/test_units/test_kclvm/test_tools/test_printer/test_data/quant.output new file mode 100644 index 000000000..a6a65a3c2 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/quant.output @@ -0,0 +1,8 @@ +a = all x in [-1, 0, 1, 2, 3] { + x >= 1 if x > 0 +} +b = any x in {k1 = "v1", k2 = "v2"} {x in ["k1", "v2"]} +c = map x in {k1 = "v1", k2 = "v2"} {x} +d = filter x in [1, 2, 3] { + x > 1 +} diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/rule.input b/test/test_units/test_kclvm/test_tools/test_printer/test_data/rule.input new file mode 100644 index 000000000..662e8eec3 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/rule.input @@ -0,0 +1,19 @@ +age = 1 + +schema MainProtocol: + """Protocol doc""" + var: int + +schema MainMixin for MainProtocol: + var: int + +@deprecated +rule Base: + """Rule doc""" + age > 0 + age < 10 + +rule Main[var](Base) for MainProtocol: + var + +Main(1) diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/rule.output b/test/test_units/test_kclvm/test_tools/test_printer/test_data/rule.output new file mode 100644 index 000000000..f45bfac37 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/rule.output @@ -0,0 +1,16 @@ +age = 1 +protocol MainProtocol: + """Protocol doc""" + var: int + +mixin MainMixin for MainProtocol: + var: int + +@deprecated +rule Base: + """Rule doc""" + age > 0 + age < 10 +rule Main[var](Base) for MainProtocol: + var +Main(1) diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/type_alias.input b/test/test_units/test_kclvm/test_tools/test_printer/test_data/type_alias.input new file mode 100644 index 000000000..9924443d4 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/type_alias.input @@ -0,0 +1,18 @@ +type Color = "Red" | "Yellow" | "Blue" + +colorRed: Color = "Red" +colorYellow: Color = "Yellow" +colorBlue: Color = "Blue" + +schema Data: + color: Color + +dataColorRed = Data { + color = "Red" +} +dataColorYellow = Data { + color = "Yellow" +} +dataColorBlue = Data { + color = "Blue" +} diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/type_alias.output b/test/test_units/test_kclvm/test_tools/test_printer/test_data/type_alias.output new file mode 100644 index 000000000..ecb38accb --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/type_alias.output @@ -0,0 +1,19 @@ +type Color = "Red"|"Yellow"|"Blue" +colorRed: Color = "Red" +colorYellow: Color = "Yellow" +colorBlue: Color = "Blue" +schema Data: + color: Color + +dataColorRed = Data { + color = "Red" +} + +dataColorYellow = Data { + color = "Yellow" +} + +dataColorBlue = Data { + color = "Blue" +} + diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/unification.input b/test/test_units/test_kclvm/test_tools/test_printer/test_data/unification.input new file mode 100644 index 000000000..22d05d0bb --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/unification.input @@ -0,0 +1,5 @@ +schema Config: + name: str +config: Config { + name = "name" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_data/unification.output b/test/test_units/test_kclvm/test_tools/test_printer/test_data/unification.output new file mode 100644 index 000000000..a8351ff42 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_data/unification.output @@ -0,0 +1,6 @@ +schema Config: + name: str + +config: Config { + name = "name" +} diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_printer.py b/test/test_units/test_kclvm/test_tools/test_printer/test_printer.py new file mode 100644 index 000000000..e8b720195 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_printer.py @@ -0,0 +1,71 @@ +#! /usr/bin/env python3 + +import os +import io +import unittest +import pathlib +from typing import Tuple + +from kclvm.compiler.parser import ParseFile, ParseMode + + +_FILE_INPUT_SUFFIX = ".input" +_FILE_OUTPUT_SUFFIX = ".output" +_PATH_NAME = "test_data" +_DIR_PATH = pathlib.Path(__file__).parent.joinpath(_PATH_NAME) +_TEST_CASES = [ + "arguments", + "empty", + "codelayout", + "collection_if", + "comment", + "index_sign", + "joined_str", + "lambda", + "quant", + "rule", + "type_alias", + "unification", +] + + +class KCLBasePrinterTest(unittest.TestCase): + """ + KCL base Printer test + """ + + def read_data(self, data_name: str) -> Tuple[str, str]: + """ + Read printer data according to data name + """ + input_filename = data_name + _FILE_INPUT_SUFFIX + output_filename = data_name + _FILE_OUTPUT_SUFFIX + data_input = (_DIR_PATH / input_filename).read_text() + data_output = (_DIR_PATH / output_filename).read_text() + return data_input, data_output + + def assert_printer_equal(self, data_name: str) -> None: + """ + Read printer test data according to data name and assert equal. + """ + from kclvm.tools.printer import PrintAST + + data_input, data_output = self.read_data(data_name) + module = ParseFile("", data_input, mode=ParseMode.ParseComments) + with io.StringIO() as str_io: + PrintAST(module, str_io) + self.assertEqual(str_io.getvalue(), data_output) + + +class KCLPrinterTest(KCLBasePrinterTest): + def test_printer_data(self) -> None: + """ + Test printer data for the comparison of input and golden files + """ + self.maxDiff = None + for case in _TEST_CASES: + self.assert_printer_equal(case) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_printer/test_splice.py b/test/test_units/test_kclvm/test_tools/test_printer/test_splice.py new file mode 100644 index 000000000..f552b4f91 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_printer/test_splice.py @@ -0,0 +1,194 @@ +#! /usr/bin/env python3 + +import os +import io +import unittest +import pathlib +from typing import Tuple + +from kclvm.tools.printer import SchemaRuleCodeSnippet, splice_schema_with_rule +from kclvm.tools.printer.splice import ( + build_rule_check_block_str, + add_indent_to_code_string, +) + + +class TestSplice(unittest.TestCase): + def test_splice_schema_with_rule(self): + cases = [ + { + "snippet_list": [ + SchemaRuleCodeSnippet( + schema="""\ +schema Person: + name: str +""", + rule="""\ +"a" in name +""", + ) + ], + "expected": """\ +schema Person: + name: str + + check: + "a" in name +""", + }, + { + "snippet_list": [ + SchemaRuleCodeSnippet( + schema="""\ +# Schema Person definition +schema Person: + name: str +""", + rule="""\ +# Schema Person Rule definition +"a" in name +""", + ) + ], + "expected": """\ +# Schema Person definition +schema Person: + name: str + + check: + # Schema Person Rule definition + "a" in name +""", + }, + { + "snippet_list": [ + SchemaRuleCodeSnippet( + schema="""\ +schema Person: + name: str +""", + rule="""""", + ) + ], + "expected": """\ +schema Person: + name: str +""", + }, + { + "snippet_list": [ + SchemaRuleCodeSnippet( + schema="""""", + rule="""\ +"a" in name +""", + ) + ], + "expected": """ +""", + }, + { + "snippet_list": [ + SchemaRuleCodeSnippet( + schema="""\ +schema Person: + name: str + data: Data +""", + rule="""\ +"a" in name +""", + ), + SchemaRuleCodeSnippet( + schema="""\ +schema Data: + id: int +""", + rule="""\ +id > 0 +""", + ), + ], + "expected": """\ +schema Person: + name: str + data: Data + + check: + "a" in name + +schema Data: + id: int + + check: + id > 0 +""", + }, + ] + for case in cases: + snippet_list, expected = case["snippet_list"], case["expected"] + self.assertEqual( + splice_schema_with_rule(snippet_list), expected, msg=f"{snippet_list}" + ) + + def test_splice_schema_with_rule_value_error(self): + cases = [ + {"snippet_list": None}, + {"snippet_list": 1}, + {"snippet_list": [1]}, + ] + for case in cases: + snippet_list = case["snippet_list"] + with self.assertRaises(ValueError): + splice_schema_with_rule(snippet_list) + + def test_build_rule_check_block_str(self): + cases = [ + {"schema": "Mock", "code": "", "expected": ""}, + {"schema": "", "code": "a > 1", "expected": ""}, + { + "schema": "Mock", + "code": "a > 1", + "expected": """\ +schema Mock: + check: + a > 1\ +""", + }, + { + "schema": "Mock", + "code": "a > 1\nb < 1", + "expected": """\ +schema Mock: + check: + a > 1 + b < 1\ +""", + }, + ] + for case in cases: + schema, code, expected = case["schema"], case["code"], case["expected"] + self.assertEqual( + build_rule_check_block_str(schema, code), + expected, + msg=f"schema: {schema}, code: {code}", + ) + + def test_add_indent_to_code_string(self): + cases = [ + {"code": None, "indent": 2, "expected": ""}, + {"code": "", "indent": 2, "expected": ""}, + {"code": "a = 1", "indent": 2, "expected": " a = 1"}, + {"code": "a = 1", "indent": 4, "expected": " a = 1"}, + {"code": "a = 1", "indent": 8, "expected": " a = 1"}, + {"code": "a = 1\nb = 1", "indent": 4, "expected": " a = 1\n b = 1"}, + ] + for case in cases: + code, indent, expected = case["code"], case["indent"], case["expected"] + self.assertEqual( + add_indent_to_code_string(code, indent), expected, msg=f"{code}" + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/schema.k b/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/schema.k new file mode 100644 index 000000000..7a78b3aac --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/schema.k @@ -0,0 +1,4 @@ +schema User: + name: str + age: int + message?: str diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/schema.k.json b/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/schema.k.json new file mode 100644 index 000000000..3afbbf902 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/schema.k.json @@ -0,0 +1,5 @@ +{ + "name": "Alice", + "age": "err_age", + "message": "This is Alice" +} diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/schema_with_check.k b/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/schema_with_check.k new file mode 100644 index 000000000..523f04288 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/schema_with_check.k @@ -0,0 +1,7 @@ +schema User: + name: str + age: int + message?: str + + check: + age > 10 diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/schema_with_check.k.json b/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/schema_with_check.k.json new file mode 100644 index 000000000..d01557075 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/schema_with_check.k.json @@ -0,0 +1,5 @@ +{ + "name": "Alice", + "age": "8", + "message": "This is Alice" +} diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/simple.k b/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/simple.k new file mode 100644 index 000000000..849c8e9b0 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/simple.k @@ -0,0 +1 @@ +assert typeof(value) == "int" diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/simple.k.json b/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/simple.k.json new file mode 100644 index 000000000..9459d4ba2 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_invalid_test_data/simple.k.json @@ -0,0 +1 @@ +1.1 diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/complex.k b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/complex.k new file mode 100644 index 000000000..09c880103 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/complex.k @@ -0,0 +1,11 @@ +schema User: + name: str + age: int + message?: str + data: Data + labels: {str:} + hc: [int] + +schema Data: + id: int + value: str diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/complex.k.json b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/complex.k.json new file mode 100644 index 000000000..d3e9b575d --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/complex.k.json @@ -0,0 +1,13 @@ +{ + "name": "Alice", + "age": 18, + "message": "This is Alice", + "data": { + "id": 1, + "value": "value1" + }, + "labels": { + "key": "value" + }, + "hc": [1, 2, 3] +} diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/list.k b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/list.k new file mode 100644 index 000000000..ef6d67aed --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/list.k @@ -0,0 +1,9 @@ +schema User: + name: str + age: int + message?: str + +assert typeof(value) == "list" +assert all v in value { + typeof(v) == "User" +} diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/list.k.json b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/list.k.json new file mode 100644 index 000000000..b6f772ebe --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/list.k.json @@ -0,0 +1,7 @@ +[ + { + "name": "Alice", + "age": 18, + "message": "This is Alice" + } +] diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/plain_value.k b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/plain_value.k new file mode 100644 index 000000000..820fab7ce --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/plain_value.k @@ -0,0 +1,2 @@ +assert typeof(value) == "int" +assert value >= 1 diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/plain_value.k.json b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/plain_value.k.json new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/plain_value.k.json @@ -0,0 +1 @@ +1 diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/schema_with_check.k b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/schema_with_check.k new file mode 100644 index 000000000..4c5653da4 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/schema_with_check.k @@ -0,0 +1,8 @@ +schema User: + name: str + age: int + message?: str + + check: + name == "Alice" + age > 10 diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/schema_with_check.k.json b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/schema_with_check.k.json new file mode 100644 index 000000000..02fd725d1 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/schema_with_check.k.json @@ -0,0 +1,5 @@ +{ + "name": "Alice", + "age": 18, + "message": "This is Alice" +} diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/simple.k b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/simple.k new file mode 100644 index 000000000..7a78b3aac --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/simple.k @@ -0,0 +1,4 @@ +schema User: + name: str + age: int + message?: str diff --git a/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/simple.k.json b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/simple.k.json new file mode 100644 index 000000000..02fd725d1 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/json_test_data/simple.k.json @@ -0,0 +1,5 @@ +{ + "name": "Alice", + "age": 18, + "message": "This is Alice" +} diff --git a/test/test_units/test_kclvm/test_tools/test_validation/test_validation.py b/test/test_units/test_kclvm/test_tools/test_validation/test_validation.py new file mode 100644 index 000000000..0e3b0ffd2 --- /dev/null +++ b/test/test_units/test_kclvm/test_tools/test_validation/test_validation.py @@ -0,0 +1,94 @@ +#! /usr/bin/env python3 + +import json +import unittest +import pathlib + +from kclvm.kcl.error import KCLException +from kclvm.tools.validation import validate_code, validate_code_with_attr_data + + +class KCLValidationTest(unittest.TestCase): + def test_validate_code_normal_json_data(self): + case_path = pathlib.Path(__file__).parent.joinpath("json_test_data") + json_data_list = sorted(case_path.glob("*.k.json")) + code_string_list = sorted(case_path.glob("*.k")) + for json_data_file, code_string_file in zip(json_data_list, code_string_list): + json_data = pathlib.Path(json_data_file).read_text() + code_string = pathlib.Path(code_string_file).read_text() + self.assertTrue(validate_code(json_data, code_string)) + + def test_validate_code_invalid_json_data(self): + case_path = pathlib.Path(__file__).parent.joinpath("json_invalid_test_data") + json_data_list = sorted(case_path.glob("*.k.json")) + code_string_list = sorted(case_path.glob("*.k")) + for json_data_file, code_string_file in zip(json_data_list, code_string_list): + json_data = pathlib.Path(json_data_file).read_text() + code_string = pathlib.Path(code_string_file).read_text() + with self.assertRaises(KCLException): + validate_code(json_data, code_string) + + def test_validate_code_invalid_argument(self): + invalid_argument_cases = [ + {"data": None, "code": None, "format": "json", "attribute_name": "value"}, + {"data": "1", "code": None, "format": "json", "attribute_name": "value"}, + { + "data": None, + "code": "a = 1", + "format": "json", + "attribute_name": "value", + }, + { + "data": "1", + "code": "assert value >= 1", + "format": None, + "attribute_name": "value", + }, + { + "data": "1", + "code": "assert value >= 1", + "format": "err_format", + "attribute_name": "value", + }, + { + "data": "1", + "code": "assert value >= 1", + "format": "json", + "attribute_name": None, + }, + ] + for case in invalid_argument_cases: + with self.assertRaises(ValueError, msg=f"{case}"): + data, code, format, attribute_name = ( + case["data"], + case["code"], + case["format"], + case["attribute_name"], + ) + validate_code(data, code, format=format, attribute_name=attribute_name) + + def test_validate_code_with_attr_data_normal_json_data(self): + case_path = pathlib.Path(__file__).parent.joinpath("json_test_data") + json_data_list = sorted(case_path.glob("*.k.json")) + code_string_list = sorted(case_path.glob("*.k")) + attr_name = "value" + for json_data_file, code_string_file in zip(json_data_list, code_string_list): + json_data = pathlib.Path(json_data_file).read_text() + json_data = json.dumps({attr_name: json.loads(json_data)}) + code_string = pathlib.Path(code_string_file).read_text() + self.assertTrue(validate_code_with_attr_data(json_data, code_string)) + + def test_validate_code_with_attr_data_invalid_json_data(self): + case_path = pathlib.Path(__file__).parent.joinpath("json_test_data") + json_data_list = sorted(case_path.glob("*.k.json")) + code_string_list = sorted(case_path.glob("*.k")) + attr_name = "value" + for json_data_file, code_string_file in zip(json_data_list, code_string_list): + json_data = pathlib.Path(json_data_file).read_text() + json_data = json.dumps({attr_name: json.loads(json_data), "err_key": {}}) + with self.assertRaises(ValueError): + validate_code_with_attr_data(json_data, "") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/call.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/call.k new file mode 100644 index 000000000..d713716d5 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/call.k @@ -0,0 +1,3 @@ +a = 1 +b = a() +c = "".err_lower() diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/config.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/config.k new file mode 100644 index 000000000..f21dae226 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/config.k @@ -0,0 +1,4 @@ +a = 1 +b = a { + key: "value" +} diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/index_sign.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/index_sign.k new file mode 100644 index 000000000..e86cbfdd8 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/index_sign.k @@ -0,0 +1,7 @@ +schema Data: + [str]: int + name: str + +data = Data { + name: "name" +} diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/iter.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/iter.k new file mode 100644 index 000000000..8fb7e9d89 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/iter.k @@ -0,0 +1,12 @@ +# `int` type object not iterable error +a = 1 +b = [i for i in a] +c = all i in a { + i > 0 +} +# `any` type object return any +d = None +e = [i for i in d] +f = all i in d { + i > 0 +} diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/kcl.mod b/test/test_units/test_kclvm/test_types/err_collect_test_data/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/load_attr.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/load_attr.k new file mode 100644 index 000000000..de02e69b9 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/load_attr.k @@ -0,0 +1,2 @@ +a = 1 +b = a.b diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/membership_as.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/membership_as.k new file mode 100644 index 000000000..bfba97320 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/membership_as.k @@ -0,0 +1,2 @@ +a: str = "s" +b = "1" as a[0] diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/module_not_found.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/module_not_found.k new file mode 100644 index 000000000..dc9183c26 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/module_not_found.k @@ -0,0 +1,3 @@ +import pkg_test + +variable = pkg_test.variable diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/pkg/pkg.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/pkg/pkg.k new file mode 100644 index 000000000..6679bc790 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/pkg/pkg.k @@ -0,0 +1,4 @@ +schema SomeProtocol: + data: int + +variable = 1 diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/pkg_test.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/pkg_test.k new file mode 100644 index 000000000..13dec83eb --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/pkg_test.k @@ -0,0 +1,5 @@ +import pkg + +a = pkg.a +b = err_pkg.b +pkg = 1 diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/pkg_test/pkg1.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/pkg_test/pkg1.k new file mode 100644 index 000000000..3c572b1f6 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/pkg_test/pkg1.k @@ -0,0 +1,2 @@ +import pkg + diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/pkg_test/pkg2.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/pkg_test/pkg2.k new file mode 100644 index 000000000..f3d89bcf1 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/pkg_test/pkg2.k @@ -0,0 +1 @@ +variable = pkg.variable diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/protocol.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/protocol.k new file mode 100644 index 000000000..250aa4b55 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/protocol.k @@ -0,0 +1,18 @@ +import pkg + +a = 1 + +schema DataProtocol: + data: str + +schema DataMixin for pkg.pkg.DataProtocol: + x: str = data + +schema AMixin for a: + x: str = data + +schema Data(pkg.pkg.Base) for DataProtocol: + x: str = data + +schema A(a) for a: + x: str = data diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/relaxed.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/relaxed.k new file mode 100644 index 000000000..09cdc6abc --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/relaxed.k @@ -0,0 +1,7 @@ +schema Data: + name: str + +data: Data = Data { + name: "name" + id: 1 +} diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/rule.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/rule.k new file mode 100644 index 000000000..b586f63f1 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/rule.k @@ -0,0 +1,16 @@ +import pkg + +rule SomeRule1 for pkg.SomeProtocol: + some > 0 + +# Invalid protocol type error +rule SomeRule2 for pkg.pkg.SomeProtocol: + some > 0 + +# Package attribute not found type error +rule SomeRule3 for pkg.NotFoundProtocol: + some > 0 + +# Invalid rule protocol object type +rule SomeRule4 for pkg.variable: + some > 0 diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/schema.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/schema.k new file mode 100644 index 000000000..4c76487c5 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/schema.k @@ -0,0 +1,36 @@ +import pkg + +schema DataProtocol: + data: str + +schema DataMixin: + name: str = "data" + +# Invalid schmea inherit from protocol type error +schema Data for DataProtocol: + mixin [DataMixin] + # Type check errors + a: int = "s" + b: str = 1 + c: bool = "s" + d: [int] = {} + e: {:} = [] + f: "Yellow" | "Green" = "Red" + g: int = 1 if True else "s" + i: int | str = 1.0 + + +# Invalid protocol type error +schema SomeMixin1 for pkg.pkg.SomeProtocol: + some: int + +# Pacakge attribute not found type error +schema SomeMixin2 for pkg.NotFoundProtocol: + some: int + +# Unique key error +schema _Data: + name: str + +schema _Data: + id: int diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/select.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/select.k new file mode 100644 index 000000000..d8537f13a --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/select.k @@ -0,0 +1,2 @@ +a = 1 +b = a.key diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/simple.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/simple.k new file mode 100644 index 000000000..9119606f4 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/simple.k @@ -0,0 +1,8 @@ +a: int = "s" +b: str = 1 +c: bool = "s" +d: [int] = {} +e: {:} = [] +f: "Yellow" | "Green" = "Red" +g: int = 1 if True else "s" +i: int | str = 1.0 diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/str.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/str.k new file mode 100644 index 000000000..405ff16bd --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/str.k @@ -0,0 +1,2 @@ +data = "s" +dataUpper = data.upper() diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/subscript.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/subscript.k new file mode 100644 index 000000000..a5b4ca790 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/subscript.k @@ -0,0 +1,10 @@ +a = 1 +b = a[0] +c = 1[0] +d = {} +e = None +f = e[0] + +data = {key: "value"} +dataKey = data[::-1] +dataValue = data[d] diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/type.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/type.k new file mode 100644 index 000000000..a8af59a71 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/type.k @@ -0,0 +1,2 @@ +_a: str = "s" +_a: int = 1 diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/unpack.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/unpack.k new file mode 100644 index 000000000..28e8c81bc --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/unpack.k @@ -0,0 +1,4 @@ +# only list, dict, schema object can be used * and ** unpacked +a = 1 +b = [*a] +c = {**a} diff --git a/test/test_units/test_kclvm/test_types/err_collect_test_data/var_not_defined.k b/test/test_units/test_kclvm/test_types/err_collect_test_data/var_not_defined.k new file mode 100644 index 000000000..ff9b48395 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/err_collect_test_data/var_not_defined.k @@ -0,0 +1,3 @@ +_a = b +_a = c +_d = a diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/assert/assert_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/assert/assert_0.k new file mode 100644 index 000000000..88775be26 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/assert/assert_0.k @@ -0,0 +1 @@ +assert False, 1 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/assert/assert_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/assert/assert_1.k new file mode 100644 index 000000000..b3745fe1c --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/assert/assert_1.k @@ -0,0 +1,2 @@ +a = 1 +assert False, a diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/attr_op/attr_op_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/attr_op/attr_op_0.k new file mode 100644 index 000000000..5300d6d4e --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/attr_op/attr_op_0.k @@ -0,0 +1,5 @@ +a = { + key1 = [0] # [0] + key1 += [1] # [0, 1] + key1 += None # Error: only list type can in inserted +} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/attr_op/attr_op_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/attr_op/attr_op_1.k new file mode 100644 index 000000000..51ff50dd2 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/attr_op/attr_op_1.k @@ -0,0 +1,8 @@ +schema Data: + key1: [int] + +a = Data { + key1 = [0] # [0] + key1 += [1] # [0, 1] + key1 += Undefined # Error: only list type can in inserted +} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_0.k new file mode 100644 index 000000000..d0155490a --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_0.k @@ -0,0 +1 @@ +a = 1 + "s" diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_1.k new file mode 100644 index 000000000..259f280da --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_1.k @@ -0,0 +1 @@ +a = -"s" diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_2.k new file mode 100644 index 000000000..b7f65d99e --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_2.k @@ -0,0 +1 @@ +a = "s" < 1 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_3.k b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_3.k new file mode 100644 index 000000000..13a7312e8 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_3.k @@ -0,0 +1 @@ +a = 1 / 0 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_4.k b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_4.k new file mode 100644 index 000000000..7b9544e84 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_4.k @@ -0,0 +1 @@ +a = 1 * None diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_5.k b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_5.k new file mode 100644 index 000000000..d4ef9cb9f --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_5.k @@ -0,0 +1 @@ +a = [] | {} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_6.k b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_6.k new file mode 100644 index 000000000..b0f77def3 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_6.k @@ -0,0 +1 @@ +a = 1 // 0 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_7.k b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_7.k new file mode 100644 index 000000000..257b158d8 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/calculation/calculation_7.k @@ -0,0 +1 @@ +a = 1 % 0 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/for_comp/for_comp_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/for_comp/for_comp_0.k new file mode 100644 index 000000000..1470b4b9d --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/for_comp/for_comp_0.k @@ -0,0 +1,4 @@ +data1 = {"k1": "v1", "k2": "v2"} +data2 = {"k3": "v3", "k4": "v4"} +# Error: dict unpacking cannot be used in dict comprehension +dataNew = {**{"${k1}": v1, "${k1}": v2, "${k2}": v1, "${k2}": v2} for k1, v1 in data1 for k2, v2 in data2} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/for_comp/for_comp_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/for_comp/for_comp_1.k new file mode 100644 index 000000000..d0099b7c6 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/for_comp/for_comp_1.k @@ -0,0 +1,3 @@ +data = [[1, 2], [3, 4]] +# Error: list unpacking cannot be used in list comprehension +dataNew = [*i for i in data] diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/func_call/func_call_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/func_call/func_call_0.k new file mode 100644 index 000000000..9d75fa5d8 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/func_call/func_call_0.k @@ -0,0 +1 @@ +a = 1() diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/func_call/func_call_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/func_call/func_call_1.k new file mode 100644 index 000000000..97d6f8b8d --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/func_call/func_call_1.k @@ -0,0 +1 @@ +a = ""() diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/func_call/func_call_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/func_call/func_call_2.k new file mode 100644 index 000000000..79531565b --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/func_call/func_call_2.k @@ -0,0 +1 @@ +a = func() diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/func_call/func_call_3.k b/test/test_units/test_kclvm/test_types/invalid_test_data/func_call/func_call_3.k new file mode 100644 index 000000000..02e620417 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/func_call/func_call_3.k @@ -0,0 +1,7 @@ +schema Person: + name: str + +func: any = lambda x: Person { + x.name +} +name = func(Person {name = 1}) diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/if/if_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/if/if_0.k new file mode 100644 index 000000000..57be988ea --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/if/if_0.k @@ -0,0 +1 @@ +a: int = 1 if True else "2" diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/if/if_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/if/if_1.k new file mode 100644 index 000000000..5e86489b5 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/if/if_1.k @@ -0,0 +1,3 @@ +_a: int = 1 +if True: + _a = "2" diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/import/import_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/import/import_1.k new file mode 100644 index 000000000..05827d95d --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/import/import_1.k @@ -0,0 +1 @@ +import import_2 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/import/import_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/import/import_2.k new file mode 100644 index 000000000..0b281f285 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/import/import_2.k @@ -0,0 +1 @@ +import import_1 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/import/kcl.mod b/test/test_units/test_kclvm/test_types/invalid_test_data/import/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_0.k new file mode 100644 index 000000000..c925349c0 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_0.k @@ -0,0 +1 @@ +a = [i for i in 1] \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_1.k new file mode 100644 index 000000000..21c0ce4af --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_1.k @@ -0,0 +1,4 @@ +a = 1 +b = all i in a { + i +} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_2.k new file mode 100644 index 000000000..1a4308681 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_2.k @@ -0,0 +1 @@ +a: {str:} = {i: "value" for i in [{"name": "value"}, {"name": "value"}]} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_3.k b/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_3.k new file mode 100644 index 000000000..4abc698f2 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_3.k @@ -0,0 +1 @@ +x: [int] = [i for i in [0] or ["1"]] diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_4.k b/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_4.k new file mode 100644 index 000000000..7558e5094 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_4.k @@ -0,0 +1 @@ +x: [str] = [i for i in [0] or ["1"]] diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_5.k b/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_5.k new file mode 100644 index 000000000..5e382772e --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/loop/loop_5.k @@ -0,0 +1 @@ +x = [i + v for i, v in [0] or ["1"]] diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/module/kcl.mod b/test/test_units/test_kclvm/test_types/invalid_test_data/module/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_0.k new file mode 100644 index 000000000..26ac612aa --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_0.k @@ -0,0 +1,3 @@ +import pkg + +a = pkg.b diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_1.k new file mode 100644 index 000000000..4fb05f3c3 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_1.k @@ -0,0 +1,3 @@ +import pkg + +pkg.a = 1 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_2.k new file mode 100644 index 000000000..0ed0addb0 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_2.k @@ -0,0 +1,3 @@ +import pkg as pkgpkg + +a = pkgpkg.b diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_3.k b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_3.k new file mode 100644 index 000000000..1492535ff --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_3.k @@ -0,0 +1 @@ +import math_err diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_4.k b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_4.k new file mode 100644 index 000000000..c656b420a --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_4.k @@ -0,0 +1 @@ +import kcl_plugin.err_plugin diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_5.k b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_5.k new file mode 100644 index 000000000..39cb50c91 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_5.k @@ -0,0 +1 @@ +import pkg1 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_6.k b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_6.k new file mode 100644 index 000000000..daef190bc --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/module/module_6.k @@ -0,0 +1,4 @@ +import math + +a = math.err_func(1) + diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/module/pkg/pkg.k b/test/test_units/test_kclvm/test_types/invalid_test_data/module/pkg/pkg.k new file mode 100644 index 000000000..d25d49e0f --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/module/pkg/pkg.k @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/module/pkg1/a.k b/test/test_units/test_kclvm/test_types/invalid_test_data/module/pkg1/a.k new file mode 100644 index 000000000..288701ba1 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/module/pkg1/a.k @@ -0,0 +1 @@ +import pkg diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/module/pkg1/b.k b/test/test_units/test_kclvm/test_types/invalid_test_data/module/pkg1/b.k new file mode 100644 index 000000000..bbd114505 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/module/pkg1/b.k @@ -0,0 +1 @@ +b = pkg.a diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/kcl.mod b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/pkg/pkg.k b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/pkg/pkg.k new file mode 100644 index 000000000..1c30c4acd --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/pkg/pkg.k @@ -0,0 +1,2 @@ +schema SomeProtocol: + data: int diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_1.k new file mode 100644 index 000000000..af6a60c60 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_1.k @@ -0,0 +1,5 @@ +schema DataProtocol: + data: str + +schema Data for DataProtocol: + x: int = data diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_2.k new file mode 100644 index 000000000..ee9f7d81d --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_2.k @@ -0,0 +1,7 @@ +import pkg + +schema DataProtocol: + data: str + +schema DataMixin for pkg.DataProtocol: + x: int = data diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_3.k b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_3.k new file mode 100644 index 000000000..6cd890a1f --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_3.k @@ -0,0 +1,7 @@ +import pkg + +schema DataProtocol: + data: str + +schema DataMixin for pkg.pkg.DataProtocol: + x: int = data diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_4.k b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_4.k new file mode 100644 index 000000000..a35a69a33 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_4.k @@ -0,0 +1,4 @@ +a = None + +schema DataMixin for a: + x: int = data diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_5.k b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_5.k new file mode 100644 index 000000000..a9d78448d --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/protocol/protocol_5.k @@ -0,0 +1,3 @@ +protocol DataProtocol: + x: str = "1" + x = "2" diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/kcl.mod b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/pkg/pkg.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/pkg/pkg.k new file mode 100644 index 000000000..69d89eea9 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/pkg/pkg.k @@ -0,0 +1,8 @@ +schema Person: + name: str = "Alice" + age: int = 18 + +Base = 1 + +schema PersonMixin: + nameUpper: str = name.upper() diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_0.k new file mode 100644 index 000000000..7cc8d965c --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_0.k @@ -0,0 +1,2 @@ +schema Data(pkg.Base.Base): + name: str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_1.k new file mode 100644 index 000000000..851f6ba8f --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_1.k @@ -0,0 +1,2 @@ +schema Data(Base): + name: str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_10.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_10.k new file mode 100644 index 000000000..725308ede --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_10.k @@ -0,0 +1,5 @@ +schema _Data: + name: str + +schema _Data: + name: str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_11.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_11.k new file mode 100644 index 000000000..5e6ffe064 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_11.k @@ -0,0 +1,6 @@ +schema Data: + name: str + +data = Data { + data: 1 +} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_12.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_12.k new file mode 100644 index 000000000..d7f6d5765 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_12.k @@ -0,0 +1,12 @@ +schema NamedMap: + [str]: str + +data = { + key1: "value1" + key2: "value2" +} + +named_data = NamedMap { + **data + data: 1 +} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_13.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_13.k new file mode 100644 index 000000000..924123d57 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_13.k @@ -0,0 +1,3 @@ +schema NamedMap: + [str]: str + data: int diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_14.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_14.k new file mode 100644 index 000000000..27c9bb4bc --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_14.k @@ -0,0 +1,4 @@ +schema Person[name: str, age: int]: + """""" + +person = Person(name="Alice", name="Bob") {} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_15.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_15.k new file mode 100644 index 000000000..9369932fb --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_15.k @@ -0,0 +1,4 @@ +schema Person[name: str, age: int]: + """""" + +person = Person(name="Alice", name="Bob") diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_16.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_16.k new file mode 100644 index 000000000..003f22796 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_16.k @@ -0,0 +1,4 @@ +schema Person[name: str, age: int]: + """""" + +person = Person(err_name="Alice") diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_17.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_17.k new file mode 100644 index 000000000..9ff2da45e --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_17.k @@ -0,0 +1,4 @@ +schema Person[name: str, age: int]: + """""" + +person = Person(10, "name") diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_18.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_18.k new file mode 100644 index 000000000..f62e33f2c --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_18.k @@ -0,0 +1,5 @@ +@deprecated(err_version="v1.16") +schema Person[name: str, age: int]: + """""" + +person = Person("name", 10) diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_19.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_19.k new file mode 100644 index 000000000..707cc4962 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_19.k @@ -0,0 +1,4 @@ +Person = 1 +b = Person { + name: "Alice" +} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_2.k new file mode 100644 index 000000000..82c22b698 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_2.k @@ -0,0 +1,4 @@ +Base = 1 + +schema Data(Base): + name: str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_20.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_20.k new file mode 100644 index 000000000..0bf8facc8 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_20.k @@ -0,0 +1,7 @@ +schema Data: + [str]: int + +data = Data { + key1: 1 + key2: "value" +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_21.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_21.k new file mode 100644 index 000000000..3bf2fe880 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_21.k @@ -0,0 +1,9 @@ +base = 1 + +schema Data: + [str]: int + +data = Data { + **base + key1: 1 +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_22.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_22.k new file mode 100644 index 000000000..ba9d6358e --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_22.k @@ -0,0 +1,4 @@ +schema Data: + [str]: int + +data_list = Data.err_instances() diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_23.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_23.k new file mode 100644 index 000000000..6b645cd77 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_23.k @@ -0,0 +1,9 @@ +schema Person: + name: str + temp: str + + _temp = None + temp = _temp + + _name = 123 + name = _name diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_24.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_24.k new file mode 100644 index 000000000..444ea8d06 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_24.k @@ -0,0 +1,5 @@ +schema Parent(Son): + name: str + +schema Son(Parent): + name: str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_25.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_25.k new file mode 100644 index 000000000..25f27ade9 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_25.k @@ -0,0 +1,2 @@ +schema Conifg: + name.attr: str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_26.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_26.k new file mode 100644 index 000000000..25f27ade9 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_26.k @@ -0,0 +1,2 @@ +schema Conifg: + name.attr: str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_27.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_27.k new file mode 100644 index 000000000..8c8fdadf2 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_27.k @@ -0,0 +1,2 @@ +schema Person[name: str]: + name: str = name diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_28.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_28.k new file mode 100644 index 000000000..e1498e3a4 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_28.k @@ -0,0 +1,6 @@ +# Arguments cannot be used in the schema modification expression +schema Person: + name: str + +personOrigin = Person { name = "Alice" } +personNew = personOrigin("invalid arguments") { name = "Bob" } diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_29.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_29.k new file mode 100644 index 000000000..d4e1249c4 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_29.k @@ -0,0 +1,2 @@ +schema str: + name: str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_3.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_3.k new file mode 100644 index 000000000..1f0c17dc3 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_3.k @@ -0,0 +1,4 @@ +import pkg + +schema Data(pkg.ErrorName): + name: str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_30.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_30.k new file mode 100644 index 000000000..5ba39ea00 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_30.k @@ -0,0 +1,4 @@ +id = 1 + +rule str: + id > 0 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_31.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_31.k new file mode 100644 index 000000000..0b6537a84 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_31.k @@ -0,0 +1,3 @@ +schema Data: + a = 1 + a = "1" diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_32.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_32.k new file mode 100644 index 000000000..ea52735fd --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_32.k @@ -0,0 +1,8 @@ +schema Person: + name: str = "kcl" + age: int = 1 + +schema Data: + x: Person = { + age = "123" + } diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_33.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_33.k new file mode 100644 index 000000000..d7c59ea75 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_33.k @@ -0,0 +1,7 @@ +schema Person: + name: str = "kcl" + age: int = 1 + +x: Person = { + age = "123" +} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_34.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_34.k new file mode 100644 index 000000000..62abcac8c --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_34.k @@ -0,0 +1,11 @@ +schema Base: + name: str + +schema Person(Base): + name?: str + age?: int + +person = Person { + name: "Alice" + age: 18 +} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_35.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_35.k new file mode 100644 index 000000000..db7cfa2dc --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_35.k @@ -0,0 +1,2 @@ +schema Data: + [int]: int diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_4.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_4.k new file mode 100644 index 000000000..0da6d0d5d --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_4.k @@ -0,0 +1,4 @@ +import pkg + +schema Data(err_pkg.Person): + name: str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_5.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_5.k new file mode 100644 index 000000000..b842cac00 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_5.k @@ -0,0 +1,4 @@ +import pkg + +schema Data(pkg.Base): + name: str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_6.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_6.k new file mode 100644 index 000000000..3d64f8883 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_6.k @@ -0,0 +1,5 @@ +schema DataMixin: + name: str + +schema Data(DataMixin): + name: str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_7.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_7.k new file mode 100644 index 000000000..a62ce95e2 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_7.k @@ -0,0 +1,10 @@ +import pkg + +schema DataMixin: + name: str + +schema Data: + mixin [ + pkg.PersonMixin, + DataMixinError + ] diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_8.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_8.k new file mode 100644 index 000000000..e57d4f1c5 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_8.k @@ -0,0 +1,3 @@ +schema Data: + name: str + name: int diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_9.k b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_9.k new file mode 100644 index 000000000..17451d18d --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/schema/schema_9.k @@ -0,0 +1,3 @@ +schema Data: + [name: str]: int + name: int diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/select_attr/select_attr_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/select_attr/select_attr_1.k new file mode 100644 index 000000000..7b3e58dbb --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/select_attr/select_attr_1.k @@ -0,0 +1 @@ +name = "Alice".err_upper() \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/select_attr/select_attr_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/select_attr/select_attr_2.k new file mode 100644 index 000000000..bbf2c74a4 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/select_attr/select_attr_2.k @@ -0,0 +1,9 @@ +schema Person: + name: str + age: int + +person = Person { + name: "Alice" + age: 18 +} +age = person.err_age diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/subscript/subscript_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/subscript/subscript_0.k new file mode 100644 index 000000000..96366b60a --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/subscript/subscript_0.k @@ -0,0 +1,2 @@ +dataList = [1, 2, 3] +dataList0: int = dataList["1"] diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/subscript/subscript_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/subscript/subscript_1.k new file mode 100644 index 000000000..0ca79e770 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/subscript/subscript_1.k @@ -0,0 +1,3 @@ +dataDict = {key: "value"} +keyType = [1] +dataKeyValue: str = dataDict[keyType] diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/subscript/subscript_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/subscript/subscript_2.k new file mode 100644 index 000000000..3fbe05562 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/subscript/subscript_2.k @@ -0,0 +1 @@ +data = 1[0] diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/kcl.mod b/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/pkg/pkg1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/pkg/pkg1.k new file mode 100644 index 000000000..b4c3153b0 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/pkg/pkg1.k @@ -0,0 +1,3 @@ +schema Person: + name: str + age: int diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/pkg/pkg2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/pkg/pkg2.k new file mode 100644 index 000000000..4b875356f --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/pkg/pkg2.k @@ -0,0 +1,9 @@ +schema Data: + person: Person + +data = Data { + person: Person { + name = "Alice" + age = 18 + } +} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/type_alias_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/type_alias_0.k new file mode 100644 index 000000000..868488628 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/type_alias_0.k @@ -0,0 +1,5 @@ +import pkg + +type Person = pkg.Person + +data: int = pkg.data diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/type_alias_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/type_alias_1.k new file mode 100644 index 000000000..f1e3ecb8c --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/type_alias_1.k @@ -0,0 +1 @@ +x: Int = 1.0 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/type_alias_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/type_alias_2.k new file mode 100644 index 000000000..dd9d9cdcb --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_alias/type_alias_2.k @@ -0,0 +1 @@ +type int = str diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_0.k new file mode 100644 index 000000000..21c6db817 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_0.k @@ -0,0 +1 @@ +a: int = "s" diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_1.k new file mode 100644 index 000000000..1cd4be8ad --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_1.k @@ -0,0 +1 @@ +a: str = 1 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_2.k new file mode 100644 index 000000000..7d71b1d77 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_2.k @@ -0,0 +1,10 @@ +schema Data: + id?: int + +schema Person: + data: Data + name: a = 1 + +a: int = 1 + +person = Person {} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_3.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_3.k new file mode 100644 index 000000000..c4e485afd --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_3.k @@ -0,0 +1,3 @@ +_a: str = 1 +_a = "123" +a = _a diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_4.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_4.k new file mode 100644 index 000000000..16d29cdfd --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_4.k @@ -0,0 +1,2 @@ +_a: str = 1 +a = _a diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_5.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_5.k new file mode 100644 index 000000000..feafc7489 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_5.k @@ -0,0 +1,2 @@ +_a: str = "1" +_a: int = 1 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_6.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_6.k new file mode 100644 index 000000000..d6aad8c25 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_6.k @@ -0,0 +1,2 @@ +a: str = "1" +b: int = a as int diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_7.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_7.k new file mode 100644 index 000000000..fd484e0d5 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_7.k @@ -0,0 +1,2 @@ +a: str = "1" +b: int = "1" as a[0] diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_8.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_8.k new file mode 100644 index 000000000..6336306f0 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_annotation/type_annotation_8.k @@ -0,0 +1,2 @@ +a: str = "1" +b: int = "1" as a diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/kcl.mod b/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/pkg/pkg1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/pkg/pkg1.k new file mode 100644 index 000000000..b4c3153b0 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/pkg/pkg1.k @@ -0,0 +1,3 @@ +schema Person: + name: str + age: int diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/pkg/pkg2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/pkg/pkg2.k new file mode 100644 index 000000000..c22670caf --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/pkg/pkg2.k @@ -0,0 +1,9 @@ +schema Data: + person: Person + +data = Data { + person: Person { + name = "Alice" + age = 18 + } as Person +} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/type_as_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/type_as_0.k new file mode 100644 index 000000000..a282f9e38 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/type_as_0.k @@ -0,0 +1,3 @@ +import pkg + +data: int = pkg.data as float diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/type_as_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/type_as_1.k new file mode 100644 index 000000000..b15c20e31 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/type_as/type_as_1.k @@ -0,0 +1 @@ +x = 1.0 as Int diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/unification/unification.k b/test/test_units/test_kclvm/test_types/invalid_test_data/unification/unification.k new file mode 100644 index 000000000..43a88e7f1 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/unification/unification.k @@ -0,0 +1,8 @@ +schema Config: + name: str + id?: int + +config.name: Config { + name: "config" + id: 1 +} diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/unique/unique_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/unique/unique_1.k new file mode 100644 index 000000000..20e49fa50 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/unique/unique_1.k @@ -0,0 +1,2 @@ +a = 1 +a = 2 \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/unique/unique_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/unique/unique_2.k new file mode 100644 index 000000000..30ba79261 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/unique/unique_2.k @@ -0,0 +1,4 @@ +schema Person: + name: str + +Person = 1 diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/unique/unique_3.k b/test/test_units/test_kclvm/test_types/invalid_test_data/unique/unique_3.k new file mode 100644 index 000000000..5e72165dd --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/unique/unique_3.k @@ -0,0 +1,2 @@ +_a: int = 1 +_a: str = "s" diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/unpack/unpack_0.k b/test/test_units/test_kclvm/test_types/invalid_test_data/unpack/unpack_0.k new file mode 100644 index 000000000..6e08a93d8 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/unpack/unpack_0.k @@ -0,0 +1,2 @@ +a = 1 +b = [*a] diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/unpack/unpack_1.k b/test/test_units/test_kclvm/test_types/invalid_test_data/unpack/unpack_1.k new file mode 100644 index 000000000..a95fc530b --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/unpack/unpack_1.k @@ -0,0 +1 @@ +a = [*1] diff --git a/test/test_units/test_kclvm/test_types/invalid_test_data/unpack/unpack_2.k b/test/test_units/test_kclvm/test_types/invalid_test_data/unpack/unpack_2.k new file mode 100644 index 000000000..97798e94d --- /dev/null +++ b/test/test_units/test_kclvm/test_types/invalid_test_data/unpack/unpack_2.k @@ -0,0 +1,10 @@ +schema Person: + name?: str + +data: {str:str} | Person = {name: "Alice"} +a = { + if True: **None + elif False: **data + else: **{} +} +b = {if True: **1} diff --git a/test/test_units/test_kclvm/test_types/kcl.mod b/test/test_units/test_kclvm/test_types/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/assert.k b/test/test_units/test_kclvm/test_types/normal_test_data/assert.k new file mode 100644 index 000000000..7b997c1e5 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/assert.k @@ -0,0 +1,2 @@ +assert True +assert False, "message" diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/builtin.k b/test/test_units/test_kclvm/test_types/normal_test_data/builtin.k new file mode 100644 index 000000000..6ec348c55 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/builtin.k @@ -0,0 +1,17 @@ +import math +import net + + +schema Person: + name: str + age: int + +a: any = math.log(10) +b: any = 1 + 2 +c: any = net.is_IP("192.168.0.1") +d: str = ".".replace(".", "") +e: bool = "A".isupper() +f: [] = Person.instances() +g: any = 1.1 +h: int = int(1.1) +i: float = float(1.1) diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/calculation.k b/test/test_units/test_kclvm/test_types/normal_test_data/calculation.k new file mode 100644 index 000000000..96e1465fc --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/calculation.k @@ -0,0 +1,290 @@ +schema Data: + [str]: str + name: str + +# 1. Binary Operator +# Add +add_a = 1 + 1 +add_b = 1 + 1.1 +add_c = 1.1 + 1.1 +add_d = 1.1 + 1 +add_e = True + 1 +add_f = 1 + False +add_g = "s" + "s" +add_h = [0] + [1] +add_i = 1 + add_b +add_j = add_c + 1 +add_k = add_e + add_f +add_l = 1 + 1 + 1 +add_m = 1 + 1.1 + 1 +add_n = 1.1 + True + 1 + 1.0 +# Sub +sub_a = 1 - 1 +sub_b = 1 - 1.1 +sub_c = 1.1 - 1.1 +sub_d = 1.1 - 1 +sub_e = True - 1 +sub_f = 1 - False +sub_g = 0 - 0.0 +sub_h = True - False +sub_i = 1 - sub_b +sub_j = sub_c - 1 +sub_k = sub_e - sub_f +sub_l = 1 - 1 - 1 +sub_m = 1 - 1.1 - 1 +sub_n = 1.1 - True - 1 - 1.0 +# Mul +mul_a = 1 * 1 +mul_b = 1 * 1.1 +mul_c = 1.1 * 1.1 +mul_d = 1.1 * 1 +mul_e = True * 1 +mul_f = 1 * False +mul_g = "s" * 2 + 2 * "s" +mul_h = 2 * [0] + [0] * 2 +mul_i = 1 * mul_b +mul_j = mul_c * 1 +mul_k = mul_e * mul_f +mul_l = 1 * 1 * 1 +mul_m = 1 * 1.1 * 1 +mul_n = 1.1 * True * 1 * 1.0 +mul_o = (1 if mul_n else "1") * 2 +# Div +div_a = 1 / 1 +div_b = 1 / 1.1 +div_c = 1.1 / 1.1 +div_d = 1.1 / 1 +div_e = False / 1 +div_f = 1 / True +div_g = False / True / 1.0 +div_h = 1.0 / 1 / True / True +div_i = 1 / div_b +div_j = div_c / 1 +div_k = div_e / div_f +div_l = 1 / 1 / 1 +div_m = 1 / 1.1 / 1 +div_n = 1.1 / True / 1 / 1.0 +# FloorDiv +floor_div_a = 1 // 1 +floor_div_b = 1 // 1.1 +floor_div_c = 1.1 // 1.1 +floor_div_d = 1.1 // 1 +floor_div_e = False // 1 +floor_div_f = 1 // True +floor_div_g = False // True // 1.0 +floor_div_h = 1.0 // 1 // True // True +floor_div_i = 1 // floor_div_b +floor_div_j = floor_div_c // 1 +floor_div_k = floor_div_e // floor_div_f +floor_div_l = 1 // 1 // 1 +floor_div_m = 1 // 1.1 // 1 +floor_div_n = 1.1 // True // 1 // 1.0 +# Mod +mod_a = 1 % 1 +mod_b = 1 % 1.1 +mod_c = 1.1 % 1.1 +mod_d = 1.1 % 1 +mod_e = False % 1 +mod_f = 1 % True +mod_g = False % True % 1.0 +mod_h = 1.0 % 1 % True % True +mod_i = 1 % mod_b +mod_j = mod_c % 1 +mod_k = mod_e % mod_f +mod_l = 1 % 1 % 1 +mod_m = 1 % 1.1 % 1 +mod_n = 1.1 % True % 1 % 1.0 +# Pow +pow_a = 1 ** 1 +pow_b = 1 ** 1.1 +pow_c = 1.1 ** 1.1 +pow_d = 1.1 ** 1 +pow_e = False ** 1 +pow_f = 1 ** True +pow_g = False ** True ** 1.0 +pow_h = 1.0 ** 1 ** True ** True +pow_i = 1 ** pow_b +pow_j = pow_c ** 1 +pow_k = pow_e ** pow_f +pow_l = 1 ** 1 ** 1 +pow_m = 1 ** 1.1 ** 1 +pow_n = 1.1 ** True ** 1 ** 1.0 +# LShift +lshift_a = 1 >> 0 +lshift_b = 0 >> 1 +lshift_c = 2 >> 2 +lshift_d = 2 >> 1 +lshift_e = 1 >> 2 +# RShift +rshift_a = 1 << 0 +rshift_b = 0 << 1 +rshift_c = 2 << 2 +rshift_d = 2 << 1 +rshift_e = 1 << 2 +# BitOr +bitor_a = 1 | 2 +bitor_b = 2 | 3 +bitor_c = 0xAb | 0xbA +bitor_d = [] | [] +bitor_e = {} | {} +bitor_f = [1] | [2] +bitor_g = {key: "value1"} | {key: "value2"} +bitor_h = bitor_e | bitor_g +bitor_i = bitor_e | None +bitor_j = bitor_f | None +bitor_k = None | {key: "value2"} +bitor_l = Data {name: "v"} | {name: "v"} +bitor_m = Data {name: "v"} | Data {name: "v"} +# BitXOr +bitxor_a = 1 ^ 2 +bitxor_b = 2 ^ 3 +bitxor_c = 0xAb ^ 0xbA +bitxor_d = bitxor_a & 0xFF +bitxor_e = 0xFF & bitxor_b +bitxor_f = bitxor_c & bitxor_d +# BitAdd +bitand_a = 1 & 2 +bitand_b = 2 & 3 +bitand_c = 0xAb & 0xbA +bitand_d = bitand_a & 0xFF +bitand_e = 0xFF & bitand_b +bitand_f = bitand_c & bitand_d +# And +logicand_a = 1 and 2 +logicand_b = "" and 2 +logicand_c = None and 2.0 and "" and Undefined +logicand_d = logicand_a == 1 and 2.0 and "" +logicand_e = logicand_b and False and {} and [] +# Or +logicor_a = 1 or 2 +logicor_b = "" or 2 +logicor_c = None or 2.0 or "" or Undefined +logicor_d = logicor_a == 1 or 2.0 or "" +logicor_e = logicor_b or False or {} and [] +# 2. Compare Operator +# Eq +compare_eq_a = 1 == 1 +compare_eq_b = 1 == 1.0 +compare_eq_c = 1.0 == 1 +compare_eq_d = True == False +compare_eq_e = None == False +compare_eq_f = None == Undefined +# NotEq +compare_not_eq_a = 1 == 1 +compare_not_eq_b = 1 == 1.0 +compare_not_eq_c = 1.0 == 1 +compare_not_eq_d = True == False +compare_not_eq_e = None == False +compare_not_eq_f = None == Undefined +compare_not_eq_g = [] == [] +compare_not_eq_h = {} == {} +compare_not_eq_i = "" == "" +compare_not_eq_j = compare_not_eq_h == True +# Lt +compare_lt_a = 1 < 2 +compare_lt_b = 1 < 2.0 +compare_lt_c = 1.0 < 2 +compare_lt_d = 1.0 < 2.0 +compare_lt_e = compare_lt_a < compare_lt_d +compare_lt_f = "s" < "ss" +compare_lt_g = [1] < [2] +# LtE +compare_lte_a = 1 <= 2 +compare_lte_b = 1 <= 2.0 +compare_lte_c = 1.0 <= 2 +compare_lte_d = 1.0 <= 2.0 +compare_lte_e = compare_lte_a <= compare_lte_d +compare_lte_f = "s" <= "ss" +compare_lte_g = [1] <= [2] +# Gt +compare_gt_a = 1 > 2 +compare_gt_b = 1 > 2.0 +compare_gt_c = 1.0 > 2 +compare_gt_d = 1.0 > 2.0 +compare_gt_e = compare_gt_a > compare_gt_d +compare_gt_f = "s" > "ss" +compare_gt_g = [1] > [2] +# GtE +compare_gte_a = 1 >= 2 +compare_gte_b = 1 >= 2.0 +compare_gte_c = 1.0 >= 2 +compare_gte_d = 1.0 >= 2.0 +compare_gte_e = compare_gte_a >= compare_gte_d +compare_gte_f = "s" >= "ss" +compare_gte_g = [1] >= [2] +# Is +compare_is_a = 1 is 2 +compare_is_b = 1 is 2.0 +compare_is_c = 1.0 is 2 +compare_is_d = 1.0 is 2.0 +compare_is_e = compare_is_a is compare_is_d +compare_is_f = "s" is "ss" +compare_is_g = [1] is [2] +compare_is_h = compare_is_g is True +compare_is_i = None is None +compare_is_j = True is False +compare_is_k = compare_is_f is Undefined +# In +compare_in_a = 1 in [2] +compare_in_b = "key" in {} +compare_in_c = "s" in "ss" +compare_in_d = compare_in_a in [True] +compare_in_e = compare_in_d in {} +# Not +compare_not_a = 1 not 2 +compare_not_b = 1 not 2.0 +compare_not_c = 1.0 not 2 +compare_not_d = 1.0 not 2.0 +compare_not_e = compare_not_a not compare_not_d +compare_not_f = "s" not "ss" +compare_not_g = [1] not [2] +compare_not_h = compare_not_g not True +compare_not_i = None not None +compare_not_j = True not False +compare_not_k = compare_not_f not Undefined +# IsNot +compare_is_not_a = 1 is not 2 +compare_is_not_b = 1 is not 2.0 +compare_is_not_c = 1.0 is not 2 +compare_is_not_d = 1.0 is not 2.0 +compare_is_not_e = compare_is_not_a is not compare_is_not_d +compare_is_not_f = "s" is not "ss" +compare_is_not_g = [1] is not [2] +compare_is_not_h = compare_is_not_g is not True +compare_is_not_i = None is not None +compare_is_not_j = True is not False +compare_is_not_k = compare_is_not_f is not Undefined +# NotIn +compare_not_in_a = 1 not in [2] +compare_not_in_b = "key" not in {} +compare_not_in_c = "s" not in "ss" +compare_not_in_d = compare_not_in_a not in [True] +compare_not_in_e = compare_not_in_d not in {} +# 3. Unary Operator +# UAdd +unary_add_a = +1 +unary_add_b = +1.0 +unary_add_c = +True +unart_add_d = None +unart_add_e = +unart_add_d +# USub +unary_sub_a = -1 +unary_sub_b = -1.0 +unary_sub_c = -True +unart_sub_d = None +unart_sub_e = -unart_sub_d +# Invert +unary_invert_a = ~1 +unary_invert_b = ~True +unary_invert_c = ~False +unart_invert_d = None +unart_invert_e = ~unart_invert_d +# Not +unary_not_a = not 1 +unary_not_b = not 1.0 +unary_not_c = not "" +unary_not_d = not False +unary_not_e = not [] +unary_not_f = not {} +unary_not_g = not None +unary_not_h = not Undefined diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/config_op.k b/test/test_units/test_kclvm/test_types/normal_test_data/config_op.k new file mode 100644 index 000000000..6f619a7af --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/config_op.k @@ -0,0 +1,20 @@ +schema Person: + [str]: any + name: str = "Alice" + age: int = 10 + data: [int] = [1, 2, 3] + labels: {str:} + +name = "" +age = 1 +person = Person { + name: name + age: age + "1": 1 + data[0] += 1 + data[1] = 1 + data += [2] + "temp": "temp" + labels.key1: 1 + labels.key2: "1" +} diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/dict.k b/test/test_units/test_kclvm/test_types/normal_test_data/dict.k new file mode 100644 index 000000000..e50864113 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/dict.k @@ -0,0 +1,9 @@ +data: {str:str} = { + key1: "value1" + key2: "value2" + if True: key3: "value3" + if True: "key4": "value4" +} +dataNew = data { + key5 = "value5" +} diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/dict_assign_to_schema.k b/test/test_units/test_kclvm/test_types/normal_test_data/dict_assign_to_schema.k new file mode 100644 index 000000000..bf667a575 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/dict_assign_to_schema.k @@ -0,0 +1,34 @@ +# Dict to schema test +schema Person: + name: str + age: int + +data0: {str:str} = {name: "Alice"} +data1: {str:int} = {age: 18} +data2: {:} = {} +data3: {any:any} = {} +data4: any = {} + +person0 = Person {**data0} +person1 = Person {**data1} +person2 = Person {**data2} +person3 = Person {**data3} +person4 = Person {**data4} + +schema KeyVal: + labels?: {str:} + +schema Data: + keyVals?: KeyVal + +# Dict to schema in schema test +data = Data { + keyVals: { + labels.key: 456 + } + keyVals.labels.key2: 789 +} + +# Any dict to schema +personData: any = {name: "Alice", age: 18} +person: Person = personData diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/final.k b/test/test_units/test_kclvm/test_types/normal_test_data/final.k new file mode 100644 index 000000000..bd1ab30f9 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/final.k @@ -0,0 +1,51 @@ +schema Name: + name: str = "Kiki" + age: int = 10 + +schema Name0: + name0: Name + +schema Name1: + name1: Name0 + +schema Name2: + name2: Name1 + +schema Name3: + name3: Name2 + +schema Name4: + name4: Name3 + +schema Name5: + name5: Name4 + +schema Name6: + name6: Name5 + +schema Name7: + name7: Name6 + +schema Person: + name: Name7 + +John = Person{ + name.name7.name6:{ + name5: Name4{ + name4:{ + name3: Name2{ + if True: name2.name1: { + name0:{ + age = 11 + } + } + elif False: name2.name1: { + name0:{ + age = 12 + } + } + } + } + } + } +} diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/if_expr.k b/test/test_units/test_kclvm/test_types/normal_test_data/if_expr.k new file mode 100644 index 000000000..0c8a5f597 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/if_expr.k @@ -0,0 +1,6 @@ +a = 1 if True else "ss" +b = "1" if a else 1 +c = 1 + 2 if True else 3 +d: int|str = 1 +e: bool = d == "s" +f: [int] = [1, 2] + ([3, 4] + [5, 6] if True else [7, 8]) diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/if_item.k b/test/test_units/test_kclvm/test_types/normal_test_data/if_item.k new file mode 100644 index 000000000..f625f9423 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/if_item.k @@ -0,0 +1,11 @@ +data1 = { + if True: name: "Alice" + age: int +} +data2 = [ + if True: {key1: "value1"} + {key2: "value2"} +] +data3 = { + if True: key.key1.key2: "value" +} diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/if_stmt.k b/test/test_units/test_kclvm/test_types/normal_test_data/if_stmt.k new file mode 100644 index 000000000..a2a7f66a8 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/if_stmt.k @@ -0,0 +1,7 @@ +a = 1 +if a > 2: + _b = 1 +elif a > 1: + _b = 2 +else: + _b = 3 diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/index_signature.k b/test/test_units/test_kclvm/test_types/normal_test_data/index_signature.k new file mode 100644 index 000000000..30032f733 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/index_signature.k @@ -0,0 +1,10 @@ +schema Data: + [name: str]: str + + check: + name.islower() + +data = Data { + key: "value" +} +value = data.key diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/kcl.mod b/test/test_units/test_kclvm/test_types/normal_test_data/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/list.k b/test/test_units/test_kclvm/test_types/normal_test_data/list.k new file mode 100644 index 000000000..604ab8ba7 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/list.k @@ -0,0 +1,6 @@ +data: [str] = [ + "value1" + "value2" + if True: "value3" + if True: "value4" +] diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/loop.k b/test/test_units/test_kclvm/test_types/normal_test_data/loop.k new file mode 100644 index 000000000..3f8099889 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/loop.k @@ -0,0 +1,17 @@ +str0 = [str(i) + ": " + c.upper() for i, c in "apple"] +str1 = [c.upper() for c in "apple"] +str2 = {str(i): c.upper() for i, c in "apple"} +str3 = {c: c.upper() for c in "apple"} + +list0 = [str(i) + ": " + c.upper() for i, c in ["a", "p", "p"]] +list1 = [c.upper() for c in ["a", "p", "p"]] +list2 = {str(i): c.upper() for i, c in ["a", "p", "p"]} +list3 = {c: c.upper() for c in ["a", "p", "p"]} + +dict0 = [str(k) + ": " + v.upper() for k, v in {"a": "A", "p": "P"}] +dict1 = [c.upper() for c in {"a": "A", "p": "P"}] +dict2 = {str(k): v.upper() for k, v in {"a": "A", "p": "P"}} +dict3 = {c: c.upper() for c in {"a": "A", "p": "P"}} + +listUnion0 = [i for i in [0] or ["1"]] +listUnion1 = [i for i in [0, "1"] or ["1", 0]] diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/nest_var.k b/test/test_units/test_kclvm/test_types/normal_test_data/nest_var.k new file mode 100644 index 000000000..58e8c6e46 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/nest_var.k @@ -0,0 +1,10 @@ +schema Person: + name: str = "Alice" + +person = { + "name": "Alice" +} + +data = Person { + person["name"]: person["name"].upper() +} diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/pkg/pkg.k b/test/test_units/test_kclvm/test_types/normal_test_data/pkg/pkg.k new file mode 100644 index 000000000..93d194a75 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/pkg/pkg.k @@ -0,0 +1,6 @@ +schema Person: + name: str + age: int + +rule PkgRule: + age < 10 diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/protocol.k b/test/test_units/test_kclvm/test_types/normal_test_data/protocol.k new file mode 100644 index 000000000..dabf99b66 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/protocol.k @@ -0,0 +1,8 @@ +schema DataProtocol: + a: int + +schema DataMixin for DataProtocol: + x: int = a + +schema Data: + mixin [DataMixin] diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/quant.k b/test/test_units/test_kclvm/test_types/normal_test_data/quant.k new file mode 100644 index 000000000..1958307cd --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/quant.k @@ -0,0 +1,27 @@ +schema Person: + name: str = "Alice" + + check: + all c in name { + c.isupper() + } + +data0 = all i, c in "app" { + c.upper() if 1 > 1 +} +data1: bool = all i in [1, 2, 3] { + i if i > 2 +} +data2: bool = any i in [1, 2, 3] { + i if i > 2 +} +data3: [str] = map k, v in {key1: "value1", key2: "value2"} { + k + v +} +data4: [int] = filter i, elem in [1, 2, 3] { + i > 1 and elem > 2 +} +value = [0] or ["0"] +data5: bool = all v in value { + int(v) > 1 +} diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/rule.k b/test/test_units/test_kclvm/test_types/normal_test_data/rule.k new file mode 100644 index 000000000..363bd85be --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/rule.k @@ -0,0 +1,20 @@ +import pkg + +age = 1 + +schema MainProtocol: + var: int + age: int + +schema MainMixin for MainProtocol: + var: int + +rule Base: + age > 0 + +rule Main[var, default=1](Base, pkg.PkgRule) for MainProtocol: + age < 10 + +Main(1) { + age = 1 +} diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/schema.k b/test/test_units/test_kclvm/test_types/normal_test_data/schema.k new file mode 100644 index 000000000..f3e0b2c10 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/schema.k @@ -0,0 +1,18 @@ +schema PersonMixin: + nameUpper: str = name.upper() + +schema Person: + mixin [PersonMixin] + _temp = 1 + _temp = 2 + name: str = "Alice" + age: int = 10 + +name = "Bob" +age = 10 +base = {name: "Alice", age: 10} +person = Person { + **base + name: name + age: age +} diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/select_attr.k b/test/test_units/test_kclvm/test_types/normal_test_data/select_attr.k new file mode 100644 index 000000000..fc6134693 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/select_attr.k @@ -0,0 +1,18 @@ +schema Person[nameVar: str]: + name: str = nameVar + age: int = 18 + +_person = Person("Alice") +_person.name = "Bob" + +data = { + key1: "value1" + key2: "value2" +} +schemaSelectAttr: int = _person.age +dictSelectAttr: str = data.key1 +strSelectAttr0: any = "abc".upper() +strSelectAttr1: any = strSelectAttr0.upper() + +unionData: {str:} | Person = Person("Bob") +unionDataAttr: int = unionData.age diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/starred_expr.k b/test/test_units/test_kclvm/test_types/normal_test_data/starred_expr.k new file mode 100644 index 000000000..53af8c775 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/starred_expr.k @@ -0,0 +1,14 @@ +schema Person: + name: str + +a = [1, 2, 3] +b: [int] = [*a, 4, 5] +c = {key1: "value1"} +d: {str:str} = {**c, key2: "value2"} +e: [str] = [*d] +f: [any] = [*Undefined] +g: [any] = [*None] +h: {str:} = {**Undefined, key: "value"} +i: {str:} = {**None, key: "value"} +j: {str:} | Person = {name: "name"} +k: {str:} = {**j} diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/subscript.k b/test/test_units/test_kclvm/test_types/normal_test_data/subscript.k new file mode 100644 index 000000000..635799ee9 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/subscript.k @@ -0,0 +1,15 @@ +dataList = [1, 2, 3] +dataList0: int = dataList[0] +dataListSlice: [int] = dataList[0:1:2] +dataDict = {key: "value"} +dataKeyValue: str = dataDict["key"] +dataStr: str = "apple" +dataStrSlice = dataStr[::-1] + "apple"[::-1] +keyAny: any = None +dataDictAny = dataDict[keyAny] + +# Literal subscript +literalSubscript0 = [1][0] +literalSubscript1 = [1, 2][0] +literalSubscript2 = {key: "value"}.key +literalSubscript3 = {key: "value"}.key1 diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/type_alias.k b/test/test_units/test_kclvm/test_types/normal_test_data/type_alias.k new file mode 100644 index 000000000..aed0d442a --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/type_alias.k @@ -0,0 +1,21 @@ +import pkg + +type Color = "Red" | "Yellow" | "Blue" +type Person = pkg.Person + +person: Person = Person { + name = "Alice" + age = 18 +} +color: Color = "Red" + +type Int = int +type FloatUnionInt = float | Int +type BoolUnionFloatUnionInt = bool | FloatUnionInt +type StringUnionBoolUnionFloatUnionInt = str | BoolUnionFloatUnionInt + +a: Int = 1 +b: FloatUnionInt = 2.0 +c: BoolUnionFloatUnionInt = True +d: StringUnionBoolUnionFloatUnionInt = "xx" + diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/type_annotation.k b/test/test_units/test_kclvm/test_types/normal_test_data/type_annotation.k new file mode 100644 index 000000000..c44d7e68f --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/type_annotation.k @@ -0,0 +1,69 @@ +"""Type annotation test""" + +import pkg +import math +import pkg + +schema PersonMixin: + nameUpper: str = name.upper() + +schema Person(Base): + # Mixin + mixin [PersonMixin] + # Index signature + [str]: int|str|{str:} + # Attributes + name: str = "Alice" + + _age: int = 18 + _age += 10 + labels: {str:} = {key1: "value1"} + lebels: {str:} |= {key2: "value2"} + # Check block + check: + _age > 0 + +@deprecated +schema Base(pkg.Person): + data?: str + +print("Hello type annotation check") + +a: int = 1 +b: float = 1.0 +c: float = 1 +d: bool = True +e: bool = False +f: bool = None +g: bool = Undefined +h: [str] = ["Red", "Yellow", "Blue"] +i: [] = ["Red", "Yellow", "Blue"] +j: ["Red"|"Yellow"|"Blue"] = ["Red", "Yellow", "Blue"] +k: [int|str] = [1, 2, "s", "ss"] +l: [{str:}] = [{key: "value"}, {key.key1.key2: "value"}] +m: {str:str|int} = {"key1": "value1", "key2": 2} +n: "Red"|"Yellow"|"Blue" = "Red" +o: Person = Person { + name: "Bob" + age: 10 +} +p: pkg.Person = pkg.Person { + name: "Alice" + age: 18 +} +q: Person = { + name: "Bob" + age: 18 +} +r: Base = Person { + name: "Bob" + age: 18 +} +s: any = 1 +t: any = "s" +u: any = (r + s) +v: str = p.name +w: int = q?.age +x: int|str = 1 if True else "s" +y: [int] = [i ** 2 for i in [1, 2, 3]] +z: {str:} = {"${k}": int(k) for k in ["1", "2", "3"]} diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/type_as.k b/test/test_units/test_kclvm/test_types/normal_test_data/type_as.k new file mode 100644 index 000000000..75281b690 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/type_as.k @@ -0,0 +1,7 @@ +import pkg + +person: pkg.Person = pkg.Person { + name = "Alice" + age = 18 +} +personOther = person as pkg.Person diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/type_dict.k b/test/test_units/test_kclvm/test_types/normal_test_data/type_dict.k new file mode 100644 index 000000000..63f97c404 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/type_dict.k @@ -0,0 +1,22 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema Group: + persons: {str:Person} + +group = Group { + "persons": { + "1": { + "name": { + "firstName": "Alice", + "lastName": "Terry" + }, + "age": 12 + } + } +} diff --git a/test/test_units/test_kclvm/test_types/normal_test_data/unfication_stmt.k b/test/test_units/test_kclvm/test_types/normal_test_data/unfication_stmt.k new file mode 100644 index 000000000..3fbedf3d7 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/normal_test_data/unfication_stmt.k @@ -0,0 +1,8 @@ +schema Config: + name: str + id?: int + +config: Config { + name: "config" + id: 1 +} diff --git a/test/test_units/test_kclvm/test_types/scope_test_data/inherit.k b/test/test_units/test_kclvm/test_types/scope_test_data/inherit.k new file mode 100644 index 000000000..c2f4cc22e --- /dev/null +++ b/test/test_units/test_kclvm/test_types/scope_test_data/inherit.k @@ -0,0 +1,5 @@ +schema Parent: + name: str + +schema Son(Parent): + age: int diff --git a/test/test_units/test_kclvm/test_types/scope_test_data/package.k b/test/test_units/test_kclvm/test_types/scope_test_data/package.k new file mode 100644 index 000000000..a07b5340a --- /dev/null +++ b/test/test_units/test_kclvm/test_types/scope_test_data/package.k @@ -0,0 +1,12 @@ +import scope_test_data.pkg as pkg1 +import scope_test_data.pkg.pkg as pkg2 + +person1 = pkg1.Person { + name: "Alice" + age: 18 +} + +person2 = pkg2.Person { + name: "Alice" + age: 18 +} diff --git a/test/test_units/test_kclvm/test_types/scope_test_data/pkg/pkg/test.k b/test/test_units/test_kclvm/test_types/scope_test_data/pkg/pkg/test.k new file mode 100644 index 000000000..b4c3153b0 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/scope_test_data/pkg/pkg/test.k @@ -0,0 +1,3 @@ +schema Person: + name: str + age: int diff --git a/test/test_units/test_kclvm/test_types/scope_test_data/pkg/test.k b/test/test_units/test_kclvm/test_types/scope_test_data/pkg/test.k new file mode 100644 index 000000000..b4c3153b0 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/scope_test_data/pkg/test.k @@ -0,0 +1,3 @@ +schema Person: + name: str + age: int diff --git a/test/test_units/test_kclvm/test_types/scope_test_data/schema.k b/test/test_units/test_kclvm/test_types/scope_test_data/schema.k new file mode 100644 index 000000000..ed88216af --- /dev/null +++ b/test/test_units/test_kclvm/test_types/scope_test_data/schema.k @@ -0,0 +1,8 @@ +schema Person: + name: str + age: int = 10 + +person: Person = Person { + name: "Alice" + age: 18 +} diff --git a/test/test_units/test_kclvm/test_types/scope_test_data/simple.k b/test/test_units/test_kclvm/test_types/scope_test_data/simple.k new file mode 100644 index 000000000..ab88e0917 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/scope_test_data/simple.k @@ -0,0 +1,30 @@ +schema Base: + id0: str = 'id-base' + id1: str + +schema Person[_name:str, id='id-person'](Base): + name: str = 'kcl' + age: int = 1 + x: 1 | "aaa" | True = 1 + +person = Person(_name="arg-name") { + name: 'Alice' +} + +none0 = Undefined +none1 = None +bool1 = True +bool2 = False +bool3: bool = None +s0 = 'abc' +s1: str = 'abc' +i0 = 123.3 +f0 = 1.5 +x0 = 1 + 1.5 +x1 = x0 + 1 +personOther: Person = Person {} +if person.name == 'Alice': + print("hello KCL") + +rule SomeRule: + x0 > 0 diff --git a/test/test_units/test_kclvm/test_types/test_scope.py b/test/test_units/test_kclvm/test_types/test_scope.py new file mode 100644 index 000000000..2190b4bcf --- /dev/null +++ b/test/test_units/test_kclvm/test_types/test_scope.py @@ -0,0 +1,43 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import unittest +import pathlib +import typing + +import kclvm.kcl.ast as ast +import kclvm.compiler.parser as parser +import kclvm.kcl.types as types + + +path = pathlib.Path(__file__).parent +simple_case_file = str(path.joinpath("scope_test_data/simple.k")) + + +class ScopeTest(unittest.TestCase): + def test_scope_inner_most(self): + program = parser.LoadProgram(simple_case_file) + scope = types.ResolveProgram(program).main_scope + while scope.parent is not None: + scope = scope.parent + inner_most = scope.inner_most(pos=ast.Position(filename=simple_case_file, line=6, column=2)) + self.assertIsNotNone(inner_most) + self.assertTrue(isinstance(inner_most.node, ast.SchemaStmt)) + self.assertTrue(typing.cast(ast.SchemaStmt, inner_most.node).name == "Person") + + def test_scope_contains_pos(self): + program = parser.LoadProgram(simple_case_file) + scope = types.ResolveProgram(program).main_scope + self.assertTrue(isinstance(scope, types.PackageScope)) + # Schema Statement + self.assertTrue(scope.contains_pos(pos=ast.Position(filename=simple_case_file, line=3, column=2))) + # Rule Statement + self.assertTrue(scope.contains_pos(pos=ast.Position(filename=simple_case_file, line=30, column=5))) + for child in scope.children: + if isinstance(child.node, ast.SchemaStmt) and child.node.name == "Base": + self.assertTrue(child.contains_pos(pos=ast.Position(filename=simple_case_file, line=3, column=2))) + elif isinstance(child.node, ast.RuleStmt) and child.node.name == "SomeRule": + self.assertTrue(child.contains_pos(pos=ast.Position(filename=simple_case_file, line=30, column=5))) + self.assertFalse(child.contains_pos(pos=ast.Position(filename=simple_case_file, line=28, column=5))) + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_types/test_type.py b/test/test_units/test_kclvm/test_types/test_type.py new file mode 100644 index 000000000..f4dfecf23 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/test_type.py @@ -0,0 +1,177 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import unittest +import pathlib +from typing import Tuple + +import kclvm.api.object as objpkg +import kclvm.kcl.types as types + + +class TypeTest(unittest.TestCase): + def test_sup(self): + cases = [ + {"types": [], "expected": types.ANY_TYPE}, + {"types": [types.ANY_TYPE], "expected": types.ANY_TYPE}, + {"types": [types.STR_TYPE], "expected": types.STR_TYPE}, + { + "types": [types.STR_TYPE, types.INT_TYPE], + "expected": objpkg.KCLUnionTypeObject( + types=[types.INT_TYPE, types.STR_TYPE] + ), + }, + { + "types": [ + types.STR_TYPE, + types.INT_TYPE, + objpkg.KCLUnionTypeObject(types=[types.INT_TYPE, types.STR_TYPE]), + ], + "expected": objpkg.KCLUnionTypeObject( + types=[types.INT_TYPE, types.STR_TYPE] + ), + }, + { + "types": [types.BOOL_TYPE, types.TRUE_LIT_TYPE], + "expected": types.BOOL_TYPE, + }, + { + "types": [ + objpkg.KCLStringLitTypeObject("Blue"), + objpkg.KCLStringLitTypeObject("Yellow"), + objpkg.KCLStringLitTypeObject("Red"), + ], + "expected": objpkg.KCLUnionTypeObject( + types=[ + objpkg.KCLStringLitTypeObject("Blue"), + objpkg.KCLStringLitTypeObject("Yellow"), + objpkg.KCLStringLitTypeObject("Red"), + ] + ), + }, + { + "types": [ + objpkg.KCLListTypeObject( + objpkg.KCLUnionTypeObject( + [ + objpkg.KCLIntLitTypeObject(1), + objpkg.KCLIntLitTypeObject(2), + ] + ) + ), + objpkg.KCLListTypeObject( + objpkg.KCLUnionTypeObject( + [ + objpkg.KCLIntLitTypeObject(3), + objpkg.KCLIntLitTypeObject(4), + ] + ) + ), + ], + "expected": objpkg.KCLUnionTypeObject( + [ + objpkg.KCLListTypeObject( + objpkg.KCLUnionTypeObject( + [ + objpkg.KCLIntLitTypeObject(1), + objpkg.KCLIntLitTypeObject(2), + ] + ) + ), + objpkg.KCLListTypeObject( + objpkg.KCLUnionTypeObject( + [ + objpkg.KCLIntLitTypeObject(3), + objpkg.KCLIntLitTypeObject(4), + ] + ), + ), + ] + ), + }, + { + "types": [ + objpkg.KCLUnionTypeObject( + [ + types.STR_TYPE, + types.DICT_STR_STR_TYPE, + ] + ), + types.DICT_ANY_ANY_TYPE, + ], + "expected": objpkg.KCLUnionTypeObject( + [ + types.STR_TYPE, + types.DICT_ANY_ANY_TYPE, + ] + ), + }, + ] + for case in cases: + type_list, expected = case["types"], case["expected"] + got = types.sup(type_list) + self.assertEqual( + got, + expected, + msg=f"assert error on type list {type_list}, got {got}", + ) + + def test_assignale_to(self): + cases = [ + {"type1": types.NONE_TYPE, "type2": types.ANY_TYPE, "expected": True}, + {"type1": types.ANY_TYPE, "type2": types.ANY_TYPE, "expected": True}, + {"type1": types.ANY_TYPE, "type2": types.INT_TYPE, "expected": True}, + {"type1": types.INT_TYPE, "type2": types.ANY_TYPE, "expected": True}, + {"type1": types.INT_TYPE, "type2": types.FLOAT_TYPE, "expected": True}, + { + "type1": objpkg.KCLStringLitTypeObject("ss"), + "type2": types.STR_TYPE, + "expected": True, + }, + { + "type1": types.INT_TYPE, + "type2": objpkg.KCLUnionTypeObject( + types=[types.INT_TYPE, types.STR_TYPE] + ), + "expected": True, + }, + { + "type1": types.DICT_STR_STR_TYPE, + "type2": types.DICT_STR_ANY_TYPE, + "expected": True, + }, + {"type1": types.VOID_TYPE, "type2": types.ANY_TYPE, "expected": False}, + {"type1": types.FLOAT_TYPE, "type2": types.INT_TYPE, "expected": False}, + { + "type1": objpkg.KCLSchemaTypeObject( + name="Person", + runtime_type="runtime_type_543fa9efacae37b4c698a94214cdf779_Person", + ), + "type2": objpkg.KCLSchemaTypeObject( + name="Person", + runtime_type="runtime_type_543fa9efacae37b4c698a94214cdf779_Person", + ), + "expected": True, + }, + ] + for case in cases: + type1, type2, expected = case["type1"], case["type2"], case["expected"] + self.assertEqual( + types.assignable_to(type1, type2), + expected, + msg=f"assert error on types {type1} and {type2}", + ) + + def test_type_to_kcl_type_annotation_str_invalid(self): + with self.assertRaises(Exception): + types.type_to_kcl_type_annotation_str(None) + + self.assertEqual( + types.type_to_kcl_type_annotation_str( + objpkg.KCLFunctionTypeObject("test", None, None, None) + ), + "", + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_types/test_type_checker.py b/test/test_units/test_kclvm/test_types/test_type_checker.py new file mode 100644 index 000000000..4069bf930 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/test_type_checker.py @@ -0,0 +1,207 @@ +# Copyright 2021 The KCL Authors. All rights reserved. +import ast +import unittest +import pathlib + +import kclvm.compiler.parser as parser +import kclvm.compiler.check.check_type as check_type +import kclvm.compiler.extension.builtin as builtin +import kclvm.api.object as objpkg +import kclvm.kcl.types as types +import kclvm.kcl.error as kcl_error +import kclvm.internal.util.check_utils as check_utils + +path = pathlib.Path(__file__).parent +invalid_case_path_list = [ + "assert", + "attr_op", + "calculation", + "final", + "for_comp", + "func_call", + "if", + "import", + "loop", + "module", + "schema", + "select_attr", + "subscript", + "type_alias", + "type_annotation", + "type_as", + "unificaion", + "unique", + "unpack", +] +simple_case_file = str(path.joinpath("scope_test_data/simple.k")) +schema_case_file = str(path.joinpath("scope_test_data/schema.k")) +package_case_file = str(path.joinpath("scope_test_data/package.k")) +normal_cases = list(path.joinpath("normal_test_data").glob("*.k")) +err_collect_cases = list(path.joinpath("err_collect_test_data").glob("*.k")) +invalid_cases = [file for p in invalid_case_path_list for file in path.joinpath(f"invalid_test_data/{p}").glob("*.k")] + + +class TypeCheckerTest(unittest.TestCase): + + def test_internal_bug(self): + check_utils.CHECK_MODE = True + program = parser.LoadProgram(simple_case_file) + tc = types.checker.TypeChecker(program) + tc.config_expr_context.append("invalid_expr") + with self.assertRaises( + AssertionError, + msg=f"Here is unreachable unless a bug occurs" + ): + tc.find_schema_attr_obj_from_schema_expr_stack("test") + + def test_switch_config_expr_context_by_key(self): + check_utils.CHECK_MODE = True + program = parser.LoadProgram(simple_case_file) + tc = types.checker.TypeChecker(program) + self.assertEqual(0, tc.switch_config_expr_context_by_key(ast.AST())) + + def test_clear_config_expr_context(self): + check_utils.CHECK_MODE = True + program = parser.LoadProgram(simple_case_file) + tc = types.checker.TypeChecker(program) + tc.config_expr_context.append("invalid_expr") + self.assertEqual(1, len(tc.config_expr_context)) + tc.config_expr_context.append("invalid_expr") + tc.config_expr_context.append("invalid_expr") + self.assertEqual(3, len(tc.config_expr_context)) + tc.clear_config_expr_context(clear_all=True) + self.assertEqual(0, len(tc.config_expr_context)) + + def test_type_checker_simple_case(self): + program = parser.LoadProgram(simple_case_file) + prog_scope = types.ResolveProgram(program) + scope = prog_scope.main_scope + pkgpaths = prog_scope.pkgpaths + base_schema_type = scope.elems["Base"].type.schema_type + person_schema_type = scope.elems["Person"].type.schema_type + self.assertListEqual(pkgpaths, ["__main__"]) + self.assertEqual(scope.elems["Base"].type.schema_type.type_str(), "Base") + self.assertEqual(scope.elems["Person"].type.schema_type.type_str(), "Person") + self.assertEqual(scope.parent, types.BUILTIN_SCOPE) + self.assertEqual(len(scope.parent.elems), len(builtin.BUILTIN_FUNCTIONS)) + self.assertIsInstance(base_schema_type, objpkg.KCLSchemaTypeObject) + self.assertIsInstance(person_schema_type, objpkg.KCLSchemaTypeObject) + self.assertEqual(base_schema_type.name, "Base") + self.assertEqual(person_schema_type.name, "Person") + self.assertEqual(base_schema_type.base, None) + self.assertIsInstance(person_schema_type.base, objpkg.KCLSchemaTypeObject) + self.assertEqual(person_schema_type.base.name, "Base") + + def test_type_checker_schema_case(self): + program = parser.LoadProgram(schema_case_file) + scope = types.ResolveProgram(program).main_scope + person_schema_type = scope.elems["Person"].type.schema_type + self.assertIsInstance(person_schema_type, objpkg.KCLSchemaTypeObject) + + def test_type_checker_package_case(self): + program = parser.LoadProgram(package_case_file) + scope = types.ResolveProgram(program).main_scope + person1_obj = scope.elems["person1"].type + person2_obj = scope.elems["person2"].type + self.assertIsInstance(person1_obj, objpkg.KCLSchemaTypeObject) + self.assertIsInstance(person2_obj, objpkg.KCLSchemaTypeObject) + self.assertEqual(package_case_file in scope.file_begin_position_map, True) + self.assertEqual(scope.file_begin_position_map[package_case_file].filename, package_case_file) + self.assertEqual(scope.file_begin_position_map[package_case_file].line, 1) + self.assertEqual(scope.file_begin_position_map[package_case_file].column, 1) + self.assertEqual(package_case_file in scope.file_end_position_map, True) + self.assertEqual(scope.file_end_position_map[package_case_file].filename, package_case_file) + self.assertEqual(scope.file_end_position_map[package_case_file].line, 12) + self.assertEqual(scope.file_end_position_map[package_case_file].column, 2) + self.assertEqual(person1_obj.pkgpath, "scope_test_data.pkg") + self.assertEqual(person2_obj.pkgpath, "scope_test_data.pkg.pkg") + + for person_obj in [person1_obj, person2_obj]: + self.assertTrue("__settings__" in person_obj.attr_obj_map) + self.assertTrue("name" in person_obj.attr_obj_map) + self.assertTrue("age" in person_obj.attr_obj_map) + self.assertEqual(types.DICT_STR_ANY_TYPE, person_obj.attr_obj_map["__settings__"].attr_type) + self.assertEqual(types.STR_TYPE, person_obj.attr_obj_map["name"].attr_type) + self.assertEqual(types.INT_TYPE, person_obj.attr_obj_map["age"].attr_type) + + def test_type_checker_normal_case(self): + for case in normal_cases: + program = parser.LoadProgram(case) + types.ResolveProgram(program) + + def test_type_checker_invalid_case(self): + for case in invalid_cases: + with self.assertRaises( + kcl_error.KCLException, msg=f"case: {case}" + ): + program = parser.LoadProgram(case) + types.ResolveProgram(program) + + def test_type_checker_err_collect_case(self): + for case in err_collect_cases: + program = parser.LoadProgram(case) + types.ResolveProgram(program, config=types.CheckConfig( + raise_err=False, + )) + + def test_check_type(self): + cases = [ + # True cases + {"value": None, "expected_type": "int", "result": True}, + {"value": objpkg.Undefined, "expected_type": "int", "result": True}, + {"value": 1, "expected_type": "float", "result": True}, + {"value": 1, "expected_type": "", "result": True}, + {"value": 1, "expected_type": "int", "result": True}, + {"value": 1.1, "expected_type": "float", "result": True}, + {"value": "s", "expected_type": "str", "result": True}, + {"value": True, "expected_type": "bool", "result": True}, + {"value": [1, 2, 3], "expected_type": "[int]", "result": True}, + {"value": {"key": "value"}, "expected_type": "{str:}", "result": True}, + # False cases + {"value": 1, "expected_type": "str", "result": False}, + {"value": 1.1, "expected_type": "int", "result": False}, + {"value": "s", "expected_type": "int", "result": False}, + {"value": True, "expected_type": "str", "result": False}, + {"value": [1, 2, 3], "expected_type": "[str]", "result": False}, + {"value": {"key": "value"}, "expected_type": "{str:int}", "result": False}, + ] + for case in cases: + value = objpkg.to_kcl_obj(case["value"]) + expected_type = case["expected_type"] + result = case["result"] + self.assertEqual( + check_type.check_type(value, expected_type)[0], + result, + msg=f"value: {value}, expected_type: {expected_type}" + ) + + def test_check_type_builtin(self): + cases = [ + # True cases + {"value": 1, "expected_types": [], "result": True}, + {"value": 1, "expected_types": ["float"], "result": True}, + {"value": 1, "expected_types": ["int"], "result": True}, + {"value": 1, "expected_types": ["str", "int"], "result": True}, + {"value": 1.1, "expected_types": ["float"], "result": True}, + {"value": 1.1, "expected_types": ["float", "int"], "result": True}, + {"value": "s", "expected_types": ["str"], "result": True}, + {"value": True, "expected_types": ["bool"], "result": True}, + # False cases + {"value": 1, "expected_types": ["str"], "result": False}, + {"value": 1.1, "expected_types": ["int"], "result": False}, + {"value": "s", "expected_types": ["int"], "result": False}, + {"value": True, "expected_types": ["str"], "result": False}, + ] + for case in cases: + value = objpkg.to_kcl_obj(case["value"]) + expected_types = case["expected_types"] + result = case["result"] + self.assertEqual( + check_type.check_type_builtin(value, expected_types, should_raise_err=False), + result, + msg=f"value: {value}, expected_types: {expected_types}" + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_types/test_type_convension.py b/test/test_units/test_kclvm/test_types/test_type_convension.py new file mode 100644 index 000000000..8dde91532 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/test_type_convension.py @@ -0,0 +1,67 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import unittest +import pathlib + +import kclvm.kcl.error as kcl_error +import kclvm.api.object as objpkg +import kclvm.kcl.types as types + + +class TypeConvensionTest(unittest.TestCase): + def test_type_convert(self): + cases = [ + { + "obj": objpkg.NONE_INSTANCE, + "tpe": objpkg.KCLIntTypeObject(), + "expected": objpkg.NONE_INSTANCE, + }, + { + "obj": objpkg.UNDEFINED_INSTANCE, + "tpe": objpkg.KCLIntTypeObject(), + "expected": objpkg.UNDEFINED_INSTANCE, + }, + { + "obj": objpkg.KCLIntObject(1), + "tpe": types.ANY_TYPE, + "expected": objpkg.KCLIntObject(1), + }, + { + "obj": objpkg.KCLDictObject(value={"key": objpkg.KCLStringObject("s")}), + "tpe": types.DICT_STR_STR_TYPE, + "expected": objpkg.KCLDictObject(value={"key": objpkg.KCLStringObject("s")}), + }, + { + "obj": objpkg.KCLListObject(items=[objpkg.KCLStringObject("s")]), + "tpe": objpkg.KCLListTypeObject(objpkg.KCLStringTypeObject()), + "expected": objpkg.KCLListObject(items=[objpkg.KCLStringObject("s")]), + }, + ] + for case in cases: + obj, tpe, expected = case["obj"], case["tpe"], case["expected"] + self.assertEqual(types.type_convert(obj, tpe), expected) + + def test_type_convert_failed(self): + cases = [ + {"obj": objpkg.KCLIntObject(0), "tpe": objpkg.KCLStringTypeObject()}, + {"obj": objpkg.KCLStringObject("s"), "tpe": objpkg.KCLIntTypeObject()}, + ] + for case in cases: + obj, tpe = case["obj"], case["tpe"] + with self.assertRaises(kcl_error.EvaluationError): + types.type_convert(obj, tpe) + + def test_type_convert_invalid_params(self): + cases = [ + {"obj": None, "tpe": None}, + {"obj": objpkg.KCLIntObject(0), "tpe": None}, + {"obj": None, "tpe": objpkg.KCLIntTypeObject()}, + ] + for case in cases: + obj, tpe = case["obj"], case["tpe"] + with self.assertRaises(ValueError): + types.type_convert(obj, tpe) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_types/test_type_parser.py b/test/test_units/test_kclvm/test_types/test_type_parser.py new file mode 100644 index 000000000..92afae7a8 --- /dev/null +++ b/test/test_units/test_kclvm/test_types/test_type_parser.py @@ -0,0 +1,338 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import unittest +import pathlib +from typing import Tuple + +import kclvm.api.object as objpkg +import kclvm.api.object.internal.common as common +import kclvm.kcl.types as types +import kclvm.kcl.types.type_parser as type_parser + + +class TypeParserTest(unittest.TestCase): + def test_is_lit_type(self): + cases = [ + {"type_str": "1", "expected": True}, + {"type_str": "1.1", "expected": True}, + {"type_str": "1.e+0", "expected": True}, + {"type_str": ".1", "expected": True}, + {"type_str": "True", "expected": True}, + {"type_str": "False", "expected": True}, + {"type_str": "'s'", "expected": True}, + {"type_str": "\"s\"", "expected": True}, + {"type_str": "true", "expected": False}, + {"type_str": "false", "expected": False}, + {"type_str": "\"s", "expected": False}, + {"type_str": "'s", "expected": False}, + {"type_str": "schema", "expected": False}, + {"type_str": "pkg.schema", "expected": False}, + ] + for case in cases: + type_str, expected = case["type_str"], case["expected"] + self.assertEqual( + type_parser.is_lit_type_str(type_str), expected + ) + + def test_is_type_union(self): + cases = [ + {"type_str": "A|B|C", "expected": True}, + {"type_str": "'123'|'456'|'789'", "expected": True}, + {"type_str": "'|'|'||'|'|||'", "expected": True}, + {"type_str": '"aa\\"ab|"|"aa\\"abccc"', "expected": True}, + {"type_str": '["|"]|""', "expected": True}, + {"type_str": '{str:"|"}|"|"', "expected": True}, + {"type_str": '"aa\\"ab|"', "expected": False}, + {"type_str": '"|aa\\"ab|"', "expected": False}, + ] + for case in cases: + type_str, expected = case["type_str"], case["expected"] + self.assertEqual( + common.is_type_union(type_str), expected + ) + + def test_split_type_union(self): + cases = [ + {"type_str": "A|B|C", "expected": ["A", "B", "C"]}, + {"type_str": "'123'|'456'|'789'", "expected": ["'123'", "'456'", "'789'"]}, + {"type_str": "'|'|'||'|'|||'", "expected": ["'|'", "'||'", "'|||'"]}, + {"type_str": '["|"]|""', "expected": ['["|"]', '""']}, + {"type_str": '{str:"|"}|"|"', "expected": ['{str:"|"}', '"|"']}, + ] + for case in cases: + type_str, expected = case["type_str"], case["expected"] + self.assertEqual( + common.split_type_union(type_str), expected + ) + + def test_parse_type_str_normal(self): + cases = [ + # Common built-in types + {"type_str": None, "expected": types.ANY_TYPE}, + {"type_str": "", "expected": types.ANY_TYPE}, + {"type_str": "any", "expected": types.ANY_TYPE}, + {"type_str": "any", "expected": objpkg.KCLAnyTypeObject()}, + {"type_str": "int", "expected": types.INT_TYPE}, + {"type_str": "float", "expected": types.FLOAT_TYPE}, + {"type_str": "str", "expected": types.STR_TYPE}, + {"type_str": "bool", "expected": types.BOOL_TYPE}, + # Dict types + { + "type_str": "{:}", + "expected": objpkg.KCLDictTypeObject( + key_type=types.ANY_TYPE, value_type=types.ANY_TYPE + ), + }, + { + "type_str": "{str:}", + "expected": objpkg.KCLDictTypeObject( + key_type=types.STR_TYPE, value_type=types.ANY_TYPE + ), + }, + { + "type_str": "{str:any}", + "expected": objpkg.KCLDictTypeObject( + key_type=types.STR_TYPE, value_type=types.ANY_TYPE + ), + }, + { + "type_str": "{str:str}", + "expected": objpkg.KCLDictTypeObject( + key_type=types.STR_TYPE, value_type=types.STR_TYPE + ), + }, + { + "type_str": "{str:{str:str}}", + "expected": objpkg.KCLDictTypeObject( + key_type=types.STR_TYPE, + value_type=objpkg.KCLDictTypeObject( + key_type=types.STR_TYPE, + value_type=types.STR_TYPE, + ), + ), + }, + { + "type_str": "{str:[str]}", + "expected": objpkg.KCLDictTypeObject( + key_type=types.STR_TYPE, + value_type=objpkg.KCLListTypeObject(item_type=types.STR_TYPE), + ), + }, + # List types + { + "type_str": "[]", + "expected": objpkg.KCLListTypeObject(item_type=types.ANY_TYPE), + }, + { + "type_str": "[any]", + "expected": objpkg.KCLListTypeObject(item_type=types.ANY_TYPE), + }, + { + "type_str": "[str]", + "expected": objpkg.KCLListTypeObject(item_type=types.STR_TYPE), + }, + { + "type_str": "[{str:}]", + "expected": objpkg.KCLListTypeObject(item_type=types.DICT_STR_ANY_TYPE), + }, + { + "type_str": "[{str:str}]", + "expected": objpkg.KCLListTypeObject(item_type=types.DICT_STR_STR_TYPE), + }, + # Union types + { + "type_str": "str|int", + "expected": objpkg.KCLUnionTypeObject( + types=[ + types.INT_TYPE, + types.STR_TYPE, + ] + ), + }, + { + "type_str": "int|str", + "expected": objpkg.KCLUnionTypeObject( + types=[ + types.INT_TYPE, + types.STR_TYPE, + ] + ), + }, + { + "type_str": "int|str|int", + "expected": objpkg.KCLUnionTypeObject( + types=[ + types.INT_TYPE, + types.STR_TYPE, + ] + ), + }, + { + "type_str": "int|str|int|str", + "expected": objpkg.KCLUnionTypeObject( + types=[ + types.INT_TYPE, + types.STR_TYPE, + ] + ), + }, + { + "type_str": "{str:int|str}", + "expected": objpkg.KCLDictTypeObject( + key_type=types.STR_TYPE, + value_type=objpkg.KCLUnionTypeObject( + types=[ + types.INT_TYPE, + types.STR_TYPE, + ] + ), + ), + }, + { + "type_str": "{str|int:int|str}", + "expected": objpkg.KCLDictTypeObject( + key_type=objpkg.KCLUnionTypeObject( + types=[ + types.INT_TYPE, + types.STR_TYPE, + ] + ), + value_type=objpkg.KCLUnionTypeObject( + types=[ + types.INT_TYPE, + types.STR_TYPE, + ] + ), + ), + }, + { + "type_str": "[int|str]", + "expected": objpkg.KCLListTypeObject( + item_type=objpkg.KCLUnionTypeObject( + types=[ + types.INT_TYPE, + types.STR_TYPE, + ] + ), + ), + }, + # Literal types + {"type_str": "True", "expected": types.TRUE_LIT_TYPE}, + {"type_str": "False", "expected": types.FALSE_LIT_TYPE}, + { + "type_str": "123", + "expected": objpkg.KCLIntLitTypeObject(value=123), + }, + { + "type_str": "123.0", + "expected": objpkg.KCLFloatLitTypeObject(value=123.0), + }, + { + "type_str": "'ss'", + "expected": objpkg.KCLStringLitTypeObject(value="ss"), + }, + { + "type_str": '"ss"', + "expected": objpkg.KCLStringLitTypeObject(value="ss"), + }, + { + "type_str": "'Red'|'Yellow'|'Blue'", + "expected": objpkg.KCLUnionTypeObject( + types=[ + objpkg.KCLStringLitTypeObject(value="Red"), + objpkg.KCLStringLitTypeObject(value="Yellow"), + objpkg.KCLStringLitTypeObject(value="Blue"), + ] + ), + }, + { + "type_str": "1|2|3", + "expected": objpkg.KCLUnionTypeObject( + types=[ + objpkg.KCLIntLitTypeObject(value=1), + objpkg.KCLIntLitTypeObject(value=2), + objpkg.KCLIntLitTypeObject(value=3), + ] + ), + }, + # Partially ordered types + { + "type_str": "str|'ss'|int", + "expected": objpkg.KCLUnionTypeObject( + types=[ + types.INT_TYPE, + types.STR_TYPE, + ] + ), + }, + { + "type_str": "str|'ss'|int|1|bool|True", + "expected": objpkg.KCLUnionTypeObject( + types=[ + types.BOOL_TYPE, + types.INT_TYPE, + types.STR_TYPE, + ] + ), + }, + { + "type_str": "1|1|2", + "expected": objpkg.KCLUnionTypeObject( + types=[ + objpkg.KCLIntLitTypeObject(value=1), + objpkg.KCLIntLitTypeObject(value=2), + ] + ), + }, + { + "type_str": "2|1|1", + "expected": objpkg.KCLUnionTypeObject( + types=[ + objpkg.KCLIntLitTypeObject(value=2), + objpkg.KCLIntLitTypeObject(value=1), + ] + ), + }, + { + "type_str": "{str:}|{str:str}", + "expected": types.DICT_STR_ANY_TYPE, + }, + { + "type_str": "[]|[str]", + "expected": objpkg.KCLListTypeObject(item_type=types.ANY_TYPE), + }, + { + "type_str": '1|"aaa"|True', + "expected": objpkg.KCLUnionTypeObject( + types=[ + types.TRUE_LIT_TYPE, + objpkg.KCLIntLitTypeObject(1), + objpkg.KCLStringLitTypeObject("aaa"), + ] + ), + }, + ] + for case in cases: + type_str, expected = case["type_str"], case["expected"] + self.assertEqual( + types.parse_type_str(type_str), + expected, + msg=f"Assert error type: {type_str}", + ) + + def test_parse_type_str_invalid(self): + cases = [ + {"type_str": True}, + {"type_str": 1}, + {"type_str": []}, + {"type_str": {}}, + {"type_str": ()}, + ] + for case in cases: + type_str = case["type_str"] + with self.assertRaises(ValueError): + types.parse_type_str(type_str) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_types/test_type_walker.py b/test/test_units/test_kclvm/test_types/test_type_walker.py new file mode 100644 index 000000000..e9ae8703a --- /dev/null +++ b/test/test_units/test_kclvm/test_types/test_type_walker.py @@ -0,0 +1,86 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import unittest +import pathlib +from typing import Tuple + +import kclvm.api.object as objpkg +import kclvm.kcl.types as types +import kclvm.kcl.types.walker as type_walker + + +class TypeTest(unittest.TestCase): + def test_type_walker_convert_int_to_str(self): + def walk_fn(t): + if isinstance(t, objpkg.KCLIntTypeObject): + return objpkg.KCLStringTypeObject() + return t + + cases = [ + {"type": types.INT_TYPE, "expected": types.STR_TYPE}, + { + "type": objpkg.KCLListTypeObject(types.ANY_TYPE), + "expected": objpkg.KCLListTypeObject(types.ANY_TYPE), + }, + { + "type": objpkg.KCLListTypeObject(types.STR_TYPE), + "expected": objpkg.KCLListTypeObject(types.STR_TYPE), + }, + { + "type": objpkg.KCLListTypeObject(types.INT_TYPE), + "expected": objpkg.KCLListTypeObject(types.STR_TYPE), + }, + { + "type": objpkg.KCLUnionTypeObject([types.STR_TYPE, types.INT_TYPE]), + "expected": objpkg.KCLUnionTypeObject( + types=[types.STR_TYPE, types.STR_TYPE] + ), + }, + { + "type": objpkg.KCLUnionTypeObject( + [ + types.STR_TYPE, + types.INT_TYPE, + objpkg.KCLUnionTypeObject( + types=[types.INT_TYPE, types.STR_TYPE] + ), + ] + ), + "expected": objpkg.KCLUnionTypeObject( + [ + types.STR_TYPE, + types.STR_TYPE, + objpkg.KCLUnionTypeObject( + types=[types.STR_TYPE, types.STR_TYPE] + ), + ] + ), + }, + { + "type": objpkg.KCLUnionTypeObject( + [types.BOOL_TYPE, types.TRUE_LIT_TYPE] + ), + "expected": objpkg.KCLUnionTypeObject( + [types.BOOL_TYPE, types.TRUE_LIT_TYPE] + ), + }, + { + "type": objpkg.KCLDictTypeObject( + key_type=types.INT_TYPE, value_type=types.INT_TYPE + ), + "expected": objpkg.KCLDictTypeObject( + key_type=types.STR_TYPE, value_type=types.STR_TYPE + ), + }, + ] + for case in cases: + tpe, expected = case["type"], case["expected"] + self.assertEqual( + type_walker.WalkType(tpe, walk_fn), + expected, + msg=f"assert error on type {tpe}, and the expected is {expected}", + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_unification/test_data/collection_if.code b/test/test_units/test_kclvm/test_unification/test_data/collection_if.code new file mode 100644 index 000000000..d0a8e22df --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/collection_if.code @@ -0,0 +1,10 @@ +schema Person: + name: str + age: int + +alice = Person { + if True: + name: "Alice" + if True: + age = 18 +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/collection_if.input b/test/test_units/test_kclvm/test_unification/test_data/collection_if.input new file mode 100644 index 000000000..091888d3a --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/collection_if.input @@ -0,0 +1,13 @@ +schema Person: + name: str + age: int + +alice = Person { + if True: + name: "Alice" +} + +alice = Person { + if True: + age = 18 +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/collection_if.vertex b/test/test_units/test_kclvm/test_unification/test_data/collection_if.vertex new file mode 100644 index 000000000..16a23cb77 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/collection_if.vertex @@ -0,0 +1,14 @@ +name: @root +is_unique: False +adjs: + name: alice + is_unique: False + config_name: Person + adjs: + name: None + is_unique: False + value: + + name: None + is_unique: False + value: diff --git a/test/test_units/test_kclvm/test_unification/test_data/empty.code b/test/test_units/test_kclvm/test_unification/test_data/empty.code new file mode 100644 index 000000000..75037e666 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/empty.code @@ -0,0 +1 @@ +config = Config {} diff --git a/test/test_units/test_kclvm/test_unification/test_data/empty.input b/test/test_units/test_kclvm/test_unification/test_data/empty.input new file mode 100644 index 000000000..24e854d0d --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/empty.input @@ -0,0 +1,10 @@ +schema Config: + name?: str + args?: [str] + labels?: {str:} + +config = Config {} + +config = {} + +config = {} diff --git a/test/test_units/test_kclvm/test_unification/test_data/empty.vertex b/test/test_units/test_kclvm/test_unification/test_data/empty.vertex new file mode 100644 index 000000000..578627739 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/empty.vertex @@ -0,0 +1,7 @@ +name: @root +is_unique: False +adjs: + name: config + is_unique: False + config_name: Config + value: config: > diff --git a/test/test_units/test_kclvm/test_unification/test_data/insert.code b/test/test_units/test_kclvm/test_unification/test_data/insert.code new file mode 100644 index 000000000..33ba46548 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/insert.code @@ -0,0 +1,4 @@ +person = Person { + info = {"key2": "value2"} + hc[0] += 4 +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/insert.input b/test/test_units/test_kclvm/test_unification/test_data/insert.input new file mode 100644 index 000000000..a6417714b --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/insert.input @@ -0,0 +1,11 @@ +schema Person: + info: {str:} = {"key1": "value1"} + hc: [int] = [1, 2, 3] + +person = Person { + info = {"key2": "value2"} +} + +person = Person { + hc[0] += 4 +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/insert.vertex b/test/test_units/test_kclvm/test_unification/test_data/insert.vertex new file mode 100644 index 000000000..8351028ca --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/insert.vertex @@ -0,0 +1,17 @@ +name: @root +is_unique: False +adjs: + name: person + is_unique: False + config_name: Person + adjs: + name: info + is_unique: False + adjs: + name: key2 + is_unique: False + value: + + name: + is_unique: False + value: diff --git a/test/test_units/test_kclvm/test_unification/test_data/int_dict.code b/test/test_units/test_kclvm/test_unification/test_data/int_dict.code new file mode 100644 index 000000000..9560f5f3f --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/int_dict.code @@ -0,0 +1,11 @@ +group = Group { + persons: { + 1: { + name: { + firstName: "Alice" + lastName: "Terry" + } + age: 12 + } + } +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/int_dict.input b/test/test_units/test_kclvm/test_unification/test_data/int_dict.input new file mode 100644 index 000000000..baae380e9 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/int_dict.input @@ -0,0 +1,30 @@ +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema Group: + persons: {int:Person} + +group = Group { + "persons": { + 1: { + "name": { + "firstName": "Alice", + }, + "age": 12 + } + } +} +group = Group { + "persons": { + 1: { + "name": { + "lastName": "Terry" + }, + } + } +} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_unification/test_data/int_dict.vertex b/test/test_units/test_kclvm/test_unification/test_data/int_dict.vertex new file mode 100644 index 000000000..76a48dcc1 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/int_dict.vertex @@ -0,0 +1,27 @@ +name: @root +is_unique: False +adjs: + name: group + is_unique: False + config_name: Group + adjs: + name: persons + is_unique: False + adjs: + name: 1 + is_unique: False + adjs: + name: name + is_unique: False + adjs: + name: firstName + is_unique: False + value: + + name: lastName + is_unique: False + value: + + name: age + is_unique: False + value: diff --git a/test/test_units/test_kclvm/test_unification/test_data/nest_declaration.code b/test/test_units/test_kclvm/test_unification/test_data/nest_declaration.code new file mode 100644 index 000000000..8fca0c7b7 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/nest_declaration.code @@ -0,0 +1,6 @@ +config = Config { + args: ["kcl", "main.k"] + labels: { + key1: "value1" + } +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/nest_declaration.input b/test/test_units/test_kclvm/test_unification/test_data/nest_declaration.input new file mode 100644 index 000000000..ec15d23cd --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/nest_declaration.input @@ -0,0 +1,11 @@ +schema Config: + name?: str + args: [str] + labels: {str:} + +config = Config { + args: ["kcl", "main.k"] + labels.key1: "value1" +} + +config.name = "config" diff --git a/test/test_units/test_kclvm/test_unification/test_data/nest_declaration.vertex b/test/test_units/test_kclvm/test_unification/test_data/nest_declaration.vertex new file mode 100644 index 000000000..3ecf1d6ad --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/nest_declaration.vertex @@ -0,0 +1,17 @@ +name: @root +is_unique: False +adjs: + name: config + is_unique: False + config_name: Config + adjs: + name: args + is_unique: False + value: , ]> + + name: labels + is_unique: False + adjs: + name: key1 + is_unique: False + value: diff --git a/test/test_units/test_kclvm/test_unification/test_data/nest_var_0.code b/test/test_units/test_kclvm/test_unification/test_data/nest_var_0.code new file mode 100644 index 000000000..ce2a49612 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/nest_var_0.code @@ -0,0 +1,8 @@ +config = Config { + args: ["kcl", "main.k"] + labels: { + key1: "value1" + key2: "value2" + key3: "value3" + } +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/nest_var_0.input b/test/test_units/test_kclvm/test_unification/test_data/nest_var_0.input new file mode 100644 index 000000000..7b945cc6e --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/nest_var_0.input @@ -0,0 +1,20 @@ +schema Config: + args: [str] + labels: {str:} + +config = Config { + args: ["kcl", "main.k"] + labels.key1: "value1" +} + +config = Config { + labels: { + key2: "value2" + } +} + +config = Config { + "labels": { + "key3": "value3" + } +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/nest_var_0.vertex b/test/test_units/test_kclvm/test_unification/test_data/nest_var_0.vertex new file mode 100644 index 000000000..4cefdeb3e --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/nest_var_0.vertex @@ -0,0 +1,25 @@ +name: @root +is_unique: False +adjs: + name: config + is_unique: False + config_name: Config + adjs: + name: args + is_unique: False + value: , ]> + + name: labels + is_unique: False + adjs: + name: key1 + is_unique: False + value: + + name: key2 + is_unique: False + value: + + name: key3 + is_unique: False + value: diff --git a/test/test_units/test_kclvm/test_unification/test_data/nest_var_1.code b/test/test_units/test_kclvm/test_unification/test_data/nest_var_1.code new file mode 100644 index 000000000..aed22117f --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/nest_var_1.code @@ -0,0 +1,13 @@ +config = Config { + args: ["kcl", "main.k"] + labels: { + key1: "value1" + key2: "value2" + } + name: { + name: { + name: "name" + data: 1 + } + } +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/nest_var_1.input b/test/test_units/test_kclvm/test_unification/test_data/nest_var_1.input new file mode 100644 index 000000000..acbcbbec5 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/nest_var_1.input @@ -0,0 +1,24 @@ +schema Name0: + name?: str + data?: int + +schema Name: + name?: Name0 + +schema Config: + args?: [str] + labels: {str:} + name: Name + +config = Config { + args: ["kcl", "main.k"] + labels.key1: "value1" + name.name.name: "name" +} + +config = Config { + labels: { + key2: "value2" + } + name.name: Name0 {data: 1} +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/nest_var_1.vertex b/test/test_units/test_kclvm/test_unification/test_data/nest_var_1.vertex new file mode 100644 index 000000000..eb042c96f --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/nest_var_1.vertex @@ -0,0 +1,35 @@ +name: @root +is_unique: False +adjs: + name: config + is_unique: False + config_name: Config + adjs: + name: args + is_unique: False + value: , ]> + + name: labels + is_unique: False + adjs: + name: key1 + is_unique: False + value: + + name: key2 + is_unique: False + value: + + name: name + is_unique: False + adjs: + name: name + is_unique: False + adjs: + name: name + is_unique: False + value: + + name: data + is_unique: False + value: diff --git a/test/test_units/test_kclvm/test_unification/test_data/override.code b/test/test_units/test_kclvm/test_unification/test_data/override.code new file mode 100644 index 000000000..5c6186393 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/override.code @@ -0,0 +1,10 @@ +config = Config { + name: "config1" + name: "config2" + args: ["kcl", "main.k"] + args: ["kcl2"] + labels: { + key1: "value1" + key2: "value2" + } +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/override.input b/test/test_units/test_kclvm/test_unification/test_data/override.input new file mode 100644 index 000000000..168491818 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/override.input @@ -0,0 +1,17 @@ +schema Config: + """Config Definition""" + name?: str + args: [str] + labels: {str:} + +config = Config { + name: "config1" # Literal Initial + args: ["kcl", "main.k"] # List Initial + labels.key1: "value1" # Dict Initial +} + +config = Config { + name = "config2" # Literal Override + args: ["kcl2"] # List Override + labels: {key2: "value2"} # Dict Unification +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/override.vertex b/test/test_units/test_kclvm/test_unification/test_data/override.vertex new file mode 100644 index 000000000..4f97906d1 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/override.vertex @@ -0,0 +1,33 @@ +name: @root +is_unique: False +adjs: + name: config + is_unique: False + config_name: Config + adjs: + name: name + is_unique: False + value: + + name: name + is_unique: False + value: + + name: args + is_unique: False + value: , ]> + + name: args + is_unique: False + value: ]> + + name: labels + is_unique: False + adjs: + name: key1 + is_unique: False + value: + + name: key2 + is_unique: False + value: diff --git a/test/test_units/test_kclvm/test_unification/test_data/schema.code b/test/test_units/test_kclvm/test_unification/test_data/schema.code new file mode 100644 index 000000000..2f2140ef3 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/schema.code @@ -0,0 +1,5 @@ +config = Config { + name: "Alice" + age: 18 + hc: [1] +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/schema.input b/test/test_units/test_kclvm/test_unification/test_data/schema.input new file mode 100644 index 000000000..13190b0da --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/schema.input @@ -0,0 +1,16 @@ +schema Config: + name?: str + age?: int + hc: [int] + +config = Config { + name: "Alice" +} + +config = Config { + age: 18 +} + +config = Config { + hc: [1] +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/schema.vertex b/test/test_units/test_kclvm/test_unification/test_data/schema.vertex new file mode 100644 index 000000000..1a7ae9e11 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/schema.vertex @@ -0,0 +1,18 @@ +name: @root +is_unique: False +adjs: + name: config + is_unique: False + config_name: Config + adjs: + name: name + is_unique: False + value: + + name: age + is_unique: False + value: + + name: hc + is_unique: False + value: ]> diff --git a/test/test_units/test_kclvm/test_unification/test_data/schema_and_dict.code b/test/test_units/test_kclvm/test_unification/test_data/schema_and_dict.code new file mode 100644 index 000000000..2f2140ef3 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/schema_and_dict.code @@ -0,0 +1,5 @@ +config = Config { + name: "Alice" + age: 18 + hc: [1] +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/schema_and_dict.input b/test/test_units/test_kclvm/test_unification/test_data/schema_and_dict.input new file mode 100644 index 000000000..7dcc10109 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/schema_and_dict.input @@ -0,0 +1,16 @@ +schema Config: + name?: str + age?: int + hc: [int] + +config = Config { + name: "Alice" +} + +config = { + "age": 18 +} + +config = Config { + hc: [1] +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/schema_and_dict.vertex b/test/test_units/test_kclvm/test_unification/test_data/schema_and_dict.vertex new file mode 100644 index 000000000..de31ebc14 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/schema_and_dict.vertex @@ -0,0 +1,18 @@ +name: @root +is_unique: False +adjs: + name: config + is_unique: False + config_name: Config + adjs: + name: name + is_unique: False + value: + + name: age + is_unique: False + value: + + name: hc + is_unique: False + value: ]> diff --git a/test/test_units/test_kclvm/test_unification/test_data/schema_with_list.code b/test/test_units/test_kclvm/test_unification/test_data/schema_with_list.code new file mode 100644 index 000000000..3423a61b5 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/schema_with_list.code @@ -0,0 +1,14 @@ +_main = Main { + env: ["123", "456"] + args: ["1"] +} + +config = Config { + name: "Alice" + main: _main + main: Main { + env: ["123"] + args: [] + } + age: 18 +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/schema_with_list.input b/test/test_units/test_kclvm/test_unification/test_data/schema_with_list.input new file mode 100644 index 000000000..6ccc0c230 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/schema_with_list.input @@ -0,0 +1,27 @@ +schema Main: + env: [str] + args: [str] + +schema Config: + name?: str + age?: int + hc?: [int] + main: Main + +_main = Main { + env: ["123", "456"] + args: ["1"] +} + +config = Config { + name: "Alice" + main: _main +} + +config = Config { + age: 18 + main: Main { + env: ["123"] + args: [] + } +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/schema_with_list.vertex b/test/test_units/test_kclvm/test_unification/test_data/schema_with_list.vertex new file mode 100644 index 000000000..28ebe660c --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/schema_with_list.vertex @@ -0,0 +1,42 @@ +name: @root +is_unique: False +adjs: + name: _main + is_unique: False + config_name: Main + adjs: + name: env + is_unique: False + value: , ]> + + name: args + is_unique: False + value: ]> + + name: config + is_unique: False + config_name: Config + adjs: + name: name + is_unique: False + value: + + name: main + is_unique: False + value: + + name: main + is_unique: False + config_name: Main + adjs: + name: env + is_unique: False + value: ]> + + name: args + is_unique: False + value: + + name: age + is_unique: False + value: diff --git a/test/test_units/test_kclvm/test_unification/test_data/single.code b/test/test_units/test_kclvm/test_unification/test_data/single.code new file mode 100644 index 000000000..94475c2ff --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/single.code @@ -0,0 +1,7 @@ +config = Config { + labels: { + env1 = "v1" + env2 = "v2" + env3 = "v3" + } +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/single.input b/test/test_units/test_kclvm/test_unification/test_data/single.input new file mode 100644 index 000000000..2b5a96181 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/single.input @@ -0,0 +1,12 @@ +schema Config: + name?: str + args?: [str] + labels?: {str:} + +config = Config { + labels.env1 = "v1" + labels.env2 = "v2" + labels: { + env3 = "v3" + } +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/single.vertex b/test/test_units/test_kclvm/test_unification/test_data/single.vertex new file mode 100644 index 000000000..415949e6e --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/single.vertex @@ -0,0 +1,21 @@ +name: @root +is_unique: False +adjs: + name: config + is_unique: False + config_name: Config + adjs: + name: labels + is_unique: False + adjs: + name: env1 + is_unique: False + value: + + name: env2 + is_unique: False + value: + + name: env3 + is_unique: False + value: diff --git a/test/test_units/test_kclvm/test_unification/test_data/str_interpolation.code b/test/test_units/test_kclvm/test_unification/test_data/str_interpolation.code new file mode 100644 index 000000000..a0a68ab3d --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/str_interpolation.code @@ -0,0 +1,5 @@ +key = "age" +alice = Person { + name: "Alice" + "${key}": 18 +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/str_interpolation.input b/test/test_units/test_kclvm/test_unification/test_data/str_interpolation.input new file mode 100644 index 000000000..19960eeaa --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/str_interpolation.input @@ -0,0 +1,9 @@ +schema Person: + name: str + age: int + +key = "age" +alice = Person { + name: "Alice" +} +alice = {"${key}": 18} diff --git a/test/test_units/test_kclvm/test_unification/test_data/str_interpolation.vertex b/test/test_units/test_kclvm/test_unification/test_data/str_interpolation.vertex new file mode 100644 index 000000000..3972eb7d2 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/str_interpolation.vertex @@ -0,0 +1,18 @@ +name: @root +is_unique: False +adjs: + name: key + is_unique: False + value: + + name: alice + is_unique: False + config_name: Person + adjs: + name: name + is_unique: False + value: + + name: + is_unique: False + value: diff --git a/test/test_units/test_kclvm/test_unification/test_data/unification.code b/test/test_units/test_kclvm/test_unification/test_data/unification.code new file mode 100644 index 000000000..ed1328819 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/unification.code @@ -0,0 +1,17 @@ +schema Config: + """Config Definition""" + name?: str + args: [str] + labels: {str:} + +config: Config { + name: "config1" # Literal Initial + args: ["kcl", "main.k"] # List Initial + labels.key1: "value1" # Dict Initial +} + +config: Config { + name = "config2" # Literal Override + args: ["kcl2"] # List Override + labels: {key2: "value2"} # Dict Unification +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/unification.input b/test/test_units/test_kclvm/test_unification/test_data/unification.input new file mode 100644 index 000000000..ed1328819 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/unification.input @@ -0,0 +1,17 @@ +schema Config: + """Config Definition""" + name?: str + args: [str] + labels: {str:} + +config: Config { + name: "config1" # Literal Initial + args: ["kcl", "main.k"] # List Initial + labels.key1: "value1" # Dict Initial +} + +config: Config { + name = "config2" # Literal Override + args: ["kcl2"] # List Override + labels: {key2: "value2"} # Dict Unification +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/unification.vertex b/test/test_units/test_kclvm/test_unification/test_data/unification.vertex new file mode 100644 index 000000000..4f97906d1 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/unification.vertex @@ -0,0 +1,33 @@ +name: @root +is_unique: False +adjs: + name: config + is_unique: False + config_name: Config + adjs: + name: name + is_unique: False + value: + + name: name + is_unique: False + value: + + name: args + is_unique: False + value: , ]> + + name: args + is_unique: False + value: ]> + + name: labels + is_unique: False + adjs: + name: key1 + is_unique: False + value: + + name: key2 + is_unique: False + value: diff --git a/test/test_units/test_kclvm/test_unification/test_data/unpack.code b/test/test_units/test_kclvm/test_unification/test_data/unpack.code new file mode 100644 index 000000000..77231632d --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/unpack.code @@ -0,0 +1,14 @@ +schema Person: + name: str + age: int + +_base = { + name = "Bob" + age = 18 +} + +alice = Person { + **_base + name = "Alice" + age = 18 +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/unpack.input b/test/test_units/test_kclvm/test_unification/test_data/unpack.input new file mode 100644 index 000000000..241949694 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/unpack.input @@ -0,0 +1,17 @@ +schema Person: + name: str + age: int + +_base = { + name = "Bob" + age = 18 +} + +alice = Person { + **_base +} + +alice = Person { + name = "Alice" + age = 18 +} diff --git a/test/test_units/test_kclvm/test_unification/test_data/unpack.vertex b/test/test_units/test_kclvm/test_unification/test_data/unpack.vertex new file mode 100644 index 000000000..fef1e9afe --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_data/unpack.vertex @@ -0,0 +1,29 @@ +name: @root +is_unique: False +adjs: + name: _base + is_unique: False + adjs: + name: name + is_unique: False + value: + + name: age + is_unique: False + value: + + name: alice + is_unique: False + config_name: Person + adjs: + name: None + is_unique: False + value: + + name: name + is_unique: False + value: + + name: age + is_unique: False + value: diff --git a/test/test_units/test_kclvm/test_unification/test_merge.py b/test/test_units/test_kclvm/test_unification/test_merge.py new file mode 100644 index 000000000..f1f47ef09 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_merge.py @@ -0,0 +1,101 @@ +#! /usr/bin/env python3 + +import unittest + +import kclvm.kcl.ast as ast +import kclvm.kcl.error as kcl_error +from kclvm.compiler.parser import ParseFile +from kclvm.unification import MergeAST, MergeASTList + +codes = [ + """ +schema Person: + name: str + age: int + labels: {str:} + +person = Person { + name: "Alice" +} +person = Person { + labels.key: "value" +} +""", + """ +person = Person { + age: 18 +} +persons = Person.instances() +""", +] +type_merge_not_conflict_case = """\ +schema Id1: + id1?: int + +schema Id2: + id2?: int + +schema Data: + id?: Id1 | Id2 + +data = Data { + id = Id1 {id1 = 1} + id = Id2 {id2 = 2} +} +""" +type_merge_conflict_case = """\ +schema Id1: + id1?: int + +schema Id2: + id2?: int + +schema Data: + id?: Id1 | Id2 + +data = Data { + id: Id1 {id1 = 1} + id: Id2 {id2 = 2} +} +""" + + +class KCLMergeTest(unittest.TestCase): + def setUp(self): + self.maxDiff = None + self.modules = [ParseFile(f"test-{i}.k", code) for i, code in enumerate(codes)] + return super().setUp() + + def test_merge(self): + module = MergeAST(self.modules[0]) + self.assertEqual(len(module.body), 2) + self.assertIsInstance(module.body[0], ast.SchemaStmt) + self.assertIsInstance(module.body[1], ast.AssignStmt) + self.assertEqual(len(module.body[1].value.config.items), 2) + self.assertEqual(module.body[1].value.config.keys[0].get_name(), "name") + self.assertEqual(module.body[1].value.config.keys[1].get_name(), "labels") + + def test_merge_list(self): + modules = MergeASTList(self.modules) + self.assertEqual(len(modules), 2) + # The first module + self.assertEqual(len(modules[0].body), 1) + self.assertIsInstance(modules[0].body[0], ast.SchemaStmt) + # The second module + self.assertIsInstance(modules[1].body[0], ast.AssignStmt) + self.assertEqual(len(modules[1].body[0].value.config.items), 3) + self.assertEqual(modules[1].body[0].value.config.keys[0].get_name(), "name") + self.assertEqual(modules[1].body[0].value.config.keys[1].get_name(), "labels") + self.assertEqual(modules[1].body[0].value.config.keys[2].get_name(), "age") + + def test_merge_type_conflict(self): + module = ParseFile("type_merge_not_conflict_case", type_merge_not_conflict_case) + module = MergeASTList([module]) + module = ParseFile("type_merge_conflict_case", type_merge_conflict_case) + with self.assertRaises(kcl_error.CompileError) as err: + module = MergeASTList([module]) + self.assertIn("conflict unification types between Id2 and Id1", str(err.exception)) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_unification/test_subsume.py b/test/test_units/test_kclvm/test_unification/test_subsume.py new file mode 100644 index 000000000..283b8a57b --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_subsume.py @@ -0,0 +1,360 @@ +#! /usr/bin/env python3 + +import os +import unittest +import pathlib +from typing import Tuple + +import kclvm.api.object as obj +import kclvm.kcl.types as types +from kclvm.compiler.parser import ParseFile +from kclvm.unification import value_subsume, type_subsume + +VALUE1_KEY = "value1" +VALUE2_KEY = "value2" +EXPECTED_KEY = "expected" + + +class KCLSubsumeTest(unittest.TestCase): + def test_value_subsume(self): + cases = [ + # Left None + {VALUE1_KEY: None, VALUE2_KEY: 1, EXPECTED_KEY: True}, + {VALUE1_KEY: None, VALUE2_KEY: 1.1, EXPECTED_KEY: True}, + {VALUE1_KEY: None, VALUE2_KEY: [], EXPECTED_KEY: True}, + {VALUE1_KEY: None, VALUE2_KEY: {}, EXPECTED_KEY: True}, + {VALUE1_KEY: None, VALUE2_KEY: "s", EXPECTED_KEY: True}, + {VALUE1_KEY: None, VALUE2_KEY: True, EXPECTED_KEY: True}, + {VALUE1_KEY: None, VALUE2_KEY: False, EXPECTED_KEY: True}, + {VALUE1_KEY: None, VALUE2_KEY: None, EXPECTED_KEY: True}, + # Right None + {VALUE1_KEY: 1, VALUE2_KEY: None, EXPECTED_KEY: True}, + {VALUE1_KEY: 1.1, VALUE2_KEY: None, EXPECTED_KEY: True}, + {VALUE1_KEY: [], VALUE2_KEY: None, EXPECTED_KEY: True}, + {VALUE1_KEY: {}, VALUE2_KEY: None, EXPECTED_KEY: True}, + {VALUE1_KEY: "s", VALUE2_KEY: None, EXPECTED_KEY: True}, + {VALUE1_KEY: True, VALUE2_KEY: None, EXPECTED_KEY: True}, + {VALUE1_KEY: False, VALUE2_KEY: None, EXPECTED_KEY: True}, + {VALUE1_KEY: None, VALUE2_KEY: None, EXPECTED_KEY: True}, + # Left Undefined + {VALUE1_KEY: obj.Undefined, VALUE2_KEY: 1, EXPECTED_KEY: True}, + {VALUE1_KEY: obj.Undefined, VALUE2_KEY: 1.1, EXPECTED_KEY: True}, + {VALUE1_KEY: obj.Undefined, VALUE2_KEY: [], EXPECTED_KEY: True}, + {VALUE1_KEY: obj.Undefined, VALUE2_KEY: {}, EXPECTED_KEY: True}, + {VALUE1_KEY: obj.Undefined, VALUE2_KEY: "s", EXPECTED_KEY: True}, + {VALUE1_KEY: obj.Undefined, VALUE2_KEY: True, EXPECTED_KEY: True}, + {VALUE1_KEY: obj.Undefined, VALUE2_KEY: False, EXPECTED_KEY: True}, + {VALUE1_KEY: obj.Undefined, VALUE2_KEY: None, EXPECTED_KEY: True}, + # Right Undefined + {VALUE1_KEY: 1, VALUE2_KEY: obj.Undefined, EXPECTED_KEY: True}, + {VALUE1_KEY: 1.1, VALUE2_KEY: obj.Undefined, EXPECTED_KEY: True}, + {VALUE1_KEY: [], VALUE2_KEY: obj.Undefined, EXPECTED_KEY: True}, + {VALUE1_KEY: {}, VALUE2_KEY: obj.Undefined, EXPECTED_KEY: True}, + {VALUE1_KEY: "s", VALUE2_KEY: obj.Undefined, EXPECTED_KEY: True}, + {VALUE1_KEY: True, VALUE2_KEY: obj.Undefined, EXPECTED_KEY: True}, + {VALUE1_KEY: False, VALUE2_KEY: obj.Undefined, EXPECTED_KEY: True}, + {VALUE1_KEY: None, VALUE2_KEY: obj.Undefined, EXPECTED_KEY: True}, + # Int + {VALUE1_KEY: 1, VALUE2_KEY: 1, EXPECTED_KEY: True}, + {VALUE1_KEY: 1, VALUE2_KEY: 2, EXPECTED_KEY: False}, + {VALUE1_KEY: 1, VALUE2_KEY: 1.0, EXPECTED_KEY: False}, + {VALUE1_KEY: 1, VALUE2_KEY: 1.1, EXPECTED_KEY: False}, + {VALUE1_KEY: 1, VALUE2_KEY: [], EXPECTED_KEY: False}, + {VALUE1_KEY: 1, VALUE2_KEY: {}, EXPECTED_KEY: False}, + {VALUE1_KEY: 1, VALUE2_KEY: "s", EXPECTED_KEY: False}, + {VALUE1_KEY: 1, VALUE2_KEY: True, EXPECTED_KEY: False}, + {VALUE1_KEY: 1, VALUE2_KEY: False, EXPECTED_KEY: False}, + # Float + {VALUE1_KEY: 1.1, VALUE2_KEY: 1, EXPECTED_KEY: False}, + {VALUE1_KEY: 1.1, VALUE2_KEY: 2, EXPECTED_KEY: False}, + {VALUE1_KEY: 1.1, VALUE2_KEY: 1.0, EXPECTED_KEY: False}, + {VALUE1_KEY: 1.1, VALUE2_KEY: 1.1, EXPECTED_KEY: True}, + {VALUE1_KEY: 1.1, VALUE2_KEY: [], EXPECTED_KEY: False}, + {VALUE1_KEY: 1.1, VALUE2_KEY: {}, EXPECTED_KEY: False}, + {VALUE1_KEY: 1.1, VALUE2_KEY: "s", EXPECTED_KEY: False}, + {VALUE1_KEY: 1.1, VALUE2_KEY: True, EXPECTED_KEY: False}, + {VALUE1_KEY: 1.1, VALUE2_KEY: False, EXPECTED_KEY: False}, + # String + {VALUE1_KEY: "s", VALUE2_KEY: 1, EXPECTED_KEY: False}, + {VALUE1_KEY: "s", VALUE2_KEY: 2, EXPECTED_KEY: False}, + {VALUE1_KEY: "s", VALUE2_KEY: 1.0, EXPECTED_KEY: False}, + {VALUE1_KEY: "s", VALUE2_KEY: 1.1, EXPECTED_KEY: False}, + {VALUE1_KEY: "s", VALUE2_KEY: [], EXPECTED_KEY: False}, + {VALUE1_KEY: "s", VALUE2_KEY: {}, EXPECTED_KEY: False}, + {VALUE1_KEY: "s", VALUE2_KEY: "s", EXPECTED_KEY: True}, + {VALUE1_KEY: "s", VALUE2_KEY: "", EXPECTED_KEY: False}, + {VALUE1_KEY: "s", VALUE2_KEY: "ss", EXPECTED_KEY: False}, + {VALUE1_KEY: "s", VALUE2_KEY: True, EXPECTED_KEY: False}, + {VALUE1_KEY: "s", VALUE2_KEY: False, EXPECTED_KEY: False}, + # Boolean True + {VALUE1_KEY: True, VALUE2_KEY: 1, EXPECTED_KEY: False}, + {VALUE1_KEY: True, VALUE2_KEY: 2, EXPECTED_KEY: False}, + {VALUE1_KEY: True, VALUE2_KEY: 1.0, EXPECTED_KEY: False}, + {VALUE1_KEY: True, VALUE2_KEY: 1.1, EXPECTED_KEY: False}, + {VALUE1_KEY: True, VALUE2_KEY: [], EXPECTED_KEY: False}, + {VALUE1_KEY: True, VALUE2_KEY: {}, EXPECTED_KEY: False}, + {VALUE1_KEY: True, VALUE2_KEY: "s", EXPECTED_KEY: False}, + {VALUE1_KEY: True, VALUE2_KEY: "", EXPECTED_KEY: False}, + {VALUE1_KEY: True, VALUE2_KEY: "ss", EXPECTED_KEY: False}, + {VALUE1_KEY: True, VALUE2_KEY: True, EXPECTED_KEY: True}, + {VALUE1_KEY: True, VALUE2_KEY: False, EXPECTED_KEY: False}, + # Boolean False + {VALUE1_KEY: False, VALUE2_KEY: 1, EXPECTED_KEY: False}, + {VALUE1_KEY: False, VALUE2_KEY: 2, EXPECTED_KEY: False}, + {VALUE1_KEY: False, VALUE2_KEY: 1.0, EXPECTED_KEY: False}, + {VALUE1_KEY: False, VALUE2_KEY: 1.1, EXPECTED_KEY: False}, + {VALUE1_KEY: False, VALUE2_KEY: [], EXPECTED_KEY: False}, + {VALUE1_KEY: False, VALUE2_KEY: {}, EXPECTED_KEY: False}, + {VALUE1_KEY: False, VALUE2_KEY: "s", EXPECTED_KEY: False}, + {VALUE1_KEY: False, VALUE2_KEY: "", EXPECTED_KEY: False}, + {VALUE1_KEY: False, VALUE2_KEY: "ss", EXPECTED_KEY: False}, + {VALUE1_KEY: False, VALUE2_KEY: True, EXPECTED_KEY: False}, + {VALUE1_KEY: False, VALUE2_KEY: False, EXPECTED_KEY: True}, + # List + {VALUE1_KEY: [], VALUE2_KEY: 1, EXPECTED_KEY: False}, + {VALUE1_KEY: [], VALUE2_KEY: 2, EXPECTED_KEY: False}, + {VALUE1_KEY: [], VALUE2_KEY: 1.0, EXPECTED_KEY: False}, + {VALUE1_KEY: [], VALUE2_KEY: 1.1, EXPECTED_KEY: False}, + {VALUE1_KEY: [], VALUE2_KEY: [], EXPECTED_KEY: True}, + {VALUE1_KEY: [], VALUE2_KEY: {}, EXPECTED_KEY: False}, + {VALUE1_KEY: [], VALUE2_KEY: "s", EXPECTED_KEY: False}, + {VALUE1_KEY: [], VALUE2_KEY: True, EXPECTED_KEY: False}, + {VALUE1_KEY: [], VALUE2_KEY: False, EXPECTED_KEY: False}, + {VALUE1_KEY: [1], VALUE2_KEY: [1], EXPECTED_KEY: True}, + {VALUE1_KEY: [1], VALUE2_KEY: [2], EXPECTED_KEY: False}, + {VALUE1_KEY: [1], VALUE2_KEY: [1, 1], EXPECTED_KEY: False}, + { + VALUE1_KEY: [{"key1": "value1"}], + VALUE2_KEY: [{"key1": "value1"}], + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: [{"key1": "value1"}], + VALUE2_KEY: [{"key1": "value2"}], + EXPECTED_KEY: False, + }, + { + VALUE1_KEY: [{"key1": "value1"}], + VALUE2_KEY: [{"key2": "value2"}], + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: [{"key1": "value1"}], + VALUE2_KEY: [{"key1": "value1", "key2": "value2"}], + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: [{"key1": "value1"}], + VALUE2_KEY: [{"key2": "value2", "key1": "value1"}], + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: [{"key1": "value1", "key2": "value2"}], + VALUE2_KEY: [{"key1": "value1"}], + EXPECTED_KEY: True, + }, + # Dict + {VALUE1_KEY: {}, VALUE2_KEY: 1, EXPECTED_KEY: False}, + {VALUE1_KEY: {}, VALUE2_KEY: 2, EXPECTED_KEY: False}, + {VALUE1_KEY: {}, VALUE2_KEY: 1.0, EXPECTED_KEY: False}, + {VALUE1_KEY: {}, VALUE2_KEY: 1.1, EXPECTED_KEY: False}, + {VALUE1_KEY: {}, VALUE2_KEY: [], EXPECTED_KEY: False}, + {VALUE1_KEY: {}, VALUE2_KEY: {}, EXPECTED_KEY: True}, + {VALUE1_KEY: {}, VALUE2_KEY: "s", EXPECTED_KEY: False}, + {VALUE1_KEY: {}, VALUE2_KEY: True, EXPECTED_KEY: False}, + {VALUE1_KEY: {}, VALUE2_KEY: False, EXPECTED_KEY: False}, + {VALUE1_KEY: {}, VALUE2_KEY: {"key1": "value1"}, EXPECTED_KEY: True}, + {VALUE1_KEY: {"key1": "value1"}, VALUE2_KEY: {}, EXPECTED_KEY: True}, + { + VALUE1_KEY: {"key1": "value1"}, + VALUE2_KEY: {"key1": "value1"}, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: {"key1": "value1"}, + VALUE2_KEY: {"key1": "value1", "key2": "value2"}, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: {"key1": "value1", "key2": "value2"}, + VALUE2_KEY: {"key1": "value1"}, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: {"s": {"key1": "value1"}}, + VALUE2_KEY: {"s": {"key1": "value1", "key2": "value2"}}, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: {"s": {"key1": "value1", "key2": "value2"}}, + VALUE2_KEY: {"s": {"key1": "value1"}}, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: {"key2": "value2", "key1": "value1"}, + VALUE2_KEY: {"key1": "value1", "key2": "value2"}, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: {"key2": "value2", "key1": "value1"}, + VALUE2_KEY: {"key1": "value1", "key2": "value2"}, + EXPECTED_KEY: True, + }, + # Schema + ] + for case in cases: + try: + value1 = obj.to_kcl_obj(case[VALUE1_KEY]) + value2 = obj.to_kcl_obj(case[VALUE2_KEY]) + expected = case[EXPECTED_KEY] + self.assertEqual(value_subsume(value1, value2), expected) + except AssertionError as err: + print( + f"Assert fail between the value1 {obj.to_python_obj(value1)} and the value2 {obj.to_python_obj(value2)}" + ) + raise err + + def test_type_subsume(self): + cases = [ + # The same types + {VALUE1_KEY: None, VALUE2_KEY: None, EXPECTED_KEY: False}, + {VALUE1_KEY: 1, VALUE2_KEY: 2, EXPECTED_KEY: False}, + { + VALUE1_KEY: obj.NONE_INSTANCE, + VALUE2_KEY: obj.NONE_INSTANCE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.ANY_TYPE, + VALUE2_KEY: types.ANY_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.STR_TYPE, + VALUE2_KEY: types.STR_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.INT_TYPE, + VALUE2_KEY: types.INT_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.FLOAT_TYPE, + VALUE2_KEY: types.FLOAT_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.BOOL_TYPE, + VALUE2_KEY: types.BOOL_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.NONE_TYPE, + VALUE2_KEY: types.BOOL_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.INT_TYPE, + VALUE2_KEY: types.FLOAT_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.DICT_STR_ANY_TYPE, + VALUE2_KEY: types.DICT_STR_ANY_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.DICT_STR_STR_TYPE, + VALUE2_KEY: types.DICT_STR_ANY_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: obj.KCLIntLitTypeObject(1), + VALUE2_KEY: types.INT_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: obj.KCLFloatLitTypeObject(1.0), + VALUE2_KEY: types.FLOAT_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: obj.KCLStringLitTypeObject("s"), + VALUE2_KEY: types.STR_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.TRUE_LIT_TYPE, + VALUE2_KEY: types.BOOL_TYPE, + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.INT_TYPE, + VALUE2_KEY: obj.KCLUnionTypeObject([types.INT_TYPE, types.STR_TYPE]), + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.STR_TYPE, + VALUE2_KEY: obj.KCLUnionTypeObject([types.INT_TYPE, types.STR_TYPE]), + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: types.INT_OR_STR_TYPE, + VALUE2_KEY: obj.KCLUnionTypeObject([types.STR_TYPE, types.INT_TYPE]), + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: obj.KCLIntObject(1), + VALUE2_KEY: obj.KCLUnionTypeObject([types.STR_TYPE, types.INT_TYPE]), + EXPECTED_KEY: False, + }, + { + VALUE1_KEY: obj.KCLIntLitTypeObject(1), + VALUE2_KEY: obj.KCLUnionTypeObject( + types=[ + obj.KCLIntLitTypeObject(1), + types.TRUE_LIT_TYPE, + obj.KCLStringLitTypeObject("aaa"), + ] + ), + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: obj.KCLNumberMultiplierTypeObject( + value=1024, + raw_value=1, + binary_suffix="1Mi" + ), + VALUE2_KEY: obj.KCLNumberMultiplierTypeObject( + value=1024, + raw_value=1, + binary_suffix="1Mi" + ), + EXPECTED_KEY: True, + }, + { + VALUE1_KEY: obj.KCLNumberMultiplierTypeObject(), + VALUE2_KEY: obj.KCLNumberMultiplierTypeObject( + value=1024, + raw_value=1, + binary_suffix="1Mi" + ), + EXPECTED_KEY: True, + }, + ] + for case in cases: + type1, type2, expected = ( + case[VALUE1_KEY], + case[VALUE2_KEY], + case[EXPECTED_KEY], + ) + self.assertEqual( + type_subsume(type1, type2), + expected, + msg=f"Assert error between {type1} and {type2}", + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_unification/test_unifier.py b/test/test_units/test_kclvm/test_unification/test_unifier.py new file mode 100644 index 000000000..33a9e92d2 --- /dev/null +++ b/test/test_units/test_kclvm/test_unification/test_unifier.py @@ -0,0 +1,98 @@ +#! /usr/bin/env python3 + +import os +import unittest +import pathlib +from typing import Tuple + +import kclvm.kcl.ast as ast +from kclvm.compiler.parser import ParseFile +from kclvm.compiler.astutil.filter import filter_stmt +from kclvm.unification import MergeASTToVertex, MergeStrategy, Vertex + + +_FILE_INPUT_SUFFIX = ".input" +_FILE_VERTEX_SUFFIX = ".vertex" +_FILE_CODE_SUFFIX = ".code" +_PATH_NAME = "test_data" +_DIR_PATH = pathlib.Path(__file__).parent.joinpath(_PATH_NAME) +_TEST_CASE_NAMES = [ + "collection_if", + "empty", + "insert", + "int_dict", + "nest_declaration", + "nest_var_0", + "nest_var_1", + "override", + "schema_and_dict", + "schema_with_list", + "schema", + "single", + "str_interpolation", + "unification", + "unpack", +] + + +class KCLUnifierBaseTest(unittest.TestCase): + def setUp(self): + self.maxDiff = None + self.test_cases = [self.read_data(case) for case in _TEST_CASE_NAMES] + + def read_data_with_suffix(self, data_name: str, suffix: str): + return (_DIR_PATH / (data_name + suffix)).read_text() + + def read_data(self, data_name: str) -> Tuple[str, str]: + """Read test data""" + data_input = self.read_data_with_suffix(data_name, _FILE_INPUT_SUFFIX) + data_vertex = self.read_data_with_suffix(data_name, _FILE_VERTEX_SUFFIX) + data_code = self.read_data_with_suffix(data_name, _FILE_CODE_SUFFIX) + return data_input, data_vertex, data_code + + def assert_vertex_unify_equal( + self, + input_str: str, + vertex_str: str, + code_str: str, + strategy: MergeStrategy = MergeStrategy.UNION, + msg=None, + ): + origin_module = ParseFile("", input_str) + code_module = ParseFile("", code_str) + origin_vertex, unify_vertex, merge_module = MergeASTToVertex(origin_module) + # TODO: Optimize the function test to smaller granularity test. @lingzhi.xpf + self.assertEqual(unify_vertex.pretty(), vertex_str, msg=msg) + + +class KCLUnifierTest(KCLUnifierBaseTest): + def test_vertex_unification(self) -> None: + for input, vertex, code in self.test_cases: + self.assert_vertex_unify_equal(input, vertex, code, msg=f"the fail code is\n{code}") + + def test_vertex_override_merge(self) -> None: + for input, vertex, code in self.test_cases: + self.assert_vertex_unify_equal(input, vertex, code, MergeStrategy.OVERRIDE) + + +def print_vertex_unification_result(test_file: str): + """Print vertex unification result + + Usage: + print_vertex_unification_result("schema.input") + """ + if not test_file: + return + filename = str(pathlib.Path(__file__).parent.joinpath(_DIR_PATH, test_file)) + module = ParseFile(filename) + vertex, unify_vertex, merged_module = MergeASTToVertex(module) + print("Origin vertex formed by AST as follows:\n") + print(vertex.pretty()) + print("Unify vertex result as follows:\n") + print(unify_vertex.pretty()) + print("Merged AST as follows:\n") + print(merged_module.to_json()) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_vm/invalid_test_data/recursive.k b/test/test_units/test_kclvm/test_vm/invalid_test_data/recursive.k new file mode 100644 index 000000000..778942b3d --- /dev/null +++ b/test/test_units/test_kclvm/test_vm/invalid_test_data/recursive.k @@ -0,0 +1,13 @@ +schema Son: + son_field: str + parent: Parent = None + +schema Parent[name](Son): + field: str = name + son: Son = Son { + parent: Parent(name) { + "field": "123" + } + } + +parent = Parent(name = "123") {} diff --git a/test/test_units/test_kclvm/test_vm/invalid_test_data/unification.k b/test/test_units/test_kclvm/test_vm/invalid_test_data/unification.k new file mode 100644 index 000000000..7926dd595 --- /dev/null +++ b/test/test_units/test_kclvm/test_vm/invalid_test_data/unification.k @@ -0,0 +1 @@ +data = {id: 1} | {id: 2} \ No newline at end of file diff --git a/test/test_units/test_kclvm/test_vm/test_code.py b/test/test_units/test_kclvm/test_vm/test_code.py new file mode 100644 index 000000000..c0ef13c09 --- /dev/null +++ b/test/test_units/test_kclvm/test_vm/test_code.py @@ -0,0 +1,61 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import pathlib +import unittest + +import kclvm.compiler.parser as parser +import kclvm.compiler.build.compiler as compiler +import kclvm.vm.code.code as code +import kclvm.vm.code.code_factory as code_factory +import kclvm.vm as vm + + +TEST_PKG_PATH = "__main__" +TEST_FILE_META = ("main.k", 1, 1) +TEST_SCHEMA_NAME = "TestPerson" +TEST_CODE = """\ +schema Person: + name: str = "Alice" + age: int = 18 + +person = Person {} +""" + + +class VMCodeTest(unittest.TestCase): + def test_code_factory(self): + codes = [code.Opcode.INVALID, code.Opcode.POP_TOP, TEST_FILE_META] + opcode_factory = code_factory.OpcodeFactory.build_from_codes( + codes, TEST_PKG_PATH + ) + opcode_factory.pretty_print() + opcode_factory.values = [] + opcode_factory.pretty_print() + program = compiler.CompileProgram( + parser.LoadProgram(TEST_FILE_META[0], k_code_list=[TEST_CODE]) + ) + opcode_factory = code_factory.SchemaBodyOpcodeFactory.build_from_codes( + program.pkgs[TEST_PKG_PATH].instructions, TEST_PKG_PATH, TEST_SCHEMA_NAME + ) + opcode_factory.pretty_print() + opcode_factory.values = [] + opcode_factory.pretty_print() + + def test_code_actions(self): + program = compiler.CompileProgram( + parser.LoadProgram(TEST_FILE_META[0], k_code_list=[TEST_CODE]) + ) + for opcode in [ + code.Opcode.DEBUG_GLOBALS, + code.Opcode.DEBUG_LOCALS, + code.Opcode.DEBUG_NAMES, + code.Opcode.DEBUG_STACK, + ]: + program.pkgs[TEST_PKG_PATH].instructions.extend( + [opcode, 0, 0, 0, TEST_FILE_META] + ) + vm.Run(program) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_vm/test_data/kcl.mod b/test/test_units/test_kclvm/test_vm/test_data/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_kclvm/test_vm/test_data/main.k b/test/test_units/test_kclvm/test_vm/test_data/main.k new file mode 100644 index 000000000..eab736f11 --- /dev/null +++ b/test/test_units/test_kclvm/test_vm/test_data/main.k @@ -0,0 +1,6 @@ +import pkg + +person = pkg.Person { + name = "Alice" + age = 18 +} diff --git a/test/test_units/test_kclvm/test_vm/test_data/pkg/pkg.k b/test/test_units/test_kclvm/test_vm/test_data/pkg/pkg.k new file mode 100644 index 000000000..b4c3153b0 --- /dev/null +++ b/test/test_units/test_kclvm/test_vm/test_data/pkg/pkg.k @@ -0,0 +1,3 @@ +schema Person: + name: str + age: int diff --git a/test/test_units/test_kclvm/test_vm/test_evaluator.py b/test/test_units/test_kclvm/test_vm/test_evaluator.py new file mode 100644 index 000000000..2aa96e47b --- /dev/null +++ b/test/test_units/test_kclvm/test_vm/test_evaluator.py @@ -0,0 +1,175 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import pathlib +import unittest + +import kclvm.kcl.error as kcl_error +import kclvm.api.object as objpkg +import kclvm.vm.code as vm_code +import kclvm.vm.runtime.evaluator.eval as eval + + +class EvaluatorTest(unittest.TestCase): + def setUp(self): + self._evaluator = eval.Evaluator() + return super().setUp() + + def test_eval_binary_op(self): + cases = [ + { + "left": objpkg.KCLIntObject(1), + "right": objpkg.KCLIntObject(1), + "code": vm_code.Opcode.BINARY_ADD, + "expected": objpkg.KCLIntObject(2), + }, + { + "left": objpkg.KCLIntObject(1), + "right": objpkg.KCLIntObject(1), + "code": vm_code.Opcode.BINARY_SUBTRACT, + "expected": objpkg.KCLIntObject(0), + }, + ] + for case in cases: + left, right, code, expected = ( + case["left"], + case["right"], + case["code"], + case["expected"], + ) + result = self._evaluator.eval_binary_op(left, right, code) + self.assertEqual(result, expected) + + def test_eval_binary_op_invalid(self): + cases = [ + {"left": None, "right": None, "code": vm_code.Opcode.BINARY_ADD}, + { + "left": objpkg.KCLIntObject(1), + "right": objpkg.KCLIntObject(1), + "code": vm_code.Opcode.NOP, + }, + ] + for case in cases: + left, right, code = case["left"], case["right"], case["code"] + with self.assertRaises(Exception): + self._evaluator.eval_binary_op(left, right, code) + + def test_eval_compare_op(self): + cases = [ + { + "left": objpkg.KCLIntObject(1), + "right": objpkg.KCLIntObject(1), + "code": vm_code.Opcode.COMPARE_EQUAL_TO, + "expected": objpkg.TRUE_INSTANCE, + }, + { + "left": objpkg.KCLIntObject(1), + "right": objpkg.KCLIntObject(1), + "code": vm_code.Opcode.COMPARE_GREATER_THAN_OR_EQUAL_TO, + "expected": objpkg.TRUE_INSTANCE, + }, + ] + for case in cases: + left, right, code, expected = ( + case["left"], + case["right"], + case["code"], + case["expected"], + ) + result = self._evaluator.eval_compare_op(left, right, code) + self.assertEqual(result, expected) + + def test_eval_compare_op_invalid(self): + cases = [ + {"left": None, "right": None, "code": vm_code.Opcode.BINARY_ADD}, + { + "left": objpkg.KCLIntObject(1), + "right": objpkg.KCLIntObject(1), + "code": vm_code.Opcode.NOP, + }, + ] + for case in cases: + left, right, code = case["left"], case["right"], case["code"] + with self.assertRaises(Exception): + self._evaluator.eval_compare_op(left, right, code) + + def test_eval_unary_op(self): + cases = [ + { + "operand": objpkg.KCLIntObject(1), + "code": vm_code.Opcode.UNARY_POSITIVE, + "expected": objpkg.KCLIntObject(1), + }, + { + "operand": objpkg.KCLIntObject(1), + "code": vm_code.Opcode.UNARY_NEGATIVE, + "expected": objpkg.KCLIntObject(-1), + }, + ] + for case in cases: + operand, code, expected = ( + case["operand"], + case["code"], + case["expected"], + ) + result = self._evaluator.eval_unary_op(operand, code) + self.assertEqual(result, expected) + + def test_eval_unary_op_invalid(self): + cases = [ + {"operand": None, "code": vm_code.Opcode.BINARY_ADD}, + { + "operand": objpkg.KCLIntObject(1), + "code": vm_code.Opcode.NOP, + }, + ] + for case in cases: + operand, code = case["operand"], case["code"] + with self.assertRaises(Exception): + self._evaluator.eval_unary_op(operand, code) + + def test_set_item(self): + cases = [ + { + "operand": objpkg.to_kcl_obj({"key": "value"}), + "item": objpkg.KCLStringObject("key"), + "value": objpkg.KCLStringObject("override"), + "expected": objpkg.to_kcl_obj({"key": "override"}), + }, + { + "operand": objpkg.to_kcl_obj([0, 0]), + "item": objpkg.KCLIntObject(0), + "value": objpkg.KCLIntObject(1), + "expected": objpkg.to_kcl_obj([1, 0]), + }, + ] + for case in cases: + operand, item, value, expected = case["operand"], case["item"], case["value"], case["expected"] + result = self._evaluator.set_item(operand, item, value) + self.assertEqual(result, expected) + + def test_format_value(self): + cases = [ + { + "operand": objpkg.to_kcl_obj({"key": "value"}), + "format_spec": None, + "expected": objpkg.KCLStringObject(value="{'key': 'value'}"), + }, + { + "operand": objpkg.to_kcl_obj({"key": "value"}), + "format_spec": "#json", + "expected": objpkg.KCLStringObject(value='{"key": "value"}'), + }, + { + "operand": objpkg.to_kcl_obj({"key": "value"}), + "format_spec": "#yaml", + "expected": objpkg.KCLStringObject(value='key: value\n'), + }, + ] + for case in cases: + operand, format_spec, expected = case["operand"], case["format_spec"], case["expected"] + result = self._evaluator.format_value(operand, format_spec) + self.assertEqual(result, expected) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_vm/test_planner.py b/test/test_units/test_kclvm/test_vm/test_planner.py new file mode 100644 index 000000000..b1c2f4156 --- /dev/null +++ b/test/test_units/test_kclvm/test_vm/test_planner.py @@ -0,0 +1,46 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import unittest + +import kclvm.api.object as objpkg +import kclvm.vm.planner as planner + + +class PlannerTest(unittest.TestCase): + def test_object_planner(self): + cases = [ + {"obj": {"key": 1}, "expected": {"key": 1}}, + { + "obj": {"key": [1, 2, 3]}, + "expected": {"key": [1, 2, 3]}, + }, + { + "obj": {"key": True}, + "expected": {"key": True}, + }, + { + "obj": {"key": {"key": 1}}, + "expected": {"key": {"key": 1}}, + }, + ] + for case in cases: + obj, expected = case["obj"], case["expected"] + self.assertEqual( + planner.ObjectPlanner().to_python(obj), + expected, + msg=f"{obj}", + ) + self.assertEqual( + planner.ObjectPlanner().to_python(objpkg.to_kcl_obj(obj)), + expected, + msg=f"{obj}", + ) + self.assertEqual( + planner.ObjectPlanner().to_python(objpkg.to_kcl_obj(obj).value), + expected, + msg=f"{obj}", + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_kclvm/test_vm/test_vm.py b/test/test_units/test_kclvm/test_vm/test_vm.py new file mode 100644 index 000000000..5f7cff5de --- /dev/null +++ b/test/test_units/test_kclvm/test_vm/test_vm.py @@ -0,0 +1,71 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import pathlib +import unittest + +import kclvm.kcl.error as kcl_error +import kclvm.vm as vm +import kclvm.api.object as kcl_object +import kclvm.compiler.parser as parser +import kclvm.compiler.build.compiler as compiler + +from kclvm.api.object import KCLCompiledFunctionObject, Parameter + + +class VMTest(unittest.TestCase): + def get_vm(self, filepath: str) -> vm.VirtualMachine: + program = compiler.CompileProgram(parser.LoadProgram(filepath)) + return vm.VirtualMachine(program) + + def test_vm_run(self): + filepath = str( + pathlib.Path(__file__).parent.joinpath("test_data").joinpath("main.k") + ) + machine = self.get_vm(filepath) + result = machine.Run() + self.assertEqual(result.filename, filepath) + self.assertEqual(list(result.m.keys()), ["person", "@pkg"]) + self.assertEqual(list(machine.state.modules.keys()), ["pkg"]) + self.assertEqual(list(machine.all_schema_types.keys()), ["pkg.Person"]) + self.assertEqual(machine.last_popped_obj(), kcl_object.NONE_INSTANCE) + + def test_vm_invalid_run(self): + filepaths = [ + str( + pathlib.Path(__file__) + .parent.joinpath("invalid_test_data") + .joinpath("unification.k") + ), + str( + pathlib.Path(__file__) + .parent.joinpath("invalid_test_data") + .joinpath("recursive.k") + ), + ] + for filepath in filepaths: + with self.assertRaises(kcl_error.KCLException): + self.get_vm(filepath).Run() + + def test_default_not_full(self): + app = kcl_object.KCLProgram() + app.pkgs = {"testpkg": kcl_object.KCLBytecode()} + app.main = "testpkg" + test_vm = vm.VirtualMachine(app=app) + + test_f = vm.Frame() + test_f.locals = {} + test_f.globals = {} + test_vm.ctx = test_f + + test_func = KCLCompiledFunctionObject(name="test_function") + test_func.params = [ + Parameter(name="test_name", value=kcl_object.to_kcl_obj(10)) + ] + test_vm.push_frame_using_callable( + pkgpath="test_pkg", func=test_func, args=[], kwargs=[] + ) + self.assertEqual(test_vm.ctx.locals["test_name"], kcl_object.to_kcl_obj(10)) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_langserver/__init__.py b/test/test_units/test_langserver/__init__.py new file mode 100644 index 000000000..378661dd8 --- /dev/null +++ b/test/test_units/test_langserver/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- + diff --git a/test/test_units/test_langserver/test_common.py b/test/test_units/test_langserver/test_common.py new file mode 100644 index 000000000..8f8be141e --- /dev/null +++ b/test/test_units/test_langserver/test_common.py @@ -0,0 +1,182 @@ +# Copyright 2021 The KCL Authors. All rights reserved. +import unittest +import pathlib +from typing import Optional + +import kclvm.tools.langserver.common as common +import kclvm.kcl.ast as ast +import kclvm.kcl.types.scope as scope + +_TEST_PATH_NAME = "test_data" +_GO_TO_DEF_PATH_NAME = "go_to_def" +_DIR_PATH = ( + pathlib.Path(__file__) + .parent.joinpath(_TEST_PATH_NAME) + .joinpath(_GO_TO_DEF_PATH_NAME) +) + + +class PosToScopeTest(unittest.TestCase): + def test_pos_to_scope(self): + # check invalid pos + pos = ast.Position(line=0, column=0) + _, got, _ = common.pos_to_scope(pos) + self.assertIsNone( + got, f"find scope from invalid position {pos}, expect: None, got: {got}" + ) + + file_path = "invalid_path" + _, got, _ = common.pos_to_scope( + pos=ast.Position(filename=file_path, line=1, column=1) + ) + self.assertIsNone( + got, + f"find scope from invalid file path {file_path}, expect: None, got: {got}", + ) + + file_path = str(_DIR_PATH / "schema.k") + prog = common.file_to_prog(file_path) + prog.pkgs['__main__'][0].body[0].body[0].type_str = None + + _, got = common.file_or_prog_to_scope(prog, file_path) + self.assertIsInstance( + got, + scope.ProgramScope, + f"find scope from invalid prog {file_path}, expect: None, got: {got}" + ) + + +class PosToNodeTestCase: + def __init__( + self, + filename: str, + line: int, + column: int, + name: Optional[str] = None, + start_pos: Optional[ast.Position] = None, + end_pos: Optional[ast.Position] = None, + ): + self.filename: str = filename + self.line: int = line + self.column: int = column + self.name: Optional[str] = name + self.start_pos: Optional[ast.Position] = start_pos + self.end_pos: Optional[ast.Position] = end_pos + + +class PosToNodeTest(unittest.TestCase): + _cases = [ + PosToNodeTestCase( + filename="member_access.k", + line=6, + column=1, + name="name", + start_pos=ast.Position(line=6, column=1), + end_pos=ast.Position(line=6, column=5), + ), + PosToNodeTestCase( + filename="schema.k", + line=5, + column=9, + name="Person", + start_pos=ast.Position(line=5, column=9), + end_pos=ast.Position(line=5, column=15), + ), + PosToNodeTestCase( + filename="simple.k", + line=2, + column=5, + name="d", + start_pos=ast.Position(line=2, column=5), + end_pos=ast.Position(line=2, column=6), + ), + PosToNodeTestCase( + filename="invalid_grammar.k", + line=1, + column=1, + ), + PosToNodeTestCase( + filename="", + line=0, + column=0, + ), + PosToNodeTestCase( + filename="invalid_path.k", + line=1, + column=1, + ), + PosToNodeTestCase( + filename="simple.k", + line=1, + column=3, + ), + ] + + def test_pos_to_node(self): + for t_case in self._cases: + _, node = common.pos_to_node( + pos=ast.Position( + filename=str(_DIR_PATH / t_case.filename), + line=t_case.line, + column=t_case.column, + ), + ) + if node: + self.assertEqual( + str(_DIR_PATH / t_case.filename), + node.filename, + msg="filename not match", + ) + self.assertEqual( + t_case.start_pos.line, + node.line, + msg=f"start line not match. filename={t_case.filename}", + ) + self.assertEqual( + t_case.start_pos.column, + node.column, + msg=f"start column not match. filename={t_case.filename}", + ) + self.assertEqual( + t_case.end_pos.line, + node.end_line, + msg=f"end line not match. filename={t_case.filename}", + ) + self.assertEqual( + t_case.end_pos.column, + node.end_column, + msg=f"end column not match. filename={t_case.filename}", + ) + if isinstance(node, ast.Name): + self.assertEqual( + t_case.name, + node.value, + msg=f"node value not match. filename={t_case.filename}", + ) + else: + self.assertIsNone( + t_case.name, + msg=f"node value not match, filename: {t_case.filename}", + ) + self.assertIsNone( + t_case.start_pos, + msg=f"node start pos not match, filename: {t_case.filename}", + ) + self.assertIsNone( + t_case.end_pos, + msg=f"node end pos not match, filename: {t_case.filename}", + ) + + +class ScopeObjToLocationTest(unittest.TestCase): + def test_scope_obj_to_location_None_input(self): + self.assertIsNone(common.scope_obj_to_location(None)) + self.assertIsNone(common.scope_obj_to_location(scope.ScopeObject(name="mock",node=None,type=None))) + + def test_file_to_location_invalid_path(self): + self.assertIsNone(common.file_to_location(filepath="")) + self.assertIsNone(common.file_to_location(filepath="mock.txt")) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_langserver/test_complete.py b/test/test_units/test_langserver/test_complete.py new file mode 100644 index 000000000..bed0bce17 --- /dev/null +++ b/test/test_units/test_langserver/test_complete.py @@ -0,0 +1,92 @@ +import pathlib +import unittest +import typing + +from pygls.lsp.types.language_features.completion import ( + CompletionList, + CompletionItem, + CompletionItemKind, +) + +import kclvm.kcl.ast as ast +from kclvm.tools.langserver.complete import complete + + +_TEST_PATH_NAME = "test_data" +_COMPLETE_PATH_NAME = "complete" +_DIR_PATH = ( + pathlib.Path(__file__) + .parent.joinpath(_TEST_PATH_NAME) + .joinpath(_COMPLETE_PATH_NAME) +) + + +class CompleteTest(unittest.TestCase): + def test_complete(self): + for t_case in test_cases: + got_completions = complete( + pos=ast.Position( + filename=str(_DIR_PATH / t_case.filename), + line=t_case.line, + column=t_case.column, + ), + name=t_case.name, + ) + + expect_completions = t_case.completions + + self.assertEqual( + expect_completions, + got_completions, + f"expect: {expect_completions}, got: {got_completions}", + ) + + +class CompletionTestCase: + def __init__( + self, + filename: str, + line: int, + column: int, + name: str, + completions: typing.List[CompletionItem], + ): + self.filename: str = filename + self.line: int = line + self.column: int = column + self.name: str = name + self.completions: typing.List[CompletionItem] = completions + + +test_cases = [ + CompletionTestCase( + filename="simple.k", + line=2, + column=6, + name="a", + completions=CompletionList( + is_incomplete=False, + items=[ + CompletionItem(label="aa", kind=CompletionItemKind.Value), + CompletionItem(label="abs", kind=CompletionItemKind.Function), + CompletionItem(label="all_true", kind=CompletionItemKind.Function), + CompletionItem(label="any_true", kind=CompletionItemKind.Function), + ], + ).items, + ), + CompletionTestCase( + filename="schema.k", + line=4, + column=8, + name="Per", + completions=CompletionList( + is_incomplete=False, + items=[ + CompletionItem(label="Person", kind=CompletionItemKind.Struct), + ], + ).items, + ), +] + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_langserver/test_data/complete/schema.k b/test/test_units/test_langserver/test_data/complete/schema.k new file mode 100644 index 000000000..7a38bc357 --- /dev/null +++ b/test/test_units/test_langserver/test_data/complete/schema.k @@ -0,0 +1,4 @@ +schema Person: + name: str + +p = Per \ No newline at end of file diff --git a/test/test_units/test_langserver/test_data/complete/simple.k b/test/test_units/test_langserver/test_data/complete/simple.k new file mode 100644 index 000000000..a8bdfc1cd --- /dev/null +++ b/test/test_units/test_langserver/test_data/complete/simple.k @@ -0,0 +1,2 @@ +aa = 2 +b = a diff --git a/test/test_units/test_langserver/test_data/document_symbol/invalid_grammar.k b/test/test_units/test_langserver/test_data/document_symbol/invalid_grammar.k new file mode 100644 index 000000000..2199c03be --- /dev/null +++ b/test/test_units/test_langserver/test_data/document_symbol/invalid_grammar.k @@ -0,0 +1,2 @@ +a = 1 +b = diff --git a/test/test_units/test_langserver/test_data/document_symbol/invalid_semantic.k b/test/test_units/test_langserver/test_data/document_symbol/invalid_semantic.k new file mode 100644 index 000000000..b7fe1e3f7 --- /dev/null +++ b/test/test_units/test_langserver/test_data/document_symbol/invalid_semantic.k @@ -0,0 +1,2 @@ +a = b +c = a diff --git a/test/test_units/test_langserver/test_data/document_symbol/symbol.k b/test/test_units/test_langserver/test_data/document_symbol/symbol.k new file mode 100644 index 000000000..0dc451e75 --- /dev/null +++ b/test/test_units/test_langserver/test_data/document_symbol/symbol.k @@ -0,0 +1,12 @@ +a = b = 1 + +schema Person: + mixin [ + aMixin + ] + age : int = 1 + name : str = "1" + +person = Person { + age = 1 +} \ No newline at end of file diff --git a/test/test_units/test_langserver/test_data/go_to_def/attr.k b/test/test_units/test_langserver/test_data/go_to_def/attr.k new file mode 100644 index 000000000..69f759620 --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/attr.k @@ -0,0 +1,17 @@ +alice_first_name = "alice" + +schema Name: + first: str + last: str + +schema Person: + name: Name + name.first = "a" + name.last = "b" + +alice = Person { + name: { + first = alice_first_name + last = "Green" + } +} diff --git a/test/test_units/test_langserver/test_data/go_to_def/dict_fix_me.k b/test/test_units/test_langserver/test_data/go_to_def/dict_fix_me.k new file mode 100644 index 000000000..3331f7207 --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/dict_fix_me.k @@ -0,0 +1,24 @@ +schema Person: + name: Name + age: int + additional: {str: Info} + +schema Info: + value: str + +schema Name: + first: str + last: str + +alice: Person = { + name: { + first: "alice" + last: "Green" + } + age: 10 + additional: { + hobby: { + value: "piano" + } + } +} \ No newline at end of file diff --git a/test/test_units/test_langserver/test_data/go_to_def/import_module.k b/test/test_units/test_langserver/test_data/go_to_def/import_module.k new file mode 100644 index 000000000..786ce6978 --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/import_module.k @@ -0,0 +1,9 @@ +import .pkg.parent as p + +alice = p.Parent { + name: "alice" +} + +bob = p.Parent_Typo { + name: "alice" +} \ No newline at end of file diff --git a/test/test_units/test_langserver/test_data/go_to_def/inherit.k b/test/test_units/test_langserver/test_data/go_to_def/inherit.k new file mode 100644 index 000000000..a1bef7126 --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/inherit.k @@ -0,0 +1,10 @@ +schema Parent: + name: str + +schema Son(Parent): + age: int + son_name: str = name + +schema GrandSon(Son): + grand_son_name: str = name + a: str = name_not_exist diff --git a/test/test_units/test_langserver/test_data/go_to_def/inherit_pkg.k b/test/test_units/test_langserver/test_data/go_to_def/inherit_pkg.k new file mode 100644 index 000000000..39cdf9b1d --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/inherit_pkg.k @@ -0,0 +1,4 @@ +import pkg + +schema Son(pkg.Parent): + son_name: str = name diff --git a/test/test_units/test_langserver/test_data/go_to_def/invalid_grammar.k b/test/test_units/test_langserver/test_data/go_to_def/invalid_grammar.k new file mode 100644 index 000000000..2199c03be --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/invalid_grammar.k @@ -0,0 +1,2 @@ +a = 1 +b = diff --git a/test/test_units/test_langserver/test_data/go_to_def/invalid_semantic.k b/test/test_units/test_langserver/test_data/go_to_def/invalid_semantic.k new file mode 100644 index 000000000..b7fe1e3f7 --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/invalid_semantic.k @@ -0,0 +1,2 @@ +a = b +c = a diff --git a/test/test_units/test_langserver/test_data/go_to_def/kcl.mod b/test/test_units/test_langserver/test_data/go_to_def/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_units/test_langserver/test_data/go_to_def/list_comp.k b/test/test_units/test_langserver/test_data/go_to_def/list_comp.k new file mode 100644 index 000000000..63a9fdc7c --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/list_comp.k @@ -0,0 +1,7 @@ +a = e = 1 + +schema S: + a: str + b: [str] + c: [str] = [a for a in b] + d: int = e diff --git a/test/test_units/test_langserver/test_data/go_to_def/member_access.k b/test/test_units/test_langserver/test_data/go_to_def/member_access.k new file mode 100644 index 000000000..a1ddd16fd --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/member_access.k @@ -0,0 +1,6 @@ +schema Person: + name: str = "default" + +alice = Person{} + +name = alice.name \ No newline at end of file diff --git a/test/test_units/test_langserver/test_data/go_to_def/parent_attr.k b/test/test_units/test_langserver/test_data/go_to_def/parent_attr.k new file mode 100644 index 000000000..5e0a6924c --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/parent_attr.k @@ -0,0 +1,9 @@ +import pkg + +schema Son(pkg.Parent): + age: int + +alice = Son { + name: "alice" + age: 10 +} \ No newline at end of file diff --git a/test/test_units/test_langserver/test_data/go_to_def/pkg/import_abs.k b/test/test_units/test_langserver/test_data/go_to_def/pkg/import_abs.k new file mode 100644 index 000000000..ed9885a69 --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/pkg/import_abs.k @@ -0,0 +1,5 @@ +import pkg.parent + +alice = parent.Parent { + name: "alice" +} \ No newline at end of file diff --git a/test/test_units/test_langserver/test_data/go_to_def/pkg/parent.k b/test/test_units/test_langserver/test_data/go_to_def/pkg/parent.k new file mode 100644 index 000000000..10a407aa2 --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/pkg/parent.k @@ -0,0 +1,2 @@ +schema Parent: + name: str diff --git a/test/test_units/test_langserver/test_data/go_to_def/schema.k b/test/test_units/test_langserver/test_data/go_to_def/schema.k new file mode 100644 index 000000000..0a9e7c301 --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/schema.k @@ -0,0 +1,7 @@ +schema Person: + name: str + info: str = name + +alice = Person { + name: "alice" +} \ No newline at end of file diff --git a/test/test_units/test_langserver/test_data/go_to_def/schema_index_signature.k b/test/test_units/test_langserver/test_data/go_to_def/schema_index_signature.k new file mode 100644 index 000000000..30032f733 --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/schema_index_signature.k @@ -0,0 +1,10 @@ +schema Data: + [name: str]: str + + check: + name.islower() + +data = Data { + key: "value" +} +value = data.key diff --git a/test/test_units/test_langserver/test_data/go_to_def/simple.k b/test/test_units/test_langserver/test_data/go_to_def/simple.k new file mode 100644 index 000000000..49f7694af --- /dev/null +++ b/test/test_units/test_langserver/test_data/go_to_def/simple.k @@ -0,0 +1,2 @@ +d = 1 +c = d \ No newline at end of file diff --git a/test/test_units/test_langserver/test_data/hover/built_in.k b/test/test_units/test_langserver/test_data/hover/built_in.k new file mode 100644 index 000000000..596bd3e83 --- /dev/null +++ b/test/test_units/test_langserver/test_data/hover/built_in.k @@ -0,0 +1,42 @@ +import base64 +import crypto + +# str built in +a = "hello World".capitalize() +b = "hello world".islower() +c = "Hello World".islower() +d = "Hello world, Hello world".count("o") +e = "Hello world, Hello world".count("Hello", 5, 20) +f = "Hello World".endswith("rld") +g = "Hello World".endswith("rld", 0, 9) + + +# base 64 +decode = base64.decode("MC4zLjA=") + + +# crypto +md5 = crypto.md5("ABCDEF") +sha = crypto.sha1("ABCDEF") + + +# regex +regex_result = regex.match("192.168.0.1", "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."+"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$") + + +# default +h = max(80, 100, 1000) +i = max(-80, -20, -10) + + +# type of +none1 = typeof(None) + +# option +j = option("a", default=42, help="set a value") + +# deprecated +@deprecated(version="1.2.3", reason="schema-deprecated", strict=False) +schema Person: + @deprecated(version="4.5.6", reason="attr-deprecated", strict=True) + name: str \ No newline at end of file diff --git a/test/test_units/test_langserver/test_data/hover/hello.k b/test/test_units/test_langserver/test_data/hover/hello.k new file mode 100644 index 000000000..b0921f6a2 --- /dev/null +++ b/test/test_units/test_langserver/test_data/hover/hello.k @@ -0,0 +1,12 @@ +schema Server: + name: str + image: str + replica: int = 1 + +myServer = Server { + name: "demo" + image: "demo image" + replica: 10 +} + +n = myServer.name \ No newline at end of file diff --git a/test/test_units/test_langserver/test_data/hover/import.k b/test/test_units/test_langserver/test_data/hover/import.k new file mode 100644 index 000000000..6c1c8a97d --- /dev/null +++ b/test/test_units/test_langserver/test_data/hover/import.k @@ -0,0 +1 @@ +import .hello \ No newline at end of file diff --git a/test/test_units/test_langserver/test_data/hover/incomplete.k b/test/test_units/test_langserver/test_data/hover/incomplete.k new file mode 100644 index 000000000..3b7d46a4d --- /dev/null +++ b/test/test_units/test_langserver/test_data/hover/incomplete.k @@ -0,0 +1,7 @@ +schema ProviderConfig: + __settings__?: {str:str} = {"output_type": "INLINE"} + +schema AConfig(ProviderConfig): +schema BConfig(ProviderConfig): + access_key?: str = option("ak") + secret_key?: str = option("sk") \ No newline at end of file diff --git a/test/test_units/test_langserver/test_document_symbol.py b/test/test_units/test_langserver/test_document_symbol.py new file mode 100644 index 000000000..e9b873d6a --- /dev/null +++ b/test/test_units/test_langserver/test_document_symbol.py @@ -0,0 +1,174 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import unittest +import pathlib + +from pygls.lsp.types.basic_structures import Range, Position +from pygls.lsp.types.language_features.document_symbol import DocumentSymbol, SymbolKind + +import kclvm.tools.langserver.document_symbol as symbol + +_DOCUMENT_SYMBOL_DIR = pathlib.Path(__file__).parent.joinpath( + "test_data/document_symbol" +) + + +class DocumentSymbolTest(unittest.TestCase): + def test_file_to_symbol(self): + expect_result = [ + DocumentSymbol( + name="a", + kind=SymbolKind.Variable, + range=Range( + start=Position(line=0, character=0), + end=Position(line=0, character=9), + ), + selection_range=Range( + start=Position(line=0, character=0), + end=Position(line=0, character=1), + ), + ), + DocumentSymbol( + name="b", + kind=SymbolKind.Variable, + range=Range( + start=Position(line=0, character=0), + end=Position(line=0, character=9), + ), + selection_range=Range( + start=Position(line=0, character=4), + end=Position(line=0, character=5), + ), + ), + DocumentSymbol( + name="Person", + kind=SymbolKind.Struct, + range=Range( + start=Position(line=2, character=0), + end=Position(line=7, character=20), + ), + selection_range=Range( + start=Position(line=2, character=7), + end=Position(line=2, character=13), + ), + children=[ + DocumentSymbol( + name="mixin", + kind=SymbolKind.Property, + range=Range( + start=Position(line=3, character=4), + end=Position(line=3, character=9), + ), + selection_range=Range( + start=Position(line=3, character=4), + end=Position(line=3, character=9), + ), + children=[ + DocumentSymbol( + name="aMixin", + kind=SymbolKind.Variable, + range=Range( + start=Position(line=4, character=8), + end=Position(line=4, character=14), + ), + selection_range=Range( + start=Position(line=4, character=8), + end=Position(line=4, character=14), + ), + ) + ], + ), + DocumentSymbol( + name="age", + kind=SymbolKind.Property, + range=Range( + start=Position(line=6, character=4), + end=Position(line=6, character=17), + ), + selection_range=Range( + start=Position(line=6, character=4), + end=Position(line=6, character=7), + ), + ), + DocumentSymbol( + name="name", + kind=SymbolKind.Property, + range=Range( + start=Position(line=7, character=4), + end=Position(line=7, character=20), + ), + selection_range=Range( + start=Position(line=7, character=4), + end=Position(line=7, character=8), + ), + ), + ], + ), + DocumentSymbol( + name="person", + kind=SymbolKind.Variable, + range=Range( + start=Position(line=9, character=0), + end=Position(line=11, character=1), + ), + selection_range=Range( + start=Position(line=9, character=0), + end=Position(line=9, character=6), + ), + ), + ] + symbols = symbol.document_symbol(_DOCUMENT_SYMBOL_DIR.joinpath("symbol.k")) + self.check_result(expect_result, symbols) + + def test_invalid_grammar(self): + symbols = symbol.document_symbol( + _DOCUMENT_SYMBOL_DIR.joinpath("invalid_grammar.k") + ) + assert ( + not symbols + ), "invalid grammar should got empty document symbol result for now" + + def test_invalid_semantic(self): + expect_result = [ + DocumentSymbol( + name="a", + kind=SymbolKind.Variable, + range=Range( + start=Position(line=0, character=0), + end=Position(line=0, character=5), + ), + selection_range=Range( + start=Position(line=0, character=0), + end=Position(line=0, character=1), + ), + ), + DocumentSymbol( + name="c", + kind=SymbolKind.Variable, + range=Range( + start=Position(line=1, character=0), + end=Position(line=1, character=5), + ), + selection_range=Range( + start=Position(line=1, character=0), + end=Position(line=1, character=1), + ), + ), + ] + symbols = symbol.document_symbol( + _DOCUMENT_SYMBOL_DIR.joinpath("invalid_semantic.k") + ) + self.check_result(expect_result, symbols) + + def check_result(self, expect, got): + self.assertEqual( + len(expect), + len(got), + f"inconsistent number of symbols found, expect: {len(expect)}, got: {len(got)}", + ) + for i, s in enumerate(got): + self.assertEqual(expect[i], s, f"expect: {expect[i]}, got: {s}") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_langserver/test_go_to_def.py b/test/test_units/test_langserver/test_go_to_def.py new file mode 100644 index 000000000..14e0f65c4 --- /dev/null +++ b/test/test_units/test_langserver/test_go_to_def.py @@ -0,0 +1,460 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import pathlib +import unittest + +from typing import List +from pygls.lsp.types.basic_structures import Location, Range, Position + +import kclvm.kcl.ast as ast +import kclvm.tools.langserver.common as common +import kclvm.tools.langserver.go_to_def as go_to_def + + +_TEST_PATH_NAME = "test_data" +_GO_TO_DEF_PATH_NAME = "go_to_def" +_DIR_PATH = ( + pathlib.Path(__file__) + .parent.joinpath(_TEST_PATH_NAME) + .joinpath(_GO_TO_DEF_PATH_NAME) +) + + +class GoToDefTestCase: + def __init__( + self, + filename: str, + line: int, + column: int, + locations: List[Location], + ): + self.filename: str = filename + self.line: int = line + self.column: int = column + self.locations: List[Location] = locations + + +class GoToDefTest(unittest.TestCase): + test_cases = [ + GoToDefTestCase( + filename="dict_fix_me.k", + line=15, + column=9, + locations=[ + Location( + uri="dict_fix_me.k", + range=Range( + start=Position(line=14, character=8), + end=Position(line=14, character=13), + ), + # range=Range( + # start=Position(line=9, character=4), + # end=Position(line=9, character=9), + # ), + ) + ], + ), + GoToDefTestCase( + filename="dict_fix_me.k", + line=14, + column=6, + locations=[ + Location( + uri="dict_fix_me.k", + range=Range( + start=Position(line=13, character=4), + end=Position(line=13, character=8), + ), + # range=Range( + # start=Position(line=1, character=4), + # end=Position(line=1, character=8), + # ), + ) + ], + ), + GoToDefTestCase( + filename="import_module.k", + line=3, + column=11, + locations=[ + Location( + uri="pkg/parent.k", + range=Range( + start=Position(line=0, character=7), + end=Position(line=0, character=13), + ), + ) + ], + ), + GoToDefTestCase( + filename="pkg/import_abs.k", + line=4, + column=5, + locations=[ + Location( + uri="pkg/parent.k", + range=Range( + start=Position(line=1, character=4), + end=Position(line=1, character=8), + ), + ) + ], + ), + GoToDefTestCase( + filename="pkg/import_abs.k", + line=1, + column=12, + locations=[ + Location( + uri="pkg/parent.k", + range=Range( + start=Position(line=0, character=0), + end=Position(line=0, character=0), + ), + ) + ], + ), + GoToDefTestCase( + filename="simple.k", + line=2, + column=5, + locations=[ + Location( + uri="simple.k", + range=Range( + start=Position(line=0, character=0), + end=Position(line=0, character=1), + ), + ) + ], + ), + GoToDefTestCase( + filename="schema.k", + line=3, + column=17, + locations=[ + Location( + uri="schema.k", + range=Range( + start=Position(line=1, character=4), + end=Position(line=1, character=8), + ), + ) + ], + ), + GoToDefTestCase( + filename="import_module.k", + line=1, + column=13, + locations=[ + Location( + uri="pkg/parent.k", + range=common.emptyRange(), + ) + ], + ), + GoToDefTestCase( + filename="import_module.k", + line=3, + column=11, + locations=[ + Location( + uri="pkg/parent.k", + range=Range( + start=Position(line=0, character=7), + end=Position(line=0, character=13), + ), + ) + ], + ), + GoToDefTestCase( + filename="import_module.k", + line=3, + column=9, + locations=[ + Location( + uri="import_module.k", + range=Range( + start=Position(line=0, character=22), + end=Position(line=0, character=23), + ), + ) + ], + ), + GoToDefTestCase( + filename="import_module.k", + line=1, + column=23, + locations=[ + Location( + uri="import_module.k", + range=Range( + start=Position(line=0, character=22), + end=Position(line=0, character=23), + ), + ) + ], + ), + GoToDefTestCase( + filename="import_module.k", + line=7, + column=9, + locations=[], + ), + GoToDefTestCase( + filename="attr.k", + line=9, + column=10, + locations=[ + Location( + uri="attr.k", + range=Range( + start=Position(line=3, character=4), + end=Position(line=3, character=9), + ), + ) + ], + ), + GoToDefTestCase( + filename="attr.k", + line=14, + column=17, + locations=[ + Location( + uri="attr.k", + range=Range( + start=Position(line=0, character=0), + end=Position(line=0, character=16), + ), + ) + ], + ), + GoToDefTestCase( + filename="inherit.k", + line=1, + column=8, + locations=[ + Location( + uri="inherit.k", + range=Range( + start=Position(line=0, character=7), + end=Position(line=0, character=13), + ), + ) + ], + ), + GoToDefTestCase( + filename="inherit.k", + line=9, + column=27, + locations=[ + Location( + uri="inherit.k", + range=Range( + start=Position(line=1, character=4), + end=Position(line=1, character=8), + ), + ) + ], + ), + GoToDefTestCase( + filename="inherit.k", + line=10, + column=14, + locations=[], + ), + GoToDefTestCase( + filename="schema.k", + line=5, + column=9, + locations=[ + Location( + uri="schema.k", + range=Range( + start=Position(line=0, character=7), + end=Position(line=0, character=13), + ), + ) + ], + ), + GoToDefTestCase( + filename="schema_index_signature.k", + line=5, + column=9, + locations=[ + Location( + uri="schema_index_signature.k", + range=Range( + start=Position(line=1, character=5), + end=Position(line=1, character=9), + ), + ) + ], + ), + GoToDefTestCase( + filename="parent_attr.k", + line=7, + column=5, + locations=[ + Location( + uri="pkg/parent.k", + range=Range( + start=Position(line=1, character=4), + end=Position(line=1, character=8), + ), + ) + ], + ), + GoToDefTestCase( + filename="list_comp.k", + line=6, + column=17, + locations=[ + Location( + uri="list_comp.k", + range=Range( + start=Position(line=5, character=22), + end=Position(line=5, character=23), + ), + ) + ], + ), + GoToDefTestCase( + filename="list_comp.k", + line=7, + column=14, + # name="e", + locations=[ + Location( + uri="list_comp.k", + range=Range( + start=Position(line=0, character=4), + end=Position(line=0, character=5), + ), + ) + ], + ), + GoToDefTestCase( + filename="inherit.k", + line=6, + column=21, + locations=[ + Location( + uri="inherit.k", + range=Range( + start=Position(line=1, character=4), + end=Position(line=1, character=8), + ), + ) + ], + ), + GoToDefTestCase( + filename="inherit_pkg.k", + line=4, + column=21, + locations=[ + Location( + uri=str(pathlib.Path("pkg") / "parent.k"), + range=Range( + start=Position(line=1, character=4), + end=Position(line=1, character=8), + ), + ) + ], + ), + GoToDefTestCase( + filename="inherit_pkg.k", + line=3, + column=12, + locations=[ + Location( + uri=str("inherit_pkg.k"), + range=Range( + start=Position(line=0, character=7), + end=Position(line=0, character=10), + ), + ) + ], + ), + GoToDefTestCase( + filename="inherit_pkg.k", + line=7, + column=1, + locations=[], + ), + GoToDefTestCase( + filename="invalid_semantic.k", + line=2, + column=5, + locations=[ + Location( + uri="invalid_semantic.k", + range=Range( + start=Position(line=0, character=0), + end=Position(line=0, character=1), + ), + ) + ], + ), + GoToDefTestCase( + filename="invalid_grammar.k", + line=1, + column=1, + locations=[], + ), + ] + + def test_go_to_def(self): + for i, t_case in enumerate(self.test_cases): + got_locations = go_to_def.go_to_def( + pos=ast.Position( + filename=str(_DIR_PATH / t_case.filename), + line=t_case.line, + column=t_case.column, + ), + ) + expect_locations = [ + Location(uri=str(_DIR_PATH / loc.uri), range=loc.range) + for loc in t_case.locations + ] + self.assertEqual( + expect_locations, + got_locations, + f"err go to def for case[{i}], from pos:{t_case.filename}:{t_case.line}:{t_case.column}\nexpect: {expect_locations}, got: {got_locations}", + ) + + +class NoneInputTest(unittest.TestCase): + def test_find_declaration_None_input(self): + self.assertIsNone(go_to_def.find_declaration(None, None, None)) + + def test_find_declaration_by_scope_obj_None_input(self): + self.assertIsNone( + go_to_def.find_declaration_by_scope_obj(None, None, None, None) + ) + + def test_find_declaration_obj_by_pos_and_name_None_input(self): + self.assertIsNone( + go_to_def.find_declaration_obj_by_pos_and_name(None, None, None) + ) + + def test_find_inner_name_None_input(self): + self.assertIsNone(go_to_def.find_inner_name(None, None, None)) + + def test_find_attr_by_name_None_input(self): + self.assertIsNone(go_to_def.find_attr_by_name(None, None, None)) + + +def test_find_declaration_obj_by_pos_and_name(): + pos: ast.Position = ast.Position(filename="simple.k", line=2, column=5) + _, prog_scope = common.file_or_prog_to_scope(file_path=_DIR_PATH / "simple.k") + assert ( + go_to_def.find_declaration_obj_by_pos_and_name( + pos=pos, name="d", prog_scope=prog_scope + ) + is None + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/test/test_units/test_langserver/test_hover.py b/test/test_units/test_langserver/test_hover.py new file mode 100644 index 000000000..9eac125b9 --- /dev/null +++ b/test/test_units/test_langserver/test_hover.py @@ -0,0 +1,161 @@ +import pathlib +import unittest +import typing + +from pygls.lsp.types.basic_structures import MarkupContent, MarkupKind, Range, Position +import pygls.lsp.types.language_features.hover as pygls_hover + +import kclvm.kcl.ast as ast +import kclvm.tools.langserver.hover as hover +import kclvm.tools.langserver.go_to_def as go_to_def + + +_TEST_PATH_NAME = "test_data" +_HOVER_PATH_NAME = "hover" +_DIR_PATH = ( + pathlib.Path(__file__).parent.joinpath(_TEST_PATH_NAME).joinpath(_HOVER_PATH_NAME) +) + + +class HoverTestCase: + def __init__( + self, + filename: str, + line: int, + column: int, + msg: str = None, + range: typing.Optional[Range] = None, + no_result: bool = False, + ): + self.filename: str = filename + self.line: int = line + self.column: int = column + self.msg: str = msg + self.range: Range = range + self.no_result: bool = no_result + + +class HoverTest(unittest.TestCase): + test_cases = [ + HoverTestCase( + filename="incomplete.k", + line=4, + column=23, + msg=f"ProviderConfig\ntype: ProviderConfig\ndefined in:{_DIR_PATH}/incomplete.k", + range=Range( + start=Position(line=3, character=15), + end=Position(line=3, character=29), + ), + ), + HoverTestCase( + filename="built_in.k", + line=28, + column=5, + msg="(built-in) max(): any\nWith a single iterable argument, return its biggest item.\n The default keyword-only argument specifies an object to return\n if the provided iterable is empty. With two or more arguments,\n return the largest argument.\n ", + range=Range( + start=Position(line=27, character=4), + end=Position(line=27, character=7), + ), + ), + HoverTestCase( + filename="built_in.k", + line=33, + column=11, + msg="(built-in) typeof(x: any, full_name: bool=False): str\nReturn the type of the kcl object", + range=Range( + start=Position(line=32, character=8), + end=Position(line=32, character=14), + ), + ), + HoverTestCase( + filename="built_in.k", + line=36, + column=5, + msg='(built-in) option(key: str, type: str="", required: bool=False, default: any=None, help: str="", help: str="", file: str="", line: int=0): any\nReturn the top level argument by the key', + range=Range( + start=Position(line=35, character=4), + end=Position(line=35, character=10), + ), + ), + HoverTestCase( + filename="hello.k", + line=8, + column=5, + msg=f"image\ntype: str\ndefined in:{_DIR_PATH}/hello.k", + range=Range( + start=Position(line=7, character=4), + end=Position(line=7, character=9), + ), + ), + HoverTestCase( + filename="hello.k", + line=12, + column=15, + msg=f"name\ntype: str\ndefined in:{_DIR_PATH}/hello.k", + range=Range( + start=Position(line=11, character=13), + end=Position(line=11, character=17), + ), + ), + HoverTestCase( + filename="hello.k", + line=7, + column=11, + no_result=True, + ), + HoverTestCase( + filename="hello.k", + line=5, + column=1, + no_result=True, + ), + HoverTestCase( + filename="import.k", + line=1, + column=10, + msg="hello", + range=Range( + start=Position(line=0, character=8), + end=Position(line=0, character=13), + ), + ), + ] + + def test_hover(self): + for i, t_case in enumerate(self.test_cases): + if i != 0: + return + got = hover.hover( + pos=ast.Position( + filename=str(_DIR_PATH / t_case.filename), + line=t_case.line, + column=t_case.column, + ), + ) + expect = ( + pygls_hover.Hover( + contents=MarkupContent( + kind=MarkupKind.PlainText, + value=t_case.msg, + ), + range=t_case.range, + ) + if not t_case.no_result + else None + ) + self.assertEqual( + expect, + got, + f"err hover for case[{i}], from pos:{t_case.filename}:{t_case.line}:{t_case.column}\nexpect: {expect}, got: {got}", + ) + + +class NoneInputTest(unittest.TestCase): + def test_definition_None_input(self): + node, scope_obj = go_to_def.definition(None, None) + self.assertIsNone(node) + self.assertIsNone(scope_obj) + + def test_scope_obj_desc_None_input(self): + result = hover.scope_obj_desc(None, None) + self.assertIsNone(result) diff --git a/test/test_units/test_langserver/test_wrapper.py b/test/test_units/test_langserver/test_wrapper.py new file mode 100644 index 000000000..8f588e424 --- /dev/null +++ b/test/test_units/test_langserver/test_wrapper.py @@ -0,0 +1,76 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import pathlib +import unittest +import json +from pathlib import Path +import pygls.lsp.types.basic_structures as pygls_basic +from kclvm.internal.gpyrpc.gpyrpc_pb2 import Position +import kclvm.tools.langserver.grpc_wrapper as wrapper + +_TEST_PATH_NAME = "test_data" +_GO_TO_DEF_PATH_NAME = "go_to_def" +_COMPLETE_PATH_NAME = "complete" +_DOCUMENT_SYMBOL_PATH_NAME = "document_symbol" +_HOVER_PATH_NAME = "hover" +_DIR_PATH = pathlib.Path(__file__).parent.joinpath(_TEST_PATH_NAME) + + +class RequestWrapperTest(unittest.TestCase): + def test_go_to_def_wrapper(self): + filename = str(_DIR_PATH / _GO_TO_DEF_PATH_NAME / "simple.k") + got_result = wrapper.go_to_def_wrapper( + Position(line=1, column=4, filename=filename), + ) + expect_result = ( + '[{"uri": "' + + filename + + '", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 1}}}]' + ) + self.assertEqual( + expect_result, got_result, f"expect: {expect_result}, got: {got_result}" + ) + + def test_complete_wrapper(self): + filename = str(_DIR_PATH / _COMPLETE_PATH_NAME / "simple.k") + got_result = wrapper.complete_wrapper( + pos=Position(line=1, column=5, filename=filename), + name="a", + ) + expect_result = '[{"label": "aa", "kind": 12, "tags": null, "detail": null, "documentation": null, "deprecated": false, "preselect": false, "sort_text": null, "filter_text": null, "insert_text": null, "insert_text_format": null, "text_edit": null, "additional_text_edits": null, "commit_characters": null, "command": null, "data": null}, {"label": "abs", "kind": 3, "tags": null, "detail": null, "documentation": null, "deprecated": false, "preselect": false, "sort_text": null, "filter_text": null, "insert_text": null, "insert_text_format": null, "text_edit": null, "additional_text_edits": null, "commit_characters": null, "command": null, "data": null}, {"label": "all_true", "kind": 3, "tags": null, "detail": null, "documentation": null, "deprecated": false, "preselect": false, "sort_text": null, "filter_text": null, "insert_text": null, "insert_text_format": null, "text_edit": null, "additional_text_edits": null, "commit_characters": null, "command": null, "data": null}, {"label": "any_true", "kind": 3, "tags": null, "detail": null, "documentation": null, "deprecated": false, "preselect": false, "sort_text": null, "filter_text": null, "insert_text": null, "insert_text_format": null, "text_edit": null, "additional_text_edits": null, "commit_characters": null, "command": null, "data": null}]' + self.assertEqual( + expect_result, got_result, f"expect: {expect_result}, got: {got_result}" + ) + + def test_document_symbol_wrapper(self): + filename = str(_DIR_PATH / _DOCUMENT_SYMBOL_PATH_NAME / "symbol.k") + got_result = wrapper.document_symbol_wrapper(filename) + expect_result = '[{"name": "a", "kind": 13, "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 9}}, "selectionRange": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 1}}, "detail": null, "children": null, "deprecated": false}, {"name": "b", "kind": 13, "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 9}}, "selectionRange": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, "detail": null, "children": null, "deprecated": false}, {"name": "Person", "kind": 23, "range": {"start": {"line": 2, "character": 0}, "end": {"line": 7, "character": 20}}, "selectionRange": {"start": {"line": 2, "character": 7}, "end": {"line": 2, "character": 13}}, "detail": null, "children": [{"name": "mixin", "kind": 7, "range": {"start": {"line": 3, "character": 4}, "end": {"line": 3, "character": 9}}, "selectionRange": {"start": {"line": 3, "character": 4}, "end": {"line": 3, "character": 9}}, "detail": null, "children": [{"name": "aMixin", "kind": 13, "range": {"start": {"line": 4, "character": 8}, "end": {"line": 4, "character": 14}}, "selectionRange": {"start": {"line": 4, "character": 8}, "end": {"line": 4, "character": 14}}, "detail": null, "children": null, "deprecated": false}], "deprecated": false}, {"name": "age", "kind": 7, "range": {"start": {"line": 6, "character": 4}, "end": {"line": 6, "character": 17}}, "selectionRange": {"start": {"line": 6, "character": 4}, "end": {"line": 6, "character": 7}}, "detail": null, "children": null, "deprecated": false}, {"name": "name", "kind": 7, "range": {"start": {"line": 7, "character": 4}, "end": {"line": 7, "character": 20}}, "selectionRange": {"start": {"line": 7, "character": 4}, "end": {"line": 7, "character": 8}}, "detail": null, "children": null, "deprecated": false}], "deprecated": false}, {"name": "person", "kind": 13, "range": {"start": {"line": 9, "character": 0}, "end": {"line": 11, "character": 1}}, "selectionRange": {"start": {"line": 9, "character": 0}, "end": {"line": 9, "character": 6}}, "detail": null, "children": null, "deprecated": false}]' + self.assertEqual( + expect_result, got_result, f"expect: {expect_result}, got: {got_result}" + ) + + def test_hover_wrapper(self): + filename = str(_DIR_PATH / _HOVER_PATH_NAME / "hello.k") + got_result = wrapper.hover_wrapper( + pos=Position(line=7, column=4, filename=filename), + code=Path(filename).read_text(encoding="utf-8") + ) + expect = { + "contents": { + "kind": "plaintext", + "value": f"image\ntype: str\ndefined in:{filename}" + }, + "range": pygls_basic.Range( + start=pygls_basic.Position(line=7, character=4), + end=pygls_basic.Position(line=7, character=9), + ), + } + expect_result = json.dumps(obj=expect, default=lambda x: x.__dict__) + self.assertEqual( + expect_result, got_result, f"expect: {expect_result}, got: {got_result}" + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2)